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_in
和sys_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 可以查看原始数据:
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 打开:
sysview 多通道及多模式#
多通道#
sysview 软件本身支持 JLinkRTT,串口,网络三种方式连接,目前 zephyr 只支持 RTT 方式。
多模式#
连续记录#
运行时连续记录事件,需要使用 sysview 使用任一通道连接到目标板。
sysview 会一直读取与清理 RTT 控制块缓存。
单次记录#
程序中使用函数开始和停止记录,不依赖其他工具,可保存文件使用 sysview 打开
记录事件大小取决于 RTT 控制块的 buff 大小。
事后分析#
程序崩溃后分析崩溃前发生的事件。
可以连接 sysview,也可以不连接,主要原理是 RTT 控制块缓存覆盖刷新,保持包含最新的事件内容。