dbx Debugger (Unix Tool)

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 ส่วน

  1. Symbol Table Inspection
    dbx อาศัยข้อมูลสัญลักษณ์ (Debug Symbols) ที่คอมไพลเลอร์ฝังไว้ในไฟล์ไบนารี เช่น ฟอร์แมต DWARF, XCOFF หรือ stabs เพื่อแมปแอดเดรสหน่วยความจำกลับมาเป็นชื่อตัวแปร ฟังก์ชัน และบรรทัดของซอร์สโค้ด
  2. Process Tracing (ptrace)
    ใช้งาน system call พื้นฐานของ Unix เช่น ptrace ในการแนบตัวเข้ากับโพรเซส (Attach), หยุดการทำงาน (Suspend), อ่าน/เขียนค่าใน Registers และควบคุมจังหวะการรันทีละคำสั่ง
  3. 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 — สั่งให้โปรแกรมทำงานต่อหลังจากถูกหยุดโดย Breakpoint
  • step (หรือ 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_ptr
  • where — แสดง 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

  1. คำสั่ง where ชี้ให้เห็นทันทีว่าคำสั่งที่พังอยู่ที่บรรทัด 78 ในไฟล์ network.c
  2. การตรวจสอบค่าด้วย print payload ยืนยันว่าพอยน์เตอร์มีค่าเป็น nil (Null Pointer) ทำให้เมื่อโปรแกรมพยายาม Dereference ค่าหน่วยความจำผ่าน payload->data[0] ซีพียูจึงส่งสัญญาณ SIGSEGV เข้ามาขัดจังหวะทันที

ตารางเปรียบเทียบคำสั่ง: dbx vs GDB vs LLDB

หน้าที่การทำงานdbx (Unix Native)GDB (GNU)LLDB (LLVM)
แสดง Stack Tracewherebacktrace / btthread 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 / nnext / nthread step-over / n
ก้าวเข้าไปในฟังก์ชัน (Into)step / sstep / sthread step-in / s
พิมพ์ค่าตัวแปรprint <var>print <var> / p <var>frame variable <var> / p <var>
ดู Local Variables ทั้งหมดdumpinfo localsframe variable
ดูรายการ Breakpointstatusinfo breakbreakpoint 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)