Debug để gỡ lỗi trong Java

1. Debugging là gì?

Debug được xem như những con bọ nhỏ dẫn đến lỗi chương trình. Chương trình sẽ không chạy theo đúng ý muốn người đã lập trình ra nó. Debugging là một chương trình truy tìm những co bọ có khả năng gây nên lỗi. Có nhiều phương pháp giúp lập trình viên debug một chương trình. Bài viết này sẽ đề cập đến chúng và chia sẽ kinh nghiệm để giảm đi lỗi ít nhất có thể.

Khi máy tính thực thi một đoạn mã lệnh hay nhiều khối mã lệnh, nó chạy rất nhanh. Quá trình chạy này sẽ không thể từmg bước hay tạm dừng để ta có thể xem các giá trị của biến. Điều này gây khó khăn cho Lập trình viên biết nơi đâu có những con bọ để gỡ nó ra khỏi dòng mã nhằm giúp đoạn mã hay nhiều đoạn mã đó chạy đúng như ý muốn.

Con bọ (bug) có khả năng xuất hiện ở khắp nơi. Và nó thường khá nhỏ nên khó thấy. Đôi khi ta thấy một đoạn mã gần như hiển nhiên như 1 + 1 = 2 nhưng có thể xảy ra lỗi khi ta không thể hiểu hết các trường hợp xảy ra với nó. Chẳng hạn như là kiểu dữ liệu khác nhau hay giá trị của biến bị trống (NULL)…

Để có thể chạy chương trình truy tìm lỗi trong một đoạn mã thì các công cụ debug (debugger tool) sẽ chạy đoạn mã trên môi trường giả lập. Môi trường này giúp nó có thể quản lý được giá trị của các biến. Và nó cũng giúp cho người debug xác định các điểm dừng tạm thời để xem giá trị của nó. Thậm chí cũng có thể thay đổi giá trị của nó khi muốn nó chạy trong một số trường hợp nào đó mà ta muốn.

2. Phương pháp Debug

Để debug một chương trình ta có nhiều cách. Từ sử dụng công cụ (tool) đến sử dụng các phương thức thủ công. Mõi phương pháp điều có nhược điểm và lợi thế khác nhau do đó ta cần xem phương pháp nào thích hợp để sử dụng.

Debugging Tool là công cụ giúp Lập trình viên có thể debug một chương trình thông qua Trình soạn thảo (editor). Các công cụ ấy thường được gọi là debugger. Hầu hết các trình soạn thảo giành cho Java điều tích hợp tính năng debugging. Đây được xem là cách debug một chương trình sâu nhất. Nó giúp cho Lập trình viên có thể đi sâu vào mã nguồn để đặt các điểm dừng[1] cần thiết.

Đối với các chương trình dành cho các ứng dụng nhúng_[2] _(embeded). Lập trình viên khó sử dụng các Trình soạn thảo phổ biến để debug. Thường thì họ cần phải sử dụng các chương trình chuyên dụng cho nó. Vì do các chương trình này phụ thuộc nhiều và thiết bị phần cứng. Khi Lập trình viên phát triển ứng dụng trên nó cần tìm hiểu thêm cách debug riêng.

Printling là một cách phổ biến nhất mà tôi thường dùng. Ngôn ngữ lập trình phổ biến điều có phương thức in ra màn hình. Tôi thường dùng nó để in ra giá trị của một biến mà tôi cần kiểm tra giá trị của nó trong quá trình thực thi. Cách này nhanh hơn debugging ở trên. Chương trình chạy sẽ không dừng mà chỉ in ra ở giao diện console[3] giá trị các biến. Sau đó ta nhìn vào giá trị của chúng để đoán nguyên nhân gây nên lỗi.

Mặc dù nhanh nhưng nó sẽ làm xấu đi đoạn mã trong chương trình vì các dòng lệnh in ra. Nó cũng bị hạn chế đối với các biến có kiểu dữ liệu không thể chuyển thành văn bản để in ra màn hình. Để in ra nó trong Java thì các lớp tạo nên đối tượng cần phải ghi đè (override) phương thức toString. Java thường dùng câu lệnh System.out.prinln(String text) để in ra màn hình.

Logging một cách khác gần giống với printling. Nó vẫn dùng để in ra giá trị của biến nhưng không giới hạn ở màn hình mà nó có thể ghi lại thành tập tin hay đẩy các nội dụng văn bản đó thông qua mạng để lưu trữ trong một cơ sở dữ liệu cụ thể. Cách này xem là cách mở rộng của printling thôi. Trong Java có thư viện đang đảm nhiệm tốt chức năng này là log4j.

