/* (C) 2003-2007 Willem Jan Hengeveld * Web: http://www.xs4all.nl/~itsme/ * http://wiki.xda-developers.com/ * * $Id$ * * this program attaches to or runs programs on the windows ce device in * debug mode, and dumping all debug events, and dumping the stack when * an exception occors. * */ // todo: // ITWaitForDebugEvent should return multiple events. ... like max 16. // then should get rid of some buffer issues. // // ... sometimes the output is way behind what is happening // // todo: add option to attach to a running process by name. // #include #include #include #include #include "args.h" #include "itsutils.h" #include "stringutils.h" #include "debug.h" #include "dllversion.h" #include "util/boostthread.h" #include "util/process.h" #include #include // !!! I am mixing up handles and processid's quite often. // on WinCE this is not a problem, but this needs to be // fixed before porting to another platform. bool g_bLessInformation= false; bool g_bFilterForProcess= true; bool g_bSysMessagesOnly= false; typedef struct _tagDebugEvent { DWORD dwProcessId; DWORD dwThreadId; DWORD dwDebugEventCode; std::string description; } DebugEvent; DWORD ITGetProcessId(const char *szProcessName); HANDLE ITGetProcessHandle(const char *szProcessName); bool ITStartDebuggingProcess(DWORD dwProcessId, const std::string& exename, const std::string& cmdline, DWORD *pdwDebugger, bool bFilterForProcess, bool bSysMessagesOnly); bool ITWaitForDebugEvent(DWORD dwDebugger, DebugEvent *ev, DWORD *pdwMissedEvents); bool ITStopDebuggingProcess(DWORD dwDebugger); void WriteDebugEvent(DebugEvent *ev); class DebugProcess : public process { DWORD _debugger; std::string _exename; std::string _cmdline; DWORD _pid; public: DebugProcess(DWORD dwProcessId, const std::string& exename, const std::string& cmdline) : _pid(dwProcessId), _exename(exename), _cmdline(cmdline), _debugger(0) { start(); } virtual ~DebugProcess() { stop(); } virtual const char*name() { return "DebugProcess"; } virtual void service() { if (!ITStartDebuggingProcess(_pid, _exename, _cmdline, &_debugger, g_bFilterForProcess, g_bSysMessagesOnly)) throw "ERROR: ITStartDebuggingProcess failed"; DebugEvent ev; DWORD missed= 0; DWORD totalmissed= 0; while (ITWaitForDebugEvent(_debugger, &ev, &missed)) { if (missed) { debug("MISSED %d events\n", missed); totalmissed += missed; } WriteDebugEvent(&ev); } debug("total missed %d events\n", totalmissed); if (!ITStopDebuggingProcess(_debugger)) { throw "ERROR: ITStopDebuggingProcess failed"; } terminateservice(); } void stopdebugger() { if (!ITStopDebuggingProcess(_debugger)) { debug("ERROR: ITStopDebuggingProcess failed\n"); } else { debug("stopped\n"); } } }; class Console { private: std::string buf; int eolcharcount; HANDLE hStdin; HANDLE hStdout; DWORD fdwSaveOldMode; bool _ctrlcpressed; public: Console() { _ctrlcpressed= false; eolcharcount= 0; hStdin= INVALID_HANDLE_VALUE; hStdout= INVALID_HANDLE_VALUE; fdwSaveOldMode= 0; } ~Console() { SetConsoleMode(hStdin, fdwSaveOldMode); } bool init() { DWORD fdwMode; // Get the standard input handle. hStdin = GetStdHandle(STD_INPUT_HANDLE); if (hStdin == INVALID_HANDLE_VALUE) { error("GetStdHandle"); return false; } hStdout = GetStdHandle(STD_OUTPUT_HANDLE); if (hStdout == INVALID_HANDLE_VALUE) { error("GetStdHandle"); return false; } // Save the current input mode, to be restored on exit. if (! GetConsoleMode(hStdin, &fdwSaveOldMode) ) { error("GetConsoleMode"); return false; } // Enable the window and mouse input events. fdwMode = 0; if (! SetConsoleMode(hStdin, fdwMode) ) { error("SetConsoleMode"); return false; } return true; } bool KeyEventProc(KEY_EVENT_RECORD*evt) { if (evt->bKeyDown && ( evt->uChar.AsciiChar || (evt->wVirtualKeyCode=='2') ) ) buf += evt->uChar.AsciiChar; //debug("key d=%d r=%d vk=%02x vs=%02x c=%04x ctrl=%04x\n", evt->bKeyDown, evt->wRepeatCount, evt->wVirtualKeyCode, evt->wVirtualScanCode, evt->uChar.UnicodeChar, evt->dwControlKeyState); if ((evt->dwControlKeyState&(RIGHT_CTRL_PRESSED|LEFT_CTRL_PRESSED))!=0 && evt->wVirtualKeyCode=='C') _ctrlcpressed=true; return true; } bool MouseEventProc(MOUSE_EVENT_RECORD* /*evt*/) { //debug("mouse pos=(%d,%d) btn=%04x ctl=%04x evt=%04x", evt->dwMousePosition.X, evt->dwMousePosition.Y, evt->dwButtonState, evt->dwControlKeyState, evt->dwEventFlags); return true; } bool ResizeEventProc(WINDOW_BUFFER_SIZE_RECORD* /*evt*/) { //debug("resize size=(%d,%d)", evt->dwSize.X, evt->dwSize.Y ); return true; } bool FocusEventProc(FOCUS_EVENT_RECORD* /*evt*/) { //debug("focus = %d", evt->bSetFocus); return true; } bool MenuEventProc(MENU_EVENT_RECORD* /*evt*/) { //debug("menu = %d", evt->dwCommandId); return true; } bool read(std::string& line) { DWORD tStart= GetTickCount(); buf.clear(); while (GetTickCount()-tStart < 50) { INPUT_RECORD irInBuf[128]; DWORD cNumRead; if (! PeekConsoleInput( hStdin, irInBuf, 128, &cNumRead) ) { error("PeekConsoleInput"); return false; } if (cNumRead==0) break; if (! ReadConsoleInput( hStdin, irInBuf, 128, &cNumRead) ) { error("ReadConsoleInput"); return false; } for (size_t i=0 ; i\n"); printf(" or: pdebug [options] -x [--] [cmdline args]\n"); printf(" -q : output only to logfile\n"); printf(" -o FILENAME : specify logfile to output debug events to\n"); printf(" -h HANDLE : specify handle of running process to attach to\n"); printf(" -x EXENAME : run, and attach to new process\n"); printf(" -a : show messages from all processes\n"); printf(" -n : sys systemmessages from all processes ( no-debugmessages )\n"); printf(" -s : less verbose output\n"); printf(" -- : don't process options after this\n"); } int main(int argc, char *argv[]) { bool bStartProcess= false; DWORD dwProcessId= 0; std::string exename; std::string cmdline; char *szLogFile= NULL; bool bQuiet= false; bool bProcessOptions= true; int argsfound=0; for (int i=1 ; i1 && !bStartProcess)) { usage(); return 1; } CheckITSDll(); if (szLogFile) DebugSetLogfile(szLogFile); if (!bQuiet) DebugStdOut(); if (dwProcessId==0 && !bStartProcess) { std::string processname; size_t ls= exename.find_last_of('\\'); if (ls==std::string::npos) processname= exename; else processname= exename.substr(ls+1); dwProcessId= ITGetProcessId(processname.c_str()); } Console cons; if (!cons.init()) { error("error initializing console"); return 1; } DebugProcess dbg(dwProcessId, exename, cmdline); std::string reqline; bool quit_program= false; while (!dbg.hasstopped() && !cons.ctrlcpressed() && !quit_program) { std::string request; if (!cons.read(request)) { error("error in console"); return 1; } cons.showreq(request); reqline += request; size_t crpos= reqline.find('\r'); if (crpos!=reqline.npos) { reqline.resize(crpos); if (reqline == "quit") { printf("stopping debugger\n"); dbg.stopdebugger(); quit_program=true; } } Sleep(10); } StopItsutils(); return 0; } bool ITStartDebuggingProcess(DWORD dwProcessId, const std::string& exename, const std::string& cmdline, DWORD *pdwDebugger, bool bFilterForProcess, bool bSysMessagesOnly) { StartDebuggingProcessParams inbuf; memset(&inbuf, 0, sizeof(inbuf)); inbuf.dwProcessId= dwProcessId; inbuf.bFilterForProcess= bFilterForProcess; inbuf.bSysMessagesOnly= bSysMessagesOnly; if (dwProcessId==0) { strncpy(inbuf.exename, exename.c_str(), sizeof(inbuf.exename)); strncpy(inbuf.cmdline, cmdline.c_str(), sizeof(inbuf.cmdline)); } StartDebuggingProcessResult *outbuf; DWORD outsize= 0; HRESULT res= ItsutilsInvoke("ITStartDebuggingProcess", sizeof(StartDebuggingProcessParams), (BYTE*)&inbuf, &outsize, (BYTE**)&outbuf); if (res || outbuf==NULL) { error(res, "ITStartDebuggingProcess(%08lx, %s, %s)", dwProcessId, exename.c_str(), cmdline.c_str()); return false; } *pdwDebugger= outbuf->debugger; RapiFree(outbuf); return true; } bool ITWaitForDebugEvent(DWORD dwDebugger, DebugEvent *ev, DWORD *pdwMissedEvents) { WaitForDebugEventParams inbuf; WaitForDebugEventResult *outbuf=NULL; DWORD outsize=0; inbuf.debugger= dwDebugger; HRESULT res= ItsutilsInvoke("ITWaitForDebugEvent", sizeof(WaitForDebugEventParams), (BYTE*)&inbuf, &outsize, (BYTE**)&outbuf); if (res==-1) return false; if (res || outbuf==NULL) { error(res, "ITWaitForDebugEvent"); return false; } ev->dwProcessId= outbuf->dwProcessId; ev->dwThreadId= outbuf->dwThreadId; ev->dwDebugEventCode= outbuf->dwDebugEventCode; ev->description.resize(outbuf->len); memcpy(stringptr(ev->description), outbuf->description, outbuf->len); *pdwMissedEvents= outbuf->dwMissedEvents; RapiFree(outbuf); return true; } bool ITStopDebuggingProcess(DWORD dwDebugger) { StopDebuggingProcessParams inbuf; inbuf.debugger= dwDebugger; DWORD outsize=0; HRESULT res= ItsutilsInvoke("ITStopDebuggingProcess", sizeof(StopDebuggingProcessParams), (BYTE*)&inbuf, &outsize, NULL); if (res) { error(res, "ITStopDebuggingProcess"); return false; } return true; } void WriteDebugEvent(DebugEvent *ev) { if (g_bLessInformation) debug("%s\n", ev->description.c_str()); else debug("%08lx:%08lx %d %s\n", ev->dwProcessId, ev->dwThreadId, ev->dwDebugEventCode, ev->description.c_str()); } DWORD ITGetProcessId(const char *szProcessName) { return (DWORD)ITGetProcessHandle(szProcessName); } HANDLE ITGetProcessHandle(const char *szProcessName) { std::wstring wstr = ToWString(szProcessName); DWORD insize= (wstr.size()+1)*sizeof(WCHAR); WCHAR *inbuf= (WCHAR*)RapiAlloc(insize); wcscpy(inbuf, wstr.c_str()); DWORD outsize=0; HANDLE *outbuf=NULL; HRESULT res= ItsutilsInvoke("ITGetProcessHandle", insize, (BYTE*)inbuf, &outsize, (BYTE**)&outbuf); if (res || outbuf==NULL) { error(res, "ITGetProcessHandle"); return INVALID_HANDLE_VALUE; } HANDLE hproc= *outbuf; RapiFree(outbuf); return hproc; }