Skip to content

Commit

Permalink
Fix #479, add name association to stub arguments
Browse files Browse the repository at this point in the history
Add the capability to store parameter names in addtion
to pointers as part of the stub argument context data.

Macroize the UT_Stub_RegisterContext function utstubs.h to
also pass the parameter name in addition to the pointer.

Also add hook-side accessor functions/macros to make it
easier and more reliable to get this information from
the context data.
  • Loading branch information
jphickey committed May 21, 2020
1 parent c2bcebb commit 2218036
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 2218036

Please sign in to comment.