printf to USB-CDC on FreeRTOS - c

I'm working on a STM32 Cortex M4 with a FreeRTOS running.
I try to redirect my printf to the USB CDC output but it seems like my implementation isn't thread safe. A loop of printf('test') prints
ettttt
tttetttttttst
ttetettttttettttttettttttt
ttttttttttttt
t
tttttttttetttttttstttt
tettttttttttttt
t
tttttttetttetttttttett
ttttettttttetettetttt
ttttttttt
ttetetttt
t
ttt
I use the following putchar prototype
PUTCHAR_PROTOTYPE {
CDC_Transmit_FS((uint8_t*)&ch, 1);
return ch;
}
Additionally I use the printf-stdarg.c, at least I thought I do. But when I change
int printf(const char *format, ...)
{
va_list args;
va_start( args, format );
return print( 0, format, args );
}
to an empty implementation
int printf(const char *format, ...)
{
return 0;
}
it still prints to the console as before. So it seems like it's not actually using the implementation from printf-stdarg.c.
My idea was to surround the print call with a mutex.

Related

Wrapper for printf

I am coding under Arduino and I would like to develop serial print formatting function, so I am trying to use sprintf of unknown sized buffer. Basically, we can avoid talking about Arduino and its serial output and consider writing text to a buffer and then printing it by using printf. I've tried this one:
#include <stdio.h>
#include <stdarg.h>
void printf0( const char* format, ... ) {
va_list args;
va_start(args, format);
vprintf(format, args);
va_end( args );
}
void printf1(const char* format,...) {
va_list args;
va_start(args, format);
char buf[vsnprintf(NULL, 0, format, args)];
sprintf(buf, format, args);
printf(buf);
va_end(args);
}
int main()
{
printf0("Hello, %d!\n", 15);
printf1("Hello, %d!\n", 15);
return 0;
}
printf0 function is an accurate example I found here. My tries is function printf1, which produces unpredictable number. Example output of the above programme is:
Hello, 15!
Hello, 860799736!
args is a va_list, so you cannot call sprintf with it. You have to use vsprintf or vsnprintf:
sprintf(buf, format, args);
should be
vsnprintf(buf, sizeof buf, format, args);
Also you should add 1 to the size of buf for the 0-terminator of the string:
char buf[vsnprintf(NULL, 0, format, args) + 1];
It seems that the first call to vsnprintf changes args, so you have to add
va_end(args);
va_start(args, format);
between the 2 calls:
http://ideone.com/5YI4Or
It seems that the first call to vsnprintf changes args, but you should not call va_start twice. You should use va_copy instead, so add
va_list args2;
va_copy(args2, args);
after initializing args. Also do not forget to call va_end(args2); too:
http://ideone.com/loTRNL
Link to the va_copy man page: https://linux.die.net/man/3/va_copy

Pass a function with arguments as argument

I want to pass a function with arguments as argument into some other function, but I don't know how (or if) I can do that.
What I want to do is:
#include <stdio.h>
void getInput(char* request, int (*func)());
int main()
{
double input;
getInput("Input: ", scanf("%lf", &input));
}
void getInput(char* request, int (*func)())
{
// do stuff
func()
// do more stuff
}
Unfortunately this doesn't work. I think the scanf is getting executed when I'm trying to pass it as an argument.
How do I get this to run in getInput?
You can't do that in C, the language doesn't support such a construct.
You can often solve this exact type of problem using variable-arguments functions again:
void getInput(const char *request, const char *fmt, ...)
{
va_list args;
printf("%s: ", request);
fflush(stdout);
va_start(fmt, args);
vscanf(fmt, args);
va_end(args);
}
Usage is like this:
int main(void)
{
double input;
getInput("Input", "%lf", &input);
return 0;
}
Of course you might want to add proper return-value handling, otherwise it's impossible to know if the embedded vscanf() succeeded so the variable is OK to use.

Reporting information using syslog

I am trying to write a function which will take a priority level and a variable amount of strings as arguments to log information in an application.
The function looks something like this so far:
int _logf(int priority, char *fmt, ...)
{
if (log.priority >= priority) {
syslog(priority, "LOG:%s", fmt);
}
/* stderr and syslog */
}
log.priority is an int set at run time which could be LOG_INFO / LOG_DEBUG / LOG_ERR
and in use:
_logf(LOG_INFO, "Starting app version %s", "1.0");
Is this an acceptable way to send log messages to syslog?
This won't work as you do not involve the variable number of parameters possibly passed into your function.
On how to do this you might take a look a the following example using vsyslog():
#include <syslog.h>
#include <stdarg.h>
...
int _logf(int priority, const char * fmt, ...)
{
if (log.priority >= priority)
{
va_list ap;
va_start(ap, fmt);
vsyslog(priority, fmt, ap);
va_end(ap);
}
}
Then call it for example like so:
_logf(LOG_INFO, "LOG %s:%d (%s) - %s", __FILE__, __LINE__, __func__, "Just some info.");
Update:
To additionally log to stderr you might like to look ath function vfprintf().

Can I use two level of variable length argument functions...?

