1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-03-05 15:05:48 +02:00

Exclude mem context name from production builds.

The mem context name is used to produce clearer debug errors but it has no purpose in production builds.

Also remove memContextName() and access the struct directly since the name is only used within the common/memContext module.

Note that a few errors that were thrown in production builds (and required the name) are now only thrown in debug builds. In practice we have not seen these errors in production builds due to extensive coverage so it does not seem worth modifying the error to work without the context name.

This saves some memory, which is worthwhile, but the goal is to refactor Strings and Variants to have their own mem contexts and this change will prevent them from using more memory than they are now, along with other changes that will be coming later.
This commit is contained in:
David Steele 2022-05-02 15:17:34 -04:00
parent 0055fa40fe
commit eb435becb3
3 changed files with 46 additions and 41 deletions

View File

@ -49,7 +49,9 @@ Contains information about the memory context
***********************************************************************************************************************************/
struct MemContext
{
#ifdef DEBUG
const char *name; // Indicates what the context is being used for
#endif
MemContextState state:1; // Current state of the context
size_t allocExtra:16; // Size of extra allocation (1kB max)
@ -74,7 +76,13 @@ Top context
The top context always exists and can never be freed. All other contexts are children of the top context. The top context is
generally used to allocate memory that exists for the life of the program.
***********************************************************************************************************************************/
static MemContext contextTop = {.state = memContextStateActive, .name = "TOP"};
static MemContext contextTop =
{
.state = memContextStateActive,
#ifdef DEBUG
.name = "TOP",
#endif
};
/***********************************************************************************************************************************
Memory context stack types
@ -257,7 +265,11 @@ memContextNewIndex(MemContext *memContext)
/**********************************************************************************************************************************/
MemContext *
memContextNew(const char *const name, const MemContextNewParam param)
memContextNew(
#ifdef DEBUG
const char *const name,
#endif
const MemContextNewParam param)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRINGZ, name);
@ -280,8 +292,10 @@ memContextNew(const char *const name, const MemContextNewParam param)
*this = (MemContext)
{
#ifdef DEBUG
// Set the context name
.name = name,
#endif
// Set extra allocation
.allocExtra = param.allocExtra,
@ -373,9 +387,11 @@ memContextCallbackSet(MemContext *this, void (*callbackFunction)(void *), void *
if (this == &contextTop)
THROW(AssertError, "top context may not have a callback");
#ifdef DEBUG
// Error if callback has already been set - there may be valid use cases for this but error until one is found
if (this->callbackFunction)
THROW_FMT(AssertError, "callback is already set for context '%s'", this->name);
#endif
// Set callback function and argument
this->callbackFunction = callbackFunction;
@ -629,7 +645,7 @@ memContextSwitchBack(void)
{
THROW_FMT(
AssertError, "current context expected but new context '%s' found",
memContextName(memContextStack[memContextMaxStackIdx].memContext));
memContextStack[memContextMaxStackIdx].memContext->name);
}
#endif
@ -658,7 +674,7 @@ memContextKeep(void)
{
THROW_FMT(
AssertError, "new context expected but current context '%s' found",
memContextName(memContextStack[memContextMaxStackIdx].memContext));
memContextStack[memContextMaxStackIdx].memContext->name);
}
#endif
@ -679,7 +695,7 @@ memContextDiscard(void)
{
THROW_FMT(
AssertError, "new context expected but current context '%s' found",
memContextName(memContextStack[memContextMaxStackIdx].memContext));
memContextStack[memContextMaxStackIdx].memContext->name);
}
#endif
@ -718,23 +734,6 @@ memContextFreeing(const MemContext *const this)
FUNCTION_TEST_RETURN(BOOL, this->state == memContextStateFreeing);
}
/**********************************************************************************************************************************/
const char *
memContextName(const MemContext *const this)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(MEM_CONTEXT, this);
FUNCTION_TEST_END();
ASSERT(this != NULL);
// Error if context is not active
if (this->state != memContextStateActive)
THROW(AssertError, "cannot get name for inactive context");
FUNCTION_TEST_RETURN_CONST(STRINGZ, this->name);
}
/**********************************************************************************************************************************/
MemContext *
memContextPrior(void)
@ -827,9 +826,11 @@ memContextFree(MemContext *this)
// If context is already freeing then return if memContextFree() is called again - this can happen in callbacks
if (this->state != memContextStateFreeing)
{
#ifdef DEBUG
// Current context cannot be freed unless it is top (top is never really freed, just the stuff under it)
if (this == memContextStack[memContextCurrentStackIdx].memContext && this != &contextTop)
THROW_FMT(AssertError, "cannot free current context '%s'", this->name);
#endif
// Free child contexts
for (unsigned int contextIdx = 0; contextIdx < this->contextChildListSize; contextIdx++)

View File

@ -207,10 +207,19 @@ typedef struct MemContextNewParam
uint16_t allocExtra; // Extra memory to allocate with the context
} MemContextNewParam;
#define memContextNewP(name, ...) \
memContextNew(name, (MemContextNewParam){VAR_PARAM_INIT, __VA_ARGS__})
#ifdef DEBUG
#define memContextNewP(name, ...) \
memContextNew(name, (MemContextNewParam){VAR_PARAM_INIT, __VA_ARGS__})
#else
#define memContextNewP(name, ...) \
memContextNew((MemContextNewParam){VAR_PARAM_INIT, __VA_ARGS__})
#endif
MemContext *memContextNew(const char *name, MemContextNewParam param);
MemContext *memContextNew(
#ifdef DEBUG
const char *name,
#endif
MemContextNewParam param);
// Switch to a context making it the current mem context
void memContextSwitch(MemContext *this);
@ -268,9 +277,6 @@ MemContext *memContextPrior(void);
// good place to put long-lived mem contexts since they won't be automatically freed until the program exits.
MemContext *memContextTop(void);
// Mem context name
const char *memContextName(const MemContext *this);
// Get total size of mem context and all children
size_t memContextSize(const MemContext *this);