Rất là thuận tiện khi tôi sử dụng cách này để debug trên các môi trường triển khai mà không phải là trên máy tính cá nhân. Tôi đã sử dụng nó trong các môi trường sản phẩm (production). Nó ghi lại lịch sử thực thi mã nguồn bằng tập tin. Sau đó tôi dùng elasticsearch để đọc các thông tin và phân tích các thông tin này để rà soát khả năng lỗi của chúng.

Pair Programinghoặc Review bởi đồng nghiệplà một phương pháp dùng sức người để dự đoán khả năng xảy ra lỗi. Những Lập trình viên trẻ cần được các Lập trình viên có nhiều kinh nghiệm review mã để nâng cao kinh nghiệm của mình hơn. Đây là cách cũng rất phổ biến ở các công ty phần mềm đang sử dụng. Công ty tôi đang sử dụng upsource, một ứng dụng web giúp các Lập trình viên có thể review mã và ghi chú (comment) những dòng mã của các Lập trình viên với nhau.

3. Debug trên Trình soạn thảo

Như đã đề cập ở trên. Các Trình soạn thảo điều có các chương trình debug được tích hợp trong nó. Trong Netbean, Eclipse, IntelliJ… là những Trình soạn thảo phổ biến. Nên ở đây sẽ đề cập đến 3 trình soạn thảo này. Dưới đây là các bước cơ bản để debug một chương trình.

  • Bước 1: Viết một đoạn mã hay mở một chương trình cần debug trong Trình soạn thảo.
  • Bước 2: Đặt các breakpoint (nơi mà ta muốn chương trình tạm dừng) ở những dòng lệnh mà ta cần tạm dừng để xem giá trị của các biến cần xem xét.
  • Bước 3: Chạy chương trình ở trạng thái debug để xem giá trị của các biến. Sử dụng các phím tắt để chạy từng dòng lệnh, khối lệnh…
  • Bước 4: Trong quá trình kiểm tra giá trị các biến. Thấy giá trị đó có như ý muốn của Lập trình viên không? Nếu không thì cần đoán xem khả năng sai ở đâu để thay đổi mã sau đó chạy lại bước 1. Quá trình này sẽ được lặp lại cho đến khi thỏa mãn.

3.1. Debug trên Netbean

Netbean là một trong các trình soạn thảo phổ biến. Nó hoàn toàn miễn phí và hỗ trợ rất tốt có các Lập trình viên viết mã Java. Netbean đang thuộc top 3 trình soạn thảo giành cho Java ngoài Eclipse và IntelliJ.

Ta có đoạn mã muốn in ra màn hình hôm nay là ngày thứ mấy? Biến day sẽ chứa giá trị kiểu int từ đối tượng Date với phương thức getDay trong Java. Đây là hình ảnh minh họa cách debug một chương trình trong Netbean.


Hình 1 – Cách debug một đoạn mã chương trình trong Netbean.

Đây là bộ công cụ giúp ta thực hiện các bước trong debug.

Bảng 1 – Bảng các phím tắt và mô tả các tính năng của Debug trong Netbean.

Finish Debugger Session Ctrl+F5 Giúp kết thúc chương trình debug đang chạy.
Continue F5 Chạy đến _breakpoint_ tiếp theo nếu có. Nếu không sẽ chạy đến kết thúc chương trình.
Step Over F8 Chạy dòng lệnh kế tiếp. Chức năng này hữu dụng khi ta cần chạy từng dòng lệnh một.
Step Over Exception Shift+F8 Chức năng này giúp debug chi tiết hơn nếu trong chương trình có các biểu thức. Giá trị các biểu thức sẽ được hiện lên trong phần _watch_ của Trình soạn thảo. Nếu không có biểu thức thì nó hoàn toàn giống Step Over.
Step Into F7 Giúp ta đi sâu vào các hàm bên trong thay vì chạy qua các hàm đó rồi trả về kết quả.
Step Out Ctrl+F7 Giúp ta kết thúc nhanh phương thức mà nó đang đứng. Nếu nó còn phương thức bên ngoài thì sẽ dừng ở phương thức bên ngoài nếu không sẽ chạy xong và thoát chương trình.
Run to Cursor F4 Chạy chương trình đến cho đến con trỏ đang đứng.

Trên đây là cách debug và các tính năng để điều khiển một chương trình debug trong Netbean. Ta có thể sử dụng bằng cách nhấp vào các icon ở trên hoặc sử dụng phím tắt (nằm ở cột 2 của bảng) để debug.

