// ReleaseTracer.cpp : コンソール アプリケーション用のエントリ ポイントの定義
//
/*
Main Function:
this programe catches the debug events and output the debug info.
this can works even for release version EXE files.
Any suggestion will be welcome.
*/
#include "stdafx.h"
#include "windows.h"
#include <iostream>
#include <string>
#include <iomanip>
using namespace std;
struct ExpName { DWORD ExpID; const char* ExpDescription; };
ExpName gExceptions[] =
{ { EXCEPTION_ACCESS_VIOLATION, "Access Violation." }
, { EXCEPTION_BREAKPOINT, "Breakpoint was encountered" }
, { EXCEPTION_DATATYPE_MISALIGNMENT, "Misaligned data access." }
, { EXCEPTION_SINGLE_STEP, "Single-Step Instruction." }
, { EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "Array Index is Out Of Bounds" }
, { EXCEPTION_FLT_DENORMAL_OPERAND, "Floating-Point op on Denormal value." }
, { EXCEPTION_FLT_DIVIDE_BY_ZERO, "Floating-Point Divide by Zero" }
, { EXCEPTION_FLT_INEXACT_RESULT, "Floating-Point Inexact Result" }
, { EXCEPTION_FLT_INVALID_OPERATION, "Invalid Floating-Point Operation" }
, { EXCEPTION_FLT_OVERFLOW, "Floating-Point Overflow" }
, { EXCEPTION_FLT_STACK_CHECK, "Floating Point Stack Error" }
, { EXCEPTION_FLT_UNDERFLOW, "Floating-Point Underflow" }
, { EXCEPTION_INT_DIVIDE_BY_ZERO, "Integer Divide by Zero" }
, { EXCEPTION_INT_OVERFLOW, "Integer Overflow" }
, { EXCEPTION_PRIV_INSTRUCTION, "Attempt to execute Private Instruction" }
, { EXCEPTION_NONCONTINUABLE_EXCEPTION, "Non-Continuable Exception" }
, { 0x0eedfade, "Language Exception" } // at least this is what my compiler uses
, { 0, "Unknown Exception" }
};
const char* getExpDescription(DWORD code)
{
ExpName* scan = gExceptions;
while( scan->ExpID && scan->ExpID!=code ) ++scan;
return scan->ExpDescription;
}
bool getProcessMem(HANDLE hProcess, const void* clientAddr, void* buf, int size)
{
DWORD nRead;
return ReadProcessMemory(hProcess, clientAddr, buf, size, &nRead) && nRead==(unsigned)size;
}
string getProcessCString(HANDLE hProcess, bool bUnicode, LPSTR clientAddr, int size=0)
{
#define MAXSTRINGLEN 4096
char buf[MAXSTRINGLEN];
if( size<=0 || size>sizeof(buf)) size = sizeof(buf);
DWORD nRead;
if( !clientAddr || !ReadProcessMemory( hProcess, clientAddr, buf, size, &nRead )) return std::string();
if( bUnicode )//not so good but fast
{
nRead /= 2;
unsigned int i = 0;
while( i!=nRead){
buf[i] = buf[2*i];
if (!buf[i])
{
break;
}
++i;
}
}
return buf;
}
int main(int argc, char* argv[])
{
if(argc<=1) {
cout<<"Usage: ReleaseTracer XXX.exe -option(for example: ReleaseTracer Kaikei.exe -d)."<< endl;
cout<<"Option:"<< endl;
cout<<" -d Trace the DLL loading and unloading event."<< endl;
cout<<" -t Trace the string output event."<< endl;
cout<<" -e Trace the exception event."<< endl;
return 1;
}
cout << "Starting up" << endl;
LPTSTR lpstrcmd = argv[1];
/*
LPTSTR lpstrcmd = GetCommandLine();
while( isspace(*lpstrcmd) ) ++lpstrcmd;
if( '"' == *lpstrcmd ) {
do ++lpstrcmd; while( '"' != *lpstrcmd);
++lpstrcmd;
}
else do ++lpstrcmd; while( ! isspace(*lpstrcmd) );
while( isspace(*lpstrcmd) ) ++lpstrcmd;
*/
enum enumTrace {STRING,DLL,EXCEPT};
enumTrace enumTraceType = STRING;
if(argc == 3) {
if(argv[2][1] == 't'||argv[2][1] == 'T') {
enumTraceType = STRING;
} else if(argv[2][1] == 'd'||argv[2][1] == 'D') {
enumTraceType = DLL;
} else if(argv[2][1] == 'e'||argv[2][1] == 'E') {
enumTraceType = EXCEPT;
}
}
STARTUPINFO si;
::memset(&si,0,sizeof(STARTUPINFO));
si.cb = sizeof(si);
::GetStartupInfo(&si);
si.cb = sizeof(si);
si.dwFlags = STARTF_FORCEONFEEDBACK | STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOWNORMAL;
PROCESS_INFORMATION pi;
::memset(&pi,0,sizeof(PROCESS_INFORMATION));
BOOL bCreateProcess = ::CreateProcess
( 0, lpstrcmd, 0, 0, false
, DEBUG_ONLY_THIS_PROCESS
, 0, 0, &si, &pi
);
if( !bCreateProcess ) {
cout << "Process Launch failed." << endl;
return -1;
}
cout << "Process Launched..." << endl;
cout << setbase(16);
int bRet = -1;
DEBUG_EVENT de;
while(::WaitForDebugEvent(&de, INFINITE), true )
{
bool cont = true;
switch(de.dwDebugEventCode) {
case EXCEPTION_DEBUG_EVENT:
if(enumTraceType == EXCEPT){
const DWORD xcode = de.u.Exception.ExceptionRecord.ExceptionCode;
const bool nonCont = EXCEPTION_NONCONTINUABLE == de.u.Exception.ExceptionRecord.ExceptionFlags;
const bool firstChance = !! de.u.Exception.dwFirstChance;
cout<< "EXCEPTION " << (nonCont?"noncont":"cont")<<"-"<<(firstChance?"first":"final")
<<"@"<<de.u.Exception.ExceptionRecord.ExceptionAddress
<<":"<<getExpDescription(de.u.Exception.ExceptionRecord.ExceptionCode)<< endl;
if( firstChance && xcode!=EXCEPTION_BREAKPOINT )
cont = false;
if( xcode == EXCEPTION_NONCONTINUABLE_EXCEPTION )
{
cout << "Unexpected error: programe to be terminated" << endl;
TerminateProcess(pi.hProcess,(UINT)-1);
bRet = 2;
}
}
break;
case OUTPUT_DEBUG_STRING_EVENT:
if(enumTraceType == STRING){
const OUTPUT_DEBUG_STRING_INFO& si = de.u.DebugString;
cout << getProcessCString( pi.hProcess, !!si.fUnicode, si.lpDebugStringData, si.nDebugStringLength )
<< std::endl;
}
break;
case CREATE_PROCESS_DEBUG_EVENT:
{
cout << "Process Created" << endl;
}
break;
case CREATE_THREAD_DEBUG_EVENT:
{
cout << "Thread Created" << endl;
}
break;
case EXIT_THREAD_DEBUG_EVENT:
{
cout << "Thread Exit: " << de.u.ExitProcess.dwExitCode << endl;
}
break;
case EXIT_PROCESS_DEBUG_EVENT:
{
const DWORD exitValue = de.u.ExitThread.dwExitCode;
cout << "Process Exit: " << exitValue << endl;
if(bRet == -1)
bRet = (exitValue==0) ? 0 : 1;
}
break;
case LOAD_DLL_DEBUG_EVENT:
if(enumTraceType == DLL)
{
LPSTR clientAddr;
std::string sName;
if(!!de.u.LoadDll.lpImageName&&getProcessMem(pi.hProcess, de.u.LoadDll.lpImageName, &clientAddr, sizeof(clientAddr))&&!!clientAddr)
sName = getProcessCString(pi.hProcess, !! de.u.LoadDll.fUnicode , clientAddr, 128 );
if( sName.empty() )
sName = "<unknown>";
cout << "DLL Loaded: basePointer"<<de.u.LoadDll.lpBaseOfDll << " " <<sName << endl;
}
break;
case UNLOAD_DLL_DEBUG_EVENT:
if(enumTraceType == DLL)
{
cout << "DLL Unloaded: basePointer"<<de.u.LoadDll.lpBaseOfDll << " " <<endl;
}
break;
default:
cout << "Unknown Event Code: "<<de.dwDebugEventCode << endl;
};
if( bRet != -1 )
break;
BOOL bContinue = ContinueDebugEvent(de.dwProcessId, de.dwThreadId, cont?DBG_CONTINUE:DBG_EXCEPTION_NOT_HANDLED);
if( !bContinue ) { cout << "Fail in ContinueDebugEvent"; break; }
};
cout << "Result: ";
switch(bRet) {
case 0: cout << "OK - Completed normally."; break;
case 1: cout << "Warn - Non-zero error failure."; break;
case 2: cout << "Fail - Fatal error,to be exterminated."; break;
default: cout <<"Unexpected error."; break;
}
cout << endl;
return bRet;
}