summaryrefslogtreecommitdiff
path: root/linux/time_stats.c
diff options
context:
space:
mode:
Diffstat (limited to 'linux/time_stats.c')
-rw-r--r--linux/time_stats.c373
1 files changed, 0 insertions, 373 deletions
diff --git a/linux/time_stats.c b/linux/time_stats.c
deleted file mode 100644
index d7dd64ba..00000000
--- a/linux/time_stats.c
+++ /dev/null
@@ -1,373 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#include <linux/eytzinger.h>
-#include <linux/jiffies.h>
-#include <linux/module.h>
-#include <linux/percpu.h>
-#include <linux/preempt.h>
-#include <linux/time.h>
-#include <linux/time_stats.h>
-#include <linux/spinlock.h>
-
-static const struct time_unit time_units[] = {
- { "ns", 1 },
- { "us", NSEC_PER_USEC },
- { "ms", NSEC_PER_MSEC },
- { "s", NSEC_PER_SEC },
- { "m", (u64) NSEC_PER_SEC * 60},
- { "h", (u64) NSEC_PER_SEC * 3600},
- { "d", (u64) NSEC_PER_SEC * 3600 * 24},
- { "w", (u64) NSEC_PER_SEC * 3600 * 24 * 7},
- { "y", (u64) NSEC_PER_SEC * ((3600 * 24 * 7 * 365) + (3600 * (24 / 4) * 7))}, /* 365.25d */
- { "eon", U64_MAX },
-};
-
-const struct time_unit *pick_time_units(u64 ns)
-{
- const struct time_unit *u;
-
- for (u = time_units;
- u + 1 < time_units + ARRAY_SIZE(time_units) &&
- ns >= u[1].nsecs << 1;
- u++)
- ;
-
- return u;
-}
-EXPORT_SYMBOL_GPL(pick_time_units);
-
-static void quantiles_update(struct quantiles *q, u64 v)
-{
- unsigned i = 0;
-
- while (i < ARRAY_SIZE(q->entries)) {
- struct quantile_entry *e = q->entries + i;
-
- if (unlikely(!e->step)) {
- e->m = v;
- e->step = max_t(unsigned, v / 2, 1024);
- } else if (e->m > v) {
- e->m = e->m >= e->step
- ? e->m - e->step
- : 0;
- } else if (e->m < v) {
- e->m = e->m + e->step > e->m
- ? e->m + e->step
- : U32_MAX;
- }
-
- if ((e->m > v ? e->m - v : v - e->m) < e->step)
- e->step = max_t(unsigned, e->step / 2, 1);
-
- if (v >= e->m)
- break;
-
- i = eytzinger0_child(i, v > e->m);
- }
-}
-
-static inline void time_stats_update_one(struct time_stats *stats,
- u64 start, u64 end)
-{
- u64 duration, freq;
- bool initted = stats->last_event != 0;
-
- if (time_after64(end, start)) {
- struct quantiles *quantiles = time_stats_to_quantiles(stats);
-
- duration = end - start;
- mean_and_variance_update(&stats->duration_stats, duration);
- mean_and_variance_weighted_update(&stats->duration_stats_weighted,
- duration, initted, TIME_STATS_MV_WEIGHT);
- stats->max_duration = max(stats->max_duration, duration);
- stats->min_duration = min(stats->min_duration, duration);
- stats->total_duration += duration;
-
- if (quantiles)
- quantiles_update(quantiles, duration);
- }
-
- if (stats->last_event && time_after64(end, stats->last_event)) {
- freq = end - stats->last_event;
- mean_and_variance_update(&stats->freq_stats, freq);
- mean_and_variance_weighted_update(&stats->freq_stats_weighted,
- freq, initted, TIME_STATS_MV_WEIGHT);
- stats->max_freq = max(stats->max_freq, freq);
- stats->min_freq = min(stats->min_freq, freq);
- }
-
- stats->last_event = end;
-}
-
-void __time_stats_clear_buffer(struct time_stats *stats,
- struct time_stat_buffer *b)
-{
- for (struct time_stat_buffer_entry *i = b->entries;
- i < b->entries + ARRAY_SIZE(b->entries);
- i++)
- time_stats_update_one(stats, i->start, i->end);
- b->nr = 0;
-}
-EXPORT_SYMBOL_GPL(__time_stats_clear_buffer);
-
-static noinline void time_stats_clear_buffer(struct time_stats *stats,
- struct time_stat_buffer *b)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&stats->lock, flags);
- __time_stats_clear_buffer(stats, b);
- spin_unlock_irqrestore(&stats->lock, flags);
-}
-
-void __time_stats_update(struct time_stats *stats, u64 start, u64 end)
-{
- unsigned long flags;
-
- if (!stats->buffer) {
- spin_lock_irqsave(&stats->lock, flags);
- time_stats_update_one(stats, start, end);
-
- if (mean_and_variance_weighted_get_mean(stats->freq_stats_weighted, TIME_STATS_MV_WEIGHT) < 32 &&
- stats->duration_stats.n > 1024)
- stats->buffer =
- alloc_percpu_gfp(struct time_stat_buffer,
- GFP_ATOMIC);
- spin_unlock_irqrestore(&stats->lock, flags);
- } else {
- struct time_stat_buffer *b;
-
- preempt_disable();
- b = this_cpu_ptr(stats->buffer);
-
- BUG_ON(b->nr >= ARRAY_SIZE(b->entries));
- b->entries[b->nr++] = (struct time_stat_buffer_entry) {
- .start = start,
- .end = end
- };
-
- if (unlikely(b->nr == ARRAY_SIZE(b->entries)))
- time_stats_clear_buffer(stats, b);
- preempt_enable();
- }
-}
-EXPORT_SYMBOL_GPL(__time_stats_update);
-
-#include <linux/seq_buf.h>
-
-static void seq_buf_time_units_aligned(struct seq_buf *out, u64 ns)
-{
- const struct time_unit *u = pick_time_units(ns);
-
- seq_buf_printf(out, "%8llu %s", div64_u64(ns, u->nsecs), u->name);
-}
-
-static inline u64 time_stats_lifetime(const struct time_stats *stats)
-{
- return local_clock() - stats->start_time;
-}
-
-void time_stats_to_seq_buf(struct seq_buf *out, struct time_stats *stats,
- const char *epoch_name, unsigned int flags)
-{
- struct quantiles *quantiles = time_stats_to_quantiles(stats);
- s64 f_mean = 0, d_mean = 0;
- u64 f_stddev = 0, d_stddev = 0;
- u64 lifetime = time_stats_lifetime(stats);
-
- if (stats->buffer) {
- int cpu;
-
- spin_lock_irq(&stats->lock);
- for_each_possible_cpu(cpu)
- __time_stats_clear_buffer(stats, per_cpu_ptr(stats->buffer, cpu));
- spin_unlock_irq(&stats->lock);
- }
-
- if (stats->freq_stats.n) {
- /* avoid divide by zero */
- f_mean = mean_and_variance_get_mean(stats->freq_stats);
- f_stddev = mean_and_variance_get_stddev(stats->freq_stats);
- d_mean = mean_and_variance_get_mean(stats->duration_stats);
- d_stddev = mean_and_variance_get_stddev(stats->duration_stats);
- } else if (flags & TIME_STATS_PRINT_NO_ZEROES) {
- /* unless we didn't want zeroes anyway */
- return;
- }
-
- seq_buf_printf(out, "count: %llu\n", stats->duration_stats.n);
- seq_buf_printf(out, "lifetime: ");
- seq_buf_time_units_aligned(out, lifetime);
- seq_buf_printf(out, "\n");
-
- seq_buf_printf(out, " since %-12s recent\n", epoch_name);
-
- seq_buf_printf(out, "duration of events\n");
-
- seq_buf_printf(out, " min: ");
- seq_buf_time_units_aligned(out, stats->min_duration);
- seq_buf_printf(out, "\n");
-
- seq_buf_printf(out, " max: ");
- seq_buf_time_units_aligned(out, stats->max_duration);
- seq_buf_printf(out, "\n");
-
- seq_buf_printf(out, " total: ");
- seq_buf_time_units_aligned(out, stats->total_duration);
- seq_buf_printf(out, "\n");
-
- seq_buf_printf(out, " mean: ");
- seq_buf_time_units_aligned(out, d_mean);
- seq_buf_time_units_aligned(out, mean_and_variance_weighted_get_mean(stats->duration_stats_weighted, TIME_STATS_MV_WEIGHT));
- seq_buf_printf(out, "\n");
-
- seq_buf_printf(out, " stddev: ");
- seq_buf_time_units_aligned(out, d_stddev);
- seq_buf_time_units_aligned(out, mean_and_variance_weighted_get_stddev(stats->duration_stats_weighted, TIME_STATS_MV_WEIGHT));
- seq_buf_printf(out, "\n");
-
- seq_buf_printf(out, "time between events\n");
-
- seq_buf_printf(out, " min: ");
- seq_buf_time_units_aligned(out, stats->min_freq);
- seq_buf_printf(out, "\n");
-
- seq_buf_printf(out, " max: ");
- seq_buf_time_units_aligned(out, stats->max_freq);
- seq_buf_printf(out, "\n");
-
- seq_buf_printf(out, " mean: ");
- seq_buf_time_units_aligned(out, f_mean);
- seq_buf_time_units_aligned(out, mean_and_variance_weighted_get_mean(stats->freq_stats_weighted, TIME_STATS_MV_WEIGHT));
- seq_buf_printf(out, "\n");
-
- seq_buf_printf(out, " stddev: ");
- seq_buf_time_units_aligned(out, f_stddev);
- seq_buf_time_units_aligned(out, mean_and_variance_weighted_get_stddev(stats->freq_stats_weighted, TIME_STATS_MV_WEIGHT));
- seq_buf_printf(out, "\n");
-
- if (quantiles) {
- int i = eytzinger0_first(NR_QUANTILES);
- const struct time_unit *u =
- pick_time_units(quantiles->entries[i].m);
- u64 last_q = 0;
-
- seq_buf_printf(out, "quantiles (%s):\t", u->name);
- eytzinger0_for_each(i, NR_QUANTILES) {
- bool is_last = eytzinger0_next(i, NR_QUANTILES) == -1;
-
- u64 q = max(quantiles->entries[i].m, last_q);
- seq_buf_printf(out, "%llu ", div_u64(q, u->nsecs));
- if (is_last)
- seq_buf_printf(out, "\n");
- last_q = q;
- }
- }
-}
-EXPORT_SYMBOL_GPL(time_stats_to_seq_buf);
-
-void time_stats_to_json(struct seq_buf *out, struct time_stats *stats,
- const char *epoch_name, unsigned int flags)
-{
- struct quantiles *quantiles = time_stats_to_quantiles(stats);
- s64 f_mean = 0, d_mean = 0;
- u64 f_stddev = 0, d_stddev = 0;
-
- if (stats->buffer) {
- int cpu;
-
- spin_lock_irq(&stats->lock);
- for_each_possible_cpu(cpu)
- __time_stats_clear_buffer(stats, per_cpu_ptr(stats->buffer, cpu));
- spin_unlock_irq(&stats->lock);
- }
-
- if (stats->freq_stats.n) {
- /* avoid divide by zero */
- f_mean = mean_and_variance_get_mean(stats->freq_stats);
- f_stddev = mean_and_variance_get_stddev(stats->freq_stats);
- d_mean = mean_and_variance_get_mean(stats->duration_stats);
- d_stddev = mean_and_variance_get_stddev(stats->duration_stats);
- } else if (flags & TIME_STATS_PRINT_NO_ZEROES) {
- /* unless we didn't want zeroes anyway */
- return;
- }
-
- seq_buf_printf(out, "{\n");
- seq_buf_printf(out, " \"epoch\": \"%s\",\n", epoch_name);
- seq_buf_printf(out, " \"count\": %llu,\n", stats->duration_stats.n);
-
- seq_buf_printf(out, " \"duration_ns\": {\n");
- seq_buf_printf(out, " \"min\": %llu,\n", stats->min_duration);
- seq_buf_printf(out, " \"max\": %llu,\n", stats->max_duration);
- seq_buf_printf(out, " \"total\": %llu,\n", stats->total_duration);
- seq_buf_printf(out, " \"mean\": %llu,\n", d_mean);
- seq_buf_printf(out, " \"stddev\": %llu\n", d_stddev);
- seq_buf_printf(out, " },\n");
-
- d_mean = mean_and_variance_weighted_get_mean(stats->duration_stats_weighted, TIME_STATS_MV_WEIGHT);
- d_stddev = mean_and_variance_weighted_get_stddev(stats->duration_stats_weighted, TIME_STATS_MV_WEIGHT);
-
- seq_buf_printf(out, " \"duration_ewma_ns\": {\n");
- seq_buf_printf(out, " \"mean\": %llu,\n", d_mean);
- seq_buf_printf(out, " \"stddev\": %llu\n", d_stddev);
- seq_buf_printf(out, " },\n");
-
- seq_buf_printf(out, " \"between_ns\": {\n");
- seq_buf_printf(out, " \"min\": %llu,\n", stats->min_freq);
- seq_buf_printf(out, " \"max\": %llu,\n", stats->max_freq);
- seq_buf_printf(out, " \"mean\": %llu,\n", f_mean);
- seq_buf_printf(out, " \"stddev\": %llu\n", f_stddev);
- seq_buf_printf(out, " },\n");
-
- f_mean = mean_and_variance_weighted_get_mean(stats->freq_stats_weighted, TIME_STATS_MV_WEIGHT);
- f_stddev = mean_and_variance_weighted_get_stddev(stats->freq_stats_weighted, TIME_STATS_MV_WEIGHT);
-
- seq_buf_printf(out, " \"between_ewma_ns\": {\n");
- seq_buf_printf(out, " \"mean\": %llu,\n", f_mean);
- seq_buf_printf(out, " \"stddev\": %llu\n", f_stddev);
-
- if (quantiles) {
- u64 last_q = 0;
-
- /* close between_ewma_ns but signal more items */
- seq_buf_printf(out, " },\n");
-
- seq_buf_printf(out, " \"quantiles_ns\": [\n");
- eytzinger0_for_each(i, NR_QUANTILES) {
- bool is_last = eytzinger0_next(i, NR_QUANTILES) == -1;
-
- u64 q = max(quantiles->entries[i].m, last_q);
- seq_buf_printf(out, " %llu", q);
- if (!is_last)
- seq_buf_printf(out, ", ");
- last_q = q;
- }
- seq_buf_printf(out, " ]\n");
- } else {
- /* close between_ewma_ns without dumping further */
- seq_buf_printf(out, " }\n");
- }
-
- seq_buf_printf(out, "}\n");
-}
-EXPORT_SYMBOL_GPL(time_stats_to_json);
-
-void time_stats_exit(struct time_stats *stats)
-{
- free_percpu(stats->buffer);
-}
-EXPORT_SYMBOL_GPL(time_stats_exit);
-
-void time_stats_init(struct time_stats *stats)
-{
- memset(stats, 0, sizeof(*stats));
- stats->min_duration = U64_MAX;
- stats->min_freq = U64_MAX;
- stats->start_time = local_clock();
- spin_lock_init(&stats->lock);
-}
-EXPORT_SYMBOL_GPL(time_stats_init);
-
-MODULE_AUTHOR("Kent Overstreet");
-MODULE_LICENSE("GPL");