3.2. Debug trên Eclipse

Ta cũng sử dụng đoạn mã trên trong Eclipse. Ta cần in ra hôm này là thức mấy trong qua new Date().getDay() trả về kiểu dữ liệu là Interger.

Chọn đoạn mã mà ta cần debug. Đặt breakpoint để báo cho chương trình debug của Eclipse tạm dừng ở đây để ta kiểm tra giá trị của các biến. Chọn menu Run ð Debug ð Java Application. Chương trình sẽ tạm dừng ở breakpoint.


Hình 2 – Mô tả giao diện khi debug một đoạn mã trong Eclipse.

Tương tự Netbean, Eclipse cũng có các công cụ hỗ trợ quá trình debug.

Bảng 2 – Bảng các phím tắt và mô tả tính năng được Eclipse hỗ trợ trong Debugger.

Step Into F5 Chạy sâu vào phương thức nếu dòng lệnh đang chạy debug là một phương thức.
Step Over F6 Chạy từng dòng lệnh của phương thức hiện tại đang debug.
Use Step Filter Shift+F5 Giúp ta bỏ qua những kiểu dữ liệu mà ta không muốn xem. Chẳng hạng như java.lang.Object.
Next Annotation Ctrl+.  
Previous Annotation Ctrl+,  
Terminate Ctrl+F2 Dừng chương trình đang debug.

Để thấy được hướng dẫn cách debug trong Eclipse chúng ta có thể tham khảo ở hướng dẫn này (1) để có thêm chi tiết hơn.

3.3. Debug trên IntelliJ

IntelliJ là Trình soạn thảo tốt được nhiều Lập trình viên Java chọn là Trình soạn thảo chính cho họ tron quá trình lập trình Java. IntelliJ đang cung cấp người dùng với phiên bản miễn phíphiên bản trả phí. Với phiên bản miễn phí sử dụng cho Java thì ta không thể tích hợp các webserver để chạy như Glassfish, Tomcat.

Cũng giống như Netbean và Eclipse, IntelliJ cũng có những công cụ giúp ta điều khiển quá trình debug. Công cụ được thể hiện ở.


Hình 3 – Các công cụ điều khiển trong quá trình Debug trong IntelliJ

Dưới đây là bảng các tính năng thường được sử dụng trong quá trình debug một chương trình trong IntelliJ.

Bảng 3 – Bảng phím tắt và các mô tả các tính năng của debugger trong IntelliJ.

Step Over F8 Chạy từng dòng lệnh trong trong phương thức hiện tại.
Step Into F7 Đi sâu vào bên trong phương thức thay vì chỉ chạy phương thức đó rồi nhận về kết quả.
Force Step Into Alt+Shift+F7 Chạy vào thân của các phương thức đang được gọi trong phương thức đang thực thi.
Step Out Shift+F8 Thoát khỏi phương thức hiện tại bằng cách thực thi hết tất cả các dòng lệnh của nó.
Run to Cursor Alt+F9 Chạy các dòng lệnh đến khi gặp con trỏ sẽ tạm dừng.
Resume Program F9 Chạy chương trình đến _breakpoint_ tiếp theo. Nếu không còn _breakpoint_ chương trình sẽ chạy đến khi thoát.
Stop Ctrl+F2 Dừng chương trình debug ở thời điểm hiện tại.
View Breakpoint Ctrl+Shift+F8 Hiển thị danh sách các _breakpoint_ trong chương trình debug hiện tại.
Mute Breakpoint   Tắt các _breakpoint_ hiện tại.
Return Ctrl+F5 Dừng chương trình hiện tại và thực thi lại chương trình.

Trên là giới thiệu cách debug cơ bản trong Netbean, Eclipse và IntelliJ. Khi ta đi sâu vào sử dụng sẽ hiểu hơn. Chúng tôi nghĩ sẽ không cần đi sâu quá chi tiết. Vì những phần còn lại dễ dàng biết trong quá trình sử dụng. Nếu tôi trình bày quá chi tiết sẽ mất nhiều thời gian không cần thiết cho bạn lẫn tôi.

4. Remote Debug – Debug từ xa

Debug từ xa (remote debug) là một giải pháp giúp ta có thể debug một chương trình trong trường hợp máy cá nhân không thể triển khai (deploy) chương trình hoặc quá trình triển khai chương trình đồi hỏi các bước phức tạp. Nó cũng rất hữu dụng khi có những co bọ chỉ xảy ra trên một môi trường mà máy tính cá nhân không thể tái hiện được. Chẳng hạng như xảy ra trên môi trường staging, testing và thỉnh thoảng trên cả production.

