[Repost] General Methods for Debugging Drivers under WINCE
</p>
I don't normally repost (except for Dean Wan's classic of classics). Today I stumbled upon an article that reminded me of my own days fumbling with driver development on CE. For those new to embedded development, some basic methods and mindsets do require a "brain reset."
This article is clearly the author's experience summary. Although I don't fully agree with some points, I'll keep it as-is. It will definitely help those who are still exploring.
Original URL: http://blog.csdn.net/xqhrs232/archive/2009/11/27/4888577.aspx
1. Print messages via serial port — only suitable for general messages. Don't print in real-time critical sections because serial printing is slow. If you must print, keep it minimal and selective — e.g., print once every 100 times.
// Serial printing can also roughly analyze inter-thread lock contention on shared resources
2. Write LOG files — file writing is faster than serial printing. LOGs are suitable for analyzing large amounts of data.
3. Use static variables flexibly — static variables have memory retention. Use this to diagnose program reliability — especially in interrupt reception. Storing tens of KB of data then writing to a LOG file is a good debugging method.
4. Driver development often requires precise delays — correctly implementing US/MS delays is important.
// WINCE microsecond delay function
void delay_us(int n)
{
LARGE_INTEGER litmp;
LONGLONG QPart1,QPart2;
double dfMinus, dfFreq, dfTim;
if(QueryPerformanceFrequency(&litmp)==FALSE)
{
MessageBox(NULL,TEXT("Error: QueryPerformanceFrequency"),TEXT("Error"),MB_OK);
return;
}
dfFreq = (double)litmp.QuadPart;
QueryPerformanceCounter(&litmp);
QPart1 = litmp.QuadPart;
do
{
QueryPerformanceCounter(&litmp);
QPart2=litmp.QuadPart;
dfMinus=(double)(QPart2-QPart1);
dfTim=dfMinus/dfFreq;
}while(dfTim <0.000001*n);
}
// WINCE millisecond delay function
void delay_ms(DWORD tmp_time)
{
DWORD start;
DWORD time_i=0;
start=GetTickCount();
while(time_i<=tmp_time)
{
time_i=GetTickCount()-start;
}
}
5. Driver development usually involves interrupt handling — correctly implementing interrupt enable/disable at both AP and driver levels is essential.
Especially when analyzing whether interrupts are working properly — capture a certain amount of interrupt data then disable interrupts to ensure received data isn't corrupted, so your analysis reflects reality.
6. Measure code execution time — use GetTickCount to roughly measure how long a section of code takes to execute, finding program hotspots.
dwStartTickCount=GetTickCount( );
......XXXXXX
// code snippet to measure
dwEndTickCount=GetTickCount( );
7. Drivers require high real-time performance, so keep code streamlined and optimized.
1》First, good algorithm selection and optimization
2》Prefer addition/subtraction over multiplication/division
3》Use bit-shift operations instead of multiplication/division
4》Remove unnecessary variables in function calls, as each call involves push/pop operations; align variables to 32-bit
5》Reduce multiple function calls; group related functionality into one function
6》Memory COPY is faster than looped assignment
7》Reduce nesting depth and iteration count
8》Lower computational complexity
9》Prefer library functions over custom ones; library functions are generally more reliable
10》Proper selection of processes, threads, critical sections, message queues, and mutexes can simplify program logic and optimize resource contention
11》Minimize compiler warnings and reduce forced type casting
8. Using the volatile qualifier — prevents unwanted optimization, forces re-read from memory. Many detailed articles online.
Commonly used in interrupt handlers, where a variable switch is controlled by the AP at any time.
9. Check system error messages, analyze MAP files and COD files from compilation.
1》For DATA ABORT errors, analyzing MAP files can pinpoint the offending function.
2》Analyzing COD files can pinpoint the exact statement, though COD files are in assembly.
10. Using VIEWBIN/DUMPBIN/SET tools.
1》VIEWBIN is used to inspect NK contents, check if a driver is included in NK.
2》DUMPBIN can show what function interfaces a driver exports.
3》SET tool checks system environment variables.
11. WINCE Remote Tools.
1》Check memory status for leaks.
2》Check registry to see if the driver has been loaded.
3》Check CPU usage to understand system performance and whether your driver is consuming excessive CPU.
4》File copy back and forth.
12. Using KITL — the KITL debugging approach.
DEBUG can locate many issues — you can inspect variables, memory, and function call stacks.
13. Use GlobalMemoryStatus to periodically diagnose system memory status for insufficient memory or leaks.
1》Pair malloc and free.
2》Pair new and delete.
3》Use delete[] for arrays allocated with new[].
4》Properly release bitmaps/resources; correctly use DeleteObject/DeleteDC/ReleaseDC.
5》Prevent wild pointers; safely use/release pointers; always check pointer validity before use.
6》Prevent array access out of bounds.
14. Variable and buffer placement — consider holistically whether to place on heap, stack, or globally.
15. Minimize dynamic and repeated memory allocation/deallocation — prone to fragmentation. For fixed-size allocations, consider using arrays instead.
16. When creating events bound to threads, it's best to name them — named events can be manipulated from the AP layer, allowing simulation of driver operations without actual hardware.
Named events are unique system-wide; multiple opens reference the same event.
17. Flexibly define IOCTL macros for AP operations, allowing AP to simulate driver data formats and operations, making it possible to exercise the entire driver flow without actual hardware.
18. WINCE Driver Debugging Assistant — very convenient to use, no need to update NK every time.
19. CETK tool is worth a try, though I haven't used it much!
20. There's also a tool by the talented we-hjb — Register Read/Write Tool, available for both WINCE50 and WINCE60.
Allows you to inspect SFR configuration at any time. A very handy tool.
21. Compile switches and debug code.
1》Flexibly use #if/#else/#endif to switch between debug and production code.
2》There's a school of thought: test-first. Write test code before writing production code. Code without tests is just garbage.
22. Flexible use of hardware tools:
1》Multimeter
2》Oscilloscope — check waveform for interference, decide on pull-up resistors and their values.
3》Logic analyzer — capture several KB of data for analysis; can analyze common bus protocols.
4》Spectrum analyzer
</span>