Windows Debugging
--> Gotcha's <--
Local
External
Exporting a debug variable to other modules
// In the defining module
namespace
{
extern "C"
{
__declspec(dllexport) int jbpSkipFlag = 0;
}
}
// In the using module
namespace
{
extern "C"
{
__declspec(dllimport) int jbpSkipFlag;
}
}
High Resolution Timer
Time one event
LARGE_INTEGER jstart;
QueryPerformanceCounter(&jstart);
// Do stuff that needs timing
LARGE_INTEGER jend;
QueryPerformanceCounter(&jend);
LARGE_INTEGER span;
span.QuadPart = jend.QuadPart - jstart.QuadPart;
Timer Frequency
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
std::cout << "Frequency=0x" << std::hex << freq.u.HighPart << std::hex << freq.u.LowPart << std::endl;
Average Time of event
LARGE_INTEGER jstart;
QueryPerformanceCounter(&jstart);
// Do stuff to time here
// Do stuff to time here
// Do stuff to time here
// Do stuff to time here
// Do stuff to time here
LARGE_INTEGER jend;
QueryPerformanceCounter(&jend);
static int count = 0;
static LONGLONG total = 0;
LONGLONG span = jend.QuadPart - jstart.QuadPart;
HANDLE mutex2 = CreateMutex( 0, false, "uniqueName");
WaitForSingleObject( mutex2, INFINITE);
count++;
total += span;
if (count == 1543)
{
std::cout << "Per=" << total / count << std::endl;
}
ReleaseMutex( mutex2);
Save Raw RGB to .bmp
void SaveRawToBmpLayerInterleaved(bool* pInitialized, const char* zDirNamePrefix, unsigned char* pData, unsigned long wide, unsigned long tall)
//void SaveRawToBmpPixelInterleaved(
// const char* zDirNamePrefix,
// unsigned char* pData,
// unsigned long wide, unsigned long tall)
{
static HANDLE mutex = CreateMutex(0, false, L"blocksavergb");
WaitForSingleObject(mutex, INFINITE);
static int blockNum = 0;
static int timestamp = ::GetTickCount();
char zName[1000];
if (!(*pInitialized))
{
sprintf(zName, "c:\\delme\\blocks-%08x-%s", timestamp, zDirNamePrefix);
CreateDirectoryA(zName, NULL);
(*pInitialized) = true;
}
sprintf(zName, "c:\\delme\\blocks-%08x-%s\\block-%08d.bmp", timestamp, zDirNamePrefix, blockNum);
blockNum++;
FILE* fout = fopen(zName, "wb");
if (fout)
{
char bmpHeader[0x36];
char* pMagic0 = bmpHeader + 0x00;
char* pMagic1 = bmpHeader + 0x01;
DWORD* pFileSize = (DWORD*)(bmpHeader + 0x02);
DWORD* pStartOfData = (DWORD*)(bmpHeader + 0x0a);
DWORD* pSizeOfHeader = (DWORD*)(bmpHeader + 0x0e);
DWORD* pWidth = (DWORD*)(bmpHeader + 0x12);
DWORD* pHeight = (DWORD*)(bmpHeader + 0x16);
WORD* pPlanes = (WORD*)(bmpHeader + 0x1a);
WORD* pBitsPerPixel = (WORD*)(bmpHeader + 0x1c);
DWORD* pImageDataSize = (DWORD*)(bmpHeader + 0x22); // size of file - headersize // headersize is 0x36
memset(bmpHeader, 0, sizeof(bmpHeader));
*pMagic0 = 'B';
*pMagic1 = 'M';
*pFileSize = 0x36 + wide * tall * 3;
*pStartOfData = 0x36;
*pSizeOfHeader = 0x28;
*pWidth = wide;
*pHeight = tall;
*pPlanes = 1;
*pBitsPerPixel = 24;
*pImageDataSize = *pFileSize - 0x36;
size_t written;
written = fwrite(bmpHeader, sizeof(bmpHeader), 1, fout);
//unsigned char* pLine = new unsigned char[bh * bw * 3];
unsigned char* pOut = new unsigned char[tall * wide * 3];
unsigned char* pTo = pOut;
int span = wide;
unsigned char* pFromR = pData + tall * wide * 3 * 1;
unsigned char* pFromG = pData + tall * wide * 3 * 2;
unsigned char* pFromB = pData + tall * wide * 3 * 3;
for (int y = tall - 1; y >= 0; y--)
{
pFromR -= span;
pFromG -= span;
pFromB -= span;
//pTo = pLine;
for (int x = 0; x < wide; x++)
{
*pTo++ = pFromR[x];
*pTo++ = pFromG[x];
*pTo++ = pFromB[x];
}
}
fwrite(pOut, tall * wide * 3, 1, fout);
delete[] pOut;
fclose(fout);
}
ReleaseMutex(mutex);
}
void SaveRawToBmpPixelInterleaved(bool* pInitialized, const char* zDirNamePrefix, unsigned char* pData, unsigned long wide, unsigned long tall)
//void SaveRawToBmpPixelInterleaved(
// const char* zDirNamePrefix,
// unsigned char* pData,
// unsigned long wide, unsigned long tall)
{
static HANDLE mutex = CreateMutex(0, false, L"blocksavergb");
WaitForSingleObject(mutex, INFINITE);
static int blockNum = 0;
static int timestamp = ::GetTickCount();
char zName[1000];
if (!(*pInitialized))
{
sprintf(zName, "c:\\delme\\blocks-%08x-%s", timestamp, zDirNamePrefix);
CreateDirectoryA(zName, NULL);
(*pInitialized) = true;
}
sprintf(zName, "c:\\delme\\blocks-%08x-%s\\block-%08d.bmp", timestamp, zDirNamePrefix, blockNum);
blockNum++;
FILE* fout = fopen(zName, "wb");
if (fout)
{
char bmpHeader[0x36];
char* pMagic0 = bmpHeader + 0x00;
char* pMagic1 = bmpHeader + 0x01;
DWORD* pFileSize = (DWORD*)(bmpHeader + 0x02);
DWORD* pStartOfData = (DWORD*)(bmpHeader + 0x0a);
DWORD* pSizeOfHeader = (DWORD*)(bmpHeader + 0x0e);
DWORD* pWidth = (DWORD*)(bmpHeader + 0x12);
DWORD* pHeight = (DWORD*)(bmpHeader + 0x16);
WORD* pPlanes = (WORD*)(bmpHeader + 0x1a);
WORD* pBitsPerPixel = (WORD*)(bmpHeader + 0x1c);
DWORD* pImageDataSize = (DWORD*)(bmpHeader + 0x22); // size of file - headersize // headersize is 0x36
memset(bmpHeader, 0, sizeof(bmpHeader));
*pMagic0 = 'B';
*pMagic1 = 'M';
*pFileSize = 0x36 + wide * tall * 3;
*pStartOfData = 0x36;
*pSizeOfHeader = 0x28;
*pWidth = wide;
*pHeight = tall;
*pPlanes = 1;
*pBitsPerPixel = 24;
*pImageDataSize = *pFileSize - 0x36;
size_t written;
written = fwrite(bmpHeader, sizeof(bmpHeader), 1, fout);
//unsigned char* pLine = new unsigned char[bh * bw * 3];
unsigned char* pOut = new unsigned char[tall * wide * 3];
unsigned char* pTo = pOut;
int span = wide * 3;
unsigned char* pFromR = pData + tall * wide * 3;
unsigned char* pFromG = pFromR + 1;
unsigned char* pFromB = pFromR + 2;
for (int y = tall - 1; y >= 0; y--)
{
pFromR -= span;
pFromG -= span;
pFromB -= span;
//pTo = pLine;
for (int x = 0; x < wide; x++)
{
*pTo++ = pFromR[x];
*pTo++ = pFromG[x];
*pTo++ = pFromB[x];
}
}
fwrite(pOut, tall * wide * 3, 1, fout);
delete[] pOut;
fclose(fout);
}
ReleaseMutex(mutex);
}
void SaveRawToBmp(bool* pInitialized, const char* zDirNamePrefix, unsigned char* pDataR, unsigned char* pDataG, unsigned char* pDataB, unsigned long wide, unsigned long tall)
{
static HANDLE mutex = CreateMutex(0, false, L"blocksavergb");
WaitForSingleObject(mutex, INFINITE);
static int blockNum = 0;
static int timestamp = ::GetTickCount();
char zName[1000];
if (!(*pInitialized))
{
sprintf(zName, "c:\\delme\\blocks-%08x-%s", timestamp, zDirNamePrefix);
CreateDirectoryA(zName, NULL);
(*pInitialized) = true;
}
sprintf(zName, "c:\\delme\\blocks-%08x-%s\\block-%08d.bmp", timestamp, zDirNamePrefix, blockNum);
blockNum++;
FILE* fout = fopen(zName, "wb");
if (fout)
{
char bmpHeader[0x36];
char* pMagic0 = bmpHeader + 0x00;
char* pMagic1 = bmpHeader + 0x01;
DWORD* pFileSize = (DWORD*)(bmpHeader + 0x02);
DWORD* pStartOfData = (DWORD*)(bmpHeader + 0x0a);
DWORD* pSizeOfHeader = (DWORD*)(bmpHeader + 0x0e);
DWORD* pWidth = (DWORD*)(bmpHeader + 0x12);
DWORD* pHeight = (DWORD*)(bmpHeader + 0x16);
WORD* pPlanes = (WORD*)(bmpHeader + 0x1a);
WORD* pBitsPerPixel = (WORD*)(bmpHeader + 0x1c);
DWORD* pImageDataSize = (DWORD*)(bmpHeader + 0x22); // size of file - headersize // headersize is 0x36
memset(bmpHeader, 0, sizeof(bmpHeader));
*pMagic0 = 'B';
*pMagic1 = 'M';
*pFileSize = 0x36 + wide * tall * 3;
*pStartOfData = 0x36;
*pSizeOfHeader = 0x28;
*pWidth = wide;
*pHeight = tall;
*pPlanes = 1;
*pBitsPerPixel = 24;
*pImageDataSize = *pFileSize - 0x36;
size_t written;
written = fwrite(bmpHeader, sizeof(bmpHeader), 1, fout);
//unsigned char* pLine = new unsigned char[bh * bw * 3];
unsigned char* pOut = new unsigned char[tall * wide * 3];
unsigned char* pTo = pOut;
int span = wide;
unsigned char* pFromR = pDataR + tall * wide;
unsigned char* pFromG = pDataG + tall * wide;
unsigned char* pFromB = pDataB + tall * wide;
for (int y = tall - 1; y >= 0; y--)
{
pFromR -= span;
pFromG -= span;
pFromB -= span;
//pTo = pLine;
for (int x = 0; x < wide; x++)
{
*pTo++ = pFromR[x];
*pTo++ = pFromG[x];
*pTo++ = pFromB[x];
}
}
fwrite(pOut, tall * wide * 3, 1, fout);
delete[] pOut;
fclose(fout);
}
ReleaseMutex(mutex);
}
Memory change detector (Debug)
In a low level library put this
#include <memory.h>
#include <vector>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
struct WatchTarget
{
public:
void* m_pWhere;
int m_nLen;
unsigned char* m_pData;
WatchTarget(void* pWhere, int nLen)
{
m_pWhere = pWhere;
m_nLen = nLen;
m_pData = new unsigned char[m_nLen];
memcpy(m_pData, m_pWhere, m_nLen);
}
};
std::vector<WatchTarget*> g_watches;
HANDLE mutex = CreateMutex( 0, false, "jbpdebug");
extern "C"
{
__declspec(dllexport) void addWatch(void *pWhere, int nLen);
__declspec(dllexport) void removeWatch(void *pWhere);
__declspec(dllexport) void verifyWatchesUnchanged();
}
void addWatch(void *pWhere, int nLen)
{
WaitForSingleObject(mutex, INFINITE);
WatchTarget* pWatch = new WatchTarget(pWhere, nLen);
g_watches.push_back(pWatch);
ReleaseMutex(mutex);
}
void removeWatch(void *pWhere)
{
WaitForSingleObject(mutex, INFINITE);
std::vector<WatchTarget*>::iterator it;
for (it = g_watches.begin(); it != g_watches.end(); it++)
{
WatchTarget* pWatch = *it;
if (pWatch->m_pWhere == pWhere)
{
g_watches.erase(it);
break;
}
}
ReleaseMutex(mutex);
}
void verifyWatchesUnchanged()
{
WaitForSingleObject(mutex, INFINITE);
std::vector<WatchTarget*>::iterator it;
for (it = g_watches.begin(); it != g_watches.end(); it++)
{
WatchTarget* pWatch = *it;
if (memcmp(pWatch->m_pWhere, pWatch->m_pData, pWatch->m_nLen))
{
_asm int 3;
}
}
ReleaseMutex(mutex);
}
Put this where it will be used
__declspec(dllimport) void addWatch(void *pWhere, int nLen);
__declspec(dllimport) void removeWatch(void *pWhere);
__declspec(dllimport) void verifyWatchesUnchanged();
Example Use
static Thing* pThing = 0;
if (0 == pThing)
{
pThing = new Thing();
// do once
addWatch(pThing, sizeof(*pThing));
}
// do alot:
verifyWatchesUnchanged();
if (deleting it for some reason)
{
removeWatch(pThing);
delete pThing;
}
Heap Debugging in Windows
To use:
#include <crtdbg.h>
Functions
| Function |
Description |
| _CrtCheckMemory() |
Checks heap integrity |
| _CrtSetDbgFlag |
Sets or gets debug heap flags |
int debugHeapFlags = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
debugHeapFlags |= _CRTDBG_CHECK_EVERY_1024_DF;
_CrtSetDbgFlag(debugHeapFlags);
Internet Access, disabling
To temporarily disable your access to the internet, you can open a command prompt and type:
route ADD 0.0.0.0 MASK 0.0.0.0 [your IP address]
This will redirect all traffic destined outside the LAN back to your machine. Bye bye internet. . .
example:
route delete 0.0.0.0 mask 0.0.0.0 10.44.8.133
To restore your internet access:
route DELETE 0.0.0.0 MASK 0.0.0.0 [your IP address]
Library Loading problems, diagnose with via "Loader Snaps"
See http://blogs.msdn.com/junfeng/archive/2006/11/20/debugging-loadlibrary-failures.aspx
Get "Debugging Tools for Windows" here http://www.microsoft.com/whdc/devtools/debugging/default.mspx
RtlAllocateHeapSlowly
To find info about what happened, in the debugger look at the output pane, there is usually text about what caused this.
GetLastError not returning a reasonable code, How to deal with it
Debugging 'Last Error' problems
On a few occasions I have run into problems where some Windows API was returning a 'strange' error code. Here is a technique that works with some APIs:
1. Go get public symbols for your operating system – add http://msdl.microsoft.com/download/symbols to your symbol path and define a symbol cache directory
2. Set a breakpoint on SetLastError: to be on the safe side, set the breakpoint on both {,,kernel32}_SetLastError@4 and {,,ntdll}_RtlSetLastWin32Error@4
3. Assuming that you get a million calls, figure out where the last error is being passed to the set last error functions. For me, this is *(unsigned*)(@esp+4), but your mileage may vary. Once you know where the error code is passed, you can either set a tracepoint, or turn the breakpoint into a conditional breakpoint (see more information bellow).
4. Run your scenario and find your bug
To set a tracepoint:
1. Right click on the breakpoint, and click 'When Hit...'
2. Check 'print a message'
3. Set the message to '{*(unsigned*)(@esp+4)} -- $CALLSTACK', or whatever information you want
4. Click 'okay'
To set a conditional breakpoint:
1. Right click on the breakpoint, and click 'Condition...'
2. Set your condition to be '*(unsigned*)(@esp+4)==1234' where 1234 is the strange error code that you are seeing
Article on Debugging, with info about tools
Here are links to the article
Heap Messes
Excellent Quote
From http://www.codeguru.com/forum/archive/index.php/t-229394.html
Paul McKenzie
02-06-2003, 11:03 PM
Sorry RobAnd, galathaea is correct.
You never mentioned what version of the run-time library you were using. This is the deciding factor, not the source code.
If you are using the DLL version of the C runtime library, you won't get an error, since your app and DLL are using the same heap.
Change your project settings to use the non-DLL version of the runtime library. You will be using two different heap managers -- you can't create a pointer from one heap manager and delete it with another heap manager. Your program will crash, if not now, eventually it will.
This is a well-known problem for years, and has been written about in various articles, most notably in the C/C++ User's Journal a couple of years ago when discussing VC++ and STL (Sorry, I can't find the article).
Also note the many posters on CodeGuru that have come across this problem (there must be at least 50 or so threads dealing with this topic). The remedy is to either use the DLL version of the CRT, or restructure the code so that only one module is responsible for memory allocation / deletion.
Regards,
Paul McKenzie
Sorry RobAnd, galathaea is correct.
You never mentioned what version of the run-time library you were using. This is the deciding factor, not the source code.
If you are using the DLL version of the C runtime library, you won't get an error, since your app and DLL are using the same heap.
Change your project settings to use the non-DLL version of the runtime library. You will be using two different heap managers -- you can't create a pointer from one heap manager and delete it with another heap manager. Your program will crash, if not now, eventually it will.
This is a well-known problem for years, and has been written about in various articles, most notably in the C/C++ User's Journal a couple of years ago when discussing VC++ and STL (Sorry, I can't find the article).
Also note the many posters on CodeGuru that have come across this problem (there must be at least 50 or so threads dealing with this topic). The remedy is to either use the DLL version of the CRT, or restructure the code so that only one module is responsible for memory allocation / deletion.
Regards,
Paul McKenzie
Symbols, how to provide them to the debugger
What does this mean? If you debug something and look at the stack, you will
often see some criptic call levels. If you provide the debugger with the right
symbols, it will show you reasonable names for call levels that are associated
with the provided symbols.
Setting them up in Visual Studio .NET 2003 (VS7.1)
Microsoft "HOW TO: Use a Symbol Server with the Visual Studio .NET Debugger" http://support.microsoft.com/default.aspx?scid=kb;en-us;319037
- Get the symchk utility to get the Microsoft symbols. The utility can be
found with the "Debugging Tools for Windows" at http://www.microsoft.com/ddk/debugging
- [why??] You should add the "Debugging Tools for Windows" directory to your system PATH
- Run symchk from the command line to get the symbols you want. [see below]
- Copy the
symsrv.dll file from the "Debugging Tools for Windows" directory to the
C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE directory.
{yes this looks like it is for VS7.0, but I tested that it works with VS7.1)
- Specify the location of the local symbol cache in your SOLUTIONS properties
Select the Solution | Context Menu | Properties | Debug Symbol Files | {New Folder Icon}
And put a value like this there srv*c:\symbols
# substitute your windows path for winnt where appropriate
#symbols for kernel32.dll
symchk /r c:\winnt\system32\kernel32.dll /s srv*c:\symbols*http://msdl.microsoft.com/download/symbols
# ntdll.dll
symchk /r c:\winnt\system32\ntdll.dll /s srv*c:\symbols*http://msdl.microsoft.com/download/symbols
symchk /r c:\winnt\system32\msvcr71.dll /s srv*c:\symbols*http://msdl.microsoft.com/download/symbols
symchk /r c:\winnt\system32\kernel32.dll /s srv*c:\symbols*http://msdl.microsoft.com/download/symbols
# everything at once !!!
symchk /r c:\winnt\system32 /s srv*c:\symbols*http://msdl.microsoft.com/download/symbols
More useful info
- Tools often look for an environment value to find the symbols. It is
_NT_SYMBOL_PATH. Typically you set it as a System Environment
variable Start | My Computer | Properties | Advanced | Environment Variables | System variables | New
An example value is srv*c:\symbols*http://msdl.microsoft.com/download/symbols
Base info for this was found at "greggm's WebLog" "Bad Native Callstacks" http://blogs.msdn.com/greggm/archive/2004/02/10/71125.aspx