/* ** Copyright (C) 2001-2025 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 "zbxdiag.h" #include "diag_server.h" #include "zbxcachevalue.h" #include "../lld/lld_protocol.h" #include "zbxalerter.h" #include "zbxtime.h" #include "zbxpreproc.h" #define ZBX_DIAG_LLD_RULES 0x00000001 #define ZBX_DIAG_LLD_VALUES 0x00000002 #define ZBX_DIAG_LLD_SIMPLE (ZBX_DIAG_LLD_RULES | \ ZBX_DIAG_LLD_VALUES) #define ZBX_DIAG_ALERTING_ALERTS 0x00000001 #define ZBX_DIAG_ALERTING_SIMPLE (ZBX_DIAG_ALERTING_ALERTS) /****************************************************************************** * * * Purpose: sort itemid,values_num pair by values_num in descending order * * * ******************************************************************************/ static int diag_valuecache_item_compare_values(const void *d1, const void *d2) { zbx_vc_item_stats_t *i1 = *(zbx_vc_item_stats_t **)d1; zbx_vc_item_stats_t *i2 = *(zbx_vc_item_stats_t **)d2; return i2->values_num - i1->values_num; } /****************************************************************************** * * * Purpose: sort itemid,values_num pair by hourly_num in descending order * * * ******************************************************************************/ static int diag_valuecache_item_compare_hourly(const void *d1, const void *d2) { zbx_vc_item_stats_t *i1 = *(zbx_vc_item_stats_t **)d1; zbx_vc_item_stats_t *i2 = *(zbx_vc_item_stats_t **)d2; return i2->hourly_num - i1->hourly_num; } /****************************************************************************** * * * Purpose: add valuecache items diagnostic statistics to json * * * ******************************************************************************/ static void diag_valuecache_add_items(struct zbx_json *json, const char *field, zbx_vc_item_stats_t **items, int items_num) { int i; zbx_json_addarray(json, field); for (i = 0; i < items_num; i++) { zbx_json_addobject(json, NULL); zbx_json_addint64(json, "itemid", items[i]->itemid); zbx_json_addint64(json, "values", items[i]->values_num); zbx_json_addint64(json, "request.values", items[i]->hourly_num); zbx_json_close(json); } zbx_json_close(json); } #define ZBX_DIAG_VALUECACHE_ITEMS 0x00000001 #define ZBX_DIAG_VALUECACHE_VALUES 0x00000002 #define ZBX_DIAG_VALUECACHE_MODE 0x00000004 #define ZBX_DIAG_VALUECACHE_MEMORY 0x00000008 #define ZBX_DIAG_VALUECACHE_SIMPLE (ZBX_DIAG_VALUECACHE_ITEMS | \ ZBX_DIAG_VALUECACHE_VALUES | \ ZBX_DIAG_VALUECACHE_MODE) /****************************************************************************** * * * Purpose: add requested value cache diagnostic information to json data * * * * Parameters: jp - [IN] the request * * json - [IN/OUT] the json to update * * error - [OUT] error message * * * * Return value: SUCCEED - the information was added successfully * * FAIL - otherwise * * * ******************************************************************************/ static int diag_add_valuecache_info(const struct zbx_json_parse *jp, struct zbx_json *json, char **error) { zbx_vector_diag_map_ptr_t tops; int ret; double time1, time2, time_total = 0; zbx_uint64_t fields; zbx_diag_map_t field_map[] = { {"", ZBX_DIAG_VALUECACHE_SIMPLE | ZBX_DIAG_VALUECACHE_MEMORY}, {"items", ZBX_DIAG_VALUECACHE_ITEMS}, {"values", ZBX_DIAG_VALUECACHE_VALUES}, {"mode", ZBX_DIAG_VALUECACHE_MODE}, {"memory", ZBX_DIAG_VALUECACHE_MEMORY}, {NULL, 0} }; zbx_vector_diag_map_ptr_create(&tops); if (SUCCEED == (ret = zbx_diag_parse_request(jp, field_map, &fields, &tops, error))) { zbx_json_addobject(json, ZBX_DIAG_VALUECACHE); if (0 != (fields & ZBX_DIAG_VALUECACHE_SIMPLE)) { zbx_uint64_t values_num, items_num; int mode; time1 = zbx_time(); zbx_vc_get_diag_stats(&items_num, &values_num, &mode); time2 = zbx_time(); time_total += time2 - time1; if (0 != (fields & ZBX_DIAG_VALUECACHE_ITEMS)) zbx_json_addint64(json, "items", items_num); if (0 != (fields & ZBX_DIAG_VALUECACHE_VALUES)) zbx_json_addint64(json, "values", values_num); if (0 != (fields & ZBX_DIAG_VALUECACHE_MODE)) zbx_json_addint64(json, "mode", mode); } if (0 != (fields & ZBX_DIAG_VALUECACHE_MEMORY)) { zbx_shmem_stats_t mem; time1 = zbx_time(); zbx_vc_get_mem_stats(&mem); time2 = zbx_time(); time_total += time2 - time1; zbx_diag_add_mem_stats(json, "memory", &mem); } if (0 != tops.values_num) { zbx_vector_vc_item_stats_ptr_t items; zbx_vector_vc_item_stats_ptr_create(&items); time1 = zbx_time(); zbx_vc_get_item_stats(&items); time2 = zbx_time(); time_total += time2 - time1; zbx_json_addobject(json, "top"); for (int i = 0; i < tops.values_num; i++) { zbx_diag_map_t *map = tops.values[i]; int limit; if (0 == strcmp(map->name, "values")) { zbx_vector_vc_item_stats_ptr_sort(&items, diag_valuecache_item_compare_values); } else if (0 == strcmp(map->name, "request.values")) { zbx_vector_vc_item_stats_ptr_sort(&items, diag_valuecache_item_compare_hourly); } else { *error = zbx_dsprintf(*error, "Unsupported top field: %s", map->name); ret = FAIL; goto out; } limit = MIN((int)map->value, items.values_num); diag_valuecache_add_items(json, map->name, items.values, limit); } zbx_json_close(json); zbx_vector_vc_item_stats_ptr_clear_ext(&items, zbx_vc_item_stats_free); zbx_vector_vc_item_stats_ptr_destroy(&items); } zbx_json_addfloat(json, "time", time_total); zbx_json_close(json); } out: zbx_vector_diag_map_ptr_clear_ext(&tops, zbx_diag_map_free); zbx_vector_diag_map_ptr_destroy(&tops); return ret; } #undef ZBX_DIAG_VALUECACHE_SIMPLE #undef ZBX_DIAG_VALUECACHE_ITEMS #undef ZBX_DIAG_VALUECACHE_VALUES #undef ZBX_DIAG_VALUECACHE_MODE #undef ZBX_DIAG_VALUECACHE_MEMORY /****************************************************************************** * * * Purpose: add lld item top list to output json * * * ******************************************************************************/ static void diag_add_lld_items(struct zbx_json *json, const char *field, const zbx_vector_uint64_pair_t *items) { int i; zbx_json_addarray(json, field); for (i = 0; i < items->values_num; i++) { zbx_json_addobject(json, NULL); zbx_json_adduint64(json, "itemid", items->values[i].first); zbx_json_adduint64(json, "values", items->values[i].second); zbx_json_close(json); } zbx_json_close(json); } /****************************************************************************** * * * Purpose: add requested lld manager diagnostic information to json data * * * * Parameters: jp - [IN] the request * * json - [IN/OUT] the json to update * * error - [OUT] error message * * * * Return value: SUCCEED - the information was added successfully * * FAIL - otherwise * * * ******************************************************************************/ static int diag_add_lld_info(const struct zbx_json_parse *jp, struct zbx_json *json, char **error) { zbx_vector_diag_map_ptr_t tops; int ret; double time1, time2, time_total = 0; zbx_uint64_t fields; zbx_diag_map_t field_map[] = { {"", ZBX_DIAG_LLD_SIMPLE}, {"rules", ZBX_DIAG_LLD_RULES}, {"values", ZBX_DIAG_LLD_VALUES}, {NULL, 0} }; zbx_vector_diag_map_ptr_create(&tops); if (SUCCEED == (ret = zbx_diag_parse_request(jp, field_map, &fields, &tops, error))) { zbx_json_addobject(json, ZBX_DIAG_LLD); if (0 != (fields & ZBX_DIAG_LLD_SIMPLE)) { zbx_uint64_t values_num, items_num; time1 = zbx_time(); if (FAIL == (ret = zbx_lld_get_diag_stats(&items_num, &values_num, error))) goto out; time2 = zbx_time(); time_total += time2 - time1; if (0 != (fields & ZBX_DIAG_LLD_RULES)) zbx_json_addint64(json, "rules", items_num); if (0 != (fields & ZBX_DIAG_LLD_VALUES)) zbx_json_addint64(json, "values", values_num); } if (0 != tops.values_num) { zbx_json_addobject(json, "top"); for (int i = 0; i < tops.values_num; i++) { zbx_diag_map_t *map = tops.values[i]; if (0 == strcmp(map->name, "values")) { zbx_vector_uint64_pair_t items; zbx_vector_uint64_pair_create(&items); time1 = zbx_time(); if (FAIL == (ret = zbx_lld_get_top_items(map->value, &items, error))) { zbx_vector_uint64_pair_destroy(&items); goto out; } time2 = zbx_time(); time_total += time2 - time1; diag_add_lld_items(json, map->name, &items); zbx_vector_uint64_pair_destroy(&items); } else { *error = zbx_dsprintf(*error, "Unsupported top field: %s", map->name); ret = FAIL; goto out; } } zbx_json_close(json); } zbx_json_addfloat(json, "time", time_total); zbx_json_close(json); } out: zbx_vector_diag_map_ptr_clear_ext(&tops, zbx_diag_map_free); zbx_vector_diag_map_ptr_destroy(&tops); return ret; } /****************************************************************************** * * * Purpose: add mediatype top list to output json * * * * Parameters: json - [OUT] the output json * * field - [IN] the field name * * items - [IN] a top mediatype list consisting of * * mediatype, alerts_num pairs * * * ******************************************************************************/ static void diag_add_alerting_mediatypes(struct zbx_json *json, const char *field, const zbx_vector_uint64_pair_t *mediatypes) { int i; zbx_json_addarray(json, field); for (i = 0; i < mediatypes->values_num; i++) { zbx_json_addobject(json, NULL); zbx_json_adduint64(json, "mediatypeid", mediatypes->values[i].first); zbx_json_adduint64(json, "alerts", mediatypes->values[i].second); zbx_json_close(json); } zbx_json_close(json); } /****************************************************************************** * * * Purpose: add alert source top list to output json * * * * Parameters: json - [OUT] output json * * field - [IN] field name * * sources - [IN] top alert source list consisting of * * zbx_am_source_stats_t structures * * * ******************************************************************************/ static void diag_add_alerting_sources(struct zbx_json *json, const char *field, const zbx_vector_am_source_stats_ptr_t *sources) { int i; zbx_json_addarray(json, field); for (i = 0; i < sources->values_num; i++) { const zbx_am_source_stats_t *source = (const zbx_am_source_stats_t *)sources->values[i]; zbx_json_addobject(json, NULL); zbx_json_adduint64(json, "source", source->source); zbx_json_adduint64(json, "object", source->object); zbx_json_adduint64(json, "objectid", source->objectid); zbx_json_adduint64(json, "alerts", source->alerts_num); zbx_json_close(json); } zbx_json_close(json); } /****************************************************************************** * * * Purpose: add requested alert manager diagnostic information to json data * * * * Parameters: jp - [IN] the request * * json - [IN/OUT] the json to update * * error - [OUT] error message * * * * Return value: SUCCEED - the information was added successfully * * FAIL - otherwise * * * ******************************************************************************/ static int diag_add_alerting_info(const struct zbx_json_parse *jp, struct zbx_json *json, char **error) { zbx_vector_diag_map_ptr_t tops; int ret; double time1, time2, time_total = 0; zbx_uint64_t fields; zbx_diag_map_t field_map[] = { {"", ZBX_DIAG_ALERTING_SIMPLE}, {"alerts", ZBX_DIAG_ALERTING_ALERTS}, {NULL, 0} }; zbx_vector_diag_map_ptr_create(&tops); if (SUCCEED == (ret = zbx_diag_parse_request(jp, field_map, &fields, &tops, error))) { zbx_json_addobject(json, ZBX_DIAG_ALERTING); if (0 != (fields & ZBX_DIAG_ALERTING_SIMPLE)) { zbx_uint64_t alerts_num; time1 = zbx_time(); if (FAIL == (ret = zbx_alerter_get_diag_stats(&alerts_num, error))) goto out; time2 = zbx_time(); time_total += time2 - time1; if (0 != (fields & ZBX_DIAG_ALERTING_ALERTS)) zbx_json_addint64(json, "alerts", alerts_num); } if (0 != tops.values_num) { int i; zbx_json_addobject(json, "top"); for (i = 0; i < tops.values_num; i++) { zbx_diag_map_t *map = (zbx_diag_map_t *)tops.values[i]; if (0 == strcmp(map->name, "media.alerts")) { zbx_vector_uint64_pair_t mediatypes; zbx_vector_uint64_pair_create(&mediatypes); time1 = zbx_time(); if (FAIL == (ret = zbx_alerter_get_top_mediatypes(map->value, &mediatypes, error))) { zbx_vector_uint64_pair_destroy(&mediatypes); goto out; } time2 = zbx_time(); time_total += time2 - time1; diag_add_alerting_mediatypes(json, map->name, &mediatypes); zbx_vector_uint64_pair_destroy(&mediatypes); } else if (0 == strcmp(map->name, "source.alerts")) { zbx_vector_am_source_stats_ptr_t sources; zbx_vector_am_source_stats_ptr_create(&sources); time1 = zbx_time(); if (FAIL == (ret = zbx_alerter_get_top_sources(map->value, &sources, error))) { zbx_vector_am_source_stats_ptr_clear_ext(&sources, (zbx_am_source_stats_ptr_free_func_t)zbx_ptr_free); zbx_vector_am_source_stats_ptr_destroy(&sources); goto out; } time2 = zbx_time(); time_total += time2 - time1; diag_add_alerting_sources(json, map->name, &sources); zbx_vector_am_source_stats_ptr_clear_ext(&sources, (zbx_am_source_stats_ptr_free_func_t)zbx_ptr_free); zbx_vector_am_source_stats_ptr_destroy(&sources); } else { *error = zbx_dsprintf(*error, "Unsupported top field: %s", map->name); ret = FAIL; goto out; } } zbx_json_close(json); } zbx_json_addfloat(json, "time", time_total); zbx_json_close(json); } out: zbx_vector_diag_map_ptr_clear_ext(&tops, zbx_diag_map_free); zbx_vector_diag_map_ptr_destroy(&tops); return ret; } /****************************************************************************** * * * Purpose: add requested section diagnostic information * * * * Parameters: section - [IN] the section name * * jp - [IN] the request * * json - [IN/OUT] the json to update * * error - [OUT] the error message * * * * Return value: SUCCEED - the information was retrieved successfully * * FAIL - otherwise * * * ******************************************************************************/ int diag_add_section_info(const char *section, const struct zbx_json_parse *jp, struct zbx_json *json, char **error) { int ret = FAIL; if (0 == strcmp(section, ZBX_DIAG_HISTORYCACHE)) ret = zbx_diag_add_historycache_info(jp, json, error); else if (0 == strcmp(section, ZBX_DIAG_VALUECACHE)) ret = diag_add_valuecache_info(jp, json, error); else if (0 == strcmp(section, ZBX_DIAG_PREPROCESSING)) ret = zbx_diag_add_preproc_info(jp, json, error); else if (0 == strcmp(section, ZBX_DIAG_LLD)) ret = diag_add_lld_info(jp, json, error); else if (0 == strcmp(section, ZBX_DIAG_ALERTING)) ret = diag_add_alerting_info(jp, json, error); else if (0 == strcmp(section, ZBX_DIAG_LOCKS)) { zbx_diag_add_locks_info(json); ret = SUCCEED; } else if (0 == strcmp(section, ZBX_DIAG_CONNECTOR)) ret = zbx_diag_add_connector_info(jp, json, error); else *error = zbx_dsprintf(*error, "Unsupported diagnostics section: %s", section); return ret; }