• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            S.l.e!ep.¢%

            像打了激速一樣,以四倍的速度運轉,開心的工作
            簡單、開放、平等的公司文化;尊重個性、自由與個人價值;
            posts - 1098, comments - 335, trackbacks - 0, articles - 1
              C++博客 :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

            GDB怎么調試運行著的程序

            Posted on 2010-11-04 17:03 S.l.e!ep.¢% 閱讀(2762) 評論(0)  編輯 收藏 引用 所屬分類: Unix

            GDB怎么調試運行著的程序

            Posted by 機器人 on 10th 九月 2009 in c/c++, linux/server

            對于GDB這里就不作介紹了,隨便找個搜索引擎一搜GDB,介紹就已經是很詳細了,這篇文章主要是來談怎么使用GDB來調試一個運行著的程序,或者說怎么調試一個進程,似乎標題有些拗口,其次也會對fork()分離出現的多子進程的調試加以說明。
            下面是一段測試代碼。
            test.c

            										#include < stdio.h >
            										#include < unistd.h >
            ?
            staticvoid PrintMessage(int i);staticvoid GoToSleep(void);
            ?
            ?
            int main(void){int i =100000;
            ?
            	while(1){
            		PrintMessage( i );
            		GoToSleep();
            		i -=1;}
            ?
            	return0;}
            ?
            ?
            ?
            void PrintMessage(int i){char buf[1024];
            		sprintf(buf,"%d bottles of beer on the wall.\n", i);printf("%s",buf);
            ?
            }
            ?
            ?
            ?
            staticvoid GoToSleep(void){
            	sleep(3);}

            接下來是編譯時使用的Makefile文件.

            TARGET = test
            SRC    =  $(TARGET).c
            OBJ    =  $(TARGET).o
            CC     =  gcc
            CFLAGS = -g3-W-Wall-std=c99
            ?
            $(TARGET): $(OBJS)
            ?
            ?
            .PHONY: clean
            ?
            clean:
            	$(RM) $(TARGET) $(OBJS)

            此程序是一個服務程序,程序一旦啟動,將作為一個進程永駐內存,可以通過

            ~@hqlong ps-ef|grep"test"

            來查看該進程的信息。
            此程序主要實現每3秒鐘向墻上打印一瓶啤酒。對于這樣的一個啟動就作為一個進程進駐內存的程序應該怎么來進行調試呢?接下來的事情就是要來回來這個問題,
            通過make來對源文件進行編譯。

            ~@hqlong make

            這里會在當前目錄下產生一個test的可執行文件。
            在對程序進行正式調試之前來回憶一個使用GDB調試一個非服務程序的步驟。假設test這個可執行文件是一個非服務程序,那么一般是通過如下幾步方式來進行調試的。

            hqlong@ubuntu:/tmp$ gdbtest
            GNU gdb6.8-debian
            Copyright (C)2008 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"i486-linux-gnu"...
            (gdb) b 1
            Breakpoint 1 at 0x80483f4: file beer-process.c, line 1.
            (gdb) r
            Starting program: /tmp/test 
            ?
            Breakpoint 1, main () at beer-process.c:9
            warning: Source file is more recent than executable.
            9{(gdb) n
            main () at beer-process.c:1010		int i = 100000;
            (gdb) q
            The program is running.  Exit anyway? (y or n) y
            </http>

            首先是通過gdb test來調試程序。然后使用b(break) 1在第一行設置斷點,然后使用r(run) 來運行程序,最后使用n來單步運行程序,如果想要查看運行中某變量的值,可能通過p(print)來打印。如查看i的值,就可以通過p i。最后使用q(quit)來退出程序。

            由于服務程序一旦啟動,就以進程的方式進駐內存,不退出,所以和非服務程序的調試方式有一些區別。
            服務一旦啟動后,系統會分配一個pid,然后使用gdb綁定上這個pid,最后就可以通過通用方式進行調試了。
            綁定進程的方式有下幾種。

            hqlong@ubuntu:/tmp$ ./test&100000 bottles of beer on the wall.
            [1]25292

            方式一
            通過–pid參數來綁定指定的進程程序。

            ~@hqlong gdb--pid25552

            方式二
            通過程序和進程號來綁定。

            ~@hqlong gdbtest25552

            方式二
            先啟動gdb后,通過attach來綁定pid

            ~@hqlong gdbgdb) attach 25552

            將pid和gdb綁定后,就可以來對每一段代碼進行調試。
            下面是對上面例子的完整調試過程。
            1. 啟動進程

            hqlong@ubuntu:/tmp$ ./test&[1]25615
            hqlong@ubuntu:/tmp$ 100000 bottles of beer on the wall.
            99999 bottles of beer on the wall.
            99998 bottles of beer on the wall.

            可以看見,啟動test后,系統所分配的pid(進程號)為25615,然后每隔3秒鐘就打印出一條信息。
            2.使用gdb來綁定test進程。
            這里需要重新啟動一個新的shell來進行調試,也就是新開一個窗口,然后使用

            hqlong@ubuntu:/tmp$ gdb--pid25615
            GNU gdb6.8-debian
            Copyright (C)2008 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"i486-linux-gnu".
            Attaching to process 25615
            Reading symbols from /tmp/test...done.
            Reading symbols from /lib/tls/i686/cmov/libc.so.6...done.
            Loaded symbols for/lib/tls/i686/cmov/libc.so.6
            Reading symbols from /lib/ld-linux.so.2...done.
            Loaded symbols for/lib/ld-linux.so.2
            0xb7ffb430 in __kernel_vsyscall ()(gdb)</http>

            來將gdb與已啟動的test進行綁定,從而進入調試狀態,這里會發現,當進入gdb調試狀態后,先前啟動程序的窗口就不再每隔3秒鐘打印信息了,而是將控制權將給了gdb.
            3.使用bt來顯示當前程序的函數調用棧結構(也就是函數的調用順序)。

            										(
            										gdb
            										) bt
            #0  0xb7ffb430 in __kernel_vsyscall ()#1  0xb7f23780 in nanosleep () from /lib/tls/i686/cmov/libc.so.6#2  0xb7f235be in sleep () from /lib/tls/i686/cmov/libc.so.6#3  0x0804851e in GoToSleep () at beer-process.c:35#4  0x080484ac in main () at beer-process.c:15(gdb)

            可知函數的調用過程為:main調用GoToSleep,GoToSleep調用sleep等。
            使用frame來選擇程序的調試起點,也可以使用break來選擇指定的行或者函數來作為斷點。

            										(
            										gdb
            										) frame 4#4  0x080484ac in main () at beer-process.c:1515			GoToSleep();
            (gdb)

            這里我們選擇最底層的棧,也就是整個程序的入口main函數來作為起點。
            4. 調試跟蹤過程
            使用n(next)來一步一步的跟蹤。

            										(
            										gdb
            										) n
            Single stepping untilexit from functionsleep, 
            which has no line number information.
            GoToSleep () at beer-process.c:3636}(gdb) 
            main () at beer-process.c:1616			i -= 1;
            (gdb)14			PrintMessage( i );
            (gdb)

            這里有個技巧,如果要重復執行上一次執行的操作,可以直接回車。
            通過上面的調試,發現程序運行到了14行,也就是PrintMessage函數處,如果這個時候我們繼續n(next),程序就直接跳過函數,運行到函數的下一行,但如果想進入函數內部去調試,可以使用s(step),來進入函數體內進行調試。
            進入函數體

            										(
            										gdb
            										)
            										14			PrintMessage( i );
            (gdb) s
            PrintMessage (i=99897) at beer-process.c:2525{(gdb) n
            27	    sprintf(buf,"%d bottles of beer on the wall.\n", i);
            (gdb) n
            28printf("%s",buf);
            (gdb)

            以上是進入PrintMessage體內,然后繼續使用n(next)來一步一步的運行。進行到了28行,發現有兩個變量,一個為i,另一個為buf,可以通過p(print)來打印該變量的值。

            										(
            										gdb
            										) p i
            $1 = 99897(gdb) p buf
            $2 = "99897 bottles of beer on the wall.\n\000\000R\000?0u??????$u???\227\001?\000\000\000\000p???\000\000\000\000\000\000\000\000\001\000\000\000??DB?x??8???\000\000\000\000?\217\001?p\226\001?0u??$u???\226\000?\001\000\000\000\000\000\000\000?v???\222\001?\205???", '\0'<repeats 12times>, "?\226\000??\217\001?\000\000\000\000\000u???t???\020\001?\b\000\000\000\f\000\000\000\000????v??b\221\000?\000???\020???\f\000\000\000?\217"...
            (gdb)</repeats>

            發現通過p i 來打印i的值,結果是正確的,但通過p buf來打印buf 的值,卻顯示了很多不可讀的亂碼。這里因為buf代表這個變量在內存中的首地址,所以p不知道變量在什么時候結束,而i則不同,因為i是整型,在內存中一般占4個字節,所以p直接從首地址向后數4位就行了。所以這里打印字符指針或者數組時,需要指定一個長度。語法為 p *buf@len. 這里的len可以通過strlen(buf)來取得。

            										(
            										gdb
            										) p *buf@strlen(buf)
            $3 = "99897 bottles of beer on the wall.\n"(gdb)

            這一下子就對了。
            接下來繼續使用n(next)來單步運行。

            										(
            										gdb
            										)
            										28
            										printf
            										(
            										"%s",buf);

            發現運行到這一步時,啟動程序的那個窗口打印出了一條信息。

            										99897 bottles of beer on the wall.

            這說明,程序正好運行到了打印字符的地方。
            如果這個時候想退出函數,直接返回,可能通過 finish.

            										(
            										gdb
            										) finish 
            Run till exit from #0  PrintMessage (i=99895) at beer-process.c:28
            main () at beer-process.c:1515			GoToSleep();
            (gdb)
            退出PrintMessage函數,接著運行下一行代碼GoToSleep.
            如果調試完畢,或能通過q(quit)來退出GDB。
            
            										(
            										gdb
            										) q
            The program is running.  Quit anyway (and detach it)? (y or n) y
            Detaching from program: /tmp/test, process 25615

            以上是調試一個進程的過程。

            下面附帶說明一下怎么調試一個需要通過fork()分離進程的程序。下面是一段一個Web服務器的在接受請求時的代碼片斷。

            										for
            										(
            										;;
            										)
            										{
            										if
            										(
            										(connfd = accept(listenfd, (struct sockaddr *)&clientaddr,
            					&clientaddrlen)) == -1){perror("http server: accept error");
            		continue;
            	}if(fork() == 0)
            		accept_request(connfd);
            	else close(connfd);
            ?
            	int statloc;
            	waitpid(-1, &statloc, WNOHANG);
            }

            當單步運行到if(fork() == 0)這一步時,那怕是分離進程成功,也不會運行accept_request(connfd);而是直接跳到跳過,運行下面的代碼。在調試這樣的程序里,需要在進入gdb控制臺時,需要將follow-fork-mode的值設成child.

            										(
            										gdb
            										)
            										set follow-fork-mode child

            本文主要是體驗一下使用GDB的調試過程,對一些指令和俗語沒有作過多的解釋,如果有不明白可直接留言,或者參考相關資料。
            參數資料
            http://www.gnu.org/software/gdb/
            http://dirac.org/linux/gdb/06-Debugging_A_Running_Process.php

            結束

            91精品免费久久久久久久久| 奇米影视7777久久精品| 99久久精品费精品国产| 一本久久久久久久| 亚洲午夜精品久久久久久app| 久久久久亚洲av综合波多野结衣| 亚洲精品无码久久久影院相关影片| 欧美日韩精品久久免费| 久久久久女人精品毛片| 欧美久久一级内射wwwwww.| 亚洲成色www久久网站夜月| 99久久精品免费观看国产| 国产成人精品三上悠亚久久| 久久婷婷国产综合精品| 久久精品国产99国产精品| 久久精品国产亚洲av麻豆小说| 久久婷婷五月综合97色直播| 99精品久久精品一区二区| 国内精品欧美久久精品| 69久久夜色精品国产69| 精品国产99久久久久久麻豆| 久久久91人妻无码精品蜜桃HD| 成人资源影音先锋久久资源网| 久久九九兔免费精品6| 国产精品热久久毛片| 热re99久久精品国产99热| 久久这里只有精品18| 久久精品国产乱子伦| 色欲综合久久躁天天躁| 久久久久国产一区二区| 久久亚洲国产午夜精品理论片| 日韩久久久久久中文人妻| 狠狠色丁香久久婷婷综合_中| 99久久精品国产一区二区| 久久久青草久久久青草| 99久久精品无码一区二区毛片| 久久福利青草精品资源站| 久久九九全国免费| 久久99国产一区二区三区| 久久久网中文字幕| 伊人久久大香线蕉无码麻豆|