Khi sử dụng cách debug này. Công cụ debug (debug tool) cũng đặt các breakpoint. Khi chạy chương trình thì quá trình thì các bản tin quá trình gỡ lỗi (message debug) sẽ được JVM trên môi trường đang chạy đẩy về máy cá nhân trong qua socket trên một port nào đó do Lập trình viên định nghĩa.

Cách debug này ta thường thấy ở các Lập trình viên Android. Họ chạy chương trình trên thiết bị Android (device android) sau đó thiết lập debug đến máy tính cá nhân của họ. Khi thực hiện các hành động trong chương trình chạy trong Android. Thiết bị sẽ đẩy thông tin debug về trình soạn thảo. Trình soạn thảo sẽ thực hiện các lệnh dừng thông qua các breakpoint giúp ta debug chương trình.

Remote Debug không đơn giản chỉ chạy chế độ debug trên Trình soạn thảo là được. Nó đồi hỏi ta khai báo cho JVM ở môi trường đạng chạy. Báo cho JMV biết nó cần phải chạy ở chế độ debug và đẩy thông tin từ quá trình debug về cho máy tính cá nhân nào thông qua dòng thiết lập dòng lệnh này trong JVM.

java -Xdebug -Xrunjdwp:transport=dt_socket,suspend=n,\n
    server=120.1212.212,address=9998 -jar MYAPP.jar

Trên là dòng lệnh để thiết lập một biến debug trong JVM. Đây là một vài điểm lưu ý trong dòng lệnh:

  • _MYAPP.jar_ là tên của gói ứng dụng mà ta chạy ở chế độ debug.
  • _address__server_ giúp ta khai báo IP và Port để JVM đẩy về khi chạy trong chế độ debug.
  • _-Xrunjdwp: transport=dt_socket_ chỉ ra cho JVM chạy ở chế độ Jave Debug Wire Protocol với transport socket.

Sau khi đã chạy dòng lệnh trên trong môi trường triển khai sản phẩm (deploy). Thì trong trình soạn thảo cần phải cấu hình chạy debug với chế độ remote debug.


Hình 4 – Cấu hình kết nối remote debug thông qua Attach Debugger

Trong Netbean. Vào Debug ð Attache Debugger… để hiện được hộp thoại (Hình 4 – Cấu hình kết nối remote debug thông qua Attach Debugger…) để ta tiến hành kết nối với môi trường đang chạy debug sản phẩm.

5. Để Debug được tốt hơn

Nếu ta chịu khó debug chi tiết thì sẽ biết được chương trình đang bị lỗi gì. Nhưng nếu ta chú ý một vài điểm trong quá trình viết mã thì quá trình debug sẽ nhanh hơn, dể phát hiện lỗi xảy ra hơn. Sau đây là những điểm cần lưu ý khi viết mã.

Thêm ghi chú trong quá trình viết mã. Khi ta chạy chương trình debug ngoài xem các giá trị của biến cần ta cần xem thêm các ghi chú (comment) để hiểu hơn nghiệp vụ xử lý trong chương trình. Từ đó có thể đoán được khả năng xảy ra lỗi trong chương trình nhanh hơn.

Đặt tên biến, phương thức, lớp… rõ ràng hơn. Nếu mã nguồn tuân thủ tốt code conventation thì thật dể dàng để ta đoán được kết quả của nó. Hiển nhiên ta không nên đoán được chính xác khi không thực thi mã nguồn. Nhưng nếu biến đó có tên là student name nhưng trả về lên là teach name thì dể dàng khoanh vùng lỗi đúng không?

Đọc cẩn thận các dòng thông báo lỗi khi chạy đoạn mã. Thỉnh thoảng tôi không quan tâm nhiều đến thông báo của các ngoại lệ (exception). Tôi tốn khá nhiều thời gian để chạy lại, tái hiện và đoán lỗi. Sau khi vẫn không thể nhận ra nguyên nhân. Tôi phải chấp nhận giải pháp cuối cùng là ngồi đọc lại từng dòng báo lỗi mong tìm ra một điểm nào đó để sửa lỗi với tư tưởng hên xui. Thật sự bất ngời khi những dòng báo lỗi đã thông báo cho ta nguyên nhân là gì rồi. Nếu tôi làm nó ngay từ đâu thì nó sẽ nhanh gấp vài chục lần. Do đó nên cẩn thận đọc các dòng báo lỗi từ Java là một giải pháp tiết kiệm nhiều thời gian.

