From: oetiker Date: Sat, 10 Mar 2001 23:54:41 +0000 (+0000) Subject: Support for COMPUTE data sources (CDEF data sources). Removes the RPN X-Git-Url: https://git.octo.it/?p=rrdtool.git;a=commitdiff_plain;h=a278779cdf2ded56b89fc404d6914bff7ad7dcbd Support for COMPUTE data sources (CDEF data sources). Removes the RPN parser and calculator from rrd_graph and puts then in a new file, rrd_rpncalc.c. Changes to core files rrd_create and rrd_update. Some clean-up of aberrant behavior stuff, including a bug fix. Documentation update (rrdcreate.pod, rrdupdate.pod). Change xml format. -- Jake Brutlag git-svn-id: svn://svn.oetiker.ch/rrdtool/trunk/program@31 a5681a0c-68f1-0310-ab6d-d61299d08faa --- diff --git a/NEWS b/NEWS index 7adfad0..febb618 100644 --- a/NEWS +++ b/NEWS @@ -1,25 +1,33 @@ RRDTOOL NEWS ============ -x In this file I am noting the Major changes to rrdtool for details check the cvs ChangeLog -2001/03/07 Tobias Oetiker - Integrated complete rewrite of rrdgraph documentation by - Alex van den Bogaerdt . THis also contains - info on his planned changes to the rrdgraph module +2001/03/10 Jake Brutlag + Support for COMPUTE data sources (CDEF data sources). Removes the RPN + parser and calculator from rrd_graph and puts then in a new file, + rrd_rpncalc.c. Changes to core files rrd_create and rrd_update. Some + clean-up of aberrant behavior stuff, including a bug fix. + Documentation update (rrdcreate.pod, rrdupdate.pod). Change xml format. + + +2001/03/07 Tobias Oetiker + Integrated complete rewrite + of rrdgraph documentation by Alex van den Bogaerdt + . This also contains info on his planned + changes to the rrdgraph module 2001/03/02 Tobias Oetiker Added Aberrant Patch from Jake Brutlag From now one, new rrd files use version tag 0002. They can - NOT be read by the old 1.0.x rrdtools - - Jack: + NOT be read by the old 1.0.x rrdtools. + + Jake: Aberrant Behavior Detection support. A brief overview added to rrdtool.pod. Major updates to rrd_update.c, rrd_create.c. Minor update to other core files. Updated documentation: rrdcreate.pod, rrdgraph.pod, - rrdtune.pod. This is backwards compatible! See - http://cricket.sourceforge.net/aberrant/rrd_hw.htm - + rrdtune.pod. This is backwards compatible (i.e. new tool can read and will + leave the binary header unchanged for old files). + See http://cricket.sourceforge.net/aberrant/rrd_hw.htm diff --git a/doc/rrdcreate.pod b/doc/rrdcreate.pod index 0d5c6ce..74cfa34 100644 --- a/doc/rrdcreate.pod +++ b/doc/rrdcreate.pod @@ -9,7 +9,8 @@ rrdtool create - Set up a new Round Robin Database B B I S<[B<--start>|B<-b> I]> S<[B<--step>|B<-s> I]> -S<[BIB<:>IB<:>IB<:>IB<:>I]> +S<[BIB<:>IB<:>I]> +IB<:>IB<:>I]> S<[BIB<:>I]> =head1 DESCRIPTION @@ -41,7 +42,7 @@ I documentation for more ways to specify time. Specifies the base interval in seconds with which data will be fed into the B. -=item BIB<:>IB<:>IB<:>IB<:>I +=item BIB<:>IB<:>I A single B can accept input from several data sources (B). (e.g. Incoming and Outgoing traffic on a specific communication @@ -52,8 +53,18 @@ I is the name you will use to reference this particular data source from an B. A I must be 1 to 19 characters long in the characters [a-zA-Z0-9_]. -I defines the Data Source Type. See the section on "How to Measure" below for further insight. -The Datasource Type must be onw of the following: +I defines the Data Source Type. The remaining arguments of a +data source entry depend upon the data source type. For GAUGE, COUNTER, +DERIVE, and ABSOLUTE the format for a data source entry is: + +BIB<:>IB<:>IB<:>IB<:>I + +For COMPUTE data sources, the format is: + +BIB<:>IB<:>I + +To decide on a data source type, review the definitions that follow. +Consult the section on "HOW TO MEASURE" for further insight. =over 4 @@ -111,6 +122,16 @@ after every read to make sure you have a maximal time available before the next overflow. Another usage is for things you count like number of messages since the last update. +=item B + +is for storing the result of a formula applied to other data sources in +the B. This data source is not supplied a value on update, but rather +its Primary Data Points (PDPs) are computed from the PDPs of the data sources +according to the rpn-expression that defines the formula. Consolidation +functions are then applied normally to the PDPs of the COMPUTE data source +(that is the rpn-expression is only applied to generate PDPs). In database +software, these are referred to as "virtual" or "computed" columns. + =back I defines the maximum number of seconds that may pass @@ -129,6 +150,16 @@ I +I defines the formula used to compute the PDPs of a COMPUTE +data source from other data sources in the same . It is similar to defining +a B argument for the graph command. Please refer to that manual page +for a list and description of RPN operations supported. For +COMPUTE data sources, the following RPN operations are not supported: PREV, +TIME, and LTIME. In addition, in defining the RPN expression, the COMPUTE +data source may only refer to the names of data source listed previously +in the create command. This is similar to the restriction that Bs must +refer only to Bs and Bs previously defined in the same graph command. + =item BIB<:>I The purpose of an B is to store data in the round robin archives @@ -141,7 +172,7 @@ the length defined with the B<-s> option becoming a I. The data is also processed with the consolidation function (I) of the archive. There are several consolidation functions that consolidate primary data points via an aggregate function: B, B, B, B. -The format of B line for these consolidation function is: +The format of B line for these consolidation functions is: BIB<:>IB<:>IB<:>I @@ -439,6 +470,29 @@ RRA:FAILURES:288:7:9:5> Of course, explicit creation need not replicate implicit create, a number of arguments could be changed. +=head1 EXAMPLE 3 + +C + +This example is monitoring the average request duration during each 300 sec +interval for requests processed by a web proxy during the interval. +In this case, the proxy exposes two counters, the number of requests +processed since boot and the total cumulative duration of all processed +requests. Clearly these counters both have some rollover point, but using the +DERIVE data source also handles the reset that occurs when the web proxy is +stopped and restarted. + +In the B, the first data source stores the requests per second rate +during the interval. The second data source stores the total duration of all +requests processed during the interval divided by 300. The COMPUTE data source +divides each PDP of the AccumDuration by the corresponding PDP of +TotalRequests and stores the average request duration. The remainder of the +RPN expression handles the divide by zero case. + =head1 AUTHOR Tobias Oetiker Eoetiker@ee.ethz.chE diff --git a/doc/rrdinfo.pod b/doc/rrdinfo.pod index ac61cc8..98d7ba9 100644 --- a/doc/rrdinfo.pod +++ b/doc/rrdinfo.pod @@ -16,6 +16,10 @@ a parsing friendly format. Check L if you are uncertain about the meaning of the individual keys. +The B function will always report the true version of the B; +unlike B which will generate a current version (0002) xml dump for +older version (0001) files (although the version of the B is unchanged). + =head1 EXAMPLE This is the output generated by running B on a simple rrd which diff --git a/doc/rrdtune.pod b/doc/rrdtune.pod index f96d8d1..18f478d 100644 --- a/doc/rrdtune.pod +++ b/doc/rrdtune.pod @@ -31,7 +31,10 @@ database as *UNKNOWN*. One application of the B function is to relax the validation rules on an B. This allows to fill a new B with data available in larger intervals than what you would normally want -to permit. +to permit. Be very careful with tune operations for COMPUTE data sources. +Setting the I, I, and I for a COMPUTE data source +without changing the data source type to a non-COMPUTE B WILL corrupt +the data source header in the B. A second application of the B function is to set or alter parameters used by the specialized function B for aberrant behavior detection. @@ -70,9 +73,9 @@ rename a data source Alter the deviation scaling factor for the upper bound of the confidence band used internally to calculate violations for the FAILURES B. The default value is 2. Note that this parameter is not related to graphing confidence -bounds, that scale factor is specified as a CDEV argument to generate a graph with -confidence bounds. It need agree with the value used internally by the FAILURES -B (although common sense dictates it should). +bounds, that scale factor must be specified as a CDEF argument to generate +a graph with confidence bounds. It need not agree with the value used internally +by the FAILURES B (although common sense dictates it should). =item S I> diff --git a/doc/rrdupdate.pod b/doc/rrdupdate.pod index f150054..1fd3210 100644 --- a/doc/rrdupdate.pod +++ b/doc/rrdupdate.pod @@ -26,15 +26,25 @@ The name of the B you want to update. =item B<--template>|B<-t> I[B<:>I]... -by default, the update function expects the data input in the order, -the data sources are defined in the RRD. This is not very error -resistant, as you might be sending the wrong data into a RRD. +by default, the B function expects the data input in the order +the data sources are defined in the RRD, excluding any COMPUTE data sources +(i.e. if the third data source B is COMPUTE, the third input value +will be mapped to the fourth data source in the B). This is not very +error resistant, as you might be sending the wrong data into a RRD. The template switch allows you to specify which data sources you are going to update and in which order. If the data sources specified in the template are not available in the rrd file, the update process will abort with an error message. +While it appears possible with the template switch to update data sources +asynchronously, B implicitly assigns non-COMPUTE data sources missing +from the template the I<*UNKNOWN*> value. + +Do not specify a value for a COMPUTE B in the B function. If +this is done accidently (and this can only be done using the template switch), +B will ignore the value specified for the COMPUTE B. + =item B|IB<:>I[B<:>I...] The data used for updating the RRD was acquired at a certain time. This diff --git a/src/Makefile.am b/src/Makefile.am index 33f6790..c7aea97 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -23,7 +23,7 @@ RRD_C_FILES = \ gifsize.c \ parsetime.c \ hash_32.c \ - rrd_hw.c \ + rrd_hw.c \ pngsize.c \ rrd_create.c \ rrd_diff.c \ @@ -37,9 +37,10 @@ RRD_C_FILES = \ rrd_open.c \ rrd_resize.c \ rrd_restore.c \ + rrd_rpncalc.c \ rrd_tune.c \ rrd_update.c \ - getopt.h ntconfig.h parsetime.h rrd_format.h rrd_tool.h rrd.h + getopt.h ntconfig.h parsetime.h rrd_format.h rrd_tool.h rrd.h rrd_hw.h rrd_rpncalc.h # Build two libraries. One is a public one that gets installed in # $prefix/lib. Libtool does not create an archive of the PIC compiled diff --git a/src/rrd_create.c b/src/rrd_create.c index d60e2e9..8e6d0c4 100644 --- a/src/rrd_create.c +++ b/src/rrd_create.c @@ -5,17 +5,20 @@ *****************************************************************************/ #include "rrd_tool.h" +#include "rrd_rpncalc.h" -/* prototype for FnvHash */ unsigned long FnvHash(char *str); +int create_hw_contingent_rras(rrd_t *rrd, unsigned short period, unsigned long hashed_name); +void parseGENERIC_DS(char *def,rrd_t *rrd, int ds_idx); /* #define DEBUG */ int rrd_create(int argc, char **argv) { - rrd_t rrd; - long i,long_tmp; - time_t last_up; + rrd_t rrd; + long i,long_tmp; + int offset; + time_t last_up; struct time_value last_up_tv; char *parsetime_error = NULL; char *token; @@ -116,7 +119,6 @@ rrd_create(int argc, char **argv) * arrays. */ hashed_name = FnvHash(argv[optind]); for(i=optind+1;ids_cnt); @@ -127,49 +129,44 @@ rrd_create(int argc, char **argv) return(-1); } memset(&rrd.ds_def[rrd.stat_head->ds_cnt], 0, sizeof(ds_def_t)); + /* extract the name and type */ if (sscanf(&argv[i][3], - DS_NAM_FMT ":" DST_FMT ":%lu:%18[^:]:%18[^:]", + DS_NAM_FMT ":" DST_FMT ":%n", rrd.ds_def[rrd.stat_head->ds_cnt].ds_nam, - rrd.ds_def[rrd.stat_head->ds_cnt].dst, - &rrd.ds_def[rrd.stat_head->ds_cnt].par[DS_mrhb_cnt].u_cnt, - minstr,maxstr) == 5){ - /* check for duplicate datasource names */ - for(ii=0;iids_cnt;ii++){ + rrd.ds_def[rrd.stat_head->ds_cnt].dst,&offset) == 2) + { + /* check for duplicate datasource names */ + for(ii=0;iids_cnt;ii++) if(strcmp(rrd.ds_def[rrd.stat_head->ds_cnt].ds_nam, rrd.ds_def[ii].ds_nam) == 0){ rrd_set_error("Duplicate DS name: %s",rrd.ds_def[ii].ds_nam); - rrd_free(&rrd); - return(-1); } + } else { + rrd_set_error("invalid DS format"); } - if(dst_conv(rrd.ds_def[rrd.stat_head->ds_cnt].dst) == -1){ - rrd_free(&rrd); - return (-1); - } - if (minstr[0] == 'U' && minstr[1] == 0) - rrd.ds_def[rrd.stat_head->ds_cnt].par[DS_min_val].u_val = DNAN; - else - rrd.ds_def[rrd.stat_head->ds_cnt].par[DS_min_val].u_val = atof(minstr); - - if (maxstr[0] == 'U' && maxstr[1] == 0) - rrd.ds_def[rrd.stat_head->ds_cnt].par[DS_max_val].u_val = DNAN; - else - rrd.ds_def[rrd.stat_head->ds_cnt].par[DS_max_val].u_val = atof(maxstr); - - if (! isnan(rrd.ds_def[rrd.stat_head->ds_cnt].par[DS_min_val].u_val) && - ! isnan(rrd.ds_def[rrd.stat_head->ds_cnt].par[DS_max_val].u_val) && - rrd.ds_def[rrd.stat_head->ds_cnt].par[DS_min_val].u_val - >= rrd.ds_def[rrd.stat_head->ds_cnt].par[DS_max_val].u_val ) { - rrd_set_error("min must be less than max in DS definition"); - rrd_free(&rrd); - return (-1); + + /* parse the remainder of the arguments */ + switch(dst_conv(rrd.ds_def[rrd.stat_head->ds_cnt].dst)) + { + case DST_COUNTER: + case DST_ABSOLUTE: + case DST_GAUGE: + case DST_DERIVE: + parseGENERIC_DS(&argv[i][offset+3],&rrd, rrd.stat_head->ds_cnt); + break; + case DST_CDEF: + parseCDEF_DS(&argv[i][offset+3],&rrd, rrd.stat_head->ds_cnt); + break; + default: + rrd_set_error("invalid DS type specified"); + break; } - rrd.stat_head->ds_cnt++; - } else { - rrd_set_error("can't parse argument '%s'",argv[i]); - rrd_free(&rrd); - return (-1); - } + + if (rrd_test_error()) { + rrd_free(&rrd); + return -1; + } + rrd.stat_head -> ds_cnt++; } else if (strncmp(argv[i],"RRA:",3)==0){ size_t old_size = sizeof(rra_def_t)*(rrd.stat_head->rra_cnt); if((rrd.rra_def = rrd_realloc(rrd.rra_def, @@ -406,6 +403,42 @@ rrd_create(int argc, char **argv) return rrd_create_fn(argv[optind],&rrd); } +void parseGENERIC_DS(char *def,rrd_t *rrd, int ds_idx) +{ + char minstr[DS_NAM_SIZE], maxstr[DS_NAM_SIZE]; + /* + int temp; + + temp = sscanf(def,"%lu:%18[^:]:%18[^:]", + &(rrd -> ds_def[ds_idx].par[DS_mrhb_cnt].u_cnt), + minstr,maxstr); + */ + if (sscanf(def,"%lu:%18[^:]:%18[^:]", + &(rrd -> ds_def[ds_idx].par[DS_mrhb_cnt].u_cnt), + minstr,maxstr) == 3) + { + if (minstr[0] == 'U' && minstr[1] == 0) + rrd -> ds_def[ds_idx].par[DS_min_val].u_val = DNAN; + else + rrd -> ds_def[ds_idx].par[DS_min_val].u_val = atof(minstr); + + if (maxstr[0] == 'U' && maxstr[1] == 0) + rrd -> ds_def[ds_idx].par[DS_max_val].u_val = DNAN; + else + rrd -> ds_def[ds_idx].par[DS_max_val].u_val = atof(maxstr); + + if (! isnan(rrd -> ds_def[ds_idx].par[DS_min_val].u_val) && + ! isnan(rrd -> ds_def[ds_idx].par[DS_max_val].u_val) && + rrd -> ds_def[ds_idx].par[DS_min_val].u_val + >= rrd -> ds_def[ds_idx].par[DS_max_val].u_val ) { + rrd_set_error("min must be less than max in DS definition"); + return; + } + } else { + rrd_set_error("failed to parse data source %s", def); + } +} + /* Create the CF_DEVPREDICT, CF_DEVSEASONAL, CF_SEASONAL, and CF_FAILURES RRAs * associated with a CF_HWPREDICT RRA. */ int diff --git a/src/rrd_dump.c b/src/rrd_dump.c index a25fbe9..32b227f 100644 --- a/src/rrd_dump.c +++ b/src/rrd_dump.c @@ -5,19 +5,23 @@ ***************************************************************************** * $Id$ * $Log$ - * Revision 1.2 2001/03/04 13:01:55 oetiker - * Aberrant Behavior Detection support. A brief overview added to rrdtool.pod. - * Major updates to rrd_update.c, rrd_create.c. Minor update to other core files. - * This is backwards compatible! But new files using the Aberrant stuff are not readable - * by old rrdtool versions. See http://cricket.sourceforge.net/aberrant/rrd_hw.htm + * Revision 1.3 2001/03/10 23:54:39 oetiker + * Support for COMPUTE data sources (CDEF data sources). Removes the RPN + * parser and calculator from rrd_graph and puts then in a new file, + * rrd_rpncalc.c. Changes to core files rrd_create and rrd_update. Some + * clean-up of aberrant behavior stuff, including a bug fix. + * Documentation update (rrdcreate.pod, rrdupdate.pod). Change xml format. * -- Jake Brutlag * + * Revision 1.2 2001/03/04 13:01:55 oetiker + * * Revision 1.1.1.1 2001/02/25 22:25:05 oetiker * checkin * *****************************************************************************/ #include "rrd_tool.h" +#include "rrd_rpncalc.h" extern char *tzname[2]; @@ -31,7 +35,7 @@ rrd_dump(int argc, char **argv) long rra_base, rra_start, rra_next; FILE *in_file; rrd_t rrd; - unival value; + rrd_value_t value; if(rrd_open(argv[1],&in_file,&rrd, RRD_READONLY)==-1){ return(-1); @@ -53,6 +57,7 @@ rrd_dump(int argc, char **argv) printf("\t\n"); printf("\t\t %s \n",rrd.ds_def[i].ds_nam); printf("\t\t %s \n",rrd.ds_def[i].dst); + if (dst_conv(rrd.ds_def[i].dst) != DST_CDEF) { printf("\t\t %lu \n",rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt); if (isnan(rrd.ds_def[i].par[DS_min_val].u_val)){ printf("\t\t NaN \n"); @@ -64,6 +69,12 @@ rrd_dump(int argc, char **argv) } else { printf("\t\t %0.10e \n",rrd.ds_def[i].par[DS_max_val].u_val); } + } else { /* DST_CDEF */ + char *str; + rpn_compact2str((rpn_cdefds_t *) &(rrd.ds_def[i].par[DS_cdef]),rrd.ds_def,&str); + printf("\t\t %s \n", str); + free(str); + } printf("\n\t\t\n"); printf("\t\t %s \n",rrd.pdp_prep[i].last_ds); if (isnan(rrd.pdp_prep[i].scratch[PDP_val].u_val)){ @@ -94,54 +105,151 @@ rrd_dump(int argc, char **argv) printf("\t\t %lu \n\n", rrd.rra_def[i].pdp_cnt, rrd.rra_def[i].pdp_cnt *rrd.stat_head->pdp_step); - /* added support for RRA parameters */ - printf("\t\t"); - for (ii = 0; ii < MAX_RRA_PAR_EN; ii++) - { - value = rrd.rra_def[i].par[ii]; - if (ii == RRA_dependent_rra_idx || - ii == RRA_seasonal_smooth_idx || - ii == RRA_failure_threshold) - printf(" %lu ", value.u_cnt); - else { - if (isnan(value.u_val)) { - printf(" NaN "); - } else { - printf(" %0.10e ", value.u_val); - } - } + /* support for RRA parameters */ + printf("\t\t\n"); + switch(cf_conv(rrd.rra_def[i].cf_nam)) { + case CF_HWPREDICT: + printf("\t\t %0.10e \n", + rrd.rra_def[i].par[RRA_hw_alpha].u_val); + printf("\t\t %0.10e \n", + rrd.rra_def[i].par[RRA_hw_beta].u_val); + printf("\t\t %lu \n", + rrd.rra_def[i].par[RRA_dependent_rra_idx].u_cnt); + break; + case CF_SEASONAL: + case CF_DEVSEASONAL: + printf("\t\t %0.10e \n", + rrd.rra_def[i].par[RRA_seasonal_gamma].u_val); + printf("\t\t %lu \n", + rrd.rra_def[i].par[RRA_seasonal_smooth_idx].u_cnt); + printf("\t\t %lu \n", + rrd.rra_def[i].par[RRA_dependent_rra_idx].u_cnt); + break; + case CF_FAILURES: + printf("\t\t %0.10e \n", + rrd.rra_def[i].par[RRA_delta_pos].u_val); + printf("\t\t %0.10e \n", + rrd.rra_def[i].par[RRA_delta_neg].u_val); + printf("\t\t %lu \n", + rrd.rra_def[i].par[RRA_window_len].u_cnt); + printf("\t\t %lu \n", + rrd.rra_def[i].par[RRA_failure_threshold].u_cnt); + /* fall thru */ + case CF_DEVPREDICT: + printf("\t\t %lu \n", + rrd.rra_def[i].par[RRA_dependent_rra_idx].u_cnt); + break; + case CF_AVERAGE: + case CF_MAXIMUM: + case CF_MINIMUM: + case CF_LAST: + default: + printf("\t\t %0.10e \n", rrd.rra_def[i].par[RRA_cdp_xff_val].u_val); + break; } printf("\t\t\n"); printf("\t\t\n"); for(ii=0;iids_cnt;ii++){ -#if 0 - double value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_val].u_val; -#endif - printf("\t\t\t"); - /* added support for exporting all CDP parameters */ - for (iii=0; iii < MAX_CDP_PAR_EN ; iii++) - { - value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt + ii].scratch[iii]; - /* handle integer values as a special case */ - if (cf_conv(rrd.rra_def[i].cf_nam) == CF_FAILURES || - iii == CDP_unkn_pdp_cnt || - iii == CDP_null_count || - iii == CDP_last_null_count) - printf(" %lu ", value.u_cnt); - else { - if (isnan(value.u_val)) { - printf(" NaN "); - } else { - printf(" %0.10e ", value.u_val); - } - } - } -#if 0 - printf(" %lu ", + unsigned long ivalue; + printf("\t\t\t\n"); + /* support for exporting all CDP parameters */ + /* parameters common to all CFs */ + /* primary_val and secondary_val do not need to be saved between updates + * so strictly speaking they could be omitted. + * However, they can be useful for diagnostic purposes, so are included here. */ + value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt + +ii].scratch[CDP_primary_val].u_val; + if (isnan(value)) { + printf("\t\t\t NaN \n"); + } else { + printf("\t\t\t %0.10e \n", value); + } + value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_secondary_val].u_val; + if (isnan(value)) { + printf("\t\t\t NaN \n"); + } else { + printf("\t\t\t %0.10e \n", value); + } + switch(cf_conv(rrd.rra_def[i].cf_nam)) { + case CF_HWPREDICT: + value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_hw_intercept].u_val; + if (isnan(value)) { + printf("\t\t\t NaN \n"); + } else { + printf("\t\t\t %0.10e \n", value); + } + value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_hw_last_intercept].u_val; + if (isnan(value)) { + printf("\t\t\t NaN \n"); + } else { + printf("\t\t\t %0.10e \n", value); + } + value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_hw_slope].u_val; + if (isnan(value)) { + printf("\t\t\t NaN \n"); + } else { + printf("\t\t\t %0.10e \n", value); + } + value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_hw_last_slope].u_val; + if (isnan(value)) { + printf("\t\t\t NaN \n"); + } else { + printf("\t\t\t %0.10e \n", value); + } + ivalue = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_null_count].u_cnt; + printf("\t\t\t %lu \n", ivalue); + ivalue = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_last_null_count].u_cnt; + printf("\t\t\t %lu \n", ivalue); + break; + case CF_SEASONAL: + case CF_DEVSEASONAL: + value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_hw_seasonal].u_val; + if (isnan(value)) { + printf("\t\t\t NaN \n"); + } else { + printf("\t\t\t %0.10e \n", value); + } + value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_hw_last_seasonal].u_val; + if (isnan(value)) { + printf("\t\t\t NaN \n"); + } else { + printf("\t\t\t %0.10e \n", value); + } + ivalue = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_init_seasonal].u_cnt; + printf("\t\t\t %lu \n", ivalue); + break; + case CF_DEVPREDICT: + break; + case CF_FAILURES: + { + short vidx; + char *violations_array = (char *) ((void*) + rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch); + printf("\t\t\t "); + for (vidx = 0; vidx < rrd.rra_def[i].par[RRA_window_len].u_cnt; ++vidx) + { + printf("%d",violations_array[vidx]); + } + printf(" \n"); + } + break; + case CF_AVERAGE: + case CF_MAXIMUM: + case CF_MINIMUM: + case CF_LAST: + default: + value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_val].u_val; + if (isnan(value)) { + printf("\t\t\t NaN \n"); + } else { + printf("\t\t\t %0.10e \n", value); + } + printf("\t\t\t %lu \n", rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_unkn_pdp_cnt].u_cnt); -#endif - printf("\n"); - } + break; + } + printf("\t\t\t\n"); + } printf("\t\t\n"); printf("\t\t\n"); diff --git a/src/rrd_format.c b/src/rrd_format.c index 1f4eb1e..d7a747d 100644 --- a/src/rrd_format.c +++ b/src/rrd_format.c @@ -5,8 +5,16 @@ ***************************************************************************** * $Id$ * $Log$ - * Revision 1.1 2001/02/25 22:25:05 oetiker - * Initial revision + * Revision 1.2 2001/03/10 23:54:39 oetiker + * Support for COMPUTE data sources (CDEF data sources). Removes the RPN + * parser and calculator from rrd_graph and puts then in a new file, + * rrd_rpncalc.c. Changes to core files rrd_create and rrd_update. Some + * clean-up of aberrant behavior stuff, including a bug fix. + * Documentation update (rrdcreate.pod, rrdupdate.pod). Change xml format. + * -- Jake Brutlag + * + * Revision 1.1.1.1 2001/02/25 22:25:05 oetiker + * checkin * * Revision 1.3 1998/03/08 12:35:11 oetiker * checkpointing things because the current setup seems to work @@ -32,7 +40,8 @@ enum dst_en dst_conv(char *string) converter(ABSOLUTE,DST_ABSOLUTE) converter(GAUGE,DST_GAUGE) converter(DERIVE,DST_DERIVE) - rrd_set_error("unknown date aquisition function '%s'",string); + converter(COMPUTE,DST_CDEF) + rrd_set_error("unknown data aquisition function '%s'",string); return(-1); } @@ -44,6 +53,11 @@ enum cf_en cf_conv(char *string) converter(MIN,CF_MINIMUM) converter(MAX,CF_MAXIMUM) converter(LAST,CF_LAST) + converter(HWPREDICT,CF_HWPREDICT) + converter(DEVPREDICT,CF_DEVPREDICT) + converter(SEASONAL,CF_SEASONAL) + converter(DEVSEASONAL,CF_DEVSEASONAL) + converter(FAILURES,CF_FAILURES) rrd_set_error("unknown consolidation function '%s'",string); return(-1); } @@ -59,10 +73,3 @@ ds_match(rrd_t *rrd,char *ds_nam){ rrd_set_error("unknown data source name '%s'",ds_nam); return -1; } - - - - - - - diff --git a/src/rrd_format.h b/src/rrd_format.h index 3528362..ad08e85 100644 --- a/src/rrd_format.h +++ b/src/rrd_format.h @@ -136,7 +136,8 @@ typedef struct stat_head_t { enum dst_en { DST_COUNTER=0, /* data source types available */ DST_ABSOLUTE, DST_GAUGE, - DST_DERIVE}; + DST_DERIVE, + DST_CDEF}; enum ds_param_en { DS_mrhb_cnt=0, /* minimum required heartbeat. A * data source must provide input at @@ -144,10 +145,12 @@ enum ds_param_en { DS_mrhb_cnt=0, /* minimum required heartbeat. A * otherwise it is regarded dead and * will be set to UNKNOWN */ DS_min_val, /* the processed input of a ds must */ - DS_max_val }; /* be between max_val and min_val + DS_max_val, /* be between max_val and min_val * both can be set to UNKNOWN if you * do not care. Data outside the limits * set to UNKNOWN */ + DS_cdef = DS_mrhb_cnt}; /* pointer to encoded rpn + * expression only applies to DST_CDEF */ /* The magic number here is one less than DS_NAM_SIZE */ #define DS_NAM_FMT "%19[a-zA-Z0-9_-]" diff --git a/src/rrd_graph.c b/src/rrd_graph.c index f42acaf..019f499 100644 --- a/src/rrd_graph.c +++ b/src/rrd_graph.c @@ -13,6 +13,7 @@ #include #include #endif +#include "rrd_rpncalc.h" #define SmallFont gdLucidaNormal10 #define LargeFont gdLucidaBold12 @@ -37,26 +38,8 @@ enum grc_en {GRC_CANVAS=0,GRC_BACK,GRC_SHADEA,GRC_SHADEB, enum gf_en {GF_PRINT=0,GF_GPRINT,GF_COMMENT,GF_HRULE,GF_VRULE,GF_LINE1, GF_LINE2,GF_LINE3,GF_AREA,GF_STACK,GF_TICK,GF_DEF, GF_CDEF}; -enum op_en {OP_NUMBER=0,OP_VARIABLE,OP_INF,OP_PREV,OP_NEGINF, - OP_UNKN,OP_NOW,OP_TIME,OP_LTIME,OP_ADD,OP_MOD, - OP_SUB,OP_MUL, - OP_DIV,OP_SIN, OP_DUP, OP_EXC, OP_POP, - OP_COS,OP_LOG,OP_EXP,OP_LT,OP_LE,OP_GT,OP_GE,OP_EQ,OP_IF, - OP_MIN,OP_MAX,OP_LIMIT, OP_FLOOR, OP_CEIL, - OP_UN,OP_END}; - enum if_en {IF_GIF=0,IF_PNG=1}; -typedef struct rpnp_t { - enum op_en op; - double val; /* value for a OP_NUMBER */ - long ptr; /* pointer into the gdes array for OP_VAR */ - double *data; /* pointer to the current value from OP_VAR DAS*/ - long ds_cnt; /* data source count for data pointer */ - long step; /* time step for OP_VAR das */ -} rpnp_t; - - typedef struct col_trip_t { int red; /* red = -1 is no color */ int green; @@ -247,13 +230,13 @@ void expand_range(image_desc_t *); void reduce_data( enum cf_en, unsigned long, time_t *, time_t *, unsigned long *, unsigned long *, rrd_value_t **); int data_fetch( image_desc_t *); long find_var(image_desc_t *, char *); +long find_var_wrapper(void *arg1, char *key); long lcd(long *); int data_calc( image_desc_t *); int data_proc( image_desc_t *); time_t find_first_time( time_t, enum tmt_en, long); time_t find_next_time( time_t, enum tmt_en, long); void gator( gdImagePtr, int, int); -int tzoffset(time_t); int print_calc(image_desc_t *, char ***); int leg_place(image_desc_t *); int horizontal_grid(gdImagePtr, image_desc_t *); @@ -268,7 +251,6 @@ int gdes_alloc(image_desc_t *); int scan_for_col(char *, int, char *); int rrd_graph(int, char **, char ***, int *, int *); int bad_format(char *); -rpnp_t * str2rpn(image_desc_t *,char *); /* translate time values into x coordinates */ /*#define xtr(x) (int)((double)im->xorigin \ @@ -688,18 +670,18 @@ reduce_data( if (isnan(newval)) newval = srcptr[i*(*ds_cnt)+col]; else { switch (cf) { - case CF_HWPREDICT: - case CF_DEVSEASONAL: - case CF_DEVPREDICT: - case CF_SEASONAL: + case CF_HWPREDICT: + case CF_DEVSEASONAL: + case CF_DEVPREDICT: + case CF_SEASONAL: case CF_AVERAGE: newval += srcptr[i*(*ds_cnt)+col]; break; case CF_MINIMUM: newval = min (newval,srcptr[i*(*ds_cnt)+col]); break; - case CF_FAILURES: - /* an interval contains a failure if any subintervals contained a failure */ + case CF_FAILURES: + /* an interval contains a failure if any subintervals contained a failure */ case CF_MAXIMUM: newval = max (newval,srcptr[i*(*ds_cnt)+col]); break; @@ -711,15 +693,15 @@ reduce_data( } if (validval == 0){newval = DNAN;} else{ switch (cf) { - case CF_HWPREDICT: - case CF_DEVSEASONAL: - case CF_DEVPREDICT: - case CF_SEASONAL: - case CF_AVERAGE: - newval /= validval; + case CF_HWPREDICT: + case CF_DEVSEASONAL: + case CF_DEVPREDICT: + case CF_SEASONAL: + case CF_AVERAGE: + newval /= validval; break; case CF_MINIMUM: - case CF_FAILURES: + case CF_FAILURES: case CF_MAXIMUM: case CF_LAST: break; @@ -822,6 +804,12 @@ data_fetch( image_desc_t *im ) * CDEF stuff *************************************************************/ +long +find_var_wrapper(void *arg1, char *key) +{ + return find_var((image_desc_t *) arg1, key); +} + /* find gdes containing var*/ long find_var(image_desc_t *im, char *key){ @@ -853,144 +841,18 @@ lcd(long *num){ return num[i]; } - -/* convert string to rpnp */ -rpnp_t * -str2rpn(image_desc_t *im,char *expr){ - int pos=0; - long steps=-1; - rpnp_t *rpnp; - char vname[30]; - - rpnp=NULL; - - while(*expr){ - if ((rpnp = (rpnp_t *) rrd_realloc(rpnp, (++steps + 2)* - sizeof(rpnp_t)))==NULL){ - return NULL; - } - - else if((sscanf(expr,"%lf%n",&rpnp[steps].val,&pos) == 1) - && (expr[pos] == ',')){ - rpnp[steps].op = OP_NUMBER; - expr+=pos; - } - -#define match_op(VV,VVV) \ - else if (strncmp(expr, #VVV, strlen(#VVV))==0 && \ - (expr[strlen(#VVV)] == ',' || expr[strlen(#VVV)] == '\0') ){ \ - rpnp[steps].op = VV; \ - expr+=strlen(#VVV); \ - } - - match_op(OP_ADD,+) - match_op(OP_SUB,-) - match_op(OP_MUL,*) - match_op(OP_DIV,/) - match_op(OP_MOD,%) - match_op(OP_SIN,SIN) - match_op(OP_COS,COS) - match_op(OP_LOG,LOG) - match_op(OP_FLOOR,FLOOR) - match_op(OP_CEIL,CEIL) - match_op(OP_EXP,EXP) - match_op(OP_DUP,DUP) - match_op(OP_EXC,EXC) - match_op(OP_POP,POP) - match_op(OP_LT,LT) - match_op(OP_LE,LE) - match_op(OP_GT,GT) - match_op(OP_GE,GE) - match_op(OP_EQ,EQ) - match_op(OP_IF,IF) - match_op(OP_MIN,MIN) - match_op(OP_MAX,MAX) - match_op(OP_LIMIT,LIMIT) - /* order is important here ! .. match longest first */ - match_op(OP_UNKN,UNKN) - match_op(OP_UN,UN) - match_op(OP_NEGINF,NEGINF) - match_op(OP_PREV,PREV) - match_op(OP_INF,INF) - match_op(OP_NOW,NOW) - match_op(OP_LTIME,LTIME) - match_op(OP_TIME,TIME) - - -#undef match_op - - - else if ((sscanf(expr,DEF_NAM_FMT "%n", - vname,&pos) == 1) - && ((rpnp[steps].ptr = find_var(im,vname)) != -1)){ - rpnp[steps].op = OP_VARIABLE; - expr+=pos; - } - - else { - free(rpnp); - return NULL; - } - if (*expr == 0) - break; - if (*expr == ',') - expr++; - else { - free(rpnp); - return NULL; - } - } - rpnp[steps+1].op = OP_END; - return rpnp; -} - -/* figure out what the local timezone offset for any point in - time was. Return it in seconds */ - -int -tzoffset( time_t now ){ - int gm_sec, gm_min, gm_hour, gm_yday, gm_year, - l_sec, l_min, l_hour, l_yday, l_year; - struct tm *t; - int off; - t = gmtime(&now); - gm_sec = t->tm_sec; - gm_min = t->tm_min; - gm_hour = t->tm_hour; - gm_yday = t->tm_yday; - gm_year = t->tm_year; - t = localtime(&now); - l_sec = t->tm_sec; - l_min = t->tm_min; - l_hour = t->tm_hour; - l_yday = t->tm_yday; - l_year = t->tm_year; - off = (l_sec-gm_sec)+(l_min-gm_min)*60+(l_hour-gm_hour)*3600; - if ( l_yday > gm_yday || l_year > gm_year){ - off += 24*3600; - } else if ( l_yday < gm_yday || l_year < gm_year){ - off -= 24*3600; - } - - return off; -} - - - -#define dc_stackblock 100 - /* run the rpn calculator on all the CDEF arguments */ - int data_calc( image_desc_t *im){ - int gdi,rpi; + int gdi; int dataidx; - long *steparray; + long *steparray, rpi; int stepcnt; time_t now; - double *stack = NULL; - long dc_stacksize = 0; + rpnstack_t rpnstack; + + rpnstack_init(&rpnstack); for (gdi=0;gdigdes_c;gdi++){ /* only GF_CDEF elements are of interest */ @@ -1006,7 +868,7 @@ data_calc( image_desc_t *im){ dataidx=-1; /* find the variables in the expression. And calc the lowest - common denominator of all step sizes of the data sources involved. + common denominator of all step sizes of the data sources involved. this will be the step size for the cdef created data source*/ for(rpi=0;im->gdes[gdi].rpnp[rpi].op != OP_END;rpi++){ @@ -1014,7 +876,7 @@ data_calc( image_desc_t *im){ long ptr = im->gdes[gdi].rpnp[rpi].ptr; if ((steparray = rrd_realloc(steparray, (++stepcnt+1)*sizeof(*steparray)))==NULL){ rrd_set_error("realloc steparray"); - free(stack); + rpnstack_free(&rpnstack); return -1; }; @@ -1036,15 +898,18 @@ data_calc( image_desc_t *im){ data for variable, further save step size and data source count of this rra*/ im->gdes[gdi].rpnp[rpi].data = - im->gdes[ptr].data + im->gdes[ptr].ds; + im->gdes[ptr].data + im->gdes[ptr].ds; im->gdes[gdi].rpnp[rpi].step = im->gdes[ptr].step; im->gdes[gdi].rpnp[rpi].ds_cnt = im->gdes[ptr].ds_cnt; + /* backoff the *.data ptr; this is done so rpncalc() function + * doesn't have to treat the first case differently */ + im->gdes[gdi].rpnp[rpi].data -= im->gdes[ptr].ds_cnt; } } if(steparray == NULL){ rrd_set_error("rpn expressions without variables are not supported"); - free(stack); + rpnstack_free(&rpnstack); return -1; } steparray[stepcnt]=0; @@ -1061,7 +926,7 @@ data_calc( image_desc_t *im){ / im->gdes[gdi].step +1) * sizeof(double)))==NULL){ rrd_set_error("malloc im->gdes[gdi].data"); - free(stack); + rpnstack_free(&rpnstack); return -1; } @@ -1069,320 +934,26 @@ data_calc( image_desc_t *im){ for (now = im->gdes[gdi].start; now<=im->gdes[gdi].end; now += im->gdes[gdi].step){ - long stptr=-1; - /* process each op from the rpn in turn */ - for (rpi=0;im->gdes[gdi].rpnp[rpi].op != OP_END;rpi++){ - if (stptr +5 > dc_stacksize){ - dc_stacksize += dc_stackblock; - stack = rrd_realloc(stack,dc_stacksize*sizeof(*stack)); - if (stack==NULL){ - rrd_set_error("RPN stack overflow"); - return -1; - } - } - switch (im->gdes[gdi].rpnp[rpi].op){ - case OP_NUMBER: - stack[++stptr] = im->gdes[gdi].rpnp[rpi].val; - break; - case OP_VARIABLE: - /* make sure we pull the correct value from the *.data array */ - /* adjust the pointer into the array acordingly. */ - if(now > im->gdes[gdi].start && - now % im->gdes[gdi].rpnp[rpi].step == 0){ - im->gdes[gdi].rpnp[rpi].data += - im->gdes[gdi].rpnp[rpi].ds_cnt; - } - stack[++stptr] = *im->gdes[gdi].rpnp[rpi].data; - break; - case OP_PREV: - if (dataidx <= 0) { - stack[++stptr] = DNAN; - } else { - stack[++stptr] = im->gdes[gdi].data[dataidx]; - } - break; - case OP_UNKN: - stack[++stptr] = DNAN; - break; - case OP_INF: - stack[++stptr] = DINF; - break; - case OP_NEGINF: - stack[++stptr] = -DINF; - break; - case OP_NOW: - stack[++stptr] = (double)time(NULL); - break; - case OP_TIME: - stack[++stptr] = (double)now; - break; - case OP_LTIME: - stack[++stptr] = (double)tzoffset(now)+(double)now; - break; - case OP_ADD: - if(stptr<1){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - stack[stptr-1] = stack[stptr-1] + stack[stptr]; - stptr--; - break; - case OP_SUB: - if(stptr<1){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - stack[stptr-1] = stack[stptr-1] - stack[stptr]; - stptr--; - break; - case OP_MUL: - if(stptr<1){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - stack[stptr-1] = stack[stptr-1] * stack[stptr]; - stptr--; - break; - case OP_DIV: - if(stptr<1){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - stack[stptr-1] = stack[stptr-1] / stack[stptr]; - stptr--; - break; - case OP_MOD: - if(stptr<1){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - stack[stptr-1] = fmod(stack[stptr-1],stack[stptr]); - stptr--; - break; - case OP_SIN: - if(stptr<0){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - stack[stptr] = sin(stack[stptr]); - break; - case OP_COS: - if(stptr<0){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - stack[stptr] = cos(stack[stptr]); - break; - case OP_CEIL: - if(stptr<0){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - stack[stptr] = ceil(stack[stptr]); - break; - case OP_FLOOR: - if(stptr<0){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - stack[stptr] = floor(stack[stptr]); - break; - case OP_LOG: - if(stptr<0){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - stack[stptr] = log(stack[stptr]); - break; - case OP_DUP: - if(stptr<0){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - stack[stptr+1] = stack[stptr]; - stptr++; - break; - case OP_POP: - if(stptr<0){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - stptr--; - break; - case OP_EXC: - if(stptr<1){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } else { - double dummy; - dummy = stack[stptr] ; - stack[stptr] = stack[stptr-1]; - stack[stptr-1] = dummy; - } - break; - case OP_EXP: - if(stptr<0){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - stack[stptr] = exp(stack[stptr]); - break; - case OP_LT: - if(stptr<1){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - if (isnan(stack[stptr-1]) || isnan(stack[stptr])) - stack[stptr-1] = 0.0; - else - stack[stptr-1] = stack[stptr-1] < stack[stptr] ? 1.0 : 0.0; - stptr--; - break; - case OP_LE: - if(stptr<1){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - if (isnan(stack[stptr-1]) || isnan(stack[stptr])) - stack[stptr-1] = 0.0; - else - stack[stptr-1] = stack[stptr-1] <= stack[stptr] ? 1.0 : 0.0; - stptr--; - break; - case OP_GT: - if(stptr<1){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - if (isnan(stack[stptr-1]) || isnan(stack[stptr])) - stack[stptr-1] = 0.0; - else - stack[stptr-1] = stack[stptr-1] > stack[stptr] ? 1.0 : 0.0; - stptr--; - break; - case OP_GE: - if(stptr<1){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - if (isnan(stack[stptr-1]) || isnan(stack[stptr])) - stack[stptr-1] = 0.0; - else - stack[stptr-1] = stack[stptr-1] >= stack[stptr] ? 1.0 : 0.0; - stptr--; - break; - case OP_EQ: - if(stptr<1){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - if (isnan(stack[stptr-1]) || isnan(stack[stptr])) - stack[stptr-1] = 0.0; - else - stack[stptr-1] = stack[stptr-1] == stack[stptr] ? 1.0 : 0.0; - stptr--; - break; - case OP_IF: - if(stptr<2){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - stack[stptr-2] = stack[stptr-2] != 0.0 ? stack[stptr-1] : stack[stptr]; - stptr--; - stptr--; - break; - case OP_MIN: - if(stptr<1){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - if (isnan(stack[stptr-1])) - ; - else if (isnan(stack[stptr])) - stack[stptr-1] = stack[stptr]; - else if (stack[stptr-1] > stack[stptr]) - stack[stptr-1] = stack[stptr]; - stptr--; - break; - case OP_MAX: - if(stptr<1){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - if (isnan(stack[stptr-1])) - ; - else if (isnan(stack[stptr])) - stack[stptr-1] = stack[stptr]; - else if (stack[stptr-1] < stack[stptr]) - stack[stptr-1] = stack[stptr]; - stptr--; - break; - case OP_LIMIT: - if(stptr<2){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - if (isnan(stack[stptr-2])) - ; - else if (isnan(stack[stptr-1])) - stack[stptr-2] = stack[stptr-1]; - else if (isnan(stack[stptr])) - stack[stptr-2] = stack[stptr]; - else if (stack[stptr-2] < stack[stptr-1]) - stack[stptr-2] = DNAN; - else if (stack[stptr-2] > stack[stptr]) - stack[stptr-2] = DNAN; - stptr-=2; - break; - case OP_UN: - if(stptr<0){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - stack[stptr] = isnan(stack[stptr]) ? 1.0 : 0.0; - break; - case OP_END: - break; - } - } - if(stptr!=0){ - rrd_set_error("RPN final stack size != 1"); - free(stack); - return -1; - } - im->gdes[gdi].data[++dataidx] = stack[0]; - } - } - free(stack); + rpnp_t *rpnp = im -> gdes[gdi].rpnp; + + /* 3rd arg of rpn_calc is for OP_VARIABLE lookups; + * in this case we are advancing by timesteps; + * we use the fact that time_t is a synonym for long + */ + if (rpn_calc(rpnp,&rpnstack,(long) now, + im->gdes[gdi].data,++dataidx) == -1) + { + /* rpn_calc sets the error string */ + rpnstack_free(&rpnstack); + return -1; + } + + } /* enumerate over time steps within a CDEF */ + } /* enumerate over CDEFs */ + rpnstack_free(&rpnstack); return 0; } -#undef dc_stacksize - /* massage data so, that we get one value for each x coordinate in the graph */ int data_proc( image_desc_t *im ){ @@ -3322,7 +2893,8 @@ rrd_graph(int argc, char **argv, char ***prdata, int *xsize, int *ysize) im.gdes[im.gdes_c-1].vname); return -1; } - if((im.gdes[im.gdes_c-1].rpnp = str2rpn(&im,rpnex))== NULL){ + if((im.gdes[im.gdes_c-1].rpnp = + rpn_parse((void*)&im,rpnex,&find_var_wrapper))== NULL){ rrd_set_error("invalid rpn expression '%s'", rpnex); im_free(&im); return -1; diff --git a/src/rrd_hw.c b/src/rrd_hw.c index 85b4fd7..baedc9e 100644 --- a/src/rrd_hw.c +++ b/src/rrd_hw.c @@ -1,17 +1,18 @@ /***************************************************************************** - * RRDtool 1.0.21 Copyright Tobias Oetiker, 1997 - 2000 + * RRDtool 1.0.33 Copyright Tobias Oetiker, 1997 - 2000 ***************************************************************************** - * rrd_hw.c + * rrd_hw.c : Support for Holt-Winters Smoothing/ Aberrant Behavior Detection ***************************************************************************** * Initial version by Jake Brutlag, WebTV Networks, 5/1/00 *****************************************************************************/ #include "rrd_tool.h" +#include "rrd_hw.h" /* #define DEBUG */ +/* private functions */ unsigned long MyMod(signed long val, unsigned long mod); - int update_hwpredict(rrd_t *rrd, unsigned long cdp_idx, unsigned long rra_idx, unsigned long ds_idx, unsigned short CDP_scratch_idx); int update_seasonal(rrd_t *rrd, unsigned long cdp_idx, unsigned long rra_idx, @@ -352,12 +353,12 @@ update_devseasonal(rrd_t *rrd, unsigned long cdp_idx, unsigned long rra_idx, fprintf(stderr,"Initialization of seasonal deviation\n"); #endif rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val = - abs(prediction - rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val); + fabs(prediction - rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val); } else { /* exponential smoothing update */ rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val = (rrd -> rra_def[rra_idx].par[RRA_seasonal_gamma].u_val)* - abs(prediction - rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val) + fabs(prediction - rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val) + (1 - rrd -> rra_def[rra_idx].par[RRA_seasonal_gamma].u_val)* (rrd -> cdp_prep[cdp_idx].scratch[CDP_last_seasonal_deviation].u_val); } @@ -442,9 +443,6 @@ update_failures(rrd_t *rrd, unsigned long cdp_idx, unsigned long rra_idx, /* determine if a failure has occurred and update the failure array */ violation_cnt = violation; - /* WARNING: this cast makes XML files non-portable across platforms, - * because an array of longs on disk is treated as an array of chars - * in memory. */ violations_array = (char *) ((void *) rrd -> cdp_prep[cdp_idx].scratch); for (i = current_rra -> par[RRA_window_len].u_cnt; i > 1; i--) { diff --git a/src/rrd_hw.h b/src/rrd_hw.h new file mode 100644 index 0000000..0d943fc --- /dev/null +++ b/src/rrd_hw.h @@ -0,0 +1,30 @@ +/***************************************************************************** + * RRDtool 1.0.33 Copyright Tobias Oetiker, 1997 - 2000 + ***************************************************************************** + * rrd_hw.h : Support for Holt-Winters Smoothing/ Aberrant Behavior Detection + *****************************************************************************/ + +/* functions implemented in rrd_hw.c */ +int update_aberrant_CF(rrd_t *rrd, rrd_value_t pdp_val, enum cf_en current_cf, + unsigned long cdp_idx, unsigned long rra_idx, unsigned long ds_idx, + unsigned short CDP_scratch_idx, rrd_value_t *seasonal_coef); +int create_hw_contingent_rras(rrd_t *rrd, unsigned short period, + unsigned long hashed_name); +int lookup_seasonal(rrd_t *rrd, unsigned long rra_idx, unsigned long rra_start, + FILE *rrd_file, unsigned long offset, rrd_value_t **seasonal_coef); +void erase_violations(rrd_t *rrd, unsigned long cdp_idx, unsigned long rra_idx); +int apply_smoother(rrd_t *rrd, unsigned long rra_idx, unsigned long rra_start, + FILE *rrd_file); +#define BURNIN_CYCLES 3 + +/* a standard fixed-capacity FIFO queue implementation */ +typedef struct FIFOqueue { + rrd_value_t *queue; + int capacity, head, tail; +} FIFOqueue; + +int queue_alloc(FIFOqueue **q,int capacity); +void queue_dealloc(FIFOqueue *q); +void queue_push(FIFOqueue *q, rrd_value_t value); +int queue_isempty(FIFOqueue *q); +rrd_value_t queue_pop(FIFOqueue *q); diff --git a/src/rrd_info.c b/src/rrd_info.c index 9f030e7..b7bb8f0 100644 --- a/src/rrd_info.c +++ b/src/rrd_info.c @@ -5,6 +5,7 @@ *****************************************************************************/ #include "rrd_tool.h" +#include "rrd_rpncalc.h" #include /* proto */ @@ -68,6 +69,7 @@ rrd_info(int argc, char **argv) { info_t *data,*cd; infoval info; enum cf_en current_cf; + enum dst_en current_ds; if(rrd_open(argv[1],&in_file,&rrd, RRD_READONLY)==-1){ return(NULL); @@ -91,15 +93,30 @@ rrd_info(int argc, char **argv) { info.u_str=rrd.ds_def[i].dst; cd=push(cd,sprintf_alloc("ds[%s].type", rrd.ds_def[i].ds_nam), RD_I_STR, info); + + current_ds = dst_conv(rrd.ds_def[i].dst); + switch (current_ds) { + case DST_CDEF: + { + char *buffer; + rpn_compact2str((rpn_cdefds_t *) &(rrd.ds_def[i].par[DS_cdef]), + rrd.ds_def, &buffer); + info.u_str = buffer; + cd=push(cd,sprintf_alloc("ds[%s].cdef",rrd.ds_def[i].ds_nam),RD_I_STR,info); + free(buffer); + } + break; + default: + info.u_cnt=rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt; + cd=push(cd,sprintf_alloc("ds[%s].minimal_heartbeat",rrd.ds_def[i].ds_nam), RD_I_CNT, info); - info.u_cnt=rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt; - cd=push(cd,sprintf_alloc("ds[%s].minimal_heartbeat",rrd.ds_def[i].ds_nam), RD_I_CNT, info); - - info.u_val=rrd.ds_def[i].par[DS_min_val].u_val; - cd=push(cd,sprintf_alloc("ds[%s].min", rrd.ds_def[i].ds_nam), RD_I_VAL, info); + info.u_val=rrd.ds_def[i].par[DS_min_val].u_val; + cd=push(cd,sprintf_alloc("ds[%s].min",rrd.ds_def[i].ds_nam), RD_I_VAL, info); - info.u_val=rrd.ds_def[i].par[DS_max_val].u_val; - cd=push(cd,sprintf_alloc("ds[%s].max", rrd.ds_def[i].ds_nam), RD_I_VAL, info); + info.u_val=rrd.ds_def[i].par[DS_max_val].u_val; + cd=push(cd,sprintf_alloc("ds[%s].max",rrd.ds_def[i].ds_nam), RD_I_VAL, info); + break; + } info.u_str=rrd.pdp_prep[i].last_ds; cd=push(cd,sprintf_alloc("ds[%s].last_ds", rrd.ds_def[i].ds_nam), RD_I_STR, info); @@ -154,8 +171,6 @@ rrd_info(int argc, char **argv) { } for(ii=0;iids_cnt;ii++){ - info.u_val=rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_val].u_val; - cd=push(cd,sprintf_alloc("rra[%d].cdp_prep[%d].value",i,ii), RD_I_VAL, info); switch(current_cf) { case CF_HWPREDICT: @@ -175,9 +190,23 @@ rrd_info(int argc, char **argv) { cd=push(cd,sprintf_alloc("rra[%d].cdp_prep[%d].deviation",i,ii), RD_I_VAL, info); break; case CF_DEVPREDICT: + break; case CF_FAILURES: + { + short j; + char *violations_array; + char history[MAX_FAILURES_WINDOW_LEN+1]; + violations_array = (char*) rrd.cdp_prep[i*rrd.stat_head->ds_cnt +ii].scratch; + for (j = 0; j < rrd.rra_def[i].par[RRA_window_len].u_cnt; ++j) + history[j] = (violations_array[j] == 1) ? '1' : '0'; + history[j] = '\0'; + info.u_str = history; + cd=push(cd,sprintf_alloc("rra[%d].cdp_prep[%d].history",i,ii), RD_I_STR, info); + } break; default: + info.u_val=rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_val].u_val; + cd=push(cd,sprintf_alloc("rra[%d].cdp_prep[%d].value",i,ii), RD_I_VAL, info); info.u_cnt=rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_unkn_pdp_cnt].u_cnt; cd=push(cd,sprintf_alloc("rra[%d].cdp_prep[%d].unknown_datapoints",i,ii), RD_I_CNT, info); break; diff --git a/src/rrd_restore.c b/src/rrd_restore.c index f1d1fad..088193c 100644 --- a/src/rrd_restore.c +++ b/src/rrd_restore.c @@ -5,6 +5,7 @@ *****************************************************************************/ #include "rrd_tool.h" +#include "rrd_rpncalc.h" /* Prototypes */ @@ -14,6 +15,9 @@ int eat_tag(char **, char *); int read_tag(char **, char *, char *, void *); int xml2rrd(char*, rrd_t*, char); int rrd_write(char *, rrd_t *); +void parse_patch1028_RRA_params(char **buf, rrd_t *rrd, int rra_index); +void parse_patch1028_CDP_params(char **buf, rrd_t *rrd, int rra_index, int ds_index); +void parse_FAILURES_history(char **buf, rrd_t *rrd, int rra_index, int ds_index); /* convert all ocurances of to */ @@ -99,7 +103,8 @@ int read_tag(char **buf, char *tag, char *format, void *value){ int xml2rrd(char* buf, rrd_t* rrd, char rc){ /* pass 1 identify number of RRAs */ char *ptr,*ptr2,*ptr3; /* walks thought the buffer */ - long rows=0,mempool=0,i=0,ii; + long rows=0,mempool=0,i=0; + int rra_index; xml_lc(buf); /* lets lowercase all active parts of the xml */ ptr=buf; ptr2=buf; @@ -116,7 +121,7 @@ int xml2rrd(char* buf, rrd_t* rrd, char rc){ strcpy(rrd->stat_head->cookie,RRD_COOKIE); read_tag(&ptr,"version","%4[0-9]",rrd->stat_head->version); /* added primitive version checking */ - if (atoi(rrd -> stat_head -> version) < 2) + if (atoi(rrd -> stat_head -> version) != 2) { rrd_set_error("Incompatible file version, detected version %s, required version %s\n", rrd -> stat_head -> version, RRD_VERSION); @@ -159,14 +164,21 @@ int xml2rrd(char* buf, rrd_t* rrd, char rc){ /* test for valid type */ if(dst_conv(rrd->ds_def[rrd->stat_head->ds_cnt-1].dst) == -1) return -1; + if (dst_conv(rrd->ds_def[rrd->stat_head->ds_cnt-1].dst) != DST_CDEF) + { read_tag(&ptr2,"minimal_heartbeat","%lu", &(rrd->ds_def[rrd->stat_head->ds_cnt-1].par[DS_mrhb_cnt].u_cnt)); read_tag(&ptr2,"min","%lf",&(rrd->ds_def[rrd->stat_head->ds_cnt-1].par[DS_min_val].u_val)); read_tag(&ptr2,"max","%lf",&(rrd->ds_def[rrd->stat_head->ds_cnt-1].par[DS_max_val].u_val)); + } else { /* DST_CDEF */ + char buffer[1024]; + read_tag(&ptr2,"cdef","%s",buffer); + parseCDEF_DS(buffer,rrd,rrd -> stat_head -> ds_cnt - 1); + } read_tag(&ptr2,"last_ds","%30s",rrd->pdp_prep[rrd->stat_head->ds_cnt-1].last_ds); read_tag(&ptr2,"value","%lf",&(rrd->pdp_prep[rrd->stat_head->ds_cnt-1].scratch[PDP_val].u_val)); - read_tag(&ptr2,"unknown_sec","%lu",&(rrd->pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt)); + read_tag(&ptr2,"unknown_sec","%lu",&(rrd->pdp_prep[rrd->stat_head->ds_cnt-1].scratch[PDP_unkn_sec_cnt].u_cnt)); eat_tag(&ptr2,"/ds"); ptr=ptr2; } @@ -195,47 +207,123 @@ int xml2rrd(char* buf, rrd_t* rrd, char rc){ if(cf_conv(rrd->rra_def[rrd->stat_head->rra_cnt-1].cf_nam) == -1) return -1; read_tag(&ptr2,"pdp_per_row","%lu",&(rrd->rra_def[rrd->stat_head->rra_cnt-1].pdp_cnt)); - /* add support to read RRA parameters */ + /* support to read RRA parameters */ eat_tag(&ptr2, "params"); - for (i = 0; i < MAX_RRA_PAR_EN; i++) - { - if (i == RRA_dependent_rra_idx || - i == RRA_seasonal_smooth_idx || - i == RRA_failure_threshold) - read_tag(&ptr2, "value","%u", - &(rrd->rra_def[rrd->stat_head->rra_cnt-1].par[i].u_cnt)); - else - read_tag(&ptr2, "value","%lf", - &(rrd->rra_def[rrd->stat_head->rra_cnt-1].par[i].u_val)); + skip(&ptr2); + rra_index = rrd->stat_head->rra_cnt - 1; + /* backwards compatibility w/ old patch */ + if (strncmp(ptr2, "",7) == 0) { + parse_patch1028_RRA_params(&ptr2,rrd,rra_index); + } else { + switch(cf_conv(rrd -> rra_def[rra_index].cf_nam)) { + case CF_HWPREDICT: + read_tag(&ptr2, "hw_alpha", "%lf", + &(rrd->rra_def[rra_index].par[RRA_hw_alpha].u_val)); + read_tag(&ptr2, "hw_beta", "%lf", + &(rrd->rra_def[rra_index].par[RRA_hw_beta].u_val)); + read_tag(&ptr2, "dependent_rra_idx", "%lu", + &(rrd->rra_def[rra_index].par[RRA_dependent_rra_idx].u_cnt)); + break; + case CF_SEASONAL: + case CF_DEVSEASONAL: + read_tag(&ptr2, "seasonal_gamma", "%lf", + &(rrd->rra_def[rra_index].par[RRA_seasonal_gamma].u_val)); + read_tag(&ptr2, "seasonal_smooth_idx", "%lu", + &(rrd->rra_def[rra_index].par[RRA_seasonal_smooth_idx].u_cnt)); + read_tag(&ptr2, "dependent_rra_idx", "%lu", + &(rrd->rra_def[rra_index].par[RRA_dependent_rra_idx].u_cnt)); + break; + case CF_FAILURES: + read_tag(&ptr2, "delta_pos", "%lf", + &(rrd->rra_def[rra_index].par[RRA_delta_pos].u_val)); + read_tag(&ptr2, "delta_neg", "%lf", + &(rrd->rra_def[rra_index].par[RRA_delta_neg].u_val)); + read_tag(&ptr2, "window_len", "%lu", + &(rrd->rra_def[rra_index].par[RRA_window_len].u_cnt)); + read_tag(&ptr2, "failure_threshold", "%lu", + &(rrd->rra_def[rra_index].par[RRA_failure_threshold].u_cnt)); + /* fall thru */ + case CF_DEVPREDICT: + read_tag(&ptr2, "dependent_rra_idx", "%lu", + &(rrd->rra_def[rra_index].par[RRA_dependent_rra_idx].u_cnt)); + break; + case CF_AVERAGE: + case CF_MAXIMUM: + case CF_MINIMUM: + case CF_LAST: + default: + read_tag(&ptr2, "xff","%lf", + &(rrd->rra_def[rra_index].par[RRA_cdp_xff_val].u_val)); + } } eat_tag(&ptr2, "/params"); eat_tag(&ptr2,"cdp_prep"); for(i=0;istat_head->ds_cnt;i++) { - eat_tag(&ptr2,"ds"); - /* add suport to read CDP parameters */ - for (ii = 0; ii < MAX_CDP_PAR_EN; ii++) - { - /* handle integer values as a special case */ - if (cf_conv(rrd->rra_def[rrd->stat_head->rra_cnt-1].cf_nam) == CF_FAILURES || - ii == CDP_unkn_pdp_cnt || - ii == CDP_null_count || - ii == CDP_last_null_count) - { - read_tag(&ptr2,"value","%lu", - &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rrd->stat_head->rra_cnt-1) - +i].scratch[ii].u_cnt)); - } else { - read_tag(&ptr2,"value","%lf",&(rrd->cdp_prep[rrd->stat_head->ds_cnt* - (rrd->stat_head->rra_cnt-1) +i].scratch[ii].u_val)); - } - -#if 0 - read_tag(&ptr2,"unknown_datapoints","%lu",&(rrd->cdp_prep[rrd->stat_head->ds_cnt - *(rrd->stat_head->rra_cnt-1) +i].scratch[CDP_unkn_pdp_cnt].u_cnt)); -#endif - } /* end for */ - eat_tag(&ptr2,"/ds"); + eat_tag(&ptr2,"ds"); + /* support to read CDP parameters */ + rra_index = rrd->stat_head->rra_cnt-1; + skip(&ptr2); + if (strncmp(ptr2, "",7) == 0) { + parse_patch1028_CDP_params(&ptr2,rrd,rra_index,i); + } else { + read_tag(&ptr2, "primary_value","%lf", + &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index) + +i].scratch[CDP_primary_val].u_val)); + read_tag(&ptr2, "secondary_value","%lf", + &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index) + +i].scratch[CDP_secondary_val].u_val)); + switch(cf_conv(rrd->rra_def[rra_index].cf_nam)) { + case CF_HWPREDICT: + read_tag(&ptr2,"intercept","%lf", + &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index) + +i].scratch[CDP_hw_intercept].u_val)); + read_tag(&ptr2,"last_intercept","%lf", + &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index) + +i].scratch[CDP_hw_last_intercept].u_val)); + read_tag(&ptr2,"slope","%lf", + &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index) + +i].scratch[CDP_hw_slope].u_val)); + read_tag(&ptr2,"last_slope","%lf", + &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index) + +i].scratch[CDP_hw_last_slope].u_val)); + read_tag(&ptr2,"nan_count","%lu", + &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index) + +i].scratch[CDP_null_count].u_cnt)); + read_tag(&ptr2,"last_nan_count","%lu", + &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index) + +i].scratch[CDP_last_null_count].u_cnt)); + break; + case CF_SEASONAL: + case CF_DEVSEASONAL: + read_tag(&ptr2,"seasonal","%lf", + &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index) + +i].scratch[CDP_hw_seasonal].u_val)); + read_tag(&ptr2,"last_seasonal","%lf", + &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index) + +i].scratch[CDP_hw_last_seasonal].u_val)); + read_tag(&ptr2,"init_flag","%lu", + &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index) + +i].scratch[CDP_init_seasonal].u_cnt)); + break; + case CF_DEVPREDICT: + break; + case CF_FAILURES: + parse_FAILURES_history(&ptr2,rrd,rra_index,i); + break; + case CF_AVERAGE: + case CF_MAXIMUM: + case CF_MINIMUM: + case CF_LAST: + default: + read_tag(&ptr2,"value","%lf",&(rrd->cdp_prep[rrd->stat_head->ds_cnt + *(rra_index) +i].scratch[CDP_val].u_val)); + read_tag(&ptr2,"unknown_datapoints","%lu",&(rrd->cdp_prep[rrd->stat_head->ds_cnt + *(rra_index) +i].scratch[CDP_unkn_pdp_cnt].u_cnt)); + break; + } + } + eat_tag(&ptr2,"/ds"); } eat_tag(&ptr2,"/cdp_prep"); rrd->rra_def[rrd->stat_head->rra_cnt-1].row_cnt=0; @@ -264,6 +352,8 @@ int xml2rrd(char* buf, rrd_t* rrd, char rc){ && (!isnan(*value)) /* not a NAN value */ && + (dst_conv(rrd->ds_def[i].dst) != DST_CDEF) + && ( /* min defined and in the range ? */ (!isnan(rrd->ds_def[i].par[DS_min_val].u_val) && (*value < rrd->ds_def[i].par[DS_min_val].u_val)) @@ -417,19 +507,60 @@ rrd_restore(int argc, char **argv) return 0; } +/* a backwards compatibility routine that will parse the RRA params section + * generated by the aberrant patch to 1.0.28. */ +void +parse_patch1028_RRA_params(char **buf, rrd_t *rrd, int rra_index) +{ + int i; + for (i = 0; i < MAX_RRA_PAR_EN; i++) + { + if (i == RRA_dependent_rra_idx || + i == RRA_seasonal_smooth_idx || + i == RRA_failure_threshold) + read_tag(buf, "value","%lu", + &(rrd->rra_def[rra_index].par[i].u_cnt)); + else + read_tag(buf, "value","%lf", + &(rrd->rra_def[rra_index].par[i].u_val)); + } +} +/* a backwards compatibility routine that will parse the CDP params section + * generated by the aberrant patch to 1.0.28. */ +void +parse_patch1028_CDP_params(char **buf, rrd_t *rrd, int rra_index, int ds_index) +{ + int ii; + for (ii = 0; ii < MAX_CDP_PAR_EN; ii++) + { + if (cf_conv(rrd->rra_def[rra_index].cf_nam) == CF_FAILURES || + ii == CDP_unkn_pdp_cnt || + ii == CDP_null_count || + ii == CDP_last_null_count) + { + read_tag(buf,"value","%lu", + &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index) + ds_index].scratch[ii].u_cnt)); + } else { + read_tag(buf,"value","%lf",&(rrd->cdp_prep[rrd->stat_head->ds_cnt* + (rra_index) + ds_index].scratch[ii].u_val)); + } + } +} +void +parse_FAILURES_history(char **buf, rrd_t *rrd, int rra_index, int ds_index) +{ + char history[MAX_FAILURES_WINDOW_LEN + 1]; + char *violations_array; + short i; + + /* 28 = MAX_FAILURES_WINDOW_LEN */ + read_tag(buf, "history", "%28[0-1]", history); + violations_array = (char*) rrd -> cdp_prep[rrd->stat_head->ds_cnt*(rra_index) + + ds_index].scratch; + + for (i = 0; i < rrd -> rra_def[rra_index].par[RRA_window_len].u_cnt; ++i) + violations_array[i] = (history[i] == '1') ? 1 : 0; - - - - - - - - - - - - - +} diff --git a/src/rrd_rpncalc.c b/src/rrd_rpncalc.c new file mode 100644 index 0000000..42b9c10 --- /dev/null +++ b/src/rrd_rpncalc.c @@ -0,0 +1,689 @@ +/**************************************************************************** + * RRDtool 1.0.28 Copyright Tobias Oetiker, 1997 - 2000 + **************************************************************************** + * rrd_rpncalc.c RPN calculator functions + ****************************************************************************/ + +#include "rrd_tool.h" +#include "rrd_rpncalc.h" +#include + +short addop2str(enum op_en op, enum op_en op_type, char *op_str, + char **result_str, unsigned short *offset); +int tzoffset(time_t); /* used to implement LTIME */ + +short rpn_compact(rpnp_t *rpnp, rpn_cdefds_t **rpnc, short *count) +{ + short i; + *count = 0; + /* count the number of rpn nodes */ + while(rpnp[*count].op != OP_END) (*count)++; + if (++(*count) > DS_CDEF_MAX_RPN_NODES) { + rrd_set_error("Maximum %d RPN nodes permitted", + DS_CDEF_MAX_RPN_NODES); + return -1; + } + + /* allocate memory */ + *rpnc = (rpn_cdefds_t *) calloc(*count,sizeof(rpn_cdefds_t)); + for (i = 0; rpnp[i].op != OP_END; i++) + { + (*rpnc)[i].op = (char) rpnp[i].op; + if (rpnp[i].op == OP_NUMBER) { + /* rpnp.val is a double, rpnc.val is a short */ + double temp = floor(rpnp[i].val); + if (temp < SHRT_MIN || temp > SHRT_MAX) { + rrd_set_error( + "constants must be integers in the interval (%d, %d)", + SHRT_MIN, SHRT_MAX); + free(*rpnc); + return -1; + } + (*rpnc)[i].val = (short) temp; + } else if (rpnp[i].op == OP_VARIABLE) { + (*rpnc)[i].val = (short) rpnp[i].ptr; + } + } + /* terminate the sequence */ + (*rpnc)[(*count) - 1].op = OP_END; + return 0; +} + +rpnp_t * rpn_expand(rpn_cdefds_t *rpnc) +{ + short i; + rpnp_t *rpnp; + + /* DS_CDEF_MAX_RPN_NODES is small, so at the expense of some wasted + * memory we avoid any reallocs */ + rpnp = (rpnp_t *) calloc(DS_CDEF_MAX_RPN_NODES,sizeof(rpnp_t)); + if (rpnp == NULL) return NULL; + for (i = 0; rpnc[i].op != OP_END; ++i) + { + rpnp[i].op = (long) rpnc[i].op; + if (rpnp[i].op == OP_NUMBER) { + rpnp[i].val = (double) rpnc[i].val; + } else if (rpnp[i].op == OP_VARIABLE) { + rpnp[i].ptr = (long) rpnc[i].val; + } + } + /* terminate the sequence */ + rpnp[i].op = OP_END; + return rpnp; +} + +/* rpn_compact2str: convert a compact sequence of RPN operator nodes back + * into a CDEF string. This function is used by rrd_dump. + * arguments: + * rpnc: an array of compact RPN operator nodes + * rrd: a pointer an rrd header (only the ds_cnt and ds_def elements need + * to be valid) for lookup of data source names by index + * str: out string, memory is allocated by the function, must be freed by the + * the caller */ +void rpn_compact2str(rpn_cdefds_t *rpnc,ds_def_t *ds_def,char **str) +{ + unsigned short i,offset = 0; + char buffer[7]; /* short as a string */ + + for (i = 0; rpnc[i].op != OP_END; i++) + { + if (i > 0) (*str)[offset++] = ','; + +#define add_op(VV,VVV) \ + if (addop2str(rpnc[i].op, VV, VVV, str, &offset) == 1) continue; + + if (rpnc[i].op == OP_NUMBER) { + /* convert a short into a string */ +#ifdef WIN32 + _itoa(rpnc[i].val,buffer,10); +#else + sprintf(buffer,"%d",rpnc[i].val); +#endif + add_op(OP_NUMBER,buffer) + } + + if (rpnc[i].op == OP_VARIABLE) { + char *ds_name = ds_def[rpnc[i].val].ds_nam; + add_op(OP_VARIABLE, ds_name) + } +#undef add_op + +#define add_op(VV,VVV) \ + if (addop2str(rpnc[i].op, VV, #VVV, str, &offset) == 1) continue; + + add_op(OP_ADD,+) + add_op(OP_SUB,-) + add_op(OP_MUL,*) + add_op(OP_DIV,/) + add_op(OP_MOD,%) + add_op(OP_SIN,SIN) + add_op(OP_COS,COS) + add_op(OP_LOG,LOG) + add_op(OP_FLOOR,FLOOR) + add_op(OP_CEIL,CEIL) + add_op(OP_EXP,EXP) + add_op(OP_DUP,DUP) + add_op(OP_EXC,EXC) + add_op(OP_POP,POP) + add_op(OP_LT,LT) + add_op(OP_LE,LE) + add_op(OP_GT,GT) + add_op(OP_GE,GE) + add_op(OP_EQ,EQ) + add_op(OP_IF,IF) + add_op(OP_MIN,MIN) + add_op(OP_MAX,MAX) + add_op(OP_LIMIT,LIMIT) + add_op(OP_UNKN,UNKN) + add_op(OP_UN,UN) + add_op(OP_NEGINF,NEGINF) + add_op(OP_PREV,PREV) + add_op(OP_INF,INF) + add_op(OP_NOW,NOW) + add_op(OP_LTIME,LTIME) + add_op(OP_TIME,TIME) + +#undef add_op + } + (*str)[offset] = '\0'; + +} + +short addop2str(enum op_en op, enum op_en op_type, char *op_str, + char **result_str, unsigned short *offset) +{ + if (op == op_type) { + short op_len; + op_len = strlen(op_str); + *result_str = (char *) rrd_realloc(*result_str, + (op_len + 1 + *offset)*sizeof(char)); + if (*result_str == NULL) { + rrd_set_error("failed to alloc memory in addop2str"); + return -1; + } + strncpy(&((*result_str)[*offset]),op_str,op_len); + *offset += op_len; + return 1; + } + return 0; +} + +void parseCDEF_DS(char *def,rrd_t *rrd, int ds_idx) +{ + rpnp_t *rpnp = NULL; + rpn_cdefds_t *rpnc = NULL; + short count, i; + + rpnp = rpn_parse((void*) rrd, def, &lookup_DS); + if (rpnp == NULL) { + rrd_set_error("failed to parse computed data source %s", def); + return; + } + /* Check for OP nodes not permitted in COMPUTE DS. + * Moved this check from within rpn_compact() because it really is + * COMPUTE DS specific. This is less efficient, but creation doesn't + * occur too often. */ + for (i = 0; rpnp[i].op != OP_END; i++) { + if (rpnp[i].op == OP_TIME || rpnp[i].op == OP_LTIME || + rpnp[i].op == OP_PREV) + { + rrd_set_error( + "operators time, ltime and prev not supported with DS COMPUTE"); + free(rpnp); + return; + } + } + if (rpn_compact(rpnp,&rpnc,&count) == -1) { + free(rpnp); + return; + } + /* copy the compact rpn representation over the ds_def par array */ + memcpy((void*) &(rrd -> ds_def[ds_idx].par[DS_cdef]), + (void*) rpnc, count*sizeof(rpn_cdefds_t)); + free(rpnp); + free(rpnc); +} + +/* lookup a data source name in the rrd struct and return the index, + * should use ds_match() here except: + * (1) need a void * pointer to the rrd + * (2) error handling is left to the caller + */ +long lookup_DS(void *rrd_vptr,char *ds_name) +{ + int i; + rrd_t *rrd; + + rrd = (rrd_t *) rrd_vptr; + + for (i = 0; i < rrd -> stat_head -> ds_cnt; ++i) + { + if(strcmp(ds_name,rrd -> ds_def[i].ds_nam) == 0) + return i; + } + /* the caller handles a bad data source name in the rpn string */ + return -1; +} + +/* rpn_parse : parse a string and generate a rpnp array; modified + * str2rpn() originally included in rrd_graph.c + * arguments: + * key_hash: a transparent argument passed to lookup(); conceptually this + * is a hash object for lookup of a numeric key given a variable name + * expr: the string RPN expression, including variable names + * lookup(): a function that retrieves a numeric key given a variable name + */ +rpnp_t * +rpn_parse(void *key_hash,char *expr,long (*lookup)(void *,char*)){ + int pos=0; + long steps=-1; + rpnp_t *rpnp; + char vname[30]; + + rpnp=NULL; + + while(*expr){ + if ((rpnp = (rpnp_t *) rrd_realloc(rpnp, (++steps + 2)* + sizeof(rpnp_t)))==NULL){ + return NULL; + } + + else if((sscanf(expr,"%lf%n",&rpnp[steps].val,&pos) == 1) && (expr[pos] == ',')){ + rpnp[steps].op = OP_NUMBER; + expr+=pos; + } + +#define match_op(VV,VVV) \ + else if (strncmp(expr, #VVV, strlen(#VVV))==0){ \ + rpnp[steps].op = VV; \ + expr+=strlen(#VVV); \ + } + + match_op(OP_ADD,+) + match_op(OP_SUB,-) + match_op(OP_MUL,*) + match_op(OP_DIV,/) + match_op(OP_MOD,%) + match_op(OP_SIN,SIN) + match_op(OP_COS,COS) + match_op(OP_LOG,LOG) + match_op(OP_FLOOR,FLOOR) + match_op(OP_CEIL,CEIL) + match_op(OP_EXP,EXP) + match_op(OP_DUP,DUP) + match_op(OP_EXC,EXC) + match_op(OP_POP,POP) + match_op(OP_LT,LT) + match_op(OP_LE,LE) + match_op(OP_GT,GT) + match_op(OP_GE,GE) + match_op(OP_EQ,EQ) + match_op(OP_IF,IF) + match_op(OP_MIN,MIN) + match_op(OP_MAX,MAX) + match_op(OP_LIMIT,LIMIT) + /* order is important here ! .. match longest first */ + match_op(OP_UNKN,UNKN) + match_op(OP_UN,UN) + match_op(OP_NEGINF,NEGINF) + match_op(OP_PREV,PREV) + match_op(OP_INF,INF) + match_op(OP_NOW,NOW) + match_op(OP_LTIME,LTIME) + match_op(OP_TIME,TIME) + +#undef match_op + + + else if ((sscanf(expr,"%29[_A-Za-z0-9]%n", + vname,&pos) == 1) + && ((rpnp[steps].ptr = (*lookup)(key_hash,vname)) != -1)){ + rpnp[steps].op = OP_VARIABLE; + expr+=pos; + } + + else { + free(rpnp); + return NULL; + } + if (*expr == 0) + break; + if (*expr == ',') + expr++; + else { + free(rpnp); + return NULL; + } + } + rpnp[steps+1].op = OP_END; + return rpnp; +} + +void +rpnstack_init(rpnstack_t *rpnstack) +{ + rpnstack -> s = NULL; + rpnstack -> dc_stacksize = 0; + rpnstack -> dc_stackblock = 100; +} + +void +rpnstack_free(rpnstack_t *rpnstack) +{ + if (rpnstack -> s != NULL) + free(rpnstack -> s); + rpnstack -> dc_stacksize = 0; +} + +/* rpn_calc: run the RPN calculator; also performs variable substitution; + * moved and modified from data_calc() originally included in rrd_graph.c + * arguments: + * rpnp : an array of RPN operators (including variable references) + * rpnstack : the initialized stack + * data_idx : when data_idx is a multiple of rpnp.step, the rpnp.data pointer + * is advanced by rpnp.ds_cnt; used only for variable substitution + * output : an array of output values; OP_PREV assumes this array contains + * the "previous" value at index position output_idx-1; the definition of + * "previous" depends on the calling environment + * output_idx : an index into the output array in which to store the output + * of the RPN calculator + * returns: -1 if the computation failed (also calls rrd_set_error) + * 0 on success + */ +short +rpn_calc(rpnp_t *rpnp, rpnstack_t *rpnstack, long data_idx, + rrd_value_t *output, int output_idx) +{ + int rpi; + long stptr = -1; + + /* process each op from the rpn in turn */ + for (rpi=0; rpnp[rpi].op != OP_END; rpi++){ + /* allocate or grow the stack */ + if (stptr + 5 > rpnstack -> dc_stacksize){ + /* could move this to a separate function */ + rpnstack -> dc_stacksize += rpnstack -> dc_stackblock; + rpnstack -> s = rrd_realloc(rpnstack -> s, + (rpnstack -> dc_stacksize)*sizeof(*(rpnstack -> s))); + if (rpnstack -> s == NULL){ + rrd_set_error("RPN stack overflow"); + return -1; + } + } + switch (rpnp[rpi].op){ + case OP_NUMBER: + rpnstack -> s[++stptr] = rpnp[rpi].val; + break; + case OP_VARIABLE: + /* make sure we pull the correct value from the *.data array + * adjust the pointer into the array acordingly. + * Advance the ptr one row in the rra (skip over + * non-relevant data sources) */ + if (data_idx % rpnp[rpi].step == 0){ + rpnp[rpi].data += rpnp[rpi].ds_cnt; + } + rpnstack -> s[++stptr] = *(rpnp[rpi].data); + break; + case OP_PREV: + if ((output_idx-1) <= 0) { + rpnstack -> s[++stptr] = DNAN; + } else { + rpnstack -> s[++stptr] = output[output_idx-1]; + } + break; + case OP_UNKN: + rpnstack -> s[++stptr] = DNAN; + break; + case OP_INF: + rpnstack -> s[++stptr] = DINF; + break; + case OP_NEGINF: + rpnstack -> s[++stptr] = -DINF; + break; + case OP_NOW: + rpnstack -> s[++stptr] = (double)time(NULL); + break; + case OP_TIME: + /* HACK: this relies on the data_idx being the time, + * which the within-function scope is unaware of */ + rpnstack -> s[++stptr] = (double) data_idx; + break; + case OP_LTIME: + rpnstack -> s[++stptr] = (double) tzoffset(data_idx) + (double)data_idx; + break; + case OP_ADD: + if(stptr<1){ + rrd_set_error("RPN stack underflow"); + return -1; + } + rpnstack -> s[stptr-1] = rpnstack -> s[stptr-1] + + rpnstack -> s[stptr]; + stptr--; + break; + case OP_SUB: + if(stptr<1){ + rrd_set_error("RPN stack underflow"); + return -1; + } + rpnstack -> s[stptr-1] = rpnstack -> s[stptr-1] - rpnstack -> s[stptr]; + stptr--; + break; + case OP_MUL: + if(stptr<1){ + rrd_set_error("RPN stack underflow"); + return -1; + } + rpnstack -> s[stptr-1] = (rpnstack -> s[stptr-1]) * (rpnstack -> s[stptr]); + stptr--; + break; + case OP_DIV: + if(stptr<1){ + rrd_set_error("RPN stack underflow"); + return -1; + } + rpnstack -> s[stptr-1] = rpnstack -> s[stptr-1] / rpnstack -> s[stptr]; + stptr--; + break; + case OP_MOD: + if(stptr<1){ + rrd_set_error("RPN stack underflow"); + return -1; + } + rpnstack -> s[stptr-1] = fmod(rpnstack -> s[stptr-1],rpnstack -> s[stptr]); + stptr--; + break; + case OP_SIN: + if(stptr<0){ + rrd_set_error("RPN stack underflow"); + return -1; + } + rpnstack -> s[stptr] = sin(rpnstack -> s[stptr]); + break; + case OP_COS: + if(stptr<0){ + rrd_set_error("RPN stack underflow"); + return -1; + } + rpnstack -> s[stptr] = cos(rpnstack -> s[stptr]); + break; + case OP_CEIL: + if(stptr<0){ + rrd_set_error("RPN stack underflow"); + return -1; + } + rpnstack -> s[stptr] = ceil(rpnstack -> s[stptr]); + break; + case OP_FLOOR: + if(stptr<0){ + rrd_set_error("RPN stack underflow"); + return -1; + } + rpnstack -> s[stptr] = floor(rpnstack -> s[stptr]); + break; + case OP_LOG: + if(stptr<0){ + rrd_set_error("RPN stack underflow"); + return -1; + } + rpnstack -> s[stptr] = log(rpnstack -> s[stptr]); + break; + case OP_DUP: + if(stptr<0){ + rrd_set_error("RPN stack underflow"); + return -1; + } + rpnstack -> s[stptr+1] = rpnstack -> s[stptr]; + stptr++; + break; + case OP_POP: + if(stptr<0){ + rrd_set_error("RPN stack underflow"); + return -1; + } + stptr--; + break; + case OP_EXC: + if(stptr<1){ + rrd_set_error("RPN stack underflow"); + return -1; + } else { + double dummy; + dummy = rpnstack -> s[stptr] ; + rpnstack -> s[stptr] = rpnstack -> s[stptr-1]; + rpnstack -> s[stptr-1] = dummy; + } + break; + case OP_EXP: + if(stptr<0){ + rrd_set_error("RPN stack underflow"); + return -1; + } + rpnstack -> s[stptr] = exp(rpnstack -> s[stptr]); + break; + case OP_LT: + if(stptr<1){ + rrd_set_error("RPN stack underflow"); + return -1; + } + if (isnan(rpnstack -> s[stptr-1])) + ; + else if (isnan(rpnstack -> s[stptr])) + rpnstack -> s[stptr-1] = rpnstack -> s[stptr]; + else + rpnstack -> s[stptr-1] = rpnstack -> s[stptr-1] < rpnstack -> s[stptr] ? 1.0 : 0.0; + stptr--; + break; + case OP_LE: + if(stptr<1){ + rrd_set_error("RPN stack underflow"); + return -1; + } + if (isnan(rpnstack -> s[stptr-1])) + ; + else if (isnan(rpnstack -> s[stptr])) + rpnstack -> s[stptr-1] = rpnstack -> s[stptr]; + else + rpnstack -> s[stptr-1] = rpnstack -> s[stptr-1] <= rpnstack -> s[stptr] ? 1.0 : 0.0; + stptr--; + break; + case OP_GT: + if(stptr<1){ + rrd_set_error("RPN stack underflow"); + return -1; + } + if (isnan(rpnstack -> s[stptr-1])) + ; + else if (isnan(rpnstack -> s[stptr])) + rpnstack -> s[stptr-1] = rpnstack -> s[stptr]; + else + rpnstack -> s[stptr-1] = rpnstack -> s[stptr-1] > rpnstack -> s[stptr] ? 1.0 : 0.0; + stptr--; + break; + case OP_GE: + if(stptr<1){ + rrd_set_error("RPN stack underflow"); + return -1; + } + if (isnan(rpnstack -> s[stptr-1])) + ; + else if (isnan(rpnstack -> s[stptr])) + rpnstack -> s[stptr-1] = rpnstack -> s[stptr]; + else + rpnstack -> s[stptr-1] = rpnstack -> s[stptr-1] >= rpnstack -> s[stptr] ? 1.0 : 0.0; + stptr--; + break; + case OP_EQ: + if(stptr<1){ + rrd_set_error("RPN stack underflow"); + return -1; + } + if (isnan(rpnstack -> s[stptr-1])) + ; + else if (isnan(rpnstack -> s[stptr])) + rpnstack -> s[stptr-1] = rpnstack -> s[stptr]; + else + rpnstack -> s[stptr-1] = rpnstack -> s[stptr-1] == rpnstack -> s[stptr] ? 1.0 : 0.0; + stptr--; + break; + case OP_IF: + if(stptr<2){ + rrd_set_error("RPN stack underflow"); + return -1; + } + rpnstack->s[stptr-2] = rpnstack->s[stptr-2] != 0.0 ? rpnstack->s[stptr-1] : rpnstack->s[stptr]; + stptr--; + stptr--; + break; + case OP_MIN: + if(stptr<1){ + rrd_set_error("RPN stack underflow"); + return -1; + } + if (isnan(rpnstack->s[stptr-1])) + ; + else if (isnan(rpnstack->s[stptr])) + rpnstack->s[stptr-1] = rpnstack->s[stptr]; + else if (rpnstack->s[stptr-1] > rpnstack->s[stptr]) + rpnstack->s[stptr-1] = rpnstack->s[stptr]; + stptr--; + break; + case OP_MAX: + if(stptr<1){ + rrd_set_error("RPN stack underflow"); + return -1; + } + if (isnan(rpnstack->s[stptr-1])) + ; + else if (isnan(rpnstack->s[stptr])) + rpnstack->s[stptr-1] = rpnstack->s[stptr]; + else if (rpnstack->s[stptr-1] < rpnstack->s[stptr]) + rpnstack->s[stptr-1] = rpnstack->s[stptr]; + stptr--; + break; + case OP_LIMIT: + if(stptr<2){ + rrd_set_error("RPN stack underflow"); + free(rpnstack->s); + return -1; + } + if (isnan(rpnstack->s[stptr-2])) + ; + else if (isnan(rpnstack->s[stptr-1])) + rpnstack->s[stptr-2] = rpnstack->s[stptr-1]; + else if (isnan(rpnstack->s[stptr])) + rpnstack->s[stptr-2] = rpnstack->s[stptr]; + else if (rpnstack->s[stptr-2] < rpnstack->s[stptr-1]) + rpnstack->s[stptr-2] = DNAN; + else if (rpnstack->s[stptr-2] > rpnstack->s[stptr]) + rpnstack->s[stptr-2] = DNAN; + stptr-=2; + break; + case OP_UN: + if(stptr<0){ + rrd_set_error("RPN stack underflow"); + return -1; + } + rpnstack->s[stptr] = isnan(rpnstack->s[stptr]) ? 1.0 : 0.0; + break; + case OP_END: + break; + } + } + if(stptr!=0){ + rrd_set_error("RPN final stack size != 1"); + return -1; + } + + output[output_idx] = rpnstack->s[0]; + return 0; +} + +/* figure out what the local timezone offset for any point in + time was. Return it in seconds */ +int +tzoffset( time_t now ){ + int gm_sec, gm_min, gm_hour, gm_yday, gm_year, + l_sec, l_min, l_hour, l_yday, l_year; + struct tm *t; + int off; + t = gmtime(&now); + gm_sec = t->tm_sec; + gm_min = t->tm_min; + gm_hour = t->tm_hour; + gm_yday = t->tm_yday; + gm_year = t->tm_year; + t = localtime(&now); + l_sec = t->tm_sec; + l_min = t->tm_min; + l_hour = t->tm_hour; + l_yday = t->tm_yday; + l_year = t->tm_year; + off = (l_sec-gm_sec)+(l_min-gm_min)*60+(l_hour-gm_hour)*3600; + if ( l_yday > gm_yday || l_year > gm_year){ + off += 24*3600; + } else if ( l_yday < gm_yday || l_year < gm_year){ + off -= 24*3600; + } + + return off; +} diff --git a/src/rrd_rpncalc.h b/src/rrd_rpncalc.h new file mode 100644 index 0000000..67e54c1 --- /dev/null +++ b/src/rrd_rpncalc.h @@ -0,0 +1,52 @@ +/**************************************************************************** + * RRDtool 1.0.33 Copyright Tobias Oetiker, 1997 - 2000 + **************************************************************************** + * rrd_rpncalc.h RPN calculator functions + ****************************************************************************/ + +/* WARNING: if new operators are added, they MUST be added after OP_END. + * This is because COMPUTE (CDEF) DS store OP nodes by number (name is not + * an option due to limited par array size). OP nodes must have the same + * numeric values, otherwise the stored numbers will mean something different. */ +enum op_en {OP_NUMBER=0,OP_VARIABLE,OP_INF,OP_PREV,OP_NEGINF, + OP_UNKN,OP_NOW,OP_TIME,OP_ADD,OP_MOD,OP_SUB,OP_MUL, + OP_DIV,OP_SIN, OP_DUP, OP_EXC, OP_POP, + OP_COS,OP_LOG,OP_EXP,OP_LT,OP_LE,OP_GT,OP_GE,OP_EQ,OP_IF, + OP_MIN,OP_MAX,OP_LIMIT, OP_FLOOR, OP_CEIL, + OP_UN,OP_END,OP_LTIME}; + +typedef struct rpnp_t { + enum op_en op; + double val; /* value for a OP_NUMBER */ + long ptr; /* pointer into the gdes array for OP_VAR */ + double *data; /* pointer to the current value from OP_VAR DAS*/ + long ds_cnt; /* data source count for data pointer */ + long step; /* time step for OP_VAR das */ +} rpnp_t; + +/* a compact representation of rpnp_t for computed data sources */ +typedef struct rpn_cdefds_t { + char op; /* rpn operator type */ + short val; /* used by OP_NUMBER and OP_VARIABLE */ +} rpn_cdefds_t; + +/* limit imposed by sizeof(rpn_cdefs_t) and rrd.ds_def.par */ +#define DS_CDEF_MAX_RPN_NODES 26 + +typedef struct rpnstack_t { + double *s; + long dc_stacksize; + long dc_stackblock; +} rpnstack_t; + +void rpnstack_init(rpnstack_t *rpnstack); +void rpnstack_free(rpnstack_t *rpnstack); + +void parseCDEF_DS(char *def, rrd_t *rrd, int ds_idx); +long lookup_DS(void *rrd_vptr, char *ds_name); + +short rpn_compact(rpnp_t *rpnp,rpn_cdefds_t **rpnc,short *count); +rpnp_t * rpn_expand(rpn_cdefds_t *rpnc); +void rpn_compact2str(rpn_cdefds_t *rpnc,ds_def_t *ds_def,char **str); +rpnp_t * rpn_parse(void *key_hash,char *expr, long (*lookup)(void *,char *)); +short rpn_calc(rpnp_t *rpnp, rpnstack_t *rpnstack, long data_idx, rrd_value_t *output, int output_idx); diff --git a/src/rrd_tool.h b/src/rrd_tool.h index 2795665..40e5480 100644 --- a/src/rrd_tool.h +++ b/src/rrd_tool.h @@ -2,19 +2,6 @@ * RRDtool 1.0.33 Copyright Tobias Oetiker, 1997 - 2000 ***************************************************************************** * rrd_tool.h Common Header File - ***************************************************************************** - * $Id$ - * $Log$ - * Revision 1.2 2001/03/04 13:01:55 oetiker - * Aberrant Behavior Detection support. A brief overview added to rrdtool.pod. - * Major updates to rrd_update.c, rrd_create.c. Minor update to other core files. - * This is backwards compatible! But new files using the Aberrant stuff are not readable - * by old rrdtool versions. See http://cricket.sourceforge.net/aberrant/rrd_hw.htm - * -- Jake Brutlag - * - * Revision 1.1.1.1 2001/02/25 22:25:06 oetiker - * checkin - * *****************************************************************************/ #ifdef __cplusplus extern "C" { @@ -160,36 +147,8 @@ enum dst_en dst_conv(char *string); long ds_match(rrd_t *rrd,char *ds_nam); double rrd_diff(char *a, char *b); -/* functions added for aberrant behavior detection. - * implemented for the most part in rrd_hw.c */ -int update_aberrant_CF(rrd_t *rrd, rrd_value_t pdp_val, enum cf_en current_cf, - unsigned long cdp_idx, unsigned long rra_idx, unsigned long ds_idx, - unsigned short CDP_scratch_idx, rrd_value_t *seasonal_coef); -int create_hw_contingent_rras(rrd_t *rrd, unsigned short period, - unsigned long hashed_name); -int lookup_seasonal(rrd_t *rrd, unsigned long rra_idx, unsigned long rra_start, - FILE *rrd_file, unsigned long offset, rrd_value_t **seasonal_coef); -void erase_violations(rrd_t *rrd, unsigned long cdp_idx, unsigned long rra_idx); -int apply_smoother(rrd_t *rrd, unsigned long rra_idx, unsigned long rra_start, - FILE *rrd_file); - -/* a standard fixed-capacity FIFO queue implementation */ -typedef struct FIFOqueue { - rrd_value_t *queue; - int capacity, head, tail; -} FIFOqueue; - -int queue_alloc(FIFOqueue **q,int capacity); -void queue_dealloc(FIFOqueue *q); -void queue_push(FIFOqueue *q, rrd_value_t value); -int queue_isempty(FIFOqueue *q); -rrd_value_t queue_pop(FIFOqueue *q); - -#define BURNIN_CYCLES 3 - #endif - #ifdef __cplusplus } #endif diff --git a/src/rrd_update.c b/src/rrd_update.c index 08ecb49..35ac6a6 100644 --- a/src/rrd_update.c +++ b/src/rrd_update.c @@ -5,6 +5,14 @@ ***************************************************************************** * $Id$ * $Log$ + * Revision 1.4 2001/03/10 23:54:41 oetiker + * Support for COMPUTE data sources (CDEF data sources). Removes the RPN + * parser and calculator from rrd_graph and puts then in a new file, + * rrd_rpncalc.c. Changes to core files rrd_create and rrd_update. Some + * clean-up of aberrant behavior stuff, including a bug fix. + * Documentation update (rrdcreate.pod, rrdupdate.pod). Change xml format. + * -- Jake Brutlag + * * Revision 1.3 2001/03/04 13:01:55 oetiker * Aberrant Behavior Detection support. A brief overview added to rrdtool.pod. * Major updates to rrd_update.c, rrd_create.c. Minor update to other core files. @@ -24,6 +32,8 @@ #include "rrd_tool.h" #include #include +#include "rrd_hw.h" +#include "rrd_rpncalc.h" #ifdef WIN32 #include @@ -31,13 +41,11 @@ #include #endif -/* Prototypes */ +/* Local prototypes */ int LockRRD(FILE *rrd_file); void write_RRA_row (rrd_t *rrd, unsigned long rra_idx, unsigned long *rra_current, unsigned short CDP_scratch_idx, FILE *rrd_file); -/*#define DEBUG */ - #define IFDNAN(X,Y) (isnan(X) ? (Y) : (X)); @@ -124,6 +132,9 @@ rrd_update(int argc, char **argv) /* index into the CDP scratch array */ enum cf_en current_cf; /* numeric id of the current consolidation function */ + rpnstack_t rpnstack; /* used for COMPUTE DS */ + + rpnstack_init(&rpnstack); while (1) { static struct option long_options[] = @@ -209,14 +220,19 @@ rrd_update(int argc, char **argv) return(-1); } /* initialize template redirector */ - /* default config + /* default config example (assume DS 1 is a CDEF DS) tmpl_idx[0] -> 0; (time) tmpl_idx[1] -> 1; (DS 0) - tmpl_idx[2] -> 2; (DS 1) - tmpl_idx[3] -> 3; (DS 2) - ... */ - for (i=0;i<=rrd.stat_head->ds_cnt;i++) tmpl_idx[i]=i; - tmpl_cnt=rrd.stat_head->ds_cnt+1; + tmpl_idx[2] -> 3; (DS 2) + tmpl_idx[3] -> 4; (DS 3) */ + tmpl_idx[0] = 0; /* time */ + for (i = 1, ii = 1 ; i <= rrd.stat_head->ds_cnt ; i++) + { + if (dst_conv(rrd.ds_def[i-1].dst) != DST_CDEF) + tmpl_idx[ii++]=i; + } + tmpl_cnt= ii; + if (template) { char *dsname; int tmpl_len; @@ -401,7 +417,12 @@ rrd_update(int argc, char **argv) for(i=0;ids_cnt;i++){ enum dst_en dst_idx; dst_idx= dst_conv(rrd.ds_def[i].dst); + /* NOTE: DST_CDEF should never enter this if block, because + * updvals[i+1][0] is initialized to 'U'; unless the caller + * accidently specified a value for the DST_CDEF. To handle + * this case, an extra check is required. */ if((updvals[i+1][0] != 'U') && + (dst_idx != DST_CDEF) && rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt >= interval) { double rate = DNAN; /* the data source type defines how to process the data */ @@ -512,7 +533,6 @@ rrd_update(int argc, char **argv) pdp_new[] contains rate*seconds from the latest run. pdp_temp[] will contain the rate for cdp */ - for(i=0;ids_cnt;i++){ /* update pdp_prep to the current pdp_st */ if(isnan(pdp_new[i])) @@ -533,6 +553,27 @@ rrd_update(int argc, char **argv) - proc_pdp_st - rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt); } + + /* process CDEF data sources; remember each CDEF DS can + * only reference other DS with a lower index number */ + if (dst_conv(rrd.ds_def[i].dst) == DST_CDEF) { + rpnp_t *rpnp; + rpnp = rpn_expand((rpn_cdefds_t *) &(rrd.ds_def[i].par[DS_cdef])); + /* substitue data values for OP_VARIABLE nodes */ + for (ii = 0; rpnp[ii].op != OP_END; ii++) + { + if (rpnp[ii].op == OP_VARIABLE) { + rpnp[ii].op = OP_NUMBER; + rpnp[ii].val = pdp_temp[rpnp[ii].ptr]; + } + } + /* run the rpn calculator */ + if (rpn_calc(rpnp,&rpnstack,0,pdp_temp,i) == -1) { + free(rpnp); + break; /* exits the data sources pdp_temp loop */ + } + } + /* make pdp_prep ready for the next run */ if(isnan(pdp_new[i])){ rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt = post_int; @@ -555,6 +596,12 @@ rrd_update(int argc, char **argv) #endif } + /* if there were errors during the last loop, bail out here */ + if (rrd_test_error()){ + free(step_start); + break; + } + /* compute the number of elapsed pdp_st moments */ elapsed_pdp_st = (occu_pdp_st - proc_pdp_st) / rrd.stat_head -> pdp_step; #ifdef DEBUG @@ -891,6 +938,7 @@ rrd_update(int argc, char **argv) lookup_seasonal(&rrd,i,rra_start,rrd_file, elapsed_pdp_st + (scratch_idx == CDP_primary_val ? 1 : 2), &seasonal_coef); + rra_current = ftell(rrd_file); } if (rrd_test_error()) break; /* loop over data soures within each RRA */ @@ -936,6 +984,7 @@ rrd_update(int argc, char **argv) } rra_current = rra_pos_tmp; } + #ifdef DEBUG fprintf(stderr," -- RRA Postseek %ld\n",ftell(rrd_file)); #endif @@ -988,6 +1037,7 @@ rrd_update(int argc, char **argv) if (seasonal_coef != NULL) free(seasonal_coef); if (last_seasonal_coef != NULL) free(last_seasonal_coef); if (rra_step_cnt != NULL) free(rra_step_cnt); + rpnstack_free(&rpnstack); /* if we got here and if there is an error and if the file has not been * written to, then close things up and return. */ @@ -997,7 +1047,7 @@ rrd_update(int argc, char **argv) rrd_free(&rrd); free(pdp_temp); free(pdp_new); - fclose(rrd_file); + fclose(rrd_file); return(-1); } @@ -1014,7 +1064,7 @@ rrd_update(int argc, char **argv) rrd_free(&rrd); free(pdp_temp); free(pdp_new); - fclose(rrd_file); + fclose(rrd_file); return(-1); } @@ -1026,7 +1076,7 @@ rrd_update(int argc, char **argv) free(tmpl_idx); free(pdp_temp); free(pdp_new); - fclose(rrd_file); + fclose(rrd_file); return(-1); } @@ -1039,7 +1089,7 @@ rrd_update(int argc, char **argv) free(tmpl_idx); free(pdp_temp); free(pdp_new); - fclose(rrd_file); + fclose(rrd_file); return(-1); } @@ -1054,7 +1104,7 @@ rrd_update(int argc, char **argv) rrd_free(&rrd); free(pdp_temp); free(pdp_new); - fclose(rrd_file); + fclose(rrd_file); return(-1); } @@ -1067,7 +1117,7 @@ rrd_update(int argc, char **argv) rrd_free(&rrd); free(pdp_temp); free(pdp_new); - fclose(rrd_file); + fclose(rrd_file); return(-1); } @@ -1172,7 +1222,7 @@ write_RRA_row (rrd_t *rrd, unsigned long rra_idx, unsigned long *rra_current, fprintf(stderr," -- RRA WRITE VALUE %e, at %ld CF:%s\n", rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val,ftell(rrd_file), rrd -> rra_def[rra_idx].cf_nam); -#endif +#endif if(fwrite(&(rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val), sizeof(rrd_value_t),1,rrd_file) != 1)