]> git.proxmox.com Git - libgit2.git/blob - src/win32/w32_stack.c
New upstream version 1.1.0+dfsg.1
[libgit2.git] / src / win32 / w32_stack.c
1 /*
2 * Copyright (C) the libgit2 contributors. All rights reserved.
3 *
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
6 */
7
8 #include "w32_stack.h"
9
10 #if defined(GIT_MSVC_CRTDBG)
11 #include "Windows.h"
12 #include "Dbghelp.h"
13 #include "win32/posix.h"
14 #include "hash.h"
15
16 static bool g_win32_stack_initialized = false;
17 static HANDLE g_win32_stack_process = INVALID_HANDLE_VALUE;
18 static git_win32__stack__aux_cb_alloc g_aux_cb_alloc = NULL;
19 static git_win32__stack__aux_cb_lookup g_aux_cb_lookup = NULL;
20
21 int git_win32__stack__set_aux_cb(
22 git_win32__stack__aux_cb_alloc cb_alloc,
23 git_win32__stack__aux_cb_lookup cb_lookup)
24 {
25 g_aux_cb_alloc = cb_alloc;
26 g_aux_cb_lookup = cb_lookup;
27
28 return 0;
29 }
30
31 void git_win32__stack_init(void)
32 {
33 if (!g_win32_stack_initialized) {
34 g_win32_stack_process = GetCurrentProcess();
35 SymSetOptions(SYMOPT_LOAD_LINES);
36 SymInitialize(g_win32_stack_process, NULL, TRUE);
37 g_win32_stack_initialized = true;
38 }
39 }
40
41 void git_win32__stack_cleanup(void)
42 {
43 if (g_win32_stack_initialized) {
44 SymCleanup(g_win32_stack_process);
45 g_win32_stack_process = INVALID_HANDLE_VALUE;
46 g_win32_stack_initialized = false;
47 }
48 }
49
50 int git_win32__stack_capture(git_win32__stack__raw_data *pdata, int skip)
51 {
52 if (!g_win32_stack_initialized) {
53 git_error_set(GIT_ERROR_INVALID, "git_win32_stack not initialized.");
54 return GIT_ERROR;
55 }
56
57 memset(pdata, 0, sizeof(*pdata));
58 pdata->nr_frames = RtlCaptureStackBackTrace(
59 skip+1, GIT_WIN32__STACK__MAX_FRAMES, pdata->frames, NULL);
60
61 /* If an "aux" data provider was registered, ask it to capture
62 * whatever data it needs and give us an "aux_id" to it so that
63 * we can refer to it later when reporting.
64 */
65 if (g_aux_cb_alloc)
66 (g_aux_cb_alloc)(&pdata->aux_id);
67
68 return 0;
69 }
70
71 int git_win32__stack_compare(
72 git_win32__stack__raw_data *d1,
73 git_win32__stack__raw_data *d2)
74 {
75 return memcmp(d1, d2, sizeof(*d1));
76 }
77
78 int git_win32__stack_format(
79 char *pbuf, size_t buf_len,
80 const git_win32__stack__raw_data *pdata,
81 const char *prefix, const char *suffix)
82 {
83 #define MY_MAX_FILENAME 255
84
85 /* SYMBOL_INFO has char FileName[1] at the end. The docs say to
86 * to malloc it with extra space for your desired max filename.
87 */
88 struct {
89 SYMBOL_INFO symbol;
90 char extra[MY_MAX_FILENAME + 1];
91 } s;
92
93 IMAGEHLP_LINE64 line;
94 size_t buf_used = 0;
95 unsigned int k;
96 char detail[MY_MAX_FILENAME * 2]; /* filename plus space for function name and formatting */
97 size_t detail_len;
98
99 if (!g_win32_stack_initialized) {
100 git_error_set(GIT_ERROR_INVALID, "git_win32_stack not initialized.");
101 return GIT_ERROR;
102 }
103
104 if (!prefix)
105 prefix = "\t";
106 if (!suffix)
107 suffix = "\n";
108
109 memset(pbuf, 0, buf_len);
110
111 memset(&s, 0, sizeof(s));
112 s.symbol.MaxNameLen = MY_MAX_FILENAME;
113 s.symbol.SizeOfStruct = sizeof(SYMBOL_INFO);
114
115 memset(&line, 0, sizeof(line));
116 line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
117
118 for (k=0; k < pdata->nr_frames; k++) {
119 DWORD64 frame_k = (DWORD64)pdata->frames[k];
120 DWORD dwUnused;
121
122 if (SymFromAddr(g_win32_stack_process, frame_k, 0, &s.symbol) &&
123 SymGetLineFromAddr64(g_win32_stack_process, frame_k, &dwUnused, &line)) {
124 const char *pslash;
125 const char *pfile;
126
127 pslash = strrchr(line.FileName, '\\');
128 pfile = ((pslash) ? (pslash+1) : line.FileName);
129 p_snprintf(detail, sizeof(detail), "%s%s:%d> %s%s",
130 prefix, pfile, line.LineNumber, s.symbol.Name, suffix);
131 } else {
132 /* This happens when we cross into another module.
133 * For example, in CLAR tests, this is typically
134 * the CRT startup code. Just print an unknown
135 * frame and continue.
136 */
137 p_snprintf(detail, sizeof(detail), "%s??%s", prefix, suffix);
138 }
139 detail_len = strlen(detail);
140
141 if (buf_len < (buf_used + detail_len + 1)) {
142 /* we don't have room for this frame in the buffer, so just stop. */
143 break;
144 }
145
146 memcpy(&pbuf[buf_used], detail, detail_len);
147 buf_used += detail_len;
148 }
149
150 /* "aux_id" 0 is reserved to mean no aux data. This is needed to handle
151 * allocs that occur before the aux callbacks were registered.
152 */
153 if (pdata->aux_id > 0) {
154 p_snprintf(detail, sizeof(detail), "%saux_id: %d%s",
155 prefix, pdata->aux_id, suffix);
156 detail_len = strlen(detail);
157 if ((buf_used + detail_len + 1) < buf_len) {
158 memcpy(&pbuf[buf_used], detail, detail_len);
159 buf_used += detail_len;
160 }
161
162 /* If an "aux" data provider is still registered, ask it to append its detailed
163 * data to the end of ours using the "aux_id" it gave us when this de-duped
164 * item was created.
165 */
166 if (g_aux_cb_lookup)
167 (g_aux_cb_lookup)(pdata->aux_id, &pbuf[buf_used], (buf_len - buf_used - 1));
168 }
169
170 return GIT_OK;
171 }
172
173 int git_win32__stack(
174 char * pbuf, size_t buf_len,
175 int skip,
176 const char *prefix, const char *suffix)
177 {
178 git_win32__stack__raw_data data;
179 int error;
180
181 if ((error = git_win32__stack_capture(&data, skip)) < 0)
182 return error;
183 if ((error = git_win32__stack_format(pbuf, buf_len, &data, prefix, suffix)) < 0)
184 return error;
185 return 0;
186 }
187
188 #endif