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