utility.h
//
// Visual Leak Detector - Various Utility Definitions
// Copyright (c) 2005-2006 Dan Moulding
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
//
// See COPYING.txt for the full terms of the GNU Lesser General Public License.
//
#pragma once
#include <cstdio>
#include <windows.h>
#ifdef _WIN64
#define ADDRESSFORMAT L"0x%.16X" // Format string for 64-bit addresses
#else
#define ADDRESSFORMAT L"0x%.8X" // Format string for 32-bit addresses
#endif // _WIN64
#define BOM 0xFEFF // Unicode byte-order mark.
#define MAXREPORTLENGTH 511 // Maximum length, in characters, of "report" messages.
// Architecture-specific definitions for x86 and x64
#if defined(_M_IX86)
#define SIZEOFPTR 4
#define X86X64ARCHITECTURE IMAGE_FILE_MACHINE_I386
#define AXREG Eax
#define BPREG Ebp
#define IPREG Eip
#define SPREG Esp
#elif defined(_M_X64)
#define SIZEOFPTR 8
#define X86X64ARCHITECTURE IMAGE_FILE_MACHINE_AMD64
#define AXREG Rax
#define BPREG Rbp
#define IPREG Rip
#define SPREG Rsp
#endif // _M_IX86
#if defined(_M_IX86) || defined (_M_X64)
#define FRAMEPOINTER(fp) __asm {mov fp, BPREG} // Copies the current frame pointer to the supplied variable.
#else
// If you want to retarget Visual Leak Detector to another processor
// architecture then you'll need to provide an architecture-specific macro to
// obtain the frame pointer (or other address) which can be used to obtain the
// return address and stack pointer of the calling frame.
#error "Visual Leak Detector is not supported on this architecture."
#endif // _M_IX86 || _M_X64
// Miscellaneous definitions
#define R2VA(modulebase, rva) (((PBYTE)modulebase) + rva) // Relative Virtual Address to Virtual Address conversion.
#define BYTEFORMATBUFFERLENGTH 4
#define HEXDUMPLINELENGTH 58
// Reports can be encoded as either ASCII or Unicode (UTF-16).
enum encoding_e {
ascii,
unicode
};
// Utility functions. See function definitions for details.
VOID dumpmemorya (LPCVOID address, SIZE_T length);
VOID dumpmemoryw (LPCVOID address, SIZE_T length);
VOID report (LPCWSTR format, ...);
VOID setreportencoding (encoding_e encoding);
VOID setreportfile (FILE *file, BOOL copydebugger);
VOID str_append (LPWSTR *dest, LPCWSTR source);
//
// Visual Leak Detector - Various Utility Functions
// Copyright (c) 2005-2009 Dan Moulding
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
//
// See COPYING.txt for the full terms of the GNU Lesser General Public License.
//
#include <cassert>
#include <cstdio>
#include <windows.h>
#if _WIN32_WINNT < 0x0600 // Windows XP or earlier, no GetProcessIdOfThread()
#include <winternl.h>
#endif
#ifndef __out_xcount
#define __out_xcount(x) // Workaround for the specstrings.h bug in the Platform SDK.
#endif
#define DBGHELP_TRANSLATE_TCHAR
#include <dbghelp.h> // Provides portable executable (PE) image access functions.
#define VLDBUILD // Declares that we are building Visual Leak Detector.
#include "utility.h" // Provides various utility functions and macros.
// Global variables.
static BOOL reportdelay = FALSE; // If TRUE, we sleep for a bit after calling OutputDebugString to give the debugger time to catch up.
static FILE *reportfile = NULL; // Pointer to the file, if any, to send the memory leak report to.
static BOOL reporttodebugger = TRUE; // If TRUE, a copy of the memory leak report will be sent to the debugger for display.
static encoding_e reportencoding = ascii; // Output encoding of the memory leak report.
// dumpmemorya - Dumps a nicely formatted rendition of a region of memory.
// Includes both the hex value of each byte and its ASCII equivalent (if
// printable).
//
// - address (IN): Pointer to the beginning of the memory region to dump.
//
// - size (IN): The size, in bytes, of the region to dump.
//
// Return Value:
//
// None.
//
VOID dumpmemorya (LPCVOID address, SIZE_T size)
{
WCHAR ascdump [18] = {0};
SIZE_T ascindex;
BYTE byte;
SIZE_T byteindex;
SIZE_T bytesdone;
SIZE_T dumplen;
WCHAR formatbuf [BYTEFORMATBUFFERLENGTH];
WCHAR hexdump [HEXDUMPLINELENGTH] = {0};
SIZE_T hexindex;
// Each line of output is 16 bytes.
if ((size % 16) == 0) {
// No padding needed.
dumplen = size;
}
else {
// We'll need to pad the last line out to 16 bytes.
dumplen = size + (16 - (size % 16));
}
// For each byte of data, get both the ASCII equivalent (if it is a
// printable character) and the hex representation.
bytesdone = 0;
for (byteindex = 0; byteindex < dumplen; byteindex++) {
hexindex = 3 * ((byteindex % 16) + ((byteindex % 16) / 4)); // 3 characters per byte, plus a 3-character space after every 4 bytes.
ascindex = (byteindex % 16) + (byteindex % 16) / 8; // 1 character per byte, plus a 1-character space after every 8 bytes.
if (byteindex < size) {
byte = ((PBYTE)address)[byteindex];
_snwprintf_s(formatbuf, BYTEFORMATBUFFERLENGTH, _TRUNCATE, L"%.2X ", byte);
formatbuf[3] = '\0';
wcsncpy_s(hexdump + hexindex, HEXDUMPLINELENGTH - hexindex, formatbuf, 4);
if (isgraph(byte)) {
ascdump[ascindex] = (WCHAR)byte;
}
else {
ascdump[ascindex] = L'.';
}
}
else {
// Add padding to fill out the last line to 16 bytes.
wcsncpy_s(hexdump + hexindex, HEXDUMPLINELENGTH - hexindex, L" ", 4);
ascdump[ascindex] = L'.';
}
bytesdone++;
if ((bytesdone % 16) == 0) {
// Print one line of data for every 16 bytes. Include the
// ASCII dump and the hex dump side-by-side.
report(L" %s %s\n", hexdump, ascdump);
}
else {
if ((bytesdone % 8) == 0) {
// Add a spacer in the ASCII dump after every 8 bytes.
ascdump[ascindex + 1] = L' ';
}
if ((bytesdone % 4) == 0) {
// Add a spacer in the hex dump after every 4 bytes.
wcsncpy_s(hexdump + hexindex + 3, HEXDUMPLINELENGTH - hexindex - 3, L" ", 4);
}
}
}
}
// dumpmemoryw - Dumps a nicely formatted rendition of a region of memory.
// Includes both the hex value of each byte and its Unicode equivalent.
//
// - address (IN): Pointer to the beginning of the memory region to dump.
//
// - size (IN): The size, in bytes, of the region to dump.
//
// Return Value:
//
// None.
//
VOID dumpmemoryw (LPCVOID address, SIZE_T size)
{
BYTE byte;
SIZE_T byteindex;
SIZE_T bytesdone;
SIZE_T dumplen;
WCHAR formatbuf [BYTEFORMATBUFFERLENGTH];
WCHAR hexdump [HEXDUMPLINELENGTH] = {0};
SIZE_T hexindex;
WORD word;
WCHAR unidump [18] = {0};
SIZE_T uniindex;
// Each line of output is 16 bytes.
if ((size % 16) == 0) {
// No padding needed.
dumplen = size;
}
else {
// We'll need to pad the last line out to 16 bytes.
dumplen = size + (16 - (size % 16));
}
// For each word of data, get both the Unicode equivalent and the hex
// representation.
bytesdone = 0;
for (byteindex = 0; byteindex < dumplen; byteindex++) {
hexindex = 3 * ((byteindex % 16) + ((byteindex % 16) / 4)); // 3 characters per byte, plus a 3-character space after every 4 bytes.
uniindex = ((byteindex / 2) % 8) + ((byteindex / 2) % 8) / 8; // 1 character every other byte, plus a 1-character space after every 8 bytes.
if (byteindex < size) {
byte = ((PBYTE)address)[byteindex];
_snwprintf_s(formatbuf, BYTEFORMATBUFFERLENGTH, _TRUNCATE, L"%.2X ", byte);
formatbuf[BYTEFORMATBUFFERLENGTH - 1] = '\0';
wcsncpy_s(hexdump + hexindex, HEXDUMPLINELENGTH - hexindex, formatbuf, 4);
if (((byteindex % 2) == 0) && ((byteindex + 1) < dumplen)) {
// On every even byte, print one character.
word = ((PWORD)address)[byteindex / 2];
if ((word == 0x0000) || (word == 0x0020)) {
unidump[uniindex] = L'.';
}
else {
unidump[uniindex] = word;
}
}
}
else {
// Add padding to fill out the last line to 16 bytes.
wcsncpy_s(hexdump + hexindex, HEXDUMPLINELENGTH - hexindex, L" ", 4);
unidump[uniindex] = L'.';
}
bytesdone++;
if ((bytesdone % 16) == 0) {
// Print one line of data for every 16 bytes. Include the
// ASCII dump and the hex dump side-by-side.
report(L" %s %s\n", hexdump, unidump);
}
else {
if ((bytesdone % 8) == 0) {
// Add a spacer in the ASCII dump after every 8 bytes.
unidump[uniindex + 1] = L' ';
}
if ((bytesdone % 4) == 0) {
// Add a spacer in the hex dump after every 4 bytes.
wcsncpy_s(hexdump + hexindex + 3, HEXDUMPLINELENGTH - hexindex - 3, L" ", 4);
}
}
}
}
// report - Sends a printf-style formatted message to the debugger for display
// and/or to a file.
//
// Note: A message longer than MAXREPORTLENGTH characters will be truncated
// to MAXREPORTLENGTH.
//
// - format (IN): Specifies a printf-compliant format string containing the
// message to be sent to the debugger.
//
// - ... (IN): Arguments to be formatted using the specified format string.
//
// Return Value:
//
// None.
//
VOID report (LPCWSTR format, ...)
{
va_list args;
size_t count;
CHAR messagea [MAXREPORTLENGTH + 1];
WCHAR messagew [MAXREPORTLENGTH + 1];
va_start(args, format);
_vsnwprintf_s(messagew, MAXREPORTLENGTH + 1, _TRUNCATE, format, args);
va_end(args);
messagew[MAXREPORTLENGTH] = L'\0';
if (reportencoding == unicode) {
if (reportfile != NULL) {
// Send the report to the previously specified file.
fwrite(messagew, sizeof(WCHAR), wcslen(messagew), reportfile);
}
if (reporttodebugger) {
OutputDebugStringW(messagew);
wprintf(messagew);
}
}
else {
if (wcstombs_s(&count, messagea, MAXREPORTLENGTH + 1, messagew, _TRUNCATE) == -1) {
// Failed to convert the Unicode message to ASCII.
assert(FALSE);
return;
}
messagea[MAXREPORTLENGTH] = '\0';
if (reportfile != NULL) {
// Send the report to the previously specified file.
fwrite(messagea, sizeof(CHAR), strlen(messagea), reportfile);
}
if (reporttodebugger) {
OutputDebugStringA(messagea);
printf(messagea);
}
}
if (reporttodebugger && (reportdelay == TRUE)) {
Sleep(10); // Workaround the Visual Studio 6 bug where debug strings are sometimes lost if they're sent too fast.
}
}
// setreportencoding - Sets the output encoding of report messages to either
// ASCII (the default) or Unicode.
//
// - encoding (IN): Specifies either "ascii" or "unicode".
//
// Return Value:
//
// None.
//
VOID setreportencoding (encoding_e encoding)
{
switch (encoding) {
case ascii:
case unicode:
reportencoding = encoding;
break;
default:
assert(FALSE);
}
}
// setreportfile - Sets a destination file to which all report messages should
// be sent. If this function is not called to set a destination file, then
// report messages will be sent to the debugger instead of to a file.
//
// - file (IN): Pointer to an open file, to which future report messages should
// be sent.
//
// - copydebugger (IN): If true, in addition to sending report messages to
// the specified file, a copy of each message will also be sent to the
// debugger.
//
// Return Value:
//
// None.
//
VOID setreportfile (FILE *file, BOOL copydebugger)
{
reportfile = file;
reporttodebugger = copydebugger;
}
// str_append - Appends the specified source string to the specified destination
// string. Allocates additional space so that the destination string "grows"
// as new strings are appended to it. This function is fairly infrequently
// used so efficiency is not a major concern.
//
// - dest (IN/OUT): Address of the destination string. Receives the resulting
// combined string after the append operation.
//
// - source (IN): Source string to be appended to the destination string.
//
// Return Value:
//
// None.
//
VOID str_append (LPWSTR *dest, LPCWSTR source)
{
SIZE_T length;
LPWSTR temp;
temp = *dest;
length = wcslen(*dest) + wcslen(source);
*dest = new WCHAR [length + 1];
wcsncpy_s(*dest, length + 1, temp, _TRUNCATE);
wcsncat_s(*dest, length + 1, source, _TRUNCATE);
delete [] temp;
}
typedef struct Person{
int id;
int age;
char name[10];
char city[10];
} Person;
struct Student:Person{
int score;
char classroom[10];
} Student;
struct Teacher:Person{
int level;
char subject[10];
} Teacher;
void test_a()
{
FILE* fp=fopen("log_a.txt","w+");
setreportfile(fp,TRUE);
setreportencoding(ascii);
Person p;
p.id=1024;
p.age=26;
strcpy(p.name,"yunshouhu");
strcpy(p.city,"shenzhen");
cout << "==========Person============" << endl;
dumpmemorya(&p,sizeof(p));
cout << "======Student================" << endl;
struct Student stu;
stu.id=1048;
stu.age=27;
strcpy(stu.name,"yunshouhu001");
strcpy(stu.city,"北京");
stu.score=99;
strcpy(stu.classroom,"一年级二班");
dumpmemorya(&stu,sizeof(stu));
struct Teacher teacher;
teacher.id=p.id;
teacher.age=p.age;
strcpy(teacher.name,p.name);
strcpy(teacher.city,p.city);
strcpy(teacher.subject,"chinese");
teacher.level=3;
cout << "==========teacher============" << endl;
dumpmemorya(&teacher,sizeof(teacher));
int a=512;
cout << "==========int============" << endl;
dumpmemorya(&a,sizeof(a));
int* aa=&a;
cout << "==========int aa============" << endl;
dumpmemorya(aa,sizeof(aa));
int aaa=*aa;
cout << "==========int aaa============" << endl;
dumpmemorya(&aaa,sizeof(aaa));
float b=33.3f;
cout << "==========float============" << endl;
dumpmemorya(&b,sizeof(float));
char c='c';
cout << "==========char============" << endl;
dumpmemorya(&c,sizeof(char));
}
void test_b()
{
FILE* fp=fopen("log_w.txt","w+");
setreportfile(fp,TRUE);
setreportencoding(unicode);
Person p;
p.id=1024;
p.age=26;
strcpy(p.name,"yunshouhu");
strcpy(p.city,"shenzhen");
cout << "==========Person============" << endl;
dumpmemoryw(&p,sizeof(p));
cout << "======Student================" << endl;
struct Student stu;
stu.id=1048;
stu.age=27;
strcpy(stu.name,"yunshouhu001");
strcpy(stu.city,"");
stu.score=99;
strcpy(stu.classroom,"");
dumpmemoryw(&stu,sizeof(stu));
struct Teacher teacher;
teacher.id=p.id;
teacher.age=p.age;
strcpy(teacher.name,p.name);
strcpy(teacher.city,p.city);
strcpy(teacher.subject,"chinese");
teacher.level=3;
cout << "==========teacher============" << endl;
dumpmemoryw(&teacher,sizeof(teacher));
int a=512;
cout << "==========int============" << endl;
dumpmemoryw(&a,sizeof(a));
int* aa=&a;
cout << "==========int aa============" << endl;
dumpmemoryw(aa,sizeof(aa));
int aaa=*aa;
cout << "==========int aaa============" << endl;
dumpmemoryw(&aaa,sizeof(aaa));
float b=33.3f;
cout << "==========float============" << endl;
dumpmemoryw(&b,sizeof(float));
char c='c';
cout << "==========char============" << endl;
dumpmemoryw(&c,sizeof(char));
}