Debug #1 – GDB - B4050N
Có thể bạn quan tâm
LỜI TỰA
Tiếp theo phần trước, ở phần này, chủ đề chính của chúng ta là làm quen với các công cụ debug. Mục tiêu của bài viết là cung cấp những thao tác làm việc cơ bản với gdb trên giao diện dòng lệnh và so sánh chúng với giao diện gỡ lỗi đồ họa trên Eclipse. Đây cũng là phần chuẩn bị để các bạn có thể hình dung tốt hơn diễn biến của những phần demo trong các bài viết tiếp theo. Ngoài ra, mình cũng sẽ giới thiệu sơ qua về các option yêu cầu khi biên dịch chương trình cho mục đích debug. Đương nhiên, minh họa cho phần này vẫn sẽ sử dụng trình biên dịch quen thuộc trong bộ công cụ GCC.
Cùng điểm qua thứ tự các nội dung: 1. Cài đặt công cụ trên môi trường Linux 2. Các thao tác cơ bản khi thực hiện gỡ lỗi 3. Tùy chọn bổ sung khi biên dịch chương trình cho mục đích debug 4. Tóm lược
Sau đây là chi tiết:
1. Cài đặt công cụ trên môi trường Linux
Vẫn là môi trường làm việc quen thuộc như trong các bài viết trước. Nói là phải cài đặt nhưng thực tế thì cũng chẳng cần làm gì to tát. Đơn giản là vì những công cụ này đa phần là đã có sẵn trên các bản phân phối Linux, hoặc là bạn chỉ cần lên internet, download chúng về và sử dụng được ngay.
1.1. GNU Compiler Collection
GCC – hay đầy đủ hơn là GNU Compiler Collection là một hệ thống các trình biên dịch được tạo ra từ dự án GNU, hỗ trợ rất nhiều các ngôn ngữ lập trình. GCC là thành phần cốt lõi của GNU Toolchain, một tập hợp lớn các công cụ lập trình trong dự án GNU, đóng vai trò quan trọng trong phát triển Linux và phần mềm cho hệ thống nhúng. Đây cũng chính là bộ trình biên dịch tiêu chuẩn trên các hệ điều hành Unix-like. Ngoài ra, GCC cũng được port sang nhiều nền tảng khác (Cygwin hay MinGW trên Windows chẳng hạn).
Gần như hầu hết các bản phân phối Linux hiện này đều đã được cài đặt sẵn ít nhất 1 phiên bản của GCC. Để kiểm tra xem đã cài đặt GCC hay chưa, bạn có thể dùng lệnh
gcc --versionNếu bạn đã sở hữu GCC trên máy tính, kết quả thu được có dạng như sau:

Trong hình trên, mình đang sử dụng GCC phiên bản 5.4.0. trên Ubuntu. Ngoài ra, để các bạn cũng nên cài đặt thêm 2 gói phần mềm build-essential và binutils. Đặc biệt, một số công cụ trong bin-utils sẽ hữu ích trong quá trình gỡ lỗi. Trên Ubuntu:
sudo apt install build-essential binutilsTương tự, với gdb, ta cũng kiểm tra tương tự.
gdb --versionDưới đây là kết quả ví dụ, mình đang sử dụng gdb 7.11.1:

