天天看點

linux環境下檢視C/C++程式的堆棧資訊前言測試環境檢視方法具體實踐總結

文章目錄

  • 前言
  • 測試環境
  • 檢視方法
  • 具體實踐
    • gdb調試core檔案
    • gdb附加到程序
    • pstack輸出堆棧資訊
    • strace列印程式運作情況
  • 總結

前言

經常在Windows上開發的工程師們可能已經習慣了圖形化的調試界面,在源代碼的編輯框上點選就可以添加斷點,在調用堆棧的視窗就可以看到程式運作的堆棧資訊,但是在 linux 環境下,面對指令行的天下,我們需要掌握一些指令,才能夠檢視C/C++程式的堆棧資訊。

測試環境

[[email protected]#13:58:34#/home/albert]$cat /etc/issue
CentOS release 6.3 (Final)
Kernel \r on an \m

[[email protected]#13:58:43#/home/albert]$g++ --version
g++ (GCC) 4.4.7 20120313 (Red Hat 4.4.7-18)
Copyright ?? 2010 Free Software Foundation, Inc.

           

檢視方法

  1. 使用gdb程式調試core檔案,格式為

    gdb test_proc core.proc_id

  2. 使用gdb程式附加到調試程式的程序上,格式為

    gdb attach proc_id

  3. 使用pstack程式輸出調試程式的堆棧資訊,格式為

    pstack proc_id

  4. 使用strace程式列印調試程式的運作資訊,格式為

    strace -p proc_id

具體實踐

  • 一般檢視堆棧資訊時常常面對的都是多線程的程式,是以我們也來寫一個簡單的多線程小程式,代碼如下:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

static void* thread_proc(void* arg)
{
    unsigned int sum = 3;
    while(true)
    {
        for (int idx = 0; idx < 1000000000; ++idx)
            sum += idx;

        printf("thread sum = %u\n", sum);
        sleep(2);
    }

    return 0;
}

int main()
{
    pthread_t thread_id;
    pthread_create(&thread_id, NULL, thread_proc, NULL);
    unsigned int sum = 0;

    while(true)
    {
        for (int idx = 0; idx < 1000000000; ++idx)
            sum += idx;

        printf("main sum = %u\n", sum);
        sleep(1);
    }

    return 0;
}
           
  • 編譯程式并運作,程式開始不斷的列印計算結果
[[email protected]#15:06:54#/home/albert/test/threadtest]$g++ threadtest.cpp -O0 -pthread -o threadtest
[[email protected]#15:08:27#/home/albert/test/threadtest]$./threadtest
thread sum = 3051657987
main sum = 3051657984
thread sum = 1808348675
main sum = 1808348672
main sum = 565039360
thread sum = 565039363
main sum = 3616697344
thread sum = 3616697347
...
           
  • 現在可以通過上面描述的方法來檢視threadtest程式堆棧資訊了,幾乎所有的指令都需要程序id,是以我們可以再開一個終端先通過

    pidof

    指令來獲得:
[[email protected]#15:39:35#/home/albert/test/threadtest]$pidof threadtest
21473
           

gdb調試core檔案

  1. 通過kill指令産生core檔案

使用指令

kill -11 21473

可以将正在運作的程式殺死,并且産生core檔案core.21473,

-11

表示段錯誤信号,通常是通路了無效的記憶體導緻

  1. 通過gcore指令産生core檔案

使用指令

gcore 21473

可以産生core檔案core.21473,但是不會殺死程式,适用于調試線上程式,又不影響使用者使用的情況,可以測試一下:

[[email protected]#15:39:43#/home/albert/test/threadtest]$gcore 21473
warning: the debug information found in "/usr/lib/debug//lib64/libm-2.12.so.debug" does not match "/lib64/libm.so.6" (CRC mismatch)
warning: the debug information found in "/usr/lib/debug/lib64/libm-2.12.so.debug" does not match "/lib64/libm.so.6" (CRC mismatch)
warning: the debug information found in "/usr/lib/debug//lib64/libpthread-2.12.so.debug" does not match "/lib64/libpthread.so.0" (CRC mismatch)
warning: the debug information found in "/usr/lib/debug/lib64/libpthread-2.12.so.debug" does not match "/lib64/libpthread.so.0" (CRC mismatch)
[New LWP 21474]
[Thread debugging using libthread_db enabled]
warning: the debug information found in "/usr/lib/debug//lib64/libc-2.12.so.debug" does not match "/lib64/libc.so.6" (CRC mismatch)
warning: the debug information found in "/usr/lib/debug/lib64/libc-2.12.so.debug" does not match "/lib64/libc.so.6" (CRC mismatch)
warning: the debug information found in "/usr/lib/debug//lib64/ld-2.12.so.debug" does not match "/lib64/ld-linux-x86-64.so.2" (CRC mismatch)
warning: the debug information found in "/usr/lib/debug/lib64/ld-2.12.so.debug" does not match "/lib64/ld-linux-x86-64.so.2" (CRC mismatch)
0x00000000004006eb in main ()
Saved corefile core.21473
           

然後使用gdb調試core檔案:

[[email protected]#15:47:13#/home/albert/test/threadtest]$gdb threadtest core.21473
GNU gdb (GDB) Red Hat Enterprise Linux (7.2-83.el6)
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/albert/test/threadtest/threadtest...(no debugging symbols found)...done.
[New Thread 21474]
[New Thread 21473]
Missing separate debuginfo for
Try: yum --enablerepo='*-debug*' install /usr/lib/debug/.build-id/80/1b9608daa2cd5f7035ad415e9c7dd06ebdb0a2
Reading symbols from /usr/lib64/libstdc++.so.6...(no debugging symbols found)...done.
Loaded symbols for /usr/lib64/libstdc++.so.6
Reading symbols from /lib64/libm.so.6...

...省略無關資訊

(no debugging symbols found)...done.
Loaded symbols for /lib64/ld-linux-x86-64.so.2
Core was generated by `./threadtest'.
#0  0x0000000000400691 in thread_proc(void*) ()
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.209.el6_9.2.x86_64 libstdc++-4.4.7-18.el6_9.2.x86_64
(gdb)
           

顯示所有線程資訊,可以使用gdb指令

thread apply all bt

(gdb) thread apply all bt

Thread 2 (Thread 0x7f1b4e1d2720 (LWP 21473)):
#0  0x00000000004006eb in main ()

Thread 1 (Thread 0x7f1b4d270700 (LWP 21474)):
#0  0x0000000000400691 in thread_proc(void*) ()
#1  0x00007f1b4d60caa1 in start_thread () from /lib64/libpthread.so.0
#2  0x00007f1b4d359bcd in clone () from /lib64/libc.so.6
           

gdb附加到程序

可以通過

gdb attach pid

直接附加到正在運作的程式上,然後檢視線程資訊

thread apply all bt

[[email protected]#15:54:59#/home/albert/test/threadtest]$gdb attach 21473
GNU gdb (GDB) Red Hat Enterprise Linux (7.2-83.el6)
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
attach: 沒有那個檔案或目錄.
Attaching to process 21473
Reading symbols from /home/albert/test/threadtest/threadtest...(no debugging symbols found)...done.
Reading symbols from /usr/lib64/libstdc++.so.6...(no debugging symbols found)...done.
Loaded symbols for /usr/lib64/libstdc++.so.6
Reading symbols from /lib64/libm.so.6...

...省略無關資訊

(no debugging symbols found)...done.
Loaded symbols for /lib64/ld-linux-x86-64.so.2
0x00007f1b4d31dc4d in nanosleep () from /lib64/libc.so.6
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.209.el6_9.2.x86_64 libstdc++-4.4.7-18.el6_9.2.x86_64
(gdb) thread apply all bt

Thread 2 (Thread 0x7f1b4d270700 (LWP 21474)):
#0  0x00007f1b4d31dc4d in nanosleep () from /lib64/libc.so.6
#1  0x00007f1b4d31dac0 in sleep () from /lib64/libc.so.6
#2  0x00000000004006b6 in thread_proc(void*) ()
#3  0x00007f1b4d60caa1 in start_thread () from /lib64/libpthread.so.0
#4  0x00007f1b4d359bcd in clone () from /lib64/libc.so.6

Thread 1 (Thread 0x7f1b4e1d2720 (LWP 21473)):
#0  0x00007f1b4d31dc4d in nanosleep () from /lib64/libc.so.6
#1  0x00007f1b4d31dac0 in sleep () from /lib64/libc.so.6
#2  0x0000000000400721 in main ()
           

pstack輸出堆棧資訊

如果不需要調試,隻想檢視運作程式目前的堆棧資訊,可以使用pstack指令,輸出資訊很簡潔:

[[email protected]#15:57:53#/home/albert/test/threadtest]$pstack 21473
Thread 2 (Thread 0x7f1b4d270700 (LWP 21474)):
#0  0x0000000000400683 in thread_proc(void*) ()
#1  0x00007f1b4d60caa1 in start_thread () from /lib64/libpthread.so.0
#2  0x00007f1b4d359bcd in clone () from /lib64/libc.so.6
Thread 1 (Thread 0x7f1b4e1d2720 (LWP 21473)):
#0  0x00007f1b4d31dc4d in nanosleep () from /lib64/libc.so.6
#1  0x00007f1b4d31dac0 in sleep () from /lib64/libc.so.6
#2  0x0000000000400721 in main ()
           

strace列印程式運作情況

strace輸出的不是堆棧資訊,而是類似于程式的運作步驟,具體資訊如下:

[[email protected]#15:57:56#/home/albert/test/threadtest]$strace -p 21473
Process 21473 attached
write(1, "main sum = 2580918016\n", 22) = 22
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
nanosleep({1, 0}, 0x7fff56a49c50)       = 0
write(1, "main sum = 1337608704\n", 22) = 22
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
nanosleep({1, 0}, 0x7fff56a49c50)       = 0
write(1, "main sum = 94299392\n", 20)   = 20
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
nanosleep({1, 0}, 0x7fff56a49c50)       = 0
^CProcess 21473 detached
           

總結

  • 在解決實際問題的過程中,上述幾種方法可以結合使用,選取合适的使用方法,比如面對程式突然崩潰,那麼

    gdb proc core

    就是調試的首選方法。
  • 如果隻是想簡單的檢視堆棧資訊,可以使用

    pstack pid

    這種方式,免去了生成巨大core檔案的麻煩。
  • 如果還想檢視運作邏輯中的變量資訊,那麼gdb使我們可以幫助我們動态調試程式,檢視一些程式運作時的狀态。

繼續閱讀