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