Tài trợ bài viết này và giới thiệu dịch vụ, sản phẩm, thương hiệu, nhu cầu tuyển dụng của doanh nghiệp đến với cộng đồng.
La Kiến Vinh Tư duy Debug, nếu bạn đang bão hòa về mặt tư duy debug, bài viết này sẽ giúp bạn mở rộng hơn và giúp bạn có được các tư duy debug tốt hơn. Nếu không có một kinh nghiệm dày dặn và một tâm hồn rộng mở, thì khi được hỏi về Debug, bạn chỉ có thể nói F5.
Nội dung bài viết

Giới thiệu

Programmers chính là các công cụ sinh ra Bugs nhiều nhất vì họ lập trình. Và chính vì vậy, công cụ tốt nhất để gỡ bỏ bớt Bugs cũng chính là các Programmers. Bài viết này nhằm chia sẻ các kinh nghiệm cá nhân của tôi nhằm giải quyết nhiều vấn đề tìm và khắc phục lỗi trong lập trình ở mức cơ bản.

Tiền đề bài viết

Sau nhiều năm làm việc trên nhiều dự án liên quan đến Web, Mobile, Game, 3D và một số Script, tôi tích lũy được nhiều kinh nghiệm để tìm và khắc phục các Bugs (lỗi) trong lúc lập trình. Nhận thấy cần một nơi lưu trữ lại và cập nhật thường xuyên nhằm chia sẻ cho các bạn nên tôi hướng đến việc viết lại các trải nghiệm này.

Bug là gì?

Bugs là các con bọ, bọ này ám chỉ các lỗi xảy ra trong logic, hay bất kỳ vấn đề gì gây ra việc làm cho ứng dụng không thực thi được hoặc thực thi sai.

Bugs luôn tiềm ẩn ở mọi nơi, và 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 làm giảm nó đến mức thấp nhất có thể tùy vào khả năng của ta tại thời điểm phát triển và bảo trì ứng dụng.

Ý nghĩa của Debug

Debug là quá trình tìm kiếm ra lỗi hay nguyên nhân gây ra lỗi (bug ở đâu) để có hướng sửa lỗi (fix bug). Do đó, các phương pháp để tìm kiếm bug của ứng dụng thì có rất nhiều cách và nó phụ thuộc vào kinh nghiệm của người debug.

Debuger có trong Visual Studio

Trong thời gian dài phỏng vấn, tôi nhận được câu trả lời ở các bạn chưa có kinh nghiệm thường là "Trong Visual Studio, đặt breakpoint, nhấn F5 là... nhấn F10 là..." và điều đó cũng không hề sai.

Visual Studio có thể nói là đúng với tên gọi của nó, cái vẻ đẹp và khá hoàn thiện, dễ chịu cho người lập trình viên khi làm việc với nó, đó có thể xem là một điểm cộng cho bộ công cụ này.

Các kỹ thuật Debug

Lời chia sẻ

Như đã đề cập ở phần ý nghĩa của debug, đó là các phương pháp mà chúng ta có thể nghĩ ra được (và không nên giới hạn) để có thể xác định được bug ở đâu. Với ý tưởng như vậy, chúng ta sẽ xem Debugger trong bộ Visual Studio là một trong các phương pháp (có thể nó trực quan và hữu hiệu) chứ không phải là phương pháp duy nhất.

Dựa trên kinh nghiệm bản thân, tôi sẽ nêu ra các trường hợp cụ thể (càng nhiều càng tốt và sẽ cập nhật thường xuyên) để các bạn có thể vượt qua sự cản trở của bản thân trong tư duy với vấn đề này.

Luôn nhớ rằng: Debug là quá trình xác định và "tiêu diệt" bug chứ không phải là F5 và đặt breakpoint. Nguyên lý của Debug là cố gắng để "phát ra một tín hiệu" từ việc lập trình hay chương trình đang thực thi tại gần điểm xảy ra bug và dự đoán.

Phát ra một tín hiệu

[1] Tạo ra lỗi ở mã nguồn

Trong quá trình lập trình, có nhiều lập trình viên từng gặp trường hợp như đã sửa chương trình rất nhiều nhưng vẫn không thấy thay đổi khi tiến hành build và run. Có rất nhiều lý do, trong đó, lý do có thể gặp là: quá cẩn thận, hàm đó không được gọi, chương trình đã có đoạn làm ngược lại hoặc cập nhật lại quá trình đó.

Quá cẩn thận: tôi từng gặp nhiều lập trình viên đã clone (sao chép) project của họ ra thành nhiều phiên bản khác nhau, do đó, họ mở một file mã bằng nhiều công cụ khác nhau như Editor của Visual Studio, Notepad++, Notepad, Beyond Compare và tự làm khổ mình. Và lý do là, bạn đang sửa một file của bản clone khác, không phải bản chính mà bạn đang làm việc. Phương pháp đơn giản để kiểm tra xem có phải đang sửa không đúng file hay không bằng cách gõ dòng lệnh "huyền bí" sau vào file đó và tiến hành build thử, đoạn mã đó như sau kadjfksins.stdio.vnldjaflstdio.vnkjadfj, nếu có thể build được, chứng tỏ đó không phải là file trong project chính.

