【问题标题】:Valgrind reports "still reachable" bytes when using vfwprintf使用 vfwprintf 时,Valgrind 报告“仍然可以访问”字节
【发布时间】:2019-11-19 04:59:32
【问题描述】:

我正在编写两个函数作为打印到stderr 的便捷快捷方式:一个我调用eprintf 来打印常规字符串;和ewprintf 用于打印宽字符串。我写了eprintf如下:

int eprintf(const char* fmt, ...) {
    va_list args;
    va_start(args, fmt);
    int written = vfprintf(stderr, fmt, args);
    va_end(args);
    return written;
}

ewprintf 是一样的,只是它的fmt 参数是const wchar_t* 类型,而我使用vfwprintf 来写stderr

我在名为eprintf.h 的头文件中声明了这两个函数,然后在eprintf.c 中定义。我的主要功能只是为了测试这些:

#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include "eprintf.h"

int main(int argc, char** argv) {
    setlocale(LC_ALL, "");
    ewprintf(L"This is a test.\n");
    return 0;
}

我用eprintf 替换了ewprintf,然后用valgrind 测试了程序,没有任何问题——分配给释放的分配数量相同,没有标记为“仍可访问”的字节;但是,当使用ewprintf--调用vfwprintf--valgrind 时报告两个块中的5120 个字节“仍然可以访问”,有33 个分配,但只有31 个释放。运行更详细的泄漏检查以查找“仍然可访问”内存的原因,valgrind 提供了包含vfwprintf 的跟踪。

我还尝试将eprintfewprintf 定义为可变参数宏,就像看到的here 一样。但是,我在将ewprintf 写成宏时遇到了同样的问题。

那么,我的第一个问题是:在这种情况下,这个“仍然可以解决”的问题实际上是否值得关注?请注意,valgrind 不会将任何字节报告为“肯定丢失”、“间接丢失”或“可能丢失”。其次,即使这实际上不是问题本身,我能做些什么来解决它吗?

更新:这是我对ewprintf 的实现,以进一步澄清:

// Notice that its parameter is a wide-character string, and it calls `vfwprintf`.
int ewprintf(const wchar_t* fmt, ...) {
    va_list args;
    va_start(args, fmt);
    int written = vfwprintf(stderr, fmt, args);
    va_end(args);
    return written;
}

【问题讨论】:

  • 为了消除任何误解,也许您应该向我们展示ewprintf()功能,而不仅仅是描述与eprintf()相比的差异。
  • 这是一个可以忽略的非问题。

标签: c


【解决方案1】:

简答:

缓冲区与stderr 相关联。可以在退出前通过显式closing(stderr) 删除该消息,但请参阅下面的注释。

长答案:

根据 valgrind,“仍然可以访问”的内存是从 vfwprintf 的缓冲写入中分配的。那些与 stderr 关联的缓冲,并在第一次调用时分配。

当 stderr 关闭时,缓冲区会自动关闭。通常,这是在程序退出之前完成的。看起来 valgrind 报告挂钩是在 final 退出之前执行的。可以通过在main 中显式关闭 stderr 来消除此消息。见下文。

虽然可以使用此选项,但我认为最好让 stderr 保持打开状态,以防在程序退出期间需要显示某些内容(例如,atexit 或其他清理功能)。尝试访问 stderr 的代码会在没有任何有用的消息帮助调试的情况下使程序崩溃。

编辑 1:

根据 valgrind 文档 (http://valgrind.org/docs/manual/manual-core-adv.html#manual-core-adv.clientreq),可以使用宏 RUNNING_ON_VALGRIND 来检测是否在 valgrind 下运行,只有在这种情况下才可能关闭 stderr。免责声明:我没有任何实际使用此宏的经验,请谨慎使用。

int main(int argc, char** argv) {
    setlocale(LC_ALL, "");
    ewprintf(L"This is a test.\n");
    fclose(stderr) ;
    return 0;
}
valgrind --leak-check=full --show-leak-kinds=all ./a.out

==38349== Memcheck, a memory error detector
==38349== Command: ./a.out
==38349== 
This is a test.
==38349== 
==38349== HEAP SUMMARY:
==38349==     in use at exit: 5,120 bytes in 2 blocks
==38349==   total heap usage: 32 allocs, 30 frees, 9,500 bytes allocated
==38349== 
==38349== 1,024 bytes in 1 blocks are still reachable in loss record 1 of 2
==38349==    at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==38349==    by 0x4EBA18B: _IO_file_doallocate (filedoalloc.c:101)
==38349==    by 0x4EBB88C: _IO_wfile_doallocate (wfiledoalloc.c:70)
==38349==    by 0x4ECA378: _IO_doallocbuf (genops.c:365)
...
==38349==    by 0x4EA7089: buffered_vfprintf (vfprintf.c:2343)
==38349==    by 0x4EA40FD: vfwprintf (vfprintf.c:1301)
==38349==    by 0x1087F1: ewprintf (in /home/owner/Projects/SO/58904606/a.out)
==38349==    by 0x108847: main (in /home/owner/Projects/SO/58904606/a.out)
==38349== 
==38349== 4,096 bytes in 1 blocks are still reachable in loss record 2 of 2
==38349==    at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==38349==    by 0x4EBB858: _IO_wfile_doallocate (wfiledoalloc.c:79)
==38349==    by 0x4ECA378: _IO_doallocbuf (genops.c:365)
...
==38349==    by 0x4EA7089: buffered_vfprintf (vfprintf.c:2343)
==38349==    by 0x4EA40FD: vfwprintf (vfprintf.c:1301)
==38349==    by 0x1087F1: ewprintf (in /home/owner/Projects/SO/58904606/a.out)
==38349==    by 0x108847: main (in /home/owner/Projects/SO/58904606/a.out)
==38349== 
==38349== LEAK SUMMARY:
==38349==    definitely lost: 0 bytes in 0 blocks
==38349==    indirectly lost: 0 bytes in 0 blocks
==38349==      possibly lost: 0 bytes in 0 blocks
==38349==    still reachable: 5,120 bytes in 2 blocks
==38349==         suppressed: 0 bytes in 0 blocks
==38349== 
==38349== For counts of detected and suppressed errors, rerun with: -v
==38349== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

【讨论】:

  • 感谢您的回复。在我使用atexit 注册的函数末尾关闭stderr 或其他文件流是否合适?
  • @Personage 考虑到可能的不利方面(在程序退出期间丢失重要消息)与有利方面(干净的 valgrind 报告),我认为最好不要执行无条件关闭。我将在有关条件关闭的答案中添加一些 cmets(例如,仅在使用 valgrind 运行时才关闭)
猜你喜欢
  • 2011-05-23
  • 1970-01-01
  • 2012-05-07
  • 2023-03-29
  • 2015-08-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-13
相关资源
最近更新 更多