1 /*****************************************************************************
2 * RRDtool 1.3.5 Copyright by Tobi Oetiker, 1997-2008
3 *****************************************************************************
4 * rrd_dump Display a RRD
5 *****************************************************************************
6 * $Id: rrd_dump.c 1710 2008-12-15 22:06:22Z oetiker $
7 * $Log$
8 * Revision 1.7 2004/05/25 20:53:21 oetiker
9 * prevent small leak when resources are exhausted -- Mike Slifcak
10 *
11 * Revision 1.6 2004/05/25 20:51:49 oetiker
12 * Update displayed copyright messages to be consistent. -- Mike Slifcak
13 *
14 * Revision 1.5 2003/02/13 07:05:27 oetiker
15 * Find attached the patch I promised to send to you. Please note that there
16 * are three new source files (src/rrd_is_thread_safe.h, src/rrd_thread_safe.c
17 * and src/rrd_not_thread_safe.c) and the introduction of librrd_th. This
18 * library is identical to librrd, but it contains support code for per-thread
19 * global variables currently used for error information only. This is similar
20 * to how errno per-thread variables are implemented. librrd_th must be linked
21 * alongside of libpthred
22 *
23 * There is also a new file "THREADS", holding some documentation.
24 *
25 * -- Peter Stamfest <peter@stamfest.at>
26 *
27 * Revision 1.4 2002/02/01 20:34:49 oetiker
28 * fixed version number and date/time
29 *
30 * Revision 1.3 2001/03/10 23:54:39 oetiker
31 * Support for COMPUTE data sources (CDEF data sources). Removes the RPN
32 * parser and calculator from rrd_graph and puts then in a new file,
33 * rrd_rpncalc.c. Changes to core files rrd_create and rrd_update. Some
34 * clean-up of aberrant behavior stuff, including a bug fix.
35 * Documentation update (rrdcreate.pod, rrdupdate.pod). Change xml format.
36 * -- Jake Brutlag <jakeb@corp.webtv.net>
37 *
38 * Revision 1.2 2001/03/04 13:01:55 oetiker
39 *
40 * Revision 1.1.1.1 2001/02/25 22:25:05 oetiker
41 * checkin
42 *
43 *****************************************************************************/
45 #ifdef WIN32
46 #include <stdlib.h>
47 #endif
49 #include "rrd_tool.h"
50 #include "rrd_rpncalc.h"
52 #if !(defined(NETWARE) || defined(WIN32))
53 extern char *tzname[2];
54 #endif
57 int rrd_dump_opt_r(
58 const char *filename,
59 char *outname,
60 int opt_noheader)
61 {
62 unsigned int i, ii, ix, iii = 0;
63 time_t now;
64 char somestring[255];
65 rrd_value_t my_cdp;
66 off_t rra_base, rra_start, rra_next;
67 rrd_file_t *rrd_file;
68 FILE *out_file;
69 rrd_t rrd;
70 rrd_value_t value;
71 struct tm tm;
73 rrd_file = rrd_open(filename, &rrd, RRD_READONLY | RRD_READAHEAD);
74 if (rrd_file == NULL) {
75 rrd_free(&rrd);
76 return (-1);
77 }
79 out_file = NULL;
80 if (outname) {
81 if (!(out_file = fopen(outname, "w"))) {
82 return (-1);
83 }
84 } else {
85 out_file = stdout;
86 }
88 if (!opt_noheader) {
89 fputs("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n", out_file);
90 fputs
91 ("<!DOCTYPE rrd SYSTEM \"http://oss.oetiker.ch/rrdtool/rrdtool.dtd\">\n",
92 out_file);
93 }
94 fputs("<!-- Round Robin Database Dump -->", out_file);
95 fputs("<rrd>", out_file);
96 if (atoi(rrd.stat_head->version) <= 3) {
97 fprintf(out_file, "\t<version> %s </version>\n", RRD_VERSION3);
98 } else {
99 fprintf(out_file, "\t<version> %s </version>\n", RRD_VERSION);
100 }
101 fprintf(out_file, "\t<step> %lu </step> <!-- Seconds -->\n",
102 rrd.stat_head->pdp_step);
103 #if HAVE_STRFTIME
104 localtime_r(&rrd.live_head->last_up, &tm);
105 strftime(somestring, 200, "%Y-%m-%d %H:%M:%S %Z", &tm);
106 #else
107 # error "Need strftime"
108 #endif
109 fprintf(out_file, "\t<lastupdate> %lu </lastupdate> <!-- %s -->\n\n",
110 (unsigned long) rrd.live_head->last_up, somestring);
111 for (i = 0; i < rrd.stat_head->ds_cnt; i++) {
112 fprintf(out_file, "\t<ds>\n");
113 fprintf(out_file, "\t\t<name> %s </name>\n", rrd.ds_def[i].ds_nam);
114 fprintf(out_file, "\t\t<type> %s </type>\n", rrd.ds_def[i].dst);
115 if (dst_conv(rrd.ds_def[i].dst) != DST_CDEF) {
116 fprintf(out_file,
117 "\t\t<minimal_heartbeat> %lu </minimal_heartbeat>\n",
118 rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt);
119 if (isnan(rrd.ds_def[i].par[DS_min_val].u_val)) {
120 fprintf(out_file, "\t\t<min> NaN </min>\n");
121 } else {
122 fprintf(out_file, "\t\t<min> %0.10e </min>\n",
123 rrd.ds_def[i].par[DS_min_val].u_val);
124 }
125 if (isnan(rrd.ds_def[i].par[DS_max_val].u_val)) {
126 fprintf(out_file, "\t\t<max> NaN </max>\n");
127 } else {
128 fprintf(out_file, "\t\t<max> %0.10e </max>\n",
129 rrd.ds_def[i].par[DS_max_val].u_val);
130 }
131 } else { /* DST_CDEF */
132 char *str = NULL;
134 rpn_compact2str((rpn_cdefds_t *) &(rrd.ds_def[i].par[DS_cdef]),
135 rrd.ds_def, &str);
136 fprintf(out_file, "\t\t<cdef> %s </cdef>\n", str);
137 free(str);
138 }
139 fprintf(out_file, "\n\t\t<!-- PDP Status -->\n");
140 fprintf(out_file, "\t\t<last_ds> %s </last_ds>\n",
141 rrd.pdp_prep[i].last_ds);
142 if (isnan(rrd.pdp_prep[i].scratch[PDP_val].u_val)) {
143 fprintf(out_file, "\t\t<value> NaN </value>\n");
144 } else {
145 fprintf(out_file, "\t\t<value> %0.10e </value>\n",
146 rrd.pdp_prep[i].scratch[PDP_val].u_val);
147 }
148 fprintf(out_file, "\t\t<unknown_sec> %lu </unknown_sec>\n",
149 rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt);
151 fprintf(out_file, "\t</ds>\n\n");
152 }
154 fputs("<!-- Round Robin Archives -->", out_file);
156 rra_base = rrd_file->header_len;
157 rra_next = rra_base;
159 for (i = 0; i < rrd.stat_head->rra_cnt; i++) {
161 long timer = 0;
163 rra_start = rra_next;
164 rra_next += (rrd.stat_head->ds_cnt
165 * rrd.rra_def[i].row_cnt * sizeof(rrd_value_t));
166 fprintf(out_file, "\t<rra>\n");
167 fprintf(out_file, "\t\t<cf> %s </cf>\n", rrd.rra_def[i].cf_nam);
168 fprintf(out_file,
169 "\t\t<pdp_per_row> %lu </pdp_per_row> <!-- %lu seconds -->\n\n",
170 rrd.rra_def[i].pdp_cnt,
171 rrd.rra_def[i].pdp_cnt * rrd.stat_head->pdp_step);
172 /* support for RRA parameters */
173 fprintf(out_file, "\t\t<params>\n");
174 switch (cf_conv(rrd.rra_def[i].cf_nam)) {
175 case CF_HWPREDICT:
176 case CF_MHWPREDICT:
177 fprintf(out_file, "\t\t<hw_alpha> %0.10e </hw_alpha>\n",
178 rrd.rra_def[i].par[RRA_hw_alpha].u_val);
179 fprintf(out_file, "\t\t<hw_beta> %0.10e </hw_beta>\n",
180 rrd.rra_def[i].par[RRA_hw_beta].u_val);
181 fprintf(out_file,
182 "\t\t<dependent_rra_idx> %lu </dependent_rra_idx>\n",
183 rrd.rra_def[i].par[RRA_dependent_rra_idx].u_cnt);
184 break;
185 case CF_SEASONAL:
186 case CF_DEVSEASONAL:
187 fprintf(out_file,
188 "\t\t<seasonal_gamma> %0.10e </seasonal_gamma>\n",
189 rrd.rra_def[i].par[RRA_seasonal_gamma].u_val);
190 fprintf(out_file,
191 "\t\t<seasonal_smooth_idx> %lu </seasonal_smooth_idx>\n",
192 rrd.rra_def[i].par[RRA_seasonal_smooth_idx].u_cnt);
193 if (atoi(rrd.stat_head->version) >= 4) {
194 fprintf(out_file,
195 "\t\t<smoothing_window> %0.10e </smoothing_window>\n",
196 rrd.rra_def[i].par[RRA_seasonal_smoothing_window].
197 u_val);
198 }
199 fprintf(out_file,
200 "\t\t<dependent_rra_idx> %lu </dependent_rra_idx>\n",
201 rrd.rra_def[i].par[RRA_dependent_rra_idx].u_cnt);
202 break;
203 case CF_FAILURES:
204 fprintf(out_file, "\t\t<delta_pos> %0.10e </delta_pos>\n",
205 rrd.rra_def[i].par[RRA_delta_pos].u_val);
206 fprintf(out_file, "\t\t<delta_neg> %0.10e </delta_neg>\n",
207 rrd.rra_def[i].par[RRA_delta_neg].u_val);
208 fprintf(out_file, "\t\t<window_len> %lu </window_len>\n",
209 rrd.rra_def[i].par[RRA_window_len].u_cnt);
210 fprintf(out_file,
211 "\t\t<failure_threshold> %lu </failure_threshold>\n",
212 rrd.rra_def[i].par[RRA_failure_threshold].u_cnt);
213 /* fall thru */
214 case CF_DEVPREDICT:
215 fprintf(out_file,
216 "\t\t<dependent_rra_idx> %lu </dependent_rra_idx>\n",
217 rrd.rra_def[i].par[RRA_dependent_rra_idx].u_cnt);
218 break;
219 case CF_AVERAGE:
220 case CF_MAXIMUM:
221 case CF_MINIMUM:
222 case CF_LAST:
223 default:
224 fprintf(out_file, "\t\t<xff> %0.10e </xff>\n",
225 rrd.rra_def[i].par[RRA_cdp_xff_val].u_val);
226 break;
227 }
228 fprintf(out_file, "\t\t</params>\n");
229 fprintf(out_file, "\t\t<cdp_prep>\n");
230 for (ii = 0; ii < rrd.stat_head->ds_cnt; ii++) {
231 unsigned long ivalue;
233 fprintf(out_file, "\t\t\t<ds>\n");
234 /* support for exporting all CDP parameters */
235 /* parameters common to all CFs */
236 /* primary_val and secondary_val do not need to be saved between updates
237 * so strictly speaking they could be omitted.
238 * However, they can be useful for diagnostic purposes, so are included here. */
239 value = rrd.cdp_prep[i * rrd.stat_head->ds_cnt
240 + ii].scratch[CDP_primary_val].u_val;
241 if (isnan(value)) {
242 fprintf(out_file,
243 "\t\t\t<primary_value> NaN </primary_value>\n");
244 } else {
245 fprintf(out_file,
246 "\t\t\t<primary_value> %0.10e </primary_value>\n",
247 value);
248 }
249 value =
250 rrd.cdp_prep[i * rrd.stat_head->ds_cnt +
251 ii].scratch[CDP_secondary_val].u_val;
252 if (isnan(value)) {
253 fprintf(out_file,
254 "\t\t\t<secondary_value> NaN </secondary_value>\n");
255 } else {
256 fprintf(out_file,
257 "\t\t\t<secondary_value> %0.10e </secondary_value>\n",
258 value);
259 }
260 switch (cf_conv(rrd.rra_def[i].cf_nam)) {
261 case CF_HWPREDICT:
262 case CF_MHWPREDICT:
263 value =
264 rrd.cdp_prep[i * rrd.stat_head->ds_cnt +
265 ii].scratch[CDP_hw_intercept].u_val;
266 if (isnan(value)) {
267 fprintf(out_file, "\t\t\t<intercept> NaN </intercept>\n");
268 } else {
269 fprintf(out_file,
270 "\t\t\t<intercept> %0.10e </intercept>\n", value);
271 }
272 value =
273 rrd.cdp_prep[i * rrd.stat_head->ds_cnt +
274 ii].scratch[CDP_hw_last_intercept].u_val;
275 if (isnan(value)) {
276 fprintf(out_file,
277 "\t\t\t<last_intercept> NaN </last_intercept>\n");
278 } else {
279 fprintf(out_file,
280 "\t\t\t<last_intercept> %0.10e </last_intercept>\n",
281 value);
282 }
283 value =
284 rrd.cdp_prep[i * rrd.stat_head->ds_cnt +
285 ii].scratch[CDP_hw_slope].u_val;
286 if (isnan(value)) {
287 fprintf(out_file, "\t\t\t<slope> NaN </slope>\n");
288 } else {
289 fprintf(out_file, "\t\t\t<slope> %0.10e </slope>\n",
290 value);
291 }
292 value =
293 rrd.cdp_prep[i * rrd.stat_head->ds_cnt +
294 ii].scratch[CDP_hw_last_slope].u_val;
295 if (isnan(value)) {
296 fprintf(out_file,
297 "\t\t\t<last_slope> NaN </last_slope>\n");
298 } else {
299 fprintf(out_file,
300 "\t\t\t<last_slope> %0.10e </last_slope>\n",
301 value);
302 }
303 ivalue =
304 rrd.cdp_prep[i * rrd.stat_head->ds_cnt +
305 ii].scratch[CDP_null_count].u_cnt;
306 fprintf(out_file, "\t\t\t<nan_count> %lu </nan_count>\n",
307 ivalue);
308 ivalue =
309 rrd.cdp_prep[i * rrd.stat_head->ds_cnt +
310 ii].scratch[CDP_last_null_count].u_cnt;
311 fprintf(out_file,
312 "\t\t\t<last_nan_count> %lu </last_nan_count>\n",
313 ivalue);
314 break;
315 case CF_SEASONAL:
316 case CF_DEVSEASONAL:
317 value =
318 rrd.cdp_prep[i * rrd.stat_head->ds_cnt +
319 ii].scratch[CDP_hw_seasonal].u_val;
320 if (isnan(value)) {
321 fprintf(out_file, "\t\t\t<seasonal> NaN </seasonal>\n");
322 } else {
323 fprintf(out_file, "\t\t\t<seasonal> %0.10e </seasonal>\n",
324 value);
325 }
326 value =
327 rrd.cdp_prep[i * rrd.stat_head->ds_cnt +
328 ii].scratch[CDP_hw_last_seasonal].u_val;
329 if (isnan(value)) {
330 fprintf(out_file,
331 "\t\t\t<last_seasonal> NaN </last_seasonal>\n");
332 } else {
333 fprintf(out_file,
334 "\t\t\t<last_seasonal> %0.10e </last_seasonal>\n",
335 value);
336 }
337 ivalue =
338 rrd.cdp_prep[i * rrd.stat_head->ds_cnt +
339 ii].scratch[CDP_init_seasonal].u_cnt;
340 fprintf(out_file, "\t\t\t<init_flag> %lu </init_flag>\n",
341 ivalue);
342 break;
343 case CF_DEVPREDICT:
344 break;
345 case CF_FAILURES:
346 {
347 unsigned short vidx;
348 char *violations_array = (char *) ((void *)
349 rrd.cdp_prep[i *
350 rrd.
351 stat_head->
352 ds_cnt +
353 ii].
354 scratch);
355 fprintf(out_file, "\t\t\t<history> ");
356 for (vidx = 0;
357 vidx < rrd.rra_def[i].par[RRA_window_len].u_cnt;
358 ++vidx) {
359 fprintf(out_file, "%d", violations_array[vidx]);
360 }
361 fprintf(out_file, " </history>\n");
362 }
363 break;
364 case CF_AVERAGE:
365 case CF_MAXIMUM:
366 case CF_MINIMUM:
367 case CF_LAST:
368 default:
369 value =
370 rrd.cdp_prep[i * rrd.stat_head->ds_cnt +
371 ii].scratch[CDP_val].u_val;
372 if (isnan(value)) {
373 fprintf(out_file, "\t\t\t<value> NaN </value>\n");
374 } else {
375 fprintf(out_file, "\t\t\t<value> %0.10e </value>\n",
376 value);
377 }
378 fprintf(out_file,
379 "\t\t\t<unknown_datapoints> %lu </unknown_datapoints>\n",
380 rrd.cdp_prep[i * rrd.stat_head->ds_cnt +
381 ii].scratch[CDP_unkn_pdp_cnt].u_cnt);
382 break;
383 }
384 fprintf(out_file, "\t\t\t</ds>\n");
385 }
386 fprintf(out_file, "\t\t</cdp_prep>\n");
388 fprintf(out_file, "\t\t<database>\n");
389 rrd_seek(rrd_file, (rra_start + (rrd.rra_ptr[i].cur_row + 1)
390 * rrd.stat_head->ds_cnt
391 * sizeof(rrd_value_t)), SEEK_SET);
392 timer = -(rrd.rra_def[i].row_cnt - 1);
393 ii = rrd.rra_ptr[i].cur_row;
394 for (ix = 0; ix < rrd.rra_def[i].row_cnt; ix++) {
395 ii++;
396 if (ii >= rrd.rra_def[i].row_cnt) {
397 rrd_seek(rrd_file, rra_start, SEEK_SET);
398 ii = 0; /* wrap if max row cnt is reached */
399 }
400 now = (rrd.live_head->last_up
401 - rrd.live_head->last_up
402 % (rrd.rra_def[i].pdp_cnt * rrd.stat_head->pdp_step))
403 + (timer * rrd.rra_def[i].pdp_cnt * rrd.stat_head->pdp_step);
405 timer++;
406 #if HAVE_STRFTIME
407 localtime_r(&now, &tm);
408 strftime(somestring, 200, "%Y-%m-%d %H:%M:%S %Z", &tm);
409 #else
410 # error "Need strftime"
411 #endif
412 fprintf(out_file, "\t\t\t<!-- %s / %d --> <row>", somestring,
413 (int) now);
414 for (iii = 0; iii < rrd.stat_head->ds_cnt; iii++) {
415 rrd_read(rrd_file, &my_cdp, sizeof(rrd_value_t) * 1);
416 if (isnan(my_cdp)) {
417 fprintf(out_file, "<v> NaN </v>");
418 } else {
419 fprintf(out_file, "<v> %0.10e </v>", my_cdp);
420 };
421 }
422 fprintf(out_file, "</row>\n");
423 }
424 fprintf(out_file, "\t\t</database>\n\t</rra>\n");
426 }
427 fprintf(out_file, "</rrd>\n");
428 rrd_free(&rrd);
429 if (out_file != stdout) {
430 fclose(out_file);
431 }
432 return rrd_close(rrd_file);
433 }
435 /* backward compatibility with 1.2.x */
436 int rrd_dump_r(
437 const char *filename,
438 char *outname)
439 {
440 return rrd_dump_opt_r(filename, outname, 0);
441 }
443 int rrd_dump(
444 int argc,
445 char **argv)
446 {
447 int rc;
448 int opt_noheader = 0;
450 /* init rrd clean */
452 optind = 0;
453 opterr = 0; /* initialize getopt */
455 while (42) {
456 int opt;
457 int option_index = 0;
458 static struct option long_options[] = {
459 {"no-header", no_argument, 0, 'n'},
460 {0, 0, 0, 0}
461 };
463 opt = getopt_long(argc, argv, "n", long_options, &option_index);
465 if (opt == EOF)
466 break;
468 switch (opt) {
469 case 'n':
470 opt_noheader = 1;
471 break;
473 default:
474 rrd_set_error("usage rrdtool %s [--no-header|-n] "
475 "file.rrd [file.xml]", argv[0]);
476 return (-1);
477 break;
478 }
479 } /* while (42) */
481 if ((argc - optind) < 1 || (argc - optind) > 2) {
482 rrd_set_error("usage rrdtool %s [--no-header|-n] "
483 "file.rrd [file.xml]", argv[0]);
484 return (-1);
485 }
487 if ((argc - optind) == 2) {
488 rc = rrd_dump_opt_r(argv[optind], argv[optind + 1], opt_noheader);
489 } else {
490 rc = rrd_dump_opt_r(argv[optind], NULL, opt_noheader);
491 }
493 return rc;
494 }