1.2. ARM-None-EABI-GCC
Toolchain quen thuộc này mình đã có giới thiệu ở bài Tools #2 – Debug trên console. Đây là bộ công cụ được thiết kế dựa theo GCC dành cho kiến trúc ARM. Theo quy ước thì cách đặt tên của 1 toolchain có dạng Arch-[Vendor]-[OS]-ABI:
- Arch: kiến trúc đích, ví dụ: arm, i686, x86_64 …
- Vendor: phía nhà cung cấp toolchain, ví dụ: linaro, fsl …
- OS: hệ điều hành đích mà chương trình triển khai, ví dụ: linux, freebsd …
- ABI: Application Binary Interface, ví dụ: eabi, gnueabi …
Như vậy, ARM-None-EABI-GCC có nghĩa là bộ tập hợp trình biên dịch GNU cho kiến trúc ARM biên dịch với giao diện Embedded ABI (eabi), không có vendor (khuyết tên), và không triển khai trên hệ điều hành (none – hay bare metal).
Về các thức cài đặt ARM-None-EABI-GCC, các bạn xem tại mục 2.2 – GNU ARM Embedded Toolchain, bài viết Tools #2 – Debug trên console.
1.3. Eclipse
Nếu chỉ đơn giản là lập trình ứng dụng trên các máy tính thông thường, bạn có thể download bất kỳ phiên bản Eclipse nào mà bạn muốn. Các phiên bản mới của IDE này đã hỗ trợ các trình đơn cài đặt, rất thuận tiện kể cả khi sử dụng hệ điều hành Windows hay Linux. Với các phiên bản cũ hơn, các bạn sẽ download về tệp tin nén tương ứng, tiến hành giải nén và có thể cần thêm đường dẫn vào biến môi trường tùy theo mục đích sử dụng của bạn.
Vì mình có ý định sử dụng giao diện debug của Eclipse để minh họa phần gỡ lỗi trên vi điều khiển ARM Cortex-M3 (ARMv7-m), ta nên chọn phiên bản Mars 2. Các bạn có thể download ở đây. Mình sử dụng phiên bản này vì nó hoạt động khá ổn định với GNU MCU Eclipse plugins. Plugins này cung cấp khá nhiều thứ hay ho: hỗ trợ khởi tạo project C/C++ cho các vi điều khiển ARM Cortex-M (đặc biệt là các dòng sản phẩm ST), hỗ trợ thiết lập phiên debug với arm-none-eabi-gdb, hỗ trợ JLinkGDBServer, dự án OpenOCD (nếu bạn muốn sử dụng TI ICDI, STLink…). Cách tải và cài đặt plugins này được chia sẻ trên website của dự án, khá chi tiết nên mình sẽ không viết lại ở đây nữa.
2. Các thao tác cơ bản khi thực hiện gỡ lỗi
Chúng ta sẽ cùng xem qua một số thao tác cơ bản khi bạn thực hiện gỡ lỗi. Phần này, mình sẽ tiến hành minh họa trên cả giao diện lệnh và đồ họa, để các bạn thuận tiện trong quá trình theo dõi. Các thao tác chia theo nhóm, câu lệnh trong gdb và thao tác trên giao diện Eclipse của một chức năng tương đương. Để phần trình bày bớt phức tạp, mình sẽ chỉ thực hiện các thao tác diễn ra khi debug. Phần kết nối trong với máy chủ gỡ lỗi khi debug vi điều khiển, các bạn có thể xem lại trong bài Tools #2 – Debug trên console hoặc bài viết kế tiếp. Mình sẽ cố gắng sắp xếp thời gian ghi lại video phần demo.
Trước tiên, chúng ta cần phải làm quen với giao diện debug.
2.1. Giao diện tổng quát GDB và Eclipse Debug perspective
Chắc cũng chẳng cần mô tả các bạn cũng biết giao diện của gdb hoặc arm-none-eabi-gdb sẽ trông ra sao… nền đen chữ trắng, đơn giản mà hiệu quả.

Đơn giản, bạn chỉ cần gõ gdb vào Terminal và mọi thứ sẽ hiện lên giống như hình trên đây. Tương tự, bên phía Eclipse, bạn sẽ vào giao diện debug thông qua biểu tượng hình con bọ trên thanh điều khiển
hoặc nhấp phải chuột vào tên project đang thực hiện và truy cập thông qua lựa chọn Debug As trong trình đơn ngữ cảnh. Lưu ý, là bạn cần cấu hình trước. Phần cấu hình này mình sẽ trình bày ở bài sau, khi chạy minh họa.

