Kỹ thuật debug trên Visual Studio | CppDeveloper

Debug là một phần không hề thiếu trong tăng trưởng ứng dụng, cũng giống như thịt chó không hề thiếu mắm tôm, đi ị thì không hề không đái vậy. Và có nhiều khi thời hạn tất cả chúng ta ngồi debug còn nhiều hơn thời hạn code :). Debug là một việc làm mê hoặc, ảo diệu nhưng đầy thử thách, đôi lúc rất khó hiểu, gây cho tất cả chúng ta sự tức bực, điên tiết và chửi thề như chí phèo ( kiểu như “ cái đkm, đéo hiểu, ảo vc, cái nồi gì vậy … etc ” ). Chính vì thế nên nếu tất cả chúng ta muốn giảm thời hạn debug, giảm thiếu sự ức chế do debug gây ra thì tất cả chúng ta phải có kiến thức và kỹ năng, phải có chiêu thức khi debug .
Trong khoanh vùng phạm vi bài viết này Cpp • Developer sẽ san sẻ với bạn bè 1 số ít kỹ năng và kiến thức debug trên Visual Studio .

1. Sử dụng Call Stack

Call Stack dùng để xem các lời gọi hàm hoặc thủ tục hiện có trong Stack. Để mở cửa sổ Call Stack thì khi đang debug, chọn menu Debug → Windows → Call Stack ( xem hình dưới )

Mở của sổ Call Stack

Cửa sổ sẽ Call Stack hiển thị tên của từng hàm kèm theo list tham số, dòng code đang chạy, tên của ngôn từ lập trình …

tin tức trên Call Stack

Mũi tên màu vàng chỉ ra Stack Frame nơi con trỏ thực thi đang nằm. Theo mặc định thì các thông tin liên quan của Frame này sẽ hiển thị trong các cửa sổ DisassemblyLocalsWatch, và Autos.

2. Xem giá trị biến, biểu thức nhanh bằng chuột

Khi debug tất cả chúng ta luôn luôn phải chạy vào các hàm khả nghi để khám phá xem cái quái gì đang xảy ra, lướt qua Call Stack để xem 1 số ít giá trị mờ ám bắt nguồn từ đâu … Những lúc như vậy, việc thêm biến / biểu thức vào list theo dõi ( watch ) hoặc xem qua list locals hoàn toàn có thể mất khá nhiều thời hạn. Rất may là Visual Studio có tính năng tương hỗ để mọi thứ trở nên thuận tiện hơn. Nếu bạn chỏ chuột vào một biến mà bạn chăm sóc thì giá trị của biến đó hoặc giá trị của toàn bộ các trường của nó ( so với class / struct ) sẽ được show ra giúp bạn hoàn toàn có thể kiểm tra một cách nhanh gọn và thuận tiện .

Di chuột vào biến testStruct để xem giá trị các trường của nó

3. Thay đổi giá trị biến trực tiếp khi đang chạy (on-the-fly)

Debugger ( công cụ debug ) không phải chỉ dùng để tìm hiểu và tìm lỗi khi đã có lỗi xảy ra rồi. Nhiều lỗi hoàn toàn có thể được ngăn ngừa ngay từ lúc code bằng cách chạy thử qua hàm mới được viết và kiểm tra xem nó có hoạt động giải trí như mong đợi hay không. Đôi khi, bạn muốn biết hàm có chạy đúng nếu một điều kiện kèm theo nào đó là “ true ” hoặc “ false ” hay không ? Hoặc hàm có chạy đúng nếu một biến nào đó nhận một giá trị đơn cử nào đó hay không ?
Hầu hết những trường hợp như vậy bạn hoàn toàn có thể test được luôn trong lúc debug mà không cần sửa code và chạy lại. Chỉ cần di chuột vào biến, nhấp đúp vào giá trị, nhập vào giá trị mà bạn muốn, enter và sau đó liên tục debug .

4. Chạy dòng lệnh được chỉ định

Một trường hợp thường hay gặp khi debug là nghiên cứu và phân tích nguyên do tại sao một hàm lại trả về lỗi bằng cách chạy các hàm từng step một. Và thật là thốn nếu tất cả chúng ta phát hiện ra rằng một hàm vừa call một hàm khác và hàm đó là hàm trả về lỗi ?

Khi ta vừa phát hiện ra hàm functionA trả về false nhưng lại lỡ chạy qua hàm đó mất rồi.

