6

I am working with FreeRTOS (newlib) on a small embedded system and have found that printf and family waste a tremendous amount of stack space. I have many tasks, and I do not have enough ram to make the stack for each large enough to support printf() calls. To work around this, I am working to create a "printf server" task that will have a large stack and do all printf()s on the behalf of other tasks.

So my question is, what is the proper way to transport the va_list to the other thread(task)? The example below produces garbage params.

A quick word about how this works: task_printf() will plop its parameters into static variables and then signal the server_task to perform the actual print. When the server_task is done, it signals the client to continue.

// printf parameters
static va_list static_args;
static const char *static_format;
static int static_result;


// printf server task.  Processes printf requests forever
void server_task(void *pvParameters)
{
    while(1)
    {
        xSemaphoreTake(printf_start, portMAX_DELAY);  // wait for start command
        static_result = vprintf(static_format, static_args);
        xSemaphoreGive(printf_finished);  // give finish signal
    }
}


// get server task to print something for us
int task_printf(const char *format, ...)
{
    int result;

    xSemaphoreTake(printf_mutex, portMAX_DELAY); // lock

    va_start(static_args, format);
    static_format = format;

    xSemaphoreGive(printf_start);  // give start signal
    xSemaphoreTake(printf_finished, portMAX_DELAY);  // wait for completion

    va_end(static_args);

    ...
}
goertzenator
  • 1,960
  • 18
  • 28
  • Would it possibly make more sense to fix/replace the printf implementation in newlib not to need so much stack space? I know my printf implementation in musl libc uses less than 1kb for format strings that don't involve floating point. – R.. GitHub STOP HELPING ICE Jul 20 '12 at 21:51
  • The default stack size for FreeRTOS is 320 bytes and is good enough for most tasks, so requiring an extra 1kB of RAM for each task when I have many adds up to a lot of wasted RAM. I have about 30kB to work with on the microcontroller I am using. – goertzenator Jul 22 '12 at 02:09
  • Wow, that's pretty small. My implementation uses about 850 bytes right now, but I think it could be dropped to under 400 if some of the speed optimizations were dropped and XSI i18n argument reordering were removed. With that said, I think you might have the right approach. – R.. GitHub STOP HELPING ICE Jul 22 '12 at 03:43

1 Answers1

5

Well, the above actually works now. I messed up on semaphore initialization (not shown) which allowed the caller's stack to trash the args before the printf server could use them.

goertzenator
  • 1,960
  • 18
  • 28
  • I am looking for something similar, but it should not block the calling task (like your solution does). Any idea how to create sort of a copy of the va_list and send it to the other task via a queue? – LPG Jul 30 '14 at 13:08
  • If you have c++11 at your disposal you could probably implement task_printf as a variadic template that forms a closure and then queue that to the server without waiting for a response. But, even if that works on a little uC, I bet it is worse than just calling printf directly. I've done some c++11 on a uC, but not lambdas and closures. – goertzenator Aug 22 '14 at 15:40