Nhìn qua, các bạn có thể thấy mọi thứ hơi rắc rối. Mình sẽ mô tả về các khu vực, các tab chúng ta thường xuyên thao tác và câu lệnh gdb thực hiện chức năng tương đương của tab đó trên giao diện lệnh.
2.1.1. Call Stack và thẻ Debug
Đầu tiên là call stack, hay còn gọi là execution stack, program stack, control stack, run-time stack hay machine stack. Nó lưu trữ thông tin về các lời gọi thủ tục/hàm đang hoạt động trong quá trình chạy ứng dụng. Call stack giúp lập trình viên biết thủ tục/hàm nào đang được gọi (nằm ở đỉnh ngăn xếp) và danh sách + thứ tứ các thủ tục/hàm đang chờ thực thi sau khi thủ tục/hàm hiện tại kết thúc. Mô hình của Call stack, đúng như cái tên của nó, được tổ chức dưới dạng ngăn xếp.
Một ví dụ, từ hàm main(), bạn gọi đến hàm A(), hàm A() đang thực thi lại gọi tới hàm B(), hàm B() đang thực thi lại gọi tới hàm C(). Như vậy, nếu trong quá trình debug, vị trí của con trỏ chương trình dừng lại ở đâu đó trong hàm C(), thứ tự của Call stack sẽ là C() – B() – A() – main(). Nghĩa là, C() đang được thực thi, là đỉnh của ngăn xếp. Sau khi C() kết thúc và trả kết quả/điều khiển, B() sẽ thực thi phần mã còn lại kể từ sau vị trí gọi C(). Tương tự, B() kết thúc sẽ trả điều khiển/kết quả để thực hiện tiếp A(). Và sau khi A() thực thi xong, con trỏ chương trình sẽ trở về hàm main(), ở vị trí kế tiếp lệnh gọi đến A().
Vẫn ví dụ ấy, nhưng sau khi C() thực hiện xong, trả kết quả/điều khiển, B() tiếp tục gọi tới D(). Lúc này, Call stack sẽ có thứ tự D() – B() – A() – main(). Hoặc nếu C() đang thực thi lại gọi E(), Call stack sẽ có thứ tự E() – C() – B() – A() – main(). Nên nhớ, Call stack là một khái niệm khác biệt so với ngăn xếp bộ nhớ vật lý (vùng nhớ stack trong bộ nhớ máy tính). Đừng nhầm lẫn 2 khái niệm này.
Call stack được hiển thị trong thẻ Debug trên giao diện gỡ lỗi của Eclipse. Như các bạn thấy trên hình:

Trong hình có thể thấy, Call stack hiện tại trong chương trình gồm hai hàm nonInlineFunc() và main(). Đối với giao diện console, GDB cung cấp cho chúng ta lệnh backtrace (có thể viết tắt là bt):

Trên thẻ Debug, các bạn còn thấy thứ tự luồng mà các hàm đang hoạt động. Trong giao diện console, lệnh info threads tương đương với chức năng này:

2.1.2. Thẻ Console
Thẻ Console sẽ hiển thị nội dung tùy theo ngữ cảnh được chọn trong thẻ Debug. Nếu bạn trỏ vào các dòng Thread hoặc các nhánh bên trong các cụm đó, bạn có thể sử dụng các thao tác trên thanh công cụ như Step, Over, Return, Resume, v.v… Lúc này, nội dung trong thẻ Console sẽ là kết quả thực thi chương trình của bạn, tính tới trước vị trí của con trỏ lệnh hiện tại.

Nếu bạn chọn qua dòng gdb, như hình bên dưới là gdb(7.11.1), tương ứng với phiên bản gdb đang cài đặt trên máy, nội dung trong thẻ Console sẽ thay đổi giống với giao diện lệnh khi làm việc trên Terminal.Ở ngữ cảnh này, các thao tác trên thanh công cụ sẽ không thể sử dụng được. Bạn chỉ có thể nhập các lệnh điều khiển vào đây và trên giao diện sẽ là kết quả của lệnh bạn vừa đưa vào.

2.1.3. Các thẻ Source code
Mã nguồn trong từng tệp tin được hiển thị trong các thẻ với tên tương ứng, như trong giao diện lập trình. Tại đây, bạn có thể thấy mũi tên tượng trưng cho con trỏ lệnh, trỏ vào câu lệnh sẽ thực thi tiếp theo. Lệnh sẽ được thực hiện cũng được highlight để bạn dễ quan sát hơn. Trên giao diện lệnh bạn sử dụng list để có thể theo dõi vị trí và nội dung câu lệnh đang thực thi. Mặc định, sau khi gọi list, trên màn hình sẽ in ra 10 dòng mã, và vị trí của con trỏ lệnh sẽ ở dòng thứ 6

