HALFRED  0.4.0
Macros | Typedefs | Functions
DIAG module

Detailed Description

Introduction.

The diagnostics module in HAL (called DIAG) provides a standard way for user modules and applications to report their condition. The DIAG module supports:

The DIAG module is also used internally by other HAL modules, to report their errors and log status.

Module architecture.

The DIAG module is build around a concept, that a single software project is built using separated software components. Such components are reusable and redistributable. One of such components is HAL itself. Another key thing is that each of the components can have different level of detail of debugging information it provides. The DIAG module captures these ideas in a concept of diagnostic channels and diagnostic level.

Diagnostic channels.

All status logging is done through diagnostic channels. Each channel can be seen as a data pipe, which can be activated or deactivated and which has an associated output IODevice. Obviously, if the application uses distinct software components, each of such components may be associated with different debugging channel. This actually allows the developer to enable, disable and adjust diagnostic output form different software components independently.

The number of channels that can be used is controlled by the HAL_DIAG_MAX_CHANNELS definition in HAL configuration file.

Diagnostic level.

Each software component may have different level of detail as far as diagnostic output is concerned. The DIAG module provides status logging tools that allow for controllable level of detail. One of key aspects here, is that using DIAG tools it is possible not only to control whether or not the diagnostic messages are displayed, but also whether the functionality of displaying then is built into the object code or not.

DIAG provides 8 standard levels of detail for diagnostic information called simply DIAG levels. These levels range from 0 to 7, where 0 means the least detail and 7 means the most detail. It's up to the developer to assign diagnostic messages to an appropriate level.

Let's look at the following example: Assume we have a component that handles some sort of serial port communication protocol. The lowest level status reporting messages would include things like "I'm ready to receive new data", or "An error occurred". Such messages may be assigned to level 0. Perhaps level 1 would be something like "Packet received" or "Retransmission needed" etc. The most detailed status logging such as information about a single processed character could be level 7. Let's follow this example and assume that our component provided diagnostic output at levels 0, 1 and 7. In order to control what diagnostic output will be built into the object code the HAL_DIAG_LEVEL definition is used. The HAL_DIAG_LEVEL definition denotes the highest diagnostic level of detail that will be built into the object code. In our example: if HAL_DIAG_LEVEL is set to 0, only level 0 diagnostic messages will be built into the object code. if HAL_DIAG_LEVEL is set to 1, level 0 and 1 diagnostic messages will be built into the object code. The same happens when HAL_DIAG_LEVEL is set from 2 to 6. The level 7 diagnostic output will be compiled in when HAL_DIAG_LEVEL is set to 7.

Level-aware status logging.

The status logging is particularly useful during development stage, but many times it is also left inactive in the final product, making the servicing easier. The DIAG module API provides several functions to make the log output formating and control easier. These functions are: DIAGx_LogChar, DIAGx_LogMsg, DIAGx_LogINT, DIAGx_LogUINT and DIAGx_LogNL where x is the diagnostic level. The log output is directed to the IODevice, associated with the DIAG module, with a prior call to DIAG_SetOutputDevice.This IODevice must be properly configured and operational.

Level-aware assertions.

The diagnostics module provides macros for making assertions in the program flow. Assertions are simply true-false test statements used to express, that some conditions during code execution should be met. If they're not then something is wrong. The DIAG module provides two types of macros for handling assertions: DIAGx_ASSERT() and DIAGx_ASSERT_AND_EXECUTE() where x is the diagnostic level (0..7)

int divide(int x, int y)
{
DIAG0_ASSERT(3, y != 0);
DIAG1_ASSERT(3, x > y);
return x/y;
}

When channel 3 diagnostic level is set to 1 or higher, the following function will report violations when y is equal to zero and when x is less or equal to y. If level 1 diagnostic messages are not enabled, only the first violation is checked. However, even though the violation is reported, the division in the example above is still executed, which can lead to unwanted behavior. To protect execution of a code block, under assertion condition use DIAGx_ASSERT_AND_EXECUTE(). Let's modify the example above:

int divide(int x, int y)
{
DIAG0_ASSERT_AND_EXECUTE(3, y != 0) {
DIAG1_ASSERT(3, x > y) {
return x/y;
}
}
return 0;
}

Now, the division is only executed when y is not equal to 0. If y is 0, an error is reported and the following code block is not executed. The division is never executed also, when x <= y whether the diagnostic level 1 is enabled or not. The only difference is that when it is enabled, an error is reported, when x <= y.

