diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2023-05-08 16:27:04 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2023-05-08 16:27:04 +0000 |
commit | a836a244a3d2bdd4da1ee2641e3e957850668cea (patch) | |
tree | cb87c75b3677fab7144f868435243f864048a1e6 /libnetdata | |
parent | Adding upstream version 1.38.1. (diff) | |
download | netdata-a836a244a3d2bdd4da1ee2641e3e957850668cea.tar.xz netdata-a836a244a3d2bdd4da1ee2641e3e957850668cea.zip |
Adding upstream version 1.39.0.upstream/1.39.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libnetdata')
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]; |