• <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.¢%

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

            GDB怎么調(diào)試運(yùn)行著的程序

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

            GDB怎么調(diào)試運(yùn)行著的程序

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

            對于GDB這里就不作介紹了,隨便找個(gè)搜索引擎一搜GDB,介紹就已經(jīng)是很詳細(xì)了,這篇文章主要是來談怎么使用GDB來調(diào)試一個(gè)運(yùn)行著的程序,或者說怎么調(diào)試一個(gè)進(jìn)程,似乎標(biāo)題有些拗口,其次也會對fork()分離出現(xiàn)的多子進(jìn)程的調(diào)試加以說明。
            下面是一段測試代碼。
            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);}

            接下來是編譯時(shí)使用的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)

            此程序是一個(gè)服務(wù)程序,程序一旦啟動,將作為一個(gè)進(jìn)程永駐內(nèi)存,可以通過

            ~@hqlong ps-ef|grep"test"

            來查看該進(jìn)程的信息。
            此程序主要實(shí)現(xiàn)每3秒鐘向墻上打印一瓶啤酒。對于這樣的一個(gè)啟動就作為一個(gè)進(jìn)程進(jìn)駐內(nèi)存的程序應(yīng)該怎么來進(jìn)行調(diào)試呢?接下來的事情就是要來回來這個(gè)問題,
            通過make來對源文件進(jìn)行編譯。

            ~@hqlong make

            這里會在當(dāng)前目錄下產(chǎn)生一個(gè)test的可執(zhí)行文件。
            在對程序進(jìn)行正式調(diào)試之前來回憶一個(gè)使用GDB調(diào)試一個(gè)非服務(wù)程序的步驟。假設(shè)test這個(gè)可執(zhí)行文件是一個(gè)非服務(wù)程序,那么一般是通過如下幾步方式來進(jìn)行調(diào)試的。

            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來調(diào)試程序。然后使用b(break) 1在第一行設(shè)置斷點(diǎn),然后使用r(run) 來運(yùn)行程序,最后使用n來單步運(yùn)行程序,如果想要查看運(yùn)行中某變量的值,可能通過p(print)來打印。如查看i的值,就可以通過p i。最后使用q(quit)來退出程序。

            由于服務(wù)程序一旦啟動,就以進(jìn)程的方式進(jìn)駐內(nèi)存,不退出,所以和非服務(wù)程序的調(diào)試方式有一些區(qū)別。
            服務(wù)一旦啟動后,系統(tǒng)會分配一個(gè)pid,然后使用gdb綁定上這個(gè)pid,最后就可以通過通用方式進(jìn)行調(diào)試了。
            綁定進(jìn)程的方式有下幾種。

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

            方式一
            通過–pid參數(shù)來綁定指定的進(jìn)程程序。

            ~@hqlong gdb--pid25552

            方式二
            通過程序和進(jìn)程號來綁定。

            ~@hqlong gdbtest25552

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

            ~@hqlong gdbgdb) attach 25552

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

            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后,系統(tǒng)所分配的pid(進(jìn)程號)為25615,然后每隔3秒鐘就打印出一條信息。
            2.使用gdb來綁定test進(jìn)程。
            這里需要重新啟動一個(gè)新的shell來進(jìn)行調(diào)試,也就是新開一個(gè)窗口,然后使用

            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進(jìn)行綁定,從而進(jìn)入調(diào)試狀態(tài),這里會發(fā)現(xiàn),當(dāng)進(jìn)入gdb調(diào)試狀態(tài)后,先前啟動程序的窗口就不再每隔3秒鐘打印信息了,而是將控制權(quán)將給了gdb.
            3.使用bt來顯示當(dāng)前程序的函數(shù)調(diào)用棧結(jié)構(gòu)(也就是函數(shù)的調(diào)用順序)。

            										(
            										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)

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

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

            這里我們選擇最底層的棧,也就是整個(gè)程序的入口main函數(shù)來作為起點(diǎn)。
            4. 調(diào)試跟蹤過程
            使用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)

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

            										(
            										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)

            以上是進(jìn)入PrintMessage體內(nèi),然后繼續(xù)使用n(next)來一步一步的運(yùn)行。進(jìn)行到了28行,發(fā)現(xiàn)有兩個(gè)變量,一個(gè)為i,另一個(gè)為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>

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

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

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

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

            發(fā)現(xiàn)運(yùn)行到這一步時(shí),啟動程序的那個(gè)窗口打印出了一條信息。

            										99897 bottles of beer on the wall.

            這說明,程序正好運(yùn)行到了打印字符的地方。
            如果這個(gè)時(shí)候想退出函數(shù),直接返回,可能通過 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函數(shù),接著運(yùn)行下一行代碼GoToSleep.
            如果調(diào)試完畢,或能通過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

            以上是調(diào)試一個(gè)進(jìn)程的過程。

            下面附帶說明一下怎么調(diào)試一個(gè)需要通過fork()分離進(jìn)程的程序。下面是一段一個(gè)Web服務(wù)器的在接受請求時(shí)的代碼片斷。

            										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);
            }

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

            										(
            										gdb
            										)
            										set follow-fork-mode child

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

            結(jié)束

            国产成人无码精品久久久免费| 伊人久久综合热线大杳蕉下载| 亚洲αv久久久噜噜噜噜噜| 97精品伊人久久久大香线蕉| 国产成人精品久久亚洲| 亚洲AV无码一区东京热久久| a级成人毛片久久| 区亚洲欧美一级久久精品亚洲精品成人网久久久久 | 狠狠综合久久综合中文88 | 亚洲日本va中文字幕久久| 久久精品国产亚洲77777| 久久er国产精品免费观看8| 亚洲午夜无码久久久久小说| 麻豆精品久久精品色综合| 狠狠色婷婷久久综合频道日韩 | 99久久亚洲综合精品网站| 久久天天躁狠狠躁夜夜不卡 | 久久久久一区二区三区| 99久久99久久精品国产片果冻 | 99国产精品久久久久久久成人热| 欧美激情精品久久久久久久九九九 | 少妇被又大又粗又爽毛片久久黑人| 久久婷婷成人综合色综合| 日韩电影久久久被窝网| 久久人人爽人人爽AV片| 国产精品热久久毛片| 久久被窝电影亚洲爽爽爽| 91精品国产高清久久久久久io| 亚洲国产小视频精品久久久三级| 国产亚州精品女人久久久久久| 国产精品岛国久久久久| 久久久久亚洲av无码专区喷水| 国产精品99久久久精品无码| 久久国产成人亚洲精品影院| 久久精品中文字幕第23页| 一本伊大人香蕉久久网手机| 国产精品亚洲综合专区片高清久久久| 亚洲欧美日韩精品久久| 久久福利青草精品资源站| 国产叼嘿久久精品久久| 久久99久久成人免费播放|