本文博客链接:http://blog.csdn.net/bjr2016,作者:bjr2016,未经允许不得转载。
本节介绍如何将SEGGER SystemView集成到操作系统或中间件模块中,以记录其执行情况。
SEGGER SystemView可以集成在任何(实时)操作系统中,用于获取关于任务执行、OS内部活动(如调度程序)和OS API调用的信息。对于以下的RTOS,已经完成了这个集成,并且可以在盒子外使用。
要集成到其他操作系统中,请与操作系统分销商联系,或者按照本节中的说明进行集成。
本节中的示例是用来说明何时调用特定的SystemView函数的伪代码。为了让跟踪检测工具对这些函数进行通用集成,可以将这些函数集成为函数宏,或者通过可配置的跟踪API来集成。
集成到操作系统核心
为了能够记录任务执行和上下文切换,操作系统核心必须被检测用来在适当的核心函数中生成SystemView事件。
在大多数情况下,中断执行也是由操作系统处理的。这允许在输入和退出中断的情况下对操作系统进行检测,否则将在应用程序中对每个ISR进行处理。
操作操作系统核心的第三个方面是提供运行时信息,以便进行更详细的分析。该信息包括系统时间,允许SystemView显示相对于应用程序开始时的时间戳,而不是开始记录的时间,并且任务列表被SystemView使用,以显示任务名称、堆栈信息和按优先级排序任务。
SystemView可以记录系统和操作系统活动主要信息(如任务执行)的预定义系统事件集合。这些事件应该由操作系统在相应的函数中生成。
预定义的事件有:
事件 | 描述 | SystemView API |
---|---|---|
Task Create | 创建了新任务 | SEGGER_SYSVIEW_OnTaskCreate |
Task Start Ready | 任务被标记为准备好启动或者恢复执行 | SEGGER_SYSVIEW_OnTaskStartReady |
Task Start Exec | 任务被激活(启动或者恢复执行) | SEGGER_SYSVIEW_OnTaskStart |
Task Stop Ready | 任务被阻塞或暂停 | SEGGER_SYSVIEW_OnTaskStopReady |
Task Stop Exec | 任务终止 | SEGGER_SYSVIEW_OnTaskStopExec |
System Idle | 没有任务执行,系统进入空闲状态 | SEGGER_SYSVIEW_OnIdle |
任务创建事件发生在系统创建一个任务时。
在任务创建事件调用带有新任务的Id的SEGGER_SYSVIEW_OnTaskCreate()函数。此外,建议使用SEGGER_SYSVIEW_SendTaskInfo()记录新任务的任务信息。
示例
void OS_CreateTask(TaskFunc* pF, unsigned Prio, const char* sName, void* pStack) {
SEGGER_SYSVIEW_TASKINFO Info;
OS_TASK* pTask; // Pseudo struct to be replaced
[OS specific code ...]
SEGGER_SYSVIEW_OnTaskCreate((unsigned)pTask);
memset(&Info, 0, sizeof(Info));
//
// Fill elements with current task information
//
Info.TaskID = (U32)pTask;
Info.sName = pTask->Name;
Info.Prio = pTask->Priority;
Info.StackBase = (U32)pTask->pStack;
Info.StackSize = pTask->StackSize;
SEGGER_SYSVIEW_SendTaskInfo(&Info);
}
当任务的延迟时间结束,或者当任务等待的资源可用,或者当事件触发时,任务开始准备事件可以发生。
在使用Task Start Ready事件时,将调用带有已准备就绪的任务的Id的SEGGER_SYSVIEW_OnTaskStartReady()函数。
示例
int OS_HandleTick(void) {
int TaskReady = 0; // Pseudo variable indicating a task is ready
[OS specific code ...]
if (TaskReady) {
SEGGER_SYSVIEW_OnTaskStartReady((unsigned)pTask);
}
}
Task Start Exec事件发生发生在上下文即将被切换到激活任务。这通常是调度程序在有准备好的任务时完成的。
调用带有将要执行的任务的Id的SEGGER_SYSVIEW_OnTaskStartExec()函数,创建Task Start Exec事件。
示例
void OS_Switch(void) {
[OS specific code ...]
//
// If a task is activated
//
SEGGER_SYSVIEW_OnTaskStartExec((unsigned)pTask);
//
// Else no task activated, go into idle state
//
SEGGER_SYSVIEW_OnIdle()
}
任务被阻塞或暂停。
当任务被暂停或阻塞时, Task Stop Ready事件发生。例如因为它延迟了一个特定的时间,或者当它试图声明一个资源正在被另一个任务使用,或者当它等待事件发生时。当任务被暂停或阻塞时,调度器将激活另一个任务或进入空闲状态。
调用SEGGER_SYSVIEW_OnTaskStopReady()函数,并传入被阻塞的任务的Id,以及一个可以指示为什么任务被阻塞的理由,来创建Task Stop Ready事件。
示例
void OS_Delay(unsigned NumTicks) {
[OS specific code ...]
SEGGER_SYSVIEW_OnTaskStopReady(OS_Global.pCurrentTask, OS_CAUSE_WAITING);
}
任务终止。
当任务最终停止执行时,例如当它完成任务并终止执行时,Task Stop Exec事件发生。
在Task Stop Ready事件中,调用SEGGER_SYSVIEW_OnTaskStopExec()来记录当前任务的停止状态。
示例
void OS_TerminateTask(void) {
[OS specific code ...]
SEGGER_SYSVIEW_OnTaskStopExec();
}
没有任务执行时,系统进入空闲状态。
System Idle事件是当一个任务被暂停或停止时,而没有其他任务准备好时发生的。系统可以切换到一个空闲状态以节省电力,等待中断或任务准备就绪。
在某些操作系统中,空闲是由另一个任务处理的。在这种情况下,当空闲任务被激活时,建议记录系统空闲事件。
在SystemView中,空闲状态时间被显示为不是CPU负载。
在系统空闲事件中调用SEGGER_SYSVIEW_OnIdle()。
示例
void OS_Switch(void) {
[OS specific code ...]
//
// If a task is activated
//
SEGGER_SYSVIEW_OnTaskStartExec((unsigned)pTask);
//
// Else no task activated, go into idle state
//
SEGGER_SYSVIEW_OnIdle()
}
SystemView可以记录进入和退出中断服务例程(ISRs)。SystemView API为这些事件提供了函数,当它提供用于标记中断执行的函数时,该函数应该添加到操作系统中。
当OS调度程序被中断控制时,例如SysTick中断,退出中断事件应该区分恢复正常执行还是切换到调度器,并调用适当的SystemView函数。
当操作系统提供一个函数来通知OS,中断代码正在执行时,在中断服务函数(ISR)开始时调用,OS函数应该调用SEGGER_SYSVIEW_RecordEnterISR()来记录进入中断事件。
当操作系统没有提供进入中断函数,或者ISR没有调用它时,用户有必要调用SEGGER_SYSVIEW_RecordEnterISR()来记录中断执行。
segger_sysview_conf. h中的SEGGER_SYSVIEW_RecordEnterISR()函数通过SEGGER_SYSVIEW_GET_INTERRUPT_ID()函数宏自动检索中断ID。
示例
void OS_EnterInterrupt(void) {
[OS specific code ...]
SEGGER_SYSVIEW_RecordEnterISR();
}
当操作系统提供一个函数来通知OS ,中断代码已经执行完时,该函数应该在中断服务函数结尾调用,OS函数应该调用:
示例
void OS_ExitInterrupt(void) {
[OS specific code ...]
//
// If the interrupt will switch to the Scheduler
//
SEGGER_SYSVIEW_RecordExitISRToScheduler();
//
// Otherwise
//
SEGGER_SYSVIEW_RecordExitISR();
}
下面的两个例子展示了如何用OS中断处理和不使用OS中断来记录中断执行。
使用OS处理的例子
void Timer_Handler(void) {
//
// Inform OS about start of interrupt execution
// (records SystemView Enter Interrupt event).
//
OS_EnterInterrupt();
//
// Interrupt functionality could be here
//
APP_TimerCnt++;
//
// Inform OS about end of interrupt execution
// (records SystemView Exit Interrupt event).
//
OS_ExitInterrupt();
}
不使用OS处理的例子
void ADC_Handler(void) {
//
// Explicitly record SystemView Enter Interrupt event.
// Should not be called in high-frequency interrupts.
//
SEGGER_SYSVIEW_RecordEnterISR();
//
// Interrupt functionality could be here
//
APP_ADCValue = ADC.Value;
//
// Explicitly record SystemView Exit Interrupt event.
// Should not be called in high-frequency interrupts.
//
SEGGER_SYSVIEW_RecordExitISR();
}
SystemView可以记录更详细的运行时的信息,如系统时间和任务信息。当SystemView运行时,记录开始并周期性地请求这些信息时,记录这些信息。
为了请求信息,在初始化时可以将带有操作系统特定函数的SEGGER_SYSVIEW_OS_API结构传递给SystemView。
SEGGER_SYSVIEW_OS_API设置是可选的,但建议允许SystemView显示更详细的信息。
SEGGER_SYSVIEW_OS_API
typedef struct {
U64 (*pfGetTime) (void);
void (*pfSendTaskList) (void);
} SEGGER_SYSVIEW_OS_API;
参数
参数 | 描述 |
---|---|
pfGetTime | 指向返回系统时间的函数 |
pfSendTaskList | 指向记录整个任务列表的函数 |
描述
获取系统时间,也就是自系统启动后经过的时间,单位us。
如果pfGetTime为空,SystemView可以显示相对于记录开始时的时间戳。
原型
U64 (*pfGetTime) (void);
描述
通过SEGGER_SYSVIEW_SendTaskInfo()记录整个任务列表。
如果pfSendTaskList为空,SystemView可能只会获取在记录时新创建任务的任务信息。当SystemView连接到当前任务列表时,将定期调用pfSendTaskList。
原型
void (*pfSendTaskList) (void);
例子
void cbSendTaskList(void) {
SEGGER_SYSVIEW_TASKINFO Info;
OS_TASK* pTask;
OS_EnterRegion(); // Disable scheduling to make sure the task list does not change.
for (pTask = OS_Global.pTask; pTask; pTask = pTask->pNext) {
//
// Fill all elements with 0 to allow extending the structure
// in future version without breaking the code.
//
memset(&Info, 0, sizeof(Info));
//
// Fill elements with current task information
//
Info.TaskID = (U32)pTask;
Info.sName = pTask->Name;
Info.Prio = pTask->Priority;
Info.StackBase = (U32)pTask->pStackBot;
Info.StackSize = pTask->StackSize;
//
// Record current task information
//
SEGGER_SYSVIEW_SendTaskInfo(&Info);
}
OS_LeaveRegion(); // Enable scheduling again.
}
除了操作系统核心工具之外,SystemView还可以记录应用程序中的OS API调用。API函数可以像操作系统的核心一样被检测。
当传递简单参数时,或使用适当的SEGGER_SYSVIEW_EncodeXXX()函数创建SystemView事件并调用SEGGER_SYSVIEW_SendPacket()来记录它时,可以使用现成的SEGGER_SYSVIEW_RecordXXX()函数来完成记录API事件。
示例
/*********************************************************************
*
* OS_malloc()
*
* Function description
* API function to allocate memory on the heap.
*/
void OS_malloc(unsigned Size) {
SEGGER_SYSVIEW_RecordU32(ID_OS_MALLOC, // Id of OS_malloc (>= 32)
Size // First parameter
);
[OS specific code...]
}
为了记录API函数执行时间并记录其返回值的时间,API函数的返回也可以通过调用SEGGER_SYSVIEW_RecordEndCall来只记录返回值或者调用SEGGER_SYSVIEW_RecordEndCallReturnValue来记录退出事件以及返回值。
为了使SystemView正确识别API调用,它需要在SystemView的 /description/ 目录中显示一个描述文件。文件的名称必须是SYSVIEW_<操作系统名>.txt,其中<操作系统名>是系统描述中发送的名称。
描述文件包括所有可以由操作系统记录的API函数。文件中的每一行都是一个函数,格式如下:
|
EventID是为API函数记录的Id。取值范围:32~511。
FunctionName是API函数的名称,显示在SystemView的事件列中。它可以不包含空格。
ParameterDescription是用API函数记录的参数的描述字符串。
ReturnValueDescription是返回值的描述字符串,可以用SystemView进行记录。ReturnValueDescription是可选的。
参数显示可以通过一组修饰符来配置:
例子
下面的例子显示了SYSVIEW_embOS.txt的部分内容
35 OS_CheckTimer pGlobal=%p
42 OS_Delay Delay=%u
43 OS_DelayUntil Time=%u
44 OS_setPriority Task=%t Pri=%u
45 OS_WakeTask Task=%t
46 OS_CreateTask Task=%t Pri=%u Stack=%p Size=%u
除了默认的修饰符,描述文件还可以定义NamedTypes来将数值映射到字符串,例如,可以用来显示枚举的文本值或错误代码。
NamedTypes有以下格式:
NamedType = [= ...]
NamedTypes可以在参数描述和返回值描述中使用。
示例
#
# Types for parameter formatters
#
NamedType OSErr 0=OS_ERR_NONE
NamedType OSErr 10000=OS_ERR_A 10001=OS_ERR_ACCEPT_ISR
NamedType OSErr 12000=OS_ERR_C 12001=OS_ERR_CREATE_ISR
NamedType OSErr 13000=OS_ERR_D 13001=OS_ERR_DEL_ISR
NamedType OSFlag 0=FLAG_NONE 1=FLAG_READ 2=FLAG_WRITE 3=FLAG_READ_WRITE
#
# API Functions
#
34 OSFunc Param=%OSFlag | Returns %OSErr
当一个任务暂停执行时,它的状态被记录在SystemView事件中。
这个任务状态可以被转换为SystemView中的文本表示,并使用任务状态解析。
TaskState以下格式:
TaskState =, [=, ...]
示例
#
# Task States
#
TaskState 0xFF 0=Ready, 1=Delayed or Timeout, 2=Pending, 3=Pending with Timeout,
4=Suspended, 5=Suspended with Timeout, 6=Suspended and Pending, 7=Suspended and
Pending with Timeout, 255=Deleted
在描述文件中也可以设置操作系统特定选项来配置SystemView。
目前可以在描述文件中插入的选项为:
Option ReversePriority:更高的任务优先级值等于较低的任务优先级。
下面的代码展示了如何在基于伪代码片段的操作系统中集成SystemView,并可作为参考使用。
/*********************************************************************
* (c) SEGGER Microcontroller GmbH & Co. KG *
* The Embedded Experts *
* www.segger.com *
**********************************************************************
-------------------------- END-OF-HEADER -----------------------------
Purpose : Pseudo-code OS with SEGGER SystemView integration.
*/
/*********************************************************************
*
* OS_CreateTask()
*
* Function description
* Create a new task and add it to the system.
*/
void OS_CreateTask (TaskFunc* pF, unsigned Prio, const char* sName, void* pStack) {
SEGGER_SYSVIEW_TASKINFO Info;
OS_TASK* pTask; // Pseudo struct to be replaced
[OS specific code ...]
SEGGER_SYSVIEW_OnTaskCreate ((unsigned)pTask);
memset (&Info, 0, sizeof(Info));
//
// Fill elements with current task information
//
Info.TaskID = (U32)pTask;
Info.sName = pTask->Name;
Info.Prio = pTask->Priority;
Info.StackBase = (U32)pTask->pStack;
Info.StackSize = pTask->StackSize;
SEGGER_SYSVIEW_SendTaskInfo (&Info);
}
/*********************************************************************
*
* OS_TerminateTask()
*
* Function description
* Terminate a task and remove it from the system.
*/
void OS_TerminateTask (void) {
[OS specific code ...]
SEGGER_SYSVIEW_OnTaskStopExec ();
}
/*********************************************************************
*
* OS_Delay()
*
* Function description
* Delay and suspend a task for the given time.
*/
void OS_Delay (unsigned NumTicks) {
[OS specific code ...]
SEGGER_SYSVIEW_OnTaskStopReady (OS_Global.pCurrentTask, OS_CAUSE_WAITING);
}
/*********************************************************************
*
* OS_HandleTick()
*
* Function description
* OS System Tick handler.
*/
int OS_HandleTick (void) {
int TaskReady = 0; // Pseudo variable indicating a task is ready
[OS specific code ...]
if (TaskReady) {
SEGGER_SYSVIEW_OnTaskStartReady ((unsigned)pTask);
}
}
/*********************************************************************
*
* OS_Switch()
*
* Function description
* Switch to the next ready task or go to idle.
*/
void OS_Switch (void) {
[OS specific code ...]
//
// If a task is activated
//
SEGGER_SYSVIEW_OnTaskStartExec ((unsigned)pTask);
//
// Else no task activated, go into idle state
//
SEGGER_SYSVIEW_OnIdle ()
}
/*********************************************************************
*
* OS_EnterInterrupt()
*
* Function description
* Inform the OS about start of interrupt execution.
*/
void OS_EnterInterrupt (void) {
[OS specific code ...]
SEGGER_SYSVIEW_RecordEnterISR ();
}
/*********************************************************************
*
* OS_ExitInterrupt()
*
* Function description
* Inform the OS about end of interrupt execution and switch to
* Scheduler if necessary.
*/
void OS_ExitInterrupt (void) {
[OS specific code ...]
//
// If the interrupt will switch to the Scheduler
//
SEGGER_SYSVIEW_RecordExitISRToScheduler ();
//
// Otherwise
//
SEGGER_SYSVIEW_RecordExitISR ();
}
SEGGER SystemView还可以集成到中间件模块,甚至是应用程序模块中,以获取关于这些模块的执行的信息,比如API调用或中断触发事件。此集成用于在SEGGER embOS/IP中使用,以监视通过IP和SEGGER emFile发送和接收数据包,以记录API调用。
要集成到其他模块,请与您的分销商联系,或者按照本节中的说明进行集成。
为了能够记录中间件模块事件,模块必须通过SEGGER_SYSVIEW_RegisterModule()在SystemView进行注册。
该模块传递一个SEGGER_SYSVIEW_MODULE 结构体指针,该指针包含有关模块的信息,并接收模块可以生成的事件id的事件偏移量。
在注册时,必须在SEGGER_SYSVIEW_MODULE结构中设置sDescription和NumEvents。也可以设置pfSendModuleDesc。
当SEGGER_SYSVIEW_RegisterModule()返回时,SEGGER_SYSVIEW_MODULE结构中的EventOffset被设置为模块可能生成的最低的事件Id,而pNext将指向下一个注册模块,以创建一个链表。因此,SEGGER_SYSVIEW_MODULE结构必须是可写的,并且可能不会在堆栈上分配。
SEGGER_SYSVIEW_MODULE
struct SEGGER_SYSVIEW_MODULE {
const char* sModule;
U32 NumEvents;
U32 EventOffset;
void (*pfSendModuleDesc)(void);
SEGGER_SYSVIEW_MODULE* pNext;
};
参数
参数 | 描述 |
---|---|
sModule | 指向包含模块名称和模块事件描述的字符串的指针。 |
NumEvents | 模块需要注册的事件数 |
EventOffset | 事件id的偏移量。输出参数,由这个函数设置。调用此函数后不修改 |
pfSendModuleDesc | 回调函数指针,向SystemView发送更详细的模块描述。 |
pNext | 指向下一个注册模块的指针。输出参数,由这个函数设置。调用此函数后不修改 |
示例
SEGGER_SYSVIEW_MODULE IPModule = {
"M=embOSIP, " \
"0 SendPacket IFace=%u NumBytes=%u, " \
"1 ReceivePacket Iface=%d NumBytes=%u", // sModule
2, // NumEvents
0,
// EventOffset, Set by SEGGER_SYSVIEW_RegisterModule()
NULL,
// pfSendModuleDesc, NULL: No additional module description
NULL,
// pNext, Set by SEGGER_SYSVIEW_RegisterModule()
};
static void _IPTraceConfig (void) {
//
// Register embOS/IP at SystemView.
// SystemView has to be initialized before.
//
SEGGER_SYSVIEW_RegisterModule (&IPModule);
}
为了能够记录模块的活动,模块必须被检测以在适当的函数中生成SystemView事件。
通过对SystemView函数进行直接集成,可以通过可配置的宏函数或API结构来实现对模块的检测,这些功能可以通过SystemView来填充和设置。
当传递简单参数时,或使用适当的SEGGER_SYSVIEW_EncodeXXX()函数创建SystemView事件,并调用SEGGER_SYSVIEW_SendPacket()来记录该事件时,可以使用随时可用的SEGGER_SYSVIEW_RecordXXX()函数来完成记录事件。
示例
int SendPacket (IP_PACKET *pPacket) {
//
// The IP stack sends a packet.
// Record it according to the module description of SendPacket.
//
SEGGER_SYSVIEW_RecordU32x2 (
// Id of SendPacket (0) + Offset for the registered module
ID_SENDPACKET + IPModule.EventOffset,
// First parameter (displayed as event parameter IFace)
pPacket->Interface,
// Second parameter (displayed as event parameter NumBytes)
pPacket->NumBytes
);
[Module specific code...]
}
有关更多信息,请参阅第108页上的OS API调用和第117页的API引用。
与操作系统一样,可以在描述文件中使用模块的名称(M=的值)提供中间件模块描述。参见第108页的OS描述文件。
SEGGER_SYSVIEW_MODULE.sModule指向一个包含注册模块的基本信息的字符串,该模块是一个逗号分隔的列表,可以包含以下内容:
项 | 标识 | 举例 |
---|---|---|
模块名称 | M | “M=embOSIP” |
模块令牌 | T | “T=IP” |
描述 | S | “S=’embOS/IP V12.09’” |
模块事件 | “0 SendPacket IFace=%u NumBytes=%u” |
字符串长度不能超过SEGGER_SYSVIEW_MAX_STRING_LEN,默认情况下是128。
要发送额外的描述字符串,并发送由模块所使用和记录的资源的名称,在注册模块时可以设置SEGGER_SYSVIEW_MODULE.fSendModuleDesc。
SEGGER_SYSVIEW_MODULE.pfSendModuleDesc在SystemView连接时被周期性调用。它可以调用SEGGER_SYSVIEW_RecordModuleDescription()和SEGGER_SYSVIEW_NameResource()。
示例
static void _cbSendIPModuleDesc (void) {
SEGGER_SYSVIEW_NameResource ((U32)&(RxPacketFifo), "Rx FIFO");
SEGGER_SYSVIEW_NameResource ((U32)&(TxPacketFifo), "Tx FIFO");
SEGGER_SYSVIEW_RecordModuleDescription (&IPModule, "T=IP, S='embOS/IP V12.09'");
}
SEGGER_SYSVIEW_MODULE IPModule = {
"M=embOSIP, " \
"0 SendPacket IFace=%u NumBytes=%u, " \
"1 ReceivePacket Iface=%d NumBytes=%u", // sModule
2, // NumEvents
0, // EventOffset, Set by RegisterModule()
_cbSendIPModuleDesc, // pfSendModuleDesc
NULL, // pNext, Set by RegisterModule()
};