Ngoài ra, các bạn thấy có các dấu tròn ở vị trí bên trái số thứ tự dòng code, cùng cột với mũi tên con trỏ lệnh. Đây là các chính breakpoint. Trên giao diện đồ họa, bạn có thể đặt breakpoint trực tiếp bằng cách click đúp vào số thứ tự dòng code mà bạn muốn chương trình sẽ dừng lại ở đó. Để gỡ bỏ breakpoint, bạn cũng sử dụng thao tác tương tự.
Để đặt breakpoint trên giao diện lệnh, bạn có thể sử dụng lệnh break (viết tắt là b). Nếu chỉ đơn giản là break, breakpoint sẽ được đặt tại vị trí hiện tại của con trỏ lệnh. Bạn có thể đặt breakpoint tại bất kỳ vị trí nào trong chương trình bằng cách sử dụng break kết hợp với thứ tự dòng code với mã C/C++ hoặc địa chỉ của lệnh assembly.

Để xóa breakpoint, bạn có thể sử dụng lệnh delete breakpoints , trong đó là chỉ số breakpoint, ví dụ delete breakpoints 5.
2.1.4. Thẻ Disassembly
Thẻ này hiển thị nội dung mã assembly tương ứng của chương trình. Trong thẻ Disassembly, bạn có thể thực hiện các thao tác tương tự như trên các thẻ mã nguồn C/C++.

Bạn có thể sử dụng lệnh disassemble trên giao diện lệnh nếu có nhu cầu xem hợp ngữ.

2.1.5. Các thẻ thông tin
Có 3 thẻ chúng ta sẽ quan tâm là Variables, Breakpoints và Registers. Thẻ Varialbes hiển thị thông tin các biến cục bộ của hàm đang thực thi trong ngữ cảnh hiện tại (hàm nằm ở đỉnh của Call stack). Thẻ Breakpoints chứa danh sách các breakpoint đang được đặt trong quá trình gỡ lỗi. Thẻ Registers chứa danh sách và nội dung các thanh ghi trên vi xử lý.
Dĩ nhiên, vẫn còn một số thẻ khác, nhưng có lẽ chúng chưa quá quan trọng nên mình sẽ không trình bày ở đây. Bạn nào tò mò tìm hiểu nhớ ghi chép chia sẻ cho anh em bạn bè.
2.1.6. Memory Browser
Thẻ này cho phép bạn duyệt nội dung bộ nhớ. Bạn sẽ nhập vào địa chỉ bắt đầu của vùng nhớ muốn duyệt, thông tin của vùng nhớ sẽ xuất hiện ở dạng hexadecimal kèm địa chỉ.

2.1.7. Chức năng trên thanh công cụ
Mình sẽ trình bày qua về các chức năng hay sử dụng trên thanh công cụ. Từ trái qua phải:

