2 * collectd - src/target_scale.c
3 * Copyright (C) 2008-2009 Florian Forster
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:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
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.
24 * Florian Forster <octo at collectd.org>
30 #include "filter_chain.h"
32 #include "utils_cache.h"
39 size_t data_sources_num;
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 uint64_t curr_counter;
49 /* Required meta data */
50 uint64_t prev_counter;
51 char key_prev_counter[128];
53 char key_int_counter[128];
55 char key_int_fraction[128];
57 curr_counter = (uint64_t)vl->values[dsrc_index].counter;
59 ssnprintf(key_prev_counter, sizeof(key_prev_counter),
60 "target_scale[%p,%i]:prev_counter", (void *)data, dsrc_index);
61 ssnprintf(key_int_counter, sizeof(key_int_counter),
62 "target_scale[%p,%i]:int_counter", (void *)data, dsrc_index);
63 ssnprintf(key_int_fraction, sizeof(key_int_fraction),
64 "target_scale[%p,%i]:int_fraction", (void *)data, dsrc_index);
66 prev_counter = curr_counter;
70 /* Query the meta data */
73 status = uc_meta_data_get_unsigned_int(vl, key_prev_counter, &prev_counter);
77 status = uc_meta_data_get_unsigned_int(vl, key_int_counter, &int_counter);
81 status = uc_meta_data_get_double(vl, key_int_fraction, &int_fraction);
89 diff = (uint64_t)counter_diff(prev_counter, curr_counter);
90 rate = ((double)diff) / CDTIME_T_TO_DOUBLE(vl->interval);
92 /* Modify the rate. */
93 if (!isnan(data->factor))
95 if (!isnan(data->offset))
98 /* Calculate the internal counter. */
99 int_fraction += (rate * CDTIME_T_TO_DOUBLE(vl->interval));
100 diff = (uint64_t)int_fraction;
101 int_fraction -= ((double)diff);
104 assert(int_fraction >= 0.0);
105 assert(int_fraction < 1.0);
107 DEBUG("Target `scale': ts_invoke_counter: %" PRIu64 " -> %g -> %" PRIu64
109 curr_counter, rate, int_counter, int_fraction);
110 } else /* (failure != 0) */
116 vl->values[dsrc_index].counter = (counter_t)int_counter;
118 /* Update to the new counter value */
119 uc_meta_data_add_unsigned_int(vl, key_prev_counter, curr_counter);
120 uc_meta_data_add_unsigned_int(vl, key_int_counter, int_counter);
121 uc_meta_data_add_double(vl, key_int_fraction, int_fraction);
124 } /* }}} int ts_invoke_counter */
126 static int ts_invoke_gauge(const data_set_t *ds, value_list_t *vl, /* {{{ */
127 ts_data_t *data, int dsrc_index) {
128 if (!isnan(data->factor))
129 vl->values[dsrc_index].gauge *= data->factor;
130 if (!isnan(data->offset))
131 vl->values[dsrc_index].gauge += data->offset;
134 } /* }}} int ts_invoke_gauge */
136 static int ts_invoke_derive(const data_set_t *ds, value_list_t *vl, /* {{{ */
137 ts_data_t *data, int dsrc_index) {
142 /* Required meta data */
144 char key_prev_derive[128];
146 char key_int_derive[128];
148 char key_int_fraction[128];
150 curr_derive = (int64_t)vl->values[dsrc_index].derive;
152 ssnprintf(key_prev_derive, sizeof(key_prev_derive),
153 "target_scale[%p,%i]:prev_derive", (void *)data, dsrc_index);
154 ssnprintf(key_int_derive, sizeof(key_int_derive),
155 "target_scale[%p,%i]:int_derive", (void *)data, dsrc_index);
156 ssnprintf(key_int_fraction, sizeof(key_int_fraction),
157 "target_scale[%p,%i]:int_fraction", (void *)data, dsrc_index);
159 prev_derive = curr_derive;
163 /* Query the meta data */
166 status = uc_meta_data_get_signed_int(vl, key_prev_derive, &prev_derive);
170 status = uc_meta_data_get_signed_int(vl, key_int_derive, &int_derive);
174 status = uc_meta_data_get_double(vl, key_int_fraction, &int_fraction);
182 /* Calcualte the rate */
183 difference = curr_derive - prev_derive;
184 rate = ((double)difference) / CDTIME_T_TO_DOUBLE(vl->interval);
186 /* Modify the rate. */
187 if (!isnan(data->factor))
188 rate *= data->factor;
189 if (!isnan(data->offset))
190 rate += data->offset;
192 /* Calculate the internal derive. */
193 int_fraction += (rate * CDTIME_T_TO_DOUBLE(vl->interval));
194 if (int_fraction < 0.0) /* handle negative integer rounding correctly */
195 difference = ((int64_t)int_fraction) - 1;
197 difference = (int64_t)int_fraction;
198 int_fraction -= ((double)difference);
199 int_derive += difference;
201 assert(int_fraction >= 0.0);
202 assert(int_fraction < 1.0);
204 DEBUG("Target `scale': ts_invoke_derive: %" PRIu64 " -> %g -> %" PRIu64
206 curr_derive, rate, int_derive, int_fraction);
207 } else /* (failure != 0) */
213 vl->values[dsrc_index].derive = (derive_t)int_derive;
215 /* Update to the new derive value */
216 uc_meta_data_add_signed_int(vl, key_prev_derive, curr_derive);
217 uc_meta_data_add_signed_int(vl, key_int_derive, int_derive);
218 uc_meta_data_add_double(vl, key_int_fraction, int_fraction);
221 } /* }}} int ts_invoke_derive */
223 static int ts_invoke_absolute(const data_set_t *ds, value_list_t *vl, /* {{{ */
224 ts_data_t *data, int dsrc_index) {
225 uint64_t curr_absolute;
229 /* Required meta data */
231 char key_int_fraction[128];
233 curr_absolute = (uint64_t)vl->values[dsrc_index].absolute;
235 ssnprintf(key_int_fraction, sizeof(key_int_fraction),
236 "target_scale[%p,%i]:int_fraction", (void *)data, dsrc_index);
240 /* Query the meta data */
241 status = uc_meta_data_get_double(vl, key_int_fraction, &int_fraction);
245 rate = ((double)curr_absolute) / CDTIME_T_TO_DOUBLE(vl->interval);
247 /* Modify the rate. */
248 if (!isnan(data->factor))
249 rate *= data->factor;
250 if (!isnan(data->offset))
251 rate += data->offset;
253 /* Calculate the new absolute. */
254 int_fraction += (rate * CDTIME_T_TO_DOUBLE(vl->interval));
255 curr_absolute = (uint64_t)int_fraction;
256 int_fraction -= ((double)curr_absolute);
258 vl->values[dsrc_index].absolute = (absolute_t)curr_absolute;
260 /* Update to the new absolute value */
261 uc_meta_data_add_double(vl, key_int_fraction, int_fraction);
264 } /* }}} int ts_invoke_absolute */
266 static int ts_config_set_double(double *ret, oconfig_item_t *ci) /* {{{ */
268 if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) {
269 WARNING("scale target: The `%s' config option needs "
270 "exactly one numeric argument.",
275 *ret = ci->values[0].value.number;
276 DEBUG("ts_config_set_double: *ret = %g", *ret);
279 } /* }}} int ts_config_set_double */
281 static int ts_config_add_data_source(ts_data_t *data, /* {{{ */
282 oconfig_item_t *ci) {
283 size_t new_data_sources_num;
286 /* Check number of arbuments. */
287 if (ci->values_num < 1) {
288 ERROR("`value' match: `%s' needs at least one argument.", ci->key);
292 /* Check type of arguments */
293 for (int i = 0; i < ci->values_num; i++) {
294 if (ci->values[i].type == OCONFIG_TYPE_STRING)
297 ERROR("`value' match: `%s' accepts only string arguments "
298 "(argument %i is a %s).",
300 (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) ? "truth value"
305 /* Allocate space for the char pointers */
306 new_data_sources_num = data->data_sources_num + ((size_t)ci->values_num);
307 temp = realloc(data->data_sources, new_data_sources_num * sizeof(char *));
309 ERROR("`value' match: realloc failed.");
312 data->data_sources = temp;
314 /* Copy the strings, allocating memory as needed. */
315 for (int i = 0; i < ci->values_num; i++) {
318 /* If we get here, there better be memory for us to write to. */
319 assert(data->data_sources_num < new_data_sources_num);
321 j = data->data_sources_num;
322 data->data_sources[j] = sstrdup(ci->values[i].value.string);
323 if (data->data_sources[j] == NULL) {
324 ERROR("`value' match: sstrdup failed.");
327 data->data_sources_num++;
331 } /* }}} int ts_config_add_data_source */
333 static int ts_destroy(void **user_data) /* {{{ */
337 if (user_data == NULL)
340 data = (ts_data_t *)*user_data;
342 if ((data != NULL) && (data->data_sources != NULL)) {
343 for (size_t i = 0; i < data->data_sources_num; i++)
344 sfree(data->data_sources[i]);
345 sfree(data->data_sources);
352 } /* }}} int ts_destroy */
354 static int ts_create(const oconfig_item_t *ci, void **user_data) /* {{{ */
359 data = calloc(1, sizeof(*data));
361 ERROR("ts_create: calloc failed.");
369 for (int i = 0; i < ci->children_num; i++) {
370 oconfig_item_t *child = ci->children + i;
372 if (strcasecmp("Factor", child->key) == 0)
373 status = ts_config_set_double(&data->factor, child);
374 else if (strcasecmp("Offset", child->key) == 0)
375 status = ts_config_set_double(&data->offset, child);
376 else if (strcasecmp("DataSource", child->key) == 0)
377 status = ts_config_add_data_source(data, child);
379 ERROR("Target `scale': The `%s' configuration option is not understood "
380 "and will be ignored.",
389 /* Additional sanity-checking */
390 while (status == 0) {
391 if (isnan(data->factor) && isnan(data->offset)) {
392 ERROR("Target `scale': You need to at least set either the `Factor' "
393 "or `Offset' option!");
401 ts_destroy((void *)&data);
407 } /* }}} int ts_create */
409 static int ts_invoke(const data_set_t *ds, value_list_t *vl, /* {{{ */
410 notification_meta_t __attribute__((unused)) * *meta,
414 if ((ds == NULL) || (vl == NULL) || (user_data == NULL))
419 ERROR("Target `scale': Invoke: `data' is NULL.");
423 for (size_t i = 0; i < ds->ds_num; i++) {
424 /* If we've got a list of data sources, is it in the list? */
425 if (data->data_sources) {
427 for (j = 0; j < data->data_sources_num; j++)
428 if (strcasecmp(ds->ds[i].name, data->data_sources[j]) == 0)
431 /* No match, ignore */
432 if (j >= data->data_sources_num)
436 if (ds->ds[i].type == DS_TYPE_COUNTER)
437 ts_invoke_counter(ds, vl, data, i);
438 else if (ds->ds[i].type == DS_TYPE_GAUGE)
439 ts_invoke_gauge(ds, vl, data, i);
440 else if (ds->ds[i].type == DS_TYPE_DERIVE)
441 ts_invoke_derive(ds, vl, data, i);
442 else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
443 ts_invoke_absolute(ds, vl, data, i);
445 ERROR("Target `scale': Ignoring unknown data source type %i",
449 return (FC_TARGET_CONTINUE);
450 } /* }}} int ts_invoke */
452 void module_register(void) {
453 target_proc_t tproc = {0};
455 tproc.create = ts_create;
456 tproc.destroy = ts_destroy;
457 tproc.invoke = ts_invoke;
458 fc_register_target("scale", tproc);
459 } /* module_register */