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);