Zephyr Tracing 追踪调试指南¶
1 Zephyr Tracing 输出模式¶
Zephyr支持不同的tracing格式输出,相对应的也是不同的分析工具。
CONFIG_TRACING_NONE: 无任何Tracing数据输出
CONFIG_PERCEPIO_TRACERECORDER: 支持 Percepio Tracealyzer 格式数据输出
CONFIG_SEGGER_SYSTEMVIEW: 支持 Segger SystemView
CONFIG_TRACING_CTF: 支持 Common Trace Format 格式输出,工具有:线性文本(babeltrace)和图形化(TraceCompass)
CONFIG_TRACING_TEST: 用于test,一般输出格式化字符串
在zephyr的tracing kconfig也有相应描述:
config TRACING_NONE
bool "No tracing format selected"
help
None of the available tracing formats is selected.
config PERCEPIO_TRACERECORDER
bool "Percepio Tracealyzer support"
select THREAD_NAME
select INIT_STACKS
select THREAD_MONITOR
depends on ZEPHYR_TRACERECORDER_MODULE
config SEGGER_SYSTEMVIEW
bool "Segger SystemView support"
select CONSOLE
select RTT_CONSOLE
select USE_SEGGER_RTT
select THREAD_MONITOR
select SEGGER_RTT_CUSTOM_LOCKING
config TRACING_CTF
bool "Tracing via Common Trace Format support"
select TRACING_CORE
help
Enable tracing to a Common Trace Format stream.
config TRACING_TEST
bool "Tracing for test usage"
select TRACING_CORE
help
Enable tracing for testing kinds of format purpose. It must
implement the tracing hooks defined in tracing_test.h
config TRACING_USER
bool "Tracing using user-defined functions"
help
Use user-defined functions for tracing task switching and irqs
下图为Zephyr Tracing框架概览:
1.1 SystemView¶
SystemView是Segger的一个trace工具,虽然SystemView支持Uart和IP作为通信方式,但目前Zephyr只支持Jlink RTT向上位机传送tracing数据流。
SystemView的代码路径:
modules/debug/segger:Segger sysview源代码
zephyr/modules/segger: 对Segger sysview进行初始化
zephyr/subsys/tracing/sysview: 对Segger sysview进行配置,将Segger sysview的API封装成zephyr使用的tracing API
sysview实现了Zephyr所有的tracing API
1.2 Tracealyzer¶
tracerecorder按照自己的格式将数据送给上位机的Tracealyzer,目前支持Jlink的RTT和ARM ITM传送数据流。 tracerecorder的代码路径:
modules/debug/TraceRecoder:TraceRecoder的源代码,其中kernelports/Zephyr/streamports是Zephyr支持的,目前只有RTT和ARM ITM
zephyr/modules/traceRecoder: 构建文件
zephyr对traceRecoder的封装都在外部模块中
tracerecorder实现了Zephyr所有的tracing API
其中关于zephyr中使用Tracealyzer的官方使用说明:
1.3 CTF (Common Trace Format)¶
CTF是Zephyr根据CTF标准格式实现的,支持同步和异步trace,trace的后端有4种:
ram: 保存在ram中
uart: 通过uart传输
usb: 通过USB传输
posix: 保存到文件系统
CTF的代码路径: zephyr/subsys/tracing/ Zephyr trace核心代码和backend实现,只对接CTF zephyr/subsys/tracing/CTF CTF event实现和trace API封装 CTF只实现了上下文切换,isr出入,idle和部分thread的trace API
其中babeltrace需要在linux环境下运行;TraceCompass是eclipse出品的免费工具,如下图:
TraceCompass的支持如下:
Multiple trace formats supported¶
Common Trace Format(CTF), including but not limited to:
Linux LTTng kernel traces
Linux LTTng-UST userspace traces
Linux Perf traces converted to CTF
Bare metal traces
Integration with the LTTng-Analyses scripts
Hardware traces (e.g. IEEE Nexus 5001 CTF conversion). See also this link.
GDB traces for debugging
The Best Trace Format (BTF) for OSEK
The libpcap (Packet CAPture) format, for network traces
Custom text or XML parsers that can be added right from the graphical interface by the user
Can be extended to support various log or trace files.
Provided by the TraceCompass Incubator:
Linux FTrace raw textual format
Google’s trace event json format
Additional Linux Perf2ctf traces features
For more information, see the Trace Compass datasheet.
1.4 test¶
test用于,钩子函数中只有打印。 user的代码路径: zephyr/subsys/tracing/test
void sys_trace_k_thread_switched_out(void)
{
struct k_thread *thread;
thread = k_current_get();
TRACING_STRING("%s: %p\n", __func__, thread);
}
2 Tracing不同目标¶
2.1 Tracing目标类型¶
下面17项是对tracing对象的选择,默认为选中,当配置为n时将不会在该对象中埋入hook API,所以当我们关注某一类的特定目标对象时可以关闭一些不需要tracing的对象,以免数据过多导致Segger Sysview Overflow:
SYSCALL
THREAD
WORK
ISR
SEMAPHORE
MUTEX
CONDVAR
QUEUE
FIFO
LIFO
STACK
MESSAGE_QUEUE
MAILBOX
PIPE
HEAP
MEMORY_SLAB
TIMER
config SYSCALL_TRACING
bool "Enable tracing Syscalls"
default y
help
Enable tracing Syscalls.
config TRACING_THREAD
bool "Enable tracing Threads"
default y
help
Enable tracing Threads.
config TRACING_WORK
bool "Enable tracing Work"
default y
help
Enable tracing Work and Work queue events
config TRACING_ISR
bool "Enable tracing ISRs"
default y
help
Enable tracing ISRs. This requires the backend to be
very low-latency.
config TRACING_SEMAPHORE
bool "Enable tracing Semaphores"
default y
help
Enable tracing Semaphores.
config TRACING_MUTEX
bool "Enable tracing Mutexes"
default y
help
Enable tracing Mutexes.
config TRACING_CONDVAR
bool "Enable tracing Condition Variables"
default y
help
Enable tracing Condition Variables
config TRACING_QUEUE
bool "Enable tracing Queues"
default y
help
Enable tracing Queues.
config TRACING_FIFO
bool "Enable tracing FIFO queues"
default y
help
Enable tracing FIFO queues.
config TRACING_LIFO
bool "Enable tracing LIFO queues"
default y
help
Enable tracing LIFO queues.
config TRACING_STACK
bool "Enable tracing Memory Stacks"
default y
help
Enable tracing Memory Stacks.
config TRACING_MESSAGE_QUEUE
bool "Enable tracing Message Queues"
default y
help
Enable tracing Message Queues.
config TRACING_MAILBOX
bool "Enable tracing Mailboxes"
default y
help
Enable tracing Mailboxes.
config TRACING_PIPE
bool "Enable tracing Pipes"
default y
help
Enable tracing Pipes.
config TRACING_HEAP
bool "Enable tracing Memory Heaps"
default y
help
Enable tracing Memory Heaps.
config TRACING_MEMORY_SLAB
bool "Enable tracing Memory Slabs"
default y
help
Enable tracing Memory Slabs.
config TRACING_TIMER
bool "Enable tracing Timers"
default y
help
Enable tracing Timers.
2.2 Zephyr中Tracing使用的Hook宏¶
所有被trace内核对象中都是调用下面9个宏进行hook:
SYS_PORT_TRACING_FUNC(type, func, ...)
SYS_PORT_TRACING_FUNC_ENTER(type, func, ...)
SYS_PORT_TRACING_FUNC_BLOCKING(type, func, ...)
SYS_PORT_TRACING_FUNC_EXIT(type, func, ...)
SYS_PORT_TRACING_OBJ_INIT(obj_type, obj, ...)
SYS_PORT_TRACING_OBJ_FUNC(obj_type, func, obj, ...)
SYS_PORT_TRACING_OBJ_FUNC_ENTER(obj_type, func, obj, ...)
SYS_PORT_TRACING_OBJ_FUNC_BLOCKING(obj_type, func, obj, timeout, ...)
SYS_PORT_TRACING_OBJ_FUNC_EXIT(obj_type, func, obj, ...)
#define SYS_PORT_TRACING_TYPE_MASK(type, trace_call) \
_SYS_PORT_TRACING_TYPE_MASK(type)(trace_call)
#define SYS_PORT_TRACING_FUNC(type, func, ...) \
do { \
_SYS_PORT_TRACING_FUNC(type, func)(__VA_ARGS__); \
} while (false)
#define SYS_PORT_TRACING_FUNC_ENTER(type, func, ...) \
do { \
_SYS_PORT_TRACING_FUNC_ENTER(type, func)(__VA_ARGS__); \
} while (false)
#define SYS_PORT_TRACING_FUNC_BLOCKING(type, func, ...) \
do { \
_SYS_PORT_TRACING_FUNC_BLOCKING(type, func)(__VA_ARGS__); \
} while (false)
#define SYS_PORT_TRACING_FUNC_EXIT(type, func, ...) \
do { \
_SYS_PORT_TRACING_FUNC_EXIT(type, func)(__VA_ARGS__); \
} while (false)
#define SYS_PORT_TRACING_OBJ_INIT(obj_type, obj, ...) \
do { \
SYS_PORT_TRACING_TYPE_MASK(obj_type, \
_SYS_PORT_TRACING_OBJ_INIT(obj_type)(obj, ##__VA_ARGS__)); \
} while (false)
#define SYS_PORT_TRACING_OBJ_FUNC(obj_type, func, obj, ...) \
do { \
SYS_PORT_TRACING_TYPE_MASK(obj_type, \
_SYS_PORT_TRACING_OBJ_FUNC(obj_type, func)(obj, ##__VA_ARGS__)); \
} while (false)
#define SYS_PORT_TRACING_OBJ_FUNC_ENTER(obj_type, func, obj, ...) \
do { \
SYS_PORT_TRACING_TYPE_MASK(obj_type, \
_SYS_PORT_TRACING_OBJ_FUNC_ENTER(obj_type, func)(obj, ##__VA_ARGS__)); \
} while (false)
#define SYS_PORT_TRACING_OBJ_FUNC_BLOCKING(obj_type, func, obj, timeout, ...) \
do { \
SYS_PORT_TRACING_TYPE_MASK(obj_type, \
_SYS_PORT_TRACING_OBJ_FUNC_BLOCKING(obj_type, func) \
(obj, timeout, ##__VA_ARGS__)); \
} while (false)
#define SYS_PORT_TRACING_OBJ_FUNC_EXIT(obj_type, func, obj, ...) \
do { \
SYS_PORT_TRACING_TYPE_MASK(obj_type, \
_SYS_PORT_TRACING_OBJ_FUNC_EXIT(obj_type, func)(obj, ##__VA_ARGS__)); \
} while (false)
/* Helper macros used by the extended tracing system
*/
#define _SYS_PORT_TRACING_TYPE_MASK(type) \
sys_port_trace_type_mask_ ## type
#define _SYS_PORT_TRACING_FUNC(name, func) \
sys_port_trace_ ## name ## _ ## func
#define _SYS_PORT_TRACING_FUNC_ENTER(name, func) \
sys_port_trace_ ## name ## _ ## func ## _enter
#define _SYS_PORT_TRACING_FUNC_BLOCKING(name, func) \
sys_port_trace_ ## name ## _ ## func ## _blocking
#define _SYS_PORT_TRACING_FUNC_EXIT(name, func) \
sys_port_trace_ ## name ## _ ## func ## _exit
#define _SYS_PORT_TRACING_OBJ_INIT(name) \
sys_port_trace_ ## name ## _init
#define _SYS_PORT_TRACING_OBJ_FUNC(name, func) \
sys_port_trace_ ## name ## _ ## func
#define _SYS_PORT_TRACING_OBJ_FUNC_ENTER(name, func) \
sys_port_trace_ ## name ## _ ## func ## _enter
#define _SYS_PORT_TRACING_OBJ_FUNC_BLOCKING(name, func) \
sys_port_trace_ ## name ## _ ## func ## _blocking
#define _SYS_PORT_TRACING_OBJ_FUNC_EXIT(name, func) \
sys_port_trace_ ## name ## _ ## func ## _exit
这些宏最终转换成一个函数名称,同时传入一些需要调试的参数。注意:这些宏中type,func不可随意填,需要和系统实现有关,反则会导致编译一个未定义的函数而报错。
/* sched_spinlock must be held */
static void add_to_waitq_locked(struct k_thread *thread, _wait_q_t *wait_q)
{
unready_thread(thread);
z_mark_thread_as_pending(thread);
SYS_PORT_TRACING_FUNC(k_thread, sched_pend, thread);
if (wait_q != NULL) {
thread->base.pended_on = wait_q;
z_priq_wait_add(&wait_q->waitq, thread);
}
}
以上述代码为例:
SYS_PORT_TRACING_FUNC(k_thread, sched_pend, thread);
首先会已type和func组成一个函数名sys_port_trace_k_thread_sched_pend
,然后将thread作为参数传入,thread也就是正常tracing中需要跟踪的一些系统变量,一般情况下zephyr已经为我们做好处理了:
#define sys_port_trace_k_thread_sched_pend(thread) \
SEGGER_SYSVIEW_OnTaskStopReady((uint32_t)(uintptr_t)thread, 3 << 3)
以segger sysview为例:从上面展开我们可以看到,sys_port_trace_k_thread_sched_pend
最终会调用segger sysview已经定义好的tracing函数,详细的可以参考segger sysview中的mannual手册,会有具体API的介绍。
3 SystemView使用介绍¶
3.1 Zephyr对于SystemView的实现¶
Zephyr中对于一些系统跟踪也不是都实现的,详细可以查看:zephyr\subsys\tracing\sysview\tracing_sysview.h
以其中一段为例:
#define sys_port_trace_k_lifo_init_enter(lifo) \
SEGGER_SYSVIEW_RecordU32(TID_LIFO_INIT, (uint32_t)(uintptr_t)lifo)
#define sys_port_trace_k_lifo_init_exit(lifo) SEGGER_SYSVIEW_RecordEndCall(TID_LIFO_INIT)
#define sys_port_trace_k_lifo_put_enter(lifo, data) \
SEGGER_SYSVIEW_RecordU32x2(TID_LIFO_PUT, (uint32_t)(uintptr_t)lifo, \
(uint32_t)(uintptr_t)data)
#define sys_port_trace_k_lifo_put_exit(lifo, data) SEGGER_SYSVIEW_RecordEndCall(TID_LIFO_PUT)
#define sys_port_trace_k_lifo_alloc_put_enter(lifo, data) \
SEGGER_SYSVIEW_RecordU32x2(TID_LIFO_ALLOC_PUT, (uint32_t)(uintptr_t)lifo, \
(uint32_t)(uintptr_t)data)
#define sys_port_trace_k_lifo_alloc_put_exit(lifo, data, ret) \
SEGGER_SYSVIEW_RecordEndCall(TID_LIFO_ALLOC_PUT)
#define sys_port_trace_k_lifo_get_enter(lifo, timeout) \
SEGGER_SYSVIEW_RecordU32x2(TID_LIFO_GET, (uint32_t)(uintptr_t)lifo, (uint32_t)timeout.ticks)
#define sys_port_trace_k_lifo_get_exit(lifo, timeout, ret) \
SEGGER_SYSVIEW_RecordEndCall(TID_LIFO_GET)
#define sys_port_trace_k_stack_init(stack)
#define sys_port_trace_k_stack_alloc_init_enter(stack)
#define sys_port_trace_k_stack_alloc_init_exit(stack, ret)
#define sys_port_trace_k_stack_cleanup_enter(stack)
#define sys_port_trace_k_stack_cleanup_exit(stack, ret)
#define sys_port_trace_k_stack_push_enter(stack)
#define sys_port_trace_k_stack_push_exit(stack, ret)
#define sys_port_trace_k_stack_pop_enter(stack, timeout)
#define sys_port_trace_k_stack_pop_blocking(stack, timeout)
#define sys_port_trace_k_stack_pop_exit(stack, timeout, ret)
我们可以看到对于k_stack在sysview中是没有做相关实现的,具体相关实现还是要看该文档,但是在tracerecorder(Tracealyzer)中是有相关实现的modules\debug\TraceRecorder\kernelports\Zephyr\include\tracing_tracerecorder.h
。所以有些情况,比如说我们看stack的实时分析时,我们需要自己做实现或者换个分析工具看看。
3.2 如何自定义的Tracing信号¶
首先我们可以继续参考zephyr\subsys\tracing\sysview\tracing_sysview.h
比如以timer示例:
#define sys_port_trace_k_timer_start(timer) \
SEGGER_SYSVIEW_RecordU32(TID_TIMER_START, (uint32_t)(uintptr_t)timer)
sys_port_trace_k_timer_start会调用SEGGER_SYSVIEW_RecordU32
,同时会传入一个TID_TIMER_START,后面的timer是我们传入的一个32位值:
Description
Formats and sends a SystemView packet containing a single U32 parameter payload.
Prototype
void SEGGER_SYSVIEW_RecordU32(unsigned int EventID,U32 Value);
Parameter Description:
EventID SystemView event ID.
Value The 32-bit parameter encoded to SystemView packet payload.
TID_TIMER_START: #define TID_TIMER_START (56u + TID_OFFSET)
,其中#define TID_OFFSET (32u)
,意思就是在32的基础再加一个值就是我们的要传入的一个event id,zephyr中已经使用到127+32
了,所以我们自定义事件的话event id的话要在这个后面自己添加TID_PM_DEVICE_DISABLE
。
#define TID_PM_SUSPEND (124u + TID_OFFSET)
#define TID_PM_DEVICE_REQUEST (125u + TID_OFFSET)
#define TID_PM_DEVICE_ENABLE (126u + TID_OFFSET)
#define TID_PM_DEVICE_DISABLE (127u + TID_OFFSET)
/* latest ID is 127 */
segger sysview api详情参考UM08027_SystemView.pdf
SEGGER_SYSVIEW_RecordVoid():无参数传入
SEGGER_SYSVIEW_RecordU32():传入1个32值
SEGGER_SYSVIEW_RecordU32x2()):传入2个32值
SEGGER_SYSVIEW_RecordU32x3()):传入3个32值
SEGGER_SYSVIEW_RecordU32x4()):传入4个32值
SEGGER_SYSVIEW_RecordU32x5()):传入5个32值
SEGGER_SYSVIEW_RecordU32x6()):传入6个32值
SEGGER_SYSVIEW_RecordU32x7()):传入7个32值
SEGGER_SYSVIEW_RecordU32x8()):传入8个32值
SEGGER_SYSVIEW_RecordU32x9()):传入9个32值
SEGGER_SYSVIEW_RecordU32x10()):传入10个32值
SEGGER_SYSVIEW_RecordString():传入字符串
SEGGER_SYSVIEW_RecordEndCall():传入一个API调用结束的信号
End API Call event
SEGGER_SYSVIEW_RecordEndCallU32():传入一个API调用结束的信号
End API Call event
,带32位返回值
#define sys_port_trace_k_thread_foreach_enter() SEGGER_SYSVIEW_RecordVoid(TID_THREAD_FOREACH)
#define sys_port_trace_k_thread_foreach_exit() SEGGER_SYSVIEW_RecordEndCall(TID_THREAD_FOREACH)
void z_impl_k_thread_suspend(struct k_thread *thread)
{
SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_thread, suspend, thread);
(void)z_abort_thread_timeout(thread);
LOCKED(&sched_spinlock) {
if (z_is_thread_queued(thread)) {
dequeue_thread(&_kernel.ready_q.runq, thread);
}
z_mark_thread_as_suspended(thread);
update_cache(thread == _current);
}
if (thread == _current) {
z_reschedule_unlocked();
}
SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_thread, suspend, thread);
}
同时SEGGER_SYSVIEW_RecordEndCall我们就可以显示这个函数结束的信号,这样就可以知道函数执行了多长时间。
3.3 在代码添加我们自定义事件¶
3.3.1 在项目prj.conf中添加sysview使能配置¶
# enable to use thread names
CONFIG_THREAD_NAME=y
CONFIG_SEGGER_SYSTEMVIEW=y
CONFIG_USE_SEGGER_RTT=y
CONFIG_TRACING=y
# enable for post-mortem tracing
CONFIG_SEGGER_SYSVIEW_POST_MORTEM_MODE=n
3.3.2 首先包含头文件和定义好自定义事件ID¶
#include <SEGGER_SYSVIEW.h>
#define MY_TEST_TID (128u + TID_OFFSET) /**此处定义为128起始,127之前已经被zephyr使用*,128+32 = 160,此处Event ID为160/
3.3.3 在代码中添加自己的事件触发¶
static void tx_free(struct bt_conn_tx *tx)
{
tx->cb = NULL;
tx->user_data = NULL;
tx->pending_no_cb = 0U;
SEGGER_SYSVIEW_RecordVoid(MY_TEST_TID);
k_fifo_put(&free_tx, tx);
SEGGER_SYSVIEW_RecordEndCall(MY_TEST_TID);
}
打开segger SystemView中菜单栏选择Target,在下拉列表中选择Start Recording,然后在通信方式中选择选择jlink,最后配置jlink:
自定义的事件显示效果如下图,我们定义的事件ID是160(此处定义为128起始,127之前已经被zephyr使用*,128+32 = 160,此处Event ID为160):
3.3.4 添加事件的描述性文本¶
在SystemView安装目录下
SystemView_Windows_V332_x86\Description
文件夹中找到SYSVIEW_Zephyr.txt
(SystemView会根据RTT传入的系统参数自动在./Description/SYSVIEW_*.txt
找到相应的文件),打开该文件添加自自定义函数的描述,我们在此处添加了一个tx_free,因为我们没有传入任何参数,所以比较简单,这里传参也比较简单,可以参考其他定义的信号:
92 k_timer_user_data_get timer=%I | Returns %p
93 timer->expiry_fn timer=%I
94 timer->stop_fn timer=%I
160 tx_free
3.4 在自定义信号中添加传值¶
在SYSVIEW_Zephyr.txt
中tx_free添加test=%u
92 k_timer_user_data_get timer=%I | Returns %p
93 timer->expiry_fn timer=%I
94 timer->stop_fn timer=%I
160 tx_free call count=%u
在测试代码中添加记录tx_free调用次数的传参:
static uint32_t test_cnt = 0;
static void tx_free(struct bt_conn_tx *tx)
{
test_cnt++;
tx->cb = NULL;
tx->user_data = NULL;
tx->pending_no_cb = 0U;
SEGGER_SYSVIEW_RecordU32(MY_TEST_TID, test_cnt);
k_fifo_put(&free_tx, tx);
SEGGER_SYSVIEW_RecordEndCall(MY_TEST_TID);
}
3.5 在自定义信号中添加字符串¶
#include <SEGGER_SYSVIEW.h> //step1:添加包含的头文件
#define CONNECTED_TID (130u + TID_OFFSET) //step2:定义ID
static void connected(struct bt_conn *conn, uint8_t err)
{
SEGGER_SYSVIEW_RecordString(CONNECTED_TID,"connected"); //step3:添加传递字符串
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
if (err) {
LOG_ERR("Failed to connect to %s (%u)", log_strdup(addr), err);
return;
}
const struct bt_le_conn_param *param = BT_LE_CONN_PARAM(6,
6,
0,
100);
bt_conn_le_param_update(conn, param);
LOG_INF("Connected %s", log_strdup(addr));
if (bt_conn_set_security(conn, BT_SECURITY_L2)) {
LOG_ERR("Failed to set security");
}
}
在`SYSVIEW_Zephyr.txt`中添加信号描述,162为添加的信号:
94 timer->stop_fn timer=%I
160 tx_free test=%u
161 pan_ble_handle
162 connected desc:%s
效果截图:
3.6 在第三方lib中添加事件¶
#define TID_OFFSET (32u)
#define PAN_BLE_HANDLE_TID (129u + TID_OFFSET)
extern void SEGGER_SYSVIEW_RecordVoid (unsigned int EventId);
extern void SEGGER_SYSVIEW_RecordEndCall (unsigned int EventID);
void pan_ble_handle(void)
{
SEGGER_SYSVIEW_RecordVoid(PAN_BLE_HANDLE_TID);
XXX_HANDLE();
SEGGER_SYSVIEW_RecordEndCall(PAN_BLE_HANDLE_TID);
}
在SYSVIEW_Zephyr.txt
中添加信号描述,161为添加的信号:
92 k_timer_user_data_get timer=%I | Returns %p
93 timer->expiry_fn timer=%I
94 timer->stop_fn timer=%I
160 tx_free test=%u
161 pan_ble_handle
162 connected desc:%s
效果截图:
4 Tracealyzer使用介绍¶
4.1 在项目使能tracerecoder功能¶
使能该配置后可能导致flash和ram占用更多,以periheral_hr为例,flash增加了约12K,ram增加了约10K。
CONFIG_TRACING=y
CONFIG_PERCEPIO_TRACERECORDER=y
4.2 Tracealyzer添加自己的Jlink文件¶
可以直接把常用的jlink安装文件夹中的相关文件拷贝到 Tracealyzer安装目录,覆盖即可,这样pan1080的device文件就可以直接使用了(Segger Sysview也是一样):
JLink_x64.dll
JLinkARM.dll
JLinkDevices.xml
Devices(文件夹)
4.3 在代码中添加用户自定义事件¶
使用
xTraceRegisterString
注册一个用户事件通道(User Event Channel )vTracePrint
或者vTracePrintF
输出事件信息,第一个参数使用注册的chn。其中vTracePrint无格式化输出,不传入参数,速度会更快;vTracePrintF支持格式化输出,可以传入不定参数。
traceString chn = xTraceRegisterString("MyChannel");
...
vTracePrint(chn, "Hello World!");
vTracePrintF(chn, "Value: %d", myValue);
4.4 跟踪中断函数¶
定义好需要跟踪的中断函数的中断优先级
#define PRIO_ISR_HANDLER -4
注册中断函数事件:xTraceSetISRProperties,这个函数会返回一个句柄,建议放到一个全局变量中。
traceHandle isr_trace_handle = xTraceSetISRProperties("ISRHandle", PRIO_ISR_HANDLER);
vTraceStoreISRBegin和vTraceStoreISREnd()放到中断函数起始和结束。
#define PRIO_ISR_HANDLER -4
...
traceHandle isr_trace_handle = xTraceSetISRProperties("ISRHandle", PRIO_ISR_HANDLER);
...
void ISR_handler(void) {
vTraceStoreISRBegin(isr_trace_handle);
...
vTraceStoreISREnd(0);
}