mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-04 19:50:19 +00:00
181 lines
4.7 KiB
C
181 lines
4.7 KiB
C
/*
|
|
* 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.
|
|
*/
|
|
|
|
#if defined(GIT_MSVC_CRTDBG)
|
|
#include "Windows.h"
|
|
#include "Dbghelp.h"
|
|
#include "win32/posix.h"
|
|
#include "w32_stack.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.
|
|
* We use a union to do the same thing without mallocing.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/* If an "aux" data provider was 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
|