nuttx/drivers/segger/note_sysview.c
zhangyuan21 18266c1012 nuttx/sched: use pid to check idle task
Pid is more appropriate than the flink pointer to determine idle task,
when we want to use other data structure to optimize the task list.
2022-10-31 17:53:08 +09:00

653 lines
16 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 <nuttx/config.h>
#include <syslog.h>
#include <nuttx/clock.h>
#include <nuttx/sched.h>
#include <nuttx/sched_note.h>
#include <SEGGER_RTT.h>
#include <SEGGER_SYSVIEW.h>
#include "sched/sched.h"
/****************************************************************************
* Private Types
****************************************************************************/
struct sysview_s
{
unsigned int irq[CONFIG_SMP_NCPUS];
#ifdef CONFIG_SCHED_INSTRUMENTATION_FILTER
struct note_filter_mode_s mode;
#endif
#ifdef CONFIG_SCHED_INSTRUMENTATION_IRQHANDLER
struct note_filter_irq_s irq_mask;
#endif
#ifdef CONFIG_SCHED_INSTRUMENTATION_SYSCALL
struct note_filter_syscall_s syscall_mask;
struct note_filter_syscall_s syscall_marker;
#endif
};
/****************************************************************************
* Private Data
****************************************************************************/
static struct sysview_s g_sysview =
{
#ifdef CONFIG_SCHED_INSTRUMENTATION_FILTER
.mode =
{
.flag = CONFIG_SCHED_INSTRUMENTATION_FILTER_DEFAULT_MODE,
#ifdef CONFIG_SMP
.cpuset = CONFIG_SCHED_INSTRUMENTATION_CPUSET,
#endif
}
#endif
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: sysview_send_taskinfo
****************************************************************************/
static void sysview_send_taskinfo(FAR struct tcb_s *tcb)
{
SEGGER_SYSVIEW_TASKINFO info;
info.TaskID = tcb->pid;
#if CONFIG_TASK_NAME_SIZE > 0
info.sName = tcb->name;
#else
info.sName = "<noname>";
#endif
info.Prio = tcb->sched_priority;
info.StackBase = (uintptr_t)tcb->stack_base_ptr;
info.StackSize = tcb->adj_stack_size;
SEGGER_SYSVIEW_SendTaskInfo(&info);
}
/****************************************************************************
* Name: sysview_get_time
****************************************************************************/
static uint64_t sysview_get_time(void)
{
return TICK2USEC(clock_systime_ticks());
}
/****************************************************************************
* Name: sysview_send_tasklist
****************************************************************************/
static void sysview_send_tasklist(void)
{
int i;
for (i = 0; i < g_npidhash; i++)
{
if (g_pidhash[i] != NULL)
{
sysview_send_taskinfo(g_pidhash[i]);
}
}
}
/****************************************************************************
* Name: sysview_send_description
****************************************************************************/
static void 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: sysview_isenabled
*
* Description:
* Check whether the instrumentation is enabled.
*
* Input Parameters:
* None
*
* Returned Value:
* True is returned if the instrumentation is enabled.
*
****************************************************************************/
static bool sysview_isenabled(void)
{
bool enable;
enable = SEGGER_SYSVIEW_IsStarted();
#if defined(CONFIG_SCHED_INSTRUMENTATION_FILTER) && defined(CONFIG_SMP)
/* Ignore notes that are not in the set of monitored CPUs */
if (enable && (g_sysview.mode.cpuset & (1 << this_cpu())) == 0)
{
/* Not in the set of monitored CPUs. Do not log the note. */
return false;
}
#endif
#ifdef CONFIG_SCHED_INSTRUMENTATION_SYSCALL
/* 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 (!enable &&
NOTE_FILTER_SYSCALLMASK_ISSET(0, &g_sysview.syscall_marker))
{
NOTE_FILTER_SYSCALLMASK_ZERO(&g_sysview.syscall_marker);
}
#endif
return enable;
}
/****************************************************************************
* Name: sysview_isenabled_irq
*
* Description:
* Check whether the interrupt handler instrumentation is enabled.
*
* Input Parameters:
* irq - IRQ number
* enter - interrupt enter/leave flag
*
* Returned Value:
* True is returned if the instrumentation is enabled.
*
****************************************************************************/
#ifdef CONFIG_SCHED_INSTRUMENTATION_IRQHANDLER
static bool sysview_isenabled_irq(int irq, bool enter)
{
#ifdef CONFIG_SCHED_INSTRUMENTATION_FILTER
if (!sysview_isenabled())
{
return false;
}
/* If the IRQ trace is disabled or the IRQ number is masked, disable
* subsequent syscall traces until leaving the interrupt handler
*/
if ((g_sysview.mode.flag & NOTE_FILTER_MODE_FLAG_IRQ) == 0 ||
NOTE_FILTER_IRQMASK_ISSET(irq, &g_sysview.irq_mask))
{
return false;
}
#endif
return true;
}
#endif
/****************************************************************************
* Name: sysview_isenabled_syscall
*
* Description:
* Check whether the syscall instrumentation is enabled.
*
* Input Parameters:
* nr - syscall number
*
* Returned Value:
* True is returned if the instrumentation is enabled.
*
****************************************************************************/
#ifdef CONFIG_SCHED_INSTRUMENTATION_SYSCALL
static inline int sysview_isenabled_syscall(int nr)
{
#ifdef CONFIG_SCHED_INSTRUMENTATION_FILTER
if (!sysview_isenabled())
{
return false;
}
/* If the syscall trace is disabled or the syscall number is masked,
* do nothing.
*/
if ((g_sysview.mode.flag & NOTE_FILTER_MODE_FLAG_SYSCALL) == 0 ||
NOTE_FILTER_SYSCALLMASK_ISSET(nr, &g_sysview.syscall_mask))
{
return false;
}
#endif
return true;
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: sched_note_start, sched_note_stop, sched_note_switch,
* sched_note_premption
*
* Description:
* Hooks to scheduler monitor
*
* Input Parameters:
* Varies
*
* Returned Value:
* None
*
****************************************************************************/
void sched_note_start(FAR struct tcb_s *tcb)
{
if (!sysview_isenabled())
{
return;
}
SEGGER_SYSVIEW_OnTaskCreate(tcb->pid);
sysview_send_taskinfo(tcb);
}
void sched_note_stop(FAR struct tcb_s *tcb)
{
if (!sysview_isenabled())
{
return;
}
SEGGER_SYSVIEW_OnTaskTerminate(tcb->pid);
}
#ifdef CONFIG_SCHED_INSTRUMENTATION_SWITCH
void sched_note_suspend(FAR struct tcb_s *tcb)
{
if (!sysview_isenabled())
{
return;
}
if (!up_interrupt_context())
{
SEGGER_SYSVIEW_OnTaskStopExec();
}
}
void sched_note_resume(FAR struct tcb_s *tcb)
{
if (!sysview_isenabled())
{
return;
}
if (!up_interrupt_context())
{
if (is_idle_task(tcb))
{
SEGGER_SYSVIEW_OnIdle();
}
else
{
SEGGER_SYSVIEW_OnTaskStartExec(tcb->pid);
}
}
}
#endif
#ifdef CONFIG_SCHED_INSTRUMENTATION_IRQHANDLER
void sched_note_irqhandler(int irq, FAR void *handler, bool enter)
{
if (!sysview_isenabled_irq(irq, enter))
{
return;
}
if (enter)
{
g_sysview.irq[up_cpu_index()] = 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();
}
}
g_sysview.irq[up_cpu_index()] = 0;
}
}
#endif
#ifdef CONFIG_SCHED_INSTRUMENTATION_SYSCALL
void sched_note_syscall_enter(int nr, int argc, ...)
{
nr -= CONFIG_SYS_RESERVED;
if (sysview_isenabled_syscall(nr) == 0)
{
return;
}
/* Set the name marker if the current syscall nr is not active */
if (NOTE_FILTER_SYSCALLMASK_ISSET(nr, &g_sysview.syscall_marker) == 0)
{
/* Set the name marker */
SEGGER_SYSVIEW_NameMarker(nr, g_funcnames[nr]);
/* Mark the syscall active */
NOTE_FILTER_SYSCALLMASK_SET(nr, &g_sysview.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, &g_sysview.syscall_marker) == 0)
{
NOTE_FILTER_SYSCALLMASK_SET(0, &g_sysview.syscall_marker);
}
}
SEGGER_SYSVIEW_MarkStart(nr);
}
void sched_note_syscall_leave(int nr, uintptr_t result)
{
nr -= CONFIG_SYS_RESERVED;
if (sysview_isenabled_syscall(nr) == 0)
{
return;
}
if (NOTE_FILTER_SYSCALLMASK_ISSET(nr, &g_sysview.syscall_marker) != 0)
{
SEGGER_SYSVIEW_MarkStop(nr);
}
}
#endif
/****************************************************************************
* Name: sysview_get_interrupt_id
*
* Description:
* Retrieve the Id of the currently active interrupt.
*
****************************************************************************/
unsigned int sysview_get_interrupt_id(void)
{
return g_sysview.irq[up_cpu_index()];
}
/****************************************************************************
* Name: sysview_get_timestamp
*
* Description:
* Retrieve a system timestamp for SYSVIEW events.
*
****************************************************************************/
unsigned int sysview_get_timestamp(void)
{
return up_perf_gettime();
}
/****************************************************************************
* Name: 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 sysview_initialize(void)
{
uint32_t freq = up_perf_getfreq();
static const SEGGER_SYSVIEW_OS_API g_sysview_trace_api =
{
sysview_get_time,
sysview_send_tasklist,
};
SEGGER_SYSVIEW_Init(freq, freq, &g_sysview_trace_api,
sysview_send_description);
#if CONFIG_SEGGER_SYSVIEW_RAM_BASE != 0
SEGGER_SYSVIEW_SetRAMBase(CONFIG_SEGGER_SYSVIEW_RAM_BASE);
#endif
#ifdef CONFIG_SCHED_INSTRUMENTATION_FILTER
if ((g_sysview.mode.flag & NOTE_FILTER_MODE_FLAG_ENABLE) != 0)
#endif
{
SEGGER_SYSVIEW_Start();
}
syslog(LOG_NOTICE, "SEGGER RTT Control Block Address: %#" PRIxPTR "\n",
(uintptr_t)&_SEGGER_RTT +
CONFIG_SEGGER_RTT_UNCACHED_OFF);
return 0;
}
#ifdef CONFIG_SCHED_INSTRUMENTATION_FILTER
/****************************************************************************
* Name: sched_note_filter_mode
*
* Description:
* Set and get note filter mode.
* (Same as NOTECTL_GETMODE / NOTECTL_SETMODE ioctls)
*
* Input Parameters:
* oldm - A writable pointer to struct note_filter_mode_s to get current
* filter mode
* If 0, no data is written.
* newm - A read-only pointer to struct note_filter_mode_s which holds the
* new filter mode
* If 0, the filter mode is not updated.
*
* Returned Value:
* None
*
****************************************************************************/
void sched_note_filter_mode(struct note_filter_mode_s *oldm,
struct note_filter_mode_s *newm)
{
irqstate_t flags;
flags = spin_lock_irqsave(NULL);
if (oldm != NULL)
{
if (SEGGER_SYSVIEW_IsStarted())
{
g_sysview.mode.flag |= NOTE_FILTER_MODE_FLAG_ENABLE;
}
else
{
g_sysview.mode.flag &= ~NOTE_FILTER_MODE_FLAG_ENABLE;
}
*oldm = g_sysview.mode;
}
if (newm != NULL)
{
g_sysview.mode = *newm;
if ((g_sysview.mode.flag & NOTE_FILTER_MODE_FLAG_ENABLE) != 0)
{
if (!SEGGER_SYSVIEW_IsStarted())
{
SEGGER_SYSVIEW_Start();
}
}
else
{
if (SEGGER_SYSVIEW_IsStarted())
{
SEGGER_SYSVIEW_Stop();
}
}
}
spin_unlock_irqrestore(NULL, flags);
}
/****************************************************************************
* Name: sched_note_filter_irq
*
* Description:
* Set and get IRQ filter setting
* (Same as NOTECTL_GETIRQFILTER / NOTECTL_SETIRQFILTER ioctls)
*
* Input Parameters:
* oldf - A writable pointer to struct note_filter_irq_s to get
* current IRQ filter setting
* If 0, no data is written.
* newf - A read-only pointer to struct note_filter_irq_s of the new
* IRQ filter setting
* If 0, the setting is not updated.
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_SCHED_INSTRUMENTATION_IRQHANDLER
void sched_note_filter_irq(struct note_filter_irq_s *oldf,
struct note_filter_irq_s *newf)
{
irqstate_t flags;
flags = spin_lock_irqsave(NULL);
if (oldf != NULL)
{
/* Return the current filter setting */
*oldf = g_sysview.irq_mask;
}
if (newf != NULL)
{
/* Replace the syscall filter mask by the provided setting */
g_sysview.irq_mask = *newf;
}
spin_unlock_irqrestore(NULL, flags);
}
#endif
/****************************************************************************
* Name: sched_note_filter_syscall
*
* Description:
* Set and get syscall filter setting
* (Same as NOTECTL_GETSYSCALLFILTER / NOTECTL_SETSYSCALLFILTER ioctls)
*
* Input Parameters:
* oldf - A writable pointer to struct note_filter_syscall_s to get
* current syscall filter setting
* If 0, no data is written.
* newf - A read-only pointer to struct note_filter_syscall_s of the
* new syscall filter setting
* If 0, the setting is not updated.
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_SCHED_INSTRUMENTATION_SYSCALL
void sched_note_filter_syscall(struct note_filter_syscall_s *oldf,
struct note_filter_syscall_s *newf)
{
irqstate_t flags;
flags = spin_lock_irqsave(NULL);
if (oldf != NULL)
{
/* Return the current filter setting */
*oldf = g_sysview.syscall_mask;
}
if (newf != NULL)
{
/* Replace the syscall filter mask by the provided setting */
g_sysview.syscall_mask = *newf;
}
spin_unlock_irqrestore(NULL, flags);
}
#endif
#endif /* CONFIG_SCHED_INSTRUMENTATION_FILTER */