Trace system description

The trace mechanism allows tracing PXROS-HR kernel activities during the execution of an application. If this module is activated, the kernel saves all service calls and user-defined records into the predefined trace buffer.

Defining a trace buffer

To use the trace mechanism of PXROS-HR, a user needs to define a memory area for the circular trace buffer. The memory area must be aligned to MPU restrictions (8B).

The memory block for the trace buffer must be writable and readable by the PXROS-HR kernel. In the example below, the buffer is placed in the system memory. The buffer size must be a multiple of the size of a trace entry — currently 24 bytes.

Code 1. Buffer size calculations
/* Size of one element in trace circular buffer */
#define TRACE_ENTRY_SIZE          24

/* Calculate size of trace circular buffer */
#define TRACE_ARRAY_SIZE(x) ((x) * TRACE_ENTRY_SIZE)

/* Number of trace entries */
#define TRACE_BUFFER_ENTRIES      500

Buffer area

There are two possible ways of how to allocate a buffer in system memory:

  1. Static declaration during compile time

    Code 2. Defining a trace buffer
    /* Trace buffer must be accessible by the PXROS-HR Kernel */
    #pragma section ".CPU0.systemmemory" 8 awB
    PxAligned_t traceBuffer_CPU0[(TRACE_ARRAY_SIZE(TRACE_BUFFER_ENTRIES) + sizeof(PxAligned_t) - 1) / sizeof(PxAligned_t)] PXMEM_ALIGNED;
    #pragma section

    It is necessary to set the ReadProtection or WRProtection permission to the buffer for the task that will assign it later as a trace buffer.

  2. Dynamic request during runtime

    In this case, the task that will later assign the trace buffer must request the memory block from the system default memory class by calling the PxMcTakeBlk() kernel service. This task must never be terminated; otherwise, the kernel would release all task resources, including the memory block for the trace buffer. It is necessary to enlarge the system memory by the trace buffer’s size not to affect the space for task control blocks (TCBs). The SYSMEMSIZE_COREX (X = 0-5) is modified accordingly in the example below.

    Code 3. System memory size modification in "system_cfg.c"
    /* System memory (PXMcSystemdefault) shall be large enough to allow
     * allocation of all core's task control block (TCB) and to provide
     * all additional memory.
     */
    #pragma section ".CPU0.systemmemory" 8 awB
    #define SYSMEMSIZE_CORE0 (PXMEM_ADJUST((NUM_OF_TASKS_CORE0) * (PXTASK_SIZE + PXMCVARSIZED_ADDON) + TRACE_ARRAY_SIZE(TRACE_BUFFER_ENTRIES)))
    PxMemAligned_t Sysmem_Core0[SYSMEMSIZE_CORE0 / sizeof(PxMemAligned_t)];
    #pragma section
    Code 4. Definition and allocation in task function
    PxMptr_t buffer;
    
    /* Taking a block from system default class */
    buffer = PxMcTakeBlk(PXMcSystemdefault, TRACE_ARRAY_SIZE(TRACE_BUFFER_ENTRIES));
    if(!buffer)
    {
        PxPanic();
    }
    
    /* Clear the buffer */
    PxBzero((PxUChar_t *)buffer, TRACE_ARRAY_SIZE(TRACE_BUFFER_ENTRIES));

Assigning the trace buffer to PXROS-HR is done through the PxTraceAssignBuffer() function:

Code 5. Assigning the trace buffer
/* Assign trace buffer */
PxError_t error = PxTraceAssignBuffer(traceBuffer_CPU0,
                                      TRACE_BUFFER_ENTRIES,
                                      TRACE_ENTRY_SIZE);
if (error != PXERR_NOERROR)
    PxPanic();

The TRACE_ENTRY_SIZE must be given in bytes, and it must be 24 bytes for version 8.2.0 (or newer). An assigning task must have ReadProtection or WRProtection permission to the trace buffer.

Trace function

Besides assigning the trace buffer, the user must define a trace function PXROS-HR uses as a trace callback (hook). The trace function must have the following prototype:

Code 6. PXROS-HR trace function prototype
typedef void (*PxTraceFunc_t)(PxInt_t, PxArg_t, PxArg_t, PxError_t);

For example, a trace function can be defined as follows:

Code 7. User-defined trace function
void traceFunc(PxInt_t serviceType, PxArg_t Arg1, PxArg_t Arg2, PxError_t error)
{
    /* Implementation */
}

The trace function is assigned to the system through one of these PXROS-HR API functions:

  • PxTraceCtrl

    /* Set trace function */
    if (PxTraceCtrl(PXTraceSetTraceFunction, (PxArg_t) traceFunc) == -1)
        PxPanic();

    which returns the starting address of the old trace function or -1 in case of an error

  • PxSetTraceFunc

    /* Set trace function */
    if (PxSetTraceFunc((PxTraceFunc_t) traceFunc) != PXERR_NOERROR )
        PxPanic();

    which returns error code PXERR_NOERROR or PXERR_ACCESS_RIGHT

Trace control

PXROS-HR uses the PxTraceCtrl() function to control the trace mechanism. This function takes command and command-related arguments. It can set the trace function, start/stop tracing, get the tracing state (enabled, disabled), configure the tracing mask, and enable/disable tracing for a given task. All PxTraceCtrl() commands are further explained in commands.

Task willing to call PxTraceCtrl() must have at least PXACCESS_TRACECTRL access rights. When calling PxTraceCtrl() with the command PXTraceSetTraceFunction, the task must also have PXACCESS_SYSTEM_CONTROL.

Trace service codes

Each PXROS-HR kernel service is mapped to a number, the so-called service code. These codes are used for logging the kernel activity during runtime. The kernel reserves service codes 0-299. The user can define additional tracing service codes from 300 to 65535.

Kernel interface for trace function

The trace function is always executed on the kernel level, not the task level. As a result, it cannot call standard functions from the PXROS-HR API (these can be called only from tasks). PxTrace interface offers several kernel interface functions instead:

  • IPxTraceCheckService() — checks if a trace entry should be made or not based on the group mask

  • IPxTraceCheckTask() — checks if a trace entry should be made or not based on the task issuing the PxTrace() call

  • IPxTraceGetGroupMask() — returns the current group mask without triggering an additional syscall

  • IPxTraceGetHandlerTaskID() — returns the ID of the handler task

  • IPxTraceGetNextEntry() — get the next free entry in the trace buffer

  • IPxTraceGetRunTaskID() — returns the ID of the runtask