Reading Coredump
โดยปกติการอ่าน coredump เป็นพื้นฐานที่ง่ายที่สุดครับ เพราะเป็นการอ่านสิ่งที่ตายแล้ว ไม่ต้องขยับอะไรทั้งนั้น เราก็จะมาเริ่มใช้ GDB ที่อันนี้ก่อน เพื่อเข้าใจว่าอ่านค่าอะไรยังไงบ้าง
ถ้าเริ่มทำตามผมตั้งแต่ preparation จะได้ coredump มาแล้ว ก็เริ่มจากเอา GDB เข้าไปอ่าน core ให้ใช้คำสั่งตามนี้ครับ gdb ./<executable> <core_of_executable>
$ gdb ./example.o core
Reading symbols from ./example.o...done.
[New LWP 17137]
Core was generated by `./example.o'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x000055bc13b7b9a0 in getInputAndRun () at example.c:28
28 *result[1] = toupper(result[1][0]);
(gdb)ดู stack ของโปรแกรม ปัจจุบันด้วย bt หรือ backtrace
(gdb) bt
#0 0x000055bc13b7b9a0 in getInputAndRun () at example.c:28
#1 0x000055bc13b7ba1f in main () at example.c:37
(gdb) f 1
#1 0x000055bc13b7ba1f in main () at example.c:37
37 if (getInputAndRun() == -1) break;
(gdb) f 0
#0 0x000055bc13b7b9a0 in getInputAndRun () at example.c:28
28 *result[1] = toupper(result[1][0]);ดูง่ายนะครับ แปลว่า stack ของ program เราปัจจุบันรัน 2 function อยู่คือ main() และ getInputRun() เราสามารถกระโดดไปมาด้วยด้วยคำสั่ง frame <stack_no> หรือ f <stack_no>
ต่อมาดู source code ด้วย list
หรือสั่ง list แบบมี parameter เพื่ออ่านบรรทัดที่ระบุ หรือ function ที่ระบุก็ได้ครับ
นอกจาก list ที่เป็น source code ภาษา c แล้ว เรายังสามารถอ่านเป็นภาษา assembly ได้ด้วย อันนี้แล้วแต่ที่ถนัดเลยครับ ถ้า compile พร้อม debug flag (-g) แทบไม่ต้องลง assembly เลย แต่หลายครั้งการลงไปดู assembly ก็ทำให้เราเห็นรายละเอียดอะไรเยอะกว่าเช่นกัน
เนื่องจาก stack ของ main ไม่มีอะไรให้เราดู เราจะดู stack getInputAndRun() กันนะครับ เริ่มจากอ่านตัวแปรต่างๆ ด้วย p <variable_name> หรือ x/<type> <variable_or_address> อาจจะงงๆ มาดูตัวอย่างเลยดีกว่า
ตัว p คือ print ค่าออกมาครับ ซึ่งมันจะพยายามเดาประเภทของค่านั้นๆแล้ว print ให้เราอ่านง่าย แต่หลายครั้งมันก็พยายามฉลาดเกินไป เช่น p result[1] มันเห็นว่าค่าเป็น 0 เลยน่าจะเป็น integer ก็เลย print 0x0 มาให้ แต่เรารู้ว่าจริงๆมันเป็น string พอสั่ง x/s result[1] จะเห็นว่าได้ error กลับมาครับ
อันนี้ก็จบละ พื้นฐาน GDB ง่ายที่สุด คือการ print ตัวแปรออกมาดูว่าเกิดอะไรขึ้น ซึ่งถ้าเรา compile -g ไว้ก็จะเรียกตัวแปรตรงๆได้เลย แต่ถ้าเราไม่ได้ใส่ -g มา แต่มี coredump อยู่ เราก็ยังคงเรียกดูค่าใน address ก็ได้เหมือนกัน (แต่ต้องอ่าน assembly เป็นถึงจะรู้ว่า address ไหนคืออะไร)
อย่างข้อนี้เราก็เห็นแล้วว่ามันตายที่ example.c:28 ซึ่งก็ตายเพราะ result[1] มันมีค่าเป็น 0 นั่นเองทำให้ไม่สามารถ access เพื่อสั่ง toupper() ได้ เราก็ไปดูต่อว่าทำไมมันมีค่าเป็น 0 ต่อไป
ก็หวังว่าผู้อ่านรู้ถึงตรงนี้แล้ว จะไม่ไปนั่ง debug ด้วยการใส่ printf f-word เข้าไปทีละบรรทัด แล้ว compile + run ใหม่อีกนะครับ ใช้วิธีนี้ดีกว่า สะดวกเร็ว และไม่เผลอทิ้งขยะไว้ในโค้ดด้วย
Last updated
Was this helpful?