Skip to content

Commit

Permalink
Merge pull request #480 from jphickey/fix-479-utassert-stub-arg-names
Browse files Browse the repository at this point in the history
Fix #479, add name association to stub arguments
  • Loading branch information
astrogeco authored Jun 2, 2020
2 parents 50bcbfb + 2218036 commit a1aaec2
Show file tree
Hide file tree
Showing 2 changed files with 207 additions and 4 deletions.
102 changes: 100 additions & 2 deletions ut_assert/inc/utstubs.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,29 @@ typedef cpuaddr UT_EntryKey_t;
* Maximum size of a callback hook context list
*
* This is the maximum number of function arguments that can be passed to a hook
* Note that OS_TaskCreate() has (possibly) the highest parameter count in OSAL with 7 parameters
*/
#define UT_STUBCONTEXT_MAXSIZE 4
#define UT_STUBCONTEXT_MAXSIZE 8

/**
* Identifies the type of value stored in the ArgPtr field of a UT_StubContext_t object
*/
typedef enum
{
UT_STUBCONTEXT_ARG_TYPE_UNSPECIFIED = 0,
UT_STUBCONTEXT_ARG_TYPE_DIRECT, /**< Indicates "ArgPtr" is a direct copy of the actual parameter value */
UT_STUBCONTEXT_ARG_TYPE_INDIRECT /**< Indicates "ArgPtr" is a pointer to the argument value on the stack */
} UT_StubContext_Arg_Type_t;

/**
* Complete Metadata associated with a context argument
*/
typedef struct
{
UT_StubContext_Arg_Type_t Type;
const char *Name;
size_t Size;
} UT_StubArgMetaData_t;

