Các kĩ thuật Debug chương trình nhúng với Keil C

debug

Trên con đường thành công thì có rất nhiều sỏi đá, còn trên con đường trở thành lập trình viên thì có rất nhiều Bug. Debug là thước đo võ công của một lập trình viên, càng Pro thì debug càng giỏi. Trong bài này chúng ta sẽ tìm hiểu Bug và cách Debug nhé.

Kĩ năng nâng cao trong Serie Học lập trình từ A tới Z

Bug là gì?

Bug là những lỗi logic hoặc lỗi hệ thống sinh ra khi chương trình chạy, khiến nó đi sai lệch so với ý đồ lập trình ban đầu, hoặc làm treo, chậm hệ thống… Bug cũng giống như tên của nó, là một con bọ sẵn sàng gặm nhấm, phá hủy hệ thống của bạn nếu không fix.

via GIPHY

Bug luôn tiềm ẩn ở mọi nơi. Ta không thể lường trước được mọi tình huống có thể xảy ra, mà chỉ có thể cố gắng giảm nó xuống mức thấp nhất, tùy vào khả năng của mình tại thời điểm phát triển và bảo trì ứng dụng.

Đôi khi một chương trình có quá nhiều Bug mà không thể fix kịp, lập trình viên hay nói:

Đó không phải là Bug, đó là tính năng

Trong thực tế, cũng có nhiều sản phẩm mà nhà sản xuất cố tình giữ lại Bug và coi đó là 1 tính năng thú vị, thường thấy nhất trong Game. Nhưng đa số Bug thì phải tìm và diệt, quá trình đó gọi là Debug.

Debug là gì?

Nói đơn giản, Debug là quá trình tìm kiếm và tiêu diệt Bug. Công việc Debug chiếm phần lớn thời gian khi tạo ra 1 sản phẩm. Thực tế code ra 1 sản phẩm không mất quá nhiều thời gian, thế nhưng tìm diệt hết các Bug để chương trình chạy ổn định thì lại không hề đơn giản chút nào.

Debug Int feature

Bug len lỏi trong các câu lệnh của bạn, đôi khi chỉ là sai 1 dấu toán tử, sai 1 chữ trong tên biến… nhưng hậu quả để lại thì khôn lường. Việc tìm kiếm chúng trong hàng ngàn, hoặc chục ngàn dòng lệnh không hề đơn giản chút nào.
Vậy nên mới cần các kĩ thuật Debug. Trong phạm vi bài này, chúng ta sẽ nói tới việc Debug chương trình nhúng sử dụng IDE Keil C.

Các kĩ thuật Debug chương trình nhúng

Trước hết, để hiểu rõ hơn về việc debug thì các bạn nên hiểu 1 chương trình nhúng hoạt động như thế nào, trình biên dịch hoạt động như thế nào. Bạn có thể tham khảo bài viết: Quá trình biên dịch của 1 chương trình C/C++.

Trong chương trình C, các câu lệnh trong 1 hàm được thực thi tuần tự từ trên xuống (Top Down): câu lệnh bên trên được thực hiện trước, sau đó đến các câu lệnh bên dưới, trừ khi gặp lệnh rẽ nhánh, vòng lặp, lời gọi hàm hoặc ngắt.

Hãy cùng tham khảo các kĩ thuật Debug này nhé.

Các kĩ thuật dùng Debug Tools

Khi nhấn nút Debug (Start/Stop Debug Session, phím tắt Ctrl+F5) trên phần mềm, chúng ta sẽ được chuyển tới giao diện Debug.

debug

 

Giao diện bao gồm những thành phần chính sau:

debug interface

 

Kĩ thuật 1: Break Point và Run Step by Step

Chương trình luôn chạy liên tục với tốc độ cực nhanh, muốn dừng lại thì ta phải chọn 1 điểm dừng cho nó. Khi chương trình dừng lại, giá trị của tất cả các thanh ghi, biến, bộ nhớ… được giữ nguyên tại thời điểm đó để chúng ta quan sát.

Cách sử dụng: click vào lề trái, ngay cạnh số thứ tự dòng code, hiện một hình tròn màu đỏ là được.

