Merge branch 'collectd-4.10' into collectd-5.0
[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) / CDTIME_T_TO_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 * CDTIME_T_TO_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) / CDTIME_T_TO_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 * CDTIME_T_TO_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) / CDTIME_T_TO_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 * CDTIME_T_TO_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         ts_data_t **data;
306
307         if (user_data == NULL)
308                 return (-EINVAL);
309
310         data = (ts_data_t **) user_data;
311
312         free (*data);
313         *data = NULL;
314
315         return (0);
316 } /* }}} int ts_destroy */
317
318 static int ts_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
319 {
320         ts_data_t *data;
321         int status;
322         int i;
323
324         data = (ts_data_t *) malloc (sizeof (*data));
325         if (data == NULL)
326         {
327                 ERROR ("ts_create: malloc failed.");
328                 return (-ENOMEM);
329         }
330         memset (data, 0, sizeof (*data));
331
332         data->factor = NAN;
333         data->offset = NAN;
334
335         status = 0;
336         for (i = 0; i < ci->children_num; i++)
337         {
338                 oconfig_item_t *child = ci->children + i;
339
340                 if (strcasecmp ("Factor", child->key) == 0)
341                                 status = ts_config_set_double (&data->factor, child);
342                 else if (strcasecmp ("Offset", child->key) == 0)
343                                 status = ts_config_set_double (&data->offset, child);
344                 else
345                 {
346                         ERROR ("Target `scale': The `%s' configuration option is not understood "
347                                         "and will be ignored.", child->key);
348                         status = 0;
349                 }
350
351                 if (status != 0)
352                         break;
353         }
354
355         /* Additional sanity-checking */
356         while (status == 0)
357         {
358                 if (isnan (data->factor) && isnan (data->offset))
359                 {
360                         ERROR ("Target `scale': You need to at least set either the `Factor' "
361                                         "or `Offset' option!");
362                         status = -1;
363                 }
364
365                 break;
366         }
367
368         if (status != 0)
369         {
370                 ts_destroy ((void *) &data);
371                 return (status);
372         }
373
374         *user_data = data;
375         return (0);
376 } /* }}} int ts_create */
377
378 static int ts_invoke (const data_set_t *ds, value_list_t *vl, /* {{{ */
379                 notification_meta_t __attribute__((unused)) **meta, void **user_data)
380 {
381         ts_data_t *data;
382         int i;
383
384         if ((ds == NULL) || (vl == NULL) || (user_data == NULL))
385                 return (-EINVAL);
386
387         data = *user_data;
388         if (data == NULL)
389         {
390                 ERROR ("Target `scale': Invoke: `data' is NULL.");
391                 return (-EINVAL);
392         }
393
394         for (i = 0; i < ds->ds_num; i++)
395         {
396                 if (ds->ds[i].type == DS_TYPE_COUNTER)
397                         ts_invoke_counter (ds, vl, data, i);
398                 else if (ds->ds[i].type == DS_TYPE_GAUGE)
399                         ts_invoke_gauge (ds, vl, data, i);
400                 else if (ds->ds[i].type == DS_TYPE_DERIVE)
401                         ts_invoke_derive (ds, vl, data, i);
402                 else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
403                         ts_invoke_absolute (ds, vl, data, i);
404                 else
405                         ERROR ("Target `scale': Ignoring unknown data source type %i",
406                                         ds->ds[i].type);
407         }
408
409         return (FC_TARGET_CONTINUE);
410 } /* }}} int ts_invoke */
411
412 void module_register (void)
413 {
414         target_proc_t tproc;
415
416         memset (&tproc, 0, sizeof (tproc));
417         tproc.create  = ts_create;
418         tproc.destroy = ts_destroy;
419         tproc.invoke  = ts_invoke;
420         fc_register_target ("scale", tproc);
421 } /* module_register */
422
423 /* vim: set sw=2 ts=2 tw=78 fdm=marker : */
424