/**************************************************************************** * drivers/segger/note_sysview.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include "sched/sched.h" /**************************************************************************** * Private Types ****************************************************************************/ struct note_sysview_driver_s { struct note_driver_s driver; unsigned int irq[CONFIG_SMP_NCPUS]; #ifdef CONFIG_SCHED_INSTRUMENTATION_SYSCALL struct note_filter_syscall_s syscall_marker; #endif }; /**************************************************************************** * Private Function Prototypes ****************************************************************************/ static void note_sysview_start(FAR struct note_driver_s *drv, FAR struct tcb_s *tcb); static void note_sysview_stop(FAR struct note_driver_s *drv, FAR struct tcb_s *tcb); #ifdef CONFIG_SCHED_INSTRUMENTATION_SWITCH static void note_sysview_suspend(FAR struct note_driver_s *drv, FAR struct tcb_s *tcb); static void note_sysview_resume(FAR struct note_driver_s *drv, FAR struct tcb_s *tcb); #endif #ifdef CONFIG_SCHED_INSTRUMENTATION_IRQHANDLER static void note_sysview_irqhandler(FAR struct note_driver_s *drv, int irq, FAR void *handler, bool enter); #endif #ifdef CONFIG_SCHED_INSTRUMENTATION_SYSCALL static void note_sysview_syscall_enter(FAR struct note_driver_s *drv, int nr, int argc, va_list *ap); static void note_sysview_syscall_leave(FAR struct note_driver_s *drv, int nr, uintptr_t result); #endif #ifdef CONFIG_SCHED_INSTRUMENTATION_HEAP static void note_sysview_heap(FAR struct note_driver_s *drv, uint8_t event, FAR void *heap, FAR void *mem, size_t size, size_t curused); #endif #ifdef CONFIG_SCHED_INSTRUMENTATION_WDOG static void note_sysview_wdog(FAR struct note_driver_s *drv, uint8_t event, FAR void *handler, FAR const void *arg); #endif /**************************************************************************** * Private Data ****************************************************************************/ static const struct note_driver_ops_s g_note_sysview_ops = { NULL, /* add */ note_sysview_start, /* start */ note_sysview_stop, /* stop */ #ifdef CONFIG_SCHED_INSTRUMENTATION_SWITCH note_sysview_suspend, /* suspend */ note_sysview_resume, /* resume */ #endif #ifdef CONFIG_SMP NULL, /* cpu_start */ NULL, /* cpu_started */ # ifdef CONFIG_SCHED_INSTRUMENTATION_SWITCH NULL, /* cpu_pause */ NULL, /* cpu_paused */ NULL, /* cpu_resume */ NULL, /* cpu_resumed */ # endif #endif #ifdef CONFIG_SCHED_INSTRUMENTATION_PREEMPTION NULL, /* premption */ #endif #ifdef CONFIG_SCHED_INSTRUMENTATION_CSECTION NULL, /* csection */ #endif #ifdef CONFIG_SCHED_INSTRUMENTATION_SPINLOCKS NULL, /* spinlock */ #endif #ifdef CONFIG_SCHED_INSTRUMENTATION_SYSCALL note_sysview_syscall_enter, /* syscall_enter */ note_sysview_syscall_leave, /* syscall_leave */ #endif #ifdef CONFIG_SCHED_INSTRUMENTATION_IRQHANDLER note_sysview_irqhandler, /* irqhandler */ #endif #ifdef CONFIG_SCHED_INSTRUMENTATION_WDOG note_sysview_wdog, /* wdog */ #endif #ifdef CONFIG_SCHED_INSTRUMENTATION_HEAP note_sysview_heap, /* heap */ #endif }; static struct note_sysview_driver_s g_note_sysview_driver = { { &g_note_sysview_ops } }; /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: note_sysview_send_taskinfo ****************************************************************************/ static void note_sysview_send_taskinfo(FAR struct tcb_s *tcb) { SEGGER_SYSVIEW_TASKINFO info; info.TaskID = tcb->pid; info.sName = get_task_name(tcb); info.Prio = tcb->sched_priority; info.StackBase = (uintptr_t)tcb->stack_base_ptr; info.StackSize = tcb->adj_stack_size; SEGGER_SYSVIEW_SendTaskInfo(&info); } /**************************************************************************** * Name: note_sysview_get_time ****************************************************************************/ static uint64_t note_sysview_get_time(void) { return TICK2USEC(clock_systime_ticks()); } /**************************************************************************** * Name: note_sysview_send_tasklist ****************************************************************************/ static void note_sysview_send_tasklist(void) { int i; for (i = 0; i < g_npidhash; i++) { if (g_pidhash[i] != NULL) { note_sysview_send_taskinfo(g_pidhash[i]); } } } /**************************************************************************** * Name: note_sysview_send_description ****************************************************************************/ static void note_sysview_send_description(void) { SEGGER_SYSVIEW_SendSysDesc("N="SEGGER_SYSVIEW_APP_NAME); SEGGER_SYSVIEW_SendSysDesc("D="CONFIG_LIBC_HOSTNAME); SEGGER_SYSVIEW_SendSysDesc("O=NuttX"); } /**************************************************************************** * Name: note_sysview_* * * Description: * Hooks to scheduler monitor * * Input Parameters: * Varies * * Returned Value: * None * ****************************************************************************/ static void note_sysview_start(FAR struct note_driver_s *drv, FAR struct tcb_s *tcb) { SEGGER_SYSVIEW_OnTaskCreate(tcb->pid); note_sysview_send_taskinfo(tcb); } static void note_sysview_stop(FAR struct note_driver_s *drv, FAR struct tcb_s *tcb) { SEGGER_SYSVIEW_OnTaskTerminate(tcb->pid); } #ifdef CONFIG_SCHED_INSTRUMENTATION_SWITCH static void note_sysview_suspend(FAR struct note_driver_s *drv, FAR struct tcb_s *tcb) { if (!up_interrupt_context()) { SEGGER_SYSVIEW_OnTaskStopExec(); } } static void note_sysview_resume(FAR struct note_driver_s *drv, FAR struct tcb_s *tcb) { if (!up_interrupt_context()) { if (is_idle_task(tcb)) { SEGGER_SYSVIEW_OnIdle(); } else { SEGGER_SYSVIEW_OnTaskStartExec(tcb->pid); } } } #endif #ifdef CONFIG_SCHED_INSTRUMENTATION_IRQHANDLER static void note_sysview_irqhandler(FAR struct note_driver_s *drv, int irq, FAR void *handler, bool enter) { FAR struct note_sysview_driver_s *driver = (FAR struct note_sysview_driver_s *)drv; if (enter) { driver->irq[this_cpu()] = irq; SEGGER_SYSVIEW_OnTaskStopExec(); SEGGER_SYSVIEW_RecordEnterISR(); } else { SEGGER_SYSVIEW_RecordExitISR(); if (up_interrupt_context()) { FAR struct tcb_s *tcb = this_task(); if (tcb && !is_idle_task(tcb)) { SEGGER_SYSVIEW_OnTaskStartExec(tcb->pid); } else { SEGGER_SYSVIEW_OnIdle(); } } driver->irq[this_cpu()] = 0; } } #endif #ifdef CONFIG_SCHED_INSTRUMENTATION_SYSCALL static void note_sysview_syscall_enter(FAR struct note_driver_s *drv, int nr, int argc, va_list *ap) { FAR struct note_sysview_driver_s *driver = (FAR struct note_sysview_driver_s *)drv; nr -= CONFIG_SYS_RESERVED; /* Set the name marker if the current syscall nr is not active */ if (NOTE_FILTER_SYSCALLMASK_ISSET(nr, &driver->syscall_marker) == 0) { /* Set the name marker */ SEGGER_SYSVIEW_NameMarker(nr, g_funcnames[nr]); /* Mark the syscall active */ NOTE_FILTER_SYSCALLMASK_SET(nr, &driver->syscall_marker); /* Use the Syscall "0" to identify whether the syscall is enabled, * if the host tool is closed abnormally, use this bit to clear * the active set. */ if (NOTE_FILTER_SYSCALLMASK_ISSET(0, &driver->syscall_marker) == 0) { NOTE_FILTER_SYSCALLMASK_SET(0, &driver->syscall_marker); } } SEGGER_SYSVIEW_MarkStart(nr); } static void note_sysview_syscall_leave(FAR struct note_driver_s *drv, int nr, uintptr_t result) { FAR struct note_sysview_driver_s *driver = (FAR struct note_sysview_driver_s *)drv; nr -= CONFIG_SYS_RESERVED; if (NOTE_FILTER_SYSCALLMASK_ISSET(nr, &driver->syscall_marker) != 0) { SEGGER_SYSVIEW_MarkStop(nr); } } #endif #ifdef CONFIG_SCHED_INSTRUMENTATION_HEAP static void note_sysview_heap(FAR struct note_driver_s *drv, uint8_t event, FAR void *heap, FAR void *mem, size_t size, size_t curused) { switch (event) { case NOTE_HEAP_ALLOC: case NOTE_HEAP_FREE: { U32 value = (U32)curused; const SEGGER_SYSVIEW_DATA_SAMPLE data = { .ID = (U32)(uintptr_t)heap, .pU32_Value = &value, }; SEGGER_SYSVIEW_SampleData(&data); if (event == NOTE_HEAP_ALLOC) { SEGGER_SYSVIEW_HeapAlloc(heap, mem, size); } else { SEGGER_SYSVIEW_HeapFree(heap, mem); } break; } case NOTE_HEAP_ADD: { char name[32]; SEGGER_SYSVIEW_DATA_REGISTER data = { .ID = (U32)(uintptr_t)heap, .DataType = SEGGER_SYSVIEW_TYPE_U32, .Offset = 0, .RangeMin = 0, .RangeMax = 0, .ScalingFactor = 1.f, .sUnit = "B", .sName = name, }; snprintf(name, sizeof(name), "Heap%p", heap); SEGGER_SYSVIEW_RegisterData(&data); SEGGER_SYSVIEW_HeapDefine(heap, mem, size, 0); break; } default: break; } } #endif #ifdef CONFIG_SCHED_INSTRUMENTATION_WDOG static void note_sysview_wdog(FAR struct note_driver_s *drv, uint8_t event, FAR void *handler, FAR const void *arg) { if (event == NOTE_WDOG_ENTER) { SEGGER_SYSVIEW_RecordEnterTimer((uintptr_t)handler); } else if (event == NOTE_WDOG_LEAVE) { SEGGER_SYSVIEW_RecordExitTimer(); } } #endif /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: note_sysview_get_interrupt_id * * Description: * Retrieve the Id of the currently active interrupt. * ****************************************************************************/ unsigned int note_sysview_get_interrupt_id(void) { return g_note_sysview_driver.irq[this_cpu()]; } /**************************************************************************** * Name: note_sysview_get_timestamp * * Description: * Retrieve a system timestamp for SYSVIEW events. * ****************************************************************************/ unsigned long note_sysview_get_timestamp(void) { return perf_gettime(); } /**************************************************************************** * Name: note_sysview_initialize * * Description: * Initializes the SYSVIEW module. * * Input Parameters: * None. * * Returned Value: * Zero on succress. A negated errno value is returned on a failure. * ****************************************************************************/ int note_sysview_initialize(void) { unsigned long freq = perf_getfreq(); int ret; static const SEGGER_SYSVIEW_OS_API g_sysview_trace_api = { note_sysview_get_time, note_sysview_send_tasklist, }; if (freq == 0) { syslog(LOG_ERR, "up_perf isn't initialized, sysview isn't available"); PANIC(); } SEGGER_SYSVIEW_Init(freq, freq, &g_sysview_trace_api, note_sysview_send_description); #if CONFIG_SEGGER_SYSVIEW_RAM_BASE != 0 SEGGER_SYSVIEW_SetRAMBase(CONFIG_SEGGER_SYSVIEW_RAM_BASE); #endif SEGGER_SYSVIEW_Start(); ret = note_driver_register(&g_note_sysview_driver.driver); syslog(LOG_NOTICE, "SEGGER RTT Control Block Address: %#" PRIxPTR "\n", (uintptr_t)&_SEGGER_RTT + CONFIG_SEGGER_RTT_UNCACHED_OFF); return ret; }