6

Recovering Live Data with GDB

 3 years ago
source link: https://www.lujun9972.win/blog/2017/03/08/recovering-live-data-with-gdb/index.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Recovering Live Data with GDB

原文地址: http://nullprogram.com/blog/2015/09/15/

我最近遇到个问题, 有一个 运行很长时间的程序 ,它的输出被卡在一个C语言FILE变量的buffer中了. 这个程序已经运行两天了,它会把结果直接输出来,但是最后几k的内容要等到程序完成它的清理动作并退出后才能输出来. 而这个清理动作耗时可能要花几天(甚至更多). 这个问题本身要修复很简单 — 这个清理的动作其实完全是没有必要的 — 但是我不想又要花两天的时间把结果重新再计算一遍.

下面这段代码简单模拟一下这个问题. 第一个循环代表了长时间的运算过程,而后面的无限循环代表了那个无尽的清理动作.

#include <stdio.h>

int
main(void)
{
  /* Compute output. */
  for (int i = 0; i < 10; i++)
    printf("%d/%d ", i, i * i);
  putchar('\n');

  /* "Slow" cleanup operation ... */
  for (;;)
    ;
  return 0;
}

Buffered Output Review

printf and putchar 这两个C库函数,都会以某种方式来缓存输出的内容. 这意味着不是每次调用这些函数都会实际输出数据. 另一方面, POSIX定义的函数 readwrite 则是不带buffer的系统调用. 由于系统调用相对来说比较昂贵,因此一般会用带缓存的输入/输出来将大量针对小buffer的系统调用转换成一个针对大buffer的系统调用.

一般来说,若程序的标准输出为终端的话,它是按行来缓存的. 毕竟当程序完成了一行的输出内容后,用户很可能立即就想看到输出结果. 因此,若你编译该程序后是在终端上直接运行改程序的话,那么很可能在程序陷入无限循环之前就已经把结果输出来了.

$ cc -std=c99 example.c
$ ./a.out
0/0 1/1 2/4 3/9 4/16 5/25 6/36 7/49 8/64 9/81

然而若把标准输出重定向到文件或管道中的话, 这个输出很有可能就会被缓存起来了,这个缓存大小一般为4KB. 这样一来,不管你等多长时间,改程序的输出始终都为空. 真正的输出内容被卡在进程内存中的一个FILE对象的buffer中了.

$ ./a.out > output.txt

修复这个问题的主流方法是调用 fflush 函数, 在开始一段耗时漫长而无输出结果的操作前可以用它来强行将buffer中的内容输出. 可惜,我早在两天前并没有想到这一出.

Debugger to the Rescue

幸运的是,有一个东西可以暂停正在运行的程序并帮我们维护程序的状态:那就是调试器.

第一步,找出进程号(上面输入 output.txt 的那个进程).

$ pgrep a.out
12934

现在让启动GDB,让它attach那个进程,这会暂停程序的运行.

$ gdb ./a.out
Reading symbols from ./a.out...(no debugging symbols found)...done.
gdb> attach 12934
Attaching to program: /tmp/a.out, process 12934
... snip ...
0x0000000000400598 in main ()
gdb>

到了这一步,我就可以手工检查 stdout 的FILE结构,并从中抽取出buffer中的内容了. 不过最简单的方法是执行我一开始忘掉的条语句 fflush(stdout).

gdb> call fflush(stdout)
$1 = 0
gdb> quit
Detaching from program: /tmp/a.out, process 12934

程序依旧还在执行,我们已经得到结果了.

$ cat output.txt
0/0 1/1 2/4 3/9 4/16 5/25 6/36 7/49 8/64 9/81

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK