1.3: ดีบั๊กโปรแกรมด้วย GDB

AttachmentSize
Plain text icon Makefile.txt243 bytes
Plain text icon main.c.txt194 bytes
Plain text icon reciprocal.cpp_.txt137 bytes
Plain text icon reciprocal.hpp_.txt103 bytes

เมื่อเราเริ่มเขียนโปรแกรมไม่ว่าภาษาอะไรก็ตาม มักมีผู้แนะนำเสมอว่าควรใช้งานดีบักเกอร์ควบคู่กันไปด้วย เพื่อใช้ศึกษาขั้นตอนการทำงานของโปรแกรมว่าถูกต้องตามที่ตั้งใจไว้หรือไม่ หรือแม้กระทั้งตรวจสอบความผิดพลาดของโปรแกรมที่เกิดขึ้น สำหรับผู้ใช้งานระบบปฏิบัตการลินุกซ์แล้วมักจะรู้จักโปรแกรมดีบักเกอร์กันดีคือ GDB (The GNU Debugger)

การจะเริ่มใช้งาน GDB ได้นั้นต้องเริ่มตั้งแต่การคอมไพล์โปรแกรมเสียก่อนด้วยการเพิ่มออพชัน "-g" เข้าไปขณะคอมไพล์โปรแกรม ซึ่งมีตัวอย่างคอมไพล์ดังต่อไปนี้

gcc -g -c main.c
g++ -g -c reciprocal.cpp
g++ -g -o reciprocal main.o reciprocal.o

เมื่อเราคอมไพล์โปรแกรมด้วยออพชัน "-g" แล้ว คอมไพเลอร์จะเพิ่มข้อมูลบางอย่างที่จำเป็นต่อการทำงานของ GDB ลงไปในอ๊อปเจคไฟล์และโปรแกรมที่ได้จากการคอมไพล์ GDB จะใช้ข้อมูลเหล่านั้นเพื่อคำนวณหาบรรทัดของซอร์โค้ดที่มีความสัมพันธ์กัน เพื่อแสดงข้อมูลต่างๆออกมา

หลังจากที่ได้โปรแกรมที่ผ่านการคอมไพล์ด้วยออพชัน "-g" แล้วก็สามารถเริ่มการใช้งาน GDB ได้เลยโดยพิมพ์คำสั่ง gdb ผลออกมาอย่างนี้ เป็นต้น

$ gdb ./reciprocal 
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later 
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-linux-gnu"...
(gdb)

ซึ่ง "(gdb)" ที่เจอในบรรทัดสุดท้ายคือส่วนที่โปรแกรมใช้ติดต่อกับผู้ใช้งาน

หากทดลองใช้งานโปรแกรมดูโดยนำ source code ที่แนบมาด้วยนี้มาคอมไพล์ด้วยออพชัน "-g" โดยพิมพ์ $ make CFLAGS="-g" หลังจากนั้น ทดลองใช้คำสั่ง run ดู

$ gdb ./reciprocal 
(gdb) run
Starting program: /home/boatkrap/tmp/linux_programming/reciprocal 

Program received signal SIGSEGV, Segmentation fault.
0x00007fe2053c2a2b in ?? () from /lib/libc.so.6
(gdb) where
#0  0x00007fe2053c2a2b in ?? () from /lib/libc.so.6
#1  0x00007fe2053bf9d0 in atoi () from /lib/libc.so.6
#2  0x00000000004006b0 in main (argc=1, argv=0x7fff0e0c3498) at main.c:6
(gdb) up 2
#2  0x00000000004006b0 in main (argc=1, argv=0x7fff0e0c3498) at main.c:6
6	    i = atoi (argv[1]);
(gdb) 

จะพบว่าโปรแกรมเกิดข้อผิดพลาดขึ้น โดยปรากฏข้อความProgram received signal SIGSEGV, Segmentation fault.ซึ่งเป็นลักษณะการอ้างข้อมูลผิดพลาด หากลองพิมพ์ (gdb) where ดูพบว่ามี ปัญหาไฟล์ main.c บรรทัดที่ 6 หากใช้คำสั่ง up แล้วตามด้วยตัวเลขที่เราต้องการดู เช่น หลายเลขที่แสดงข้อผิดพลาดที่ไฟล์ main.c เป็นหมายเลข #2 ก็พิมพ์ (gdb) up 2 gdb จะแสดงข้อผิดพลาดที่พบกับบรรทัดที่มีปัญหา ซึ่งจะเห็นว่าโปรแกรมที่เขียนขึ้นต้องการอาร์กิวเมนท์ แต่เราไม่ได้ให้ค่าเริ่มต้นแก่โปรแกรม ซึ่งตัวแปร argc มีค่าเป็น 1 ทำให้เกิดการอ้างอิงข้อมูลที่ผิดพลาดขึ้นนั้นเอง หากเราต้องการทดสอบโปรแกรมใหม่อีกครั้งโดยให้ค่าเริ่มต้นแก่โปรแกรมด้วย เราสามารถใช้คำสั่ง run แล้วตามด้วยอาร์กิวเมนท์ได้ทันทีเช่น

(gdb) run 2
Starting program: /home/boatkrap/tmp/linux_programming/reciprocal 2
The reciprocal of 2 is 0.5

Program exited normally.

หากเราต้องการดูการทำงานโปรแกรมเป็นลำดับ เราสามารถกำหนด break point ให้กับโปรแกรมก่อน โดยสามารถกำหนดได้หลายวิธีแต่หลักๆแล้วจะกำหนดเป็นหมายเลขบรรทัด หรือกำหนดเป็น ชื่อฟังก์ชัน เช่น (gdb) break main

(gdb) break main
Breakpoint 1 at 0x40069b: file main.c, line 6.
(gdb) run 2
Starting program: /home/boatkrap/tmp/linux_programming/reciprocal 2

Breakpoint 1, main (argc=2, argv=0x7fffd564ea18) at main.c:6
6	    i = atoi (argv[1]);
(gdb) print argv[1]
$1 = 0x7fffd564f69d "2"
(gdb) next
7	    printf ("The reciprocal of %d is %g\n", i, reciprocal (i));
(gdb) print i
$2 = 2
(gdb) next
The reciprocal of 2 is 0.5
8	    return 0;
(gdb) next
9	}
(gdb) next
0x00007fa0cc932466 in __libc_start_main () from /lib/libc.so.6
(gdb) next
Single stepping until exit from function __libc_start_main, 
which has no line number information.

Program exited normally.
(gdb) 

เมื่อเรากำหนด break point ให้กับโปรแกรมแล้ว และทดลอง รันโปรแกรมใหม่อีกครั้งนึง การทำงานของโปรแกรมจะหยุดที่ฟังก์ชัน main ตามที่เราได้กำหนดไว้ แล้วแสดง statement ที่จะทำงานต่อไปให้ดู หากใช้คำสั่ง (gdb) next โปรแกรมจะทำงานตาม statement ที่แสดงให้เห็นก่อนหน้า หากมีการแสดงผลก็จะแสดงค่าออกมาให้เห็น นอกจากนี้ยังใช้คำสั่ง print แล้วตามด้วยชื่อตัวแปรเพื่อดูต่าของตัวแปรขณะนั้นได้ด้วย

นอกจากที่นำเสนอไปข้างต้นแล้วนั้นยังมีคำสั่งที่ใช้งานโปรแกรม gdb อีกหลายคำสั่งเพื่อใช้โปรแกรม สามารถศึกษาได้จาก GDB: The GNU Project Debugger

Taxonomy upgrade extras: 
Creative Commons License ลิขสิทธิ์ของบทความเป็นของเจ้าของบทความแต่ละชิ้น
ผลงานนี้ ใช้สัญญาอนุญาตของครีเอทีฟคอมมอนส์แบบ แสดงที่มา-อนุญาตแบบเดียวกัน 3.0 ที่ยังไม่ได้ปรับแก้