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.
8 #if defined(GIT_MSVC_CRTDBG)
11 #include "win32/posix.h"
12 #include "w32_stack.h"
16 * This is supposedly defined in WinBase.h (from Windows.h) but there were linker issues.
18 USHORT WINAPI
RtlCaptureStackBackTrace(ULONG
, ULONG
, PVOID
*, PULONG
);
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
;
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
)
29 g_aux_cb_alloc
= cb_alloc
;
30 g_aux_cb_lookup
= cb_lookup
;
35 void git_win32__stack_init(void)
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;
45 void git_win32__stack_cleanup(void)
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;
54 int git_win32__stack_capture(git_win32__stack__raw_data
*pdata
, int skip
)
56 if (!g_win32_stack_initialized
) {
57 giterr_set(GITERR_INVALID
, "git_win32_stack not initialized.");
61 memset(pdata
, 0, sizeof(*pdata
));
62 pdata
->nr_frames
= RtlCaptureStackBackTrace(
63 skip
+1, GIT_WIN32__STACK__MAX_FRAMES
, pdata
->frames
, NULL
);
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.
70 (g_aux_cb_alloc
)(&pdata
->aux_id
);
75 int git_win32__stack_compare(
76 git_win32__stack__raw_data
*d1
,
77 git_win32__stack__raw_data
*d2
)
79 return memcmp(d1
, d2
, sizeof(*d1
));
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
)
87 #define MY_MAX_FILENAME 255
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.
94 char extra
[MY_MAX_FILENAME
+ 1];
100 char detail
[MY_MAX_FILENAME
* 2]; /* filename plus space for function name and formatting */
103 if (!g_win32_stack_initialized
) {
104 giterr_set(GITERR_INVALID
, "git_win32_stack not initialized.");
113 memset(pbuf
, 0, buf_len
);
115 memset(&s
, 0, sizeof(s
));
116 s
.symbol
.MaxNameLen
= MY_MAX_FILENAME
;
117 s
.symbol
.SizeOfStruct
= sizeof(SYMBOL_INFO
);
119 memset(&line
, 0, sizeof(line
));
120 line
.SizeOfStruct
= sizeof(IMAGEHLP_LINE64
);
122 for (k
=0; k
< pdata
->nr_frames
; k
++) {
123 DWORD64 frame_k
= (DWORD64
)pdata
->frames
[k
];
126 if (SymFromAddr(g_win32_stack_process
, frame_k
, 0, &s
.symbol
) &&
127 SymGetLineFromAddr64(g_win32_stack_process
, frame_k
, &dwUnused
, &line
)) {
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
);
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.
141 p_snprintf(detail
, sizeof(detail
), "%s??%s", prefix
, suffix
);
143 detail_len
= strlen(detail
);
145 if (buf_len
< (buf_used
+ detail_len
+ 1)) {
146 /* we don't have room for this frame in the buffer, so just stop. */
150 memcpy(&pbuf
[buf_used
], detail
, detail_len
);
151 buf_used
+= detail_len
;
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.
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
;
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
171 (g_aux_cb_lookup
)(pdata
->aux_id
, &pbuf
[buf_used
], (buf_len
- buf_used
- 1));
177 int git_win32__stack(
178 char * pbuf
, int buf_len
,
180 const char *prefix
, const char *suffix
)
182 git_win32__stack__raw_data data
;
185 if ((error
= git_win32__stack_capture(&data
, skip
)) < 0)
187 if ((error
= git_win32__stack_format(pbuf
, buf_len
, &data
, prefix
, suffix
)) < 0)