445 lines
19 KiB
C
445 lines
19 KiB
C
/**************************************************************************/
|
|
/* */
|
|
/* Copyright (c) Microsoft Corporation. All rights reserved. */
|
|
/* */
|
|
/* This software is licensed under the Microsoft Software License */
|
|
/* Terms for Microsoft Azure RTOS. Full text of the license can be */
|
|
/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */
|
|
/* and in the root directory of this software. */
|
|
/* */
|
|
/**************************************************************************/
|
|
|
|
|
|
/**************************************************************************/
|
|
/**************************************************************************/
|
|
/** */
|
|
/** ThreadX Component */
|
|
/** */
|
|
/** Trace */
|
|
/** */
|
|
/**************************************************************************/
|
|
/**************************************************************************/
|
|
|
|
#define TX_SOURCE_CODE
|
|
|
|
|
|
/* Include necessary system files. */
|
|
|
|
#include "tx_api.h"
|
|
#ifdef TX_ENABLE_EVENT_TRACE
|
|
#include "tx_thread.h"
|
|
#include "tx_timer.h"
|
|
#include "tx_event_flags.h"
|
|
#include "tx_queue.h"
|
|
#include "tx_semaphore.h"
|
|
#include "tx_mutex.h"
|
|
#include "tx_block_pool.h"
|
|
#include "tx_byte_pool.h"
|
|
#endif
|
|
#include "tx_trace.h"
|
|
|
|
|
|
/**************************************************************************/
|
|
/* */
|
|
/* FUNCTION RELEASE */
|
|
/* */
|
|
/* _tx_trace_enable PORTABLE C */
|
|
/* 6.1 */
|
|
/* AUTHOR */
|
|
/* */
|
|
/* William E. Lamie, Microsoft Corporation */
|
|
/* */
|
|
/* DESCRIPTION */
|
|
/* */
|
|
/* This function initializes the ThreadX trace buffer and the */
|
|
/* associated control variables, enabling it for operation. */
|
|
/* */
|
|
/* INPUT */
|
|
/* */
|
|
/* trace_buffer_start Start of trace buffer */
|
|
/* trace_buffer_size Size (bytes) of trace buffer */
|
|
/* registry_entries Number of object registry */
|
|
/* entries. */
|
|
/* */
|
|
/* OUTPUT */
|
|
/* */
|
|
/* Completion Status */
|
|
/* */
|
|
/* CALLS */
|
|
/* */
|
|
/* _tx_thread_system_preempt_check Check for preemption */
|
|
/* _tx_trace_object_register Register existing objects */
|
|
/* */
|
|
/* CALLED BY */
|
|
/* */
|
|
/* Application Code */
|
|
/* */
|
|
/* RELEASE HISTORY */
|
|
/* */
|
|
/* DATE NAME DESCRIPTION */
|
|
/* */
|
|
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
|
/* 09-30-2020 Yuxin Zhou Modified comment(s), */
|
|
/* resulting in version 6.1 */
|
|
/* */
|
|
/**************************************************************************/
|
|
UINT _tx_trace_enable(VOID *trace_buffer_start, ULONG trace_buffer_size, ULONG registry_entries)
|
|
{
|
|
|
|
#ifdef TX_ENABLE_EVENT_TRACE
|
|
|
|
TX_INTERRUPT_SAVE_AREA
|
|
|
|
TX_THREAD *thread_ptr;
|
|
TX_TIMER *timer_ptr;
|
|
TX_EVENT_FLAGS_GROUP *event_flags_ptr;
|
|
TX_QUEUE *queue_ptr;
|
|
TX_SEMAPHORE *semaphore_ptr;
|
|
TX_MUTEX *mutex_ptr;
|
|
TX_BLOCK_POOL *block_pool_ptr;
|
|
TX_BYTE_POOL *byte_pool_ptr;
|
|
UCHAR *work_ptr;
|
|
UCHAR *event_start_ptr;
|
|
TX_TRACE_OBJECT_ENTRY *entry_ptr;
|
|
TX_TRACE_BUFFER_ENTRY *event_ptr;
|
|
ULONG i;
|
|
UINT status;
|
|
|
|
|
|
/* First, see if there is enough room for the control header, the registry entries, and at least one event in
|
|
memory supplied to this call. */
|
|
if (trace_buffer_size < ((sizeof(TX_TRACE_HEADER)) + ((sizeof(TX_TRACE_OBJECT_ENTRY)) * registry_entries) + (sizeof(TX_TRACE_BUFFER_ENTRY))))
|
|
{
|
|
|
|
/* No, the memory isn't big enough to hold one trace buffer entry. Return an error. */
|
|
status = TX_SIZE_ERROR;
|
|
}
|
|
|
|
/* Determine if trace is already enabled. */
|
|
else if (_tx_trace_buffer_current_ptr != TX_NULL)
|
|
{
|
|
|
|
/* Yes, trace is already enabled. */
|
|
status = TX_NOT_DONE;
|
|
}
|
|
else
|
|
{
|
|
|
|
/* Set the enable bits for all events enabled. */
|
|
_tx_trace_event_enable_bits = 0xFFFFFFFFUL;
|
|
|
|
/* Setup working pointer to the supplied memory. */
|
|
work_ptr = TX_VOID_TO_UCHAR_POINTER_CONVERT(trace_buffer_start);
|
|
|
|
/* Setup pointer to the trace control area. */
|
|
_tx_trace_header_ptr = TX_UCHAR_TO_HEADER_POINTER_CONVERT(work_ptr);
|
|
|
|
/* Move the working pointer past the control area. */
|
|
work_ptr = TX_UCHAR_POINTER_ADD(work_ptr, (sizeof(TX_TRACE_HEADER)));
|
|
|
|
/* Save the start of the trace object registry. */
|
|
_tx_trace_registry_start_ptr = TX_UCHAR_TO_OBJECT_POINTER_CONVERT(work_ptr);
|
|
|
|
/* Setup the end of the trace object registry. */
|
|
work_ptr = TX_UCHAR_POINTER_ADD(work_ptr, (sizeof(TX_TRACE_OBJECT_ENTRY))*registry_entries);
|
|
_tx_trace_registry_end_ptr = TX_UCHAR_TO_OBJECT_POINTER_CONVERT(work_ptr);
|
|
|
|
/* Loop to make all trace object registry entries empty and valid. */
|
|
for (i = ((ULONG) 0); i < registry_entries; i++)
|
|
{
|
|
|
|
/* Setup the work pointer. */
|
|
work_ptr = TX_OBJECT_TO_UCHAR_POINTER_CONVERT(_tx_trace_registry_start_ptr);
|
|
work_ptr = TX_UCHAR_POINTER_ADD(work_ptr, (sizeof(TX_TRACE_OBJECT_ENTRY))*i);
|
|
|
|
/* Convert to a registry entry pointer. */
|
|
entry_ptr = TX_UCHAR_TO_OBJECT_POINTER_CONVERT(work_ptr);
|
|
|
|
/* Initialize object registry entry. */
|
|
entry_ptr -> tx_trace_object_entry_available = (UCHAR) TX_TRUE;
|
|
entry_ptr -> tx_trace_object_entry_type = (UCHAR) TX_TRACE_OBJECT_TYPE_NOT_VALID;
|
|
entry_ptr -> tx_trace_object_entry_reserved1 = (UCHAR) 0;
|
|
entry_ptr -> tx_trace_object_entry_reserved2 = (UCHAR) 0;
|
|
entry_ptr -> tx_trace_object_entry_thread_pointer = (ULONG) 0;
|
|
}
|
|
|
|
/* Setup the total number of registry entries. */
|
|
_tx_trace_total_registry_entries = registry_entries;
|
|
|
|
/* Setup the object registry available count to the total number of registry entries. */
|
|
_tx_trace_available_registry_entries = registry_entries;
|
|
|
|
/* Setup the search starting index to the first entry. */
|
|
_tx_trace_registry_search_start = ((ULONG) 0);
|
|
|
|
/* Setup the work pointer to after the trace object registry. */
|
|
work_ptr = TX_OBJECT_TO_UCHAR_POINTER_CONVERT(_tx_trace_registry_end_ptr);
|
|
|
|
/* Adjust the remaining trace buffer size. */
|
|
trace_buffer_size = trace_buffer_size - ((sizeof(TX_TRACE_OBJECT_ENTRY)) * registry_entries) - (sizeof(TX_TRACE_HEADER));
|
|
|
|
/* Setup pointer to the start of the actual event trace log. */
|
|
_tx_trace_buffer_start_ptr = TX_UCHAR_TO_ENTRY_POINTER_CONVERT(work_ptr);
|
|
|
|
/* Save the event trace log start address. */
|
|
event_start_ptr = work_ptr;
|
|
|
|
/* Calculate the end of the trace buffer. */
|
|
work_ptr = TX_UCHAR_POINTER_ADD(work_ptr, ((trace_buffer_size/(sizeof(TX_TRACE_BUFFER_ENTRY)))*(sizeof(TX_TRACE_BUFFER_ENTRY))));
|
|
_tx_trace_buffer_end_ptr = TX_UCHAR_TO_ENTRY_POINTER_CONVERT(work_ptr);
|
|
|
|
/* Loop to mark all entries in the trace buffer as invalid. */
|
|
for (i = ((ULONG) 0); i < (trace_buffer_size/(sizeof(TX_TRACE_BUFFER_ENTRY))); i++)
|
|
{
|
|
|
|
/* Setup the work pointer. */
|
|
work_ptr = TX_UCHAR_POINTER_ADD(event_start_ptr, (sizeof(TX_TRACE_BUFFER_ENTRY))*i);
|
|
|
|
/* Convert to a trace event pointer. */
|
|
event_ptr = TX_UCHAR_TO_ENTRY_POINTER_CONVERT(work_ptr);
|
|
|
|
/* Mark this trace event as invalid. */
|
|
event_ptr -> tx_trace_buffer_entry_thread_pointer = ((ULONG) 0);
|
|
}
|
|
|
|
/* Now, fill in the event trace control header. */
|
|
_tx_trace_header_ptr -> tx_trace_header_id = TX_TRACE_VALID;
|
|
_tx_trace_header_ptr -> tx_trace_header_timer_valid_mask = TX_TRACE_TIME_MASK;
|
|
_tx_trace_header_ptr -> tx_trace_header_trace_base_address = TX_POINTER_TO_ULONG_CONVERT(trace_buffer_start);
|
|
_tx_trace_header_ptr -> tx_trace_header_registry_start_pointer = TX_POINTER_TO_ULONG_CONVERT(_tx_trace_registry_start_ptr);
|
|
_tx_trace_header_ptr -> tx_trace_header_reserved1 = ((USHORT) 0);
|
|
_tx_trace_header_ptr -> tx_trace_header_object_name_size = ((USHORT) TX_TRACE_OBJECT_REGISTRY_NAME);
|
|
_tx_trace_header_ptr -> tx_trace_header_registry_end_pointer = TX_POINTER_TO_ULONG_CONVERT(_tx_trace_registry_end_ptr);
|
|
_tx_trace_header_ptr -> tx_trace_header_buffer_start_pointer = TX_POINTER_TO_ULONG_CONVERT(_tx_trace_buffer_start_ptr);
|
|
_tx_trace_header_ptr -> tx_trace_header_buffer_end_pointer = TX_POINTER_TO_ULONG_CONVERT(_tx_trace_buffer_end_ptr);
|
|
_tx_trace_header_ptr -> tx_trace_header_buffer_current_pointer = TX_POINTER_TO_ULONG_CONVERT(_tx_trace_buffer_start_ptr);
|
|
_tx_trace_header_ptr -> tx_trace_header_reserved2 = 0xAAAAAAAAUL;
|
|
_tx_trace_header_ptr -> tx_trace_header_reserved3 = 0xBBBBBBBBUL;
|
|
_tx_trace_header_ptr -> tx_trace_header_reserved4 = 0xCCCCCCCCUL;
|
|
|
|
/* Now, loop through all existing ThreadX objects and register them in the newly setup trace buffer. */
|
|
|
|
/* Disable interrupts. */
|
|
TX_DISABLE
|
|
|
|
/* First, disable preemption. */
|
|
_tx_thread_preempt_disable++;
|
|
|
|
/* Restore interrupts. */
|
|
TX_RESTORE
|
|
|
|
/* Pickup the first thread and the number of created threads. */
|
|
thread_ptr = _tx_thread_created_ptr;
|
|
i = _tx_thread_created_count;
|
|
|
|
/* Loop to register all threads. */
|
|
while (i != ((ULONG) 0))
|
|
{
|
|
|
|
/* Decrement the counter. */
|
|
i--;
|
|
|
|
/* Register this thread. */
|
|
_tx_trace_object_register(TX_TRACE_OBJECT_TYPE_THREAD, thread_ptr, thread_ptr -> tx_thread_name,
|
|
TX_POINTER_TO_ULONG_CONVERT(thread_ptr -> tx_thread_stack_start), (ULONG) thread_ptr -> tx_thread_stack_size);
|
|
|
|
/* Move to the next thread. */
|
|
thread_ptr = thread_ptr -> tx_thread_created_next;
|
|
}
|
|
|
|
/* Pickup the first timer and the number of created timers. */
|
|
timer_ptr = _tx_timer_created_ptr;
|
|
i = _tx_timer_created_count;
|
|
|
|
/* Loop to register all timers. */
|
|
while (i != ((ULONG) 0))
|
|
{
|
|
|
|
/* Decrement the counter. */
|
|
i--;
|
|
|
|
/* Register this timer. */
|
|
_tx_trace_object_register(TX_TRACE_OBJECT_TYPE_TIMER, timer_ptr, timer_ptr -> tx_timer_name,
|
|
((ULONG) 0), timer_ptr -> tx_timer_internal.tx_timer_internal_re_initialize_ticks);
|
|
|
|
/* Move to the next timer. */
|
|
timer_ptr = timer_ptr -> tx_timer_created_next;
|
|
}
|
|
|
|
|
|
/* Pickup the first event flag group and the number of created groups. */
|
|
event_flags_ptr = _tx_event_flags_created_ptr;
|
|
i = _tx_event_flags_created_count;
|
|
|
|
/* Loop to register all event flags groups. */
|
|
while (i != ((ULONG) 0))
|
|
{
|
|
|
|
/* Decrement the counter. */
|
|
i--;
|
|
|
|
/* Register this event flags group. */
|
|
_tx_trace_object_register(TX_TRACE_OBJECT_TYPE_EVENT_FLAGS, event_flags_ptr, event_flags_ptr -> tx_event_flags_group_name, ((ULONG) 0), ((ULONG) 0));
|
|
|
|
/* Move to the next event flags group. */
|
|
event_flags_ptr = event_flags_ptr -> tx_event_flags_group_created_next;
|
|
}
|
|
|
|
/* Pickup the first queue and the number of created queues. */
|
|
queue_ptr = _tx_queue_created_ptr;
|
|
i = _tx_queue_created_count;
|
|
|
|
/* Loop to register all queues. */
|
|
while (i != ((ULONG) 0))
|
|
{
|
|
|
|
/* Decrement the counter. */
|
|
i--;
|
|
|
|
/* Register this queue. */
|
|
_tx_trace_object_register(TX_TRACE_OBJECT_TYPE_QUEUE, queue_ptr, queue_ptr -> tx_queue_name,
|
|
(queue_ptr -> tx_queue_capacity * (sizeof(ULONG))), ((ULONG) 0));
|
|
|
|
/* Move to the next queue. */
|
|
queue_ptr = queue_ptr -> tx_queue_created_next;
|
|
}
|
|
|
|
/* Pickup the first semaphore and the number of created semaphores. */
|
|
semaphore_ptr = _tx_semaphore_created_ptr;
|
|
i = _tx_semaphore_created_count;
|
|
|
|
/* Loop to register all semaphores. */
|
|
while (i != ((ULONG) 0))
|
|
{
|
|
|
|
/* Decrement the counter. */
|
|
i--;
|
|
|
|
/* Register this semaphore. */
|
|
_tx_trace_object_register(TX_TRACE_OBJECT_TYPE_SEMAPHORE, semaphore_ptr, semaphore_ptr -> tx_semaphore_name, ((ULONG) 0), ((ULONG) 0));
|
|
|
|
/* Move to the next semaphore. */
|
|
semaphore_ptr = semaphore_ptr -> tx_semaphore_created_next;
|
|
}
|
|
|
|
/* Pickup the first mutex and the number of created mutexes. */
|
|
mutex_ptr = _tx_mutex_created_ptr;
|
|
i = _tx_mutex_created_count;
|
|
|
|
/* Loop to register all mutexes. */
|
|
while (i != ((ULONG) 0))
|
|
{
|
|
|
|
/* Decrement the counter. */
|
|
i--;
|
|
|
|
/* Register this mutex. */
|
|
_tx_trace_object_register(TX_TRACE_OBJECT_TYPE_MUTEX, mutex_ptr, mutex_ptr -> tx_mutex_name,
|
|
(ULONG) mutex_ptr -> tx_mutex_inherit, ((ULONG) 0));
|
|
|
|
/* Move to the next mutex. */
|
|
mutex_ptr = mutex_ptr -> tx_mutex_created_next;
|
|
}
|
|
|
|
/* Pickup the first block pool and the number of created block pools. */
|
|
block_pool_ptr = _tx_block_pool_created_ptr;
|
|
i = _tx_block_pool_created_count;
|
|
|
|
/* Loop to register all block pools. */
|
|
while (i != ((ULONG) 0))
|
|
{
|
|
|
|
/* Decrement the counter. */
|
|
i--;
|
|
|
|
/* Register this block pool. */
|
|
_tx_trace_object_register(TX_TRACE_OBJECT_TYPE_BLOCK_POOL, block_pool_ptr, block_pool_ptr -> tx_block_pool_name,
|
|
block_pool_ptr -> tx_block_pool_size, ((ULONG) 0));
|
|
|
|
/* Move to the next block pool. */
|
|
block_pool_ptr = block_pool_ptr -> tx_block_pool_created_next;
|
|
}
|
|
|
|
/* Pickup the first byte pool and the number of created byte pools. */
|
|
byte_pool_ptr = _tx_byte_pool_created_ptr;
|
|
i = _tx_byte_pool_created_count;
|
|
|
|
/* Loop to register all byte pools. */
|
|
while (i != ((ULONG) 0))
|
|
{
|
|
|
|
/* Decrement the counter. */
|
|
i--;
|
|
|
|
/* Register this byte pool. */
|
|
_tx_trace_object_register(TX_TRACE_OBJECT_TYPE_BYTE_POOL, byte_pool_ptr, byte_pool_ptr -> tx_byte_pool_name,
|
|
byte_pool_ptr -> tx_byte_pool_size, ((ULONG) 0));
|
|
|
|
/* Move to the next byte pool. */
|
|
byte_pool_ptr = byte_pool_ptr -> tx_byte_pool_created_next;
|
|
}
|
|
|
|
/* Disable interrupts. */
|
|
TX_DISABLE
|
|
|
|
/* Release the preeemption. */
|
|
_tx_thread_preempt_disable--;
|
|
|
|
/* Finally, setup the current buffer pointer, which effectively enables the trace! */
|
|
_tx_trace_buffer_current_ptr = (TX_TRACE_BUFFER_ENTRY *) _tx_trace_buffer_start_ptr;
|
|
|
|
/* Insert two RUNNING events so the buffer is not empty. */
|
|
TX_TRACE_IN_LINE_INSERT(TX_TRACE_RUNNING, 0, 0, 0, 0, TX_TRACE_INTERNAL_EVENTS)
|
|
TX_TRACE_IN_LINE_INSERT(TX_TRACE_RUNNING, 0, 0, 0, 0, TX_TRACE_INTERNAL_EVENTS)
|
|
|
|
/* Restore interrupts. */
|
|
TX_RESTORE
|
|
|
|
/* Check for preemption. */
|
|
_tx_thread_system_preempt_check();
|
|
|
|
/* Return successful completion. */
|
|
status = TX_SUCCESS;
|
|
}
|
|
|
|
/* Return completion status. */
|
|
return(status);
|
|
#else
|
|
|
|
UINT status;
|
|
|
|
|
|
/* Access input arguments just for the sake of lint, MISRA, etc. */
|
|
if (trace_buffer_start != TX_NULL)
|
|
{
|
|
|
|
/* Trace not enabled, return an error. */
|
|
status = TX_FEATURE_NOT_ENABLED;
|
|
}
|
|
else if (trace_buffer_size == ((ULONG) 0))
|
|
{
|
|
|
|
/* Trace not enabled, return an error. */
|
|
status = TX_FEATURE_NOT_ENABLED;
|
|
}
|
|
else if (registry_entries == ((ULONG) 0))
|
|
{
|
|
|
|
/* Trace not enabled, return an error. */
|
|
status = TX_FEATURE_NOT_ENABLED;
|
|
}
|
|
else
|
|
{
|
|
|
|
/* Trace not enabled, return an error. */
|
|
status = TX_FEATURE_NOT_ENABLED;
|
|
}
|
|
|
|
/* Return completion status. */
|
|
return(status);
|
|
#endif
|
|
}
|
|
|
|
|
|
|