2018-05-18 11:57:32 -04:00
|
|
|
/***********************************************************************************************************************************
|
|
|
|
Stack Trace Handler
|
|
|
|
***********************************************************************************************************************************/
|
2019-04-26 08:08:23 -04:00
|
|
|
#include "build.auto.h"
|
|
|
|
|
2018-05-18 11:57:32 -04:00
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#ifdef WITH_BACKTRACE
|
|
|
|
#include <backtrace.h>
|
|
|
|
#include <backtrace-supported.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "common/assert.h"
|
|
|
|
#include "common/error.h"
|
|
|
|
#include "common/stackTrace.h"
|
|
|
|
|
|
|
|
/***********************************************************************************************************************************
|
|
|
|
Max call stack depth
|
|
|
|
***********************************************************************************************************************************/
|
|
|
|
#define STACK_TRACE_MAX 128
|
|
|
|
|
|
|
|
/***********************************************************************************************************************************
|
2020-08-21 16:12:44 -04:00
|
|
|
Local variables
|
2018-05-18 11:57:32 -04:00
|
|
|
***********************************************************************************************************************************/
|
2020-08-21 16:12:44 -04:00
|
|
|
// Stack trace function data
|
2018-05-18 11:57:32 -04:00
|
|
|
typedef struct StackTraceData
|
|
|
|
{
|
|
|
|
const char *fileName;
|
|
|
|
const char *functionName;
|
|
|
|
unsigned int fileLine;
|
|
|
|
LogLevel functionLogLevel;
|
|
|
|
unsigned int tryDepth;
|
|
|
|
|
|
|
|
bool paramOverflow;
|
|
|
|
bool paramLog;
|
2020-08-20 15:53:01 -04:00
|
|
|
char *param;
|
|
|
|
size_t paramSize;
|
2018-05-18 11:57:32 -04:00
|
|
|
} StackTraceData;
|
|
|
|
|
2020-08-21 16:12:44 -04:00
|
|
|
static struct StackTraceLocal
|
|
|
|
{
|
|
|
|
int stackSize; // Stack size
|
|
|
|
StackTraceData stack[STACK_TRACE_MAX]; // Stack data
|
|
|
|
char functionParamBuffer[32 * 1024]; // Buffer to hold function parameters
|
|
|
|
} stackTraceLocal;
|
2018-05-18 11:57:32 -04:00
|
|
|
|
2020-04-03 18:01:28 -04:00
|
|
|
/**********************************************************************************************************************************/
|
2018-05-18 11:57:32 -04:00
|
|
|
#ifdef WITH_BACKTRACE
|
|
|
|
|
2020-08-21 16:12:44 -04:00
|
|
|
static struct StackTraceBackLocal
|
|
|
|
{
|
|
|
|
struct backtrace_state *backTraceState; // Backtrace state struct
|
2020-09-17 15:17:13 -04:00
|
|
|
} stackTraceBackLocal;
|
2020-08-21 11:45:54 -04:00
|
|
|
|
2018-05-18 11:57:32 -04:00
|
|
|
void
|
|
|
|
stackTraceInit(const char *exe)
|
|
|
|
{
|
2020-08-21 16:12:44 -04:00
|
|
|
if (stackTraceBackLocal.backTraceState == NULL)
|
|
|
|
stackTraceBackLocal.backTraceState = backtrace_create_state(exe, false, NULL, NULL);
|
2018-05-18 11:57:32 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
backTraceCallback(void *data, uintptr_t pc, const char *filename, int lineno, const char *function)
|
|
|
|
{
|
|
|
|
(void)(data);
|
|
|
|
(void)(pc);
|
|
|
|
(void)(filename);
|
|
|
|
(void)(function);
|
|
|
|
|
2020-08-21 16:12:44 -04:00
|
|
|
if (stackTraceLocal.stackSize > 0)
|
|
|
|
stackTraceLocal.stack[stackTraceLocal.stackSize - 1].fileLine = (unsigned int)lineno;
|
2018-05-18 11:57:32 -04:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
backTraceCallbackError(void *data, const char *msg, int errnum)
|
|
|
|
{
|
|
|
|
(void)data;
|
|
|
|
(void)msg;
|
|
|
|
(void)errnum;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2020-04-03 18:01:28 -04:00
|
|
|
/**********************************************************************************************************************************/
|
2018-05-18 11:57:32 -04:00
|
|
|
#ifndef NDEBUG
|
|
|
|
|
2020-08-21 16:12:44 -04:00
|
|
|
static struct StackTraceTestLocal
|
|
|
|
{
|
|
|
|
bool testFlag; // Don't log in parameter logging functions to avoid recursion
|
|
|
|
} stackTraceTestLocal = {.testFlag = true};
|
2018-05-18 11:57:32 -04:00
|
|
|
|
|
|
|
void
|
2018-08-03 19:19:14 -04:00
|
|
|
stackTraceTestStart(void)
|
2018-05-18 11:57:32 -04:00
|
|
|
{
|
2020-08-21 16:12:44 -04:00
|
|
|
stackTraceTestLocal.testFlag = true;
|
2018-05-18 11:57:32 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2018-08-03 19:19:14 -04:00
|
|
|
stackTraceTestStop(void)
|
2018-05-18 11:57:32 -04:00
|
|
|
{
|
2020-08-21 16:12:44 -04:00
|
|
|
stackTraceTestLocal.testFlag = false;
|
2018-05-18 11:57:32 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2018-08-03 19:19:14 -04:00
|
|
|
stackTraceTest(void)
|
2018-05-18 11:57:32 -04:00
|
|
|
{
|
2020-08-21 16:12:44 -04:00
|
|
|
return stackTraceTestLocal.testFlag;
|
2018-05-18 11:57:32 -04:00
|
|
|
}
|
2020-03-22 16:04:24 -04:00
|
|
|
|
|
|
|
void
|
|
|
|
stackTraceTestFileLineSet(unsigned int fileLine)
|
|
|
|
{
|
2020-08-21 16:12:44 -04:00
|
|
|
ASSERT(stackTraceLocal.stackSize > 0);
|
2020-08-21 11:43:26 -04:00
|
|
|
|
2020-08-21 16:12:44 -04:00
|
|
|
stackTraceLocal.stack[stackTraceLocal.stackSize - 1].fileLine = fileLine;
|
2020-03-22 16:04:24 -04:00
|
|
|
}
|
|
|
|
|
2018-05-18 11:57:32 -04:00
|
|
|
#endif
|
|
|
|
|
2020-04-03 18:01:28 -04:00
|
|
|
/**********************************************************************************************************************************/
|
2018-05-18 11:57:32 -04:00
|
|
|
LogLevel
|
|
|
|
stackTracePush(const char *fileName, const char *functionName, LogLevel functionLogLevel)
|
|
|
|
{
|
2020-08-21 16:12:44 -04:00
|
|
|
ASSERT(stackTraceLocal.stackSize < STACK_TRACE_MAX - 1);
|
2018-05-18 11:57:32 -04:00
|
|
|
|
|
|
|
// Get line number from backtrace if available
|
|
|
|
#ifdef WITH_BACKTRACE
|
2020-08-21 16:12:44 -04:00
|
|
|
backtrace_full(stackTraceBackLocal.backTraceState, 2, backTraceCallback, backTraceCallbackError, NULL);
|
2018-05-18 11:57:32 -04:00
|
|
|
#endif
|
|
|
|
|
2020-01-23 14:15:58 -07:00
|
|
|
// Set function info
|
2020-08-21 16:12:44 -04:00
|
|
|
StackTraceData *data = &stackTraceLocal.stack[stackTraceLocal.stackSize];
|
2018-05-18 11:57:32 -04:00
|
|
|
|
2020-01-23 14:15:58 -07:00
|
|
|
*data = (StackTraceData)
|
|
|
|
{
|
|
|
|
.fileName = fileName,
|
|
|
|
.functionName = functionName,
|
|
|
|
.tryDepth = errorTryDepth(),
|
|
|
|
};
|
2018-05-18 11:57:32 -04:00
|
|
|
|
|
|
|
// Set param pointer
|
2020-08-21 16:12:44 -04:00
|
|
|
if (stackTraceLocal.stackSize == 0)
|
2018-05-18 11:57:32 -04:00
|
|
|
{
|
2020-08-21 16:12:44 -04:00
|
|
|
data->param = stackTraceLocal.functionParamBuffer;
|
2018-05-18 11:57:32 -04:00
|
|
|
data->functionLogLevel = functionLogLevel;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-08-21 16:12:44 -04:00
|
|
|
StackTraceData *dataPrior = &stackTraceLocal.stack[stackTraceLocal.stackSize - 1];
|
2018-05-18 11:57:32 -04:00
|
|
|
|
|
|
|
data->param = dataPrior->param + dataPrior->paramSize + 1;
|
|
|
|
|
|
|
|
// Log level cannot be lower than the prior function
|
|
|
|
if (functionLogLevel < dataPrior->functionLogLevel)
|
|
|
|
data->functionLogLevel = dataPrior->functionLogLevel;
|
|
|
|
else
|
|
|
|
data->functionLogLevel = functionLogLevel;
|
|
|
|
}
|
|
|
|
|
2020-08-21 16:12:44 -04:00
|
|
|
stackTraceLocal.stackSize++;
|
2018-05-18 11:57:32 -04:00
|
|
|
|
|
|
|
return data->functionLogLevel;
|
|
|
|
}
|
|
|
|
|
2020-04-03 18:01:28 -04:00
|
|
|
/**********************************************************************************************************************************/
|
2018-05-18 11:57:32 -04:00
|
|
|
static const char *
|
|
|
|
stackTraceParamIdx(int stackIdx)
|
|
|
|
{
|
2020-08-21 16:12:44 -04:00
|
|
|
ASSERT(stackTraceLocal.stackSize > 0);
|
|
|
|
ASSERT(stackIdx < stackTraceLocal.stackSize);
|
2018-05-18 11:57:32 -04:00
|
|
|
|
2020-08-21 16:12:44 -04:00
|
|
|
StackTraceData *data = &stackTraceLocal.stack[stackIdx];
|
2018-05-18 11:57:32 -04:00
|
|
|
|
|
|
|
if (data->paramLog)
|
|
|
|
{
|
|
|
|
if (data->paramOverflow)
|
2018-11-14 08:11:11 -05:00
|
|
|
return "buffer full - parameters not available";
|
2018-05-18 11:57:32 -04:00
|
|
|
|
|
|
|
if (data->paramSize == 0)
|
|
|
|
return "void";
|
|
|
|
|
|
|
|
return data->param;
|
|
|
|
}
|
|
|
|
|
2018-07-20 07:15:28 -04:00
|
|
|
// If no parameters return the log level required to get them
|
|
|
|
#define LOG_LEVEL_REQUIRED " log level required for parameters"
|
|
|
|
return data->functionLogLevel == logLevelTrace ? "trace" LOG_LEVEL_REQUIRED : "debug" LOG_LEVEL_REQUIRED;
|
2018-05-18 11:57:32 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
const char *
|
|
|
|
stackTraceParam()
|
|
|
|
{
|
2020-08-21 16:12:44 -04:00
|
|
|
return stackTraceParamIdx(stackTraceLocal.stackSize - 1);
|
2018-05-18 11:57:32 -04:00
|
|
|
}
|
|
|
|
|
2020-04-03 18:01:28 -04:00
|
|
|
/**********************************************************************************************************************************/
|
2018-05-18 11:57:32 -04:00
|
|
|
char *
|
|
|
|
stackTraceParamBuffer(const char *paramName)
|
|
|
|
{
|
2020-08-21 16:12:44 -04:00
|
|
|
ASSERT(stackTraceLocal.stackSize > 0);
|
2018-05-18 11:57:32 -04:00
|
|
|
|
2020-08-21 16:12:44 -04:00
|
|
|
StackTraceData *data = &stackTraceLocal.stack[stackTraceLocal.stackSize - 1];
|
2018-05-18 11:57:32 -04:00
|
|
|
size_t paramNameSize = strlen(paramName);
|
|
|
|
|
|
|
|
// Make sure that adding this parameter will not overflow the buffer
|
2020-08-21 16:12:44 -04:00
|
|
|
if ((size_t)(data->param - stackTraceLocal.functionParamBuffer) + data->paramSize + paramNameSize + 4 >
|
|
|
|
sizeof(stackTraceLocal.functionParamBuffer) - (STACK_TRACE_PARAM_MAX * 2))
|
2018-05-18 11:57:32 -04:00
|
|
|
{
|
|
|
|
// Set overflow to true
|
|
|
|
data->paramOverflow = true;
|
|
|
|
|
|
|
|
// There's no way to stop the parameter from being formatted so we reserve a space at the end where the format can safely
|
|
|
|
// take place and not disturb the rest of the buffer. Hopefully overflows just won't happen but we need to be prepared in
|
|
|
|
// case of runaway recursion or some other issue that fills the buffer because we don't want a segfault.
|
2020-08-21 16:12:44 -04:00
|
|
|
return stackTraceLocal.functionParamBuffer + sizeof(stackTraceLocal.functionParamBuffer) - STACK_TRACE_PARAM_MAX;
|
2018-05-18 11:57:32 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Add a comma if a parameter is already in the list
|
|
|
|
if (data->paramSize != 0)
|
|
|
|
{
|
|
|
|
data->param[data->paramSize++] = ',';
|
|
|
|
data->param[data->paramSize++] = ' ';
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add the parameter name
|
|
|
|
strcpy(data->param + data->paramSize, paramName);
|
|
|
|
data->paramSize += paramNameSize;
|
|
|
|
|
|
|
|
// Add param/value separator
|
|
|
|
data->param[data->paramSize++] = ':';
|
|
|
|
data->param[data->paramSize++] = ' ';
|
|
|
|
|
|
|
|
return data->param + data->paramSize;
|
|
|
|
}
|
|
|
|
|
2020-04-03 18:01:28 -04:00
|
|
|
/**********************************************************************************************************************************/
|
2018-05-18 11:57:32 -04:00
|
|
|
void
|
|
|
|
stackTraceParamAdd(size_t bufferSize)
|
|
|
|
{
|
2020-08-21 16:12:44 -04:00
|
|
|
ASSERT(stackTraceLocal.stackSize > 0);
|
2018-05-18 11:57:32 -04:00
|
|
|
|
2020-08-21 16:12:44 -04:00
|
|
|
StackTraceData *data = &stackTraceLocal.stack[stackTraceLocal.stackSize - 1];
|
2018-05-18 11:57:32 -04:00
|
|
|
|
|
|
|
if (!data->paramOverflow)
|
|
|
|
data->paramSize += bufferSize;
|
|
|
|
}
|
|
|
|
|
2020-04-03 18:01:28 -04:00
|
|
|
/**********************************************************************************************************************************/
|
2018-05-18 11:57:32 -04:00
|
|
|
void
|
2018-08-03 19:19:14 -04:00
|
|
|
stackTraceParamLog(void)
|
2018-05-18 11:57:32 -04:00
|
|
|
{
|
2020-08-21 16:12:44 -04:00
|
|
|
ASSERT(stackTraceLocal.stackSize > 0);
|
2018-05-18 11:57:32 -04:00
|
|
|
|
2020-08-21 16:12:44 -04:00
|
|
|
stackTraceLocal.stack[stackTraceLocal.stackSize - 1].paramLog = true;
|
2018-05-18 11:57:32 -04:00
|
|
|
}
|
|
|
|
|
2020-04-03 18:01:28 -04:00
|
|
|
/**********************************************************************************************************************************/
|
2018-05-18 11:57:32 -04:00
|
|
|
#ifdef NDEBUG
|
|
|
|
|
|
|
|
void
|
2018-08-03 19:19:14 -04:00
|
|
|
stackTracePop(void)
|
2018-05-18 11:57:32 -04:00
|
|
|
{
|
2020-08-21 16:12:44 -04:00
|
|
|
stackTraceLocal.stackSize--;
|
2018-05-18 11:57:32 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
void
|
2019-05-11 14:51:51 -04:00
|
|
|
stackTracePop(const char *fileName, const char *functionName, bool test)
|
2018-05-18 11:57:32 -04:00
|
|
|
{
|
2020-08-21 16:12:44 -04:00
|
|
|
ASSERT(stackTraceLocal.stackSize > 0);
|
2018-05-18 11:57:32 -04:00
|
|
|
|
2019-05-11 14:51:51 -04:00
|
|
|
if (!test || stackTraceTest())
|
|
|
|
{
|
2020-08-21 16:12:44 -04:00
|
|
|
stackTraceLocal.stackSize--;
|
2018-05-18 11:57:32 -04:00
|
|
|
|
2020-08-21 16:12:44 -04:00
|
|
|
StackTraceData *data = &stackTraceLocal.stack[stackTraceLocal.stackSize];
|
2018-05-18 11:57:32 -04:00
|
|
|
|
2019-05-11 14:51:51 -04:00
|
|
|
if (strcmp(data->fileName, fileName) != 0 || strcmp(data->functionName, functionName) != 0)
|
|
|
|
THROW_FMT(AssertError, "popping %s:%s but expected %s:%s", fileName, functionName, data->fileName, data->functionName);
|
|
|
|
}
|
2018-05-18 11:57:32 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/***********************************************************************************************************************************
|
|
|
|
Stack trace format
|
|
|
|
***********************************************************************************************************************************/
|
2021-01-22 09:50:29 -05:00
|
|
|
__attribute__((format(printf, 4, 5))) static size_t
|
2018-05-18 11:57:32 -04:00
|
|
|
stackTraceFmt(char *buffer, size_t bufferSize, size_t bufferUsed, const char *format, ...)
|
|
|
|
{
|
|
|
|
va_list argumentList;
|
|
|
|
va_start(argumentList, format);
|
|
|
|
int result = vsnprintf(
|
|
|
|
buffer + bufferUsed, bufferUsed < bufferSize ? bufferSize - bufferUsed : 0, format, argumentList);
|
|
|
|
va_end(argumentList);
|
|
|
|
|
|
|
|
return (size_t)result;
|
|
|
|
}
|
|
|
|
|
2020-04-03 18:01:28 -04:00
|
|
|
/**********************************************************************************************************************************/
|
2018-05-18 11:57:32 -04:00
|
|
|
size_t
|
|
|
|
stackTraceToZ(char *buffer, size_t bufferSize, const char *fileName, const char *functionName, unsigned int fileLine)
|
|
|
|
{
|
|
|
|
size_t result = 0;
|
|
|
|
const char *param = "test build required for parameters";
|
2020-08-21 16:12:44 -04:00
|
|
|
int stackIdx = stackTraceLocal.stackSize - 1;
|
2018-05-18 11:57:32 -04:00
|
|
|
|
|
|
|
// If the current function passed in is the same as the top function on the stack then use the parameters for that function
|
2020-08-21 16:12:44 -04:00
|
|
|
if (stackTraceLocal.stackSize > 0 && strcmp(fileName, stackTraceLocal.stack[stackIdx].fileName) == 0 &&
|
|
|
|
strcmp(functionName, stackTraceLocal.stack[stackIdx].functionName) == 0)
|
2018-05-18 11:57:32 -04:00
|
|
|
{
|
2020-08-21 16:12:44 -04:00
|
|
|
param = stackTraceParamIdx(stackTraceLocal.stackSize - 1);
|
2018-05-18 11:57:32 -04:00
|
|
|
stackIdx--;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Output the current function
|
|
|
|
result = stackTraceFmt(
|
|
|
|
buffer, bufferSize, 0, "%.*s:%s:%u:(%s)", (int)(strlen(fileName) - 2), fileName, functionName, fileLine, param);
|
|
|
|
|
|
|
|
// Output stack if there is anything on it
|
|
|
|
if (stackIdx >= 0)
|
|
|
|
{
|
|
|
|
// If the function passed in was not at the top of the stack then some functions are missing
|
2020-08-21 16:12:44 -04:00
|
|
|
if (stackIdx == stackTraceLocal.stackSize - 1)
|
2019-08-26 12:20:51 -04:00
|
|
|
result += stackTraceFmt(buffer, bufferSize, result, "\n ... function(s) omitted ...");
|
2018-05-18 11:57:32 -04:00
|
|
|
|
|
|
|
// Output the rest of the stack
|
|
|
|
for (; stackIdx >= 0; stackIdx--)
|
|
|
|
{
|
2020-08-21 16:12:44 -04:00
|
|
|
StackTraceData *data = &stackTraceLocal.stack[stackIdx];
|
2018-05-18 11:57:32 -04:00
|
|
|
|
|
|
|
result += stackTraceFmt(
|
2020-03-22 16:04:24 -04:00
|
|
|
buffer, bufferSize, result, "\n%.*s:%s", (int)(strlen(data->fileName) - 2), data->fileName, data->functionName);
|
2018-05-18 11:57:32 -04:00
|
|
|
|
2020-03-22 16:04:24 -04:00
|
|
|
if (data->fileLine > 0)
|
|
|
|
result += stackTraceFmt(buffer, bufferSize, result, ":%u", data->fileLine);
|
2018-05-18 11:57:32 -04:00
|
|
|
|
2020-03-22 16:04:24 -04:00
|
|
|
result += stackTraceFmt(buffer, bufferSize, result, ":(%s)", stackTraceParamIdx(stackIdx));
|
2018-05-18 11:57:32 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2020-04-03 18:01:28 -04:00
|
|
|
/**********************************************************************************************************************************/
|
2018-05-18 11:57:32 -04:00
|
|
|
void
|
|
|
|
stackTraceClean(unsigned int tryDepth)
|
|
|
|
{
|
2020-08-21 16:12:44 -04:00
|
|
|
while (stackTraceLocal.stackSize > 0 && stackTraceLocal.stack[stackTraceLocal.stackSize - 1].tryDepth >= tryDepth)
|
|
|
|
stackTraceLocal.stackSize--;
|
2018-05-18 11:57:32 -04:00
|
|
|
}
|