2 * collectd-td - collectd traffic generator
3 * Copyright (C) 2013 Florian octo Forster
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.
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.
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
19 * Florian Forster <ff at octo.it>
37 #include <sys/types.h>
38 #include <sys/socket.h>
42 # define __attribute__(x) /**/
45 #define DEF_NODE "localhost"
46 #define DEF_SERVICE "8125"
48 #define DEF_NUM_COUNTERS 1000
49 #define DEF_NUM_TIMERS 1000
50 #define DEF_NUM_GAUGES 100
51 #define DEF_NUM_SETS 100
52 #define DEF_SET_SIZE 128
54 static int conf_num_counters = DEF_NUM_COUNTERS;
55 static int conf_num_timers = DEF_NUM_TIMERS;
56 static int conf_num_gauges = DEF_NUM_GAUGES;
57 static int conf_num_sets = DEF_NUM_SETS;
58 static int conf_set_size = DEF_SET_SIZE;
59 static const char *conf_node = DEF_NODE;
60 static const char *conf_service = DEF_SERVICE;
62 static int conf_threads_num = 1;
64 static struct sigaction sigint_action;
65 static struct sigaction sigterm_action;
67 static unsigned long long events_sent = 0;
68 pthread_mutex_t events_sent_lock = PTHREAD_MUTEX_INITIALIZER;
69 static _Bool loop = 1;
71 __attribute__((noreturn))
72 static void exit_usage (int exit_status) /* {{{ */
74 fprintf ((exit_status == EXIT_FAILURE) ? stderr : stdout,
75 PACKAGE_NAME" -- statsd traffic generator\n"
77 " Usage: statsd-ng [OPTION]\n"
80 " -c <number> Number of counters to emulate. (Default: %i)\n"
81 " -t <number> Number of timers to emulate. (Default: %i)\n"
82 " -g <number> Number of gauges to emulate. (Default: %i)\n"
83 " -s <number> Number of sets to emulate. (Default: %i)\n"
84 " -S <size> Number of elements in each set. (Default: %i)\n"
85 " -d <dest> Destination address of the network packets.\n"
86 " (Default: "DEF_NODE")\n"
87 " -D <port> Destination port of the network packets.\n"
88 " (Default: "DEF_SERVICE")\n"
89 " -h Print usage information (this output).\n"
91 "Copyright (C) 2013 Florian Forster\n"
92 "Licensed under the GNU General Public License, version 2 (GPLv2)\n",
93 DEF_NUM_COUNTERS, DEF_NUM_TIMERS, DEF_NUM_GAUGES,
94 DEF_NUM_SETS, DEF_SET_SIZE);
96 } /* }}} void exit_usage */
98 static void signal_handler (int signal __attribute__((unused))) /* {{{ */
101 } /* }}} void signal_handler */
103 static int sock_open (void) /* {{{ */
105 struct addrinfo ai_hints;
106 struct addrinfo *ai_list = NULL;
107 struct addrinfo *ai_ptr;
112 memset (&ai_hints, 0, sizeof (ai_hints));
114 ai_hints.ai_flags = AI_ADDRCONFIG;
116 ai_hints.ai_family = AF_UNSPEC;
117 ai_hints.ai_socktype = SOCK_DGRAM;
119 status = getaddrinfo (conf_node, conf_service, &ai_hints, &ai_list);
122 fprintf (stderr, "getaddrinfo failed: %s\n", gai_strerror (status));
126 for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
130 fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
136 status = connect (fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
147 freeaddrinfo (ai_list);
151 fprintf (stderr, "Opening network socket failed.\n");
156 } /* }}} int sock_open */
158 static int send_random_event (int sock, unsigned short seed[static 3]) /* {{{ */
160 long conf_num_total = conf_num_counters + conf_num_timers
161 + conf_num_gauges + conf_num_sets;
162 /* Not completely fair, but good enough for our use-case. */
163 long rnd = nrand48 (seed) % conf_num_total;
165 long value = nrand48 (seed);
172 if (rnd < conf_num_counters)
176 value = (value % 8) + 1;
178 else if (rnd < (conf_num_counters + conf_num_timers))
182 value = (value % 1024) + 1;
184 else if (rnd < (conf_num_counters + conf_num_timers + conf_num_gauges))
188 value = (value % 128) - 64;
194 value %= conf_set_size;
197 buffer_size = snprintf (buffer, sizeof (buffer), "%06li:%li|%s",
199 assert (buffer_size > 0);
200 if (((size_t) buffer_size) >= sizeof (buffer))
202 assert (buffer[buffer_size] == 0);
204 status = send (sock, buffer, (size_t) buffer_size, /* flags = */ 0);
207 fprintf (stderr, "send failed: %s\n", strerror (errno));
212 } /* }}} int send_random_event */
214 static int get_integer_opt (const char *str, int *ret_value) /* {{{ */
221 tmp = (int) strtol (str, &endptr, /* base = */ 0);
224 fprintf (stderr, "Unable to parse option as a number: \"%s\": %s\n",
225 str, strerror (errno));
228 else if (endptr == str)
230 fprintf (stderr, "Unable to parse option as a number: \"%s\"\n", str);
233 else if (*endptr != 0)
235 fprintf (stderr, "Garbage after end of value: \"%s\"\n", str);
241 } /* }}} int get_integer_opt */
243 static int read_options (int argc, char **argv) /* {{{ */
247 #ifdef _SC_NPROCESSORS_ONLN
248 conf_threads_num = (int) sysconf (_SC_NPROCESSORS_ONLN);
251 while ((opt = getopt (argc, argv, "c:t:g:s:S:d:D:h")) != -1)
256 get_integer_opt (optarg, &conf_num_counters);
260 get_integer_opt (optarg, &conf_num_timers);
264 get_integer_opt (optarg, &conf_num_gauges);
268 get_integer_opt (optarg, &conf_num_sets);
272 get_integer_opt (optarg, &conf_set_size);
280 conf_service = optarg;
284 get_integer_opt (optarg, &conf_threads_num);
288 exit_usage (EXIT_SUCCESS);
291 exit_usage (EXIT_FAILURE);
293 } /* while (getopt) */
296 } /* }}} int read_options */
298 static void *send_thread (void *args __attribute__((unused))) /* {{{ */
301 unsigned short seed[3];
304 unsigned long long local_events_sent = 0;
306 clock_gettime (CLOCK_REALTIME, &ts);
307 seed[2] = (unsigned short) (ts.tv_nsec);
308 seed[1] = (unsigned short) (ts.tv_nsec >> 16);
309 seed[0] = (unsigned short) (ts.tv_sec);
315 send_random_event (sock, seed);
321 pthread_mutex_lock (&events_sent_lock);
322 events_sent += local_events_sent;
323 pthread_mutex_unlock (&events_sent_lock);
326 } /* }}} void *send_thread */
328 static void run_threads (void) /* {{{ */
330 pthread_t threads[conf_threads_num];
333 for (i = 0; i < conf_threads_num; i++)
337 status = pthread_create (&threads[i], /* attr = */ NULL,
338 send_thread, /* args = */ NULL);
341 fprintf (stderr, "pthread_create failed.");
346 for (i = 0; i < conf_threads_num; i++)
347 pthread_join (threads[i], /* retval = */ NULL);
348 } /* }}} void run_threads */
350 static double timespec_diff (struct timespec const *ts0, /* {{{ */
351 struct timespec const *ts1)
356 diff_sec = ts1->tv_sec - ts0->tv_sec;
357 diff_nsec += ts1->tv_nsec - ts0->tv_nsec;
359 return ((double) diff_sec) + (((double) diff_nsec) / 1.0e9);
360 } /* }}} double timespec_diff */
362 int main (int argc, char **argv) /* {{{ */
364 struct timespec ts_begin;
365 struct timespec ts_end;
368 read_options (argc, argv);
370 sigint_action.sa_handler = signal_handler;
371 sigaction (SIGINT, &sigint_action, /* old = */ NULL);
373 sigterm_action.sa_handler = signal_handler;
374 sigaction (SIGTERM, &sigterm_action, /* old = */ NULL);
376 clock_gettime (CLOCK_MONOTONIC, &ts_begin);
378 clock_gettime (CLOCK_MONOTONIC, &ts_end);
380 runtime = timespec_diff (&ts_begin, &ts_end);
381 printf ("Sent %llu events in %.0fs (%.0f events/s).\n",
382 events_sent, runtime, ((double) events_sent) / runtime);
388 /* vim: set sw=2 sts=2 et fdm=marker : */