Thốn vãi ! Chả nhẽ lại chạy lại và debug lại để phi vào cái hàm vừa trả về lỗi xem nó làm cái lồi gì mà lỗi ?
Vâng, rất may là Visual Studio đồng cảm bạn bè tất cả chúng ta và cung ứng cho tất cả chúng ta giải pháp thoải mái và dễ chịu hơn rất nhiều. Chúng ta chỉ cần kéo mũi tên màu vàng vào dòng code mà mình muốn chạy ( kéo luôn vào cái dòng gọi cái hàm ngu ngu vừa trả về lỗi chứ còn gì nữa ), sau đó F11 để phi vào hàm đó mà check. Nuột phải không bạn bè ?

Kéo mũi tên màu vàng vào dòng gọi hàm functionA và F11 để debug vào trong hàm

5. Sửa code và build không cần restart lại debug

Khi đang debug một chương trình phức tạp, bug khù khoằm ? Chúng ta đã tìm ra nguyên do ở đâu, nhưng không muốn mất thời hạn stop chương trình, build lại và chạy lại ( vì để tái hiện lại hiện trường là khá phức tạp ) .
Không sao, chỉ cần fix bug tại chỗ và liên tục debug. Visual Studio sẽ build chương trình, vận dụng code mới và liên tục debug mà không cần phải khởi động lại .
Tuy nhiên để thao tác này thì cần 1 số ít điều kiện kèm theo sau :

  • Thứ nhất, chương trình phải được cấu hình như sau:

Chuột phải vào project trên Solution Explorer → Properties ( hoặc bấm Alt + Enter )

Tiếp theo, chọn C/C++ → General → Debug Information Format, chọn “Program Database for Edit And Continue (/ZI)”

  • Thứ hai, các thay đổi phải là local bên trên trong hàm. Nếu bạn thay đổi khai báo hàm, thêm các phương thức hoặc class mới, bạn sẽ phải khởi động lại chương trình, build lại để apply những thay đổi mới sau đó debug lại.

6. Sử dụng Watch Window

Watch Window là hành lang cửa số sử dụng để theo dõi sự biến hóa giá trị của các biến ( local hay global đều được hết ). Để mở Watch Window có 2 cách

  • Cách 1: Bấm tổ hợp phím Ctrl + D + W và sau đó bấm một số nằm trong dải từ 1 đến 4 (vì có tối đa 4 của sổ Watch)
  • Cách 2: chọn menu Debug → Windows → Watch → Watch 1 hoặc 2, 3, 4 (xem hình dưới)

→ Watch Window sẽ hiện ra như hình dưới


→ Watch Window sẽ hiện ra như hình dướiĐể theo dõi một biến ở Watch Window bạn có 2 cách

  • Cách 1: Click chuột phải vào biến trên editor → chọn “Add Watch”
  • Cách 2: Nhập tên biến vào cột “Name” trên Watch Window

Ngoài ra, thông tin có thể xem được từ Watch Window không giới hạn ở các biến bình thường. Bạn có thể nhập tên của một số biến đặc biệt để theo dõi, ví dụ:

  • esp: giá trị hiện tại của stack pointer
  • err: mã lỗi của hàm cuối cùng được gọi

7. Sử dụng Threads Window

Threads Window khá có ích khi debug các ứng dụng đa luồng ( multi-threading ). Nó cho tất cả chúng ta thông tin về các Threads đang chạy, điểm Breakpoint đang dừng đang được thực thi trên Thread nào ( lưu lại bằng mũi tên màu vàng )
Để mở Threads Window có 2 cách

  • Cách 1: Bấm tổ hợp phím Ctrl + Alt + H
  • Cách 2: Chọn menu Debug → Windows → Threads (xem hình dưới)

→ Threads Window sẽ hiện ra như hình dưới

8. Conditional Breakpoints

→ Threads Window sẽ hiện ra như hình dướiNếu bạn muốn dừng chương trình tại một line code nào đó nhưng bạn không muốn cứ chạy qua đấy là dừng mà cần phải có điều kiện kèm theo nào đó mới dừng thì bạn hoàn toàn có thể làm điều đó bằng cách tạo Conditional Breakpoint .
Giả sử có vòng lặp đơn thuần như sau, i chạy từ 0 đến 99

Nếu ta muốn trình debug dừng lại tại dòng printf ( “ i = % d \ n ”, i ) ; chỉ khi nào i đang có giá trị là 5, ta làm như sau :
Đặt Breakpoint tại dòng đó → click chuột phải vào breakpoint ( màu đỏ ) → chon Conditions …

