]> arthur.barton.de Git - netdata.git/blobdiff - src/procfile.c
dns_query_time plugin: replace "." with "_" in dimensions
[netdata.git] / src / procfile.c
index ee7378713fc355414e37e2cda68c1b9c14bb8ab1..3a89e83539c75371db0f7c8c77565463d746a772 100644 (file)
@@ -1,4 +1,5 @@
 #include "common.h"
+#include "procfile.h"
 
 #define PF_PREFIX "PROCFILE"
 
@@ -10,15 +11,36 @@ int procfile_adaptive_initial_allocation = 0;
 
 // if adaptive allocation is set, these store the
 // max values we have seen so far
-uint32_t procfile_max_lines = PFLINES_INCREASE_STEP;
-uint32_t procfile_max_words = PFWORDS_INCREASE_STEP;
+size_t procfile_max_lines = PFLINES_INCREASE_STEP;
+size_t procfile_max_words = PFWORDS_INCREASE_STEP;
 size_t procfile_max_allocation = PROCFILE_INCREMENT_BUFFER;
 
+
 // ----------------------------------------------------------------------------
-// An array of words
 
+char *procfile_filename(procfile *ff) {
+    if(ff->filename[0]) return ff->filename;
+
+    char buffer[FILENAME_MAX + 1];
+    snprintfz(buffer, FILENAME_MAX, "/proc/self/fd/%d", ff->fd);
+
+    ssize_t l = readlink(buffer, ff->filename, FILENAME_MAX);
+    if(unlikely(l == -1))
+        snprintfz(ff->filename, FILENAME_MAX, "unknown filename for fd %d", ff->fd);
+    else
+        ff->filename[l] = '\0';
+
+    // on non-linux systems, something like this will be needed
+    // fcntl(ff->fd, F_GETPATH, ff->filename)
+
+    return ff->filename;
+}
+
+// ----------------------------------------------------------------------------
+// An array of words
 
