openlang

fleetcfg 在 200 台主机下主机名乱码,malloc 失败时还会越漏越多。是一个 bug 还是两个?

我维护一个小的 C 工具 fleetcfg,它加载一个返回主机列表的 Lua 配置,把它们复制进 HostList,然后打印出来。TCP 探测还是 TODO。

有两个现象,我不确定它们有没有关系。

  1. 用真实的 200 台主机的生产配置时,最后列表里有几行名字为空,或者是奇怪的字节。3 台主机的测试配置从没出过这个问题。我怀疑是 Lua 那边,可能是 luaL_dofile 在栈上留下了什么。lua_gc 那个调用我基本排除了,因为加它之前输出就是对的。这一点还没确认。

  2. 用 LD_PRELOAD 包装让 malloc/realloc 在第 N 次调用后失败,hostlist_push 里失败时会按设计打印 "out of memory" 并以 1 退出。但 valgrind 报 "definitely lost",而且这块随主机数增长。我以为是 Lua 的清理漏了什么,剩下的由 hostlist_free 兜底。

valgrind --leak-check=full ./fleetcfg $PWD/prod hosts.lua

我的 checkout 路径里有个空格,valgrind 的输出在我看来有点怪。

相关片段如下。

l->items = realloc(l->items, ncap * sizeof(Host));
if (l->items == NULL)
    return -1;
const char *name = lua_tostring(L, -2);
...
lua_pop(L, 3);
...
hostlist_push(out, name, port)

你们会从哪里开始查?我不敢保证这两个是分开的,而且我还没在调试器里跑过任何东西。

从英语翻译查看原文

后续讨论 5

顺着彼此的依据读下去
  1. 固定链接

    把 name strdup 一下,我这边乱码就这样修好了。字符串被弹出栈后 Lua 就可以回收它,而 200 台主机足够触发一次 GC。另外 l->items = realloc(l->items, ...) 在 realloc 失败时会泄漏旧的块。用个临时指针。

    从英语翻译查看原文
  2. 固定链接

    不太同意。我不会怪 luaL_dofile 和栈,我觉得就是指针的问题。

    手册说 lua_tostring 返回的字符串只在该值还在栈上时有效,之后 Lua 可以把它释放掉,见 https://www.lua.org/manual/5.4/manual.html#lua_tolstring

    你先 lua_pop(L, 3),再用 name 调用 hostlist_push,所以它已经是悬空指针了。3 台主机不会触发 GC,所以看起来没问题。200 台就会。

    修复办法是在 hostlist_push 里 strdup,检查 NULL,并在 hostlist_free 里释放每个名字。先做这个,再去追泄漏。之后重新跑 valgrind,因为这两个问题可能并不是分开的。

    从英语翻译查看原文
  3. 固定链接

    哦,有意思,这块内存随主机数量增长,这是个不错的线索!你有没有在每次循环迭代开头和 pop 之后打印 lua_gettop?还有那行 valgrind 命令里的 $PWD 带着那个空格时有没有加引号?

    从英语翻译查看原文

同一个问题的其他角度

  1. 悬垂指针的警告消失了,指针还在

    通过一个包装层把名字传给 C++ 库,第一版只有一行,CString::new(name).unwrap().as_ptr()。rustc 马上就警告说,临时的 CString 在语句结束时就销毁了,指针会悬垂。行,有道理。于是我把它挪进一个小函数,…

    回复 2最近活动 rustcpp
  2. 同样的 sink 调用,这一行是拷贝,下一行就是移动

    Nim 2 是按调用点决定拷贝还是移动的,不是按 proc。 同一个参数,同一个 proc。第一次调用会拷贝,因为后面还要用 a。第二次就移动了。调用点上没有任何东西告诉你是哪种。nim c --expandArc:main 会打印改写后的函数体,两…

    回复 2最近活动 nim
  3. getline 读出来的行是乱码,另外还有泄漏

    基本卡住了。先修哪个 bug? hosts.txt: hosts_bad.txt:

    回复 6最近活动 c