banner
ekko

ekko's blog

时间不在于你拥有多少,而在于你怎样使用
github
xbox
email

zephyr sysview 使用

sysview 简介#

SEGGER SystemView User Guide
SystemView 是 Segger 的一种可视化调试工具,用于嵌入式系统的实时调试和分析。它可以捕获和可视化系统中的各种事件,例如任务切换、中断处理和信号量等。SystemView 可以与 Segger 的 J-Link 调试器一起使用,并且支持多种嵌入式操作系统,如 FreeRTOS、Azure RTOS、[[zephyr]] 和 ThreadX。

SystemView 的主要特点包括:

  • 实时事件捕获 - SystemView 可以实时捕获系统中的各种事件,并将它们可视化显示出来。这有助于开发人员了解系统的运行情况,并找到可能导致问题的瓶颈或错误。
  • 可视化分析工具 - SystemView 提供了一组强大的可视化工具,用于分析系统的性能和行为。这些工具包括 CPU 负载图、任务调度图、信号量等待图和中断处理图。
  • 支持多种嵌入式操作系统 - SystemView 支持多种嵌入式操作系统,包括 FreeRTOS、Azure RTOS、zephyr 和 ThreadX。这使得 SystemView 可以用于各种嵌入式系统,无论是基于操作系统还是裸机系统。
  • 集成 J-Link 调试器 - SystemView 可以与 Segger 的 J-Link 调试器集成使用,从而提供了一种方便的方式来调试和分析嵌入式系统。
  • 支持多种目标平台 - SystemView 支持多种目标平台,包括 ARM、Cortex-M、Cortex-R 和 Cortex-A 处理器。这使得 SystemView 可以用于各种嵌入式系统,无论是基于 ARM 的系统还是其他处理器的系统。
    总的来说,SystemView 是一种强大的可视化调试工具,可以帮助开发人员了解嵌入式系统的运行情况,并找到可能导致问题的瓶颈或错误。

sysview 界面介绍#

[! 官网介绍]
以下内容节选机翻自官网:SEGGER SystemView 用户指南 The_SystemView_Application

时间线#

“时间轴” 窗口将所有系统信息收集到一个视图中。 它按上下文(任务、中断、 scheduler、timer 和 idle) 在系统时间上。 每行引用一个上下文项,以显示已 在应用程序被监视时使用。
上下文之间的切换显示为连接线,以便于识别 哪些事件会导致上下文切换以及它们何时发生。

标记为准备执行的任务以浅灰色条显示 直到他们的执行开始。

上下文按优先级排序。 第一行显示统一上下文中的所有活动。 中断是列表的顶部,按 Id 排序。 后跟调度程序和软件计时器,如果它们在 系统。 在计划程序(和计时器)下方,任务按优先级排序。 底部上下文显示空闲时间,当没有其他上下文处于活动状态时。

事件列表#

“事件列表” 窗口显示系统报告的所有事件并显示 他们的信息。 将显示包含以下项目的事件:

  • 用于在列表中查找事件的 ID。
  • 可选择以目标时间或记录时间显示的时间戳, 分辨率低至纳秒(如果适用)。
  • 事件报告期间的活动上下文,即正在运行的任务。
  • 事件描述,与事件类型(IRS 进入和退出、任务活动、API 调用)一起显示。
  • 描述事件参数的事件详细信息,例如 API 调用参数。

“事件” 列表允许浏览列表、跳转到下一个或 上一个上下文,或下一个或上一个类似事件。 “时间轴” 和 “CPU 负载” 窗口同步以匹配当前 选定的事件。

“事件” 列表中的时间戳可以显示为相对于录制开始的时间,或者 到目标系统时间,当系统报告时(分别→查看显示目标时间和显示记录时间)。 可以将事件设置为以下事件的时间参考,以便于轻松 测量一个事件在另一个事件之后发生的时间(快捷方式 R)。

“事件” 列表具有一个事件过滤器,允许显示或隐藏 API、ISR、系统 信息、消息、任务和用户事件。

终端#

“终端” 窗口显示目标应用程序的 printf() 输出 在发送输出的任务上下文和时间戳旁边 消息发送时。

双击消息以显示该消息以及 “事件” 列表中的所有信息。

“时间轴” 窗口还显示输出指示器。当指标重叠时 在显示中,它们按严重性级别排序 - 错误始终显示在顶部。 可以配置要在时间轴中显示的输出指示器的最低严重性级别 通过查看 → 消息指示器...。

SystemView printf 输出 (SEGGER_SYSVIEW_Print*) 可以由应用程序格式化发送,或者 未格式化所有参数,以便由 SystemView 应用程序格式化显示。

CPU 负载#

“CPU 负载” 窗口链接到 “时间轴” 中显示的时间跨度。

“时间轴” 窗口中显示的时间跨度被划分为可配置的条柱数 显示在 “CPU 负载” 窗口中。 对于每个上下文,其活动时间相对于相应的箱宽显示。CPU 的 箱中的负载分布按上下文优先级的顺序显示。

可以调整条柱的数量,以达到更细或更粗的时间粒度。 使用单个 bin 时,将在整个显示的时间轴部分上计算 CPU 负载比。

上下文#

