我正在为syslog编写一个简单的包装器,以便更轻松地从我的程序中进行日志记录,并允许在选择时将日志条目转储到控制台。我定义了以下日志功能
void logDebugFunction (int lineNumber, char* filename, const char* functionName, char* format, ...)
{
if (LOG_DEBUG >= argPtr->logLevel)
{
char buffer[1000];
char *entry;
va_list args;
va_start(args, format);
vsprintf(buffer, format, args);
va_end(args);
sprintf(entry, "%s:%d - %s - %s",filename, lineNumber, functionName, buffer);
syslog(LOG_MAKEPRI(0, (LOG_DEBUG)), "%s", entry);
if (argPtr->verbose)
{
// Print to stdout too
printf( "%s", entry);
printf("\n");
}
}
}
通过以下宏调用:
#define logDebug(format,...) logDebugFunction(__LINE__, __FILE__, __func__, format, __VA_ARGS__)
从main函数,如下:
int main(int argc, char *argv[])
{
// Set up syslog connection
openlog("ARController", LOG_CONS|LOG_PID|LOG_NDELAY, LOG_DAEMON);
// Set up our global arguments
struct arguments arguments;
argPtr = &arguments;
// set default values
arguments.verbose = 0;
arguments.foreground = 0;
arguments.logLevel = LOG_WARNING;
// Send a test debug message
logDebug("Test Debug message %d %s", 5, "a string");
// Close our syslog connection
closelog();
}
现在,当我尝试运行唯一的输出时,我得到Segmentation fault (core dumped)
,显然不是我想要的。
我使用gdb和--save-temps
标志进行了一些调查,以验证以下内容:
main.i
我可以看到main
中的logDebug调用已替换为logDebugFunction(72, "src/main.c", __func__, "Test Debug message %d %s", 5, "a string");
,这是我期望在此处看到的内容。vsprintf
logDebugFunction
行
vsprintf
之前,函数的所有必需参数都是正确的:
Breakpoint 2, logDebugFunction (lineNumber=72, filename=0x401450 "src/main.c", functionName=0x4014d3 <__func__.4035> "main", format=0x401437 "Test Debug message %d %s")
va_list
条目是我期望的条目,如以下gdb命令所示(found here)
(gdb) p *(int *)(((char*)args[0].reg_save_area)+args[0].gp_offset)
$5 = 5
(gdb) p *(char * *)(((char*)args[0].reg_save_area)+args[0].gp_offset+8)
$6 = 0x40142e "a string"
当我进入vsprintf
调用时,似乎参数是正确的:__IO_vsprintf (string=0x7ffffffedb40 "\200V
“,格式= 0x401437”测试调试消息%d%s“,args = 0x7ffffffedb28)at iovsprintf.c:32`
因此,一切似乎都井井有条,我对于问题是什么以及我接下来可以采取的步骤有点失落。
答案 0 :(得分:4)
我没有看到任何错误(忽略没有健全性检查)与您使用va_list
&amp;的方式vsprintf
,所以它可能需要超过1000个charcaters而且buffer
根本不够大或你以错误的方式传递argumnts?您是否尝试将vprintf
用于调试目的?
但我在接下来的几行中看到了一个确定的问题:
char *entry;
...
sprintf(entry, "%s:%d - %s - %s",filename, lineNumber, functionName, buffer);
entry
是一个统一指针,指向无处。如果您尝试读取/写入该指针,则会得到未定义的行为。这是一个段错误。
使用snprintf
,您可以获得表达式的长度,然后使用malloc
为它动态分配内存(不要忘记随后释放它)。或者你可以做到
char entry[1024];
...
sprintf(entry, "%s:%d - %s - %s",filename, lineNumber, functionName, buffer);
假设没有条目超过1023个字符。
来自评论的 编辑 请求详细说明从snprintf
获取长度
让我们从函数的签名开始
#include <stdio.h>
int snprintf(char *str, size_t size, const char *format, ...);
手册页描述说:
手册页printf(3)
函数
snprintf()
和vsnprintf()
最多写size
个字节 (包括终止空字节('\0'
))到str
。
如果您想获得长度,请将size
设置为0并将str
设置为NULL
int msglen = snprintf(NULL, 0, fmt, exp1, exp2, exp3,...);
请记住,此行为符合C99。使用较旧的编译器或较旧的C标准进行编译可能会为您提供未指定的返回值。
答案 1 :(得分:0)
__attribute__ ((format (printf
); snprintf
); sprintf(entry,
使用未初始化的变量entry
而不是导致未定义行为的合适缓冲区,尝试在entry
指向的随机位置写入是最可能发生段错误的原因。答案 2 :(得分:0)
在我的情况下,当我在编写C11时意外返回的标头中标有_Noreturn
的函数中遇到了此问题(但在函数本身中没有)。
此错误没有引起编译错误,没有发出警告(带有-Wall
),也没有被地址清理器(asan)或线程清理器(tsan)捕获,但是此后的代码执行回报很高,它给了我误导性的通话记录。