/**
* Structure to hold context data for callback hooks
Expand All @@ -63,6 +84,7 @@ typedef struct
{
uint32 ArgCount;
const void *ArgPtr[UT_STUBCONTEXT_MAXSIZE];
UT_StubArgMetaData_t Meta[UT_STUBCONTEXT_MAXSIZE];
} UT_StubContext_t;

/**
Expand Down Expand Up @@ -311,10 +333,70 @@ uint32 UT_Stub_CopyFromLocal(UT_EntryKey_t FuncKey, const void *LocalBuffer, uin
* passed as "void *" pointers to the actual stack values. The user code must
* then cast them to the right type again.
*
* This is now implemented as a macro which calls UT_Stub_RegisterContextWithMetaData
* to associate the name of the argument as well as the pointer.
*
* \param FuncKey The stub function to entry to use.
* \param Parameter Arbitrary parameter to pass.
*/
void UT_Stub_RegisterContext(UT_EntryKey_t FuncKey, const void *Parameter);
#define UT_Stub_RegisterContext(FuncKey, Parameter) \
UT_Stub_RegisterContextWithMetaData(FuncKey, #Parameter, UT_STUBCONTEXT_ARG_TYPE_UNSPECIFIED, Parameter, 0)

/**
* Registers a single value argument into the context for the hook callback
*
* A pointer to the stack value is actually stored into the context,
* which can be dereferenced in the hook.
*/
#define UT_Stub_RegisterContextGenericArg(FuncKey, Parameter) \
UT_Stub_RegisterContextWithMetaData(FuncKey, #Parameter, UT_STUBCONTEXT_ARG_TYPE_INDIRECT, &Parameter, sizeof(Parameter))

/**
* Registers a single context element for the hook callback
*
* Stubs may pass up to UT_STUBCONTEXT_MAXSIZE arguments to a user-defined
* hook function. These arguments are opaque to the stub function and generally
* passed as "void *" pointers to the actual stack values. The user code must
* then cast them to the right type again.
*
* \param FuncKey The stub function to entry to use.
* \param Name Argument name to associate with the pointer
* \param ParamType The type of parameter (direct, indirect, or unknown)
* \param ParamPtr Pointer to argument data
* \param ParamSize The size of the object pointed to, or zero if not known
*/
void UT_Stub_RegisterContextWithMetaData(UT_EntryKey_t FuncKey, const char *Name,
UT_StubContext_Arg_Type_t ParamType, const void *ParamPtr, size_t ParamSize);

/**
* Retrieve a context argument value by name
*
* This returns a pointer to a buffer containing the value, rather than the
* value itself, even if the argument was registered originally as a direct value.
*
* If the name is not found, this logs a UT assert failure message, as it
* indicates a mismatch between the hook and stub functions with respect to argument
* names and (possibly) types that needs to be corrected. If possible, a buffer
* containing all zeros may be used as a substitute.
*
* This does not return NULL, such that the returned value can always be dereferenced.
*
* \param ContextPtr The context structure containing arguments
* \param Name Argument name to find
* \param ExpectedSize The size of the expected object type
* \returns Pointer to buffer containing the value.
*/
const void* UT_Hook_GetArgPtr(const UT_StubContext_t *ContextPtr, const char *Name, size_t ExpectedTypeSize);

/**
* Macro which retrieves a value argument by name.
*
* This is a convenience method to easily use UT_Hook_GetArgPtr() to get the value
* associated with an argument as the correct/expected type.
*
*/
#define UT_Hook_GetArgValueByName(ContextPtr,Name,Type) \
(*(Type const *)UT_Hook_GetArgPtr(ContextPtr,Name,sizeof(Type)))

/**
* Default implementation for a stub function that takes a va_list of arguments.
Expand Down Expand Up @@ -390,6 +472,22 @@ int32 UT_DefaultStubImpl(const char *FunctionName, UT_EntryKey_t FuncKey, int32
*/
#define UT_DEFAULT_IMPL_RC_ARGS(FuncName,Rc,...) UT_DefaultStubImpl(#FuncName, UT_KEY(FuncName), Rc, __VA_ARGS__)

/**
* Macro to simplify usage of the UT_DefaultStubImplWithArgs() function
*
* This function accepts a list of arguments as a va_list
*/
#define UT_DEFAULT_IMPL_VARARGS(FuncName,va) UT_DefaultStubImplWithArgs(#FuncName, UT_KEY(FuncName), 0, va)

/**
* Macro to simplify usage of the UT_DefaultStubImplWithArgs() function
*
* This function accepts a list of arguments as a va_list and
* a nonzero default return code
*/
#define UT_DEFAULT_IMPL_RC_VARARGS(FuncName,Rc,va) UT_DefaultStubImplWithArgs(#FuncName, UT_KEY(FuncName), Rc, va)


/**
* Macro to simplify usage of the UT_DefaultStubImpl() function
*
Expand Down
109 changes: 107 additions & 2 deletions ut_assert/src/utstubs.c
Original file line number Diff line number Diff line change
Expand Up @@ -591,9 +591,73 @@ void UT_SetVaHookFunction(UT_EntryKey_t FuncKey, UT_VaHookFunc_t HookFunc, void
UT_DoSetHookFunction(FuncKey, Value, UserObj, true);
}

void UT_Stub_RegisterContext(UT_EntryKey_t FuncKey, const void *Parameter)
const void* UT_Hook_GetArgPtr(const UT_StubContext_t *ContextPtr, const char *Name, size_t ExpectedTypeSize)
{
uint32 i;
const void* Result;
const UT_StubArgMetaData_t *MetaPtr;

static const union
{
uintmax_t AsInt;
void *AsPtr;
double AsFloat;
} ARG_DEFAULT_ZERO_VALUE = { 0 };

Result = NULL;
for (i = 0; i < ContextPtr->ArgCount; ++i)
{
MetaPtr = &ContextPtr->Meta[i];
if (MetaPtr->Name != NULL)
{
if (strcmp(MetaPtr->Name, Name) == 0 &&
(MetaPtr->Size == 0 || MetaPtr->Size == ExpectedTypeSize))
{
if (MetaPtr->Type == UT_STUBCONTEXT_ARG_TYPE_DIRECT)
{
Result = &ContextPtr->ArgPtr[i];
}
else if (MetaPtr->Type == UT_STUBCONTEXT_ARG_TYPE_INDIRECT)
{
Result = ContextPtr->ArgPtr[i];
}
break;
}
}
}

/*
* If no suitable result pointer was found, this means a mismatch
* between the stub and test case, such as a change in argument/parameter names.
* This is an error that should be corrected, so report it as a failure.
*/
if (Result == NULL)
{
UtAssert_Failed("Requested parameter %s of size %lu which was not provided by the stub",
Name, (unsigned long)ExpectedTypeSize);

if (ExpectedTypeSize <= sizeof(ARG_DEFAULT_ZERO_VALUE))
{
Result = &ARG_DEFAULT_ZERO_VALUE;
}
else
{
/*
* As the caller will likely dereference the returned pointer, should
* never return NULL. Just abort here.
*/
UtAssert_Abort("No value for parameter");
}
}

return Result;
}

void UT_Stub_RegisterContextWithMetaData(UT_EntryKey_t FuncKey, const char *Name,
UT_StubContext_Arg_Type_t ParamType, const void *ParamPtr, size_t ParamSize)
{
UT_StubTableEntry_t *StubPtr;
UT_StubArgMetaData_t *MetaPtr;

/*
* First find an existing context entry for the function.
Expand All @@ -616,7 +680,48 @@ void UT_Stub_RegisterContext(UT_EntryKey_t FuncKey, const void *Parameter)
StubPtr->EntryType = UT_ENTRYTYPE_CALLBACK_CONTEXT;
if (StubPtr->Data.Context.ArgCount < UT_STUBCONTEXT_MAXSIZE)
{
StubPtr->Data.Context.ArgPtr[StubPtr->Data.Context.ArgCount] = Parameter;
StubPtr->Data.Context.ArgPtr[StubPtr->Data.Context.ArgCount] = ParamPtr;

MetaPtr = &StubPtr->Data.Context.Meta[StubPtr->Data.Context.ArgCount];
MetaPtr->Size = ParamSize;
MetaPtr->Type = ParamType;

/*
* If name was specified, then trim any leading address operator (&)
* and/or whitespace, keeping only the actual name part.
*/
if (Name != NULL)
{
/*
* If the _address_ of the stack variable was actually passed in,
* the mark this as indirect (i.e. hook must dereference ArgPtr
* to get actual parameter value). Otherwise assume it as direct.
*/
MetaPtr->Name = Name;
while (*MetaPtr->Name != 0)
{
if (*MetaPtr->Name == '&')
{
/* this means its a pointer to the value, not the value itself */
if (MetaPtr->Type == UT_STUBCONTEXT_ARG_TYPE_UNSPECIFIED)
{
MetaPtr->Type = UT_STUBCONTEXT_ARG_TYPE_INDIRECT;
}
}
else if (*MetaPtr->Name != ' ')
{
/* stop at non-whitespace */
break;
}
++MetaPtr->Name;
}

if (MetaPtr->Type == UT_STUBCONTEXT_ARG_TYPE_UNSPECIFIED)
{
MetaPtr->Type = UT_STUBCONTEXT_ARG_TYPE_DIRECT;
}

}
++StubPtr->Data.Context.ArgCount;
}
}
Expand Down

0 comments on commit a1aaec2

Please sign in to comment.