From 31d3a6408d045eadaff094d4c11bf017817743d7 Mon Sep 17 00:00:00 2001 From: sje Date: Wed, 1 Nov 2006 14:28:18 +0000 Subject: git-svn-id: https://server.scottellis.com.au/svn/mim_plugs@4 4f64403b-2f21-0410-a795-97e2b3489a10 --- attache/DebugTools.cpp | 516 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 516 insertions(+) create mode 100644 attache/DebugTools.cpp (limited to 'attache/DebugTools.cpp') diff --git a/attache/DebugTools.cpp b/attache/DebugTools.cpp new file mode 100644 index 0000000..0940a10 --- /dev/null +++ b/attache/DebugTools.cpp @@ -0,0 +1,516 @@ +#include "common.h" +#include "DebugTools.h" +#include "MiniDump.h" +#include "../../include/m_versioninfo.h" +#include "str_utils.h" +#include "options.h" +#include "resource.h" +#include "EnforceFilter.h" + +#include +#include + +#include "bzip2-1.0.3/bzlib.h" +extern "C" void compressStream ( FILE *stream, FILE *zStream ); + +#pragma comment ( lib, "dbghelp.lib" ) + +static char profile_path[MAX_PATH]; + +static char *version_info = 0; + +// used to get plugin version +typedef PLUGININFO * (__cdecl * Miranda_Plugin_Info) ( DWORD mirandaVersion ); + +///////////////////////////// +///// HTTP multipart POST upload support +#include "Uploader.h" +#include "UploadSettings.h" +#include "UploadResults.h" + +TCHAR *PHPErrorMessage(int code) { + TCHAR *error = 0; + switch(code) { + case 1: //UPLOAD_ERR_INI_SIZE + error = _T("The uploaded file exceeds the upload_max_filesize directive in php.ini. "); + break; + case 2: //UPLOAD_ERR_FORM_SIZE + error = _T("The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form."); + break; + case 3: //UPLOAD_ERR_PARTIAL + error = _T("The uploaded file was only partially uploaded."); + break; + case 4: //UPLOAD_ERR_NO_FILE + error = _T("No file was uploaded."); + break; + case 5: + error = _T("Server specific error."); + break; + case 6: //UPLOAD_ERR_NO_TMP_DIR + error = _T("Missing a temporary folder."); + break; + case 7: //UPLOAD_ERR_CANT_WRITE + error = _T("Failed to write file to disk."); + break; + default: + error = _T("Unknown PHP error."); + break; + } + return error; +} + +BOOL CALLBACK CPDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { + switch(msg) { + case WM_INITDIALOG: + { + TranslateDialogDefault(hwndDlg); + SetWindowText(GetDlgItem(hwndDlg, IDC_LNK_BUGS), _T("http://bugs.miranda-im.org")); + + TCHAR *dump_url = (TCHAR *)lParam; // passed via lParam in DialogBoxParam call + SetWindowText(GetDlgItem(hwndDlg, IDC_LNK_DUMP), dump_url); + } + return 0; + case WM_COMMAND: + if(HIWORD(wParam) == BN_CLICKED) { + switch(LOWORD(wParam)) { + case IDYES: + case IDNO: + EndDialog(hwndDlg, LOWORD(wParam)); + break; + case IDC_LNK_BUGS: + case IDC_LNK_DUMP: + { + char buff[512]; + GetDlgItemTextA(hwndDlg, LOWORD(wParam), buff, 512); + CallService(MS_UTILS_OPENURL, 1, (WPARAM)buff); + } + break; + } + return TRUE; + } + break; + + + } + return 0; +} + + +bool HttpUpload(TCHAR *filename, TCHAR *module_filename, TCHAR *module_filename2, TCHAR *module_version, TCHAR *module_version2, PVOID address, PVOID address2) { + + UploadSettings settings; + UploadResults results; + + Uploader uploader(0); + + settings.SetAddress(_T(UPLOAD_URL)); + settings.AddFile(_T("file"), filename, _T("application/x-bzip2")); + // module signature (filename) + if(module_filename && module_filename[0]) settings.AddField(_T("module_signature"), module_filename); + // module signature 2 (filename) + if(module_filename2 && module_filename2[0]) settings.AddField(_T("trace_module_signature"), module_filename2); + // module version (from pluginInfo) + if(module_version && module_version[0] != 0) settings.AddField(_T("module_version"), module_version); + // module version (from pluginInfo) + if(module_version2 && module_version2[0] != 0) settings.AddField(_T("trace_module_version"), module_version2); + // reporter_id + if(options.reporter_id[0]) settings.AddField(_T("reporter_id"), options.reporter_id); + + /* + if(version_info) { + TCHAR *stzVersionInfo = a2t(version_info); + settings.AddField(_T("version_info"), stzVersionInfo); + free(stzVersionInfo); + } + */ + { + // address + TCHAR buff[512]; + mir_sntprintf(buff, 512, "%x", address); + settings.AddField(_T("module_address"), buff); + mir_sntprintf(buff, 512, "%x", address2); + settings.AddField(_T("trace_module_address"), buff); + } + { + // event timestamp + SYSTEMTIME st; + GetSystemTime(&st); + TCHAR buff_dt[512]; + GetDateFormat(0, 0, &st, _T("yyyy-MM-dd' '"), buff_dt, 512); + GetTimeFormat(0, 0, &st, _T("HH:mm:ss"), buff_dt + _tcslen(buff_dt), 512 - _tcslen(buff_dt)); + settings.AddField(_T("datetime_event"), buff_dt); + } + { + // windows version + OSVERSIONINFOEX vi = {0}; + vi.dwOSVersionInfoSize = sizeof(vi); + GetVersionEx((OSVERSIONINFO *)&vi); + + TCHAR buff[512]; + //mir_sntprintf(buff, 512, _T("%d.%d.%d-%d.%d"), vi.dwMajorVersion, vi.dwMinorVersion, vi.dwBuildNumber, vi.wServicePackMajor, vi.wServicePackMinor); + mir_sntprintf(buff, 512, _T("%d.%d.%d"), vi.dwMajorVersion, vi.dwMinorVersion, vi.dwBuildNumber); + settings.AddField(_T("version_os"), buff); + } + + { + // miranda version + TCHAR buff[32]; + mir_sntprintf(buff, 32, _T("%d.%d.%d.%d"), (mirandaVersion >> 24) & 255, (mirandaVersion >> 16) & 255, (mirandaVersion >> 8) & 255, mirandaVersion & 255); + settings.AddField(_T("version_miranda"), buff); + + if(ServiceExists(MS_SYSTEM_GETVERSIONTEXT)) { + char szVer[512]; + CallService(MS_SYSTEM_GETVERSIONTEXT, 512, (LPARAM)szVer); + settings.AddField(_T("is_unicode"), strstr(szVer, "Unicode") == 0 ? _T("0") : _T("1")); + } + } + + { + // attache version + TCHAR buff[32]; + mir_sntprintf(buff, 32, _T("%d.%d.%d.%d"), __MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM); + settings.AddField(_T("version_attache"), buff); + } + + uploader.DoUpload(&settings, &results); + + TCHAR *error = 0; + if(results.GetResult() != S_OK) { + TCHAR buff[1024]; + mir_sntprintf(buff, 1024, TranslateT("HTTP upload failed. Status code: %d"), results.GetStatusCode()); + MessageBox(0, buff, TranslateT("MiniDump Error"), MB_OK | MB_ICONERROR); + return false; + } + + int len = results.GetResponseLength(); + if(len == 0) { + MessageBox(0, TranslateT("Upload succeeded, but there was no response from the server."), TranslateT("MiniDump Error"), MB_OK | MB_ICONERROR); + return false; + } + + // response format is: + // "n URL" or "n errormsg" + // where n is php upload error code (0-7, 5 for server specific), URL is the new url of the uploaded file (not necessarily publically accessible), and errormsg is a text error message + unsigned long code = 0; + char *msg; + int i = 0; + while(i < len && results.GetResponse()[i] >= '0' && results.GetResponse()[i] <= '9') { + code = code * 10 + (results.GetResponse()[i++] - '0'); + } + if(i + 1 < len) { + msg = &results.GetResponse()[i + 1]; + } else { + TCHAR buff[1024]; + mir_sntprintf(buff, 1024, TranslateT("Server error code: %d"), code); + MessageBox(0, buff, TranslateT("MiniDump Error"), MB_OK | MB_ICONERROR); + return false; + } + + TCHAR *tmsg = a2t(msg); + if(code != 0) { + MessageBox(0, TranslateTS(tmsg), TranslateT("MiniDump Error"), MB_OK | MB_ICONERROR); + free(tmsg); + return false; + } + + //TCHAR buff[1024]; + //mir_sntprintf(buff, 1024, TranslateT("Your dump has been successfully stored.\n\"%s\"\n\nCopy URL to clipboard?"), tmsg); + //if(MessageBox(0, buff, TranslateT("MiniDump Upload Succeeded"), MB_YESNO) == IDNO) + if(DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_CP), 0, CPDlgProc, (LPARAM)tmsg) == IDNO) + return true; + + // copy URL to clipboard + if(!OpenClipboard(0)) { + MessageBox(0, TranslateT("Failed to copy URL to clipboard."), TranslateT("MiniDump Error"), MB_OK | MB_ICONERROR); + return false; + } + EmptyClipboard(); + + // get URL + int cch = _tcslen(tmsg); + + // Allocate a global memory object for the text. + HGLOBAL hglbCopy = GlobalAlloc(GMEM_MOVEABLE, (cch + 1) * sizeof(TCHAR)); + if (hglbCopy == NULL) { + free(tmsg); + CloseClipboard(); + MessageBox(0, TranslateT("Failed to copy URL to clipboard."), TranslateT("MiniDump Error"), MB_OK | MB_ICONERROR); + return false; + } + // Lock the handle and copy the text to the buffer. + LPTSTR lptstrCopy = (LPTSTR)GlobalLock(hglbCopy); + memcpy(lptstrCopy, tmsg, cch * sizeof(TCHAR)); + lptstrCopy[cch] = (TCHAR) 0; // null character + GlobalUnlock(hglbCopy); + + free(tmsg); + + // Place the handle on the clipboard. + SetClipboardData(CF_TEXT, hglbCopy); + CloseClipboard(); + + return true; +} +////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Function declarations +// +// Author: Oleg Starodumov (www.debuginfo.com) + +LONG CreateMiniDump( EXCEPTION_POINTERS* pep ); + +LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *lpTopLevelExceptionFilter) { + return CreateMiniDump(lpTopLevelExceptionFilter); +} + +int DebugModulesLoaded(WPARAM wParam, LPARAM lParam) { + if(ServiceExists(MS_VERSIONINFO_GETINFO)) { + CallService(MS_VERSIONINFO_GETINFO, TRUE, (LPARAM)&version_info); + } + return 0; +} + +void InitializeDebug() { + // in case folders plugin unavailable + CallService(MS_DB_GETPROFILEPATH, MAX_PATH, (WPARAM)profile_path); + strcat(profile_path, "\\"); + + SetUnhandledExceptionFilter(MyUnhandledExceptionFilter); + EnforceFilter(true); + + HookEvent(ME_SYSTEM_MODULESLOADED, DebugModulesLoaded); +} + +void UninitializeDebug() { + EnforceFilter(false); + if(version_info) { + mmi.mmi_free(version_info); + version_info = 0; + } +} + +#include + + +/////////////////////////////////////////////////////////////////////////////// +// Directives +// + +/////////////////////////////////////////////////////////////////////////////// +// Custom minidump callback +// +// Author: Oleg Starodumov (www.debuginfo.com) + +BOOL CALLBACK MyMiniDumpCallback(PVOID pParam, const PMINIDUMP_CALLBACK_INPUT pInput, PMINIDUMP_CALLBACK_OUTPUT pOutput) { + BOOL bRet = FALSE; + + // Check parameters + if( pInput == 0 ) + return FALSE; + if( pOutput == 0 ) + return FALSE; + + // Process the callbacks + switch( pInput->CallbackType ) { + case IncludeModuleCallback: + // Include the module into the dump + bRet = TRUE; + break; + + case IncludeThreadCallback: + // Include the thread into the dump + bRet = TRUE; + break; + + case ModuleCallback: + // Does the module have ModuleReferencedByMemory flag set ? + if( !(pOutput->ModuleWriteFlags & ModuleReferencedByMemory) ) { + // No, it does not - exclude it + wprintf( L"Excluding module: %s \n", pInput->Module.FullPath ); + pOutput->ModuleWriteFlags &= (~ModuleWriteModule); + } + + bRet = TRUE; + break; + + case ThreadCallback: + // Include all thread information into the minidump + bRet = TRUE; + break; + + case ThreadExCallback: + // Include this information + bRet = TRUE; + break; + + case MemoryCallback: + // We do not include any information here -> return FALSE + bRet = FALSE; + break; + + //case CancelCallback: + //break; + default: + break; + } + return bRet; +} + +/////////////////////////////////////////////////////////////////////////////// +// Minidump creation function +// +// Author: Oleg Starodumov (www.debuginfo.com) + +LONG CreateMiniDump( EXCEPTION_POINTERS* pep ) { + // Open the file + char szDumpFileName[MAX_PATH]; + char szDate[128], szTime[128]; + + SYSTEMTIME st; + GetSystemTime(&st); + GetDateFormatA(LOCALE_INVARIANT, 0, &st, "yyMMdd", szDate, 128); + GetTimeFormatA(LOCALE_INVARIANT, TIME_NOTIMEMARKER, &st, "HHmmss", szTime, 128); + + if(ServiceExists(MS_FOLDERS_GET_PATH)) { + char *adp = t2a(stzDumpPath); + strcpy(szDumpFileName, adp); + strcat(szDumpFileName, "\\"); + } else { + strcpy(szDumpFileName, profile_path); + } + + strcat(szDumpFileName, "mim-"); + strcat(szDumpFileName, szDate); + strcat(szDumpFileName, szTime); + strcat(szDumpFileName, ".dmp"); + + // get the filename and version of the module that had the exception + TCHAR szModuleFilePath[MAX_PATH], szModuleFileName[MAX_PATH], szModuleFileName2[MAX_PATH], szVersion[32], szVersion2[32]; + szModuleFileName[0] = 0; + szModuleFileName2[0] = 0; + szVersion[0] = 0; + PVOID address = pep->ExceptionRecord->ExceptionAddress, address2 = 0; + + HINSTANCE hInstance; + MEMORY_BASIC_INFORMATION mbi; + if(VirtualQuery( pep->ExceptionRecord->ExceptionAddress, &mbi, sizeof(mbi) )) { + hInstance = (HINSTANCE)(mbi.AllocationBase); + + if(GetModuleFileName(hInstance, szModuleFilePath, MAX_PATH)) { + // strip path + TCHAR *p = _tcsrchr(szModuleFilePath, _T('\\')); + if(p && *p) { + p += 1; + _tcscpy(szModuleFileName, p); + } else + _tcscpy(szModuleFileName, szModuleFilePath); + } + + // get plugin version if possible + Miranda_Plugin_Info Info = (Miranda_Plugin_Info) GetProcAddress(hInstance, "MirandaPluginInfo"); + if(Info) { + PLUGININFO * pi = Info(mirandaVersion); + if(pi) { + mir_sntprintf(szVersion, 32, "%d.%d.%d.%d", (pi->version >> 24) & 255, (pi->version >> 16) & 255, (pi->version >> 8) & 255, pi->version & 255); + } + } + } + + // couldn't get a plugin version from the address at the end of the stack trace - walk up the stack to see if we can get one earlier + if(szVersion[0] == 0) { + struct _EXCEPTION_RECORD *ex = pep->ExceptionRecord->ExceptionRecord; + while(ex && VirtualQuery( ex->ExceptionAddress, &mbi, sizeof(mbi) )) { + hInstance = (HINSTANCE)(mbi.AllocationBase); + + if(GetModuleFileName(hInstance, szModuleFilePath, MAX_PATH)) { + // strip path + TCHAR *p = _tcsrchr(szModuleFilePath, _T('\\')); + if(p && *p) { + p += 1; + _tcscpy(szModuleFileName2, p); + } else + _tcscpy(szModuleFileName2, szModuleFilePath); + } + + // get plugin version if possible + Miranda_Plugin_Info Info = (Miranda_Plugin_Info) GetProcAddress(hInstance, "MirandaPluginInfo"); + if(Info) { + PLUGININFO * pi = Info(mirandaVersion); + if(pi) { + address2 = ex->ExceptionAddress; + mir_sntprintf(szVersion2, 32, "%d.%d.%d.%d", (pi->version >> 24) & 255, (pi->version >> 16) & 255, (pi->version >> 8) & 255, pi->version & 255); + break; + } + } + + ex = ex->ExceptionRecord; + } + } + + HANDLE hFile = CreateFileA(szDumpFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); + + if( ( hFile != NULL ) && ( hFile != INVALID_HANDLE_VALUE ) ) { + // Create the minidump + MINIDUMP_EXCEPTION_INFORMATION mdei; + mdei.ThreadId = GetCurrentThreadId(); + mdei.ExceptionPointers = pep; + mdei.ClientPointers = FALSE; + + MINIDUMP_CALLBACK_INFORMATION mci; + mci.CallbackRoutine = (MINIDUMP_CALLBACK_ROUTINE)MyMiniDumpCallback; + mci.CallbackParam = 0; + + MINIDUMP_TYPE mdt = (MINIDUMP_TYPE)(MiniDumpWithIndirectlyReferencedMemory | MiniDumpScanMemory); + + if(!MiniDumpWriteDump( GetCurrentProcess(), GetCurrentProcessId(), hFile, mdt, (pep != 0) ? &mdei : 0, 0, &mci )) { + TCHAR buff[512]; + mir_sntprintf(buff, 512, _T("Crash in module: %s\nMiniDumpWriteDump failed. Error: %u"), szModuleFileName, GetLastError()); + MessageBox(0, buff, _T("MiniDump Error"), MB_OK | MB_ICONWARNING); + CloseHandle( hFile ); + return EXCEPTION_EXECUTE_HANDLER; + } + + // Close the file + CloseHandle( hFile ); + + // bzip the file + char szBzipFilename[MAX_PATH]; + strcpy(szBzipFilename, szDumpFileName); + strcat(szBzipFilename, ".bz2"); + FILE *fin = fopen(szDumpFileName, "rb"), *fout = fopen(szBzipFilename, "w+b"); + if(fin) { + if(fout) { + compressStream(fin, fout); + DeleteFileA(szDumpFileName); + } else + fclose(fin); + } + + TCHAR *stzBzipFilename = a2t(szBzipFilename); + + TCHAR msg[1024]; + mir_sntprintf(msg, 1024, TranslateT("MiniDump crash dump created.\nCrash in module: %s\n\nUpload to miranda-im.org?"), szModuleFileName, stzBzipFilename); + + // upload? + if(options.upload && (!options.confirm_upload || + MessageBox(0, msg, TranslateT("Attache - Crash Dump"), MB_YESNO | MB_ICONERROR) == IDYES)) + { + // yes... + if(HttpUpload(stzBzipFilename, szModuleFileName, szModuleFileName2, szVersion, szVersion2, address, address2) && options.delete_uploaded) + DeleteFile(stzBzipFilename); + } else { + mir_sntprintf(msg, 1024, TranslateT("Crash in module: %s\nDump file: %s"), szModuleFileName, stzBzipFilename); + MessageBox(0, msg, TranslateT("MiniDump - Crash Dump Created"), MB_OK | MB_ICONERROR); + } + + free(stzBzipFilename); + } else { + TCHAR buff[512]; + mir_sntprintf(buff, 512, _T("Crash in module: %s\nMiniDumpWriteDump failed. Failed to create dump file."), szModuleFileName, GetLastError()); + MessageBox(0, buff, _T("MiniDump Error"), MB_OK | MB_ICONWARNING); + CloseHandle( hFile ); + } + + return EXCEPTION_EXECUTE_HANDLER; +} -- cgit v1.2.3