View File

@ -18,9 +18,7 @@ testFree(void *thisVoid)
TEST_RESULT_INT(this->state, memContextStateFreeing, "state should still be freeing after memContextFree() in callback");
TEST_ERROR(memContextCallbackSet(this, testFree, this), AssertError, "cannot assign callback to inactive context");
TEST_ERROR(memContextSwitch(this), AssertError, "cannot switch to inactive context");
TEST_ERROR(memContextName(this), AssertError, "cannot get name for inactive context");
memContextCallbackArgument = this;
@ -80,7 +78,7 @@ testRun(void)
// -------------------------------------------------------------------------------------------------------------------------
// Make sure top context was created
TEST_RESULT_Z(memContextName(memContextTop()), "TOP", "top context should exist");
TEST_RESULT_Z(memContextTop()->name, "TOP", "top context should exist");
TEST_RESULT_INT(memContextTop()->contextChildListSize, 0, "top context should init with zero children");
TEST_RESULT_PTR(memContextTop()->contextChildList, NULL, "top context child list empty");
@ -92,7 +90,7 @@ testRun(void)
MemContext *memContext = memContextNewP("test1");
memContextKeep();
TEST_RESULT_Z(memContextName(memContext), "test1", "test1 context name");
TEST_RESULT_Z(memContext->name, "test1", "test1 context name");
TEST_RESULT_PTR(memContext->contextParent, memContextTop(), "test1 context parent is top");
TEST_RESULT_INT(memContextTop()->contextChildListSize, MEM_CONTEXT_INITIAL_SIZE, "initial top context child list size");
@ -110,7 +108,7 @@ testRun(void)
memContextKeep();
TEST_RESULT_BOOL(
memContextTop()->contextChildList[contextIdx]->state == memContextStateActive, true, "new context is active");
TEST_RESULT_Z(memContextName(memContextTop()->contextChildList[contextIdx]), "test-filler", "new context name");
TEST_RESULT_Z(memContextTop()->contextChildList[contextIdx]->name, "test-filler", "new context name");
}
// This forces the child context array to grow
@ -134,7 +132,7 @@ testRun(void)
TEST_RESULT_BOOL(
memContextTop()->contextChildList[1]->state == memContextStateActive,
true, "new context in same index as freed context is active");
TEST_RESULT_Z(memContextName(memContextTop()->contextChildList[1]), "test-reuse", "new context name");
TEST_RESULT_Z(memContextTop()->contextChildList[1]->name, "test-reuse", "new context name");
TEST_RESULT_UINT(memContextTop()->contextChildFreeIdx, 2, "check context free idx");
// Next context will be at the end
@ -287,23 +285,23 @@ testRun(void)
// Check normal block
MEM_CONTEXT_BEGIN(memContext)
{
TEST_RESULT_Z(memContextName(memContextCurrent()), "test-block", "context is now test-block");
TEST_RESULT_Z(memContextCurrent()->name, "test-block", "context is now test-block");
}
MEM_CONTEXT_END();
TEST_RESULT_Z(memContextName(memContextCurrent()), "TOP", "context is now top");
TEST_RESULT_Z(memContextCurrent()->name, "TOP", "context is now top");
// Check block that errors
TEST_ERROR(
MEM_CONTEXT_BEGIN(memContext)
{
TEST_RESULT_Z(memContextName(memContextCurrent()), "test-block", "context is now test-block");
TEST_RESULT_Z(memContextCurrent()->name, "test-block", "context is now test-block");
THROW(AssertError, "error in test block");
}
MEM_CONTEXT_END(),
AssertError, "error in test block");
TEST_RESULT_Z(memContextName(memContextCurrent()), "TOP", "context is now top");
TEST_RESULT_Z(memContextCurrent()->name, "TOP", "context is now top");
// Reset temp mem context after a single interaction
// -------------------------------------------------------------------------------------------------------------------------
@ -333,11 +331,11 @@ testRun(void)
{
memContext = MEM_CONTEXT_NEW();
TEST_RESULT_PTR(memContext, memContextCurrent(), "new mem context is current");
TEST_RESULT_Z(memContextName(memContext), memContextTestName, "check context name");
TEST_RESULT_Z(memContext->name, memContextTestName, "check context name");
}
MEM_CONTEXT_NEW_END();
TEST_RESULT_Z(memContextName(memContextCurrent()), "TOP", "context name is now 'TOP'");
TEST_RESULT_Z(memContextCurrent()->name, "TOP", "context name is now 'TOP'");
TEST_RESULT_PTR(memContextCurrent(), memContextTop(), "context is now 'TOP'");
TEST_RESULT_BOOL(memContext->state == memContextStateActive, true, "new mem context is still active");
memContextFree(memContext);
@ -352,7 +350,7 @@ testRun(void)
MEM_CONTEXT_NEW_BEGIN(memContextTestName)
{
memContext = MEM_CONTEXT_NEW();
TEST_RESULT_Z(memContextName(memContext), memContextTestName, "check mem context name");
TEST_RESULT_Z(memContext->name, memContextTestName, "check mem context name");
THROW(AssertError, "create failed");
}
MEM_CONTEXT_NEW_END();