Paparazzi UAS  v6.2_unstable
Paparazzi is a free software Unmanned Aircraft System.
rtos_mon_arch.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2016 Gautier Hattenberger <gautier.hattenberger@enac.fr>
3  * 2020 Gautier Hattenberger, Alexandre Bustico
4  *
5  * This file is part of paparazzi
6  *
7  * paparazzi is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2, or (at your option)
10  * any later version.
11  *
12  * paparazzi is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with paparazzi; see the file COPYING. If not, see
19  * <http://www.gnu.org/licenses/>.
20  */
29 #include <ch.h>
30 
31 #if !CH_DBG_STATISTICS
32 #error CH_DBG_STATISTICS should be defined to TRUE to use this monitoring tool
33 #endif
34 
36 
37 static uint16_t get_stack_free(const thread_t *tp);
38 
39 #if USE_SHELL
40 #include "modules/core/shell.h"
41 #include "printf.h"
42 #include "string.h"
43 
44 typedef struct _ThreadCpuInfo {
46  float cpu[RTOS_MON_MAX_THREADS];
47  float totalTicks;
48  float totalISRTicks;
49 } ThreadCpuInfo ;
50 
51 
52 static void stampThreadCpuInfo(ThreadCpuInfo *ti)
53 {
54  const thread_t *tp = chRegFirstThread();
55  uint32_t idx = 0;
56 
57  ti->totalTicks = 0;
58  do {
59  ti->ticks[idx] = (float) tp->stats.cumulative;
60  ti->totalTicks += ti->ticks[idx];
61  tp = chRegNextThread((thread_t *)tp);
62  idx++;
63  } while ((tp != NULL) && (idx < RTOS_MON_MAX_THREADS));
64  ti->totalISRTicks = ch0.kernel_stats.m_crit_isr.cumulative;
65  ti->totalTicks += ti->totalISRTicks;
66  tp = chRegFirstThread();
67  idx = 0;
68  do {
69  ti->cpu[idx] = (ti->ticks[idx] * 100.f) / ti->totalTicks;
70  tp = chRegNextThread((thread_t *)tp);
71  idx++;
72  } while ((tp != NULL) && (idx < RTOS_MON_MAX_THREADS));
73 }
74 
75 static float stampThreadGetCpuPercent(const ThreadCpuInfo *ti, const uint32_t idx)
76 {
77  if (idx >= RTOS_MON_MAX_THREADS) {
78  return -1.f;
79  }
80 
81  return ti->cpu[idx];
82 }
83 
84 static float stampISRGetCpuPercent(const ThreadCpuInfo *ti)
85 {
86  return ti->totalISRTicks * 100.0f / ti->totalTicks;
87 }
88 
89 static void cmd_threads(BaseSequentialStream *lchp, int argc, const char *const argv[])
90 {
91  static const char *states[] = {CH_STATE_NAMES};
92  thread_t *tp = chRegFirstThread();
93  (void)argv;
94  (void)argc;
95  float totalTicks = 0;
96  float idleTicks = 0;
97 
98  static ThreadCpuInfo threadCpuInfo = {
99  .ticks = {[0 ... RTOS_MON_MAX_THREADS - 1] = 0.f},
100  .cpu = {[0 ... RTOS_MON_MAX_THREADS - 1] = -1.f},
101  .totalTicks = 0.f,
102  .totalISRTicks = 0.f
103  };
104 
105  stampThreadCpuInfo(&threadCpuInfo);
106 
107  chprintf(lchp, " addr stack frestk prio refs state time \t percent name\r\n");
108  uint32_t idx = 0;
109  do {
110  chprintf(lchp, "%.8lx %.8lx %6lu %4lu %4lu %9s %9lu %.2f%% \t%s\r\n",
111  (uint32_t)tp, (uint32_t)tp->ctx.sp,
112  get_stack_free(tp),
113  (uint32_t)tp->hdr.pqueue.prio, (uint32_t)(tp->refs - 1),
114  states[tp->state],
115  (uint32_t)RTC2MS(STM32_SYSCLK, tp->stats.cumulative),
116  stampThreadGetCpuPercent(&threadCpuInfo, idx),
117  chRegGetThreadNameX(tp));
118 
119  totalTicks += (float)tp->stats.cumulative;
120  if (strcmp(chRegGetThreadNameX(tp), "idle") == 0) {
121  idleTicks = (float)tp->stats.cumulative;
122  }
123  tp = chRegNextThread((thread_t *)tp);
124  idx++;
125  } while (tp != NULL);
126 
127  totalTicks += ch0.kernel_stats.m_crit_isr.cumulative;
128  const float idlePercent = (idleTicks * 100.f) / totalTicks;
129  const float cpuPercent = 100.f - idlePercent;
130  chprintf(lchp, "Interrupt Service Routine \t\t %9lu %.2f%% \tISR\r\n",
131  (uint32_t)RTC2MS(STM32_SYSCLK, threadCpuInfo.totalISRTicks),
132  stampISRGetCpuPercent(&threadCpuInfo));
133  chprintf(lchp, "\r\ncpu load = %.2f%%\r\n", cpuPercent);
134 }
135 
136 static void cmd_rtos_mon(shell_stream_t *sh, int argc, const char *const argv[])
137 {
138  (void) argv;
139  if (argc > 0) {
140  chprintf(sh, "Usage: rtos_mon\r\n");
141  return;
142  }
143 
144  chprintf(sh, "Data reported in the RTOS_MON message:\r\n");
145  chprintf(sh, " core free mem: %u\r\n", rtos_mon.core_free_memory);
146  chprintf(sh, " heap free mem: %u\r\n", rtos_mon.heap_free_memory);
147  chprintf(sh, " heap fragments: %u\r\n", rtos_mon.heap_fragments);
148  chprintf(sh, " heap largest: %u\r\n", rtos_mon.heap_largest);
149  chprintf(sh, " CPU load: %d \%\r\n", rtos_mon.cpu_load);
150  chprintf(sh, " number of threads: %d\r\n", rtos_mon.thread_counter);
151  chprintf(sh, " thread names: %s\r\n", rtos_mon.thread_names);
152  for (int i = 0; i < rtos_mon.thread_counter; i++) {
153  chprintf(sh, " thread %d load: %0.1f, free stack: %d\r\n", i,
154  (float)rtos_mon.thread_load[i] / 10.f, rtos_mon.thread_free_stack[i]);
155  }
156  chprintf(sh, " CPU time: %.2f\r\n", rtos_mon.cpu_time);
157 }
158 #endif
159 
161 {
162 #if USE_SHELL
163  shell_add_entry("rtos_mon", cmd_rtos_mon);
164  shell_add_entry("threads", cmd_threads);
165 #endif
166 }
167 
168 // Fill data structure
170 {
171  int i;
172  size_t total_fragments, total_fragmented_free_space, largest_free_block;
173  memory_area_t area;
174  total_fragments = chHeapStatus(NULL, &total_fragmented_free_space, &largest_free_block);
175  chCoreGetStatusX(&area);
176 
177  rtos_mon.core_free_memory = area.size;
178  rtos_mon.heap_fragments = total_fragments;
179  rtos_mon.heap_largest = largest_free_block;
180  rtos_mon.heap_free_memory = total_fragmented_free_space;
182 
183  // loop threads to find idle thread
184  // store info on other threads
185  thread_t *tp;
186  float idle_counter = 0.f;
187  float sum = 0.f;
189  tp = chRegFirstThread();
190  do {
191  // add beginning of thread name to buffer
192  for (i = 0; i < RTOS_MON_NAME_LEN - 1 && tp->name[i] != '\0'; i++) {
194  }
196 
197  // store free stack for this thread
199 
200  // store time spend in thread
201  thread_p_time[rtos_mon.thread_counter] = tp->stats.cumulative;
202  sum += (float)(tp->stats.cumulative);
203 
204  // if current thread is 'idle' thread, store its value separately
205  if (tp == chSysGetIdleThreadX()) {
206  idle_counter = (float)tp->stats.cumulative;
207  }
208  // get next thread
209  tp = chRegNextThread(tp);
210  // increment thread counter
212  } while (tp != NULL && rtos_mon.thread_counter < RTOS_MON_MAX_THREADS);
213  // sum the time spent in ISR
214  sum += ch0.kernel_stats.m_crit_isr.cumulative;
215  // store individual thread load (as centi-percent integer, i.e. (th_time/sum)*10*100)
216  for (i = 0; i < rtos_mon.thread_counter; i ++) {
217  rtos_mon.thread_load[i] = (uint16_t)(1000.f * (float)thread_p_time[i] / sum);
218  }
219 
220  // assume we call the counter once a second
221  // so the difference in seconds is always one
222  // NOTE: not perfectly precise, +-5% on average so take it into consideration
223  rtos_mon.cpu_load = (uint8_t)((1.f - (idle_counter / sum)) * 100.f);
224 }
225 
226 static uint16_t get_stack_free(const thread_t *tp)
227 {
228  int32_t index = 0;
229  extern const uint8_t __ram0_end__;
230  unsigned long long *stkAdr = (unsigned long long *)((uint8_t *) tp->wabase);
231  while ((stkAdr[index] == 0x5555555555555555) && (((uint8_t *) & (stkAdr[index])) < &__ram0_end__)) {
232  index++;
233  }
234  const int32_t freeBytes = index * (int32_t) sizeof(long long);
235  return (uint16_t)freeBytes;
236 }
237 
#define STM32_SYSCLK
Definition: sys_time_arch.c:43
static uint32_t thread_p_time[RTOS_MON_MAX_THREADS]
Definition: rtos_mon_arch.c:35
void rtos_mon_periodic_arch(void)
void rtos_mon_init_arch(void)
static uint16_t get_stack_free(const thread_t *tp)
static struct nodeState states[UWB_SERIAL_COMM_DIST_NUM_NODES]
if(GpsFixValid() &&e_identification_started)
int ticks
Definition: gps_sirf.c:193
void chprintf(BaseSequentialStream *lchp, const char *fmt,...)
Definition: printf.c:395
static uint32_t idx
Mini printf-like functionality.
struct rtos_monitoring rtos_mon
Definition: rtos_mon.c:30
void shell_add_entry(char *cmd_name, shell_cmd_t *cmd)
Add dynamic entry.
Definition: shell_arch.c:92
BaseSequentialStream shell_stream_t
Definition: shell_arch.h:31
System monitoring for RTOS targets return cpu load, average exec time, ...
uint8_t cpu_load
global CPU/MCU load in %
Definition: sys_mon_rtos.h:51
char thread_names[RTOS_MON_THREAD_NAMES+1]
string of thread names / identifiers
Definition: sys_mon_rtos.h:55
uint32_t core_free_memory
core free memory in bytes
Definition: sys_mon_rtos.h:47
uint16_t thread_free_stack[RTOS_MON_MAX_THREADS]
individual thread free stack in bytes
Definition: sys_mon_rtos.h:54
uint32_t heap_free_memory
Total fragmented free memory in the heap.
Definition: sys_mon_rtos.h:48
uint16_t thread_load[RTOS_MON_MAX_THREADS]
individual thread load in centi-percent (10*%)
Definition: sys_mon_rtos.h:53
uint8_t thread_name_idx
length of the string in thread_names buffer
Definition: sys_mon_rtos.h:56
uint8_t thread_counter
number of threads
Definition: sys_mon_rtos.h:52
#define RTOS_MON_MAX_THREADS
Definition: sys_mon_rtos.h:36
uint32_t heap_fragments
Number of fragments in the heap.
Definition: sys_mon_rtos.h:49
#define RTOS_MON_NAME_LEN
Definition: sys_mon_rtos.h:40
uint32_t heap_largest
Largest free block in the heap.
Definition: sys_mon_rtos.h:50
unsigned short uint16_t
Typedef defining 16 bit unsigned short type.
Definition: vl53l1_types.h:88
int int32_t
Typedef defining 32 bit int type.
Definition: vl53l1_types.h:83
unsigned int uint32_t
Typedef defining 32 bit unsigned int type.
Definition: vl53l1_types.h:78
unsigned char uint8_t
Typedef defining 8 bit unsigned char type.
Definition: vl53l1_types.h:98