Managing errors.

The error handling capability of the DIAG module can be configured and tuned to cover a wide range of applications, from simple to quite complex ones.The whole error management idea is built around one function: DIAG_ReportError that is called in the error signaling module, and provides the one and only method for reporting an error. A single error report consists of several items:

Optionally, the report can be extended with:

The error report can be processed right away - in the context of the signaling module, or it can be buffered and processed later by a call to DIAG_ProcessErrors. The specification of this behavior is configured at compile-time through definitions. See module configuration section below for more details.

Processing a single error.

No matter whether the error buffering feature is enabled or not, processing an error consists of several steps. First, the DIAG module tries to call the application's error handler, set with a prior call to DIAG_SetErrorHandler. If this handler is present, it is called and it's return value decides, if the error should be logged to the output device (return !0) or not (return 0). If the error is to be logged the information about an error is written to the output IODevice associated with the diagnostic channel. If buffering is enabled, then the processed error is removed from the queue.

Using DIAG module in multi-task software.

When HAL_ENABLE_OS is set to 1 in hal_config, the DIAG module integrates with the OS module providing access control to output IODevice. This guard relies on IODevice lock/unlock mechanisms. The following example illustrates how to use this feature:

if (0 == DIAG_Lock(DIAG_DEFAULT_CHANNEL, 100)) {
DIAG_LogMsg(DIAG_DEFAULT_CHANNEL, "This is a diagnostic message.");
DIAG_LogMsg(DIAG_DEFAULT_CHANNEL, "Hopefully it won't get split in half.");
DIAG_Unlock(DIAG_DEFAULT_CHANNEL);
}

In the example above, the diagnostic message won't be displayed if the lock is not successful. We can use ASSERT statement to reveal such cases:

DIAG0_ASSERT_AND_EXECUTE(DIAG_DEFAULT_CHANNEL, 0 == DIAG_Lock(DIAG_DEFAULT_CHANNEL, 100)) {
DIAG_LogMsg(DIAG_DEFAULT_CHANNEL, "This is a diagnostic message.");
DIAG_LogMsg(DIAG_DEFAULT_CHANNEL, "Hopefully it won't get split in half.");
DIAG_Unlock(DIAG_DEFAULT_CHANNEL);
}

It is advised to use this strategy in multi-tasking (OS-based) software for all DIAG-based output, to prevent undesired output conflicts. Please note, that the error reporting feature already uses this mechanism. In this case additional definition: HAL_DIAG_LOCK_TIMEOUT is required to configure the timeout, when waiting for IODevice lock. The default timeout value is 1 second.

Module configuration.

To enable the DIAG module, HAL_ENABLE_DIAG definition must be set to 1, in HAL configuration file.

To enable error buffering, the HAL_DIAG_USE_ERROR_BUFFERING must be set to 1. The HAL_DIAG_ERROR_BUFFER_SIZE defines how many errors can fit into error buffer. To enable buffering, it must be set to a non-zero value.

To extend error report with a timestamp, set HAL_DIAG_USE_ERROR_TIME_STAMPS to 1.

Macros

#define DIAG_DEFAULT_CHANNEL   0
 
#define HAL_ENABLE_DIAG   0
 
#define HAL_DIAG_LEVEL   0
 
#define HAL_DIAG_LOCK_TIMEOUT   1000
 
#define HAL_DIAG_USE_CALENDAR_TIME   0
 
#define DIAG_ASSERT(channel, condition)
 
#define DIAG_ASSERT_AND_EXECUTE(channel, condition)   DIAG_ASSERT(channel, condition); else
 
#define DIAG_Lock(ignored1, ignored2)   (HALRESULT_OK)
 
#define DIAG_Unlock(ignored)   (void)0
 

Typedefs

typedef uint32_t(* DIAGClockSource )(void)
 

Functions

void DIAG_Init (void)
 
void DIAG_Deinit (void)
 
HALRESULT DIAG_SetOutputDevice (unsigned int channel, IODevice iodevice)
 
IODevice DIAG_GetOutputDevice (unsigned int channel)
 
HALRESULT DIAG_ActivateChannel (unsigned int channel)
 
HALRESULT DIAG_DeactivateChannel (unsigned int channel)
 
bool DIAG_IsChannelActive (unsigned int channel)
 
HALRESULT DIAG_SetClockSource (unsigned int channel, DIAGClockSource clk_src)
 
