bef03e58769cd147ee6fcf0efe8f15cf87f46c43
[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         char **data_sources;
34         size_t data_sources_num;
35 };
36 typedef struct ts_data_s ts_data_t;
37
38 static int ts_invoke_counter (const data_set_t *ds, value_list_t *vl, /* {{{ */
39                 ts_data_t *data, int dsrc_index)
40 {
41         uint64_t curr_counter;
42         int status;
43         int failure;
44
45         /* Required meta data */
46         uint64_t prev_counter;
47         char key_prev_counter[128];
48         uint64_t int_counter;
49         char key_int_counter[128];
50         double int_fraction;
51         char key_int_fraction[128];
52
53         curr_counter = (uint64_t) vl->values[dsrc_index].counter;
54
55         ssnprintf (key_prev_counter, sizeof (key_prev_counter),
56                         "target_scale[%p,%i]:prev_counter",
57                         (void *) data, dsrc_index);
58         ssnprintf (key_int_counter, sizeof (key_int_counter),
59                         "target_scale[%p,%i]:int_counter",
60                         (void *) data, dsrc_index);
61         ssnprintf (key_int_fraction, sizeof (key_int_fraction),
62                         "target_scale[%p,%i]:int_fraction",
63                         (void *) data, dsrc_index);
64
65         prev_counter = curr_counter;
66         int_counter = 0;
67         int_fraction = 0.0;
68
69         /* Query the meta data */
70         failure = 0;
71
72         status = uc_meta_data_get_unsigned_int (vl, key_prev_counter,
73                         &prev_counter);
74         if (status != 0)
75                 failure++;
76
77         status = uc_meta_data_get_unsigned_int (vl, key_int_counter, &int_counter);
78         if (status != 0)
79                 failure++;
80
81         status = uc_meta_data_get_double (vl, key_int_fraction, &int_fraction);
82         if (status != 0)
83                 failure++;
84
85         if (failure == 0)
86         {
87                 uint64_t difference;
88                 double rate;
89
90                 /* Calcualte the rate */
91                 if (prev_counter > curr_counter) /* => counter overflow */
92                 {
93                         if (prev_counter <= 4294967295UL) /* 32 bit overflow */
94                                 difference = (4294967295UL - prev_counter) + curr_counter;
95                         else /* 64 bit overflow */
96                                 difference = (18446744073709551615ULL - prev_counter) + curr_counter;
97                 }
98                 else /* no overflow */
99                 {
100                         difference = curr_counter - prev_counter;
101                 }
102                 rate = ((double) difference) / CDTIME_T_TO_DOUBLE (vl->interval);
103
104                 /* Modify the rate. */
105                 if (!isnan (data->factor))
106                         rate *= data->factor;
107                 if (!isnan (data->offset))
108                         rate += data->offset;
109
110                 /* Calculate the internal counter. */
111                 int_fraction += (rate * CDTIME_T_TO_DOUBLE (vl->interval));
112                 difference = (uint64_t) int_fraction;
113                 int_fraction -= ((double) difference);
114                 int_counter  += difference;
115
116                 assert (int_fraction >= 0.0);
117                 assert (int_fraction <  1.0);
118
119                 DEBUG ("Target `scale': ts_invoke_counter: %"PRIu64" -> %g -> %"PRIu64
120                                 "(+%g)",
121                                 curr_counter, rate, int_counter, int_fraction);
122         }
123         else /* (failure != 0) */
124         {
125                 int_counter = 0;
126                 int_fraction = 0.0;
127         }
128
129         vl->values[dsrc_index].counter = (counter_t) int_counter;
130
131         /* Update to the new counter value */
132         uc_meta_data_add_unsigned_int (vl, key_prev_counter, curr_counter);
133         uc_meta_data_add_unsigned_int (vl, key_int_counter, int_counter);
134         uc_meta_data_add_double (vl, key_int_fraction, int_fraction);
135
136
137         return (0);
138 } /* }}} int ts_invoke_counter */
139
140 static int ts_invoke_gauge (const data_set_t *ds, value_list_t *vl, /* {{{ */
141                 ts_data_t *data, int dsrc_index)
142 {
143         if (!isnan (data->factor))
144                 vl->values[dsrc_index].gauge *= data->factor;
145         if (!isnan (data->offset))
146                 vl->values[dsrc_index].gauge += data->offset;
147
148         return (0);
149 } /* }}} int ts_invoke_gauge */
150
151 static int ts_invoke_derive (const data_set_t *ds, value_list_t *vl, /* {{{ */
152                 ts_data_t *data, int dsrc_index)
153 {
154         int64_t curr_derive;
155         int status;
156         int failure;
157
158         /* Required meta data */
159         int64_t prev_derive;
160         char key_prev_derive[128];
161         int64_t int_derive;
162         char key_int_derive[128];
163         double int_fraction;
164         char key_int_fraction[128];
165
166         curr_derive = (int64_t) vl->values[dsrc_index].derive;
167
168         ssnprintf (key_prev_derive, sizeof (key_prev_derive),
169                         "target_scale[%p,%i]:prev_derive",
170                         (void *) data, dsrc_index);
171         ssnprintf (key_int_derive, sizeof (key_int_derive),
172                         "target_scale[%p,%i]:int_derive",
173                         (void *) data, dsrc_index);
174         ssnprintf (key_int_fraction, sizeof (key_int_fraction),
175                         "target_scale[%p,%i]:int_fraction",
176                         (void *) data, dsrc_index);
177
178         prev_derive = curr_derive;
179         int_derive = 0;
180         int_fraction = 0.0;
181
182         /* Query the meta data */
183         failure = 0;
184
185         status = uc_meta_data_get_signed_int (vl, key_prev_derive,
186                         &prev_derive);
187         if (status != 0)
188                 failure++;
189
190         status = uc_meta_data_get_signed_int (vl, key_int_derive, &int_derive);
191         if (status != 0)
192                 failure++;
193
194         status = uc_meta_data_get_double (vl, key_int_fraction, &int_fraction);
195         if (status != 0)
196                 failure++;
197
198         if (failure == 0)
199         {
200                 int64_t difference;
201                 double rate;
202
203                 /* Calcualte the rate */
204                 difference = curr_derive - prev_derive;
205                 rate = ((double) difference) / CDTIME_T_TO_DOUBLE (vl->interval);
206
207                 /* Modify the rate. */
208                 if (!isnan (data->factor))
209                         rate *= data->factor;
210                 if (!isnan (data->offset))
211                         rate += data->offset;
212
213                 /* Calculate the internal derive. */
214                 int_fraction += (rate * CDTIME_T_TO_DOUBLE (vl->interval));
215                 if (int_fraction < 0.0) /* handle negative integer rounding correctly */
216                         difference = ((int64_t) int_fraction) - 1;
217                 else
218                         difference = (int64_t) int_fraction;
219                 int_fraction -= ((double) difference);
220                 int_derive  += difference;
221
222                 assert (int_fraction >= 0.0);
223                 assert (int_fraction <  1.0);
224
225                 DEBUG ("Target `scale': ts_invoke_derive: %"PRIu64" -> %g -> %"PRIu64
226                                 "(+%g)",
227                                 curr_derive, rate, int_derive, int_fraction);
228         }
229         else /* (failure != 0) */
230         {
231                 int_derive = 0;
232                 int_fraction = 0.0;
233         }
234
235         vl->values[dsrc_index].derive = (derive_t) int_derive;
236
237         /* Update to the new derive value */
238         uc_meta_data_add_signed_int (vl, key_prev_derive, curr_derive);
239         uc_meta_data_add_signed_int (vl, key_int_derive, int_derive);
240         uc_meta_data_add_double (vl, key_int_fraction, int_fraction);
241
242         return (0);
243 } /* }}} int ts_invoke_derive */
244
245 static int ts_invoke_absolute (const data_set_t *ds, value_list_t *vl, /* {{{ */
246                 ts_data_t *data, int dsrc_index)
247 {
248         uint64_t curr_absolute;
249         double rate;
250         int status;
251
252         /* Required meta data */
253         double int_fraction;
254         char key_int_fraction[128];
255
256         curr_absolute = (uint64_t) vl->values[dsrc_index].absolute;
257
258         ssnprintf (key_int_fraction, sizeof (key_int_fraction),
259                         "target_scale[%p,%i]:int_fraction",
260                         (void *) data, dsrc_index);
261
262         int_fraction = 0.0;
263
264         /* Query the meta data */
265         status = uc_meta_data_get_double (vl, key_int_fraction, &int_fraction);
266         if (status != 0)
267                 int_fraction = 0.0;
268
269         rate = ((double) curr_absolute) / CDTIME_T_TO_DOUBLE (vl->interval);
270
271         /* Modify the rate. */
272         if (!isnan (data->factor))
273                 rate *= data->factor;
274         if (!isnan (data->offset))
275                 rate += data->offset;
276
277         /* Calculate the new absolute. */
278         int_fraction += (rate * CDTIME_T_TO_DOUBLE (vl->interval));
279         curr_absolute = (uint64_t) int_fraction;
280         int_fraction -= ((double) curr_absolute);
281
282         vl->values[dsrc_index].absolute = (absolute_t) curr_absolute;
283
284         /* Update to the new absolute value */
285         uc_meta_data_add_double (vl, key_int_fraction, int_fraction);
286
287         return (0);
288 } /* }}} int ts_invoke_absolute */
289
290 static int ts_config_set_double (double *ret, oconfig_item_t *ci) /* {{{ */
291 {
292         if ((ci->values_num != 1)
293                         || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
294         {
295                 WARNING ("scale target: The `%s' config option needs "
296                                 "exactly one numeric argument.", ci->key);
297                 return (-1);
298         }
299
300         *ret = ci->values[0].value.number;
301         DEBUG ("ts_config_set_double: *ret = %g", *ret);
302
303         return (0);
304 } /* }}} int ts_config_set_double */
305
306 static int ts_config_add_data_source(ts_data_t *data, /* {{{ */
307                 oconfig_item_t *ci)
308 {
309         size_t new_data_sources_num;
310         char **temp;
311         int i;
312
313         /* Check number of arbuments. */
314         if (ci->values_num < 1)
315         {
316                 ERROR ("`value' match: `%s' needs at least one argument.",
317                                 ci->key);
318                 return (-1);
319         }
320
321         /* Check type of arguments */
322         for (i = 0; i < ci->values_num; i++)
323         {
324                 if (ci->values[i].type == OCONFIG_TYPE_STRING)
325                         continue;
326
327                 ERROR ("`value' match: `%s' accepts only string arguments "
328                                 "(argument %i is a %s).",
329                                 ci->key, i + 1,
330                                 (ci->values[i].type == OCONFIG_TYPE_BOOLEAN)
331                                 ? "truth value" : "number");
332                 return (-1);
333         }
334
335         /* Allocate space for the char pointers */
336         new_data_sources_num = data->data_sources_num + ((size_t) ci->values_num);
337         temp = (char **) realloc (data->data_sources,
338                         new_data_sources_num * sizeof (char *));
339         if (temp == NULL)
340         {
341                 ERROR ("`value' match: realloc failed.");
342                 return (-1);
343         }
344         data->data_sources = temp;
345
346         /* Copy the strings, allocating memory as needed.  */
347         for (i = 0; i < ci->values_num; i++)
348         {
349                 size_t j;
350
351                 /* If we get here, there better be memory for us to write to.  */
352                 assert (data->data_sources_num < new_data_sources_num);
353
354                 j = data->data_sources_num;
355                 data->data_sources[j] = sstrdup (ci->values[i].value.string);
356                 if (data->data_sources[j] == NULL)
357                 {
358                         ERROR ("`value' match: sstrdup failed.");
359                         continue;
360                 }
361                 data->data_sources_num++;
362         }
363
364         return (0);
365 } /* }}} int ts_config_add_data_source */
366
367 static int ts_destroy (void **user_data) /* {{{ */
368 {
369         ts_data_t *data;
370
371         if (user_data == NULL)
372                 return (-EINVAL);
373
374         data = (ts_data_t *) *user_data;
375
376         if ((data != NULL) && (data->data_sources != NULL))
377         {
378                 size_t i;
379                 for (i = 0; i < data->data_sources_num; i++)
380                         sfree (data->data_sources[i]);
381                 sfree (data->data_sources);
382         }
383
384         sfree (data);
385         *user_data = NULL;
386
387         return (0);
388 } /* }}} int ts_destroy */
389
390 static int ts_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
391 {
392         ts_data_t *data;
393         int status;
394         int i;
395
396         data = (ts_data_t *) malloc (sizeof (*data));
397         if (data == NULL)
398         {
399                 ERROR ("ts_create: malloc failed.");
400                 return (-ENOMEM);
401         }
402         memset (data, 0, sizeof (*data));
403
404         data->factor = NAN;
405         data->offset = NAN;
406
407         status = 0;
408         for (i = 0; i < ci->children_num; i++)
409         {
410                 oconfig_item_t *child = ci->children + i;
411
412                 if (strcasecmp ("Factor", child->key) == 0)
413                                 status = ts_config_set_double (&data->factor, child);
414                 else if (strcasecmp ("Offset", child->key) == 0)
415                                 status = ts_config_set_double (&data->offset, child);
416                 else if (strcasecmp ("DataSource", child->key) == 0)
417                                 status = ts_config_add_data_source(data, child);
418                 else
419                 {
420                         ERROR ("Target `scale': The `%s' configuration option is not understood "
421                                         "and will be ignored.", child->key);
422                         status = 0;
423                 }
424
425                 if (status != 0)
426                         break;
427         }
428
429         /* Additional sanity-checking */
430         while (status == 0)
431         {
432                 if (isnan (data->factor) && isnan (data->offset))
433                 {
434                         ERROR ("Target `scale': You need to at least set either the `Factor' "
435                                         "or `Offset' option!");
436                         status = -1;
437                 }
438
439                 break;
440         }
441
442         if (status != 0)
443         {
444                 ts_destroy ((void *) &data);
445                 return (status);
446         }
447
448         *user_data = data;
449         return (0);
450 } /* }}} int ts_create */
451
452 static int ts_invoke (const data_set_t *ds, value_list_t *vl, /* {{{ */
453                 notification_meta_t __attribute__((unused)) **meta, void **user_data)
454 {
455         ts_data_t *data;
456         int i;
457
458         if ((ds == NULL) || (vl == NULL) || (user_data == NULL))
459                 return (-EINVAL);
460
461         data = *user_data;
462         if (data == NULL)
463         {
464                 ERROR ("Target `scale': Invoke: `data' is NULL.");
465                 return (-EINVAL);
466         }
467
468         for (i = 0; i < ds->ds_num; i++)
469         {
470                 /* If we've got a list of data sources, is it in the list? */
471                 if (data->data_sources) {
472                         size_t j;
473                         for (j = 0; j < data->data_sources_num; j++)
474                                 if (strcasecmp(ds->ds[i].name, data->data_sources[j]) == 0)
475                                         break;
476
477                         /* No match, ignore */
478                         if (j >= data->data_sources_num)
479                                 continue;
480                 }
481
482                 if (ds->ds[i].type == DS_TYPE_COUNTER)
483                         ts_invoke_counter (ds, vl, data, i);
484                 else if (ds->ds[i].type == DS_TYPE_GAUGE)
485                         ts_invoke_gauge (ds, vl, data, i);
486                 else if (ds->ds[i].type == DS_TYPE_DERIVE)
487                         ts_invoke_derive (ds, vl, data, i);
488                 else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
489                         ts_invoke_absolute (ds, vl, data, i);
490                 else
491                         ERROR ("Target `scale': Ignoring unknown data source type %i",
492                                         ds->ds[i].type);
493         }
494
495         return (FC_TARGET_CONTINUE);
496 } /* }}} int ts_invoke */
497
498 void module_register (void)
499 {
500         target_proc_t tproc;
501
502         memset (&tproc, 0, sizeof (tproc));
503         tproc.create  = ts_create;
504         tproc.destroy = ts_destroy;
505         tproc.invoke  = ts_invoke;
506         fc_register_target ("scale", tproc);
507 } /* module_register */
508
509 /* vim: set sw=2 ts=2 tw=78 fdm=marker : */
510