Skip to content

Commit 7882108

Browse files
MarekPietanashif
authored andcommitted
debug: tracing: Add CPU stats module
Change adds CPU stats module for tracing hooks. Module provides information about percent of CPU usage based on tracing hooks for threads switching in and out, interrupts enters and exits. cpu_stats only distinguishes between idle thread, non idle thread and scheduler. Signed-off-by: Marek Pieta <[email protected]>
1 parent 58b51dd commit 7882108

File tree

5 files changed

+290
-0
lines changed

5 files changed

+290
-0
lines changed

include/tracing.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ void z_sys_trace_thread_switched_out(void);
2929

3030
#ifdef CONFIG_SEGGER_SYSTEMVIEW
3131
#include "tracing_sysview.h"
32+
33+
#elif defined CONFIG_TRACING_CPU_STATS
34+
#include "tracing_cpu_stats.h"
35+
3236
#else
3337

3438
/**

subsys/debug/Kconfig

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,4 +230,29 @@ config OPENOCD_SUPPORT
230230
OpenOCD to determine the state of running threads. (This option
231231
selects CONFIG_THREAD_MONITOR, so all of its caveats are implied.)
232232

233+
config TRACING_CPU_STATS
234+
bool "Enable CPU usage tracing"
235+
select THREAD_MONITOR
236+
select THREAD_STACK_INFO
237+
select TRACING
238+
help
239+
Module provides information about percent of CPU usage based on
240+
tracing hooks for threads switching in and out, interrupts enters
241+
and exits (only distinguishes between idle thread, non idle thread
242+
and scheduler). Use provided API or enable automatical logging to
243+
get values.
244+
245+
config TRACING_CPU_STATS_LOG
246+
bool "Enable current CPU usage logging"
247+
depends on TRACING_CPU_STATS
248+
help
249+
Periodically displays information about CPU usage.
250+
251+
config TRACING_CPU_STATS_INTERVAL
252+
int "Logging interval for CPU measurements [ms]"
253+
default 2000
254+
depends on TRACING_CPU_STATS_LOG
255+
help
256+
Time period of displaying information about CPU usage.
257+
233258
endmenu

subsys/debug/tracing/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,8 @@ zephyr_sources_ifdef(
55
sysview_config.c
66
sysview.c
77
)
8+
9+
zephyr_sources_ifdef(
10+
CONFIG_TRACING_CPU_STATS
11+
cpu_stats.c
12+
)

subsys/debug/tracing/cpu_stats.c

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

0 commit comments

Comments
 (0)