2 * Copyright (C) the libgit2 contributors. All rights reserved.
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.
10 #if defined(GIT_MSVC_CRTDBG)
13 #include "win32/posix.h"
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
;
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
)
25 g_aux_cb_alloc
= cb_alloc
;
26 g_aux_cb_lookup
= cb_lookup
;
31 void git_win32__stack_init(void)
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;
41 void git_win32__stack_cleanup(void)
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;
50 int git_win32__stack_capture(git_win32__stack__raw_data
*pdata
, int skip
)
52 if (!g_win32_stack_initialized
) {
53 git_error_set(GIT_ERROR_INVALID
, "git_win32_stack not initialized.");
57 memset(pdata
, 0, sizeof(*pdata
));
58 pdata
->nr_frames
= RtlCaptureStackBackTrace(
59 skip
+1, GIT_WIN32__STACK__MAX_FRAMES
, pdata
->frames
, NULL
);
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.
66 (g_aux_cb_alloc
)(&pdata
->aux_id
);
71 int git_win32__stack_compare(
72 git_win32__stack__raw_data
*d1
,
73 git_win32__stack__raw_data
*d2
)
75 return memcmp(d1
, d2
, sizeof(*d1
));
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
)
83 #define MY_MAX_FILENAME 255
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.
90 char extra
[MY_MAX_FILENAME
+ 1];
96 char detail
[MY_MAX_FILENAME
* 2]; /* filename plus space for function name and formatting */
99 if (!g_win32_stack_initialized
) {
100 git_error_set(GIT_ERROR_INVALID
, "git_win32_stack not initialized.");
109 memset(pbuf
, 0, buf_len
);
111 memset(&s
, 0, sizeof(s
));
112 s
.symbol
.MaxNameLen
= MY_MAX_FILENAME
;
113 s
.symbol
.SizeOfStruct
= sizeof(SYMBOL_INFO
);
115 memset(&line
, 0, sizeof(line
));
116 line
.SizeOfStruct
= sizeof(IMAGEHLP_LINE64
);
118 for (k
=0; k
< pdata
->nr_frames
; k
++) {
119 DWORD64 frame_k
= (DWORD64
)pdata
->frames
[k
];
122 if (SymFromAddr(g_win32_stack_process
, frame_k
, 0, &s
.symbol
) &&
123 SymGetLineFromAddr64(g_win32_stack_process
, frame_k
, &dwUnused
, &line
)) {
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
);
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.
137 p_snprintf(detail
, sizeof(detail
), "%s??%s", prefix
, suffix
);
139 detail_len
= strlen(detail
);
141 if (buf_len
< (buf_used
+ detail_len
+ 1)) {
142 /* we don't have room for this frame in the buffer, so just stop. */
146 memcpy(&pbuf
[buf_used
], detail
, detail_len
);
147 buf_used
+= detail_len
;
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.
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
;
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
167 (g_aux_cb_lookup
)(pdata
->aux_id
, &pbuf
[buf_used
], (buf_len
- buf_used
- 1));
173 int git_win32__stack(
174 char * pbuf
, size_t buf_len
,
176 const char *prefix
, const char *suffix
)
178 git_win32__stack__raw_data data
;
181 if ((error
= git_win32__stack_capture(&data
, skip
)) < 0)
183 if ((error
= git_win32__stack_format(pbuf
, buf_len
, &data
, prefix
, suffix
)) < 0)