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;
32 };
33 typedef struct ts_data_s ts_data_t;
35 static int ts_invoke_counter (const data_set_t *ds, value_list_t *vl, /* {{{ */
36 ts_data_t *data, int dsrc_index)
37 {
38 uint64_t curr_counter;
39 int status;
40 int failure;
42 /* Required meta data */
43 uint64_t prev_counter;
44 char key_prev_counter[128];
45 uint64_t int_counter;
46 char key_int_counter[128];
47 double int_fraction;
48 char key_int_fraction[128];
50 curr_counter = (uint64_t) vl->values[dsrc_index].counter;
52 ssnprintf (key_prev_counter, sizeof (key_prev_counter),
53 "target_scale[%p,%i]:prev_counter",
54 (void *) data, dsrc_index);
55 ssnprintf (key_int_counter, sizeof (key_int_counter),
56 "target_scale[%p,%i]:int_counter",
57 (void *) data, dsrc_index);
58 ssnprintf (key_int_fraction, sizeof (key_int_fraction),
59 "target_scale[%p,%i]:int_fraction",
60 (void *) data, dsrc_index);
62 prev_counter = curr_counter;
63 int_counter = 0;
64 int_fraction = 0.0;
66 /* Query the meta data */
67 failure = 0;
69 status = uc_meta_data_get_unsigned_int (vl, key_prev_counter,
70 &prev_counter);
71 if (status != 0)
72 failure++;
74 status = uc_meta_data_get_unsigned_int (vl, key_int_counter, &int_counter);
75 if (status != 0)
76 failure++;
78 status = uc_meta_data_get_double (vl, key_int_fraction, &int_fraction);
79 if (status != 0)
80 failure++;
82 if (failure == 0)
83 {
84 uint64_t difference;
85 double rate;
87 /* Calcualte the rate */
88 if (prev_counter > curr_counter) /* => counter overflow */
89 {
90 if (prev_counter <= 4294967295UL) /* 32 bit overflow */
91 difference = (4294967295UL - prev_counter) + curr_counter;
92 else /* 64 bit overflow */
93 difference = (18446744073709551615ULL - prev_counter) + curr_counter;
94 }
95 else /* no overflow */
96 {
97 difference = curr_counter - prev_counter;
98 }
99 rate = ((double) difference) / CDTIME_T_TO_DOUBLE (vl->interval);
101 /* Modify the rate. */
102 if (!isnan (data->factor))
103 rate *= data->factor;
104 if (!isnan (data->offset))
105 rate += data->offset;
107 /* Calculate the internal counter. */
108 int_fraction += (rate * CDTIME_T_TO_DOUBLE (vl->interval));
109 difference = (uint64_t) int_fraction;
110 int_fraction -= ((double) difference);
111 int_counter += difference;
113 assert (int_fraction >= 0.0);
114 assert (int_fraction < 1.0);
116 DEBUG ("Target `scale': ts_invoke_counter: %"PRIu64" -> %g -> %"PRIu64
117 "(+%g)",
118 curr_counter, rate, int_counter, int_fraction);
119 }
120 else /* (failure != 0) */
121 {
122 int_counter = 0;
123 int_fraction = 0.0;
124 }
126 vl->values[dsrc_index].counter = (counter_t) int_counter;
128 /* Update to the new counter value */
129 uc_meta_data_add_unsigned_int (vl, key_prev_counter, curr_counter);
130 uc_meta_data_add_unsigned_int (vl, key_int_counter, int_counter);
131 uc_meta_data_add_double (vl, key_int_fraction, int_fraction);
134 return (0);
135 } /* }}} int ts_invoke_counter */
137 static int ts_invoke_gauge (const data_set_t *ds, value_list_t *vl, /* {{{ */
138 ts_data_t *data, int dsrc_index)
139 {
140 if (!isnan (data->factor))
141 vl->values[dsrc_index].gauge *= data->factor;
142 if (!isnan (data->offset))
143 vl->values[dsrc_index].gauge += data->offset;
145 return (0);
146 } /* }}} int ts_invoke_gauge */
148 static int ts_invoke_derive (const data_set_t *ds, value_list_t *vl, /* {{{ */
149 ts_data_t *data, int dsrc_index)
150 {
151 int64_t curr_derive;
152 int status;
153 int failure;
155 /* Required meta data */
156 int64_t prev_derive;
157 char key_prev_derive[128];
158 int64_t int_derive;
159 char key_int_derive[128];
160 double int_fraction;
161 char key_int_fraction[128];
163 curr_derive = (int64_t) vl->values[dsrc_index].derive;
165 ssnprintf (key_prev_derive, sizeof (key_prev_derive),
166 "target_scale[%p,%i]:prev_derive",
167 (void *) data, dsrc_index);
168 ssnprintf (key_int_derive, sizeof (key_int_derive),
169 "target_scale[%p,%i]:int_derive",
170 (void *) data, dsrc_index);
171 ssnprintf (key_int_fraction, sizeof (key_int_fraction),
172 "target_scale[%p,%i]:int_fraction",
173 (void *) data, dsrc_index);
175 prev_derive = curr_derive;
176 int_derive = 0;
177 int_fraction = 0.0;
179 /* Query the meta data */
180 failure = 0;
182 status = uc_meta_data_get_signed_int (vl, key_prev_derive,
183 &prev_derive);
184 if (status != 0)
185 failure++;
187 status = uc_meta_data_get_signed_int (vl, key_int_derive, &int_derive);
188 if (status != 0)
189 failure++;
191 status = uc_meta_data_get_double (vl, key_int_fraction, &int_fraction);
192 if (status != 0)
193 failure++;
195 if (failure == 0)
196 {
197 int64_t difference;
198 double rate;
200 /* Calcualte the rate */
201 difference = curr_derive - prev_derive;
202 rate = ((double) difference) / CDTIME_T_TO_DOUBLE (vl->interval);
204 /* Modify the rate. */
205 if (!isnan (data->factor))
206 rate *= data->factor;
207 if (!isnan (data->offset))
208 rate += data->offset;
210 /* Calculate the internal derive. */
211 int_fraction += (rate * CDTIME_T_TO_DOUBLE (vl->interval));
212 if (int_fraction < 0.0) /* handle negative integer rounding correctly */
213 difference = ((int64_t) int_fraction) - 1;
214 else
215 difference = (int64_t) int_fraction;
216 int_fraction -= ((double) difference);
217 int_derive += difference;
219 assert (int_fraction >= 0.0);
220 assert (int_fraction < 1.0);
222 DEBUG ("Target `scale': ts_invoke_derive: %"PRIu64" -> %g -> %"PRIu64
223 "(+%g)",
224 curr_derive, rate, int_derive, int_fraction);
225 }
226 else /* (failure != 0) */
227 {
228 int_derive = 0;
229 int_fraction = 0.0;
230 }
232 vl->values[dsrc_index].derive = (derive_t) int_derive;
234 /* Update to the new derive value */
235 uc_meta_data_add_signed_int (vl, key_prev_derive, curr_derive);
236 uc_meta_data_add_signed_int (vl, key_int_derive, int_derive);
237 uc_meta_data_add_double (vl, key_int_fraction, int_fraction);
239 return (0);
240 } /* }}} int ts_invoke_derive */
242 static int ts_invoke_absolute (const data_set_t *ds, value_list_t *vl, /* {{{ */
243 ts_data_t *data, int dsrc_index)
244 {
245 uint64_t curr_absolute;
246 double rate;
247 int status;
249 /* Required meta data */
250 double int_fraction;
251 char key_int_fraction[128];
253 curr_absolute = (uint64_t) vl->values[dsrc_index].absolute;
255 ssnprintf (key_int_fraction, sizeof (key_int_fraction),
256 "target_scale[%p,%i]:int_fraction",
257 (void *) data, dsrc_index);
259 int_fraction = 0.0;
261 /* Query the meta data */
262 status = uc_meta_data_get_double (vl, key_int_fraction, &int_fraction);
263 if (status != 0)
264 int_fraction = 0.0;
266 rate = ((double) curr_absolute) / CDTIME_T_TO_DOUBLE (vl->interval);
268 /* Modify the rate. */
269 if (!isnan (data->factor))
270 rate *= data->factor;
271 if (!isnan (data->offset))
272 rate += data->offset;
274 /* Calculate the new absolute. */
275 int_fraction += (rate * CDTIME_T_TO_DOUBLE (vl->interval));
276 curr_absolute = (uint64_t) int_fraction;
277 int_fraction -= ((double) curr_absolute);
279 vl->values[dsrc_index].absolute = (absolute_t) curr_absolute;
281 /* Update to the new absolute value */
282 uc_meta_data_add_double (vl, key_int_fraction, int_fraction);
284 return (0);
285 } /* }}} int ts_invoke_absolute */
287 static int ts_config_set_double (double *ret, oconfig_item_t *ci) /* {{{ */
288 {
289 if ((ci->values_num != 1)
290 || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
291 {
292 WARNING ("scale target: The `%s' config option needs "
293 "exactly one numeric argument.", ci->key);
294 return (-1);
295 }
297 *ret = ci->values[0].value.number;
298 DEBUG ("ts_config_set_double: *ret = %g", *ret);
300 return (0);
301 } /* }}} int ts_config_set_double */
303 static int ts_destroy (void **user_data) /* {{{ */
304 {
305 ts_data_t **data;
307 if (user_data == NULL)
308 return (-EINVAL);
310 data = (ts_data_t **) user_data;
312 free (*data);
313 *data = NULL;
315 return (0);
316 } /* }}} int ts_destroy */
318 static int ts_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
319 {
320 ts_data_t *data;
321 int status;
322 int i;
324 data = (ts_data_t *) malloc (sizeof (*data));
325 if (data == NULL)
326 {
327 ERROR ("ts_create: malloc failed.");
328 return (-ENOMEM);
329 }
330 memset (data, 0, sizeof (*data));
332 data->factor = NAN;
333 data->offset = NAN;
335 status = 0;
336 for (i = 0; i < ci->children_num; i++)
337 {
338 oconfig_item_t *child = ci->children + i;
340 if (strcasecmp ("Factor", child->key) == 0)
341 status = ts_config_set_double (&data->factor, child);
342 else if (strcasecmp ("Offset", child->key) == 0)
343 status = ts_config_set_double (&data->offset, child);
344 else
345 {
346 ERROR ("Target `scale': The `%s' configuration option is not understood "
347 "and will be ignored.", child->key);
348 status = 0;
349 }
351 if (status != 0)
352 break;
353 }
355 /* Additional sanity-checking */
356 while (status == 0)
357 {
358 if (isnan (data->factor) && isnan (data->offset))
359 {
360 ERROR ("Target `scale': You need to at least set either the `Factor' "
361 "or `Offset' option!");
362 status = -1;
363 }
365 break;
366 }
368 if (status != 0)
369 {
370 ts_destroy ((void *) &data);
371 return (status);
372 }
374 *user_data = data;
375 return (0);
376 } /* }}} int ts_create */
378 static int ts_invoke (const data_set_t *ds, value_list_t *vl, /* {{{ */
379 notification_meta_t __attribute__((unused)) **meta, void **user_data)
380 {
381 ts_data_t *data;
382 int i;
384 if ((ds == NULL) || (vl == NULL) || (user_data == NULL))
385 return (-EINVAL);
387 data = *user_data;
388 if (data == NULL)
389 {
390 ERROR ("Target `scale': Invoke: `data' is NULL.");
391 return (-EINVAL);
392 }
394 for (i = 0; i < ds->ds_num; i++)
395 {
396 if (ds->ds[i].type == DS_TYPE_COUNTER)
397 ts_invoke_counter (ds, vl, data, i);
398 else if (ds->ds[i].type == DS_TYPE_GAUGE)
399 ts_invoke_gauge (ds, vl, data, i);
400 else if (ds->ds[i].type == DS_TYPE_DERIVE)
401 ts_invoke_derive (ds, vl, data, i);
402 else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
403 ts_invoke_absolute (ds, vl, data, i);
404 else
405 ERROR ("Target `scale': Ignoring unknown data source type %i",
406 ds->ds[i].type);
407 }
409 return (FC_TARGET_CONTINUE);
410 } /* }}} int ts_invoke */
412 void module_register (void)
413 {
414 target_proc_t tproc;
416 memset (&tproc, 0, sizeof (tproc));
417 tproc.create = ts_create;
418 tproc.destroy = ts_destroy;
419 tproc.invoke = ts_invoke;
420 fc_register_target ("scale", tproc);
421 } /* module_register */
423 /* vim: set sw=2 ts=2 tw=78 fdm=marker : */