31 #include <stdio_ext.h>
37 #define CLOCK_ID CLOCK_REALTIME_COARSE
38 #define DURATION_CLOCK_ID CLOCK_MONOTONIC
76 #define MAX_STATS 262144
79 #define MAX_STATS 8192
83 #define GATHER_STATS 1
85 #define GATHER_MSU_STATS 1 & GATHER_STATS
86 #define GATHER_CUSTOM_STATS 1 & GATHER_STATS
87 #define GATHER_THREAD_STATS 1 & GATHER_STATS
90 #define GATHER_PROFILING 1
92 #define GATHER_PROFILING 0
129 #define N_STAT_TYPES (sizeof(stat_types) / sizeof(*stat_types))
131 #define CHECK_INITIALIZATION \
132 if (!stats_initialized) { \
133 log_error("Statistics not initialized!"); \
190 int ln = snprintf(label, 64,
"%s:%02d:", type->
label, item->
id);
195 for (
int i=0; i<n_stats; i++) {
197 if ((rtn = fprintf(out_file,
"%s", label)) > 30 || rtn < 0) {
198 log_warn(
"printed out %d characters while outputting value %d", rtn, i);
202 if ((rtn = fprintf(out_file,
"%010ld.%09ld:",
204 (
long int)item->
stats[i].
time.tv_nsec)) > 30 || rtn < 0) {
205 log_warn(
"Printed out %d characters outputting value %d",rtn, i);
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);
210 fprintf(out_file,
"%s",
"\n");
212 log(LOG_STATS,
"Flushed %d items for type %s (rollover: %d)", n_stats, type->
label, item->
rolled_over);
228 if (pthread_rwlock_wrlock(&type->
lock) != 0) {
237 if (pthread_rwlock_unlock(&type->
lock) != 0) {
246 if (pthread_rwlock_rdlock(&type->
lock) != 0) {
258 if ( pthread_mutex_lock(&item->
mutex) != 0) {
259 log_error(
"Error locking mutex for an item with ID %u", item->
id);
270 if ( pthread_mutex_unlock(&item->
mutex) != 0) {
271 log_error(
"Error locking mutex for an item with ID %u", item->
id);
284 for (
int j=0; j<stat_types[i].
num_items; j++) {
296 pthread_mutex_destroy(&item->
mutex);
306 if (id_index == -1) {
307 log_error(
"Item ID %u not initialized for stat %s",
308 item_id, type->
label);
311 return &type->
items[id_index];
317 return (
long double)t.tv_sec + (
long double)t.tv_nsec / 1e9;
347 #define WARN_ROLLOVER(label, item_id) \
348 log_warn("Stats for type %s.%u rolling over. Log file will be missing data", \
351 #define WARN_ROLLOVER(label, item_id)
411 if ( last_index < 0 ) {
448 if (last_index >= 0) {
449 do_log = item->
stats[last_index].
value != stat;
452 if (relog || do_log) {
486 double last_stat = 0;
487 if (last_index >= 0) {
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 ) ) );
506 int start_index,
int stat_size) {
507 if (start_index < 0) {
508 start_index = stat_size - 1;
510 if (
time_cmp(&stats[start_index].time, time) <= 0) {
513 int search_index = start_index - 1;
515 if (search_index < 0) {
516 search_index = stat_size - 1;
518 if (
time_cmp(&stats[search_index].time, time) <= 0) {
522 }
while (search_index != start_index - 1);
528 struct timespec *sample_end,
struct timespec *interval,
int sample_size,
530 struct timespec sample_time = *sample_end;
535 if (sample_index < 0) {
536 sample_index = stat_size + sample_index;
540 for (i=0; i<sample_size; i++) {
546 if (sample_index < 0) {
547 log(LOG_STATS,
"Couldn't find time index for item %d", item->
id);
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;
558 sample_time.tv_sec -= interval->tv_sec;
566 int sample_size,
struct stat_sample *sample,
int n_samples) {
571 log_error(
"Attempted to sample disabled statistic %d", stat_id);
577 log_error(
"Not enough samples (%d) to fit all stat items (%d)", n_samples, type->
num_items);
588 log(LOG_TEST,
"Found :%d", item_id);
613 int sample_size,
struct stat_sample *sample,
int n_samples) {
621 return sample_stat(stat_id, &now, interval, sample_size, sample, n_samples);
635 int *n_samples_out) {
648 if (i == N_REPORTED_STAT_TYPES) {
649 log_error(
"%d is not a reported stat type", stat_id);
652 if (stat_samples[i] == NULL) {
664 log_error(
"Error sampling recent stats");
667 *n_samples_out = rtn;
668 return stat_samples[i];
677 log(LOG_STAT_REALLOC,
"Reallocated %d to %d samples", stat_id, n_samples);
694 log_error(
"Item ID %u not assigned an index", item_id);
724 log(LOG_STATS,
"Skipping initialization of type %s item %d",
725 type->
label, item_id);
732 log_error(
"Item ID %u already assigned index %d for stat %s",
737 for (index=0; index<type->
max_items; index++) {
738 if (type->
items[index].
id == -1) {
747 if ( type->
items == NULL ) {
748 log_error(
"Error reallocating stat item");
752 log(LOG_STATS,
"Item %u has index %d", item_id, index);
755 if (pthread_mutex_init(&item->
mutex,NULL)) {
756 log_error(
"Error initializing item %u lock", item_id);
765 if (item->
stats == NULL) {
766 log_error(
"Error calloc'ing item statistics");
778 log(LOG_STAT_INITS,
"Initialized stat item %s.%d (idx: %d)",
779 stat_types[stat_id].label, item_id, index);
785 log_error(
"Statistics have already been initialized");
793 if ( type->
id != i ) {
794 log_error(
"Stat type %s at incorrect index (%d, not %d)! Disabling!!",
800 if (pthread_rwlock_init(&type->
lock, NULL) != 0) {
801 log_warn(
"Error initializing lock for stat %s. Disabling!", type->
label);
809 log(LOG_STATS,
"Initialized stat type %s (%d ids)", type->
label, MAX_STAT_ITEM_ID);
814 log_info(
"Statistics initialized for eventual dump to log file");
816 log_info(
"Statistics initialized. Will **NOT** be dumped to log file");
823 if (statlog != NULL) {
825 log_info(
"Outputting stats to log. Do not quit.");
826 FILE *file = fopen(statlog,
"w");
829 log_info(
"Finished outputting stats to %s", statlog);
831 log_info(
"Skipping dump to stat log. Set DUMP_STATS=1 to enable");
839 for (
int j = 0; j < type->
num_items; j++) {
840 log(LOG_STAT_INITS,
"Destroying item %s.idx=%d",
846 pthread_rwlock_destroy(&type->
lock);
void finalize_statistics(char *statlog)
Writes the statistics to statlog if provided, and frees assocated structure.
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...
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.
#define CLOCK_ID
The clock used to mark the time at which events take place.
static int wrlock_type(struct stat_type *type)
Locks a stat type for writing.
double get_last_stat(enum stat_id stat_id, unsigned int item_id)
Returns the last statistic recorded.
#define DURATION_CLOCK_ID
#define log_info(fmt,...)
struct timed_stat * stats
The statistics in question.
struct stat_type stat_types[]
The list of all statistics that can be gathered in the system.
char * format
Format for printf.
A single stat sample for a single item.
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.
#define CHECK_INITIALIZATION
#define STAT_SAMPLE_PERIOD_MS
How often samples are sent from runtime to controller.
static struct timespec stat_sample_interval
The interval at which stats should be sampled.
stat_id
The identifiers with which stats can be logged.
static int time_cmp(struct timespec *t1, struct timespec *t2)
Returns 1 if t1 > t2, -1 if t2 > t1, 0 otherwise.
struct stat_item * items
The items of this type being logged.
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. ...
int record_start_time(enum stat_id stat_id, unsigned int item_id)
Starts a measurement of elapsed time.
#define WARN_ROLLOVER(label, item_id)
unsigned int id
A unique identifier for the item being logged.
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.
Functions for the sending and receiving of statistics between ctrl and runtime.
static long double current_double_time()
int max_items
Number of items allocated for logging.
The structure holding all items of a type of stats.
int n_stats
The size of the sample (number of stats, not number of items)
enum stat_id stat_id
The ID of the statistic being sampled.
static void destroy_stat_item(struct stat_item *item)
Frees the memory associated with an individual stat item.
The internal statistics structure where stats are aggregated One per statistic-item.
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
For custom MSU statistics.
#define log_error(fmt,...)
#define MAX_STAT_ITEM_ID
Maxmimum identifier that can be assigned to a stat item.
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.
#define GATHER_CUSTOM_STATS
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 ...
#define N_STAT_TYPES
The number of types of statistics.
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.
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.
int init_statistics()
Initializes the entire stats module.
#define N_REPORTED_STAT_TYPES
Number of reported stat types.
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.
static int lock_item(struct stat_item *item)
Locks an item_stats structure, printing an error message on failure.
unsigned int item_id
The ID for the item being sampled.
static int rlock_type(struct stat_type *type)
Locks a stat type for reading.
static void get_elapsed_time(struct timespec *t)
Gets the amount of time that has elapsed since logging started.
char * label
Name to output for this statistic.
int write_index
The write index in the circular buffer.
#define STAT_SAMPLE_SIZE
Number of statistics sampled in each send from runtime to controller.
enum stat_id id
Stat ID as defined in stats.h.
static int unlock_type(struct stat_type *type)
Unlocks a stat type.
pthread_rwlock_t lock
Lock for adding new stat items.
int record_stat(enum stat_id stat_id, unsigned int item_id, double stat, bool relog)
Records a statistic in the statlog.
bool enabled
If true, logging for this item is enabled.
#define MAX_STATS
The maximum number of statistics that can be held in the rrdb.
static void UNUSED flush_all_stats_to_log(FILE *file)
Writes all statistics to the provided log file.
int num_items
Number of items currently registered for logging.
int max_stats
Maximum number of statistics that can be held in memory.
pthread_mutex_t mutex
Lock for changing stat item.
bool rolled_over
Whether the stats structure has rolled over at least once.
struct timed_stat * stats
Timestamp and data for each gathered statistic.
For custom MSU statistics.
Holds a single timestamped value.
int increment_stat(enum stat_id stat_id, unsigned int item_id, double value)
Increments the given statistic by the provided value.
static int unlock_item(struct stat_item *item)
Unlocks an item_stats structure, printing an error message on failure.
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.
#define log(level, fmt,...)
Log at a custom level.
int id_indices[MAX_STAT_ITEM_ID]
Index at which the IDs are stored.
#define GATHER_THREAD_STATS
static bool stats_initialized
Whether init_statistics has been called.
struct stat_sample_hdr hdr
#define log_warn(fmt,...)
void free_stat_samples(struct stat_sample *sample, int n_samples)
Frees a set of stat samples.
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
static struct timespec stats_start_time
The time at which the runtime started.
For custom MSU statistics.
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.