为什么 GLib 在递归日志下中止?
大多数时候我的 GLib 程序运行良好。但是,当
出现** (process:pid): Message (recursed): blah lah blah
等日志时,程序将中止。
GLib手册说G_LOG_FLAG_RECURSION默认被认为是致命的。
但我只是不明白“递归消息”是什么意思?什么时候会出现递归消息?
谢谢
Most of time my GLib program runs well. However, when logs like
** (process:pid): Message (recursed): blah lah blah
appear, the program will abort.
GLib manual says G_LOG_FLAG_RECURSION is considered fatal by default.
But I just can't understand what does "recursive messages" mean? When will recursed message occur?
Thanks
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
浏览 glib/gmessages.c 给我一个非常强烈的印象:如果
g_logv()
需要自己记录错误,则设置G_LOG_FLAG_RECURSION
。考虑内存不足;当内存分配尝试失败时,程序将尝试记录内存分配失败,并可能退出。当日志记录例程尝试分配内存来记录消息时,它可能会失败。因此,日志记录例程会跟踪它们被调用的“深度”,并切换内存分配策略(它们在堆栈上而不是堆上分配)(如果它是递归日志记录调用)。
每当日志例程收到错误消息并想要记录该错误时,就会发生真正的错误,因此尝试使用另一种机制进行日志记录然后退出是有意义的。
所以你可能只是看到了真正问题的遥远症状。您可以使用
ltrace(1)
来尝试发现问题,或者您可以启用核心转储(ulimit -c unlimited
)并尝试查找导致该问题的调用链使用 gdb 的bt
命令导致程序崩溃。Skimming through glib/gmessages.c gives me the very strong impression that
G_LOG_FLAG_RECURSION
is set ifg_logv()
needs to log an error itself.Consider running out of memory; when a memory allocation attempt fails, the program will try to log the memory allocation failure, and probably exit. When the logging routine attempts to allocate memory to log the message, it is probably going to fail. So, the logging routines keep track of how 'deep' they have been called, and switch memory allocation strategy (they allocate on the stack instead of on the heap), if it is a recursive logging call.
Anytime the logging routines get an error message and would want to log the error, something really bad is going on, so it makes sense to try to log with another mechanism and then exit.
So you're probably just seeing a far-off symptom of the real problem. You could use
ltrace(1)
to try to spot the problem, or you could enable core dumps (ulimit -c unlimited
) and try to find the call chain that causes the program to crash using gdb'sbt
command.请注意,如果您正在注册自定义处理程序(使用 g_log_set_handler)并且该处理程序(或其被调用者之一)尝试通过调用记录错误,则也可能会发生对 g_* 日志记录例程的递归调用到 g_* 例程。
另请注意,glib 决定任何递归也应该是致命的(即使是非无限的或只有一层深度)。对于 sarnold 对内部故障递归的回答中描述的情况来说,这当然是有意义的,但在尝试修复自定义处理程序重复出现的问题时可能并不明显。因此,glib 早在第一次递归调用 g_* 时就检测到递归;它甚至没有检查您的自定义处理程序是否已附加。因此,您永远不会看到对处理程序的递归调用。所有这些意味着诸如小心地在处理程序体内取消注册处理程序(以及类似的事情)之类的努力是徒劳的。
您必须绝对确保自定义处理程序中的调用堆栈中的任何内容都不会调用 g_* 例程(如果您处理对外部代码的调用并且确实使诸如尝试将日志消息传递到远程目标之类的事情变得复杂,这可能会变得棘手或其他什么)。
Note that a recursive call to a g_* logging routine can also happen if you are registering a custom handler (with
g_log_set_handler
) and that handler (or one of its callees) tries to log an error with a call to a g_* routine.Also note that glib decided that any recursion should also be fatal (even if non-infinite of just one-level deep). This certainly makes sense for the case described in sarnold's answer of recursion on internal failure, but might not be obvious when trying to fix the issue in the case of a custom handler recurring. So, glib detects recursion as early as the first recursive call to g_*; it does not even get to checking whether your custom handler is attached. Hence, you will never see a recursive call into your handler. All this means that efforts such as carefully unregistering the handler inside the handler's body (and similar things) are futile.
You have to make absolutely sure that nothing down the call stack from your custom handler will ever call a g_* routine (which can get tricky if you handler calls into outside code and does complicate things like trying to deliver the log message to a remote destination or something).