summaryrefslogtreecommitdiffstats
path: root/libnetdata
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2023-05-08 16:27:08 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2023-05-08 16:27:08 +0000
commit81581f9719bc56f01d5aa08952671d65fda9867a (patch)
tree0f5c6b6138bf169c23c9d24b1fc0a3521385cb18 /libnetdata
parentReleasing debian version 1.38.1-1. (diff)
downloadnetdata-81581f9719bc56f01d5aa08952671d65fda9867a.tar.xz
netdata-81581f9719bc56f01d5aa08952671d65fda9867a.zip
Merging upstream version 1.39.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libnetdata')
-rw-r--r--libnetdata/Makefile.am1
-rw-r--r--libnetdata/adaptive_resortable_list/README.md2
-rw-r--r--libnetdata/adaptive_resortable_list/adaptive_resortable_list.c2
-rw-r--r--libnetdata/aral/README.md2
-rw-r--r--libnetdata/aral/aral.c16
-rw-r--r--libnetdata/avl/README.md2
-rw-r--r--libnetdata/buffer/README.md2
-rw-r--r--libnetdata/buffer/buffer.c466
-rw-r--r--libnetdata/buffer/buffer.h926
-rw-r--r--libnetdata/circular_buffer/README.md2
-rw-r--r--libnetdata/circular_buffer/circular_buffer.c5
-rw-r--r--libnetdata/clocks/README.md9
-rw-r--r--libnetdata/config/README.md2
-rw-r--r--libnetdata/config/appconfig.c17
-rw-r--r--libnetdata/config/appconfig.h1
-rw-r--r--libnetdata/dictionary/README.md2
-rw-r--r--libnetdata/dictionary/dictionary.c282
-rw-r--r--libnetdata/dictionary/dictionary.h12
-rw-r--r--libnetdata/ebpf/README.md4
-rw-r--r--libnetdata/ebpf/ebpf.c99
-rw-r--r--libnetdata/ebpf/ebpf.h32
-rw-r--r--libnetdata/health/health.c10
-rw-r--r--libnetdata/inlined.h460
-rw-r--r--libnetdata/json/README.md2
-rw-r--r--libnetdata/july/README.md2
-rw-r--r--libnetdata/libjudy/src/JudyL/JudyLTablesGen.c11
-rw-r--r--libnetdata/libnetdata.c336
-rw-r--r--libnetdata/libnetdata.h244
-rw-r--r--libnetdata/locks/README.md4
-rw-r--r--libnetdata/log/README.md4
-rw-r--r--libnetdata/log/log.c25
-rw-r--r--libnetdata/onewayalloc/README.md4
-rw-r--r--libnetdata/onewayalloc/onewayalloc.c9
-rw-r--r--libnetdata/parser/Makefile.am9
-rw-r--r--libnetdata/parser/README.md28
-rw-r--r--libnetdata/parser/parser.c225
-rw-r--r--libnetdata/parser/parser.h101
-rw-r--r--libnetdata/popen/README.md2
-rw-r--r--libnetdata/procfile/README.md2
-rw-r--r--libnetdata/simple_pattern/README.md2
-rw-r--r--libnetdata/simple_pattern/simple_pattern.c146
-rw-r--r--libnetdata/simple_pattern/simple_pattern.h26
-rw-r--r--libnetdata/socket/README.md7
-rw-r--r--libnetdata/socket/socket.c79
-rw-r--r--libnetdata/socket/socket.h6
-rw-r--r--libnetdata/statistical/README.md2
-rw-r--r--libnetdata/storage_number/README.md2
-rw-r--r--libnetdata/storage_number/storage_number.c142
-rw-r--r--libnetdata/storage_number/storage_number.h90
-rw-r--r--libnetdata/string/README.md2
-rw-r--r--libnetdata/string/string.c8
-rw-r--r--libnetdata/string/string.h1
-rw-r--r--libnetdata/string/utf8.h4
-rw-r--r--libnetdata/threads/README.md2
-rw-r--r--libnetdata/threads/threads.c74
-rw-r--r--libnetdata/threads/threads.h2
-rw-r--r--libnetdata/url/README.md2
-rw-r--r--libnetdata/url/url.c180
-rw-r--r--libnetdata/url/url.h8
-rw-r--r--libnetdata/worker_utilization/README.md6
-rw-r--r--libnetdata/worker_utilization/worker_utilization.c16
61 files changed, 2964 insertions, 1207 deletions
diff --git a/libnetdata/Makefile.am b/libnetdata/Makefile.am
index b81d620ba..4bf779136 100644
--- a/libnetdata/Makefile.am
+++ b/libnetdata/Makefile.am
@@ -20,6 +20,7 @@ SUBDIRS = \
locks \
log \
onewayalloc \
+ parser \
popen \
procfile \
simple_pattern \
diff --git a/libnetdata/adaptive_resortable_list/README.md b/libnetdata/adaptive_resortable_list/README.md
index 957578487..ceed467d6 100644
--- a/libnetdata/adaptive_resortable_list/README.md
+++ b/libnetdata/adaptive_resortable_list/README.md
@@ -4,7 +4,7 @@ custom_edit_url: https://github.com/netdata/netdata/edit/master/libnetdata/adapt
sidebar_label: "Adaptive Re-sortable List (ARL)"
learn_status: "Published"
learn_topic_type: "Tasks"
-learn_rel_path: "Developers/libnetdata libraries"
+learn_rel_path: "Developers/libnetdata"
-->
# Adaptive Re-sortable List (ARL)
diff --git a/libnetdata/adaptive_resortable_list/adaptive_resortable_list.c b/libnetdata/adaptive_resortable_list/adaptive_resortable_list.c
index 7f4c6c53d..6332fa174 100644
--- a/libnetdata/adaptive_resortable_list/adaptive_resortable_list.c
+++ b/libnetdata/adaptive_resortable_list/adaptive_resortable_list.c
@@ -9,7 +9,7 @@ inline void arl_callback_str2ull(const char *name, uint32_t hash, const char *va
(void)hash;
register unsigned long long *d = dst;
- *d = str2ull(value);
+ *d = str2ull(value, NULL);
// fprintf(stderr, "name '%s' with hash %u and value '%s' is %llu\n", name, hash, value, *d);
}
diff --git a/libnetdata/aral/README.md b/libnetdata/aral/README.md
index 3b0f5bbd6..e556144ba 100644
--- a/libnetdata/aral/README.md
+++ b/libnetdata/aral/README.md
@@ -4,7 +4,7 @@ custom_edit_url: https://github.com/netdata/netdata/edit/master/libnetdata/aral/
sidebar_label: "Array allocator"
learn_status: "Published"
learn_topic_type: "Tasks"
-learn_rel_path: "Developers/libnetdata libraries"
+learn_rel_path: "Developers/libnetdata"
-->
# Array Allocator
diff --git a/libnetdata/aral/aral.c b/libnetdata/aral/aral.c
index 4505ee0f2..60fe5e39a 100644
--- a/libnetdata/aral/aral.c
+++ b/libnetdata/aral/aral.c
@@ -465,6 +465,9 @@ static inline ARAL_PAGE *aral_acquire_a_free_slot(ARAL *ar TRACE_ALLOCATIONS_FUN
}
void *aral_mallocz_internal(ARAL *ar TRACE_ALLOCATIONS_FUNCTION_DEFINITION_PARAMS) {
+#ifdef FSANITIZE_ADDRESS
+ return mallocz(ar->config.requested_element_size);
+#endif
ARAL_PAGE *page = aral_acquire_a_free_slot(ar TRACE_ALLOCATIONS_FUNCTION_CALL_PARAMS);
@@ -614,6 +617,11 @@ static inline void aral_move_page_with_free_list___aral_lock_needed(ARAL *ar, AR
}
void aral_freez_internal(ARAL *ar, void *ptr TRACE_ALLOCATIONS_FUNCTION_DEFINITION_PARAMS) {
+#ifdef FSANITIZE_ADDRESS
+ freez(ptr);
+ return;
+#endif
+
if(unlikely(!ptr)) return;
// get the page pointer
@@ -877,10 +885,10 @@ void aral_by_size_release(ARAL *ar) {
fatal("ARAL BY SIZE: double release detected");
aral_by_size_globals.array[size].refcount--;
- if(!aral_by_size_globals.array[size].refcount) {
- aral_destroy(aral_by_size_globals.array[size].ar);
- aral_by_size_globals.array[size].ar = NULL;
- }
+// if(!aral_by_size_globals.array[size].refcount) {
+// aral_destroy(aral_by_size_globals.array[size].ar);
+// aral_by_size_globals.array[size].ar = NULL;
+// }
netdata_spinlock_unlock(&aral_by_size_globals.spinlock);
}
diff --git a/libnetdata/avl/README.md b/libnetdata/avl/README.md
index 2b03fec4a..94c0f634f 100644
--- a/libnetdata/avl/README.md
+++ b/libnetdata/avl/README.md
@@ -4,7 +4,7 @@ custom_edit_url: https://github.com/netdata/netdata/edit/master/libnetdata/avl/R
sidebar_label: "AVL"
learn_status: "Published"
learn_topic_type: "Tasks"
-learn_rel_path: "Developers/libnetdata libraries"
+learn_rel_path: "Developers/libnetdata"
-->
# AVL
diff --git a/libnetdata/buffer/README.md b/libnetdata/buffer/README.md
index 6a84fd8a3..2937ae141 100644
--- a/libnetdata/buffer/README.md
+++ b/libnetdata/buffer/README.md
@@ -4,7 +4,7 @@ custom_edit_url: https://github.com/netdata/netdata/edit/master/libnetdata/buffe
sidebar_label: "BUFFER library"
learn_status: "Published"
learn_topic_type: "Tasks"
-learn_rel_path: "Developers/libnetdata libraries"
+learn_rel_path: "Developers/libnetdata"
-->
# BUFFER
diff --git a/libnetdata/buffer/buffer.c b/libnetdata/buffer/buffer.c
index eeb283209..142fbca14 100644
--- a/libnetdata/buffer/buffer.c
+++ b/libnetdata/buffer/buffer.c
@@ -2,39 +2,16 @@
#include "../libnetdata.h"
-#define BUFFER_OVERFLOW_EOF "EOF"
-
static inline void buffer_overflow_init(BUFFER *b)
{
b->buffer[b->size] = '\0';
strcpy(&b->buffer[b->size + 1], BUFFER_OVERFLOW_EOF);
}
-#ifdef NETDATA_INTERNAL_CHECKS
-#define buffer_overflow_check(b) _buffer_overflow_check(b, __FILE__, __FUNCTION__, __LINE__)
-#else
-#define buffer_overflow_check(b)
-#endif
-
-static inline void _buffer_overflow_check(BUFFER *b, const char *file, const char *function, const unsigned long line)
-{
- if(b->len > b->size) {
- error("BUFFER: length %zu is above size %zu, at line %lu, at function %s() of file '%s'.", b->len, b->size, line, function, file);
- b->len = b->size;
- }
-
- if(b->buffer[b->size] != '\0' || strcmp(&b->buffer[b->size + 1], BUFFER_OVERFLOW_EOF) != 0) {
- error("BUFFER: detected overflow at line %lu, at function %s() of file '%s'.", line, function, file);
- buffer_overflow_init(b);
- }
-}
-
-
-void buffer_reset(BUFFER *wb)
-{
+void buffer_reset(BUFFER *wb) {
buffer_flush(wb);
- wb->contenttype = CT_TEXT_PLAIN;
+ wb->content_type = CT_TEXT_PLAIN;
wb->options = 0;
wb->date = 0;
wb->expires = 0;
@@ -52,8 +29,7 @@ const char *buffer_tostring(BUFFER *wb)
return(wb->buffer);
}
-void buffer_char_replace(BUFFER *wb, char from, char to)
-{
+void buffer_char_replace(BUFFER *wb, char from, char to) {
char *s = wb->buffer, *end = &wb->buffer[wb->len];
while(s != end) {
@@ -64,212 +40,25 @@ void buffer_char_replace(BUFFER *wb, char from, char to)
buffer_overflow_check(wb);
}
-// This trick seems to give an 80% speed increase in 32bit systems
-// print_number_llu_r() will just print the digits up to the
-// point the remaining value fits in 32 bits, and then calls
-// print_number_lu_r() to print the rest with 32 bit arithmetic.
-
-inline char *print_number_lu_r(char *str, unsigned long uvalue) {
- char *wstr = str;
-
- // print each digit
- do *wstr++ = (char)('0' + (uvalue % 10)); while(uvalue /= 10);
- return wstr;
-}
-
-inline char *print_number_llu_r(char *str, unsigned long long uvalue) {
- char *wstr = str;
-
- // print each digit
- do *wstr++ = (char)('0' + (uvalue % 10)); while((uvalue /= 10) && uvalue > (unsigned long long)0xffffffff);
- if(uvalue) return print_number_lu_r(wstr, uvalue);
- return wstr;
-}
-
-inline char *print_number_llu_r_smart(char *str, unsigned long long uvalue) {
- switch (sizeof(void *)) {
- case 4:
- str = (uvalue > (unsigned long long) 0xffffffff) ? print_number_llu_r(str, uvalue) :
- print_number_lu_r(str, uvalue);
- break;
- case 8:
- do {
- *str++ = (char) ('0' + (uvalue % 10));
- } while (uvalue /= 10);
- break;
- default:
- fatal("Netdata supports only 32-bit & 64-bit systems.");
- }
-
- return str;
-}
-
-void buffer_print_llu(BUFFER *wb, unsigned long long uvalue)
-{
- buffer_need_bytes(wb, 50);
-
- char *str = &wb->buffer[wb->len];
- char *wstr = str;
-
- switch (sizeof(void *)) {
- case 4:
- wstr = (uvalue > (unsigned long long) 0xffffffff) ? print_number_llu_r(wstr, uvalue) :
- print_number_lu_r(wstr, uvalue);
- break;
- case 8:
- do {
- *wstr++ = (char) ('0' + (uvalue % 10));
- } while (uvalue /= 10);
- break;
- default:
- fatal("Netdata supports only 32-bit & 64-bit systems.");
+void buffer_print_sn_flags(BUFFER *wb, SN_FLAGS flags, bool send_anomaly_bit) {
+ if(unlikely(flags == SN_EMPTY_SLOT)) {
+ buffer_fast_strcat(wb, "E", 1);
+ return;
}
- // terminate it
- *wstr = '\0';
-
- // reverse it
- char *begin = str, *end = wstr - 1, aux;
- while (end > begin) aux = *end, *end-- = *begin, *begin++ = aux;
-
- // return the buffer length
- wb->len += wstr - str;
-}
-
-void buffer_print_ll(BUFFER *wb, long long value)
-{
- buffer_need_bytes(wb, 50);
-
- if(value < 0) {
- buffer_fast_strcat(wb, "-", 1);
- value = -value;
+ size_t printed = 0;
+ if(likely(send_anomaly_bit && (flags & SN_FLAG_NOT_ANOMALOUS))) {
+ buffer_fast_strcat(wb, "A", 1);
+ printed++;
}
- buffer_print_llu(wb, value);
-}
-
-static unsigned char bits03_to_hex[16] = {
- [0] = '0',
- [1] = '1',
- [2] = '2',
- [3] = '3',
- [4] = '4',
- [5] = '5',
- [6] = '6',
- [7] = '7',
- [8] = '8',
- [9] = '9',
- [10] = 'A',
- [11] = 'B',
- [12] = 'C',
- [13] = 'D',
- [14] = 'E',
- [15] = 'F'
-};
-
-void buffer_print_llu_hex(BUFFER *wb, unsigned long long value)
-{
- unsigned char buffer[sizeof(unsigned long long) * 2 + 2 + 1]; // 8 bytes * 2 + '0x' + '\0'
- unsigned char *e = &buffer[sizeof(unsigned long long) * 2 + 2];
- unsigned char *p = e;
-
- *p-- = '\0';
- *p-- = bits03_to_hex[value & 0xF];
- value >>= 4;
- if(value) {
- *p-- = bits03_to_hex[value & 0xF];
- value >>= 4;
-
- while(value) {
- *p-- = bits03_to_hex[value & 0xF];
- value >>= 4;
-
- if(value) {
- *p-- = bits03_to_hex[value & 0xF];
- value >>= 4;
- }
- }
+ if(unlikely(flags & SN_FLAG_RESET)) {
+ buffer_fast_strcat(wb, "R", 1);
+ printed++;
}
- *p-- = 'x';
- *p = '0';
- buffer_fast_strcat(wb, (char *)p, e - p);
-}
-
-void buffer_fast_strcat(BUFFER *wb, const char *txt, size_t len) {
- if(unlikely(!txt || !*txt)) return;
-
- buffer_need_bytes(wb, len + 1);
-
- char *s = &wb->buffer[wb->len];
- const char *end = &txt[len + 1];
-
- while(txt != end)
- *s++ = *txt++;
-
- wb->len += len;
-
- // keep it NULL terminating
- // not counting it at wb->len
- wb->buffer[wb->len] = '\0';
-}
-
-void buffer_strcat(BUFFER *wb, const char *txt)
-{
- // buffer_sprintf(wb, "%s", txt);
-
- if(unlikely(!txt || !*txt)) return;
-
- buffer_need_bytes(wb, 1);
-
- char *s = &wb->buffer[wb->len], *start, *end = &wb->buffer[wb->size];
- size_t len = wb->len;
-
- start = s;
- while(*txt && s != end)
- *s++ = *txt++;
-
- len += s - start;
-
- wb->len = len;
- buffer_overflow_check(wb);
-
- if(*txt) {
- debug(D_WEB_BUFFER, "strcat(): increasing web_buffer at position %zu, size = %zu\n", wb->len, wb->size);
- len = strlen(txt);
- buffer_fast_strcat(wb, txt, len);
- }
- else {
- // terminate the string
- // without increasing the length
- buffer_need_bytes(wb, (size_t)1);
- wb->buffer[wb->len] = '\0';
- }
-}
-
-void buffer_strcat_jsonescape(BUFFER *wb, const char *txt)
-{
- while(*txt) {
- switch(*txt) {
- case '\\':
- buffer_need_bytes(wb, 2);
- wb->buffer[wb->len++] = '\\';
- wb->buffer[wb->len++] = '\\';
- break;
- case '"':
- buffer_need_bytes(wb, 2);
- wb->buffer[wb->len++] = '\\';
- wb->buffer[wb->len++] = '"';
- break;
- default: {
- buffer_need_bytes(wb, 1);
- wb->buffer[wb->len++] = *txt;
- }
- }
- txt++;
- }
-
- buffer_overflow_check(wb);
+ if(!printed)
+ buffer_fast_strcat(wb, "''", 2);
}
void buffer_strcat_htmlescape(BUFFER *wb, const char *txt)
@@ -358,25 +147,6 @@ void buffer_sprintf(BUFFER *wb, const char *fmt, ...)
// the buffer is \0 terminated by vsnprintf
}
-
-void buffer_rrd_value(BUFFER *wb, NETDATA_DOUBLE value)
-{
- buffer_need_bytes(wb, 50);
-
- if(isnan(value) || isinf(value)) {
- buffer_strcat(wb, "null");
- return;
- }
- else
- wb->len += print_netdata_double(&wb->buffer[wb->len], value);
-
- // terminate it
- buffer_need_bytes(wb, 1);
- wb->buffer[wb->len] = '\0';
-
- buffer_overflow_check(wb);
-}
-
// generate a javascript date, the fastest possible way...
void buffer_jsdate(BUFFER *wb, int year, int month, int day, int hours, int minutes, int seconds)
{
@@ -482,7 +252,7 @@ BUFFER *buffer_create(size_t size, size_t *statistics)
b->buffer = mallocz(size + sizeof(BUFFER_OVERFLOW_EOF) + 2);
b->buffer[0] = '\0';
b->size = size;
- b->contenttype = CT_TEXT_PLAIN;
+ b->content_type = CT_TEXT_PLAIN;
b->statistics = statistics;
buffer_overflow_init(b);
buffer_overflow_check(b);
@@ -531,3 +301,205 @@ void buffer_increase(BUFFER *b, size_t free_size_required) {
buffer_overflow_init(b);
buffer_overflow_check(b);
}
+
+// ----------------------------------------------------------------------------
+
+void buffer_json_initialize(BUFFER *wb, const char *key_quote, const char *value_quote, int depth,
+ bool add_anonymous_object, bool minify) {
+ strncpyz(wb->json.key_quote, key_quote, BUFFER_QUOTE_MAX_SIZE);
+ strncpyz(wb->json.value_quote, value_quote, BUFFER_QUOTE_MAX_SIZE);
+
+ wb->json.minify = minify;
+ wb->json.depth = (int8_t)(depth - 1);
+ _buffer_json_depth_push(wb, BUFFER_JSON_OBJECT);
+
+ if(add_anonymous_object)
+ buffer_fast_strcat(wb, "{", 1);
+}
+
+void buffer_json_finalize(BUFFER *wb) {
+ while(wb->json.depth >= 0) {
+ switch(wb->json.stack[wb->json.depth].type) {
+ case BUFFER_JSON_OBJECT:
+ buffer_json_object_close(wb);
+ break;
+
+ case BUFFER_JSON_ARRAY:
+ buffer_json_array_close(wb);
+ break;
+
+ default:
+ internal_fatal(true, "BUFFER: unknown json member type in stack");
+ break;
+ }
+ }
+
+ if(!wb->json.minify)
+ buffer_fast_strcat(wb, "\n", 1);
+}
+
+// ----------------------------------------------------------------------------
+
+const char hex_digits[16] = "0123456789ABCDEF";
+const char base64_digits[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+unsigned char hex_value_from_ascii[256];
+unsigned char base64_value_from_ascii[256];
+
+__attribute__((constructor)) void initialize_ascii_maps(void) {
+ for(size_t i = 0 ; i < 256 ; i++) {
+ hex_value_from_ascii[i] = 255;
+ base64_value_from_ascii[i] = 255;
+ }
+
+ for(size_t i = 0; i < 16 ; i++)
+ hex_value_from_ascii[(int)hex_digits[i]] = i;
+
+ for(size_t i = 0; i < 64 ; i++)
+ base64_value_from_ascii[(int)base64_digits[i]] = i;
+}
+
+// ----------------------------------------------------------------------------
+// unit test
+
+static int buffer_expect(BUFFER *wb, const char *expected) {
+ const char *generated = buffer_tostring(wb);
+
+ if(strcmp(generated, expected) != 0) {
+ error("BUFFER: mismatch.\nGenerated:\n%s\nExpected:\n%s\n",
+ generated, expected);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int buffer_uint64_roundtrip(BUFFER *wb, NUMBER_ENCODING encoding, uint64_t value, const char *expected) {
+ int errors = 0;
+ buffer_flush(wb);
+ buffer_print_uint64_encoded(wb, encoding, value);
+
+ if(expected)
+ errors += buffer_expect(wb, expected);
+
+ uint64_t v = str2ull_encoded(buffer_tostring(wb));
+ if(v != value) {
+ error("BUFFER: string '%s' does resolves to %llu, expected %llu",
+ buffer_tostring(wb), (unsigned long long)v, (unsigned long long)value);
+ errors++;
+ }
+ buffer_flush(wb);
+ return errors;
+}
+
+static int buffer_int64_roundtrip(BUFFER *wb, NUMBER_ENCODING encoding, int64_t value, const char *expected) {
+ int errors = 0;
+ buffer_flush(wb);
+ buffer_print_int64_encoded(wb, encoding, value);
+
+ if(expected)
+ errors += buffer_expect(wb, expected);
+
+ int64_t v = str2ll_encoded(buffer_tostring(wb));
+ if(v != value) {
+ error("BUFFER: string '%s' does resolves to %lld, expected %lld",
+ buffer_tostring(wb), (long long)v, (long long)value);
+ errors++;
+ }
+ buffer_flush(wb);
+ return errors;
+}
+
+static int buffer_double_roundtrip(BUFFER *wb, NUMBER_ENCODING encoding, NETDATA_DOUBLE value, const char *expected) {
+ int errors = 0;
+ buffer_flush(wb);
+ buffer_print_netdata_double_encoded(wb, encoding, value);
+
+ if(expected)
+ errors += buffer_expect(wb, expected);
+
+ NETDATA_DOUBLE v = str2ndd_encoded(buffer_tostring(wb), NULL);
+ if(v != value) {
+ error("BUFFER: string '%s' does resolves to %.12f, expected %.12f",
+ buffer_tostring(wb), v, value);
+ errors++;
+ }
+ buffer_flush(wb);
+ return errors;
+}
+
+int buffer_unittest(void) {
+ int errors = 0;
+ BUFFER *wb = buffer_create(0, NULL);
+
+ buffer_uint64_roundtrip(wb, NUMBER_ENCODING_DECIMAL, 0, "0");
+ buffer_uint64_roundtrip(wb, NUMBER_ENCODING_HEX, 0, "0x0");
+ buffer_uint64_roundtrip(wb, NUMBER_ENCODING_BASE64, 0, "#A");
+
+ buffer_uint64_roundtrip(wb, NUMBER_ENCODING_DECIMAL, 1676071986, "1676071986");
+ buffer_uint64_roundtrip(wb, NUMBER_ENCODING_HEX, 1676071986, "0x63E6D432");
+ buffer_uint64_roundtrip(wb, NUMBER_ENCODING_BASE64, 1676071986, "#Bj5tQy");
+
+ buffer_uint64_roundtrip(wb, NUMBER_ENCODING_DECIMAL, 18446744073709551615ULL, "18446744073709551615");
+ buffer_uint64_roundtrip(wb, NUMBER_ENCODING_HEX, 18446744073709551615ULL, "0xFFFFFFFFFFFFFFFF");
+ buffer_uint64_roundtrip(wb, NUMBER_ENCODING_BASE64, 18446744073709551615ULL, "#P//////////");
+
+ buffer_int64_roundtrip(wb, NUMBER_ENCODING_DECIMAL, 0, "0");
+ buffer_int64_roundtrip(wb, NUMBER_ENCODING_HEX, 0, "0x0");
+ buffer_int64_roundtrip(wb, NUMBER_ENCODING_BASE64, 0, "#A");
+
+ buffer_int64_roundtrip(wb, NUMBER_ENCODING_DECIMAL, -1676071986, "-1676071986");
+ buffer_int64_roundtrip(wb, NUMBER_ENCODING_HEX, -1676071986, "-0x63E6D432");
+ buffer_int64_roundtrip(wb, NUMBER_ENCODING_BASE64, -1676071986, "-#Bj5tQy");
+
+ buffer_int64_roundtrip(wb, NUMBER_ENCODING_DECIMAL, (int64_t)-9223372036854775807ULL, "-9223372036854775807");
+ buffer_int64_roundtrip(wb, NUMBER_ENCODING_HEX, (int64_t)-9223372036854775807ULL, "-0x7FFFFFFFFFFFFFFF");
+ buffer_int64_roundtrip(wb, NUMBER_ENCODING_BASE64, (int64_t)-9223372036854775807ULL, "-#H//////////");
+
+ buffer_double_roundtrip(wb, NUMBER_ENCODING_DECIMAL, 0, "0");
+ buffer_double_roundtrip(wb, NUMBER_ENCODING_HEX, 0, "%0");
+ buffer_double_roundtrip(wb, NUMBER_ENCODING_BASE64, 0, "@A");
+
+ buffer_double_roundtrip(wb, NUMBER_ENCODING_DECIMAL, 1.5, "1.5");
+ buffer_double_roundtrip(wb, NUMBER_ENCODING_HEX, 1.5, "%3FF8000000000000");
+ buffer_double_roundtrip(wb, NUMBER_ENCODING_BASE64, 1.5, "@D/4AAAAAAAA");
+
+ buffer_double_roundtrip(wb, NUMBER_ENCODING_DECIMAL, 1.23e+14, "123000000000000");
+ buffer_double_roundtrip(wb, NUMBER_ENCODING_HEX, 1.23e+14, "%42DBF78AD3AC0000");
+ buffer_double_roundtrip(wb, NUMBER_ENCODING_BASE64, 1.23e+14, "@ELb94rTrAAA");
+
+ buffer_double_roundtrip(wb, NUMBER_ENCODING_DECIMAL, 9.12345678901234567890123456789e+45, "9.123456789012346128e+45");
+ buffer_double_roundtrip(wb, NUMBER_ENCODING_HEX, 9.12345678901234567890123456789e+45, "%497991C25C9E4309");
+ buffer_double_roundtrip(wb, NUMBER_ENCODING_BASE64, 9.12345678901234567890123456789e+45, "@El5kcJcnkMJ");
+
+ buffer_flush(wb);
+
+ {
+ char buf[1024 + 1];
+ for(size_t i = 0; i < 1024 ;i++)
+ buf[i] = (char)(i % 26) + 'A';
+ buf[1024] = '\0';
+
+ buffer_strcat(wb, buf);
+ errors += buffer_expect(wb, buf);
+ }
+
+ buffer_flush(wb);
+
+ buffer_json_initialize(wb, "\"", "\"", 0, true, false);
+ buffer_json_finalize(wb);
+ errors += buffer_expect(wb, "{\n}\n");
+
+ buffer_flush(wb);
+
+ buffer_json_initialize(wb, "\"", "\"", 0, true, false);
+ buffer_json_member_add_string(wb, "hello", "world");
+ buffer_json_member_add_string(wb, "alpha", "this: \" is a double quote");
+ buffer_json_member_add_object(wb, "object1");
+ buffer_json_member_add_string(wb, "hello", "world");
+ buffer_json_finalize(wb);
+ errors += buffer_expect(wb, "{\n \"hello\":\"world\",\n \"alpha\":\"this: \\\" is a double quote\",\n \"object1\":{\n \"hello\":\"world\"\n }\n}\n");
+
+ buffer_free(wb);
+ return errors;
+}
+
diff --git a/libnetdata/buffer/buffer.h b/libnetdata/buffer/buffer.h
index 0fa3495b4..f5f83bc2a 100644
--- a/libnetdata/buffer/buffer.h
+++ b/libnetdata/buffer/buffer.h
@@ -3,48 +3,80 @@
#ifndef NETDATA_WEB_BUFFER_H
#define NETDATA_WEB_BUFFER_H 1
+#include "../string/utf8.h"
#include "../libnetdata.h"
#define WEB_DATA_LENGTH_INCREASE_STEP 1024
+#define BUFFER_JSON_MAX_DEPTH 32 // max is 255
+
+extern const char hex_digits[16];
+extern const char base64_digits[64];
+extern unsigned char hex_value_from_ascii[256];
+extern unsigned char base64_value_from_ascii[256];
+
+typedef enum __attribute__ ((__packed__)) {
+ BUFFER_JSON_EMPTY = 0,
+ BUFFER_JSON_OBJECT,
+ BUFFER_JSON_ARRAY,
+} BUFFER_JSON_NODE_TYPE;
+
+typedef struct web_buffer_json_node {
+ BUFFER_JSON_NODE_TYPE type;
+ uint32_t count:24;
+} BUFFER_JSON_NODE;
+
+#define BUFFER_QUOTE_MAX_SIZE 7
+
+typedef enum __attribute__ ((__packed__)) {
+ WB_CONTENT_CACHEABLE = (1 << 0),
+ WB_CONTENT_NO_CACHEABLE = (1 << 1),
+} BUFFER_OPTIONS;
+
+typedef enum __attribute__ ((__packed__)) {
+ CT_NONE = 0,
+ CT_APPLICATION_JSON,
+ CT_TEXT_PLAIN,
+ CT_TEXT_HTML,
+ CT_APPLICATION_X_JAVASCRIPT,
+ CT_TEXT_CSS,
+ CT_TEXT_XML,
+ CT_APPLICATION_XML,
+ CT_TEXT_XSL,
+ CT_APPLICATION_OCTET_STREAM,
+ CT_APPLICATION_X_FONT_TRUETYPE,
+ CT_APPLICATION_X_FONT_OPENTYPE,
+ CT_APPLICATION_FONT_WOFF,
+ CT_APPLICATION_FONT_WOFF2,
+ CT_APPLICATION_VND_MS_FONTOBJ,
+ CT_IMAGE_SVG_XML,
+ CT_IMAGE_PNG,
+ CT_IMAGE_JPG,
+ CT_IMAGE_GIF,
+ CT_IMAGE_XICON,
+ CT_IMAGE_ICNS,
+ CT_IMAGE_BMP,
+ CT_PROMETHEUS,
+} HTTP_CONTENT_TYPE;
+
typedef struct web_buffer {
- size_t size; // allocation size of buffer, in bytes
- size_t len; // current data length in buffer, in bytes
- char *buffer; // the buffer itself
- uint8_t contenttype; // the content type of the data in the buffer
- uint8_t options; // options related to the content
- time_t date; // the timestamp this content has been generated
- time_t expires; // the timestamp this content expires
+ size_t size; // allocation size of buffer, in bytes
+ size_t len; // current data length in buffer, in bytes
+ char *buffer; // the buffer itself
+ HTTP_CONTENT_TYPE content_type; // the content type of the data in the buffer
+ BUFFER_OPTIONS options; // options related to the content
+ time_t date; // the timestamp this content has been generated
+ time_t expires; // the timestamp this content expires
size_t *statistics;
-} BUFFER;
-// options
-#define WB_CONTENT_CACHEABLE 1
-#define WB_CONTENT_NO_CACHEABLE 2
-
-// content-types
-#define CT_APPLICATION_JSON 1
-#define CT_TEXT_PLAIN 2
-#define CT_TEXT_HTML 3
-#define CT_APPLICATION_X_JAVASCRIPT 4
-#define CT_TEXT_CSS 5
-#define CT_TEXT_XML 6
-#define CT_APPLICATION_XML 7
-#define CT_TEXT_XSL 8
-#define CT_APPLICATION_OCTET_STREAM 9
-#define CT_APPLICATION_X_FONT_TRUETYPE 10
-#define CT_APPLICATION_X_FONT_OPENTYPE 11
-#define CT_APPLICATION_FONT_WOFF 12
-#define CT_APPLICATION_FONT_WOFF2 13
-#define CT_APPLICATION_VND_MS_FONTOBJ 14
-#define CT_IMAGE_SVG_XML 15
-#define CT_IMAGE_PNG 16
-#define CT_IMAGE_JPG 17
-#define CT_IMAGE_GIF 18
-#define CT_IMAGE_XICON 19
-#define CT_IMAGE_ICNS 20
-#define CT_IMAGE_BMP 21
-#define CT_PROMETHEUS 22
+ struct {
+ char key_quote[BUFFER_QUOTE_MAX_SIZE + 1];
+ char value_quote[BUFFER_QUOTE_MAX_SIZE + 1];
+ int8_t depth;
+ bool minify;
+ BUFFER_JSON_NODE stack[BUFFER_JSON_MAX_DEPTH];
+ } json;
+} BUFFER;
#define buffer_cacheable(wb) do { (wb)->options |= WB_CONTENT_CACHEABLE; if((wb)->options & WB_CONTENT_NO_CACHEABLE) (wb)->options &= ~WB_CONTENT_NO_CACHEABLE; } while(0)
#define buffer_no_cacheable(wb) do { (wb)->options |= WB_CONTENT_NO_CACHEABLE; if((wb)->options & WB_CONTENT_CACHEABLE) (wb)->options &= ~WB_CONTENT_CACHEABLE; (wb)->expires = 0; } while(0)
@@ -52,12 +84,34 @@ typedef struct web_buffer {
#define buffer_strlen(wb) ((wb)->len)
const char *buffer_tostring(BUFFER *wb);
-#define buffer_flush(wb) wb->buffer[(wb)->len = 0] = '\0'
-void buffer_reset(BUFFER *wb);
+#define BUFFER_OVERFLOW_EOF "EOF"
+
+#ifdef NETDATA_INTERNAL_CHECKS
+#define buffer_overflow_check(b) _buffer_overflow_check(b)
+#else
+#define buffer_overflow_check(b)
+#endif
+
+static inline void _buffer_overflow_check(BUFFER *b) {
+ assert(b->len <= b->size &&
+ "BUFFER: length is above buffer size.");
+
+ assert(!(b->buffer && (b->buffer[b->size] != '\0' || strcmp(&b->buffer[b->size + 1], BUFFER_OVERFLOW_EOF) != 0)) &&
+ "BUFFER: detected overflow.");
+}
+
+static inline void buffer_flush(BUFFER *wb) {
+ wb->len = 0;
-void buffer_strcat(BUFFER *wb, const char *txt);
-void buffer_fast_strcat(BUFFER *wb, const char *txt, size_t len);
-void buffer_rrd_value(BUFFER *wb, NETDATA_DOUBLE value);
+ wb->json.depth = 0;
+ wb->json.stack[0].type = BUFFER_JSON_EMPTY;
+ wb->json.stack[0].count = 0;
+
+ if(wb->buffer)
+ wb->buffer[0] = '\0';
+}
+
+void buffer_reset(BUFFER *wb);
void buffer_date(BUFFER *wb, int year, int month, int day, int hours, int minutes, int seconds);
void buffer_jsdate(BUFFER *wb, int year, int month, int day, int hours, int minutes, int seconds);
@@ -69,22 +123,794 @@ void buffer_increase(BUFFER *b, size_t free_size_required);
void buffer_snprintf(BUFFER *wb, size_t len, const char *fmt, ...) PRINTFLIKE(3, 4);
void buffer_vsprintf(BUFFER *wb, const char *fmt, va_list args);
void buffer_sprintf(BUFFER *wb, const char *fmt, ...) PRINTFLIKE(2,3);
-void buffer_strcat_jsonescape(BUFFER *wb, const char *txt);
void buffer_strcat_htmlescape(BUFFER *wb, const char *txt);
void buffer_char_replace(BUFFER *wb, char from, char to);
-char *print_number_lu_r(char *str, unsigned long uvalue);
-char *print_number_llu_r(char *str, unsigned long long uvalue);
-char *print_number_llu_r_smart(char *str, unsigned long long uvalue);
-
-void buffer_print_llu(BUFFER *wb, unsigned long long uvalue);
-void buffer_print_ll(BUFFER *wb, long long value);
-void buffer_print_llu_hex(BUFFER *wb, unsigned long long value);
+void buffer_print_sn_flags(BUFFER *wb, SN_FLAGS flags, bool send_anomaly_bit);
static inline void buffer_need_bytes(BUFFER *buffer, size_t needed_free_size) {
- if(unlikely(buffer->size - buffer->len < needed_free_size))
- buffer_increase(buffer, needed_free_size);
+ if(unlikely(buffer->len + needed_free_size >= buffer->size))
+ buffer_increase(buffer, needed_free_size + 1);
+}
+
+void buffer_json_initialize(BUFFER *wb, const char *key_quote, const char *value_quote, int depth,
+ bool add_anonymous_object, bool minify);
+
+void buffer_json_finalize(BUFFER *wb);
+
+static inline void _buffer_json_depth_push(BUFFER *wb, BUFFER_JSON_NODE_TYPE type) {
+#ifdef NETDATA_INTERNAL_CHECKS
+ assert(wb->json.depth <= BUFFER_JSON_MAX_DEPTH && "BUFFER JSON: max nesting reached");
+#endif
+ wb->json.depth++;
+ wb->json.stack[wb->json.depth].count = 0;
+ wb->json.stack[wb->json.depth].type = type;
+}
+
+static inline void _buffer_json_depth_pop(BUFFER *wb) {
+ wb->json.depth--;
+}
+
+static inline void buffer_fast_charcat(BUFFER *wb, const char c) {
+
+ buffer_need_bytes(wb, 2);
+ *(&wb->buffer[wb->len]) = c;
+ wb->len += 1;
+ wb->buffer[wb->len] = '\0';
+
+ buffer_overflow_check(wb);
+}
+
+static inline void buffer_fast_rawcat(BUFFER *wb, const char *txt, size_t len) {
+ if(unlikely(!txt || !*txt || !len)) return;
+
+ buffer_need_bytes(wb, len + 1);
+
+ const char *t = txt;
+ const char *e = &txt[len];
+
+ char *d = &wb->buffer[wb->len];
+
+ while(t != e)
+ *d++ = *t++;
+
+ wb->len += len;
+ wb->buffer[wb->len] = '\0';
+
+ buffer_overflow_check(wb);
+}
+
+static inline void buffer_fast_strcat(BUFFER *wb, const char *txt, size_t len) {
+ if(unlikely(!txt || !*txt || !len)) return;
+
+ buffer_need_bytes(wb, len + 1);
+
+ const char *t = txt;
+ const char *e = &txt[len];
+
+ char *d = &wb->buffer[wb->len];
+
+ while(t != e
+#ifdef NETDATA_INTERNAL_CHECKS
+ && *t
+#endif
+ )
+ *d++ = *t++;
+
+#ifdef NETDATA_INTERNAL_CHECKS
+ assert(!(t != e && !*t) && "BUFFER: source string is shorter than the length given.");
+#endif
+
+ wb->len += len;
+ wb->buffer[wb->len] = '\0';
+
+ buffer_overflow_check(wb);
+}
+
+static inline void buffer_strcat(BUFFER *wb, const char *txt) {
+ if(unlikely(!txt || !*txt)) return;
+
+ const char *t = txt;
+ while(*t) {
+ buffer_need_bytes(wb, 100);
+ char *s = &wb->buffer[wb->len];
+ char *d = s;
+ const char *e = &wb->buffer[wb->size];
+
+ while(*t && d < e)
+ *d++ = *t++;
+
+ wb->len += d - s;
+ }
+
+ buffer_need_bytes(wb, 1);
+ wb->buffer[wb->len] = '\0';
+
+ buffer_overflow_check(wb);
+}
+
+static inline void buffer_strncat(BUFFER *wb, const char *txt, size_t len) {
+ if(unlikely(!txt || !*txt)) return;
+
+ const char *t = txt;
+ while(*t) {
+ buffer_need_bytes(wb, len);
+ char *s = &wb->buffer[wb->len];
+ char *d = s;
+ const char *e = &wb->buffer[wb->len + len];
+
+ while(*t && d < e)
+ *d++ = *t++;
+
+ wb->len += d - s;
+ }
+
+ buffer_need_bytes(wb, 1);
+ wb->buffer[wb->len] = '\0';
+
+ buffer_overflow_check(wb);
+}
+
+static inline void buffer_json_strcat(BUFFER *wb, const char *txt) {
+ if(unlikely(!txt || !*txt)) return;
+
+ const unsigned char *t = (const unsigned char *)txt;
+ while(*t) {
+ buffer_need_bytes(wb, 110);
+ unsigned char *s = (unsigned char *)&wb->buffer[wb->len];
+ unsigned char *d = s;
+ const unsigned char *e = (unsigned char *)&wb->buffer[wb->size - 10]; // make room for the max escape sequence
+
+ while(*t && d < e) {
+#ifdef BUFFER_JSON_ESCAPE_UTF
+ if(unlikely(IS_UTF8_STARTBYTE(*t) && IS_UTF8_BYTE(t[1]))) {
+ // UTF-8 multi-byte encoded character
+
+ // find how big this character is (2-4 bytes)
+ size_t utf_character_size = 2;
+ while(utf_character_size < 4 && t[utf_character_size] && IS_UTF8_BYTE(t[utf_character_size]) && !IS_UTF8_STARTBYTE(t[utf_character_size]))
+ utf_character_size++;
+
+ uint32_t code_point = 0;
+ for (size_t i = 0; i < utf_character_size; i++) {
+ code_point <<= 6;
+ code_point |= (t[i] & 0x3F);
+ }
+
+ t += utf_character_size;
+
+ // encode as \u escape sequence
+ *d++ = '\\';
+ *d++ = 'u';
+ *d++ = hex_digits[(code_point >> 12) & 0xf];
+ *d++ = hex_digits[(code_point >> 8) & 0xf];
+ *d++ = hex_digits[(code_point >> 4) & 0xf];
+ *d++ = hex_digits[code_point & 0xf];
+ }
+ else
+#endif
+ if(unlikely(*t < ' ')) {
+ uint32_t v = *t++;
+ *d++ = '\\';
+ *d++ = 'u';
+ *d++ = hex_digits[(v >> 12) & 0xf];
+ *d++ = hex_digits[(v >> 8) & 0xf];
+ *d++ = hex_digits[(v >> 4) & 0xf];
+ *d++ = hex_digits[v & 0xf];
+ }
+ else {
+ if (unlikely(*t == '\\' || *t == '\"'))
+ *d++ = '\\';
+
+ *d++ = *t++;
+ }
+ }
+
+ wb->len += d - s;
+ }
+
+ buffer_need_bytes(wb, 1);
+ wb->buffer[wb->len] = '\0';
+
+ buffer_overflow_check(wb);
+}
+
+static inline void buffer_json_quoted_strcat(BUFFER *wb, const char *txt) {
+ if(unlikely(!txt || !*txt)) return;
+
+ if(*txt == '"')
+ txt++;
+
+ const char *t = txt;
+ while(*t) {
+ buffer_need_bytes(wb, 100);
+ char *s = &wb->buffer[wb->len];
+ char *d = s;
+ const char *e = &wb->buffer[wb->size - 1]; // remove 1 to make room for the escape character
+
+ while(*t && d < e) {
+ if(unlikely(*t == '"' && !t[1])) {
+ t++;
+ continue;
+ }
+
+ if(unlikely(*t == '\\' || *t == '"'))
+ *d++ = '\\';
+
+ *d++ = *t++;
+ }
+
+ wb->len += d - s;
+ }
+
+ buffer_need_bytes(wb, 1);
+ wb->buffer[wb->len] = '\0';
+
+ buffer_overflow_check(wb);
+}
+
+// This trick seems to give an 80% speed increase in 32bit systems
+// print_number_llu_r() will just print the digits up to the
+// point the remaining value fits in 32 bits, and then calls
+// print_number_lu_r() to print the rest with 32 bit arithmetic.
+
+static inline char *print_uint32_reversed(char *dst, uint32_t value) {
+ char *d = dst;
+ do *d++ = (char)('0' + (value % 10)); while((value /= 10));
+ return d;
+}
+
+static inline char *print_uint64_reversed(char *dst, uint64_t value) {
+#ifdef ENV32BIT
+ if(value <= (uint64_t)0xffffffff)
+ return print_uint32_reversed(dst, value);
+
+ char *d = dst;
+ do *d++ = (char)('0' + (value % 10)); while((value /= 10) && value > (uint64_t)0xffffffff);
+ if(value) return print_uint32_reversed(d, value);
+ return d;
+#else
+ char *d = dst;
+ do *d++ = (char)('0' + (value % 10)); while((value /= 10));
+ return d;
+#endif
+}
+
+static inline char *print_uint32_hex_reversed(char *dst, uint32_t value) {
+ static const char *digits = "0123456789ABCDEF";
+ char *d = dst;
+ do *d++ = digits[value & 0xf]; while((value >>= 4));
+ return d;
+}
+
+static inline char *print_uint64_hex_reversed(char *dst, uint64_t value) {
+#ifdef ENV32BIT
+ if(value <= (uint64_t)0xffffffff)
+ return print_uint32_hex_reversed(dst, value);
+
+ char *d = dst;
+ do *d++ = hex_digits[value & 0xf]; while((value >>= 4) && value > (uint64_t)0xffffffff);
+ if(value) return print_uint32_hex_reversed(d, value);
+ return d;
+#else
+ char *d = dst;
+ do *d++ = hex_digits[value & 0xf]; while((value >>= 4));
+ return d;
+#endif
+}
+
+static inline char *print_uint64_base64_reversed(char *dst, uint64_t value) {
+ char *d = dst;
+ do *d++ = base64_digits[value & 63]; while ((value >>= 6));
+ return d;
+}
+
+static inline void char_array_reverse(char *from, char *to) {
+ // from and to are inclusive
+ char *begin = from, *end = to, aux;
+ while (end > begin) aux = *end, *end-- = *begin, *begin++ = aux;
+}
+
+static inline int print_netdata_double(char *dst, NETDATA_DOUBLE value) {
+ char *s = dst;
+
+ if(unlikely(value < 0)) {
+ *s++ = '-';
+ value = fabsndd(value);
+ }
+
+ uint64_t fractional_precision = 10000000ULL; // fractional part 7 digits
+ int fractional_wanted_digits = 7;
+ int exponent = 0;
+ if(unlikely(value >= (NETDATA_DOUBLE)(UINT64_MAX / 10))) {
+ // the number is too big to print using 64bit numbers
+ // so, let's convert it to exponential notation
+ exponent = (int)(floorndd(log10ndd(value)));
+ value /= powndd(10, exponent);
+
+ // the max precision we can support is 18 digits
+ // (UINT64_MAX is 20, but the first is 1)
+ fractional_precision = 1000000000000000000ULL; // fractional part 18 digits
+ fractional_wanted_digits = 18;
+ }
+
+ char *d = s;
+ NETDATA_DOUBLE integral_d, fractional_d;
+ fractional_d = modfndd(value, &integral_d);
+
+ // get the integral and the fractional parts as 64-bit integers
+ uint64_t integral = (uint64_t)integral_d;
+ uint64_t fractional = (uint64_t)llrintndd(fractional_d * (NETDATA_DOUBLE)fractional_precision);
+ if(unlikely(fractional >= fractional_precision)) {
+ integral++;
+ fractional -= fractional_precision;
+ }
+
+ // convert the integral part to string (reversed)
+ d = print_uint64_reversed(d, integral);
+ char_array_reverse(s, d - 1); // copy reversed the integral string
+
+ if(likely(fractional != 0)) {
+ *d++ = '.'; // add the dot
+
+ // convert the fractional part to string (reversed)
+ d = print_uint64_reversed(s = d, fractional);
+
+ while(d - s < fractional_wanted_digits) *d++ = '0'; // prepend zeros to reach precision
+ char_array_reverse(s, d - 1); // copy reversed the fractional string
+
+ // remove trailing zeros from the fractional part
+ while(*(d - 1) == '0') d--;
+ }
+
+ if(unlikely(exponent != 0)) {
+ *d++ = 'e';
+ *d++ = '+';
+ d = print_uint32_reversed(s = d, exponent);
+ char_array_reverse(s, d - 1);
+ }
+
+ *d = '\0';
+ return (int)(d - dst);
+}
+
+static inline void buffer_print_uint64(BUFFER *wb, uint64_t value) {
+ buffer_need_bytes(wb, 50);
+
+ char *s = &wb->buffer[wb->len];
+ char *d = print_uint64_reversed(s, value);
+ char_array_reverse(s, d - 1);
+ *d = '\0';
+ wb->len += d - s;
+
+ buffer_overflow_check(wb);
+}
+
+static inline void buffer_print_int64(BUFFER *wb, int64_t value) {
+ buffer_need_bytes(wb, 50);
+
+ if(value < 0) {
+ buffer_fast_strcat(wb, "-", 1);
+ value = -value;
+ }
+
+ buffer_print_uint64(wb, (uint64_t)value);
+
+ buffer_overflow_check(wb);
+}
+
+static inline void buffer_print_uint64_hex(BUFFER *wb, uint64_t value) {
+ buffer_need_bytes(wb, sizeof(uint64_t) * 2 + 2 + 1);
+
+ buffer_fast_strcat(wb, HEX_PREFIX, sizeof(HEX_PREFIX) - 1);
+
+ char *s = &wb->buffer[wb->len];
+ char *d = print_uint64_hex_reversed(s, value);
+ char_array_reverse(s, d - 1);
+ *d = '\0';
+ wb->len += d - s;
+
+ buffer_overflow_check(wb);
+}
+
+static inline void buffer_print_uint64_base64(BUFFER *wb, uint64_t value) {
+ buffer_need_bytes(wb, sizeof(uint64_t) * 2 + 2 + 1);
+
+ buffer_fast_strcat(wb, IEEE754_UINT64_B64_PREFIX, sizeof(IEEE754_UINT64_B64_PREFIX) - 1);
+
+ char *s = &wb->buffer[wb->len];
+ char *d = print_uint64_base64_reversed(s, value);
+ char_array_reverse(s, d - 1);
+ *d = '\0';
+ wb->len += d - s;
+
+ buffer_overflow_check(wb);
+}
+
+static inline void buffer_print_int64_hex(BUFFER *wb, int64_t value) {
+ buffer_need_bytes(wb, 2);
+
+ if(value < 0) {
+ buffer_fast_strcat(wb, "-", 1);
+ value = -value;
+ }
+
+ buffer_print_uint64_hex(wb, (uint64_t)value);
+
+ buffer_overflow_check(wb);
+}
+
+static inline void buffer_print_int64_base64(BUFFER *wb, int64_t value) {
+ buffer_need_bytes(wb, 2);
+
+ if(value < 0) {
+ buffer_fast_strcat(wb, "-", 1);
+ value = -value;
+ }
+
+ buffer_print_uint64_base64(wb, (uint64_t)value);
+
+ buffer_overflow_check(wb);
+}
+
+static inline void buffer_print_netdata_double(BUFFER *wb, NETDATA_DOUBLE value) {
+ buffer_need_bytes(wb, 512 + 2);
+
+ if(isnan(value) || isinf(value)) {
+ buffer_fast_strcat(wb, "null", 4);
+ return;
+ }
+ else
+ wb->len += print_netdata_double(&wb->buffer[wb->len], value);
+
+ // terminate it
+ buffer_need_bytes(wb, 1);
+ wb->buffer[wb->len] = '\0';
+
+ buffer_overflow_check(wb);
+}
+
+static inline void buffer_print_netdata_double_hex(BUFFER *wb, NETDATA_DOUBLE value) {
+ buffer_need_bytes(wb, sizeof(uint64_t) * 2 + 2 + 1 + 1);
+
+ uint64_t *ptr = (uint64_t *) (&value);
+ buffer_fast_strcat(wb, IEEE754_DOUBLE_HEX_PREFIX, sizeof(IEEE754_DOUBLE_HEX_PREFIX) - 1);
+
+ char *s = &wb->buffer[wb->len];
+ char *d = print_uint64_hex_reversed(s, *ptr);
+ char_array_reverse(s, d - 1);
+ *d = '\0';
+ wb->len += d - s;
+
+ buffer_overflow_check(wb);
+}
+
+static inline void buffer_print_netdata_double_base64(BUFFER *wb, NETDATA_DOUBLE value) {
+ buffer_need_bytes(wb, sizeof(uint64_t) * 2 + 2 + 1 + 1);
+
+ uint64_t *ptr = (uint64_t *) (&value);
+ buffer_fast_strcat(wb, IEEE754_DOUBLE_B64_PREFIX, sizeof(IEEE754_DOUBLE_B64_PREFIX) - 1);
+
+ char *s = &wb->buffer[wb->len];
+ char *d = print_uint64_base64_reversed(s, *ptr);
+ char_array_reverse(s, d - 1);
+ *d = '\0';
+ wb->len += d - s;
+
+ buffer_overflow_check(wb);
+}
+
+typedef enum {
+ NUMBER_ENCODING_DECIMAL,
+ NUMBER_ENCODING_HEX,
+ NUMBER_ENCODING_BASE64,
+} NUMBER_ENCODING;
+
+static inline void buffer_print_int64_encoded(BUFFER *wb, NUMBER_ENCODING encoding, int64_t value) {
+ if(encoding == NUMBER_ENCODING_BASE64)
+ return buffer_print_int64_base64(wb, value);
+
+ if(encoding == NUMBER_ENCODING_HEX)
+ return buffer_print_int64_hex(wb, value);
+
+ return buffer_print_int64(wb, value);
+}
+
+static inline void buffer_print_uint64_encoded(BUFFER *wb, NUMBER_ENCODING encoding, uint64_t value) {
+ if(encoding == NUMBER_ENCODING_BASE64)
+ return buffer_print_uint64_base64(wb, value);
+
+ if(encoding == NUMBER_ENCODING_HEX)
+ return buffer_print_uint64_hex(wb, value);
+
+ return buffer_print_uint64(wb, value);
+}
+
+static inline void buffer_print_netdata_double_encoded(BUFFER *wb, NUMBER_ENCODING encoding, NETDATA_DOUBLE value) {
+ if(encoding == NUMBER_ENCODING_BASE64)
+ return buffer_print_netdata_double_base64(wb, value);
+
+ if(encoding == NUMBER_ENCODING_HEX)
+ return buffer_print_netdata_double_hex(wb, value);
+
+ return buffer_print_netdata_double(wb, value);
+}
+
+static inline void buffer_print_spaces(BUFFER *wb, size_t spaces) {
+ buffer_need_bytes(wb, spaces * 4 + 1);
+
+ char *d = &wb->buffer[wb->len];
+ for(size_t i = 0; i < spaces; i++) {
+ *d++ = ' ';
+ *d++ = ' ';
+ *d++ = ' ';
+ *d++ = ' ';
+ }
+
+ *d = '\0';
+ wb->len += spaces * 4;
+
+ buffer_overflow_check(wb);
+}
+
+static inline void buffer_print_json_comma_newline_spacing(BUFFER *wb) {
+ if(wb->json.stack[wb->json.depth].count)
+ buffer_fast_strcat(wb, ",", 1);
+
+ if(wb->json.minify)
+ return;
+
+ buffer_fast_strcat(wb, "\n", 1);
+ buffer_print_spaces(wb, wb->json.depth + 1);
+}
+
+static inline void buffer_print_json_key(BUFFER *wb, const char *key) {
+ buffer_strcat(wb, wb->json.key_quote);
+ buffer_json_strcat(wb, key);
+ buffer_strcat(wb, wb->json.key_quote);
+}
+
+static inline void buffer_json_add_string_value(BUFFER *wb, const char *value) {
+ if(value) {
+ buffer_strcat(wb, wb->json.value_quote);
+ buffer_json_strcat(wb, value);
+ buffer_strcat(wb, wb->json.value_quote);
+ }
+ else
+ buffer_fast_strcat(wb, "null", 4);
+}
+
+static inline void buffer_json_add_quoted_string_value(BUFFER *wb, const char *value) {
+ if(value) {
+ buffer_strcat(wb, wb->json.value_quote);
+ buffer_json_quoted_strcat(wb, value);
+ buffer_strcat(wb, wb->json.value_quote);
+ }
+ else
+ buffer_fast_strcat(wb, "null", 4);
+}
+
+static inline void buffer_json_member_add_object(BUFFER *wb, const char *key) {
+ buffer_print_json_comma_newline_spacing(wb);
+ buffer_print_json_key(wb, key);
+ buffer_fast_strcat(wb, ":{", 2);
+ wb->json.stack[wb->json.depth].count++;
+
+ _buffer_json_depth_push(wb, BUFFER_JSON_OBJECT);
+}
+
+static inline void buffer_json_object_close(BUFFER *wb) {
+#ifdef NETDATA_INTERNAL_CHECKS
+ assert(wb->json.depth >= 0 && "BUFFER JSON: nothing is open to close it");
+ assert(wb->json.stack[wb->json.depth].type == BUFFER_JSON_OBJECT && "BUFFER JSON: an object is not open to close it");
+#endif
+ if(!wb->json.minify) {
+ buffer_fast_strcat(wb, "\n", 1);
+ buffer_print_spaces(wb, wb->json.depth);
+ }
+ buffer_fast_strcat(wb, "}", 1);
+ _buffer_json_depth_pop(wb);
+}
+
+static inline void buffer_json_member_add_string(BUFFER *wb, const char *key, const char *value) {
+ buffer_print_json_comma_newline_spacing(wb);
+ buffer_print_json_key(wb, key);
+ buffer_fast_strcat(wb, ":", 1);
+ buffer_json_add_string_value(wb, value);
+
+ wb->json.stack[wb->json.depth].count++;
+}
+
+static inline void buffer_json_member_add_string_or_omit(BUFFER *wb, const char *key, const char *value) {
+ if(value && *value)
+ buffer_json_member_add_string(wb, key, value);
+}
+
+static inline void buffer_json_member_add_string_or_empty(BUFFER *wb, const char *key, const char *value) {
+ if(!value)
+ value = "";
+
+ buffer_json_member_add_string(wb, key, value);
+}
+
+static inline void buffer_json_member_add_quoted_string(BUFFER *wb, const char *key, const char *value) {
+ buffer_print_json_comma_newline_spacing(wb);
+ buffer_print_json_key(wb, key);
+ buffer_fast_strcat(wb, ":", 1);
+
+ if(!value || strcmp(value, "null") == 0)
+ buffer_fast_strcat(wb, "null", 4);
+ else
+ buffer_json_add_quoted_string_value(wb, value);
+
+ wb->json.stack[wb->json.depth].count++;
+}
+
+static inline void buffer_json_member_add_uuid(BUFFER *wb, const char *key, uuid_t *value) {
+ buffer_print_json_comma_newline_spacing(wb);
+ buffer_print_json_key(wb, key);
+ buffer_fast_strcat(wb, ":", 1);
+
+ if(value) {
+ char uuid[GUID_LEN + 1];
+ uuid_unparse_lower(*value, uuid);
+ buffer_json_add_string_value(wb, uuid);
+ }
+ else
+ buffer_json_add_string_value(wb, NULL);
+
+ wb->json.stack[wb->json.depth].count++;
+}
+
+static inline void buffer_json_member_add_boolean(BUFFER *wb, const char *key, bool value) {
+ buffer_print_json_comma_newline_spacing(wb);
+ buffer_print_json_key(wb, key);
+ buffer_fast_strcat(wb, ":", 1);
+ buffer_strcat(wb, value?"true":"false");
+
+ wb->json.stack[wb->json.depth].count++;
+}
+
+static inline void buffer_json_member_add_array(BUFFER *wb, const char *key) {
+ buffer_print_json_comma_newline_spacing(wb);
+ buffer_print_json_key(wb, key);
+ buffer_fast_strcat(wb, ":[", 2);
+ wb->json.stack[wb->json.depth].count++;
+
+ _buffer_json_depth_push(wb, BUFFER_JSON_ARRAY);
+}
+
+static inline void buffer_json_add_array_item_array(BUFFER *wb) {
+ buffer_print_json_comma_newline_spacing(wb);
+
+ buffer_fast_strcat(wb, "[", 1);
+ wb->json.stack[wb->json.depth].count++;
+
+ _buffer_json_depth_push(wb, BUFFER_JSON_ARRAY);
+}
+
+static inline void buffer_json_add_array_item_string(BUFFER *wb, const char *value) {
+ if(wb->json.stack[wb->json.depth].count)
+ buffer_fast_strcat(wb, ",", 1);
+
+ buffer_json_add_string_value(wb, value);
+ wb->json.stack[wb->json.depth].count++;
+}
+
+static inline void buffer_json_add_array_item_double(BUFFER *wb, NETDATA_DOUBLE value) {
+ if(wb->json.stack[wb->json.depth].count)
+ buffer_fast_strcat(wb, ",", 1);
+
+ buffer_print_netdata_double(wb, value);
+ wb->json.stack[wb->json.depth].count++;
+}
+
+static inline void buffer_json_add_array_item_int64(BUFFER *wb, int64_t value) {
+ if(wb->json.stack[wb->json.depth].count)
+ buffer_fast_strcat(wb, ",", 1);
+
+ buffer_print_int64(wb, value);
+ wb->json.stack[wb->json.depth].count++;
+}
+
+static inline void buffer_json_add_array_item_uint64(BUFFER *wb, uint64_t value) {
+ if(wb->json.stack[wb->json.depth].count)
+ buffer_fast_strcat(wb, ",", 1);
+
+ buffer_print_uint64(wb, value);
+ wb->json.stack[wb->json.depth].count++;
+}
+
+static inline void buffer_json_add_array_item_time_t(BUFFER *wb, time_t value) {
+ if(wb->json.stack[wb->json.depth].count)
+ buffer_fast_strcat(wb, ",", 1);
+
+ buffer_print_int64(wb, value);
+ wb->json.stack[wb->json.depth].count++;
+}
+
+static inline void buffer_json_add_array_item_time_ms(BUFFER *wb, time_t value) {
+ if(wb->json.stack[wb->json.depth].count)
+ buffer_fast_strcat(wb, ",", 1);
+
+ buffer_print_int64(wb, value);
+ buffer_fast_strcat(wb, "000", 3);
+ wb->json.stack[wb->json.depth].count++;
+}
+
+static inline void buffer_json_add_array_item_time_t2ms(BUFFER *wb, time_t value) {
+ if(wb->json.stack[wb->json.depth].count)
+ buffer_fast_strcat(wb, ",", 1);
+
+ buffer_print_int64(wb, value);
+ buffer_fast_strcat(wb, "000", 3);
+ wb->json.stack[wb->json.depth].count++;
+}
+
+static inline void buffer_json_add_array_item_object(BUFFER *wb) {
+ if(wb->json.stack[wb->json.depth].count)
+ buffer_fast_strcat(wb, ",", 1);
+
+ buffer_fast_strcat(wb, "{", 1);
+ wb->json.stack[wb->json.depth].count++;
+
+ _buffer_json_depth_push(wb, BUFFER_JSON_OBJECT);
+}
+
+static inline void buffer_json_member_add_time_t(BUFFER *wb, const char *key, time_t value) {
+ buffer_print_json_comma_newline_spacing(wb);
+ buffer_print_json_key(wb, key);
+ buffer_fast_strcat(wb, ":", 1);
+ buffer_print_int64(wb, value);
+
+ wb->json.stack[wb->json.depth].count++;
+}
+
+static inline void buffer_json_member_add_time_t2ms(BUFFER *wb, const char *key, time_t value) {
+ buffer_print_json_comma_newline_spacing(wb);
+ buffer_print_json_key(wb, key);
+ buffer_fast_strcat(wb, ":", 1);
+ buffer_print_int64(wb, value);
+ buffer_fast_strcat(wb, "000", 3);
+
+ wb->json.stack[wb->json.depth].count++;
+}
+
+static inline void buffer_json_member_add_uint64(BUFFER *wb, const char *key, uint64_t value) {
+ buffer_print_json_comma_newline_spacing(wb);
+ buffer_print_json_key(wb, key);
+ buffer_fast_strcat(wb, ":", 1);
+ buffer_print_uint64(wb, value);
+
+ wb->json.stack[wb->json.depth].count++;
+}
+
+static inline void buffer_json_member_add_int64(BUFFER *wb, const char *key, int64_t value) {
+ buffer_print_json_comma_newline_spacing(wb);
+ buffer_print_json_key(wb, key);
+ buffer_fast_strcat(wb, ":", 1);
+ buffer_print_int64(wb, value);
+
+ wb->json.stack[wb->json.depth].count++;
+}
+
+static inline void buffer_json_member_add_double(BUFFER *wb, const char *key, NETDATA_DOUBLE value) {
+ buffer_print_json_comma_newline_spacing(wb);
+ buffer_print_json_key(wb, key);
+ buffer_fast_strcat(wb, ":", 1);
+ buffer_print_netdata_double(wb, value);
+
+ wb->json.stack[wb->json.depth].count++;
+}
+
+static inline void buffer_json_array_close(BUFFER *wb) {
+#ifdef NETDATA_INTERNAL_CHECKS
+ assert(wb->json.depth >= 0 && "BUFFER JSON: nothing is open to close it");
+ assert(wb->json.stack[wb->json.depth].type == BUFFER_JSON_ARRAY && "BUFFER JSON: an array is not open to close it");
+#endif
+ buffer_fast_strcat(wb, "]", 1);
+ _buffer_json_depth_pop(wb);
}
#endif /* NETDATA_WEB_BUFFER_H */
diff --git a/libnetdata/circular_buffer/README.md b/libnetdata/circular_buffer/README.md
index 23980dff3..c47386516 100644
--- a/libnetdata/circular_buffer/README.md
+++ b/libnetdata/circular_buffer/README.md
@@ -4,7 +4,7 @@ custom_edit_url: https://github.com/netdata/netdata/edit/master/libnetdata/circu
sidebar_label: "Circular Buffer"
learn_status: "Published"
learn_topic_type: "Tasks"
-learn_rel_path: "Developers/libnetdata libraries"
+learn_rel_path: "Developers/libnetdata"
-->
# Circular Buffer
diff --git a/libnetdata/circular_buffer/circular_buffer.c b/libnetdata/circular_buffer/circular_buffer.c
index b2bded179..7ffe6b8bc 100644
--- a/libnetdata/circular_buffer/circular_buffer.c
+++ b/libnetdata/circular_buffer/circular_buffer.c
@@ -16,7 +16,10 @@ struct circular_buffer *cbuffer_new(size_t initial, size_t max, size_t *statisti
}
void cbuffer_free(struct circular_buffer *buf) {
- if(buf && buf->statistics)
+ if (unlikely(!buf))
+ return;
+
+ if(buf->statistics)
__atomic_sub_fetch(buf->statistics, sizeof(struct circular_buffer) + buf->size, __ATOMIC_RELAXED);
freez(buf->data);
diff --git a/libnetdata/clocks/README.md b/libnetdata/clocks/README.md
index 3a7ce55f9..33b0f0e85 100644
--- a/libnetdata/clocks/README.md
+++ b/libnetdata/clocks/README.md
@@ -1,5 +1,10 @@
<!--
-custom_edit_url: https://github.com/netdata/netdata/edit/master/libnetdata/clocks/README.md
+custom_edit_url: "https://github.com/netdata/netdata/edit/master/libnetdata/clocks/README.md"
+title: "Clocks"
+sidebar_label: "Clocks"
+learn_status: "Published"
+learn_topic_type: "References"
+learn_rel_path: "Developers/libnetdata"
-->
-
+# Clocks \ No newline at end of file
diff --git a/libnetdata/config/README.md b/libnetdata/config/README.md
index c34cf9255..c3a9d147c 100644
--- a/libnetdata/config/README.md
+++ b/libnetdata/config/README.md
@@ -4,7 +4,7 @@ custom_edit_url: https://github.com/netdata/netdata/edit/master/libnetdata/confi
sidebar_label: "Netdata ini config files"
learn_status: "Published"
learn_topic_type: "Tasks"
-learn_rel_path: "Developers/libnetdata libraries"
+learn_rel_path: "Developers/libnetdata"
-->
# Netdata ini config files
diff --git a/libnetdata/config/appconfig.c b/libnetdata/config/appconfig.c
index 938c7dde2..d346da85f 100644
--- a/libnetdata/config/appconfig.c
+++ b/libnetdata/config/appconfig.c
@@ -831,7 +831,7 @@ void appconfig_generate(struct config *root, BUFFER *wb, int only_changed)
"#\n"
"\n# global netdata configuration\n");
- for(i = 0; i <= 16 ;i++) {
+ for(i = 0; i <= 17 ;i++) {
appconfig_wrlock(root);
for(co = root->first_section; co ; co = co->next) {
if(!strcmp(co->name, CONFIG_SECTION_GLOBAL)) pri = 0;
@@ -845,13 +845,14 @@ void appconfig_generate(struct config *root, BUFFER *wb, int only_changed)
else if(!strcmp(co->name, CONFIG_SECTION_ML)) pri = 8;
else if(!strcmp(co->name, CONFIG_SECTION_HEALTH)) pri = 9;
else if(!strcmp(co->name, CONFIG_SECTION_WEB)) pri = 10;
- // by default, new sections will get pri = 11 (set at the end, below)
- else if(!strcmp(co->name, CONFIG_SECTION_REGISTRY)) pri = 12;
- else if(!strcmp(co->name, CONFIG_SECTION_GLOBAL_STATISTICS)) pri = 13;
- else if(!strcmp(co->name, CONFIG_SECTION_PLUGINS)) pri = 14;
- else if(!strcmp(co->name, CONFIG_SECTION_STATSD)) pri = 15;
- else if(!strncmp(co->name, "plugin:", 7)) pri = 16; // << change the loop too if you change this
- else pri = 11; // this is used for any new (currently unknown) sections
+ else if(!strcmp(co->name, CONFIG_SECTION_WEBRTC)) pri = 11;
+ // by default, new sections will get pri = 12 (set at the end, below)
+ else if(!strcmp(co->name, CONFIG_SECTION_REGISTRY)) pri = 13;
+ else if(!strcmp(co->name, CONFIG_SECTION_GLOBAL_STATISTICS)) pri = 14;
+ else if(!strcmp(co->name, CONFIG_SECTION_PLUGINS)) pri = 15;
+ else if(!strcmp(co->name, CONFIG_SECTION_STATSD)) pri = 16;
+ else if(!strncmp(co->name, "plugin:", 7)) pri = 17; // << change the loop too if you change this
+ else pri = 12; // this is used for any new (currently unknown) sections
if(i == pri) {
int loaded = 0;
diff --git a/libnetdata/config/appconfig.h b/libnetdata/config/appconfig.h
index 2828e107a..b3a090248 100644
--- a/libnetdata/config/appconfig.h
+++ b/libnetdata/config/appconfig.h
@@ -88,6 +88,7 @@
#define CONFIG_SECTION_ENV_VARS "environment variables"
#define CONFIG_SECTION_SQLITE "sqlite"
#define CONFIG_SECTION_WEB "web"
+#define CONFIG_SECTION_WEBRTC "webrtc"
#define CONFIG_SECTION_STATSD "statsd"
#define CONFIG_SECTION_PLUGINS "plugins"
#define CONFIG_SECTION_CLOUD "cloud"
diff --git a/libnetdata/dictionary/README.md b/libnetdata/dictionary/README.md
index 508c4e031..3b54cf18f 100644
--- a/libnetdata/dictionary/README.md
+++ b/libnetdata/dictionary/README.md
@@ -3,7 +3,7 @@ custom_edit_url: https://github.com/netdata/netdata/edit/master/libnetdata/dicti
sidebar_label: "Dictionaries"
learn_status: "Published"
learn_topic_type: "Tasks"
-learn_rel_path: "Developers/libnetdata libraries"
+learn_rel_path: "Developers/libnetdata"
-->
# Dictionaries
diff --git a/libnetdata/dictionary/dictionary.c b/libnetdata/dictionary/dictionary.c
index 061b671ab..42e4a99f1 100644
--- a/libnetdata/dictionary/dictionary.c
+++ b/libnetdata/dictionary/dictionary.c
@@ -10,9 +10,9 @@ typedef enum __attribute__ ((__packed__)) {
DICT_FLAG_DESTROYED = (1 << 0), // this dictionary has been destroyed
} DICT_FLAGS;
-#define dict_flag_check(dict, flag) (__atomic_load_n(&((dict)->flags), __ATOMIC_SEQ_CST) & (flag))
-#define dict_flag_set(dict, flag) __atomic_or_fetch(&((dict)->flags), flag, __ATOMIC_SEQ_CST)
-#define dict_flag_clear(dict, flag) __atomic_and_fetch(&((dict)->flags), ~(flag), __ATOMIC_SEQ_CST)
+#define dict_flag_check(dict, flag) (__atomic_load_n(&((dict)->flags), __ATOMIC_RELAXED) & (flag))
+#define dict_flag_set(dict, flag) __atomic_or_fetch(&((dict)->flags), flag, __ATOMIC_RELAXED)
+#define dict_flag_clear(dict, flag) __atomic_and_fetch(&((dict)->flags), ~(flag), __ATOMIC_RELAXED)
// flags macros
#define is_dictionary_destroyed(dict) dict_flag_check(dict, DICT_FLAG_DESTROYED)
@@ -37,13 +37,13 @@ typedef enum __attribute__ ((__packed__)) item_flags {
// IMPORTANT: This is 8-bit
} ITEM_FLAGS;
-#define item_flag_check(item, flag) (__atomic_load_n(&((item)->flags), __ATOMIC_SEQ_CST) & (flag))
-#define item_flag_set(item, flag) __atomic_or_fetch(&((item)->flags), flag, __ATOMIC_SEQ_CST)
-#define item_flag_clear(item, flag) __atomic_and_fetch(&((item)->flags), ~(flag), __ATOMIC_SEQ_CST)
+#define item_flag_check(item, flag) (__atomic_load_n(&((item)->flags), __ATOMIC_RELAXED) & (flag))
+#define item_flag_set(item, flag) __atomic_or_fetch(&((item)->flags), flag, __ATOMIC_RELAXED)
+#define item_flag_clear(item, flag) __atomic_and_fetch(&((item)->flags), ~(flag), __ATOMIC_RELAXED)
-#define item_shared_flag_check(item, flag) (__atomic_load_n(&((item)->shared->flags), __ATOMIC_SEQ_CST) & (flag))
-#define item_shared_flag_set(item, flag) __atomic_or_fetch(&((item)->shared->flags), flag, __ATOMIC_SEQ_CST)
-#define item_shared_flag_clear(item, flag) __atomic_and_fetch(&((item)->shared->flags), ~(flag), __ATOMIC_SEQ_CST)
+#define item_shared_flag_check(item, flag) (__atomic_load_n(&((item)->shared->flags), __ATOMIC_RELAXED) & (flag))
+#define item_shared_flag_set(item, flag) __atomic_or_fetch(&((item)->shared->flags), flag, __ATOMIC_RELAXED)
+#define item_shared_flag_clear(item, flag) __atomic_and_fetch(&((item)->shared->flags), ~(flag), __ATOMIC_RELAXED)
#define REFCOUNT_DELETING (-100)
@@ -175,7 +175,7 @@ struct dictionary {
long int referenced_items; // how many items of the dictionary are currently being used by 3rd parties
long int pending_deletion_items; // how many items of the dictionary have been deleted, but have not been removed yet
-#ifdef NETDATA_INTERNAL_CHECKS
+#ifdef NETDATA_DICTIONARY_VALIDATE_POINTERS
netdata_mutex_t global_pointer_registry_mutex;
Pvoid_t global_pointer_registry;
#endif
@@ -205,59 +205,47 @@ static inline int item_is_not_referenced_and_can_be_removed_advanced(DICTIONARY
// ----------------------------------------------------------------------------
// validate each pointer is indexed once - internal checks only
+#ifdef NETDATA_DICTIONARY_VALIDATE_POINTERS
static inline void pointer_index_init(DICTIONARY *dict __maybe_unused) {
-#ifdef NETDATA_INTERNAL_CHECKS
netdata_mutex_init(&dict->global_pointer_registry_mutex);
-#else
- ;
-#endif
}
static inline void pointer_destroy_index(DICTIONARY *dict __maybe_unused) {
-#ifdef NETDATA_INTERNAL_CHECKS
netdata_mutex_lock(&dict->global_pointer_registry_mutex);
JudyHSFreeArray(&dict->global_pointer_registry, PJE0);
netdata_mutex_unlock(&dict->global_pointer_registry_mutex);
-#else
- ;
-#endif
}
static inline void pointer_add(DICTIONARY *dict __maybe_unused, DICTIONARY_ITEM *item __maybe_unused) {
-#ifdef NETDATA_INTERNAL_CHECKS
netdata_mutex_lock(&dict->global_pointer_registry_mutex);
Pvoid_t *PValue = JudyHSIns(&dict->global_pointer_registry, &item, sizeof(void *), PJE0);
if(*PValue != NULL)
fatal("pointer already exists in registry");
*PValue = item;
netdata_mutex_unlock(&dict->global_pointer_registry_mutex);
-#else
- ;
-#endif
}
static inline void pointer_check(DICTIONARY *dict __maybe_unused, DICTIONARY_ITEM *item __maybe_unused) {
-#ifdef NETDATA_INTERNAL_CHECKS
netdata_mutex_lock(&dict->global_pointer_registry_mutex);
Pvoid_t *PValue = JudyHSGet(dict->global_pointer_registry, &item, sizeof(void *));
if(PValue == NULL)
fatal("pointer is not found in registry");
netdata_mutex_unlock(&dict->global_pointer_registry_mutex);
-#else
- ;
-#endif
}
static inline void pointer_del(DICTIONARY *dict __maybe_unused, DICTIONARY_ITEM *item __maybe_unused) {
-#ifdef NETDATA_INTERNAL_CHECKS
netdata_mutex_lock(&dict->global_pointer_registry_mutex);
int ret = JudyHSDel(&dict->global_pointer_registry, &item, sizeof(void *), PJE0);
if(!ret)
fatal("pointer to be deleted does not exist in registry");
netdata_mutex_unlock(&dict->global_pointer_registry_mutex);
-#else
- ;
-#endif
}
+#else // !NETDATA_DICTIONARY_VALIDATE_POINTERS
+#define pointer_index_init(dict) debug_dummy()
+#define pointer_destroy_index(dict) debug_dummy()
+#define pointer_add(dict, item) debug_dummy()
+#define pointer_check(dict, item) debug_dummy()
+#define pointer_del(dict, item) debug_dummy()
+#endif // !NETDATA_DICTIONARY_VALIDATE_POINTERS
// ----------------------------------------------------------------------------
// memory statistics
@@ -298,7 +286,7 @@ static inline void dictionary_hooks_allocate(DICTIONARY *dict) {
static inline size_t dictionary_hooks_free(DICTIONARY *dict) {
if(!dict->hooks) return 0;
- REFCOUNT links = __atomic_sub_fetch(&dict->hooks->links, 1, __ATOMIC_SEQ_CST);
+ REFCOUNT links = __atomic_sub_fetch(&dict->hooks->links, 1, __ATOMIC_ACQUIRE);
if(links == 0) {
freez(dict->hooks);
dict->hooks = NULL;
@@ -356,26 +344,25 @@ size_t dictionary_version(DICTIONARY *dict) {
if(unlikely(!dict)) return 0;
// this is required for views to return the right number
- garbage_collect_pending_deletes(dict);
+ // garbage_collect_pending_deletes(dict);
- return __atomic_load_n(&dict->version, __ATOMIC_SEQ_CST);
+ return __atomic_load_n(&dict->version, __ATOMIC_RELAXED);
}
size_t dictionary_entries(DICTIONARY *dict) {
if(unlikely(!dict)) return 0;
// this is required for views to return the right number
- garbage_collect_pending_deletes(dict);
+ // garbage_collect_pending_deletes(dict);
- long int entries = __atomic_load_n(&dict->entries, __ATOMIC_SEQ_CST);
- if(entries < 0)
- fatal("DICTIONARY: entries is negative: %ld", entries);
+ long int entries = __atomic_load_n(&dict->entries, __ATOMIC_RELAXED);
+ internal_fatal(entries < 0, "DICTIONARY: entries is negative: %ld", entries);
return entries;
}
size_t dictionary_referenced_items(DICTIONARY *dict) {
if(unlikely(!dict)) return 0;
- long int referenced_items = __atomic_load_n(&dict->referenced_items, __ATOMIC_SEQ_CST);
+ long int referenced_items = __atomic_load_n(&dict->referenced_items, __ATOMIC_RELAXED);
if(referenced_items < 0)
fatal("DICTIONARY: referenced items is negative: %ld", referenced_items);
@@ -387,7 +374,7 @@ long int dictionary_stats_for_registry(DICTIONARY *dict) {
return (dict->stats->memory.index + dict->stats->memory.dict);
}
void dictionary_version_increment(DICTIONARY *dict) {
- __atomic_fetch_add(&dict->version, 1, __ATOMIC_SEQ_CST);
+ __atomic_fetch_add(&dict->version, 1, __ATOMIC_RELAXED);
}
// ----------------------------------------------------------------------------
@@ -409,9 +396,9 @@ static inline void DICTIONARY_ENTRIES_PLUS1(DICTIONARY *dict) {
}
else {
- __atomic_fetch_add(&dict->version, 1, __ATOMIC_SEQ_CST);
- __atomic_fetch_add(&dict->entries, 1, __ATOMIC_SEQ_CST);
- __atomic_fetch_add(&dict->referenced_items, 1, __ATOMIC_SEQ_CST);
+ __atomic_fetch_add(&dict->version, 1, __ATOMIC_RELAXED);
+ __atomic_fetch_add(&dict->entries, 1, __ATOMIC_RELAXED);
+ __atomic_fetch_add(&dict->referenced_items, 1, __ATOMIC_RELAXED);
}
}
static inline void DICTIONARY_ENTRIES_MINUS1(DICTIONARY *dict) {
@@ -425,17 +412,15 @@ static inline void DICTIONARY_ENTRIES_MINUS1(DICTIONARY *dict) {
entries = dict->entries++;
}
else {
- __atomic_fetch_add(&dict->version, 1, __ATOMIC_SEQ_CST);
- entries = __atomic_fetch_sub(&dict->entries, 1, __ATOMIC_SEQ_CST);
+ __atomic_fetch_add(&dict->version, 1, __ATOMIC_RELAXED);
+ entries = __atomic_fetch_sub(&dict->entries, 1, __ATOMIC_RELAXED);
}
-#ifdef NETDATA_INTERNAL_CHECKS
- if(unlikely(entries == 0))
- fatal("DICT: negative number of entries in dictionary created from %s() (%zu@%s)",
- dict->creation_function,
- dict->creation_line,
- dict->creation_file);
-#endif
+ internal_fatal(entries == 0,
+ "DICT: negative number of entries in dictionary created from %s() (%zu@%s)",
+ dict->creation_function,
+ dict->creation_line,
+ dict->creation_file);
}
static inline void DICTIONARY_VALUE_RESETS_PLUS1(DICTIONARY *dict) {
__atomic_fetch_add(&dict->stats->ops.resets, 1, __ATOMIC_RELAXED);
@@ -443,7 +428,7 @@ static inline void DICTIONARY_VALUE_RESETS_PLUS1(DICTIONARY *dict) {
if(unlikely(is_dictionary_single_threaded(dict)))
dict->version++;
else
- __atomic_fetch_add(&dict->version, 1, __ATOMIC_SEQ_CST);
+ __atomic_fetch_add(&dict->version, 1, __ATOMIC_RELAXED);
}
static inline void DICTIONARY_STATS_TRAVERSALS_PLUS1(DICTIONARY *dict) {
__atomic_fetch_add(&dict->stats->ops.traversals, 1, __ATOMIC_RELAXED);
@@ -464,16 +449,16 @@ static inline void DICTIONARY_STATS_SEARCH_IGNORES_PLUS1(DICTIONARY *dict) {
__atomic_fetch_add(&dict->stats->spin_locks.search_spins, 1, __ATOMIC_RELAXED);
}
static inline void DICTIONARY_STATS_CALLBACK_INSERTS_PLUS1(DICTIONARY *dict) {
- __atomic_fetch_add(&dict->stats->callbacks.inserts, 1, __ATOMIC_RELAXED);
+ __atomic_fetch_add(&dict->stats->callbacks.inserts, 1, __ATOMIC_RELEASE);
}
static inline void DICTIONARY_STATS_CALLBACK_CONFLICTS_PLUS1(DICTIONARY *dict) {
- __atomic_fetch_add(&dict->stats->callbacks.conflicts, 1, __ATOMIC_RELAXED);
+ __atomic_fetch_add(&dict->stats->callbacks.conflicts, 1, __ATOMIC_RELEASE);
}
static inline void DICTIONARY_STATS_CALLBACK_REACTS_PLUS1(DICTIONARY *dict) {
- __atomic_fetch_add(&dict->stats->callbacks.reacts, 1, __ATOMIC_RELAXED);
+ __atomic_fetch_add(&dict->stats->callbacks.reacts, 1, __ATOMIC_RELEASE);
}
static inline void DICTIONARY_STATS_CALLBACK_DELETES_PLUS1(DICTIONARY *dict) {
- __atomic_fetch_add(&dict->stats->callbacks.deletes, 1, __ATOMIC_RELAXED);
+ __atomic_fetch_add(&dict->stats->callbacks.deletes, 1, __ATOMIC_RELEASE);
}
static inline void DICTIONARY_STATS_GARBAGE_COLLECTIONS_PLUS1(DICTIONARY *dict) {
__atomic_fetch_add(&dict->stats->ops.garbage_collections, 1, __ATOMIC_RELAXED);
@@ -496,52 +481,48 @@ static inline void DICTIONARY_STATS_DICT_FLUSHES_PLUS1(DICTIONARY *dict) {
__atomic_fetch_add(&dict->stats->ops.flushes, 1, __ATOMIC_RELAXED);
}
-static inline long int DICTIONARY_REFERENCED_ITEMS_PLUS1(DICTIONARY *dict) {
+static inline void DICTIONARY_REFERENCED_ITEMS_PLUS1(DICTIONARY *dict) {
__atomic_fetch_add(&dict->stats->items.referenced, 1, __ATOMIC_RELAXED);
if(unlikely(is_dictionary_single_threaded(dict)))
- return ++dict->referenced_items;
+ ++dict->referenced_items;
else
- return __atomic_add_fetch(&dict->referenced_items, 1, __ATOMIC_SEQ_CST);
+ __atomic_add_fetch(&dict->referenced_items, 1, __ATOMIC_RELAXED);
}
-static inline long int DICTIONARY_REFERENCED_ITEMS_MINUS1(DICTIONARY *dict) {
+static inline void DICTIONARY_REFERENCED_ITEMS_MINUS1(DICTIONARY *dict) {
__atomic_fetch_sub(&dict->stats->items.referenced, 1, __ATOMIC_RELAXED);
- long int referenced_items;
+ long int referenced_items; (void)referenced_items;
if(unlikely(is_dictionary_single_threaded(dict)))
referenced_items = --dict->referenced_items;
else
referenced_items = __atomic_sub_fetch(&dict->referenced_items, 1, __ATOMIC_SEQ_CST);
-#ifdef NETDATA_INTERNAL_CHECKS
- if(unlikely(referenced_items < 0))
- fatal("DICT: negative number of referenced items (%ld) in dictionary created from %s() (%zu@%s)",
- referenced_items,
- dict->creation_function,
- dict->creation_line,
- dict->creation_file);
-#endif
-
- return referenced_items;
+ internal_fatal(referenced_items < 0,
+ "DICT: negative number of referenced items (%ld) in dictionary created from %s() (%zu@%s)",
+ referenced_items,
+ dict->creation_function,
+ dict->creation_line,
+ dict->creation_file);
}
-static inline long int DICTIONARY_PENDING_DELETES_PLUS1(DICTIONARY *dict) {
+static inline void DICTIONARY_PENDING_DELETES_PLUS1(DICTIONARY *dict) {
__atomic_fetch_add(&dict->stats->items.pending_deletion, 1, __ATOMIC_RELAXED);
if(unlikely(is_dictionary_single_threaded(dict)))
- return ++dict->pending_deletion_items;
+ ++dict->pending_deletion_items;
else
- return __atomic_add_fetch(&dict->pending_deletion_items, 1, __ATOMIC_SEQ_CST);
+ __atomic_add_fetch(&dict->pending_deletion_items, 1, __ATOMIC_RELEASE);
}
static inline long int DICTIONARY_PENDING_DELETES_MINUS1(DICTIONARY *dict) {
- __atomic_fetch_sub(&dict->stats->items.pending_deletion, 1, __ATOMIC_RELAXED);
+ __atomic_fetch_sub(&dict->stats->items.pending_deletion, 1, __ATOMIC_RELEASE);
if(unlikely(is_dictionary_single_threaded(dict)))
return --dict->pending_deletion_items;
else
- return __atomic_sub_fetch(&dict->pending_deletion_items, 1, __ATOMIC_SEQ_CST);
+ return __atomic_sub_fetch(&dict->pending_deletion_items, 1, __ATOMIC_ACQUIRE);
}
static inline long int DICTIONARY_PENDING_DELETES_GET(DICTIONARY *dict) {
@@ -555,11 +536,11 @@ static inline REFCOUNT DICTIONARY_ITEM_REFCOUNT_GET(DICTIONARY *dict, DICTIONARY
if(unlikely(dict && is_dictionary_single_threaded(dict))) // this is an exception, dict can be null
return item->refcount;
else
- return (REFCOUNT)__atomic_load_n(&item->refcount, __ATOMIC_SEQ_CST);
+ return (REFCOUNT)__atomic_load_n(&item->refcount, __ATOMIC_ACQUIRE);
}
static inline REFCOUNT DICTIONARY_ITEM_REFCOUNT_GET_SOLE(DICTIONARY_ITEM *item) {
- return (REFCOUNT)__atomic_load_n(&item->refcount, __ATOMIC_SEQ_CST);
+ return (REFCOUNT)__atomic_load_n(&item->refcount, __ATOMIC_ACQUIRE);
}
// ----------------------------------------------------------------------------
@@ -579,8 +560,8 @@ static void dictionary_execute_insert_callback(DICTIONARY *dict, DICTIONARY_ITEM
dict->creation_line,
dict->creation_file);
- DICTIONARY_STATS_CALLBACK_INSERTS_PLUS1(dict);
dict->hooks->ins_callback(item, item->shared->value, constructor_data?constructor_data:dict->hooks->ins_callback_data);
+ DICTIONARY_STATS_CALLBACK_INSERTS_PLUS1(dict);
}
static bool dictionary_execute_conflict_callback(DICTIONARY *dict, DICTIONARY_ITEM *item, void *new_value, void *constructor_data) {
@@ -597,10 +578,13 @@ static bool dictionary_execute_conflict_callback(DICTIONARY *dict, DICTIONARY_IT
dict->creation_line,
dict->creation_file);
- DICTIONARY_STATS_CALLBACK_CONFLICTS_PLUS1(dict);
- return dict->hooks->conflict_callback(
+ bool ret = dict->hooks->conflict_callback(
item, item->shared->value, new_value,
constructor_data ? constructor_data : dict->hooks->conflict_callback_data);
+
+ DICTIONARY_STATS_CALLBACK_CONFLICTS_PLUS1(dict);
+
+ return ret;
}
static void dictionary_execute_react_callback(DICTIONARY *dict, DICTIONARY_ITEM *item, void *constructor_data) {
@@ -617,9 +601,10 @@ static void dictionary_execute_react_callback(DICTIONARY *dict, DICTIONARY_ITEM
dict->creation_line,
dict->creation_file);
- DICTIONARY_STATS_CALLBACK_REACTS_PLUS1(dict);
dict->hooks->react_callback(item, item->shared->value,
constructor_data?constructor_data:dict->hooks->react_callback_data);
+
+ DICTIONARY_STATS_CALLBACK_REACTS_PLUS1(dict);
}
static void dictionary_execute_delete_callback(DICTIONARY *dict, DICTIONARY_ITEM *item) {
@@ -637,8 +622,9 @@ static void dictionary_execute_delete_callback(DICTIONARY *dict, DICTIONARY_ITEM
dict->creation_line,
dict->creation_file);
- DICTIONARY_STATS_CALLBACK_DELETES_PLUS1(dict);
dict->hooks->del_callback(item, item->shared->value, dict->hooks->del_callback_data);
+
+ DICTIONARY_STATS_CALLBACK_DELETES_PLUS1(dict);
}
// ----------------------------------------------------------------------------
@@ -648,8 +634,8 @@ static inline size_t dictionary_locks_init(DICTIONARY *dict) {
if(likely(!is_dictionary_single_threaded(dict))) {
netdata_rwlock_init(&dict->index.rwlock);
netdata_rwlock_init(&dict->items.rwlock);
- return 0;
}
+
return 0;
}
@@ -657,29 +643,29 @@ static inline size_t dictionary_locks_destroy(DICTIONARY *dict) {
if(likely(!is_dictionary_single_threaded(dict))) {
netdata_rwlock_destroy(&dict->index.rwlock);
netdata_rwlock_destroy(&dict->items.rwlock);
- return 0;
}
+
return 0;
}
static inline void ll_recursive_lock_set_thread_as_writer(DICTIONARY *dict) {
pid_t expected = 0, desired = gettid();
- if(!__atomic_compare_exchange_n(&dict->items.writer_pid, &expected, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
- fatal("DICTIONARY: Cannot set thread %d as exclusive writer, expected %d, desired %d, found %d.", gettid(), expected, desired, __atomic_load_n(&dict->items.writer_pid, __ATOMIC_SEQ_CST));
+ if(!__atomic_compare_exchange_n(&dict->items.writer_pid, &expected, desired, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED))
+ fatal("DICTIONARY: Cannot set thread %d as exclusive writer, expected %d, desired %d, found %d.", gettid(), expected, desired, __atomic_load_n(&dict->items.writer_pid, __ATOMIC_RELAXED));
}
static inline void ll_recursive_unlock_unset_thread_writer(DICTIONARY *dict) {
pid_t expected = gettid(), desired = 0;
- if(!__atomic_compare_exchange_n(&dict->items.writer_pid, &expected, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
- fatal("DICTIONARY: Cannot unset thread %d as exclusive writer, expected %d, desired %d, found %d.", gettid(), expected, desired, __atomic_load_n(&dict->items.writer_pid, __ATOMIC_SEQ_CST));
+ if(!__atomic_compare_exchange_n(&dict->items.writer_pid, &expected, desired, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED))
+ fatal("DICTIONARY: Cannot unset thread %d as exclusive writer, expected %d, desired %d, found %d.", gettid(), expected, desired, __atomic_load_n(&dict->items.writer_pid, __ATOMIC_RELAXED));
}
static inline bool ll_recursive_lock_is_thread_the_writer(DICTIONARY *dict) {
pid_t tid = gettid();
- return tid > 0 && tid == __atomic_load_n(&dict->items.writer_pid, __ATOMIC_SEQ_CST);
+ return tid > 0 && tid == __atomic_load_n(&dict->items.writer_pid, __ATOMIC_RELAXED);
}
-static void ll_recursive_lock(DICTIONARY *dict, char rw) {
+static inline void ll_recursive_lock(DICTIONARY *dict, char rw) {
if(unlikely(is_dictionary_single_threaded(dict)))
return;
@@ -699,7 +685,7 @@ static void ll_recursive_lock(DICTIONARY *dict, char rw) {
}
}
-static void ll_recursive_unlock(DICTIONARY *dict, char rw) {
+static inline void ll_recursive_unlock(DICTIONARY *dict, char rw) {
if(unlikely(is_dictionary_single_threaded(dict)))
return;
@@ -722,10 +708,10 @@ static void ll_recursive_unlock(DICTIONARY *dict, char rw) {
}
}
-void dictionary_write_lock(DICTIONARY *dict) {
+inline void dictionary_write_lock(DICTIONARY *dict) {
ll_recursive_lock(dict, DICTIONARY_LOCK_WRITE);
}
-void dictionary_write_unlock(DICTIONARY *dict) {
+inline void dictionary_write_unlock(DICTIONARY *dict) {
ll_recursive_unlock(dict, DICTIONARY_LOCK_WRITE);
}
@@ -760,8 +746,8 @@ static inline void dictionary_index_wrlock_unlock(DICTIONARY *dict) {
// items garbage collector
static void garbage_collect_pending_deletes(DICTIONARY *dict) {
- usec_t last_master_deletion_us = dict->hooks?__atomic_load_n(&dict->hooks->last_master_deletion_us, __ATOMIC_SEQ_CST):0;
- usec_t last_gc_run_us = __atomic_load_n(&dict->last_gc_run_us, __ATOMIC_SEQ_CST);
+ usec_t last_master_deletion_us = dict->hooks?__atomic_load_n(&dict->hooks->last_master_deletion_us, __ATOMIC_RELAXED):0;
+ usec_t last_gc_run_us = __atomic_load_n(&dict->last_gc_run_us, __ATOMIC_RELAXED);
bool is_view = is_view_dictionary(dict);
@@ -773,7 +759,7 @@ static void garbage_collect_pending_deletes(DICTIONARY *dict) {
ll_recursive_lock(dict, DICTIONARY_LOCK_WRITE);
- __atomic_store_n(&dict->last_gc_run_us, now_realtime_usec(), __ATOMIC_SEQ_CST);
+ __atomic_store_n(&dict->last_gc_run_us, now_realtime_usec(), __ATOMIC_RELAXED);
if(is_view)
dictionary_index_lock_wrlock(dict);
@@ -819,25 +805,27 @@ static void garbage_collect_pending_deletes(DICTIONARY *dict) {
(void)deleted;
(void)examined;
- internal_error(false, "DICTIONARY: garbage collected dictionary created by %s (%zu@%s), examined %zu items, deleted %zu items, still pending %zu items",
- dict->creation_function, dict->creation_line, dict->creation_file, examined, deleted, pending);
+ internal_error(false, "DICTIONARY: garbage collected dictionary created by %s (%zu@%s), "
+ "examined %zu items, deleted %zu items, still pending %zu items",
+ dict->creation_function, dict->creation_line, dict->creation_file,
+ examined, deleted, pending);
+}
+void dictionary_garbage_collect(DICTIONARY *dict) {
+ if(!dict) return;
+ garbage_collect_pending_deletes(dict);
}
// ----------------------------------------------------------------------------
// reference counters
-static inline size_t reference_counter_init(DICTIONARY *dict) {
- (void)dict;
-
+static inline size_t reference_counter_init(DICTIONARY *dict __maybe_unused) {
// allocate memory required for reference counters
// return number of bytes
return 0;
}
-static inline size_t reference_counter_free(DICTIONARY *dict) {
- (void)dict;
-
+static inline size_t reference_counter_free(DICTIONARY *dict __maybe_unused) {
// free memory required for reference counters
// return number of bytes
return 0;
@@ -846,13 +834,13 @@ static inline size_t reference_counter_free(DICTIONARY *dict) {
static void item_acquire(DICTIONARY *dict, DICTIONARY_ITEM *item) {
REFCOUNT refcount;
- if(unlikely(is_dictionary_single_threaded(dict))) {
+ if(unlikely(is_dictionary_single_threaded(dict)))
refcount = ++item->refcount;
- }
- else {
+
+ else
// increment the refcount
refcount = __atomic_add_fetch(&item->refcount, 1, __ATOMIC_SEQ_CST);
- }
+
if(refcount <= 0) {
internal_error(
@@ -900,7 +888,7 @@ static void item_release(DICTIONARY *dict, DICTIONARY_ITEM *item) {
is_deleted = item_flag_check(item, ITEM_FLAG_DELETED);
// decrement the refcount
- refcount = __atomic_sub_fetch(&item->refcount, 1, __ATOMIC_SEQ_CST);
+ refcount = __atomic_sub_fetch(&item->refcount, 1, __ATOMIC_RELEASE);
}
if(refcount < 0) {
@@ -956,14 +944,14 @@ static int item_check_and_acquire_advanced(DICTIONARY *dict, DICTIONARY_ITEM *it
desired = refcount + 1;
- } while(!__atomic_compare_exchange_n(&item->refcount, &refcount, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST));
+ } while(!__atomic_compare_exchange_n(&item->refcount, &refcount, desired, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED));
// if ret == ITEM_OK, we acquired the item
if(ret == RC_ITEM_OK) {
- if (is_view_dictionary(dict) &&
+ if (unlikely(is_view_dictionary(dict) &&
item_shared_flag_check(item, ITEM_FLAG_DELETED) &&
- !item_flag_check(item, ITEM_FLAG_DELETED)) {
+ !item_flag_check(item, ITEM_FLAG_DELETED))) {
// but, we can't use this item
if (having_index_lock) {
@@ -979,7 +967,7 @@ static int item_check_and_acquire_advanced(DICTIONARY *dict, DICTIONARY_ITEM *it
dict_item_set_deleted(dict, item);
// decrement the refcount we incremented above
- if (__atomic_sub_fetch(&item->refcount, 1, __ATOMIC_SEQ_CST) == 0) {
+ if (__atomic_sub_fetch(&item->refcount, 1, __ATOMIC_RELEASE) == 0) {
// this is a deleted item, and we are the last one
DICTIONARY_PENDING_DELETES_PLUS1(dict);
}
@@ -988,7 +976,7 @@ static int item_check_and_acquire_advanced(DICTIONARY *dict, DICTIONARY_ITEM *it
} else {
// this is traversal / walkthrough
// decrement the refcount we incremented above
- __atomic_sub_fetch(&item->refcount, 1, __ATOMIC_SEQ_CST);
+ __atomic_sub_fetch(&item->refcount, 1, __ATOMIC_RELEASE);
}
return RC_ITEM_MARKED_FOR_DELETION;
@@ -998,7 +986,6 @@ static int item_check_and_acquire_advanced(DICTIONARY *dict, DICTIONARY_ITEM *it
DICTIONARY_REFERENCED_ITEMS_PLUS1(dict);
}
-
if(unlikely(spins > 1 && dict->stats))
DICTIONARY_STATS_CHECK_SPINS_PLUS(dict, spins - 1);
@@ -1037,7 +1024,7 @@ static inline int item_is_not_referenced_and_can_be_removed_advanced(DICTIONARY
ret = RC_ITEM_IS_CURRENTLY_BEING_CREATED;
break;
}
- } while(!__atomic_compare_exchange_n(&item->refcount, &refcount, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST));
+ } while(!__atomic_compare_exchange_n(&item->refcount, &refcount, desired, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED));
#ifdef NETDATA_INTERNAL_CHECKS
if(ret == RC_ITEM_OK)
@@ -1055,8 +1042,8 @@ static inline int item_is_not_referenced_and_can_be_removed_advanced(DICTIONARY
static inline bool item_shared_release_and_check_if_it_can_be_freed(DICTIONARY *dict __maybe_unused, DICTIONARY_ITEM *item) {
// if we can set refcount to REFCOUNT_DELETING, we can delete this item
- REFCOUNT links = __atomic_sub_fetch(&item->shared->links, 1, __ATOMIC_SEQ_CST);
- if(links == 0 && __atomic_compare_exchange_n(&item->shared->links, &links, REFCOUNT_DELETING, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
+ REFCOUNT links = __atomic_sub_fetch(&item->shared->links, 1, __ATOMIC_RELEASE);
+ if(links == 0 && __atomic_compare_exchange_n(&item->shared->links, &links, REFCOUNT_DELETING, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) {
// we can delete it
return true;
@@ -1290,7 +1277,7 @@ static DICTIONARY_ITEM *dict_item_create(DICTIONARY *dict __maybe_unused, size_t
if(master_item) {
item->shared = master_item->shared;
- if(unlikely(__atomic_add_fetch(&item->shared->links, 1, __ATOMIC_SEQ_CST) <= 1))
+ if(unlikely(__atomic_add_fetch(&item->shared->links, 1, __ATOMIC_ACQUIRE) <= 1))
fatal("DICTIONARY: attempted to link to a shared item structure that had zero references");
}
else {
@@ -1478,7 +1465,7 @@ static void dict_item_shared_set_deleted(DICTIONARY *dict, DICTIONARY_ITEM *item
item_shared_flag_set(item, ITEM_FLAG_DELETED);
if(dict->hooks)
- __atomic_store_n(&dict->hooks->last_master_deletion_us, now_realtime_usec(), __ATOMIC_SEQ_CST);
+ __atomic_store_n(&dict->hooks->last_master_deletion_us, now_realtime_usec(), __ATOMIC_RELAXED);
}
}
@@ -1486,7 +1473,7 @@ static void dict_item_shared_set_deleted(DICTIONARY *dict, DICTIONARY_ITEM *item
static bool dict_item_set_deleted(DICTIONARY *dict, DICTIONARY_ITEM *item) {
ITEM_FLAGS expected, desired;
- expected = __atomic_load_n(&item->flags, __ATOMIC_SEQ_CST);
+ expected = __atomic_load_n(&item->flags, __ATOMIC_RELAXED);
do {
@@ -1495,7 +1482,7 @@ static bool dict_item_set_deleted(DICTIONARY *dict, DICTIONARY_ITEM *item) {
desired = expected | ITEM_FLAG_DELETED;
- } while(!__atomic_compare_exchange_n(&item->flags, &expected, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST));
+ } while(!__atomic_compare_exchange_n(&item->flags, &expected, desired, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED));
DICTIONARY_ENTRIES_MINUS1(dict);
return true;
@@ -2063,11 +2050,11 @@ DICTIONARY *dictionary_create_view(DICTIONARY *master) {
dictionary_hooks_allocate(master);
- if(unlikely(__atomic_load_n(&master->hooks->links, __ATOMIC_SEQ_CST)) < 1)
+ if(unlikely(__atomic_load_n(&master->hooks->links, __ATOMIC_RELAXED)) < 1)
fatal("DICTIONARY: attempted to create a view that has %d links", master->hooks->links);
dict->hooks = master->hooks;
- __atomic_add_fetch(&master->hooks->links, 1, __ATOMIC_SEQ_CST);
+ __atomic_add_fetch(&master->hooks->links, 1, __ATOMIC_ACQUIRE);
#ifdef NETDATA_INTERNAL_CHECKS
dict->creation_function = function;
@@ -2167,6 +2154,8 @@ DICT_ITEM_CONST DICTIONARY_ITEM *dictionary_view_set_and_acquire_item_advanced(D
if(unlikely(is_master_dictionary(dict)))
fatal("DICTIONARY: this dictionary is a master, you cannot add items from other dictionaries.");
+ garbage_collect_pending_deletes(dict);
+
dictionary_acquired_item_dup(dict->master, master_item);
DICTIONARY_ITEM *item = dict_item_add_or_reset_value_and_acquire(dict, name, name_len, NULL, 0, NULL, master_item);
dictionary_acquired_item_release(dict->master, master_item);
@@ -2289,7 +2278,7 @@ void *dictionary_foreach_start_rw(DICTFE *dfe, DICTIONARY *dict, char rw) {
dfe->counter = 0;
dfe->dict = dict;
dfe->rw = rw;
-
+ dfe->locked = true;
ll_recursive_lock(dict, dfe->rw);
DICTIONARY_STATS_TRAVERSALS_PLUS1(dict);
@@ -2312,8 +2301,10 @@ void *dictionary_foreach_start_rw(DICTFE *dfe, DICTIONARY *dict, char rw) {
dfe->value = NULL;
}
- if(unlikely(dfe->rw == DICTIONARY_LOCK_REENTRANT))
+ if(unlikely(dfe->rw == DICTIONARY_LOCK_REENTRANT)) {
ll_recursive_unlock(dfe->dict, dfe->rw);
+ dfe->locked = false;
+ }
return dfe->value;
}
@@ -2329,8 +2320,10 @@ void *dictionary_foreach_next(DICTFE *dfe) {
return NULL;
}
- if(unlikely(dfe->rw == DICTIONARY_LOCK_REENTRANT))
+ if(unlikely(dfe->rw == DICTIONARY_LOCK_REENTRANT) || !dfe->locked) {
ll_recursive_lock(dfe->dict, dfe->rw);
+ dfe->locked = true;
+ }
// the item we just did
DICTIONARY_ITEM *item = dfe->item;
@@ -2360,12 +2353,21 @@ void *dictionary_foreach_next(DICTFE *dfe) {
dfe->value = NULL;
}
- if(unlikely(dfe->rw == DICTIONARY_LOCK_REENTRANT))
+ if(unlikely(dfe->rw == DICTIONARY_LOCK_REENTRANT)) {
ll_recursive_unlock(dfe->dict, dfe->rw);
+ dfe->locked = false;
+ }
return dfe->value;
}
+void dictionary_foreach_unlock(DICTFE *dfe) {
+ if(dfe->locked) {
+ ll_recursive_unlock(dfe->dict, dfe->rw);
+ dfe->locked = false;
+ }
+}
+
void dictionary_foreach_done(DICTFE *dfe) {
if(unlikely(!dfe || !dfe->dict)) return;
@@ -2383,8 +2385,10 @@ void dictionary_foreach_done(DICTFE *dfe) {
// item_release(dfe->dict, item);
}
- if(likely(dfe->rw != DICTIONARY_LOCK_REENTRANT))
+ if(likely(dfe->rw != DICTIONARY_LOCK_REENTRANT) && dfe->locked) {
ll_recursive_unlock(dfe->dict, dfe->rw);
+ dfe->locked = false;
+ }
dfe->dict = NULL;
dfe->item = NULL;
@@ -2472,7 +2476,7 @@ int dictionary_sorted_walkthrough_rw(DICTIONARY *dict, char rw, int (*callback)(
DICTIONARY_STATS_WALKTHROUGHS_PLUS1(dict);
ll_recursive_lock(dict, rw);
- size_t entries = __atomic_load_n(&dict->entries, __ATOMIC_SEQ_CST);
+ size_t entries = __atomic_load_n(&dict->entries, __ATOMIC_RELAXED);
DICTIONARY_ITEM **array = mallocz(sizeof(DICTIONARY_ITEM *) * entries);
size_t i;
@@ -3293,12 +3297,12 @@ static void *unittest_dict_master_thread(void *arg) {
DICTIONARY_ITEM *item = NULL;
int loops = 0;
- while(!__atomic_load_n(&tv->join, __ATOMIC_SEQ_CST)) {
+ while(!__atomic_load_n(&tv->join, __ATOMIC_RELAXED)) {
if(!item)
item = dictionary_set_and_acquire_item(tv->master, "ITEM1", "123", strlen("123") + 1);
- if(__atomic_load_n(&tv->item_master, __ATOMIC_SEQ_CST) != NULL) {
+ if(__atomic_load_n(&tv->item_master, __ATOMIC_RELAXED) != NULL) {
dictionary_acquired_item_release(tv->master, item);
dictionary_del(tv->master, "ITEM1");
item = NULL;
@@ -3307,7 +3311,7 @@ static void *unittest_dict_master_thread(void *arg) {
}
dictionary_acquired_item_dup(tv->master, item); // for the view thread
- __atomic_store_n(&tv->item_master, item, __ATOMIC_SEQ_CST);
+ __atomic_store_n(&tv->item_master, item, __ATOMIC_RELAXED);
dictionary_del(tv->master, "ITEM1");
@@ -3333,13 +3337,13 @@ static void *unittest_dict_view_thread(void *arg) {
DICTIONARY_ITEM *m_item = NULL;
- while(!__atomic_load_n(&tv->join, __ATOMIC_SEQ_CST)) {
- if(!(m_item = __atomic_load_n(&tv->item_master, __ATOMIC_SEQ_CST)))
+ while(!__atomic_load_n(&tv->join, __ATOMIC_RELAXED)) {
+ if(!(m_item = __atomic_load_n(&tv->item_master, __ATOMIC_RELAXED)))
continue;
DICTIONARY_ITEM *v_item = dictionary_view_set_and_acquire_item(tv->view, "ITEM2", m_item);
dictionary_acquired_item_release(tv->master, m_item);
- __atomic_store_n(&tv->item_master, NULL, __ATOMIC_SEQ_CST);
+ __atomic_store_n(&tv->item_master, NULL, __ATOMIC_RELAXED);
for(int i = 0; i < tv->dups ; i++) {
dictionary_acquired_item_dup(tv->view, v_item);
@@ -3351,7 +3355,7 @@ static void *unittest_dict_view_thread(void *arg) {
dictionary_del(tv->view, "ITEM2");
- while(!__atomic_load_n(&tv->join, __ATOMIC_SEQ_CST) && !(m_item = __atomic_load_n(&tv->item_master, __ATOMIC_SEQ_CST))) {
+ while(!__atomic_load_n(&tv->join, __ATOMIC_RELAXED) && !(m_item = __atomic_load_n(&tv->item_master, __ATOMIC_RELAXED))) {
dictionary_acquired_item_dup(tv->view, v_item);
dictionary_acquired_item_release(tv->view, v_item);
}
@@ -3522,7 +3526,7 @@ size_t dictionary_unittest_views(void) {
fprintf(stderr, "\nPASS 2: Deleting master item:\n");
dictionary_del(master, "KEY 1");
- dictionary_version(view);
+ garbage_collect_pending_deletes(view);
errors += unittest_check_dictionary("master", master, 0, 0, 1, 1, 0);
errors += unittest_check_dictionary("view", view, 0, 0, 1, 1, 0);
errors += unittest_check_item("master", master, item1_on_master, "KEY 1", item1_on_master->shared->value, 1, ITEM_FLAG_DELETED, false, false, true);
diff --git a/libnetdata/dictionary/dictionary.h b/libnetdata/dictionary/dictionary.h
index 58220def0..c13d784cb 100644
--- a/libnetdata/dictionary/dictionary.h
+++ b/libnetdata/dictionary/dictionary.h
@@ -112,7 +112,7 @@ struct dictionary_stats {
#define dictionary_create_advanced(options, stats, fixed_size) dictionary_create_advanced_with_trace(options, stats, fixed_size, __FUNCTION__, __LINE__, __FILE__)
DICTIONARY *dictionary_create_advanced_with_trace(DICT_OPTIONS options, struct dictionary_stats *stats, size_t fixed_size, const char *function, size_t line, const char *file);
#else
-#define dictionary_create(options) dictionary_create_advanced(options, NULL, 0);
+#define dictionary_create(options) dictionary_create_advanced(options, NULL, 0)
DICTIONARY *dictionary_create_advanced(DICT_OPTIONS options, struct dictionary_stats *stats, size_t fixed_size);
#endif
@@ -156,6 +156,8 @@ void dictionary_flush(DICTIONARY *dict);
void dictionary_version_increment(DICTIONARY *dict);
+void dictionary_garbage_collect(DICTIONARY *dict);
+
// ----------------------------------------------------------------------------
// Set an item in the dictionary
//
@@ -261,19 +263,20 @@ void dictionary_write_lock(DICTIONARY *dict);
void dictionary_write_unlock(DICTIONARY *dict);
typedef DICTFE_CONST struct dictionary_foreach {
- DICTIONARY *dict; // the dictionary upon we work
+ DICTIONARY *dict; // the dictionary upon we work
DICTIONARY_ITEM *item; // the item we work on, to remember the position we are at
// this can be used with dictionary_acquired_item_dup() to
// acquire the currently working item.
- DICTFE_CONST char *name; // the dictionary name of the last item used
+ const char *name; // the dictionary name of the last item used
void *value; // the dictionary value of the last item used
// same as the return value of dictfe_start() and dictfe_next()
size_t counter; // counts the number of iterations made, starting from zero
char rw; // the lock mode 'r' or 'w'
+ bool locked; // true when the dictionary is locked
} DICTFE;
#define dfe_start_read(dict, value) dfe_start_rw(dict, value, DICTIONARY_LOCK_READ)
@@ -294,9 +297,12 @@ typedef DICTFE_CONST struct dictionary_foreach {
dictionary_foreach_done(&value ## _dfe); \
} while(0)
+#define dfe_unlock(value) dictionary_foreach_unlock(&value ## _dfe);
+
void *dictionary_foreach_start_rw(DICTFE *dfe, DICTIONARY *dict, char rw);
void *dictionary_foreach_next(DICTFE *dfe);
void dictionary_foreach_done(DICTFE *dfe);
+void dictionary_foreach_unlock(DICTFE *dfe);
// ----------------------------------------------------------------------------
// Get statistics about the dictionary
diff --git a/libnetdata/ebpf/README.md b/libnetdata/ebpf/README.md
index c2dabe102..bf2c6ff35 100644
--- a/libnetdata/ebpf/README.md
+++ b/libnetdata/ebpf/README.md
@@ -1,10 +1,10 @@
<!--
-title "eBPF"
+title: "eBPF"
custom_edit_url: https://github.com/netdata/netdata/edit/master/libnetdata/ebpf/README.md
sidebar_label: "eBPF"
learn_status: "Published"
learn_topic_type: "Tasks"
-learn_rel_path: "Developers/libnetdata libraries"
+learn_rel_path: "Developers/libnetdata"
-->
# eBPF library
diff --git a/libnetdata/ebpf/ebpf.c b/libnetdata/ebpf/ebpf.c
index 7cad59785..61833dd73 100644
--- a/libnetdata/ebpf/ebpf.c
+++ b/libnetdata/ebpf/ebpf.c
@@ -366,20 +366,22 @@ static uint32_t ebpf_select_index(uint32_t kernels, int is_rhf, uint32_t kver)
* V - The kernel version in string format.
*
* @param out the vector where the name will be stored
- * @param path
* @param len the size of the out vector.
+ * @param path where the binaries are stored
* @param kver the kernel version
* @param name the eBPF program name.
* @param is_return is return or entry ?
*/
-static void ebpf_mount_name(char *out, size_t len, char *path, uint32_t kver, const char *name, int is_return)
+static void ebpf_mount_name(char *out, size_t len, char *path, uint32_t kver, const char *name,
+ int is_return, int is_rhf)
{
char *version = ebpf_select_kernel_name(kver);
- snprintfz(out, len, "%s/ebpf.d/%cnetdata_ebpf_%s.%s.o",
+ snprintfz(out, len, "%s/ebpf.d/%cnetdata_ebpf_%s.%s%s.o",
path,
(is_return) ? 'r' : 'p',
name,
- version);
+ version,
+ (is_rhf != -1) ? ".rhf" : "");
}
//----------------------------------------------------------------------------------------------------------------------
@@ -439,7 +441,7 @@ void ebpf_update_stats(ebpf_plugin_stats_t *report, ebpf_module_t *em)
report->threads++;
// It is not necessary to report more information.
- if (!em->enabled)
+ if (em->enabled != NETDATA_THREAD_EBPF_RUNNING)
return;
report->running++;
@@ -454,6 +456,91 @@ void ebpf_update_stats(ebpf_plugin_stats_t *report, ebpf_module_t *em)
ebpf_stats_targets(report, em->targets);
}
+/**
+ * Update Kernel memory with memory
+ *
+ * This algorithm is an adaptation of https://elixir.bootlin.com/linux/v6.1.14/source/tools/bpf/bpftool/common.c#L402
+ * to get 'memlock' data and update report.
+ *
+ * @param report the output structure
+ * @param map pointer to a map.
+ * @param action What action will be done with this map.
+ */
+void ebpf_update_kernel_memory(ebpf_plugin_stats_t *report, ebpf_local_maps_t *map, ebpf_stats_action_t action)
+{
+ char filename[FILENAME_MAX+1];
+ snprintfz(filename, FILENAME_MAX, "/proc/self/fdinfo/%d", map->map_fd);
+ procfile *ff = procfile_open(filename, " \t", PROCFILE_FLAG_DEFAULT);
+ if(unlikely(!ff)) {
+ error("Cannot open %s", filename);
+ return;
+ }
+
+ ff = procfile_readall(ff);
+ if(unlikely(!ff))
+ return;
+
+ unsigned long j, lines = procfile_lines(ff);
+ char *memlock = { "memlock" };
+ for (j = 0; j < lines ; j++) {
+ char *cmp = procfile_lineword(ff, j,0);
+ if (!strncmp(memlock, cmp, 7)) {
+ uint64_t memsize = (uint64_t) str2l(procfile_lineword(ff, j,1));
+ switch (action) {
+ case EBPF_ACTION_STAT_ADD: {
+ report->memlock_kern += memsize;
+ report->hash_tables += 1;
+#ifdef NETDATA_DEV_MODE
+ info("Hash table %u: %s (FD = %d) is consuming %lu bytes totalizing %lu bytes",
+ report->hash_tables, map->name, map->map_fd, memsize, report->memlock_kern);
+#endif
+ break;
+ }
+ case EBPF_ACTION_STAT_REMOVE: {
+ report->memlock_kern -= memsize;
+ report->hash_tables -= 1;
+#ifdef NETDATA_DEV_MODE
+ info("Hash table %s (FD = %d) was removed releasing %lu bytes, now we have %u tables loaded totalizing %lu bytes.",
+ map->name, map->map_fd, memsize, report->hash_tables, report->memlock_kern);
+#endif
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ procfile_close(ff);
+}
+
+/**
+ * Update Kernel memory with memory
+ *
+ * This algorithm is an adaptation of https://elixir.bootlin.com/linux/v6.1.14/source/tools/bpf/bpftool/common.c#L402
+ * to get 'memlock' data and update report.
+ *
+ * @param report the output structure
+ * @param map pointer to a map. Last map must fish with name = NULL
+ */
+void ebpf_update_kernel_memory_with_vector(ebpf_plugin_stats_t *report, ebpf_local_maps_t *maps)
+{
+ if (!maps)
+ return;
+
+ ebpf_local_maps_t *map;
+ int i = 0;
+ for (map = &maps[i]; maps[i].name; i++, map = &maps[i]) {
+ int fd = map->map_fd;
+ if (fd == ND_EBPF_MAP_FD_NOT_INITIALIZED)
+ continue;
+
+ ebpf_update_kernel_memory(report, map, EBPF_ACTION_STAT_ADD);
+ }
+}
+
//----------------------------------------------------------------------------------------------------------------------
void ebpf_update_pid_table(ebpf_local_maps_t *pid, ebpf_module_t *em)
@@ -696,7 +783,7 @@ struct bpf_link **ebpf_load_program(char *plugins_dir, ebpf_module_t *em, int kv
uint32_t idx = ebpf_select_index(em->kernels, is_rhf, kver);
- ebpf_mount_name(lpath, 4095, plugins_dir, idx, em->thread_name, em->mode);
+ ebpf_mount_name(lpath, 4095, plugins_dir, idx, em->thread_name, em->mode, is_rhf);
// When this function is called ebpf.plugin is using legacy code, so we should reset the variable
em->load &= ~ NETDATA_EBPF_LOAD_METHODS;
diff --git a/libnetdata/ebpf/ebpf.h b/libnetdata/ebpf/ebpf.h
index cf3fa7ccd..bf5fdc33d 100644
--- a/libnetdata/ebpf/ebpf.h
+++ b/libnetdata/ebpf/ebpf.h
@@ -10,6 +10,7 @@
#include <linux/btf.h>
#endif
#include <stdlib.h> // Necessary for stdtoul
+#include "libnetdata/aral/aral.h"
#define NETDATA_DEBUGFS "/sys/kernel/debug/tracing/"
#define NETDATA_KALLSYMS "/proc/kallsyms"
@@ -238,18 +239,38 @@ typedef struct ebpf_plugin_stats {
uint32_t retprobes; // Number of kretprobes loaded
uint32_t tracepoints; // Number of tracepoints used
uint32_t trampolines; // Number of trampolines used
+
+ uint64_t memlock_kern; // The same information reported by bpftool, but it is not accurated
+ // https://lore.kernel.org/linux-mm/20230112155326.26902-5-laoar.shao@gmail.com/T/
+ uint32_t hash_tables; // Number of hash tables used on the system.
} ebpf_plugin_stats_t;
+typedef enum ebpf_stats_action {
+ EBPF_ACTION_STAT_ADD,
+ EBPF_ACTION_STAT_REMOVE,
+} ebpf_stats_action_t;
+
typedef enum netdata_apps_integration_flags {
NETDATA_EBPF_APPS_FLAG_NO,
NETDATA_EBPF_APPS_FLAG_YES,
NETDATA_EBPF_APPS_FLAG_CHART_CREATED
} netdata_apps_integration_flags_t;
+#define NETDATA_EBPF_CHART_MEM_LENGTH 48
+#define NETDATA_EBPF_STAT_DIMENSION_MEMORY "memory"
+#define NETDATA_EBPF_STAT_DIMENSION_ARAL "aral"
+
+enum ebpf_threads_status {
+ NETDATA_THREAD_EBPF_RUNNING,
+ NETDATA_THREAD_EBPF_STOPPING,
+ NETDATA_THREAD_EBPF_STOPPED,
+ NETDATA_THREAD_EBPF_NOT_RUNNING
+};
+
typedef struct ebpf_module {
const char *thread_name;
const char *config_name;
- int enabled;
+ enum ebpf_threads_status enabled;
void *(*start_routine)(void *);
int update_every;
int global_charts;
@@ -271,6 +292,10 @@ typedef struct ebpf_module {
struct bpf_link **probe_links;
struct bpf_object *objects;
struct netdata_static_thread *thread;
+
+ // charts
+ char memory_usage[NETDATA_EBPF_CHART_MEM_LENGTH];
+ char memory_allocations[NETDATA_EBPF_CHART_MEM_LENGTH];
} ebpf_module_t;
int ebpf_get_kernel_version();
@@ -368,4 +393,9 @@ struct btf *ebpf_load_btf_file(char *path, char *filename);
int ebpf_is_function_inside_btf(struct btf *file, char *function);
#endif
+void ebpf_update_kernel_memory_with_vector(ebpf_plugin_stats_t *report, ebpf_local_maps_t *maps);
+void ebpf_update_kernel_memory(ebpf_plugin_stats_t *report, ebpf_local_maps_t *map, ebpf_stats_action_t action);
+void ebpf_statistic_create_aral_chart(char *name, ebpf_module_t *em);
+void ebpf_send_data_aral_chart(ARAL *memory, ebpf_module_t *em);
+
#endif /* NETDATA_EBPF_H */
diff --git a/libnetdata/health/health.c b/libnetdata/health/health.c
index c44ba0828..d5403cefa 100644
--- a/libnetdata/health/health.c
+++ b/libnetdata/health/health.c
@@ -81,19 +81,19 @@ SILENCER *health_silencers_addparam(SILENCER *silencer, char *key, char *value)
if (hash == hash_alarm && !strcasecmp(key, HEALTH_ALARM_KEY)) {
silencer->alarms = strdupz(value);
- silencer->alarms_pattern = simple_pattern_create(silencer->alarms, NULL, SIMPLE_PATTERN_EXACT);
+ silencer->alarms_pattern = simple_pattern_create(silencer->alarms, NULL, SIMPLE_PATTERN_EXACT, true);
} else if (hash == hash_chart && !strcasecmp(key, HEALTH_CHART_KEY)) {
silencer->charts = strdupz(value);
- silencer->charts_pattern = simple_pattern_create(silencer->charts, NULL, SIMPLE_PATTERN_EXACT);
+ silencer->charts_pattern = simple_pattern_create(silencer->charts, NULL, SIMPLE_PATTERN_EXACT, true);
} else if (hash == hash_context && !strcasecmp(key, HEALTH_CONTEXT_KEY)) {
silencer->contexts = strdupz(value);
- silencer->contexts_pattern = simple_pattern_create(silencer->contexts, NULL, SIMPLE_PATTERN_EXACT);
+ silencer->contexts_pattern = simple_pattern_create(silencer->contexts, NULL, SIMPLE_PATTERN_EXACT, true);
} else if (hash == hash_host && !strcasecmp(key, HEALTH_HOST_KEY)) {
silencer->hosts = strdupz(value);
- silencer->hosts_pattern = simple_pattern_create(silencer->hosts, NULL, SIMPLE_PATTERN_EXACT);
+ silencer->hosts_pattern = simple_pattern_create(silencer->hosts, NULL, SIMPLE_PATTERN_EXACT, true);
} else if (hash == hash_families && !strcasecmp(key, HEALTH_FAMILIES_KEY)) {
silencer->families = strdupz(value);
- silencer->families_pattern = simple_pattern_create(silencer->families, NULL, SIMPLE_PATTERN_EXACT);
+ silencer->families_pattern = simple_pattern_create(silencer->families, NULL, SIMPLE_PATTERN_EXACT, true);
}
return silencer;
diff --git a/libnetdata/inlined.h b/libnetdata/inlined.h
index aa7f3c213..2697b9a03 100644
--- a/libnetdata/inlined.h
+++ b/libnetdata/inlined.h
@@ -7,147 +7,391 @@
#ifdef KERNEL_32BIT
typedef uint32_t kernel_uint_t;
-#define str2kernel_uint_t(string) str2uint32_t(string)
+#define str2kernel_uint_t(string) str2uint32_t(string, NULL)
#define KERNEL_UINT_FORMAT "%u"
#else
typedef uint64_t kernel_uint_t;
-#define str2kernel_uint_t(string) str2uint64_t(string)
+#define str2kernel_uint_t(string) str2uint64_t(string, NULL)
#define KERNEL_UINT_FORMAT "%" PRIu64
#endif
-#define str2pid_t(string) str2uint32_t(string)
+#define str2pid_t(string) str2uint32_t(string, NULL)
// for faster execution, allow the compiler to inline
// these functions that are called thousands of times per second
-static inline uint32_t simple_hash(const char *name) {
+static inline uint32_t djb2_hash32(const char* name) {
unsigned char *s = (unsigned char *) name;
- uint32_t hval = 0x811c9dc5;
+ uint32_t hash = 5381;
+ while (*s)
+ hash = ((hash << 5) + hash) + (uint32_t) *s++; // hash * 33 + char
+ return hash;
+}
+
+static inline uint32_t pluginsd_parser_hash32(const char *name) {
+ unsigned char *s = (unsigned char *) name;
+ uint32_t hash = 0;
while (*s) {
- hval *= 16777619;
- hval ^= (uint32_t) *s++;
+ hash <<= 5;
+ hash += *s++ - ' ';
}
- return hval;
+ return hash;
}
-static inline uint32_t simple_uhash(const char *name) {
+// https://stackoverflow.com/a/107657
+static inline uint32_t larson_hash32(const char *name) {
unsigned char *s = (unsigned char *) name;
- uint32_t hval = 0x811c9dc5, c;
+ uint32_t hash = 0;
+ while (*s)
+ hash = hash * 101 + (uint32_t) *s++;
+ return hash;
+}
+
+// http://isthe.com/chongo/tech/comp/fnv/
+static inline uint32_t fnv1_hash32(const char *name) {
+ unsigned char *s = (unsigned char *) name;
+ uint32_t hash = 0x811c9dc5;
+ while (*s) {
+ hash *= 0x01000193; // 16777619
+ hash ^= (uint32_t) *s++;
+ }
+ return hash;
+}
+
+// http://isthe.com/chongo/tech/comp/fnv/
+static inline uint32_t fnv1a_hash32(const char *name) {
+ unsigned char *s = (unsigned char *) name;
+ uint32_t hash = 0x811c9dc5;
+ while (*s) {
+ hash ^= (uint32_t) *s++;
+ hash *= 0x01000193; // 16777619
+ }
+ return hash;
+}
+
+static inline uint32_t fnv1a_uhash32(const char *name) {
+ unsigned char *s = (unsigned char *) name;
+ uint32_t hash = 0x811c9dc5, c;
while ((c = *s++)) {
if (unlikely(c >= 'A' && c <= 'Z')) c += 'a' - 'A';
- hval *= 16777619;
- hval ^= c;
+ hash ^= c;
+ hash *= 0x01000193; // 16777619
}
- return hval;
+ return hash;
}
-static inline int str2i(const char *s) {
- int n = 0;
- char c, negative = (char)(*s == '-');
- const char *e = s + 30; // max number of character to iterate
+#define simple_hash(s) fnv1a_hash32(s)
+#define simple_uhash(s) fnv1a_uhash32(s)
- for(c = (char)((negative)?*(++s):*s); c >= '0' && c <= '9' && s < e ; c = *(++s)) {
- n *= 10;
- n += c - '0';
- }
+static uint32_t murmur32(uint32_t k) __attribute__((const));
+static inline uint32_t murmur32(uint32_t k) {
+ k ^= k >> 16;
+ k *= 0x85ebca6b;
+ k ^= k >> 13;
+ k *= 0xc2b2ae35;
+ k ^= k >> 16;
- if(unlikely(negative))
- return -n;
+ return k;
+}
- return n;
+static uint64_t murmur64(uint64_t k) __attribute__((const));
+static inline uint64_t murmur64(uint64_t k) {
+ k ^= k >> 33;
+ k *= 0xff51afd7ed558ccdUL;
+ k ^= k >> 33;
+ k *= 0xc4ceb9fe1a85ec53UL;
+ k ^= k >> 33;
+
+ return k;
}
-static inline long str2l(const char *s) {
- long n = 0;
- char c, negative = (*s == '-');
- const char *e = &s[30]; // max number of character to iterate
+static inline size_t indexing_partition(Word_t ptr, Word_t modulo) __attribute__((const));
+static inline size_t indexing_partition(Word_t ptr, Word_t modulo) {
+#ifdef ENV64BIT
+ uint64_t hash = murmur64(ptr);
+ return hash % modulo;
+#else
+ uint32_t hash = murmur32(ptr);
+ return hash % modulo;
+#endif
+}
+
+static inline unsigned int str2u(const char *s) {
+ unsigned int n = 0;
- for(c = (negative)?*(++s):*s; c >= '0' && c <= '9' && s < e ; c = *(++s)) {
- n *= 10;
- n += c - '0';
+ while(*s >= '0' && *s <= '9')
+ n = n * 10 + (*s++ - '0');
+
+ return n;
+}
+
+static inline int str2i(const char *s) {
+ if(unlikely(*s == '-')) {
+ s++;
+ return -(int) str2u(s);
+ }
+ else {
+ if(unlikely(*s == '+')) s++;
+ return (int) str2u(s);
}
+}
- if(unlikely(negative))
- return -n;
+static inline unsigned long str2ul(const char *s) {
+ unsigned long n = 0;
+
+ while(*s >= '0' && *s <= '9')
+ n = n * 10 + (*s++ - '0');
return n;
}
-static inline uint32_t str2uint32_t(const char *s) {
+static inline long str2l(const char *s) {
+ if(unlikely(*s == '-')) {
+ s++;
+ return -(long) str2ul(s);
+ }
+ else {
+ if(unlikely(*s == '+')) s++;
+ return (long) str2ul(s);
+ }
+}
+
+static inline uint32_t str2uint32_t(const char *s, char **endptr) {
uint32_t n = 0;
- char c;
- const char *e = &s[30]; // max number of character to iterate
- for(c = *s; c >= '0' && c <= '9' && s < e ; c = *(++s)) {
- n *= 10;
- n += c - '0';
- }
+ while(*s >= '0' && *s <= '9')
+ n = n * 10 + (*s++ - '0');
+
+ if(unlikely(endptr))
+ *endptr = (char *)s;
+
return n;
}
-static inline uint64_t str2uint64_t(const char *s) {
+static inline uint64_t str2uint64_t(const char *s, char **endptr) {
uint64_t n = 0;
- char c;
- const char *e = &s[30]; // max number of character to iterate
- for(c = *s; c >= '0' && c <= '9' && s < e ; c = *(++s)) {
- n *= 10;
- n += c - '0';
- }
+#ifdef ENV32BIT
+ unsigned long n32 = 0;
+ while (*s >= '0' && *s <= '9' && n32 < (ULONG_MAX / 10))
+ n32 = n32 * 10 + (*s++ - '0');
+
+ n = n32;
+#endif
+
+ while(*s >= '0' && *s <= '9')
+ n = n * 10 + (*s++ - '0');
+
+ if(unlikely(endptr))
+ *endptr = (char *)s;
+
return n;
}
-static inline unsigned long str2ul(const char *s) {
- unsigned long n = 0;
- char c;
- const char *e = &s[30]; // max number of character to iterate
+static inline unsigned long long int str2ull(const char *s, char **endptr) {
+ return str2uint64_t(s, endptr);
+}
- for(c = *s; c >= '0' && c <= '9' && s < e ; c = *(++s)) {
- n *= 10;
- n += c - '0';
+static inline long long str2ll(const char *s, char **endptr) {
+ if(unlikely(*s == '-')) {
+ s++;
+ return -(long long) str2uint64_t(s, endptr);
}
- return n;
+ else {
+ if(unlikely(*s == '+')) s++;
+ return (long long) str2uint64_t(s, endptr);
+ }
+}
+
+static inline uint64_t str2uint64_hex(const char *src, char **endptr) {
+ uint64_t num = 0;
+ const unsigned char *s = (const unsigned char *)src;
+ unsigned char c;
+
+ while ((c = hex_value_from_ascii[*s]) != 255) {
+ num = (num << 4) | c;
+ s++;
+ }
+
+ if(endptr)
+ *endptr = (char *)s;
+
+ return num;
}
-static inline unsigned long long str2ull(const char *s) {
- unsigned long long n = 0;
- char c;
- const char *e = &s[30]; // max number of character to iterate
+static inline uint64_t str2uint64_base64(const char *src, char **endptr) {
+ uint64_t num = 0;
+ const unsigned char *s = (const unsigned char *)src;
+ unsigned char c;
+
+ while ((c = base64_value_from_ascii[*s]) != 255) {
+ num = (num << 6) | c;
+ s++;
+ }
+
+ if(endptr)
+ *endptr = (char *)s;
+
+ return num;
+}
+
+static inline NETDATA_DOUBLE str2ndd_parse_double_decimal_digits_internal(const char *src, int *digits) {
+ const char *s = src;
+ NETDATA_DOUBLE n = 0.0;
+
+ while(*s >= '0' && *s <= '9') {
+
+ // this works for both 32-bit and 64-bit systems
+ unsigned long ni = 0;
+ unsigned exponent = 0;
+ while (*s >= '0' && *s <= '9' && ni < (ULONG_MAX / 10)) {
+ ni = (ni * 10) + (*s++ - '0');
+ exponent++;
+ }
- for(c = *s; c >= '0' && c <= '9' && s < e ; c = *(++s)) {
- n *= 10;
- n += c - '0';
+ n = n * powndd(10.0, exponent) + (NETDATA_DOUBLE)ni;
}
+
+ *digits = (int)(s - src);
return n;
}
-static inline long long str2ll(const char *s, char **endptr) {
- int negative = 0;
+static inline NETDATA_DOUBLE str2ndd(const char *src, char **endptr) {
+ const char *s = src;
+
+ NETDATA_DOUBLE sign = 1.0;
+ NETDATA_DOUBLE result;
+ int integral_digits = 0;
+
+ NETDATA_DOUBLE fractional = 0.0;
+ int fractional_digits = 0;
+
+ NETDATA_DOUBLE exponent = 0.0;
+ int exponent_digits = 0;
+
+ switch(*s) {
+ case '-':
+ s++;
+ sign = -1.0;
+ break;
+
+ case '+':
+ s++;
+ break;
+
+ case 'n':
+ if(s[1] == 'a' && s[2] == 'n') {
+ if(endptr) *endptr = (char *)&s[3];
+ return NAN;
+ }
+ if(s[1] == 'u' && s[2] == 'l' && s[3] == 'l') {
+ if(endptr) *endptr = (char *)&s[3];
+ return NAN;
+ }
+ break;
+
+ case 'i':
+ if(s[1] == 'n' && s[2] == 'f') {
+ if(endptr) *endptr = (char *)&s[3];
+ return INFINITY;
+ }
+ break;
+
+ default:
+ break;
+ }
- if(unlikely(*s == '-')) {
+ result = str2ndd_parse_double_decimal_digits_internal(s, &integral_digits);
+ s += integral_digits;
+
+ if(unlikely(*s == '.')) {
s++;
- negative = 1;
+ fractional = str2ndd_parse_double_decimal_digits_internal(s, &fractional_digits);
+ s += fractional_digits;
}
- else if(unlikely(*s == '+'))
+
+ if (unlikely(*s == 'e' || *s == 'E')) {
+ const char *e_ptr = s;
s++;
- long long n = 0;
- char c;
- const char *e = &s[30]; // max number of character to iterate
+ int exponent_sign = 1;
+ if (*s == '-') {
+ exponent_sign = -1;
+ s++;
+ }
+ else if(*s == '+')
+ s++;
- for(c = *s; c >= '0' && c <= '9' && s < e ; c = *(++s)) {
- n *= 10;
- n += c - '0';
+ exponent = str2ndd_parse_double_decimal_digits_internal(s, &exponent_digits);
+ if(unlikely(!exponent_digits)) {
+ exponent = 0;
+ s = e_ptr;
+ }
+ else {
+ s += exponent_digits;
+ exponent *= exponent_sign;
+ }
}
if(unlikely(endptr))
*endptr = (char *)s;
- if(unlikely(negative))
- return -n;
+ if (unlikely(exponent_digits))
+ result *= powndd(10.0, exponent);
+
+ if (unlikely(fractional_digits))
+ result += fractional / powndd(10.0, fractional_digits) * (exponent_digits ? powndd(10.0, exponent) : 1.0);
+
+ return sign * result;
+}
+
+static inline unsigned long long str2ull_encoded(const char *s) {
+ if(*s == IEEE754_UINT64_B64_PREFIX[0])
+ return str2uint64_base64(s + sizeof(IEEE754_UINT64_B64_PREFIX) - 1, NULL);
+
+ if(s[0] == HEX_PREFIX[0] && s[1] == HEX_PREFIX[1])
+ return str2uint64_hex(s + 2, NULL);
+
+ return str2uint64_t(s, NULL);
+}
+
+static inline long long str2ll_encoded(const char *s) {
+ if(*s == '-')
+ return -(long long) str2ull_encoded(&s[1]);
else
- return n;
+ return (long long) str2ull_encoded(s);
+}
+
+static inline NETDATA_DOUBLE str2ndd_encoded(const char *src, char **endptr) {
+ if (*src == IEEE754_DOUBLE_B64_PREFIX[0]) {
+ // double parsing from base64
+ uint64_t n = str2uint64_base64(src + sizeof(IEEE754_DOUBLE_B64_PREFIX) - 1, endptr);
+ NETDATA_DOUBLE *ptr = (NETDATA_DOUBLE *) (&n);
+ return *ptr;
+ }
+
+ if (*src == IEEE754_DOUBLE_HEX_PREFIX[0]) {
+ // double parsing from hex
+ uint64_t n = str2uint64_hex(src + sizeof(IEEE754_DOUBLE_HEX_PREFIX) - 1, endptr);
+ NETDATA_DOUBLE *ptr = (NETDATA_DOUBLE *) (&n);
+ return *ptr;
+ }
+
+ double sign = 1.0;
+
+ if(*src == '-') {
+ sign = -1.0;
+ src++;
+ }
+
+ if(unlikely(*src == IEEE754_UINT64_B64_PREFIX[0]))
+ return (NETDATA_DOUBLE) str2uint64_base64(src + sizeof(IEEE754_UINT64_B64_PREFIX) - 1, endptr) * sign;
+
+ if(unlikely(*src == HEX_PREFIX[0] && src[1] == HEX_PREFIX[1]))
+ return (NETDATA_DOUBLE) str2uint64_hex(src + sizeof(HEX_PREFIX) - 1, endptr) * sign;
+
+ return str2ndd(src, endptr) * sign;
}
static inline char *strncpyz(char *dst, const char *src, size_t n) {
@@ -248,7 +492,7 @@ static inline int read_single_number_file(const char *filename, unsigned long lo
}
buffer[30] = '\0';
- *result = str2ull(buffer);
+ *result = str2ull(buffer, NULL);
return 0;
}
@@ -266,4 +510,66 @@ static inline int read_single_signed_number_file(const char *filename, long long
return 0;
}
+static inline int uuid_memcmp(const uuid_t *uu1, const uuid_t *uu2) {
+ return memcmp(uu1, uu2, sizeof(uuid_t));
+}
+
+static inline char *strsep_skip_consecutive_separators(char **ptr, char *s) {
+ char *p = (char *)"";
+ while (p && !p[0] && *ptr) p = strsep(ptr, s);
+ return (p);
+}
+
+// remove leading and trailing spaces; may return NULL
+static inline char *trim(char *s) {
+ // skip leading spaces
+ while (*s && isspace(*s)) s++;
+ if (!*s) return NULL;
+
+ // skip tailing spaces
+ // this way is way faster. Writes only one NUL char.
+ ssize_t l = (ssize_t)strlen(s);
+ if (--l >= 0) {
+ char *p = s + l;
+ while (p > s && isspace(*p)) p--;
+ *++p = '\0';
+ }
+
+ if (!*s) return NULL;
+
+ return s;
+}
+
+// like trim(), but also remove duplicate spaces inside the string; may return NULL
+static inline char *trim_all(char *buffer) {
+ char *d = buffer, *s = buffer;
+
+ // skip spaces
+ while(isspace(*s)) s++;
+
+ while(*s) {
+ // copy the non-space part
+ while(*s && !isspace(*s)) *d++ = *s++;
+
+ // add a space if we have to
+ if(*s && isspace(*s)) {
+ *d++ = ' ';
+ s++;
+ }
+
+ // skip spaces
+ while(isspace(*s)) s++;
+ }
+
+ *d = '\0';
+
+ if(d > buffer) {
+ d--;
+ if(isspace(*d)) *d = '\0';
+ }
+
+ if(!buffer[0]) return NULL;
+ return buffer;
+}
+
#endif //NETDATA_INLINED_H
diff --git a/libnetdata/json/README.md b/libnetdata/json/README.md
index e772f114d..1c49739be 100644
--- a/libnetdata/json/README.md
+++ b/libnetdata/json/README.md
@@ -4,7 +4,7 @@ custom_edit_url: https://github.com/netdata/netdata/edit/master/libnetdata/json/
sidebar_label: "json"
learn_status: "Published"
learn_topic_type: "Tasks"
-learn_rel_path: "Developers/libnetdata libraries"
+learn_rel_path: "Developers/libnetdata"
-->
# json
diff --git a/libnetdata/july/README.md b/libnetdata/july/README.md
index df2a3d38c..995849128 100644
--- a/libnetdata/july/README.md
+++ b/libnetdata/july/README.md
@@ -3,7 +3,7 @@ custom_edit_url: https://github.com/netdata/netdata/edit/master/libnetdata/july/
sidebar_label: "July interface"
learn_status: "Published"
learn_topic_type: "Tasks"
-learn_rel_path: "Developers/libnetdata libraries"
+learn_rel_path: "Developers/libnetdata"
-->
diff --git a/libnetdata/libjudy/src/JudyL/JudyLTablesGen.c b/libnetdata/libjudy/src/JudyL/JudyLTablesGen.c
index cb8b13ff7..ce4b37153 100644
--- a/libnetdata/libjudy/src/JudyL/JudyLTablesGen.c
+++ b/libnetdata/libjudy/src/JudyL/JudyLTablesGen.c
@@ -15,7 +15,7 @@
// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// _________________
-// @(#) $Revision: 4.37 $ $Source: /judy/src/JudyCommon/JudyTables.c $
+// @(#) $Revision: 4.37.1-netdata $ $Source: JudyTables.c $
#ifndef JU_WIN
#include <unistd.h> // unavailable on win_*.
@@ -30,7 +30,12 @@
#define TERMINATOR 999 // terminator for Alloc tables
-#define BPW sizeof(Word_t) // define bytes per word
+// define bytes per word
+#ifdef JU_64BIT
+#define BPW 8UL
+#else
+#define BPW 4UL
+#endif
#ifdef JUDY1
#include "Judy1.h"
@@ -199,7 +204,7 @@ FUNCTION int main()
}
- fprintf(fd,"// @(#) From generation tool: $Revision: 4.37 $ $Source: /judy/src/JudyCommon/JudyTables.c $\n");
+ fprintf(fd,"// @(#) From generation tool: $Revision: 4.37.1-netdata $ $Source: JudyTables.c $\n");
fprintf(fd,"//\n\n");
diff --git a/libnetdata/libnetdata.c b/libnetdata/libnetdata.c
index 666344a96..a8f26c33b 100644
--- a/libnetdata/libnetdata.c
+++ b/libnetdata/libnetdata.c
@@ -225,10 +225,6 @@ void posix_memfree(void *ptr) {
libc_free(ptr);
}
-#define MALLOC_ALIGNMENT (sizeof(uintptr_t) * 2)
-#define size_t_atomic_count(op, var, size) __atomic_## op ##_fetch(&(var), size, __ATOMIC_RELAXED)
-#define size_t_atomic_bytes(op, var, size) __atomic_## op ##_fetch(&(var), ((size) % MALLOC_ALIGNMENT)?((size) + MALLOC_ALIGNMENT - ((size) % MALLOC_ALIGNMENT)):(size), __ATOMIC_RELAXED)
-
struct malloc_header_signature {
uint32_t magic;
uint32_t size;
@@ -1046,171 +1042,6 @@ void netdata_fix_chart_id(char *s) {
while ((*s = netdata_map_chart_ids[(unsigned char) *s])) s++;
}
-/*
-// http://stackoverflow.com/questions/7666509/hash-function-for-string
-uint32_t simple_hash(const char *name)
-{
- const char *s = name;
- uint32_t hash = 5381;
- int i;
-
- while((i = *s++)) hash = ((hash << 5) + hash) + i;
-
- // fprintf(stderr, "HASH: %lu %s\n", hash, name);
-
- return hash;
-}
-*/
-
-/*
-// http://isthe.com/chongo/tech/comp/fnv/#FNV-1a
-uint32_t simple_hash(const char *name) {
- unsigned char *s = (unsigned char *) name;
- uint32_t hval = 0x811c9dc5;
-
- // FNV-1a algorithm
- while (*s) {
- // multiply by the 32 bit FNV magic prime mod 2^32
- // NOTE: No need to optimize with left shifts.
- // GCC will use imul instruction anyway.
- // Tested with 'gcc -O3 -S'
- //hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24);
- hval *= 16777619;
-
- // xor the bottom with the current octet
- hval ^= (uint32_t) *s++;
- }
-
- // fprintf(stderr, "HASH: %u = %s\n", hval, name);
- return hval;
-}
-
-uint32_t simple_uhash(const char *name) {
- unsigned char *s = (unsigned char *) name;
- uint32_t hval = 0x811c9dc5, c;
-
- // FNV-1a algorithm
- while ((c = *s++)) {
- if (unlikely(c >= 'A' && c <= 'Z')) c += 'a' - 'A';
- hval *= 16777619;
- hval ^= c;
- }
- return hval;
-}
-*/
-
-/*
-// http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx
-// one at a time hash
-uint32_t simple_hash(const char *name) {
- unsigned char *s = (unsigned char *)name;
- uint32_t h = 0;
-
- while(*s) {
- h += *s++;
- h += (h << 10);
- h ^= (h >> 6);
- }
-
- h += (h << 3);
- h ^= (h >> 11);
- h += (h << 15);
-
- // fprintf(stderr, "HASH: %u = %s\n", h, name);
-
- return h;
-}
-*/
-
-void strreverse(char *begin, char *end) {
- while (end > begin) {
- // clearer code.
- char aux = *end;
- *end-- = *begin;
- *begin++ = aux;
- }
-}
-
-char *strsep_on_1char(char **ptr, char c) {
- if(unlikely(!ptr || !*ptr))
- return NULL;
-
- // remember the position we started
- char *s = *ptr;
-
- // skip separators in front
- while(*s == c) s++;
- char *ret = s;
-
- // find the next separator
- while(*s++) {
- if(unlikely(*s == c)) {
- *s++ = '\0';
- *ptr = s;
- return ret;
- }
- }
-
- *ptr = NULL;
- return ret;
-}
-
-char *mystrsep(char **ptr, char *s) {
- char *p = "";
- while (p && !p[0] && *ptr) p = strsep(ptr, s);
- return (p);
-}
-
-char *trim(char *s) {
- // skip leading spaces
- while (*s && isspace(*s)) s++;
- if (!*s) return NULL;
-
- // skip tailing spaces
- // this way is way faster. Writes only one NUL char.
- ssize_t l = strlen(s);
- if (--l >= 0) {
- char *p = s + l;
- while (p > s && isspace(*p)) p--;
- *++p = '\0';
- }
-
- if (!*s) return NULL;
-
- return s;
-}
-
-inline char *trim_all(char *buffer) {
- char *d = buffer, *s = buffer;
-
- // skip spaces
- while(isspace(*s)) s++;
-
- while(*s) {
- // copy the non-space part
- while(*s && !isspace(*s)) *d++ = *s++;
-
- // add a space if we have to
- if(*s && isspace(*s)) {
- *d++ = ' ';
- s++;
- }
-
- // skip spaces
- while(isspace(*s)) s++;
- }
-
- *d = '\0';
-
- if(d > buffer) {
- d--;
- if(isspace(*d)) *d = '\0';
- }
-
- if(!buffer[0]) return NULL;
- return buffer;
-}
-
static int memory_file_open(const char *filename, size_t size) {
// info("memory_file_open('%s', %zu", filename, size);
@@ -1883,17 +1714,20 @@ inline int config_isspace(char c)
}
// split a text into words, respecting quotes
-inline size_t quoted_strings_splitter(char *str, char **words, size_t max_words, int (*custom_isspace)(char), char *recover_input, char **recover_location, int max_recover)
+inline size_t quoted_strings_splitter(char *str, char **words, size_t max_words, int (*custom_isspace)(char))
{
char *s = str, quote = 0;
size_t i = 0;
- int rec = 0;
- char *recover = recover_input;
// skip all white space
while (unlikely(custom_isspace(*s)))
s++;
+ if(unlikely(!*s)) {
+ words[i] = NULL;
+ return 0;
+ }
+
// check for quote
if (unlikely(*s == '\'' || *s == '"')) {
quote = *s; // remember the quote
@@ -1905,19 +1739,15 @@ inline size_t quoted_strings_splitter(char *str, char **words, size_t max_words,
// while we have something
while (likely(*s)) {
- // if it is escape
+ // if it is an escape
if (unlikely(*s == '\\' && s[1])) {
s += 2;
continue;
}
- // if it is quote
+ // if it is a quote
else if (unlikely(*s == quote)) {
quote = 0;
- if (recover && rec < max_recover) {
- recover_location[rec++] = s;
- *recover++ = *s;
- }
*s = ' ';
continue;
}
@@ -1925,19 +1755,13 @@ inline size_t quoted_strings_splitter(char *str, char **words, size_t max_words,
// if it is a space
else if (unlikely(quote == 0 && custom_isspace(*s))) {
// terminate the word
- if (recover && rec < max_recover) {
- if (!rec || recover_location[rec-1] != s) {
- recover_location[rec++] = s;
- *recover++ = *s;
- }
- }
*s++ = '\0';
// skip all white space
while (likely(custom_isspace(*s)))
s++;
- // check for quote
+ // check for a quote
if (unlikely(*s == '\'' || *s == '"')) {
quote = *s; // remember the quote
s++; // skip the quote
@@ -1965,9 +1789,9 @@ inline size_t quoted_strings_splitter(char *str, char **words, size_t max_words,
return i;
}
-inline size_t pluginsd_split_words(char *str, char **words, size_t max_words, char *recover_input, char **recover_location, int max_recover)
+inline size_t pluginsd_split_words(char *str, char **words, size_t max_words)
{
- return quoted_strings_splitter(str, words, max_words, pluginsd_space, recover_input, recover_location, max_recover);
+ return quoted_strings_splitter(str, words, max_words, pluginsd_space);
}
bool bitmap256_get_bit(BITMAP256 *ptr, uint8_t idx) {
@@ -2072,3 +1896,141 @@ void for_each_open_fd(OPEN_FD_ACTION action, OPEN_FD_EXCLUDE excluded_fds){
closedir(dir);
}
}
+
+struct timing_steps {
+ const char *name;
+ usec_t time;
+ size_t count;
+} timing_steps[TIMING_STEP_MAX + 1] = {
+ [TIMING_STEP_INTERNAL] = { .name = "internal", .time = 0, },
+
+ [TIMING_STEP_BEGIN2_PREPARE] = { .name = "BEGIN2 prepare", .time = 0, },
+ [TIMING_STEP_BEGIN2_FIND_CHART] = { .name = "BEGIN2 find chart", .time = 0, },
+ [TIMING_STEP_BEGIN2_PARSE] = { .name = "BEGIN2 parse", .time = 0, },
+ [TIMING_STEP_BEGIN2_ML] = { .name = "BEGIN2 ml", .time = 0, },
+ [TIMING_STEP_BEGIN2_PROPAGATE] = { .name = "BEGIN2 propagate", .time = 0, },
+ [TIMING_STEP_BEGIN2_STORE] = { .name = "BEGIN2 store", .time = 0, },
+
+ [TIMING_STEP_SET2_PREPARE] = { .name = "SET2 prepare", .time = 0, },
+ [TIMING_STEP_SET2_LOOKUP_DIMENSION] = { .name = "SET2 find dimension", .time = 0, },
+ [TIMING_STEP_SET2_PARSE] = { .name = "SET2 parse", .time = 0, },
+ [TIMING_STEP_SET2_ML] = { .name = "SET2 ml", .time = 0, },
+ [TIMING_STEP_SET2_PROPAGATE] = { .name = "SET2 propagate", .time = 0, },
+ [TIMING_STEP_RRDSET_STORE_METRIC] = { .name = "SET2 rrdset store", .time = 0, },
+ [TIMING_STEP_DBENGINE_FIRST_CHECK] = { .name = "db 1st check", .time = 0, },
+ [TIMING_STEP_DBENGINE_CHECK_DATA] = { .name = "db check data", .time = 0, },
+ [TIMING_STEP_DBENGINE_PACK] = { .name = "db pack", .time = 0, },
+ [TIMING_STEP_DBENGINE_PAGE_FIN] = { .name = "db page fin", .time = 0, },
+ [TIMING_STEP_DBENGINE_MRG_UPDATE] = { .name = "db mrg update", .time = 0, },
+ [TIMING_STEP_DBENGINE_PAGE_ALLOC] = { .name = "db page alloc", .time = 0, },
+ [TIMING_STEP_DBENGINE_CREATE_NEW_PAGE] = { .name = "db new page", .time = 0, },
+ [TIMING_STEP_DBENGINE_FLUSH_PAGE] = { .name = "db page flush", .time = 0, },
+ [TIMING_STEP_SET2_STORE] = { .name = "SET2 store", .time = 0, },
+
+ [TIMING_STEP_END2_PREPARE] = { .name = "END2 prepare", .time = 0, },
+ [TIMING_STEP_END2_PUSH_V1] = { .name = "END2 push v1", .time = 0, },
+ [TIMING_STEP_END2_ML] = { .name = "END2 ml", .time = 0, },
+ [TIMING_STEP_END2_RRDSET] = { .name = "END2 rrdset", .time = 0, },
+ [TIMING_STEP_END2_PROPAGATE] = { .name = "END2 propagate", .time = 0, },
+ [TIMING_STEP_END2_STORE] = { .name = "END2 store", .time = 0, },
+
+ // terminator
+ [TIMING_STEP_MAX] = { .name = NULL, .time = 0, },
+};
+
+void timing_action(TIMING_ACTION action, TIMING_STEP step) {
+ static __thread usec_t last_action_time = 0;
+ static struct timing_steps timings2[TIMING_STEP_MAX + 1] = {};
+
+ switch(action) {
+ case TIMING_ACTION_INIT:
+ last_action_time = now_monotonic_usec();
+ break;
+
+ case TIMING_ACTION_STEP: {
+ if(!last_action_time)
+ return;
+
+ usec_t now = now_monotonic_usec();
+ __atomic_add_fetch(&timing_steps[step].time, now - last_action_time, __ATOMIC_RELAXED);
+ __atomic_add_fetch(&timing_steps[step].count, 1, __ATOMIC_RELAXED);
+ last_action_time = now;
+ break;
+ }
+
+ case TIMING_ACTION_FINISH: {
+ if(!last_action_time)
+ return;
+
+ usec_t expected = __atomic_load_n(&timing_steps[TIMING_STEP_INTERNAL].time, __ATOMIC_RELAXED);
+ if(last_action_time - expected < 10 * USEC_PER_SEC) {
+ last_action_time = 0;
+ return;
+ }
+
+ if(!__atomic_compare_exchange_n(&timing_steps[TIMING_STEP_INTERNAL].time, &expected, last_action_time, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
+ last_action_time = 0;
+ return;
+ }
+
+ struct timing_steps timings3[TIMING_STEP_MAX + 1];
+ memcpy(timings3, timing_steps, sizeof(timings3));
+
+ size_t total_reqs = 0;
+ usec_t total_usec = 0;
+ for(size_t t = 1; t < TIMING_STEP_MAX ; t++) {
+ total_usec += timings3[t].time - timings2[t].time;
+ total_reqs += timings3[t].count - timings2[t].count;
+ }
+
+ BUFFER *wb = buffer_create(1024, NULL);
+
+ for(size_t t = 1; t < TIMING_STEP_MAX ; t++) {
+ size_t requests = timings3[t].count - timings2[t].count;
+ if(!requests) continue;
+
+ buffer_sprintf(wb, "TIMINGS REPORT: [%3zu. %-20s]: # %10zu, t %11.2f ms (%6.2f %%), avg %6.2f usec/run\n",
+ t,
+ timing_steps[t].name ? timing_steps[t].name : "x",
+ requests,
+ (double) (timings3[t].time - timings2[t].time) / (double)USEC_PER_MS,
+ (double) (timings3[t].time - timings2[t].time) * 100.0 / (double) total_usec,
+ (double) (timings3[t].time - timings2[t].time) / (double)requests
+ );
+ }
+
+ info("TIMINGS REPORT:\n%sTIMINGS REPORT: total # %10zu, t %11.2f ms",
+ buffer_tostring(wb), total_reqs, (double)total_usec / USEC_PER_MS);
+
+ memcpy(timings2, timings3, sizeof(timings2));
+
+ last_action_time = 0;
+ buffer_free(wb);
+ }
+ }
+}
+
+int hash256_string(const unsigned char *string, size_t size, char *hash) {
+ EVP_MD_CTX *ctx;
+ ctx = EVP_MD_CTX_create();
+
+ if (!ctx)
+ return 0;
+
+ if (!EVP_DigestInit(ctx, EVP_sha256())) {
+ EVP_MD_CTX_destroy(ctx);
+ return 0;
+ }
+
+ if (!EVP_DigestUpdate(ctx, string, size)) {
+ EVP_MD_CTX_destroy(ctx);
+ return 0;
+ }
+
+ if (!EVP_DigestFinal(ctx, (unsigned char *)hash, NULL)) {
+ EVP_MD_CTX_destroy(ctx);
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/libnetdata/libnetdata.h b/libnetdata/libnetdata.h
index c504bd4bd..c24494930 100644
--- a/libnetdata/libnetdata.h
+++ b/libnetdata/libnetdata.h
@@ -32,6 +32,9 @@ extern "C" {
#define OS_FREEBSD 2
#define OS_MACOS 3
+#define MALLOC_ALIGNMENT (sizeof(uintptr_t) * 2)
+#define size_t_atomic_count(op, var, size) __atomic_## op ##_fetch(&(var), size, __ATOMIC_RELAXED)
+#define size_t_atomic_bytes(op, var, size) __atomic_## op ##_fetch(&(var), ((size) % MALLOC_ALIGNMENT)?((size) + MALLOC_ALIGNMENT - ((size) % MALLOC_ALIGNMENT)):(size), __ATOMIC_RELAXED)
// ----------------------------------------------------------------------------
// system include files for all netdata C programs
@@ -363,15 +366,131 @@ size_t judy_aral_structures(void);
// ---------------------------------------------------------------------------------------------
+#include "storage_number/storage_number.h"
+
+typedef struct storage_point {
+ NETDATA_DOUBLE min; // when count > 1, this is the minimum among them
+ NETDATA_DOUBLE max; // when count > 1, this is the maximum among them
+ NETDATA_DOUBLE sum; // the point sum - divided by count gives the average
+
+ // end_time - start_time = point duration
+ time_t start_time_s; // the time the point starts
+ time_t end_time_s; // the time the point ends
+
+ uint32_t count; // the number of original points aggregated
+ uint32_t anomaly_count; // the number of original points found anomalous
+
+ SN_FLAGS flags; // flags stored with the point
+} STORAGE_POINT;
+
+#define storage_point_unset(x) do { \
+ (x).min = (x).max = (x).sum = NAN; \
+ (x).count = 0; \
+ (x).anomaly_count = 0; \
+ (x).flags = SN_FLAG_NONE; \
+ (x).start_time_s = 0; \
+ (x).end_time_s = 0; \
+ } while(0)
+
+#define storage_point_empty(x, start_s, end_s) do { \
+ (x).min = (x).max = (x).sum = NAN; \
+ (x).count = 1; \
+ (x).anomaly_count = 0; \
+ (x).flags = SN_FLAG_NONE; \
+ (x).start_time_s = start_s; \
+ (x).end_time_s = end_s; \
+ } while(0)
+
+#define STORAGE_POINT_UNSET (STORAGE_POINT){ .min = NAN, .max = NAN, .sum = NAN, .count = 0, .anomaly_count = 0, .flags = SN_FLAG_NONE, .start_time_s = 0, .end_time_s = 0 }
+
+#define storage_point_is_unset(x) (!(x).count)
+#define storage_point_is_gap(x) (!netdata_double_isnumber((x).sum))
+#define storage_point_is_zero(x) (!(x).count || (netdata_double_is_zero((x).min) && netdata_double_is_zero((x).max) && netdata_double_is_zero((x).sum) && (x).anomaly_count == 0))
+
+#define storage_point_merge_to(dst, src) do { \
+ if(storage_point_is_unset(dst)) \
+ (dst) = (src); \
+ \
+ else if(!storage_point_is_unset(src) && \
+ !storage_point_is_gap(src)) { \
+ \
+ if((src).start_time_s < (dst).start_time_s) \
+ (dst).start_time_s = (src).start_time_s;\
+ \
+ if((src).end_time_s > (dst).end_time_s) \
+ (dst).end_time_s = (src).end_time_s; \
+ \
+ if((src).min < (dst).min) \
+ (dst).min = (src).min; \
+ \
+ if((src).max > (dst).max) \
+ (dst).max = (src).max; \
+ \
+ (dst).sum += (src).sum; \
+ \
+ (dst).count += (src).count; \
+ (dst).anomaly_count += (src).anomaly_count; \
+ \
+ (dst).flags |= (src).flags & SN_FLAG_RESET; \
+ } \
+} while(0)
+
+#define storage_point_add_to(dst, src) do { \
+ if(storage_point_is_unset(dst)) \
+ (dst) = (src); \
+ \
+ else if(!storage_point_is_unset(src) && \
+ !storage_point_is_gap(src)) { \
+ \
+ if((src).start_time_s < (dst).start_time_s) \
+ (dst).start_time_s = (src).start_time_s;\
+ \
+ if((src).end_time_s > (dst).end_time_s) \
+ (dst).end_time_s = (src).end_time_s; \
+ \
+ (dst).min += (src).min; \
+ (dst).max += (src).max; \
+ (dst).sum += (src).sum; \
+ \
+ (dst).count += (src).count; \
+ (dst).anomaly_count += (src).anomaly_count; \
+ \
+ (dst).flags |= (src).flags & SN_FLAG_RESET; \
+ } \
+} while(0)
+
+#define storage_point_make_positive(sp) do { \
+ if(!storage_point_is_unset(sp) && \
+ !storage_point_is_gap(sp)) { \
+ \
+ if(unlikely(signbit((sp).sum))) \
+ (sp).sum = -(sp).sum; \
+ \
+ if(unlikely(signbit((sp).min))) \
+ (sp).min = -(sp).min; \
+ \
+ if(unlikely(signbit((sp).max))) \
+ (sp).max = -(sp).max; \
+ \
+ if(unlikely((sp).min > (sp).max)) { \
+ NETDATA_DOUBLE t = (sp).min; \
+ (sp).min = (sp).max; \
+ (sp).max = t; \
+ } \
+ } \
+} while(0)
+
+#define storage_point_anomaly_rate(sp) \
+ (NETDATA_DOUBLE)(storage_point_is_unset(sp) ? 0.0 : (NETDATA_DOUBLE)((sp).anomaly_count) * 100.0 / (NETDATA_DOUBLE)((sp).count))
+
+#define storage_point_average_value(sp) \
+ ((sp).count ? (sp).sum / (NETDATA_DOUBLE)((sp).count) : 0.0)
+
+// ---------------------------------------------------------------------------------------------
void netdata_fix_chart_id(char *s);
void netdata_fix_chart_name(char *s);
-void strreverse(char* begin, char* end);
-char *mystrsep(char **ptr, char *s);
-char *trim(char *s); // remove leading and trailing spaces; may return NULL
-char *trim_all(char *buffer); // like trim(), but also remove duplicate spaces inside the string; may return NULL
-
int madvise_sequential(void *mem, size_t len);
int madvise_random(void *mem, size_t len);
int madvise_dontfork(void *mem, size_t len);
@@ -485,8 +604,8 @@ void bitmap256_set_bit(BITMAP256 *ptr, uint8_t idx, bool value);
int config_isspace(char c);
int pluginsd_space(char c);
-size_t quoted_strings_splitter(char *str, char **words, size_t max_words, int (*custom_isspace)(char), char *recover_input, char **recover_location, int max_recover);
-size_t pluginsd_split_words(char *str, char **words, size_t max_words, char *recover_string, char **recover_location, int max_recover);
+size_t quoted_strings_splitter(char *str, char **words, size_t max_words, int (*custom_isspace)(char));
+size_t pluginsd_split_words(char *str, char **words, size_t max_words);
static inline char *get_word(char **words, size_t num_words, size_t index) {
if (index >= num_words)
@@ -514,7 +633,6 @@ extern char *netdata_configured_host_prefix;
#include "libjudy/src/Judy.h"
#include "july/july.h"
#include "os.h"
-#include "storage_number/storage_number.h"
#include "threads/threads.h"
#include "buffer/buffer.h"
#include "locks/locks.h"
@@ -547,10 +665,11 @@ extern char *netdata_configured_host_prefix;
#include "libnetdata/aral/aral.h"
#include "onewayalloc/onewayalloc.h"
#include "worker_utilization/worker_utilization.h"
+#include "parser/parser.h"
+#include "yaml.h"
-// BEWARE: Outside of the C code this also exists in alarm-notify.sh
-#define DEFAULT_CLOUD_BASE_URL "https://api.netdata.cloud"
-#define DEFAULT_CLOUD_UI_URL "https://app.netdata.cloud"
+// BEWARE: this exists in alarm-notify.sh
+#define DEFAULT_CLOUD_BASE_URL "https://app.netdata.cloud"
#define RRD_STORAGE_TIERS 5
@@ -609,58 +728,61 @@ static inline PPvoid_t JudyLLastThenPrev(Pcvoid_t PArray, Word_t * PIndex, bool
return JudyLPrev(PArray, PIndex, PJE0);
}
-static inline size_t indexing_partition_old(Word_t ptr, Word_t modulo) {
- size_t total = 0;
-
- total += (ptr & 0xff) >> 0;
- total += (ptr & 0xff00) >> 8;
- total += (ptr & 0xff0000) >> 16;
- total += (ptr & 0xff000000) >> 24;
-
- if(sizeof(Word_t) > 4) {
- total += (ptr & 0xff00000000) >> 32;
- total += (ptr & 0xff0000000000) >> 40;
- total += (ptr & 0xff000000000000) >> 48;
- total += (ptr & 0xff00000000000000) >> 56;
- }
-
- return (total % modulo);
-}
-
-static uint32_t murmur32(uint32_t h) __attribute__((const));
-static inline uint32_t murmur32(uint32_t h) {
- h ^= h >> 16;
- h *= 0x85ebca6b;
- h ^= h >> 13;
- h *= 0xc2b2ae35;
- h ^= h >> 16;
-
- return h;
-}
-
-static uint64_t murmur64(uint64_t h) __attribute__((const));
-static inline uint64_t murmur64(uint64_t k) {
- k ^= k >> 33;
- k *= 0xff51afd7ed558ccdUL;
- k ^= k >> 33;
- k *= 0xc4ceb9fe1a85ec53UL;
- k ^= k >> 33;
-
- return k;
-}
+typedef enum {
+ TIMING_STEP_INTERNAL = 0,
+
+ TIMING_STEP_BEGIN2_PREPARE,
+ TIMING_STEP_BEGIN2_FIND_CHART,
+ TIMING_STEP_BEGIN2_PARSE,
+ TIMING_STEP_BEGIN2_ML,
+ TIMING_STEP_BEGIN2_PROPAGATE,
+ TIMING_STEP_BEGIN2_STORE,
+
+ TIMING_STEP_SET2_PREPARE,
+ TIMING_STEP_SET2_LOOKUP_DIMENSION,
+ TIMING_STEP_SET2_PARSE,
+ TIMING_STEP_SET2_ML,
+ TIMING_STEP_SET2_PROPAGATE,
+ TIMING_STEP_RRDSET_STORE_METRIC,
+ TIMING_STEP_DBENGINE_FIRST_CHECK,
+ TIMING_STEP_DBENGINE_CHECK_DATA,
+ TIMING_STEP_DBENGINE_PACK,
+ TIMING_STEP_DBENGINE_PAGE_FIN,
+ TIMING_STEP_DBENGINE_MRG_UPDATE,
+ TIMING_STEP_DBENGINE_PAGE_ALLOC,
+ TIMING_STEP_DBENGINE_CREATE_NEW_PAGE,
+ TIMING_STEP_DBENGINE_FLUSH_PAGE,
+ TIMING_STEP_SET2_STORE,
+
+ TIMING_STEP_END2_PREPARE,
+ TIMING_STEP_END2_PUSH_V1,
+ TIMING_STEP_END2_ML,
+ TIMING_STEP_END2_RRDSET,
+ TIMING_STEP_END2_PROPAGATE,
+ TIMING_STEP_END2_STORE,
+
+ // terminator
+ TIMING_STEP_MAX,
+} TIMING_STEP;
-static inline size_t indexing_partition(Word_t ptr, Word_t modulo) __attribute__((const));
-static inline size_t indexing_partition(Word_t ptr, Word_t modulo) {
- if(sizeof(Word_t) == 8) {
- uint64_t hash = murmur64(ptr);
- return hash % modulo;
- }
- else {
- uint32_t hash = murmur32(ptr);
- return hash % modulo;
- }
-}
+typedef enum {
+ TIMING_ACTION_INIT,
+ TIMING_ACTION_STEP,
+ TIMING_ACTION_FINISH,
+} TIMING_ACTION;
+
+#ifdef NETDATA_TIMING_REPORT
+#define timing_init() timing_action(TIMING_ACTION_INIT, TIMING_STEP_INTERNAL)
+#define timing_step(step) timing_action(TIMING_ACTION_STEP, step)
+#define timing_report() timing_action(TIMING_ACTION_FINISH, TIMING_STEP_INTERNAL)
+#else
+#define timing_init() debug_dummy()
+#define timing_step(step) debug_dummy()
+#define timing_report() debug_dummy()
+#endif
+void timing_action(TIMING_ACTION action, TIMING_STEP step);
+int hash256_string(const unsigned char *string, size_t size, char *hash);
# ifdef __cplusplus
}
# endif
diff --git a/libnetdata/locks/README.md b/libnetdata/locks/README.md
index 8810e3d17..5560832b7 100644
--- a/libnetdata/locks/README.md
+++ b/libnetdata/locks/README.md
@@ -4,9 +4,11 @@ custom_edit_url: https://github.com/netdata/netdata/edit/master/libnetdata/locks
sidebar_label: "Locks"
learn_status: "Published"
learn_topic_type: "Tasks"
-learn_rel_path: "Developers/libnetdata libraries"
+learn_rel_path: "Developers/libnetdata"
-->
+# Locks
+
## How to trace netdata locks
To enable tracing rwlocks in netdata, compile netdata by setting `CFLAGS="-DNETDATA_TRACE_RWLOCKS=1"`, like this:
diff --git a/libnetdata/log/README.md b/libnetdata/log/README.md
index 5f9e5bc7b..3684abd68 100644
--- a/libnetdata/log/README.md
+++ b/libnetdata/log/README.md
@@ -1,10 +1,10 @@
<!--
-title "Log"
+title: "Log"
custom_edit_url: https://github.com/netdata/netdata/edit/master/libnetdata/log/README.md
sidebar_label: "Log"
learn_status: "Published"
learn_topic_type: "Tasks"
-learn_rel_path: "Developers/libnetdata libraries"
+learn_rel_path: "Developers/libnetdata"
-->
# Log
diff --git a/libnetdata/log/log.c b/libnetdata/log/log.c
index 1dcdba9c2..6832d628d 100644
--- a/libnetdata/log/log.c
+++ b/libnetdata/log/log.c
@@ -582,9 +582,11 @@ void reopen_all_log_files() {
open_log_file(STDERR_FILENO, stderr, stdcollector_filename, &collector_log_syslog, 0, NULL);
if(stderr_filename) {
- log_lock();
- stderror = open_log_file(stdcollector_fd, stderror, stderr_filename, &error_log_syslog, 1, &stdcollector_fd);
- log_unlock();
+ // Netdata starts using stderr and if it has success to open file it redirects
+ FILE *fp = open_log_file(stdcollector_fd, stderror, stderr_filename,
+ &error_log_syslog, 1, &stdcollector_fd);
+ if (fp)
+ stderror = fp;
}
#ifdef ENABLE_ACLK
@@ -606,9 +608,10 @@ void open_all_log_files() {
open_log_file(STDOUT_FILENO, stdout, stdout_filename, &output_log_syslog, 0, NULL);
open_log_file(STDERR_FILENO, stderr, stdcollector_filename, &collector_log_syslog, 0, NULL);
- log_lock();
- stderror = open_log_file(stdcollector_fd, NULL, stderr_filename, &error_log_syslog, 1, &stdcollector_fd);
- log_unlock();
+ // Netdata starts using stderr and if it has success to open file it redirects
+ FILE *fp = open_log_file(stdcollector_fd, NULL, stderr_filename, &error_log_syslog, 1, &stdcollector_fd);
+ if (fp)
+ stderror = fp;
#ifdef ENABLE_ACLK
if(aclklog_enabled)
@@ -631,7 +634,7 @@ int error_log_limit(int reset) {
static time_t start = 0;
static unsigned long counter = 0, prevented = 0;
- FILE *fp = (!stderror) ? stderr : stderror;
+ FILE *fp = stderror;
// fprintf(fp, "FLOOD: counter=%lu, allowed=%lu, backup=%lu, period=%llu\n", counter, error_log_errors_per_period, error_log_errors_per_period_backup, (unsigned long long)error_log_throttle_period);
@@ -778,7 +781,7 @@ void debug_int( const char *file, const char *function, const unsigned long line
void info_int( int is_collector, const char *file __maybe_unused, const char *function __maybe_unused, const unsigned long line __maybe_unused, const char *fmt, ... )
{
va_list args;
- FILE *fp = (is_collector || !stderror) ? stderr : stderror;
+ FILE *fp = (is_collector) ? stderr : stderror;
log_lock();
@@ -838,7 +841,7 @@ static const char *strerror_result_string(const char *a, const char *b) { (void)
#endif
void error_limit_int(ERROR_LIMIT *erl, const char *prefix, const char *file __maybe_unused, const char *function __maybe_unused, const unsigned long line __maybe_unused, const char *fmt, ... ) {
- FILE *fp = (!stderror) ? stderr : stderror;
+ FILE *fp = stderror;
if(erl->sleep_ut)
sleep_usec(erl->sleep_ut);
@@ -907,7 +910,7 @@ void error_limit_int(ERROR_LIMIT *erl, const char *prefix, const char *file __ma
void error_int(int is_collector, const char *prefix, const char *file __maybe_unused, const char *function __maybe_unused, const unsigned long line __maybe_unused, const char *fmt, ... ) {
// save a copy of errno - just in case this function generates a new error
int __errno = errno;
- FILE *fp = (is_collector || !stderror) ? stderr : stderror;
+ FILE *fp = (is_collector) ? stderr : stderror;
va_list args;
@@ -972,7 +975,7 @@ static void print_call_stack(void) {
#endif
void fatal_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... ) {
- FILE *fp = (!stderror) ? stderr : stderror;
+ FILE *fp = stderror;
// save a copy of errno - just in case this function generates a new error
int __errno = errno;
diff --git a/libnetdata/onewayalloc/README.md b/libnetdata/onewayalloc/README.md
index 3fa0d9fd3..38d92cea6 100644
--- a/libnetdata/onewayalloc/README.md
+++ b/libnetdata/onewayalloc/README.md
@@ -1,10 +1,10 @@
<!--
title: "One Way Allocator"
-custom_edit_url: https://github.com/netdata/netdata/edit/master/libnetdata/onewayallocator/README.md
+custom_edit_url: "https://github.com/netdata/netdata/edit/master/libnetdata/onewayalloc/README.md"
sidebar_label: "One way allocator"
learn_status: "Published"
learn_topic_type: "Tasks"
-learn_rel_path: "Developers/libnetdata libraries"
+learn_rel_path: "Developers/libnetdata"
-->
# One Way Allocator
diff --git a/libnetdata/onewayalloc/onewayalloc.c b/libnetdata/onewayalloc/onewayalloc.c
index 2f007b189..489ce73d7 100644
--- a/libnetdata/onewayalloc/onewayalloc.c
+++ b/libnetdata/onewayalloc/onewayalloc.c
@@ -97,6 +97,10 @@ ONEWAYALLOC *onewayalloc_create(size_t size_hint) {
}
void *onewayalloc_mallocz(ONEWAYALLOC *owa, size_t size) {
+#ifdef FSANITIZE_ADDRESS
+ return mallocz(size);
+#endif
+
OWA_PAGE *head = (OWA_PAGE *)owa;
OWA_PAGE *page = head->last;
@@ -142,6 +146,11 @@ void *onewayalloc_memdupz(ONEWAYALLOC *owa, const void *src, size_t size) {
}
void onewayalloc_freez(ONEWAYALLOC *owa __maybe_unused, const void *ptr __maybe_unused) {
+#ifdef FSANITIZE_ADDRESS
+ freez((void *)ptr);
+ return;
+#endif
+
#ifdef NETDATA_INTERNAL_CHECKS
// allow the caller to call us for a mallocz() allocation
// so try to find it in our memory and if it is not there
diff --git a/libnetdata/parser/Makefile.am b/libnetdata/parser/Makefile.am
new file mode 100644
index 000000000..02fe3a314
--- /dev/null
+++ b/libnetdata/parser/Makefile.am
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+AUTOMAKE_OPTIONS = subdir-objects
+MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
+
+dist_noinst_DATA = \
+ README.md \
+ $(NULL)
+
diff --git a/libnetdata/parser/README.md b/libnetdata/parser/README.md
new file mode 100644
index 000000000..136c23c69
--- /dev/null
+++ b/libnetdata/parser/README.md
@@ -0,0 +1,28 @@
+<!--
+title: "Parser"
+custom_edit_url: https://github.com/netdata/netdata/blob/master/parser/README.md
+sidebar_label: "Parser"
+learn_status: "Published"
+learn_topic_type: "References"
+learn_rel_path: "Developers/Database"
+-->
+
+# Parser
+
+## Introduction
+
+Generic parser that is used to register keywords and a corresponding function that will be executed when that
+keyword is encountered in the command stream (either from plugins or via streaming)
+
+To use a parser do the following:
+
+1. Define a structure that will be used to share user state across calls (user defined `void *user`)
+2. Initialize the parser using `parser_init`
+3. Register keywords with their associated callback function using `parser_add_keyword`
+4. Start a loop for as long there is input (or parser_action returns error)
+ 1. Fetch the next line using `parser_next` (if needed)
+ 2. Process the line using `parser_action`
+5. Release the parser using `parser_destroy`
+6. Release the user structure
+
+See examples in receiver.c / pluginsd_parser.c
diff --git a/libnetdata/parser/parser.c b/libnetdata/parser/parser.c
new file mode 100644
index 000000000..c3eebcd16
--- /dev/null
+++ b/libnetdata/parser/parser.c
@@ -0,0 +1,225 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "parser.h"
+#include "collectors/plugins.d/pluginsd_parser.h"
+
+static inline int find_first_keyword(const char *src, char *dst, int dst_size, int (*custom_isspace)(char)) {
+ const char *s = src, *keyword_start;
+
+ while (unlikely(custom_isspace(*s))) s++;
+ keyword_start = s;
+
+ while (likely(*s && !custom_isspace(*s)) && dst_size > 1) {
+ *dst++ = *s++;
+ dst_size--;
+ }
+ *dst = '\0';
+ return dst_size == 0 ? 0 : (int) (s - keyword_start);
+}
+
+/*
+ * Initialize a parser
+ * user : as defined by the user, will be shared across calls
+ * input : main input stream (auto detect stream -- file, socket, pipe)
+ * buffer : This is the buffer to be used (if null a buffer of size will be allocated)
+ * size : buffer size either passed or will be allocated
+ * If the buffer is auto allocated, it will auto freed when the parser is destroyed
+ *
+ *
+ */
+
+PARSER *parser_init(void *user, FILE *fp_input, FILE *fp_output, int fd,
+ PARSER_INPUT_TYPE flags, void *ssl __maybe_unused)
+{
+ PARSER *parser;
+
+ parser = callocz(1, sizeof(*parser));
+ parser->user = user;
+ parser->fd = fd;
+ parser->fp_input = fp_input;
+ parser->fp_output = fp_output;
+#ifdef ENABLE_HTTPS
+ parser->ssl_output = ssl;
+#endif
+ parser->flags = flags;
+ parser->worker_job_next_id = WORKER_PARSER_FIRST_JOB;
+
+ return parser;
+}
+
+
+static inline PARSER_KEYWORD *parser_find_keyword(PARSER *parser, const char *command) {
+ uint32_t hash = parser_hash_function(command);
+ uint32_t slot = hash % PARSER_KEYWORDS_HASHTABLE_SIZE;
+ PARSER_KEYWORD *t = parser->keywords.hashtable[slot];
+
+ if(likely(t && strcmp(t->keyword, command) == 0))
+ return t;
+
+ return NULL;
+}
+
+/*
+ * Add a keyword and the corresponding function that will be called
+ * Multiple functions may be added
+ * Input : keyword
+ * : callback function
+ * : flags
+ * Output: > 0 registered function number
+ * : 0 Error
+ */
+
+void parser_add_keyword(PARSER *parser, char *keyword, keyword_function func) {
+ if(unlikely(!parser || !keyword || !*keyword || !func))
+ fatal("PARSER: invalid parameters");
+
+ PARSER_KEYWORD *t = callocz(1, sizeof(*t));
+ t->worker_job_id = parser->worker_job_next_id++;
+ t->keyword = strdupz(keyword);
+ t->func = func;
+
+ uint32_t hash = parser_hash_function(keyword);
+ uint32_t slot = hash % PARSER_KEYWORDS_HASHTABLE_SIZE;
+
+ if(unlikely(parser->keywords.hashtable[slot]))
+ fatal("PARSER: hashtable collision between keyword '%s' and '%s' on slot %u. "
+ "Change the hashtable size and / or the hashing function. "
+ "Run the unit test to find the optimal values.",
+ parser->keywords.hashtable[slot]->keyword,
+ t->keyword,
+ slot
+ );
+
+ parser->keywords.hashtable[slot] = t;
+
+ worker_register_job_name(t->worker_job_id, t->keyword);
+}
+
+/*
+ * Cleanup a previously allocated parser
+ */
+
+void parser_destroy(PARSER *parser)
+{
+ if (unlikely(!parser))
+ return;
+
+ dictionary_destroy(parser->inflight.functions);
+
+ // Remove keywords
+ for(size_t i = 0 ; i < PARSER_KEYWORDS_HASHTABLE_SIZE; i++) {
+ PARSER_KEYWORD *t = parser->keywords.hashtable[i];
+ if (t) {
+ freez(t->keyword);
+ freez(t);
+ }
+ }
+
+ freez(parser);
+}
+
+
+/*
+ * Fetch the next line to process
+ *
+ */
+
+int parser_next(PARSER *parser, char *buffer, size_t buffer_size)
+{
+ char *tmp = fgets(buffer, (int)buffer_size, (FILE *)parser->fp_input);
+
+ if (unlikely(!tmp)) {
+ if (feof((FILE *)parser->fp_input))
+ error("PARSER: read failed: end of file");
+
+ else if (ferror((FILE *)parser->fp_input))
+ error("PARSER: read failed: input error");
+
+ else
+ error("PARSER: read failed: unknown error");
+
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/*
+* Takes an initialized parser object that has an unprocessed entry (by calling parser_next)
+* and if it contains a valid keyword, it will execute all the callbacks
+*
+*/
+
+inline int parser_action(PARSER *parser, char *input)
+{
+ parser->line++;
+
+ if(unlikely(parser->flags & PARSER_DEFER_UNTIL_KEYWORD)) {
+ char command[PLUGINSD_LINE_MAX + 1];
+ bool has_keyword = find_first_keyword(input, command, PLUGINSD_LINE_MAX, pluginsd_space);
+
+ if(!has_keyword || strcmp(command, parser->defer.end_keyword) != 0) {
+ if(parser->defer.response) {
+ buffer_strcat(parser->defer.response, input);
+ if(buffer_strlen(parser->defer.response) > 10 * 1024 * 1024) {
+ // more than 10MB of data
+ // a bad plugin that did not send the end_keyword
+ internal_error(true, "PLUGINSD: deferred response is too big (%zu bytes). Stopping this plugin.", buffer_strlen(parser->defer.response));
+ return 1;
+ }
+ }
+ return 0;
+ }
+ else {
+ // call the action
+ parser->defer.action(parser, parser->defer.action_data);
+
+ // empty everything
+ parser->defer.action = NULL;
+ parser->defer.action_data = NULL;
+ parser->defer.end_keyword = NULL;
+ parser->defer.response = NULL;
+ parser->flags &= ~PARSER_DEFER_UNTIL_KEYWORD;
+ }
+ return 0;
+ }
+
+ char *words[PLUGINSD_MAX_WORDS];
+ size_t num_words = pluginsd_split_words(input, words, PLUGINSD_MAX_WORDS);
+ const char *command = get_word(words, num_words, 0);
+
+ if(unlikely(!command))
+ return 0;
+
+ PARSER_RC rc;
+ PARSER_KEYWORD *t = parser_find_keyword(parser, command);
+ if(likely(t)) {
+ worker_is_busy(t->worker_job_id);
+ rc = (*t->func)(words, num_words, parser->user);
+ worker_is_idle();
+ }
+ else
+ rc = PARSER_RC_ERROR;
+
+#ifdef NETDATA_INTERNAL_CHECKS
+ if(rc == PARSER_RC_ERROR) {
+ BUFFER *wb = buffer_create(PLUGINSD_LINE_MAX, NULL);
+ for(size_t i = 0; i < num_words ;i++) {
+ if(i) buffer_fast_strcat(wb, " ", 1);
+
+ buffer_fast_strcat(wb, "\"", 1);
+ const char *s = get_word(words, num_words, i);
+ buffer_strcat(wb, s?s:"");
+ buffer_fast_strcat(wb, "\"", 1);
+ }
+
+ internal_error(true, "PLUGINSD: parser_action('%s') failed on line %zu: { %s } (quotes added to show parsing)",
+ command, parser->line, buffer_tostring(wb));
+
+ buffer_free(wb);
+ }
+#endif
+
+ return (rc == PARSER_RC_ERROR || rc == PARSER_RC_STOP);
+}
diff --git a/libnetdata/parser/parser.h b/libnetdata/parser/parser.h
new file mode 100644
index 000000000..9e0d3480d
--- /dev/null
+++ b/libnetdata/parser/parser.h
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef NETDATA_INCREMENTAL_PARSER_H
+#define NETDATA_INCREMENTAL_PARSER_H 1
+
+#include "../libnetdata.h"
+
+#define WORKER_PARSER_FIRST_JOB 3
+
+// this has to be in-sync with the same at receiver.c
+#define WORKER_RECEIVER_JOB_REPLICATION_COMPLETION (WORKER_PARSER_FIRST_JOB - 3)
+
+#define PARSER_KEYWORDS_HASHTABLE_SIZE 73 // unittest finds this magic number
+//#define parser_hash_function(s) djb2_hash32(s)
+//#define parser_hash_function(s) fnv1_hash32(s)
+//#define parser_hash_function(s) fnv1a_hash32(s)
+//#define parser_hash_function(s) larson_hash32(s)
+#define parser_hash_function(s) pluginsd_parser_hash32(s)
+
+// PARSER return codes
+typedef enum __attribute__ ((__packed__)) parser_rc {
+ PARSER_RC_OK, // Callback was successful, go on
+ PARSER_RC_STOP, // Callback says STOP
+ PARSER_RC_ERROR // Callback failed (abort rest of callbacks)
+} PARSER_RC;
+
+typedef enum __attribute__ ((__packed__)) parser_input_type {
+ PARSER_INPUT_SPLIT = (1 << 1),
+ PARSER_DEFER_UNTIL_KEYWORD = (1 << 2),
+} PARSER_INPUT_TYPE;
+
+typedef PARSER_RC (*keyword_function)(char **words, size_t num_words, void *user_data);
+
+typedef struct parser_keyword {
+ size_t worker_job_id;
+ char *keyword;
+ keyword_function func;
+} PARSER_KEYWORD;
+
+typedef struct parser {
+ size_t worker_job_next_id;
+ uint8_t version; // Parser version
+ int fd; // Socket
+ FILE *fp_input; // Input source e.g. stream
+ FILE *fp_output; // Stream to send commands to plugin
+#ifdef ENABLE_HTTPS
+ struct netdata_ssl *ssl_output;
+#endif
+ void *user; // User defined structure to hold extra state between calls
+ uint32_t flags;
+ size_t line;
+
+ struct {
+ PARSER_KEYWORD *hashtable[PARSER_KEYWORDS_HASHTABLE_SIZE];
+ } keywords;
+
+ struct {
+ const char *end_keyword;
+ BUFFER *response;
+ void (*action)(struct parser *parser, void *action_data);
+ void *action_data;
+ } defer;
+
+ struct {
+ DICTIONARY *functions;
+ usec_t smaller_timeout;
+ } inflight;
+} PARSER;
+
+PARSER *parser_init(void *user, FILE *fp_input, FILE *fp_output, int fd, PARSER_INPUT_TYPE flags, void *ssl);
+void parser_add_keyword(PARSER *working_parser, char *keyword, keyword_function func);
+int parser_next(PARSER *working_parser, char *buffer, size_t buffer_size);
+int parser_action(PARSER *working_parser, char *input);
+void parser_destroy(PARSER *working_parser);
+
+PARSER_RC pluginsd_set(char **words, size_t num_words, void *user);
+PARSER_RC pluginsd_begin(char **words, size_t num_words, void *user);
+PARSER_RC pluginsd_end(char **words, size_t num_words, void *user);
+PARSER_RC pluginsd_chart(char **words, size_t num_words, void *user);
+PARSER_RC pluginsd_chart_definition_end(char **words, size_t num_words, void *user);
+PARSER_RC pluginsd_dimension(char **words, size_t num_words, void *user);
+PARSER_RC pluginsd_variable(char **words, size_t num_words, void *user);
+PARSER_RC pluginsd_flush(char **words, size_t num_words, void *user);
+PARSER_RC pluginsd_disable(char **words, size_t num_words, void *user);
+PARSER_RC pluginsd_label(char **words, size_t num_words, void *user);
+PARSER_RC pluginsd_overwrite(char **words, size_t num_words, void *user);
+PARSER_RC pluginsd_clabel_commit(char **words, size_t num_words, void *user);
+PARSER_RC pluginsd_clabel(char **words, size_t num_words, void *user);
+
+PARSER_RC pluginsd_replay_begin(char **words, size_t num_words, void *user);
+PARSER_RC pluginsd_replay_rrddim_collection_state(char **words, size_t num_words, void *user);
+PARSER_RC pluginsd_replay_rrdset_collection_state(char **words, size_t num_words, void *user);
+PARSER_RC pluginsd_replay_set(char **words, size_t num_words, void *user);
+PARSER_RC pluginsd_replay_end(char **words, size_t num_words, void *user);
+
+PARSER_RC pluginsd_begin_v2(char **words, size_t num_words, void *user);
+PARSER_RC pluginsd_set_v2(char **words, size_t num_words, void *user);
+PARSER_RC pluginsd_end_v2(char **words, size_t num_words, void *user);
+void pluginsd_cleanup_v2(void *user);
+
+#endif
diff --git a/libnetdata/popen/README.md b/libnetdata/popen/README.md
index 804690d13..7bd2bd3dd 100644
--- a/libnetdata/popen/README.md
+++ b/libnetdata/popen/README.md
@@ -4,7 +4,7 @@ custom_edit_url: https://github.com/netdata/netdata/edit/master/libnetdata/popen
sidebar_label: "popen"
learn_status: "Published"
learn_topic_type: "Tasks"
-learn_rel_path: "Developers/libnetdata libraries"
+learn_rel_path: "Developers/libnetdata"
-->
# popen
diff --git a/libnetdata/procfile/README.md b/libnetdata/procfile/README.md
index 8610e77e5..37138bd19 100644
--- a/libnetdata/procfile/README.md
+++ b/libnetdata/procfile/README.md
@@ -4,7 +4,7 @@ custom_edit_url: https://github.com/netdata/netdata/edit/master/libnetdata/procf
sidebar_label: "Procfile"
learn_status: "Published"
learn_topic_type: "Tasks"
-learn_rel_path: "Developers/libnetdata libraries"
+learn_rel_path: "Developers/libnetdata"
-->
# PROCFILE
diff --git a/libnetdata/simple_pattern/README.md b/libnetdata/simple_pattern/README.md
index a0a7cf688..e00006d37 100644
--- a/libnetdata/simple_pattern/README.md
+++ b/libnetdata/simple_pattern/README.md
@@ -5,7 +5,7 @@ custom_edit_url: https://github.com/netdata/netdata/edit/master/libnetdata/simpl
sidebar_label: "Simple patterns"
learn_status: "Published"
learn_topic_type: "Tasks"
-learn_rel_path: "Developers/libnetdata libraries"
+learn_rel_path: "Developers/libnetdata"
-->
# Simple patterns
diff --git a/libnetdata/simple_pattern/simple_pattern.c b/libnetdata/simple_pattern/simple_pattern.c
index 81c2ed0b8..a26ae4f92 100644
--- a/libnetdata/simple_pattern/simple_pattern.c
+++ b/libnetdata/simple_pattern/simple_pattern.c
@@ -4,17 +4,20 @@
struct simple_pattern {
const char *match;
- size_t len;
+ uint32_t len;
SIMPLE_PREFIX_MODE mode;
- char negative;
+ bool negative;
+ bool case_sensitive;
struct simple_pattern *child;
-
struct simple_pattern *next;
};
-static inline struct simple_pattern *parse_pattern(char *str, SIMPLE_PREFIX_MODE default_mode) {
+static struct simple_pattern *parse_pattern(char *str, SIMPLE_PREFIX_MODE default_mode, size_t count) {
+ if(unlikely(count >= 1000))
+ return NULL;
+
// fprintf(stderr, "PARSING PATTERN: '%s'\n", str);
SIMPLE_PREFIX_MODE mode;
@@ -31,7 +34,7 @@ static inline struct simple_pattern *parse_pattern(char *str, SIMPLE_PREFIX_MODE
// do we have an asterisk in the middle?
if(*c == '*' && c[1] != '\0') {
// yes, we have
- child = parse_pattern(c, default_mode);
+ child = parse_pattern(c, default_mode, count + 1);
c[1] = '\0';
}
@@ -70,12 +73,12 @@ static inline struct simple_pattern *parse_pattern(char *str, SIMPLE_PREFIX_MODE
return m;
}
-SIMPLE_PATTERN *simple_pattern_create(const char *list, const char *separators, SIMPLE_PREFIX_MODE default_mode) {
+SIMPLE_PATTERN *simple_pattern_create(const char *list, const char *separators, SIMPLE_PREFIX_MODE default_mode, bool case_sensitive) {
struct simple_pattern *root = NULL, *last = NULL;
if(unlikely(!list || !*list)) return root;
- int isseparator[256] = {
+ char isseparator[256] = {
[' '] = 1 // space
, ['\t'] = 1 // tab
, ['\r'] = 1 // carriage return
@@ -96,14 +99,14 @@ SIMPLE_PATTERN *simple_pattern_create(const char *list, const char *separators,
buf[0] = '\0';
char *c = buf;
- char negative = 0;
+ bool negative = false;
// skip all spaces
while(isseparator[(unsigned char)*s])
s++;
if(*s == '!') {
- negative = 1;
+ negative = true;
s++;
}
@@ -137,8 +140,9 @@ SIMPLE_PATTERN *simple_pattern_create(const char *list, const char *separators,
continue;
// fprintf(stderr, "FOUND PATTERN: '%s'\n", buf);
- struct simple_pattern *m = parse_pattern(buf, default_mode);
+ struct simple_pattern *m = parse_pattern(buf, default_mode, 0);
m->negative = negative;
+ m->case_sensitive = case_sensitive;
// link it at the end
if(unlikely(!root))
@@ -174,76 +178,128 @@ static inline char *add_wildcarded(const char *matched, size_t matched_size, cha
return wildcarded;
}
-static inline int match_pattern(struct simple_pattern *m, const char *str, size_t len, char *wildcarded, size_t *wildcarded_size) {
+static inline int sp_strcmp(const char *s1, const char *s2, bool case_sensitive) {
+ if(case_sensitive)
+ return strcmp(s1, s2);
+
+ return strcasecmp(s1, s2);
+}
+
+static inline int sp_strncmp(const char *s1, const char *s2, size_t n, bool case_sensitive) {
+ if(case_sensitive)
+ return strncmp(s1, s2, n);
+
+ return strncasecmp(s1, s2, n);
+}
+
+static inline char *sp_strstr(const char *haystack, const char *needle, bool case_sensitive) {
+ if(case_sensitive)
+ return strstr(haystack, needle);
+
+ return strcasestr(haystack, needle);
+}
+
+static inline bool match_pattern(struct simple_pattern *m, const char *str, size_t len, char *wildcarded, size_t *wildcarded_size) {
char *s;
- if(m->len <= len) {
+ bool loop = true;
+ while(loop && m->len <= len) {
+ loop = false;
+
switch(m->mode) {
+ default:
+ case SIMPLE_PATTERN_EXACT:
+ if(unlikely(sp_strcmp(str, m->match, m->case_sensitive) == 0)) {
+ if(!m->child) return true;
+ return false;
+ }
+ break;
+
case SIMPLE_PATTERN_SUBSTRING:
- if(!m->len) return 1;
- if((s = strstr(str, m->match))) {
+ if(!m->len) return true;
+ if((s = sp_strstr(str, m->match, m->case_sensitive))) {
wildcarded = add_wildcarded(str, s - str, wildcarded, wildcarded_size);
if(!m->child) {
- wildcarded = add_wildcarded(&s[m->len], len - (&s[m->len] - str), wildcarded, wildcarded_size);
- return 1;
+ add_wildcarded(&s[m->len], len - (&s[m->len] - str), wildcarded, wildcarded_size);
+ return true;
+ }
+
+ // instead of recursion
+ {
+ len = len - (s - str) - m->len;
+ str = &s[m->len];
+ m = m->child;
+ loop = true;
+ // return match_pattern(m->child, &s[m->len], len - (s - str) - m->len, wildcarded, wildcarded_size);
}
- return match_pattern(m->child, &s[m->len], len - (s - str) - m->len, wildcarded, wildcarded_size);
}
break;
case SIMPLE_PATTERN_PREFIX:
- if(unlikely(strncmp(str, m->match, m->len) == 0)) {
+ if(unlikely(sp_strncmp(str, m->match, m->len, m->case_sensitive) == 0)) {
if(!m->child) {
- wildcarded = add_wildcarded(&str[m->len], len - m->len, wildcarded, wildcarded_size);
- return 1;
+ add_wildcarded(&str[m->len], len - m->len, wildcarded, wildcarded_size);
+ return true;
+ }
+ // instead of recursion
+ {
+ len = len - m->len;
+ str = &str[m->len];
+ m = m->child;
+ loop = true;
+ // return match_pattern(m->child, &str[m->len], len - m->len, wildcarded, wildcarded_size);
}
- return match_pattern(m->child, &str[m->len], len - m->len, wildcarded, wildcarded_size);
}
break;
case SIMPLE_PATTERN_SUFFIX:
- if(unlikely(strcmp(&str[len - m->len], m->match) == 0)) {
- wildcarded = add_wildcarded(str, len - m->len, wildcarded, wildcarded_size);
- if(!m->child) return 1;
- return 0;
- }
- break;
-
- case SIMPLE_PATTERN_EXACT:
- default:
- if(unlikely(strcmp(str, m->match) == 0)) {
- if(!m->child) return 1;
- return 0;
+ if(unlikely(sp_strcmp(&str[len - m->len], m->match, m->case_sensitive) == 0)) {
+ add_wildcarded(str, len - m->len, wildcarded, wildcarded_size);
+ if(!m->child) return true;
+ return false;
}
break;
}
}
- return 0;
+ return false;
}
-int simple_pattern_matches_extract(SIMPLE_PATTERN *list, const char *str, char *wildcarded, size_t wildcarded_size) {
+static inline SIMPLE_PATTERN_RESULT simple_pattern_matches_extract_with_length(SIMPLE_PATTERN *list, const char *str, size_t len, char *wildcarded, size_t wildcarded_size) {
struct simple_pattern *m, *root = (struct simple_pattern *)list;
- if(unlikely(!root || !str || !*str)) return 0;
-
- size_t len = strlen(str);
for(m = root; m ; m = m->next) {
char *ws = wildcarded;
size_t wss = wildcarded_size;
if(unlikely(ws)) *ws = '\0';
if (match_pattern(m, str, len, ws, &wss)) {
-
- //if(ws && wss)
- // fprintf(stderr, "FINAL WILDCARDED '%s' of length %zu\n", ws, strlen(ws));
-
- if (m->negative) return 0;
- return 1;
+ if (m->negative) return SP_MATCHED_NEGATIVE;
+ return SP_MATCHED_POSITIVE;
}
}
- return 0;
+ return SP_NOT_MATCHED;
+}
+
+SIMPLE_PATTERN_RESULT simple_pattern_matches_buffer_extract(SIMPLE_PATTERN *list, BUFFER *str, char *wildcarded, size_t wildcarded_size) {
+ if(!list || !str || buffer_strlen(str)) return SP_NOT_MATCHED;
+ return simple_pattern_matches_extract_with_length(list, buffer_tostring(str), buffer_strlen(str), wildcarded, wildcarded_size);
+}
+
+SIMPLE_PATTERN_RESULT simple_pattern_matches_string_extract(SIMPLE_PATTERN *list, STRING *str, char *wildcarded, size_t wildcarded_size) {
+ if(!list || !str) return SP_NOT_MATCHED;
+ return simple_pattern_matches_extract_with_length(list, string2str(str), string_strlen(str), wildcarded, wildcarded_size);
+}
+
+SIMPLE_PATTERN_RESULT simple_pattern_matches_extract(SIMPLE_PATTERN *list, const char *str, char *wildcarded, size_t wildcarded_size) {
+ if(!list || !str || !*str) return SP_NOT_MATCHED;
+ return simple_pattern_matches_extract_with_length(list, str, strlen(str), wildcarded, wildcarded_size);
+}
+
+SIMPLE_PATTERN_RESULT simple_pattern_matches_length_extract(SIMPLE_PATTERN *list, const char *str, size_t len, char *wildcarded, size_t wildcarded_size) {
+ if(!list || !str || !*str || !len) return SP_NOT_MATCHED;
+ return simple_pattern_matches_extract_with_length(list, str, len, wildcarded, wildcarded_size);
}
static inline void free_pattern(struct simple_pattern *m) {
diff --git a/libnetdata/simple_pattern/simple_pattern.h b/libnetdata/simple_pattern/simple_pattern.h
index 7282053e8..1a8d8f7d6 100644
--- a/libnetdata/simple_pattern/simple_pattern.h
+++ b/libnetdata/simple_pattern/simple_pattern.h
@@ -6,25 +6,38 @@
#include "../libnetdata.h"
-typedef enum {
+typedef enum __attribute__ ((__packed__)) {
SIMPLE_PATTERN_EXACT,
SIMPLE_PATTERN_PREFIX,
SIMPLE_PATTERN_SUFFIX,
SIMPLE_PATTERN_SUBSTRING
} SIMPLE_PREFIX_MODE;
+typedef enum __attribute__ ((__packed__)) {
+ SP_NOT_MATCHED,
+ SP_MATCHED_NEGATIVE,
+ SP_MATCHED_POSITIVE,
+} SIMPLE_PATTERN_RESULT;
+
typedef void SIMPLE_PATTERN;
// create a simple_pattern from the string given
// default_mode is used in cases where EXACT matches, without an asterisk,
// should be considered PREFIX matches.
-SIMPLE_PATTERN *simple_pattern_create(const char *list, const char *separators, SIMPLE_PREFIX_MODE default_mode);
+SIMPLE_PATTERN *simple_pattern_create(const char *list, const char *separators, SIMPLE_PREFIX_MODE default_mode, bool case_sensitive);
+
+struct netdata_string;
// test if string str is matched from the pattern and fill 'wildcarded' with the parts matched by '*'
-int simple_pattern_matches_extract(SIMPLE_PATTERN *list, const char *str, char *wildcarded, size_t wildcarded_size);
+SIMPLE_PATTERN_RESULT simple_pattern_matches_extract(SIMPLE_PATTERN *list, const char *str, char *wildcarded, size_t wildcarded_size);
+SIMPLE_PATTERN_RESULT simple_pattern_matches_string_extract(SIMPLE_PATTERN *list, struct netdata_string *str, char *wildcarded, size_t wildcarded_size);
+SIMPLE_PATTERN_RESULT simple_pattern_matches_buffer_extract(SIMPLE_PATTERN *list, BUFFER *str, char *wildcarded, size_t wildcarded_size);
+SIMPLE_PATTERN_RESULT simple_pattern_matches_length_extract(SIMPLE_PATTERN *list, const char *str, size_t len, char *wildcarded, size_t wildcarded_size);
// test if string str is matched from the pattern
-#define simple_pattern_matches(list, str) simple_pattern_matches_extract(list, str, NULL, 0)
+#define simple_pattern_matches(list, str) (simple_pattern_matches_extract(list, str, NULL, 0) == SP_MATCHED_POSITIVE)
+#define simple_pattern_matches_string(list, str) (simple_pattern_matches_string_extract(list, str, NULL, 0) == SP_MATCHED_POSITIVE)
+#define simple_pattern_matches_buffer(list, str) (simple_pattern_matches_buffer_extract(list, str, NULL, 0) == SP_MATCHED_POSITIVE)
// free a simple_pattern that was created with simple_pattern_create()
// list can be NULL, in which case, this does nothing.
@@ -37,6 +50,11 @@ char *simple_pattern_iterate(SIMPLE_PATTERN **p);
// Auxiliary function to create a pattern
char *simple_pattern_trim_around_equal(char *src);
+#define SIMPLE_PATTERN_DEFAULT_WEB_SEPARATORS ",|\t\r\n\f\v"
+
#define is_valid_sp(x) ((x) && *(x) && !((x)[0] == '*' && (x)[1] == '\0'))
+#define string_to_simple_pattern(str) (is_valid_sp(str) ? simple_pattern_create(str, SIMPLE_PATTERN_DEFAULT_WEB_SEPARATORS, SIMPLE_PATTERN_EXACT, true) : NULL)
+#define string_to_simple_pattern_nocase(str) (is_valid_sp(str) ? simple_pattern_create(str, SIMPLE_PATTERN_DEFAULT_WEB_SEPARATORS, SIMPLE_PATTERN_EXACT, false) : NULL)
+
#endif //NETDATA_SIMPLE_PATTERN_H
diff --git a/libnetdata/socket/README.md b/libnetdata/socket/README.md
index 70bfd3441..e339a071c 100644
--- a/libnetdata/socket/README.md
+++ b/libnetdata/socket/README.md
@@ -1,5 +1,8 @@
<!--
+Title: "Socket"
custom_edit_url: https://github.com/netdata/netdata/edit/master/libnetdata/socket/README.md
+sidebar_label: "Socket"
+learn_status: "Published"
+learn_topic_type: "References"
+learn_rel_path: "Developers/libnetdata"
-->
-
-
diff --git a/libnetdata/socket/socket.c b/libnetdata/socket/socket.c
index 69124b949..7eb212b33 100644
--- a/libnetdata/socket/socket.c
+++ b/libnetdata/socket/socket.c
@@ -1,5 +1,13 @@
// SPDX-License-Identifier: GPL-3.0-or-later
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE // for POLLRDHUP
+#endif
+
+#ifndef __BSD_VISIBLE
+#define __BSD_VISIBLE // for POLLRDHUP
+#endif
+
#include "../libnetdata.h"
// --------------------------------------------------------------------------------------------------------------------
@@ -11,6 +19,46 @@
#define LARGE_SOCK_SIZE 4096
#endif
+bool fd_is_socket(int fd) {
+ int type;
+ socklen_t len = sizeof(type);
+ if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &len) == -1)
+ return false;
+
+ return true;
+}
+
+bool sock_has_output_error(int fd) {
+ if(fd < 0) {
+ //internal_error(true, "invalid socket %d", fd);
+ return false;
+ }
+
+// if(!fd_is_socket(fd)) {
+// //internal_error(true, "fd %d is not a socket", fd);
+// return false;
+// }
+
+ short int errors = POLLERR | POLLHUP | POLLNVAL;
+
+#ifdef POLLRDHUP
+ errors |= POLLRDHUP;
+#endif
+
+ struct pollfd pfd = {
+ .fd = fd,
+ .events = POLLOUT | errors,
+ .revents = 0,
+ };
+
+ if(poll(&pfd, 1, 0) == -1) {
+ //internal_error(true, "poll() failed");
+ return false;
+ }
+
+ return ((pfd.revents & errors) || !(pfd.revents & POLLOUT));
+}
+
int sock_setnonblock(int fd) {
int flags;
@@ -923,53 +971,36 @@ int connect_to_one_of_urls(const char *destination, int default_port, struct tim
ssize_t netdata_ssl_read(SSL *ssl, void *buf, size_t num) {
error_limit_static_thread_var(erl, 1, 0);
- int bytes, err, retries = 0;
+ int bytes, err;
- //do {
bytes = SSL_read(ssl, buf, (int)num);
err = SSL_get_error(ssl, bytes);
- retries++;
- //} while (bytes <= 0 && err == SSL_ERROR_WANT_READ);
if(unlikely(bytes <= 0)) {
if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
bytes = 0;
} else
- error("SSL_write() returned %d bytes, SSL error %d", bytes, err);
+ error_limit(&erl, "SSL_write() returned %d bytes, SSL error %d", bytes, err);
}
- if(retries > 1)
- error_limit(&erl, "SSL_read() retried %d times", retries);
-
return bytes;
}
ssize_t netdata_ssl_write(SSL *ssl, const void *buf, size_t num) {
error_limit_static_thread_var(erl, 1, 0);
- int bytes, err, retries = 0;
- size_t total = 0;
+ int bytes, err;
- //do {
- bytes = SSL_write(ssl, (uint8_t *)buf + total, (int)(num - total));
+ bytes = SSL_write(ssl, (uint8_t *)buf, (int)num);
err = SSL_get_error(ssl, bytes);
- retries++;
-
- if(bytes > 0)
- total += bytes;
-
- //} while ((bytes <= 0 && (err == SSL_ERROR_WANT_WRITE)) || (bytes > 0 && total < num));
if(unlikely(bytes <= 0)) {
if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
bytes = 0;
} else
- error("SSL_write() returned %d bytes, SSL error %d", bytes, err);
+ error_limit(&erl, "SSL_write() returned %d bytes, SSL error %d", bytes, err);
}
- if(retries > 1)
- error_limit(&erl, "SSL_write() retried %d times", retries);
-
return bytes;
}
#endif
@@ -1118,8 +1149,8 @@ int accept4(int sock, struct sockaddr *addr, socklen_t *addrlen, int flags) {
* update the client_host if uninitialized - ensure the hostsize is the number
* of *writable* bytes (i.e. be aware of the strdup used to compact the pollinfo).
*/
-extern int connection_allowed(int fd, char *client_ip, char *client_host, size_t hostsize, SIMPLE_PATTERN *access_list,
- const char *patname, int allow_dns)
+int connection_allowed(int fd, char *client_ip, char *client_host, size_t hostsize, SIMPLE_PATTERN *access_list,
+ const char *patname, int allow_dns)
{
debug(D_LISTENER,"checking %s... (allow_dns=%d)", patname, allow_dns);
if (!access_list)
diff --git a/libnetdata/socket/socket.h b/libnetdata/socket/socket.h
index 9577453d5..110063014 100644
--- a/libnetdata/socket/socket.h
+++ b/libnetdata/socket/socket.h
@@ -22,8 +22,11 @@ typedef enum web_client_acl {
WEB_CLIENT_ACL_SSL_FORCE = (1 << 7),
WEB_CLIENT_ACL_SSL_DEFAULT = (1 << 8),
WEB_CLIENT_ACL_ACLK = (1 << 9),
+ WEB_CLIENT_ACL_WEBRTC = (1 << 10),
} WEB_CLIENT_ACL;
+#define WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC (WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK | WEB_CLIENT_ACL_WEBRTC)
+
#define WEB_CLIENT_ACL_ALL 0xFFFF
#define web_client_can_access_dashboard(w) ((w)->acl & WEB_CLIENT_ACL_DASHBOARD)
@@ -74,6 +77,9 @@ ssize_t recv_timeout(int sockfd, void *buf, size_t len, int flags, int timeout);
ssize_t send_timeout(int sockfd, void *buf, size_t len, int flags, int timeout);
#endif
+bool fd_is_socket(int fd);
+bool sock_has_output_error(int fd);
+
int sock_setnonblock(int fd);
int sock_delnonblock(int fd);
int sock_setreuse(int fd, int reuse);
diff --git a/libnetdata/statistical/README.md b/libnetdata/statistical/README.md
index 8fa101f0e..937c26ac6 100644
--- a/libnetdata/statistical/README.md
+++ b/libnetdata/statistical/README.md
@@ -4,7 +4,7 @@ custom_edit_url: https://github.com/netdata/netdata/edit/master/libnetdata/stati
sidebar_label: "Statistical functions"
learn_status: "Published"
learn_topic_type: "Tasks"
-learn_rel_path: "Developers/libnetdata libraries"
+learn_rel_path: "Developers/libnetdata"
-->
# Statistical functions
diff --git a/libnetdata/storage_number/README.md b/libnetdata/storage_number/README.md
index da2c3ccfd..2dd6df6cf 100644
--- a/libnetdata/storage_number/README.md
+++ b/libnetdata/storage_number/README.md
@@ -4,7 +4,7 @@ custom_edit_url: https://github.com/netdata/netdata/edit/master/libnetdata/stora
sidebar_label: "Storage number"
learn_status: "Published"
learn_topic_type: "Tasks"
-learn_rel_path: "Developers/libnetdata libraries"
+learn_rel_path: "Developers/libnetdata"
-->
# Netdata storage number
diff --git a/libnetdata/storage_number/storage_number.c b/libnetdata/storage_number/storage_number.c
index 7511f3a79..ebae71d81 100644
--- a/libnetdata/storage_number/storage_number.c
+++ b/libnetdata/storage_number/storage_number.c
@@ -2,6 +2,78 @@
#include "../libnetdata.h"
+bool is_system_ieee754_double(void) {
+ static bool logged = false;
+
+ struct {
+ NETDATA_DOUBLE original;
+
+ union {
+ uint64_t i;
+ NETDATA_DOUBLE d;
+ };
+ } tests[] = {
+ { .original = 1.25, .i = 0x3FF4000000000000 },
+ { .original = 1.0, .i = 0x3FF0000000000000 },
+ { .original = 2.0, .i = 0x4000000000000000 },
+ { .original = 4.0, .i = 0x4010000000000000 },
+ { .original = 8.8, .i = 0x402199999999999A },
+ { .original = 16.16, .i = 0x403028F5C28F5C29 },
+ { .original = 32.32, .i = 0x404028F5C28F5C29 },
+ { .original = 64.64, .i = 0x405028F5C28F5C29 },
+ { .original = 128.128, .i = 0x406004189374BC6A },
+ { .original = 32768.32768, .i = 0x40E0000A7C5AC472 },
+ { .original = 65536.65536, .i = 0x40F0000A7C5AC472 },
+ { .original = -65536.65536, .i = 0xC0F0000A7C5AC472 },
+ { .original = 65535.65535, .i = 0x40EFFFF4F8A0902E },
+ { .original = -65535.65535, .i = 0xC0EFFFF4F8A0902E },
+ { .original = 4.503599627e15, .i = 0x432FFFFFFFF4B180 },
+ { .original = -4.503599627e15, .i = 0xC32FFFFFFFF4B180 },
+ { .original = 1.25e25, .i = 0x4524ADF4B7320335 },
+ { .original = 1.25e307, .i = 0x7FB1CCF385EBC8A0 },
+ { .original = 1.25e-25, .i = 0x3AC357C299A88EA7 },
+ { .original = 1.25e-100, .i = 0x2B317F7D4ED8C33E },
+ { .original = NAN, .i = 0x7FF8000000000000 },
+ { .original = -INFINITY, .i = 0xFFF0000000000000 },
+ { .original = INFINITY, .i = 0x7FF0000000000000 },
+ { .original = 1.25e-132, .i = 0x248C6463225AB7EC },
+ { .original = 0.0, .i = 0x0000000000000000 },
+ { .original = -0.0, .i = 0x8000000000000000 },
+ { .original = DBL_MIN, .i = 0x0010000000000000 },
+ { .original = DBL_MAX, .i = 0x7FEFFFFFFFFFFFFF },
+ { .original = -DBL_MIN, .i = 0x8010000000000000 },
+ { .original = -DBL_MAX, .i = 0xFFEFFFFFFFFFFFFF },
+ };
+
+ size_t errors = 0;
+ size_t elements = sizeof(tests) / sizeof(tests[0]);
+ for(size_t i = 0; i < elements ; i++) {
+ uint64_t *ptr = (uint64_t *)&tests[i].original;
+
+ if(*ptr != tests[i].i && (tests[i].original == tests[i].d || (isnan(tests[i].original) && isnan(tests[i].d)))) {
+ if(!logged)
+ info("IEEE754: test #%zu, value " NETDATA_DOUBLE_FORMAT_G " is represented in this system as %lX, but it was expected as %lX",
+ i+1, tests[i].original, *ptr, tests[i].i);
+ errors++;
+ }
+ }
+
+ if(!errors && sizeof(NETDATA_DOUBLE) == sizeof(uint64_t)) {
+ if(!logged)
+ info("IEEE754: system is using IEEE754 DOUBLE PRECISION values");
+
+ logged = true;
+ return true;
+ }
+ else {
+ if(!logged)
+ info("IEEE754: system is NOT compatible with IEEE754 DOUBLE PRECISION values");
+
+ logged = true;
+ return false;
+ }
+}
+
storage_number pack_storage_number(NETDATA_DOUBLE value, SN_FLAGS flags) {
// bit 32 = sign 0:positive, 1:negative
// bit 31 = 0:divide, 1:multiply
@@ -159,73 +231,3 @@ int print_netdata_double(char *str, NETDATA_DOUBLE value)
return (int) ((wstr - str) + 2 + decimal );
}
*/
-
-int print_netdata_double(char *str, NETDATA_DOUBLE value) {
- // info("printing number " NETDATA_DOUBLE_FORMAT, value);
- char integral_str[50], fractional_str[50];
-
- char *wstr = str;
-
- if(unlikely(value < 0)) {
- *wstr++ = '-';
- value = -value;
- }
-
- NETDATA_DOUBLE integral, fractional;
-
-#ifdef STORAGE_WITH_MATH
- fractional = modfndd(value, &integral) * 10000000.0;
-#else
- integral = (NETDATA_DOUBLE)((unsigned long long)(value * 10000000ULL) / 10000000ULL);
- fractional = (NETDATA_DOUBLE)((unsigned long long)(value * 10000000ULL) % 10000000ULL);
-#endif
-
- unsigned long long integral_int = (unsigned long long)integral;
- unsigned long long fractional_int = (unsigned long long)llrintndd(fractional);
- if(unlikely(fractional_int >= 10000000)) {
- integral_int += 1;
- fractional_int -= 10000000;
- }
-
- // info("integral " NETDATA_DOUBLE_FORMAT " (%llu), fractional " NETDATA_DOUBLE_FORMAT " (%llu)", integral, integral_int, fractional, fractional_int);
-
- char *istre;
- if(unlikely(integral_int == 0)) {
- integral_str[0] = '0';
- istre = &integral_str[1];
- }
- else
- // convert the integral part to string (reversed)
- istre = print_number_llu_r_smart(integral_str, integral_int);
-
- // copy reversed the integral string
- istre--;
- while( istre >= integral_str ) *wstr++ = *istre--;
-
- if(likely(fractional_int != 0)) {
- // add a dot
- *wstr++ = '.';
-
- // convert the fractional part to string (reversed)
- char *fstre = print_number_llu_r_smart(fractional_str, fractional_int);
-
- // prepend zeros to reach 7 digits length
- int decimal = 7;
- int len = (int)(fstre - fractional_str);
- while(len < decimal) {
- *wstr++ = '0';
- len++;
- }
-
- char *begin = fractional_str;
- while(begin < fstre && *begin == '0') begin++;
-
- // copy reversed the fractional string
- fstre--;
- while( fstre >= begin ) *wstr++ = *fstre--;
- }
-
- *wstr = '\0';
- // info("printed number '%s'", str);
- return (int)(wstr - str);
-}
diff --git a/libnetdata/storage_number/storage_number.h b/libnetdata/storage_number/storage_number.h
index faea47751..82c870d69 100644
--- a/libnetdata/storage_number/storage_number.h
+++ b/libnetdata/storage_number/storage_number.h
@@ -13,6 +13,7 @@ typedef long double NETDATA_DOUBLE;
#define NETDATA_DOUBLE_FORMAT_ZERO "%0.0Lf"
#define NETDATA_DOUBLE_FORMAT_AUTO "%Lf"
#define NETDATA_DOUBLE_MODIFIER "Lf"
+#define NETDATA_DOUBLE_FORMAT_G "%0.19Le"
#define NETDATA_DOUBLE_MAX LDBL_MAX
@@ -24,6 +25,9 @@ typedef long double NETDATA_DOUBLE;
#define copysignndd(x, y) copysignl(x, y)
#define modfndd(x, y) modfl(x, y)
#define fabsndd(x) fabsl(x)
+#define floorndd(x) floorl(x)
+#define ceilndd(x) ceill(x)
+#define log10ndd(x) log10l(x)
#else // NETDATA_WITH_LONG_DOUBLE
@@ -32,6 +36,7 @@ typedef double NETDATA_DOUBLE;
#define NETDATA_DOUBLE_FORMAT_ZERO "%0.0f"
#define NETDATA_DOUBLE_FORMAT_AUTO "%f"
#define NETDATA_DOUBLE_MODIFIER "f"
+#define NETDATA_DOUBLE_FORMAT_G "%0.19e"
#define NETDATA_DOUBLE_MAX DBL_MAX
@@ -43,6 +48,9 @@ typedef double NETDATA_DOUBLE;
#define copysignndd(x, y) copysign(x, y)
#define modfndd(x, y) modf(x, y)
#define fabsndd(x) fabs(x)
+#define floorndd(x) floor(x)
+#define ceilndd(x) ceil(x)
+#define log10ndd(x) log10(x)
#endif // NETDATA_WITH_LONG_DOUBLE
@@ -62,6 +70,9 @@ typedef long long collected_number;
#define netdata_double_isnumber(a) (fpclassify(a) != FP_NAN && fpclassify(a) != FP_INFINITE)
#endif
+#define netdata_double_is_zero(a) (!netdata_double_isnumber(a) || considered_equal_ndd(a, 0.0))
+#define netdata_double_is_nonzero(a) (!netdata_double_is_zero(a))
+
typedef uint32_t storage_number;
typedef struct storage_number_tier1 {
@@ -104,8 +115,6 @@ typedef enum {
storage_number pack_storage_number(NETDATA_DOUBLE value, SN_FLAGS flags) __attribute__((const));
static inline NETDATA_DOUBLE unpack_storage_number(storage_number value) __attribute__((const));
-int print_netdata_double(char *str, NETDATA_DOUBLE value);
-
// sign div/mul <--- multiplier / divider ---> 10/100 RESET EXISTS VALUE
#define STORAGE_NUMBER_POSITIVE_MAX_RAW (storage_number)( (0 << 31) | (1 << 30) | (1 << 29) | (1 << 28) | (1 << 27) | (1 << 26) | (0 << 25) | (1 << 24) | 0x00ffffff )
#define STORAGE_NUMBER_POSITIVE_MIN_RAW (storage_number)( (0 << 31) | (0 << 30) | (1 << 29) | (1 << 28) | (1 << 27) | (0 << 26) | (0 << 25) | (1 << 24) | 0x00000001 )
@@ -158,75 +167,12 @@ static inline NETDATA_DOUBLE unpack_storage_number(storage_number value) {
return sign * unpack_storage_number_lut10x[(factor * 16) + (exp * 8) + mul] * n;
}
-static inline NETDATA_DOUBLE str2ndd(const char *s, char **endptr) {
- int negative = 0;
- const char *start = s;
- unsigned long long integer_part = 0;
- unsigned long decimal_part = 0;
- size_t decimal_digits = 0;
-
- switch(*s) {
- case '-':
- s++;
- negative = 1;
- break;
-
- case '+':
- s++;
- break;
-
- case 'n':
- if(s[1] == 'a' && s[2] == 'n') {
- if(endptr) *endptr = (char *)&s[3];
- return NAN;
- }
- break;
-
- case 'i':
- if(s[1] == 'n' && s[2] == 'f') {
- if(endptr) *endptr = (char *)&s[3];
- return INFINITY;
- }
- break;
-
- default:
- break;
- }
-
- while (*s >= '0' && *s <= '9') {
- integer_part = (integer_part * 10) + (*s - '0');
- s++;
- }
-
- if(unlikely(*s == '.')) {
- decimal_part = 0;
- s++;
-
- while (*s >= '0' && *s <= '9') {
- decimal_part = (decimal_part * 10) + (*s - '0');
- s++;
- decimal_digits++;
- }
- }
-
- if(unlikely(*s == 'e' || *s == 'E'))
- return strtondd(start, endptr);
-
- if(unlikely(endptr))
- *endptr = (char *)s;
-
- if(unlikely(negative)) {
- if(unlikely(decimal_digits))
- return -((NETDATA_DOUBLE)integer_part + (NETDATA_DOUBLE)decimal_part / powndd(10.0, decimal_digits));
- else
- return -((NETDATA_DOUBLE)integer_part);
- }
- else {
- if(unlikely(decimal_digits))
- return (NETDATA_DOUBLE)integer_part + (NETDATA_DOUBLE)decimal_part / powndd(10.0, decimal_digits);
- else
- return (NETDATA_DOUBLE)integer_part;
- }
-}
+// all these prefixes should use characters that are not allowed in the numbers they represent
+#define HEX_PREFIX "0x" // we check 2 characters when parsing
+#define IEEE754_UINT64_B64_PREFIX "#" // we check the 1st character during parsing
+#define IEEE754_DOUBLE_B64_PREFIX "@" // we check the 1st character during parsing
+#define IEEE754_DOUBLE_HEX_PREFIX "%" // we check the 1st character during parsing
+
+bool is_system_ieee754_double(void);
#endif /* NETDATA_STORAGE_NUMBER_H */
diff --git a/libnetdata/string/README.md b/libnetdata/string/README.md
index b1c6e61c3..4fd442507 100644
--- a/libnetdata/string/README.md
+++ b/libnetdata/string/README.md
@@ -4,7 +4,7 @@ custom_edit_url: https://github.com/netdata/netdata/edit/master/libnetdata/strin
sidebar_label: "String"
learn_status: "Published"
learn_topic_type: "Tasks"
-learn_rel_path: "Developers/libnetdata libraries"
+learn_rel_path: "Developers/libnetdata"
-->
# STRING
diff --git a/libnetdata/string/string.c b/libnetdata/string/string.c
index 4e232523c..9385aa6e8 100644
--- a/libnetdata/string/string.c
+++ b/libnetdata/string/string.c
@@ -300,16 +300,20 @@ void string_freez(STRING *string) {
string_stats_atomic_increment(releases);
}
-size_t string_strlen(STRING *string) {
+inline size_t string_strlen(STRING *string) {
if(unlikely(!string)) return 0;
return string->length - 1;
}
-const char *string2str(STRING *string) {
+inline const char *string2str(STRING *string) {
if(unlikely(!string)) return "";
return string->str;
}
+int string_strcmp(STRING *string, const char *s) {
+ return strcmp(string2str(string), s);
+}
+
STRING *string_2way_merge(STRING *a, STRING *b) {
static STRING *X = NULL;
diff --git a/libnetdata/string/string.h b/libnetdata/string/string.h
index cec44ebd9..70840ee9a 100644
--- a/libnetdata/string/string.h
+++ b/libnetdata/string/string.h
@@ -13,6 +13,7 @@ STRING *string_dup(STRING *string);
void string_freez(STRING *string);
size_t string_strlen(STRING *string);
const char *string2str(STRING *string) NEVERNULL;
+int string_strcmp(STRING *string, const char *s);
// keep common prefix/suffix and replace everything else with [x]
STRING *string_2way_merge(STRING *a, STRING *b);
diff --git a/libnetdata/string/utf8.h b/libnetdata/string/utf8.h
index 133ec710b..3e6c8c288 100644
--- a/libnetdata/string/utf8.h
+++ b/libnetdata/string/utf8.h
@@ -3,7 +3,7 @@
#ifndef NETDATA_STRING_UTF8_H
#define NETDATA_STRING_UTF8_H 1
-#define IS_UTF8_BYTE(x) (x & 0x80)
-#define IS_UTF8_STARTBYTE(x) (IS_UTF8_BYTE(x)&&(x & 0x40))
+#define IS_UTF8_BYTE(x) ((x) & 0x80)
+#define IS_UTF8_STARTBYTE(x) (IS_UTF8_BYTE(x)&&((x) & 0x40))
#endif /* NETDATA_STRING_UTF8_H */
diff --git a/libnetdata/threads/README.md b/libnetdata/threads/README.md
index 71979feac..7e9e493f4 100644
--- a/libnetdata/threads/README.md
+++ b/libnetdata/threads/README.md
@@ -4,7 +4,7 @@ custom_edit_url: https://github.com/netdata/netdata/edit/master/libnetdata/threa
sidebar_label: "Threads"
learn_status: "Published"
learn_topic_type: "Tasks"
-learn_rel_path: "Developers/libnetdata libraries"
+learn_rel_path: "Developers/libnetdata"
-->
# Threads
diff --git a/libnetdata/threads/threads.c b/libnetdata/threads/threads.c
index 16de45fd1..a4591d5ac 100644
--- a/libnetdata/threads/threads.c
+++ b/libnetdata/threads/threads.c
@@ -10,7 +10,7 @@ static pthread_attr_t *netdata_threads_attr = NULL;
typedef struct {
void *arg;
pthread_t *thread;
- const char *tag;
+ char tag[NETDATA_THREAD_NAME_MAX + 1];
void *(*start_routine) (void *);
NETDATA_THREAD_OPTIONS options;
} NETDATA_THREAD;
@@ -18,11 +18,66 @@ typedef struct {
static __thread NETDATA_THREAD *netdata_thread = NULL;
inline int netdata_thread_tag_exists(void) {
- return (netdata_thread && netdata_thread->tag && *netdata_thread->tag);
+ return (netdata_thread && *netdata_thread->tag);
+}
+
+static const char *thread_name_get(bool recheck) {
+ static __thread char threadname[NETDATA_THREAD_NAME_MAX + 1] = "";
+
+ if(netdata_thread_tag_exists())
+ strncpyz(threadname, netdata_thread->tag, NETDATA_THREAD_NAME_MAX);
+ else {
+ if(!recheck && threadname[0])
+ return threadname;
+
+#if defined(__FreeBSD__)
+ pthread_get_name_np(pthread_self(), threadname, NETDATA_THREAD_NAME_MAX + 1);
+ if(strcmp(threadname, "netdata") == 0)
+ strncpyz(threadname, "MAIN", NETDATA_THREAD_NAME_MAX);
+#elif defined(__APPLE__)
+ strncpyz(threadname, "MAIN", NETDATA_THREAD_NAME_MAX);
+#elif defined(HAVE_PTHREAD_GETNAME_NP)
+ pthread_getname_np(pthread_self(), threadname, NETDATA_THREAD_NAME_MAX + 1);
+ if(strcmp(threadname, "netdata") == 0)
+ strncpyz(threadname, "MAIN", NETDATA_THREAD_NAME_MAX);
+#else
+ strncpyz(threadname, "MAIN", NETDATA_THREAD_NAME_MAX);
+#endif
+ }
+
+ return threadname;
}
const char *netdata_thread_tag(void) {
- return (netdata_thread_tag_exists() ? netdata_thread->tag : "MAIN");
+ return thread_name_get(false);
+}
+
+static size_t webrtc_id = 0;
+static __thread bool webrtc_name_set = false;
+void webrtc_set_thread_name(void) {
+ if(!netdata_thread && !webrtc_name_set) {
+ webrtc_name_set = true;
+ char threadname[NETDATA_THREAD_NAME_MAX + 1];
+
+#if defined(__FreeBSD__)
+ snprintfz(threadname, NETDATA_THREAD_NAME_MAX, "WEBRTC[%zu]", __atomic_fetch_add(&webrtc_id, 1, __ATOMIC_RELAXED));
+ pthread_set_name_np(pthread_self(), threadname);
+#elif defined(__APPLE__)
+ snprintfz(threadname, NETDATA_THREAD_NAME_MAX, "WEBRTC[%zu]", __atomic_fetch_add(&webrtc_id, 1, __ATOMIC_RELAXED));
+ pthread_setname_np(threadname);
+#elif defined(HAVE_PTHREAD_GETNAME_NP)
+ pthread_getname_np(pthread_self(), threadname, NETDATA_THREAD_NAME_MAX+1);
+ if(strcmp(threadname, "netdata") == 0) {
+ snprintfz(threadname, NETDATA_THREAD_NAME_MAX, "WEBRTC[%zu]", __atomic_fetch_add(&webrtc_id, 1, __ATOMIC_RELAXED));
+ pthread_setname_np(pthread_self(), threadname);
+ }
+#else
+ snprintfz(threadname, NETDATA_THREAD_NAME_MAX, "WEBRTC[%zu]", __atomic_fetch_add(&webrtc_id, 1, __ATOMIC_RELAXED));
+ pthread_setname_np(pthread_self(), threadname);
+#endif
+
+ thread_name_get(true);
+ }
}
// ----------------------------------------------------------------------------
@@ -127,8 +182,7 @@ static void thread_cleanup(void *ptr) {
service_exits();
worker_unregister();
- freez((void *)netdata_thread->tag);
- netdata_thread->tag = NULL;
+ netdata_thread->tag[0] = '\0';
freez(netdata_thread);
netdata_thread = NULL;
@@ -136,7 +190,7 @@ static void thread_cleanup(void *ptr) {
static void thread_set_name_np(NETDATA_THREAD *nt) {
- if (nt->tag) {
+ if (nt && nt->tag[0]) {
int ret = 0;
char threadname[NETDATA_THREAD_NAME_MAX+1];
@@ -173,6 +227,8 @@ void uv_thread_set_name_np(uv_thread_t ut, const char* name) {
ret = pthread_setname_np(ut, threadname);
#endif
+ thread_name_get(true);
+
if (ret)
info("cannot set libuv thread name to %s. Err: %d", threadname, ret);
}
@@ -187,7 +243,7 @@ void os_thread_get_current_name_np(char threadname[NETDATA_THREAD_NAME_MAX + 1])
#endif
}
-static void *thread_start(void *ptr) {
+static void *netdata_thread_init(void *ptr) {
netdata_thread = (NETDATA_THREAD *)ptr;
if(!(netdata_thread->options & NETDATA_THREAD_OPTION_DONT_LOG_STARTUP))
@@ -213,11 +269,11 @@ int netdata_thread_create(netdata_thread_t *thread, const char *tag, NETDATA_THR
NETDATA_THREAD *info = mallocz(sizeof(NETDATA_THREAD));
info->arg = arg;
info->thread = thread;
- info->tag = strdupz(tag);
info->start_routine = start_routine;
info->options = options;
+ strncpyz(info->tag, tag, NETDATA_THREAD_NAME_MAX);
- int ret = pthread_create(thread, netdata_threads_attr, thread_start, info);
+ int ret = pthread_create(thread, netdata_threads_attr, netdata_thread_init, info);
if(ret != 0)
error("failed to create new thread for %s. pthread_create() failed with code %d", tag, ret);
diff --git a/libnetdata/threads/threads.h b/libnetdata/threads/threads.h
index ccc18aff0..ad31b8816 100644
--- a/libnetdata/threads/threads.h
+++ b/libnetdata/threads/threads.h
@@ -43,6 +43,8 @@ int netdata_thread_detach(pthread_t thread);
void uv_thread_set_name_np(uv_thread_t ut, const char* name);
void os_thread_get_current_name_np(char threadname[NETDATA_THREAD_NAME_MAX + 1]);
+void webrtc_set_thread_name(void);
+
#define netdata_thread_self pthread_self
#define netdata_thread_testcancel pthread_testcancel
diff --git a/libnetdata/url/README.md b/libnetdata/url/README.md
index cca6f8731..1ba51e2ea 100644
--- a/libnetdata/url/README.md
+++ b/libnetdata/url/README.md
@@ -4,7 +4,7 @@ custom_edit_url: https://github.com/netdata/netdata/edit/master/libnetdata/url/R
sidebar_label: "URL"
learn_status: "Published"
learn_topic_type: "Tasks"
-learn_rel_path: "Developers/libnetdata libraries"
+learn_rel_path: "Developers/libnetdata"
-->
# URL
diff --git a/libnetdata/url/url.c b/libnetdata/url/url.c
index f90b3d589..7a671946a 100644
--- a/libnetdata/url/url.c
+++ b/libnetdata/url/url.c
@@ -17,7 +17,7 @@ char to_hex(char code) {
return hex[code & 15];
}
-/* Returns a url-encoded version of str */
+/* Returns an url-encoded version of str */
/* IMPORTANT: be sure to free() the returned string after use */
char *url_encode(char *str) {
char *buf, *pbuf;
@@ -33,8 +33,8 @@ char *url_encode(char *str) {
else{
*pbuf++ = '%';
- *pbuf++ = to_hex(*str >> 4);
- *pbuf++ = to_hex(*str & 15);
+ *pbuf++ = to_hex((char)(*str >> 4));
+ *pbuf++ = to_hex((char)(*str & 15));
}
str++;
@@ -55,9 +55,9 @@ char *url_encode(char *str) {
*
* @return The character decoded on success and 0 otherwise
*/
-char url_percent_escape_decode(char *s) {
+char url_percent_escape_decode(const char *s) {
if(likely(s[1] && s[2]))
- return from_hex(s[1]) << 4 | from_hex(s[2]);
+ return (char)(from_hex(s[1]) << 4 | from_hex(s[2]));
return 0;
}
@@ -98,7 +98,7 @@ char url_utf8_get_byte_length(char c) {
*
* @return count of bytes written to *d
*/
-char url_decode_multibyte_utf8(char *s, char *d, char *d_end) {
+char url_decode_multibyte_utf8(const char *s, char *d, const char *d_end) {
char first_byte = url_percent_escape_decode(s);
if(unlikely(!first_byte || !IS_UTF8_STARTBYTE(first_byte)))
@@ -189,9 +189,9 @@ unsigned char *utf8_check(unsigned char *s)
return NULL;
}
-char *url_decode_r(char *to, char *url, size_t size) {
- char *s = url, // source
- *d = to, // destination
+char *url_decode_r(char *to, const char *url, size_t size) {
+ const char *s = url; // source
+ char *d = to, // destination
*e = &to[size - 1]; // destination end
while(*s && d < e) {
@@ -236,31 +236,45 @@ fail_cleanup:
return NULL;
}
-/**
- * Is request complete?
- *
- * Check whether the request is complete.
- * This function cannot check all the requests METHODS, for example, case you are working with POST, it will fail.
- *
- * @param begin is the first character of the sequence to analyse.
- * @param end is the last character of the sequence
- * @param length is the length of the total of bytes read, it is not the difference between end and begin.
- *
- * @return It returns 1 when the request is complete and 0 otherwise.
- */
-inline int url_is_request_complete(char *begin, char *end, size_t length) {
+inline bool url_is_request_complete(char *begin, char *end, size_t length, char **post_payload, size_t *post_payload_size) {
+ if (begin == end || length < 4)
+ return false;
- if ( begin == end) {
- //Message cannot be complete when first and last address are the same
- return 0;
+ if(likely(strncmp(begin, "GET ", 4)) == 0) {
+ return strstr(end - 4, "\r\n\r\n");
}
+ else if(unlikely(strncmp(begin, "POST ", 5) == 0)) {
+ char *cl = strstr(begin, "Content-Length: ");
+ if(!cl) return false;
+ cl = &cl[16];
- //This math to verify the last is valid, because we are discarding the POST
- if (length > 4) {
- begin = end - 4;
- }
+ size_t content_length = str2ul(cl);
+
+ char *payload = strstr(cl, "\r\n\r\n");
+ if(!payload) return false;
+ payload += 4;
+
+ size_t payload_length = length - (payload - begin);
+
+ if(payload_length == content_length) {
+ if(post_payload && post_payload_size) {
+ if (*post_payload)
+ freez(*post_payload);
+
+ *post_payload = mallocz(payload_length + 1);
+ memcpy(*post_payload, payload, payload_length);
+ (*post_payload)[payload_length] = '\0';
+
+ *post_payload_size = payload_length;
+ }
+ return true;
+ }
- return (strstr(begin, "\r\n\r\n"))?1:0;
+ return false;
+ }
+ else {
+ return strstr(end - 4, "\r\n\r\n");
+ }
}
/**
@@ -283,109 +297,3 @@ inline char *url_find_protocol(char *s) {
return s;
}
-
-/**
- * Map query string
- *
- * Map the query string fields that will be decoded.
- * This functions must be called after to check the presence of query strings,
- * here we are assuming that you already tested this.
- *
- * @param out the pointer to pointers that will be used to map
- * @param url the input url that we are decoding.
- *
- * @return It returns the number of total variables in the query string.
- */
-int url_map_query_string(char **out, char *url) {
- (void)out;
- (void)url;
- int count = 0;
-
- //First we try to parse considering that there was not URL encode process
- char *moveme = url;
- char *ptr;
-
- //We always we have at least one here, so I can set this.
- out[count++] = moveme;
- while(moveme) {
- ptr = strchr((moveme+1), '&');
- if(ptr) {
- out[count++] = ptr;
- }
-
- moveme = ptr;
- }
-
- //I could not find any '&', so I am assuming now it is like '%26'
- if (count == 1) {
- moveme = url;
- while(moveme) {
- ptr = strchr((moveme+1), '%');
- if(ptr) {
- char *test = (ptr+1);
- if (!strncmp(test, "3f", 2) || !strncmp(test, "3F", 2)) {
- out[count++] = ptr;
- }
- }
- moveme = ptr;
- }
- }
-
- return count;
-}
-
-/**
- * Parse query string
- *
- * Parse the query string mapped and store it inside output.
- *
- * @param output is a vector where I will store the string.
- * @param max is the maximum length of the output
- * @param map the map done by the function url_map_query_string.
- * @param total the total number of variables inside map
- *
- * @return It returns 0 on success and -1 otherwise
- */
-int url_parse_query_string(char *output, size_t max, char **map, int total) {
- if(!total) {
- return 0;
- }
-
- int counter, next;
- size_t length;
- char *end;
- char *begin = map[0];
- char save;
- size_t copied = 0;
- for(counter = 0, next=1 ; next <= total ; ++counter, ++next) {
- if (next != total) {
- end = map[next];
- length = (size_t) (end - begin);
- save = *end;
- *end = 0x00;
- } else {
- length = strlen(begin);
- end = NULL;
- }
- length++;
-
- if (length > (max - copied)) {
- error("Parsing query string: we cannot parse a query string so big");
- break;
- }
-
- if(!url_decode_r(output, begin, length)) {
- return -1;
- }
- length = strlen(output);
- copied += length;
- output += length;
-
- begin = end;
- if (begin) {
- *begin = save;
- }
- }
-
- return 0;
-}
diff --git a/libnetdata/url/url.h b/libnetdata/url/url.h
index da0f69ac1..9db018f0b 100644
--- a/libnetdata/url/url.h
+++ b/libnetdata/url/url.h
@@ -23,13 +23,9 @@ char *url_encode(char *str);
/* IMPORTANT: be sure to free() the returned string after use */
char *url_decode(char *str);
-char *url_decode_r(char *to, char *url, size_t size);
+char *url_decode_r(char *to, const char *url, size_t size);
-#define WEB_FIELDS_MAX 400
-int url_map_query_string(char **out, char *url);
-int url_parse_query_string(char *output, size_t max, char **map, int total);
-
-int url_is_request_complete(char *begin,char *end,size_t length);
+bool url_is_request_complete(char *begin, char *end, size_t length, char **post_payload, size_t *post_payload_length);
char *url_find_protocol(char *s);
#endif /* NETDATA_URL_H */
diff --git a/libnetdata/worker_utilization/README.md b/libnetdata/worker_utilization/README.md
index 35f30b40b..04dfb6215 100644
--- a/libnetdata/worker_utilization/README.md
+++ b/libnetdata/worker_utilization/README.md
@@ -1,6 +1,10 @@
<!--
title: "Worker Utilization"
-custom_edit_url: https://github.com/netdata/netdata/edit/master/libnetdata/onewayallocator/README.md
+custom_edit_url: https://github.com/netdata/netdata/edit/master/libnetdata/worker_utilization/README.md
+sidebar_label: "Worker Utilization"
+learn_status: "Published"
+learn_topic_type: "References"
+learn_rel_path: "Developers/libnetdata"
-->
# Worker Utilization
diff --git a/libnetdata/worker_utilization/worker_utilization.c b/libnetdata/worker_utilization/worker_utilization.c
index 8028e3a21..d47d81c4e 100644
--- a/libnetdata/worker_utilization/worker_utilization.c
+++ b/libnetdata/worker_utilization/worker_utilization.c
@@ -61,6 +61,14 @@ static struct workers_globals {
static __thread struct worker *worker = NULL; // the current thread worker
+static inline usec_t worker_now_monotonic_usec(void) {
+#ifdef NETDATA_WITHOUT_WORKERS_LATENCY
+ return 0;
+#else
+ return now_monotonic_usec();
+#endif
+}
+
size_t workers_allocated_memory(void) {
netdata_spinlock_lock(&workers_globals.spinlock);
size_t memory = workers_globals.memory;
@@ -77,7 +85,7 @@ void worker_register(const char *name) {
worker->tag = strdupz(netdata_thread_tag());
worker->workname = strdupz(name);
- usec_t now = now_monotonic_usec();
+ usec_t now = worker_now_monotonic_usec();
worker->statistics_last_checkpoint = now;
worker->last_action_timestamp = now;
worker->last_action = WORKER_IDLE;
@@ -181,14 +189,14 @@ static inline void worker_is_idle_with_time(usec_t now) {
void worker_is_idle(void) {
if(unlikely(!worker || worker->last_action != WORKER_BUSY)) return;
- worker_is_idle_with_time(now_monotonic_usec());
+ worker_is_idle_with_time(worker_now_monotonic_usec());
}
void worker_is_busy(size_t job_id) {
if(unlikely(!worker || job_id >= WORKER_UTILIZATION_MAX_JOB_TYPES))
return;
- usec_t now = now_monotonic_usec();
+ usec_t now = worker_now_monotonic_usec();
if(worker->last_action == WORKER_BUSY)
worker_is_idle_with_time(now);
@@ -260,7 +268,7 @@ void workers_foreach(const char *name, void (*callback)(
struct worker *p;
DOUBLE_LINKED_LIST_FOREACH_FORWARD(workname->base, p, prev, next) {
- usec_t now = now_monotonic_usec();
+ usec_t now = worker_now_monotonic_usec();
// find per job type statistics
STRING *per_job_type_name[WORKER_UTILIZATION_MAX_JOB_TYPES];