/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #if defined(GIT_MSVC_CRTDBG) #include "Windows.h" #include "Dbghelp.h" #include "win32/posix.h" #include "w32_stack.h" #include "hash.h" /** * This is supposedly defined in WinBase.h (from Windows.h) but there were linker issues. */ USHORT WINAPI RtlCaptureStackBackTrace(ULONG, ULONG, PVOID*, PULONG); static bool g_win32_stack_initialized = false; static HANDLE g_win32_stack_process = INVALID_HANDLE_VALUE; static git_win32__stack__aux_cb_alloc g_aux_cb_alloc = NULL; static git_win32__stack__aux_cb_lookup g_aux_cb_lookup = NULL; int git_win32__stack__set_aux_cb( git_win32__stack__aux_cb_alloc cb_alloc, git_win32__stack__aux_cb_lookup cb_lookup) { g_aux_cb_alloc = cb_alloc; g_aux_cb_lookup = cb_lookup; return 0; } void git_win32__stack_init(void) { if (!g_win32_stack_initialized) { g_win32_stack_process = GetCurrentProcess(); SymSetOptions(SYMOPT_LOAD_LINES); SymInitialize(g_win32_stack_process, NULL, TRUE); g_win32_stack_initialized = true; } } void git_win32__stack_cleanup(void) { if (g_win32_stack_initialized) { SymCleanup(g_win32_stack_process); g_win32_stack_process = INVALID_HANDLE_VALUE; g_win32_stack_initialized = false; } } int git_win32__stack_capture(git_win32__stack__raw_data *pdata, int skip) { if (!g_win32_stack_initialized) { giterr_set(GITERR_INVALID, "git_win32_stack not initialized."); return GIT_ERROR; } memset(pdata, 0, sizeof(*pdata)); pdata->nr_frames = RtlCaptureStackBackTrace( skip+1, GIT_WIN32__STACK__MAX_FRAMES, pdata->frames, NULL); /* If an "aux" data provider was registered, ask it to capture * whatever data it needs and give us an "aux_id" to it so that * we can refer to it later when reporting. */ if (g_aux_cb_alloc) (g_aux_cb_alloc)(&pdata->aux_id); return 0; } int git_win32__stack_compare( git_win32__stack__raw_data *d1, git_win32__stack__raw_data *d2) { return memcmp(d1, d2, sizeof(*d1)); } int git_win32__stack_format( char *pbuf, int buf_len, const git_win32__stack__raw_data *pdata, const char *prefix, const char *suffix) { #define MY_MAX_FILENAME 255 /* SYMBOL_INFO has char FileName[1] at the end. The docs say to * to malloc it with extra space for your desired max filename. */ struct { SYMBOL_INFO symbol; char extra[MY_MAX_FILENAME + 1]; } s; IMAGEHLP_LINE64 line; int buf_used = 0; unsigned int k; char detail[MY_MAX_FILENAME * 2]; /* filename plus space for function name and formatting */ int detail_len; if (!g_win32_stack_initialized) { giterr_set(GITERR_INVALID, "git_win32_stack not initialized."); return GIT_ERROR; } if (!prefix) prefix = "\t"; if (!suffix) suffix = "\n"; memset(pbuf, 0, buf_len); memset(&s, 0, sizeof(s)); s.symbol.MaxNameLen = MY_MAX_FILENAME; s.symbol.SizeOfStruct = sizeof(SYMBOL_INFO); memset(&line, 0, sizeof(line)); line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); for (k=0; k < pdata->nr_frames; k++) { DWORD64 frame_k = (DWORD64)pdata->frames[k]; DWORD dwUnused; if (SymFromAddr(g_win32_stack_process, frame_k, 0, &s.symbol) && SymGetLineFromAddr64(g_win32_stack_process, frame_k, &dwUnused, &line)) { const char *pslash; const char *pfile; pslash = strrchr(line.FileName, '\\'); pfile = ((pslash) ? (pslash+1) : line.FileName); p_snprintf(detail, sizeof(detail), "%s%s:%d> %s%s", prefix, pfile, line.LineNumber, s.symbol.Name, suffix); } else { /* This happens when we cross into another module. * For example, in CLAR tests, this is typically * the CRT startup code. Just print an unknown * frame and continue. */ p_snprintf(detail, sizeof(detail), "%s??%s", prefix, suffix); } detail_len = strlen(detail); if (buf_len < (buf_used + detail_len + 1)) { /* we don't have room for this frame in the buffer, so just stop. */ break; } memcpy(&pbuf[buf_used], detail, detail_len); buf_used += detail_len; } /* "aux_id" 0 is reserved to mean no aux data. This is needed to handle * allocs that occur before the aux callbacks were registered. */ if (pdata->aux_id > 0) { p_snprintf(detail, sizeof(detail), "%saux_id: %d%s", prefix, pdata->aux_id, suffix); detail_len = strlen(detail); if ((buf_used + detail_len + 1) < buf_len) { memcpy(&pbuf[buf_used], detail, detail_len); buf_used += detail_len; } /* If an "aux" data provider is still registered, ask it to append its detailed * data to the end of ours using the "aux_id" it gave us when this de-duped * item was created. */ if (g_aux_cb_lookup) (g_aux_cb_lookup)(pdata->aux_id, &pbuf[buf_used], (buf_len - buf_used - 1)); } return GIT_OK; } int git_win32__stack( char * pbuf, int buf_len, int skip, const char *prefix, const char *suffix) { git_win32__stack__raw_data data; int error; if ((error = git_win32__stack_capture(&data, skip)) < 0) return error; if ((error = git_win32__stack_format(pbuf, buf_len, &data, prefix, suffix)) < 0) return error; return 0; } #endif