Linux Audio
Check our new training course
Embedded Linux Audio
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
/* * Copyright (c) 2018 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #include <tracing_cpu_stats.h> #include <misc/printk.h> enum cpu_state { CPU_STATE_IDLE, CPU_STATE_NON_IDLE, CPU_STATE_SCHEDULER }; static enum cpu_state last_cpu_state = CPU_STATE_SCHEDULER; static enum cpu_state cpu_state_before_interrupts; static u32_t last_time; static struct cpu_stats stats_hw_tick; static int nested_interrupts; static struct k_thread *current_thread; #ifndef CONFIG_SMP extern k_tid_t const _idle_thread; #endif static int is_idle_thread(struct k_thread *thread) { #ifdef CONFIG_SMP return thread->base.is_idle; #else return thread == _idle_thread; #endif } void update_counter(volatile u64_t *cnt) { u32_t time = k_cycle_get_32(); if (time >= last_time) { (*cnt) += (time - last_time); } else { (*cnt) += (UINT32_MAX - last_time + 1 + time); } last_time = time; } static void cpu_stats_update_counters(void) { switch (last_cpu_state) { case CPU_STATE_IDLE: update_counter(&stats_hw_tick.idle); break; case CPU_STATE_NON_IDLE: update_counter(&stats_hw_tick.non_idle); break; case CPU_STATE_SCHEDULER: update_counter(&stats_hw_tick.sched); break; default: /* Invalid CPU state */ __ASSERT_NO_MSG(false); break; } } void cpu_stats_get_ns(struct cpu_stats *cpu_stats_ns) { int key = irq_lock(); cpu_stats_update_counters(); cpu_stats_ns->idle = SYS_CLOCK_HW_CYCLES_TO_NS(stats_hw_tick.idle); cpu_stats_ns->non_idle = SYS_CLOCK_HW_CYCLES_TO_NS( stats_hw_tick.non_idle); cpu_stats_ns->sched = SYS_CLOCK_HW_CYCLES_TO_NS(stats_hw_tick.sched); irq_unlock(key); } u32_t cpu_stats_non_idle_and_sched_get_percent(void) { int key = irq_lock(); cpu_stats_update_counters(); irq_unlock(key); return ((stats_hw_tick.non_idle + stats_hw_tick.sched) * 100) / (stats_hw_tick.idle + stats_hw_tick.non_idle + stats_hw_tick.sched); } void cpu_stats_reset_counters(void) { int key = irq_lock(); stats_hw_tick.idle = 0; stats_hw_tick.non_idle = 0; stats_hw_tick.sched = 0; last_time = k_cycle_get_32(); irq_unlock(key); } void sys_trace_thread_switched_in(void) { int key = irq_lock(); __ASSERT_NO_MSG(nested_interrupts == 0); cpu_stats_update_counters(); current_thread = k_current_get(); if (is_idle_thread(current_thread)) { last_cpu_state = CPU_STATE_IDLE; } else { last_cpu_state = CPU_STATE_NON_IDLE; } irq_unlock(key); } void sys_trace_thread_switched_out(void) { int key = irq_lock(); __ASSERT_NO_MSG(nested_interrupts == 0); __ASSERT_NO_MSG(current_thread == k_current_get()); cpu_stats_update_counters(); last_cpu_state = CPU_STATE_SCHEDULER; irq_unlock(key); } void sys_trace_isr_enter(void) { int key = irq_lock(); if (nested_interrupts == 0) { cpu_stats_update_counters(); cpu_state_before_interrupts = last_cpu_state; last_cpu_state = CPU_STATE_NON_IDLE; } nested_interrupts++; irq_unlock(key); } void sys_trace_isr_exit(void) { int key = irq_lock(); nested_interrupts--; if (nested_interrupts == 0) { cpu_stats_update_counters(); last_cpu_state = cpu_state_before_interrupts; } irq_unlock(key); } void sys_trace_idle(void) { } void z_sys_trace_idle(void) { sys_trace_idle(); } void z_sys_trace_isr_enter(void) { sys_trace_isr_enter(); } void z_sys_trace_isr_exit(void) { sys_trace_isr_exit(); } void z_sys_trace_thread_switched_in(void) { sys_trace_thread_switched_in(); } void z_sys_trace_thread_switched_out(void) { sys_trace_thread_switched_out(); } #ifdef CONFIG_TRACING_CPU_STATS_LOG static struct k_delayed_work cpu_stats_log; static void cpu_stats_display(void) { printk("CPU usage: %u\n", cpu_stats_non_idle_and_sched_get_percent()); } static void cpu_stats_log_fn(struct k_work *item) { cpu_stats_display(); cpu_stats_reset_counters(); k_delayed_work_submit(&cpu_stats_log, CONFIG_TRACING_CPU_STATS_INTERVAL); } static int cpu_stats_log_init(struct device *dev) { k_delayed_work_init(&cpu_stats_log, cpu_stats_log_fn); k_delayed_work_submit(&cpu_stats_log, CONFIG_TRACING_CPU_STATS_INTERVAL); return 0; } SYS_INIT(cpu_stats_log_init, APPLICATION, 0); #endif /* CONFIG_TRACING_CPU_STATS_LOG */