• Thứ Hai, 27/11/2006 16:00 (GMT+7)

    Sử dụng Pseudoregister trong gỡ lỗi với Visual C++

    Nếu là lập trình viên C++, có lẽ hàm API GetLastError() đã quá quen thuộc với bạn. Nó trả về mã lỗi (nếu có) của hàm được gọi liền trước nó. Điều này rất quan trọng trong quá trình gỡ lỗi vì nó cho bạn biết những trục trặc trong quá trình thực thi chương trình. Tuy nhiên, thật là mệt mỏi nếu như sau mỗi lời gọi hàm quan trọng bạn lại phải thêm những dòng lệnh kiểu như:
    DWORD dwResult = GetLastError();

    Điều này khiến bạn tốn khá nhiều thời gian, vì sau khi chương trình đã chạy tốt, bạn lại phải xóa đi các câu lệnh trên (giữ các câu lệnh này trong phiên bản release rõ ràng không có lợi vì làm tốn thêm tài nguyên). Thật may, Visual C++ đã cung cấp một chức năng thay thế hàm này, đó là các pseudoregister (thanh ghi giả). Nó không phải là register (thanh ghi) phần cứng thật sự, nhưng được dùng như register phần cứng. Các pseudoregister cho phép bạn xem xét hầu hết các thông tin cần thiết như mã lỗi, thông tin về tiến trình đang chạy. Có khá nhiều pseudoregister để bạn sử dụng. Bài viết này giới thiệu 2 pseudoregister quan trọng nhất; hữu ích cho lập trình viên.

    Để thay thế hàm GetLastError() ở trên, bạn có thể dùng pseudoregister @ERR. Sử dụng nó rất đơn giản, bạn chỉ cần thêm một điểm ngắt (break point) tại điểm cần kiểm tra, ví dụ:

    Bạn tiến hành gỡ lỗi (debug), chương trình sẽ dừng lại ở điểm ngắt này. Bạn hãy thêm @ERR vào cửa sổ watch. Ta sẽ có giá trị trả về là 2.

    Bây giờ, bạn chỉ việc vào Tool/Error Lookup để kiểm tra xem thực sự điều gì đã xảy ra. Bạn có thể thấy thông báo lỗi của câu lệnh trên như sau:

    Rất tiện lợi phải không? Bạn có thể làm được nhiều hơn. Để VC++ tự động chuyển từ mã lỗi sang thông báo lỗi, bạn chỉ việc sửa @ERR thành @ERR,hr. Và đây là những gì bạn nhận được:

    Một điểm rất hay là ERR có thể được dùng làm điều kiện kiểm tra các điểm ngắt. Bạn có thể thêm đoạn mã sau:

    if (fp)

    {

    fclose(fp);

    }

    Hãy đặt con trỏ ở câu lệnh if (fp). Mở danh sách điểm ngắt bằng Edit/BreakPoints (hoặc nhấn Ctrl+F9). Nhấn chuột phải vào điểm ngắt tương ứng ở trên và chọn Condition. Bạn có thể nhập vào @ERR==2, như sau:

    Giờ đây, điểm ngắt ở trên sẽ chỉ có tác dụng trong trường hợp ERR nhận giá trị là 2. Trong các trường hợp khác, chương trình vẫn sẽ tiếp tục, ngay cả khi xảy ra lỗi nhưng không phải là lỗi 2 (ví dụ lỗi 4 chẳng hạn).

    Chúng ta xem tại sao ERR lại có thể làm được như vậy. Rất đơn giản, GetLastError() tương ứng với 3 lệnh Assembly sau đây:

    mov eax,fs:[00000018h]

    mov eax,dword ptr [eax+34h]

    ret

    ERR đoạt giá trị DWORD tại vị trí eax+34h trong khối thông tin về thread trỏ bởi fs:[18h]. Giá trị của ERR cũng giống như giá trị GetLastError() trả về.

    ERR không phải là pseudoregister duy nhất. Một pseudoregister khác cũng rất hữu ích là TIB (thread information block),đây là thông tin về luồng (thread) hiện tại, rất hữu ích trong việc gỡ lỗi ứng dụng đa luồng (multithread). Nếu bạn đặt một điểm ngắt tại một điểm mà tất cả các luồng đều xử lí, thì chương trình gỡ lỗi sẽ luôn dừng lại đó, cho dù có thể không thật sự cần. Điều này thật bất tiện nếu như bạn đang theo dõi đoạn mã này, mà trình gỡ lỗi lại nhảy đến đoạn mã khác có điểm ngắt, đơn giản vì có một thread khác thực thi nó. Bạn có thể dùng @TIB để chỉ cho trình gỡ lỗi luồng mà bạn cần ngắt.

    Cách làm rất đơn giản. Khi chương trình dừng lại ở thread mà bạn cần, bạn thêm @TIB vào cửa sổ Watch. Bạn sẽ thấy một giá trị, có thể là 2147340288 hay 0x7ffa6000. Bạn mở danh sách điểm ngắt (theo cách đã nói ở trên) và thêm điều kiện cho điểm ngắt này là @TIB== 0x7ffa6000. Từ nay, trình gỡ lỗi sẽ chỉ dừng lại ở đây, tại đúng thread mà bạn cần gỡ lỗi, các thread khác sẽ không phải dừng lại.

    Bạn có thể tham khảo thêm thông tin trong bài viết “Under the Hood: A Tale of Real-world Debugging” trong thư viện MSDN.

    Mai Văn Quân.
    Email:
    Vimvq1987@yahoo.com.

    ID: A0611_126