void DIAG_Log (unsigned int channel, const char *str,...)
 
void DIAG_LogChar (unsigned int channel, char character)
 
void DIAG_LogMsg (unsigned int channel, const char *msg)
 
void DIAG_LogINT (unsigned int channel, int32_t value, uint8_t base)
 
void DIAG_LogUINT (unsigned int channel, uint32_t value, uint8_t base)
 
void DIAG_LogUINT64 (unsigned int channel, uint64_t value, uint8_t base)
 
void DIAG_LogINT64 (unsigned int channel, int64_t value, uint8_t base)
 
void DIAG_LogFLOAT (unsigned int channel, float value, uint8_t precision)
 
void DIAG_LogNL (unsigned int channel)
 
void DIAG_LogHeader (unsigned int channel, const char *module)
 
void DIAG_LogData (unsigned int channel, uint8_t *data, size_t size, int base, int spacing, const char *space)
 
void DIAG_LogIOBuf (unsigned int channel, IOBuf iobuf, int indent)
 
void DIAG_ReportError (unsigned int channel, uint16_t error_no, uint32_t code_line, void *user_data, const char *description)
 
uint32_t DIAG_ProcessErrors (size_t max_error_count)
 
void DIAG_SetErrorHandler (int(*error_handler)(unsigned int, uint16_t, uint16_t, void *, const char *))
 

Macro Definition Documentation

#define DIAG_DEFAULT_CHANNEL   0
#define HAL_ENABLE_DIAG   0

Enables (1) or disables (0) diagnostics component.

#define HAL_DIAG_LEVEL   0

Controls the detail level of the diagnostic messages. Zero means the least detail.

#define HAL_DIAG_LOCK_TIMEOUT   1000

Sets the timeout when trying to lock diagnostic output device

#define HAL_DIAG_USE_CALENDAR_TIME   0

Disables the usage of calendar time (requires OS module)

#define DIAG_ASSERT (   channel,
  condition 
)
Value:
if (!(condition)) do { \
if (HALRESULT_OK == DIAG_Lock((channel), HAL_DIAG_LOCK_TIMEOUT)) { \
DIAG0_LogMsg((channel), "Assertion failed in " __FILE__ ":"); \
DIAG0_LogUINT((channel), __LINE__, 10); \
DIAG0_LogNL(channel); \
DIAG_Unlock((channel)); \
} \
} while (0)
#define HAL_DIAG_LOCK_TIMEOUT
Definition: hal_diag.h:248

Tests the assertion condition.

Parameters
channeldiagnostic channel number
conditionexpression to evaluate. This must be true to pass the assertion test.
#define DIAG_ASSERT_AND_EXECUTE (   channel,
  condition 
)    DIAG_ASSERT(channel, condition); else

Tests the assertion condition and executes the following code block, if the condition is true.

Parameters
channeldiagnostic channel number
conditionexpression to evaluate. This must be true to pass the assertion test.

Function Documentation

void DIAG_Init ( void  )

Initializes the diagnostics module

void DIAG_Deinit ( void  )

Deinitializes the diagnostics module.

HALRESULT DIAG_SetOutputDevice ( unsigned int  channel,
IODevice  iodevice 
)

Sets the output device for all diagnostic messages and error logs. Setting this to NULL disables all output, but not the error processing itself.

Parameters
channeldiagnostic channel number
iodeviceiodevice used for diagnostic output
Returns
HALRESULT_OK if the iodevice was set or HALRESULT_INVALID_DIAG_CHANNEL_NUMBER if the provided channel number was out of bounds (see HAL_DIAG_MAX_CHANNELS definition)
IODevice DIAG_GetOutputDevice ( unsigned int  channel)

Gets the currently set output device.

Parameters
channeldiagnostic channel number
Returns
IODevice currently used for diagnostic output
HALRESULT DIAG_ActivateChannel ( unsigned int  channel)

Activates log output from a given diagnostic channel.

Parameters
channeldiagnostic channel number
Returns
HALRESULT_OK if channel was activated or HALRESULT_INVALID_DIAG_CHANNEL_NUMBER if the provided channel number was out of bounds (see HAL_DIAG_MAX_CHANNELS definition)
HALRESULT DIAG_DeactivateChannel ( unsigned int  channel)

Deactivates log output from a given diagnostic channel.

