502 lines
15 KiB
C
502 lines
15 KiB
C
/****************************************************************************
|
|
* 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 <stddef.h>
|
|
#include <stdio.h>
|
|
#include <syslog.h>
|
|
|
|
#include <nuttx/clock.h>
|
|
#include <nuttx/sched.h>
|
|
#include <nuttx/sched_note.h>
|
|
#include <nuttx/note/note_driver.h>
|
|
#include <nuttx/segger/sysview.h>
|
|
|
|
#include <SEGGER_RTT.h>
|
|
#include <SEGGER_SYSVIEW.h>
|
|
|
|
#include "sched/sched.h"
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
struct note_sysview_driver_s
|
|
{
|
|
struct note_driver_s driver;
|
|
unsigned int irq[CONFIG_SMP_NCPUS];
|
|
};
|
|
|
|
/****************************************************************************
|
|
* 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
|
|
|
|
#ifdef CONFIG_SCHED_INSTRUMENTATION_DUMP
|
|
static void note_sysview_vprintf(FAR struct note_driver_s *drv, uintptr_t ip,
|
|
FAR const char *fmt, va_list va);
|
|
#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
|
|
|
|
#ifdef CONFIG_SCHED_INSTRUMENTATION_DUMP
|
|
NULL, /* event */
|
|
note_sysview_vprintf, /* vprintf */
|
|
#endif
|
|
};
|
|
|
|
static struct note_sysview_driver_s g_note_sysview_driver =
|
|
{
|
|
{
|
|
#ifdef CONFIG_SCHED_INSTRUMENTATION_FILTER
|
|
"sysview",
|
|
{
|
|
{
|
|
CONFIG_SCHED_INSTRUMENTATION_FILTER_DEFAULT_MODE,
|
|
# ifdef CONFIG_SMP
|
|
CONFIG_SCHED_INSTRUMENTATION_CPUSET
|
|
# endif
|
|
},
|
|
},
|
|
#endif
|
|
&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->driver.filter.syscall_mask) == 0)
|
|
{
|
|
/* Set the name marker */
|
|
|
|
SEGGER_SYSVIEW_NameMarker(nr, g_funcnames[nr]);
|
|
|
|
/* Mark the syscall active */
|
|
|
|
NOTE_FILTER_SYSCALLMASK_SET(nr, &driver->driver.filter.syscall_mask);
|
|
|
|
/* 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->driver.filter.syscall_mask) == 0)
|
|
{
|
|
NOTE_FILTER_SYSCALLMASK_SET(0,
|
|
&driver->driver.filter.syscall_mask);
|
|
}
|
|
}
|
|
|
|
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->driver.filter.syscall_mask))
|
|
{
|
|
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
|
|
|
|
#ifdef CONFIG_SCHED_INSTRUMENTATION_DUMP
|
|
static void note_sysview_vprintf(FAR struct note_driver_s *drv, uintptr_t ip,
|
|
FAR const char *fmt, va_list va)
|
|
{
|
|
SEGGER_SYSVIEW_VPrintfHost(fmt, &va);
|
|
}
|
|
#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_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;
|
|
}
|