性能工具調試工具 GDB(你以為性能分析中用不到嗎?)

      網友投稿 890 2022-05-30

      前言

      環境依賴

      Helloword 示例

      調試 Redis 示例

      下載 Redis 源碼并解壓

      確認編譯選項

      檢查編譯

      GDB 調用 redis-server

      方法一

      方法二

      前言

      環境依賴

      Helloword 示例

      調試 Redis 示例

      下載 Redis 源碼并解壓

      確認編譯選項

      檢查編譯

      GDB 調用 redis-server

      方法一

      方法二

      視頻示例

      6、GDB 調試過程

      總結

      前言

      前幾天碰到一個 redis 的問題。

      說復雜倒也不復雜,但是我想在重現的時候下載了 redis 的源碼,編譯什么的折騰了一遍。

      在這個過程中,也用到了調試工具GDB。

      我以為這個工具早就寫過了文章了,結果一查自己公眾號和博客,居然沒有。

      我現在已經有了一個習慣,在碰到性能分析方面不會的時候,先查自己的公眾號和博客,一般都會找到答案

      環境依賴

      前幾天因為遇到個 redis 的問題,所以編譯了一下 reids,并且做一些監控。這里把一部分的操作實踐記錄下來,后面如果有需要,我再把 GDB 的一些命令使用整理一下。

      首先,GDB 的環境會有一些要求,debug 包一定要全,比如說,我的主機上裝的有如下 debug 包。

      [root@7dgroup redis]# rpm -qa|grep debug gcc-base-debuginfo-4.8.5-4.el7.x86_64 glibc-debuginfo-common-2.17-157.el7_3.1.x86_64 nss-softokn-debuginfo-3.28.3-8.el7_4.x86_64 ncurses-debuginfo-5.9-13.20130511.el7.x86_64 yum-plugin-auto-update-debug-info-1.1.31-50.el7.noarch procps-ng-debuginfo-3.3.10-5.el7_2.x86_64 kernel-debuginfo-3.10.0-327.28.2.el7.x86_64 net-snmp-debuginfo-5.7.2-33.el7_5.2.x86_64 gcc-debuginfo-4.8.5-4.el7.x86_64 zlib-debuginfo-1.2.7-15.el7.x86_64 pcre-debuginfo-8.32-15.el7_2.1.x86_64 systemd-debuginfo-219-19.el7_2.12.x86_64 glibc-debuginfo-2.17-157.el7_3.1.x86_64 kernel-debuginfo-common-x86_64-3.10.0-327.28.2.el7.x86_64 [root@7dgroup redis]#

      推薦大家都最好不要自己安裝各種包依賴,比如說像這樣安裝:

      [root@7dgroup ]# yum install gdb [root@7dgroup ]# debuginfo-install libgcc-4.8.5-4.el7.x86_64 [root@7dgroup ]# debuginfo-install nss-softokn-freebl-3.28.3-8.el7_4.x86_64 pcre-8.32-15.el7_2.1.x86_64 zlib-1.2.7-15.el7.x86_64

      所有的依賴都會給你加上。

      Helloword 示例

      先看一個例子吧:

      真是人越老,越覺得 helloword 示例的好處。直觀又簡單。

      GDB 是一個老牌的調試工具。

      上面的示例中,我寫了一個死循環的代碼,左邊窗口執行,右邊窗口調試。

      之前經常看到有人用 GDB 調試 coredump 文件。

      當然 GDB 也可以調試正在運行中的程序,不過這里有前提,就是編譯時加上 -g 的參數。

      通過 attach 連進去之后,可以做很多動作,不過要知道 attach 的同時也會導致運行中的程序暫停。要想接著運行,就輸入c(continue)。

      GDB 的調試要求是個靜態的狀態。

      當然現在也有很多動態調試的手段了,后面如果有需要我也接著寫一下。

      attach 之后,可以看線程信息 info threads。

      輸入 bt 是查看當前程序運行到了哪里。

      調試 coredump 文件的好處是,程序已經 crash 了,這時直接 bt 就知道 crash 在了什么地方。但是同樣也是需要在編譯時加上-g的參數的。

      另外,調試會要求系統中有 debuginfo 的支持。這個版本一定要和系統內核版本對應,不然是不可用的。

      GDB 支持設置斷點、觀察點、捕捉點。還有查看堆棧、運行時數據、源代碼等等好使的命令。

      本來想著能寫很多示例命令的,可是寫著寫著,又覺得那樣像在寫操作手冊。

      而操作手冊已經很多了,所以還是算了,如果有人覺得有興趣,我再根據讀者的要求寫相應的示例。

      我們在性能分析中經常用的的場景是:

      就是查看斷在了什么地方,然后跟著堆棧去找代碼。

      設置斷點在某個函數或變量上,執行程序,當斷點命中的時候,查看堆棧信息。

      后面也寫一下動態調試工具,像 systemtap 之類的。

      性能分析的過程中經常會用到一些看似不是性能工程師的技術所需的工具,我經常會聽到有些做性能的人覺得這不是自己應該做的。還是按我以前的觀點,職位可以受限,但是能力不要給自己設限。

      但是,也不要學偏了,技術需要有一條主線,所有的能力都會主線服務,不然不知道你要干嗎了。

      再回到說性能分析。經常有些初學者或者有些經驗的人,在跟測試工具糾結較勁。

      調試 Redis 示例

      下載 Redis 源碼并解壓

      [root@7dgroup GDB]# wget wget http://download.redis.io/releases/redis-5.0.5.tar.gz --2019-08-04 10:15:14-- http://wget/ 正在解析主機 wget (wget)... 失敗:未知的名稱或服務。 wget: 無法解析主機地址 “wget” --2019-08-04 10:15:14-- http://download.redis.io/releases/redis-5.0.5.tar.gz 正在解析主機 download.redis.io (download.redis.io)... 109.74.203.151 正在連接 download.redis.io (download.redis.io)|109.74.203.151|:80... 已連接。 已發出 HTTP 請求,正在等待回應... 200 OK 長度:1975750 (1.9M) [application/x-gzip] 正在保存至: “redis-5.0.5.tar.gz” 100%[=======================================================================================================================================================>] 1,975,750 20.2KB/s 用時 1m 47s 2019-08-04 10:17:02 (18.0 KB/s) - 已保存 “redis-5.0.5.tar.gz” [1975750/1975750]) FINISHED --2019-08-04 10:17:02-- Total wall clock time: 1m 48s Downloaded: 1 files, 1.9M in 1m 47s (18.0 KB/s) [root@7dgroup GDB]# tar zxvf redis-5.0.5.tar.gz [root@7dgroup GDB]# cd redis-5.0.5 [root@7dgroup redis-5.0.5]# tree -h . ├── [104K] 00-RELEASENOTES ├── [ 53] BUGS ...... ...... ...... ├── [3.7K] speed-regression.tcl └── [ 693] whatisdoing.sh 68 directories, 725 files [root@7dgroup redis-5.0.5]#

      確認編譯選項

      [root@7dgroup redis-5.0.5]# grep DEBUG src/Makefile FINAL_CFLAGS=$(STD) $(WARN) $(OPT) $(DEBUG) $(CFLAGS) $(REDIS_CFLAGS) FINAL_LDFLAGS=$(LDFLAGS) $(REDIS_LDFLAGS) $(DEBUG) DEBUG=-g -ggdb DEBUG=-g DEBUG_FLAGS=-g export CFLAGS LDFLAGS DEBUG DEBUG_FLAGS [root@7dgroup redis-5.0.5]#

      可以看到默認 redis 源碼中就已經把 debug 選項配置好了,真是貼心。

      [root@7dgroup redis-5.0.5]# make PREFIX=/root/GDB/redis-5.0.5/redis install cd src && make install make[1]: 進入目錄“/root/GDB/redis-5.0.5/src” CC Makefile.dep make[1]: 離開目錄“/root/GDB/redis-5.0.5/src” make[1]: 進入目錄“/root/GDB/redis-5.0.5/src” rm -rf redis-server redis-sentinel redis-cli redis-benchmark redis-check-rdb redis-check-aof *.o *.gcda *.gcno *.gcov redis.info lcov-html Makefile.dep dict-benchmark (cd ../deps && make distclean) make[2]: 進入目錄“/root/GDB/redis-5.0.5/deps” ...... ...... ...... (cd hiredis && make clean) > /dev/null || true Hint: It's a good idea to run 'make test' ;) INSTALL install INSTALL install INSTALL install INSTALL install INSTALL install make[1]: 離開目錄“/root/GDB/redis-5.0.5/src”

      請注意,redis 默認使用了 -O2 的編譯優化選項。可以去掉,這個在官方的文檔中有說明。所以這里的編譯最好用如下命令:

      make noopt PREFIX=/root/GDB/redis-5.0.5/redis install

      另外,這里我也指定了安裝目錄。

      檢查編譯

      [root@7dgroup redis-5.0.5]# make test cd src && make test make[1]: 進入目錄“/root/GDB/redis-5.0.5/src” CC Makefile.dep make[1]: 離開目錄“/root/GDB/redis-5.0.5/src” make[1]: 進入目錄“/root/GDB/redis-5.0.5/src” Cleanup: may take some time... OK Starting test server at port 11111 [ready]: 28175 Testing unit/printver [ready]: 28177 Testing unit/dump [ready]: 28180 Testing unit/auth ...... ...... ...... o/ All tests passed without errors! Cleanup: may take some time... OK make[1]: 離開目錄“/root/GDB/redis-5.0.5/src” [root@7dgroup redis-5.0.5]#

      全部通過沒有錯誤。

      以上為什么把 redis 的編譯列這么清楚呢。主要是如果有些人在過程中遇到的雜七雜八的問題,可以有個參照。

      GDB 調用 redis-server

      直接通過 GDB 啟動。

      [root@7dgroup redis]# gdb bin/redis-server ....... ....... ....... Reading symbols from /root/GDB/redis-5.0.5/redis/bin/redis-server...done. (gdb)

      先啟動 redis-server。

      [root@7dgroup redis]# ./bin/redis-server & [1] 4001 [root@7dgroup redis]# 4001:C 04 Aug 2019 11:01:39.195 # oO0OoO0Oo bit ...... .-`` .-```. ```\/ _.,_ ''-._ ( ' , .-` | `, ) Running in standalone mode |`-._`-...-` __...-.``-._|'` _.-'| Port: 6379 | `-._ `._ / _.-' | PID: 4001 `-._ `-._ `-./ _.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | http://redis.io `-._ `-._`-.__.-'_.-' _.-' ...... 4001:M 04 Aug 2019 11:01:39.196 * Ready to accept connections

      查 redis 的 pid

      [root@7dgroup redis]# ps -ef|grep redis root 4001 742 0 11:01 pts/3 00:00:00 ./bin/redis-server *:6379 root 4014 742 0 11:01 pts/3 00:00:00 grep --color=auto redis [root@7dgroup redis]#

      連上 GDB

      性能工具之調試工具 GDB(你以為性能分析中用不到嗎?)

      [root@7dgroup redis]# gdb attach 4001 GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-80.el7 ...... Loaded symbols for /lib64/libdl.so.2 Reading symbols from /lib64/librt.so.1...Reading symbols from /usr/lib/debug/usr/lib64/librt-2.17.so.debug...done. done. Loaded symbols for /lib64/librt.so.1 Reading symbols from /lib64/libpthread.so.0...Reading symbols from /usr/lib/debug/usr/lib64/libpthread-2.17.so.debug...done. done. [New LWP 4004] [New LWP 4003] [New LWP 4002] [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib64/libthread_db.so.1". Loaded symbols for /lib64/libpthread.so.0 Reading symbols from /lib64/libc.so.6...Reading symbols from /usr/lib/debug/usr/lib64/libc-2.17.so.debug...done. done. Loaded symbols for /lib64/libc.so.6 Reading symbols from /lib64/ld-linux-x86-64.so.2...Reading symbols from /usr/lib/debug/usr/lib64/ld-2.17.so.debug...done. done. Loaded symbols for /lib64/ld-linux-x86-64.so.2 0x00007f8a60ce5d13 in epoll_wait () at ../sysdeps/unix/syscall-template.S:81 81 T_PSEUDO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS) (gdb)

      或者這樣:

      [root@7dgroup redis]# gdb ./bin/redis-server 4001 ...... Reading symbols from /root/GDB/redis-5.0.5/redis/bin/redis-server...done. Attaching to program: /root/GDB/redis-5.0.5/redis/./bin/redis-server, process 4001 Reading symbols from /lib64/libm.so.6...Reading symbols from /usr/lib/debug/usr/lib64/libm-2.17.so.debug...done. done. Loaded symbols for /lib64/libm.so.6 Reading symbols from /lib64/libdl.so.2...Reading symbols from /usr/lib/debug/usr/lib64/libdl-2.17.so.debug...done. done. Loaded symbols for /lib64/libdl.so.2 Reading symbols from /lib64/librt.so.1...Reading symbols from /usr/lib/debug/usr/lib64/librt-2.17.so.debug...done. done. Loaded symbols for /lib64/librt.so.1 Reading symbols from /lib64/libpthread.so.0...Reading symbols from /usr/lib/debug/usr/lib64/libpthread-2.17.so.debug...done. done. [New LWP 4004] [New LWP 4003] [New LWP 4002] [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib64/libthread_db.so.1". Loaded symbols for /lib64/libpthread.so.0 Reading symbols from /lib64/libc.so.6...Reading symbols from /usr/lib/debug/usr/lib64/libc-2.17.so.debug...done. done. Loaded symbols for /lib64/libc.so.6 Reading symbols from /lib64/ld-linux-x86-64.so.2...Reading symbols from /usr/lib/debug/usr/lib64/ld-2.17.so.debug...done. done. Loaded symbols for /lib64/ld-linux-x86-64.so.2 0x00007f8a60ce5d13 in epoll_wait () at ../sysdeps/unix/syscall-template.S:81 81 T_PSEUDO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS) (gdb)

      請大家注意,symbols 的加載最好不要出現任務警告或錯誤,不然調試過程中遇到搞不清楚的問題。

      這些 symbols 也是因為有了前面的 debug 編譯選項才產生的。并且官方說不影響性能。

      下面就可以調試了。

      視頻示例

      DEMO 給人直觀的感覺。在上面的 demo 中,我用了 -tui 參數,在調試時,把代碼窗口也顯示出來,這樣就知道當前執行到了哪行代碼。

      如果你在使用時沒有顯示出源碼,則需要用directory命令把源碼加載進來。

      命令如下:

      (gdb) directory /root/GDB/redis-5.0.5/src Source directories searched: /root/GDB/redis-5.0.5/src:$cdir:$cwd (gdb)

      6、GDB 調試過程

      設置斷點

      (gdb) b setCommand Breakpoint 1 at 0x452c80: file t_string.c, line 96. (gdb)

      繼續執行

      (gdb) c Continuing. Breakpoint 1, setCommand (c=0x7f8a6070da40) at t_string.c:96 96 void setCommand(client *c) {

      斷點命中后查看線程

      (gdb) info threads Id Target Id Frame 4 Thread 0x7f8a59cd6700 (LWP 4002) "redis-server" pthread_cond_wait@@GLIBC_2.3.2 () at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S:185 3 Thread 0x7f8a594d5700 (LWP 4003) "redis-server" pthread_cond_wait@@GLIBC_2.3.2 () at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S:185 2 Thread 0x7f8a58cd4700 (LWP 4004) "redis-server" pthread_cond_wait@@GLIBC_2.3.2 () at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S:185 * 1 Thread 0x7f8a61ae3f80 (LWP 4001) "redis-server" setCommand (c=0x7f8a6070da40) at t_string.c:96 (gdb)

      線程號前面的*代表是當前正在執行的線程。

      切換到線程中去

      (gdb) thread 1 [Switching to thread 1 (Thread 0x7f8a61ae3f80 (LWP 4001))] #0 setCommand (c=0x7f8a6070da40) at t_string.c:96 96 void setCommand(client *c) { (gdb)

      切換到thread 1。

      查看斷點

      查看斷點:

      (gdb) bt #0 setCommand (c=0x7f8a6070da40) at t_string.c:96 #1 0x0000000000430ac7 in call (c=c@entry=0x7f8a6070da40, flags=flags@entry=15) at server.c:2439 #2 0x0000000000431d5f in processCommand (c=0x7f8a6070da40) at server.c:2733 #3 0x0000000000440a55 in processInputBuffer (c=0x7f8a6070da40) at networking.c:1470 #4 0x000000000042af80 in aeProcessEvents (eventLoop=eventLoop@entry=0x7f8a6062b0a0, flags=flags@entry=11) at ae.c:443 #5 0x000000000042b24b in aeMain (eventLoop=0x7f8a6062b0a0) at ae.c:501 #6 0x00000000004280ff in main (argc=, argv=0x7ffe1bf60ac8) at server.c:4200 (gdb)

      單步跟蹤 next

      (gdb) n 102 for (j = 3; j < c->argc; j++) {

      進入到函數內部 step

      (gdb) s 100 int flags = OBJ_SET_NO_FLAGS; (gdb)

      打印變量值

      (gdb) p j $3 = 3 (gdb) p *a $6 = 58989 (gdb) (gdb) p 't_string.c'::a $7 = {58989, 57068, 5} (gdb) i locals j = 3 expire = 0x0 unit = 0 flags = 0 (gdb)

      指定查看全局變量的值,通過 :: 操作符。

      file::variable

      function::variable

      因為當全局變量與局部變量沖突時,全局變量會被隱藏。

      查看當前 stack frame 局部變量

      (gdb) i locals j = 3 expire = 0x0 unit = 0 flags = 0 (gdb)

      查看當前 stack frame 參數

      (gdb) info args c = 0x7f8a6070da40 (gdb)

      修改變量的值

      (gdb) i locals j = 0 expire = 0x5d46a88f unit = 0 flags = 4392628 (gdb) set var j = 1 (gdb) i locals j = 1 expire = 0x5d46a88f unit = 0 flags = 4392628 (gdb)

      上面這些操作讓大家有一個直觀的認識,看到了完整的調試過程。但是并不是 GDB 所有的指令集。

      至少有了一個感覺就是我們在調試時對程序是想干嗎干嗎。

      總結

      我看到有挺多的 GDB 的指令集的教程,有興趣的可以一一試下指令。

      本來我也是整理了指令集的,但是感覺和其他人整理的也沒有什么區別,所以就不想發出來了。

      后面有的 GDB 調試的具體場景,再看 GDB 在具體場景中的使用。

      Linux Redis 任務調度 應用性能管理 APM 應用性能調優

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:OpenCV中的圖像處理 —— 傅里葉變換+模板匹配(opencv進行圖像處理)
      下一篇:SpringCloud系列之服務容錯保護Netflix Hystrix(springcloud容錯機制)
      相關文章
      国产精品亚洲一区二区三区久久 | 亚洲日产乱码一二三区别| 亚洲激情中文字幕| 亚洲中文字幕日产乱码高清app| 亚洲成a人片在线观看老师| 高清在线亚洲精品国产二区| 久久精品熟女亚洲av麻豆| 亚洲av无一区二区三区| 久久精品亚洲日本波多野结衣| 亚洲GV天堂GV无码男同| 亚洲另类无码一区二区三区| 亚洲乱码国产乱码精华| 亚洲精品乱码久久久久久V| 亚洲av综合日韩| 亚洲AⅤ视频一区二区三区| 夜色阁亚洲一区二区三区| 国产精品亚洲产品一区二区三区| 久久久久亚洲AV综合波多野结衣| 日日噜噜噜噜夜夜爽亚洲精品| 亚洲熟妇无码AV在线播放| 亚洲乱码国产乱码精品精| 亚洲国产成人高清在线观看| 亚洲av不卡一区二区三区| 亚洲黑人嫩小videos| 亚洲午夜国产精品无卡| 亚洲一线产品二线产品| 亚洲AV日韩精品久久久久 | 亚洲第一成年网站视频| 亚洲av日韩av永久无码电影| 亚洲1区2区3区精华液| 亚洲国产精品嫩草影院久久| 亚洲尤码不卡AV麻豆| 亚洲成人午夜在线| 亚洲伊人久久精品| 亚洲av无码一区二区三区在线播放 | 亚洲大成色www永久网址| 亚洲日本成本人观看| 亚洲av无码成人精品区在线播放| 亚洲中文字幕久久精品无码喷水| 亚洲人成在线播放网站岛国| 亚洲1234区乱码|