break point

Sau khi đặt Break Point, chúng ta nhấn nút Debug để vào chế độ Debug. Sử dụng công cụ Run (F5), khi chương trình chạy đến Break Point sẽ tự động dừng lại.

Run

Ngoài ra các bạn có thể sử dụng các chế độ Step (F11, đi vào trong hàm), Step Over (F10, chạy qua cả hàm), Step Out (Ctrl+F11, chạy nốt hàm hiện tại rồi thoát ra) để chạy từng bước cho chương trình, giúp bắt lỗi tại những đoạn code nghi ngờ.

Kĩ thuật này phối hợp rất tốt với các kĩ thuật Watch sau đây.

Kĩ thuật 2: Watch Window

Chương trình hoạt động dẫn tới sự thay đổi về biến, bộ nhớ, thanh ghi… Để nhìn thấy sự thay đổi này, xem chúng có thay đổi đúng theo logic không, ta sử dụng Watch.

Watch Memory

Chuyển qua tab Memory, sau đó gõ địa chỉ cần xem. Với công cụ này chúng ta có thể xem được trực tiếp giá trị của bộ nhớ bên trong MCU.

watch

Watch Register

Xem thanh ghi cũng rất quan trọng trong quá trình debug. Các bạn phải nắm rõ cách ngoại vi hoạt động, rồi đối chiếu với sự thay đổi của thanh ghi mới biết chúng có hoạt động đúng hay không.

register
Chọn Thanh Ghi
register 2
Cửa sổ xem thanh ghi

Watch Variable

Với các biến, ta sử dụng cửa sổ Watch, sau đó Add biến cần xem vào.

watch variable

Lưu ý: Biến toàn cục (nằm cố định trong RAM) thì xem được ở mọi lúc, kể cả khi chương trình đang chạy. Biến cục bộ chỉ xem được khi chương trình đang dừng bên trong hàm chứa nó (cửa sổ Call Stack + Locals). Nếu Watch báo biến cục bộ “not in scope” hoặc giá trị nhảy lung tung, hãy kiểm tra Optimization trong Options for Target → C/C++, để mức -O0 khi debug vì trình biên dịch có thể đã tối ưu biến đó vào thanh ghi.

Các kĩ thuật Debug khác

Kĩ thuật 1: Tạo lưu đồ giải thuật

Muốn chương trình ít Bug thì đừng tạo ra nhiều Bug ngay từ đầu.

Hãy rõ ràng ngay từ khâu phát triển chương trình bằng các lưu đồ giải thuật và các thiết kế hệ thống như State Machine… Việc không có tài liệu thiết kế hệ thống sẽ tạo ra Bug ngay từ khâu thiết kế. Và các Bug này thường siêu to khổng lồ, có thể bạn phải đập cả hệ thống đi và xây lại đó.

flowchart example

Kĩ thuật 2: In log hay Logging (nhật kí chương trình)

Bạn có thường thấy, khi cài đặt chương trình trên Windows thường có các dòng hiện lên: Copying file…, Install success, Install fail… Các dòng text này được in ra liên tục giúp bạn biết được chương trình đã cài đặt tới đâu rồi.

Trong lập trình, đó gọi là Log hay kĩ thuật Logging.

Trong hoặc sau khi thực hiện một tác vụ bất kì, ta có thể in ra 1 đoạn văn bản thông báo (với STM32 thường in qua UART). Nếu chương trình bị treo, bạn chỉ cần tìm tới nơi in ra dòng log cuối cùng và tìm kiếm xung quanh nó.

Bạn có thể sử dụng chỉ thị tiền xử lý để bỏ đi các đoạn log khi không cần thiết nữa.

Ví dụ:

#define DEBUG_MOD 1
#ifdef DEBUG_MOD
   printf(" In Log tại đây\r\n");
#endif

 

Trong ví dụ này, khi bạn bỏ dòng #define DEBUG_MOD 1 thì tất cả các dòng lệnh từ #ifdef tới #endif đều bị loại bỏ ngay ở bước tiền xử lý (không làm nặng chương trình). Cấu trúc #ifdef và #endif có thể dùng ở nhiều chỗ khác nhau trong chương trình mà chỉ cần define 1 lần.

