生成core文件
要寻找还未写出的日志消息,需要用到core dump时生成的core文件,但是在默认情况下是不生成的,因此需要先开启。
通过ulimit -c可查看core文件是否开启:
如果显示为0,说明未开启,此时可以通过ulimit -c unlimited开启,这里unlimited指的是core文件的最大大小,可以设置为其它数字
ulimit -c unlimited
可以看到,运行程序生成的可执行文件core_dump,程序发生了异常退出,此时生成了名为"core"的文件,这个文件就记录了程序崩溃的信息。此外还可以看到,已经生成了一个.log日志文件,打开这个日志文件,如下所示:
gdb调试Core文件
从core文件中去获得需要的信息。
通过gdb [execfile] [corefile]进行调试,如下所示:(注意:调试所用的execfile必须是debug生成的!!!否则很多关键信息都看不到)
特别需要注意gdb core文件的格式,是gdb 执行程序 coredump文件
gdb main_log_test core
通过gdb信息可以看到,Program terminated with signal SIGSEGV, Segmentation fault.。LWP是线程的标识,这里当前线程的LWP为87016,一共有两个线程:LWP 87016和87017,这个数字相当于线程的tid,每个线程都是不同的,无论是否在同一进程。从这里我们可以看出来崩溃是在主线程,但我们关注的是日志写入线程:
通过thread info 查看线程信息:
看不到什么有效的信息,那我们直接用thread id切换线程栈,然后用bt命令查看是否是日志写入栈。
我们直接用thread 2切换后可以看到,这里有两个线程,可以看到,Id2线程位于pthread_cond_timewait,因此这显然就是后端日志线程,异常的线程显然就是Id1线程了。
用bt(即backtrace)查看线程的调用栈信息:
说明此时日志线程并没有再写,而且此时
buffers_为空,我们则只需要关心currentBuffer_。
然后通过frame 2切换栈空间
此时所在的环境,就相当于是append函数的栈帧未写出的日志消息,只可能存在于currentBuffer_或buffers_中,而currentBuffer实际上是一个unique_ptr,因此可以通过currentBuffer.get()来获取currentBuffer所指向的LogStream,用print打印变量,用p打印指针如下所示:
print *currentBuffer_.get()
这里报错是因为FixedBuffer中的值太大,超过了max-value-size,因此重新设置max-value-size为无限大:
set max-value-size unlimited
然后再print *currentBuffer_.get()
可以看到此时已经在data_后面显示了一部分日志消息,不过还有很多被省略了,这是因为gdb的终端输出长度有限制,默认为200个字符,可以修改这个限制,如下所示:
show print elements
set print elements unlimited
show print elements
然后重新打印变量值print *currentBuffer_.get(),如下所示(此时会打印会比较慢,需要等待一小会):
可以看到满屏幕都是一条一条的日志消息,如果我们想把这些消息全部输出到外部的文件中,可以通过logging实现,即在print之前通过set logging file [filename]来指定输出文件,然后set logging on来开启拷贝,此后终端上的所有打印的信息都会拷贝到指定的输出文件中,通过set logging off来停止拷贝。
set logging file gdbinfo.txt
set logging on
然后再print *currentBuffer_.get()
,一直按回车,以打印buffer所有的数据。
此时就可以进行print了,此后直到调用set logging off,所有终端上的打印信息都会拷贝到gdbInfo.txt中。
然后就可以用vim打开gdbinfo.txt:
此时所有数据都被当作了一行,这是因为在拷贝时,将‘\n’作为了两个普通的字符而不是换行符。因此在vim中将其进行查找替换即可:在命令模式下输入:%s/\\n/\r/g回车即可。
这是文件尾部内容,可以看到里面有日志,然后再在命令模式下输入0跳转到文件头部:
(该文件可以/1000000 查找第100万条日志)
但并不是我们说的应该是973469的下一条,973470,