Nhập “i == 5” như hình dưới

Chạy debug và chương trình sẽ dừng tại Breakpoint khi i có giá trị 5

9. Sử dụng Memory Window

Nhập “ i = = 5 ” như hình dướiChạy debug và chương trình sẽ dừng tại Breakpoint khi i có giá trị 5Có những bug tương quan đến chỉnh sửa bộ nhớ, buffer overflow yên cầu tất cả chúng ta phải theo dõi giá trị của memory một cách chi tiết cụ thể và sát sao mới hoàn toàn có thể phát hiện ra. Trong những trường hợp như vậy tất cả chúng ta sẽ cần đến Memory Window .
Để mở Memory Window có 2 cách

  • Cách 1: Bấm tổ hợp phím Ctrl + Alt + M → sau đó bấm một số nằm trong dải từ 1 đến 4 (Visual Studio hỗ trợ tối đa 4 Memory Windows)
  • Cách 2: Chọn menu Debug → Windows → Memory → Memory 1 hoặc 2, 3, 4 (xem hình dưới)

→ Memory Window sẽ hiện ra như hình dưới


→ Memory Window sẽ hiện ra như hình dướiNếu muốn vùng nhớ nào đó hiển thị lên trên cùng của Memory Window cho tiện theo dõi thì hoàn toàn có thể nhập địa chỉ của vùng nhớ đó vào ô “ Address ”, xem hình dưới

10. Data Breakpoints

Trong các chương trình phức tạp, đôi lúc tất cả chúng ta gặp trường hợp giá trị của một biến hay một vùng nhớ nào đó bị biến hóa nhưng có quá nhiều đoạn code truy vấn đến biến hóa biến / vùng nhớ đó. Chúng ta sẽ rất khó khăn vất vả để debug theo cách thường thì vì méo biết đặt debug vào đâu .
Trong những trường hợp như vậy thì Data Breakpoints là sự lựa chọn tuyệt đối. Mục đích của Data Breakpoints là làm cho trình debug dừng lại khi một vùng nhớ tại một địa chỉ đơn cử ( do tất cả chúng ta chỉ định ) có sự biến hóa .
Ví dụ có chương trình sau →

Có một biến toàn cục gTest kiểu int ( 4 bytes ). Có 2 hàm cùng đổi khác giá trị của gTest là functionA và functionB, 2 hàm này được chạy trên 2 threads khác nhau. Bây giờ tôi muốn chương trình sẽ tạm dừng khi gTest bị biến hóa, tôi sẽ làm như sau

  • Bước 1: Để tổng quát, tôi sẽ không đặt Breakpoint vào cụ thể vào functionA hay functionB mà đặt debug vào điểm entry point của chương trình → dòng bắt đầu hàm main – line 18 → F5 để chạy debug chương trình

  • Bước 2: Khi chương trình dừng tại điểm bắt đầu hàm main, việc cần làm bây giờ là lấy địa chỉ của biến gTest. Để lấy địa chỉ của gTest thì có thể dùng Watch Window (gõ vào &gTest), nhưng tiện thể ở đây hướng dẫn anh em một cách khác, là dùng Immediate Window. Cách mở Immediate Window thì cũng đơn giản như Watch Window, Threads Window,… anh em có thể tự triển được rồi. Khi đã mở được Immediate Window thì gõ vào &gTest (xem hình dưới)

  • Bước 3: Tạo Data Breakpoint, chọn Debug → New Breakpoint → Data Breakpoint…


→ Paste địa chỉ của gTest vào ô Address, chọn số byte của vùng nhớ ( ở đây là 4 ) → OK

  • Bước 4: F5 để tiếp tục debug chương trình. Khi gTest bị thay đổi giá trị chương trình sẽ tạm dừng và Visual Studio sẽ thông báo như hình dưới

→ Bấm OK, vào phi vào Call Stack xem hàm nào thay đổi giá trị của gTest


→ Nhìn vào Call Stack thì thấy hàm functionA chính là thủ phạm. Việc giờ đây khá đơn thuần, phi vào functionA xem nó đang chạy ở dòng nào .
Đây chỉ là một ví dụ đơn thuần để demo cách sử dụng Data Breakpoint. Các bạn hãy vận dụng linh động vào những trường hợp đơn cử mà mình gặp phải, sẽ rất có ích đấy .
— Phạm Minh Tuấn ( Shun ) —