当前文档版本为 v0.8.0,您可以访问当前页面的 开发中 版本以获取最近可能的更新。

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框架概览:

zephyr tracing overview

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
Segger SystemView

Segger SYstemView软件界面

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
TraceAndCPULoad

Tracealyzer软件界面

其中关于zephyr中使用Tracealyzer的官方使用说明:

getting-started-with-tracealyzer-for-zephyr-rtos

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

TraceCompass软件界面

TraceCompass的支持如下:

Multiple trace formats supported

  • Common Trace Format(CTF), including but not limited to:

  • 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:

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:

SysView_jlinK

SystemView Jlink配置界面

自定义的事件显示效果如下图,我们定义的事件ID是160(此处定义为128起始,127之前已经被zephyr使用*,128+32 = 160,此处Event ID为160):

sysview_custom_event

SystemView Custom Event

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
tx_free_sample

显示自定义的事件描述信息

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);
}
images/tx_free_count.png

在自定义信号中添加传值

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

​效果截图:

event_addstring

在自定义信号中添加字符串

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

效果截图:

lib_event.png

在第三方lib中添加事件

4 Tracealyzer使用介绍

4.1 在项目使能tracerecoder功能

使能该配置后可能导致flash和ram占用更多,以periheral_hr为例,flash增加了约12K,ram增加了约10K。

CONFIG_TRACING=y
CONFIG_PERCEPIO_TRACERECORDER=y

4.3 在代码中添加用户自定义事件

  1. 使用xTraceRegisterString注册一个用户事件通道(User Event Channel )

  2. vTracePrint或者vTracePrintF输出事件信息,第一个参数使用注册的chn。其中vTracePrint无格式化输出,不传入参数,速度会更快;vTracePrintF支持格式化输出,可以传入不定参数。

traceString chn = xTraceRegisterString("MyChannel");
...
vTracePrint(chn, "Hello World!");
vTracePrintF(chn, "Value: %d", myValue);

4.4 跟踪中断函数

  1. 定义好需要跟踪的中断函数的中断优先级

#define PRIO_ISR_HANDLER -4
  1. 注册中断函数事件:xTraceSetISRProperties,这个函数会返回一个句柄,建议放到一个全局变量中。

traceHandle isr_trace_handle = xTraceSetISRProperties("ISRHandle", PRIO_ISR_HANDLER);
  1. vTraceStoreISRBeginvTraceStoreISREnd()放到中断函数起始和结束。

#define PRIO_ISR_HANDLER -4
...
traceHandle isr_trace_handle = xTraceSetISRProperties("ISRHandle", PRIO_ISR_HANDLER);
...
void ISR_handler(void) {
  vTraceStoreISRBegin(isr_trace_handle);
  ...
  vTraceStoreISREnd(0);
}

4.5 Tracealyzer Mannual 链接

Tracealyzer Mannual

4.6 Tracealyzer 下载和注册

该软件为收费版本,下载时可以通过邮箱先申请评估版本(10天):

tracealyzer_download

Tracealyzer下载注册