tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

metrics_store_entry.c (7342B)


      1 /* Copyright (c) 2020-2021, The Tor Project, Inc. */
      2 /* See LICENSE for licensing information */
      3 
      4 /**
      5 * @file metrics_store_entry.c
      6 * @brief Metrics store entry which contains the gathered data.
      7 **/
      8 
      9 #include "metrics_common.h"
     10 #define METRICS_STORE_ENTRY_PRIVATE
     11 
     12 #include <string.h>
     13 
     14 #include "orconfig.h"
     15 
     16 #include "lib/container/smartlist.h"
     17 #include "lib/log/util_bug.h"
     18 #include "lib/malloc/malloc.h"
     19 
     20 #include "lib/metrics/metrics_store_entry.h"
     21 
     22 /*
     23 * Public API.
     24 */
     25 
     26 /** Return newly allocated store entry of the specified type. */
     27 metrics_store_entry_t *
     28 metrics_store_entry_new(const metrics_type_t type, const char *name,
     29                        const char *help, size_t bucket_count,
     30                        const int64_t *buckets)
     31 {
     32  metrics_store_entry_t *entry = tor_malloc_zero(sizeof(*entry));
     33 
     34  tor_assert(name);
     35 
     36  entry->type = type;
     37  entry->name = tor_strdup(name);
     38  entry->labels = smartlist_new();
     39  if (help) {
     40    entry->help = tor_strdup(help);
     41  }
     42 
     43  if (type == METRICS_TYPE_HISTOGRAM && bucket_count > 0) {
     44    tor_assert(buckets);
     45 
     46    entry->u.histogram.bucket_count = bucket_count;
     47    entry->u.histogram.buckets =
     48        tor_malloc_zero(sizeof(metrics_histogram_bucket_t) * bucket_count);
     49 
     50    for (size_t i = 0; i < bucket_count; ++i) {
     51      entry->u.histogram.buckets[i].bucket = buckets[i];
     52    }
     53  }
     54 
     55  return entry;
     56 }
     57 
     58 /** Free a store entry. */
     59 void
     60 metrics_store_entry_free_(metrics_store_entry_t *entry)
     61 {
     62  if (!entry) {
     63    return;
     64  }
     65  SMARTLIST_FOREACH(entry->labels, char *, l, tor_free(l));
     66  smartlist_free(entry->labels);
     67  tor_free(entry->name);
     68  tor_free(entry->help);
     69 
     70  if (entry->type == METRICS_TYPE_HISTOGRAM) {
     71    tor_free(entry->u.histogram.buckets);
     72  }
     73 
     74  tor_free(entry);
     75 }
     76 
     77 /** Update a store entry with value. */
     78 void
     79 metrics_store_entry_update(metrics_store_entry_t *entry, const int64_t value)
     80 {
     81  tor_assert(entry);
     82 
     83  /* Histogram values are updated using metrics_store_hist_entry_update */
     84  if (BUG(entry->type == METRICS_TYPE_HISTOGRAM)) {
     85    return;
     86  }
     87 
     88  switch (entry->type) {
     89  case METRICS_TYPE_COUNTER:
     90    /* Counter can ONLY be positive. */
     91    if (BUG(value < 0)) {
     92      return;
     93    }
     94    entry->u.counter.value += value;
     95    break;
     96  case METRICS_TYPE_GAUGE:
     97    /* Gauge can increment or decrement. And can be positive or negative. */
     98    entry->u.gauge.value += value;
     99    break;
    100  case METRICS_TYPE_HISTOGRAM:
    101    tor_assert_unreached();
    102  }
    103 }
    104 
    105 /** Update a store entry with value for the specified observation obs.
    106 *
    107 * Note: entry **must** be a histogram. */
    108 void
    109 metrics_store_hist_entry_update(metrics_store_entry_t *entry,
    110                                const int64_t value, const int64_t obs)
    111 {
    112  if (BUG(entry->type != METRICS_TYPE_HISTOGRAM)) {
    113    return;
    114  }
    115 
    116  /* Counter can ONLY be positive for histograms. */
    117  if (BUG(value < 0)) {
    118    return;
    119  }
    120 
    121  /* If we're about to overflow or underflow the sum, reset all counters back
    122   * to 0 before recording the observation. */
    123  if (PREDICT_UNLIKELY(
    124          (obs > 0 && entry->u.histogram.sum > INT64_MAX - obs) ||
    125          (obs < 0 && entry->u.histogram.sum < INT64_MIN - obs))) {
    126    metrics_store_entry_reset(entry);
    127  }
    128 
    129  entry->u.histogram.count += value;
    130  entry->u.histogram.sum += obs;
    131 
    132  for (size_t i = 0; i < entry->u.histogram.bucket_count; ++i) {
    133    metrics_histogram_bucket_t *hb = &entry->u.histogram.buckets[i];
    134    if (obs <= hb->bucket) {
    135      hb->value += value;
    136    }
    137  }
    138 }
    139 
    140 /** Reset a store entry that is set its metric data to 0. */
    141 void
    142 metrics_store_entry_reset(metrics_store_entry_t *entry)
    143 {
    144  tor_assert(entry);
    145 
    146  switch (entry->type) {
    147  case METRICS_TYPE_COUNTER: FALLTHROUGH;
    148  case METRICS_TYPE_GAUGE:
    149    /* Everything back to 0. */
    150    memset(&entry->u, 0, sizeof(entry->u));
    151    break;
    152  case METRICS_TYPE_HISTOGRAM:
    153    for (size_t i = 0; i < entry->u.histogram.bucket_count; ++i) {
    154      metrics_histogram_bucket_t *hb = &entry->u.histogram.buckets[i];
    155      hb->value = 0;
    156    }
    157    entry->u.histogram.sum = 0;
    158    entry->u.histogram.count = 0;
    159    break;
    160  }
    161 }
    162 
    163 /** Return store entry value. */
    164 int64_t
    165 metrics_store_entry_get_value(const metrics_store_entry_t *entry)
    166 {
    167  tor_assert(entry);
    168 
    169  /* Histogram values are accessed using metrics_store_hist_entry_get_value. */
    170  if (BUG(entry->type == METRICS_TYPE_HISTOGRAM)) {
    171    return 0;
    172  }
    173 
    174  switch (entry->type) {
    175  case METRICS_TYPE_COUNTER:
    176    if (entry->u.counter.value > INT64_MAX) {
    177      return INT64_MAX;
    178    }
    179    return entry->u.counter.value;
    180  case METRICS_TYPE_GAUGE:
    181    return entry->u.gauge.value;
    182  case METRICS_TYPE_HISTOGRAM:
    183    tor_assert_unreached();
    184    return 0;
    185  }
    186 
    187  // LCOV_EXCL_START
    188  tor_assert_unreached();
    189  // LCOV_EXCL_STOP
    190 }
    191 
    192 /** Return store entry value for the specified bucket.
    193 *
    194 * Note: entry **must** be a histogram. */
    195 uint64_t
    196 metrics_store_hist_entry_get_value(const metrics_store_entry_t *entry,
    197                                   const int64_t bucket)
    198 {
    199  tor_assert(entry);
    200 
    201  if (BUG(entry->type != METRICS_TYPE_HISTOGRAM)) {
    202    return 0;
    203  }
    204 
    205  for (size_t i = 0; i <= entry->u.histogram.bucket_count; ++i) {
    206    metrics_histogram_bucket_t hb = entry->u.histogram.buckets[i];
    207    if (bucket == hb.bucket) {
    208      if (hb.value > INT64_MAX) {
    209        return INT64_MAX;
    210      } else {
    211        return hb.value;
    212      }
    213    }
    214  }
    215 
    216  tor_assertf_nonfatal(false, "attempted to get the value of non-existent "
    217                       "bucket %" PRId64, bucket);
    218  return 0;
    219 }
    220 
    221 /** Add a label into the given entry.*/
    222 void
    223 metrics_store_entry_add_label(metrics_store_entry_t *entry,
    224                              const char *label)
    225 {
    226  tor_assert(entry);
    227  tor_assert(label);
    228 
    229  smartlist_add(entry->labels, tor_strdup(label));
    230 }
    231 
    232 /** Return true iff the given entry has the given label. */
    233 bool
    234 metrics_store_entry_has_label(const metrics_store_entry_t *entry,
    235                              const char *label)
    236 {
    237  tor_assert(entry);
    238  tor_assert(label);
    239 
    240  return smartlist_contains_string(entry->labels, label);
    241 }
    242 
    243 /** Return the first entry that has the given label, or NULL if none
    244 * of the entries have the label. */
    245 metrics_store_entry_t *
    246 metrics_store_find_entry_with_label(const smartlist_t *entries,
    247                                    const char *label)
    248 {
    249  tor_assert(entries);
    250  tor_assert(label);
    251 
    252  SMARTLIST_FOREACH_BEGIN(entries, metrics_store_entry_t *, entry) {
    253    tor_assert(entry);
    254 
    255    if (smartlist_contains_string(entry->labels, label)) {
    256      return entry;
    257    }
    258  } SMARTLIST_FOREACH_END(entry);
    259 
    260  return NULL;
    261 }
    262 
    263 /** Return true iff the specified entry is a histogram. */
    264 bool
    265 metrics_store_entry_is_histogram(const metrics_store_entry_t *entry)
    266 {
    267  if (entry->type == METRICS_TYPE_HISTOGRAM) {
    268    return true;
    269  }
    270 
    271  return false;
    272 }
    273 
    274 /** Return the total number of observations for the specified histogram. */
    275 uint64_t
    276 metrics_store_hist_entry_get_count(const metrics_store_entry_t *entry)
    277 {
    278  tor_assert(entry);
    279 
    280  if (BUG(entry->type != METRICS_TYPE_HISTOGRAM)) {
    281    return 0;
    282  }
    283 
    284  return entry->u.histogram.count;
    285 }
    286 
    287 /** Return the sum of all observations for the specified histogram. */
    288 int64_t
    289 metrics_store_hist_entry_get_sum(const metrics_store_entry_t *entry)
    290 {
    291  tor_assert(entry);
    292 
    293  if (BUG(entry->type != METRICS_TYPE_HISTOGRAM)) {
    294    return 0;
    295  }
    296 
    297  return entry->u.histogram.sum;
    298 }