/* ** Copyright (C) 2001-2024 Zabbix SIA ** ** This program is free software: you can redistribute it and/or modify it under the terms of ** the GNU Affero General Public License as published by the Free Software Foundation, version 3. ** ** This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; ** without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU Affero General Public License for more details. ** ** You should have received a copy of the GNU Affero General Public License along with this program. ** If not, see <https://www.gnu.org/licenses/>. **/ #include "browser_perf.h" #include "zbxjson.h" #ifdef HAVE_LIBCURL #include "zbxalgo.h" #include "zbxnum.h" #include "zbxstr.h" #define WD_PERF_TAG_ENTRY_TYPE "entryType" #define WD_PERF_ENTRY_NAVIGATION "navigation" #define WD_PERF_ENTRY_RESOURCE "resource" #define WD_PERF_ENTRY_MARK "mark" #define WD_PERF_ENTRY_MEASURE "measure" #define WD_PERF_ATTR_COUNT "count" #define WD_PERF_ATTR_REDIRECT_TIME "redirect_time" #define WD_PERF_ATTR_REDIRECT_COUNT "redirect_count" #define WD_PERF_ATTR_DNS_LOOKUP_TIME "dns_lookup_time" #define WD_PERF_ATTR_TCP_HANDSHAKE_TIME "tcp_handshake_time" #define WD_PERF_ATTR_TLS_NEGOTIATION_TIME "tls_negotiation_time" #define WD_PERF_ATTR_REQUEST_TIME "request_time" #define WD_PERF_ATTR_RESPONSE_TIME "response_time" #define WD_PERF_ATTR_RESOURCE_FETCH_TIME "resource_fetch_time" #define WD_PERF_ATTR_UNLOAD_EVENT_HANDLER_TIME "unload_event_handler_time" #define WD_PERF_ATTR_LOAD_EVENT_HANDLER_TIME "load_event_handler_time" #define WD_PERF_ATTR_DOM_CONTENT_LOADING_TIME "dom_content_loading_time" #define WD_PERF_ATTR_TRANSFERRED_SIZE "transferred_size" #define WD_PERF_ATTR_ENCODED_SIZE "encoded_size" #define WD_PERF_ATTR_TOTAL_SIZE "total_size" #define WD_PERF_ATTR_LOAD_FINISHED "load_finished" #define WD_PERF_ATTR_MIN_PROTOCOL "min_protocol" ZBX_PTR_VECTOR_IMPL(wd_attr_ptr, zbx_wd_attr_t *) ZBX_PTR_VECTOR_IMPL(wd_perf_entry_ptr, zbx_wd_perf_entry_t *) ZBX_VECTOR_IMPL(wd_perf_details, zbx_wd_perf_details_t) ZBX_VECTOR_IMPL(wd_perf_bookmark, zbx_wd_perf_bookmark_t) static int wd_perf_attr_compare(const void *d1, const void *d2) { const char *n1 = *(const char * const *)d1; const char *n2 = *(const char * const *)d2; return strcmp(n1, n2); } /****************************************************************************** * * * Purpose: check if the attribute contains time based metric * * * * Return value: SUCCEED - attribute contains time based metric * * FAIL - otherwise * * * ******************************************************************************/ static int wd_perf_is_time_based_attribute(const char *name) { static const char *attributes[] = {"activation_start", "connect_end", "connect_start", "critical_ch_restart", "dom_complete", "dom_content_loaded_event_end", "dom_content_loaded_event_start", "dom_interactive", "domain_lookup_end", "domain_lookup_start", "duration", "fetch_start", "first_interim_response_start", "load_event_end", "load_event_start", "redirect_end", "redirect_start", "request_start", "response_end", "response_start", "secure_connection_start", "start_time", "unload_event_end", "unload_event_start", "worker_start"}; static int sorted = 0; if (0 == sorted) { /* attributes MUST be sorted as they are used in binary search, sort */ /* it once to avoid possible sorting mistakes in array declaration */ qsort(attributes, ARRSIZE(attributes), sizeof(attributes[0]), wd_perf_attr_compare); sorted = 1; } if (NULL == bsearch(&name, attributes, ARRSIZE(attributes), sizeof(attributes[0]), wd_perf_attr_compare)) return FAIL; return SUCCEED; } /****************************************************************************** * * * Purpose: extract attribute from json key,value pair * * * ******************************************************************************/ static int wd_perf_init_attribute_from_json(zbx_wd_attr_t *attr, const char *name, const char *p) { char *value = NULL; size_t value_alloc = 0; zbx_json_type_t value_type; if (NULL == zbx_json_decodevalue_dyn(p, &value, &value_alloc, &value_type)) { struct zbx_json_parse jp_value; void *json_raw; if (FAIL == zbx_json_brackets_open(p, &jp_value)) { zabbix_log(LOG_LEVEL_DEBUG, "cannot parse attribute \"%s\" value: %s", name, zbx_json_strerror()); return FAIL; } json_raw = zbx_variant_data_bin_create(jp_value.start, (zbx_uint32_t)(jp_value.end - jp_value.start + 1)); zbx_replace_invalid_utf8(json_raw); zbx_variant_set_bin(&attr->value, json_raw); } else { double value_dbl; switch (value_type) { case ZBX_JSON_TYPE_INT: case ZBX_JSON_TYPE_NUMBER: (void)zbx_is_double(value, &value_dbl); /* convert time based attribute values to seconds */ if (SUCCEED == wd_perf_is_time_based_attribute(name)) value_dbl /= 1000; zbx_variant_set_dbl(&attr->value, value_dbl); zbx_free(value); break; case ZBX_JSON_TYPE_STRING: case ZBX_JSON_TYPE_TRUE: case ZBX_JSON_TYPE_FALSE: zbx_replace_invalid_utf8(value); zbx_variant_set_str(&attr->value, value); break; case ZBX_JSON_TYPE_NULL: zbx_variant_set_none(&attr->value); zbx_free(value); break; default: zabbix_log(LOG_LEVEL_DEBUG, "invalid attribute \"%s\" value \"%s\"", name, value); zbx_free(value); return FAIL; } } attr->name = zbx_strdup(NULL, name); return SUCCEED; } /****************************************************************************** * * * Purpose: initialize attribute of floating point type * * * ******************************************************************************/ static void wd_perf_init_attribute_from_dbl(zbx_wd_attr_t *attr, const char *name, double value) { attr->name = zbx_strdup(NULL, name); zbx_variant_set_dbl(&attr->value, value); } /****************************************************************************** * * * Purpose: set performance entry attribute * * * * Comments: The performance entry takes over attribute ownership. * * If the attribute has been already set its value is overwritten * * (freeing old resources). * * * ******************************************************************************/ static void wd_perf_entry_set_attribute(zbx_wd_perf_entry_t *entry, zbx_wd_attr_t *attr_local) { zbx_wd_attr_t *attr; attr = (zbx_wd_attr_t *)zbx_hashset_insert(&entry->attrs, attr_local, sizeof(zbx_wd_attr_t)); /* check if the attribute was actually inserted or existing one was returned */ if (attr->name != attr_local->name) { /* overwrite existing attribute value */ zbx_variant_clear(&attr->value); attr->value = attr_local->value; zbx_free(attr_local->name); } } /****************************************************************************** * * * Purpose: get performance entry attribute * * * * Comments: The performance entry takes over attribute ownership. * * If the attribute has been already set its value is overwritten * * (freeing old resources). * * * ******************************************************************************/ static zbx_wd_attr_t *wd_perf_entry_get_attribute(zbx_wd_perf_entry_t *entry, const char *name) { zbx_wd_attr_t attr_local; attr_local.name = (char *)(uintptr_t)name; return (zbx_wd_attr_t *)zbx_hashset_search(&entry->attrs, &attr_local); } /****************************************************************************** * * * Purpose: attribute hashset support functions * * * ******************************************************************************/ static zbx_hash_t wd_attr_hash(const void *d) { const zbx_wd_attr_t *attr = (const zbx_wd_attr_t *)d; return ZBX_DEFAULT_STRING_HASH_FUNC(attr->name); } static int wd_attr_compare(const void *d1, const void *d2) { const zbx_wd_attr_t *attr1 = (const zbx_wd_attr_t *)d1; const zbx_wd_attr_t *attr2 = (const zbx_wd_attr_t *)d2; return strcmp(attr1->name, attr2->name); } /****************************************************************************** * * * Purpose: clear attribute freeing its resources * * * ******************************************************************************/ static void wd_attr_clear(void *d) { zbx_wd_attr_t *attr = (zbx_wd_attr_t *)d; zbx_free(attr->name); zbx_variant_clear(&attr->value); } /****************************************************************************** * * * Purpose: create empty performance entry * * * ******************************************************************************/ static zbx_wd_perf_entry_t *wd_perf_entry_create(void) { zbx_wd_perf_entry_t *entry; entry = (zbx_wd_perf_entry_t *)zbx_malloc(NULL, sizeof(zbx_wd_perf_entry_t)); zbx_hashset_create_ext(&entry->attrs, 0, wd_attr_hash, wd_attr_compare, wd_attr_clear, ZBX_DEFAULT_MEM_MALLOC_FUNC, ZBX_DEFAULT_MEM_REALLOC_FUNC, ZBX_DEFAULT_MEM_FREE_FUNC); return entry; } /****************************************************************************** * * * Purpose: convert lower camel case attribute name to lower snake case * * * * Comments: The name will be converted inside input buffer if possible. * * If the input buffer is too small, then the return value will be * * allocated and must be freed by caller. * * * ******************************************************************************/ static char *wd_convert_attribute_name(char *buf, size_t size) { char *in, *out; size_t new_size = 0; int upper_num = 0, lower_num = 0; for (out = buf; '\0' != *out; out++) { if (0 != isupper((int)*out)) { if (0 == upper_num++ && 0 != lower_num) new_size++; } else { lower_num++; if (1 < upper_num) new_size++; upper_num = 0; } } in = out; new_size += (size_t)(out - buf + 1); if (new_size > size) buf = (char *)zbx_malloc(NULL, new_size); out = buf + new_size - 1; upper_num = 0; lower_num = 0; /* copy terminating zero */ *out-- = *in--; /* copy the rest of data */ while (out > buf && out != in) { if (0 != isupper((int)*in)) { *out = (char)tolower((int)*in); if (0 == upper_num++ && 0 != lower_num) *--out = '_'; } else { if (1 < upper_num) *out-- = '_'; upper_num = 0; lower_num++; *out = *in; } in--; out--; } return buf; } /****************************************************************************** * * * Purpose: create performance entry from retrieved entry in json format * * * ******************************************************************************/ static zbx_wd_perf_entry_t *wd_perf_entry_create_from_json(const struct zbx_json_parse *jp) { zbx_wd_perf_entry_t *entry; const char *p = NULL; char buf[MAX_STRING_LEN], *name; entry = wd_perf_entry_create(); while (NULL != (p = zbx_json_pair_next(jp, p, buf, sizeof(buf)))) { zbx_wd_attr_t attr_local; name = wd_convert_attribute_name(buf, sizeof(buf)); if (SUCCEED == wd_perf_init_attribute_from_json(&attr_local, name, p)) wd_perf_entry_set_attribute(entry, &attr_local); if (buf != name) zbx_free(name); } return entry; } /****************************************************************************** * * * Purpose: free performance entry * * * ******************************************************************************/ static void wd_perf_entry_free(zbx_wd_perf_entry_t *entry) { if (NULL != entry) { zbx_hashset_destroy(&entry->attrs); zbx_free(entry); } } /****************************************************************************** * * * Purpose: merge attribute src into dst * * * * Comments: numeric values are added * * string/error values are concatenated with newline separator * * vectors are appended * * * ******************************************************************************/ static void wd_attr_merge(zbx_wd_attr_t *dst, const zbx_wd_attr_t *src) { char *tmp = NULL; size_t tmp_alloc = 0, tmp_offset = 0; if (dst->value.type != src->value.type) { zabbix_log(LOG_LEVEL_DEBUG, "cannot merge attribute \"%s\" values of different types \"%s\" and \"%s\"", dst->name, zbx_variant_type_desc(&dst->value), zbx_variant_type_desc(&src->value)); return; } switch (dst->value.type) { case ZBX_VARIANT_STR: zbx_strcpy_alloc(&tmp, &tmp_alloc, &tmp_offset, dst->value.data.str); zbx_chrcpy_alloc(&tmp, &tmp_alloc, &tmp_offset, '\n'); zbx_strcpy_alloc(&tmp, &tmp_alloc, &tmp_offset, src->value.data.str); zbx_variant_clear(&dst->value); zbx_variant_set_str(&dst->value, tmp); break; case ZBX_VARIANT_DBL: dst->value.data.dbl += src->value.data.dbl; break; case ZBX_VARIANT_UI64: dst->value.data.ui64 += src->value.data.ui64; break; case ZBX_VARIANT_VECTOR: for (int i = 0; i < src->value.data.vector->values_num; i++) { zbx_variant_t var; zbx_variant_copy(&var, &src->value.data.vector->values[i]); zbx_vector_var_append(dst->value.data.vector, var); } break; case ZBX_VARIANT_ERR: zbx_strcpy_alloc(&tmp, &tmp_alloc, &tmp_offset, dst->value.data.err); zbx_chrcpy_alloc(&tmp, &tmp_alloc, &tmp_offset, '\n'); zbx_strcpy_alloc(&tmp, &tmp_alloc, &tmp_offset, src->value.data.err); zbx_variant_clear(&dst->value); zbx_variant_set_str(&dst->value, tmp); break; default: zabbix_log(LOG_LEVEL_DEBUG, "cannot merge attribute \"%s\" values of type \"%s\"", dst->name, zbx_variant_type_desc(&dst->value)); break; } } /****************************************************************************** * * * Purpose: merge src performance entry into dst by merging existing * * attributes and copying over new ones * * * ******************************************************************************/ static void wd_perf_entry_merge(zbx_wd_perf_entry_t *dst, zbx_wd_perf_entry_t *src) { zbx_hashset_iter_t iter; zbx_wd_attr_t *attr_src, *attr_dst, attr_local; zbx_hashset_iter_reset(&src->attrs, &iter); while (NULL != (attr_src = (zbx_wd_attr_t *)zbx_hashset_iter_next(&iter))) { if (NULL == (attr_dst = (zbx_wd_attr_t *)zbx_hashset_search(&dst->attrs, attr_src))) { attr_local.name = zbx_strdup(NULL, attr_src->name); zbx_variant_copy(&attr_local.value, &attr_src->value); zbx_hashset_insert(&dst->attrs, &attr_local, sizeof(attr_local)); } else wd_attr_merge(attr_dst, attr_src); } } /****************************************************************************** * * * Purpose: copy attribute from src performance entry into dst * * * * Parameters: dst - [IN] destination performance entry * * dst_name - [IN] destination attribute name * * src - [IN] source performance entry * * src_name - [IN] source attribute name * * * ******************************************************************************/ static void wd_perf_entry_copy_attr(zbx_wd_perf_entry_t *dst, const char *dst_name, zbx_wd_perf_entry_t *src, const char *src_name) { zbx_wd_attr_t attr_local, *attr; attr_local.name = zbx_strdup(NULL, dst_name); if (NULL != (attr = wd_perf_entry_get_attribute(src, src_name))) zbx_variant_copy(&attr_local.value, &attr->value); else zbx_variant_set_dbl(&attr_local.value, 0.0); wd_perf_entry_set_attribute(dst, &attr_local); } /****************************************************************************** * * * Purpose: get attribute value as floating point or return 0 if it * * cannot be converted * * * ******************************************************************************/ static double wd_attr_get_value_dbl(zbx_wd_attr_t *attr) { zbx_variant_t var; double value; zbx_variant_copy(&var, &attr->value); if (SUCCEED == zbx_variant_convert(&var, ZBX_VARIANT_DBL)) value = var.data.dbl; else value = 0; zbx_variant_clear(&var); return value; } /****************************************************************************** * * * Purpose: store difference between end and start attributes from src * * performance entry to dst_name attribute into dst * * * * Parameters: dst - [IN] destination performance entry * * dst_name - [IN] destination attribute name * * src - [IN] source performance entry * * start - [IN] start attribute name * * end - [IN] end attribute name * * * ******************************************************************************/ static void wd_perf_entry_diff_attrs(zbx_wd_perf_entry_t *dst, const char *dst_name, zbx_wd_perf_entry_t *src, const char *start, const char *end) { zbx_wd_attr_t attr_local, *attr; double value; attr_local.name = zbx_strdup(NULL, dst_name); if (NULL != (attr = wd_perf_entry_get_attribute(src, end))) value = wd_attr_get_value_dbl(attr); else value = 0; zbx_variant_set_dbl(&attr_local.value, value); if (NULL != (attr = wd_perf_entry_get_attribute(src, start))) value = wd_attr_get_value_dbl(attr); else value = 0; attr_local.value.data.dbl -= value; wd_perf_entry_set_attribute(dst, &attr_local); } /****************************************************************************** * * * Purpose: set minimum protocol version attribute * * * * Parameters: dst - [IN] destination performance entry * * src - [IN] source performance entry * * * * Comments: Compare min_protocol attribute in dst and src performance * * entries and set the less value to dst min_protocol attribute * * * ******************************************************************************/ static void wd_perf_entry_set_min_protocol(zbx_wd_perf_entry_t *dst, zbx_wd_perf_entry_t *src) { zbx_wd_attr_t *attr_src, *attr_dst, attr_local; if (NULL == (attr_src = wd_perf_entry_get_attribute(src, "next_hop_protocol"))) { if (NULL == (attr_src = wd_perf_entry_get_attribute(src, WD_PERF_ATTR_MIN_PROTOCOL))) return; } if (NULL == (attr_dst = wd_perf_entry_get_attribute(dst, WD_PERF_ATTR_MIN_PROTOCOL))) { attr_local.name = zbx_strdup(NULL, WD_PERF_ATTR_MIN_PROTOCOL); zbx_variant_copy(&attr_local.value, &attr_src->value); wd_perf_entry_set_attribute(dst, &attr_local); } else { if (0 > zbx_variant_compare(&attr_src->value, &attr_dst->value)) { zbx_variant_clear(&attr_dst->value); zbx_variant_copy(&attr_dst->value, &attr_src->value); } } } /****************************************************************************** * * * Purpose: return new performance entry with aggregated data * * * * Parameters: in - [IN] performance entry with attributes to aggregate * * * * Return value: new performance entry with aggregated data * * * ******************************************************************************/ static zbx_wd_perf_entry_t *wd_perf_entry_aggregate_common_data(zbx_wd_perf_entry_t *in) { zbx_wd_perf_entry_t *out; zbx_wd_attr_t attr; out = wd_perf_entry_create(); wd_perf_init_attribute_from_dbl(&attr, WD_PERF_ATTR_COUNT, 1.0); wd_perf_entry_set_attribute(out, &attr); wd_perf_entry_copy_attr(out, WD_PERF_ATTR_REDIRECT_COUNT, in, "redirect_count"); wd_perf_entry_copy_attr(out, WD_PERF_ATTR_TRANSFERRED_SIZE, in, "transfer_size"); wd_perf_entry_copy_attr(out, WD_PERF_ATTR_ENCODED_SIZE, in, "encoded_body_size"); wd_perf_entry_copy_attr(out, WD_PERF_ATTR_TOTAL_SIZE, in, "decoded_body_size"); wd_perf_entry_copy_attr(out, WD_PERF_ATTR_LOAD_FINISHED, in, "load_event_end"); wd_perf_entry_diff_attrs(out, WD_PERF_ATTR_REDIRECT_TIME, in, "redirect_start", "redirect_end"); wd_perf_entry_diff_attrs(out, WD_PERF_ATTR_DNS_LOOKUP_TIME, in, "domain_lookup_start", "domain_lookup_end"); wd_perf_entry_diff_attrs(out, WD_PERF_ATTR_TCP_HANDSHAKE_TIME, in, "connect_start", "connect_end"); wd_perf_entry_diff_attrs(out, WD_PERF_ATTR_REQUEST_TIME, in, "request_start", "response_start"); wd_perf_entry_diff_attrs(out, WD_PERF_ATTR_RESPONSE_TIME, in, "response_start", "response_end"); wd_perf_entry_diff_attrs(out, WD_PERF_ATTR_RESOURCE_FETCH_TIME, in, "fetch_start", "response_end"); wd_perf_entry_diff_attrs(out, WD_PERF_ATTR_UNLOAD_EVENT_HANDLER_TIME, in, "unload_event_start", "unload_event_end"); wd_perf_entry_diff_attrs(out, WD_PERF_ATTR_LOAD_EVENT_HANDLER_TIME, in, "load_event_start", "load_event_end"); wd_perf_entry_diff_attrs(out, WD_PERF_ATTR_DOM_CONTENT_LOADING_TIME, in, "dom_content_loaded_event_start", "dom_content_loaded_event_end"); return out; } /****************************************************************************** * * * Purpose: return new performance entry with aggregated navigation data * * * * Parameters: in - [IN] performance entry with attributes to aggregate * * * * Return value: new performance entry with aggregated data * * * ******************************************************************************/ static zbx_wd_perf_entry_t *wd_perf_entry_aggregate_navigation_data(zbx_wd_perf_entry_t *in) { zbx_wd_perf_entry_t *out; out = wd_perf_entry_aggregate_common_data(in); wd_perf_entry_copy_attr(out, WD_PERF_ATTR_TLS_NEGOTIATION_TIME, in, WD_PERF_ATTR_TLS_NEGOTIATION_TIME); return out; } static int wd_attr_ptr_compare(const void *d1, const void *d2) { const zbx_wd_attr_t *a1 = *(const zbx_wd_attr_t * const *)d1; const zbx_wd_attr_t *a2 = *(const zbx_wd_attr_t * const *)d2; return strcmp(a1->name, a2->name); } /****************************************************************************** * * * Purpose: dump performance entry contents into log * * * ******************************************************************************/ static void wd_perf_dump_entry(const char *name, zbx_wd_perf_entry_t *entry) { zbx_hashset_iter_t iter; zbx_vector_wd_attr_ptr_t attrs; zbx_wd_attr_t *attr; if (NULL == entry) return; zbx_vector_wd_attr_ptr_create(&attrs); zbx_hashset_iter_reset(&entry->attrs, &iter); while (NULL != (attr = (zbx_wd_attr_t *)zbx_hashset_iter_next(&iter))) zbx_vector_wd_attr_ptr_append(&attrs, attr); zbx_vector_wd_attr_ptr_sort(&attrs, wd_attr_ptr_compare); zabbix_log(LOG_LEVEL_TRACE, " %s", name); for (int i = 0; i < attrs.values_num; i++) { attr = attrs.values[i]; zabbix_log(LOG_LEVEL_TRACE, " %s: %s (%s)", attr->name, zbx_variant_value_desc(&attr->value), zbx_variant_type_desc(&attr->value)); } zbx_vector_wd_attr_ptr_destroy(&attrs); } /****************************************************************************** * * * Purpose: dump collected performance data into log * * * ******************************************************************************/ static void wd_perf_dump(zbx_wd_perf_t *perf) { zabbix_log(LOG_LEVEL_TRACE, "browser performance data"); zabbix_log(LOG_LEVEL_TRACE, "details:"); for (int i = 0; i < perf->details.values_num; i++) { zabbix_log(LOG_LEVEL_TRACE, " %d.", i); wd_perf_dump_entry("navigation", perf->details.values[i].navigation); wd_perf_dump_entry("resource", perf->details.values[i].resource); zabbix_log(LOG_LEVEL_TRACE, " user:"); for (int j = 0; j < perf->details.values[i].user.values_num; j++) wd_perf_dump_entry(" ", perf->details.values[i].user.values[j]); } zabbix_log(LOG_LEVEL_TRACE, "bookmarks:"); for (int i = 0; i < perf->bookmarks.values_num; i++) { zabbix_log(LOG_LEVEL_TRACE, " %s", perf->bookmarks.values[i].name); wd_perf_dump_entry("navigation", perf->bookmarks.values[i].details->navigation); wd_perf_dump_entry("resource", perf->bookmarks.values[i].details->resource); } zabbix_log(LOG_LEVEL_TRACE, "summaries:"); wd_perf_dump_entry("navigation", perf->navigation_summary); wd_perf_dump_entry("resource", perf->resource_summary); } /****************************************************************************** * * * Purpose: collect performance entries from json data * * * ******************************************************************************/ int wd_perf_collect(zbx_wd_perf_t *perf, const char *bookmark_name, const struct zbx_json_parse *jp, char **error) { #define WD_PERF_MAX_ENTRY_COUNT 1000 #define WD_PERF_MAX_BOOKMARK_LENGTH 1000 const char *p = NULL; zbx_wd_perf_entry_t *entry, *resource; zbx_wd_perf_details_t details = {0}; zbx_wd_attr_t attr; if (WD_PERF_MAX_ENTRY_COUNT <= perf->details.values_num) { *error = zbx_dsprintf(*error, "maximum count of performance entries has been reached (%d)", WD_PERF_MAX_ENTRY_COUNT); return FAIL; } if (NULL != bookmark_name && WD_PERF_MAX_BOOKMARK_LENGTH < zbx_strlen_utf8(bookmark_name)) { *error = zbx_dsprintf(*error, "maximum allowed mark name length exceeded (%d)", WD_PERF_MAX_BOOKMARK_LENGTH); return FAIL; } zbx_vector_wd_perf_entry_ptr_create(&details.user); details.resource = wd_perf_entry_create(); wd_perf_init_attribute_from_dbl(&attr, WD_PERF_ATTR_COUNT, 0.0); wd_perf_entry_set_attribute(details.resource, &attr); while (NULL != (p = zbx_json_next(jp, p))) { struct zbx_json_parse jp_entry; char buf[MAX_STRING_LEN]; if (SUCCEED != zbx_json_brackets_open(p, &jp_entry)) { zabbix_log(LOG_LEVEL_DEBUG, "cannot open performance entry object"); continue; } if (SUCCEED != zbx_json_value_by_name(&jp_entry, WD_PERF_TAG_ENTRY_TYPE, buf, sizeof(buf), NULL)) { zabbix_log(LOG_LEVEL_DEBUG, "cannot find entryType tag in performance entry"); continue; } if (0 == strcmp(buf, WD_PERF_ENTRY_NAVIGATION)) { if (NULL != details.navigation) { zabbix_log(LOG_LEVEL_DEBUG, "duplicate navigation entry type found in performance" " data"); continue; } details.navigation = wd_perf_entry_create_from_json(&jp_entry); wd_perf_entry_diff_attrs(details.navigation, WD_PERF_ATTR_TLS_NEGOTIATION_TIME, details.navigation, "secure_connection_start", "request_start"); entry = wd_perf_entry_aggregate_navigation_data(details.navigation); wd_perf_entry_merge(perf->navigation_summary, entry); wd_perf_entry_free(entry); wd_perf_entry_set_min_protocol(perf->navigation_summary, details.navigation); } else if (0 == strcmp(buf, WD_PERF_ENTRY_RESOURCE)) { resource = wd_perf_entry_create_from_json(&jp_entry); entry = wd_perf_entry_aggregate_common_data(resource); wd_perf_entry_merge(perf->resource_summary, entry); wd_perf_entry_set_min_protocol(perf->resource_summary, resource); wd_perf_entry_merge(details.resource, entry); wd_perf_entry_free(entry); wd_perf_entry_set_min_protocol(details.resource, resource); wd_perf_entry_free(resource); } else if (0 == strcmp(buf, WD_PERF_ENTRY_MARK) || 0 == strcmp(buf, WD_PERF_ENTRY_MEASURE)) { entry = wd_perf_entry_create_from_json(&jp_entry); zbx_vector_wd_perf_entry_ptr_append(&details.user, entry); } } zbx_vector_wd_perf_details_append(&perf->details, details); if (NULL != bookmark_name) { zbx_wd_perf_bookmark_t bookmark; bookmark.name = zbx_strdup(NULL, bookmark_name); bookmark.details = &perf->details.values[perf->details.values_num - 1]; zbx_vector_wd_perf_bookmark_append(&perf->bookmarks, bookmark); } if (SUCCEED == ZBX_CHECK_LOG_LEVEL(LOG_LEVEL_TRACE)) wd_perf_dump(perf); return SUCCEED; #undef WD_PERF_MAX_ENTRY_COUNT #undef WD_PERF_MAX_BOOKMARK_LENGTH } /****************************************************************************** * * * Purpose: initialize performance collector * * * ******************************************************************************/ void wd_perf_init(zbx_wd_perf_t *perf) { zbx_vector_wd_perf_details_create(&perf->details); zbx_vector_wd_perf_bookmark_create(&perf->bookmarks); perf->navigation_summary = wd_perf_entry_create(); perf->resource_summary = wd_perf_entry_create(); zbx_wd_attr_t attr; wd_perf_init_attribute_from_dbl(&attr, WD_PERF_ATTR_COUNT, 0.0); wd_perf_entry_set_attribute(perf->navigation_summary, &attr); wd_perf_init_attribute_from_dbl(&attr, WD_PERF_ATTR_COUNT, 0.0); wd_perf_entry_set_attribute(perf->resource_summary, &attr); } /****************************************************************************** * * * Purpose: destroy performance collector * * * ******************************************************************************/ void wd_perf_destroy(zbx_wd_perf_t *perf) { for (int i = 0; i < perf->bookmarks.values_num; i++) zbx_free(perf->bookmarks.values[i].name); zbx_vector_wd_perf_bookmark_destroy(&perf->bookmarks); for (int i = 0; i < perf->details.values_num; i++) { wd_perf_entry_free(perf->details.values[i].navigation); wd_perf_entry_free(perf->details.values[i].resource); zbx_vector_wd_perf_entry_ptr_clear_ext(&perf->details.values[i].user, wd_perf_entry_free); zbx_vector_wd_perf_entry_ptr_destroy(&perf->details.values[i].user); } zbx_vector_wd_perf_details_destroy(&perf->details); wd_perf_entry_free(perf->navigation_summary); wd_perf_entry_free(perf->resource_summary); } #endif