Tor 0.4.9.0-alpha-dev
hs_metrics.c
Go to the documentation of this file.
1/* Copyright (c) 2020-2021, The Tor Project, Inc. */
2/* See LICENSE for licensing information */
3
4/**
5 * @file hs_metrics.c
6 * @brief Onion service metrics exposed through the MetricsPort
7 **/
8
9#define HS_METRICS_ENTRY_PRIVATE
10
11#include "orconfig.h"
12
13#include "lib/malloc/malloc.h"
16#include "lib/log/util_bug.h"
17
21
22/** Return a static buffer pointer that contains the port as a string.
23 *
24 * Subsequent call to this function invalidates the previous buffer. */
25static const char *
26port_to_str(const uint16_t port)
27{
28 static char buf[8];
29 tor_snprintf(buf, sizeof(buf), "%u", port);
30 return buf;
31}
32
33/** Add a new metric to the metrics store of the service.
34 *
35 * <b>metric</b> is the index of the metric in the <b>base_metrics</b> array.
36 */
37static void
38add_metric_with_labels(hs_service_t *service, hs_metrics_key_t metric,
39 bool port_as_label, uint16_t port)
40{
41 metrics_store_t *store;
42 const char **error_reasons = NULL;
43 size_t num_error_reasons = 0;
44
45 tor_assert(service);
46
47 if (BUG(metric >= base_metrics_size))
48 return;
49
50 store = service->metrics.store;
51
52 /* Check whether the current metric is an error metric, because error metrics
53 * require an additional `reason` label. */
54 switch (metric) {
55 case HS_METRICS_NUM_REJECTED_INTRO_REQ:
58 break;
59 case HS_METRICS_NUM_FAILED_RDV:
60 error_reasons = hs_metrics_rend_error_reasons;
61 num_error_reasons = hs_metrics_rend_error_reasons_size;
62 break;
63 /* Fall through for all other metrics, as they don't need a
64 * reason label. */
65 case HS_METRICS_NUM_INTRODUCTIONS: FALLTHROUGH;
66 case HS_METRICS_APP_WRITE_BYTES: FALLTHROUGH;
67 case HS_METRICS_APP_READ_BYTES: FALLTHROUGH;
68 case HS_METRICS_NUM_ESTABLISHED_RDV: FALLTHROUGH;
69 case HS_METRICS_NUM_RDV: FALLTHROUGH;
70 case HS_METRICS_NUM_ESTABLISHED_INTRO: FALLTHROUGH;
71 case HS_METRICS_POW_NUM_PQUEUE_RDV: FALLTHROUGH;
72 case HS_METRICS_POW_SUGGESTED_EFFORT: FALLTHROUGH;
73 case HS_METRICS_INTRO_CIRC_BUILD_TIME: FALLTHROUGH;
74 case HS_METRICS_REND_CIRC_BUILD_TIME: FALLTHROUGH;
75 default:
76 break;
77 }
78
79 /* We don't need a reason label for this metric */
80 if (!num_error_reasons) {
81 metrics_store_entry_t *entry = metrics_store_add(
82 store, base_metrics[metric].type, base_metrics[metric].name,
83 base_metrics[metric].help, base_metrics[metric].bucket_count,
84 base_metrics[metric].buckets);
85
87 metrics_format_label("onion", service->onion_address));
88
89 if (port_as_label) {
91 metrics_format_label("port", port_to_str(port)));
92 }
93
94 return;
95 }
96
97 tor_assert(error_reasons);
98
99 /* Add entries with reason as label. We need one metric line per
100 * reason. */
101 for (size_t i = 0; i < num_error_reasons; ++i) {
102 metrics_store_entry_t *entry =
103 metrics_store_add(store, base_metrics[metric].type,
104 base_metrics[metric].name,
105 base_metrics[metric].help,
106 base_metrics[metric].bucket_count,
107 base_metrics[metric].buckets);
108 /* Add labels to the entry. */
110 metrics_format_label("onion", service->onion_address));
112 metrics_format_label("reason", error_reasons[i]));
113
114 if (port_as_label) {
116 metrics_format_label("port", port_to_str(port)));
117 }
118 }
119}
120
121/** Initialize a metrics store for the given service.
122 *
123 * Essentially, this goes over the base_metrics array and adds them all to the
124 * store set with their label(s) if any. */
125static void
127{
128 tor_assert(service);
129
130 for (size_t i = 0; i < base_metrics_size; ++i) {
131 /* Add entries with port as label. We need one metric line per port. */
132 if (base_metrics[i].port_as_label && service->config.ports) {
134 const hs_port_config_t *, p) {
135 add_metric_with_labels(service, base_metrics[i].key, true,
136 p->virtual_port);
137 } SMARTLIST_FOREACH_END(p);
138 } else {
139 add_metric_with_labels(service, base_metrics[i].key, false, 0);
140 }
141 }
142}
143
144/** Update the metrics key entry in the store in the given service. The port,
145 * if non 0, and the reason label, if non NULL, are used to find the correct
146 * metrics entry. The value n is the value used to update the entry. */
147void
148hs_metrics_update_by_service(const hs_metrics_key_t key,
149 const hs_service_t *service,
150 uint16_t port, const char *reason,
151 int64_t n, int64_t obs, bool reset)
152{
153 tor_assert(service);
154
155 /* Get the metrics entry in the store. */
157 base_metrics[key].name);
158 if (BUG(!entries)) {
159 return;
160 }
161
162 /* We need to find the right metrics entry by finding the port label if any.
163 *
164 * XXX: This is not the most optimal due to the string format. Maybe at some
165 * point turn this into a kvline and a map in a metric entry? */
166 SMARTLIST_FOREACH_BEGIN(entries, metrics_store_entry_t *, entry) {
167 if ((port == 0 ||
169 entry, metrics_format_label("port", port_to_str(port)))) &&
171 entry, metrics_format_label("reason", reason))))) {
172 if (reset) {
174 }
175
177 metrics_store_hist_entry_update(entry, n, obs);
178 } else {
180 }
181
182 break;
183 }
184 } SMARTLIST_FOREACH_END(entry);
185}
186
187/** Update the metrics key entry in the store of a service identified by the
188 * given identity public key. The port, if non 0, and the reason label, if non
189 * NULL, are used to find the correct metrics entry. The value n is the value
190 * used to update the entry.
191 *
192 * This is used by callsite that have access to the key but not the service
193 * object so an extra lookup is done to find the service. */
194void
195hs_metrics_update_by_ident(const hs_metrics_key_t key,
196 const ed25519_public_key_t *ident_pk,
197 const uint16_t port, const char *reason,
198 int64_t n, int64_t obs, bool reset)
199{
200 hs_service_t *service;
201
202 if (!ident_pk) {
203 /* We can end up here in case this is used from a failure/closing path for
204 * which we might not have any identity key attacehed to a circuit or
205 * connection yet. Simply don't assume we have one. */
206 return;
207 }
208
209 service = hs_service_find(ident_pk);
210 if (!service) {
211 /* This is possible because an onion service client can end up here due to
212 * having an identity key onto a connection _to_ an onion service. We
213 * can't differentiate that from an actual onion service initiated by a
214 * service and thus the only way to know is to lookup the service. */
215 return;
216 }
217 hs_metrics_update_by_service(key, service, port, reason, n, obs, reset);
218}
219
220/** Return a list of all the onion service metrics stores. This is the
221 * function attached to the .get_metrics() member of the subsys_t. */
222const smartlist_t *
224{
225 /* We can't have the caller to free the returned list so keep it static,
226 * simply update it. */
227 static smartlist_t *stores_list = NULL;
228
229 smartlist_free(stores_list);
230 stores_list = hs_service_get_metrics_stores();
231 return stores_list;
232}
233
234/** Initialize the metrics store in the given service. */
235void
237{
238 tor_assert(service);
239
240 /* This function is called when we register a service and so it could either
241 * be a new service or a service that was just reloaded through a HUP signal
242 * for instance. Thus, it is possible that the service has already an
243 * initialized store. If so, just return. */
244 if (service->metrics.store) {
245 return;
246 }
247
248 service->metrics.store = metrics_store_new();
249 init_store(service);
250}
251
252/** Free the metrics store in the given service. */
253void
255{
256 tor_assert(service);
257
258 metrics_store_free(service->metrics.store);
259}
const char * name
Definition: config.c:2462
const smartlist_t * hs_metrics_get_stores(void)
Definition: hs_metrics.c:223
void hs_metrics_update_by_service(const hs_metrics_key_t key, const hs_service_t *service, uint16_t port, const char *reason, int64_t n, int64_t obs, bool reset)
Definition: hs_metrics.c:148
void hs_metrics_service_free(hs_service_t *service)
Definition: hs_metrics.c:254
void hs_metrics_service_init(hs_service_t *service)
Definition: hs_metrics.c:236
static const char * port_to_str(const uint16_t port)
Definition: hs_metrics.c:26
static void add_metric_with_labels(hs_service_t *service, hs_metrics_key_t metric, bool port_as_label, uint16_t port)
Definition: hs_metrics.c:38
static void init_store(hs_service_t *service)
Definition: hs_metrics.c:126
void hs_metrics_update_by_ident(const hs_metrics_key_t key, const ed25519_public_key_t *ident_pk, const uint16_t port, const char *reason, int64_t n, int64_t obs, bool reset)
Definition: hs_metrics.c:195
Header for feature/hs/hs_metrics.c.
const char * hs_metrics_rend_error_reasons[]
const hs_metrics_entry_t base_metrics[]
const size_t hs_metrics_intro_req_error_reasons_size
const size_t base_metrics_size
const char * hs_metrics_intro_req_error_reasons[]
const size_t hs_metrics_rend_error_reasons_size
Header for feature/hs/hs_metrics_entry.c.
smartlist_t * hs_service_get_metrics_stores(void)
Definition: hs_service.c:4575
hs_service_t * hs_service_find(const ed25519_public_key_t *identity_pk)
Definition: hs_service.c:4591
Header file containing service data for the HS subsystem.
Headers for util_malloc.c.
const char * metrics_format_label(const char *key, const char *value)
metrics_store_t * metrics_store_new(void)
Definition: metrics_store.c:74
smartlist_t * metrics_store_get_all(const metrics_store_t *store, const char *name)
Definition: metrics_store.c:98
metrics_store_entry_t * metrics_store_add(metrics_store_t *store, metrics_type_t type, const char *name, const char *help, size_t bucket_count, const int64_t *buckets)
Header for lib/metrics/metrics_store.c.
void metrics_store_entry_update(metrics_store_entry_t *entry, const int64_t value)
bool metrics_store_entry_is_histogram(const metrics_store_entry_t *entry)
void metrics_store_entry_add_label(metrics_store_entry_t *entry, const char *label)
void metrics_store_hist_entry_update(metrics_store_entry_t *entry, const int64_t value, const int64_t obs)
void metrics_store_entry_reset(metrics_store_entry_t *entry)
bool metrics_store_entry_has_label(const metrics_store_entry_t *entry, const char *label)
int tor_snprintf(char *str, size_t size, const char *format,...)
Definition: printf.c:27
Header for smartlist.c.
#define SMARTLIST_FOREACH_BEGIN(sl, type, var)
smartlist_t * ports
Definition: hs_service.h:223
metrics_store_t * store
Definition: hs_service.h:46
char onion_address[HS_SERVICE_ADDR_LEN_BASE32+1]
Definition: hs_service.h:318
hs_service_config_t config
Definition: hs_service.h:331
hs_service_metrics_t metrics
Definition: hs_service.h:339
Macros to manage assertions, fatal and non-fatal.
#define tor_assert(expr)
Definition: util_bug.h:103