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 **/
22 #include "collectd.h"
23 #include "common.h"
24 #include "filter_chain.h"
26 #include "utils_cache.h"
28 struct ts_data_s
29 {
30 double factor;
31 double offset;
33 char **data_sources;
34 size_t data_sources_num;
35 };
36 typedef struct ts_data_s ts_data_t;
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;
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];
53 curr_counter = (uint64_t) vl->values[dsrc_index].counter;
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);
65 prev_counter = curr_counter;
66 int_counter = 0;
67 int_fraction = 0.0;
69 /* Query the meta data */
70 failure = 0;
72 status = uc_meta_data_get_unsigned_int (vl, key_prev_counter,
73 &prev_counter);
74 if (status != 0)
75 failure++;
77 status = uc_meta_data_get_unsigned_int (vl, key_int_counter, &int_counter);
78 if (status != 0)
79 failure++;
81 status = uc_meta_data_get_double (vl, key_int_fraction, &int_fraction);
82 if (status != 0)
83 failure++;
85 if (failure == 0)
86 {
87 uint64_t difference;
88 double rate;
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);
104 /* Modify the rate. */
105 if (!isnan (data->factor))
106 rate *= data->factor;
107 if (!isnan (data->offset))
108 rate += data->offset;
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;
116 assert (int_fraction >= 0.0);
117 assert (int_fraction < 1.0);
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 }
129 vl->values[dsrc_index].counter = (counter_t) int_counter;
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);
137 return (0);
138 } /* }}} int ts_invoke_counter */
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;
148 return (0);
149 } /* }}} int ts_invoke_gauge */
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;
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];
166 curr_derive = (int64_t) vl->values[dsrc_index].derive;
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);
178 prev_derive = curr_derive;
179 int_derive = 0;
180 int_fraction = 0.0;
182 /* Query the meta data */
183 failure = 0;
185 status = uc_meta_data_get_signed_int (vl, key_prev_derive,
186 &prev_derive);
187 if (status != 0)
188 failure++;
190 status = uc_meta_data_get_signed_int (vl, key_int_derive, &int_derive);
191 if (status != 0)
192 failure++;
194 status = uc_meta_data_get_double (vl, key_int_fraction, &int_fraction);
195 if (status != 0)
196 failure++;
198 if (failure == 0)
199 {
200 int64_t difference;
201 double rate;
203 /* Calcualte the rate */
204 difference = curr_derive - prev_derive;
205 rate = ((double) difference) / CDTIME_T_TO_DOUBLE (vl->interval);
207 /* Modify the rate. */
208 if (!isnan (data->factor))
209 rate *= data->factor;
210 if (!isnan (data->offset))
211 rate += data->offset;
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;
222 assert (int_fraction >= 0.0);
223 assert (int_fraction < 1.0);
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 }
235 vl->values[dsrc_index].derive = (derive_t) int_derive;
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);
242 return (0);
243 } /* }}} int ts_invoke_derive */
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;
252 /* Required meta data */
253 double int_fraction;
254 char key_int_fraction[128];
256 curr_absolute = (uint64_t) vl->values[dsrc_index].absolute;
258 ssnprintf (key_int_fraction, sizeof (key_int_fraction),
259 "target_scale[%p,%i]:int_fraction",
260 (void *) data, dsrc_index);
262 int_fraction = 0.0;
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;
269 rate = ((double) curr_absolute) / CDTIME_T_TO_DOUBLE (vl->interval);
271 /* Modify the rate. */
272 if (!isnan (data->factor))
273 rate *= data->factor;
274 if (!isnan (data->offset))
275 rate += data->offset;
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);
282 vl->values[dsrc_index].absolute = (absolute_t) curr_absolute;
284 /* Update to the new absolute value */
285 uc_meta_data_add_double (vl, key_int_fraction, int_fraction);
287 return (0);
288 } /* }}} int ts_invoke_absolute */
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 }
300 *ret = ci->values[0].value.number;
301 DEBUG ("ts_config_set_double: *ret = %g", *ret);
303 return (0);
304 } /* }}} int ts_config_set_double */
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;
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 }
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;
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 }
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;
346 /* Copy the strings, allocating memory as needed. */
347 for (i = 0; i < ci->values_num; i++)
348 {
349 size_t j;
351 /* If we get here, there better be memory for us to write to. */
352 assert (data->data_sources_num < new_data_sources_num);
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 }
364 return (0);
365 } /* }}} int ts_config_add_data_source */
367 static int ts_destroy (void **user_data) /* {{{ */
368 {
369 ts_data_t *data;
371 if (user_data == NULL)
372 return (-EINVAL);
374 data = (ts_data_t *) *user_data;
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 }
384 sfree (data);
385 *user_data = NULL;
387 return (0);
388 } /* }}} int ts_destroy */
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;
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));
404 data->factor = NAN;
405 data->offset = NAN;
407 status = 0;
408 for (i = 0; i < ci->children_num; i++)
409 {
410 oconfig_item_t *child = ci->children + i;
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 }
425 if (status != 0)
426 break;
427 }
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 }
439 break;
440 }
442 if (status != 0)
443 {
444 ts_destroy ((void *) &data);
445 return (status);
446 }
448 *user_data = data;
449 return (0);
450 } /* }}} int ts_create */
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;
458 if ((ds == NULL) || (vl == NULL) || (user_data == NULL))
459 return (-EINVAL);
461 data = *user_data;
462 if (data == NULL)
463 {
464 ERROR ("Target `scale': Invoke: `data' is NULL.");
465 return (-EINVAL);
466 }
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;
477 /* No match, ignore */
478 if (j >= data->data_sources_num)
479 continue;
480 }
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 }
495 return (FC_TARGET_CONTINUE);
496 } /* }}} int ts_invoke */
498 void module_register (void)
499 {
500 target_proc_t tproc;
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 */
509 /* vim: set sw=2 ts=2 tw=78 fdm=marker : */