dbx คือหนึ่งในเครื่องมือดีบักระดับ Source-level ตัวแรก ๆ บนระบบปฏิบัติการตระกูล Unix พัฒนาขึ้นครั้งแรกช่วงต้นทศวรรษ 1980 ที่ University of California, Berkeley (UC Berkeley) โดย Mark Linton เพื่อใช้งานบนระบบ 4.2BSD ก่อนจะกลายมาเป็นดีบักเกอร์มาตรฐานประจำระบบปฏิบัติการเชิงพาณิชย์ระดับองค์กร เช่น IBM AIX, SunOS/Solaris, SGI IRIX และ DEC OSF/1
แม้ในปัจจุบันนักพัฒนาส่วนใหญ่จะคุ้นเคยกับ GDB (GNU Debugger) หรือ LLDB แต่บนเครื่องเซิร์ฟเวอร์ Unix ดั้งเดิม โดยเฉพาะ IBM AIX เครื่องมือ dbx ยังคงเป็นยูทิลิตีหลักที่ทรงพลังอย่างยิ่งในการแกะรอยปัญหาของโปรแกรมที่คอมไพล์จากภาษา C, C++, Fortran และ Pascal
สถาปัตยกรรมและการทำงานของ dbx
dbx ทำงานผ่านการควบคุมโพรเซสและเข้าถึงพื้นที่หน่วยความจำของระบบโดยตรง ผ่านกลไกหลัก 3 ส่วน
- Symbol Table Inspection
dbx อาศัยข้อมูลสัญลักษณ์ (Debug Symbols) ที่คอมไพลเลอร์ฝังไว้ในไฟล์ไบนารี เช่น ฟอร์แมต DWARF, XCOFF หรือ stabs เพื่อแมปแอดเดรสหน่วยความจำกลับมาเป็นชื่อตัวแปร ฟังก์ชัน และบรรทัดของซอร์สโค้ด - Process Tracing (ptrace)
ใช้งาน system call พื้นฐานของ Unix เช่นptraceในการแนบตัวเข้ากับโพรเซส (Attach), หยุดการทำงาน (Suspend), อ่าน/เขียนค่าใน Registers และควบคุมจังหวะการรันทีละคำสั่ง - Core Dump Post-Mortem Analysis
เมื่อโปรแกรมเกิดข้อผิดพลาดร้ายแรงจนระบบปฏิบัติการยุติการทำงานและเขียนภาพจำลองหน่วยความจำออกมาเป็นไฟล์coreตัว dbx สามารถโหลดไฟล์ไบนารีพร้อมไฟล์ core ขึ้นมาเพื่อย้อนดูสแต็กการเรียกใช้ฟังก์ชัน ณ วินาทีที่แครชได้ทันที
รูปแบบคำสั่งและการใช้งานจริง
การจะใช้งาน dbx ได้เต็มประสิทธิภาพ ต้องคอมไพล์โปรแกรมพร้อมแฟล็ก -g เพื่อให้คอมไพลเลอร์สร้าง Symbol Table เสมอ
# ตัวอย่างการคอมไพล์ด้วย GCC หรือ IBM XL C
gcc -g -o myapp myapp.c
1. การเริ่มต้นเซสชันดีบัก
# ดีบักโปรแกรมตั้งแต่เริ่มสตาร์ต
dbx myapp
# ดีบักจากไฟล์ Core Dump เพื่อหาสาเหตุการแครช
dbx myapp core
# ดีบักด้วยการ Attach เข้าไปเกาะ Process ID (PID) ที่กำลังรันอยู่บนระบบ
dbx -a 12345
2. คำสั่งควบคุมการรัน (Execution Control)
run [args]— เริ่มสั่งรันโปรแกรมพร้อมพารามิเตอร์ที่ต้องการcont— สั่งให้โปรแกรมทำงานต่อหลังจากถูกหยุดโดย Breakpointstep(หรือs) — ก้าวข้ามไป 1 คำสั่ง โดยกระโดดเข้าไปในฟังก์ชันย่อย (Step Into)next(หรือn) — ก้าวข้ามไป 1 คำสั่ง โดยข้ามการเข้าไปในฟังก์ชันย่อย (Step Over)return— สั่งให้ฟังก์ชันปัจจุบันรันจนจบและคืนค่ากลับไปยัง Caller ทันทีquit(หรือq) — ออกจากโปรแกรม dbx
3. การวางจุดหยุด (Breakpoints & Tracing)
dbx มีไวยากรณ์สำหรับตั้งจุดหยุดที่อ่านเข้าใจง่าย โดยใช้คำนำหน้าว่า stop
(dbx) stop at 42 # หยุดเมื่อโปรแกรมทำงานถึงบรรทัดที่ 42
(dbx) stop in process_data # หยุดเมื่อมีการเรียกฟังก์ชัน process_data
(dbx) stop at 55 if total > 1000 # Conditional Breakpoint หยุดเฉพาะเมื่อเงื่อนไขเป็นจริง
(dbx) status # ดูรายการ Breakpoint ทั้งหมดที่เปิดใช้งานอยู่
(dbx) delete 1 # ลบ Breakpoint หมายเลข 1
นอกจากนี้ยังมีคำสั่ง trace สำหรับมอนิเตอร์ค่าตัวแปรโดยไม่ต้องสั่งหยุดโปรแกรม
(dbx) trace counter at 80 # แสดงค่าตัวแปร counter ทุกครั้งที่ผ่านบรรทัดที่ 80
(dbx) trace in update_records # ติดตามทุกครั้งที่เข้า-ออกฟังก์ชัน update_records
4. การตรวจสอบหน่วยความจำและตัวแปร (Inspection)
print <variable>— พิมพ์ค่าปัจจุบันของตัวแปร เช่นprint *user_ptrwhere— แสดง Call Stack Trace ทั้งหมด เพื่อดูว่าฟังก์ชันปัจจุบันถูกเรียกต่อมาจากฟังก์ชันใด (เทียบเท่าคำสั่งbacktraceหรือbtบน GDB)up/down— สลับดู Frame ของ Stack ขึ้นหรือลงตามลำดับการเรียกdump— แสดงค่าของตัวแปร Local Variable ทั้งหมดในฟังก์ชันปัจจุบันregisters— พิมพ์ค่าที่อยู่ใน CPU Registers ทั้งหมด
Use Case ตัวอย่าง: การวิเคราะห์ Segmentation Fault ด้วย Core Dump
เมื่อโปรแกรมระดับโปรดักชันบน Unix เกิดอาการแครช เช่น เข้าถึง Null Pointer หรือเกิด Buffer Overflow ระบบจะสร้างไฟล์ core ขึ้นมา การใช้ dbx ตรวจสอบสามารถทำได้เป็นขั้นตอนดังนี้
$ dbx myapp core
Core file "core" for program "myapp" produced by pid 49210
Type 'help' for help.
[using memory image in core]
reading symbolic information ...
Segmentation fault in process_payload at line 78 in file "network.c"
78 *buffer_out = payload->data[0];
(dbx) where
process_payload(payload = (nil), buffer_out = 0x20014abc), line 78 in "network.c"
worker_thread(arg = 0x20014a00), line 145 in "server.c"
main(argc = 2, argv = 0x2ff22a50), line 34 in "main.c"
(dbx) print payload
(nil)
จากข้อมูลใน Session
- คำสั่ง
whereชี้ให้เห็นทันทีว่าคำสั่งที่พังอยู่ที่บรรทัด 78 ในไฟล์network.c - การตรวจสอบค่าด้วย
print payloadยืนยันว่าพอยน์เตอร์มีค่าเป็นnil(Null Pointer) ทำให้เมื่อโปรแกรมพยายาม Dereference ค่าหน่วยความจำผ่านpayload->data[0]ซีพียูจึงส่งสัญญาณSIGSEGVเข้ามาขัดจังหวะทันที
ตารางเปรียบเทียบคำสั่ง: dbx vs GDB vs LLDB
| หน้าที่การทำงาน | dbx (Unix Native) | GDB (GNU) | LLDB (LLVM) |
| แสดง Stack Trace | where | backtrace / bt | thread backtrace / bt |
| ตั้ง Breakpoint ที่บรรทัด | stop at <line> | break <line> / b <line> | breakpoint set -l <line> / b <line> |
| ตั้ง Breakpoint ที่ฟังก์ชัน | stop in <func> | break <func> / b <func> | breakpoint set -n <func> / b <func> |
| รันต่อไปทีละบรรทัด (Over) | next / n | next / n | thread step-over / n |
| ก้าวเข้าไปในฟังก์ชัน (Into) | step / s | step / s | thread step-in / s |
| พิมพ์ค่าตัวแปร | print <var> | print <var> / p <var> | frame variable <var> / p <var> |
| ดู Local Variables ทั้งหมด | dump | info locals | frame variable |
| ดูรายการ Breakpoint | status | info break | breakpoint list |
บทบาทของ dbx ในปัจจุบัน
แม้ปัจจุบันแพลตฟอร์มส่วนใหญ่จะย้ายไปสู่ Linux ที่ใช้ GDB/LLDB เป็นมาตรฐาน แต่ความรู้เรื่อง dbx ยังคงจำเป็นอย่างยิ่งในสายงาน
- Enterprise Infrastructure
ระบบธนาคารและโทรคมนาคมที่ยังคงรันงานสำคัญ (Mission-Critical Workloads) บน IBM Power Systems (AIX) - Legacy C/C++ Codebase Maintenance
การตรวจสอบปัญหาหน่วยความจำบนซอฟต์แวร์ระดับ Core Banking หรือ ERP รุ่นดั้งเดิมที่รันอยู่บน Unix แพลตฟอร์มแบบปิด - Debugging Tools Foundation
ไวยากรณ์หลายอย่างของ dbx ส่งอิทธิพลโดยตรงต่อการออกแบบดีบักเกอร์รุ่นหลัง เช่น โหมดจำลองคำสั่ง dbx ภายใน GDB หรือเครื่องมือ TotalView บนระบบ High-Performance Computing (HPC)