|
Packit |
ae9e2a |
/*
|
|
Packit |
ae9e2a |
* Copyright (C) the libgit2 contributors. All rights reserved.
|
|
Packit |
ae9e2a |
*
|
|
Packit |
ae9e2a |
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
|
Packit |
ae9e2a |
* a Linking Exception. For full terms see the included COPYING file.
|
|
Packit |
ae9e2a |
*/
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
#if defined(GIT_MSVC_CRTDBG)
|
|
Packit |
ae9e2a |
#include "w32_stack.h"
|
|
Packit |
ae9e2a |
#include "w32_crtdbg_stacktrace.h"
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
#define CRTDBG_STACKTRACE__UID_LEN (15)
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
/**
|
|
Packit |
ae9e2a |
* The stacktrace of an allocation can be distilled
|
|
Packit |
ae9e2a |
* to a unique id based upon the stackframe pointers
|
|
Packit |
ae9e2a |
* and ignoring any size arguments. We will use these
|
|
Packit |
ae9e2a |
* UIDs as the (char const*) __FILE__ argument we
|
|
Packit |
ae9e2a |
* give to the CRT malloc routines.
|
|
Packit |
ae9e2a |
*/
|
|
Packit |
ae9e2a |
typedef struct {
|
|
Packit |
ae9e2a |
char uid[CRTDBG_STACKTRACE__UID_LEN + 1];
|
|
Packit |
ae9e2a |
} git_win32__crtdbg_stacktrace__uid;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
/**
|
|
Packit |
ae9e2a |
* All mallocs with the same stacktrace will be de-duped
|
|
Packit |
ae9e2a |
* and aggregated into this row.
|
|
Packit |
ae9e2a |
*/
|
|
Packit |
ae9e2a |
typedef struct {
|
|
Packit |
ae9e2a |
git_win32__crtdbg_stacktrace__uid uid; /* must be first */
|
|
Packit |
ae9e2a |
git_win32__stack__raw_data raw_data;
|
|
Packit |
ae9e2a |
unsigned int count_allocs; /* times this alloc signature seen since init */
|
|
Packit |
ae9e2a |
unsigned int count_allocs_at_last_checkpoint; /* times since last mark */
|
|
Packit |
ae9e2a |
unsigned int transient_count_leaks; /* sum of leaks */
|
|
Packit |
ae9e2a |
} git_win32__crtdbg_stacktrace__row;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
static CRITICAL_SECTION g_crtdbg_stacktrace_cs;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
/**
|
|
Packit |
ae9e2a |
* CRTDBG memory leak tracking takes a "char const * const file_name"
|
|
Packit |
ae9e2a |
* and stores the pointer in the heap data (instead of allocing a copy
|
|
Packit |
ae9e2a |
* for itself). Normally, this is not a problem, since we usually pass
|
|
Packit |
ae9e2a |
* in __FILE__. But I'm going to lie to it and pass in the address of
|
|
Packit |
ae9e2a |
* the UID in place of the file_name. Also, I do not want to alloc the
|
|
Packit |
ae9e2a |
* stacktrace data (because we are called from inside our alloc routines).
|
|
Packit |
ae9e2a |
* Therefore, I'm creating a very large static pool array to store row
|
|
Packit |
ae9e2a |
* data. This also eliminates the temptation to realloc it (and move the
|
|
Packit |
ae9e2a |
* UID pointers).
|
|
Packit |
ae9e2a |
*
|
|
Packit |
ae9e2a |
* And to efficiently look for duplicates we need an index on the rows
|
|
Packit |
ae9e2a |
* so we can bsearch it. Again, without mallocing.
|
|
Packit |
ae9e2a |
*
|
|
Packit |
ae9e2a |
* If we observe more than MY_ROW_LIMIT unique malloc signatures, we
|
|
Packit |
ae9e2a |
* fall through and use the traditional __FILE__ processing and don't
|
|
Packit |
ae9e2a |
* try to de-dup them. If your testing hits this limit, just increase
|
|
Packit |
ae9e2a |
* it and try again.
|
|
Packit |
ae9e2a |
*/
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
#define MY_ROW_LIMIT (1024 * 1024)
|
|
Packit |
ae9e2a |
static git_win32__crtdbg_stacktrace__row g_cs_rows[MY_ROW_LIMIT];
|
|
Packit |
ae9e2a |
static git_win32__crtdbg_stacktrace__row *g_cs_index[MY_ROW_LIMIT];
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
static unsigned int g_cs_end = MY_ROW_LIMIT;
|
|
Packit |
ae9e2a |
static unsigned int g_cs_ins = 0; /* insertion point == unique allocs seen */
|
|
Packit |
ae9e2a |
static unsigned int g_count_total_allocs = 0; /* number of allocs seen */
|
|
Packit |
ae9e2a |
static unsigned int g_transient_count_total_leaks = 0; /* number of total leaks */
|
|
Packit |
ae9e2a |
static unsigned int g_transient_count_dedup_leaks = 0; /* number of unique leaks */
|
|
Packit |
ae9e2a |
static bool g_limit_reached = false; /* had allocs after we filled row table */
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
static unsigned int g_checkpoint_id = 0; /* to better label leak checkpoints */
|
|
Packit |
ae9e2a |
static bool g_transient_leaks_since_mark = false; /* payload for hook */
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
/**
|
|
Packit |
ae9e2a |
* Compare function for bsearch on g_cs_index table.
|
|
Packit |
ae9e2a |
*/
|
|
Packit |
ae9e2a |
static int row_cmp(const void *v1, const void *v2)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
git_win32__stack__raw_data *d1 = (git_win32__stack__raw_data*)v1;
|
|
Packit |
ae9e2a |
git_win32__crtdbg_stacktrace__row *r2 = (git_win32__crtdbg_stacktrace__row *)v2;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
return (git_win32__stack_compare(d1, &r2->raw_data));
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
/**
|
|
Packit |
ae9e2a |
* Unique insert the new data into the row and index tables.
|
|
Packit |
ae9e2a |
* We have to sort by the stackframe data itself, not the uid.
|
|
Packit |
ae9e2a |
*/
|
|
Packit |
ae9e2a |
static git_win32__crtdbg_stacktrace__row * insert_unique(
|
|
Packit |
ae9e2a |
const git_win32__stack__raw_data *pdata)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
size_t pos;
|
|
Packit |
ae9e2a |
if (git__bsearch(g_cs_index, g_cs_ins, pdata, row_cmp, &pos) < 0) {
|
|
Packit |
ae9e2a |
/* Append new unique item to row table. */
|
|
Packit |
ae9e2a |
memcpy(&g_cs_rows[g_cs_ins].raw_data, pdata, sizeof(*pdata));
|
|
Packit |
ae9e2a |
sprintf(g_cs_rows[g_cs_ins].uid.uid, "##%08lx", g_cs_ins);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
/* Insert pointer to it into the proper place in the index table. */
|
|
Packit |
ae9e2a |
if (pos < g_cs_ins)
|
|
Packit |
ae9e2a |
memmove(&g_cs_index[pos+1], &g_cs_index[pos], (g_cs_ins - pos)*sizeof(g_cs_index[0]));
|
|
Packit |
ae9e2a |
g_cs_index[pos] = &g_cs_rows[g_cs_ins];
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
g_cs_ins++;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
g_cs_index[pos]->count_allocs++;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
return g_cs_index[pos];
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
/**
|
|
Packit |
ae9e2a |
* Hook function to receive leak data from the CRT. (This includes
|
|
Packit |
ae9e2a |
* both "<file_name>:(<line_number>)" data, but also each of the
|
|
Packit |
ae9e2a |
* various headers and fields.
|
|
Packit |
ae9e2a |
*
|
|
Packit |
ae9e2a |
* Scan this for the special "##<pos>" UID forms that we substituted
|
|
Packit |
ae9e2a |
* for the "<file_name>". Map <pos> back to the row data and
|
|
Packit |
ae9e2a |
* increment its leak count.
|
|
Packit |
ae9e2a |
*
|
|
Packit |
ae9e2a |
* See https://msdn.microsoft.com/en-us/library/74kabxyx.aspx
|
|
Packit |
ae9e2a |
*
|
|
Packit |
ae9e2a |
* We suppress the actual crtdbg output.
|
|
Packit |
ae9e2a |
*/
|
|
Packit |
ae9e2a |
static int __cdecl report_hook(int nRptType, char *szMsg, int *retVal)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
static int hook_result = TRUE; /* FALSE to get stock dump; TRUE to suppress. */
|
|
Packit |
ae9e2a |
unsigned int pos;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
*retVal = 0; /* do not invoke debugger */
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if ((szMsg[0] != '#') || (szMsg[1] != '#'))
|
|
Packit |
ae9e2a |
return hook_result;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (sscanf(&szMsg[2], "%08lx", &pos) < 1)
|
|
Packit |
ae9e2a |
return hook_result;
|
|
Packit |
ae9e2a |
if (pos >= g_cs_ins)
|
|
Packit |
ae9e2a |
return hook_result;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (g_transient_leaks_since_mark) {
|
|
Packit |
ae9e2a |
if (g_cs_rows[pos].count_allocs == g_cs_rows[pos].count_allocs_at_last_checkpoint)
|
|
Packit |
ae9e2a |
return hook_result;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
g_cs_rows[pos].transient_count_leaks++;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (g_cs_rows[pos].transient_count_leaks == 1)
|
|
Packit |
ae9e2a |
g_transient_count_dedup_leaks++;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
g_transient_count_total_leaks++;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
return hook_result;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
/**
|
|
Packit |
ae9e2a |
* Write leak data to all of the various places we need.
|
|
Packit |
ae9e2a |
* We force the caller to sprintf() the message first
|
|
Packit |
ae9e2a |
* because we want to avoid fprintf() because it allocs.
|
|
Packit |
ae9e2a |
*/
|
|
Packit |
ae9e2a |
static void my_output(const char *buf)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
fwrite(buf, strlen(buf), 1, stderr);
|
|
Packit |
ae9e2a |
OutputDebugString(buf);
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
/**
|
|
Packit |
ae9e2a |
* For each row with leaks, dump a stacktrace for it.
|
|
Packit |
ae9e2a |
*/
|
|
Packit |
ae9e2a |
static void dump_summary(const char *label)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
unsigned int k;
|
|
Packit |
ae9e2a |
char buf[10 * 1024];
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (g_transient_count_total_leaks == 0)
|
|
Packit |
ae9e2a |
return;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
fflush(stdout);
|
|
Packit |
ae9e2a |
fflush(stderr);
|
|
Packit |
ae9e2a |
my_output("\n");
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (g_limit_reached) {
|
|
Packit |
ae9e2a |
sprintf(buf,
|
|
Packit |
ae9e2a |
"LEAK SUMMARY: de-dup row table[%d] filled. Increase MY_ROW_LIMIT.\n",
|
|
Packit |
ae9e2a |
MY_ROW_LIMIT);
|
|
Packit |
ae9e2a |
my_output(buf);
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (!label)
|
|
Packit |
ae9e2a |
label = "";
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (g_transient_leaks_since_mark) {
|
|
Packit |
ae9e2a |
sprintf(buf, "LEAK CHECKPOINT %d: leaks %d unique %d: %s\n",
|
|
Packit |
ae9e2a |
g_checkpoint_id, g_transient_count_total_leaks, g_transient_count_dedup_leaks, label);
|
|
Packit |
ae9e2a |
my_output(buf);
|
|
Packit |
ae9e2a |
} else {
|
|
Packit |
ae9e2a |
sprintf(buf, "LEAK SUMMARY: TOTAL leaks %d de-duped %d: %s\n",
|
|
Packit |
ae9e2a |
g_transient_count_total_leaks, g_transient_count_dedup_leaks, label);
|
|
Packit |
ae9e2a |
my_output(buf);
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
my_output("\n");
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
for (k = 0; k < g_cs_ins; k++) {
|
|
Packit |
ae9e2a |
if (g_cs_rows[k].transient_count_leaks > 0) {
|
|
Packit |
ae9e2a |
sprintf(buf, "LEAK: %s leaked %d of %d times:\n",
|
|
Packit |
ae9e2a |
g_cs_rows[k].uid.uid,
|
|
Packit |
ae9e2a |
g_cs_rows[k].transient_count_leaks,
|
|
Packit |
ae9e2a |
g_cs_rows[k].count_allocs);
|
|
Packit |
ae9e2a |
my_output(buf);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (git_win32__stack_format(
|
|
Packit |
ae9e2a |
buf, sizeof(buf), &g_cs_rows[k].raw_data,
|
|
Packit |
ae9e2a |
NULL, NULL) >= 0) {
|
|
Packit |
ae9e2a |
my_output(buf);
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
my_output("\n");
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
fflush(stderr);
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
void git_win32__crtdbg_stacktrace_init(void)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
InitializeCriticalSection(&g_crtdbg_stacktrace_cs);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
EnterCriticalSection(&g_crtdbg_stacktrace_cs);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE);
|
|
Packit |
ae9e2a |
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE);
|
|
Packit |
ae9e2a |
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
|
|
Packit |
ae9e2a |
_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
|
|
Packit |
ae9e2a |
_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
LeaveCriticalSection(&g_crtdbg_stacktrace_cs);
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
int git_win32__crtdbg_stacktrace__dump(
|
|
Packit |
ae9e2a |
git_win32__crtdbg_stacktrace_options opt,
|
|
Packit |
ae9e2a |
const char *label)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
_CRT_REPORT_HOOK old;
|
|
Packit |
ae9e2a |
unsigned int k;
|
|
Packit |
ae9e2a |
int r = 0;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
#define IS_BIT_SET(o,b) (((o) & (b)) != 0)
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
bool b_set_mark = IS_BIT_SET(opt, GIT_WIN32__CRTDBG_STACKTRACE__SET_MARK);
|
|
Packit |
ae9e2a |
bool b_leaks_since_mark = IS_BIT_SET(opt, GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK);
|
|
Packit |
ae9e2a |
bool b_leaks_total = IS_BIT_SET(opt, GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_TOTAL);
|
|
Packit |
ae9e2a |
bool b_quiet = IS_BIT_SET(opt, GIT_WIN32__CRTDBG_STACKTRACE__QUIET);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (b_leaks_since_mark && b_leaks_total) {
|
|
Packit |
ae9e2a |
giterr_set(GITERR_INVALID, "cannot combine LEAKS_SINCE_MARK and LEAKS_TOTAL.");
|
|
Packit |
ae9e2a |
return GIT_ERROR;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
if (!b_set_mark && !b_leaks_since_mark && !b_leaks_total) {
|
|
Packit |
ae9e2a |
giterr_set(GITERR_INVALID, "nothing to do.");
|
|
Packit |
ae9e2a |
return GIT_ERROR;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
EnterCriticalSection(&g_crtdbg_stacktrace_cs);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (b_leaks_since_mark || b_leaks_total) {
|
|
Packit |
ae9e2a |
/* All variables with "transient" in the name are per-dump counters
|
|
Packit |
ae9e2a |
* and reset before each dump. This lets us handle checkpoints.
|
|
Packit |
ae9e2a |
*/
|
|
Packit |
ae9e2a |
g_transient_count_total_leaks = 0;
|
|
Packit |
ae9e2a |
g_transient_count_dedup_leaks = 0;
|
|
Packit |
ae9e2a |
for (k = 0; k < g_cs_ins; k++) {
|
|
Packit |
ae9e2a |
g_cs_rows[k].transient_count_leaks = 0;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
g_transient_leaks_since_mark = b_leaks_since_mark;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
old = _CrtSetReportHook(report_hook);
|
|
Packit |
ae9e2a |
_CrtDumpMemoryLeaks();
|
|
Packit |
ae9e2a |
_CrtSetReportHook(old);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (b_leaks_since_mark || b_leaks_total) {
|
|
Packit |
ae9e2a |
r = g_transient_count_dedup_leaks;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (!b_quiet)
|
|
Packit |
ae9e2a |
dump_summary(label);
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (b_set_mark) {
|
|
Packit |
ae9e2a |
for (k = 0; k < g_cs_ins; k++) {
|
|
Packit |
ae9e2a |
g_cs_rows[k].count_allocs_at_last_checkpoint = g_cs_rows[k].count_allocs;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
g_checkpoint_id++;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
LeaveCriticalSection(&g_crtdbg_stacktrace_cs);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
return r;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
void git_win32__crtdbg_stacktrace_cleanup(void)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
/* At shutdown/cleanup, dump cummulative leak info
|
|
Packit |
ae9e2a |
* with everything since startup. This might generate
|
|
Packit |
ae9e2a |
* extra noise if the caller has been doing checkpoint
|
|
Packit |
ae9e2a |
* dumps, but it might also eliminate some false
|
|
Packit |
ae9e2a |
* positives for resources previously reported during
|
|
Packit |
ae9e2a |
* checkpoints.
|
|
Packit |
ae9e2a |
*/
|
|
Packit |
ae9e2a |
git_win32__crtdbg_stacktrace__dump(
|
|
Packit |
ae9e2a |
GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_TOTAL,
|
|
Packit |
ae9e2a |
"CLEANUP");
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
DeleteCriticalSection(&g_crtdbg_stacktrace_cs);
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
const char *git_win32__crtdbg_stacktrace(int skip, const char *file)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
git_win32__stack__raw_data new_data;
|
|
Packit |
ae9e2a |
git_win32__crtdbg_stacktrace__row *row;
|
|
Packit |
ae9e2a |
const char * result = file;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (git_win32__stack_capture(&new_data, skip+1) < 0)
|
|
Packit |
ae9e2a |
return result;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
EnterCriticalSection(&g_crtdbg_stacktrace_cs);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (g_cs_ins < g_cs_end) {
|
|
Packit |
ae9e2a |
row = insert_unique(&new_data);
|
|
Packit |
ae9e2a |
result = row->uid.uid;
|
|
Packit |
ae9e2a |
} else {
|
|
Packit |
ae9e2a |
g_limit_reached = true;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
g_count_total_allocs++;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
LeaveCriticalSection(&g_crtdbg_stacktrace_cs);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
return result;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
#endif
|