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