Blame src/win32/w32_stack.c

Packit Service 20376f
/*
Packit Service 20376f
 * Copyright (C) the libgit2 contributors. All rights reserved.
Packit Service 20376f
 *
Packit Service 20376f
 * This file is part of libgit2, distributed under the GNU GPL v2 with
Packit Service 20376f
 * a Linking Exception. For full terms see the included COPYING file.
Packit Service 20376f
 */
Packit Service 20376f
Packit Service 20376f
#if defined(GIT_MSVC_CRTDBG)
Packit Service 20376f
#include "Windows.h"
Packit Service 20376f
#include "Dbghelp.h"
Packit Service 20376f
#include "win32/posix.h"
Packit Service 20376f
#include "w32_stack.h"
Packit Service 20376f
#include "hash.h"
Packit Service 20376f
Packit Service 20376f
/**
Packit Service 20376f
 * This is supposedly defined in WinBase.h (from Windows.h) but there were linker issues.
Packit Service 20376f
 */
Packit Service 20376f
USHORT WINAPI RtlCaptureStackBackTrace(ULONG, ULONG, PVOID*, PULONG);
Packit Service 20376f
Packit Service 20376f
static bool   g_win32_stack_initialized = false;
Packit Service 20376f
static HANDLE g_win32_stack_process = INVALID_HANDLE_VALUE;
Packit Service 20376f
static git_win32__stack__aux_cb_alloc  g_aux_cb_alloc  = NULL;
Packit Service 20376f
static git_win32__stack__aux_cb_lookup g_aux_cb_lookup = NULL;
Packit Service 20376f
Packit Service 20376f
int git_win32__stack__set_aux_cb(
Packit Service 20376f
	git_win32__stack__aux_cb_alloc cb_alloc,
Packit Service 20376f
	git_win32__stack__aux_cb_lookup cb_lookup)