1. Skip all breakpoint: bỏ qua tất cả các breakpoint, tương đương disable breakpoints trên console.
2. Resume: thực thi chương trình cho tới khi gặp breakpoint tiếp theo, tương đương continue trên console.
3. Suspend: tạm dừng, thường dùng khi gặp một khối lệnh thực thi quá lâu hay vòng lặp, bạn muốn tạm dừng để kiểm tra giá trị biến, tương đương tổ hợp CTRL + C trên console.
4. Terminate: dùng khi bạn muốn kết thúc phiên debug, tương đương quit trên console.
5. Disconnect: dùng khi bạn muốn ngắt kết nối tới chương trình debug, tương đương disconnect trên console.
6. Step Into: thực thi câu lệnh kế tiếp, nếu lệnh đó là một hàm, đi tới nội dung bên trong hàm. Tương đương step trên console.
7. Step Over: thực thi qua câu lệnh kế tiếp, nếu lệnh đó là một hàm, thực thi xuyên suốt từ đầu tới cuối hàm. Tương đương next trên console.
8. Step Return: khi đang dừng lại trong một hàm con, thực thi đến khi thoát ra khỏi hàm và trả về kết quả. Tương đương return trên console.
9. Instruction Stepping Mode: nếu được chọn, các lệnh thao tác 6 và 7 được thực hiện trên mã assembly tương ứng thay cho hàm C/C++. Các lệnh stepi và nexti cho kết quả tương tư trên console.
2.2. Tổng hợp với câu lệnh trên GDB
Ở trên đây, chúng ta đã dạo qua một lượt giao diện và các thao tác tương ứng với giữa gdb và Eclipse Debug perspective. Phần này, mình sẽ hệ thống lại các câu lệnh gdb theo từng nhóm chức năng để dễ quản lý hơn.
2.2.1. Các lệnh về breakpoint
2.2.2. Các lệnh điều khiển thực thi
2.2.3. Biến và thao tác với biến
2.2.4. Khởi chạy chương trình
Các bạn lưu ý, lệnh run có được thực thi hay không phụ thuộc vào môi trường gỡ lỗi đích. Nếu môi trường bạn đang làm việc không hỗ trợ, như ví dụ gỡ lỗi trên vi điều khiển với arm-none-eabi-gdb, sẽ có một thông báo xuất hiện với nội dung như sau:
The "remote" target does not support "run". Try "help target" or "continue".2.2.5. Hiển thị thông tin
Trong đó, nếu sử dụng các lệnh print và set (ở mục 2.2.3) thao tác với biến hoặc con trỏ trong chương trình C/C++, xuất hiện một số ngữ cảnh tương đối giống với lập trình: có thể bạn muốn biết địa chỉ của biến hay giá trị nơi con trỏ trỏ tới. Lúc này, cú pháp vẫn giốn như khi bạn làm việc với ngôn ngữ C/C++:
- Nếu muốn xem địa chỉ của biến var, sử dụng print &var.
- Nếu muốn xem giá trị con trỏ ptr đang trỏ đến, sử dụng print *ptr.
- Sử dụng set *(data-type *) addr = value, nếu bạn muốn gán giá trị cho một ô nhớ có địa chỉ cho trước. Cú pháp này rất hữu dụng, còn vì sao hữu dụng thì các bạn hãy đoán xem?
2.2.6. Thao tác file
Lệnh load sẵn có hay không phụ thuộc vào cấu hình gỡ lỗi thiết lập trong gdb, thường thì xuất hiện nếu bạn debug chương trình trên một máy tính ở xa, hoặc trên một mạch debug bên ngoài như khi làm việc với vi điều khiển. Nếu bạn cố tình sử dụng load khi câu lệnh này không sẵn có, một thông báo như sau sẽ xuất hiện:
You can't do that when your target is …Hãy lưu ý kiểm tra trước khi tiến hành viết script tự động.
2.2.7. Kết nối GDB Server và debug từ xa/cục bộ
Phần này thường gặp khi bạn phải debug ở xa nơi cài đặt chương trình hoặc debug trên board mạch bên ngoài.
Muốn debug từ xa, cần chắc chắn rằng máy chủ debug đã được thiết lập. Với GDBServer, bạn thực thi theo cú pháp:
gdbserver :port prog [arg...]Trên JLinkGDBServer, mình đã trình bày ở bài Tools #2 – Debug trên console. Một số lệnh sẽ sử dụng trong quá trình debug từ xa.
Nếu chỉ debug cục bộ (debug trực tiếp trên máy tính của bản thân), bạn chỉ cần gọi gdb kèm theo tệp tin chương trình thực thi mà bạn muốn debug là đủ, ví dụ:
gdb demo.exeTrên đây là những thao tác theo mình là cần thiết khi gỡ lỗi với GDB trên giao diện lệnh. Nếu các bạn muốn có một trải nghiệm tốt hơn mà vẫn chỉ sử dụng GDB, hãy tìm hiểu về tùy chọn -tui (text ui) cùng 2 lệnh focus và layout trong chế độ này. Mình sẽ không trình bày các thao tác trên giao diện đồ họa, mà sẽ để dành phần này trong video demo. Nhìn chung, trên giao diện đồ họa, các thao tác này khá đơn giản và đã thiết kế, hỗ trợ đầy đủ.
3. Tùy chọn bổ sung khi biên dịch chương trình cho mục đích debug
Những ai đã và đang làm trong lĩnh vực phần mềm chắc chẳng lạ gì điều này. Nhưng đa số bạn sinh viên sẽ không để tâm đến nó. Các bạn có bao giờ tự hỏi, tại sao build project xong lại vào tệp thực thi ở thư mục Debug khi sử dụng VS, QT Creator, Android Studio hay Eclipse chưa? Hoặc, bạn có đặt ra thắc mắc tương tự khi nhìn thấy những hình ảnh dưới đây?