-pfwords *pfwords_add(pfwords *fw, char *str) {
+static inline pfwords *pfwords_add(pfwords *fw, char *str) NEVERNULL;
+static inline pfwords *pfwords_add(pfwords *fw, char *str) {
     // debug(D_PROCFILE, PF_PREFIX ":   adding word No %d: '%s'", fw->len, str);
 
     if(unlikely(fw->len == fw->size)) {
@@ -33,10 +55,11 @@ pfwords *pfwords_add(pfwords *fw, char *str) {
     return fw;
 }
 
-pfwords *pfwords_new(void) {
+static inline pfwords *pfwords_new(void) NEVERNULL;
+static inline pfwords *pfwords_new(void) {
     // debug(D_PROCFILE, PF_PREFIX ":   initializing words");
 
-    uint32_t size = (procfile_adaptive_initial_allocation) ? procfile_max_words : PFWORDS_INCREASE_STEP;
+    size_t size = (procfile_adaptive_initial_allocation) ? procfile_max_words : PFWORDS_INCREASE_STEP;
 
     pfwords *new = mallocz(sizeof(pfwords) + size * sizeof(char *));
     new->len = 0;
@@ -44,12 +67,12 @@ pfwords *pfwords_new(void) {
     return new;
 }
 
-void pfwords_reset(pfwords *fw) {
+static inline void pfwords_reset(pfwords *fw) {
     // debug(D_PROCFILE, PF_PREFIX ":   reseting words");
     fw->len = 0;
 }
 
-void pfwords_free(pfwords *fw) {
+static inline void pfwords_free(pfwords *fw) {
     // debug(D_PROCFILE, PF_PREFIX ":   freeing words");
 
     freez(fw);
@@ -59,7 +82,8 @@ void pfwords_free(pfwords *fw) {
 // ----------------------------------------------------------------------------
 // An array of lines
 
-pflines *pflines_add(pflines *fl, uint32_t first_word) {
+static inline pflines *pflines_add(pflines *fl, size_t first_word) NEVERNULL;
+static inline pflines *pflines_add(pflines *fl, size_t first_word) {
     // debug(D_PROCFILE, PF_PREFIX ":   adding line %d at word %d", fl->len, first_word);
 
     if(unlikely(fl->len == fl->size)) {
@@ -75,10 +99,11 @@ pflines *pflines_add(pflines *fl, uint32_t first_word) {
     return fl;
 }
 
-pflines *pflines_new(void) {
+static inline pflines *pflines_new(void) NEVERNULL;
+static inline pflines *pflines_new(void) {
     // debug(D_PROCFILE, PF_PREFIX ":   initializing lines");
 
-    uint32_t size = (unlikely(procfile_adaptive_initial_allocation)) ? procfile_max_words : PFLINES_INCREASE_STEP;
+    size_t size = (unlikely(procfile_adaptive_initial_allocation)) ? procfile_max_words : PFLINES_INCREASE_STEP;
 
     pflines *new = mallocz(sizeof(pflines) + size * sizeof(ffline));
     new->len = 0;
@@ -86,13 +111,13 @@ pflines *pflines_new(void) {
     return new;
 }
 
-void pflines_reset(pflines *fl) {
+static inline void pflines_reset(pflines *fl) {
     // debug(D_PROCFILE, PF_PREFIX ":   reseting lines");
 
     fl->len = 0;
 }
 
-void pflines_free(pflines *fl) {
+static inline void pflines_free(pflines *fl) {
     // debug(D_PROCFILE, PF_PREFIX ":   freeing lines");
 
     freez(fl);
@@ -102,15 +127,10 @@ void pflines_free(pflines *fl) {
 // ----------------------------------------------------------------------------
 // The procfile
 
-#define PF_CHAR_IS_SEPARATOR    ' '
-#define PF_CHAR_IS_NEWLINE      'N'
-#define PF_CHAR_IS_WORD         'W'
-#define PF_CHAR_IS_QUOTE        'Q'
-#define PF_CHAR_IS_OPEN         'O'
-#define PF_CHAR_IS_CLOSE        'C'
-
 void procfile_close(procfile *ff) {
-    debug(D_PROCFILE, PF_PREFIX ": Closing file '%s'", ff->filename);
+    if(unlikely(!ff)) return;
+
+    debug(D_PROCFILE, PF_PREFIX ": Closing file '%s'", procfile_filename(ff));
 
     if(likely(ff->lines)) pflines_free(ff->lines);
     if(likely(ff->words)) pfwords_free(ff->words);
@@ -119,125 +139,122 @@ void procfile_close(procfile *ff) {
     freez(ff);
 }
 
-procfile *procfile_parser(procfile *ff) {
-    debug(D_PROCFILE, PF_PREFIX ": Parsing file '%s'", ff->filename);
+static inline void procfile_parser(procfile *ff) {
+    // debug(D_PROCFILE, PF_PREFIX ": Parsing file '%s'", ff->filename);
 
-    char *s = ff->data, *e = &ff->data[ff->len], *t = ff->data, quote = 0;
-    uint32_t l = 0, w = 0;
-    int opened = 0;
+    char  *s = ff->data                 // our current position
+        , *e = &ff->data[ff->len]       // the terminating null
+        , *t = ff->data;                // the first character of a quoted or a parenthesized string
+
+                                        // the look up array to find our type of character
+    PF_CHAR_TYPE *separators = ff->separators;
+
+    char quote = 0;                     // the quote character - only when in quoted string
+
+    size_t
+          l = 0                         // counts the number of lines we added
+        , w = 0                         // counts the number of words we added
+        , opened = 0;                   // counts the number of open parenthesis
 
     ff->lines = pflines_add(ff->lines, w);
-    if(unlikely(!ff->lines)) goto cleanup;
 
     while(likely(s < e)) {
         // we are not at the end
+        PF_CHAR_TYPE ct = separators[(unsigned char)(*s)];
 
-        switch(ff->separators[(uint8_t)(*s)]) {
-            case PF_CHAR_IS_OPEN:
-                if(s == t) {
-                    opened++;
-                    t = ++s;
-                }
-                else if(opened) {
-                    opened++;
-                    s++;
-                }
-                else
-                    s++;
+        // this is faster than a switch()
+        if(likely(ct == PF_CHAR_IS_WORD)) {
+            s++;
+        }
+        else if(likely(ct == PF_CHAR_IS_SEPARATOR)) {
+            if(unlikely(quote || opened)) {
+                // we are inside a quote
+                s++;
                 continue;
+            }
 
-            case PF_CHAR_IS_CLOSE:
-                if(opened) {
-                    opened--;
-
-                    if(!opened) {
-                        *s = '\0';
-                        ff->words = pfwords_add(ff->words, t);
-                        if(unlikely(!ff->words)) goto cleanup;
-
-                        ff->lines->lines[l].words++;
-                        w++;
-
-                        t = ++s;
-                    }
-                    else
-                        s++;
-                }
-                else
-                    s++;
+            if(unlikely(s == t)) {
+                // skip all leading white spaces
+                t = ++s;
                 continue;
+            }
 
-            case PF_CHAR_IS_QUOTE:
-                if(unlikely(!quote && s == t)) {
-                    // quote opened at the beginning
-                    quote = *s;
-                    t = ++s;
-                }
-                else if(unlikely(quote && quote == *s)) {
-                    // quote closed
-                    quote = 0;
-
-                    *s = '\0';
-                    ff->words = pfwords_add(ff->words, t);
-                    if(unlikely(!ff->words)) goto cleanup;
-
-                    ff->lines->lines[l].words++;
-                    w++;
-
-                    t = ++s;
-                }
-                else
-                    s++;
-                continue;
+            // end of word
+            *s = '\0';
 
-            case PF_CHAR_IS_SEPARATOR:
-                if(unlikely(quote || opened)) {
-                    // we are inside a quote
-                    s++;
-                    continue;
-                }
+            ff->words = pfwords_add(ff->words, t);
+            ff->lines->lines[l].words++;
+            w++;
 
-                if(unlikely(s == t)) {
-                    // skip all leading white spaces
-                    t = ++s;
-                    continue;
-                }
+            t = ++s;
+        }
+        else if(likely(ct == PF_CHAR_IS_NEWLINE)) {
+            // end of line
+            *s = '\0';
 
-                // end of word
-                *s = '\0';
+            ff->words = pfwords_add(ff->words, t);
+            ff->lines->lines[l].words++;
+            w++;
 
-                ff->words = pfwords_add(ff->words, t);
-                if(unlikely(!ff->words)) goto cleanup;
+            // debug(D_PROCFILE, PF_PREFIX ":   ended line %d with %d words", l, ff->lines->lines[l].words);
 
-                ff->lines->lines[l].words++;
-                w++;
+            ff->lines = pflines_add(ff->lines, w);
+            l++;
 
+            t = ++s;
+        }
+        else if(likely(ct == PF_CHAR_IS_QUOTE)) {
+            if(unlikely(!quote && s == t)) {
+                // quote opened at the beginning
+                quote = *s;
                 t = ++s;
-                continue;
+            }
+            else if(unlikely(quote && quote == *s)) {
+                // quote closed
+                quote = 0;
 
-            case PF_CHAR_IS_NEWLINE:
-                // end of line
                 *s = '\0';
-
                 ff->words = pfwords_add(ff->words, t);
-                if(unlikely(!ff->words)) goto cleanup;
-
                 ff->lines->lines[l].words++;
                 w++;
 
-                // debug(D_PROCFILE, PF_PREFIX ":   ended line %d with %d words", l, ff->lines->lines[l].words);
-
-                ff->lines = pflines_add(ff->lines, w);
-                if(unlikely(!ff->lines)) goto cleanup;
-                l++;
-
                 t = ++s;
-                continue;
+            }
+            else
+                s++;
+        }
+        else if(likely(ct == PF_CHAR_IS_OPEN)) {
+            if(s == t) {
+                opened++;
+                t = ++s;
+            }
+            else if(opened) {
+                opened++;
+                s++;
+            }
+            else
+                s++;
+        }
+        else if(likely(ct == PF_CHAR_IS_CLOSE)) {
+            if(opened) {
+                opened--;
 
-            default:
+                if(!opened) {
+                    *s = '\0';
+                    ff->words = pfwords_add(ff->words, t);
+                    ff->lines->lines[l].words++;
+                    w++;
+
+                    t = ++s;
+                }
+                else
+                    s++;
+            }
+            else
                 s++;
-                continue;
         }
+        else
+            fatal("Internal Error: procfile_readall() does not handle all the cases.");
     }
 
     if(likely(s > t && t < e)) {
@@ -250,41 +267,29 @@ procfile *procfile_parser(procfile *ff) {
         }
 
         ff->words = pfwords_add(ff->words, t);
-        if(unlikely(!ff->words)) goto cleanup;
-
         ff->lines->lines[l].words++;
-        w++;
     }
-
-    return ff;
-
-cleanup:
-    error(PF_PREFIX ": Failed to parse file '%s'", ff->filename);
-    procfile_close(ff);
-    return NULL;
 }
 
 procfile *procfile_readall(procfile *ff) {
-    debug(D_PROCFILE, PF_PREFIX ": Reading file '%s'.", ff->filename);
+    // debug(D_PROCFILE, PF_PREFIX ": Reading file '%s'.", ff->filename);
 
-    ssize_t s, r = 1, x;
-    ff->len = 0;
-
-    while(likely(r > 0)) {
-        s = ff->len;
-        x = ff->size - s;
+    ff->len = 0;    // zero the used size
+    ssize_t r = 1;  // read at least once
+    while(r > 0) {
+        ssize_t s = ff->len;
+        ssize_t x = ff->size - s;
 
         if(unlikely(!x)) {
-            debug(D_PROCFILE, PF_PREFIX ": Expanding data buffer for file '%s'.", ff->filename);
-
+            debug(D_PROCFILE, PF_PREFIX ": Expanding data buffer for file '%s'.", procfile_filename(ff));
             ff = reallocz(ff, sizeof(procfile) + ff->size + PROCFILE_INCREMENT_BUFFER);
             ff->size += PROCFILE_INCREMENT_BUFFER;
         }
 
-        debug(D_PROCFILE, "Reading file '%s', from position %ld with length %lu", ff->filename, s, ff->size - s);
+        debug(D_PROCFILE, "Reading file '%s', from position %zd with length %zd", procfile_filename(ff), s, (ssize_t)(ff->size - s));
         r = read(ff->fd, &ff->data[s], ff->size - s);
         if(unlikely(r == -1)) {
-            if(unlikely(!(ff->flags & PROCFILE_FLAG_NO_ERROR_ON_FILE_IO))) error(PF_PREFIX ": Cannot read from file '%s'", ff->filename);
+            if(unlikely(!(ff->flags & PROCFILE_FLAG_NO_ERROR_ON_FILE_IO))) error(PF_PREFIX ": Cannot read from file '%s'", procfile_filename(ff));
             procfile_close(ff);
             return NULL;
         }
@@ -292,17 +297,16 @@ procfile *procfile_readall(procfile *ff) {
         ff->len += r;
     }
 
-    debug(D_PROCFILE, "Rewinding file '%s'", ff->filename);
+    // debug(D_PROCFILE, "Rewinding file '%s'", ff->filename);
     if(unlikely(lseek(ff->fd, 0, SEEK_SET) == -1)) {
-        if(unlikely(!(ff->flags & PROCFILE_FLAG_NO_ERROR_ON_FILE_IO))) error(PF_PREFIX ": Cannot rewind on file '%s'.", ff->filename);
+        if(unlikely(!(ff->flags & PROCFILE_FLAG_NO_ERROR_ON_FILE_IO))) error(PF_PREFIX ": Cannot rewind on file '%s'.", procfile_filename(ff));
         procfile_close(ff);
         return NULL;
     }
 
     pflines_reset(ff->lines);
     pfwords_reset(ff->words);
-
-    ff = procfile_parser(ff);
+    procfile_parser(ff);
 
     if(unlikely(procfile_adaptive_initial_allocation)) {
         if(unlikely(ff->len > procfile_max_allocation)) procfile_max_allocation = ff->len;
@@ -310,29 +314,37 @@ procfile *procfile_readall(procfile *ff) {
         if(unlikely(ff->words->len > procfile_max_words)) procfile_max_words = ff->words->len;
     }
 
-    debug(D_PROCFILE, "File '%s' updated.", ff->filename);
+    // debug(D_PROCFILE, "File '%s' updated.", ff->filename);
     return ff;
 }
 
-static void procfile_set_separators(procfile *ff, const char *separators) {
-    static char def[256] = { [0 ... 255] = 0 };
-    int i;
+static inline void procfile_set_separators(procfile *ff, const char *separators) {
+    static PF_CHAR_TYPE def[256];
+    static char initilized = 0;
 
-    if(unlikely(!def[255])) {
+    if(unlikely(!initilized)) {
         // this is thread safe
-        // we check that the last byte is non-zero
-        // if it is zero, multiple threads may be executing this at the same time
-        // setting in def[] the exact same values
-        for(i = 0; likely(i < 256) ;i++) {
-            if(unlikely(i == '\n' || i == '\r')) def[i] = PF_CHAR_IS_NEWLINE;
-            else if(unlikely(isspace(i) || !isprint(i))) def[i] = PF_CHAR_IS_SEPARATOR;
-            else def[i] = PF_CHAR_IS_WORD;
+        // if initialized is zero, multiple threads may be executing
+        // this code at the same time, setting in def[] the exact same values
+        int i = 256;
+        while(i--) {
+            if(unlikely(i == '\n' || i == '\r'))
+                def[i] = PF_CHAR_IS_NEWLINE;
+
+            else if(unlikely(isspace(i) || !isprint(i)))
+                def[i] = PF_CHAR_IS_SEPARATOR;
+
+            else
+                def[i] = PF_CHAR_IS_WORD;
         }
+
+        initilized = 1;
     }
 
     // copy the default
-    char *ffs = ff->separators, *ffd = def, *ffe = &def[256];
-    while(likely(ffd != ffe)) *ffs++ = *ffd++;
+    PF_CHAR_TYPE *ffs = ff->separators, *ffd = def, *ffe = &def[256];
+    while(ffd != ffe)
+        *ffs++ = *ffd++;
 
     // set the separators
     if(unlikely(!separators))
@@ -340,47 +352,50 @@ static void procfile_set_separators(procfile *ff, const char *separators) {
 
     ffs = ff->separators;
     const char *s = separators;
-    while(likely(*s))
+    while(*s)
         ffs[(int)*s++] = PF_CHAR_IS_SEPARATOR;
 }
 
 void procfile_set_quotes(procfile *ff, const char *quotes) {
+    PF_CHAR_TYPE *ffs = ff->separators;
+
     // remove all quotes
-    int i;
-    for(i = 0; i < 256 ; i++)
-        if(unlikely(ff->separators[i] == PF_CHAR_IS_QUOTE))
-            ff->separators[i] = PF_CHAR_IS_WORD;
+    int i = 256;
+    while(i--)
+        if(unlikely(ffs[i] == PF_CHAR_IS_QUOTE))
+            ffs[i] = PF_CHAR_IS_WORD;
 
     // if nothing given, return
     if(unlikely(!quotes || !*quotes))
         return;
 
     // set the quotes
-    char *ffs = ff->separators;
     const char *s = quotes;
-    while(likely(*s))
+    while(*s)
         ffs[(int)*s++] = PF_CHAR_IS_QUOTE;
 }
 
 void procfile_set_open_close(procfile *ff, const char *open, const char *close) {
+    PF_CHAR_TYPE *ffs = ff->separators;
+
     // remove all open/close
-    int i;
-    for(i = 0; i < 256 ; i++)
-        if(unlikely(ff->separators[i] == PF_CHAR_IS_OPEN || ff->separators[i] == PF_CHAR_IS_CLOSE))
-            ff->separators[i] = PF_CHAR_IS_WORD;
+    int i = 256;
+    while(i--)
+        if(unlikely(ffs[i] == PF_CHAR_IS_OPEN || ffs[i] == PF_CHAR_IS_CLOSE))
+            ffs[i] = PF_CHAR_IS_WORD;
 
     // if nothing given, return
     if(unlikely(!open || !*open || !close || !*close))
         return;
 
     // set the openings
-    char *ffs = ff->separators;
     const char *s = open;
-    while(likely(*s))
+    while(*s)
         ffs[(int)*s++] = PF_CHAR_IS_OPEN;
 
+    // set the closings
     s = close;
-    while(likely(*s))
+    while(*s)
         ffs[(int)*s++] = PF_CHAR_IS_CLOSE;
 }
 
@@ -395,7 +410,9 @@ procfile *procfile_open(const char *filename, const char *separators, uint32_t f
 
     size_t size = (unlikely(procfile_adaptive_initial_allocation)) ? procfile_max_allocation : PROCFILE_INCREMENT_BUFFER;
     procfile *ff = mallocz(sizeof(procfile) + size);
-    strncpyz(ff->filename, filename, FILENAME_MAX);
+
+    //strncpyz(ff->filename, filename, FILENAME_MAX);
+    ff->filename[0] = '\0';
 
     ff->fd = fd;
     ff->size = size;
@@ -405,12 +422,6 @@ procfile *procfile_open(const char *filename, const char *separators, uint32_t f
     ff->lines = pflines_new();
     ff->words = pfwords_new();
 
-    if(unlikely(!ff->lines || !ff->words)) {
-        error(PF_PREFIX ": Cannot initialize parser for file '%s'", filename);
-        procfile_close(ff);
-        return NULL;
-    }
-
     procfile_set_separators(ff, separators);
 
     debug(D_PROCFILE, "File '%s' opened.", filename);
@@ -428,7 +439,8 @@ procfile *procfile_reopen(procfile *ff, const char *filename, const char *separa
         return NULL;
     }
 
-    strncpyz(ff->filename, filename, FILENAME_MAX);
+    //strncpyz(ff->filename, filename, FILENAME_MAX);
+    ff->filename[0] = '\0';
 
     ff->flags = flags;
 
@@ -442,20 +454,20 @@ procfile *procfile_reopen(procfile *ff, const char *filename, const char *separa
 // example parsing of procfile data
 
 void procfile_print(procfile *ff) {
-    uint32_t lines = procfile_lines(ff), l;
-    uint32_t words, w;
+    size_t lines = procfile_lines(ff), l;
     char *s;
 
-    debug(D_PROCFILE, "File '%s' with %u lines and %u words", ff->filename, ff->lines->len, ff->words->len);
+    debug(D_PROCFILE, "File '%s' with %zu lines and %zu words", procfile_filename(ff), ff->lines->len, ff->words->len);
 
     for(l = 0; likely(l < lines) ;l++) {
-        words = procfile_linewords(ff, l);
+        size_t words = procfile_linewords(ff, l);
 
-        debug(D_PROCFILE, " line %u starts at word %u and has %u words", l, ff->lines->lines[l].first, ff->lines->lines[l].words);
+        debug(D_PROCFILE, " line %zu starts at word %zu and has %zu words", l, ff->lines->lines[l].first, ff->lines->lines[l].words);
 
+        size_t w;
         for(w = 0; likely(w < words) ;w++) {
             s = procfile_lineword(ff, l, w);
-            debug(D_PROCFILE, "     [%u.%u] '%s'", l, w, s);
+            debug(D_PROCFILE, "     [%zu.%zu] '%s'", l, w, s);
         }
     }
 }