@cryptotaxi247 / netdata-1 / commits / 758d9c405

full memory tracking and profiling of Netdata Agent (#13789)

* full memory tracking and profiling of Netdata Agent * initialize dbengine only when it is needed * handling of dbengine compiled but not available * restore unittest * restore unittest again * more improvements about ifdef dbengine * fix compilation when dbengine is not enabled * check if dbengine is enabled on exit * call freez() not free() * aral unittest * internal checks activate trace allocations; dev mode activates internal checks

Costa Tsaousis committed Oct 9, 2022 at 21:58 UTC 758d9c405d2d768a3c125052a02c7a1503b01bd8
26 files changed +778 -259
aclk/aclk_tx_msgs.c
+9 -2
@@ -15,6 +15,13 @@
15 // version for aclk legacy (old cloud arch)
16 #define ACLK_VERSION 2
17
18 +static void freez_aclk_publish5a(void *ptr) {
19 + freez(ptr);
20 +}
21 +static void freez_aclk_publish5b(void *ptr) {
22 + freez(ptr);
23 +}
24 +
25 uint16_t aclk_send_bin_message_subtopic_pid(mqtt_wss_client client, char *msg, size_t msg_len, enum aclk_topics subtopic, const char *msgname)
26 {
27 #ifndef ACLK_LOG_CONVERSATION_DIR
@@ -29,7 +36,7 @@ uint16_t aclk_send_bin_message_subtopic_pid(mqtt_wss_client client, char *msg, s
36 }
37
38 if (use_mqtt_5)
32 - mqtt_wss_publish5(client, (char*)topic, NULL, msg, &freez, msg_len, MQTT_WSS_PUB_QOS1, &packet_id);
39 + mqtt_wss_publish5(client, (char*)topic, NULL, msg, &freez_aclk_publish5a, msg_len, MQTT_WSS_PUB_QOS1, &packet_id);
40 else
41 mqtt_wss_publish_pid(client, topic, msg, msg_len, MQTT_WSS_PUB_QOS1, &packet_id);
42
@@ -81,7 +88,7 @@ static int aclk_send_message_with_bin_payload(mqtt_wss_client client, json_objec
88 }
89
90 if (use_mqtt_5)
84 - mqtt_wss_publish5(client, (char*)topic, NULL, (char*)(payload_len ? full_msg : str), (payload_len ? &freez : &json_object_put_wrapper), len, MQTT_WSS_PUB_QOS1, &packet_id);
91 + mqtt_wss_publish5(client, (char*)topic, NULL, (char*)(payload_len ? full_msg : str), (payload_len ? &freez_aclk_publish5b : &json_object_put_wrapper), len, MQTT_WSS_PUB_QOS1, &packet_id);
92 else {
93 rc = mqtt_wss_publish_pid_block(client, topic, payload_len ? full_msg : str, len, MQTT_WSS_PUB_QOS1, &packet_id, 5000);
94 freez(full_msg);
daemon/global_statistics.c
+159 -3
@@ -11,8 +11,9 @@
11 #define WORKER_JOB_HEARTBEAT 4
12 #define WORKER_JOB_STRINGS 5
13 #define WORKER_JOB_DICTIONARIES 6
14 +#define WORKER_JOB_MALLOC_TRACE 7
15
15 -#if WORKER_UTILIZATION_MAX_JOB_TYPES < 7
16 +#if WORKER_UTILIZATION_MAX_JOB_TYPES < 8
17 #error WORKER_UTILIZATION_MAX_JOB_TYPES has to be at least 5
18 #endif
19
@@ -1571,6 +1572,153 @@ static void update_dictionary_category_charts(struct dictionary_categories *c) {
1572 }
1573 }
1574
1575 +#ifdef NETDATA_TRACE_ALLOCATIONS
1576 +
1577 +struct memory_trace_data {
1578 + RRDSET *st_memory;
1579 + RRDSET *st_allocations;
1580 + RRDSET *st_avg_alloc;
1581 + RRDSET *st_ops;
1582 +};
1583 +
1584 +static int do_memory_trace_item(void *item, void *data) {
1585 + struct memory_trace_data *tmp = data;
1586 + struct malloc_trace *p = item;
1587 +
1588 + // ------------------------------------------------------------------------
1589 +
1590 + if(!p->rd_bytes)
1591 + p->rd_bytes = rrddim_add(tmp->st_memory, p->function, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
1592 +
1593 + collected_number bytes = (collected_number)__atomic_load_n(&p->bytes, __ATOMIC_RELAXED);
1594 + rrddim_set_by_pointer(tmp->st_memory, p->rd_bytes, bytes);
1595 +
1596 + // ------------------------------------------------------------------------
1597 +
1598 + if(!p->rd_allocations)
1599 + p->rd_allocations = rrddim_add(tmp->st_allocations, p->function, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
1600 +
1601 + collected_number allocs = (collected_number)__atomic_load_n(&p->allocations, __ATOMIC_RELAXED);
1602 + rrddim_set_by_pointer(tmp->st_allocations, p->rd_allocations, allocs);
1603 +
1604 + // ------------------------------------------------------------------------
1605 +
1606 + if(!p->rd_avg_alloc)
1607 + p->rd_avg_alloc = rrddim_add(tmp->st_avg_alloc, p->function, NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE);
1608 +
1609 + collected_number avg_alloc = (allocs)?(bytes * 100 / allocs):0;
1610 + rrddim_set_by_pointer(tmp->st_avg_alloc, p->rd_avg_alloc, avg_alloc);
1611 +
1612 + // ------------------------------------------------------------------------
1613 +
1614 + if(!p->rd_ops)
1615 + p->rd_ops = rrddim_add(tmp->st_ops, p->function, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1616 +
1617 + collected_number ops = 0;
1618 + ops += (collected_number)__atomic_load_n(&p->malloc_calls, __ATOMIC_RELAXED);
1619 + ops += (collected_number)__atomic_load_n(&p->calloc_calls, __ATOMIC_RELAXED);
1620 + ops += (collected_number)__atomic_load_n(&p->realloc_calls, __ATOMIC_RELAXED);
1621 + ops += (collected_number)__atomic_load_n(&p->strdup_calls, __ATOMIC_RELAXED);
1622 + ops += (collected_number)__atomic_load_n(&p->free_calls, __ATOMIC_RELAXED);
1623 + rrddim_set_by_pointer(tmp->st_ops, p->rd_ops, ops);
1624 +
1625 + // ------------------------------------------------------------------------
1626 +
1627 + return 1;
1628 +}
1629 +static void malloc_trace_statistics(void) {
1630 + static struct memory_trace_data tmp = {
1631 + .st_memory = NULL,
1632 + .st_allocations = NULL,
1633 + .st_avg_alloc = NULL,
1634 + .st_ops = NULL,
1635 + };
1636 +
1637 + if(!tmp.st_memory) {
1638 + tmp.st_memory = rrdset_create_localhost(
1639 + "netdata"
1640 + , "memory_size"
1641 + , NULL
1642 + , "memory"
1643 + , "netdata.memory.size"
1644 + , "Netdata Memory Used by Function"
1645 + , "bytes"
1646 + , "netdata"
1647 + , "stats"
1648 + , 900000
1649 + , localhost->rrd_update_every
1650 + , RRDSET_TYPE_STACKED
1651 + );
1652 + }
1653 + else
1654 + rrdset_next(tmp.st_memory);
1655 +
1656 + if(!tmp.st_ops) {
1657 + tmp.st_ops = rrdset_create_localhost(
1658 + "netdata"
1659 + , "memory_operations"
1660 + , NULL
1661 + , "memory"
1662 + , "netdata.memory.operations"
1663 + , "Netdata Memory Operations by Function"
1664 + , "ops/s"
1665 + , "netdata"
1666 + , "stats"
1667 + , 900001
1668 + , localhost->rrd_update_every
1669 + , RRDSET_TYPE_LINE
1670 + );
1671 + }
1672 + else
1673 + rrdset_next(tmp.st_ops);
1674 +
1675 + if(!tmp.st_allocations) {
1676 + tmp.st_allocations = rrdset_create_localhost(
1677 + "netdata"
1678 + , "memory_allocations"
1679 + , NULL
1680 + , "memory"
1681 + , "netdata.memory.allocations"
1682 + , "Netdata Memory Allocations by Function"
1683 + , "allocations"
1684 + , "netdata"
1685 + , "stats"
1686 + , 900002
1687 + , localhost->rrd_update_every
1688 + , RRDSET_TYPE_STACKED
1689 + );
1690 + }
1691 + else
1692 + rrdset_next(tmp.st_allocations);
1693 +
1694 + if(!tmp.st_avg_alloc) {
1695 + tmp.st_avg_alloc = rrdset_create_localhost(
1696 + "netdata"
1697 + , "memory_avg_alloc"
1698 + , NULL
1699 + , "memory"
1700 + , "netdata.memory.avg_alloc"
1701 + , "Netdata Average Allocation Size by Function"
1702 + , "bytes"
1703 + , "netdata"
1704 + , "stats"
1705 + , 900003
1706 + , localhost->rrd_update_every
1707 + , RRDSET_TYPE_LINE
1708 + );
1709 + }
1710 + else
1711 + rrdset_next(tmp.st_avg_alloc);
1712 +
1713 + malloc_trace_walkthrough(do_memory_trace_item, &tmp);
1714 +
1715 + rrdset_done(tmp.st_memory);
1716 + rrdset_done(tmp.st_ops);
1717 + rrdset_done(tmp.st_allocations);
1718 + rrdset_done(tmp.st_avg_alloc);
1719 +}
1720 +#endif
1721 +
1722 static void dictionary_statistics(void) {
1723 for(int i = 0; dictionary_categories[i].stats ;i++) {
1724 update_dictionary_category_charts(&dictionary_categories[i]);
@@ -2375,6 +2523,7 @@ void *global_statistics_main(void *ptr)
2523 worker_register_job_name(WORKER_JOB_DBENGINE, "dbengine");
2524 worker_register_job_name(WORKER_JOB_STRINGS, "strings");
2525 worker_register_job_name(WORKER_JOB_DICTIONARIES, "dictionaries");
2526 + worker_register_job_name(WORKER_JOB_MALLOC_TRACE, "malloc_trace");
2527
2528 netdata_thread_cleanup_push(global_statistics_cleanup, ptr);
2529
@@ -2404,8 +2553,10 @@ void *global_statistics_main(void *ptr)
2553 worker_is_busy(WORKER_JOB_REGISTRY);
2554 registry_statistics();
2555
2407 - worker_is_busy(WORKER_JOB_DBENGINE);
2408 - dbengine_statistics_charts();
2556 + if(dbengine_enabled) {
2557 + worker_is_busy(WORKER_JOB_DBENGINE);
2558 + dbengine_statistics_charts();
2559 + }
2560
2561 worker_is_busy(WORKER_JOB_HEARTBEAT);
2562 update_heartbeat_charts();
@@ -2415,6 +2566,11 @@ void *global_statistics_main(void *ptr)
2566
2567 worker_is_busy(WORKER_JOB_DICTIONARIES);
2568 dictionary_statistics();
2569 +
2570 +#ifdef NETDATA_TRACE_ALLOCATIONS
2571 + worker_is_busy(WORKER_JOB_MALLOC_TRACE);
2572 + malloc_trace_statistics();
2573 +#endif
2574 }
2575
2576 netdata_thread_cleanup_pop(1);
daemon/main.c
+15 -5
@@ -55,13 +55,17 @@ void netdata_cleanup_and_exit(int ret) {
55 // free the database
56 info("EXIT: freeing database memory...");
57 #ifdef ENABLE_DBENGINE
58 - for(int tier = 0; tier < storage_tiers ; tier++)
59 - rrdeng_prepare_exit(multidb_ctx[tier]);
58 + if(dbengine_enabled) {
59 + for (int tier = 0; tier < storage_tiers; tier++)
60 + rrdeng_prepare_exit(multidb_ctx[tier]);
61 + }
62 #endif
63 rrdhost_free_all();
64 #ifdef ENABLE_DBENGINE
63 - for(int tier = 0; tier < storage_tiers ; tier++)
64 - rrdeng_exit(multidb_ctx[tier]);
65 + if(dbengine_enabled) {
66 + for (int tier = 0; tier < storage_tiers; tier++)
67 + rrdeng_exit(multidb_ctx[tier]);
68 + }
69 #endif
70 }
71 sql_close_context_database();
@@ -255,7 +259,8 @@ void cancel_main_threads() {
259
260 for (i = 0; static_threads[i].name != NULL ; i++)
261 freez(static_threads[i].thread);
258 - free(static_threads);
262 +
263 + freez(static_threads);
264 }
265
266 struct option_def option_definitions[] = {
@@ -1001,6 +1006,8 @@ int main(int argc, char **argv) {
1006 if(string_unittest(10000)) return 1;
1007 if (dictionary_unittest(10000))
1008 return 1;
1009 + if(aral_unittest(10000))
1010 + return 1;
1011 if (rrdlabels_unittest())
1012 return 1;
1013 if (ctx_unittest())
@@ -1023,6 +1030,9 @@ int main(int argc, char **argv) {
1030 else if(strcmp(optarg, "dicttest") == 0) {
1031 return dictionary_unittest(10000);
1032 }
1033 + else if(strcmp(optarg, "araltest") == 0) {
1034 + return aral_unittest(10000);
1035 + }
1036 else if(strcmp(optarg, "stringtest") == 0) {
1037 return string_unittest(10000);
1038 }
daemon/service.c
-2
@@ -216,10 +216,8 @@ restart_after_removal:
216 info("Host '%s' with machine guid '%s' is obsolete - cleaning up.", rrdhost_hostname(host), host->machine_guid);
217
218 if (rrdhost_option_check(host, RRDHOST_OPTION_DELETE_ORPHAN_HOST)
219 -#ifdef ENABLE_DBENGINE
219 /* don't delete multi-host DB host files */
220 && !(host->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE && is_storage_engine_shared(host->storage_instance[0]))
222 -#endif
221 ) {
222 worker_is_busy(WORKER_JOB_DELETE_HOST_CHARTS);
223 rrdhost_delete_charts(host);
database/engine/journalfile.c
+1 -1
@@ -527,7 +527,7 @@ int load_journal_file(struct rrdengine_instance *ctx, struct rrdengine_journalfi
527
528 info("Journal file \"%s\" loaded (size:%"PRIu64").", path, file_size);
529 if (likely(journalfile->data))
530 - munmap(journalfile->data, file_size);
530 + netdata_munmap(journalfile->data, file_size);
531 return 0;
532
533 error:
database/engine/rrdengine.c
+1 -1
@@ -30,7 +30,7 @@ void dbengine_page_free(void *page) {
30 if (unlikely(db_engine_use_malloc))
31 freez(page);
32 else
33 - munmap(page, RRDENG_BLOCK_SIZE);
33 + netdata_munmap(page, RRDENG_BLOCK_SIZE);
34 }
35
36 static void sanity_check(void)
database/ram/rrddim_mem.c
+3 -3
@@ -16,7 +16,7 @@ void rrddim_metric_free(STORAGE_METRIC_HANDLE *db_metric_handle __maybe_unused)
16 STORAGE_COLLECT_HANDLE *rrddim_collect_init(STORAGE_METRIC_HANDLE *db_metric_handle) {
17 RRDDIM *rd = (RRDDIM *)db_metric_handle;
18 rd->db[rd->rrdset->current_entry] = pack_storage_number(NAN, SN_FLAG_NONE);
19 - struct mem_collect_handle *ch = calloc(1, sizeof(struct mem_collect_handle));
19 + struct mem_collect_handle *ch = callocz(1, sizeof(struct mem_collect_handle));
20 ch->rd = rd;
21 return (STORAGE_COLLECT_HANDLE *)ch;
22 }
@@ -46,7 +46,7 @@ void rrddim_store_metric_flush(STORAGE_COLLECT_HANDLE *collection_handle) {
46 }
47
48 int rrddim_collect_finalize(STORAGE_COLLECT_HANDLE *collection_handle) {
49 - free(collection_handle);
49 + freez(collection_handle);
50 return 0;
51 }
52
@@ -142,7 +142,7 @@ void rrddim_query_init(STORAGE_METRIC_HANDLE *db_metric_handle, struct rrddim_qu
142 handle->rd = rd;
143 handle->start_time = start_time;
144 handle->end_time = end_time;
145 - struct mem_query_handle* h = calloc(1, sizeof(struct mem_query_handle));
145 + struct mem_query_handle* h = mallocz(sizeof(struct mem_query_handle));
146 h->slot = rrddim_time2slot(rd, start_time);
147 h->last_slot = rrddim_time2slot(rd, end_time);
148 h->dt = rd->rrdset->update_every;
database/rrd.h
+1
@@ -52,6 +52,7 @@ struct pg_cache_page_index;
52 #include "sqlite/sqlite_health.h"
53 #include "rrdcontext.h"
54
55 +extern bool dbengine_enabled;
56 extern int storage_tiers;
57 extern int storage_tiers_grouping_iterations[RRD_STORAGE_TIERS];
58
database/rrdcontext.c
+5 -1
@@ -2200,7 +2200,7 @@ static void rrdmetric_update_retention(RRDMETRIC *rm) {
2200 max_last_time_t = rrddim_last_entry_t(rm->rrddim);
2201 }
2202 #ifdef ENABLE_DBENGINE
2203 - else {
2203 + else if (dbengine_enabled) {
2204 RRDHOST *rrdhost = rm->ri->rc->rrdhost;
2205 for (int tier = 0; tier < storage_tiers; tier++) {
2206 if(!rrdhost->storage_instance[tier]) continue;
@@ -2215,6 +2215,10 @@ static void rrdmetric_update_retention(RRDMETRIC *rm) {
2215 }
2216 }
2217 }
2218 + else {
2219 + // cannot get retention
2220 + return;
2221 + }
2222 #endif
2223
2224 if(min_first_time_t == LONG_MAX)
database/rrddim.c
+9 -6
@@ -2,9 +2,6 @@
2
3 #define NETDATA_RRD_INTERNALS
4 #include "rrd.h"
5 -#ifdef ENABLE_DBENGINE
6 -#include "database/engine/rrdengineapi.h"
7 -#endif
5 #include "storage_engine.h"
6
7 // ----------------------------------------------------------------------------
@@ -27,6 +24,12 @@ struct rrddim_constructor {
24
25 };
26
27 +// isolated call to appear
28 +// separate in statistics
29 +static void *rrddim_alloc_db(size_t entries) {
30 + return callocz(entries, sizeof(storage_number));
31 +}
32 +
33 static void rrddim_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrddim, void *constructor_data) {
34 struct rrddim_constructor *ctr = constructor_data;
35 RRDDIM *rd = rrddim;
@@ -73,7 +76,7 @@ static void rrddim_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, v
76 size_t entries = st->entries;
77 if(entries < 5) entries = 5;
78
76 - rd->db = callocz(entries, sizeof(storage_number));
79 + rd->db = rrddim_alloc_db(entries);
80 rd->memsize = entries * sizeof(storage_number);
81 }
82
@@ -222,7 +225,7 @@ static void rrddim_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, v
225
226 if(rd->db) {
227 if(rd->rrd_memory_mode == RRD_MEMORY_MODE_RAM)
225 - munmap(rd->db, rd->memsize);
228 + netdata_munmap(rd->db, rd->memsize);
229 else
230 freez(rd->db);
231 }
@@ -641,7 +644,7 @@ void rrddim_memory_file_free(RRDDIM *rd) {
644
645 struct rrddim_map_save_v019 *rd_on_file = rd->rd_on_file;
646 freez(rd_on_file->cache_filename);
644 - munmap(rd_on_file, rd_on_file->memsize);
647 + netdata_munmap(rd_on_file, rd_on_file->memsize);
648
649 // remove the pointers from the RRDDIM
650 rd->rd_on_file = NULL;
database/rrdhost.c
+54 -24
@@ -3,6 +3,7 @@
3 #define NETDATA_RRD_INTERNALS
4 #include "rrd.h"
5
6 +bool dbengine_enabled = false; // will become true if and when dbengine is initialized
7 int storage_tiers = 1;
8 int storage_tiers_grouping_iterations[RRD_STORAGE_TIERS] = { 1, 60, 60, 60, 60 };
9 RRD_BACKFILL storage_tiers_backfill[RRD_STORAGE_TIERS] = { RRD_BACKFILL_NEW, RRD_BACKFILL_NEW, RRD_BACKFILL_NEW, RRD_BACKFILL_NEW, RRD_BACKFILL_NEW };
@@ -328,12 +329,18 @@ RRDHOST *rrdhost_create(const char *hostname,
329 ) {
330 debug(D_RRDHOST, "Host '%s': adding with guid '%s'", hostname, guid);
331
332 + rrd_check_wrlock();
333 +
334 + if(memory_mode == RRD_MEMORY_MODE_DBENGINE && !dbengine_enabled) {
335 + error("memory mode 'dbengine' is not enabled, but host '%s' is configured for it. Falling back to 'alloc'", hostname);
336 + memory_mode = RRD_MEMORY_MODE_ALLOC;
337 + }
338 +
339 #ifdef ENABLE_DBENGINE
340 int is_legacy = (memory_mode == RRD_MEMORY_MODE_DBENGINE) && is_legacy_child(guid);
341 #else
334 - int is_legacy = 1;
342 +int is_legacy = 1;
343 #endif
336 - rrd_check_wrlock();
344
345 int is_in_multihost = (memory_mode == RRD_MEMORY_MODE_DBENGINE && !is_legacy);
346 RRDHOST *host = callocz(1, sizeof(RRDHOST));
@@ -384,8 +391,8 @@ RRDHOST *rrdhost_create(const char *hostname,
391 host->cache_dir = strdupz(filename);
392 }
393
387 - if((host->rrd_memory_mode == RRD_MEMORY_MODE_MAP || host->rrd_memory_mode == RRD_MEMORY_MODE_SAVE || (
388 - host->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE && is_legacy))) {
394 + if((host->rrd_memory_mode == RRD_MEMORY_MODE_MAP || host->rrd_memory_mode == RRD_MEMORY_MODE_SAVE ||
395 + (host->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE && is_legacy))) {
396 int r = mkdir(host->cache_dir, 0775);
397 if(r != 0 && errno != EEXIST)
398 error("Host '%s': cannot create directory '%s'", rrdhost_hostname(host), host->cache_dir);
@@ -754,22 +761,7 @@ inline int rrdhost_should_be_removed(RRDHOST *host, RRDHOST *protected_host, tim
761 // ----------------------------------------------------------------------------
762 // RRDHOST global / startup initialization
763
757 -int rrd_init(char *hostname, struct rrdhost_system_info *system_info) {
758 - rrdhost_init();
759 -
760 - if (unlikely(sql_init_database(DB_CHECK_NONE, system_info ? 0 : 1))) {
761 - if (default_rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE)
762 - fatal("Failed to initialize SQLite");
763 - info("Skipping SQLITE metadata initialization since memory mode is not dbengine");
764 - }
765 -
766 - if (unlikely(sql_init_context_database(system_info ? 0 : 1))) {
767 - error_report("Failed to initialize context metadata database");
768 - }
769 -
770 - if (unlikely(!system_info))
771 - goto unittest;
772 -
764 +void dbengine_init(char *hostname) {
765 #ifdef ENABLE_DBENGINE
766 storage_tiers = config_get_number(CONFIG_SECTION_DB, "storage tiers", storage_tiers);
767 if(storage_tiers < 1) {
@@ -857,7 +849,7 @@ int rrd_init(char *hostname, struct rrdhost_system_info *system_info) {
849 error("DBENGINE on '%s': dbengine tier %d gives aggregation of more than 65535 points of tier 0. Disabling tiers above %d", hostname, tier, tier);
850 break;
851 }
860 -
852 +
853 internal_error(true, "DBENGINE tier %d grouping iterations is set to %d", tier, storage_tiers_grouping_iterations[tier]);
854 ret = rrdeng_init(NULL, NULL, dbenginepath, page_cache_mb, disk_space_mb, tier);
855 if(ret != 0) {
@@ -877,6 +869,7 @@ int rrd_init(char *hostname, struct rrdhost_system_info *system_info) {
869 else if(!created_tiers)
870 fatal("DBENGINE on '%s', failed to initialize databases at '%s'.", hostname, netdata_configured_cache_dir);
871
872 + dbengine_enabled = true;
873 #else
874 storage_tiers = config_get_number(CONFIG_SECTION_DB, "storage tiers", 1);
875 if(storage_tiers != 1) {
@@ -885,10 +878,49 @@ int rrd_init(char *hostname, struct rrdhost_system_info *system_info) {
878 config_set_number(CONFIG_SECTION_DB, "storage tiers", storage_tiers);
879 }
880 #endif
881 +}
882 +
883 +int rrd_init(char *hostname, struct rrdhost_system_info *system_info) {
884 + rrdhost_init();
885 +
886 + if (unlikely(sql_init_database(DB_CHECK_NONE, system_info ? 0 : 1))) {
887 + if (default_rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE)
888 + fatal("Failed to initialize SQLite");
889 + info("Skipping SQLITE metadata initialization since memory mode is not dbengine");
890 + }
891 +
892 + if (unlikely(sql_init_context_database(system_info ? 0 : 1))) {
893 + error_report("Failed to initialize context metadata database");
894 + }
895 +
896 + if (unlikely(strcmp(hostname, "unittest") == 0)) {
897 + dbengine_enabled = true;
898 + goto unittest;
899 + }
900
889 - health_init();
901 rrdpush_init();
902
903 + if(default_rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE || storage_tiers > 1 || rrdpush_receiver_needs_dbengine()) {
904 + info("Initializing dbengine...");
905 + dbengine_init(hostname);
906 + }
907 + else
908 + info("Not initializing dbengine...");
909 +
910 + if(!dbengine_enabled) {
911 + if (storage_tiers > 1) {
912 + error("dbengine is not enabled, but %d tiers have been requested. Resetting tiers to 1", storage_tiers);
913 + storage_tiers = 1;
914 + }
915 +
916 + if(default_rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) {
917 + error("dbengine is not enabled, but it has been given as the default db mode. Resetting db mode to alloc");
918 + default_rrd_memory_mode = RRD_MEMORY_MODE_ALLOC;
919 + }
920 + }
921 +
922 + health_init();
923 +
924 unittest:
925 debug(D_RRDHOST, "Initializing localhost with hostname '%s'", hostname);
926 rrd_wrlock();
@@ -1418,10 +1450,8 @@ void rrdhost_cleanup_all(void) {
1450 RRDHOST *host;
1451 rrdhost_foreach_read(host) {
1452 if (host != localhost && rrdhost_option_check(host, RRDHOST_OPTION_DELETE_ORPHAN_HOST) && !host->receiver
1421 -#ifdef ENABLE_DBENGINE
1453 /* don't delete multi-host DB host files */
1454 && !(host->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE && is_storage_engine_shared(host->storage_instance[0]))
1424 -#endif
1455 )
1456 rrdhost_delete_charts(host);
1457 else
database/rrdset.c
+1 -1
@@ -1972,7 +1972,7 @@ void rrdset_memory_file_free(RRDSET *st) {
1972 rrdset_memory_file_update(st);
1973
1974 struct rrdset_map_save_v019 *st_on_file = st->st_on_file;
1975 - munmap(st_on_file, st_on_file->memsize);
1975 + netdata_munmap(st_on_file, st_on_file->memsize);
1976
1977 // remove the pointers from the RRDDIM
1978 st->st_on_file = NULL;
database/sqlite/sqlite_aclk.c
+1 -8
@@ -264,13 +264,6 @@ static int create_host_callback(void *data, int argc, char **argv, char **column
264
265 struct rrdhost_system_info *system_info = callocz(1, sizeof(struct rrdhost_system_info));
266 system_info->hops = str2i((const char *) argv[IDX_HOPS]);
267 - RRD_MEMORY_MODE memory_mode;
268 -
269 -#ifdef ENABLE_DBENGINE
270 - memory_mode = RRD_MEMORY_MODE_DBENGINE;
271 -#else
272 - memory_mode = RRD_MEMORY_MODE_RAM;
273 -#endif
267
268 sql_build_host_system_info((uuid_t *)argv[IDX_HOST_ID], system_info);
269
@@ -287,7 +280,7 @@ static int create_host_callback(void *data, int argc, char **argv, char **column
280 , (const char *) (argv[IDX_PROGRAM_VERSION] ? argv[IDX_PROGRAM_VERSION] : "unknown")
281 , argv[3] ? str2i(argv[IDX_UPDATE_EVERY]) : 1
282 , argv[13] ? str2i(argv[IDX_ENTRIES]) : 0
290 - , memory_mode
283 + , default_rrd_memory_mode
284 , 0 // health
285 , 0 // rrdpush enabled
286 , NULL //destination
database/sqlite/sqlite_functions.c
+4 -2
@@ -1408,8 +1408,10 @@ RRDHOST *sql_create_host_by_uuid(char *hostname)
1408 rrdhost_flag_set(host, RRDHOST_FLAG_ARCHIVED);
1409
1410 #ifdef ENABLE_DBENGINE
1411 - for(int tier = 0; tier < storage_tiers ; tier++)
1412 - host->storage_instance[tier] = (STORAGE_INSTANCE *)multidb_ctx[tier];
1411 + if(dbengine_enabled) {
1412 + for (int tier = 0; tier < storage_tiers; tier++)
1413 + host->storage_instance[tier] = (STORAGE_INSTANCE *)multidb_ctx[tier];
1414 + }
1415 #endif
1416
1417 failed:
libnetdata/arrayalloc/arrayalloc.c
+119 -6
@@ -162,7 +162,11 @@ static inline ARAL_PAGE *find_page_with_allocation(ARAL *ar, void *ptr) {
162 return page;
163 }
164
165 -static void arrayalloc_increase(ARAL *ar) {
165 +#ifdef NETDATA_TRACE_ALLOCATIONS
166 +static void arrayalloc_add_page(ARAL *ar, const char *file, const char *function, size_t line) {
167 +#else
168 +static void arrayalloc_add_page(ARAL *ar) {
169 +#endif
170 if(unlikely(!ar->internal.initialized))
171 arrayalloc_init(ar);
172
@@ -182,8 +186,13 @@ static void arrayalloc_increase(ARAL *ar) {
186 if (unlikely(!page->data))
187 fatal("Cannot allocate arrayalloc buffer of size %zu on filename '%s'", page->size, page->filename);
188 }
185 - else
189 + else {
190 +#ifdef NETDATA_TRACE_ALLOCATIONS
191 + page->data = mallocz_int(page->size, file, function, line);
192 +#else
193 page->data = mallocz(page->size);
194 +#endif
195 + }
196
197 // link the free space to its page
198 ARAL_FREE *fr = (ARAL_FREE *)page->data;
@@ -217,14 +226,23 @@ ARAL *arrayalloc_create(size_t element_size, size_t elements, const char *filena
226 return ar;
227 }
228
229 +#ifdef NETDATA_TRACE_ALLOCATIONS
230 +void *arrayalloc_mallocz_int(ARAL *ar, const char *file, const char *function, size_t line) {
231 +#else
232 void *arrayalloc_mallocz(ARAL *ar) {
233 +#endif
234 if(unlikely(!ar->internal.initialized))
235 arrayalloc_init(ar);
236
237 arrayalloc_lock(ar);
238
226 - if(unlikely(!ar->internal.first_page || !ar->internal.first_page->free_list))
227 - arrayalloc_increase(ar);
239 + if(unlikely(!ar->internal.first_page || !ar->internal.first_page->free_list)) {
240 +#ifdef NETDATA_TRACE_ALLOCATIONS
241 + arrayalloc_add_page(ar, file, function, line);
242 +#else
243 + arrayalloc_add_page(ar);
244 +#endif
245 + }
246
247 ARAL_PAGE *page = ar->internal.first_page;
248 ARAL_FREE *fr = page->free_list;
@@ -266,7 +284,11 @@ void *arrayalloc_mallocz(ARAL *ar) {
284 return (void *)fr;
285 }
286
287 +#ifdef NETDATA_TRACE_ALLOCATIONS
288 +void arrayalloc_freez_int(ARAL *ar, void *ptr, const char *file, const char *function, size_t line) {
289 +#else
290 void arrayalloc_freez(ARAL *ar, void *ptr) {
291 +#endif
292 if(!ptr) return;
293 arrayalloc_lock(ar);
294
@@ -319,13 +341,18 @@ void arrayalloc_freez(ARAL *ar, void *ptr) {
341
342 // free it
343 if(ar->internal.mmap) {
322 - munmap(page->data, page->size);
344 + netdata_munmap(page->data, page->size);
345 if (unlikely(unlink(page->filename) == 1))
346 error("Cannot delete file '%s'", page->filename);
347 freez((void *)page->filename);
348 }
327 - else
349 + else {
350 +#ifdef NETDATA_TRACE_ALLOCATIONS
351 + freez_int(page->data, file, function, line);
352 +#else
353 freez(page->data);
354 +#endif
355 + }
356
357 freez(page);
358 }
@@ -336,3 +363,89 @@ void arrayalloc_freez(ARAL *ar, void *ptr) {
363
364 arrayalloc_unlock(ar);
365 }
366 +
367 +int aral_unittest(size_t elements) {
368 + char *cache_dir = "/tmp/";
369 + ARAL *ar = arrayalloc_create(20, 10, "test-aral", &cache_dir);
370 + ar->use_mmap = false;
371 +
372 + void *pointers[elements];
373 +
374 + for(size_t i = 0; i < elements ;i++) {
375 + pointers[i] = arrayalloc_mallocz(ar);
376 + }
377 +
378 + for(size_t div = 5; div >= 2 ;div--) {
379 + for (size_t i = 0; i < elements / div; i++) {
380 + arrayalloc_freez(ar, pointers[i]);
381 + }
382 +
383 + for (size_t i = 0; i < elements / div; i++) {
384 + pointers[i] = arrayalloc_mallocz(ar);
385 + }
386 + }
387 +
388 + for(size_t step = 50; step >= 10 ;step -= 10) {
389 + for (size_t i = 0; i < elements; i += step) {
390 + arrayalloc_freez(ar, pointers[i]);
391 + }
392 +
393 + for (size_t i = 0; i < elements; i += step) {
394 + pointers[i] = arrayalloc_mallocz(ar);
395 + }
396 + }
397 +
398 + for(size_t i = 0; i < elements ;i++) {
399 + arrayalloc_freez(ar, pointers[i]);
400 + }
401 +
402 + if(ar->internal.first_page) {
403 + fprintf(stderr, "ARAL leftovers detected (1)");
404 + return 1;
405 + }
406 +
407 + size_t ops = 0;
408 + size_t increment = elements / 10;
409 + size_t allocated = 0;
410 + for(size_t all = increment; all <= elements ; all += increment) {
411 +
412 + for(; allocated < all ; allocated++) {
413 + pointers[allocated] = arrayalloc_mallocz(ar);
414 + ops++;
415 + }
416 +
417 + size_t to_free = now_realtime_usec() % all;
418 + size_t free_list[to_free];
419 + for(size_t i = 0; i < to_free ;i++) {
420 + size_t pos;
421 + do {
422 + pos = now_realtime_usec() % all;
423 + } while(!pointers[pos]);
424 +
425 + arrayalloc_freez(ar, pointers[pos]);
426 + pointers[pos] = NULL;
427 + free_list[i] = pos;
428 + ops++;
429 + }
430 +
431 + for(size_t i = 0; i < to_free ;i++) {
432 + size_t pos = free_list[i];
433 + pointers[pos] = arrayalloc_mallocz(ar);
434 + ops++;
435 + }
436 + }
437 +
438 + for(size_t i = 0; i < allocated - 1 ;i++) {
439 + arrayalloc_freez(ar, pointers[i]);
440 + ops++;
441 + }
442 +
443 + arrayalloc_freez(ar, pointers[allocated - 1]);
444 +
445 + if(ar->internal.first_page) {
446 + fprintf(stderr, "ARAL leftovers detected (2)");
447 + return 1;
448 + }
449 +
450 + return 0;
451 +}
libnetdata/arrayalloc/arrayalloc.h
+14
@@ -29,7 +29,21 @@ typedef struct arrayalloc {
29 } ARAL;
30
31 ARAL *arrayalloc_create(size_t element_size, size_t elements, const char *filename, char **cache_dir);
32 +int aral_unittest(size_t elements);
33 +
34 +#ifdef NETDATA_TRACE_ALLOCATIONS
35 +
36 +#define arrayalloc_mallocz(ar) arrayalloc_mallocz_int(ar, __FILE__, __FUNCTION__, __LINE__)
37 +#define arrayalloc_freez(ar, ptr) arrayalloc_freez_int(ar, ptr, __FILE__, __FUNCTION__, __LINE__)
38 +
39 +void *arrayalloc_mallocz_int(ARAL *ar, const char *file, const char *function, size_t line);
40 +void arrayalloc_freez_int(ARAL *ar, void *ptr, const char *file, const char *function, size_t line);
41 +
42 +#else // NETDATA_TRACE_ALLOCATIONS
43 +
44 void *arrayalloc_mallocz(ARAL *ar);
45 void arrayalloc_freez(ARAL *ar, void *ptr);
46
47 +#endif // NETDATA_TRACE_ALLOCATIONS
48 +
49 #endif // ARRAYALLOC_H
libnetdata/config/appconfig.c
+1 -1
@@ -473,7 +473,7 @@ NETDATA_DOUBLE appconfig_get_float(struct config *root, const char *section, con
473 return str2ndd(s, NULL);
474 }
475
476 -static inline int appconfig_test_boolean_value(char *s) {
476 +inline int appconfig_test_boolean_value(char *s) {
477 if(!strcasecmp(s, "yes") || !strcasecmp(s, "true") || !strcasecmp(s, "on")
478 || !strcasecmp(s, "auto") || !strcasecmp(s, "on demand"))
479 return 1;
libnetdata/config/appconfig.h
+2
@@ -199,6 +199,8 @@ struct section *appconfig_get_section(struct config *root, const char *name);
199 void appconfig_wrlock(struct config *root);
200 void appconfig_unlock(struct config *root);
201
202 +int appconfig_test_boolean_value(char *s);
203 +
204 struct connector_instance {
205 char instance_name[CONFIG_MAX_NAME + 1];
206 char connector_name[CONFIG_MAX_NAME + 1];
libnetdata/dictionary/dictionary.c
+57 -50
@@ -173,7 +173,7 @@ struct dictionary {
173 // forward definitions of functions used in reverse order in the code
174 static void garbage_collect_pending_deletes(DICTIONARY *dict);
175 static inline void item_linked_list_remove(DICTIONARY *dict, DICTIONARY_ITEM *item);
176 -static size_t item_free_with_hooks(DICTIONARY *dict, DICTIONARY_ITEM *item);
176 +static size_t dict_item_free_with_hooks(DICTIONARY *dict, DICTIONARY_ITEM *item);
177 static inline const char *item_get_name(const DICTIONARY_ITEM *item);
178 static bool item_is_not_referenced_and_can_be_removed(DICTIONARY *dict, DICTIONARY_ITEM *item);
179 static inline int hashtable_delete_unsafe(DICTIONARY *dict, const char *name, size_t name_len, void *item);
@@ -688,7 +688,7 @@ static void garbage_collect_pending_deletes(DICTIONARY *dict) {
688
689 if(item_is_not_referenced_and_can_be_removed(dict, item)) {
690 DOUBLE_LINKED_LIST_REMOVE_UNSAFE(dict->items.list, item, prev, next);
691 - item_free_with_hooks(dict, item);
691 + dict_item_free_with_hooks(dict, item);
692 deleted++;
693
694 pending = DICTIONARY_PENDING_DELETES_MINUS1(dict);
@@ -1075,7 +1075,7 @@ static inline const char *item_get_name(const DICTIONARY_ITEM *item) {
1075 return item->caller_name;
1076 }
1077
1078 -static DICTIONARY_ITEM *item_allocate(DICTIONARY *dict __maybe_unused, size_t *allocated_bytes, DICTIONARY_ITEM *master_item) {
1078 +static DICTIONARY_ITEM *dict_item_create(DICTIONARY *dict __maybe_unused, size_t *allocated_bytes, DICTIONARY_ITEM *master_item) {
1079 DICTIONARY_ITEM *item;
1080
1081 size_t size = sizeof(DICTIONARY_ITEM);
@@ -1102,7 +1102,29 @@ static DICTIONARY_ITEM *item_allocate(DICTIONARY *dict __maybe_unused, size_t *a
1102 return item;
1103 }
1104
1105 -static DICTIONARY_ITEM *item_create_with_hooks(DICTIONARY *dict, const char *name, size_t name_len, void *value, size_t value_len, void *constructor_data, DICTIONARY_ITEM *master_item) {
1105 +static void *dict_item_value_create(void *value, size_t value_len) {
1106 + void *ptr = NULL;
1107 +
1108 + if(likely(value_len)) {
1109 + if (likely(value)) {
1110 + // a value has been supplied
1111 + // copy it
1112 + ptr = mallocz(value_len);
1113 + memcpy(ptr, value, value_len);
1114 + }
1115 + else {
1116 + // no value has been supplied
1117 + // allocate a clear memory block
1118 + ptr = callocz(1, value_len);
1119 + }
1120 + }
1121 + // else
1122 + // the caller wants an item without any value
1123 +
1124 + return ptr;
1125 +}
1126 +
1127 +static DICTIONARY_ITEM *dict_item_create_with_hooks(DICTIONARY *dict, const char *name, size_t name_len, void *value, size_t value_len, void *constructor_data, DICTIONARY_ITEM *master_item) {
1128 #ifdef NETDATA_INTERNAL_CHECKS
1129 if(unlikely(name_len > KEY_LEN_MAX))
1130 fatal("DICTIONARY: tried to index a key of size %zu, but the maximum acceptable is %zu", name_len, (size_t)KEY_LEN_MAX);
@@ -1113,7 +1135,7 @@ static DICTIONARY_ITEM *item_create_with_hooks(DICTIONARY *dict, const char *nam
1135
1136 size_t item_size = 0, key_size = 0, value_size = 0;
1137
1116 - DICTIONARY_ITEM *item = item_allocate(dict, &item_size, master_item);
1138 + DICTIONARY_ITEM *item = dict_item_create(dict, &item_size, master_item);
1139 key_size += item_set_name(dict, item, name, name_len);
1140
1141 if(unlikely(is_view_dictionary(dict))) {
@@ -1129,28 +1151,11 @@ static DICTIONARY_ITEM *item_create_with_hooks(DICTIONARY *dict, const char *nam
1151 else {
1152 // we are on the master dictionary
1153
1132 - if(likely(dict->options & DICT_OPTION_VALUE_LINK_DONT_CLONE))
1154 + if(unlikely(dict->options & DICT_OPTION_VALUE_LINK_DONT_CLONE))
1155 item->shared->value = value;
1134 - else {
1135 - if(likely(value_len)) {
1136 - if(value) {
1137 - // a value has been supplied
1138 - // copy it
1139 - item->shared->value = mallocz(value_len);
1140 - memcpy(item->shared->value, value, value_len);
1141 - }
1142 - else {
1143 - // no value has been supplied
1144 - // allocate a clear memory block
1145 - item->shared->value = callocz(1, value_len);
1146 - }
1156 + else
1157 + item->shared->value = dict_item_value_create(value, value_len);
1158
1148 - }
1149 - else {
1150 - // the caller wants an item without any value
1151 - item->shared->value = NULL;
1152 - }
1153 - }
1159 item->shared->value_len = value_len;
1160 value_size += value_len;
1161
@@ -1163,7 +1168,7 @@ static DICTIONARY_ITEM *item_create_with_hooks(DICTIONARY *dict, const char *nam
1168 return item;
1169 }
1170
1166 -static void item_reset_value_with_hooks(DICTIONARY *dict, DICTIONARY_ITEM *item, void *value, size_t value_len, void *constructor_data) {
1171 +static void dict_item_reset_value_with_hooks(DICTIONARY *dict, DICTIONARY_ITEM *item, void *value, size_t value_len, void *constructor_data) {
1172 if(unlikely(is_view_dictionary(dict)))
1173 fatal("DICTIONARY: %s() should never be called on views.", __FUNCTION__ );
1174
@@ -1203,7 +1208,7 @@ static void item_reset_value_with_hooks(DICTIONARY *dict, DICTIONARY_ITEM *item,
1208 dictionary_execute_insert_callback(dict, item, constructor_data);
1209 }
1210
1206 -static size_t item_free_with_hooks(DICTIONARY *dict, DICTIONARY_ITEM *item) {
1211 +static size_t dict_item_free_with_hooks(DICTIONARY *dict, DICTIONARY_ITEM *item) {
1212 debug(D_DICTIONARY, "Destroying name value entry for name '%s'.", item_get_name(item));
1213
1214 size_t item_size = 0, key_size = 0, value_size = 0;
@@ -1239,7 +1244,7 @@ static size_t item_free_with_hooks(DICTIONARY *dict, DICTIONARY_ITEM *item) {
1244 // ----------------------------------------------------------------------------
1245 // item operations
1246
1242 -static void item_shared_set_deleted(DICTIONARY *dict, DICTIONARY_ITEM *item) {
1247 +static void dict_item_shared_set_deleted(DICTIONARY *dict, DICTIONARY_ITEM *item) {
1248 if(is_master_dictionary(dict)) {
1249 item_shared_flag_set(item, ITEM_FLAG_DELETED);
1250
@@ -1248,14 +1253,14 @@ static void item_shared_set_deleted(DICTIONARY *dict, DICTIONARY_ITEM *item) {
1253 }
1254 }
1255
1251 -static inline void item_free_or_mark_deleted(DICTIONARY *dict, DICTIONARY_ITEM *item) {
1256 +static inline void dict_item_free_or_mark_deleted(DICTIONARY *dict, DICTIONARY_ITEM *item) {
1257 if(item_is_not_referenced_and_can_be_removed(dict, item)) {
1253 - item_shared_set_deleted(dict, item);
1258 + dict_item_shared_set_deleted(dict, item);
1259 item_linked_list_remove(dict, item);
1255 - item_free_with_hooks(dict, item);
1260 + dict_item_free_with_hooks(dict, item);
1261 }
1262 else {
1258 - item_shared_set_deleted(dict, item);
1263 + dict_item_shared_set_deleted(dict, item);
1264 item_flag_set(item, ITEM_FLAG_DELETED);
1265 // after this point do not touch the item
1266 }
@@ -1269,7 +1274,7 @@ static inline void item_free_or_mark_deleted(DICTIONARY *dict, DICTIONARY_ITEM *
1274 // the need for the garbage collector to kick-in later.
1275 // Most deletions happen during traversal, so this is a nice hack
1276 // to speed up everything!
1272 -static inline void item_release_and_check_if_it_is_deleted_and_can_be_removed_under_this_lock_mode(DICTIONARY *dict, DICTIONARY_ITEM *item, char rw) {
1277 +static inline void dict_item_release_and_check_if_it_is_deleted_and_can_be_removed_under_this_lock_mode(DICTIONARY *dict, DICTIONARY_ITEM *item, char rw) {
1278 if(rw == DICTIONARY_LOCK_WRITE) {
1279 bool should_be_deleted = item_flag_check(item, ITEM_FLAG_DELETED);
1280
@@ -1281,7 +1286,7 @@ static inline void item_release_and_check_if_it_is_deleted_and_can_be_removed_un
1286 DICTIONARY_PENDING_DELETES_MINUS1(dict);
1287
1288 item_linked_list_remove(dict, item);
1284 - item_free_with_hooks(dict, item);
1289 + dict_item_free_with_hooks(dict, item);
1290 }
1291 }
1292 else {
@@ -1290,7 +1295,7 @@ static inline void item_release_and_check_if_it_is_deleted_and_can_be_removed_un
1295 }
1296 }
1297
1293 -static bool item_del(DICTIONARY *dict, const char *name, ssize_t name_len) {
1298 +static bool dict_item_del(DICTIONARY *dict, const char *name, ssize_t name_len) {
1299 if(unlikely(!name || !*name)) {
1300 internal_error(
1301 true,
@@ -1330,14 +1335,14 @@ static bool item_del(DICTIONARY *dict, const char *name, ssize_t name_len) {
1335
1336 dictionary_index_lock_unlock(dict);
1337
1333 - item_free_or_mark_deleted(dict, item);
1338 + dict_item_free_or_mark_deleted(dict, item);
1339 ret = true;
1340 }
1341
1342 return ret;
1343 }
1344
1340 -static DICTIONARY_ITEM *item_add_or_reset_value_and_acquire(DICTIONARY *dict, const char *name, ssize_t name_len, void *value, size_t value_len, void *constructor_data, DICTIONARY_ITEM *master_item) {
1345 +static DICTIONARY_ITEM *dict_item_add_or_reset_value_and_acquire(DICTIONARY *dict, const char *name, ssize_t name_len, void *value, size_t value_len, void *constructor_data, DICTIONARY_ITEM *master_item) {
1346 if(unlikely(!name || !*name)) {
1347 internal_error(
1348 true,
@@ -1381,7 +1386,8 @@ static DICTIONARY_ITEM *item_add_or_reset_value_and_acquire(DICTIONARY *dict, co
1386 // a new item added to the index
1387
1388 // create the dictionary item
1384 - item = *item_pptr = item_create_with_hooks(dict, name, name_len, value, value_len, constructor_data, master_item);
1389 + item = *item_pptr =
1390 + dict_item_create_with_hooks(dict, name, name_len, value, value_len, constructor_data, master_item);
1391
1392 // call the hashtable react
1393 hashtable_inserted_item_unsafe(dict, item);
@@ -1420,7 +1426,7 @@ static DICTIONARY_ITEM *item_add_or_reset_value_and_acquire(DICTIONARY *dict, co
1426 // the user wants to reset its value
1427
1428 if (!(dict->options & DICT_OPTION_DONT_OVERWRITE_VALUE)) {
1423 - item_reset_value_with_hooks(dict, item, value, value_len, constructor_data);
1429 + dict_item_reset_value_with_hooks(dict, item, value, value_len, constructor_data);
1430 added_or_updated = true;
1431 }
1432
@@ -1449,7 +1455,7 @@ static DICTIONARY_ITEM *item_add_or_reset_value_and_acquire(DICTIONARY *dict, co
1455 return item;
1456 }
1457
1452 -static DICTIONARY_ITEM *item_find_and_acquire(DICTIONARY *dict, const char *name, ssize_t name_len) {
1458 +static DICTIONARY_ITEM *dict_item_find_and_acquire(DICTIONARY *dict, const char *name, ssize_t name_len) {
1459 if(unlikely(!name || !*name)) {
1460 internal_error(
1461 true,
@@ -1517,7 +1523,7 @@ static bool dictionary_free_all_resources(DICTIONARY *dict, size_t *mem, bool fo
1523 // cache item->next
1524 // because we are going to free item
1525 DICTIONARY_ITEM *item_next = item->next;
1520 - item_size += item_free_with_hooks(dict, item);
1526 + item_size += dict_item_free_with_hooks(dict, item);
1527 item = item_next;
1528
1529 DICTIONARY_ENTRIES_MINUS1(dict);
@@ -1800,7 +1806,7 @@ void dictionary_flush(DICTIONARY *dict) {
1806 item_next = item->next;
1807
1808 if(!item_flag_check(item, ITEM_FLAG_DELETED))
1803 - item_free_or_mark_deleted(dict, item);
1809 + dict_item_free_or_mark_deleted(dict, item);
1810 }
1811 ll_recursive_unlock(dict, DICTIONARY_LOCK_WRITE);
1812
@@ -1850,7 +1856,8 @@ DICT_ITEM_CONST DICTIONARY_ITEM *dictionary_set_and_acquire_item_advanced(DICTIO
1856 if(unlikely(is_view_dictionary(dict)))
1857 fatal("DICTIONARY: this dictionary is a view, you cannot add items other than the ones from the master dictionary.");
1858
1853 - DICTIONARY_ITEM *item = item_add_or_reset_value_and_acquire(dict, name, name_len, value, value_len, constructor_data, NULL);
1859 + DICTIONARY_ITEM *item =
1860 + dict_item_add_or_reset_value_and_acquire(dict, name, name_len, value, value_len, constructor_data, NULL);
1861 api_internal_check(dict, item, false, false);
1862 return item;
1863 }
@@ -1877,7 +1884,7 @@ DICT_ITEM_CONST DICTIONARY_ITEM *dictionary_view_set_and_acquire_item_advanced(D
1884 fatal("DICTIONARY: this dictionary is a master, you cannot add items from other dictionaries.");
1885
1886 dictionary_acquired_item_dup(dict->master, master_item);
1880 - DICTIONARY_ITEM *item = item_add_or_reset_value_and_acquire(dict, name, name_len, NULL, 0, NULL, master_item);
1887 + DICTIONARY_ITEM *item = dict_item_add_or_reset_value_and_acquire(dict, name, name_len, NULL, 0, NULL, master_item);
1888 dictionary_acquired_item_release(dict->master, master_item);
1889
1890 api_internal_check(dict, item, false, false);
@@ -1904,7 +1911,7 @@ DICT_ITEM_CONST DICTIONARY_ITEM *dictionary_get_and_acquire_item_advanced(DICTIO
1911 return NULL;
1912
1913 api_internal_check(dict, NULL, false, true);
1907 - DICTIONARY_ITEM *item = item_find_and_acquire(dict, name, name_len);
1914 + DICTIONARY_ITEM *item = dict_item_find_and_acquire(dict, name, name_len);
1915 api_internal_check(dict, item, false, true);
1916 return item;
1917 }
@@ -1977,7 +1984,7 @@ bool dictionary_del_advanced(DICTIONARY *dict, const char *name, ssize_t name_le
1984 return false;
1985
1986 api_internal_check(dict, NULL, false, true);
1980 - return item_del(dict, name, name_len);
1987 + return dict_item_del(dict, name, name_len);
1988 }
1989
1990 // ----------------------------------------------------------------------------
@@ -2052,7 +2059,7 @@ void *dictionary_foreach_next(DICTFE *dfe) {
2059 item_next = item_next->next;
2060
2061 if(likely(item)) {
2055 - item_release_and_check_if_it_is_deleted_and_can_be_removed_under_this_lock_mode(dfe->dict, item, dfe->rw);
2062 + dict_item_release_and_check_if_it_is_deleted_and_can_be_removed_under_this_lock_mode(dfe->dict, item, dfe->rw);
2063 // item_release(dfe->dict, item);
2064 }
2065
@@ -2088,7 +2095,7 @@ void dictionary_foreach_done(DICTFE *dfe) {
2095
2096 // release it, so that it can possibly be deleted
2097 if(likely(item)) {
2091 - item_release_and_check_if_it_is_deleted_and_can_be_removed_under_this_lock_mode(dfe->dict, item, dfe->rw);
2098 + dict_item_release_and_check_if_it_is_deleted_and_can_be_removed_under_this_lock_mode(dfe->dict, item, dfe->rw);
2099 // item_release(dfe->dict, item);
2100 }
2101
@@ -2143,7 +2150,7 @@ int dictionary_walkthrough_rw(DICTIONARY *dict, char rw, int (*callback)(const D
2150 // until we release the reference counter, so the pointers are there
2151 item_next = item->next;
2152
2146 - item_release_and_check_if_it_is_deleted_and_can_be_removed_under_this_lock_mode(dict, item, rw);
2153 + dict_item_release_and_check_if_it_is_deleted_and_can_be_removed_under_this_lock_mode(dict, item, rw);
2154 // item_release(dict, item);
2155
2156 if(unlikely(r < 0)) {
@@ -2203,7 +2210,7 @@ int dictionary_sorted_walkthrough_rw(DICTIONARY *dict, char rw, int (*callback)(
2210 if(callit)
2211 r = callback(item, item->shared->value, data);
2212
2206 - item_release_and_check_if_it_is_deleted_and_can_be_removed_under_this_lock_mode(dict, item, rw);
2213 + dict_item_release_and_check_if_it_is_deleted_and_can_be_removed_under_this_lock_mode(dict, item, rw);
2214 // item_release(dict, item);
2215
2216 if(r < 0) {
libnetdata/libnetdata.c
+213 -97
@@ -30,128 +30,237 @@ const char *program_version = VERSION;
30 // its lifetime), these can be used to override the default system allocation
31 // routines.
32
33 -#ifdef NETDATA_LOG_ALLOCATIONS
34 -#warning NETDATA_LOG_ALLOCATIONS ENABLED - set log_thread_memory_allocations=1 on any thread to log all its allocations - or use log_allocations() to log them on demand
35 -
36 -static __thread struct memory_statistics {
37 - volatile ssize_t malloc_calls_made;
38 - volatile ssize_t calloc_calls_made;
39 - volatile ssize_t realloc_calls_made;
40 - volatile ssize_t strdup_calls_made;
41 - volatile ssize_t free_calls_made;
42 - volatile ssize_t memory_calls_made;
43 - volatile ssize_t allocated_memory;
44 - volatile ssize_t mmapped_memory;
45 -} memory_statistics = { 0, 0, 0, 0, 0, 0, 0, 0 };
46 -
47 -__thread size_t log_thread_memory_allocations = 0;
48 -
49 -inline void log_allocations_int(const char *file, const char *function, const unsigned long line) {
50 - static __thread struct memory_statistics old = { 0, 0, 0, 0, 0, 0, 0, 0 };
51 -
52 - fprintf(stderr, "%s MEMORY ALLOCATIONS: (%04lu@%s:%s): Allocated %zd KiB (%+zd B), mmapped %zd KiB (%+zd B): : malloc %zd (%+zd), calloc %zd (%+zd), realloc %zd (%+zd), strdup %zd (%+zd), free %zd (%+zd)\n",
53 - netdata_thread_tag(),
54 - line, file, function,
55 - (memory_statistics.allocated_memory + 512) / 1024, memory_statistics.allocated_memory - old.allocated_memory,
56 - (memory_statistics.mmapped_memory + 512) / 1024, memory_statistics.mmapped_memory - old.mmapped_memory,
57 - memory_statistics.malloc_calls_made, memory_statistics.malloc_calls_made - old.malloc_calls_made,
58 - memory_statistics.calloc_calls_made, memory_statistics.calloc_calls_made - old.calloc_calls_made,
59 - memory_statistics.realloc_calls_made, memory_statistics.realloc_calls_made - old.realloc_calls_made,
60 - memory_statistics.strdup_calls_made, memory_statistics.strdup_calls_made - old.strdup_calls_made,
61 - memory_statistics.free_calls_made, memory_statistics.free_calls_made - old.free_calls_made
62 - );
63 -
64 - memcpy(&old, &memory_statistics, sizeof(struct memory_statistics));
65 -}
66 -
67 -static inline void mmap_accounting(size_t size) {
68 - if(log_thread_memory_allocations) {
69 - memory_statistics.memory_calls_made++;
70 - memory_statistics.mmapped_memory += size;
71 - }
33 +#ifdef NETDATA_TRACE_ALLOCATIONS
34 +#warning NETDATA_TRACE_ALLOCATIONS ENABLED
35 +#include "Judy.h"
36 +
37 +Word_t JudyMalloc(Word_t Words) {
38 + Word_t Addr;
39 +
40 + Addr = (Word_t) mallocz(Words * sizeof(Word_t));
41 + return(Addr);
42 +}
43 +void JudyFree(void * PWord, Word_t Words) {
44 + (void)Words;
45 + freez(PWord);
46 +}
47 +Word_t JudyMallocVirtual(Word_t Words) {
48 + Word_t Addr;
49 +
50 + Addr = (Word_t) mallocz(Words * sizeof(Word_t));
51 + return(Addr);
52 +}
53 +void JudyFreeVirtual(void * PWord, Word_t Words) {
54 + (void)Words;
55 + freez(PWord);
56 }
57
74 -void *mallocz_int(const char *file, const char *function, const unsigned long line, size_t size) {
75 - memory_statistics.memory_calls_made++;
76 - memory_statistics.malloc_calls_made++;
77 - memory_statistics.allocated_memory += size;
58 +#define MALLOC_ALIGNMENT (sizeof(uintptr_t) * 2)
59 +#define size_t_atomic_count(op, var, size) __atomic_## op ##_fetch(&(var), size, __ATOMIC_RELAXED)
60 +#define size_t_atomic_bytes(op, var, size) __atomic_## op ##_fetch(&(var), ((size) % MALLOC_ALIGNMENT)?((size) + MALLOC_ALIGNMENT - (size % MALLOC_ALIGNMENT)):(size), __ATOMIC_RELAXED)
61
79 - if(log_thread_memory_allocations)
80 - log_allocations_int(file, function, line);
62 +struct malloc_header_signature {
63 + uint32_t magic;
64 + uint32_t size;
65 + struct malloc_trace *trace;
66 +};
67 +
68 +struct malloc_header {
69 + struct malloc_header_signature signature;
70 + uint8_t padding[(sizeof(struct malloc_header_signature) % MALLOC_ALIGNMENT) ? MALLOC_ALIGNMENT - (sizeof(struct malloc_header_signature) % MALLOC_ALIGNMENT) : 0];
71 + uint8_t data[];
72 +};
73 +
74 +static size_t malloc_header_size = sizeof(struct malloc_header);
75
82 - size_t *n = (size_t *)malloc(sizeof(size_t) + size);
83 - if (unlikely(!n)) fatal("mallocz() cannot allocate %zu bytes of memory.", size);
84 - *n = size;
85 - return (void *)&n[1];
76 +int malloc_trace_compare(void *A, void *B) {
77 + struct malloc_trace *a = A;
78 + struct malloc_trace *b = B;
79 + return strcmp(a->function, b->function);
80 }
81
88 -void *callocz_int(const char *file, const char *function, const unsigned long line, size_t nmemb, size_t size) {
89 - size = nmemb * size;
82 +static avl_tree_lock malloc_trace_index = {
83 + .avl_tree = {
84 + .root = NULL,
85 + .compar = malloc_trace_compare},
86 + .rwlock = NETDATA_RWLOCK_INITIALIZER
87 +};
88 +
89 +int malloc_trace_walkthrough(int (*callback)(void *item, void *data), void *data) {
90 + return avl_traverse_lock(&malloc_trace_index, callback, data);
91 +}
92 +
93 +NEVERNULL WARNUNUSED
94 +static struct malloc_trace *malloc_trace_find_or_create(const char *file, const char *function, size_t line) {
95 + struct malloc_trace tmp = {
96 + .line = line,
97 + .function = function,
98 + .file = file,
99 + };
100 +
101 + struct malloc_trace *t = (struct malloc_trace *)avl_search_lock(&malloc_trace_index, (avl_t *)&tmp);
102 + if(!t) {
103 + t = calloc(1, sizeof(struct malloc_trace));
104 + if(!t) fatal("No memory");
105 + t->line = line;
106 + t->function = function;
107 + t->file = file;
108 +
109 + struct malloc_trace *t2 = (struct malloc_trace *)avl_insert_lock(&malloc_trace_index, (avl_t *)t);
110 + if(t2 != t)
111 + free(t);
112 +
113 + t = t2;
114 + }
115 +
116 + if(!t)
117 + fatal("Cannot insert to AVL");
118 +
119 + return t;
120 +}
121
91 - memory_statistics.memory_calls_made++;
92 - memory_statistics.calloc_calls_made++;
93 - memory_statistics.allocated_memory += size;
94 - if(log_thread_memory_allocations)
95 - log_allocations_int(file, function, line);
122 +void malloc_trace_mmap(size_t size) {
123 + struct malloc_trace *p = malloc_trace_find_or_create("unknown", "netdata_mmap", 1);
124 + size_t_atomic_count(add, p->mmap_calls, 1);
125 + size_t_atomic_count(add, p->allocations, 1);
126 + size_t_atomic_bytes(add, p->bytes, size);
127 +}
128
97 - size_t *n = (size_t *)calloc(1, sizeof(size_t) + size);
98 - if (unlikely(!n)) fatal("callocz() cannot allocate %zu bytes of memory.", size);
99 - *n = size;
100 - return (void *)&n[1];
129 +void malloc_trace_munmap(size_t size) {
130 + struct malloc_trace *p = malloc_trace_find_or_create("unknown", "netdata_mmap", 1);
131 + size_t_atomic_count(add, p->munmap_calls, 1);
132 + size_t_atomic_count(sub, p->allocations, 1);
133 + size_t_atomic_bytes(sub, p->bytes, size);
134 }
135
103 -void *reallocz_int(const char *file, const char *function, const unsigned long line, void *ptr, size_t size) {
104 - if(!ptr) return mallocz_int(file, function, line, size);
136 +void *mallocz_int(size_t size, const char *file, const char *function, size_t line) {
137 + struct malloc_trace *p = malloc_trace_find_or_create(file, function, line);
138
106 - size_t *n = (size_t *)ptr;
107 - n--;
108 - size_t old_size = *n;
139 + size_t_atomic_count(add, p->malloc_calls, 1);
140 + size_t_atomic_count(add, p->allocations, 1);
141 + size_t_atomic_bytes(add, p->bytes, size);
142
110 - n = realloc(n, sizeof(size_t) + size);
111 - if (unlikely(!n)) fatal("reallocz() cannot allocate %zu bytes of memory (from %zu bytes).", size, old_size);
143 + struct malloc_header *t = (struct malloc_header *)malloc(malloc_header_size + size);
144 + if (unlikely(!t)) fatal("mallocz() cannot allocate %zu bytes of memory (%zu with header).", size, malloc_header_size + size);
145 + t->signature.magic = 0x0BADCAFE;
146 + t->signature.trace = p;
147 + t->signature.size = size;
148
113 - memory_statistics.memory_calls_made++;
114 - memory_statistics.realloc_calls_made++;
115 - memory_statistics.allocated_memory += (size - old_size);
116 - if(log_thread_memory_allocations)
117 - log_allocations_int(file, function, line);
149 +#ifdef NETDATA_INTERNAL_CHECKS
150 + for(ssize_t i = 0; i < (ssize_t)sizeof(t->padding) ;i++) // signed to avoid compiler warning when zero-padded
151 + t->padding[i] = 0xFF;
152 +#endif
153
119 - *n = size;
120 - return (void *)&n[1];
154 + return (void *)&t->data;
155 }
156
123 -char *strdupz_int(const char *file, const char *function, const unsigned long line, const char *s) {
157 +void *callocz_int(size_t nmemb, size_t size, const char *file, const char *function, size_t line) {
158 + struct malloc_trace *p = malloc_trace_find_or_create(file, function, line);
159 + size = nmemb * size;
160 +
161 + size_t_atomic_count(add, p->calloc_calls, 1);
162 + size_t_atomic_count(add, p->allocations, 1);
163 + size_t_atomic_bytes(add, p->bytes, size);
164 +
165 + struct malloc_header *t = (struct malloc_header *)calloc(1, malloc_header_size + size);
166 + if (unlikely(!t)) fatal("mallocz() cannot allocate %zu bytes of memory (%zu with header).", size, malloc_header_size + size);
167 + t->signature.magic = 0x0BADCAFE;
168 + t->signature.trace = p;
169 + t->signature.size = size;
170 +
171 +#ifdef NETDATA_INTERNAL_CHECKS
172 + for(ssize_t i = 0; i < (ssize_t)sizeof(t->padding) ;i++) // signed to avoid compiler warning when zero-padded
173 + t->padding[i] = 0xFF;
174 +#endif
175 +
176 + return &t->data;
177 +}
178 +
179 +char *strdupz_int(const char *s, const char *file, const char *function, size_t line) {
180 + struct malloc_trace *p = malloc_trace_find_or_create(file, function, line);
181 size_t size = strlen(s) + 1;
182
126 - memory_statistics.memory_calls_made++;
127 - memory_statistics.strdup_calls_made++;
128 - memory_statistics.allocated_memory += size;
129 - if(log_thread_memory_allocations)
130 - log_allocations_int(file, function, line);
183 + size_t_atomic_count(add, p->strdup_calls, 1);
184 + size_t_atomic_count(add, p->allocations, 1);
185 + size_t_atomic_bytes(add, p->bytes, size);
186 +
187 + struct malloc_header *t = (struct malloc_header *)malloc(malloc_header_size + size);
188 + if (unlikely(!t)) fatal("strdupz() cannot allocate %zu bytes of memory (%zu with header).", size, malloc_header_size + size);
189 + t->signature.magic = 0x0BADCAFE;
190 + t->signature.trace = p;
191 + t->signature.size = size;
192 +
193 +#ifdef NETDATA_INTERNAL_CHECKS
194 + for(ssize_t i = 0; i < (ssize_t)sizeof(t->padding) ;i++) // signed to avoid compiler warning when zero-padded
195 + t->padding[i] = 0xFF;
196 +#endif
197 +
198 + strcpy((char *)&t->data, s);
199 + return (char *)&t->data;
200 +}
201 +
202 +static struct malloc_header *malloc_get_header(void *ptr, const char *caller, const char *file, const char *function, size_t line) {
203 + uint8_t *ret = (uint8_t *)ptr - malloc_header_size;
204 + struct malloc_header *t = (struct malloc_header *)ret;
205
132 - size_t *n = (size_t *)malloc(sizeof(size_t) + size);
133 - if (unlikely(!n)) fatal("strdupz() cannot allocate %zu bytes of memory.", size);
206 + if(t->signature.magic != 0x0BADCAFE) {
207 + error("pointer %p is not our pointer (called %s() from %zu@%s, %s()).", ptr, caller, line, file, function);
208 + return NULL;
209 + }
210
135 - *n = size;
136 - char *t = (char *)&n[1];
137 - strcpy(t, s);
211 return t;
212 }
213
141 -void freez_int(const char *file, const char *function, const unsigned long line, void *ptr) {
214 +void *reallocz_int(void *ptr, size_t size, const char *file, const char *function, size_t line) {
215 + if(!ptr) return mallocz_int(size, file, function, line);
216 +
217 + struct malloc_header *t = malloc_get_header(ptr, __FUNCTION__, file, function, line);
218 + if(!t)
219 + return realloc(ptr, size);
220 +
221 + if(t->signature.size == size) return ptr;
222 + size_t_atomic_count(add, t->signature.trace->free_calls, 1);
223 + size_t_atomic_count(sub, t->signature.trace->allocations, 1);
224 + size_t_atomic_bytes(sub, t->signature.trace->bytes, t->signature.size);
225 +
226 + struct malloc_trace *p = malloc_trace_find_or_create(file, function, line);
227 + size_t_atomic_count(add, p->realloc_calls, 1);
228 + size_t_atomic_count(add, p->allocations, 1);
229 + size_t_atomic_bytes(add, p->bytes, size);
230 +
231 + t = (struct malloc_header *)realloc(t, malloc_header_size + size);
232 + if (unlikely(!t)) fatal("reallocz() cannot allocate %zu bytes of memory (%zu with header).", size, malloc_header_size + size);
233 + t->signature.magic = 0x0BADCAFE;
234 + t->signature.trace = p;
235 + t->signature.size = size;
236 +
237 +#ifdef NETDATA_INTERNAL_CHECKS
238 + for(ssize_t i = 0; i < (ssize_t)sizeof(t->padding) ;i++) // signed to avoid compiler warning when zero-padded
239 + t->padding[i] = 0xFF;
240 +#endif
241 +
242 + return (void *)&t->data;
243 +}
244 +
245 +void freez_int(void *ptr, const char *file, const char *function, size_t line) {
246 if(unlikely(!ptr)) return;
247
144 - size_t *n = (size_t *)ptr;
145 - n--;
146 - size_t size = *n;
248 + struct malloc_header *t = malloc_get_header(ptr, __FUNCTION__, file, function, line);
249 + if(!t) {
250 + free(ptr);
251 + return;
252 + }
253 +
254 + size_t_atomic_count(add, t->signature.trace->free_calls, 1);
255 + size_t_atomic_count(sub, t->signature.trace->allocations, 1);
256 + size_t_atomic_bytes(sub, t->signature.trace->bytes, t->signature.size);
257
148 - memory_statistics.memory_calls_made++;
149 - memory_statistics.free_calls_made++;
150 - memory_statistics.allocated_memory -= size;
151 - if(log_thread_memory_allocations)
152 - log_allocations_int(file, function, line);
258 +#ifdef NETDATA_INTERNAL_CHECKS
259 + // it should crash if it is used after freeing it
260 + memset(t, 0, malloc_header_size + t->signature.size);
261 +#endif
262
154 - free(n);
263 + free(t);
264 }
265 #else
266
@@ -1031,8 +1140,8 @@ void *netdata_mmap(const char *filename, size_t size, int flags, int ksm) {
1140 mem = mmap(NULL, size, PROT_READ | PROT_WRITE, flags, fd_for_mmap, 0);
1141 if (mem != MAP_FAILED) {
1142
1034 -#ifdef NETDATA_LOG_ALLOCATIONS
1035 - mmap_accounting(size);
1143 +#ifdef NETDATA_TRACE_ALLOCATIONS
1144 + malloc_trace_mmap(size);
1145 #endif
1146
1147 // if we have a file open, but we didn't give it to mmap(),
@@ -1059,6 +1168,13 @@ cleanup:
1168 return mem;
1169 }
1170
1171 +int netdata_munmap(void *ptr, size_t size) {
1172 +#ifdef NETDATA_TRACE_ALLOCATIONS
1173 + malloc_trace_munmap(size);
1174 +#endif
1175 + return munmap(ptr, size);
1176 +}
1177 +
1178 int memory_file_save(const char *filename, void *mem, size_t size) {
1179 char tmpfilename[FILENAME_MAX + 1];
1180
libnetdata/libnetdata.h
+53 -18
@@ -11,6 +11,14 @@ extern "C" {
11 #include <config.h>
12 #endif
13
14 +#if defined(NETDATA_DEV_MODE) && !defined(NETDATA_INTERNAL_CHECKS)
15 +#define NETDATA_INTERNAL_CHECKS 1
16 +#endif
17 +
18 +#if defined(NETDATA_INTERNAL_CHECKS) && !defined(NETDATA_TRACE_ALLOCATIONS)
19 +#define NETDATA_TRACE_ALLOCATIONS 1
20 +#endif
21 +
22 #define OS_LINUX 1
23 #define OS_FREEBSD 2
24 #define OS_MACOS 3
@@ -296,34 +304,34 @@ int vsnprintfz(char *dst, size_t n, const char *fmt, va_list args);
304 int snprintfz(char *dst, size_t n, const char *fmt, ...) PRINTFLIKE(3, 4);
305
306 // memory allocation functions that handle failures
299 -#ifdef NETDATA_LOG_ALLOCATIONS
300 -extern __thread size_t log_thread_memory_allocations;
301 -#define strdupz(s) strdupz_int(__FILE__, __FUNCTION__, __LINE__, s)
302 -#define callocz(nmemb, size) callocz_int(__FILE__, __FUNCTION__, __LINE__, nmemb, size)
303 -#define mallocz(size) mallocz_int(__FILE__, __FUNCTION__, __LINE__, size)
304 -#define reallocz(ptr, size) reallocz_int(__FILE__, __FUNCTION__, __LINE__, ptr, size)
305 -#define freez(ptr) freez_int(__FILE__, __FUNCTION__, __LINE__, ptr)
306 -#define log_allocations() log_allocations_int(__FILE__, __FUNCTION__, __LINE__)
307 -
308 -char *strdupz_int(const char *file, const char *function, const unsigned long line, const char *s);
309 -void *callocz_int(const char *file, const char *function, const unsigned long line, size_t nmemb, size_t size);
310 -void *mallocz_int(const char *file, const char *function, const unsigned long line, size_t size);
311 -void *reallocz_int(const char *file, const char *function, const unsigned long line, void *ptr, size_t size);
312 -void freez_int(const char *file, const char *function, const unsigned long line, void *ptr);
313 -void log_allocations_int(const char *file, const char *function, const unsigned long line);
314 -
315 -#else // NETDATA_LOG_ALLOCATIONS
307 +#ifdef NETDATA_TRACE_ALLOCATIONS
308 +int malloc_trace_walkthrough(int (*callback)(void *item, void *data), void *data);
309 +
310 +#define strdupz(s) strdupz_int(s, __FILE__, __FUNCTION__, __LINE__)
311 +#define callocz(nmemb, size) callocz_int(nmemb, size, __FILE__, __FUNCTION__, __LINE__)
312 +#define mallocz(size) mallocz_int(size, __FILE__, __FUNCTION__, __LINE__)
313 +#define reallocz(ptr, size) reallocz_int(ptr, size, __FILE__, __FUNCTION__, __LINE__)
314 +#define freez(ptr) freez_int(ptr, __FILE__, __FUNCTION__, __LINE__)
315 +
316 +char *strdupz_int(const char *s, const char *file, const char *function, size_t line);
317 +void *callocz_int(size_t nmemb, size_t size, const char *file, const char *function, size_t line);
318 +void *mallocz_int(size_t size, const char *file, const char *function, size_t line);
319 +void *reallocz_int(void *ptr, size_t size, const char *file, const char *function, size_t line);
320 +void freez_int(void *ptr, const char *file, const char *function, size_t line);
321 +
322 +#else // NETDATA_TRACE_ALLOCATIONS
323 char *strdupz(const char *s) MALLOCLIKE NEVERNULL;
324 void *callocz(size_t nmemb, size_t size) MALLOCLIKE NEVERNULL;
325 void *mallocz(size_t size) MALLOCLIKE NEVERNULL;
326 void *reallocz(void *ptr, size_t size) MALLOCLIKE NEVERNULL;
327 void freez(void *ptr);
321 -#endif // NETDATA_LOG_ALLOCATIONS
328 +#endif // NETDATA_TRACE_ALLOCATIONS
329
330 void json_escape_string(char *dst, const char *src, size_t size);
331 void json_fix_string(char *s);
332
333 void *netdata_mmap(const char *filename, size_t size, int flags, int ksm);
334 +int netdata_munmap(void *ptr, size_t size);
335 int memory_file_save(const char *filename, void *mem, size_t size);
336
337 int fd_is_valid(int fd);
@@ -450,6 +458,33 @@ static inline size_t struct_natural_alignment(size_t size) {
458 return size;
459 }
460
461 +#ifdef NETDATA_TRACE_ALLOCATIONS
462 +struct malloc_trace {
463 + avl_t avl;
464 +
465 + const char *function;
466 + const char *file;
467 + size_t line;
468 +
469 + size_t malloc_calls;
470 + size_t calloc_calls;
471 + size_t realloc_calls;
472 + size_t strdup_calls;
473 + size_t free_calls;
474 +
475 + size_t mmap_calls;
476 + size_t munmap_calls;
477 +
478 + size_t allocations;
479 + size_t bytes;
480 +
481 + struct rrddim *rd_bytes;
482 + struct rrddim *rd_allocations;
483 + struct rrddim *rd_avg_alloc;
484 + struct rrddim *rd_ops;
485 +};
486 +#endif // NETDATA_TRACE_ALLOCATIONS
487 +
488 # ifdef __cplusplus
489 }
490 # endif
libnetdata/procfile/procfile.c
+21 -21
@@ -42,7 +42,7 @@ char *procfile_filename(procfile *ff) {
42 // ----------------------------------------------------------------------------
43 // An array of words
44
45 -static inline void pfwords_add(procfile *ff, char *str) {
45 +static inline void procfile_words_add(procfile *ff, char *str) {
46 // debug(D_PROCFILE, PF_PREFIX ": adding word No %d: '%s'", fw->len, str);
47
48 pfwords *fw = ff->words;
@@ -60,7 +60,7 @@ static inline void pfwords_add(procfile *ff, char *str) {
60 }
61
62 NEVERNULL
63 -static inline pfwords *pfwords_new(void) {
63 +static inline pfwords *procfile_words_create(void) {
64 // debug(D_PROCFILE, PF_PREFIX ": initializing words");
65
66 size_t size = (procfile_adaptive_initial_allocation) ? procfile_max_words : PFWORDS_INCREASE_STEP;
@@ -71,12 +71,12 @@ static inline pfwords *pfwords_new(void) {
71 return new;
72 }
73
74 -static inline void pfwords_reset(pfwords *fw) {
74 +static inline void procfile_words_reset(pfwords *fw) {
75 // debug(D_PROCFILE, PF_PREFIX ": resetting words");
76 fw->len = 0;
77 }
78
79 -static inline void pfwords_free(pfwords *fw) {
79 +static inline void procfile_words_free(pfwords *fw) {
80 // debug(D_PROCFILE, PF_PREFIX ": freeing words");
81
82 freez(fw);
@@ -87,7 +87,7 @@ static inline void pfwords_free(pfwords *fw) {
87 // An array of lines
88
89 NEVERNULL
90 -static inline size_t *pflines_add(procfile *ff) {
90 +static inline size_t *procfile_lines_add(procfile *ff) {
91 // debug(D_PROCFILE, PF_PREFIX ": adding line %d at word %d", fl->len, first_word);
92
93 pflines *fl = ff->lines;
@@ -109,7 +109,7 @@ static inline size_t *pflines_add(procfile *ff) {
109 }
110
111 NEVERNULL
112 -static inline pflines *pflines_new(void) {
112 +static inline pflines *procfile_lines_create(void) {
113 // debug(D_PROCFILE, PF_PREFIX ": initializing lines");
114
115 size_t size = (unlikely(procfile_adaptive_initial_allocation)) ? procfile_max_words : PFLINES_INCREASE_STEP;
@@ -120,13 +120,13 @@ static inline pflines *pflines_new(void) {
120 return new;
121 }
122
123 -static inline void pflines_reset(pflines *fl) {
123 +static inline void procfile_lines_reset(pflines *fl) {
124 // debug(D_PROCFILE, PF_PREFIX ": resetting lines");
125
126 fl->len = 0;
127 }
128
129 -static inline void pflines_free(pflines *fl) {
129 +static inline void procfile_lines_free(pflines *fl) {
130 // debug(D_PROCFILE, PF_PREFIX ": freeing lines");
131
132 freez(fl);
@@ -141,8 +141,8 @@ void procfile_close(procfile *ff) {
141
142 debug(D_PROCFILE, PF_PREFIX ": Closing file '%s'", procfile_filename(ff));
143
144 - if(likely(ff->lines)) pflines_free(ff->lines);
145 - if(likely(ff->words)) pfwords_free(ff->words);
144 + if(likely(ff->lines)) procfile_lines_free(ff->lines);
145 + if(likely(ff->words)) procfile_words_free(ff->words);
146
147 if(likely(ff->fd != -1)) close(ff->fd);
148 freez(ff);
@@ -162,7 +162,7 @@ static void procfile_parser(procfile *ff) {
162 char quote = 0; // the quote character - only when in quoted string
163 size_t opened = 0; // counts the number of open parenthesis
164
165 - size_t *line_words = pflines_add(ff);
165 + size_t *line_words = procfile_lines_add(ff);
166
167 while(s < e) {
168 PF_CHAR_TYPE ct = separators[(unsigned char)(*s)];
@@ -177,7 +177,7 @@ static void procfile_parser(procfile *ff) {
177 if (s != t) {
178 // separator, but we have word before it
179 *s = '\0';
180 - pfwords_add(ff, t);
180 + procfile_words_add(ff, t);
181 (*line_words)++;
182 t = ++s;
183 }
@@ -196,13 +196,13 @@ static void procfile_parser(procfile *ff) {
196 // end of line
197
198 *s = '\0';
199 - pfwords_add(ff, t);
199 + procfile_words_add(ff, t);
200 (*line_words)++;
201 t = ++s;
202
203 // debug(D_PROCFILE, PF_PREFIX ": ended line %d with %d words", l, ff->lines->lines[l].words);
204
205 - line_words = pflines_add(ff);
205 + line_words = procfile_lines_add(ff);
206 }
207 else if(likely(ct == PF_CHAR_IS_QUOTE)) {
208 if(unlikely(!quote && s == t)) {
@@ -215,7 +215,7 @@ static void procfile_parser(procfile *ff) {
215 quote = 0;
216
217 *s = '\0';
218 - pfwords_add(ff, t);
218 + procfile_words_add(ff, t);
219 (*line_words)++;
220 t = ++s;
221 }
@@ -240,7 +240,7 @@ static void procfile_parser(procfile *ff) {
240
241 if(!opened) {
242 *s = '\0';
243 - pfwords_add(ff, t);
243 + procfile_words_add(ff, t);
244 (*line_words)++;
245 t = ++s;
246 }
@@ -262,7 +262,7 @@ static void procfile_parser(procfile *ff) {
262 }
263
264 *s = '\0';
265 - pfwords_add(ff, t);
265 + procfile_words_add(ff, t);
266 (*line_words)++;
267 // t = ++s;
268 }
@@ -305,8 +305,8 @@ procfile *procfile_readall(procfile *ff) {
305 return NULL;
306 }
307
308 - pflines_reset(ff->lines);
309 - pfwords_reset(ff->words);
308 + procfile_lines_reset(ff->lines);
309 + procfile_words_reset(ff->words);
310 procfile_parser(ff);
311
312 if(unlikely(procfile_adaptive_initial_allocation)) {
@@ -423,8 +423,8 @@ procfile *procfile_open(const char *filename, const char *separators, uint32_t f
423 ff->len = 0;
424 ff->flags = flags;
425
426 - ff->lines = pflines_new();
427 - ff->words = pfwords_new();
426 + ff->lines = procfile_lines_create();
427 + ff->words = procfile_words_create();
428
429 procfile_set_separators(ff, separators);
430
streaming/receiver.c
+3 -6
@@ -487,13 +487,10 @@ static int rrdpush_receive(struct receiver_state *rpt)
487 mode = rrd_memory_mode_id(appconfig_get(&stream_config, rpt->key, "default memory mode", rrd_memory_mode_name(mode)));
488 mode = rrd_memory_mode_id(appconfig_get(&stream_config, rpt->machine_guid, "memory mode", rrd_memory_mode_name(mode)));
489
490 -#ifndef ENABLE_DBENGINE
491 - if (unlikely(mode == RRD_MEMORY_MODE_DBENGINE)) {
492 - close(rpt->fd);
493 - log_stream_connection(rpt->client_ip, rpt->client_port, rpt->key, rpt->machine_guid, rpt->hostname, "REJECTED -- DBENGINE MEMORY MODE NOT SUPPORTED");
494 - return 1;
490 + if (unlikely(mode == RRD_MEMORY_MODE_DBENGINE && !dbengine_enabled)) {
491 + error("STREAM %s [receive from %s:%s]: dbengine is not enabled, falling back to default.", rpt->hostname, rpt->client_ip, rpt->client_port);
492 + mode = default_rrd_memory_mode;
493 }
496 -#endif
494
495 health_enabled = appconfig_get_boolean_ondemand(&stream_config, rpt->key, "health enabled by default", health_enabled);
496 health_enabled = appconfig_get_boolean_ondemand(&stream_config, rpt->machine_guid, "health enabled", health_enabled);
streaming/rrdpush.c
+25
@@ -66,6 +66,31 @@ static void load_stream_conf() {
66 freez(filename);
67 }
68
69 +bool rrdpush_receiver_needs_dbengine() {
70 + struct section *co;
71 +
72 + for(co = stream_config.first_section; co; co = co->next) {
73 + if(strcmp(co->name, "stream") == 0)
74 + continue; // the first section is not relevant
75 +
76 + char *s;
77 +
78 + s = appconfig_get_by_section(co, "enabled", NULL);
79 + if(!s || !appconfig_test_boolean_value(s))
80 + continue;
81 +
82 + s = appconfig_get_by_section(co, "default memory mode", NULL);
83 + if(s && strcmp(s, "dbengine") == 0)
84 + return true;
85 +
86 + s = appconfig_get_by_section(co, "memory mode", NULL);
87 + if(s && strcmp(s, "dbengine") == 0)
88 + return true;
89 + }
90 +
91 + return false;
92 +}
93 +
94 int rrdpush_init() {
95 // --------------------------------------------------------------------
96 // load stream.conf
streaming/rrdpush.h
+1
@@ -228,6 +228,7 @@ BUFFER *sender_start(struct sender_state *s);
228 void sender_commit(struct sender_state *s, BUFFER *wb);
229 void sender_cancel(struct sender_state *s);
230 int rrdpush_init();
231 +bool rrdpush_receiver_needs_dbengine();
232 int configured_as_parent();
233 void rrdset_done_push(RRDSET *st);
234 bool rrdset_push_chart_definition_now(RRDSET *st);
web/api/web_api_v1.c
+6 -1
@@ -1621,9 +1621,14 @@ int web_client_api_request_v1_dbengine_stats(RRDHOST *host __maybe_unused, struc
1621
1622 BUFFER *wb = w->response.data;
1623 buffer_flush(wb);
1624 +
1625 + if(!dbengine_enabled) {
1626 + buffer_strcat(wb, "dbengine is not enabled");
1627 + return HTTP_RESP_NOT_FOUND;
1628 + }
1629 +
1630 wb->contenttype = CT_APPLICATION_JSON;
1631 buffer_no_cacheable(wb);
1626 -
1632 buffer_strcat(wb, "{");
1633 for(int tier = 0; tier < storage_tiers ;tier++) {
1634 buffer_sprintf(wb, "%s\n\t\"tier%d\": {", tier?",":"", tier);