Phân vùng lỗi thông qua các breakpoint. Nếu ta chạy từng dòng lệnh một cách chi tiết sẽ tốn nhiều thời gian. Thường thì tôi đoán lỗi khả năng xảy ra ở đâu. Sau đó đặt các breakpoint ở đó. Chạy debug để loại bỏ các dự đoán khả năng lỗi. Rồi phân vùng khả năng lỗi để tiến hành debug sâu dưới dạng step into để gỡ lỗi. Theo kinh nghiệm của tôi thì làm cách này tôi thấy phát hiện lỗi được nhanh hơn. Nhưng nhanh hay chậm cũng phụ thuộc kinh nghiệm. Nếu kinh nghiệm tốt sẽ đoán được phân vùng lỗi nhanh và đỡ tốn thời gian hơn.

6. Tóm lược

Debug là phương pháp giúp Lập trình viên tìm ra những con bọ (bug) trong chương trình. Chương trình dùng để debug được gọi là Debugger. Quá trình debug được gọi là Debugging.

Debugging thực chất là quá trình để Java thực thi mã nguồn trên một môi trường giả lập để nó có thể tạm dừng thông qua các breakpoint. Giá trị của các biến cũng sẽ được kiểm tra thông qua giao diện watch của trình soạn thảo.

Có nhiều cách debug, ta có thể sử dụng bất cứ cách nào miễn sao ta thấy được giá trị của một biến tại một thời điểm nào đó mà ta muốn. Hiện có 4 cách debug chính thường được sử dụng: Debug Tools, Printling, Logging và Pair Programming – Review Code

Debug Tool điều được tích hợp trong các trình soạn thảo phổ biến. Netbean, Eclipse, IntelliJ là 3 trình soạn thảo được tôi giới thiệu trong bài viết này. Nhưng phần giới thiệu này sẽ không đề cập chi tiết về cách sử dụng mà chỉ nhằm giúp bạn có cái nhìn tổng quan khi bắt đầu. Hướng dẫn chi tiết thì có thể xem các đường liên kết tôi có kèm theo hoặc bạn sẽ nhận biết ngay trong quá trình sử dụng. Vì nó đơn gian đến mức không cần hướng dẫn cũng biết.

Đối với vài chương trình đặc trưng như phụ thuộc phần cứng hay phụ thuộc môi trường dẫn đến khó khăn để triển khai trên máy tính cá nhân. Remote Debug là một giải pháp giúp ta debug. Giải phải đồi hỏi phải được cấu hình để các máy tính có thể giao tiếp nhau thông qua socket.

Đây là bài viết giới thiệu về công cụ debug giúp Lập trình viên gỡ những con bọ của mình một các nhanh chóng nhằm nâng cao hiệu quả làm việc. Hi vọng nó sẽ giúp ích cho các bạn lập trình viên mới vào ghề.

Bạn có thể tham khảo mã nguồn ở
https://github.com/tiennguyen2009/java-basic/tree/master/src/com/tiennguyen/chuong_4/debug

Tài liệu tham khảo

1. GP Coder. Hướng dẫn Debug Code trong Eclipse. https://gpcoder.com/3621-huong-dan-debug-code-trong-eclipse/. [Online] 04 2019.

2. Techblog. Giới thiệu về Debug. https://techblog.vn/gioi-thieu-ve-debug. [Online] 04 2019.

3. SAGAR ARORA. Top 10 Java Debugging Tips. https://stackify.com/java-debugging-tips/. [Online] 04 2019.

4. Lê, Thạch. Remote Debug trong Java. http://thachleblog.com/remote-debug-trong-java/. [Online] 04 2019.

5. MKyong. Log4j Hello World Example. https://www.mkyong.com/logging/log4j-hello-world-example/. [Online]

6. Eclipse Help. Debug View. https://help.eclipse.org/neon/index.jsp?topic=%2Forg.eclipse.jdt.doc.user%2Freference%2Fviews%2Fdebug%2Fref-usestepfilters.htm. [Online] 04 2019.


[1] Điểm dừng là nơi đặt các điểm mà ta cần chương trình chạy tạm dừng để xem giá trị các biến hoặc kiểm tra các ngoại lệ cần thiết cho quá trình gỡ lỗi.

[2] Ứng dụng Nhúng là dạng phần mềm sử dụng trong các phần cứng đặc trưng và thường bị giới hạn nhỏ về bộ nhớ và vi xử lý.

[3] Console là một giao diện nhập/ xuất dạng văn bản đơn thuần sử dụng trong quá trình phát triển phần mềm dành cho các Lập trình viên.

Trả lời

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 *