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 " -T <threads> Number of threads to use to generate load.\n"
90 " -h Print usage information (this output).\n"
92 "Copyright (C) 2013 Florian Forster\n"
93 "Licensed under the GNU General Public License, version 2 (GPLv2)\n",
94 DEF_NUM_COUNTERS, DEF_NUM_TIMERS, DEF_NUM_GAUGES,
95 DEF_NUM_SETS, DEF_SET_SIZE);
97 } /* }}} void exit_usage */
99 static void signal_handler (int signal __attribute__((unused))) /* {{{ */
102 } /* }}} void signal_handler */
104 static int sock_open (void) /* {{{ */
106 struct addrinfo ai_hints;
107 struct addrinfo *ai_list = NULL;
108 struct addrinfo *ai_ptr;
113 memset (&ai_hints, 0, sizeof (ai_hints));
115 ai_hints.ai_flags = AI_ADDRCONFIG;
117 ai_hints.ai_family = AF_UNSPEC;
118 ai_hints.ai_socktype = SOCK_DGRAM;
120 status = getaddrinfo (conf_node, conf_service, &ai_hints, &ai_list);
123 fprintf (stderr, "getaddrinfo failed: %s\n", gai_strerror (status));
127 for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
131 fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
137 status = connect (fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
148 freeaddrinfo (ai_list);
152 fprintf (stderr, "Opening network socket failed.\n");
157 } /* }}} int sock_open */
159 static int send_random_event (int sock, unsigned short seed[static 3]) /* {{{ */
161 long conf_num_total = conf_num_counters + conf_num_timers
162 + conf_num_gauges + conf_num_sets;
163 /* Not completely fair, but good enough for our use-case. */
164 long rnd = nrand48 (seed) % conf_num_total;
166 long value = nrand48 (seed);
173 if (rnd < conf_num_counters)
177 value = (value % 8) + 1;
179 else if (rnd < (conf_num_counters + conf_num_timers))
183 value = (value % 1024) + 1;
185 else if (rnd < (conf_num_counters + conf_num_timers + conf_num_gauges))
189 value = (value % 128) - 64;
195 value %= conf_set_size;
198 buffer_size = snprintf (buffer, sizeof (buffer), "%06li:%li|%s",
200 assert (buffer_size > 0);
201 if (((size_t) buffer_size) >= sizeof (buffer))
203 assert (buffer[buffer_size] == 0);
205 status = send (sock, buffer, (size_t) buffer_size, /* flags = */ 0);
208 fprintf (stderr, "send failed: %s\n", strerror (errno));
213 } /* }}} int send_random_event */
215 static int get_integer_opt (const char *str, int *ret_value) /* {{{ */
222 tmp = (int) strtol (str, &endptr, /* base = */ 0);
225 fprintf (stderr, "Unable to parse option as a number: \"%s\": %s\n",
226 str, strerror (errno));
229 else if (endptr == str)
231 fprintf (stderr, "Unable to parse option as a number: \"%s\"\n", str);
234 else if (*endptr != 0)
236 fprintf (stderr, "Garbage after end of value: \"%s\"\n", str);
242 } /* }}} int get_integer_opt */
244 static int read_options (int argc, char **argv) /* {{{ */
248 #ifdef _SC_NPROCESSORS_ONLN
249 conf_threads_num = (int) sysconf (_SC_NPROCESSORS_ONLN);
252 while ((opt = getopt (argc, argv, "c:t:g:s:S:d:D:T:h")) != -1)
257 get_integer_opt (optarg, &conf_num_counters);
261 get_integer_opt (optarg, &conf_num_timers);
265 get_integer_opt (optarg, &conf_num_gauges);
269 get_integer_opt (optarg, &conf_num_sets);
273 get_integer_opt (optarg, &conf_set_size);
281 conf_service = optarg;
285 get_integer_opt (optarg, &conf_threads_num);
289 exit_usage (EXIT_SUCCESS);
292 exit_usage (EXIT_FAILURE);
294 } /* while (getopt) */
297 } /* }}} int read_options */
299 static void *send_thread (void *args __attribute__((unused))) /* {{{ */
302 unsigned short seed[3];
305 unsigned long long local_events_sent = 0;
307 clock_gettime (CLOCK_REALTIME, &ts);
308 seed[2] = (unsigned short) (ts.tv_nsec);
309 seed[1] = (unsigned short) (ts.tv_nsec >> 16);
310 seed[0] = (unsigned short) (ts.tv_sec);
316 send_random_event (sock, seed);
322 pthread_mutex_lock (&events_sent_lock);
323 events_sent += local_events_sent;
324 pthread_mutex_unlock (&events_sent_lock);
327 } /* }}} void *send_thread */
329 static void run_threads (void) /* {{{ */
331 pthread_t threads[conf_threads_num];
334 for (i = 0; i < conf_threads_num; i++)
338 status = pthread_create (&threads[i], /* attr = */ NULL,
339 send_thread, /* args = */ NULL);
342 fprintf (stderr, "pthread_create failed.");
347 for (i = 0; i < conf_threads_num; i++)
348 pthread_join (threads[i], /* retval = */ NULL);
349 } /* }}} void run_threads */
351 static double timespec_diff (struct timespec const *ts0, /* {{{ */
352 struct timespec const *ts1)
357 diff_sec = ts1->tv_sec - ts0->tv_sec;
358 diff_nsec += ts1->tv_nsec - ts0->tv_nsec;
360 return ((double) diff_sec) + (((double) diff_nsec) / 1.0e9);
361 } /* }}} double timespec_diff */
363 int main (int argc, char **argv) /* {{{ */
365 struct timespec ts_begin;
366 struct timespec ts_end;
369 read_options (argc, argv);
371 sigint_action.sa_handler = signal_handler;
372 sigaction (SIGINT, &sigint_action, /* old = */ NULL);
374 sigterm_action.sa_handler = signal_handler;
375 sigaction (SIGTERM, &sigterm_action, /* old = */ NULL);
377 clock_gettime (CLOCK_MONOTONIC, &ts_begin);
379 clock_gettime (CLOCK_MONOTONIC, &ts_end);
381 runtime = timespec_diff (&ts_begin, &ts_end);
382 printf ("Sent %llu events in %.0fs (%.0f events/s).\n",
383 events_sent, runtime, ((double) events_sent) / runtime);
389 /* vim: set sw=2 sts=2 et fdm=marker : */