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 <oetiker@ee.ethz.ch>
- Integrated complete rewrite of rrdgraph documentation by
- Alex van den Bogaerdt <alex@slot.hollandcasino.nl>. THis also contains
- info on his planned changes to the rrdgraph module
+2001/03/10 Jake Brutlag <jakeb@corp.webtv.net>
+ 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 <oetiker@ee.ethz.ch>
+ Integrated complete rewrite
+ of rrdgraph documentation by Alex van den Bogaerdt
+ <alex@slot.hollandcasino.nl>. This also contains info on his planned
+ changes to the rrdgraph module
2001/03/02 Tobias Oetiker <oetiker@ee.ethz.ch>
Added Aberrant Patch from Jake Brutlag <jakeb@microsoft.com>
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
B<rrdtool> B<create> I<filename>
S<[B<--start>|B<-b> I<start time>]>
S<[B<--step>|B<-s> I<step>]>
-S<[B<DS:>I<ds-name>B<:>I<DST>B<:>I<heartbeat>B<:>I<min>B<:>I<max>]>
+S<[B<DS:>I<ds-name>B<:>I<DST>B<:>I<dst arguments>]>
+I<heartbeat>B<:>I<min>B<:>I<max>]>
S<[B<RRA:>I<CF>B<:>I<cf arguments>]>
=head1 DESCRIPTION
Specifies the base interval in seconds with which data will be fed
into the B<RRD>.
-=item B<DS:>I<ds-name>B<:>I<DST>B<:>I<heartbeat>B<:>I<min>B<:>I<max>
+=item B<DS:>I<ds-name>B<:>I<DST>B<:>I<dst arguments>
A single B<RRD> can accept input from several data sources (B<DS>).
(e.g. Incoming and Outgoing traffic on a specific communication
source from an B<RRD>. A I<ds-name> must be 1 to 19 characters long in
the characters [a-zA-Z0-9_].
-I<DST> 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<DST> 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:
+
+B<DS:>I<ds-name>B<:>I<GAUGE | COUNTER | DERIVE | ABSOLUTE>B<:>I<heartbeat>B<:>I<min>B<:>I<max>
+
+For COMPUTE data sources, the format is:
+
+B<DS:>I<ds-name>B<:>I<COMPUTE>B<:>I<rpn-expression>
+
+To decide on a data source type, review the definitions that follow.
+Consult the section on "HOW TO MEASURE" for further insight.
=over 4
next overflow. Another usage is for things you count like number of messages
since the last update.
+=item B<COMPUTE>
+
+is for storing the result of a formula applied to other data sources in
+the B<RRD>. 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<heartbeat> defines the maximum number of seconds that may pass
always set the min and/or max properties. This will help RRDtool in
doing a simple sanity check on the data supplied when running update.>
+I<rpn-expression> defines the formula used to compute the PDPs of a COMPUTE
+data source from other data sources in the same <RRD>. It is similar to defining
+a B<CDEF> 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 B<CDEF>s must
+refer only to B<DEF>s and B<CDEF>s previously defined in the same graph command.
+
=item B<RRA:>I<CF>B<:>I<cf arguments>
The purpose of an B<RRD> is to store data in the round robin archives
The data is also processed with the consolidation function (I<CF>)
of the archive. There are several consolidation functions that consolidate
primary data points via an aggregate function: B<AVERAGE>, B<MIN>, B<MAX>, B<LAST>.
-The format of B<RRA> line for these consolidation function is:
+The format of B<RRA> line for these consolidation functions is:
B<RRA:>I<AVERAGE | MIN | MAX | LAST>B<:>I<xff>B<:>I<steps>B<:>I<rows>
Of course, explicit creation need not replicate implicit create, a number of arguments
could be changed.
+=head1 EXAMPLE 3
+
+C<rrdtool create proxy.rrd --step 300
+DS:TotalRequests:DERIVE:1800:0:U
+DS:AccumDuration:DERIVE:1800:0:U
+DS:AvgReqDuration:COMPUTE:AccumDuration,TotalRequests,0,EQ,1,TotalRequests,IF,/
+RRA:AVERAGE:0.5:1:2016>
+
+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<RRD>, 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 E<lt>oetiker@ee.ethz.chE<gt>
Check L<rrdcreate> if you are uncertain about the meaning of the
individual keys.
+The B<info> function will always report the true version of the B<RRD>;
+unlike B<dump> which will generate a current version (0002) xml dump for
+older version (0001) files (although the version of the B<RRD> is unchanged).
+
=head1 EXAMPLE
This is the output generated by running B<info> on a simple rrd which
One application of the B<tune> function is to relax the
validation rules on an B<RRD>. This allows to fill a new B<RRD> 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<min>, I<max>, and I<heartbeat> for a COMPUTE data source
+without changing the data source type to a non-COMPUTE B<DST> WILL corrupt
+the data source header in the B<RRD>.
A second application of the B<tune> function is to set or alter parameters
used by the specialized function B<RRAs> for aberrant behavior detection.
Alter the deviation scaling factor for the upper bound of the confidence band
used internally to calculate violations for the FAILURES B<RRA>. 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<RRA> (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<RRA> (although common sense dictates it should).
=item S<B<--deltaneg> I<scale-value>>
=item B<--template>|B<-t> I<ds-name>[B<:>I<ds-name>]...
-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<update> 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<DST> is COMPUTE, the third input value
+will be mapped to the fourth data source in the B<RRD>). 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<RRDtool> implicitly assigns non-COMPUTE data sources missing
+from the template the I<*UNKNOWN*> value.
+
+Do not specify a value for a COMPUTE B<DST> in the B<update> function. If
+this is done accidently (and this can only be done using the template switch),
+B<RRDtool> will ignore the value specified for the COMPUTE B<DST>.
+
=item B<N>|I<timestamp>B<:>I<value>[B<:>I<value>...]
The data used for updating the RRD was acquired at a certain time. This
gifsize.c \
parsetime.c \
hash_32.c \
- rrd_hw.c \
+ rrd_hw.c \
pngsize.c \
rrd_create.c \
rrd_diff.c \
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
*****************************************************************************/
#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;
* arrays. */
hashed_name = FnvHash(argv[optind]);
for(i=optind+1;i<argc;i++){
- char minstr[DS_NAM_SIZE], maxstr[DS_NAM_SIZE];
int ii;
if (strncmp(argv[i],"DS:",3)==0){
size_t old_size = sizeof(ds_def_t)*(rrd.stat_head->ds_cnt);
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;ii<rrd.stat_head->ds_cnt;ii++){
+ rrd.ds_def[rrd.stat_head->ds_cnt].dst,&offset) == 2)
+ {
+ /* check for duplicate datasource names */
+ for(ii=0;ii<rrd.stat_head->ds_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,
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
*****************************************************************************
* $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 <jakeb@corp.webtv.net>
*
+ * 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];
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);
printf("\t<ds>\n");
printf("\t\t<name> %s </name>\n",rrd.ds_def[i].ds_nam);
printf("\t\t<type> %s </type>\n",rrd.ds_def[i].dst);
+ if (dst_conv(rrd.ds_def[i].dst) != DST_CDEF) {
printf("\t\t<minimal_heartbeat> %lu </minimal_heartbeat>\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<min> NaN </min>\n");
} else {
printf("\t\t<max> %0.10e </max>\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<cdef> %s </cdef>\n", str);
+ free(str);
+ }
printf("\n\t\t<!-- PDP Status -->\n");
printf("\t\t<last_ds> %s </last_ds>\n",rrd.pdp_prep[i].last_ds);
if (isnan(rrd.pdp_prep[i].scratch[PDP_val].u_val)){
printf("\t\t<pdp_per_row> %lu </pdp_per_row> <!-- %lu seconds -->\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<params>");
- 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("<value> %lu </value>", value.u_cnt);
- else {
- if (isnan(value.u_val)) {
- printf("<value> NaN </value>");
- } else {
- printf("<value> %0.10e </value>", value.u_val);
- }
- }
+ /* support for RRA parameters */
+ printf("\t\t<params>\n");
+ switch(cf_conv(rrd.rra_def[i].cf_nam)) {
+ case CF_HWPREDICT:
+ printf("\t\t<hw_alpha> %0.10e </hw_alpha>\n",
+ rrd.rra_def[i].par[RRA_hw_alpha].u_val);
+ printf("\t\t<hw_beta> %0.10e </hw_beta>\n",
+ rrd.rra_def[i].par[RRA_hw_beta].u_val);
+ printf("\t\t<dependent_rra_idx> %lu </dependent_rra_idx>\n",
+ rrd.rra_def[i].par[RRA_dependent_rra_idx].u_cnt);
+ break;
+ case CF_SEASONAL:
+ case CF_DEVSEASONAL:
+ printf("\t\t<seasonal_gamma> %0.10e </seasonal_gamma>\n",
+ rrd.rra_def[i].par[RRA_seasonal_gamma].u_val);
+ printf("\t\t<seasonal_smooth_idx> %lu </seasonal_smooth_idx>\n",
+ rrd.rra_def[i].par[RRA_seasonal_smooth_idx].u_cnt);
+ printf("\t\t<dependent_rra_idx> %lu </dependent_rra_idx>\n",
+ rrd.rra_def[i].par[RRA_dependent_rra_idx].u_cnt);
+ break;
+ case CF_FAILURES:
+ printf("\t\t<delta_pos> %0.10e </delta_pos>\n",
+ rrd.rra_def[i].par[RRA_delta_pos].u_val);
+ printf("\t\t<delta_neg> %0.10e </delta_neg>\n",
+ rrd.rra_def[i].par[RRA_delta_neg].u_val);
+ printf("\t\t<window_len> %lu </window_len>\n",
+ rrd.rra_def[i].par[RRA_window_len].u_cnt);
+ printf("\t\t<failure_threshold> %lu </failure_threshold>\n",
+ rrd.rra_def[i].par[RRA_failure_threshold].u_cnt);
+ /* fall thru */
+ case CF_DEVPREDICT:
+ printf("\t\t<dependent_rra_idx> %lu </dependent_rra_idx>\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<xff> %0.10e </xff>\n", rrd.rra_def[i].par[RRA_cdp_xff_val].u_val);
+ break;
}
printf("\t\t</params>\n");
printf("\t\t<cdp_prep>\n");
for(ii=0;ii<rrd.stat_head->ds_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<ds>");
- /* 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("<value> %lu </value>", value.u_cnt);
- else {
- if (isnan(value.u_val)) {
- printf("<value> NaN </value>");
- } else {
- printf("<value> %0.10e </value>", value.u_val);
- }
- }
- }
-#if 0
- printf(" <unknown_datapoints> %lu </unknown_datapoints>",
+ unsigned long ivalue;
+ printf("\t\t\t<ds>\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<primary_value> NaN </primary_value>\n");
+ } else {
+ printf("\t\t\t<primary_value> %0.10e </primary_value>\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<secondary_value> NaN </secondary_value>\n");
+ } else {
+ printf("\t\t\t<secondary_value> %0.10e </secondary_value>\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<intercept> NaN </intercept>\n");
+ } else {
+ printf("\t\t\t<intercept> %0.10e </intercept>\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<last_intercept> NaN </last_intercept>\n");
+ } else {
+ printf("\t\t\t<last_intercept> %0.10e </last_intercept>\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<slope> NaN </slope>\n");
+ } else {
+ printf("\t\t\t<slope> %0.10e </slope>\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<last_slope> NaN </last_slope>\n");
+ } else {
+ printf("\t\t\t<last_slope> %0.10e </last_slope>\n", value);
+ }
+ ivalue = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_null_count].u_cnt;
+ printf("\t\t\t<nan_count> %lu </nan_count>\n", ivalue);
+ ivalue = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_last_null_count].u_cnt;
+ printf("\t\t\t<last_nan_count> %lu </last_nan_count>\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<seasonal> NaN </seasonal>\n");
+ } else {
+ printf("\t\t\t<seasonal> %0.10e </seasonal>\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<last_seasonal> NaN </last_seasonal>\n");
+ } else {
+ printf("\t\t\t<last_seasonal> %0.10e </last_seasonal>\n", value);
+ }
+ ivalue = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_init_seasonal].u_cnt;
+ printf("\t\t\t<init_flag> %lu </init_flag>\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<history> ");
+ for (vidx = 0; vidx < rrd.rra_def[i].par[RRA_window_len].u_cnt; ++vidx)
+ {
+ printf("%d",violations_array[vidx]);
+ }
+ printf(" </history>\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<value> NaN </value>\n");
+ } else {
+ printf("\t\t\t<value> %0.10e </value>\n", value);
+ }
+ printf("\t\t\t<unknown_datapoints> %lu </unknown_datapoints>\n",
rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_unkn_pdp_cnt].u_cnt);
-#endif
- printf("</ds>\n");
- }
+ break;
+ }
+ printf("\t\t\t</ds>\n");
+ }
printf("\t\t</cdp_prep>\n");
printf("\t\t<database>\n");
*****************************************************************************
* $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 <jakeb@corp.webtv.net>
+ *
+ * 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
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);
}
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);
}
rrd_set_error("unknown data source name '%s'",ds_nam);
return -1;
}
-
-
-
-
-
-
-
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
* 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_-]"
#include <io.h>
#include <fcntl.h>
#endif
+#include "rrd_rpncalc.h"
#define SmallFont gdLucidaNormal10
#define LargeFont gdLucidaBold12
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;
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 *);
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 \
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;
}
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;
* 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){
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;gdi<im->gdes_c;gdi++){
/* only GF_CDEF elements are of interest */
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++){
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;
};
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;
/ im->gdes[gdi].step +1)
* sizeof(double)))==NULL){
rrd_set_error("malloc im->gdes[gdi].data");
- free(stack);
+ rpnstack_free(&rpnstack);
return -1;
}
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 ){
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;
/*****************************************************************************
- * 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,
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);
}
/* 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--)
{
--- /dev/null
+/*****************************************************************************
+ * 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);
*****************************************************************************/
#include "rrd_tool.h"
+#include "rrd_rpncalc.h"
#include <stdarg.h>
/* proto */
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);
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);
}
for(ii=0;ii<rrd.stat_head->ds_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:
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;
*****************************************************************************/
#include "rrd_tool.h"
+#include "rrd_rpncalc.h"
/* Prototypes */
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 <BlaBlaBla> to <blablabla> */
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;
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);
/* 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;
}
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, "<value>",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;i<rrd->stat_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, "<value>",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;
&&
(!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))
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;
-
-
-
-
-
-
-
-
-
-
-
-
-
+}
--- /dev/null
+/****************************************************************************
+ * RRDtool 1.0.28 Copyright Tobias Oetiker, 1997 - 2000
+ ****************************************************************************
+ * rrd_rpncalc.c RPN calculator functions
+ ****************************************************************************/
+
+#include "rrd_tool.h"
+#include "rrd_rpncalc.h"
+#include <limits.h>
+
+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;
+}
--- /dev/null
+/****************************************************************************
+ * 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);
* 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 <jakeb@corp.webtv.net>
- *
- * Revision 1.1.1.1 2001/02/25 22:25:06 oetiker
- * checkin
- *
*****************************************************************************/
#ifdef __cplusplus
extern "C" {
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
*****************************************************************************
* $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 <jakeb@corp.webtv.net>
+ *
* 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.
#include "rrd_tool.h"
#include <sys/types.h>
#include <fcntl.h>
+#include "rrd_hw.h"
+#include "rrd_rpncalc.h"
#ifdef WIN32
#include <sys/locking.h>
#include <io.h>
#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));
/* 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[] =
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;
for(i=0;i<rrd.stat_head->ds_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 */
pdp_new[] contains rate*seconds from the latest run.
pdp_temp[] will contain the rate for cdp */
-
for(i=0;i<rrd.stat_head->ds_cnt;i++){
/* update pdp_prep to the current pdp_st */
if(isnan(pdp_new[i]))
- 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;
#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
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 */
}
rra_current = rra_pos_tmp;
}
+
#ifdef DEBUG
fprintf(stderr," -- RRA Postseek %ld\n",ftell(rrd_file));
#endif
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. */
rrd_free(&rrd);
free(pdp_temp);
free(pdp_new);
- fclose(rrd_file);
+ fclose(rrd_file);
return(-1);
}
rrd_free(&rrd);
free(pdp_temp);
free(pdp_new);
- fclose(rrd_file);
+ fclose(rrd_file);
return(-1);
}
free(tmpl_idx);
free(pdp_temp);
free(pdp_new);
- fclose(rrd_file);
+ fclose(rrd_file);
return(-1);
}
free(tmpl_idx);
free(pdp_temp);
free(pdp_new);
- fclose(rrd_file);
+ fclose(rrd_file);
return(-1);
}
rrd_free(&rrd);
free(pdp_temp);
free(pdp_new);
- fclose(rrd_file);
+ fclose(rrd_file);
return(-1);
}
rrd_free(&rrd);
free(pdp_temp);
free(pdp_new);
- fclose(rrd_file);
+ fclose(rrd_file);
return(-1);
}
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)