HALFRED
0.4.0
|
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)
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:
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:
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:
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 *)) |
#define DIAG_DEFAULT_CHANNEL 0 |
Default diagnostic channel used internally by HAL.
Referenced by IOSERIAL_DisableCTS(), IOSERIAL_DisableRTS(), IOSERIAL_EnableCTS(), IOSERIAL_EnableRTS(), IOSERIAL_GetBaudrate(), IOSERIAL_GetCTS(), IOSERIAL_GetFrameFormat(), IOSERIAL_GetHandshaking(), IOSERIAL_GetRTS(), IOSERIAL_SetBaudrate(), IOSERIAL_SetCTS(), IOSERIAL_SetFrameFormat(), IOSERIAL_SetHandshaking(), IOSERIAL_SetRTS(), LLST_AddItem(), LLST_Deinit(), LLST_GetFirstItem(), LLST_GetItemByData(), LLST_GetItemByIndex(), LLST_GetItemData(), LLST_GetLastItem(), LLST_GetNextItem(), LLST_Init(), LLST_SetItemData(), LLST_UnlinkFirstItem(), and LLST_UnlinkItem().
#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 | |||
) |
Tests the assertion condition.
channel | diagnostic channel number |
condition | expression 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.
channel | diagnostic channel number |
condition | expression to evaluate. This must be true to pass the assertion test. |
void DIAG_Init | ( | void | ) |
Initializes the diagnostics module
void DIAG_Deinit | ( | void | ) |
Deinitializes the diagnostics module.
Sets the output device for all diagnostic messages and error logs. Setting this to NULL disables all output, but not the error processing itself.
channel | diagnostic channel number |
iodevice | iodevice used for diagnostic output |
IODevice DIAG_GetOutputDevice | ( | unsigned int | channel | ) |
Gets the currently set output device.
channel | diagnostic channel number |
HALRESULT DIAG_ActivateChannel | ( | unsigned int | channel | ) |
Activates log output from a given diagnostic channel.
channel | diagnostic channel number |
HALRESULT DIAG_DeactivateChannel | ( | unsigned int | channel | ) |
Deactivates log output from a given diagnostic channel.
channel | diagnostic channel number |
bool DIAG_IsChannelActive | ( | unsigned int | channel | ) |
Checks if the given diagnostic channel is active.
channel | diagnostic channel number |
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.
channel | diagnostic channel number |
clk_src | a function that will supply the clock information |
void DIAG_Log | ( | unsigned int | channel, |
const char * | str, | ||
... | |||
) |
Logs formatted string.
channel | diagnostic channel number |
str | string that contains text to be logged including embedded format tags |
void DIAG_LogChar | ( | unsigned int | channel, |
char | character | ||
) |
Logs a single character.
channel | diagnostic channel number |
character | character that will be written to the output device |
void DIAG_LogMsg | ( | unsigned int | channel, |
const char * | msg | ||
) |
Logs a string message.
channel | diagnostic channel number |
msg | string 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.
channel | diagnostic channel number |
value | signed value that will be written to the output device |
base | a base for integer to string conversion |
void DIAG_LogUINT | ( | unsigned int | channel, |
uint32_t | value, | ||
uint8_t | base | ||
) |
Logs an unsigned integer value.
channel | diagnostic channel number |
value | unsigned value that will be written to the output device |
base | a base for integer to string conversion |
void DIAG_LogUINT64 | ( | unsigned int | channel, |
uint64_t | value, | ||
uint8_t | base | ||
) |
Logs an unsigned integer value.
channel | diagnostic channel number |
value | unsigned value that will be written to the output device |
base | a base for integer to string conversion |
void DIAG_LogINT64 | ( | unsigned int | channel, |
int64_t | value, | ||
uint8_t | base | ||
) |
Logs an unsigned integer value.
channel | diagnostic channel number |
value | unsigned value that will be written to the output device |
base | a base for integer to string conversion |
void DIAG_LogFLOAT | ( | unsigned int | channel, |
float | value, | ||
uint8_t | precision | ||
) |
Logs a floating point value.
channel | diagnostic channel number |
value | floating point value that will be written to the output device |
precision | number of fractional digits to print |
void DIAG_LogNL | ( | unsigned int | channel | ) |
Logs an end-of-line character.
channel | diagnostic channel number |
void DIAG_LogHeader | ( | unsigned int | channel, |
const char * | module | ||
) |
Logs a message header.
channel | diagnostic channel number |
module | module 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.
channel | diagnostic channel number |
data | data buffer |
size | size of the data buffer |
base | a base for integer to string conversion |
spacing | number of bytes after which a space string should be inserted |
space | space 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.
channel | diagnostic channel number |
iobuf | IOBuf to print |
indent | indentation 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
channel | diagnostic channel number |
error_no | error number |
code_line | line of code from which the notification was called |
user_data | user supplied data attached to the error |
description | pointer 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.
max_error_count | specifies 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.