I have one problem with variable length argument debug log print function. I will just simulate the code here.
void SecondPrint(int level, const char* format,...)
{
//Printing the log here
va_list arg;
va_start(arg, format);
vprintf(format, arg);
va_end(arg);
}
void FirstPrint(int level, const char* format,...)
{
SecondPrint(level,format);
}
void main()
{
int level = 100;
FirstPrintf("Log level = %d message = %s \n",level,"Error message");
}
"SecondPrint" is supposed to print "100 Error message" as expected, But its not printing like that its printing " Error message".
I am not getting whats wrong with this one. I am suspecting the way of calling "SecondPrint" from "FirstPrint" function. FirstPrint is receiving remaining arguments through ... but its invoking SecondPrint with level and format arguments.
I can't use SecondPrint from main directly. I have to use FirstPrint and FirstPrint has to call SecondPrint to print the log. So how can I achieve this one.. I thought to use VA_ARGS but it is for only macro definitions not in function definition.
And one more thing I can't do like *#define FirstPrint(a,b,...) SecondPrint(a,b,...)*
any help is highly appreciated thanks in advance.
C varargs are not designed to be passed more than one level; the kind of necessary stack manipulation required is too deep for the language. Typically, in a case like this, you would have a version of SecondPrint analagous to vprintf -- SecondPrintV or similar, and you would have FirstPrint invoke SecondPrintV after extracting the varargs, rather than invoking SecondPrint directly (and, for consistency, usually have SecondPrint invoke SecondPrintV internally).
You need to pass a va_list to SecondPrint, so it can access the arguments to FirstPrint
void SecondPrint(int level, const char* format, va_list args)
{
//Printing the log here
vprintf(format, arg);
}
void FirstPrint(int level, const char* format,...)
{
va_list args;
va_start(args, format);
SecondPrint(level, format, args);
va_end(args);
}
void main()
{
int level = 100;
FirstPrintf("Log level = %d message = %s \n",level,"Error message");
}
It may be possible to do it in specific platform-specific ways, or (since the arguments are exactly the same in the same order) use assembly to "replace" the current function frame with the function to call. GCC also has an extension to do it with the __builtin_apply_args() and __builtin_apply() functions (although you must somehow figure out how to compute the size of the stack argument data).

How can I temporarily redirect printf output to a c-string?

I'm writing an assignment which involves adding some functionality to PostgreSQL on a Solaris box. As part of the assignment, we need to print some information on the client side (i.e.: using elog.)
PostgreSQL already has lots of helper methods which print out the required information, however, the helper methods are packed with hundreds of printf calls, and the elog method only works with c-style strings.
Is there I way that I could temporarily redirect printf calls to a buffer so I could easily send it over elog to the client?
If that's not possible, what would be the simplest way to modify the helper methods to end up with a buffer as output?
If you define your own version of printf and link to it prior to the libc version, your version will supersede the standard version.
You should also be able to supersede the standard version by using LD_PRELOAD to load a library that has printf defined.
To write your own printf, you will want to use stdarg functionality:
int printf(const char *fmt, ...)
{
int rv;
va_list ap;
va_start(ap, fmt);
if (redirect_printf)
{
#ifdef HAVE_VLOG
// If you have a vlog function that takes a va_list
vlog(fmt, ap);
rv = ...;
#else
char buffer[LARGESIZE];
rv = vsnprintf(buffer, sizeof(buffer), fmt, ap);
log(buffer);
#endif;
}
else
{
rv = vprintf(fmt, ap);
}
return rv;
}
This simple version will truncate data when the final formatted output is greater than LARGESIZE. If you don't want that, you can also call vsnprintf first with a NULL buffer to get the final size, do a dynamic allocation and then a second call to vsprintf to format the buffer.
You're wrong — elog supports format strings just like printf. Here's an example from Postgres source code:
elog(DEBUG4, "TZ \"%s\" gets max score %d", tzname, i);
So all you need is to add elog where there is printf using the same parameters.
The simplest way is to modify the helper methods to call sprintf(). Whether or not you can hack that in easily, I don't know. Maybe
#define printf(...) sprintf(buffer, __VA_ARGS__)
Will do it for you. You'll still need to define buffer for each helper function, and get its contents returned to whoever cares about them.
If you can tolerate the use of a temporary file you could redirect standard out with the freopen() call:-
newstdout = freopen("/tmp/log", "w", stdout);
This will force all the printf's to be written to /tmp/log instead of the console output. At some convenient point later in your program you could then open the same file for reading:-
readfd = fopen("/tmp/log", "r");
and forward the contents that have been added using something like this:-
void forward_to_elog(void)
{
int bytesread;
char buf[100];
memset(buf,0,100);
do {
memset(buf,0,100);
bytesread = fread(buf, sizeof(buf)-1, 1, readfd);
/* call elog(buf) */ ;
} while(bytesread);
}
If you keep the file open you can call forward_to_elog() multiple times to incrementally forward the contents that have been added.
The tmpnam() function can be used to get a name for the temporary file if you don't want to have to statically code one.

Resources