/**************************************************************************** * drivers/note/notesnap_driver.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 /**************************************************************************** * Private Types ****************************************************************************/ struct notesnap_chunk_s { uint8_t type; #ifdef CONFIG_SMP uint8_t cpu; #endif pid_t pid; clock_t count; uintptr_t args; }; struct notesnap_s { struct note_driver_s driver; struct notifier_block nb; size_t index; bool dumping; struct notesnap_chunk_s buffer[CONFIG_DRIVERS_NOTESNAP_NBUFFERS]; }; /**************************************************************************** * Private Function Prototypes ****************************************************************************/ static void notesnap_start(FAR struct note_driver_s *drv, FAR struct tcb_s *tcb); static void notesnap_stop(FAR struct note_driver_s *drv, FAR struct tcb_s *tcb); #ifdef CONFIG_SCHED_INSTRUMENTATION_SWITCH static void notesnap_suspend(FAR struct note_driver_s *drv, FAR struct tcb_s *tcb); static void notesnap_resume(FAR struct note_driver_s *drv, FAR struct tcb_s *tcb); #endif #ifdef CONFIG_SMP static void notesnap_cpu_start(FAR struct note_driver_s *drv, FAR struct tcb_s *tcb, int cpu); static void notesnap_cpu_started(FAR struct note_driver_s *drv, FAR struct tcb_s *tcb); # ifdef CONFIG_SCHED_INSTRUMENTATION_SWITCH static void notesnap_cpu_pause(FAR struct note_driver_s *drv, FAR struct tcb_s *tcb, int cpu); static void notesnap_cpu_paused(FAR struct note_driver_s *drv, FAR struct tcb_s *tcb); static void notesnap_cpu_resume(FAR struct note_driver_s *drv, FAR struct tcb_s *tcb, int cpu); static void notesnap_cpu_resumed(FAR struct note_driver_s *drv, FAR struct tcb_s *tcb); # endif #endif #ifdef CONFIG_SCHED_INSTRUMENTATION_PREEMPTION static void notesnap_premption(FAR struct note_driver_s *drv, FAR struct tcb_s *tcb, bool locked); #endif #ifdef CONFIG_SCHED_INSTRUMENTATION_CSECTION static void notesnap_csection(FAR struct note_driver_s *drv, FAR struct tcb_s *tcb, bool enter); #endif #ifdef CONFIG_SCHED_INSTRUMENTATION_SPINLOCKS static void notesnap_spinlock(FAR struct note_driver_s *drv, FAR struct tcb_s *tcb, FAR volatile void *spinlock, int type); #endif #ifdef CONFIG_SCHED_INSTRUMENTATION_SYSCALL static void notesnap_syscall_enter(FAR struct note_driver_s *drv, int nr, int argc, FAR va_list *ap); static void notesnap_syscall_leave(FAR struct note_driver_s *drv, int nr, uintptr_t result); #endif #ifdef CONFIG_SCHED_INSTRUMENTATION_IRQHANDLER static void notesnap_irqhandler(FAR struct note_driver_s *drv, int irq, FAR void *handler, bool enter); #endif /**************************************************************************** * Private Data ****************************************************************************/ static const struct note_driver_ops_s g_notesnap_ops = { NULL, notesnap_start, notesnap_stop, #ifdef CONFIG_SCHED_INSTRUMENTATION_SWITCH notesnap_suspend, notesnap_resume, #endif #ifdef CONFIG_SMP notesnap_cpu_start, notesnap_cpu_started, # ifdef CONFIG_SCHED_INSTRUMENTATION_SWITCH notesnap_cpu_pause, notesnap_cpu_paused, notesnap_cpu_resume, notesnap_cpu_resumed, # endif #endif #ifdef CONFIG_SCHED_INSTRUMENTATION_PREEMPTION notesnap_premption, #endif #ifdef CONFIG_SCHED_INSTRUMENTATION_CSECTION notesnap_csection, #endif #ifdef CONFIG_SCHED_INSTRUMENTATION_SPINLOCKS notesnap_spinlock, #endif #ifdef CONFIG_SCHED_INSTRUMENTATION_SYSCALL notesnap_syscall_enter, notesnap_syscall_leave, #endif #ifdef CONFIG_SCHED_INSTRUMENTATION_IRQHANDLER notesnap_irqhandler, #endif }; static struct notesnap_s g_notesnap = { {&g_notesnap_ops} }; static FAR const char *g_notesnap_type[] = { "NOTE_START", "NOTE_STOP", "NOTE_SUSPEND", "NOTE_RESUME", "NOTE_CPU_START", "NOTE_CPU_STARTED", "NOTE_CPU_PAUSE", "NOTE_CPU_PAUSED", "NOTE_CPU_RESUME", "NOTE_CPU_RESUMED", "NOTE_PREEMPT_LOCK", "NOTE_PREEMPT_UNLOCK", "NOTE_CSECTION_ENTER", "NOTE_CSECTION_LEAVE", "NOTE_SPINLOCK_LOCK", "NOTE_SPINLOCK_LOCKED", "NOTE_SPINLOCK_UNLOCK", "NOTE_SPINLOCK_ABORT", "NOTE_SYSCALL_ENTER", "NOTE_SYSCALL_LEAVE", "NOTE_IRQ_ENTER", "NOTE_IRQ_LEAVE", "NOTE_DUMP_STRING", "NOTE_DUMP_BINARY" }; /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: notesnap_common ****************************************************************************/ static inline void notesnap_common(FAR struct note_driver_s *drv, FAR struct tcb_s *tcb, uint8_t type, uintptr_t args) { FAR struct notesnap_s *snap = (FAR struct notesnap_s *)drv; FAR struct notesnap_chunk_s *note; size_t index; if (snap->dumping) { return; } /* Atomic operation, equivalent to snap.index++; */ index = atomic_fetch_add(&snap->index, 1); note = &snap->buffer[index % CONFIG_DRIVERS_NOTESNAP_NBUFFERS]; note->type = type; #ifdef CONFIG_SMP note->cpu = tcb->cpu; #endif note->count = perf_gettime(); note->pid = tcb->pid; note->args = args; } /**************************************************************************** * Name: notesnap_* ****************************************************************************/ static void notesnap_start(FAR struct note_driver_s *drv, FAR struct tcb_s *tcb) { notesnap_common(drv, tcb, NOTE_START, 0); } static void notesnap_stop(FAR struct note_driver_s *drv, FAR struct tcb_s *tcb) { notesnap_common(drv, tcb, NOTE_STOP, 0); } #ifdef CONFIG_SCHED_INSTRUMENTATION_SWITCH static void notesnap_suspend(FAR struct note_driver_s *drv, FAR struct tcb_s *tcb) { notesnap_common(drv, tcb, NOTE_SUSPEND, 0); } static void notesnap_resume(FAR struct note_driver_s *drv, FAR struct tcb_s *tcb) { notesnap_common(drv, tcb, NOTE_RESUME, 0); } #endif #ifdef CONFIG_SMP static void notesnap_cpu_start(FAR struct note_driver_s *drv, FAR struct tcb_s *tcb, int cpu) { notesnap_common(drv, tcb, NOTE_CPU_START, cpu); } static void notesnap_cpu_started(FAR struct note_driver_s *drv, FAR struct tcb_s *tcb) { notesnap_common(drv, tcb, NOTE_CPU_STARTED, 0); } # ifdef CONFIG_SCHED_INSTRUMENTATION_SWITCH static void notesnap_cpu_pause(FAR struct note_driver_s *drv, FAR struct tcb_s *tcb, int cpu) { notesnap_common(drv, tcb, NOTE_CPU_PAUSE, cpu); } static void notesnap_cpu_paused(FAR struct note_driver_s *drv, FAR struct tcb_s *tcb) { notesnap_common(drv, tcb, NOTE_CPU_PAUSED, 0); } static void notesnap_cpu_resume(FAR struct note_driver_s *drv, FAR struct tcb_s *tcb, int cpu) { notesnap_common(drv, tcb, NOTE_CPU_RESUME, cpu); } static void notesnap_cpu_resumed(FAR struct note_driver_s *drv, FAR struct tcb_s *tcb) { notesnap_common(drv, tcb, NOTE_CPU_RESUMED, 0); } # endif #endif #ifdef CONFIG_SCHED_INSTRUMENTATION_PREEMPTION static void notesnap_premption(FAR struct note_driver_s *drv, FAR struct tcb_s *tcb, bool locked) { notesnap_common(drv, tcb, locked ? NOTE_PREEMPT_LOCK : NOTE_PREEMPT_UNLOCK, 0); } #endif #ifdef CONFIG_SCHED_INSTRUMENTATION_CSECTION static void notesnap_csection(FAR struct note_driver_s *drv, FAR struct tcb_s *tcb, bool enter) { notesnap_common(drv, tcb, enter ? NOTE_CSECTION_ENTER : NOTE_CSECTION_LEAVE, 0); } #endif #ifdef CONFIG_SCHED_INSTRUMENTATION_SPINLOCKS static void notesnap_spinlock(FAR struct note_driver_s *drv, FAR struct tcb_s *tcb, FAR volatile void *spinlock, int type) { notesnap_common(drv, tcb, type, (uintptr_t)spinlock); } #endif #ifdef CONFIG_SCHED_INSTRUMENTATION_IRQHANDLER static void notesnap_irqhandler(FAR struct note_driver_s *drv, int irq, FAR void *handler, bool enter) { notesnap_common(drv, this_task(), enter ? NOTE_IRQ_ENTER : NOTE_IRQ_LEAVE, irq); } #endif #ifdef CONFIG_SCHED_INSTRUMENTATION_SYSCALL static void notesnap_syscall_enter(FAR struct note_driver_s *drv, int nr, int argc, FAR va_list *ap) { notesnap_common(drv, this_task(), NOTE_SYSCALL_ENTER, nr); } static void notesnap_syscall_leave(FAR struct note_driver_s *drv, int nr, uintptr_t result) { notesnap_common(drv, this_task(), NOTE_SYSCALL_LEAVE, nr); } #endif static int notesnap_notifier(FAR struct notifier_block *nb, unsigned long action, FAR void *data) { if (action == PANIC_KERNEL) { notesnap_dump(); } return 0; } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: notesnap_register ****************************************************************************/ int notesnap_register(void) { g_notesnap.nb.notifier_call = notesnap_notifier; panic_notifier_chain_register(&g_notesnap.nb); return note_driver_register(&g_notesnap.driver); } /**************************************************************************** * Name: notesnap_dump_with_stream ****************************************************************************/ void notesnap_dump_with_stream(FAR struct lib_outstream_s *stream) { size_t i; size_t index = g_notesnap.index % CONFIG_DRIVERS_NOTESNAP_NBUFFERS; clock_t lastcount = g_notesnap.buffer[index].count; struct timespec lasttime = { 0 }; /* Stop recording while dumping */ atomic_store(&g_notesnap.dumping, true); for (i = index; i != index - 1; i == CONFIG_DRIVERS_NOTESNAP_NBUFFERS - 1 ? i = 0 : i++) { FAR struct notesnap_chunk_s *note = &g_notesnap.buffer[i]; struct timespec time; clock_t elapsed = note->count < lastcount ? note->count + CLOCK_MAX - lastcount : note->count - lastcount; perf_convert(elapsed, &time); clock_timespec_add(&lasttime, &time, &lasttime); lastcount = note->count; lib_sprintf(stream, "snapshoot: [%u.%09u] " #ifdef CONFIG_SMP "[CPU%d] " #endif "[%d] %-16s %#" PRIxPTR "\n", (unsigned)lasttime.tv_sec, (unsigned)lasttime.tv_nsec, #ifdef CONFIG_SMP note->cpu, #endif note->pid, g_notesnap_type[note->type], note->args); } atomic_store(&g_notesnap.dumping, false); } /**************************************************************************** * Name: notesnap_dump ****************************************************************************/ void notesnap_dump(void) { struct lib_syslograwstream_s stream; lib_syslograwstream_open(&stream); notesnap_dump_with_stream(&stream.common); lib_syslograwstream_close(stream); }