void foo(int fmt, ...)
{
}
//I hook foo
static void (*original_foo)(int fmt, ...);
void replaced_foo(int fmt, ...)
{
printf("Hooking");
va_list args;
va_start(args, fmt);
//do something
va_end(args);
//But I want to call the original_foo function,
//I do not know how to invoke it ...
}
//Hook Function not include ...
Hook(foo, replaced_foo, (void **)&original_foo);
If you have a corresponding original_foo_v() which takes a va_args, you are lucky: you can use that.
If you don't (such as if you use DbgPrintf() or LStrPrintf() for interfacing with LabVIEW), you'll have to craft something on your own.
Essentially, you'll have to
examine the va_list you get,
find its stack frame by walking along the stack,
allocate as much memory on the stack as you need, assuming that you need the whole area between where the va_list points to and the next stack frame,
calling the non-va-aware function.
Of course, you'll have to do this for each and every platform you intend to support...
Good luck and have fun.
Related
I am looking to make a small logger to be used like this:
logger log;
const char* text = "World";
log.write("Hello %s", text);
log.write("Step %d", 1);
This is my code, which doesn't work correctly:
class logger
{
public:
void write(const char* msg, ...)
{
FILE* file = fopen("c:/test.txt", "a");
if(file != NULL)
{
va_list args;
va_start(args, msg);
fprintf(file, "%s\n", msg, args);
va_end(args);
fclose(file);
}
}
};
This is what I get:
Hello %s
Step %d
I never used varargs before so I am not sure if I am using it correctly.
You're thinking that C behaves in a manner similar to Java or Python or other languages that can "splat" an array argument to a function that accepts varargs, but C isn't that sophisticated. When you pass args to fprintf, C literally pushes the value of args (a variable of type va_list) onto the stack. What you need instead is to push the contents of args onto the stack.
Or, instead, you could use a function that accepts a va_list as a parameter. That function is vprintf (and friends vsprintf, vfprintf, etc.).
You have another issue, which is that you're fprintf-ing the caller's "msg" parameter using "%s" but then apparently expecting fprintf to recursively use the result as a format string and fprintf the args too. That's not going to work. Instead just use the caller's msg as the format string.
va_list args;
va_start(args, msg);
vfprintf(file, msg, args);
fputc('\n', file);
va_end(args);
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
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().
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).
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.