• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * lws Generic Metrics
3  *
4  * Copyright (C) 2019 - 2021 Andy Green <andy@warmcat.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to
8  * deal in the Software without restriction, including without limitation the
9  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10  * sell copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22  * IN THE SOFTWARE.
23  */
24 
25 #include "private-lib-core.h"
26 #include <assert.h>
27 
28 int
lws_metrics_tag_add(lws_dll2_owner_t * owner,const char * name,const char * val)29 lws_metrics_tag_add(lws_dll2_owner_t *owner, const char *name, const char *val)
30 {
31 	size_t vl = strlen(val);
32 	lws_metrics_tag_t *tag;
33 
34 	// lwsl_notice("%s: adding %s=%s\n", __func__, name, val);
35 
36 	/*
37 	 * Remove (in order to replace) any existing tag of same name
38 	 */
39 
40 	lws_start_foreach_dll(struct lws_dll2 *, d, owner->head) {
41 		tag = lws_container_of(d, lws_metrics_tag_t, list);
42 
43 		if (!strcmp(name, tag->name)) {
44 			lws_dll2_remove(&tag->list);
45 			lws_free(tag);
46 			break;
47 		}
48 
49 	} lws_end_foreach_dll(d);
50 
51 	/*
52 	 * Create the new tag
53 	 */
54 
55 	tag = lws_malloc(sizeof(*tag) + vl + 1, __func__);
56 	if (!tag)
57 		return 1;
58 
59 	lws_dll2_clear(&tag->list);
60 	tag->name = name;
61 	memcpy(&tag[1], val, vl + 1);
62 
63 	lws_dll2_add_tail(&tag->list, owner);
64 
65 	return 0;
66 }
67 
68 int
lws_metrics_tag_wsi_add(struct lws * wsi,const char * name,const char * val)69 lws_metrics_tag_wsi_add(struct lws *wsi, const char *name, const char *val)
70 {
71 	__lws_lc_tag(wsi->a.context, NULL, &wsi->lc, "|%s", val);
72 
73 	return lws_metrics_tag_add(&wsi->cal_conn.mtags_owner, name, val);
74 }
75 
76 #if defined(LWS_WITH_SECURE_STREAMS)
77 int
lws_metrics_tag_ss_add(struct lws_ss_handle * ss,const char * name,const char * val)78 lws_metrics_tag_ss_add(struct lws_ss_handle *ss, const char *name, const char *val)
79 {
80 	__lws_lc_tag(ss->context, NULL, &ss->lc, "|%s", val);
81 	return lws_metrics_tag_add(&ss->cal_txn.mtags_owner, name, val);
82 }
83 #if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)
84 int
lws_metrics_tag_sspc_add(struct lws_sspc_handle * sspc,const char * name,const char * val)85 lws_metrics_tag_sspc_add(struct lws_sspc_handle *sspc, const char *name,
86 			 const char *val)
87 {
88 	__lws_lc_tag(sspc->context, NULL, &sspc->lc, "|%s", val);
89 	return lws_metrics_tag_add(&sspc->cal_txn.mtags_owner, name, val);
90 }
91 #endif
92 #endif
93 
94 void
lws_metrics_tags_destroy(lws_dll2_owner_t * owner)95 lws_metrics_tags_destroy(lws_dll2_owner_t *owner)
96 {
97 	lws_metrics_tag_t *t;
98 
99 	lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, owner->head) {
100 		t = lws_container_of(d, lws_metrics_tag_t, list);
101 
102 		lws_dll2_remove(&t->list);
103 		lws_free(t);
104 
105 	} lws_end_foreach_dll_safe(d, d1);
106 }
107 
108 size_t
lws_metrics_tags_serialize(lws_dll2_owner_t * owner,char * buf,size_t len)109 lws_metrics_tags_serialize(lws_dll2_owner_t *owner, char *buf, size_t len)
110 {
111 	char *end = buf + len - 1, *p = buf;
112 	lws_metrics_tag_t *t;
113 
114 	lws_start_foreach_dll(struct lws_dll2 *, d, owner->head) {
115 		t = lws_container_of(d, lws_metrics_tag_t, list);
116 
117 		p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
118 				  "%s=\"%s\"", t->name, (const char *)&t[1]);
119 
120 		if (d->next && p + 2 < end)
121 			*p++ = ',';
122 
123 	} lws_end_foreach_dll(d);
124 
125 	*p = '\0';
126 
127 	return lws_ptr_diff_size_t(p, buf);
128 }
129 
130 const char *
lws_metrics_tag_get(lws_dll2_owner_t * owner,const char * name)131 lws_metrics_tag_get(lws_dll2_owner_t *owner, const char *name)
132 {
133 	lws_metrics_tag_t *t;
134 
135 	lws_start_foreach_dll(struct lws_dll2 *, d, owner->head) {
136 		t = lws_container_of(d, lws_metrics_tag_t, list);
137 
138 		if (!strcmp(name, t->name))
139 			return (const char *)&t[1];
140 
141 	} lws_end_foreach_dll(d);
142 
143 	return NULL;
144 }
145 
146 static int
147 lws_metrics_dump_cb(lws_metric_pub_t *pub, void *user);
148 
149 static void
lws_metrics_report_and_maybe_clear(struct lws_context * ctx,lws_metric_pub_t * pub)150 lws_metrics_report_and_maybe_clear(struct lws_context *ctx, lws_metric_pub_t *pub)
151 {
152 	if (!pub->us_first || pub->us_last == pub->us_dumped)
153 		return;
154 
155 	lws_metrics_dump_cb(pub, ctx);
156 }
157 
158 static void
lws_metrics_periodic_cb(lws_sorted_usec_list_t * sul)159 lws_metrics_periodic_cb(lws_sorted_usec_list_t *sul)
160 {
161 	lws_metric_policy_dyn_t *dmp = lws_container_of(sul,
162 						lws_metric_policy_dyn_t, sul);
163 	struct lws_context *ctx = lws_container_of(dmp->list.owner,
164 					struct lws_context, owner_mtr_dynpol);
165 
166 	if (!ctx->system_ops || !ctx->system_ops->metric_report)
167 		return;
168 
169 	lws_start_foreach_dll(struct lws_dll2 *, d, dmp->owner.head) {
170 		lws_metric_t *mt = lws_container_of(d, lws_metric_t, list);
171 		lws_metric_pub_t *pub = lws_metrics_priv_to_pub(mt);
172 
173 		lws_metrics_report_and_maybe_clear(ctx, pub);
174 
175 	} lws_end_foreach_dll(d);
176 
177 #if defined(LWS_WITH_SYS_SMD) && defined(LWS_WITH_SECURE_STREAMS)
178 	(void)lws_smd_msg_printf(ctx, LWSSMDCL_METRICS,
179 				 "{\"dump\":\"%s\",\"ts\":%lu}",
180 				   dmp->policy->name,
181 				   (long)ctx->last_policy);
182 #endif
183 
184 	if (dmp->policy->us_schedule)
185 		lws_sul_schedule(ctx, 0, &dmp->sul,
186 				 lws_metrics_periodic_cb,
187 				 (lws_usec_t)dmp->policy->us_schedule);
188 }
189 
190 /*
191  * Policies are in two pieces, a const policy and a dynamic part that contains
192  * lists and sul timers for the policy etc.  This creates a dynmic part
193  * corresponding to the static part.
194  *
195  * Metrics can exist detached from being bound to any policy about how to
196  * report them, these are collected but not reported unless they later become
197  * bound to a reporting policy dynamically.
198  */
199 
200 lws_metric_policy_dyn_t *
lws_metrics_policy_dyn_create(struct lws_context * ctx,const lws_metric_policy_t * po)201 lws_metrics_policy_dyn_create(struct lws_context *ctx,
202 			      const lws_metric_policy_t *po)
203 {
204 	lws_metric_policy_dyn_t *dmet;
205 
206 	dmet = lws_zalloc(sizeof(*dmet), __func__);
207 	if (!dmet)
208 		return NULL;
209 
210 	dmet->policy = po;
211 	lws_dll2_add_tail(&dmet->list, &ctx->owner_mtr_dynpol);
212 
213 	if (po->us_schedule)
214 		lws_sul_schedule(ctx, 0, &dmet->sul,
215 				 lws_metrics_periodic_cb,
216 				 (lws_usec_t)po->us_schedule);
217 
218 	return dmet;
219 }
220 
221 /*
222  * Get a dynamic metrics policy from the const one, may return NULL if OOM
223  */
224 
225 lws_metric_policy_dyn_t *
lws_metrics_policy_get_dyn(struct lws_context * ctx,const lws_metric_policy_t * po)226 lws_metrics_policy_get_dyn(struct lws_context *ctx,
227 			   const lws_metric_policy_t *po)
228 {
229 	lws_start_foreach_dll(struct lws_dll2 *, d, ctx->owner_mtr_dynpol.head) {
230 		lws_metric_policy_dyn_t *dm =
231 			lws_container_of(d, lws_metric_policy_dyn_t, list);
232 
233 		if (dm->policy == po)
234 			return dm;
235 
236 	} lws_end_foreach_dll(d);
237 
238 	/*
239 	 * no dyn policy part for this const policy --> create one
240 	 *
241 	 * We want a dynamic part for listing metrics that bound to the policy
242 	 */
243 
244 	return lws_metrics_policy_dyn_create(ctx, po);
245 }
246 
247 static int
lws_metrics_check_in_policy(const char * polstring,const char * name)248 lws_metrics_check_in_policy(const char *polstring, const char *name)
249 {
250 	struct lws_tokenize ts;
251 
252 	memset(&ts, 0, sizeof(ts));
253 
254 	ts.start = polstring;
255 	ts.len = strlen(polstring);
256 	ts.flags = (uint16_t)(LWS_TOKENIZE_F_MINUS_NONTERM |
257 			      LWS_TOKENIZE_F_ASTERISK_NONTERM |
258 			      LWS_TOKENIZE_F_COMMA_SEP_LIST |
259 			      LWS_TOKENIZE_F_NO_FLOATS |
260 			      LWS_TOKENIZE_F_DOT_NONTERM);
261 
262 	do {
263 		ts.e = (int8_t)lws_tokenize(&ts);
264 
265 		if (ts.e == LWS_TOKZE_TOKEN) {
266 			if (!lws_strcmp_wildcard(ts.token, ts.token_len, name,
267 						 strlen(name)))
268 				/* yes, we are mentioned in this guy's policy */
269 				return 0;
270 		}
271 	} while (ts.e > 0);
272 
273 	/* no, this policy doesn't apply to a metric with our name */
274 
275 	return 1;
276 }
277 
278 static const lws_metric_policy_t *
lws_metrics_find_policy(struct lws_context * ctx,const char * name)279 lws_metrics_find_policy(struct lws_context *ctx, const char *name)
280 {
281 	const lws_metric_policy_t *mp = ctx->metrics_policies;
282 
283 	if (!mp) {
284 #if defined(LWS_WITH_SECURE_STREAMS)
285 		if (ctx->pss_policies)
286 			mp = ctx->pss_policies->metrics;
287 #endif
288 		if (!mp)
289 			return NULL;
290 	}
291 
292 	while (mp) {
293 		if (mp->report && !lws_metrics_check_in_policy(mp->report, name))
294 			return mp;
295 
296 		mp = mp->next;
297 	}
298 
299 	return NULL;
300 }
301 
302 /*
303  * Create a lws_metric_t, bind to a named policy if possible (or add to the
304  * context list of unbound metrics) and set its lws_system
305  * idx.  The metrics objects themselves are typically composed into other
306  * objects and are well-known composed members of them.
307  */
308 
309 lws_metric_t *
lws_metric_create(struct lws_context * ctx,uint8_t flags,const char * name)310 lws_metric_create(struct lws_context *ctx, uint8_t flags, const char *name)
311 {
312 	const lws_metric_policy_t *po;
313 	lws_metric_policy_dyn_t *dmp;
314 	lws_metric_pub_t *pub;
315 	lws_metric_t *mt;
316 	char pname[32];
317 	size_t nl;
318 
319 	if (ctx->metrics_prefix) {
320 
321 		/*
322 		 * In multi-process case, we want to prefix metrics from this
323 		 * process / context with a string distinguishing which
324 		 * application they came from
325 		 */
326 
327 		nl = (size_t)lws_snprintf(pname, sizeof(pname) - 1, "%s.%s",
328 				  ctx->metrics_prefix, name);
329 		name = pname;
330 	} else
331 		nl = strlen(name);
332 
333 	mt = (lws_metric_t *)lws_zalloc(sizeof(*mt) /* private */ +
334 					sizeof(lws_metric_pub_t) +
335 					nl + 1 /* copy of metric name */,
336 					__func__);
337 	if (!mt)
338 		return NULL;
339 
340 	pub = lws_metrics_priv_to_pub(mt);
341 	pub->name = (char *)pub + sizeof(lws_metric_pub_t);
342 	memcpy((char *)pub->name, name, nl + 1);
343 	pub->flags = flags;
344 
345 	/* after these common members, we have to use the right type */
346 
347 	if (!(flags & LWSMTFL_REPORT_HIST)) {
348 		/* anything is smaller or equal to this */
349 		pub->u.agg.min = ~(u_mt_t)0;
350 		pub->us_first = lws_now_usecs();
351 	}
352 
353 	mt->ctx = ctx;
354 
355 	/*
356 	 * Let's see if we can bind to a reporting policy straight away
357 	 */
358 
359 	po = lws_metrics_find_policy(ctx, name);
360 	if (po) {
361 		dmp = lws_metrics_policy_get_dyn(ctx, po);
362 		if (dmp) {
363 			lwsl_notice("%s: metpol %s\n", __func__, name);
364 			lws_dll2_add_tail(&mt->list, &dmp->owner);
365 
366 			return 0;
367 		}
368 	}
369 
370 	/*
371 	 * If not, well, let's go on without and maybe later at runtime, he'll
372 	 * get interested in us and apply a reporting policy
373 	 */
374 
375 	lws_dll2_add_tail(&mt->list, &ctx->owner_mtr_no_pol);
376 
377 	return mt;
378 }
379 
380 /*
381  * If our metric is bound to a reporting policy, return a pointer to it,
382  * otherwise NULL
383  */
384 
385 const lws_metric_policy_t *
lws_metric_get_policy(lws_metric_t * mt)386 lws_metric_get_policy(lws_metric_t *mt)
387 {
388 	lws_metric_policy_dyn_t *dp;
389 
390 	/*
391 	 * Our metric must either be on the "no policy" context list or
392 	 * listed by the dynamic part of the policy it is bound to
393 	 */
394 	assert(mt->list.owner);
395 
396 	if ((char *)mt->list.owner >= (char *)mt->ctx &&
397 	    (char *)mt->list.owner < (char *)mt->ctx + sizeof(struct lws_context))
398 		/* we are on the "no policy" context list */
399 		return NULL;
400 
401 	/* we are listed by a dynamic policy owner */
402 
403 	dp = lws_container_of(mt->list.owner, lws_metric_policy_dyn_t, owner);
404 
405 	/* return the const policy the dynamic policy represents */
406 
407 	return dp->policy;
408 }
409 
410 void
lws_metric_rebind_policies(struct lws_context * ctx)411 lws_metric_rebind_policies(struct lws_context *ctx)
412 {
413 	const lws_metric_policy_t *po;
414 	lws_metric_policy_dyn_t *dmp;
415 
416 	lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
417 				   ctx->owner_mtr_no_pol.head) {
418 		lws_metric_t *mt = lws_container_of(d, lws_metric_t, list);
419 		lws_metric_pub_t *pub = lws_metrics_priv_to_pub(mt);
420 
421 		po = lws_metrics_find_policy(ctx, pub->name);
422 		if (po) {
423 			dmp = lws_metrics_policy_get_dyn(ctx, po);
424 			if (dmp) {
425 				lwsl_info("%s: %s <- pol %s\n", __func__,
426 						pub->name, po->name);
427 				lws_dll2_remove(&mt->list);
428 				lws_dll2_add_tail(&mt->list, &dmp->owner);
429 			}
430 		} else
431 			lwsl_debug("%s: no pol for %s\n", __func__, pub->name);
432 
433 	} lws_end_foreach_dll_safe(d, d1);
434 }
435 
436 int
lws_metric_destroy(lws_metric_t ** pmt,int keep)437 lws_metric_destroy(lws_metric_t **pmt, int keep)
438 {
439 	lws_metric_t *mt = *pmt;
440 	lws_metric_pub_t *pub;
441 
442 	if (!mt)
443 		return 0;
444 
445 	pub = lws_metrics_priv_to_pub(mt);
446 
447 	lws_dll2_remove(&mt->list);
448 
449 	if (keep) {
450 		lws_dll2_add_tail(&mt->list, &mt->ctx->owner_mtr_no_pol);
451 
452 		return 0;
453 	}
454 
455 	if (pub->flags & LWSMTFL_REPORT_HIST) {
456 		lws_metric_bucket_t *b = pub->u.hist.head, *b1;
457 
458 		pub->u.hist.head = NULL;
459 
460 		while (b) {
461 			b1 = b->next;
462 			lws_free(b);
463 			b = b1;
464 		}
465 	}
466 
467 	lws_free(mt);
468 	*pmt = NULL;
469 
470 	return 0;
471 }
472 
473 /*
474  * Allow an existing metric to have its reporting policy changed at runtime
475  */
476 
477 int
lws_metric_switch_policy(lws_metric_t * mt,const char * polname)478 lws_metric_switch_policy(lws_metric_t *mt, const char *polname)
479 {
480 	const lws_metric_policy_t *po;
481 	lws_metric_policy_dyn_t *dmp;
482 
483 	po = lws_metrics_find_policy(mt->ctx, polname);
484 	if (!po)
485 		return 1;
486 
487 	dmp = lws_metrics_policy_get_dyn(mt->ctx, po);
488 	if (!dmp)
489 		return 1;
490 
491 	lws_dll2_remove(&mt->list);
492 	lws_dll2_add_tail(&mt->list, &dmp->owner);
493 
494 	return 0;
495 }
496 
497 /*
498  * If keep is set, don't destroy existing metrics objects, just detach them
499  * from the policy being deleted and keep track of them on ctx->
500  * owner_mtr_no_pol
501  */
502 
503 void
lws_metric_policy_dyn_destroy(lws_metric_policy_dyn_t * dm,int keep)504 lws_metric_policy_dyn_destroy(lws_metric_policy_dyn_t *dm, int keep)
505 {
506 	lws_sul_cancel(&dm->sul);
507 
508 	lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, dm->owner.head) {
509 		lws_metric_t *m = lws_container_of(d, lws_metric_t, list);
510 
511 		lws_metric_destroy(&m, keep);
512 
513 	} lws_end_foreach_dll_safe(d, d1);
514 
515 	lws_sul_cancel(&dm->sul);
516 
517 	lws_dll2_remove(&dm->list);
518 	lws_free(dm);
519 }
520 
521 /*
522  * Destroy all dynamic metrics policies, deinit any metrics still using them
523  */
524 
525 void
lws_metrics_destroy(struct lws_context * ctx)526 lws_metrics_destroy(struct lws_context *ctx)
527 {
528 	lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
529 				   ctx->owner_mtr_dynpol.head) {
530 		lws_metric_policy_dyn_t *dm =
531 			lws_container_of(d, lws_metric_policy_dyn_t, list);
532 
533 		lws_metric_policy_dyn_destroy(dm, 0); /* don't keep */
534 
535 	} lws_end_foreach_dll_safe(d, d1);
536 
537 	/* destroy metrics with no current policy too... */
538 
539 	lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
540 				   ctx->owner_mtr_no_pol.head) {
541 		lws_metric_t *mt = lws_container_of(d, lws_metric_t, list);
542 
543 		lws_metric_destroy(&mt, 0); /* don't keep */
544 
545 	} lws_end_foreach_dll_safe(d, d1);
546 
547 	/* ... that's the whole allocated metrics footprint gone... */
548 }
549 
550 int
lws_metrics_hist_bump_(lws_metric_pub_t * pub,const char * name)551 lws_metrics_hist_bump_(lws_metric_pub_t *pub, const char *name)
552 {
553 	lws_metric_bucket_t *buck = pub->u.hist.head;
554 	size_t nl = strlen(name);
555 	char *nm;
556 
557 	if (!(pub->flags & LWSMTFL_REPORT_HIST)) {
558 		lwsl_err("%s: %s not histogram: flags %d\n", __func__,
559 				pub->name, pub->flags);
560 		assert(0);
561 	}
562 	assert(nl < 255);
563 
564 	pub->us_last = lws_now_usecs();
565 	if (!pub->us_first)
566 		pub->us_first = pub->us_last;
567 
568 	while (buck) {
569 		if (lws_metric_bucket_name_len(buck) == nl &&
570 		    !strcmp(name, lws_metric_bucket_name(buck))) {
571 			buck->count++;
572 			goto happy;
573 		}
574 		buck = buck->next;
575 	}
576 
577 	buck = lws_malloc(sizeof(*buck) + nl + 2, __func__);
578 	if (!buck)
579 		return 1;
580 
581 	nm = (char *)buck + sizeof(*buck);
582 	/* length byte at beginning of name, avoid struct alignment overhead */
583 	*nm = (char)nl;
584 	memcpy(nm + 1, name, nl + 1);
585 
586 	buck->next = pub->u.hist.head;
587 	pub->u.hist.head = buck;
588 	buck->count = 1;
589 	pub->u.hist.list_size++;
590 
591 happy:
592 	pub->u.hist.total_count++;
593 
594 	return 0;
595 }
596 
597 int
lws_metrics_hist_bump_describe_wsi(struct lws * wsi,lws_metric_pub_t * pub,const char * name)598 lws_metrics_hist_bump_describe_wsi(struct lws *wsi, lws_metric_pub_t *pub,
599 				   const char *name)
600 {
601 	char desc[192], d1[48], *p = desc, *end = desc + sizeof(desc);
602 
603 #if defined(LWS_WITH_SECURE_STREAMS)
604 #if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)
605 	if (wsi->client_bound_sspc) {
606 		lws_sspc_handle_t *h = (lws_sspc_handle_t *)wsi->a.opaque_user_data;
607 		if (h)
608 			p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "ss=\"%s\",",
609 				  h->ssi.streamtype);
610 	} else
611 		if (wsi->client_proxy_onward) {
612 			lws_ss_handle_t *h = (lws_ss_handle_t *)wsi->a.opaque_user_data;
613 			struct conn *conn = h->conn_if_sspc_onw;
614 
615 			if (conn && conn->ss)
616 				p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
617 						  "ss=\"%s\",",
618 						  conn->ss->info.streamtype);
619 		} else
620 #endif
621 	if (wsi->for_ss) {
622 		lws_ss_handle_t *h = (lws_ss_handle_t *)wsi->a.opaque_user_data;
623 		if (h)
624 			p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "ss=\"%s\",",
625 				  h->info.streamtype);
626 	}
627 #endif
628 
629 #if defined(LWS_WITH_CLIENT)
630 	if (wsi->stash && wsi->stash->cis[CIS_HOST])
631 		p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "hostname=\"%s\",",
632 				wsi->stash->cis[CIS_HOST]);
633 #endif
634 
635 	lws_sa46_write_numeric_address(&wsi->sa46_peer, d1, sizeof(d1));
636 	p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "peer=\"%s\",", d1);
637 
638 	p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "%s", name);
639 
640 	lws_metrics_hist_bump_(pub, desc);
641 
642 	return 0;
643 }
644 
645 int
lws_metrics_foreach(struct lws_context * ctx,void * user,int (* cb)(lws_metric_pub_t * pub,void * user))646 lws_metrics_foreach(struct lws_context *ctx, void *user,
647 		    int (*cb)(lws_metric_pub_t *pub, void *user))
648 {
649 	int n;
650 
651 	lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
652 				   ctx->owner_mtr_no_pol.head) {
653 		lws_metric_t *mt = lws_container_of(d, lws_metric_t, list);
654 
655 		n = cb(lws_metrics_priv_to_pub(mt), user);
656 		if (n)
657 			return n;
658 
659 	} lws_end_foreach_dll_safe(d, d1);
660 
661 	lws_start_foreach_dll_safe(struct lws_dll2 *, d2, d3,
662 				   ctx->owner_mtr_dynpol.head) {
663 		lws_metric_policy_dyn_t *dm =
664 			lws_container_of(d2, lws_metric_policy_dyn_t, list);
665 
666 		lws_start_foreach_dll_safe(struct lws_dll2 *, e, e1,
667 					   dm->owner.head) {
668 
669 			lws_metric_t *mt = lws_container_of(e, lws_metric_t, list);
670 
671 			n = cb(lws_metrics_priv_to_pub(mt), user);
672 			if (n)
673 				return n;
674 
675 		} lws_end_foreach_dll_safe(e, e1);
676 
677 	} lws_end_foreach_dll_safe(d2, d3);
678 
679 	return 0;
680 }
681 
682 static int
lws_metrics_dump_cb(lws_metric_pub_t * pub,void * user)683 lws_metrics_dump_cb(lws_metric_pub_t *pub, void *user)
684 {
685 	struct lws_context *ctx = (struct lws_context *)user;
686 	int n;
687 
688 	if (!ctx->system_ops || !ctx->system_ops->metric_report)
689 		return 0;
690 
691 	/*
692 	 * return nonzero to reset stats
693 	 */
694 
695 	n = ctx->system_ops->metric_report(pub);
696 
697 	/* track when we dumped it... */
698 
699 	pub->us_first = pub->us_dumped = lws_now_usecs();
700 	pub->us_last = 0;
701 
702 	if (!n)
703 		return 0;
704 
705 	/* ... and clear it back to 0 */
706 
707 	if (pub->flags & LWSMTFL_REPORT_HIST) {
708 		lws_metric_bucket_t *b = pub->u.hist.head, *b1;
709 		pub->u.hist.head = NULL;
710 
711 		while (b) {
712 			b1 = b->next;
713 			lws_free(b);
714 			b = b1;
715 		}
716 		pub->u.hist.total_count = 0;
717 		pub->u.hist.list_size = 0;
718 	} else
719 		memset(&pub->u.agg, 0, sizeof(pub->u.agg));
720 
721 	return 0;
722 }
723 
724 void
lws_metrics_dump(struct lws_context * ctx)725 lws_metrics_dump(struct lws_context *ctx)
726 {
727 	lws_metrics_foreach(ctx, ctx, lws_metrics_dump_cb);
728 }
729 
730 static int
_lws_metrics_format(lws_metric_pub_t * pub,lws_usec_t now,int gng,char * buf,size_t len)731 _lws_metrics_format(lws_metric_pub_t *pub, lws_usec_t now, int gng,
732 		    char *buf, size_t len)
733 {
734 	const lws_humanize_unit_t *schema = humanize_schema_si;
735 	char *end = buf + len - 1, *obuf = buf;
736 
737 	if (pub->flags & LWSMTFL_REPORT_DUTY_WALLCLOCK_US)
738 		schema = humanize_schema_us;
739 
740 	if (!(pub->flags & LWSMTFL_REPORT_MEAN)) {
741 		/* only the sum is meaningful */
742 		if (pub->flags & LWSMTFL_REPORT_DUTY_WALLCLOCK_US) {
743 
744 			buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf), " %u, ",
745 						(unsigned int)pub->u.agg.count[gng]);
746 
747 			buf += lws_humanize(buf, lws_ptr_diff_size_t(end, buf),
748 					    (uint64_t)pub->u.agg.sum[gng],
749 					    humanize_schema_us);
750 
751 			buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf), " / ");
752 
753 			buf += lws_humanize(buf, lws_ptr_diff_size_t(end, buf),
754 					    (uint64_t)(now - pub->us_first),
755 					    humanize_schema_us);
756 
757 			buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf),
758 					    " (%d%%)", (int)((100 * pub->u.agg.sum[gng]) /
759 						(unsigned long)(now - pub->us_first)));
760 		} else {
761 			/* it's a monotonic ordinal, like total tx */
762 			buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf), "(%u) ",
763 					(unsigned int)pub->u.agg.count[gng]);
764 			buf += lws_humanize(buf, lws_ptr_diff_size_t(end, buf),
765 					    (uint64_t)pub->u.agg.sum[gng],
766 					    humanize_schema_si);
767 		}
768 
769 	} else {
770 		buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf), "%u, mean: ", (unsigned int)pub->u.agg.count[gng]);
771 		/* the average over the period is meaningful */
772 		buf += lws_humanize(buf, lws_ptr_diff_size_t(end, buf),
773 				    (uint64_t)(pub->u.agg.count[gng] ?
774 					 pub->u.agg.sum[gng] / pub->u.agg.count[gng] : 0),
775 				    schema);
776 	}
777 
778 	return lws_ptr_diff(buf, obuf);
779 }
780 
781 int
lws_metrics_format(lws_metric_pub_t * pub,lws_metric_bucket_t ** sub,char * buf,size_t len)782 lws_metrics_format(lws_metric_pub_t *pub, lws_metric_bucket_t **sub, char *buf, size_t len)
783 {
784 	char *end = buf + len - 1, *obuf = buf;
785 	lws_usec_t t = lws_now_usecs();
786 	const lws_humanize_unit_t *schema = humanize_schema_si;
787 
788 	if (pub->flags & LWSMTFL_REPORT_DUTY_WALLCLOCK_US)
789 		schema = humanize_schema_us;
790 
791 	if (pub->flags & LWSMTFL_REPORT_HIST) {
792 
793 		if (*sub == NULL)
794 			return 0;
795 
796 		if (*sub) {
797 			buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf),
798 					    "%s{%s} %llu", pub->name,
799 					    lws_metric_bucket_name(*sub),
800 					    (unsigned long long)(*sub)->count);
801 
802 			*sub = (*sub)->next;
803 		}
804 
805 		goto happy;
806 	}
807 
808 	buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf), "%s: ",
809 				pub->name);
810 
811 	if (!pub->u.agg.count[METRES_GO] && !pub->u.agg.count[METRES_NOGO])
812 		return 0;
813 
814 	if (pub->u.agg.count[METRES_GO]) {
815 		if (!(pub->flags & LWSMTFL_REPORT_ONLY_GO))
816 			buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf),
817 					    "Go: ");
818 		buf += _lws_metrics_format(pub, t, METRES_GO, buf,
819 					   lws_ptr_diff_size_t(end, buf));
820 	}
821 
822 	if (!(pub->flags & LWSMTFL_REPORT_ONLY_GO) && pub->u.agg.count[METRES_NOGO]) {
823 		buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf), ", NoGo: ");
824 		buf += _lws_metrics_format(pub, t, METRES_NOGO, buf,
825 					   lws_ptr_diff_size_t(end, buf));
826 	}
827 
828 	if (pub->flags & LWSMTFL_REPORT_MEAN) {
829 		buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf), ", min: ");
830 		buf += lws_humanize(buf, lws_ptr_diff_size_t(end, buf), pub->u.agg.min,
831 				    schema);
832 		buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf), ", max: ");
833 		buf += lws_humanize(buf, lws_ptr_diff_size_t(end, buf), pub->u.agg.max,
834 				    schema);
835 	}
836 
837 happy:
838 	if (pub->flags & LWSMTFL_REPORT_HIST)
839 		return 1;
840 
841 	*sub = NULL;
842 
843 	return lws_ptr_diff(buf, obuf);
844 }
845 
846 /*
847  * We want to, at least internally, record an event... depending on the policy,
848  * that might cause us to call through to the lws_system apis, or just update
849  * our local stats about it and dump at the next periodic chance (also set by
850  * the policy)
851  */
852 
853 void
lws_metric_event(lws_metric_t * mt,char go_nogo,u_mt_t val)854 lws_metric_event(lws_metric_t *mt, char go_nogo, u_mt_t val)
855 {
856 	lws_metric_pub_t *pub;
857 
858 	assert((go_nogo & 0xfe) == 0);
859 
860 	if (!mt)
861 		return;
862 
863 	pub = lws_metrics_priv_to_pub(mt);
864 	assert(!(pub->flags & LWSMTFL_REPORT_HIST));
865 
866 	pub->us_last = lws_now_usecs();
867 	if (!pub->us_first)
868 		pub->us_first = pub->us_last;
869 	pub->u.agg.count[(int)go_nogo]++;
870 	pub->u.agg.sum[(int)go_nogo] += val;
871 	if (val > pub->u.agg.max)
872 		pub->u.agg.max = val;
873 	if (val < pub->u.agg.min)
874 		pub->u.agg.min = val;
875 
876 	if (pub->flags & LWSMTFL_REPORT_OOB)
877 		lws_metrics_report_and_maybe_clear(mt->ctx, pub);
878 }
879 
880 
881 void
lws_metrics_hist_bump_priv_tagged(lws_metric_pub_t * mt,lws_dll2_owner_t * tow,lws_dll2_owner_t * tow2)882 lws_metrics_hist_bump_priv_tagged(lws_metric_pub_t *mt, lws_dll2_owner_t *tow,
883 				  lws_dll2_owner_t *tow2)
884 {
885 	char qual[192];
886 	size_t p;
887 
888 	p = lws_metrics_tags_serialize(tow, qual, sizeof(qual));
889 	if (tow2)
890 		lws_metrics_tags_serialize(tow2, qual + p,
891 				sizeof(qual) - p);
892 
893 	lws_metrics_hist_bump(mt, qual);
894 }
895