Hàm đó không được gọi: có thể vì lý do hàm được override hoặc overload, cũng là lý do làm cho bản thân ta nhầm lẫn giữa việc gọi các hàm. Do đó, có thể suy luận xem, có thể ta đang sửa sai các hàm không được gọi hay không? Cũng có thể đó là deadcode mà bạn không biết. Nếu bạn làm việc với C/C++, bạn có thể thử in-line một số ASM vào để bắt buộc trình biên dịch phải có đoạn mã đó.

__asm
{
	int 3
}

Đoạn mã đã bị cập nhật lại: khi làm việc với CSS, tôi thường gặp vấn đề sau, tuy là dễ giải quyết, nhưng vẫn gặp thường xuyên

wrapper_stdio_content
{
	width:100%;
	min-height:512px;
	float:right;
	padding-right:10px; /* Đoạn sẽ bị cập nhật lại */
	margin:0;
	font-size:25px;
	color:#ffbb33;
	padding:0 15px 5px 5px /* Đoạn cập nhật lại padding-right là 15px */
}

[2] Các phương pháp log

Để có một tín hiệu, ta phải tận dụng tất cả những cách thức mà chương trình cung cấp cho ta, với tôi, các "hàm huyền thoại" dùng cho debug có liên quan tới việc xuất ra màn hình các thông tin cần thiết mà tôi có thể liệt kê:

  • Javascript: alert("Debug Infor");
  • C/C++ Windows Console Application: printf("Debug Infor");
  • PHP: echo "Debug Infor";
  • Tùy vào nền tảng cụ thể mà ta đang làm việc, sẽ có các hàm hoặc cách thức tương ứng, nhưng chỉ cần có một tín hiệu nào dù ít hay nhiều, ta cũng nên bám vào.

Khi làm việc với Game, tôi thường vẽ hẳn các thông tin lên màn hình game như thông tin về FPS, thông tin về Memory đang sử dụng.

Khi sử dụng phương pháp này, ta cần có chiến lược cụ thể vì mỗi lần build và deploy ứng dụng có thể mất thời gian rất nhiều, nếu đặt các "trigger" (các hàm đó) tại nhiều điểm trong ứng dụng thì tốn thời gian và rối rắm, nhất là tốn thời gian để gỡ bỏ sau này. Ta có thể áp dụng chiến lược nhị phân tìm kiếm, tức là đặt các trigger này ở điểm đầu chương trình, điểm giữa, và điểm cuối. Sau đó phát hiện ra khu vực lỗi thì tiếp tục chia khu vực đó ra theo nguyên tắc nhị phân và cứ thế cho đến điểm lỗi thật sự.

[3] Ghi log vào file cho các lỗi xảy ra do quá trình

Ghi log vào file liên tục là cách để chúng ta khảo sát 1 lỗi xảy ra do quá trình. Thông thường log chúng ta chỉ giúp ta dễ dàng nhận xét 1 lỗi ở 1 thời điểm nhất định, và các trình debug cũng vậy. Tuy nhiên có những lỗi xảy ra là do quá trình.

Tôi từng làm 1 sản phẩm liên quan đến hiệu năng (1 game 3D), tôi đã gặp phải trường hợp thỉnh thoảng trong chương trình thực thi chậm hẳn nhưng đó là "thỉnh thoảng" do đó, không biết được rằng lý do vì sao. Tôi dùng nhiều phương pháp kết hợp với nhau bao gồm nhị phân tìm kiếm (nhằm xác định được đoạn gây ra lỗi trên) và trong đó, tôi áp dụng việc ghi thời gian vào file log thực thi cho 1 số hàm mà tôi nghi ngờ là có thể dẫn đến chậm chương trình.

Một ví dụ khác đó là tôi từng làm 1 game đá bóng và theo thời gian quả bóng nảy lên ngày càng cao; theo cảm quan, tôi tiến hành ghi log vào file các sự kiện có thể nghi ngờ dẫn đến trình trạng trên và phát hiện được chính xác rằng, độ sai số của số thực mỗi 1 frame sai lại sai 1 ít (ai đã từng tìm hiểu về số thực float thì có thể hiểu được điều này).

Lời kết

Bài viết không thể nêu được quá nhiều trường hợp cụ thể mà tôi đã từng gặp, và tôi sẽ cập nhật thường xuyên hơn cho bài viết này.

Debug cần rất nhiều sự sáng tạo, sự tinh tế, và cần nhiều kinh nghiệm. Ở bài này, tôi không phủ nhận rằng công cụ Visual Studio rất tốt cho việc hỗ trợ debug vì tôi vẫn dùng Visual Studio phát triển các game của mình trước với Win32 rồi mới đưa lên các nền tảng khác như Android, iOS hay Windows Phone hay Tizen, BlackBerry.

Thông qua bài viết này, tôi hy vọng các bạn sẽ cởi mở hơn trong vấn đề debug, và thử nghiệm "tin vào chính mình" trước khi tin vào các công cụ hỗ trợ khi bạn đang học tập, và dĩ nhiên, không nên phủ nhận sức mạnh của các công cụ hỗ trợ khi bạn đang phát triển ứng dụng.

THẢO LUẬN
ĐÓNG