Tor  0.4.7.0-alpha-dev
relay_metrics.c
Go to the documentation of this file.
1 /* Copyright (c) 2021, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
3 
4 /**
5  * @file relay_metrics.c
6  * @brief Relay metrics exposed through the MetricsPort
7  **/
8 
9 #define RELAY_METRICS_ENTRY_PRIVATE
10 
11 #include "orconfig.h"
12 
13 #include "core/or/or.h"
14 #include "core/or/relay.h"
15 
16 #include "lib/malloc/malloc.h"
19 #include "lib/log/util_bug.h"
20 
22 #include "feature/stats/rephist.h"
23 
24 #include <event2/dns.h>
25 
26 /** Declarations of each fill function for metrics defined in base_metrics. */
27 static void fill_dns_error_values(void);
28 static void fill_dns_query_values(void);
29 static void fill_global_bw_limit_values(void);
30 static void fill_socket_values(void);
31 static void fill_onionskins_values(void);
32 static void fill_oom_values(void);
33 static void fill_tcp_exhaustion_values(void);
34 
35 /** The base metrics that is a static array of metrics added to the metrics
36  * store.
37  *
38  * The key member MUST be also the index of the entry in the array. */
40 {
41  {
43  .type = METRICS_TYPE_COUNTER,
44  .name = METRICS_NAME(relay_load_oom_bytes_total),
45  .help = "Total number of bytes the OOM has freed by subsystem",
46  .fill_fn = fill_oom_values,
47  },
48  {
50  .type = METRICS_TYPE_COUNTER,
51  .name = METRICS_NAME(relay_load_onionskins_total),
52  .help = "Total number of onionskins handled",
53  .fill_fn = fill_onionskins_values,
54  },
55  {
57  .type = METRICS_TYPE_GAUGE,
58  .name = METRICS_NAME(relay_load_socket_total),
59  .help = "Total number of sockets",
60  .fill_fn = fill_socket_values,
61  },
62  {
64  .type = METRICS_TYPE_COUNTER,
65  .name = METRICS_NAME(relay_load_global_rate_limit_reached_total),
66  .help = "Total number of global connection bucket limit reached",
67  .fill_fn = fill_global_bw_limit_values,
68  },
69  {
70  .key = RELAY_METRICS_NUM_DNS,
71  .type = METRICS_TYPE_COUNTER,
72  .name = METRICS_NAME(relay_exit_dns_query_total),
73  .help = "Total number of DNS queries done by this relay",
74  .fill_fn = fill_dns_query_values,
75  },
76  {
78  .type = METRICS_TYPE_COUNTER,
79  .name = METRICS_NAME(relay_exit_dns_error_total),
80  .help = "Total number of DNS errors encountered by this relay",
81  .fill_fn = fill_dns_error_values,
82  },
83  {
85  .type = METRICS_TYPE_COUNTER,
86  .name = METRICS_NAME(relay_load_tcp_exhaustion_total),
87  .help = "Total number of times we ran out of TCP ports",
88  .fill_fn = fill_tcp_exhaustion_values,
89  },
90 };
91 static const size_t num_base_metrics = ARRAY_LENGTH(base_metrics);
92 
93 /** The only and single store of all the relay metrics. */
95 
96 /** Helper function to convert an handshake type into a string. */
97 static inline const char *
98 handshake_type_to_str(const uint16_t type)
99 {
100  switch (type) {
101  case ONION_HANDSHAKE_TYPE_TAP:
102  return "tap";
103  case ONION_HANDSHAKE_TYPE_FAST:
104  return "fast";
105  case ONION_HANDSHAKE_TYPE_NTOR:
106  return "ntor";
107  default:
108  // LCOV_EXCL_START
109  tor_assert_unreached();
110  // LCOV_EXCL_STOP
111  }
112 }
113 
114 /** Fill function for the RELAY_METRICS_NUM_DNS metrics. */
115 static void
117 {
118  metrics_store_entry_t *sentry;
119  const relay_metrics_entry_t *rentry =
121 
122  sentry = metrics_store_add(the_store, rentry->type, rentry->name,
123  rentry->help);
125 }
126 
127 /** Helper array containing mapping for the name of the different DNS records
128  * and their corresponding libevent values. */
129 static struct dns_type {
130  const char *name;
131  uint8_t type;
132 } dns_types[] = {
133  { .name = "A", .type = DNS_IPv4_A },
134  { .name = "PTR", .type = DNS_PTR },
135  { .name = "AAAA", .type = DNS_IPv6_AAAA },
136 };
137 static const size_t num_dns_types = ARRAY_LENGTH(dns_types);
138 
139 /** Fill function for the RELAY_METRICS_NUM_DNS_ERRORS metrics. */
140 static void
142 {
143  metrics_store_entry_t *sentry;
144  const relay_metrics_entry_t *rentry =
146 
147  /* Helper array to map libeven DNS errors to their names and so we can
148  * iterate over this array to add all metrics. */
149  static struct dns_error {
150  const char *name;
151  uint8_t key;
152  } errors[] = {
153  { .name = "success", .key = DNS_ERR_NONE },
154  { .name = "format", .key = DNS_ERR_FORMAT },
155  { .name = "serverfailed", .key = DNS_ERR_SERVERFAILED },
156  { .name = "notexist", .key = DNS_ERR_NOTEXIST },
157  { .name = "notimpl", .key = DNS_ERR_NOTIMPL },
158  { .name = "refused", .key = DNS_ERR_REFUSED },
159  { .name = "truncated", .key = DNS_ERR_TRUNCATED },
160  { .name = "unknown", .key = DNS_ERR_UNKNOWN },
161  { .name = "timeout", .key = DNS_ERR_TIMEOUT },
162  { .name = "shutdown", .key = DNS_ERR_SHUTDOWN },
163  { .name = "cancel", .key = DNS_ERR_CANCEL },
164  { .name = "nodata", .key = DNS_ERR_NODATA },
165  };
166  static const size_t num_errors = ARRAY_LENGTH(errors);
167 
168  for (size_t i = 0; i < num_dns_types; i++) {
169  /* Dup the label because metrics_format_label() returns a pointer to a
170  * string on the stack and we need that label for all metrics. */
171  char *record_label =
172  tor_strdup(metrics_format_label("record", dns_types[i].name));
173 
174  for (size_t j = 0; j < num_errors; j++) {
175  sentry = metrics_store_add(the_store, rentry->type, rentry->name,
176  rentry->help);
177  metrics_store_entry_add_label(sentry, record_label);
179  metrics_format_label("reason", errors[j].name));
181  rep_hist_get_n_dns_error(dns_types[i].type, errors[j].key));
182  }
183  tor_free(record_label);
184  }
185 }
186 
187 /** Fill function for the RELAY_METRICS_NUM_DNS metrics. */
188 static void
190 {
191  metrics_store_entry_t *sentry;
192  const relay_metrics_entry_t *rentry =
194 
195  for (size_t i = 0; i < num_dns_types; i++) {
196  /* Dup the label because metrics_format_label() returns a pointer to a
197  * string on the stack and we need that label for all metrics. */
198  char *record_label =
199  tor_strdup(metrics_format_label("record", dns_types[i].name));
200  sentry = metrics_store_add(the_store, rentry->type, rentry->name,
201  rentry->help);
202  metrics_store_entry_add_label(sentry, record_label);
204  rep_hist_get_n_dns_request(dns_types[i].type));
205  tor_free(record_label);
206  }
207 }
208 
209 /** Fill function for the RELAY_METRICS_NUM_GLOBAL_RW_LIMIT metrics. */
210 static void
212 {
213  metrics_store_entry_t *sentry;
214  const relay_metrics_entry_t *rentry =
216 
217  sentry = metrics_store_add(the_store, rentry->type, rentry->name,
218  rentry->help);
220  metrics_format_label("side", "read"));
222 
223  sentry = metrics_store_add(the_store, rentry->type, rentry->name,
224  rentry->help);
226  metrics_format_label("side", "write"));
228 }
229 
230 /** Fill function for the RELAY_METRICS_NUM_SOCKETS metrics. */
231 static void
233 {
234  metrics_store_entry_t *sentry;
235  const relay_metrics_entry_t *rentry =
237 
238  sentry = metrics_store_add(the_store, rentry->type, rentry->name,
239  rentry->help);
241  metrics_format_label("state", "opened"));
243 
244  sentry = metrics_store_add(the_store, rentry->type, rentry->name,
245  rentry->help);
247 }
248 
249 /** Fill function for the RELAY_METRICS_NUM_ONIONSKINS metrics. */
250 static void
252 {
253  metrics_store_entry_t *sentry;
254  const relay_metrics_entry_t *rentry =
256 
257  for (uint16_t t = 0; t <= MAX_ONION_HANDSHAKE_TYPE; t++) {
258  /* Dup the label because metrics_format_label() returns a pointer to a
259  * string on the stack and we need that label for all metrics. */
260  char *type_label =
261  tor_strdup(metrics_format_label("type", handshake_type_to_str(t)));
262  sentry = metrics_store_add(the_store, rentry->type, rentry->name,
263  rentry->help);
264  metrics_store_entry_add_label(sentry, type_label);
266  metrics_format_label("action", "processed"));
269 
270  sentry = metrics_store_add(the_store, rentry->type, rentry->name,
271  rentry->help);
272  metrics_store_entry_add_label(sentry, type_label);
274  metrics_format_label("action", "dropped"));
277  tor_free(type_label);
278  }
279 }
280 
281 /** Fill function for the RELAY_METRICS_NUM_OOM_BYTES metrics. */
282 static void
284 {
285  metrics_store_entry_t *sentry;
286  const relay_metrics_entry_t *rentry =
288 
289  sentry = metrics_store_add(the_store, rentry->type, rentry->name,
290  rentry->help);
292  metrics_format_label("subsys", "cell"));
293  metrics_store_entry_update(sentry, oom_stats_n_bytes_removed_cell);
294 
295  sentry = metrics_store_add(the_store, rentry->type, rentry->name,
296  rentry->help);
298  metrics_format_label("subsys", "dns"));
300 
301  sentry = metrics_store_add(the_store, rentry->type, rentry->name,
302  rentry->help);
304  metrics_format_label("subsys", "geoip"));
305  metrics_store_entry_update(sentry, oom_stats_n_bytes_removed_geoip);
306 
307  sentry = metrics_store_add(the_store, rentry->type, rentry->name,
308  rentry->help);
310  metrics_format_label("subsys", "hsdir"));
311  metrics_store_entry_update(sentry, oom_stats_n_bytes_removed_hsdir);
312 }
313 
314 /** Reset the global store and fill it with all the metrics from base_metrics
315  * and their associated values.
316  *
317  * To pull this off, every metrics has a "fill" function that is called and in
318  * charge of adding the metrics to the store, appropriate labels and finally
319  * updating the value to report. */
320 static void
322 {
323  /* Reset the current store, we are about to fill it with all the things. */
325 
326  /* Call the fill function for each metrics. */
327  for (size_t i = 0; i < num_base_metrics; i++) {
328  if (BUG(!base_metrics[i].fill_fn)) {
329  continue;
330  }
331  base_metrics[i].fill_fn();
332  }
333 }
334 
335 /** Return a list of all the relay metrics stores. This is the
336  * function attached to the .get_metrics() member of the subsys_t. */
337 const smartlist_t *
339 {
340  /* We can't have the caller to free the returned list so keep it static,
341  * simply update it. */
342  static smartlist_t *stores_list = NULL;
343 
344  /* We dynamically fill the store with all the metrics upon a request. The
345  * reason for this is because the exposed metrics of a relay are often
346  * internal counters in the fast path and thus we fetch the value when a
347  * metrics port request arrives instead of keeping a local metrics store of
348  * those values. */
349  fill_store();
350 
351  if (!stores_list) {
352  stores_list = smartlist_new();
353  smartlist_add(stores_list, the_store);
354  }
355 
356  return stores_list;
357 }
358 
359 /** Initialize the relay metrics. */
360 void
362 {
363  if (BUG(the_store)) {
364  return;
365  }
367 }
368 
369 /** Free the relay metrics. */
370 void
372 {
373  if (!the_store) {
374  return;
375  }
376  /* NULL is set with this call. */
377  metrics_store_free(the_store);
378 }
#define ARRAY_LENGTH(x)
const char * name
Definition: config.c:2434
Headers for util_malloc.c.
#define tor_free(p)
Definition: malloc.h:52
const char * metrics_format_label(const char *key, const char *value)
metrics_store_entry_t * metrics_store_add(metrics_store_t *store, metrics_type_t type, const char *name, const char *help)
metrics_store_t * metrics_store_new(void)
Definition: metrics_store.c:74
void metrics_store_reset(metrics_store_t *store)
Header for lib/metrics/metrics_store.c.
void metrics_store_entry_update(metrics_store_entry_t *entry, const int64_t value)
void metrics_store_entry_add_label(metrics_store_entry_t *entry, const char *label)
Master header file for Tor-specific functionality.
uint64_t oom_stats_n_bytes_removed_dns
Definition: relay.c:2705
Header file for relay.c.
static void fill_oom_values(void)
static const char * handshake_type_to_str(const uint16_t type)
Definition: relay_metrics.c:98
static void fill_store(void)
static void fill_tcp_exhaustion_values(void)
static void fill_dns_query_values(void)
static void fill_onionskins_values(void)
static void fill_dns_error_values(void)
void relay_metrics_init(void)
static void fill_global_bw_limit_values(void)
const smartlist_t * relay_metrics_get_stores(void)
void relay_metrics_free(void)
static const relay_metrics_entry_t base_metrics[]
Definition: relay_metrics.c:39
static void fill_socket_values(void)
static metrics_store_t * the_store
Definition: relay_metrics.c:94
Header for feature/relay/relay_metrics.c.
@ RELAY_METRICS_NUM_GLOBAL_RW_LIMIT
Definition: relay_metrics.h:25
@ RELAY_METRICS_NUM_TCP_EXHAUSTION
Definition: relay_metrics.h:31
@ RELAY_METRICS_NUM_SOCKETS
Definition: relay_metrics.h:23
@ RELAY_METRICS_NUM_ONIONSKINS
Definition: relay_metrics.h:21
@ RELAY_METRICS_NUM_DNS
Definition: relay_metrics.h:27
@ RELAY_METRICS_NUM_DNS_ERRORS
Definition: relay_metrics.h:29
@ RELAY_METRICS_NUM_OOM_BYTES
Definition: relay_metrics.h:19
uint64_t rep_hist_get_n_dns_request(int type)
Definition: rephist.c:313
uint64_t rep_hist_get_circuit_handshake_dropped(uint16_t type)
Definition: rephist.c:2055
int rep_hist_get_circuit_handshake_assigned(uint16_t type)
Definition: rephist.c:2045
uint64_t rep_hist_get_n_dns_error(int type, uint8_t error)
Definition: rephist.c:272
uint64_t rep_hist_get_n_read_limit_reached(void)
Definition: rephist.c:405
uint64_t rep_hist_get_n_tcp_exhaustion(void)
Definition: rephist.c:530
uint64_t rep_hist_get_n_write_limit_reached(void)
Definition: rephist.c:412
Header file for rephist.c.
Header for smartlist.c.
smartlist_t * smartlist_new(void)
void smartlist_add(smartlist_t *sl, void *element)
int get_max_sockets(void)
Definition: socket.c:98
int get_n_open_sockets(void)
Definition: socket.c:440
Definition: relay_metrics.h:35
Macros to manage assertions, fatal and non-fatal.