]> arthur.barton.de Git - netdata.git/blob - src/rrd2json.c
operational health templatizer
[netdata.git] / src / rrd2json.c
1 #include "common.h"
2
3 #define HOSTNAME_MAX 1024
4 char *hostname = "unknown";
5
6 void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb)
7 {
8     pthread_rwlock_rdlock(&st->rwlock);
9
10     buffer_sprintf(wb,
11         "\t\t{\n"
12         "\t\t\t\"id\": \"%s\",\n"
13         "\t\t\t\"name\": \"%s\",\n"
14         "\t\t\t\"type\": \"%s\",\n"
15         "\t\t\t\"family\": \"%s\",\n"
16         "\t\t\t\"context\": \"%s\",\n"
17         "\t\t\t\"title\": \"%s\",\n"
18         "\t\t\t\"priority\": %ld,\n"
19         "\t\t\t\"enabled\": %s,\n"
20         "\t\t\t\"units\": \"%s\",\n"
21         "\t\t\t\"data_url\": \"/api/v1/data?chart=%s\",\n"
22         "\t\t\t\"chart_type\": \"%s\",\n"
23         "\t\t\t\"duration\": %ld,\n"
24         "\t\t\t\"first_entry\": %ld,\n"
25         "\t\t\t\"last_entry\": %ld,\n"
26         "\t\t\t\"update_every\": %d,\n"
27         "\t\t\t\"dimensions\": {\n"
28         , st->id
29         , st->name
30         , st->type
31         , st->family
32         , st->context
33         , st->title
34         , st->priority
35         , st->enabled?"true":"false"
36         , st->units
37         , st->name
38         , rrdset_type_name(st->chart_type)
39         , st->entries * st->update_every
40         , rrdset_first_entry_t(st)
41         , rrdset_last_entry_t(st)
42         , st->update_every
43         );
44
45     unsigned long memory = st->memsize;
46
47     int c = 0;
48     RRDDIM *rd;
49     for(rd = st->dimensions; rd ; rd = rd->next) {
50         if(rd->flags & RRDDIM_FLAG_HIDDEN) continue;
51
52         memory += rd->memsize;
53
54         buffer_sprintf(wb,
55             "%s"
56             "\t\t\t\t\"%s\": { \"name\": \"%s\" }"
57             , c?",\n":""
58             , rd->id
59             , rd->name
60             );
61
62         c++;
63     }
64
65     buffer_strcat(wb, "\n\t\t\t},\n\t\t\t\"green\": ");
66     buffer_rrd_value(wb, st->green);
67     buffer_strcat(wb, ",\n\t\t\t\"red\": ");
68     buffer_rrd_value(wb, st->red);
69
70     buffer_sprintf(wb,
71         "\n\t\t}"
72         );
73
74     pthread_rwlock_unlock(&st->rwlock);
75 }
76
77 void rrd_stats_api_v1_charts(BUFFER *wb)
78 {
79     long c;
80     RRDSET *st;
81
82     buffer_sprintf(wb, "{\n"
83            "\t\"hostname\": \"%s\""
84         ",\n\t\"update_every\": %d"
85         ",\n\t\"history\": %d"
86         ",\n\t\"charts\": {"
87         , hostname
88         , rrd_update_every
89         , rrd_default_history_entries
90         );
91
92     pthread_rwlock_rdlock(&localhost.rrdset_root_rwlock);
93     for(st = localhost.rrdset_root, c = 0; st ; st = st->next) {
94         if(st->enabled && st->dimensions) {
95             if(c) buffer_strcat(wb, ",");
96             buffer_strcat(wb, "\n\t\t\"");
97             buffer_strcat(wb, st->id);
98             buffer_strcat(wb, "\": ");
99             rrd_stats_api_v1_chart(st, wb);
100             c++;
101         }
102     }
103     pthread_rwlock_unlock(&localhost.rrdset_root_rwlock);
104
105     buffer_strcat(wb, "\n\t}\n}\n");
106 }
107
108
109 unsigned long rrd_stats_one_json(RRDSET *st, char *options, BUFFER *wb)
110 {
111     time_t now = time(NULL);
112
113     pthread_rwlock_rdlock(&st->rwlock);
114
115     buffer_sprintf(wb,
116         "\t\t{\n"
117         "\t\t\t\"id\": \"%s\",\n"
118         "\t\t\t\"name\": \"%s\",\n"
119         "\t\t\t\"type\": \"%s\",\n"
120         "\t\t\t\"family\": \"%s\",\n"
121         "\t\t\t\"context\": \"%s\",\n"
122         "\t\t\t\"title\": \"%s\",\n"
123         "\t\t\t\"priority\": %ld,\n"
124         "\t\t\t\"enabled\": %d,\n"
125         "\t\t\t\"units\": \"%s\",\n"
126         "\t\t\t\"url\": \"/data/%s/%s\",\n"
127         "\t\t\t\"chart_type\": \"%s\",\n"
128         "\t\t\t\"counter\": %lu,\n"
129         "\t\t\t\"entries\": %ld,\n"
130         "\t\t\t\"first_entry_t\": %ld,\n"
131         "\t\t\t\"last_entry\": %lu,\n"
132         "\t\t\t\"last_entry_t\": %ld,\n"
133         "\t\t\t\"last_entry_secs_ago\": %ld,\n"
134         "\t\t\t\"update_every\": %d,\n"
135         "\t\t\t\"isdetail\": %d,\n"
136         "\t\t\t\"usec_since_last_update\": %llu,\n"
137         "\t\t\t\"collected_total\": " TOTAL_NUMBER_FORMAT ",\n"
138         "\t\t\t\"last_collected_total\": " TOTAL_NUMBER_FORMAT ",\n"
139         "\t\t\t\"dimensions\": [\n"
140         , st->id
141         , st->name
142         , st->type
143         , st->family
144         , st->context
145         , st->title
146         , st->priority
147         , st->enabled
148         , st->units
149         , st->name, options?options:""
150         , rrdset_type_name(st->chart_type)
151         , st->counter
152         , st->entries
153         , rrdset_first_entry_t(st)
154         , rrdset_last_slot(st)
155         , rrdset_last_entry_t(st)
156         , (now < rrdset_last_entry_t(st)) ? (time_t)0 : now - rrdset_last_entry_t(st)
157         , st->update_every
158         , st->isdetail
159         , st->usec_since_last_update
160         , st->collected_total
161         , st->last_collected_total
162         );
163
164     unsigned long memory = st->memsize;
165
166     RRDDIM *rd;
167     for(rd = st->dimensions; rd ; rd = rd->next) {
168
169         memory += rd->memsize;
170
171         buffer_sprintf(wb,
172             "\t\t\t\t{\n"
173             "\t\t\t\t\t\"id\": \"%s\",\n"
174             "\t\t\t\t\t\"name\": \"%s\",\n"
175             "\t\t\t\t\t\"entries\": %ld,\n"
176             "\t\t\t\t\t\"isHidden\": %d,\n"
177             "\t\t\t\t\t\"algorithm\": \"%s\",\n"
178             "\t\t\t\t\t\"multiplier\": %ld,\n"
179             "\t\t\t\t\t\"divisor\": %ld,\n"
180             "\t\t\t\t\t\"last_entry_t\": %ld,\n"
181             "\t\t\t\t\t\"collected_value\": " COLLECTED_NUMBER_FORMAT ",\n"
182             "\t\t\t\t\t\"calculated_value\": " CALCULATED_NUMBER_FORMAT ",\n"
183             "\t\t\t\t\t\"last_collected_value\": " COLLECTED_NUMBER_FORMAT ",\n"
184             "\t\t\t\t\t\"last_calculated_value\": " CALCULATED_NUMBER_FORMAT ",\n"
185             "\t\t\t\t\t\"memory\": %lu\n"
186             "\t\t\t\t}%s\n"
187             , rd->id
188             , rd->name
189             , rd->entries
190             , (rd->flags & RRDDIM_FLAG_HIDDEN)?1:0
191             , rrddim_algorithm_name(rd->algorithm)
192             , rd->multiplier
193             , rd->divisor
194             , rd->last_collected_time.tv_sec
195             , rd->collected_value
196             , rd->calculated_value
197             , rd->last_collected_value
198             , rd->last_calculated_value
199             , rd->memsize
200             , rd->next?",":""
201             );
202     }
203
204     buffer_sprintf(wb,
205         "\t\t\t],\n"
206         "\t\t\t\"memory\" : %lu\n"
207         "\t\t}"
208         , memory
209         );
210
211     pthread_rwlock_unlock(&st->rwlock);
212     return memory;
213 }
214
215 #define RRD_GRAPH_JSON_HEADER "{\n\t\"charts\": [\n"
216 #define RRD_GRAPH_JSON_FOOTER "\n\t]\n}\n"
217
218 void rrd_stats_graph_json(RRDSET *st, char *options, BUFFER *wb)
219 {
220     buffer_strcat(wb, RRD_GRAPH_JSON_HEADER);
221     rrd_stats_one_json(st, options, wb);
222     buffer_strcat(wb, RRD_GRAPH_JSON_FOOTER);
223 }
224
225 void rrd_stats_all_json(BUFFER *wb)
226 {
227     unsigned long memory = 0;
228     long c;
229     RRDSET *st;
230
231     buffer_strcat(wb, RRD_GRAPH_JSON_HEADER);
232
233     pthread_rwlock_rdlock(&localhost.rrdset_root_rwlock);
234     for(st = localhost.rrdset_root, c = 0; st ; st = st->next) {
235         if(st->enabled && st->dimensions) {
236             if(c) buffer_strcat(wb, ",\n");
237             memory += rrd_stats_one_json(st, NULL, wb);
238             c++;
239         }
240     }
241     pthread_rwlock_unlock(&localhost.rrdset_root_rwlock);
242
243     buffer_sprintf(wb, "\n\t],\n"
244         "\t\"hostname\": \"%s\",\n"
245         "\t\"update_every\": %d,\n"
246         "\t\"history\": %d,\n"
247         "\t\"memory\": %lu\n"
248         "}\n"
249         , hostname
250         , rrd_update_every
251         , rrd_default_history_entries
252         , memory
253         );
254 }
255
256
257
258 // ----------------------------------------------------------------------------
259
260 // RRDR dimension options
261 #define RRDR_EMPTY      0x01 // the dimension contains / the value is empty (null)
262 #define RRDR_RESET      0x02 // the dimension contains / the value is reset
263 #define RRDR_HIDDEN     0x04 // the dimension contains / the value is hidden
264 #define RRDR_NONZERO    0x08 // the dimension contains / the value is non-zero
265
266 // RRDR result options
267 #define RRDR_RESULT_OPTION_ABSOLUTE 0x00000001
268 #define RRDR_RESULT_OPTION_RELATIVE 0x00000002
269
270 typedef struct rrdresult {
271     RRDSET *st;         // the chart this result refers to
272
273     uint32_t result_options;    // RRDR_RESULT_OPTION_*
274
275     int d;                  // the number of dimensions
276     long n;                 // the number of values in the arrays
277     long rows;              // the number of rows used
278
279     uint8_t *od;            // the options for the dimensions
280
281     time_t *t;              // array of n timestamps
282     calculated_number *v;   // array n x d values
283     uint8_t *o;             // array n x d options
284
285     long c;                 // current line ( -1 ~ n ), ( -1 = none, use rrdr_rows() to get number of rows )
286
287     long group;             // how many collected values were grouped for each row
288     int update_every;       // what is the suggested update frequency in seconds
289
290     calculated_number min;
291     calculated_number max;
292
293     time_t before;
294     time_t after;
295
296     int has_st_lock;        // if st is read locked by us
297 } RRDR;
298
299 #define rrdr_rows(r) ((r)->rows)
300
301 /*
302 static void rrdr_dump(RRDR *r)
303 {
304     long c, i;
305     RRDDIM *d;
306
307     fprintf(stderr, "\nCHART %s (%s)\n", r->st->id, r->st->name);
308
309     for(c = 0, d = r->st->dimensions; d ;c++, d = d->next) {
310         fprintf(stderr, "DIMENSION %s (%s), %s%s%s%s\n"
311                 , d->id
312                 , d->name
313                 , (r->od[c] & RRDR_EMPTY)?"EMPTY ":""
314                 , (r->od[c] & RRDR_RESET)?"RESET ":""
315                 , (r->od[c] & RRDR_HIDDEN)?"HIDDEN ":""
316                 , (r->od[c] & RRDR_NONZERO)?"NONZERO ":""
317                 );
318     }
319
320     if(r->rows <= 0) {
321         fprintf(stderr, "RRDR does not have any values in it.\n");
322         return;
323     }
324
325     fprintf(stderr, "RRDR includes %d values in it:\n", r->rows);
326
327     // for each line in the array
328     for(i = 0; i < r->rows ;i++) {
329         calculated_number *cn = &r->v[ i * r->d ];
330         uint8_t *co = &r->o[ i * r->d ];
331
332         // print the id and the timestamp of the line
333         fprintf(stderr, "%ld %ld ", i + 1, r->t[i]);
334
335         // for each dimension
336         for(c = 0, d = r->st->dimensions; d ;c++, d = d->next) {
337             if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
338             if(unlikely(!(r->od[c] & RRDR_NONZERO))) continue;
339
340             if(co[c] & RRDR_EMPTY)
341                 fprintf(stderr, "null ");
342             else
343                 fprintf(stderr, CALCULATED_NUMBER_FORMAT " %s%s%s%s "
344                     , cn[c]
345                     , (co[c] & RRDR_EMPTY)?"E":" "
346                     , (co[c] & RRDR_RESET)?"R":" "
347                     , (co[c] & RRDR_HIDDEN)?"H":" "
348                     , (co[c] & RRDR_NONZERO)?"N":" "
349                     );
350         }
351
352         fprintf(stderr, "\n");
353     }
354 }
355 */
356
357 void rrdr_disable_not_selected_dimensions(RRDR *r, const char *dims)
358 {
359     char b[strlen(dims) + 1];
360     char *o = b, *tok;
361     strcpy(o, dims);
362
363     long c;
364     RRDDIM *d;
365
366     // disable all of them
367     for(c = 0, d = r->st->dimensions; d ;c++, d = d->next)
368         r->od[c] |= RRDR_HIDDEN;
369
370     while(o && *o && (tok = mystrsep(&o, ",|"))) {
371         if(!*tok) continue;
372         
373         uint32_t hash = simple_hash(tok);
374
375         // find it and enable it
376         for(c = 0, d = r->st->dimensions; d ;c++, d = d->next) {
377             if(unlikely((hash == d->hash && !strcmp(d->id, tok)) || !strcmp(d->name, tok))) {
378                 r->od[c] &= ~RRDR_HIDDEN;
379
380                 // since the user needs this dimension
381                 // make it appear as NONZERO, to return it
382                 // even if the dimension has only zeros
383                 r->od[c] |= RRDR_NONZERO;
384             }
385         }
386     }
387 }
388
389 void rrdr_buffer_print_format(BUFFER *wb, uint32_t format)
390 {
391     switch(format) {
392     case DATASOURCE_JSON:
393         buffer_strcat(wb, DATASOURCE_FORMAT_JSON);
394         break;
395
396     case DATASOURCE_DATATABLE_JSON:
397         buffer_strcat(wb, DATASOURCE_FORMAT_DATATABLE_JSON);
398         break;
399
400     case DATASOURCE_DATATABLE_JSONP:
401         buffer_strcat(wb, DATASOURCE_FORMAT_DATATABLE_JSONP);
402         break;
403
404     case DATASOURCE_JSONP:
405         buffer_strcat(wb, DATASOURCE_FORMAT_JSONP);
406         break;
407
408     case DATASOURCE_SSV:
409         buffer_strcat(wb, DATASOURCE_FORMAT_SSV);
410         break;
411
412     case DATASOURCE_CSV:
413         buffer_strcat(wb, DATASOURCE_FORMAT_CSV);
414         break;
415
416     case DATASOURCE_TSV:
417         buffer_strcat(wb, DATASOURCE_FORMAT_TSV);
418         break;
419
420     case DATASOURCE_HTML:
421         buffer_strcat(wb, DATASOURCE_FORMAT_HTML);
422         break;
423
424     case DATASOURCE_JS_ARRAY:
425         buffer_strcat(wb, DATASOURCE_FORMAT_JS_ARRAY);
426         break;
427
428     case DATASOURCE_SSV_COMMA:
429         buffer_strcat(wb, DATASOURCE_FORMAT_SSV_COMMA);
430         break;
431
432     default:
433         buffer_strcat(wb, "unknown");
434         break;
435     }
436 }
437
438 uint32_t rrdr_check_options(RRDR *r, uint32_t options, const char *dims)
439 {
440     if(options & RRDR_OPTION_NONZERO) {
441         long i;
442
443         if(dims && *dims) {
444             // the caller wants specific dimensions
445             // disable NONZERO option
446             // to make sure we don't accidentally prevent
447             // the specific dimensions from being returned
448             i = 0;
449         }
450         else {
451             // find how many dimensions are not zero
452             long c;
453             RRDDIM *rd;
454             for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ; c++, rd = rd->next) {
455                 if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
456                 if(unlikely(!(r->od[c] & RRDR_NONZERO))) continue;
457                 i++;
458             }
459         }
460
461         // if with nonzero we get i = 0 (no dimensions will be returned)
462         // disable nonzero to show all dimensions
463         if(!i) options &= ~RRDR_OPTION_NONZERO;
464     }
465
466     return options;
467 }
468
469 void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, uint32_t options, int string_value)
470 {
471     long rows = rrdr_rows(r);
472     long c, i;
473     RRDDIM *rd;
474
475     //info("JSONWRAPPER(): %s: BEGIN", r->st->id);
476     char kq[2] = "",                    // key quote
477         sq[2] = "";                     // string quote
478
479     if( options & RRDR_OPTION_GOOGLE_JSON ) {
480         kq[0] = '\0';
481         sq[0] = '\'';
482     }
483     else {
484         kq[0] = '"';
485         sq[0] = '"';
486     }
487
488     buffer_sprintf(wb, "{\n"
489             "   %sapi%s: 1,\n"
490             "   %sid%s: %s%s%s,\n"
491             "   %sname%s: %s%s%s,\n"
492             "   %sview_update_every%s: %d,\n"
493             "   %supdate_every%s: %d,\n"
494             "   %sfirst_entry%s: %u,\n"
495             "   %slast_entry%s: %u,\n"
496             "   %sbefore%s: %u,\n"
497             "   %safter%s: %u,\n"
498             "   %sdimension_names%s: ["
499             , kq, kq
500             , kq, kq, sq, r->st->id, sq
501             , kq, kq, sq, r->st->name, sq
502             , kq, kq, r->update_every
503             , kq, kq, r->st->update_every
504             , kq, kq, (uint32_t)rrdset_first_entry_t(r->st)
505             , kq, kq, (uint32_t)rrdset_last_entry_t(r->st)
506             , kq, kq, (uint32_t)r->before
507             , kq, kq, (uint32_t)r->after
508             , kq, kq);
509
510     for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
511         if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
512         if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
513
514         if(i) buffer_strcat(wb, ", ");
515         buffer_strcat(wb, sq);
516         buffer_strcat(wb, rd->name);
517         buffer_strcat(wb, sq);
518         i++;
519     }
520     if(!i) {
521 #ifdef NETDATA_INTERNAL_CHECKS
522         error("RRDR is empty for %s (RRDR has %d dimensions, options is 0x%08x)", r->st->id, r->d, options);
523 #endif
524         rows = 0;
525         buffer_strcat(wb, sq);
526         buffer_strcat(wb, "no data");
527         buffer_strcat(wb, sq);
528     }
529
530     buffer_sprintf(wb, "],\n"
531             "   %sdimension_ids%s: ["
532             , kq, kq);
533
534     for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
535         if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
536         if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
537
538         if(i) buffer_strcat(wb, ", ");
539         buffer_strcat(wb, sq);
540         buffer_strcat(wb, rd->id);
541         buffer_strcat(wb, sq);
542         i++;
543     }
544     if(!i) {
545         rows = 0;
546         buffer_strcat(wb, sq);
547         buffer_strcat(wb, "no data");
548         buffer_strcat(wb, sq);
549     }
550
551     buffer_sprintf(wb, "],\n"
552             "   %slatest_values%s: ["
553             , kq, kq);
554
555     for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
556         if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
557         if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
558
559         if(i) buffer_strcat(wb, ", ");
560         i++;
561
562         storage_number n = rd->values[rrdset_last_slot(r->st)];
563
564         if(!does_storage_number_exist(n))
565             buffer_strcat(wb, "null");
566         else
567             buffer_rrd_value(wb, unpack_storage_number(n));
568     }
569     if(!i) {
570         rows = 0;
571         buffer_strcat(wb, "null");
572     }
573
574     buffer_sprintf(wb, "],\n"
575             "   %sview_latest_values%s: ["
576             , kq, kq);
577
578     i = 0;
579     if(rows) {
580         for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
581             if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
582             if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
583
584             if(i) buffer_strcat(wb, ", ");
585             i++;
586
587             calculated_number *cn = &r->v[ (0) * r->d ];
588             uint8_t *co = &r->o[ (0) * r->d ];
589
590             if(co[c] & RRDR_EMPTY)
591                 buffer_strcat(wb, "null");
592             else
593                 buffer_rrd_value(wb, cn[c]);
594         }
595     }
596     if(!i) {
597         rows = 0;
598         buffer_strcat(wb, "null");
599     }
600
601     buffer_sprintf(wb, "],\n"
602             "   %sdimensions%s: %ld,\n"
603             "   %spoints%s: %ld,\n"
604             "   %sformat%s: %s"
605             , kq, kq, i
606             , kq, kq, rows
607             , kq, kq, sq
608             );
609
610     rrdr_buffer_print_format(wb, format);
611
612     buffer_sprintf(wb, "%s,\n"
613             "   %sresult%s: "
614             , sq
615             , kq, kq
616             );
617
618     if(string_value) buffer_strcat(wb, sq);
619     //info("JSONWRAPPER(): %s: END", r->st->id);
620 }
621
622 void rrdr_json_wrapper_end(RRDR *r, BUFFER *wb, uint32_t format, uint32_t options, int string_value)
623 {
624     (void)format;
625
626     char kq[2] = "",                    // key quote
627         sq[2] = "";                     // string quote
628
629     if( options & RRDR_OPTION_GOOGLE_JSON ) {
630         kq[0] = '\0';
631         sq[0] = '\'';
632     }
633     else {
634         kq[0] = '"';
635         sq[0] = '"';
636     }
637
638     if(string_value) buffer_strcat(wb, sq);
639
640     buffer_sprintf(wb, ",\n %smin%s: ", kq, kq);
641     buffer_rrd_value(wb, r->min);
642     buffer_sprintf(wb, ",\n %smax%s: ", kq, kq);
643     buffer_rrd_value(wb, r->max);
644     buffer_strcat(wb, "\n}\n");
645 }
646
647 #define JSON_DATES_JS 1
648 #define JSON_DATES_TIMESTAMP 2
649
650 static void rrdr2json(RRDR *r, BUFFER *wb, uint32_t options, int datatable)
651 {
652     //info("RRD2JSON(): %s: BEGIN", r->st->id);
653     int row_annotations = 0, dates, dates_with_new = 0;
654     char kq[2] = "",                    // key quote
655         sq[2] = "",                     // string quote
656         pre_label[101] = "",            // before each label
657         post_label[101] = "",           // after each label
658         pre_date[101] = "",             // the beginning of line, to the date
659         post_date[101] = "",            // closing the date
660         pre_value[101] = "",            // before each value
661         post_value[101] = "",           // after each value
662         post_line[101] = "",            // at the end of each row
663         normal_annotation[201] = "",    // default row annotation
664         overflow_annotation[201] = "",  // overflow row annotation
665         data_begin[101] = "",           // between labels and values
666         finish[101] = "";               // at the end of everything
667
668     if(datatable) {
669         dates = JSON_DATES_JS;
670         if( options & RRDR_OPTION_GOOGLE_JSON ) {
671             kq[0] = '\0';
672             sq[0] = '\'';
673         }
674         else {
675             kq[0] = '"';
676             sq[0] = '"';
677         }
678         row_annotations = 1;
679         snprintfz(pre_date,   100, "        {%sc%s:[{%sv%s:%s", kq, kq, kq, kq, sq);
680         snprintfz(post_date,  100, "%s}", sq);
681         snprintfz(pre_label,  100, ",\n     {%sid%s:%s%s,%slabel%s:%s", kq, kq, sq, sq, kq, kq, sq);
682         snprintfz(post_label, 100, "%s,%spattern%s:%s%s,%stype%s:%snumber%s}", sq, kq, kq, sq, sq, kq, kq, sq, sq);
683         snprintfz(pre_value,  100, ",{%sv%s:", kq, kq);
684         strcpy(post_value,         "}");
685         strcpy(post_line,          "]}");
686         snprintfz(data_begin, 100, "\n  ],\n    %srows%s:\n [\n", kq, kq);
687         strcpy(finish,             "\n  ]\n}");
688
689         snprintfz(overflow_annotation, 200, ",{%sv%s:%sRESET OR OVERFLOW%s},{%sv%s:%sThe counters have been wrapped.%s}", kq, kq, sq, sq, kq, kq, sq, sq);
690         snprintfz(normal_annotation,   200, ",{%sv%s:null},{%sv%s:null}", kq, kq, kq, kq);
691
692         buffer_sprintf(wb, "{\n %scols%s:\n [\n", kq, kq);
693         buffer_sprintf(wb, "        {%sid%s:%s%s,%slabel%s:%stime%s,%spattern%s:%s%s,%stype%s:%sdatetime%s},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq);
694         buffer_sprintf(wb, "        {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotation%s}},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
695         buffer_sprintf(wb, "        {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotationText%s}}", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
696
697         // remove the valueobjects flag
698         // google wants its own keys
699         if(options & RRDR_OPTION_OBJECTSROWS)
700             options &= ~RRDR_OPTION_OBJECTSROWS;
701     }
702     else {
703         kq[0] = '"';
704         sq[0] = '"';
705         if((options & RRDR_OPTION_SECONDS) || (options & RRDR_OPTION_MILLISECONDS)) {
706             dates = JSON_DATES_TIMESTAMP;
707             dates_with_new = 0;
708         }
709         else {
710             dates = JSON_DATES_JS;
711             dates_with_new = 1;
712         }
713         if( options & RRDR_OPTION_OBJECTSROWS )
714             strcpy(pre_date, "      { ");
715         else
716             strcpy(pre_date, "      [ ");
717         strcpy(pre_label,  ", \"");
718         strcpy(post_label, "\"");
719         strcpy(pre_value,  ", ");
720         if( options & RRDR_OPTION_OBJECTSROWS )
721             strcpy(post_line, "}");
722         else
723             strcpy(post_line, "]");
724         snprintfz(data_begin, 100, "],\n    %sdata%s:\n [\n", kq, kq);
725         strcpy(finish,             "\n  ]\n}");
726
727         buffer_sprintf(wb, "{\n %slabels%s: [", kq, kq);
728         buffer_sprintf(wb, "%stime%s", sq, sq);
729     }
730
731     // -------------------------------------------------------------------------
732     // print the JSON header
733
734     long c, i;
735     RRDDIM *rd;
736
737     // print the header lines
738     for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
739         if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
740         if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
741
742         buffer_strcat(wb, pre_label);
743         buffer_strcat(wb, rd->name);
744         buffer_strcat(wb, post_label);
745         i++;
746     }
747     if(!i) {
748         buffer_strcat(wb, pre_label);
749         buffer_strcat(wb, "no data");
750         buffer_strcat(wb, post_label);
751     }
752
753     // print the begin of row data
754     buffer_strcat(wb, data_begin);
755
756     // if all dimensions are hidden, print a null
757     if(!i) {
758         buffer_strcat(wb, finish);
759         return;
760     }
761
762     long start = 0, end = rrdr_rows(r), step = 1;
763     if((options & RRDR_OPTION_REVERSED)) {
764         start = rrdr_rows(r) - 1;
765         end = -1;
766         step = -1;
767     }
768
769     // for each line in the array
770     calculated_number total = 1;
771     for(i = start; i != end ;i += step) {
772         calculated_number *cn = &r->v[ i * r->d ];
773         uint8_t *co = &r->o[ i * r->d ];
774
775         time_t now = r->t[i];
776
777         if(dates == JSON_DATES_JS) {
778             // generate the local date time
779             struct tm tmbuf, *tm = localtime_r(&now, &tmbuf);
780             if(!tm) { error("localtime_r() failed."); continue; }
781
782             if(likely(i != start)) buffer_strcat(wb, ",\n");
783             buffer_strcat(wb, pre_date);
784
785             if( options & RRDR_OPTION_OBJECTSROWS )
786                 buffer_sprintf(wb, "%stime%s: ", kq, kq);
787
788             if(dates_with_new)
789                 buffer_strcat(wb, "new ");
790
791             buffer_jsdate(wb, tm->tm_year + 1900, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
792
793             buffer_strcat(wb, post_date);
794
795             if(row_annotations) {
796                 // google supports one annotation per row
797                 int annotation_found = 0;
798                 for(c = 0, rd = r->st->dimensions; rd ;c++, rd = rd->next) {
799                     if(co[c] & RRDR_RESET) {
800                         buffer_strcat(wb, overflow_annotation);
801                         annotation_found = 1;
802                         break;
803                     }
804                 }
805                 if(!annotation_found)
806                     buffer_strcat(wb, normal_annotation);
807             }
808         }
809         else {
810             // print the timestamp of the line
811             if(likely(i != start)) buffer_strcat(wb, ",\n");
812             buffer_strcat(wb, pre_date);
813
814             if( options & RRDR_OPTION_OBJECTSROWS )
815                 buffer_sprintf(wb, "%stime%s: ", kq, kq);
816
817             buffer_rrd_value(wb, (calculated_number)r->t[i]);
818             // in ms
819             if(options & RRDR_OPTION_MILLISECONDS) buffer_strcat(wb, "000");
820
821             buffer_strcat(wb, post_date);
822         }
823
824         if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
825             total = 0;
826             for(c = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
827                 calculated_number n = cn[c];
828
829                 if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
830                     n = -n;
831
832                 total += n;
833             }
834             // prevent a division by zero
835             if(total == 0) total = 1;
836         }
837
838         // for each dimension
839         for(c = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
840             if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
841             if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
842
843             calculated_number n = cn[c];
844
845             buffer_strcat(wb, pre_value);
846
847             if( options & RRDR_OPTION_OBJECTSROWS )
848                 buffer_sprintf(wb, "%s%s%s: ", kq, rd->name, kq);
849
850             if(co[c] & RRDR_EMPTY) {
851                 if(options & RRDR_OPTION_NULL2ZERO)
852                     buffer_strcat(wb, "0");
853                 else
854                     buffer_strcat(wb, "null");
855             }
856             else {
857                 if(unlikely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
858                     n = -n;
859
860                 if(unlikely(options & RRDR_OPTION_PERCENTAGE))
861                     n = n * 100 / total;
862
863                 buffer_rrd_value(wb, n);
864             }
865
866             buffer_strcat(wb, post_value);
867         }
868
869         buffer_strcat(wb, post_line);
870     }
871
872     buffer_strcat(wb, finish);
873     //info("RRD2JSON(): %s: END", r->st->id);
874 }
875
876 static void rrdr2csv(RRDR *r, BUFFER *wb, uint32_t options, const char *startline, const char *separator, const char *endline, const char *betweenlines)
877 {
878     //info("RRD2CSV(): %s: BEGIN", r->st->id);
879     long c, i;
880     RRDDIM *d;
881
882     // print the csv header
883     for(c = 0, i = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
884         if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
885         if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
886
887         if(!i) {
888             buffer_strcat(wb, startline);
889             if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
890             buffer_strcat(wb, "time");
891             if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
892         }
893         buffer_strcat(wb, separator);
894         if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
895         buffer_strcat(wb, d->name);
896         if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
897         i++;
898     }
899     buffer_strcat(wb, endline);
900
901     if(!i) {
902         // no dimensions present
903         return;
904     }
905
906     long start = 0, end = rrdr_rows(r), step = 1;
907     if((options & RRDR_OPTION_REVERSED)) {
908         start = rrdr_rows(r) - 1;
909         end = -1;
910         step = -1;
911     }
912
913     // for each line in the array
914     calculated_number total = 1;
915     for(i = start; i != end ;i += step) {
916         calculated_number *cn = &r->v[ i * r->d ];
917         uint8_t *co = &r->o[ i * r->d ];
918
919         buffer_strcat(wb, betweenlines);
920         buffer_strcat(wb, startline);
921
922         time_t now = r->t[i];
923
924         if((options & RRDR_OPTION_SECONDS) || (options & RRDR_OPTION_MILLISECONDS)) {
925             // print the timestamp of the line
926             buffer_rrd_value(wb, (calculated_number)now);
927             // in ms
928             if(options & RRDR_OPTION_MILLISECONDS) buffer_strcat(wb, "000");
929         }
930         else {
931             // generate the local date time
932             struct tm tmbuf, *tm = localtime_r(&now, &tmbuf);
933             if(!tm) { error("localtime() failed."); continue; }
934             buffer_date(wb, tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
935         }
936
937         if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
938             total = 0;
939             for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
940                 calculated_number n = cn[c];
941
942                 if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
943                     n = -n;
944
945                 total += n;
946             }
947             // prevent a division by zero
948             if(total == 0) total = 1;
949         }
950
951         // for each dimension
952         for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
953             if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
954             if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
955
956             buffer_strcat(wb, separator);
957
958             calculated_number n = cn[c];
959
960             if(co[c] & RRDR_EMPTY) {
961                 if(options & RRDR_OPTION_NULL2ZERO)
962                     buffer_strcat(wb, "0");
963                 else
964                     buffer_strcat(wb, "null");
965             }
966             else {
967                 if(unlikely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
968                     n = -n;
969
970                 if(unlikely(options & RRDR_OPTION_PERCENTAGE))
971                     n = n * 100 / total;
972
973                 buffer_rrd_value(wb, n);
974             }
975         }
976
977         buffer_strcat(wb, endline);
978     }
979     //info("RRD2CSV(): %s: END", r->st->id);
980 }
981
982 inline static calculated_number rrdr2value(RRDR *r, long i, uint32_t options, int *all_values_are_null) {
983     long c;
984     RRDDIM *d;
985
986     calculated_number *cn = &r->v[ i * r->d ];
987     uint8_t *co = &r->o[ i * r->d ];
988
989     calculated_number sum = 0, min = 0, max = 0, v;
990     int all_null = 1, init = 1;
991
992     calculated_number total = 1;
993     if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
994         total = 0;
995         for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
996             calculated_number n = cn[c];
997
998             if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
999                 n = -n;
1000
1001             total += n;
1002         }
1003         // prevent a division by zero
1004         if(total == 0) total = 1;
1005     }
1006
1007     // for each dimension
1008     for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
1009         if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
1010         if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
1011
1012         calculated_number n = cn[c];
1013
1014         if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
1015             n = -n;
1016
1017         if(unlikely(options & RRDR_OPTION_PERCENTAGE))
1018             n = n * 100 / total;
1019
1020         if(unlikely(init)) {
1021             if(n > 0) {
1022                 min = 0;
1023                 max = n;
1024             }
1025             else {
1026                 min = n;
1027                 max = 0;
1028             }
1029             init = 0;
1030         }
1031
1032         if(likely(!(co[c] & RRDR_EMPTY))) {
1033             all_null = 0;
1034             sum += n;
1035         }
1036
1037         if(n < min) min = n;
1038         if(n > max) max = n;
1039     }
1040
1041     if(unlikely(all_null)) {
1042         if(likely(*all_values_are_null))
1043             *all_values_are_null = 1;
1044         return 0;
1045     }
1046     else {
1047         if(likely(*all_values_are_null))
1048             *all_values_are_null = 0;
1049     }
1050
1051     if(options & RRDR_OPTION_MIN2MAX)
1052         v = max - min;
1053     else
1054         v = sum;
1055
1056     return v;
1057 }
1058
1059 static void rrdr2ssv(RRDR *r, BUFFER *wb, uint32_t options, const char *prefix, const char *separator, const char *suffix)
1060 {
1061     //info("RRD2SSV(): %s: BEGIN", r->st->id);
1062     long i;
1063
1064     buffer_strcat(wb, prefix);
1065     long start = 0, end = rrdr_rows(r), step = 1;
1066     if((options & RRDR_OPTION_REVERSED)) {
1067         start = rrdr_rows(r) - 1;
1068         end = -1;
1069         step = -1;
1070     }
1071
1072     // for each line in the array
1073     for(i = start; i != end ;i += step) {
1074         int all_values_are_null = 0;
1075         calculated_number v = rrdr2value(r, i, options, &all_values_are_null);
1076
1077         if(likely(i != start)) {
1078             if(r->min > v) r->min = v;
1079             if(r->max < v) r->max = v;
1080         }
1081         else {
1082             r->min = v;
1083             r->max = v;
1084         }
1085
1086         if(likely(i != start))
1087             buffer_strcat(wb, separator);
1088
1089         if(all_values_are_null) {
1090             if(options & RRDR_OPTION_NULL2ZERO)
1091                 buffer_strcat(wb, "0");
1092             else
1093                 buffer_strcat(wb, "null");
1094         }
1095         else
1096             buffer_rrd_value(wb, v);
1097     }
1098     buffer_strcat(wb, suffix);
1099     //info("RRD2SSV(): %s: END", r->st->id);
1100 }
1101
1102 inline static calculated_number *rrdr_line_values(RRDR *r)
1103 {
1104     return &r->v[ r->c * r->d ];
1105 }
1106
1107 inline static uint8_t *rrdr_line_options(RRDR *r)
1108 {
1109     return &r->o[ r->c * r->d ];
1110 }
1111
1112 inline static int rrdr_line_init(RRDR *r, time_t t)
1113 {
1114     r->c++;
1115
1116     if(unlikely(r->c >= r->n)) {
1117         error("requested to step above RRDR size for chart %s", r->st->name);
1118         r->c = r->n - 1;
1119     }
1120
1121     // save the time
1122     r->t[r->c] = t;
1123
1124     return 1;
1125 }
1126
1127 inline static void rrdr_lock_rrdset(RRDR *r) {
1128     if(unlikely(!r)) {
1129         error("NULL value given!");
1130         return;
1131     }
1132
1133     pthread_rwlock_rdlock(&r->st->rwlock);
1134     r->has_st_lock = 1;
1135 }
1136
1137 inline static void rrdr_unlock_rrdset(RRDR *r) {
1138     if(unlikely(!r)) {
1139         error("NULL value given!");
1140         return;
1141     }
1142
1143     if(likely(r->has_st_lock)) {
1144         pthread_rwlock_unlock(&r->st->rwlock);
1145         r->has_st_lock = 0;
1146     }
1147 }
1148
1149 inline static void rrdr_free(RRDR *r)
1150 {
1151     if(unlikely(!r)) {
1152         error("NULL value given!");
1153         return;
1154     }
1155
1156     rrdr_unlock_rrdset(r);
1157     freez(r->t);
1158     freez(r->v);
1159     freez(r->o);
1160     freez(r->od);
1161     freez(r);
1162 }
1163
1164 inline void rrdr_done(RRDR *r)
1165 {
1166     r->rows = r->c + 1;
1167     r->c = 0;
1168 }
1169
1170 static RRDR *rrdr_create(RRDSET *st, long n)
1171 {
1172     if(unlikely(!st)) {
1173         error("NULL value given!");
1174         return NULL;
1175     }
1176
1177     RRDR *r = callocz(1, sizeof(RRDR));
1178     r->st = st;
1179
1180     rrdr_lock_rrdset(r);
1181
1182     RRDDIM *rd;
1183     for(rd = st->dimensions ; rd ; rd = rd->next) r->d++;
1184
1185     r->n = n;
1186
1187     r->t = mallocz(n * sizeof(time_t));
1188     r->v = mallocz(n * r->d * sizeof(calculated_number));
1189     r->o = mallocz(n * r->d * sizeof(uint8_t));
1190     r->od = mallocz(r->d * sizeof(uint8_t));
1191
1192     // set the hidden flag on hidden dimensions
1193     int c;
1194     for(c = 0, rd = st->dimensions ; rd ; c++, rd = rd->next) {
1195         if(unlikely(rd->flags & RRDDIM_FLAG_HIDDEN)) r->od[c] = RRDR_HIDDEN;
1196         else r->od[c] = 0;
1197     }
1198
1199     r->c = -1;
1200     r->group = 1;
1201     r->update_every = 1;
1202
1203     return r;
1204 }
1205
1206 RRDR *rrd2rrdr(RRDSET *st, long points, long long after, long long before, int group_method, int aligned)
1207 {
1208     int debug = st->debug;
1209     int absolute_period_requested = -1;
1210
1211     time_t first_entry_t = rrdset_first_entry_t(st);
1212     time_t last_entry_t  = rrdset_last_entry_t(st);
1213
1214     if(before == 0 && after == 0) {
1215         before = last_entry_t;
1216         after = first_entry_t;
1217         absolute_period_requested = 0;
1218     }
1219
1220     // allow relative for before and after
1221     if(((before < 0)?-before:before) <= (st->update_every * st->entries)) {
1222         before = last_entry_t + before;
1223         absolute_period_requested = 0;
1224     }
1225
1226     if(((after < 0)?-after:after) <= (st->update_every * st->entries)) {
1227         if(after == 0) after = -st->update_every;
1228         after = before + after;
1229         absolute_period_requested = 0;
1230     }
1231
1232     if(absolute_period_requested == -1)
1233         absolute_period_requested = 1;
1234
1235     // make sure they are within our timeframe
1236     if(before > last_entry_t)  before = last_entry_t;
1237     if(before < first_entry_t) before = first_entry_t;
1238
1239     if(after > last_entry_t)  after = last_entry_t;
1240     if(after < first_entry_t) after = first_entry_t;
1241
1242     // check if they are upside down
1243     if(after > before) {
1244         time_t tmp = before;
1245         before = after;
1246         after = tmp;
1247     }
1248
1249     // the duration of the chart
1250     time_t duration = before - after;
1251     long available_points = duration / st->update_every;
1252
1253     if(duration <= 0 || available_points <= 0)
1254         return rrdr_create(st, 1);
1255
1256     // check the wanted points
1257     if(points < 0) points = -points;
1258     if(points > available_points) points = available_points;
1259     if(points == 0) points = available_points;
1260
1261     // calculate proper grouping of source data
1262     long group = available_points / points;
1263     if(group <= 0) group = 1;
1264
1265     // round group to the closest integer
1266     if(available_points % points > points / 2) group++;
1267
1268     time_t after_new  = (aligned) ? (after  - (after  % (group * st->update_every))) : after;
1269     time_t before_new = (aligned) ? (before - (before % (group * st->update_every))) : before;
1270     long points_new   = (before_new - after_new) / st->update_every / group;
1271
1272     // find the starting and ending slots in our round robin db
1273     long    start_at_slot = rrdset_time2slot(st, before_new),
1274             stop_at_slot  = rrdset_time2slot(st, after_new);
1275
1276 #ifdef NETDATA_INTERNAL_CHECKS
1277     if(after_new < first_entry_t) {
1278         error("after_new %u is too small, minimum %u", (uint32_t)after_new, (uint32_t)first_entry_t);
1279     }
1280     if(after_new > last_entry_t) {
1281         error("after_new %u is too big, maximum %u", (uint32_t)after_new, (uint32_t)last_entry_t);
1282     }
1283     if(before_new < first_entry_t) {
1284         error("before_new %u is too small, minimum %u", (uint32_t)before_new, (uint32_t)first_entry_t);
1285     }
1286     if(before_new > last_entry_t) {
1287         error("before_new %u is too big, maximum %u", (uint32_t)before_new, (uint32_t)last_entry_t);
1288     }
1289     if(start_at_slot < 0 || start_at_slot >= st->entries) {
1290         error("start_at_slot is invalid %ld, expected 0 to %ld", start_at_slot, st->entries - 1);
1291     }
1292     if(stop_at_slot < 0 || stop_at_slot >= st->entries) {
1293         error("stop_at_slot is invalid %ld, expected 0 to %ld", stop_at_slot, st->entries - 1);
1294     }
1295     if(points_new > (before_new - after_new) / group / st->update_every + 1) {
1296         error("points_new %ld is more than points %ld", points_new, (before_new - after_new) / group / st->update_every + 1);
1297     }
1298 #endif
1299
1300     //info("RRD2RRDR(): %s: wanted %ld points, got %ld - group=%ld, wanted duration=%u, got %u - wanted %ld - %ld, got %ld - %ld", st->id, points, points_new, group, before - after, before_new - after_new, after, before, after_new, before_new);
1301
1302     after = after_new;
1303     before = before_new;
1304     duration = before - after;
1305     points = points_new;
1306
1307     // Now we have:
1308     // before = the end time of the calculation
1309     // after = the start time of the calculation
1310     // duration = the duration of the calculation
1311     // group = the number of source points to aggregate / group together
1312     // method = the method of grouping source points
1313     // points = the number of points to generate
1314
1315
1316     // -------------------------------------------------------------------------
1317     // initialize our result set
1318
1319     RRDR *r = rrdr_create(st, points);
1320     if(!r) {
1321 #ifdef NETDATA_INTERNAL_CHECKS
1322         error("Cannot create RRDR for %s, after=%u, before=%u, duration=%u, points=%ld", st->id, (uint32_t)after, (uint32_t)before, (uint32_t)duration, points);
1323 #endif
1324         return NULL;
1325     }
1326     if(!r->d) {
1327 #ifdef NETDATA_INTERNAL_CHECKS
1328         error("Returning empty RRDR (no dimensions in RRDSET) for %s, after=%u, before=%u, duration=%u, points=%ld", st->id, (uint32_t)after, (uint32_t)before, (uint32_t)duration, points);
1329 #endif
1330         return r;
1331     }
1332
1333     if(absolute_period_requested == 1)
1334         r->result_options |= RRDR_RESULT_OPTION_ABSOLUTE;
1335     else
1336         r->result_options |= RRDR_RESULT_OPTION_RELATIVE;
1337
1338     // find how many dimensions we have
1339     long dimensions = r->d;
1340
1341
1342     // -------------------------------------------------------------------------
1343     // checks for debugging
1344
1345     if(debug) debug(D_RRD_STATS, "INFO %s first_t: %u, last_t: %u, all_duration: %u, after: %u, before: %u, duration: %u, points: %ld, group: %ld"
1346             , st->id
1347             , (uint32_t)first_entry_t
1348             , (uint32_t)last_entry_t
1349             , (uint32_t)(last_entry_t - first_entry_t)
1350             , (uint32_t)after
1351             , (uint32_t)before
1352             , (uint32_t)duration
1353             , points
1354             , group
1355             );
1356
1357
1358     // -------------------------------------------------------------------------
1359     // temp arrays for keeping values per dimension
1360
1361     calculated_number   last_values[dimensions]; // keep the last value of each dimension
1362     calculated_number   group_values[dimensions]; // keep sums when grouping
1363     long                group_counts[dimensions]; // keep the number of values added to group_values
1364     uint8_t             group_options[dimensions];
1365     uint8_t             found_non_zero[dimensions];
1366
1367
1368     // initialize them
1369     RRDDIM *rd;
1370     long c;
1371     for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
1372         last_values[c] = 0;
1373         group_values[c] = 0;
1374         group_counts[c] = 0;
1375         group_options[c] = 0;
1376         found_non_zero[c] = 0;
1377     }
1378
1379
1380     // -------------------------------------------------------------------------
1381     // the main loop
1382
1383     time_t  now = rrdset_slot2time(st, start_at_slot),
1384             dt = st->update_every,
1385             group_start_t = 0;
1386
1387     if(unlikely(debug)) debug(D_RRD_STATS, "BEGIN %s after_t: %u (stop_at_t: %ld), before_t: %u (start_at_t: %ld), start_t(now): %u, current_entry: %ld, entries: %ld"
1388             , st->id
1389             , (uint32_t)after
1390             , stop_at_slot
1391             , (uint32_t)before
1392             , start_at_slot
1393             , (uint32_t)now
1394             , st->current_entry
1395             , st->entries
1396             );
1397
1398     r->group = group;
1399     r->update_every = group * st->update_every;
1400     r->before = now;
1401     r->after = now;
1402
1403     //info("RRD2RRDR(): %s: STARTING", st->id);
1404
1405     long slot = start_at_slot, counter = 0, stop_now = 0, added = 0, group_count = 0, add_this = 0;
1406     for(; !stop_now ; now -= dt, slot--, counter++) {
1407         if(unlikely(slot < 0)) slot = st->entries - 1;
1408         if(unlikely(slot == stop_at_slot)) stop_now = counter;
1409
1410         if(unlikely(debug)) debug(D_RRD_STATS, "ROW %s slot: %ld, entries_counter: %ld, group_count: %ld, added: %ld, now: %ld, %s %s"
1411                 , st->id
1412                 , slot
1413                 , counter
1414                 , group_count + 1
1415                 , added
1416                 , now
1417                 , (group_count + 1 == group)?"PRINT":"  -  "
1418                 , (now >= after && now <= before)?"RANGE":"  -  "
1419                 );
1420
1421         // make sure we return data in the proper time range
1422         if(unlikely(now > before)) continue;
1423         if(unlikely(now < after)) break;
1424
1425         if(unlikely(group_count == 0)) {
1426             group_start_t = now;
1427         }
1428         group_count++;
1429
1430         if(unlikely(group_count == group)) {
1431             if(unlikely(added >= points)) break;
1432             add_this = 1;
1433         }
1434
1435         // do the calculations
1436         for(rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
1437             storage_number n = rd->values[slot];
1438             if(unlikely(!does_storage_number_exist(n))) continue;
1439
1440             group_counts[c]++;
1441
1442             calculated_number value = unpack_storage_number(n);
1443             if(likely(value != 0.0)) {
1444                 group_options[c] |= RRDR_NONZERO;
1445                 found_non_zero[c] = 1;
1446             }
1447
1448             if(unlikely(did_storage_number_reset(n)))
1449                 group_options[c] |= RRDR_RESET;
1450
1451             switch(group_method) {
1452                 case GROUP_MAX:
1453                     if(unlikely(fabsl(value) > fabsl(group_values[c])))
1454                         group_values[c] = value;
1455                     break;
1456
1457                 default:
1458                 case GROUP_SUM:
1459                 case GROUP_AVERAGE:
1460                     group_values[c] += value;
1461                     break;
1462
1463                 case GROUP_INCREMENTAL_SUM:
1464                     if(unlikely(slot == start_at_slot))
1465                         last_values[c] = value;
1466
1467                     group_values[c] += last_values[c] - value;
1468                     last_values[c] = value;
1469                     break;
1470             }
1471         }
1472
1473         // added it
1474         if(unlikely(add_this)) {
1475             if(unlikely(!rrdr_line_init(r, group_start_t))) break;
1476
1477             r->after = now;
1478
1479             calculated_number *cn = rrdr_line_values(r);
1480             uint8_t *co = rrdr_line_options(r);
1481
1482             for(rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
1483
1484                 // update the dimension options
1485                 if(likely(found_non_zero[c])) r->od[c] |= RRDR_NONZERO;
1486
1487                 // store the specific point options
1488                 co[c] = group_options[c];
1489
1490                 // store the value
1491                 if(unlikely(group_counts[c] == 0)) {
1492                     cn[c] = 0.0;
1493                     co[c] |= RRDR_EMPTY;
1494                 }
1495                 else if(unlikely(group_method == GROUP_AVERAGE)) {
1496                     // GROUP_AVERAGE
1497                     cn[c] = group_values[c] / group_counts[c];
1498                 }
1499                 else {
1500                     // GROUP_SUM
1501                     // GROUP_MAX
1502                     // GROUP_INCREMENTAL_SUM
1503                     cn[c] = group_values[c];
1504                 }
1505
1506                 if(cn[c] < r->min) r->min = cn[c];
1507                 if(cn[c] > r->max) r->max = cn[c];
1508
1509                 // reset them for the next loop
1510                 group_values[c] = 0;
1511                 group_counts[c] = 0;
1512                 group_options[c] = 0;
1513             }
1514
1515             added++;
1516             group_count = 0;
1517             add_this = 0;
1518         }
1519     }
1520
1521     rrdr_done(r);
1522     //info("RRD2RRDR(): %s: END %ld loops made, %ld points generated", st->id, counter, rrdr_rows(r));
1523     //error("SHIFT: %s: wanted %ld points, got %ld", st->id, points, rrdr_rows(r));
1524     return r;
1525 }
1526
1527 int rrd2value(RRDSET *st, BUFFER *wb, calculated_number *n, BUFFER *dimensions, long points, long long after, long long before, int group_method, uint32_t options, time_t *latest_timestamp, int *value_is_null)
1528 {
1529     RRDR *r = rrd2rrdr(st, points, after, before, group_method, !(options & RRDR_OPTION_NOT_ALIGNED));
1530     if(!r) {
1531         if(value_is_null) *value_is_null = 1;
1532         return 500;
1533     }
1534
1535     if(rrdr_rows(r) == 0) {
1536         rrdr_free(r);
1537         if(value_is_null) *value_is_null = 1;
1538         return 400;
1539     }
1540
1541     if(r->result_options & RRDR_RESULT_OPTION_RELATIVE)
1542         wb->options |= WB_CONTENT_NO_CACHEABLE;
1543     else if(r->result_options & RRDR_RESULT_OPTION_ABSOLUTE)
1544         wb->options |= WB_CONTENT_CACHEABLE;
1545
1546     options = rrdr_check_options(r, options, (dimensions)?buffer_tostring(dimensions):NULL);
1547
1548     if(dimensions)
1549         rrdr_disable_not_selected_dimensions(r, buffer_tostring(dimensions));
1550
1551     if(latest_timestamp)
1552         *latest_timestamp = r->before;
1553
1554     long i = (options & RRDR_OPTION_REVERSED)?rrdr_rows(r) - 1:0;
1555     *n = rrdr2value(r, i, options, value_is_null);
1556
1557     rrdr_free(r);
1558     return 200;
1559 }
1560
1561 int rrd2format(RRDSET *st, BUFFER *wb, BUFFER *dimensions, uint32_t format, long points, long long after, long long before, int group_method, uint32_t options, time_t *latest_timestamp)
1562 {
1563     RRDR *r = rrd2rrdr(st, points, after, before, group_method, !(options & RRDR_OPTION_NOT_ALIGNED));
1564     if(!r) {
1565         buffer_strcat(wb, "Cannot generate output with these parameters on this chart.");
1566         return 500;
1567     }
1568
1569     if(r->result_options & RRDR_RESULT_OPTION_RELATIVE)
1570         wb->options |= WB_CONTENT_NO_CACHEABLE;
1571     else if(r->result_options & RRDR_RESULT_OPTION_ABSOLUTE)
1572         wb->options |= WB_CONTENT_CACHEABLE;
1573
1574     options = rrdr_check_options(r, options, (dimensions)?buffer_tostring(dimensions):NULL);
1575
1576     if(dimensions)
1577         rrdr_disable_not_selected_dimensions(r, buffer_tostring(dimensions));
1578
1579     if(latest_timestamp && rrdr_rows(r) > 0)
1580         *latest_timestamp = r->before;
1581
1582     switch(format) {
1583     case DATASOURCE_SSV:
1584         if(options & RRDR_OPTION_JSON_WRAP) {
1585             wb->contenttype = CT_APPLICATION_JSON;
1586             rrdr_json_wrapper_begin(r, wb, format, options, 1);
1587             rrdr2ssv(r, wb, options, "", " ", "");
1588             rrdr_json_wrapper_end(r, wb, format, options, 1);
1589         }
1590         else {
1591             wb->contenttype = CT_TEXT_PLAIN;
1592             rrdr2ssv(r, wb, options, "", " ", "");
1593         }
1594         break;
1595
1596     case DATASOURCE_SSV_COMMA:
1597         if(options & RRDR_OPTION_JSON_WRAP) {
1598             wb->contenttype = CT_APPLICATION_JSON;
1599             rrdr_json_wrapper_begin(r, wb, format, options, 1);
1600             rrdr2ssv(r, wb, options, "", ",", "");
1601             rrdr_json_wrapper_end(r, wb, format, options, 1);
1602         }
1603         else {
1604             wb->contenttype = CT_TEXT_PLAIN;
1605             rrdr2ssv(r, wb, options, "", ",", "");
1606         }
1607         break;
1608
1609     case DATASOURCE_JS_ARRAY:
1610         if(options & RRDR_OPTION_JSON_WRAP) {
1611             wb->contenttype = CT_APPLICATION_JSON;
1612             rrdr_json_wrapper_begin(r, wb, format, options, 0);
1613             rrdr2ssv(r, wb, options, "[", ",", "]");
1614             rrdr_json_wrapper_end(r, wb, format, options, 0);
1615         }
1616         else {
1617             wb->contenttype = CT_APPLICATION_JSON;
1618             rrdr2ssv(r, wb, options, "[", ",", "]");
1619         }
1620         break;
1621
1622     case DATASOURCE_CSV:
1623         if(options & RRDR_OPTION_JSON_WRAP) {
1624             wb->contenttype = CT_APPLICATION_JSON;
1625             rrdr_json_wrapper_begin(r, wb, format, options, 1);
1626             rrdr2csv(r, wb, options, "", ",", "\\n", "");
1627             rrdr_json_wrapper_end(r, wb, format, options, 1);
1628         }
1629         else {
1630             wb->contenttype = CT_TEXT_PLAIN;
1631             rrdr2csv(r, wb, options, "", ",", "\r\n", "");
1632         }
1633         break;
1634
1635     case DATASOURCE_CSV_JSON_ARRAY:
1636         wb->contenttype = CT_APPLICATION_JSON;
1637         if(options & RRDR_OPTION_JSON_WRAP) {
1638             rrdr_json_wrapper_begin(r, wb, format, options, 0);
1639             buffer_strcat(wb, "[\n");
1640             rrdr2csv(r, wb, options + RRDR_OPTION_LABEL_QUOTES, "[", ",", "]", ",\n");
1641             buffer_strcat(wb, "\n]");
1642             rrdr_json_wrapper_end(r, wb, format, options, 0);
1643         }
1644         else {
1645             wb->contenttype = CT_TEXT_PLAIN;
1646             buffer_strcat(wb, "[\n");
1647             rrdr2csv(r, wb, options + RRDR_OPTION_LABEL_QUOTES, "[", ",", "]", ",\n");
1648             buffer_strcat(wb, "\n]");
1649         }
1650         break;
1651
1652     case DATASOURCE_TSV:
1653         if(options & RRDR_OPTION_JSON_WRAP) {
1654             wb->contenttype = CT_APPLICATION_JSON;
1655             rrdr_json_wrapper_begin(r, wb, format, options, 1);
1656             rrdr2csv(r, wb, options, "", "\t", "\\n", "");
1657             rrdr_json_wrapper_end(r, wb, format, options, 1);
1658         }
1659         else {
1660             wb->contenttype = CT_TEXT_PLAIN;
1661             rrdr2csv(r, wb, options, "", "\t", "\r\n", "");
1662         }
1663         break;
1664
1665     case DATASOURCE_HTML:
1666         if(options & RRDR_OPTION_JSON_WRAP) {
1667             wb->contenttype = CT_APPLICATION_JSON;
1668             rrdr_json_wrapper_begin(r, wb, format, options, 1);
1669             buffer_strcat(wb, "<html>\\n<center>\\n<table border=\\\"0\\\" cellpadding=\\\"5\\\" cellspacing=\\\"5\\\">\\n");
1670             rrdr2csv(r, wb, options, "<tr><td>", "</td><td>", "</td></tr>\\n", "");
1671             buffer_strcat(wb, "</table>\\n</center>\\n</html>\\n");
1672             rrdr_json_wrapper_end(r, wb, format, options, 1);
1673         }
1674         else {
1675             wb->contenttype = CT_TEXT_HTML;
1676             buffer_strcat(wb, "<html>\n<center>\n<table border=\"0\" cellpadding=\"5\" cellspacing=\"5\">\n");
1677             rrdr2csv(r, wb, options, "<tr><td>", "</td><td>", "</td></tr>\n", "");
1678             buffer_strcat(wb, "</table>\n</center>\n</html>\n");
1679         }
1680         break;
1681
1682     case DATASOURCE_DATATABLE_JSONP:
1683         wb->contenttype = CT_APPLICATION_X_JAVASCRIPT;
1684
1685         if(options & RRDR_OPTION_JSON_WRAP)
1686             rrdr_json_wrapper_begin(r, wb, format, options, 0);
1687
1688         rrdr2json(r, wb, options, 1);
1689
1690         if(options & RRDR_OPTION_JSON_WRAP)
1691             rrdr_json_wrapper_end(r, wb, format, options, 0);
1692         break;
1693
1694     case DATASOURCE_DATATABLE_JSON:
1695         wb->contenttype = CT_APPLICATION_JSON;
1696
1697         if(options & RRDR_OPTION_JSON_WRAP)
1698             rrdr_json_wrapper_begin(r, wb, format, options, 0);
1699
1700         rrdr2json(r, wb, options, 1);
1701
1702         if(options & RRDR_OPTION_JSON_WRAP)
1703             rrdr_json_wrapper_end(r, wb, format, options, 0);
1704         break;
1705
1706     case DATASOURCE_JSONP:
1707         wb->contenttype = CT_APPLICATION_X_JAVASCRIPT;
1708         if(options & RRDR_OPTION_JSON_WRAP)
1709             rrdr_json_wrapper_begin(r, wb, format, options, 0);
1710
1711         rrdr2json(r, wb, options, 0);
1712
1713         if(options & RRDR_OPTION_JSON_WRAP)
1714             rrdr_json_wrapper_end(r, wb, format, options, 0);
1715         break;
1716
1717     case DATASOURCE_JSON:
1718     default:
1719         wb->contenttype = CT_APPLICATION_JSON;
1720
1721         if(options & RRDR_OPTION_JSON_WRAP)
1722             rrdr_json_wrapper_begin(r, wb, format, options, 0);
1723
1724         rrdr2json(r, wb, options, 0);
1725
1726         if(options & RRDR_OPTION_JSON_WRAP)
1727             rrdr_json_wrapper_end(r, wb, format, options, 0);
1728         break;
1729     }
1730
1731     rrdr_free(r);
1732     return 200;
1733 }
1734
1735 time_t rrd_stats_json(int type, RRDSET *st, BUFFER *wb, long points, long group, int group_method, time_t after, time_t before, int only_non_zero)
1736 {
1737     int c;
1738     pthread_rwlock_rdlock(&st->rwlock);
1739
1740
1741     // -------------------------------------------------------------------------
1742     // switch from JSON to google JSON
1743
1744     char kq[2] = "\"";
1745     char sq[2] = "\"";
1746     switch(type) {
1747         case DATASOURCE_DATATABLE_JSON:
1748         case DATASOURCE_DATATABLE_JSONP:
1749             kq[0] = '\0';
1750             sq[0] = '\'';
1751             break;
1752
1753         case DATASOURCE_JSON:
1754         default:
1755             break;
1756     }
1757
1758
1759     // -------------------------------------------------------------------------
1760     // validate the parameters
1761
1762     if(points < 1) points = 1;
1763     if(group < 1) group = 1;
1764
1765     if(before == 0 || before > rrdset_last_entry_t(st)) before = rrdset_last_entry_t(st);
1766     if(after  == 0 || after < rrdset_first_entry_t(st)) after = rrdset_first_entry_t(st);
1767
1768     // ---
1769
1770     // our return value (the last timestamp printed)
1771     // this is required to detect re-transmit in google JSONP
1772     time_t last_timestamp = 0;
1773
1774
1775     // -------------------------------------------------------------------------
1776     // find how many dimensions we have
1777
1778     int dimensions = 0;
1779     RRDDIM *rd;
1780     for( rd = st->dimensions ; rd ; rd = rd->next) dimensions++;
1781     if(!dimensions) {
1782         pthread_rwlock_unlock(&st->rwlock);
1783         buffer_strcat(wb, "No dimensions yet.");
1784         return 0;
1785     }
1786
1787
1788     // -------------------------------------------------------------------------
1789     // prepare various strings, to speed up the loop
1790
1791     char overflow_annotation[201]; snprintfz(overflow_annotation, 200, ",{%sv%s:%sRESET OR OVERFLOW%s},{%sv%s:%sThe counters have been wrapped.%s}", kq, kq, sq, sq, kq, kq, sq, sq);
1792     char normal_annotation[201];   snprintfz(normal_annotation,   200, ",{%sv%s:null},{%sv%s:null}", kq, kq, kq, kq);
1793     char pre_date[51];             snprintfz(pre_date,             50, "        {%sc%s:[{%sv%s:%s", kq, kq, kq, kq, sq);
1794     char post_date[21];            snprintfz(post_date,            20, "%s}", sq);
1795     char pre_value[21];            snprintfz(pre_value,            20, ",{%sv%s:", kq, kq);
1796     char post_value[21];           strcpy(post_value,                  "}");
1797
1798
1799     // -------------------------------------------------------------------------
1800     // checks for debugging
1801
1802     if(st->debug) {
1803         debug(D_RRD_STATS, "%s first_entry_t = %ld, last_entry_t = %ld, duration = %ld, after = %ld, before = %ld, duration = %ld, entries_to_show = %ld, group = %ld"
1804             , st->id
1805             , rrdset_first_entry_t(st)
1806             , rrdset_last_entry_t(st)
1807             , rrdset_last_entry_t(st) - rrdset_first_entry_t(st)
1808             , after
1809             , before
1810             , before - after
1811             , points
1812             , group
1813             );
1814
1815         if(before < after)
1816             debug(D_RRD_STATS, "WARNING: %s The newest value in the database (%ld) is earlier than the oldest (%ld)", st->name, before, after);
1817
1818         if((before - after) > st->entries * st->update_every)
1819             debug(D_RRD_STATS, "WARNING: %s The time difference between the oldest and the newest entries (%ld) is higher than the capacity of the database (%ld)", st->name, before - after, st->entries * st->update_every);
1820     }
1821
1822
1823     // -------------------------------------------------------------------------
1824     // temp arrays for keeping values per dimension
1825
1826     calculated_number group_values[dimensions]; // keep sums when grouping
1827     int               print_hidden[dimensions]; // keep hidden flags
1828     int               found_non_zero[dimensions];
1829     int               found_non_existing[dimensions];
1830
1831     // initialize them
1832     for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
1833         group_values[c] = 0;
1834         print_hidden[c] = (rd->flags & RRDDIM_FLAG_HIDDEN)?1:0;
1835         found_non_zero[c] = 0;
1836         found_non_existing[c] = 0;
1837     }
1838
1839
1840     // error("OLD: points=%d after=%d before=%d group=%d, duration=%d", entries_to_show, before - (st->update_every * group * entries_to_show), before, group, before - after + 1);
1841     // rrd2array(st, entries_to_show, before - (st->update_every * group * entries_to_show), before, group_method, only_non_zero);
1842     // rrd2rrdr(st, entries_to_show, before - (st->update_every * group * entries_to_show), before, group_method);
1843
1844     // -------------------------------------------------------------------------
1845     // remove dimensions that contain only zeros
1846
1847     int max_loop = 1;
1848     if(only_non_zero) max_loop = 2;
1849
1850     for(; max_loop ; max_loop--) {
1851
1852         // -------------------------------------------------------------------------
1853         // print the JSON header
1854
1855         buffer_sprintf(wb, "{\n %scols%s:\n [\n", kq, kq);
1856         buffer_sprintf(wb, "        {%sid%s:%s%s,%slabel%s:%stime%s,%spattern%s:%s%s,%stype%s:%sdatetime%s},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq);
1857         buffer_sprintf(wb, "        {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotation%s}},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
1858         buffer_sprintf(wb, "        {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotationText%s}}", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
1859
1860         // print the header for each dimension
1861         // and update the print_hidden array for the dimensions that should be hidden
1862         int pc = 0;
1863         for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
1864             if(!print_hidden[c]) {
1865                 pc++;
1866                 buffer_sprintf(wb, ",\n     {%sid%s:%s%s,%slabel%s:%s%s%s,%spattern%s:%s%s,%stype%s:%snumber%s}", kq, kq, sq, sq, kq, kq, sq, rd->name, sq, kq, kq, sq, sq, kq, kq, sq, sq);
1867             }
1868         }
1869         if(!pc) {
1870             buffer_sprintf(wb, ",\n     {%sid%s:%s%s,%slabel%s:%s%s%s,%spattern%s:%s%s,%stype%s:%snumber%s}", kq, kq, sq, sq, kq, kq, sq, "no data", sq, kq, kq, sq, sq, kq, kq, sq, sq);
1871         }
1872
1873         // print the begin of row data
1874         buffer_sprintf(wb, "\n  ],\n    %srows%s:\n [\n", kq, kq);
1875
1876
1877         // -------------------------------------------------------------------------
1878         // the main loop
1879
1880         int annotate_reset = 0;
1881         int annotation_count = 0;
1882
1883         long    t = rrdset_time2slot(st, before),
1884                 stop_at_t = rrdset_time2slot(st, after),
1885                 stop_now = 0;
1886
1887         t -= t % group;
1888
1889         time_t  now = rrdset_slot2time(st, t),
1890                 dt = st->update_every;
1891
1892         long count = 0, printed = 0, group_count = 0;
1893         last_timestamp = 0;
1894
1895         if(st->debug) debug(D_RRD_STATS, "%s: REQUEST after:%u before:%u, points:%ld, group:%ld, CHART cur:%ld first: %u last:%u, CALC start_t:%ld, stop_t:%ld"
1896                     , st->id
1897                     , (uint32_t)after
1898                     , (uint32_t)before
1899                     , points
1900                     , group
1901                     , st->current_entry
1902                     , (uint32_t)rrdset_first_entry_t(st)
1903                     , (uint32_t)rrdset_last_entry_t(st)
1904                     , t
1905                     , stop_at_t
1906                     );
1907
1908         long counter = 0;
1909         for(; !stop_now ; now -= dt, t--, counter++) {
1910             if(t < 0) t = st->entries - 1;
1911             if(t == stop_at_t) stop_now = counter;
1912
1913             int print_this = 0;
1914
1915             if(st->debug) debug(D_RRD_STATS, "%s t = %ld, count = %ld, group_count = %ld, printed = %ld, now = %ld, %s %s"
1916                     , st->id
1917                     , t
1918                     , count + 1
1919                     , group_count + 1
1920                     , printed
1921                     , now
1922                     , (group_count + 1 == group)?"PRINT":"  -  "
1923                     , (now >= after && now <= before)?"RANGE":"  -  "
1924                     );
1925
1926
1927             // make sure we return data in the proper time range
1928             if(now > before) continue;
1929             if(now < after) break;
1930
1931             //if(rrdset_slot2time(st, t) != now)
1932             //  error("%s: slot=%ld, now=%ld, slot2time=%ld, diff=%ld, last_entry_t=%ld, rrdset_last_slot=%ld", st->id, t, now, rrdset_slot2time(st,t), now - rrdset_slot2time(st,t), rrdset_last_entry_t(st), rrdset_last_slot(st));
1933
1934             count++;
1935             group_count++;
1936
1937             // check if we have to print this now
1938             if(group_count == group) {
1939                 if(printed >= points) {
1940                     // debug(D_RRD_STATS, "Already printed all rows. Stopping.");
1941                     break;
1942                 }
1943
1944                 // generate the local date time
1945                 struct tm tmbuf, *tm = localtime_r(&now, &tmbuf);
1946                 if(!tm) { error("localtime() failed."); continue; }
1947                 if(now > last_timestamp) last_timestamp = now;
1948
1949                 if(printed) buffer_strcat(wb, "]},\n");
1950                 buffer_strcat(wb, pre_date);
1951                 buffer_jsdate(wb, tm->tm_year + 1900, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
1952                 buffer_strcat(wb, post_date);
1953
1954                 print_this = 1;
1955             }
1956
1957             // do the calculations
1958             for(rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
1959                 storage_number n = rd->values[t];
1960                 calculated_number value = unpack_storage_number(n);
1961
1962                 if(!does_storage_number_exist(n)) {
1963                     value = 0.0;
1964                     found_non_existing[c]++;
1965                 }
1966                 if(did_storage_number_reset(n)) annotate_reset = 1;
1967
1968                 switch(group_method) {
1969                     case GROUP_MAX:
1970                         if(abs(value) > abs(group_values[c])) group_values[c] = value;
1971                         break;
1972
1973                     case GROUP_SUM:
1974                         group_values[c] += value;
1975                         break;
1976
1977                     default:
1978                     case GROUP_AVERAGE:
1979                         group_values[c] += value;
1980                         if(print_this) group_values[c] /= ( group_count - found_non_existing[c] );
1981                         break;
1982                 }
1983             }
1984
1985             if(print_this) {
1986                 if(annotate_reset) {
1987                     annotation_count++;
1988                     buffer_strcat(wb, overflow_annotation);
1989                     annotate_reset = 0;
1990                 }
1991                 else
1992                     buffer_strcat(wb, normal_annotation);
1993
1994                 pc = 0;
1995                 for(c = 0 ; c < dimensions ; c++) {
1996                     if(found_non_existing[c] == group_count) {
1997                         // all entries are non-existing
1998                         pc++;
1999                         buffer_strcat(wb, pre_value);
2000                         buffer_strcat(wb, "null");
2001                         buffer_strcat(wb, post_value);
2002                     }
2003                     else if(!print_hidden[c]) {
2004                         pc++;
2005                         buffer_strcat(wb, pre_value);
2006                         buffer_rrd_value(wb, group_values[c]);
2007                         buffer_strcat(wb, post_value);
2008
2009                         if(group_values[c]) found_non_zero[c]++;
2010                     }
2011
2012                     // reset them for the next loop
2013                     group_values[c] = 0;
2014                     found_non_existing[c] = 0;
2015                 }
2016
2017                 // if all dimensions are hidden, print a null
2018                 if(!pc) {
2019                     buffer_strcat(wb, pre_value);
2020                     buffer_strcat(wb, "null");
2021                     buffer_strcat(wb, post_value);
2022                 }
2023
2024                 printed++;
2025                 group_count = 0;
2026             }
2027         }
2028
2029         if(printed) buffer_strcat(wb, "]}");
2030         buffer_strcat(wb, "\n   ]\n}\n");
2031
2032         if(only_non_zero && max_loop > 1) {
2033             int changed = 0;
2034             for(rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
2035                 group_values[c] = 0;
2036                 found_non_existing[c] = 0;
2037
2038                 if(!print_hidden[c] && !found_non_zero[c]) {
2039                     changed = 1;
2040                     print_hidden[c] = 1;
2041                 }
2042             }
2043
2044             if(changed) buffer_flush(wb);
2045             else break;
2046         }
2047         else break;
2048
2049     } // max_loop
2050
2051     debug(D_RRD_STATS, "RRD_STATS_JSON: %s total %lu bytes", st->name, wb->len);
2052
2053     pthread_rwlock_unlock(&st->rwlock);
2054     return last_timestamp;
2055 }