mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-02-21 19:48:29 +02:00
Enhance libbacktrace to handle incomplete stack traces.
This fills in backtrace info at the bottom of the call stack when the stack trace is incomplete due to testing. This does not affect release builds, which is why it did not make the first cut, but it turns out to be useful for testing and barely changes the release code (when we do release this). The recursion test in common/error was simplified because it would now return a very large trace.
This commit is contained in:
parent
57fc4aaeb9
commit
0becb6da31
@ -291,13 +291,9 @@ static int
|
||||
stackTraceBackCallback(
|
||||
void *const dataVoid, const uintptr_t pc, const char *fileName, const int fileLine, const char *const functionName)
|
||||
{
|
||||
(void)(pc);
|
||||
(void)pc;
|
||||
StackTraceBackData *const data = dataVoid;
|
||||
|
||||
// Stop if the stack has been exhausted
|
||||
if (data->stackIdx < 0)
|
||||
return true;
|
||||
|
||||
// Catch any unset parameters which indicates the debug data is not available
|
||||
if (fileName == NULL || fileLine == 0 || functionName == NULL)
|
||||
{
|
||||
@ -314,13 +310,12 @@ stackTraceBackCallback(
|
||||
data->firstCall = false;
|
||||
|
||||
// If the function name matches combine backtrace data with stack data
|
||||
const StackTraceData *const traceData = &stackTraceLocal.stack[data->stackIdx];
|
||||
|
||||
if (strcmp(functionName, traceData->functionName) == 0)
|
||||
if (data->stackIdx >= 0 && strcmp(functionName, stackTraceLocal.stack[data->stackIdx].functionName) == 0)
|
||||
{
|
||||
data->result += stackTraceFmt(
|
||||
data->buffer, data->bufferSize, data->result, "%s%s:%s:%d:(%s)", data->firstLine ? "" : "\n",
|
||||
stackTraceTrimSrc(traceData->fileName), functionName, fileLine, stackTraceParamIdx(data->stackIdx));
|
||||
stackTraceTrimSrc(stackTraceLocal.stack[data->stackIdx].fileName), functionName, fileLine,
|
||||
stackTraceParamIdx(data->stackIdx));
|
||||
|
||||
data->stackIdx--;
|
||||
}
|
||||
|
@ -65,6 +65,12 @@ unit:
|
||||
- name: stack-trace
|
||||
total: 4
|
||||
feature: stackTrace
|
||||
harness:
|
||||
name: stackTrace
|
||||
shim:
|
||||
common/stackTrace:
|
||||
function:
|
||||
- stackTraceBackCallback
|
||||
|
||||
coverage:
|
||||
- common/stackTrace
|
||||
|
59
test/src/common/harnessStackTrace.c
Normal file
59
test/src/common/harnessStackTrace.c
Normal file
@ -0,0 +1,59 @@
|
||||
/***********************************************************************************************************************************
|
||||
Harness for Stack Trace Testing
|
||||
***********************************************************************************************************************************/
|
||||
#include "build.auto.h"
|
||||
|
||||
#include "common/harnessDebug.h"
|
||||
#include "common/harnessStackTrace.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Include shimmed C modules
|
||||
***********************************************************************************************************************************/
|
||||
{[SHIM_MODULE]}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim install state
|
||||
***********************************************************************************************************************************/
|
||||
#ifdef HAVE_LIBBACKTRACE
|
||||
|
||||
static struct
|
||||
{
|
||||
bool backTraceShimInstalled;
|
||||
} hrnStackTraceStatic;
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim to return true on backtrace callback indicating that there is no backtrace
|
||||
***********************************************************************************************************************************/
|
||||
static int
|
||||
stackTraceBackCallback(
|
||||
void *const dataVoid, const uintptr_t pc, const char *fileName, const int fileLine, const char *const functionName)
|
||||
{
|
||||
if (hrnStackTraceStatic.backTraceShimInstalled)
|
||||
return true;
|
||||
|
||||
return stackTraceBackCallback_SHIMMED(dataVoid, pc, fileName, fileLine, functionName);
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
void
|
||||
hrnStackTraceBackShimInstall(void)
|
||||
{
|
||||
FUNCTION_HARNESS_VOID();
|
||||
|
||||
hrnStackTraceStatic.backTraceShimInstalled = true;
|
||||
|
||||
FUNCTION_HARNESS_RETURN_VOID();
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
void
|
||||
hrnStackTraceBackShimUninstall(void)
|
||||
{
|
||||
FUNCTION_HARNESS_VOID();
|
||||
|
||||
hrnStackTraceStatic.backTraceShimInstalled = false;
|
||||
|
||||
FUNCTION_HARNESS_RETURN_VOID();
|
||||
}
|
||||
|
||||
#endif // HAVE_LIBBACKTRACE
|
12
test/src/common/harnessStackTrace.h
Normal file
12
test/src/common/harnessStackTrace.h
Normal file
@ -0,0 +1,12 @@
|
||||
/***********************************************************************************************************************************
|
||||
Harness for Stack Trace Testing
|
||||
***********************************************************************************************************************************/
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Functions
|
||||
***********************************************************************************************************************************/
|
||||
// Install/uninstall shim to return true from stackTraceBackCallback() to indicate that no backtrace data was found
|
||||
#ifdef HAVE_LIBBACKTRACE
|
||||
void hrnStackTraceBackShimInstall(void);
|
||||
void hrnStackTraceBackShimUninstall(void);
|
||||
#endif
|
@ -250,11 +250,7 @@ testRun(void)
|
||||
assert(strcmp(errorFileName(), TEST_PGB_PATH "/test/src/module/common/errorTest.c") == 0);
|
||||
assert(strcmp(errorFunctionName(), "testTryRecurse") == 0);
|
||||
assert(errorFileLine() == 29);
|
||||
assert(
|
||||
strcmp(
|
||||
errorStackTrace(),
|
||||
"module/common/errorTest.c:testTryRecurse:29:(test build required for parameters)")
|
||||
== 0);
|
||||
assert(errorStackTrace() != NULL);
|
||||
assert(strcmp(errorMessage(), "too many nested try blocks") == 0);
|
||||
assert(strcmp(errorName(), AssertError.name) == 0);
|
||||
assert(errorType() == &AssertError);
|
||||
|
@ -3,6 +3,8 @@ Test Stack Trace Handler
|
||||
***********************************************************************************************************************************/
|
||||
#include <assert.h>
|
||||
|
||||
#include "common/harnessStackTrace.h"
|
||||
|
||||
#ifdef HAVE_LIBBACKTRACE
|
||||
|
||||
FN_NO_RETURN void
|
||||
@ -98,12 +100,23 @@ testRun(void)
|
||||
{
|
||||
TRY_BEGIN()
|
||||
{
|
||||
char buffer[4096];
|
||||
snprintf(buffer, sizeof(buffer), "%s", errorStackTrace());
|
||||
|
||||
if (strstr(buffer, ":testRun:") != NULL)
|
||||
memcpy(strstr(buffer, ":testRun:") + 9, "XX", 2);
|
||||
|
||||
if (strstr(buffer, ":main:") != NULL)
|
||||
memcpy(strstr(buffer, ":main:") + 6, "XXX", 3);
|
||||
|
||||
TEST_RESULT_Z(
|
||||
errorStackTrace(),
|
||||
"module/common/stackTraceTest.c:testStackTraceError3:12:(trace log level required for parameters)\n"
|
||||
"module/common/stackTraceTest.c:testStackTraceError2:18:(no parameters available)\n"
|
||||
"file1.c:testStackTraceError1:25:(debug log level required for parameters)",
|
||||
"check stack trace");
|
||||
buffer,
|
||||
"module/common/stackTraceTest.c:testStackTraceError3:14:(trace log level required for parameters)\n"
|
||||
"module/common/stackTraceTest.c:testStackTraceError2:20:(no parameters available)\n"
|
||||
"file1.c:testStackTraceError1:27:(debug log level required for parameters)\n"
|
||||
"module/common/stackTraceTest.c:testRun:XX:(no parameters available)\n"
|
||||
"../test.c:main:XXX:(no parameters available)",
|
||||
"check stack trace");
|
||||
}
|
||||
CATCH(TestError)
|
||||
{
|
||||
@ -117,9 +130,9 @@ testRun(void)
|
||||
{
|
||||
TEST_RESULT_Z(
|
||||
errorStackTrace(),
|
||||
"module/common/stackTraceTest.c:testStackTraceError3:12:(trace log level required for parameters)\n"
|
||||
"file1.c:testStackTraceError1:(debug log level required for parameters)",
|
||||
"check stack trace");
|
||||
"module/common/stackTraceTest.c:testStackTraceError3:14:(trace log level required for parameters)\n"
|
||||
"file1.c:testStackTraceError1:(debug log level required for parameters)",
|
||||
"check stack trace");
|
||||
}
|
||||
TRY_END();
|
||||
}
|
||||
@ -135,11 +148,22 @@ testRun(void)
|
||||
{
|
||||
TRY_BEGIN()
|
||||
{
|
||||
char buffer[4096];
|
||||
snprintf(buffer, sizeof(buffer), "%s", errorStackTrace());
|
||||
|
||||
if (strstr(buffer, ":testRun:") != NULL)
|
||||
memcpy(strstr(buffer, ":testRun:") + 9, "XXX", 3);
|
||||
|
||||
if (strstr(buffer, ":main:") != NULL)
|
||||
memcpy(strstr(buffer, ":main:") + 6, "XXX", 3);
|
||||
|
||||
TEST_RESULT_Z(
|
||||
errorStackTrace(),
|
||||
"module/common/stackTraceTest.c:testStackTraceError5:31:(no parameters available)\n"
|
||||
"file4.c:testStackTraceError4:38:(trace log level required for parameters)",
|
||||
"check stack trace");
|
||||
buffer,
|
||||
"module/common/stackTraceTest.c:testStackTraceError5:33:(no parameters available)\n"
|
||||
"file4.c:testStackTraceError4:40:(trace log level required for parameters)\n"
|
||||
"module/common/stackTraceTest.c:testRun:XXX:(no parameters available)\n"
|
||||
"../test.c:main:XXX:(no parameters available)",
|
||||
"check stack trace");
|
||||
}
|
||||
CATCH(TestError)
|
||||
{
|
||||
@ -153,16 +177,26 @@ testRun(void)
|
||||
{
|
||||
TEST_RESULT_Z(
|
||||
errorStackTrace(),
|
||||
"module/common/stackTraceTest.c:testStackTraceError5:31:(test build required for parameters)\n"
|
||||
" ... function(s) omitted ...\n"
|
||||
"file4.c:testStackTraceError4:(trace log level required for parameters)",
|
||||
"check stack trace");
|
||||
"module/common/stackTraceTest.c:testStackTraceError5:33:(test build required for parameters)\n"
|
||||
" ... function(s) omitted ...\n"
|
||||
"file4.c:testStackTraceError4:(trace log level required for parameters)",
|
||||
"check stack trace");
|
||||
}
|
||||
TRY_END();
|
||||
}
|
||||
TRY_END();
|
||||
}
|
||||
TRY_END();
|
||||
|
||||
// *************************************************************************************************************************
|
||||
TEST_TITLE("disable backtrace to make sure default code is called");
|
||||
|
||||
hrnStackTraceBackShimInstall();
|
||||
|
||||
char buffer[4096];
|
||||
stackTraceToZ(buffer, sizeof(buffer), "file", "function", 1);
|
||||
|
||||
hrnStackTraceBackShimUninstall();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -170,6 +170,12 @@ testRun(void)
|
||||
" - name: stack-trace\n"
|
||||
" total: 4\n"
|
||||
" feature: stackTrace\n"
|
||||
" harness:\n"
|
||||
" name: stackTrace\n"
|
||||
" shim:\n"
|
||||
" common/stackTrace:\n"
|
||||
" function:\n"
|
||||
" - stackTraceBackCallback\n"
|
||||
" coverage:\n"
|
||||
" - common/stackTrace\n"
|
||||
" depend:\n"
|
||||
@ -239,6 +245,8 @@ testRun(void)
|
||||
"test/src/common/harnessLog.h",
|
||||
"test/src/common/harnessError.c",
|
||||
"test/src/common/harnessError.h",
|
||||
"test/src/common/harnessStackTrace.h",
|
||||
"test/src/common/harnessStackTrace.c",
|
||||
"test/src/common/harnessTest.c",
|
||||
"test/src/common/harnessTest.h",
|
||||
"test/src/common/harnessTest.intern.h",
|
||||
@ -258,7 +266,9 @@ testRun(void)
|
||||
fileList,
|
||||
"meson.build\n"
|
||||
"meson_options.txt\n"
|
||||
"src/common/stackTrace.c\n"
|
||||
"test/src/common/harnessError.c\n"
|
||||
"test/src/common/harnessStackTrace.c\n"
|
||||
"test.c\n",
|
||||
"check files");
|
||||
|
||||
@ -312,6 +322,10 @@ testRun(void)
|
||||
{
|
||||
TEST_STORAGE_GET(storageUnit, strZ(file), mesonOption);
|
||||
}
|
||||
else if (strEqZ(file, "src/common/stackTrace.c") || strEqZ(file, "test/src/common/harnessStackTrace.c"))
|
||||
{
|
||||
// No test needed
|
||||
}
|
||||
else if (strEqZ(file, "test/src/common/harnessError.c"))
|
||||
{
|
||||
TEST_STORAGE_GET(storageUnit, strZ(file), strZ(harnessErrorC));
|
||||
@ -327,7 +341,7 @@ testRun(void)
|
||||
strReplace(testCDup, STRDEF("{[C_TEST_PROJECT_EXE]}"), STRDEF(TEST_PATH "/test/build/none/src/pgbackrest"));
|
||||
strReplace(testCDup, STRDEF("{[C_TEST_TZ]}"), STRDEF("// No timezone specified"));
|
||||
|
||||
strReplace(testCDup, STRDEF("{[C_INCLUDE]}"), STRDEF("#include \"../../../repo/src/common/stackTrace.c\""));
|
||||
strReplace(testCDup, STRDEF("{[C_INCLUDE]}"), STRDEF("#include \"test/src/common/harnessStackTrace.c\""));
|
||||
strReplace(
|
||||
testCDup, STRDEF("{[C_TEST_INCLUDE]}"),
|
||||
STRDEF("#include \"../../../repo/test/src/module/common/stackTraceTest.c\""));
|
||||
@ -554,8 +568,10 @@ testRun(void)
|
||||
fileList,
|
||||
"meson.build\n"
|
||||
"meson_options.txt\n"
|
||||
"src/common/stackTrace.c\n"
|
||||
"test/src/common/harnessError.c\n"
|
||||
"test/src/common/harnessShim.c\n"
|
||||
"test/src/common/harnessStackTrace.c\n"
|
||||
"test/src/common/shim.c\n"
|
||||
"test.c\n",
|
||||
"check files");
|
||||
@ -580,8 +596,8 @@ testRun(void)
|
||||
"# Unit test\n"
|
||||
MESON_COMMENT_BLOCK "\n"
|
||||
"src_unit = files(\n"
|
||||
" '../../../repo/src/common/stackTrace.c',\n"
|
||||
" '../../../repo/src/common/debug.c',\n"
|
||||
" 'test/src/common/harnessStackTrace.c',\n"
|
||||
" '../../../repo/test/src/common/harnessNoShim.c',\n"
|
||||
" '../../../repo/test/src/common/harnessShim/sub.c',\n"
|
||||
" '../../../repo/test/src/common/harnessTest.c',\n"
|
||||
@ -624,6 +640,10 @@ testRun(void)
|
||||
{
|
||||
TEST_STORAGE_GET(storageUnit, strZ(file), mesonOption);
|
||||
}
|
||||
else if (strEqZ(file, "src/common/stackTrace.c") || strEqZ(file, "test/src/common/harnessStackTrace.c"))
|
||||
{
|
||||
// No test needed
|
||||
}
|
||||
else if (strEqZ(file, "test/src/common/harnessError.c"))
|
||||
{
|
||||
TEST_STORAGE_GET(storageUnit, strZ(file), strZ(harnessErrorC));
|
||||
@ -714,8 +734,10 @@ testRun(void)
|
||||
fileList,
|
||||
"meson.build\n"
|
||||
"meson_options.txt\n"
|
||||
"src/common/stackTrace.c\n"
|
||||
"test/src/common/harnessError.c\n"
|
||||
"test/src/common/harnessShim.c\n"
|
||||
"test/src/common/harnessStackTrace.c\n"
|
||||
"test/src/common/shim.c\n"
|
||||
"test.c\n",
|
||||
"check files");
|
||||
@ -738,9 +760,9 @@ testRun(void)
|
||||
"# Unit test\n"
|
||||
MESON_COMMENT_BLOCK "\n"
|
||||
"src_unit = files(\n"
|
||||
" '../../../repo/src/common/stackTrace.c',\n"
|
||||
" '../../../repo/src/common/debug.c',\n"
|
||||
" 'test/src/common/harnessError.c',\n"
|
||||
" 'test/src/common/harnessStackTrace.c',\n"
|
||||
" '../../../repo/test/src/common/harnessNoShim.c',\n"
|
||||
" '../../../repo/test/src/common/harnessShim/sub.c',\n"
|
||||
" 'test/src/common/harnessShim.c',\n"
|
||||
@ -784,6 +806,10 @@ testRun(void)
|
||||
{
|
||||
TEST_STORAGE_GET(storageUnit, strZ(file), mesonOption);
|
||||
}
|
||||
else if (strEqZ(file, "src/common/stackTrace.c") || strEqZ(file, "test/src/common/harnessStackTrace.c"))
|
||||
{
|
||||
// No test needed
|
||||
}
|
||||
else if (strEqZ(file, "test/src/common/harnessError.c"))
|
||||
{
|
||||
TEST_STORAGE_GET(storageUnit, strZ(file), strZ(harnessErrorC));
|
||||
|
Loading…
x
Reference in New Issue
Block a user