#ifndef STATISTICS_H_ #define STATISTICS_H_ #include "helpers.h" #include "bencode.h" #include "control_ng.h" #include "graphite.h" // "gauge" style stats struct global_stats_gauge { #define F(x) atomic64 x; #include "gauge_stats_fields.inc" #undef F }; // high/low water marks struct global_gauge_min_max { struct global_stats_gauge min; struct global_stats_gauge max; }; // "sampled" style stats struct global_stats_sampled_fields { #define F(x) atomic64 x; #define FA(x, n) atomic64 x[n]; #include "sampled_stats_fields.inc" #undef F #undef FA }; struct global_stats_sampled { struct global_stats_sampled_fields sums; struct global_stats_sampled_fields sums_squared; struct global_stats_sampled_fields counts; }; struct global_sampled_min_max { struct global_stats_sampled_fields min; struct global_stats_sampled_fields max; }; struct global_sampled_avg { struct global_stats_sampled_fields avg; struct global_stats_sampled_fields stddev; }; // "counter" style stats that are incremental and are kept cumulative or per-interval struct global_stats_counter { #define F(x) atomic64 x; #define FA(x, n) atomic64 x[n]; #include "counter_stats_fields.inc" #undef F #undef FA }; struct global_rate_min_max { struct global_stats_counter min; struct global_stats_counter max; }; struct global_rate_min_max_avg { struct global_stats_counter min; struct global_stats_counter max; struct global_stats_counter avg; }; struct codec_stats { char *chain; char *chain_brief; int num_transcoders; // 3 entries: [0] and [1] for per-second stats, [2] for total count // last_tv_sec keeps track of rollovers int last_tv_sec[2]; atomic64 packets_input[3]; atomic64 bytes_input[3]; atomic64 pcm_samples[3]; }; struct stats_metric { char *label; char *descr; char *value_short; char *value_long; char *value_raw; int64_t int_value; double double_value; int is_bracket; int is_close_bracket; int is_brace; int is_follow_up; int is_int; int is_double; const char *prom_name; const char *prom_type; char *prom_label; }; TYPED_GQUEUE(stats_metric, stats_metric) struct call_stats { int64_t last_packet_us; struct stream_stats totals[4]; /* rtp in, rtcp in, rtp out, rtcp out */ }; extern int64_t rtpe_started; extern mutex_t rtpe_codec_stats_lock; extern codec_stats_ht rtpe_codec_stats; extern struct global_stats_gauge rtpe_stats_gauge; // master values extern struct global_gauge_min_max rtpe_gauge_min_max; // master lifetime min/max #define RTPE_GAUGE_SET_MIN_MAX(field, min_max_struct, val) \ do { \ atomic64_min(&min_max_struct.min.field, val); \ atomic64_max(&min_max_struct.max.field, val); \ } while (0) #define RTPE_GAUGE_SET(field, num) \ do { \ atomic64_set_na(&rtpe_stats_gauge.field, num); \ RTPE_GAUGE_SET_MIN_MAX(field, rtpe_gauge_min_max, num); \ if (graphite_is_enabled()) \ RTPE_GAUGE_SET_MIN_MAX(field, rtpe_gauge_graphite_min_max, num); \ } while (0) #define RTPE_GAUGE_ADD(field, num) \ do { \ uint64_t __old = atomic64_add_na(&rtpe_stats_gauge.field, num); \ RTPE_GAUGE_SET_MIN_MAX(field, rtpe_gauge_min_max, __old + num); \ if (graphite_is_enabled()) \ RTPE_GAUGE_SET_MIN_MAX(field, rtpe_gauge_graphite_min_max, __old + num); \ } while (0) #define RTPE_GAUGE_INC(field) RTPE_GAUGE_ADD(field, 1) #define RTPE_GAUGE_DEC(field) RTPE_GAUGE_ADD(field, -1) extern struct global_stats_sampled rtpe_stats_sampled; // master cumulative values extern struct global_sampled_min_max rtpe_sampled_min_max; // master lifetime min/max #define RTPE_STATS_SAMPLE(field, num) \ do { \ atomic64_add_na(&rtpe_stats_sampled.sums.field, num); \ atomic64_add_na(&rtpe_stats_sampled.sums_squared.field, num * num); \ atomic64_inc_na(&rtpe_stats_sampled.counts.field); \ RTPE_GAUGE_SET_MIN_MAX(field, rtpe_sampled_min_max, num); \ RTPE_GAUGE_SET_MIN_MAX(field, rtpe_sampled_graphite_min_max, num); \ } while (0) // TODO: ^ skip doing this for graphite if it's not actually enabled #define RTPE_SAMPLE_SFD(field, num, sfd) \ do { \ RTPE_STATS_SAMPLE(field, num); \ if (sfd) { \ struct local_intf *__intf = sfd->local_intf; \ atomic64_add_na(&__intf->stats->sampled.sums.field, num); \ atomic64_add_na(&__intf->stats->sampled.sums_squared.field, num * num); \ atomic64_inc_na(&__intf->stats->sampled.counts.field); \ } \ } while (0) extern struct global_stats_counter *rtpe_stats; // total, cumulative, master extern struct global_stats_counter rtpe_stats_rate; // per-second, calculated once per timer run extern struct global_stats_counter rtpe_stats_intv; // per-second, calculated once per timer run #define RTPE_STATS_ADD(field, num) atomic64_add_na(&rtpe_stats->field, num) #define RTPE_STATS_INC(field) RTPE_STATS_ADD(field, 1) void statistics_update_oneway(call_t *); void statistics_update_ip46_inc_dec(call_t *, int op); void statistics_update_foreignown_dec(call_t *); void statistics_update_foreignown_inc(call_t * c); stats_metric_q *statistics_gather_metrics(struct interface_sampled_rate_stats *); void statistics_free_metrics(stats_metric_q *); G_DEFINE_AUTOPTR_CLEANUP_FUNC(stats_metric_q, statistics_free_metrics) const char *statistics_ng(ng_command_ctx_t *); enum thread_looper_action call_rate_stats_updater(void); /** * Calculation of the call rate counters. * If used with the `stats_rate_min_max()` must only be called in advance, so before that. */ INLINE void stats_counters_calc_rate(const struct global_stats_counter *stats, int64_t run_diff_us, struct global_stats_counter *intv, struct global_stats_counter *rate) { #define F(x) atomic64_calc_rate(&stats->x, run_diff_us, &intv->x, &rate->x); #define FA(x, n) for (int i = 0; i < n; i++) { F(x[i]) } #include "counter_stats_fields.inc" #undef F #undef FA } INLINE void stats_counters_calc_diff(const struct global_stats_counter *stats, struct global_stats_counter *intv, struct global_stats_counter *diff) { #define F(x) atomic64_calc_diff(&stats->x, &intv->x, &diff->x); #define FA(x, n) for (int i = 0; i < n; i++) { F(x[i]) } #include "counter_stats_fields.inc" #undef F #undef FA } /** * Update the running min/max counter `mm` with the newly calculated per-sec rate values `inp`. * If used with the `stats_counters_calc_rate()`, it must be called only after that. */ INLINE void stats_rate_min_max(struct global_rate_min_max *mm, struct global_stats_counter *inp) { #define F(x) \ atomic64_mina(&mm->min.x, &inp->x); \ atomic64_maxa(&mm->max.x, &inp->x); #define FA(x, n) for (int i = 0; i < n; i++) { F(x[i]) } #include "counter_stats_fields.inc" #undef F #undef FA } // sample running min/max from `mm` into `loc` and reset `mm` to zero. // calculate average values in `loc` from `counter_diff` and `time_diff_us` INLINE void stats_rate_min_max_avg_sample(struct global_rate_min_max *mm, struct global_rate_min_max_avg *loc, int64_t run_diff_us, const struct global_stats_counter *counter_diff) { #define F(x) STAT_MIN_MAX_AVG(x, mm, loc, run_diff_us, counter_diff) #define FA(x, n) for (int i = 0; i < n; i++) { F(x[i]) } #include "counter_stats_fields.inc" #undef F #undef FA } INLINE void stats_sampled_calc_diff(const struct global_stats_sampled *stats, struct global_stats_sampled *intv, struct global_stats_sampled *diff) { #define F(x) STAT_SAMPLED_CALC_DIFF(x, stats, intv, diff) #define FA(x, n) for (int i = 0; i < n; i++) { F(x[i]) } #include "sampled_stats_fields.inc" #undef F #undef FA } // sample running min/max from `mm` into `loc` and reset `mm` to zero. INLINE void stats_sampled_min_max_sample(struct global_sampled_min_max *mm, struct global_sampled_min_max *loc) { #define F(x) STAT_MIN_MAX_RESET_ZERO(x, mm, loc) #define FA(x, n) for (int i = 0; i < n; i++) { F(x[i]) } #include "sampled_stats_fields.inc" #undef F #undef FA } INLINE void stats_sampled_avg(struct global_sampled_avg *loc, const struct global_stats_sampled *diff) { #define F(x) STAT_SAMPLED_AVG_STDDEV(x, loc, diff) #define FA(x, n) for (int i = 0; i < n; i++) { F(x[i]) } #include "sampled_stats_fields.inc" #undef F #undef FA } // sample running min/max from `in_reset` into `out` and reset `in_reset` to the current value. INLINE void stats_gauge_min_max_sample(struct global_gauge_min_max *out, struct global_gauge_min_max *in_reset, const struct global_stats_gauge *cur) { #define F(x) STAT_MIN_MAX(x, out, in_reset, cur) #include "gauge_stats_fields.inc" #undef F } void statistics_init(void); void statistics_free(void); #endif /* STATISTICS_H_ */