PX4 Firmware
PX4 Autopilot Software http://px4.io
perf_counter.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (c) 2012-2016 PX4 Development Team. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in
13  * the documentation and/or other materials provided with the
14  * distribution.
15  * 3. Neither the name PX4 nor the names of its contributors may be
16  * used to endorse or promote products derived from this software
17  * without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  *
32  ****************************************************************************/
33 
34 /**
35  * @file perf_counter.c
36  *
37  * @brief Performance measuring tools.
38  */
39 
40 #include <stdlib.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <sys/queue.h>
44 #include <drivers/drv_hrt.h>
45 #include <math.h>
46 #include <pthread.h>
47 #include <systemlib/err.h>
48 
49 #include "perf_counter.h"
50 
51 /* latency histogram */
53 const uint16_t latency_buckets[LATENCY_BUCKET_COUNT] = { 1, 2, 5, 10, 20, 50, 100, 1000 };
55 
56 
57 #ifdef __PX4_QURT
58 // There is presumably no dprintf on QURT. Therefore use the usual output to mini-dm.
59 #define dprintf(_fd, _text, ...) ((_fd) == 1 ? PX4_INFO((_text), ##__VA_ARGS__) : (void)(_fd))
60 #endif
61 
62 /**
63  * Header common to all counters.
64  */
66  sq_entry_t link; /**< list linkage */
67  enum perf_counter_type type; /**< counter type */
68  const char *name; /**< counter name */
69 };
70 
71 /**
72  * PC_EVENT counter.
73  */
75  uint64_t event_count{0};
76 };
77 
78 /**
79  * PC_ELAPSED counter.
80  */
82  uint64_t event_count{0};
83  uint64_t time_start{0};
84  uint64_t time_total{0};
85  uint32_t time_least{0};
86  uint32_t time_most{0};
87  float mean{0.0f};
88  float M2{0.0f};
89 };
90 
91 /**
92  * PC_INTERVAL counter.
93  */
95  uint64_t event_count{0};
96  uint64_t time_event{0};
97  uint64_t time_first{0};
98  uint64_t time_last{0};
99  uint32_t time_least{0};
100  uint32_t time_most{0};
101  float mean{0.0f};
102  float M2{0.0f};
103 };
104 
105 /**
106  * List of all known counters.
107  */
108 static sq_queue_t perf_counters = { nullptr, nullptr };
109 
110 /**
111  * mutex protecting access to the perf_counters linked list (which is read from & written to by different threads)
112  */
113 pthread_mutex_t perf_counters_mutex = PTHREAD_MUTEX_INITIALIZER;
114 // FIXME: the mutex does **not** protect against access to/from the perf
115 // counter's data. It can still happen that a counter is updated while it is
116 // printed. This can lead to inconsistent output, or completely bogus values
117 // (especially the 64bit values which are in general not atomically updated).
118 // The same holds for shared perf counters (perf_alloc_once), that can be updated
119 // concurrently (this affects the 'ctrl_latency' counter).
120 
121 
124 {
125  perf_counter_t ctr = nullptr;
126 
127  switch (type) {
128  case PC_COUNT:
129  ctr = new perf_ctr_count();
130  break;
131 
132  case PC_ELAPSED:
133  ctr = new perf_ctr_elapsed();
134  break;
135 
136  case PC_INTERVAL:
137  ctr = new perf_ctr_interval();
138  break;
139 
140  default:
141  break;
142  }
143 
144  if (ctr != nullptr) {
145  ctr->type = type;
146  ctr->name = name;
147  pthread_mutex_lock(&perf_counters_mutex);
148  sq_addfirst(&ctr->link, &perf_counters);
149  pthread_mutex_unlock(&perf_counters_mutex);
150  }
151 
152  return ctr;
153 }
154 
157 {
158  pthread_mutex_lock(&perf_counters_mutex);
159  perf_counter_t handle = (perf_counter_t)sq_peek(&perf_counters);
160 
161  while (handle != nullptr) {
162  if (!strcmp(handle->name, name)) {
163  if (type == handle->type) {
164  /* they are the same counter */
165  pthread_mutex_unlock(&perf_counters_mutex);
166  return handle;
167 
168  } else {
169  /* same name but different type, assuming this is an error and not intended */
170  pthread_mutex_unlock(&perf_counters_mutex);
171  return nullptr;
172  }
173  }
174 
175  handle = (perf_counter_t)sq_next(&handle->link);
176  }
177 
178  pthread_mutex_unlock(&perf_counters_mutex);
179 
180  /* if the execution reaches here, no existing counter of that name was found */
181  return perf_alloc(type, name);
182 }
183 
184 void
186 {
187  if (handle == nullptr) {
188  return;
189  }
190 
191  pthread_mutex_lock(&perf_counters_mutex);
192  sq_rem(&handle->link, &perf_counters);
193  pthread_mutex_unlock(&perf_counters_mutex);
194 
195  delete handle;
196 }
197 
198 void
200 {
201  if (handle == nullptr) {
202  return;
203  }
204 
205  switch (handle->type) {
206  case PC_COUNT:
207  ((struct perf_ctr_count *)handle)->event_count++;
208  break;
209 
210  case PC_INTERVAL: {
211  struct perf_ctr_interval *pci = (struct perf_ctr_interval *)handle;
213 
214  switch (pci->event_count) {
215  case 0:
216  pci->time_first = now;
217  break;
218 
219  case 1:
220  pci->time_least = (uint32_t)(now - pci->time_last);
221  pci->time_most = (uint32_t)(now - pci->time_last);
222  pci->mean = pci->time_least / 1e6f;
223  pci->M2 = 0;
224  break;
225 
226  default: {
227  hrt_abstime interval = now - pci->time_last;
228 
229  if ((uint32_t)interval < pci->time_least) {
230  pci->time_least = (uint32_t)interval;
231  }
232 
233  if ((uint32_t)interval > pci->time_most) {
234  pci->time_most = (uint32_t)interval;
235  }
236 
237  // maintain mean and variance of interval in seconds
238  // Knuth/Welford recursive mean and variance of update intervals (via Wikipedia)
239  float dt = interval / 1e6f;
240  float delta_intvl = dt - pci->mean;
241  pci->mean += delta_intvl / pci->event_count;
242  pci->M2 += delta_intvl * (dt - pci->mean);
243  break;
244  }
245  }
246 
247  pci->time_last = now;
248  pci->event_count++;
249  break;
250  }
251 
252  default:
253  break;
254  }
255 }
256 
257 void
259 {
260  if (handle == nullptr) {
261  return;
262  }
263 
264  switch (handle->type) {
265  case PC_ELAPSED:
266  ((struct perf_ctr_elapsed *)handle)->time_start = hrt_absolute_time();
267  break;
268 
269  default:
270  break;
271  }
272 }
273 
274 void
276 {
277  if (handle == nullptr) {
278  return;
279  }
280 
281  switch (handle->type) {
282  case PC_ELAPSED: {
283  struct perf_ctr_elapsed *pce = (struct perf_ctr_elapsed *)handle;
284 
285  if (pce->time_start != 0) {
286  int64_t elapsed = hrt_absolute_time() - pce->time_start;
287 
288  if (elapsed >= 0) {
289 
290  pce->event_count++;
291  pce->time_total += elapsed;
292 
293  if ((pce->time_least > (uint32_t)elapsed) || (pce->time_least == 0)) {
294  pce->time_least = elapsed;
295  }
296 
297  if (pce->time_most < (uint32_t)elapsed) {
298  pce->time_most = elapsed;
299  }
300 
301  // maintain mean and variance of the elapsed time in seconds
302  // Knuth/Welford recursive mean and variance of update intervals (via Wikipedia)
303  float dt = elapsed / 1e6f;
304  float delta_intvl = dt - pce->mean;
305  pce->mean += delta_intvl / pce->event_count;
306  pce->M2 += delta_intvl * (dt - pce->mean);
307 
308  pce->time_start = 0;
309  }
310  }
311  }
312  break;
313 
314  default:
315  break;
316  }
317 }
318 
319 void
320 perf_set_elapsed(perf_counter_t handle, int64_t elapsed)
321 {
322  if (handle == nullptr) {
323  return;
324  }
325 
326  switch (handle->type) {
327  case PC_ELAPSED: {
328  struct perf_ctr_elapsed *pce = (struct perf_ctr_elapsed *)handle;
329 
330  if (elapsed >= 0) {
331 
332  pce->event_count++;
333  pce->time_total += elapsed;
334 
335  if ((pce->time_least > (uint32_t)elapsed) || (pce->time_least == 0)) {
336  pce->time_least = elapsed;
337  }
338 
339  if (pce->time_most < (uint32_t)elapsed) {
340  pce->time_most = elapsed;
341  }
342 
343  // maintain mean and variance of the elapsed time in seconds
344  // Knuth/Welford recursive mean and variance of update intervals (via Wikipedia)
345  float dt = elapsed / 1e6f;
346  float delta_intvl = dt - pce->mean;
347  pce->mean += delta_intvl / pce->event_count;
348  pce->M2 += delta_intvl * (dt - pce->mean);
349 
350  pce->time_start = 0;
351  }
352  }
353  break;
354 
355  default:
356  break;
357  }
358 }
359 
360 void
361 perf_set_count(perf_counter_t handle, uint64_t count)
362 {
363  if (handle == nullptr) {
364  return;
365  }
366 
367  switch (handle->type) {
368  case PC_COUNT: {
369  ((struct perf_ctr_count *)handle)->event_count = count;
370  }
371  break;
372 
373  default:
374  break;
375  }
376 
377 }
378 
379 void
381 {
382  if (handle == nullptr) {
383  return;
384  }
385 
386  switch (handle->type) {
387  case PC_ELAPSED: {
388  struct perf_ctr_elapsed *pce = (struct perf_ctr_elapsed *)handle;
389 
390  pce->time_start = 0;
391  }
392  break;
393 
394  default:
395  break;
396  }
397 }
398 
399 
400 
401 void
403 {
404  if (handle == nullptr) {
405  return;
406  }
407 
408  switch (handle->type) {
409  case PC_COUNT:
410  ((struct perf_ctr_count *)handle)->event_count = 0;
411  break;
412 
413  case PC_ELAPSED: {
414  struct perf_ctr_elapsed *pce = (struct perf_ctr_elapsed *)handle;
415  pce->event_count = 0;
416  pce->time_start = 0;
417  pce->time_total = 0;
418  pce->time_least = 0;
419  pce->time_most = 0;
420  break;
421  }
422 
423  case PC_INTERVAL: {
424  struct perf_ctr_interval *pci = (struct perf_ctr_interval *)handle;
425  pci->event_count = 0;
426  pci->time_event = 0;
427  pci->time_first = 0;
428  pci->time_last = 0;
429  pci->time_least = 0;
430  pci->time_most = 0;
431  break;
432  }
433  }
434 }
435 
436 void
438 {
439  if (handle == nullptr) {
440  return;
441  }
442 
443  perf_print_counter_fd(1, handle);
444 }
445 
446 void
448 {
449  if (handle == nullptr) {
450  return;
451  }
452 
453  switch (handle->type) {
454  case PC_COUNT:
455  dprintf(fd, "%s: %llu events\n",
456  handle->name,
457  (unsigned long long)((struct perf_ctr_count *)handle)->event_count);
458  break;
459 
460  case PC_ELAPSED: {
461  struct perf_ctr_elapsed *pce = (struct perf_ctr_elapsed *)handle;
462  float rms = sqrtf(pce->M2 / (pce->event_count - 1));
463  dprintf(fd, "%s: %llu events, %lluus elapsed, %.2fus avg, min %lluus max %lluus %5.3fus rms\n",
464  handle->name,
465  (unsigned long long)pce->event_count,
466  (unsigned long long)pce->time_total,
467  (pce->event_count == 0) ? 0 : (double)pce->time_total / (double)pce->event_count,
468  (unsigned long long)pce->time_least,
469  (unsigned long long)pce->time_most,
470  (double)(1e6f * rms));
471  break;
472  }
473 
474  case PC_INTERVAL: {
475  struct perf_ctr_interval *pci = (struct perf_ctr_interval *)handle;
476  float rms = sqrtf(pci->M2 / (pci->event_count - 1));
477 
478  dprintf(fd, "%s: %llu events, %.2fus avg, min %lluus max %lluus %5.3fus rms\n",
479  handle->name,
480  (unsigned long long)pci->event_count,
481  (pci->event_count == 0) ? 0 : (double)(pci->time_last - pci->time_first) / (double)pci->event_count,
482  (unsigned long long)pci->time_least,
483  (unsigned long long)pci->time_most,
484  (double)(1e6f * rms));
485  break;
486  }
487 
488  default:
489  break;
490  }
491 }
492 
493 
494 int
495 perf_print_counter_buffer(char *buffer, int length, perf_counter_t handle)
496 {
497  int num_written = 0;
498 
499  if (handle == nullptr) {
500  return 0;
501  }
502 
503  switch (handle->type) {
504  case PC_COUNT:
505  num_written = snprintf(buffer, length, "%s: %llu events",
506  handle->name,
507  (unsigned long long)((struct perf_ctr_count *)handle)->event_count);
508  break;
509 
510  case PC_ELAPSED: {
511  struct perf_ctr_elapsed *pce = (struct perf_ctr_elapsed *)handle;
512  float rms = sqrtf(pce->M2 / (pce->event_count - 1));
513  num_written = snprintf(buffer, length, "%s: %llu events, %lluus elapsed, %.2fus avg, min %lluus max %lluus %5.3fus rms",
514  handle->name,
515  (unsigned long long)pce->event_count,
516  (unsigned long long)pce->time_total,
517  (pce->event_count == 0) ? 0 : (double)pce->time_total / (double)pce->event_count,
518  (unsigned long long)pce->time_least,
519  (unsigned long long)pce->time_most,
520  (double)(1e6f * rms));
521  break;
522  }
523 
524  case PC_INTERVAL: {
525  struct perf_ctr_interval *pci = (struct perf_ctr_interval *)handle;
526  float rms = sqrtf(pci->M2 / (pci->event_count - 1));
527 
528  num_written = snprintf(buffer, length, "%s: %llu events, %.2f avg, min %lluus max %lluus %5.3fus rms",
529  handle->name,
530  (unsigned long long)pci->event_count,
531  (pci->event_count == 0) ? 0 : (double)(pci->time_last - pci->time_first) / (double)pci->event_count,
532  (unsigned long long)pci->time_least,
533  (unsigned long long)pci->time_most,
534  (double)(1e6f * rms));
535  break;
536  }
537 
538  default:
539  break;
540  }
541 
542  buffer[length - 1] = 0; // ensure 0-termination
543  return num_written;
544 }
545 
546 uint64_t
548 {
549  if (handle == nullptr) {
550  return 0;
551  }
552 
553  switch (handle->type) {
554  case PC_COUNT:
555  return ((struct perf_ctr_count *)handle)->event_count;
556 
557  case PC_ELAPSED: {
558  struct perf_ctr_elapsed *pce = (struct perf_ctr_elapsed *)handle;
559  return pce->event_count;
560  }
561 
562  case PC_INTERVAL: {
563  struct perf_ctr_interval *pci = (struct perf_ctr_interval *)handle;
564  return pci->event_count;
565  }
566 
567  default:
568  break;
569  }
570 
571  return 0;
572 }
573 
574 void
576 {
577  pthread_mutex_lock(&perf_counters_mutex);
578  perf_counter_t handle = (perf_counter_t)sq_peek(&perf_counters);
579 
580  while (handle != nullptr) {
581  cb(handle, user);
582  handle = (perf_counter_t)sq_next(&handle->link);
583  }
584 
585  pthread_mutex_unlock(&perf_counters_mutex);
586 }
587 
588 void
590 {
591  pthread_mutex_lock(&perf_counters_mutex);
592  perf_counter_t handle = (perf_counter_t)sq_peek(&perf_counters);
593 
594  while (handle != nullptr) {
595  perf_print_counter_fd(fd, handle);
596  handle = (perf_counter_t)sq_next(&handle->link);
597  }
598 
599  pthread_mutex_unlock(&perf_counters_mutex);
600 }
601 
602 void
604 {
605  dprintf(fd, "bucket [us] : events\n");
606 
607  for (int i = 0; i < latency_bucket_count; i++) {
608  dprintf(fd, " %4i : %li\n", latency_buckets[i], (long int)latency_counters[i]);
609  }
610 
611  // print the overflow bucket value
612  dprintf(fd, " >%4i : %i\n", latency_buckets[latency_bucket_count - 1], latency_counters[latency_bucket_count]);
613 }
614 
615 void
617 {
618  pthread_mutex_lock(&perf_counters_mutex);
619  perf_counter_t handle = (perf_counter_t)sq_peek(&perf_counters);
620 
621  while (handle != nullptr) {
622  perf_reset(handle);
623  handle = (perf_counter_t)sq_next(&handle->link);
624  }
625 
626  pthread_mutex_unlock(&perf_counters_mutex);
627 
628  for (int i = 0; i <= latency_bucket_count; i++) {
629  latency_counters[i] = 0;
630  }
631 }
int perf_print_counter_buffer(char *buffer, int length, perf_counter_t handle)
Print one performance counter to a buffer.
#define LATENCY_BUCKET_COUNT
Definition: perf_counter.h:45
measure the time elapsed performing an event
Definition: perf_counter.h:56
void perf_print_counter_fd(int fd, perf_counter_t handle)
Print one performance counter to a fd.
const char * name
counter name
static sq_queue_t perf_counters
List of all known counters.
uint64_t event_count
pthread_mutex_t perf_counters_mutex
mutex protecting access to the perf_counters linked list (which is read from & written to by differen...
perf_counter_type
Counter types.
Definition: perf_counter.h:54
Definition: I2C.hpp:51
void perf_reset_all(void)
Reset all of the performance counters.
void perf_set_elapsed(perf_counter_t handle, int64_t elapsed)
Register a measurement.
count the number of times an event occurs
Definition: perf_counter.h:55
High-resolution timer with callouts and timekeeping.
PC_INTERVAL counter.
perf_counter_t perf_alloc(enum perf_counter_type type, const char *name)
Create a new local counter.
void(* perf_callback)(perf_counter_t handle, void *user)
Definition: perf_counter.h:197
void perf_reset(perf_counter_t handle)
Reset a performance counter.
const uint16_t latency_bucket_count
void perf_count(perf_counter_t handle)
Count a performance event.
Header common to all counters.
void perf_free(perf_counter_t handle)
Free a counter.
void perf_print_all(int fd)
Print all of the performance counters.
enum perf_counter_type type
counter type
perf_counter_t perf_alloc_once(enum perf_counter_type type, const char *name)
Get the reference to an existing counter or create a new one if it does not exist.
PC_ELAPSED counter.
Simple error/warning functions, heavily inspired by the BSD functions of the same names...
void perf_end(perf_counter_t handle)
End a performance event.
__BEGIN_DECLS typedef uint64_t hrt_abstime
Absolute time, in microsecond units.
Definition: drv_hrt.h:58
int fd
Definition: dataman.cpp:146
sq_entry_t link
list linkage
void perf_iterate_all(perf_callback cb, void *user)
Iterate over all performance counters using a callback.
struct perf_ctr_header * perf_counter_t
Definition: perf_counter.h:61
float dt
Definition: px4io.c:73
void perf_print_counter(perf_counter_t handle)
Print one performance counter to stdout.
uint64_t perf_event_count(perf_counter_t handle)
Return current event_count.
void perf_cancel(perf_counter_t handle)
Cancel a performance event.
void perf_set_count(perf_counter_t handle, uint64_t count)
Set a counter.
const uint16_t latency_buckets[LATENCY_BUCKET_COUNT]
PC_EVENT counter.
void perf_print_latency(int fd)
Print hrt latency counters.
void perf_begin(perf_counter_t handle)
Begin a performance event.
__EXPORT uint32_t latency_counters[LATENCY_BUCKET_COUNT+1]
measure the interval between instances of an event
Definition: perf_counter.h:57
__EXPORT hrt_abstime hrt_absolute_time(void)
Get absolute time in [us] (does not wrap).
Performance measuring tools.