Merge branch 'collectd-4.8'
[collectd.git] / src / target_scale.c
1 /**
2  * collectd - src/target_scale.c
3  * Copyright (C) 2008-2009  Florian Forster
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; only version 2 of the License is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Authors:
19  *   Florian Forster <octo at verplant.org>
20  **/
21
22 #include "collectd.h"
23 #include "common.h"
24 #include "filter_chain.h"
25
26 #include "utils_cache.h"
27
28 struct ts_data_s
29 {
30         double factor;
31         double offset;
32 };
33 typedef struct ts_data_s ts_data_t;
34
35 static int ts_invoke_counter (const data_set_t *ds, value_list_t *vl, /* {{{ */
36                 ts_data_t *data, int dsrc_index)
37 {
38         uint64_t curr_counter;
39         int status;
40         int failure;
41
42         /* Required meta data */
43         uint64_t prev_counter;
44         char key_prev_counter[128];
45         uint64_t int_counter;
46         char key_int_counter[128];
47         double int_fraction;
48         char key_int_fraction[128];
49
50         curr_counter = (uint64_t) vl->values[dsrc_index].counter;
51
52         ssnprintf (key_prev_counter, sizeof (key_prev_counter),
53                         "target_scale[%p,%i]:prev_counter",
54                         (void *) data, dsrc_index);
55         ssnprintf (key_int_counter, sizeof (key_int_counter),
56                         "target_scale[%p,%i]:int_counter",
57                         (void *) data, dsrc_index);
58         ssnprintf (key_int_fraction, sizeof (key_int_fraction),
59                         "target_scale[%p,%i]:int_fraction",
60                         (void *) data, dsrc_index);
61
62         prev_counter = curr_counter;
63         int_counter = 0;
64         int_fraction = 0.0;
65
66         /* Query the meta data */
67         failure = 0;
68
69         status = uc_meta_data_get_unsigned_int (vl, key_prev_counter,
70                         &prev_counter);
71         if (status != 0)
72                 failure++;
73
74         status = uc_meta_data_get_unsigned_int (vl, key_int_counter, &int_counter);
75         if (status != 0)
76                 failure++;
77
78         status = uc_meta_data_get_double (vl, key_int_fraction, &int_fraction);
79         if (status != 0)
80                 failure++;
81
82         if (failure == 0)
83         {
84                 uint64_t difference;
85                 double rate;
86
87                 /* Calcualte the rate */
88                 if (prev_counter > curr_counter) /* => counter overflow */
89                 {
90                         if (prev_counter <= 4294967295UL) /* 32 bit overflow */
91                                 difference = (4294967295UL - prev_counter) + curr_counter;
92                         else /* 64 bit overflow */
93                                 difference = (18446744073709551615ULL - prev_counter) + curr_counter;
94                 }
95                 else /* no overflow */
96                 {
97                         difference = curr_counter - prev_counter;
98                 }
99                 rate = ((double) difference) / ((double) vl->interval);
100
101                 /* Modify the rate. */
102                 if (!isnan (data->factor))
103                         rate *= data->factor;
104                 if (!isnan (data->offset))
105                         rate += data->offset;
106
107                 /* Calculate the internal counter. */
108                 int_fraction += (rate * ((double) vl->interval));
109                 difference = (uint64_t) int_fraction;
110                 int_fraction -= ((double) difference);
111                 int_counter  += difference;
112
113                 assert (int_fraction >= 0.0);
114                 assert (int_fraction <  1.0);
115
116                 DEBUG ("Target `scale': ts_invoke_counter: %"PRIu64" -> %g -> %"PRIu64
117                                 "(+%g)",
118                                 curr_counter, rate, int_counter, int_fraction);
119         }
120         else /* (failure != 0) */
121         {
122                 int_counter = 0;
123                 int_fraction = 0.0;
124         }
125
126         vl->values[dsrc_index].counter = (counter_t) int_counter;
127
128         /* Update to the new counter value */
129         uc_meta_data_add_unsigned_int (vl, key_prev_counter, curr_counter);
130         uc_meta_data_add_unsigned_int (vl, key_int_counter, int_counter);
131         uc_meta_data_add_double (vl, key_int_fraction, int_fraction);
132
133
134         return (0);
135 } /* }}} int ts_invoke_counter */
136
137 static int ts_invoke_gauge (const data_set_t *ds, value_list_t *vl, /* {{{ */
138                 ts_data_t *data, int dsrc_index)
139 {
140         if (!isnan (data->factor))
141                 vl->values[dsrc_index].gauge *= data->factor;
142         if (!isnan (data->offset))
143                 vl->values[dsrc_index].gauge += data->offset;
144
145         return (0);
146 } /* }}} int ts_invoke_gauge */
147
148 static int ts_invoke_derive (const data_set_t *ds, value_list_t *vl, /* {{{ */
149                 ts_data_t *data, int dsrc_index)
150 {
151         int64_t curr_derive;
152         int status;
153         int failure;
154
155         /* Required meta data */
156         int64_t prev_derive;
157         char key_prev_derive[128];
158         int64_t int_derive;
159         char key_int_derive[128];
160         double int_fraction;
161         char key_int_fraction[128];
162
163         curr_derive = (int64_t) vl->values[dsrc_index].derive;
164
165         ssnprintf (key_prev_derive, sizeof (key_prev_derive),
166                         "target_scale[%p,%i]:prev_derive",
167                         (void *) data, dsrc_index);
168         ssnprintf (key_int_derive, sizeof (key_int_derive),
169                         "target_scale[%p,%i]:int_derive",
170                         (void *) data, dsrc_index);
171         ssnprintf (key_int_fraction, sizeof (key_int_fraction),
172                         "target_scale[%p,%i]:int_fraction",
173                         (void *) data, dsrc_index);
174
175         prev_derive = curr_derive;
176         int_derive = 0;
177         int_fraction = 0.0;
178
179         /* Query the meta data */
180         failure = 0;
181
182         status = uc_meta_data_get_signed_int (vl, key_prev_derive,
183                         &prev_derive);
184         if (status != 0)
185                 failure++;
186
187         status = uc_meta_data_get_signed_int (vl, key_int_derive, &int_derive);
188         if (status != 0)
189                 failure++;
190
191         status = uc_meta_data_get_double (vl, key_int_fraction, &int_fraction);
192         if (status != 0)
193                 failure++;
194
195         if (failure == 0)
196         {
197                 int64_t difference;
198                 double rate;
199
200                 /* Calcualte the rate */
201                 difference = curr_derive - prev_derive;
202                 rate = ((double) difference) / ((double) vl->interval);
203
204                 /* Modify the rate. */
205                 if (!isnan (data->factor))
206                         rate *= data->factor;
207                 if (!isnan (data->offset))
208                         rate += data->offset;
209
210                 /* Calculate the internal derive. */
211                 int_fraction += (rate * ((double) vl->interval));
212                 if (int_fraction < 0.0) /* handle negative integer rounding correctly */
213                         difference = ((int64_t) int_fraction) - 1;
214                 else
215                         difference = (int64_t) int_fraction;
216                 int_fraction -= ((double) difference);
217                 int_derive  += difference;
218
219                 assert (int_fraction >= 0.0);
220                 assert (int_fraction <  1.0);
221
222                 DEBUG ("Target `scale': ts_invoke_derive: %"PRIu64" -> %g -> %"PRIu64
223                                 "(+%g)",
224                                 curr_derive, rate, int_derive, int_fraction);
225         }
226         else /* (failure != 0) */
227         {
228                 int_derive = 0;
229                 int_fraction = 0.0;
230         }
231
232         vl->values[dsrc_index].derive = (derive_t) int_derive;
233
234         /* Update to the new derive value */
235         uc_meta_data_add_signed_int (vl, key_prev_derive, curr_derive);
236         uc_meta_data_add_signed_int (vl, key_int_derive, int_derive);
237         uc_meta_data_add_double (vl, key_int_fraction, int_fraction);
238
239         return (0);
240 } /* }}} int ts_invoke_derive */
241
242 static int ts_invoke_absolute (const data_set_t *ds, value_list_t *vl, /* {{{ */
243                 ts_data_t *data, int dsrc_index)
244 {
245         uint64_t curr_absolute;
246         double rate;
247         int status;
248
249         /* Required meta data */
250         double int_fraction;
251         char key_int_fraction[128];
252
253         curr_absolute = (uint64_t) vl->values[dsrc_index].absolute;
254
255         ssnprintf (key_int_fraction, sizeof (key_int_fraction),
256                         "target_scale[%p,%i]:int_fraction",
257                         (void *) data, dsrc_index);
258
259         int_fraction = 0.0;
260
261         /* Query the meta data */
262         status = uc_meta_data_get_double (vl, key_int_fraction, &int_fraction);
263         if (status != 0)
264                 int_fraction = 0.0;
265
266         rate = ((double) curr_absolute) / ((double) vl->interval);
267
268         /* Modify the rate. */
269         if (!isnan (data->factor))
270                 rate *= data->factor;
271         if (!isnan (data->offset))
272                 rate += data->offset;
273
274         /* Calculate the new absolute. */
275         int_fraction += (rate * ((double) vl->interval));
276         curr_absolute = (uint64_t) int_fraction;
277         int_fraction -= ((double) curr_absolute);
278
279         vl->values[dsrc_index].absolute = (absolute_t) curr_absolute;
280
281         /* Update to the new absolute value */
282         uc_meta_data_add_double (vl, key_int_fraction, int_fraction);
283
284         return (0);
285 } /* }}} int ts_invoke_absolute */
286
287 static int ts_config_set_double (double *ret, oconfig_item_t *ci) /* {{{ */
288 {
289         if ((ci->values_num != 1)
290                         || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
291         {
292                 WARNING ("scale target: The `%s' config option needs "
293                                 "exactly one numeric argument.", ci->key);
294                 return (-1);
295         }
296
297         *ret = ci->values[0].value.number;
298         DEBUG ("ts_config_set_double: *ret = %g", *ret);
299
300         return (0);
301 } /* }}} int ts_config_set_double */
302
303 static int ts_destroy (void **user_data) /* {{{ */
304 {
305         if (user_data == NULL)
306                 return (-EINVAL);
307
308         free (*user_data);
309         *user_data = NULL;
310
311         return (0);
312 } /* }}} int ts_destroy */
313
314 static int ts_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
315 {
316         ts_data_t *data;
317         int status;
318         int i;
319
320         data = (ts_data_t *) malloc (sizeof (*data));
321         if (data == NULL)
322         {
323                 ERROR ("ts_create: malloc failed.");
324                 return (-ENOMEM);
325         }
326         memset (data, 0, sizeof (*data));
327
328         data->factor = NAN;
329         data->offset = NAN;
330
331         status = 0;
332         for (i = 0; i < ci->children_num; i++)
333         {
334                 oconfig_item_t *child = ci->children + i;
335
336                 if (strcasecmp ("Factor", child->key) == 0)
337                                 status = ts_config_set_double (&data->factor, child);
338                 else if (strcasecmp ("Offset", child->key) == 0)
339                                 status = ts_config_set_double (&data->offset, child);
340                 else
341                 {
342                         ERROR ("Target `scale': The `%s' configuration option is not understood "
343                                         "and will be ignored.", child->key);
344                         status = 0;
345                 }
346
347                 if (status != 0)
348                         break;
349         }
350
351         /* Additional sanity-checking */
352         while (status == 0)
353         {
354                 if (isnan (data->factor) && isnan (data->offset))
355                 {
356                         ERROR ("Target `scale': You need to at least set either the `Factor' "
357                                         "or `Offset' option!");
358                         status = -1;
359                 }
360
361                 break;
362         }
363
364         if (status != 0)
365         {
366                 ts_destroy ((void *) &data);
367                 return (status);
368         }
369
370         *user_data = data;
371         return (0);
372 } /* }}} int ts_create */
373
374 static int ts_invoke (const data_set_t *ds, value_list_t *vl, /* {{{ */
375                 notification_meta_t __attribute__((unused)) **meta, void **user_data)
376 {
377         ts_data_t *data;
378         int i;
379
380         if ((ds == NULL) || (vl == NULL) || (user_data == NULL))
381                 return (-EINVAL);
382
383         data = *user_data;
384         if (data == NULL)
385         {
386                 ERROR ("Target `scale': Invoke: `data' is NULL.");
387                 return (-EINVAL);
388         }
389
390         for (i = 0; i < ds->ds_num; i++)
391         {
392                 if (ds->ds[i].type == DS_TYPE_COUNTER)
393                         ts_invoke_counter (ds, vl, data, i);
394                 else if (ds->ds[i].type == DS_TYPE_GAUGE)
395                         ts_invoke_gauge (ds, vl, data, i);
396                 else if (ds->ds[i].type == DS_TYPE_DERIVE)
397                         ts_invoke_derive (ds, vl, data, i);
398                 else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
399                         ts_invoke_absolute (ds, vl, data, i);
400                 else
401                         ERROR ("Target `scale': Ignoring unknown data source type %i",
402                                         ds->ds[i].type);
403         }
404
405         return (FC_TARGET_CONTINUE);
406 } /* }}} int ts_invoke */
407
408 void module_register (void)
409 {
410         target_proc_t tproc;
411
412         memset (&tproc, 0, sizeof (tproc));
413         tproc.create  = ts_create;
414         tproc.destroy = ts_destroy;
415         tproc.invoke  = ts_invoke;
416         fc_register_target ("scale", tproc);
417 } /* module_register */
418
419 /* vim: set sw=2 ts=2 tw=78 fdm=marker : */
420