Lưu ý: #ifdef chỉ kiểm tra macro có được define hay không, không quan tâm giá trị. Sửa thành #define DEBUG_MOD 0 thì log vẫn in ra như thường. Muốn bật tắt bằng 0/1 thì phải dùng #if DEBUG_MOD, còn dùng #ifdef thì phải xóa (hoặc comment) hẳn dòng define.

Kĩ thuật 3: Find to Define

Tìm kiếm nơi định nghĩa: khi bạn sử dụng 1 hàm hoặc một biến bất kì, đôi khi bạn quên nó dùng như thế nào hoặc biến đó có giá trị ra sao. Trong Keil C, click chuột phải vào tên hàm/biến và chọn Go To Definition (phím F12) để nhảy trực tiếp tới nơi định nghĩa nó.

Việc này giảm tối đa thời gian bạn đi tìm trong cả ngàn dòng code và vài chục file khác nhau trong 1 chương trình.

Đây là một công cụ mình rất hay sử dụng, cũng là công cụ mà Arduino IDE bản 1.x không có (thế nên mình không thích dùng Arduino IDE, dù bản 2.x sau này đã bổ sung tính năng này).

Find to Define

Kĩ thuật 4: Viết Clean Code

Nói cho cùng, bạn không thể nào Debug 1 mình mãi được. Khi code sạch (Clean Code), người khác mới có thể hiểu được bạn đang viết gì và giúp bạn được. Thử tưởng tượng bạn đặt các biến toàn là a, b, c, d rồi tự ngầm hiểu nó dùng làm gì, thì người sau đọc code của bạn có hiểu được không?

Vậy nên hãy giữ cho code của mình sạch sẽ, để không có bọ bám vào nhé.

Kĩ thuật 5: Vác code đi hỏi cao nhân

Đôi khi mò mẫm trong bóng tối quá lâu cũng sẽ làm bạn nản lòng. Những thứ bạn thấy thật khó khăn lại là những thứ cực kì đơn giản, mà chỉ có những người giàu kinh nghiệm mới nhìn ra được.

Chả thế mà các cao nhân đều là các bậc tiền bối, có tuổi đời và tuổi nghề lâu năm. Hãy cố gắng đi tìm người Mentor cho mình, đảm bảo việc học và làm việc sẽ rất suôn sẻ đó.

Nhưng lưu ý: kĩ thuật này chỉ nên sử dụng khi bạn đã làm hết các kĩ thuật ở trên mà không ra được. Chả ai muốn bị làm phiền quá nhiều bởi những câu hỏi hiển nhiên cả. Hãy nhớ Google trước khi đi hỏi nhé!

Kết

Debug là một quá trình gian khổ, mệt mỏi. Thế nhưng kĩ năng nào cũng cần rèn luyện, hãy chăm chỉ tập luyện, rồi một ngày nào đó bạn cũng sẽ trở thành 1 Dũng Sĩ Diệt Bug. Cám ơn bạn đã đón đọc, cùng vào hội Anh Em Nghiện Lập Trình để cùng trao đổi nhé.

4.8/5 - (5 bình chọn)

5 những suy nghĩ trên “Các kĩ thuật Debug chương trình nhúng với Keil C”

  1. Hieu nói:

    Trong khi chương trình đang running ở chế độ debug thì có thể stop lại ở một breakpoint mà mình click vào không ? Mình sử dụng IAR thấy việc debug rất ngon lành, đang debug mà muốn stop lại dòng mong muốn thì chỉ cần click set breakpoint ở dòng đó, nhưng khi dùng Keil C thì không được.

  2. Nguyễn Văn Anh nói:

    công việc khô khan, đọc mấy bài như này của a có đoạn kết làm e sực mỉm cười. Hóa ra cũng có chút thú vị trong chút nhàm chán “rồi một ngày nào đó bạn cũng sẽ trở thành 1 Dũng Sĩ Diệt Bug”. cảm ơn những bài viết chia sẻ hữu ích của a. Chúc a 1 ngày tốt lành
    

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *