Hash :
0c7f49dd
Author :
Date :
2017-06-30T13:39:01
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
/*
* 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.
*/
#include "w32_stack.h"
#if defined(GIT_MSVC_CRTDBG)
#include "Windows.h"
#include "Dbghelp.h"
#include "win32/posix.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