My Project
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Macros
rt_stats.c
Go to the documentation of this file.
1 /*
2 START OF LICENSE STUB
3  DeDOS: Declarative Dispersion-Oriented Software
4  Copyright (C) 2017 University of Pennsylvania, Georgetown University
5 
6  This program is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with this program. If not, see <http://www.gnu.org/licenses/>.
18 END OF LICENSE STUB
19 */
26 #include "rt_stats.h"
27 #include "stats.h"
28 #include "logging.h"
29 
30 #include <stdio.h>
31 #include <stdio_ext.h>
32 #include <stdlib.h>
33 #include <pthread.h>
34 #include <stdbool.h>
35 
37 #define CLOCK_ID CLOCK_REALTIME_COARSE
38 #define DURATION_CLOCK_ID CLOCK_MONOTONIC
39 
41 static struct timespec stats_start_time;
43 static bool stats_initialized = false;
44 
45 
50 struct stat_item
51 {
52  unsigned int id;
54  bool rolled_over;
55  struct timed_stat *stats;
56  pthread_mutex_t mutex;
57 };
58 
62 struct stat_type {
63  enum stat_id id;
64  bool enabled;
65  int max_stats;
66  char *format;
67  char *label;
69  int num_items;
70  int max_items;
71  pthread_rwlock_t lock;
72  struct stat_item *items;
73 };
74 
75 #if DUMP_STATS
76 #define MAX_STATS 262144
77 #else
78 
79 #define MAX_STATS 8192
80 #endif
81 
83 #define GATHER_STATS 1
84 
85 #define GATHER_MSU_STATS 1 & GATHER_STATS
86 #define GATHER_CUSTOM_STATS 1 & GATHER_STATS
87 #define GATHER_THREAD_STATS 1 & GATHER_STATS
88 
89 #ifdef DEDOS_PROFILER
90 #define GATHER_PROFILING 1
91 #else
92 #define GATHER_PROFILING 0
93 #endif
94 
96 struct stat_type stat_types[] = {
97  {MSU_QUEUE_LEN, GATHER_MSU_STATS, MAX_STATS, "%02.0f", "MSU_Q_LEN"},
98  {MSU_ITEMS_PROCESSED, GATHER_MSU_STATS, MAX_STATS, "%03.0f", "ITEMS_PROCESSED"},
99  {MSU_EXEC_TIME, GATHER_MSU_STATS, MAX_STATS, "%0.9f", "MSU_EXEC_TIME"},
100  {MSU_IDLE_TIME, GATHER_MSU_STATS, MAX_STATS, "%0.9f", "MSU_IDLE_TIME"},
101  {MSU_MEM_ALLOC, GATHER_MSU_STATS, MAX_STATS, "%09.0f", "MSU_MEM_ALLOC"},
102  {MSU_NUM_STATES, GATHER_MSU_STATS, MAX_STATS, "%09.0f", "MSU_NUM_STATES"},
103  {MSU_ERROR_CNT, GATHER_MSU_STATS, MAX_STATS, "%09.0f", "MSU_ERROR_CNT"},
104  {MSU_UCPUTIME, GATHER_MSU_STATS, MAX_STATS, "%09.0f", "MSU_USER_TIME"},
105  {MSU_SCPUTIME, GATHER_MSU_STATS, MAX_STATS, "%09.0f", "MSU_KERNEL_TIME"},
106  {MSU_MINFLT, GATHER_MSU_STATS, MAX_STATS, "%09.0f", "MSU_MINOR_FAULTS"},
107  {MSU_MAJFLT, GATHER_MSU_STATS, MAX_STATS, "%09.0f", "MSU_MAJOR_FAULTS"},
108  {MSU_VCSW, GATHER_MSU_STATS, MAX_STATS, "%09.0f", "MSU_VOL_CTX_SW"},
109  {MSU_IVCSW, GATHER_MSU_STATS, MAX_STATS, "%09.0f", "MSU_INVOL_CTX_SW"},
110  {THREAD_UCPUTIME, GATHER_THREAD_STATS, MAX_STATS, "%09.9f", "THREAD_USER_TIME"},
111  {THREAD_SCPUTIME, GATHER_THREAD_STATS, MAX_STATS, "%09.9f", "THREAD_KERNEL_TIME"},
112  {THREAD_MAXRSS, GATHER_THREAD_STATS, MAX_STATS, "%09.0f", "THREAD_MAX_RSS"},
113  {THREAD_MINFLT, GATHER_THREAD_STATS, MAX_STATS, "%09.0f", "THREAD_MINOR_PG_FAULTS"},
114  {THREAD_MAJFLT, GATHER_THREAD_STATS, MAX_STATS, "%09.0f", "THREAD_MAJOR_PG_FAULTS"},
115  {THREAD_VCSW, GATHER_THREAD_STATS, MAX_STATS, "%09.0f", "THREAD_VOL_CTX_SW"},
116  {THREAD_IVCSW, GATHER_THREAD_STATS, MAX_STATS, "%09.0f", "THREAD_INVOL_CTX_SW"},
117  {MSU_STAT1, GATHER_CUSTOM_STATS, MAX_STATS, "%03.0f", "MSU_STAT1"},
118  {MSU_STAT2, GATHER_CUSTOM_STATS, MAX_STATS, "%03.0f", "MSU_STAT2"},
119  {MSU_STAT3, GATHER_CUSTOM_STATS, MAX_STATS, "%03.0f", "MSU_STAT3"},
120  {PROF_ENQUEUE, GATHER_PROFILING, MAX_STATS, "%0.0f", "ENQUEUE"},
121  {PROF_DEQUEUE, GATHER_PROFILING, MAX_STATS, "%0.0f", "DEQUEUE"},
122  {PROF_REMOTE_SEND, GATHER_PROFILING, MAX_STATS, "%0.0f", "SEND"},
123  {PROF_REMOTE_RECV, GATHER_PROFILING, MAX_STATS, "%0.0f", "RECV"},
124  {PROF_DEDOS_ENTRY, GATHER_PROFILING, MAX_STATS, "%0.0f", "ENTRY"},
125  {PROF_DEDOS_EXIT, GATHER_PROFILING, MAX_STATS, "%0.0f", "EXIT"},
126 };
127 
129 #define N_STAT_TYPES (sizeof(stat_types) / sizeof(*stat_types))
130 
131 #define CHECK_INITIALIZATION \
132  if (!stats_initialized) { \
133  log_error("Statistics not initialized!"); \
134  return -1; \
135  }
136 
137 /* TODO: Incremental flushes of raw items, followed by eventual conversion
138 static inline int flush_raw_item_to_log(FILE *file, struct stat_type *type,
139  int item_idx) {
140  struct stat_item *item = &type->items[item_idx];
141  int max_stats = type->max_stats;
142 
143  fwrite(&type->id, sizeof(type->id), 1, file);
144  int label_len = strlen(type->label) + 1;
145  fwrite(&label_len, sizeof(label_len), 1, file);
146  fwrite(type->label, sizeof(char), label_len, file);
147  int fmt_len = strlen(type->format) + 1;
148  fwrite(&fmt_len, sizeof(fmt_len), 1, file);
149  fwrite(type->format, sizeof(char), fmt_len, file);
150 
151  fwrite(&item->id, sizeof(item->id), 1, file);
152  fwrite(&item->write_index, sizeof(item->write_index), 1, file);
153  fwrite(&item->rolled_over, sizeof(item->rolled_over), 1, file);
154  int n_stats = item->rolled_over ? max_stats : item->write_index;
155  fwrite(&n_stats, sizeof(n_stats), 1, file);
156  fwrite(item->stats, sizeof(*item->stats), n_stats, file);
157 
158  return 0;
159 }
160 
161 static int convert_raw_item_from_log(FILE *in_file, FILE *out_file) {
162  struct stat_type type;
163  struct stat_item item;
164 
165  fread(&type.id, sizeof(type.id), 1, in_file);
166  int label_len;
167  fread(&label_len, sizeof(label_len), 1, in_file);
168  fread(type.label, sizeof(char), label_len, in_file);
169 
170  int fmt_len;
171  fread(&fmt_len, sizeof(fmt_len), 1, in_file);
172  fread(type.format, sizeof(char), fmt_len, in_file);
173 
174  fread(&item.id, sizeof(item.id), 1, in_file);
175  fread(&item.write_index, sizeof(item.write_index), 1, in_file);
176  fread(&item.rolled_over, sizeof(item.rolled_over), 1, in_file);
177  int n_stats;
178  fread(&n_stats, sizeof(n_stats), 1, in_file);
179  item.stats = malloc(sizeof(*item.stats) * n_stats);
180  fread(item.stats, sizeof(*item.stats), n_stats, in_file);
181 
182  return write_item_to_log(out_file, &type, &item);
183 }
184 */
185 
187 static int write_item_to_log(FILE *out_file, struct stat_type *type, struct stat_item *item) {
188 
189  char label[64];
190  int ln = snprintf(label, 64, "%s:%02d:", type->label, item->id);
191  label[ln] = '\0';
192  //size_t label_size = strlen(label);
193 
194  int n_stats = item->rolled_over ? type->max_stats : item->write_index;
195  for (int i=0; i<n_stats; i++) {
196  int rtn;
197  if ((rtn = fprintf(out_file, "%s", label)) > 30 || rtn < 0) {
198  log_warn("printed out %d characters while outputting value %d", rtn, i);
199  }
200 
201  //fwrite(label, 1, label_size, out_file);
202  if ((rtn = fprintf(out_file, "%010ld.%09ld:",
203  (long int)item->stats[i].time.tv_sec,
204  (long int)item->stats[i].time.tv_nsec)) > 30 || rtn < 0) {
205  log_warn("Printed out %d characters outputting value %d",rtn, i);
206  }
207  if ((rtn = fprintf(out_file, type->format, item->stats[i].value)) > 30 || rtn < 0) {
208  log_warn("printed out %d characters while outputting value %d", rtn, i);
209  }
210  fprintf(out_file, "%s", "\n");
211  }
212  log(LOG_STATS, "Flushed %d items for type %s (rollover: %d)", n_stats, type->label, item->rolled_over);
213  return 0;
214 }
215 
216 
221 static void get_elapsed_time(struct timespec *t) {
222  clock_gettime(CLOCK_ID, t);
223  //t->tv_sec -= stats_start_time.tv_sec;
224 }
225 
227 static inline int wrlock_type(struct stat_type *type) {
228  if (pthread_rwlock_wrlock(&type->lock) != 0) {
229  log_error("Error locking stat type %s", type->label);
230  return -1;
231  }
232  return 0;
233 }
234 
236 static inline int unlock_type(struct stat_type *type) {
237  if (pthread_rwlock_unlock(&type->lock) != 0) {
238  log_error("Error unlocking stat type %s", type->label);
239  return -1;
240  }
241  return 0;
242 }
243 
245 static inline int rlock_type(struct stat_type *type) {
246  if (pthread_rwlock_rdlock(&type->lock) != 0) {
247  log_error("Error locking stat type %s", type->label);
248  return -1;
249  }
250  return 0;
251 }
252 
257 static inline int lock_item(struct stat_item *item) {
258  if ( pthread_mutex_lock(&item->mutex) != 0) {
259  log_error("Error locking mutex for an item with ID %u", item->id);
260  return -1;
261  }
262  return 0;
263 }
264 
269 static inline int unlock_item(struct stat_item *item) {
270  if ( pthread_mutex_unlock(&item->mutex) != 0) {
271  log_error("Error locking mutex for an item with ID %u", item->id);
272  return -1;
273  }
274  return 0;
275 }
276 
278 static void UNUSED flush_all_stats_to_log(FILE *file) {
279  for (int i=0; i<N_STAT_TYPES; i++) {
280  if (!stat_types[i].enabled) {
281  continue;
282  }
283  rlock_type(&stat_types[i]);
284  for (int j=0; j<stat_types[i].num_items; j++) {
285  lock_item(&stat_types[i].items[j]);
286  write_item_to_log(file, &stat_types[i], &stat_types[i].items[j]);
287  unlock_item(&stat_types[i].items[j]);
288  }
289  unlock_type(&stat_types[i]);
290  }
291 }
292 
294 static void destroy_stat_item(struct stat_item *item) {
295  free(item->stats);
296  pthread_mutex_destroy(&item->mutex);
297 }
298 
300 static struct stat_item *get_item_stat(struct stat_type *type, unsigned int item_id) {
301  if (item_id > MAX_STAT_ITEM_ID) {
302  log_error("Item ID %u too high. Max: %d", item_id, MAX_STAT_ITEM_ID);
303  return NULL;
304  }
305  int id_index = type->id_indices[item_id];
306  if (id_index == -1) {
307  log_error("Item ID %u not initialized for stat %s",
308  item_id, type->label);
309  return NULL;
310  }
311  return &type->items[id_index];
312 }
313 
314 static inline long double current_double_time() {
315  struct timespec t;
316  clock_gettime(DURATION_CLOCK_ID, &t);
317  return (long double)t.tv_sec + (long double)t.tv_nsec / 1e9;
318 }
319 
320 int record_start_time(enum stat_id stat_id, unsigned int item_id) {
322 
323  struct stat_type *type = &stat_types[stat_id];
324  if (!type->enabled) {
325  return 0;
326  }
327  if (rlock_type(type)) {
328  return -1;
329  }
330  struct stat_item *item = get_item_stat(type, item_id);
331  unlock_type(type);
332  if (item == NULL) {
333  return -1;
334  }
335 
336  if (lock_item(item)) {
337  return -1;
338  }
339  get_elapsed_time(&item->stats[item->write_index].time);
340  item->stats[item->write_index].value = current_double_time();
341  unlock_item(item);
342 
343  return 0;
344 }
345 
346 #if DUMP_STATS
347 #define WARN_ROLLOVER(label, item_id) \
348  log_warn("Stats for type %s.%u rolling over. Log file will be missing data", \
349  label, item_id);
350 #else
351 #define WARN_ROLLOVER(label, item_id)
352 #endif
353 
354 
355 int record_end_time(enum stat_id stat_id, unsigned int item_id) {
357 
358  long double new_time = current_double_time();
359 
360  struct stat_type *type = &stat_types[stat_id];
361  if (!type->enabled) {
362  return 0;
363  }
364  if (rlock_type(type)) {
365  return -1;
366  }
367  struct stat_item *item = get_item_stat(type, item_id);
368  unlock_type(type);
369  if (item == NULL) {
370  return -1;
371  }
372 
373  if (lock_item(item)) {
374  return -1;
375  }
376 
377  item->stats[item->write_index].value = new_time - item->stats[item->write_index].value;
378  item->write_index++;
379 
380  if (item->write_index == type->max_stats) {
381  WARN_ROLLOVER(type->label, item_id);
382  item->write_index = 0;
383  item->rolled_over = true;
384  }
385 
386  unlock_item(item);
387  return 0;
388 }
389 int increment_stat(enum stat_id stat_id, unsigned int item_id, double value) {
391 
392  struct stat_type *type = &stat_types[stat_id];
393  if (!type->enabled) {
394  return 0;
395  }
396  if (rlock_type(type)) {
397  return -1;
398  }
399  struct stat_item *item = get_item_stat(type, item_id);
400  unlock_type(type);
401  if (item == NULL) {
402  return -1;
403  }
404 
405  if (lock_item(item)) {
406  return -1;
407  }
408 
409  get_elapsed_time(&item->stats[item->write_index].time);
410  int last_index = item->write_index - 1;
411  if ( last_index < 0 ) {
412  last_index = type->max_stats - 1;
413  }
414  item->stats[item->write_index].value = item->stats[last_index].value + value;
415  item->write_index++;
416  if (item->write_index == type->max_stats) {
417  WARN_ROLLOVER(type->label, item_id);
418  item->write_index = 0;
419  item->rolled_over = true;
420  }
421 
422  unlock_item(item);
423  return 0;
424 }
425 
426 int record_stat(enum stat_id stat_id, unsigned int item_id, double stat, bool relog) {
428 
429  struct stat_type *type = &stat_types[stat_id];
430  if (!type->enabled) {
431  return 0;
432  }
433  if (rlock_type(type)) {
434  return -1;
435  }
436  struct stat_item *item = get_item_stat(type, item_id);
437  unlock_type(type);
438  if (item == NULL) {
439  return -1;
440  }
441 
442  if (lock_item(item)) {
443  return -1;
444  }
445 
446  int last_index = item->write_index - 1;
447  bool do_log = true;
448  if (last_index >= 0) {
449  do_log = item->stats[last_index].value != stat;
450  }
451 
452  if (relog || do_log) {
453  get_elapsed_time(&item->stats[item->write_index].time);
454  item->stats[item->write_index].value = stat;
455  item->write_index++;
456  if (item->write_index == type->max_stats) {
457  WARN_ROLLOVER(type->label, item_id);
458  item->write_index = 0;
459  item->rolled_over = true;
460  }
461  }
462  unlock_item(item);
463  return 0;
464 }
465 
466 double get_last_stat(enum stat_id stat_id, unsigned int item_id) {
468 
469  struct stat_type *type = &stat_types[stat_id];
470  if (!type->enabled) {
471  return 0;
472  }
473  if (rlock_type(type)) {
474  return -1;
475  }
476  struct stat_item *item = get_item_stat(type, item_id);
477  unlock_type(type);
478  if (item == NULL) {
479  return -1;
480  }
481  if (lock_item(item)) {
482  return -1;
483  }
484 
485  int last_index = item->write_index - 1;
486  double last_stat = 0;
487  if (last_index >= 0) {
488  last_stat = item->stats[last_index].value;
489  }
490 
491  unlock_item(item);
492 
493  return last_stat;
494 }
495 
497 static inline int time_cmp(struct timespec *t1, struct timespec *t2) {
498  return t1->tv_sec > t2->tv_sec ? 1 :
499  ( t1->tv_sec < t2->tv_sec ? -1 :
500  ( t1->tv_nsec > t2->tv_nsec ? 1 :
501  ( t1->tv_nsec < t2->tv_nsec ? -1 : 0 ) ) );
502 }
503 
505 static int find_time_index(struct timed_stat *stats, struct timespec *time,
506  int start_index, int stat_size) {
507  if (start_index < 0) {
508  start_index = stat_size - 1;
509  }
510  if (time_cmp(&stats[start_index].time, time) <= 0) {
511  return start_index;
512  }
513  int search_index = start_index - 1;
514  do {
515  if (search_index < 0) {
516  search_index = stat_size - 1;
517  }
518  if (time_cmp(&stats[search_index].time, time) <= 0) {
519  return search_index;
520  }
521  search_index--;
522  } while (search_index != start_index - 1);
523  return -1;
524 }
525 
527 static int sample_stat_item(struct stat_item *item, int stat_size,
528  struct timespec *sample_end, struct timespec *interval, int sample_size,
529  struct timed_stat *sample) {
530  struct timespec sample_time = *sample_end;
531  if (lock_item(item)) {
532  return -1;
533  }
534  int sample_index = item->write_index - 1;
535  if (sample_index < 0) {
536  sample_index = stat_size + sample_index;
537  }
538  int max_index = item->rolled_over ? stat_size : item->write_index;
539  int i;
540  for (i=0; i<sample_size; i++) {
541  if (max_index > 1) {
542  sample_index = find_time_index(item->stats, &sample_time, sample_index, max_index);
543  } else {
544  sample_index = 0;
545  }
546  if (sample_index < 0) {
547  log(LOG_STATS, "Couldn't find time index for item %d", item->id);
548  unlock_item(item);
549  return i;
550  }
551  sample[sample_size - i - 1].value = item->stats[sample_index].value;
552  sample[sample_size - i - 1].time = sample_time;
553  sample_time.tv_nsec -= interval->tv_nsec;
554  if (sample_time.tv_nsec < 0) {
555  sample_time.tv_sec -= 1;
556  sample_time.tv_nsec = 1e9 + sample_time.tv_nsec;
557  }
558  sample_time.tv_sec -= interval->tv_sec;
559  }
560  unlock_item(item);
561  return i;
562 }
563 
565 static int sample_stat(enum stat_id stat_id, struct timespec *end, struct timespec *interval,
566  int sample_size, struct stat_sample *sample, int n_samples) {
568 
569  struct stat_type *type = &stat_types[stat_id];
570  if (!type->enabled) {
571  log_error("Attempted to sample disabled statistic %d", stat_id);
572  return -1;
573  }
574 
575 
576  if (n_samples < type->num_items) {
577  log_error("Not enough samples (%d) to fit all stat items (%d)", n_samples, type->num_items);
578  return -1;
579  }
580 
581  int item_id = -1;
582  for (int i=0; i<type->num_items; i++) {
583  sample[i].hdr.stat_id = stat_id;
584  // Find the next item index
585  for (item_id += 1;
586  item_id < MAX_STAT_ITEM_ID && type->id_indices[item_id] == -1;
587  item_id++) {}
588  log(LOG_TEST, "Found :%d", item_id);
589  if (item_id == MAX_STAT_ITEM_ID) {
590  log_error("Cannot find item ID %d", i);
591  return -1;
592  }
593  int idx = type->id_indices[item_id];
594  sample[i].hdr.item_id = type->items[idx].id;
595  sample[i].hdr.n_stats = sample_size;
596  int rtn = sample_stat_item(&type->items[idx], type->max_stats, end, interval, sample_size,
597  sample[i].stats);
598 
599  if ( rtn < 0) {
600  log_error("Error sampling item %d (idx %d)", type->items[i].id, i);
601  return -1;
602  } else {
603  sample[i].hdr.n_stats = rtn;
604  }
605  }
606  return type->num_items;
607 }
608 
612 static int UNUSED sample_recent_stats(enum stat_id stat_id, struct timespec *interval,
613  int sample_size, struct stat_sample *sample, int n_samples) {
615  struct stat_type *type = &stat_types[stat_id];
616  if (!type->enabled) {
617  return 0;
618  }
619  struct timespec now;
620  get_elapsed_time(&now);
621  return sample_stat(stat_id, &now, interval, sample_size, sample, n_samples);
622 }
623 
627 
629 static struct timespec stat_sample_interval = {
630  .tv_sec = (STAT_SAMPLE_PERIOD_MS / 1000) / STAT_SAMPLE_SIZE,
631  .tv_nsec = (int)(STAT_SAMPLE_PERIOD_MS * 1e6 / STAT_SAMPLE_SIZE) % (int)1e9
632 };
633 
634 struct stat_sample *get_stat_samples(enum stat_id stat_id, struct timespec *sample_time,
635  int *n_samples_out) {
636  if (!stats_initialized) {
637  log_error("Stats not initialized");
638  return NULL;
639  }
640 
641  *n_samples_out = 0;
642  int i;
643  for (i=0; i < N_REPORTED_STAT_TYPES; i++) {
644  if (reported_stat_types[i].id == stat_id) {
645  break;
646  }
647  }
648  if (i == N_REPORTED_STAT_TYPES) {
649  log_error("%d is not a reported stat type", stat_id);
650  return NULL;
651  }
652  if (stat_samples[i] == NULL) {
653  // No stat samples registered!"
654  return NULL;
655  }
656  struct stat_type *type = &stat_types[stat_id];
657  if (rlock_type(type)) {
658  return NULL;
659  }
660  int rtn = sample_stat(stat_id, sample_time, &stat_sample_interval, STAT_SAMPLE_SIZE,
661  stat_samples[i], type->num_items);
662  unlock_type(type);
663  if (rtn < 0) {
664  log_error("Error sampling recent stats");
665  return NULL;
666  }
667  *n_samples_out = rtn;
668  return stat_samples[i];
669 }
670 
672 static void realloc_stat_samples(enum stat_id stat_id, int n_samples) {
673  for (int i=0; i < N_REPORTED_STAT_TYPES; i++) {
674  if (reported_stat_types[i].id == stat_id) {
675  free_stat_samples(stat_samples[i], n_samples - 1);
676  stat_samples[i] = init_stat_samples(STAT_SAMPLE_SIZE, n_samples);
677  log(LOG_STAT_REALLOC, "Reallocated %d to %d samples", stat_id, n_samples);
678  return;
679  }
680  }
681 }
682 
683 int remove_stat_item(enum stat_id stat_id, unsigned int item_id) {
685 
686  struct stat_type *type = &stat_types[stat_id];
687  if (!type->enabled) {
688  return 0;
689  }
690  if (wrlock_type(type) != 0) {
691  return -1;
692  }
693  if (type->id_indices[item_id] == -1) {
694  log_error("Item ID %u not assigned an index", item_id);
695  return -1;
696  }
697 
698  int index = type->id_indices[item_id];
699  struct stat_item *item = &type->items[index];
700 
701  item->id = -1;
702  item->write_index = 0;
703  item->rolled_over = false;
704  free(item->stats);
705 
706  type->id_indices[item_id] = -1;
707  type->num_items--;
708  unlock_type(type);
709  return 0;
710 }
711 
712 
719 int init_stat_item(enum stat_id stat_id, unsigned int item_id) {
721 
722  struct stat_type *type = &stat_types[stat_id];
723  if (!type->enabled) {
724  log(LOG_STATS, "Skipping initialization of type %s item %d",
725  type->label, item_id);
726  return 0;
727  }
728  if (wrlock_type(type) != 0) {
729  return -1;
730  }
731  if (type->id_indices[item_id] != -1) {
732  log_error("Item ID %u already assigned index %d for stat %s",
733  item_id, type->id_indices[item_id], type->label);
734  return -1;
735  }
736  int index;
737  for (index=0; index<type->max_items; index++) {
738  if (type->items[index].id == -1) {
739  break;
740  }
741  }
742  if (index == type->max_items) {
743  type->max_items++;
744  type->items = realloc(type->items, sizeof(*type->items) * (type->max_items));
745  realloc_stat_samples(stat_id, type->max_items);
746 
747  if ( type->items == NULL ) {
748  log_error("Error reallocating stat item");
749  return -1;
750  }
751  }
752  log(LOG_STATS, "Item %u has index %d", item_id, index);
753  struct stat_item *item = &type->items[index];
754 
755  if (pthread_mutex_init(&item->mutex,NULL)) {
756  log_error("Error initializing item %u lock", item_id);
757  return -1;
758  }
759 
760  item->id = item_id;
761  item->write_index = 0;
762  item->rolled_over = false;
763  item->stats = calloc(type->max_stats, sizeof(*item->stats));
764 
765  if (item->stats == NULL) {
766  log_error("Error calloc'ing item statistics");
767  return -1;
768  }
769 
770  type->id_indices[item_id] = index;
771 
772  type->num_items++;
773 
774  if (unlock_type(type) != 0) {
775  return -1;
776  }
777 
778  log(LOG_STAT_INITS, "Initialized stat item %s.%d (idx: %d)",
779  stat_types[stat_id].label, item_id, index);
780  return 0;
781 }
782 
784  if ( stats_initialized ) {
785  log_error("Statistics have already been initialized");
786  return -1;
787  }
788 
789  clock_gettime(CLOCK_ID, &stats_start_time);
790 
791  for (int i = 0; i < N_STAT_TYPES; i++) {
792  struct stat_type *type = &stat_types[i];
793  if ( type->id != i ) {
794  log_error("Stat type %s at incorrect index (%d, not %d)! Disabling!!",
795  type->label, i, type->id);
796  type->enabled = 0;
797  }
798 
799  if (type->enabled) {
800  if (pthread_rwlock_init(&type->lock, NULL) != 0) {
801  log_warn("Error initializing lock for stat %s. Disabling!", type->label);
802  type->enabled = 0;
803  }
804  type->num_items = 0;
805  type->items = NULL;
806  for (int j = 0; j < MAX_STAT_ITEM_ID; j++) {
807  type->id_indices[j] = -1;
808  }
809  log(LOG_STATS, "Initialized stat type %s (%d ids)", type->label, MAX_STAT_ITEM_ID);
810  }
811  }
812 
813 #if DUMP_STATS
814  log_info("Statistics initialized for eventual dump to log file");
815 #else
816  log_info("Statistics initialized. Will **NOT** be dumped to log file");
817 #endif
818  stats_initialized = true;
819  return 0;
820 }
821 
822 void finalize_statistics(char *statlog) {
823  if (statlog != NULL) {
824 #if DUMP_STATS
825  log_info("Outputting stats to log. Do not quit.");
826  FILE *file = fopen(statlog, "w");
828  fclose(file);
829  log_info("Finished outputting stats to %s", statlog);
830 #else
831  log_info("Skipping dump to stat log. Set DUMP_STATS=1 to enable");
832 #endif
833  }
834 
835  for (int i = 0; i < N_STAT_TYPES; i++) {
836  struct stat_type *type = &stat_types[i];
837  if (type->enabled) {
838  wrlock_type(type);
839  for (int j = 0; j < type->num_items; j++) {
840  log(LOG_STAT_INITS, "Destroying item %s.idx=%d",
841  type->label, j);
842  destroy_stat_item(&type->items[j]);
843  }
844  free(type->items);
845  unlock_type(type);
846  pthread_rwlock_destroy(&type->lock);
847  }
848  }
849 }
void finalize_statistics(char *statlog)
Writes the statistics to statlog if provided, and frees assocated structure.
Definition: rt_stats.c:822
static struct stat_sample * stat_samples[N_REPORTED_STAT_TYPES]
A statically allocated stat_sample structure which is pre-allocated to store samples from each stat t...
Definition: rt_stats.c:626
Collecting statistics within the runtime.
static int write_item_to_log(FILE *out_file, struct stat_type *type, struct stat_item *item)
Writes a single stat item to the log file.
Definition: rt_stats.c:187
#define CLOCK_ID
The clock used to mark the time at which events take place.
Definition: rt_stats.c:37
static int wrlock_type(struct stat_type *type)
Locks a stat type for writing.
Definition: rt_stats.c:227
double get_last_stat(enum stat_id stat_id, unsigned int item_id)
Returns the last statistic recorded.
Definition: rt_stats.c:466
#define DURATION_CLOCK_ID
Definition: rt_stats.c:38
#define log_info(fmt,...)
Definition: logging.h:88
struct timed_stat * stats
The statistics in question.
Definition: stats.h:58
struct stat_type stat_types[]
The list of all statistics that can be gathered in the system.
Definition: rt_stats.c:96
char * format
Format for printf.
Definition: rt_stats.c:66
A single stat sample for a single item.
Definition: stats.h:53
struct stat_sample * init_stat_samples(int max_stats, int n_samples)
Initilizes n sets of samples of statistics, each of which contains max_stats points.
Definition: stats.c:80
#define GATHER_MSU_STATS
Definition: rt_stats.c:85
#define CHECK_INITIALIZATION
Definition: rt_stats.c:131
#define STAT_SAMPLE_PERIOD_MS
How often samples are sent from runtime to controller.
Definition: stats.h:132
static struct timespec stat_sample_interval
The interval at which stats should be sampled.
Definition: rt_stats.c:629
Profiling.
Definition: stat_ids.h:60
stat_id
The identifiers with which stats can be logged.
Definition: stat_ids.h:32
static int time_cmp(struct timespec *t1, struct timespec *t2)
Returns 1 if t1 > t2, -1 if t2 > t1, 0 otherwise.
Definition: rt_stats.c:497
struct stat_item * items
The items of this type being logged.
Definition: rt_stats.c:72
static void realloc_stat_samples(enum stat_id stat_id, int n_samples)
Reallocates the existing stats structure to store the provided number of samples. ...
Definition: rt_stats.c:672
int record_start_time(enum stat_id stat_id, unsigned int item_id)
Starts a measurement of elapsed time.
Definition: rt_stats.c:320
#define WARN_ROLLOVER(label, item_id)
Definition: rt_stats.c:351
unsigned int id
A unique identifier for the item being logged.
Definition: rt_stats.c:52
Logging of status messages to the terminal.
static struct stat_type_label reported_stat_types[]
Static structure so the reported stat types can be referenced as an array.
Definition: stats.h:105
Functions for the sending and receiving of statistics between ctrl and runtime.
static long double current_double_time()
Definition: rt_stats.c:314
int max_items
Number of items allocated for logging.
Definition: rt_stats.c:70
The structure holding all items of a type of stats.
Definition: rt_stats.c:62
int n_stats
The size of the sample (number of stats, not number of items)
Definition: stats.h:49
enum stat_id stat_id
The ID of the statistic being sampled.
Definition: stats.h:45
static void destroy_stat_item(struct stat_item *item)
Frees the memory associated with an individual stat item.
Definition: rt_stats.c:294
struct timespec time
Definition: stats.h:38
The internal statistics structure where stats are aggregated One per statistic-item.
Definition: rt_stats.c:50
static int sample_stat_item(struct stat_item *item, int stat_size, struct timespec *sample_end, struct timespec *interval, int sample_size, struct timed_stat *sample)
Takes a sample of the provided stat item and stores it in sample
Definition: rt_stats.c:527
For custom MSU statistics.
Definition: stat_ids.h:53
#define log_error(fmt,...)
Definition: logging.h:101
#define MAX_STAT_ITEM_ID
Maxmimum identifier that can be assigned to a stat item.
Definition: stats.h:126
int init_stat_item(enum stat_id stat_id, unsigned int item_id)
Initializes a stat item so that statistics can be logged to it.
Definition: rt_stats.c:719
#define GATHER_CUSTOM_STATS
Definition: rt_stats.c:86
int remove_stat_item(enum stat_id stat_id, unsigned int item_id)
Un-registers an item so it can no longer have statistics registered, and will not be reported to the ...
Definition: rt_stats.c:683
#define N_STAT_TYPES
The number of types of statistics.
Definition: rt_stats.c:129
Profiling.
Definition: stat_ids.h:68
static int find_time_index(struct timed_stat *stats, struct timespec *time, int start_index, int stat_size)
Finds the index at which the given time occurred in the stats.
Definition: rt_stats.c:505
static struct stat_item * get_item_stat(struct stat_type *type, unsigned int item_id)
Gets the stat item associated with the given item_id in the given type.
Definition: rt_stats.c:300
int init_statistics()
Initializes the entire stats module.
Definition: rt_stats.c:783
#define N_REPORTED_STAT_TYPES
Number of reported stat types.
Definition: stats.h:109
static int UNUSED sample_recent_stats(enum stat_id stat_id, struct timespec *interval, int sample_size, struct stat_sample *sample, int n_samples)
Samples sample_size most recent stats in interval intervals.
Definition: rt_stats.c:612
static int lock_item(struct stat_item *item)
Locks an item_stats structure, printing an error message on failure.
Definition: rt_stats.c:257
unsigned int item_id
The ID for the item being sampled.
Definition: stats.h:47
static int rlock_type(struct stat_type *type)
Locks a stat type for reading.
Definition: rt_stats.c:245
static void get_elapsed_time(struct timespec *t)
Gets the amount of time that has elapsed since logging started.
Definition: rt_stats.c:221
#define GATHER_PROFILING
Definition: rt_stats.c:92
char * label
Name to output for this statistic.
Definition: rt_stats.c:67
int write_index
The write index in the circular buffer.
Definition: rt_stats.c:53
#define STAT_SAMPLE_SIZE
Number of statistics sampled in each send from runtime to controller.
Definition: stats.h:129
enum stat_id id
Stat ID as defined in stats.h.
Definition: rt_stats.c:63
static int unlock_type(struct stat_type *type)
Unlocks a stat type.
Definition: rt_stats.c:236
pthread_rwlock_t lock
Lock for adding new stat items.
Definition: rt_stats.c:71
int record_stat(enum stat_id stat_id, unsigned int item_id, double stat, bool relog)
Records a statistic in the statlog.
Definition: rt_stats.c:426
#define UNUSED
bool enabled
If true, logging for this item is enabled.
Definition: rt_stats.c:64
#define MAX_STATS
The maximum number of statistics that can be held in the rrdb.
Definition: rt_stats.c:79
static void UNUSED flush_all_stats_to_log(FILE *file)
Writes all statistics to the provided log file.
Definition: rt_stats.c:278
int num_items
Number of items currently registered for logging.
Definition: rt_stats.c:69
int max_stats
Maximum number of statistics that can be held in memory.
Definition: rt_stats.c:65
Profiling.
Definition: stat_ids.h:62
pthread_mutex_t mutex
Lock for changing stat item.
Definition: rt_stats.c:56
bool rolled_over
Whether the stats structure has rolled over at least once.
Definition: rt_stats.c:54
Profiling.
Definition: stat_ids.h:58
struct timed_stat * stats
Timestamp and data for each gathered statistic.
Definition: rt_stats.c:55
For custom MSU statistics.
Definition: stat_ids.h:54
Profiling.
Definition: stat_ids.h:66
Holds a single timestamped value.
Definition: stats.h:37
int increment_stat(enum stat_id stat_id, unsigned int item_id, double value)
Increments the given statistic by the provided value.
Definition: rt_stats.c:389
static int unlock_item(struct stat_item *item)
Unlocks an item_stats structure, printing an error message on failure.
Definition: rt_stats.c:269
struct stat_sample * get_stat_samples(enum stat_id stat_id, struct timespec *sample_time, int *n_samples_out)
Samples the statistic with the provided stat_id.
Definition: rt_stats.c:634
#define log(level, fmt,...)
Log at a custom level.
Definition: logging.h:147
int id_indices[MAX_STAT_ITEM_ID]
Index at which the IDs are stored.
Definition: rt_stats.c:68
Profiling.
Definition: stat_ids.h:64
#define GATHER_THREAD_STATS
Definition: rt_stats.c:87
static bool stats_initialized
Whether init_statistics has been called.
Definition: rt_stats.c:43
struct stat_sample_hdr hdr
Definition: stats.h:54
#define log_warn(fmt,...)
Definition: logging.h:113
void free_stat_samples(struct stat_sample *sample, int n_samples)
Frees a set of stat samples.
Definition: stats.c:71
static int sample_stat(enum stat_id stat_id, struct timespec *end, struct timespec *interval, int sample_size, struct stat_sample *sample, int n_samples)
Takes a sample of the provided stat type (stat_id) and places it in sample
Definition: rt_stats.c:565
static struct timespec stats_start_time
The time at which the runtime started.
Definition: rt_stats.c:41
For custom MSU statistics.
Definition: stat_ids.h:55
long double value
Definition: stats.h:39
int record_end_time(enum stat_id stat_id, unsigned int item_id)
Records the elapsed time since the previous call to record_start_time.
Definition: rt_stats.c:355