Bài 35.428 phút đọc
Gỡ lỗi con trỏ và cấu trúc dữ liệu
Sau bài này bạn sẽ làm được
- Đi theo chuỗi con trỏ nhiều tầng bằng một lệnh print
- Duyệt danh sách liên kết bằng biến tiện dụng và while
- Xem một struct dưới dạng byte thô để phát hiện đệm
- Nhận ra con trỏ hỏng từ giá trị của nó
Con trỏ là chỗ GDB có ích nhất, vì đây đúng là thứ printf không giúp được: bạn không in được một cấu trúc dữ liệu bằng cách rắc thêm dòng in, nhưng bạn duyệt được nó bằng ba lệnh GDB.
#Đi theo chuỗi con trỏ
terminal
# break tong, run, bt, info args, rồi đi theo chuỗi sau
gdb -q -batch -x g1.gdb ./dslk.exe
Breakpoint 1 at 0x4015be: file dslk.c, line 23.
Thread 1 hit Breakpoint 1, tong (dau=0x752830) at dslk.c:23
23 int t = 0;
#0 tong (dau=0x752830) at dslk.c:23
#1 0x000000000040168c in main () at dslk.c:39
dau = 0x752830
$1 = {ma = 30, ten = "ba", '\000' <repeats 13 times>, sau = 0x7527e0}
$2 = "ba", '\000' <repeats 13 times>
$3 = "hai", '\000' <repeats 12 times>
$4 = "mot", '\000' <repeats 12 times>
$5 = (struct Nut *) 0x0(gdb) print *dau
$1 = {ma = 30, ten = "ba", '\000' <repeats 13 times>, sau = 0x7527e0}
(gdb) print dau->ten
$2 = "ba", '\000' <repeats 13 times>
(gdb) print dau->sau->ten
$3 = "hai", '\000' <repeats 12 times>
(gdb) print dau->sau->sau->ten
$4 = "mot", '\000' <repeats 12 times>
(gdb) print dau->sau->sau->sau
$5 = (struct Nut *) 0x0 /* het danh sach */#Duyệt danh sách bằng vòng lặp
Vòng lặp trong GDB
(gdb) set $p = dau
(gdb) while $p != 0
>print $p->ma
>print $p->ten
>set $p = $p->sau
>end
$1 = 30
$2 = "ba", '\000' <repeats 13 times>
$3 = 20
$4 = "hai", '\000' <repeats 12 times>
$5 = 10
$6 = "mot", '\000' <repeats 12 times># Dong thanh mot lenh tu dinh nghia, dat trong .gdbinit
define in_ds
set $p = $arg0
set $i = 0
while $p != 0
printf "[%d] ma=%-4d ten=%s\n", $i, $p->ma, $p->ten
set $p = $p->sau
set $i = $i + 1
end
end
document in_ds
In toan bo danh sach lien ket bat dau tu nut duoc truyen vao.
Vi du: in_ds dau
end
# Dung:
(gdb) in_ds dau
(gdb) help in_ds
# Bai 35.6 se noi ky ve .gdbinit va lenh tu dinh nghia.#Nhận ra con trỏ hỏng
| Giá trị con trỏ | Nghĩa gần như chắc chắn |
|---|---|
| 0x0 | NULL, chưa gán hoặc hàm trả về NULL |
| 0x1 tới 0xfff | NULL cộng một độ dời, tức p->truong với p là NULL |
| 0xffffffffffffffff | Trả về -1 bị ép thành con trỏ |
| 0xdeadbeef, 0xbaadf00d | Mẫu đánh dấu do bộ cấp phát gỡ lỗi ghi vào |
| 0xcdcdcdcd | Bộ nhớ heap chưa khởi tạo, quy ước của MSVC |
| 0xfeeefeee | Bộ nhớ heap đã giải phóng, quy ước của MSVC |
| Giá trị trông như văn bản ASCII | Bộ đệm bị ghi đè lên con trỏ |
| Địa chỉ hợp lý nhưng dữ liệu là rác | Dùng sau khi giải phóng |
# Ba lenh kiem tra mot con tro dang nghi ngo
(gdb) print p
$1 = (Nut *) 0x752830
(gdb) print *p # co doc duoc khong?
$2 = {ma = 30, ten = "ba", ...} # doc duoc, va du lieu hop ly
(gdb) x/32xb p # xem byte tho
0x752830: 0x1e 0x00 0x00 0x00 0x62 0x61 0x00 0x00
...
(gdb) info symbol p # p co tro vao mot ky hieu biet ten khong?
No symbol matches p. # binh thuong voi bo nho heap
(gdb) info proc mappings # p nam trong vung nho nao?#Đi lại giữa các khung
terminal
gdb -q -batch -x g8.gdb ./buoc.exe
Breakpoint 1 at 0x40155b: file buoc.c, line 4. Thread 1 hit Breakpoint 1, binh_phuong (x=3) at buoc.c:4 4 int t = x * x; #0 binh_phuong (x=3) at buoc.c:4 #1 0x0000000000401584 in tinh (a=3, b=4) at buoc.c:10 #2 0x00000000004015be in main () at buoc.c:17
bt # ca chuoi goi
bt 5 # chi 5 khung tren cung
bt -5 # chi 5 khung duoi cung
bt full # kem bien cuc bo cua moi khung
frame 1 # chuyen sang khung 1 (viet tat: f 1)
up # xuong duoi mot bac, tuc ve phia ham goi
down # len tren mot bac
frame # in khung hien tai
info frame # dia chi tra ve, con tro khung, thanh ghi da luu#Xem struct dưới dạng byte thô
terminal
gdb -q -batch -x g2.gdb ./dslk.exe
0x61fe30: 5 3 9 1 0x61fe40: 7 0x61fe30: 0x05 0x00 0x00 0x00 0x03 0x00 0x00 0x00 0x61fe38: 0x09 0x00 0x00 0x00 0x01 0x00 0x00 0x00 0x61fe40: 0x07 0x00 0x00 0x00 0xb82834: "ba"
(gdb) print sizeof(Nut)
$1 = 32
(gdb) x/32xb dau /* 32 byte tho cua mot nut */
(gdb) x/8xw dau /* cung vung, doc theo word 4 byte */
(gdb) x/4xg dau /* cung vung, doc theo o 8 byte */
/* Ba cach nhin cung mot vung nho. Dung khi:
- kiem tra phan dem cua struct, doi chieu voi ptype/o
- doi chieu voi mot dinh dang tep nhi phan
- tim mot mau byte bi ghi de */Tự làm thử
- Đi theo chuỗi
dau->sau->saucho tới khi gặp NULL. - Viết vòng lặp
whiletrong GDB in cả danh sách. - Đóng nó thành lệnh
define in_dsvà thêmdocument. - Viết lệnh phát hiện vòng lặp bằng hai con trỏ và thử trên một danh sách có vòng.
- Thử
print aở khung 0 rồiframe 1và thử lại. - Dùng
x/20xbtrên một mảngintđể xác định thứ tự byte.
Trình chấm điểm tự động sẽ được bổ sung ở giai đoạn sau. Hiện tại bạn tự chạy thử trên máy.
Tóm tắt
print dau->sau->sau->tenđi qua nhiều tầng con trỏ trong một lệnh.- Vòng lặp
whilevới biến tiện dụng$pduyệt được bất kỳ cấu trúc dữ liệu nào mà không phải dịch lại. - Con trỏ hỏng có dấu hiệu nhận biết: giá trị rất nhỏ, mẫu đánh dấu như
0xdeadbeef, hoặc trông như văn bản ASCII. printchỉ thấy biến của khung hiện tại, nên phảiframe ntrước, hoặc viếtten_ham::ten_bien.- Đọc backtrace từ trên xuống và dừng ở khung đầu tiên thuộc mã của bạn.