“上下文”(Contexts) 窗口显示每个报告上下文的统计信息 (任务、中断、调度程序、计时器和空闲)。 每个上下文都可以通过其名称和类型来标识。类型包括 任务的优先级和中断的 ID(例如,Cortex-M SysTick 是 中断 ID #15。

“上下文” 窗口信息包括以下项目:

  • 上下文名称和类型。
  • 任务的堆栈信息(如果可用)。
  • 上下文的激活次数。
  • 总持续时间、最小阻止时间、总持续时间、最小持续时间和最长持续时间 上下文分别已准备就绪,但未处于活动状态。
  • 总运行时间,上下文处于活动状态的总时间。
  • Time Interrupted,上下文因中断而暂停的总时间。
  • CPU 负载,上下文活动时间与完成记录时间的比率。
  • 最后,最小和最大运行时间,分别是上下文处于活动状态的最晚、最短和最长时间的持续时间。
  • 最小和最大运行时间 / 秒,即最后记录的秒数中的最小和最大上下文活动时间。

“上下文” 窗口在录制过程中更新。

运行#

“运行时” 窗口显示每个上下文的有关其活动时间的统计度量值。 显示的度量是(在特定上下文的所有调用中):

  • 最短活动时间,
  • 四分位数 (25%, 50%, 75%)
  • 最大活动时间。

统计测量值将根据要求显示为激活时间内的箱形图,作为目标报告的 1 或 5 * 10N 个循环的倍数。 N 是动态选择的,因此在此之前出现的最大活动时间将适合。 对于特定上下文,持续时间样本的直方图始终由箱形图跨度上的 100 个条柱组成。

#

堆窗口记录记录的动态内存的分配和解除分配 使用 SEGGER_RTL_HeapAlloc()、SEGGER_RTL_HeapFree() 和其他与堆相关的 API 功能。

每个分配或解除分配事件都会更新一个或多个 SystemView 模型 由应用程序维护的堆。SystemView 维护此模型,并且每个 “事件和堆” 窗口中的 “分配和解除分配事件” 显示状态 这个模型。如果事件溢出导致分配或解除分配事件 丢失,堆的模型变为无效。

系统#

“系统” 窗口显示:

  • 目标系统,有关系统的信息,应用程序已报告以识别它。也 本节中是用户可设置的属性,用于配置操作系统、模块和系统事件的显示。
  • 记录信息,如事件数、平均和峰值事件频率以及用户提供的其他元信息 记录。
  • 分析信息,关于记录的分析阶段的统计信息。
  • 有关任务、中断、计时器和其他 SystemView 事件的统计信息。

目标系统信息包括应用程序名称、正在运行的操作系统、有关目标硬件的信息、 和计时信息。 有关任务交换机和中断频率的其他信息提供了系统的快速概览。

用户可设置的属性和元信息与记录一起保存,并允许识别和 预设记录配置以供以后分析。

上下文统计#

“上下文统计信息” 窗口显示有关任务运行时统计信息的详细信息。 目的是更详细地分析单个任务,特别是此任务被阻止或暂停的范围。 提供以下信息:

  • 任务运行的总时间
  • 任务被阻止的总时间
  • 任务暂停的总时间

除了持续时间之外,还会显示每个条目发生此事件的次数。

下拉菜单用于选择显示其信息的任务。

zephyr 开启 sysview 功能#

配置文件#

CONFIG_TRACING=y
CONFIG_SEGGER_SYSTEMVIEW=y
CONFIG_SEGGER_SYSVIEW_RTT_BUFFER_SIZE=20240 #RTT 缓存大小
CONFIG_SEGGER_SYSTEMVIEW_BOOT_ENABLE=y      #系统启动时自动记录
CONFIG_SEGGER_SYSVIEW_POST_MORTEM_MODE=n    #事后分析模式,循环刷新缓存

soc 适配#

目前 zephyr 的 sysview 功能只支持 RTT 通信方式,因此需要 soc 包含 RTT 功能

# Copyright 2022 HNU-ESNL
# Copyright 2022 openEuler SIG-Zephyr
# SPDX-License-Identifier: Apache-2.0

choice
prompt "Rockchip RK3568 SoC"
depends on SOC_SERIES_RK3568

config SOC_RK3568
	bool "Rockchip rk3568"
	select ARM64
	select CPU_CORTEX_A55
	select ARM_ARCH_TIMER
	select GIC_V3
	select HAS_SEGGER_RTT if ZEPHYR_SEGGER_MODULE #需增加HAS_SEGGER_RTT

endchoice

sysview 接口调用流程与内容#

接口头文件#

subsys/tracing/sysview/tracing_sysview.h,支持的所有 tracing 接口都在该头文件中定义,如下:

#define sys_port_trace_k_thread_start(thread)                                                      \
	SEGGER_SYSVIEW_RecordU32(TID_THREAD_START, (uint32_t)(uintptr_t)thread)

#define sys_port_trace_k_thread_abort(thread)                                                      \
	SEGGER_SYSVIEW_RecordU32(TID_THREAD_ABORT, (uint32_t)(uintptr_t)thread)

#define sys_port_trace_k_thread_suspend_enter(thread)                                              \
	SEGGER_SYSVIEW_RecordU32(TID_THREAD_SUSPEND, (uint32_t)(uintptr_t)thread)

#define sys_port_trace_k_thread_suspend_exit(thread)                                               \
	SEGGER_SYSVIEW_RecordEndCall(TID_THREAD_SUSPEND)

#define sys_port_trace_syscall_enter(id, name, ...)	\
	SEGGER_SYSVIEW_RecordString(TID_SYSCALL, (const char *)#name)

#define sys_port_trace_syscall_exit(id, name, ...)	\
	SEGGER_SYSVIEW_RecordEndCall(TID_SYSCALL)

可以看到,所有的接口的传入参数都是 TID (事件 ID) 以及不定数量的额外参数,事件 ID 小于 32 的为 sysview 内置事件,其余为 RTOS 适配事件及自定义事件。

zephyr 定义的事件 ID 在subsys/tracing/sysview/tracing_sysview_ids.h中:

/*
 * Copyright (c) 2021 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#ifndef ZEPHYR_TRACING_SYSVIEW_IDS_H_
#define ZEPHYR_TRACING_SYSVIEW_IDS_H_

#ifdef __cplusplus
extern "C" {
#endif

#define TID_OFFSET (32u)

#define TID_SCHED_LOCK (0u + TID_OFFSET)
#define TID_SCHED_UNLOCK (1u + TID_OFFSET)
#define TID_BUSYWAIT (2u + TID_OFFSET)

#define TID_IRQ_ENABLE (3u + TID_OFFSET)
#define TID_IRQ_DISABLE (4u + TID_OFFSET)

#define TID_MUTEX_INIT (5u + TID_OFFSET)
#define TID_MUTEX_UNLOCK (6u + TID_OFFSET)
#define TID_MUTEX_LOCK (7u + TID_OFFSET)

#define TID_SEMA_INIT (8u + TID_OFFSET)
#define TID_SEMA_GIVE (9u + TID_OFFSET)
#define TID_SEMA_TAKE (10u + TID_OFFSET)
#define TID_SEMA_RESET (59u + TID_OFFSET)

#define TID_QUEUE_INIT (11u + TID_OFFSET)
#define TID_QUEUE_APPEND (12u + TID_OFFSET)
#define TID_QUEUE_ALLOC_APPEND (13u + TID_OFFSET)
#define TID_QUEUE_PREPEND (14u + TID_OFFSET)
#define TID_QUEUE_ALLOC_PREPEND (15u + TID_OFFSET)
#define TID_QUEUE_INSERT (16u + TID_OFFSET)
#define TID_QUEUE_APPEND_LIST (17u + TID_OFFSET)
#define TID_QUEUE_GET (18u + TID_OFFSET)
#define TID_QUEUE_REMOVE (19u + TID_OFFSET)
#define TID_QUEUE_CANCEL_WAIT (20u + TID_OFFSET)
#define TID_QUEUE_PEAK_HEAD (21u + TID_OFFSET)
#define TID_QUEUE_PEAK_TAIL (22u + TID_OFFSET)

#define TID_STACK_INIT (23u + TID_OFFSET)
#define TID_STACK_PUSH (24u + TID_OFFSET)
#define TID_STACK_POP (25u + TID_OFFSET)
#define TID_QUEUE_STACK_CLEANUP (26u + TID_OFFSET)

#define TID_MSGQ_INIT (27u + TID_OFFSET)
#define TID_MSGQ_PUT (28u + TID_OFFSET)
#define TID_MSGQ_GET (29u + TID_OFFSET)
#define TID_MSGQ_CLEANUP (30u + TID_OFFSET)
#define TID_MSQG_PEEK (31u + TID_OFFSET)
#define TID_MSGQ_PURGE (32u + TID_OFFSET)

#define TID_MBOX_INIT (33u + TID_OFFSET)
#define TID_MBOX_PUT (34u + TID_OFFSET)
#define TID_MBOX_ASYNC_PUT (35u + TID_OFFSET)
#define TID_MBOX_GET (36u + TID_OFFSET)
#define TID_MBOX_DATA_GET (37u + TID_OFFSET)
#define TID_MBOX_DATA_BLOCK_GET (38u + TID_OFFSET)

#define TID_PIPE_INIT (39u + TID_OFFSET)
#define TID_PIPE_CLEANUP (40u + TID_OFFSET)
#define TID_PIPE_PUT (41u + TID_OFFSET)
#define TID_PIPE_GET (42u + TID_OFFSET)
#define TID_PIPE_BLOCK_GET (43u + TID_OFFSET)

#define TID_HEAP_INIT (44u + TID_OFFSET)
#define TID_HEAP_ALLOC (45u + TID_OFFSET)
#define TID_HEAP_FREE (46u + TID_OFFSET)
#define TID_HEAP_ALIGNED_ALLOC (47u + TID_OFFSET)

#define TID_MSLAB_INIT (52u + TID_OFFSET)
#define TID_MSLAB_ALLOC (53u + TID_OFFSET)
#define TID_MSLAB_FREE (54u + TID_OFFSET)

#define TID_TIMER_INIT (55u + TID_OFFSET)
#define TID_TIMER_START (56u + TID_OFFSET)
#define TID_TIMER_STOP (57u + TID_OFFSET)
#define TID_TIMER_STATUS_SYNC (58u + TID_OFFSET)
#define TID_TIMER_USER_DATA_GET (60u + TID_OFFSET)
#define TID_TIMER_EXPIRY_FN (61u + TID_OFFSET)
#define TID_TIMER_STOP_FN (62u + TID_OFFSET)

#define TID_SLEEP (63u + TID_OFFSET)
#define TID_MSLEEP (64u + TID_OFFSET)
#define TID_USLEEP (65u + TID_OFFSET)

#define TID_THREAD_PRIORITY_SET (66u + TID_OFFSET)
#define TID_THREAD_WAKEUP (67u + TID_OFFSET)
#define TID_THREAD_ABORT (68u + TID_OFFSET)
#define TID_THREAD_START (69u + TID_OFFSET)
#define TID_THREAD_SUSPEND (70u + TID_OFFSET)
#define TID_THREAD_RESUME (71u + TID_OFFSET)
#define TID_THREAD_JOIN (72u + TID_OFFSET)
#define TID_THREAD_YIELD (73u + TID_OFFSET)
#define TID_THREAD_USERMODE_ENTER (74u + TID_OFFSET)
#define TID_THREAD_FOREACH (75u + TID_OFFSET)
#define TID_THREAD_FOREACH_UNLOCKED (76u + TID_OFFSET)
#define TID_THREAD_NAME_SET (123u + TID_OFFSET)

#define TID_CONDVAR_INIT (77u + TID_OFFSET)
#define TID_CONDVAR_SIGNAL (78u + TID_OFFSET)
#define TID_CONDVAR_BROADCAST (79u + TID_OFFSET)
#define TID_CONDVAR_WAIT (80u + TID_OFFSET)

#define TID_WORK_CANCEL (81u + TID_OFFSET)
#define TID_WORK_CANCEL_DELAYABLE (82u + TID_OFFSET)
#define TID_WORK_CANCEL_DELAYABLE_SYNC (83u + TID_OFFSET)
#define TID_WORK_CANCEL_SYNC (84u + TID_OFFSET)
#define TID_WORK_DELAYABLE_INIT (85u + TID_OFFSET)
#define TID_WORK_QUEUE_DRAIN (86u + TID_OFFSET)
#define TID_WORK_FLUSH (87u + TID_OFFSET)
#define TID_WORK_FLUSH_DELAYABLE (88u + TID_OFFSET)
#define TID_WORK_INIT (89u + TID_OFFSET)
#define TID_WORK_POLL_CANCEL (90u + TID_OFFSET)
#define TID_WORK_POLL_INIT (91u + TID_OFFSET)
#define TID_WORK_POLL_SUBMIT (92u + TID_OFFSET)
#define TID_WORK_POLL_SUBMIT_TO_QUEUE (93u + TID_OFFSET)
#define TID_WORK_QUEUE_START (94u + TID_OFFSET)
#define TID_WORK_RESCHEDULE (95u + TID_OFFSET)
#define TID_WORK_RESCHEDULE_FOR_QUEUE (96u + TID_OFFSET)
#define TID_WORK_SCHEDULE (97u + TID_OFFSET)
#define TID_WORK_SCHEDULE_FOR_QUEUE (98u + TID_OFFSET)
#define TID_WORK_SUBMIT (99u + TID_OFFSET)
#define TID_WORK_SUBMIT_TO_QUEUE (100u + TID_OFFSET)
#define TID_WORK_QUEUE_UNPLUG (101u + TID_OFFSET)
#define TID_WORK_QUEUE_INIT (102u + TID_OFFSET)

#define TID_FIFO_INIT (110u + TID_OFFSET)
#define TID_FIFO_CANCEL_WAIT (111u + TID_OFFSET)
#define TID_FIFO_ALLOC_PUT (112u + TID_OFFSET)
#define TID_FIFO_PUT_LIST (113u + TID_OFFSET)
#define TID_FIFO_PUT_SLIST (114u + TID_OFFSET)
#define TID_FIFO_PEAK_HEAD (115u + TID_OFFSET)
#define TID_FIFO_PEAK_TAIL (116u + TID_OFFSET)
#define TID_FIFO_PUT (117u + TID_OFFSET)
#define TID_FIFO_GET (118u + TID_OFFSET)

#define TID_LIFO_INIT (119u + TID_OFFSET)
#define TID_LIFO_PUT (120u + TID_OFFSET)
#define TID_LIFO_GET (121u + TID_OFFSET)
#define TID_LIFO_ALLOC_PUT (122u + TID_OFFSET)


#define TID_PM_SYSTEM_SUSPEND (124u + TID_OFFSET)
#define TID_PM_DEVICE_RUNTIME_GET (125u + TID_OFFSET)
#define TID_PM_DEVICE_RUNTIME_PUT (126u + TID_OFFSET)
#define TID_PM_DEVICE_RUNTIME_PUT_ASYNC (127u + TID_OFFSET)
#define TID_PM_DEVICE_RUNTIME_ENABLE (128u + TID_OFFSET)
#define TID_PM_DEVICE_RUNTIME_DISABLE (129u + TID_OFFSET)

#define TID_SYSCALL (130u + TID_OFFSET)

/* latest ID is 130 */

#ifdef __cplusplus
}
#endif

#endif /* ZEPHYR_TRACING_SYSVIEW_IDS_H_ */

sysview 内置事件定义在modules/debug/segger/SEGGER/SEGGER_SYSVIEW.h

//
// SystemView events. First 32 IDs from 0 .. 31 are reserved for these
//
#define   SYSVIEW_EVTID_NOP                0  // Dummy packet.
#define   SYSVIEW_EVTID_OVERFLOW           1
#define   SYSVIEW_EVTID_ISR_ENTER          2
#define   SYSVIEW_EVTID_ISR_EXIT           3
#define   SYSVIEW_EVTID_TASK_START_EXEC    4
#define   SYSVIEW_EVTID_TASK_STOP_EXEC     5
#define   SYSVIEW_EVTID_TASK_START_READY   6
#define   SYSVIEW_EVTID_TASK_STOP_READY    7
#define   SYSVIEW_EVTID_TASK_CREATE        8
#define   SYSVIEW_EVTID_TASK_INFO          9
#define   SYSVIEW_EVTID_TRACE_START       10
#define   SYSVIEW_EVTID_TRACE_STOP        11
#define   SYSVIEW_EVTID_SYSTIME_CYCLES    12
#define   SYSVIEW_EVTID_SYSTIME_US        13
#define   SYSVIEW_EVTID_SYSDESC           14
#define   SYSVIEW_EVTID_MARK_START        15
#define   SYSVIEW_EVTID_MARK_STOP         16
#define   SYSVIEW_EVTID_IDLE              17
#define   SYSVIEW_EVTID_ISR_TO_SCHEDULER  18
#define   SYSVIEW_EVTID_TIMER_ENTER       19
#define   SYSVIEW_EVTID_TIMER_EXIT        20
#define   SYSVIEW_EVTID_STACK_INFO        21
#define   SYSVIEW_EVTID_MODULEDESC        22

#define   SYSVIEW_EVTID_INIT              24
#define   SYSVIEW_EVTID_NAME_RESOURCE     25
#define   SYSVIEW_EVTID_PRINT_FORMATTED   26
#define   SYSVIEW_EVTID_NUMMODULES        27
#define   SYSVIEW_EVTID_END_CALL          28
#define   SYSVIEW_EVTID_TASK_TERMINATE    29

#define   SYSVIEW_EVTID_EX                31

接口调用方式#

直接调用接口头文件定义的函数即可,不过 zephyr 是使用的函数名拼接的方式进行的调用,便于适配不同后端的 tracing 方式。

如下是使用的SYS_PORT_TRACING_FUNC进行调用:

#ifdef CONFIG_INSTRUMENT_THREAD_SWITCHING
void z_thread_mark_switched_in(void)
{
#if defined(CONFIG_SCHED_THREAD_USAGE) && !defined(CONFIG_USE_SWITCH)
	z_sched_usage_start(_current);
#endif

#ifdef CONFIG_TRACING
	SYS_PORT_TRACING_FUNC(k_thread, switched_in);
#endif
}

void z_thread_mark_switched_out(void)
{
#if defined(CONFIG_SCHED_THREAD_USAGE) && !defined(CONFIG_USE_SWITCH)
	z_sched_usage_stop();
#endif

#ifdef CONFIG_TRACING
#ifdef CONFIG_THREAD_LOCAL_STORAGE
	/* Dummy thread won't have TLS set up to run arbitrary code */
	if (!_current_cpu->current ||
	    (_current_cpu->current->base.thread_state & _THREAD_DUMMY) != 0)
		return;
#endif
	SYS_PORT_TRACING_FUNC(k_thread, switched_out);
#endif
}

实际调用的接口为sys_trace_k_thread_switched_insys_trace_k_thread_switched_out

上位机数据解析#

上位机想要解析 RTOS 适配事件,就需要先知道这些事件的信息以及参数的使用方式。

subsys/tracing/sysview/SYSVIEW_Zephyr.txt就是给 sysview 上位机的一个配置文件:

# Inspired by the the same configuration file available in the SystemView tool

Option    ReversePriority
#
# Types for parameter formatters
#
NamedType Bool          0=false 1=true

NamedType TimeOut       *="%u ticks" 0=TIMEOUT_NO_WAIT 4294967295=FOREVER


NamedType ErrCodePosix  *=%i 0=ESUCCESS  -1=EPERM        -2=ENOENT       -3=ESRCH         -4=EINTR            -5=EIO              -6=ENXIO       -7=E2BIG         -8=ENOEXEC       -9=EBADF       -10=ECHILD         -11=EAGAIN       -12=ENOMEM   -13=EACCES -14=EFAULT -15=ENOTEMPTY    -16=EBUSY   -17=EEXIST -18=EXDEV -19=ENODEV -20=ENOTDIR -21=EISDIR -22=EINVAL -23=ENFILE -24=EMFILE -25=ENOTTY -26=ENAMETOOLONG -27=EFBIG -28=ENOSPC -29=ESPIPE -30=EROFS -31=EMLINK -32=EPIPE -33=EDEADLK -34=ENOLCK -35=ENOTSUP -36=EMSGSIZE -72=ECANCELED -81=ERRMAX
NamedType ErrCodeMath   *=%i 0=ESUCCESS -37=EDOM         -38=ERANGE
NamedType ErrCodeNetArg *=%i 0=ESUCCESS -40=EDESTADDRREQ -41=EPROTOTYPE -42=ENOPROTOOPT  -43=EPROTONOSUPPORT -44=ESOCKTNOSUPPORT -45=EOPNOTSUPP -46=EPFNOSUPPORT -47=EAFNOSUPPORT -48=EADDRINUSE   -49=EADDRNOTAVAIL -50=ENOTSOCK
NamedType ErrCodeNetOps *=%i 0=ESUCCESS -51=ENETUNREACH -52=ENETRESET   -53=ECONNABORTED -54=ECONNRESET      -55=ENOBUFS         -56=EISCONN    -57=ENOTCONN     -58=ESHUTDOWN    -59=ETOOMANYREFS -60=ETIMEDOUT     -61=ECONNREFUSED -62=ENETDOWN -63=ETXTBSY -64=ELOOP -65=EHOSTUNREACH -66=ENOTBLK -67=EHOSTDOWN
NamedType ErrCodeNetIO  *=%i 0=ESUCCESS -68=EINPROGRESS -69=EALREADY    -11=EWOULDBLOCK  -71=ENOSYS
NamedType ErrCodeStream *=%i 0=ESUCCESS -74=ENOSR       -75=ENOSTR      -76=EPROTO       -77=EBADMSG         -78=ENODATA         -79=ETIME      -80=ENOMSG      -138=EILSEQ
NamedType ErrCodeMsg    *=%i 0=ESUCCESS -11=EAGAIN      -80=ENOMSG

NamedType PowerState *=%i 0=ACTIVE 1=RUNTIME_IDLE 2=SUSPEND_TO_IDLE 3=STANDBY 4=SUSPEND_TO_RAM 5=SUSPEND_TO_DISK 6=SOFT_OFF

#
# Task States
#
TaskState 0xBF 1=dummy, 2=Waiting, 4=New, 8=Terminated, 16=Suspended, 32=Terminating, 128=Ready

#
# API Functions
#
32  k_sched_lock
33  k_sched_unlock
34  k_busy_wait                Timeout=%u us

35  irq_enable                 irq=%u
36  irq_disable                irq=%u

37  k_mutex_init               | Returns %ErrCodePosix
38  k_mutex_unlock             | Returns %ErrCodePosix
39  k_mutex_lock               mutex=%I, | Returns %ErrCodePosix

40  k_sem_init                 sem=%I, initial_count=%u, | Returns %ErrCodePosix
41  k_sem_give                 sem=%I   | Returns %ErrCodePosix
42  k_sem_take                 sem=%I, Timeout=%TimeOut| Returns %ErrCodePosix
91  k_sem_reset                sem=%I

43  k_queue_init               q=%I
44  k_queue_append             q=%I, data=%p          | Returns (void)(%ErrCodePosix)
45  k_queue_alloc_append       q=%I, data=%p          | Returns %ErrCodePosix
46  k_queue_prepend            q=%I, data=%p          | Returns (void)(%ErrCodePosix)
47  k_queue_alloc_prepend      q=%I, data=%p          | Returns %ErrCodePosix
48  k_queue_insert             q=%I, data=%p          | Returns (void)(%ErrCodePosix)
49  k_queue_append_list        q=%I, head=%p, tail=%p | Returns %ErrCodePosix
50  k_queue_get                q=%I, Timeout=%TimeOut | Returns %p
51  k_queue_remove             q=%I, data=%p          | Returns &Bool
52  k_queue_cancel_wait        q=%I
53  k_queue_peek_head          q=%I     | Returns %p
54  k_queue_peek_tail          q=%I     | Returns %p

55  k_stack_init               stack=%I, buffer=%p, num_entries=%u
56  k_stack_push               stack=%I, data=%p      | Returns %ErrCodePosix
57  k_stack_pop                stack=%I, data=%p, Timeout=%TimeOut | Returns %ErrCodePosix
58  k_stack_cleanup            stack=%I | Returns %ErrCodePosix

59  k_msgq_init                msgq=%I, buffer=%p, msg_size=%u, max_msgs=%us
60  k_msgq_put                 msgq=%I, data=%p, Timeout=%TimeOut | Returns %ErrCodeMsg
61  k_msgq_get                 msgq=%I, data=%p, Timeout=%TimeOut | Returns %ErrCodeMsg
62  k_msgq_cleanup             msgq=%I  | Returns %ErrCodePosix
63  k_msgq_peek                msgq=%I, data=%p | Returns %ErrCodeMsg
64  k_msgq_purge               msgq=%I

65  k_mbox_init                mbox=%I
66  k_mbox_put                 mbox=%I, tx_msg=%p, Timeout=%TimeOut | Returns %ErrCodeMsg
67  k_mbox_async_put           mbox=%I, tx_msg=%p, sem=%I | Returns (void)(%ErrCodeMsg)
68  k_mbox_get                 mbox=%I, rx_msg=%p, buffer=%p, Timeout=%TimeOut | Returns %ErrCodeMsg
69  k_mbox_data_get            rx_msg=%p, buffer=%p
70  k_mbox_data_block_get      rx_msg=%p, pool=%p, block=%p,  Timeout=%TimeOut | Returns %ErrCodePosix

71  k_pipe_init                pipe=%I, buffer=%p, size=%u
72  k_pipe_cleanup             pipe=%I  | Returns %ErrCodePosix
73  k_pipe_put                 pipe=%I, data=%p, bytes_to_write=%u, bytes_written=%u, min_xfer=%u, Timeout=%TimeOut            | Returns %ErrCodePosix
74  k_pipe_get                 pipe=%I, data=%p, bytes_to_read=%u, bytes_read=%u, min_xfer=%u, Timeout=%TimeOut                | Returns %ErrCodePosix

76  k_heap_init                heap=%I, mem=%p, bytes=%u
77  k_heap_alloc               heap=%I, bytes=%u, Timeout=%TimeOut | Returns %p
78  k_heap_free                heap=%I, mem=%p
79  k_heap_aligned_alloc       heap=%I

84  k_mem_slab_init            slab=%I, buffer=%p, bock_size=%u, num_blocks=%u | Returns ErrCodePosix
85  k_mem_slab_alloc           slab=%I, mem=%p, Timeout=%TimeOut | Returns %ErrCodePosix
86  k_mem_slab_free            slab=%I, mem=%p

87  k_timer_init               timer=%I, expiry_fn=%I, stop_fn=%I
88  k_timer_start              timer=%I, duration=%TimeOut , period=%u ticks
89  k_timer_stop               timer=%I
90  k_timer_status_sync        timer=%I
91  k_timer_user_data_set      timer=%I, user_data=%p
92  k_timer_user_data_get      timer=%I | Returns %p
93  timer->expiry_fn           timer=%I
94  timer->stop_fn             timer=%I

95  k_sleep                    msec=%u ms | Returns %u
96  k_msleep                   msec=%u ms | Returns %u
97  k_usleep                   usec=%u us | Returns %u

98  k_thread_priority_set      thread=%t, priority=%u
99  k_thread_wakeup
100 k_thread_abort
101 k_thread_start             thread=%t
102 k_thread_suspend
103 k_thread_resume
104 k_thread_join
105 k_thread_yield
106 k_thread_user_mode_enter
107 k_thread_foreach
108 k_thread_foreach_unlocked
155 k_thread_name_set           thread=%I

109 k_condvar_init              condvar=%I
110 k_condvar_signal            condvar=%I
111 k_condvar_broadcast         condvar=%I
112 k_condvar_wait              condvar=%I


113 k_work_cancel                work=%I
114 k_work_canel_delayable       dwork=%I
115 k_work_cancel_delayable_sync dwork=%I, sync=%I
116 k_work_cancel_sync           dwork=%I, sync=%I
117 k_work_delayable_init        dwork=%I
118 k_work_queue_drain           queue=%I
119 k_work_flush                 work=%I
120 k_work_flush_delayable       dwork=%I, sync=%I
121 k_work_init                  work=%I
122 k_work_poll_cancel           work=%I
123 k_work_poll_init             work=%I
124 k_work_poll_submit           work=%I, Timeout=%TimeOut
125 k_work_poll_submit_to_queue  work_q=%I, work=%I, Timeout=%TimeOut
126 k_work_queue_start           queue=%I
127 k_work_reschedule            dwork=%I, Delay=%TimeOut | Returns %u
128 k_work_reschedule_for_queue  queue=%I, dwork=%I, Delay=%TimeOut | Returns %u
129 k_work_schedule              dwork=%I, Delay=%TimeOut | Returns %u
130 k_work_reschedule_for_queue  queue=%I, dwork=%I, Delay=%TimeOut | Returns %u
131 k_work_submit                work=%I | Returns %ErrCodePosix
132 k_work_submit_to_queue       queue=%I, work=%I | Returns %ErrCodePosix
133 k_work_queue_unplug          queue=%I | Returns %ErrCodePosix


142 k_fifo_init                  fifo=%I
143 k_fifo_cancel_wait           fifo=%I
144 k_fifo_alloc_put             fifo=%I, data=%I
145 k_fifo_put_list              fifo=%I
146 k_fifo_put_slist             fifo=%I
147 k_fifo_peak_head             fifo=%I
148 k_fifo_peak_tail             fifo=%I
149 k_fifo_put                   fifo=%I, data=%I
150 k_fifo_get                   fifo=%I, Timeout=%TimeOut


151 k_lifo_init                  lifo=%I
152 k_lifo_put                   lifo=%I
153 k_lifo_get                   lifo=%I, Timeout=%TimeOut
154 k_lifo_alloc_put             lifo=%I, data=%I


156 pm_system_suspend            ticks=%u | Returns %Bool
157 pm_device_runtime_get        dev=%I | Returns %u
158 pm_device_runtime_put        dev=%I | Returns %u
159 pm_device_runtime_put_async  dev=%I, Delay=%TimeOut | Returns %u
160 pm_device_runtime_enable     dev=%I | Returns %u
161 pm_device_runtime_disable    dev=%I | Returns %u

162 syscall                      name=%s

文件中说明了事件 ID 含义以及数据解析方式,将该文件覆盖到如下路径,windows 环境也是在 sysview 的安装路径下的Description文件夹中:

ekko@work: /opt/SEGGER/SystemView_V352a/Description
$ ls                                                                                                     
 SYSVIEW_FreeRTOS.txt             SYSVIEW_emFile.txt     SYSVIEW_embOSIP.txt         SYSVIEW_uCOS-II.txt
'SYSVIEW_Micrium OS Kernel.txt'   SYSVIEW_emFile_1.txt   SYSVIEW_embOSIP_30800.txt   SYSVIEW_uCOS-III.txt
 SYSVIEW_Zephyr.txt               SYSVIEW_emSSL.txt      SYSVIEW_embOSIP_32000.txt
 SYSVIEW_Zephyr.txt.bak           SYSVIEW_embOS.txt      SYSVIEW_embOS_Ultra.txt

新增自定义接口#

zephyr 已经实现的事件 ID 并不能够覆盖所有的测试场景,所以需要我们根据需要新增,新增的过程根据前面的内容可以分为几步。

1. 新增事件 ID#

subsys/tracing/sysview/tracing_sysview_ids.h新增事件 ID:

#define TID_PRINTK (131u + TID_OFFSET)

2. 新增函数接口#

subsys/tracing/sysview/tracing_sysview.h新增函数接口:

#define sys_port_trace_printk(thread_name)	\
	SEGGER_SYSVIEW_RecordString(TID_PRINTK, (const char *)thread_name)

3. 解析配置文件适配#

/opt/SEGGER/SystemView_V352a/Description/SYSVIEW_Zephyr.txt新增事件信息:

163 printk                       thread:%s

sysview 数据存储#

帧格式#

modules/debug/segger/SEGGER/SEGGER_SYSVIEW.c中有详细的描述:

File    : SEGGER_SYSVIEW.c
Purpose : System visualization API implementation.
Revision: $Rev: 28341 $

Additional information:
  Packet format:
    Packets with IDs 0..23 are standard packets with known structure.
    For efficiency, they do *NOT* contain a length field.
    <ID><Data><TimeStampDelta>

    Packets with IDs 24..31 are standard packets with extendible
    structure and contain a length field.
    <ID><Lenght><Data><TimeStampDelta>

    Packet ID 31 is used for SystemView extended events.
    <ID><Lenght><ID_EX><Data><TimeStampDelta>

    Packets with IDs >= 32 always contain a length field.
    <ID><Length><Data><TimeStampDelta>

  Packet IDs:
       0..  31 : Standard packets, known by SystemView.
      32..1023 : OS-definable packets, described in a SystemView description file.
    1024..2047 : User-definable packets, described in a SystemView description file.
    2048..32767: Undefined.

  Data encoding:
    Basic types (int, short, char, ...):
      Basic types are encoded little endian with most-significant bit variant
      encoding.
      Each encoded byte contains 7 data bits [6:0] and the MSB continuation bit.
      The continuation bit indicates whether the next byte belongs to the data
      (bit set) or this is the last byte (bit clear).
      The most significant bits of data are encoded first, proceeding to the
      least significant bits in the final byte (little endian).

      Example encoding:
        Data: 0x1F4 (500)
        Encoded: 0xF4 (First 7 data bits 74 | Continuation bit)
                 0x03 (Second 7 data bits 03, no continuation)

        Data: 0xFFFFFFFF
        Encoded: 0xFF 0xFF 0xFF 0xFF 0x0F

        Data: 0xA2 (162),   0x03 (3), 0x7000
        Encoded: 0xA2 0x01  0x03      0x80 0xE0 0x01

    Byte arrays and strings:
      Byte arrays and strings are encoded as <NumBytes> followed by the raw data.
      NumBytes is encoded as a basic type with a theoretical maximum of 4G.

      Example encoding:
        Data: "Hello World\0" (0x48 0x65 0x6C 0x6C 0x6F 0x20 0x57 0x6F 0x72 0x6C 0x64 0x00)
        Encoded: 0x0B 0x48 0x65 0x6C 0x6C 0x6F 0x20 0x57 0x6F 0x72 0x6C 0x64

  Examples packets:
  01 F4 03 80 80 10 // Overflow packet. Data is a single U32.
                       This packet means: 500 packets lost, Timestamp is 0x40000

  02 0F 50          // ISR(15) Enter. Timestamp 80 (0x50)

  03 20             // ISR Exit. Timestamp 32 (0x20) (Shortest possible packet.)

  Sample code for user defined Packets:
    #define MY_ID   0x400                // Any value between 0x400 and 0x7FF
    void SendMyPacket(unsigned Para0, unsigned Para1, const char* s) {
      U8  aPacket[SEGGER_SYSVIEW_INFO_SIZE + 2 * SEGGER_SYSVIEW_QUANTA_U32 + MAX_STR_LEN + 1];
      U8* pPayload;
      //
      pPayload = SEGGER_SYSVIEW_PPREPARE_PACKET(aPacket);               // Prepare the packet for SystemView
      pPayload = SEGGER_SYSVIEW_EncodeU32(pPayload, Para0);             // Add the first parameter to the packet
      pPayload = SEGGER_SYSVIEW_EncodeU32(pPayload, Para1);             // Add the second parameter to the packet
      pPayload = SEGGER_SYSVIEW_EncodeString(pPayload, s, MAX_STR_LEN); // Add the string to the packet
      //
      SEGGER_SYSVIEW_SendPacket(&aPacket[0], pPayload, MY_ID);          // Send the packet with EventId = MY_ID
    }

    #define MY_ID_1 0x401
    void SendOnePara(unsigned Para0) {
      SEGGER_SYSVIEW_RecordU32(MY_ID_1, Para0);
    }

sysview 可以查看原始数据:
Pasted image 20240613110945

RTT 控制块#

sysview 的数据实际存储在 RTT 控制块中:

//
// RTT control block which describes the number of buffers available
// as well as the configuration for each buffer
//
//
typedef struct {
  char                    acID[16];                                 // Initialized to "SEGGER RTT"
  int                     MaxNumUpBuffers;                          // Initialized to SEGGER_RTT_MAX_NUM_UP_BUFFERS (type. 2)
  int                     MaxNumDownBuffers;                        // Initialized to SEGGER_RTT_MAX_NUM_DOWN_BUFFERS (type. 2)
  SEGGER_RTT_BUFFER_UP    aUp[SEGGER_RTT_MAX_NUM_UP_BUFFERS];       // Up buffers, transferring information up from target via debug probe to host
  SEGGER_RTT_BUFFER_DOWN  aDown[SEGGER_RTT_MAX_NUM_DOWN_BUFFERS];   // Down buffers, transferring information down from host via debug probe to target
#if SEGGER_RTT__CB_PADDING
  unsigned char           aDummy[SEGGER_RTT__CB_PADDING];
#endif
} SEGGER_RTT_CB;

/*********************************************************************
*
*       Global data
*
**********************************************************************
*/
extern SEGGER_RTT_CB _SEGGER_RTT;

使用 RTT 功能时,jlink 会从内存扫描 RTT 控制块,扫描原理就是查找是否有SEGGER_RTT_CB结构体的前 24 个字节可以正常匹配。

//
// Description for a circular buffer (also called "ring buffer")
// which is used as up-buffer (T->H)
//
typedef struct {
  const     char*    sName;         // Optional name. Standard names so far are: "Terminal", "SysView", "J-Scope_t4i4"
            char*    pBuffer;       // Pointer to start of buffer
            unsigned SizeOfBuffer;  // Buffer size in bytes. Note that one byte is lost, as this implementation does not fill up the buffer in order to avoid the problem of being unable to distinguish between full and empty.
            unsigned WrOff;         // Position of next item to be written by either target.
  volatile  unsigned RdOff;         // Position of next item to be read by host. Must be volatile since it may be modified by host.
            unsigned Flags;         // Contains configuration flags. Flags[31:24] are used for validity check and must be zero. Flags[23:2] are reserved for future use. Flags[1:0] = RTT operating mode.
} SEGGER_RTT_BUFFER_UP;

sysview 默认使用序号为 1,也就是_SEGGER_RTT.aUp[1],我们在未连接 jlink 情况下也可以直接使用这个数据进行调试,官网中也有对应方法的描述:

SEGGER SystemView User Guide #Save_and_load_recordings

[!Without a J-Link or without SystemView the data can be read using following steps]

  • Halt the application in the debugger when the buffer is full or after recording has been done.
  • Get the SystemView RTT buffer address and the number of bytes used (Normally _SEGGER_RTT.aUp[1].pBuffer and _SEGGER_RTT.aUp[1].WrOff).
  • Save the number of bytes from the buffer to a file with .SVDat extension.
  • Open the file with the SystemView Application.

实现调试串口记录 sysview 事件#

只需要将 RTT 控制块中存储的记录数据保存为.SVDat格式的文件即可使用 sysview 打开。

为了方便使用调试串口传输文件,使用 base64 (3 个二进制数据转为 4 个 ascii 数据) 进行传输:

void printf_systemview()
{
	static char base64_buffer[22000];
	base64_encode(base64_buffer,sizeof(base64_buffer),&out_len,_SEGGER_RTT.aUp[1].pBuffer,((SEGGER_RTT_BUFFER_UP*)((char*)&_SEGGER_RTT.aUp[1] + SEGGER_RTT_UNCACHED_OFF))->WrOff);

	printf("base64 data : \n");
	k_msleep(10);
	for(int i = 0;i < out_len;i++)
	{
		printf("%c",base64_buffer[i]);
		k_msleep(1);
	}
	printf("\nend base64 data\n");
	return;
}

使用SEGGER_SYSVIEW_Start()开始记录,使用SEGGER_SYSVIEW_Stop()停止记录,zephry 配置文件打开CONFIG_SEGGER_SYSTEMVIEW_BOOT_ENABLE=y可以在系统启动时自动开始记录。

串口输出的 base64 字符串保存到ok3568.txt文件中,使用base64命令还原回二进制文件:

base64 -d ok3568.txt > ok3568.SVDat 

ok3568.SVDat 即可使用 sysview 打开:
Pasted image 20240613113823

sysview 多通道及多模式#

多通道#

sysview 软件本身支持 JLinkRTT,串口,网络三种方式连接,目前 zephyr 只支持 RTT 方式。
Pasted image 20240613114150

多模式#

连续记录#

运行时连续记录事件,需要使用 sysview 使用任一通道连接到目标板。

sysview 会一直读取与清理 RTT 控制块缓存。

单次记录#

程序中使用函数开始和停止记录,不依赖其他工具,可保存文件使用 sysview 打开

记录事件大小取决于 RTT 控制块的 buff 大小。

事后分析#

程序崩溃后分析崩溃前发生的事件。

可以连接 sysview,也可以不连接,主要原理是 RTT 控制块缓存覆盖刷新,保持包含最新的事件内容。

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.