static void AppendVal(const char *val, char escapeChar, bool compact, str::Str<char>& res) { const char *start = val; const char *s = start; char escaped = 0; while (*s) { char c = *s++; if (escapeChar == c) escaped = escapeChar; else if (']' == c) escaped = ']'; else if ('[' == c) escaped = '['; else if ('\n' == c) escaped = 'n'; else if ('\r' == c) escaped = 'r'; if (0 == escaped) continue; size_t len = s - start - 1; res.Append(start, len); res.Append(escapeChar); res.Append(escaped); start = s; escaped = 0; } size_t len = s - start; res.Append(start, len); if (!compact) res.Append(NL); }
void GetThreadCallstack(str::Str<char>& s, DWORD threadId) { if (threadId == GetCurrentThreadId()) return; s.AppendFmt("\r\nThread: %x\r\n", threadId); DWORD access = THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION | THREAD_SUSPEND_RESUME; HANDLE hThread = OpenThread(access, false, threadId); if (!hThread) { s.Append("Failed to OpenThread()\r\n"); return; } DWORD res = SuspendThread(hThread); if (-1 == res) { s.Append("Failed to SuspendThread()\r\n"); } else { CONTEXT ctx = { 0 }; ctx.ContextFlags = CONTEXT_FULL; BOOL ok = GetThreadContext(hThread, &ctx); if (ok) GetCallstack(s, ctx, hThread); else s.Append("Failed to GetThreadContext()\r\n"); ResumeThread(hThread); } CloseHandle(hThread); }
static void PrettyPrintNode(TxtNode *curr, int nest, str::Str<char>& res) { if (TextNode == curr->type) { PrettyPrintKeyVal(curr, nest, res); return; } if (StructNode == curr->type) { PrettyPrintKeyVal(curr, nest, res); res.Append(" + [\n"); } else if (nest >= 0) { CrashIf(ArrayNode != curr->type); AppendNest(res, nest); res.Append("[\n"); } TxtNode *child; for (size_t i = 0; i < curr->children->Count(); i++) { child = curr->children->At(i); PrettyPrintNode(child, nest + 1, res); } if (nest >= 0) { AppendNest(res, nest); res.Append("]\n"); } }
static void GetInternedStringsReport(str::Str<char>& resOut) { resOut.Append("Strings:\n"); int n = g_strInterner.StringsCount(); for (int i = 0; i < n; i++) { resOut.AppendFmt("%d|%s\n", i, g_strInterner.GetByIndex(i)); } resOut.Append("\n"); }
void GetProgramInfo(str::Str<char>& s) { s.AppendFmt("Ver: %s", QM(CURR_VERSION)); #ifdef SVN_PRE_RELEASE_VER s.AppendFmt(".%s pre-release", QM(SVN_PRE_RELEASE_VER)); #endif #ifdef DEBUG s.Append(" dbg"); #endif s.Append("\r\n"); }
void GetProgramInfo(str::Str<char>& s) { s.AppendFmt("Ver: %s", CURR_VERSION_STRA); #ifdef SVN_PRE_RELEASE_VER s.AppendFmt(" pre-release"); #endif #ifdef DEBUG if (!str::EndsWith(s.Get(), " (dbg)")) s.Append(" (dbg)"); #endif s.Append("\r\n"); }
static void PrettyPrintKeyVal(TxtNode *curr, int nest, str::Str<char>& res) { AppendNest(res, nest); if (curr->keyStart) { AppendWsTrimEnd(res, curr->keyStart, curr->keyEnd); if (StructNode != curr->type) res.Append(" + "); } AppendWsTrimEnd(res, curr->valStart, curr->valEnd); if (StructNode != curr->type) res.Append("\n"); }
static bool GetStackFrameInfo(str::Str<char>& s, STACKFRAME64 *stackFrame, CONTEXT *ctx, HANDLE hThread) { #if defined(_WIN64) int machineType = IMAGE_FILE_MACHINE_AMD64; #else int machineType = IMAGE_FILE_MACHINE_I386; #endif BOOL ok = _StackWalk64(machineType, GetCurrentProcess(), hThread, stackFrame, ctx, NULL, _SymFunctionTableAccess64, _SymGetModuleBase64, NULL); if (!ok) return false; DWORD64 addr = stackFrame->AddrPC.Offset; if (0 == addr) return true; if (addr == stackFrame->AddrReturn.Offset) { s.Append("GetStackFrameInfo(): addr == stackFrame->AddrReturn.Offset"); return false; } GetAddressInfo(s, addr); return true; }
static void AppendNest(str::Str<char>& s, int nest) { while (nest > 0) { s.Append(" "); --nest; } }
static void DumpTypes(IDiaSession *session) { IDiaSymbol * globalScope = NULL; IDiaEnumSymbols * enumSymbols = NULL; IDiaSymbol * symbol = NULL; HRESULT hr = session->get_globalScope(&globalScope); if (FAILED(hr)) return; AddReportSepLine(); g_report.Append("Types:\n"); DWORD flags = nsfCaseInsensitive|nsfUndecoratedName; // nsNone ? hr = globalScope->findChildren(SymTagUDT, 0, flags, &enumSymbols); if (FAILED(hr)) goto Exit; ULONG celt = 0; for (;;) { hr = enumSymbols->Next(1, &symbol, &celt); if (FAILED(hr) || (celt != 1)) break; DumpType(symbol, 0); symbol->Release(); } Exit: UnkReleaseSafe(enumSymbols); UnkReleaseSafe(globalScope); }
static bool AppendEntry(str::Str<char>& data, str::Str<char>& content, const WCHAR *filePath, const char *inArchiveName, lzma::FileInfo *fi=NULL) { size_t nameLen = str::Len(inArchiveName); CrashIf(nameLen > UINT32_MAX - 25); uint32_t headerSize = 25 + (uint32_t)nameLen; FILETIME ft = file::GetModificationTime(filePath); if (fi && FileTimeEq(ft, fi->ftModified)) { ReusePrevious: ByteWriterLE meta(data.AppendBlanks(24), 24); meta.Write32(headerSize); meta.Write32(fi->compressedSize); meta.Write32(fi->uncompressedSize); meta.Write32(fi->uncompressedCrc32); meta.Write32(ft.dwLowDateTime); meta.Write32(ft.dwHighDateTime); data.Append(inArchiveName, nameLen + 1); return content.AppendChecked(fi->compressedData, fi->compressedSize); } size_t fileDataLen; ScopedMem<char> fileData(file::ReadAll(filePath, &fileDataLen)); if (!fileData || fileDataLen >= UINT32_MAX) { fprintf(stderr, "Failed to read \"%S\" for compression\n", filePath); return false; } uint32_t fileDataCrc = crc32(0, (const uint8_t *)fileData.Get(), (uint32_t)fileDataLen); if (fi && fi->uncompressedCrc32 == fileDataCrc && fi->uncompressedSize == fileDataLen) goto ReusePrevious; size_t compressedSize = fileDataLen + 1; ScopedMem<char> compressed((char *)malloc(compressedSize)); if (!compressed) return false; if (!Compress(fileData, fileDataLen, compressed, &compressedSize)) return false; ByteWriterLE meta(data.AppendBlanks(24), 24); meta.Write32(headerSize); meta.Write32((uint32_t)compressedSize); meta.Write32((uint32_t)fileDataLen); meta.Write32(fileDataCrc); meta.Write32(ft.dwLowDateTime); meta.Write32(ft.dwHighDateTime); data.Append(inArchiveName, nameLen + 1); return content.AppendChecked(compressed, compressedSize); }
static void AppendNewline(str::Str<WCHAR>& extracted, Vec<RectI>& coords, const WCHAR *lineSep) { if (extracted.Count() > 0 && ' ' == extracted.Last()) { extracted.Pop(); coords.Pop(); } extracted.Append(lineSep); coords.AppendBlanks(str::Len(lineSep)); }
void GetProgramInfo(str::Str<char>& s) { s.AppendFmt("Ver: %s", CURR_VERSION_STRA); #ifdef SVN_PRE_RELEASE_VER s.AppendFmt(" pre-release"); #endif if (IsProcess64()) { s.Append(" 64-bit"); } #ifdef DEBUG if (!str::Find(s.Get(), " (dbg)")) s.Append(" (dbg)"); #endif s.Append("\r\n"); #if defined(GIT_COMMIT_ID) const char* gitSha1 = QM(GIT_COMMIT_ID); s.AppendFmt("Git: %s (https://github.com/sumatrapdfreader/sumatrapdf/tree/%s)\r\n", gitSha1, gitSha1); #endif }
void BStrToString(str::Str<char>& strInOut, BSTR str, char *defString, bool stripWhitespace) { strInOut.Reset(); if (!str) { strInOut.Append(defString); return; } OLECHAR c; int len = SysStringLen(str); for (int i=0; i<len; i++) { c = str[i]; if (stripWhitespace && isspace(c)) continue; if (c < 32 || c >= 128) c = '?'; strInOut.Append((char)c); } }
static bool GetCallstack(str::Str<char>& s, CONTEXT& ctx, HANDLE hThread) { if (!CanStackWalk()) { s.Append("GetCallstack(): CanStackWalk() returned false"); return false; } STACKFRAME64 stackFrame; memset(&stackFrame, 0, sizeof(stackFrame)); #ifdef _WIN64 stackFrame.AddrPC.Offset = ctx.Rip; stackFrame.AddrFrame.Offset = ctx.Rbp; stackFrame.AddrStack.Offset = ctx.Rsp; #else stackFrame.AddrPC.Offset = ctx.Eip; stackFrame.AddrFrame.Offset = ctx.Ebp; stackFrame.AddrStack.Offset = ctx.Esp; #endif stackFrame.AddrPC.Mode = AddrModeFlat; stackFrame.AddrFrame.Mode = AddrModeFlat; stackFrame.AddrStack.Mode = AddrModeFlat; int framesCount = 0; static const int maxFrames = 32; while (framesCount < maxFrames) { if (!GetStackFrameInfo(s, &stackFrame, &ctx, hThread)) break; framesCount++; } if (0 == framesCount) { s.Append("StackWalk64() couldn't get even the first stack frame info"); return false; } return true; }
static void DumpSymbols(IDiaSession *session) { HRESULT hr; IDiaEnumSymbolsByAddr * enumByAddr = NULL; IDiaSymbol * symbol = NULL; hr = session->getSymbolsByAddr(&enumByAddr); if (!SUCCEEDED(hr)) goto Exit; // get first symbol to get first RVA (argh) hr = enumByAddr->symbolByAddr(1, 0, &symbol); if (!SUCCEEDED(hr)) goto Exit; DWORD rva; hr = symbol->get_relativeVirtualAddress(&rva); if (S_OK != hr) goto Exit; symbol->Release(); symbol = NULL; // enumerate by rva hr = enumByAddr->symbolByRVA(rva, &symbol); if (!SUCCEEDED(hr)) goto Exit; AddReportSepLine(); g_report.Append("Symbols:\n"); ULONG numFetched; for (;;) { DumpSymbol(symbol); symbol->Release(); symbol = NULL; hr = enumByAddr->Next(1, &symbol, &numFetched); if (FAILED(hr) || (numFetched != 1)) break; } Exit: UnkReleaseSafe(symbol); UnkReleaseSafe(enumByAddr); }
bool DjVuEngineImpl::ExtractPageText(miniexp_t item, const WCHAR *lineSep, str::Str<WCHAR>& extracted, Vec<RectI>& coords) { miniexp_t type = miniexp_car(item); if (!miniexp_symbolp(type)) return false; item = miniexp_cdr(item); if (!miniexp_numberp(miniexp_car(item))) return false; int x0 = miniexp_to_int(miniexp_car(item)); item = miniexp_cdr(item); if (!miniexp_numberp(miniexp_car(item))) return false; int y0 = miniexp_to_int(miniexp_car(item)); item = miniexp_cdr(item); if (!miniexp_numberp(miniexp_car(item))) return false; int x1 = miniexp_to_int(miniexp_car(item)); item = miniexp_cdr(item); if (!miniexp_numberp(miniexp_car(item))) return false; int y1 = miniexp_to_int(miniexp_car(item)); item = miniexp_cdr(item); RectI rect = RectI::FromXY(x0, y0, x1, y1); miniexp_t str = miniexp_car(item); if (miniexp_stringp(str) && !miniexp_cdr(item)) { if (type != miniexp_symbol("char") && type != miniexp_symbol("word") || coords.Count() > 0 && rect.y < coords.Last().y - coords.Last().dy * 0.8) { AppendNewline(extracted, coords, lineSep); } const char *content = miniexp_to_str(str); WCHAR *value = str::conv::FromUtf8(content); if (value) { size_t len = str::Len(value); // TODO: split the rectangle into individual parts per glyph for (size_t i = 0; i < len; i++) coords.Append(RectI(rect.x, rect.y, rect.dx, rect.dy)); extracted.AppendAndFree(value); } if (miniexp_symbol("word") == type) { extracted.Append(' '); coords.Append(RectI(rect.x + rect.dx, rect.y, 2, rect.dy)); } item = miniexp_cdr(item); } while (miniexp_consp(str)) { ExtractPageText(str, lineSep, extracted, coords); item = miniexp_cdr(item); str = miniexp_car(item); } return !item; }
void GetExceptionInfo(str::Str<char>& s, EXCEPTION_POINTERS *excPointers) { if (!excPointers) return; EXCEPTION_RECORD *excRecord = excPointers->ExceptionRecord; DWORD excCode = excRecord->ExceptionCode; s.AppendFmt("Exception: %08X %s\r\n", (int)excCode, ExceptionNameFromCode(excCode)); s.AppendFmt("Faulting IP: "); GetAddressInfo(s, (DWORD64)excRecord->ExceptionAddress); if ((EXCEPTION_ACCESS_VIOLATION == excCode) || (EXCEPTION_IN_PAGE_ERROR == excCode)) { int readWriteFlag = (int)excRecord->ExceptionInformation[0]; DWORD64 dataVirtAddr = (DWORD64)excRecord->ExceptionInformation[1]; if (0 == readWriteFlag) { s.Append("Fault reading address "); AppendAddress(s, dataVirtAddr); } else if (1 == readWriteFlag) { s.Append("Fault writing address "); AppendAddress(s, dataVirtAddr); } else if (8 == readWriteFlag) { s.Append("DEP violation at address "); AppendAddress(s, dataVirtAddr); } else { s.Append("unknown readWriteFlag: %d", readWriteFlag); } s.Append("\r\n"); } PCONTEXT ctx = excPointers->ContextRecord; s.AppendFmt("\r\nRegisters:\r\n"); #ifdef _WIN64 s.AppendFmt("RAX:%016I64X RBX:%016I64X RCX:%016I64X\r\nRDX:%016I64X RSI:%016I64X RDI:%016I64X\r\n" "R8: %016I64X\r\nR9: %016I64X\r\nR10:%016I64X\r\nR11:%016I64X\r\nR12:%016I64X\r\nR13:%016I64X\r\nR14:%016I64X\r\nR15:%016I64X\r\n", ctx->Rax, ctx->Rbx, ctx->Rcx, ctx->Rdx, ctx->Rsi, ctx->Rdi, ctx->R9,ctx->R10,ctx->R11,ctx->R12,ctx->R13,ctx->R14,ctx->R15); s.AppendFmt("CS:RIP:%04X:%016I64X\r\n", ctx->SegCs, ctx->Rip); s.AppendFmt("SS:RSP:%04X:%016X RBP:%08X\r\n", ctx->SegSs, ctx->Rsp, ctx->Rbp); s.AppendFmt("DS:%04X ES:%04X FS:%04X GS:%04X\r\n", ctx->SegDs, ctx->SegEs, ctx->SegFs, ctx->SegGs); s.AppendFmt("Flags:%08X\r\n", ctx->EFlags); #else s.AppendFmt("EAX:%08X EBX:%08X ECX:%08X\r\nEDX:%08X ESI:%08X EDI:%08X\r\n", ctx->Eax, ctx->Ebx, ctx->Ecx, ctx->Edx, ctx->Esi, ctx->Edi); s.AppendFmt("CS:EIP:%04X:%08X\r\n", ctx->SegCs, ctx->Eip); s.AppendFmt("SS:ESP:%04X:%08X EBP:%08X\r\n", ctx->SegSs, ctx->Esp, ctx->Ebp); s.AppendFmt("DS:%04X ES:%04X FS:%04X GS:%04X\r\n", ctx->SegDs, ctx->SegEs, ctx->SegFs, ctx->SegGs); s.AppendFmt("Flags:%08X\r\n", ctx->EFlags); #endif s.Append("\r\nCrashed thread:\r\n"); // it's not really for current thread, but it seems to work GetCallstack(s, *ctx, GetCurrentThread()); }
static void GetAddressInfo(str::Str<char>& s, DWORD64 addr) { static const int MAX_SYM_LEN = 512; char buf[sizeof(SYMBOL_INFO) + MAX_SYM_LEN * sizeof(char)]; SYMBOL_INFO *symInfo = (SYMBOL_INFO*)buf; memset(buf, 0, sizeof(buf)); symInfo->SizeOfStruct = sizeof(SYMBOL_INFO); symInfo->MaxNameLen = MAX_SYM_LEN; DWORD64 symDisp = 0; char *symName = nullptr; BOOL ok = _SymFromAddr(GetCurrentProcess(), addr, &symDisp, symInfo); if (ok) symName = &(symInfo->Name[0]); char module[MAX_PATH] = { 0 }; DWORD section; DWORD_PTR offset; if (GetAddrInfo((void*)addr, module, sizeof(module), section, offset)) { str::ToLower(module); const char *moduleShort = path::GetBaseName(module); AppendAddress(s, addr); s.AppendFmt(" %02X:", section); AppendAddress(s, offset); s.AppendFmt(" %s", moduleShort); if (symName) { s.AppendFmt("!%s+0x%x", symName, (int)symDisp); } else if (symDisp != 0) { s.AppendFmt("+0x%x", (int)symDisp); } IMAGEHLP_LINE64 line; line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); DWORD disp; if (_SymGetLineFromAddr64(GetCurrentProcess(), addr, &disp, &line)) { s.AppendFmt(" %s+%d", line.FileName, line.LineNumber); } } else { AppendAddress(s, addr); } s.Append("\r\n"); }
static void DumpSections(IDiaSession *session) { HRESULT hr; IDiaEnumTables * enumTables = NULL; IDiaTable * secTable = NULL; hr = session->getEnumTables(&enumTables); if (S_OK != hr) return; AddReportSepLine(); g_report.Append("Sections:\n"); VARIANT vIndex; vIndex.vt = VT_BSTR; vIndex.bstrVal = SysAllocString(L"Sections"); hr = enumTables->Item(vIndex, &secTable); if (S_OK != hr) goto Exit; LONG count; secTable->get_Count(&count); IDiaSectionContrib *item; ULONG numFetched; for (;;) { hr = secTable->Next(1,(IUnknown **)&item, &numFetched); if (FAILED(hr) || (numFetched != 1)) break; DumpSection(item); item->Release(); } Exit: UnkReleaseSafe(secTable); SysFreeStringSafe(vIndex.bstrVal); UnkReleaseSafe(enumTables); }
static void AddReportSepLine() { if (g_report.Count() > 0) g_report.Append("\n"); }
static void AppendWsTrimEnd(str::Str<char>& res, char *s, char *e) { str::TrimWsEnd(s, e); res.Append(s, e - s); }