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 }