ThreadX(3):TraceX创建、事件分类、解析
分别从如下几方面介绍TraceX:
- 如何创建TraceX数据。
- TraceX数据格式。
- TraceX检测哪些事件。
- TraceX工具TraceX和Tracealyzer使用方法。
1 TraceX生成trace buffer
1.1 TraceX初始化
参考《Chapter 5 - Generating trace buffers》。
TraceX初始化函数:
- tx_trace_enable:初始化并使能TraceX。
- tx_trace_event_filter:过滤特定类型事件。
- tx_trace_event_unfilter:取消对特定类型事件过滤。
- tx_trace_disable:关闭TraceX。
UINT tx_trace_enable (VOID *trace_buffer_start,
ULONG trace_buffer_size, ULONG registry_entries);--以trace_buffer_start为起始地址,总大小为trace_buffer_size,object个数为registry_entries。剩余都给event buffer。
UINT tx_trace_event_filter (ULONG vent_filter_bits);
UINT tx_trace_event_unfilter (ULONG event_unfilter_bits);
UINT tx_trace_disable (VOID);
关于Filter参考《TraceX Event Filter》。
tx_trace_buffer_full_notify注册TraceX buffer溢出回调函数:
VOID tx_trace_buffer_full_notify (VOID (*full_buffer_callback)(VOID *));
_tx_trace_enable初始化TraceX数据:
- 初始化Object Entry数据。
- 初始化Event Buffer数据。
- 初始化TraceX Header。
- 依次将THREAD、TIMER、EVENT_FLAGS、QUEUE、SEMAPHORE、MUTEX、BLOCK_POOL、BYTE_POOL等Object写入TraceX数据。
- 写入两个RUNNING事件。
1.2 TraceX数据格式
参考《Chapter 11 - Format of event trace buffer》。TraceX的数据按照如下组装:
Header定义Trace的Object和Event Buffer空间:
typedef struct TX_TRACE_HEADER_STRUCT { ULONG tx_trace_header_id;--TraceX标识,固定为0x54585442。 ULONG tx_trace_header_timer_valid_mask;--时间戳中有效位的掩码。这个掩码允许系统使用不同位宽的时间源,例如32位、24位、16位或8位。 ULONG tx_trace_header_trace_base_address;--定义了TraceX的基地址。通过从TraceX指针中减去这个基地址,可以得到追踪缓冲区中的偏移量。 ULONG tx_trace_header_registry_start_pointer;--指向TraceX Object起始位置的指针。 USHORT tx_trace_header_reserved1;--保留两个字节的空间,为0x0000。 USHORT tx_trace_header_object_name_size;--TraceX Object名称的字节大小。 ULONG tx_trace_header_registry_end_pointer;--TraceX Object结束位置的指针。 ULONG tx_trace_header_buffer_start_pointer;--TraceX Buffer起始位置的指针。 ULONG tx_trace_header_buffer_end_pointer;--TraceX Buffer结束位置的指针。 ULONG tx_trace_header_buffer_current_pointer;--指向TraceX Buffer中当前指针。这个指针会在新事件写入时更新,如果缓冲区已满,它将回绕到缓冲区的顶部。 ULONG tx_trace_header_reserved2;--为0xAAAAAAAA。 ULONG tx_trace_header_reserved3;--为0xBBBBBBBB。 ULONG tx_trace_header_reserved4;--为0xCCCCCCCC。 } TX_TRACE_HEADER;
Object表示触发事件的主体:
typedef struct TX_TRACE_OBJECT_ENTRY_STRUCT { UCHAR tx_trace_object_entry_available;--1表示空间;0表示被使用。 UCHAR tx_trace_object_entry_type;--Object的类型。 UCHAR tx_trace_object_entry_reserved1; /* Should be zero - except for thread */ UCHAR tx_trace_object_entry_reserved2; /* Should be zero - except for thread */ ULONG tx_trace_object_entry_thread_pointer;--存储指向ThreadX对象的指针。 ULONG tx_trace_object_entry_param_1;--存储与Object类型相关的第一个参数值。 ULONG tx_trace_object_entry_param_2;--存储与Object类型相关的第二个参数值。 UCHAR tx_trace_object_entry_name[TX_TRACE_OBJECT_REGISTRY_NAME];--存储Object的名称。 } TX_TRACE_OBJECT_ENTRY;
TraceX支持的Object类型如下,同时注释了P1和P2参数定义:
#define TX_TRACE_OBJECT_TYPE_NOT_VALID ((UCHAR) 0) /* Object is not valid */ #define TX_TRACE_OBJECT_TYPE_THREAD ((UCHAR) 1) /* P1 = stack start address, P2 = stack size */ #define TX_TRACE_OBJECT_TYPE_TIMER ((UCHAR) 2) /* P1 = initial ticks, P2 = reschedule ticks */ #define TX_TRACE_OBJECT_TYPE_QUEUE ((UCHAR) 3) /* P1 = queue size, P2 = message size */ #define TX_TRACE_OBJECT_TYPE_SEMAPHORE ((UCHAR) 4) /* P1 = initial instances */ #define TX_TRACE_OBJECT_TYPE_MUTEX ((UCHAR) 5) /* P1 = priority inheritance flag */ #define TX_TRACE_OBJECT_TYPE_EVENT_FLAGS ((UCHAR) 6) /* none */ #define TX_TRACE_OBJECT_TYPE_BLOCK_POOL ((UCHAR) 7) /* P1 = total blocks, P2 = block size */ #define TX_TRACE_OBJECT_TYPE_BYTE_POOL ((UCHAR) 8) /* P1 = total bytes */
Event的Buffer结构体定义如下:
- 可以根据thread_pointer和Object区域匹配,获取详细信息。
- 对于SMP系统,event_id的高8位用于扩展Core ID。
typedef struct TX_TRACE_BUFFER_ENTRY_STRUCT { ULONG tx_trace_buffer_entry_thread_pointer;--存储指向触发事件的线程的指针。0xFFFFFFFF表示在ISR中触发;0xF0F0F0F0表示在初始化时触发。到Object区域匹配tx_trace_object_entry_thread_pointer,如匹配到则找到详细信息。 ULONG tx_trace_buffer_entry_thread_priority;--存储触发事件的线程的优先级。低16位是优先级;高8位固定为0x80000000,中间12位为tx_thread_preempt_threshold。 ULONG tx_trace_buffer_entry_event_id;--存储事件的标识符。对于SMP系统,高8位标识Core ID。 ULONG tx_trace_buffer_entry_time_stamp;--存储事件发生时的时间戳。 #ifdef TX_MISRA_ENABLE ULONG tx_trace_buffer_entry_info_1; ULONG tx_trace_buffer_entry_info_2; ULONG tx_trace_buffer_entry_info_3; ULONG tx_trace_buffer_entry_info_4; #else ULONG tx_trace_buffer_entry_information_field_1;--存储与事件相关的额外信息。 ULONG tx_trace_buffer_entry_information_field_2;--存储与事件相关的额外信息。 ULONG tx_trace_buffer_entry_information_field_3;--存储与事件相关的额外信息。 ULONG tx_trace_buffer_entry_information_field_4;--存储与事件相关的额外信息。 #endif } TX_TRACE_BUFFER_ENTRY;
其中Event ID按照如下划分:
- ThreadX events: 1-199
- FileX events: 200-299
- NetX events: 300-599
- USBX events: 600-999
ThreadX常用事件和额外信息定义如下:
#define TX_TRACE_THREAD_RESUME 1 /* I1 = thread ptr, I2 = previous_state, I3 = stack ptr, I4 = next thread */ #define TX_TRACE_THREAD_SUSPEND 2 /* I1 = thread ptr, I2 = new_state, I3 = stack ptr I4 = next thread */ #define TX_TRACE_ISR_ENTER 3 /* I1 = stack_ptr, I2 = ISR number, I3 = system state, I4 = preempt disable */ #define TX_TRACE_ISR_EXIT 4 /* I1 = stack_ptr, I2 = ISR number, I3 = system state, I4 = preempt disable */ #define TX_TRACE_TIME_SLICE 5 /* I1 = next thread ptr, I2 = system state, I3 = preempt disable, I4 = stack*/ #define TX_TRACE_RUNNING 6 /* None */
2 TraceX监测事件
2.1 ThreadX
2.1.1 插入ThreadX Object
除了在tx_trace_enable中初始化Object Entry,还可以通过TX_TRACE_OBJECT_REGISTER插入Object,通过TX_TRACE_OBJECT_UNREGISTER移除。
2.1.2 插入ThreadX Event
ThreadX在特定地点调用TX_TRACE_IN_LINE_INSERT,写入特定事件到TraceX,记录系统行为。
输入6个参数:
- i:事件ID。
- a,b,c,d:事件额外信息。
- e:当前事件分组,用于过滤。
#define TX_TRACE_IN_LINE_INSERT(i,a,b,c,d,e) \
{ \
TX_TRACE_BUFFER_ENTRY *trace_event_ptr; \
ULONG trace_system_state; \
ULONG trace_priority; \
TX_THREAD *trace_thread_ptr; \
ULONG core; \
trace_event_ptr = _tx_trace_buffer_current_ptr; \
if ((trace_event_ptr) && (_tx_trace_event_enable_bits & ((ULONG) (e)))) \--对事件分组e进行过滤。
{ \
TX_TRACE_PORT_EXTENSION \
trace_system_state = (ULONG) _tx_thread_smp_current_state_get(); \--0xF0F0F0F0,初始化完成;0xF0F0F0F1,初始化差不多完成;0x00000000,初始化完成。
TX_THREAD_GET_CURRENT(trace_thread_ptr) \
\
if (trace_system_state == ((ULONG) 0)) \
{ \
trace_priority = trace_thread_ptr -> tx_thread_priority; \
trace_priority = trace_priority | 0x80000000UL | (trace_thread_ptr -> tx_thread_preempt_threshold << 16); \
} \
else if (trace_system_state < 0xF0F0F0F0UL) \
{ \
trace_priority = (ULONG) trace_thread_ptr; \--对应线程指针保存在trace_priority中。
trace_thread_ptr = (TX_THREAD *) 0xFFFFFFFFUL; \--在ISR中触发。
} \
else \
{ \
trace_thread_ptr = (TX_THREAD *) 0xF0F0F0F0UL; \--在初始化时触发。
trace_priority = (ULONG) 0; \
} \
trace_event_ptr -> tx_trace_buffer_entry_thread_pointer = (ULONG) trace_thread_ptr; \
trace_event_ptr -> tx_trace_buffer_entry_thread_priority = (ULONG) trace_priority; \
core = _tx_thread_smp_core_get(); \
trace_event_ptr -> tx_trace_buffer_entry_event_id = (ULONG) (core << 24) | (i); \--记录事件ID。SMP架构,记录产生事件的Core ID。
trace_event_ptr -> tx_trace_buffer_entry_time_stamp = (ULONG) TX_TRACE_TIME_SOURCE; \--记录时间戳。
TX_TRACE_INFO_FIELD_ASSIGNMENT((a),(b),(c),(d)) \--插入4个额外信息。
trace_event_ptr++; \
if (trace_event_ptr >= _tx_trace_buffer_end_ptr) \--事件溢出,回绕插入。
{ \
trace_event_ptr = _tx_trace_buffer_start_ptr; \
_tx_trace_buffer_current_ptr = trace_event_ptr; \
_tx_trace_header_ptr -> tx_trace_header_buffer_current_pointer = (ULONG) trace_event_ptr; \
if (_tx_trace_full_notify_function) \
(_tx_trace_full_notify_function)((VOID *) _tx_trace_header_ptr); \
} \
else \
{ \
_tx_trace_buffer_current_ptr = trace_event_ptr; \
_tx_trace_header_ptr -> tx_trace_header_buffer_current_pointer = (ULONG) trace_event_ptr; \
} \
} \
}
#endif
2.1.3 Event Group和Event ID
参考《Ch. 6 - ThreadX trace events》。
Event Groupt | Event ID | Event Detail |
TX_TRACE_INTERNAL_EVENTS |
TX_TRACE_THREAD_RESUME |
/* I1 = thread ptr, I2 = previous_state, I3 = stack ptr, I4 = next thread */ |
TX_TRACE_BLOCK_POOL_EVENTS |
TX_TRACE_BLOCK_ALLOCATE |
/* I1 = pool ptr, I2 = memory ptr, I3 = wait option, I4 = remaining blocks */ |
TX_TRACE_BYTE_POOL_EVENTS |
TX_TRACE_BYTE_ALLOCATE |
/* I1 = pool ptr, I2 = memory ptr, I3 = size requested, I4 = wait option */ |
TX_TRACE_EVENT_FLAGS_EVENTS |
TX_TRACE_EVENT_FLAGS_CREATE |
/* I1 = group ptr, I2 = stack ptr */
/* I1 = group ptr, I2 = stack ptr */ |
TX_TRACE_INTERRUPT_CONTROL_EVENT | TX_TRACE_INTERRUPT_CONTROL | /* I1 = new interrupt posture, I2 = stack ptr */ |
TX_TRACE_MUTEX_EVENTS |
TX_TRACE_MUTEX_CREATE |
/* I1 = mutex ptr, I2 = inheritance, I3 = stack ptr */ |
TX_TRACE_QUEUE_EVENTS |
TX_TRACE_QUEUE_CREATE |
/* I1 = queue ptr, I2 = message size, I3 = queue start, I4 = queue size */ |
TX_TRACE_SEMAPHORE_EVENTS |
TX_TRACE_SEMAPHORE_CEILING_PUT |
/* I1 = semaphore ptr, I2 = current count, I3 = suspended count,I4 =ceiling */ |
TX_TRACE_THREAD_EVENTS |
TX_TRACE_THREAD_CREATE |
/* I1 = thread ptr, I2 = priority, I3 = stack ptr, I4 = stack_size */ |
TX_TRACE_TIME_EVENTS |
TX_TRACE_TIME_GET |
/* I1 = current time, I2 = stack ptr */ |
TX_TRACE_TIMER_EVENTS |
TX_TRACE_TIMER_ACTIVATE |
/* I1 = timer ptr */ |
TX_TRACE_USER_EVENTS |
2.2 FileX
参考《Ch. 7 - FileX trace events》。
FX_TRACE_OBJECT_REGISTER注册FileX的Object,FX_TRACE_OBJECT_UNREGISTER去注册Object。
FX_TRACE_IN_LINE_INSERT插入FileX的Event到TraceX,FX_TRACE_EVENT_UPDATE匹配EventID和Timestamp后跟新Event额外信息。
Event Group | Event ID | Event Detail |
FX_TRACE_INTERNAL_EVENTS |
FX_TRACE_INTERNAL_LOG_SECTOR_CACHE_MISS
FX_TRACE_SYSTEM_DATE_GET |
/* I1 = media ptr, I2 = sector, I3 = total misses, I4 = cache size */
/* I1 = year, I2 = month, I3 = day */ |
FX_TRACE_MEDIA_EVENTS |
FX_TRACE_MEDIA_ABORT |
/* I1 = media ptr */ |
FX_TRACE_DIRECTORY_EVENTS |
FX_TRACE_DIRECTORY_ATTRIBUTES_READ
FX_TRACE_UNICODE_DIRECTORY_CREATE |
/* I1 = media ptr, I2 = directory name, I3 = attributes */
/* I1 = media ptr, I2 = source unicode, I3 = source length, I4 = short_name */ |
FX_TRACE_FILE_EVENTS |
FX_TRACE_FILE_ALLOCATE
FX_TRACE_UNICODE_FILE_CREATE |
/* I1 = file ptr, I2 = size I3 = previous size, I4 = new size */
/* I1 = media ptr, I2 = source unicode, I3 = source length, I4 = short name */ |
2.3 NetX Duo
参考《Ch. 8 - NetX Duo trace events》。
NX_TRACE_OBJECT_REGISTER注册NetX Duo的Object,NX_TRACE_OBJECT_UNREGISTER去注册Object。
NX_TRACE_IN_LINE_INSERT插入NetX Duo的Event到TraceX,NX_TRACE_EVENT_UPDATE匹配EventID和Timestamp后跟新Event额外信息。
Event Group | Event ID | Event Detail |
NX_TRACE_INTERNAL_EVENTS |
NX_TRACE_INTERNAL_ARP_REQUEST_RECEIVE NX_TRACE_INTERNAL_IP_RECEIVE
NX_TRACE_SYSTEM_INITIALIZE |
/* I1 = ip ptr, I2 = source IP address, I3 = packet ptr */ /* I1 = ip ptr, I2 = source IP address, I3 = packet ptr, I4 = packet length */
/* none */ |
NX_TRACE_ARP_EVENTS |
NX_TRACE_ARP_DYNAMIC_ENTRIES_INVALIDATE |
/* I1 = ip ptr, I2 = entries invalidated */ |
NX_TRACE_ICMP_EVENTS |
NX_TRACE_ICMP_ENABLE |
/* I1 = ip ptr */ |
NX_TRACE_IGMP_EVENTS | NX_TRACE_IGMP_ENABLE NX_TRACE_IGMP_INFO_GET NX_TRACE_IGMP_LOOPBACK_DISABLE NX_TRACE_IGMP_LOOPBACK_ENABLE NX_TRACE_IGMP_MULTICAST_JOIN NX_TRACE_IGMP_MULTICAST_LEAVE |
/* I1 = ip ptr */ /* I1 = ip ptr, I2 = reports sent, I3 = queries received, I4 = groups joined*/ /* I1 = ip ptr */ /* I1 = ip ptr */ /* I1 = ip ptr, I2 = group address */ /* I1 = ip ptr, I2 = group_address */ |
NX_TRACE_IP_EVENTS |
NX_TRACE_IP_ADDRESS_CHANGE_NOTIFY
NX_TRACE_IP_INTERFACE_ATTACH NX_TRACE_IP_STATIC_ROUTE_ENABLE
NX_TRACE_IP_INTERFACE_INFO_GET |
/* I1 = ip ptr, I2 = ip address change notify, I3 = additional info */
/* I1 = ip ptr, I2 = ip address, I3 = interface index */ /* I1 = ip_ptr, */
/* I1 = ip_ptr, I2 = ip_address, I3 = mtu_size, I4 = interface_index */ |
NX_TRACE_PACKET_EVENTS |
NX_TRACE_PACKET_ALLOCATE
NX_TRACE_PACKET_DATA_EXTRACT_OFFSET |
/* I1 = pool ptr, I2 = packet ptr, I3 = packet type, I4 = available packets */
/* I1 = packet_ptr, I2 = buffer_length, I3 = bytes_copied, */ |
NX_TRACE_RARP_EVENTS |
NX_TRACE_RARP_DISABLE |
/* I1 = ip ptr */ |
NX_TRACE_TCP_EVENTS |
NX_TRACE_TCP_CLIENT_SOCKET_BIND
NX_TRACE_TCP_SOCKET_PEER_INFO_GET |
/* I1 = ip ptr, I2 = socket ptr, I3 = port, I4 = wait option */
/* I1 = socket ptr, I2 = network_address, I3 = port */ |
NX_TRACE_UDP_EVENTS |
NX_TRACE_UDP_ENABLE
NX_TRACE_UDP_SOCKET_BYTES_AVAILABLE NX_TRACE_UDP_SOCKET_INTERFACE_SET
|
/* I1 = ip ptr */
/* I1 = ip ptr, I2 = socket ptr, I3 = bytes available */ /* I1 = socket_ptr, I2 = interface_index, */ |
2.4 USBX
参考《Ch. 9 - USBX trace events》。
UX_TRACE_OBJECT_REGISTER注册USBX Duo的Object,UX_TRACE_OBJECT_UNREGISTER去注册Object。
UX_TRACE_IN_LINE_INSERT插入NetX Duo的Event到TraceX,UX_TRACE_EVENT_UPDATE匹配EventID和Timestamp后跟新Event额外信息。
Event Group | Event ID | Event Detail |
UX_TRACE_ERRORS | UX_TRACE_ERROR | |
UX_TRACE_HOST_STACK_EVENTS |
UX_TRACE_HOST_STACK_EVENTS_BASE |
/* I1 = class , I2 = class instance */ |
UX_TRACE_DEVICE_STACK_EVENTS |
UX_TRACE_DEVICE_STACK_EVENTS_BASE |
/* I1 = interface value */ |
UX_TRACE_HOST_CONTROLLER_EVENTS | ||
UX_TRACE_DEVICE_CONTROLLER_EVENTS | ||
UX_TRACE_HOST_CLASS_EVENTS |
UX_TRACE_HOST_CLASS_EVENTS_BASE UX_TRACE_HOST_CLASS_AUDIO_ACTIVATE UX_TRACE_HOST_CLASS_CDC_ACM_ACTIVATE UX_TRACE_HOST_CLASS_CDC_ECM_ACTIVATE UX_TRACE_HOST_CLASS_HID_ACTIVATE UX_TRACE_HOST_CLASS_HUB_ACTIVATE UX_TRACE_HOST_CLASS_PIMA_ACTIVATE UX_TRACE_HOST_CLASS_PRINTER_ACTIVATE UX_TRACE_HOST_CLASS_PROLIFIC_ACTIVATE UX_TRACE_HOST_CLASS_STORAGE_ACTIVATE UX_TRACE_HOST_CLASS_DPUMP_ACTIVATE UX_TRACE_HOST_CLASS_SWAR_ACTIVATE UX_TRACE_HOST_CLASS_GSER_ACTIVATE |
/* I1 = class instance */ /* I1 = class instance */ /* I1 = class instance */ /* I1 = class instance */ /* I1 = class instance */ /* I1 = class instance */ /* I1 = class instance */ /* I1 = class instance */ /* I1 = class instance */ /* I1 = class instance */ /* I1 = class instance */ /* I1 = class instance */ /* I1 = class instance */ |
UX_TRACE_DEVICE_CLASS_EVENTS |
UX_TRACE_DEVICE_CLASS_EVENTS_BASE UX_TRACE_DEVICE_CLASS_CDC_ACM_ACTIVATE UX_TRACE_DEVICE_CLASS_HID_ACTIVATE UX_TRACE_DEVICE_CLASS_PIMA_ACTIVATE UX_TRACE_DEVICE_CLASS_RNDIS_ACTIVATE UX_TRACE_DEVICE_CLASS_STORAGE_ACTIVATE UX_TRACE_DEVICE_CLASS_CDC_ECM_ACTIVATE UX_TRACE_DEVICE_CLASS_DFU_ACTIVATE UX_TRACE_DEVICE_CLASS_PRINTER_ACTIVATE UX_TRACE_DEVICE_CLASS_CCID_ACTIVATE |
/* I1 = class instance */ /* I1 = class instance */ /* I1 = class instance */ /* I1 = class instance */ /* I1 = class instance */ /* I1 = class instance */ /* I1 = class instance */ /* I1 = class instance */ /* I1 = class instance */ /* I1 = class instance */ |
2.5 自定义事件
参考《Ch. 10 - Customer user events》。
tx_trace_user_event_insert插入用户自定义事件:
UINT tx_trace_user_event_insert (ULONG event_id,
ULONG info_field_1, ULONG info_field_2,
ULONG info_field_3, ULONG info_field_4);
可以参考《Defining Custom User-Defined Event Icons》,定义用户事件的显示。
3 TraceX抓取和解析
3.1 TraceX
详细解读参考:
-
Chapter 3 - Description of TraceX:TraceX工具的使用方法和功能讲解。
-
Chapter 4 - TraceX performance analysis:使用TraceX进行性能分析。
3.2 Tracealyzer
Getting Started with Tracealyzer - Percepio
4 使用Visual Studio 2019环境生成TraceX
4.1 使能TraceX
#ifdef TX_ENABLE_EVENT_TRACE UCHAR event_buffer[65536]; #endif /* Define what the initial system looks like. */ void tx_application_define(void *first_unused_memory) { CHAR *pointer = TX_NULL; #ifdef TX_ENABLE_EVENT_TRACE tx_trace_enable(event_buffer, sizeof(event_buffer), 32); #endif
... }
4.2 Visual Studio 2019转储、WinDbg导出TraceX、TraceX分析
在Visual Studio 2019中,点击“本地Windows调试器”,进入调试模式:
暂停调试,然后“调试”->"将转储另存为...",保存到本地文件。
在Microsoft Store中安装WinDbg,打开转储文件。
在Disassembly中找到event_buffer地址,在Command输入".writemem D:\event_buffer.trx 0x010a9100 0x010b90ff",导出event_buffer到event_buffer.trx文件中。
使用TraceX打开,event_buffer.trx文件: