Link gốc: http://www.arm.vn/index.php?option=com_content&task=view&id=44&Itemid=1
Ở bài trước, chúng tôi đã trình bày cách xây dựng và cài đặt driver fakemodem vào Windows XP. Để việc triển khai driver được thành công và an toàn, cần phải có giai đoạn kiểm tra lỗi trong quá trình phát triển, trước và sau khi cài đặt. Phần ví dụ mình họa ở bài này sẽ là Fake Modem (src/kmdf/fakemodem).
Kiểm tra lỗi là giai đoạn rất quan trọng trong phát triển phần mềm, đảm bảo rằng sản phẩm được đưa ra không bị lỗi, hoạt động đúng chức năng và không được ảnh hưởng xấu đến hệ thống. Vì driver có thể họat động ở mức kernel nên việc driver bị lỗi gây treo hệ thống, thậm chí máy tự động restart là điều rất nguy hiểm, lúc đó driver chẳng khác gì một chương trình virus nguy hại. Ở bài này chúng tôi sẽ trình bày các kỹ thuật thông thường để phát hiện lỗi trong quá trình phát triển driver trên Windows.
Mô hình
Với việc lập trình driver ở mức đơn giản, chúng ta có thể tổ chức như sau:
Hai máy tính được nối mạng với nhau, PC1 đóng vai trò máy phát triển, và máy theo dõi lỗi xảy ra ở PC2. PC2 dùng để triển khai thử nghiệm driver. Các loại lỗi thường gặp gồm có:
Lỗi logic: Lỗi rất hay gặp và rất khó phát hiện, có thể do sai sót ở khâu lập trình, cũng có thể sai sót ở khâu thiết kế driver. Để phát hiện lỗi này, chỉ có thể theo dõi các luồng xử lý dữ liệu bằng cách in ra các dòng theo dõi lỗi (prints debug message).
Lỗi về kỹ thuật lập trình: Do hiểu sai về hàm, cách sử dụng thư viện hoặc cấu hình phần mềm không đúng. Các dạng lỗi này rất dễ phát hiện và sửa chữa.
Bộ công cụ kiểm lỗi cung cấp bởi Microsoft
Các bộ công cụ này được chứa trong thư mục tools, nằm trong thư mục cài đặt của WDF. Phần lớn các chương trình này chạy dưới dạng dòng lệnh (command line).
1. Kiểm tra inf file
File inf là file hướng dẫn cho Windows cài đặt driver vào hệ thống trong mô hình WDF. Để kiểm tra cú pháp của file inf, WDF cung cấp chương trình chkinf (INF file syntax checker), đây là chương trình được viết trên Perl, chạy dưới dạng dòng lệnh. Chkinf không thể kiểm tra các driver chạy trên Windows 98 hoặc Window Me. Để biết thêm thông tin, cấu trúc lệnh, tham số, bạn có thể tham khảo file chkinf.htm trong thư mục tools/chkinf.
Kiểm tra file: mdmfake.inf
Bước 1: chép file mdmfake.inf vào thư mục Tools/chkinf
Bước 2: thực thi chkinf.bat: >chkinf mdmfake.inf
Ở đây ta thấy file @+_mdmfake.htm (trong thư mục htm) được tạo ra để lưu kết quả kiểm tra.
Bước 3: mở file @+_mdmfake.htm để kiểm tra kết quả.
Chkinf cung cấp chi tiết lỗi rất rõ ràng. Với file mdmfake, chúng ta có 1 lỗi ở dòng 30: lỗi báo là tên của nhà cung cấp driver phải là tên khác với Microsoft: ở dòng 270 thay thế giá trị của %Mfg% bằng giá trị khác, ví dụ: mfg = "ARM Vietnam".
Bước 4: lưu lại thay đổi và kiểm tra lại file “mdmfake.inf”.
2. Kiểm tra memory leak
Để driver có thể hoạt động ổn định, ngoài việc driver hoạt động đúng theo ý muốn người dùng, việc quản lý tài nguyên một cách hợp lý cũng rất quan trọng. Vì driver hoạt động ở mức thấp, nếu tài nguyên bị chiếm dụng bởi driver mà không được giải phóng, sau một thời gian hoạt động có khả năng hệ thống sẽ cạn kiệt tài nguyên, gây ra các lỗi không kiểm soát được. WDF cung cấp công cụ rất hữu hiệu theo dõi việc sử dụng tài nguyên của các tiến trình đang hoạt động ở mức hệ thống, đó là poolmon.exe (chứa trong tools/other/i386). Tài liệu hướng dẫn của poolmon được microsoft cung cấp đầy đủ, sử dụng rất đơn giản.
Giải thích cấu trúc thông tin:
+ Type: Loại của memory: paged hoặc nonpaged.
+ Allocs: Số lần cấp phát bộ nhớ.
+ Frees: Số lần giải phóng bộ nhớ.
+ Diff: Số lần chưa giải phóng tài nguyên (bằng hiệu của Allocs trừ Frees).
+ Bytes: Số bytes sử dụng.
+ Per Alloc: Số bytes sử dụng trong mỗi lần cấp phát.
3. Theo dõi luồng xử lý
Để kiểm tra lỗi logic, chúng ta cần phải theo dõi luồng xử lý, xuất ra giá trị các biến. Để làm được điều này, cần có 2 phần:
+ Phía driver: xuất thông tin vào hê thống debug message. Phần code thông thường đựơc viết như sau:
#define _DRIVER_NAME_ "Fake webcam"
#define TRACE_LEVEL_ERROR 1
DBG_PNP
VOID
TraceEvents (
__in ULONG DebugPrintLevel,
__in ULONG DebugPrintFlag,
__drv_formatString(printf),
__in PCSTR DebugMessage,
...
)
{
#if DBG
#define TEMP_BUFFER_SIZE 1024
va_list list;
CHAR debugMessageBuffer[TEMP_BUFFER_SIZE];
NTSTATUS status;
va_start(list, DebugMessage);
if (DebugMessage) {
// Using new safe string functions
// instead of _vsnprintf.
// This function takes care of NULL terminating
// if the message
// is longer than the buffer.
status = RtlStringCbVPrintfA(
debugMessageBuffer,
sizeof(debugMessageBuffer),
DebugMessage,
list);
if(!NT_SUCCESS(status)) {
DbgPrint (_DRIVER_NAME_":
RtlStringCbVPrintfA failed 0x%x\n", status);
return;
}
if (DebugPrintLevel <= TRACE_LEVEL_ERROR ||
(DebugPrintLevel <= DebugLevel &&
((DebugPrintFlag & DebugFlag) == DebugPrintFlag)))
{
DbgPrint(
"%s %s",
_DRIVER_NAME_, debugMessageBuffer);
}
}
va_end(list);
return;
#else
UNREFERENCED_PARAMETER(TraceEventsLevel);
UNREFERENCED_PARAMETER(TraceEventsFlag);
UNREFERENCED_PARAMETER(DebugMessage);
#endif
Đây là kỹ thuật rất hay dùng khi viết driver:
+ Macro DBG: define macro DBG để theo dõi lỗi khi chạy ở chế độ debug, khi driver đưa vào triển khai thì chỉ cần undefine DBG, tránh hệ thống xuất debug message một cách không mong muốn.
+ DebugPrintLevel, DebugPrintFlag: vì driver được tổ chức thành nhiều thành phần (lớp, chức năng…) cho nên cần tổ chức cách debug lỗi theo từng thành phần, tránh xuất thông tin của các phần đã sửa chữa lỗi rồi hoặc nhiều thông tin không cần thiết.
TraceEvents(TRACE_LEVEL_ERROR,DBG_PNP,"DriverEntry: Start\n");
//xuất message khi vào hàm DriverEntry
+ Phía host: dùng DebugView truy cập và debug message để đọc thông tin. DebugView có thể truy cập vào hệ thống debug của máy host, hoặc thông qua mạng truy cập vào hệ thống debug của máy khác. Nếu sử dụng thông qua mạng, ở phía client cũng phải có chương trình DebugView, khởi động DebugView ở chế độ client (#Dbgview.exe /c), sau đó phía host truy cập vào client theo địa chỉ IP (hoặc theo tên máy).
Lời kết
Vậy là chúng ta có thể theo dõi, kiểm soát lỗi khi phát triển driver. Ở loạt bài sau chúng tôi sẽ trình bày một ví dụ có ứng dụng cụ thể: viết một webcam ảo có tính năng tương tự Fakewebcam , ứng dụng có thể đưa dữ liệu tùy ý vào webcam ảo, ứng dụng (yahoo, webcam viewer…) truy cập vào webcam ảo này để lấy dữ liệu.