Parameters
channeldiagnostic channel number
Returns
HALRESULT_OK if channel was deactivated or HALRESULT_INVALID_DIAG_CHANNEL_NUMBER if the provided channel number was out of bounds (see HAL_DIAG_MAX_CHANNELS definition)
bool DIAG_IsChannelActive ( unsigned int  channel)

Checks if the given diagnostic channel is active.

Parameters
channeldiagnostic channel number
Returns
true is channel is active, false if not
HALRESULT DIAG_SetClockSource ( unsigned int  channel,
DIAGClockSource  clk_src 
)

Sets up a clock source for a given channel. The clock information will be used by DIAG_LogHeader function.

Parameters
channeldiagnostic channel number
clk_srca function that will supply the clock information
Returns
HALRESULT_OK if the clock source was successfully assigned to a channel or HALRESULT_INVALID_DIAG_CHANNEL_NUMBER if the provided channel number was out of bounds (see HAL_DIAG_MAX_CHANNELS definition)
void DIAG_Log ( unsigned int  channel,
const char *  str,
  ... 
)

Logs formatted string.

Parameters
channeldiagnostic channel number
strstring that contains text to be logged including embedded format tags
void DIAG_LogChar ( unsigned int  channel,
char  character 
)

Logs a single character.

Parameters
channeldiagnostic channel number
charactercharacter that will be written to the output device
void DIAG_LogMsg ( unsigned int  channel,
const char *  msg 
)

Logs a string message.

Parameters
channeldiagnostic channel number
msgstring message that will be written to the output device
void DIAG_LogINT ( unsigned int  channel,
int32_t  value,
uint8_t  base 
)

Logs a signed integer value.

Parameters
channeldiagnostic channel number
valuesigned value that will be written to the output device
basea base for integer to string conversion
void DIAG_LogUINT ( unsigned int  channel,
uint32_t  value,
uint8_t  base 
)

Logs an unsigned integer value.

Parameters
channeldiagnostic channel number
valueunsigned value that will be written to the output device
basea base for integer to string conversion
void DIAG_LogUINT64 ( unsigned int  channel,
uint64_t  value,
uint8_t  base 
)

Logs an unsigned integer value.

Parameters
channeldiagnostic channel number
valueunsigned value that will be written to the output device
basea base for integer to string conversion
void DIAG_LogINT64 ( unsigned int  channel,
int64_t  value,
uint8_t  base 
)

Logs an unsigned integer value.

Parameters
channeldiagnostic channel number
valueunsigned value that will be written to the output device
basea base for integer to string conversion
void DIAG_LogFLOAT ( unsigned int  channel,
float  value,
uint8_t  precision 
)

Logs a floating point value.

Parameters
channeldiagnostic channel number
valuefloating point value that will be written to the output device
precisionnumber of fractional digits to print
void DIAG_LogNL ( unsigned int  channel)

Logs an end-of-line character.

Parameters
channeldiagnostic channel number
void DIAG_LogHeader ( unsigned int  channel,
const char *  module 
)

Logs a message header.

Parameters
channeldiagnostic channel number
modulemodule name
void DIAG_LogData ( unsigned int  channel,
uint8_t *  data,
size_t  size,
int  base,
int  spacing,
const char *  space 
)

Logs contents of a data buffer.

Parameters
channeldiagnostic channel number
datadata buffer
sizesize of the data buffer
basea base for integer to string conversion
spacingnumber of bytes after which a space string should be inserted
spacespace string or NULL
void DIAG_LogIOBuf ( unsigned int  channel,
IOBuf  iobuf,
int  indent 
)

Prints out some basic information about an IOBuf in human readable form.

Parameters
channeldiagnostic channel number
iobufIOBuf to print
indentindentation of log (number of spaces)
void DIAG_ReportError ( unsigned int  channel,
uint16_t  error_no,
uint32_t  code_line,
void *  user_data,
const char *  description 
)

Reports an error

Parameters
channeldiagnostic channel number
error_noerror number
code_lineline of code from which the notification was called
user_datauser supplied data attached to the error
descriptionpointer to the error description string
uint32_t DIAG_ProcessErrors ( size_t  max_error_count)

A parser function that handles the buffered errors. Used when error buffering for DIAG is enabled. This function should be called periodically by the application to ensure proper error handling.

Returns
number of errors still left to parse
Parameters
max_error_countspecifies the maximum number of errors that can be processed in one run of this function
void DIAG_SetErrorHandler ( int(*)(unsigned int, uint16_t, uint16_t, void *, const char *)  error_handler)

Sets an error handler callback function, that will be called when an error is signaled.