Nếu bạn chưa biết, cũng không có gì là lạ, vì chủ yếu khi mới học lập trình, các bạn thường chỉ quan tâm code biên dịch xong chạy được là được, còn chương trình sau khi build lưu tại đâu thì vào đó lấy ra thôi. Nhắc lại câu chuyện ở bài viết trước. Xét cho cùng, thói quen này hình thành là bởi chủ quan cá nhân thường chỉ chú trọng vào 1 vấn đề mà bạn thích, ở đây là viết mã, mà ít quan tâm (thậm chí là không thèm để tâm) đến những vấn đề kéo theo phía sau có tầm quan trọng không hề thua kém. Phải nhìn nhận rõ vấn đề, thầy cô, người hướng dẫn chắc chắn đã đề cập tới nó, trừ khi bạn là người tự học, song, thời gian trên lớp không đủ để có thể giải quyết mọi vấn đề. Vậy nếu có người đã chỉ ra cho bạn, mà bạn vẫn mắc sai lầm, lỗi đó là ở bạn.
Thôi, gạt qua một bên câu chuyên muôn thuở ở trên, quay lại vấn đề chính. Nếu đang sử dụng một IDE bất kỳ, chắc chắn bạn đã từng dạo qua một của sổ tùy chọn có tên Build Configurations hay Project Configurations. Đây chính là nơi bạn đưa ra quyết định cho mục đích sử dụng của tệp tin chương trình sau biên dịch. Mục đích ở đây không phải là hướng dẫn cách cài đặt hay sử dụng. Mục đích ấy có liên quan đến nghiệp vụ của một lập trình viên, cụ thể là vấn để quản lý phiên bản. Dành cho những bạn chưa biết, một người lập trình khi tham gia phát triển dự án chắc chắn sẽ rơi vào 2 hoàn cảnh sau. Thứ nhất, quá trình phát triển đang diễn ra, hoạt động kiểm thử tích cực, suốt ngày lo toan tìm lỗi, fix bug. Thứ hai, hoạt động kiểm thử đã đi vào giai đoạn kết thúc, quá trình chuyển giao cho khách hàng bắt đầu.
Ở hoàn cảnh đầu tiên, chúng ta hay gặp nó ở các pha đầu trong vòng đời phát triển sản phẩm, lỗi còn nhiều, fix còn dài. Lập trình viên lúc này sẽ chọn cấu hình debug để build chương trình với khả năng gỡ lỗi thuận tiện hết mức có thể. Lúc đó, cấu hình debug sẽ giúp chèn các dấu hiệu chỉ thị vào chương trình. Các dấu hiệu này giúp debugger nắm bắt được tốt hơn các hoạt động của phần mềm, nhưng cũng làm gia tăng kích thước của tệp tinh sản phẩm.
Hoàn cảnh sau đó là khi đã hoàn thiện, chương trình sẽ được chuyển giao cho khách hàng. Mọi người sẽ muốn tối thiểu hoá kích thước của chương trình. Lập trình viên sẽ thiết lập tùy chọn release, chương trình sẽ được build mà không kèm theo (hoặc hạn chế) các dấu hiệu chỉ thị. Bớt chúng đi, kích thước mã nguồn sẽ giảm kha khá. Hãy so sánh 2 hình ảnh dưới đây:
Bạn có thể thấy rõ sự khác biệt. Cùng một mã nguồn, trong tệp cấu hình của ảnh phía bên trái, dòng CXXFLAGS : -g -ggdb -gdwarf-2 –coverage -pg là các tùy chọn thêm của g++ phục vụ mục đích gỡ lỗi và kiểm thử khi macro DEBUG = 1. Chương trình build hoàn tất với kích thước 36.5 kB. Ảnh bên tay phải, DEBUG = 0, trong CXXFLAGS không có các tùy chọn debug và chương trình build hoàn tất với kích thước 8.8 kB.
Để hỗ trợ cho các hoạt động này, đây là lý do vì sao các IDE thường có 2 thiết lập cấu hình sẵn là debug và release, cũng như chia thư mục đầu ra theo 2 cấu hình này để quản lý tốt hơn.
Nếu bạn không sử dụng IDE, hoặc IDE đó không hỗ trợ sẵn các cấu hình build, bạn có thể tự xây dựng cấu hình build cho riêng mình. Lưu ý, cấu hình build sẽ không phụ thuộc vào IDE mà phụ thuộc vào compiler mà IDE đang sử dụng. Ví dụ đơn giản: trên Linux, Eclipse và QT Creator là 2 IDE khác nhau, nhưng theo cài đặt mặc định thì chúng đều sử dụng GCC để biên dịch chương trình C/C++. Vậy thì cấu hình build ở đây sẽ phải tuân theo các tùy chọn mà GCC cung cấp cho ngôn ngữ lập trình được sử dụng để viết mã nguồn. Hiện tại, để phục vụ mục đích học tập cũng lên blog lảm nhảm, mình thường sử dụng 2 bộ công cụ biên dịch là GCC và CLang. Các tùy chọn tối ưu cho gỡ lỗi của GCC, các bạn tham khảo chi tiết tại đây. Đối với CLang, chi tiết tại đây.
Các bạn cũng cần lưu ý, khi sử dụng các tùy chọn debug trên trình biên dịch, đồng thời cũng nên tắt các tùy chọn tối ưu mã nguồn (Optimization Options). Điều này sẽ giảm kích thước tệp thực thi. Nghe hơi ngược đời, đáng lẽ là tăng mức tối ưu hóa thì kích thước phải giảm chứ? Cái đó đúng nếu bạn sử dụng khi release. Nhưng nếu đã cấu hình debug, việc tối ưu càng nhiều sẽ làm cho quá trình chèn symbol phức tạp hơn. Trong khi tối ưu không làm giảm nhiều dung lượng, việc chèn nhiều symbol hơn sau khi tối ưu sẽ làm kích thước chương trình tăng lên. Với GCC các bạn đưa vào tùy chọn -O0, hoặc nếu vẫn muốn tối ưu bạn có thể chọn -Og. Trên CLang, các bạn vẫn có thể sử dụng -O0, song không có tùy chọn -Og.
4. Tóm lược
Hi vọng phần trình bày ở trên đã giúp các bạn nắm được phần nào cách thức sử dụng GDB cũng như làm quen với giao diện gỡ lỗi trên Eclipse. Dù chọn gỡ lỗi trên giao diện đồ họa hay dòng lệnh, mục tiêu cuối cùng của chúng ta vẫn là giúp sản phẩm cuối cùng trở nên hoàn thiện, trải nghiệm tốt hơn và ít lỗi hơn. Cũng cần phải lưu ý một vài vẫn đề liên quan đến cấu hình biên dịch để tăng cường trải nghiệm gỡ lỗi. Và cuối cùng, dù cho giao diện đồ họa rất trực quan, dễ thao tác, nhưng sức mạnh của GDB và text ui nằm ở khả năng tự động hóa và tái sử dụng kịch bản kiểm tra. Hãy tận dụng tốt thế mạnh của cả hai.
Tạm biệt.
Chia sẻ:
- Tweet
Từ khóa » Debug Bằng Gdb
-
Hướng Dẫn Debug Chương Trình Với GDB - Teknikal-Notes
-
GDB Dòng Lệnh Cơ Bản (03 - Write To Read It Later
-
Bắt đầu: Hướng Dẫn Cho Người Mới Bắt đầu Cách Sử Dụng GDB
-
Cách Chạy Debug Bằng GDB Trên OS Ubuntu
-
Cách Dùng Gdb: Gỡ Lỗi Chương Trình C Hoặc C ++ Trên Linux - Onlyhow
-
10 Công Cụ Debugger Linux Tốt Nhất Dành Cho Các Kỹ Sư Phần Mềm
-
Những Lệnh Hay Dùng Của Gdb - Tài Liệu Text - 123doc
-
Làm Thế Nào để Debug 1 Chương Trình C? Tác Dụng Của Việc Debug ...
-
[Tutorial] Hướng Dẫn Debug Kernel Linux Bằng Vmware Và Gdb - CTF
-
Lab-2 DEBUG CHƯƠNG TRÌNH DÙNG TRÌNH GDB - Chuẩn đầu Ra
-
Top 14 Gdb Trong Linux Là Gì
-
[PDF] Bài 9 GỠ LỖI VÀ KIỂM THỬ - Soict
-
How To Debug Using GDB
-
GDB Online Debugger | Compiler - Code, Compile, Run, Debug ...
-
[Pentest] Hướng Dẫn Debug Code C/c++ Trên Android - Vietsunshine
-
GDB - Lazytrick - Limited Size Memory Of Mind