nuttx/drivers/segger/note_sysview.c
xuxingliang f3d6119282 sched/note: add wdog note for segger sysview
Signed-off-by: xuxingliang <xuxingliang@xiaomi.com>
2024-10-25 19:04:02 +08:00

486 lines
14 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];
#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;
}