Packit Service 20376f
{
Packit Service 20376f
	g_aux_cb_alloc = cb_alloc;
Packit Service 20376f
	g_aux_cb_lookup = cb_lookup;
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
void git_win32__stack_init(void)
Packit Service 20376f
{
Packit Service 20376f
	if (!g_win32_stack_initialized) {
Packit Service 20376f
		g_win32_stack_process = GetCurrentProcess();
Packit Service 20376f
		SymSetOptions(SYMOPT_LOAD_LINES);
Packit Service 20376f
		SymInitialize(g_win32_stack_process, NULL, TRUE);
Packit Service 20376f
		g_win32_stack_initialized = true;
Packit Service 20376f
	}
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
void git_win32__stack_cleanup(void)
Packit Service 20376f
{
Packit Service 20376f
	if (g_win32_stack_initialized) {
Packit Service 20376f
		SymCleanup(g_win32_stack_process);
Packit Service 20376f
		g_win32_stack_process = INVALID_HANDLE_VALUE;
Packit Service 20376f
		g_win32_stack_initialized = false;
Packit Service 20376f
	}
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_win32__stack_capture(git_win32__stack__raw_data *pdata, int skip)
Packit Service 20376f
{
Packit Service 20376f
	if (!g_win32_stack_initialized) {
Packit Service 20376f
		giterr_set(GITERR_INVALID, "git_win32_stack not initialized.");
Packit Service 20376f
		return GIT_ERROR;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	memset(pdata, 0, sizeof(*pdata));
Packit Service 20376f
	pdata->nr_frames = RtlCaptureStackBackTrace(
Packit Service 20376f
		skip+1, GIT_WIN32__STACK__MAX_FRAMES, pdata->frames, NULL);
Packit Service 20376f
Packit Service 20376f
	/* If an "aux" data provider was registered, ask it to capture
Packit Service 20376f
	 * whatever data it needs and give us an "aux_id" to it so that
Packit Service 20376f
	 * we can refer to it later when reporting.
Packit Service 20376f
	 */
Packit Service 20376f
	if (g_aux_cb_alloc)
Packit Service 20376f
		(g_aux_cb_alloc)(&pdata->aux_id);
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_win32__stack_compare(
Packit Service 20376f
	git_win32__stack__raw_data *d1,
Packit Service 20376f
	git_win32__stack__raw_data *d2)
Packit Service 20376f
{
Packit Service 20376f
	return memcmp(d1, d2, sizeof(*d1));
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_win32__stack_format(
Packit Service 20376f
	char *pbuf, int buf_len,
Packit Service 20376f
	const git_win32__stack__raw_data *pdata,
Packit Service 20376f
	const char *prefix, const char *suffix)
Packit Service 20376f
{
Packit Service 20376f
#define MY_MAX_FILENAME 255
Packit Service 20376f
Packit Service 20376f
	/* SYMBOL_INFO has char FileName[1] at the end.  The docs say to
Packit Service 20376f
	 * to malloc it with extra space for your desired max filename.
Packit Service 20376f
	 */
Packit Service 20376f
	struct {
Packit Service 20376f
		SYMBOL_INFO symbol;
Packit Service 20376f
		char extra[MY_MAX_FILENAME + 1];
Packit Service 20376f
	} s;
Packit Service 20376f
Packit Service 20376f
	IMAGEHLP_LINE64 line;
Packit Service 20376f
	int buf_used = 0;
Packit Service 20376f
	unsigned int k;
Packit Service 20376f
	char detail[MY_MAX_FILENAME * 2]; /* filename plus space for function name and formatting */
Packit Service 20376f
	int detail_len;
Packit Service 20376f
Packit Service 20376f
	if (!g_win32_stack_initialized) {
Packit Service 20376f
		giterr_set(GITERR_INVALID, "git_win32_stack not initialized.");
Packit Service 20376f
		return GIT_ERROR;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (!prefix)
Packit Service 20376f
		prefix = "\t";
Packit Service 20376f
	if (!suffix)
Packit Service 20376f
		suffix = "\n";
Packit Service 20376f
Packit Service 20376f
	memset(pbuf, 0, buf_len);
Packit Service 20376f
Packit Service 20376f
	memset(&s, 0, sizeof(s));
Packit Service 20376f
	s.symbol.MaxNameLen = MY_MAX_FILENAME;
Packit Service 20376f
	s.symbol.SizeOfStruct = sizeof(SYMBOL_INFO);
Packit Service 20376f
Packit Service 20376f
	memset(&line, 0, sizeof(line));
Packit Service 20376f
	line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
Packit Service 20376f
Packit Service 20376f
	for (k=0; k < pdata->nr_frames; k++) {
Packit Service 20376f
		DWORD64 frame_k = (DWORD64)pdata->frames[k];
Packit Service 20376f
		DWORD dwUnused;
Packit Service 20376f
Packit Service 20376f
		if (SymFromAddr(g_win32_stack_process, frame_k, 0, &s.symbol) &&
Packit Service 20376f
			SymGetLineFromAddr64(g_win32_stack_process, frame_k, &dwUnused, &line)) {
Packit Service 20376f
			const char *pslash;
Packit Service 20376f
			const char *pfile;
Packit Service 20376f
Packit Service 20376f
			pslash = strrchr(line.FileName, '\\');
Packit Service 20376f
			pfile = ((pslash) ? (pslash+1) : line.FileName);
Packit Service 20376f
			p_snprintf(detail, sizeof(detail), "%s%s:%d> %s%s",
Packit Service 20376f
					   prefix, pfile, line.LineNumber, s.symbol.Name, suffix);
Packit Service 20376f
		} else {
Packit Service 20376f
			/* This happens when we cross into another module.
Packit Service 20376f
			 * For example, in CLAR tests, this is typically
Packit Service 20376f
			 * the CRT startup code.  Just print an unknown
Packit Service 20376f
			 * frame and continue.
Packit Service 20376f
			 */
Packit Service 20376f
			p_snprintf(detail, sizeof(detail), "%s??%s", prefix, suffix);
Packit Service 20376f
		}
Packit Service 20376f
		detail_len = strlen(detail);
Packit Service 20376f
Packit Service 20376f
		if (buf_len < (buf_used + detail_len + 1)) {
Packit Service 20376f
			/* we don't have room for this frame in the buffer, so just stop. */
Packit Service 20376f
			break;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		memcpy(&pbuf[buf_used], detail, detail_len);
Packit Service 20376f
		buf_used += detail_len;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/* "aux_id" 0 is reserved to mean no aux data. This is needed to handle
Packit Service 20376f
	 * allocs that occur before the aux callbacks were registered.
Packit Service 20376f
	 */
Packit Service 20376f
	if (pdata->aux_id > 0) {
Packit Service 20376f
		p_snprintf(detail, sizeof(detail), "%saux_id: %d%s",
Packit Service 20376f
				   prefix, pdata->aux_id, suffix);
Packit Service 20376f
		detail_len = strlen(detail);
Packit Service 20376f
		if ((buf_used + detail_len + 1) < buf_len) {
Packit Service 20376f
			memcpy(&pbuf[buf_used], detail, detail_len);
Packit Service 20376f
			buf_used += detail_len;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		/* If an "aux" data provider is still registered, ask it to append its detailed
Packit Service 20376f
		 * data to the end of ours using the "aux_id" it gave us when this de-duped
Packit Service 20376f
		 * item was created.
Packit Service 20376f
		 */
Packit Service 20376f
		if (g_aux_cb_lookup)
Packit Service 20376f
			(g_aux_cb_lookup)(pdata->aux_id, &pbuf[buf_used], (buf_len - buf_used - 1));
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return GIT_OK;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_win32__stack(
Packit Service 20376f
	char * pbuf, int buf_len,
Packit Service 20376f
	int skip,
Packit Service 20376f
	const char *prefix, const char *suffix)
Packit Service 20376f
{
Packit Service 20376f
	git_win32__stack__raw_data data;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_win32__stack_capture(&data, skip)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
	if ((error = git_win32__stack_format(pbuf, buf_len, &data, prefix, suffix)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
#endif