Fixed dlt-test-multi-process with fudge set to 0
[profile/ivi/dlt-daemon.git] / src / tests / dlt-test-multi-process.c
1 /**
2  * @licence app begin@
3  * Copyright (C) 2012  BMW AG
4  *
5  * This file is part of GENIVI Project Dlt - Diagnostic Log and Trace console apps.
6  *
7  * Contributions are licensed to the GENIVI Alliance under one or more
8  * Contribution License Agreements.
9  *
10  * \copyright
11  * This Source Code Form is subject to the terms of the
12  * Mozilla Public License, v. 2.0. If a  copy of the MPL was not distributed with
13  * this file, You can obtain one at http://mozilla.org/MPL/2.0/.
14  *
15  *
16  * \author Lassi Marttala <Lassi.LM.Marttala@partner.bmw.de> BMW 2011-2012
17  *
18  * \file dlt-test-multi-process.c
19  * For further information see http://www.genivi.org/.
20  * @licence end@
21  */
22
23 /*******************************************************************************
24 **                                                                            **
25 **  SRC-MODULE: dlt-test-multi-process.c                                      **
26 **                                                                            **
27 **  TARGET    : linux                                                         **
28 **                                                                            **
29 **  PROJECT   : DLT                                                           **
30 **                                                                            **
31 **  AUTHOR    : Lassi Marttala Lassi.LM.Marttala@partner.bmw.de               **
32 **                                                                            **
33 **  PURPOSE   : Stress test timing using multiple processes                   **
34 **                                                                            **
35 **  REMARKS   : Requires POSIX fork()                                         **
36 **                                                                            **
37 **  PLATFORM DEPENDANT [yes/no]: yes                                          **
38 **                                                                            **
39 **  TO BE CHANGED BY USER [yes/no]: no                                        **
40 **                                                                            **
41 *******************************************************************************/
42 #include <stdlib.h>
43 #include <stdio.h>
44 #include <ctype.h>
45 #include <unistd.h>
46 #include <limits.h>
47 #include <signal.h>
48 #include <errno.h>
49 #include <pthread.h>
50 #include <sys/wait.h>
51 #include <syslog.h>
52
53 #include "dlt.h"
54 #include "dlt_common.h"
55 #include "dlt-test-multi-process.h"
56
57 // Constants
58 #define MAX_PROCS 100
59 #define MAX_THREADS 100
60
61 // Structs
62 typedef struct {
63         int nmsgs;                      // Number of messages to send
64         int nprocs;                     // Number of processes to start
65         int nthreads;           // Number of threads to start
66         int delay;                      // Delay between logs messages for each process
67         int delay_fudge;        // Fudge the delay by 0-n to cause desynchronization
68 } s_parameters;
69
70 typedef struct {
71         s_parameters    params;
72         DltContext              ctx;
73 } s_thread_data;
74
75 // Forward declarations
76 void init_params(s_parameters * params);
77 void quit_handler(int signum);
78 void cleanup();
79 void do_forks(s_parameters params);
80 void run_threads(s_parameters params);
81 void do_logging(s_thread_data *data);
82 int wait_for_death();
83
84 // State information
85 volatile sig_atomic_t in_handler = 0;
86
87 // Globals for cleanup from main and signal handler
88 pid_t pids[MAX_PROCS];
89 unsigned int pidcount = 0;
90
91 /**
92  * Print instructions.
93  */
94 void usage(char *prog_name)
95 {
96         char version[255];
97         dlt_get_version(version);
98         s_parameters defaults;
99         init_params(&defaults);
100
101         printf("Usage: %s [options]\n", prog_name);
102         printf("Test application for stress testing the daemon with multiple processes and threads.\n");
103         printf("%s\n", version);
104         printf("Options (Default):\n");
105         printf(" -m number              Number of messages per thread to send. (%d)\n", defaults.nmsgs);
106         printf(" -p number              Number of processes to start. (%d), Max %d.\n", defaults.nprocs, MAX_PROCS);
107         printf(" -t number              Number of threads per process. (%d), Max %d.\n", defaults.nthreads, MAX_THREADS);
108         printf(" -d delay               Delay in milliseconds to wait between log messages. (%d)\n", defaults.delay);
109         printf(" -f delay               Random fudge in milliseconds to add to delay. (%d)\n", defaults.delay_fudge);
110 }
111
112 /**
113  * Set nice default values for parameters
114  */
115 void init_params(s_parameters * params) {
116         params->nmsgs           = 100;
117         params->nprocs          = 10;
118         params->nthreads        = 2;
119         params->delay           = 1000;
120         params->delay_fudge     = 100;
121 }
122
123 /**
124  * Read the command line and modify parameters
125  */
126 int read_cli(s_parameters *params, int argc, char **argv)
127 {
128         int c;
129         opterr = 0;
130     while ((c = getopt (argc, argv, "m:p:t:d:f:")) != -1)
131         {
132         switch(c)
133         {
134                 case 'm':
135                         params->nmsgs           = atoi(optarg);
136                         break;
137                 case 'p':
138                                 params->nprocs          = atoi(optarg);
139                                 if(params->nprocs > MAX_PROCS)
140                                 {
141                                         fprintf(stderr, "Too many processes selected.\n");
142                                         return -1;
143                                 }
144                                 break;
145                         case 't':
146                                 params->nthreads        = atoi(optarg);
147                                 if(params->nprocs > MAX_PROCS)
148                                 {
149                                         fprintf(stderr, "Too many threads selected.\n");
150                                         return -1;
151                                 }
152                                 break;
153                         case 'd':
154                                 params->delay           = atoi(optarg);
155                                 break;
156                         case 'f':
157                                 params->delay_fudge     = atoi(optarg);
158                                 break;
159                         case '?':
160                                 if(optopt == 'n' || optopt == 'd' || optopt == 'f')
161                                 {
162                                         fprintf(stderr, "Option -%c requires an argument.\n", optopt);
163                                 }
164                                 else if(isprint(optopt))
165                                 {
166                                         fprintf(stderr, "Unknown option '-%c'.\n", optopt);
167                                 }
168                                 else
169                                 {
170                                         fprintf(stderr, "Unknown option character '\\x%x'.\n", optopt);
171                                 }
172                                 return -1;
173                                 break;
174                         default:
175                                 abort();
176                                 return -1;//for parasoft
177         }
178         }
179         return 0;
180 }
181
182 /**
183  * Entry point
184  */
185 int main(int argc, char **argv)
186 {
187         // Prepare parameters
188         s_parameters params;
189         init_params(&params);
190         if(read_cli(&params, argc, argv) != 0) {
191                 usage(argv[0]);
192                 exit(-1);
193         }
194
195         // Launch the child processes
196         do_forks(params);
197
198         // Register signal handlers
199         if(signal(SIGINT, quit_handler) == SIG_IGN)
200                 signal(SIGINT, SIG_IGN);        // C-c
201         if(signal(SIGHUP, quit_handler) == SIG_IGN)
202                 signal(SIGHUP, SIG_IGN);        // Terminal closed
203         if(signal(SIGTERM, quit_handler) == SIG_IGN)
204                 signal(SIGTERM, SIG_IGN);       // kill (nice)
205
206         printf("Setup done. Listening. My pid: %d\n", getpid());    
207         fflush(stdout);
208
209         int err = wait_for_death();
210         cleanup();
211         return err;
212 }
213
214 /**
215  * Start the child processes
216  */
217
218 void do_forks(s_parameters params)
219 {
220     int i;
221
222         // Launch child processes
223         for(i=0;i<params.nprocs;i++)
224         {
225                 pid_t pid = fork();
226                 switch(pid)
227                 {
228                 case -1: // An error occured
229                         if(errno == EAGAIN)
230                         {
231                                 fprintf(stderr, "Could not allocate resources for child process.\n");
232                                 cleanup();
233                                 abort();
234                         }
235                         if(errno == ENOMEM)
236                         {
237                                 fprintf(stderr, "Could not allocate memory for child process' kernel structure.\n");
238                                 cleanup();
239                                 abort();
240                         }
241                         break;
242                 case 0: // Child process, start threads
243                         run_threads(params);
244                         break;
245                 default: // Parent process, store the childs pid
246                         pids[pidcount++] = pid;
247                         break;
248                 }
249         }
250 }
251
252 /**
253  * Clean up the child processes.
254  * Reraise signal to default handler.
255  */
256 void quit_handler(int signum)
257 {
258         if(in_handler)
259                 raise(signum);
260         in_handler = 1;
261
262         cleanup();
263
264         signal(signum, SIG_DFL);
265         raise(signum);
266 }
267
268 /**
269  * Ask the child processes to die
270  */
271 void cleanup()
272 {
273         int i;
274         for(i=0;i<pidcount;i++)
275         {
276                 kill(pids[i], SIGINT);
277         }
278 }
279
280 /**
281  * Generate the next sleep time
282  */
283 time_t mksleep_time(int delay, int fudge)
284 {
285     if (!fudge)
286         return delay*1000;
287     else
288         return (delay+rand()%fudge)*1000;
289 }
290
291 /**
292  * Open logging channel and proceed to spam messages
293  */
294 void do_logging(s_thread_data *data)
295 {
296         DltContext              mycontext;
297         char                    ctid[5];
298         char                    ctid_name[256];
299
300
301     sprintf(ctid,"%.2x", rand() & 0x0000ffff);
302         sprintf(ctid_name, "Child %s in dlt-test-multi-process", ctid);
303         DLT_REGISTER_CONTEXT(mycontext, ctid, ctid_name);
304
305         int msgs_left = data->params.nmsgs;
306         while(msgs_left-- > 0)
307         {
308                 DLT_LOG(mycontext, DLT_LOG_INFO, DLT_STRING(PAYLOAD_DATA));
309                 usleep(mksleep_time(data->params.delay, data->params.delay_fudge));
310         }
311         DLT_UNREGISTER_CONTEXT(mycontext);
312 }
313
314 /**
315  * Start the threads and wait for them to return.
316  */
317 void run_threads(s_parameters params)
318 {
319         pthread_t               thread[params.nthreads];
320         s_thread_data   thread_data;
321         char                    apid[5];
322         char                    apid_name[256];
323         int                     i;
324
325         srand(getpid());
326
327     sprintf(apid,"MT%02u", pidcount);
328     sprintf(apid_name, "Apps %s.", apid);
329
330         DLT_REGISTER_APP(apid, apid_name);
331
332         thread_data.params      = params;
333
334         for(i=0;i<params.nthreads;i++)
335         {
336                 if(pthread_create(&(thread[i]), NULL, (void *) &do_logging, &thread_data) != 0)
337                 {
338                         printf("Error creating thread.\n");
339                         abort();
340                 }
341         }
342
343         for(i=0;i<params.nthreads;i++)
344         {
345                 pthread_join(thread[i], NULL);
346         }
347
348
349         DLT_UNREGISTER_APP();
350         // We can exit now
351         exit(0);
352 }
353
354 /**
355  * Wait for child processes to complete their work.
356  */
357 int wait_for_death()
358 {
359         int pids_left = pidcount;
360         while(pids_left > 0)
361         {
362                 int status;
363                 pid_t w = waitpid(WAIT_ANY, &status, 0);
364                 if(status < 0)
365                 {
366                         return -1;
367                 }
368                 else
369                 {
370                         int i;
371                         for(i=0;i<pidcount;i++)
372                         {
373                                 if(pids[i] == w)
374                                 {
375                                         pids_left--;
376                                         break;
377                                 }
378                         }
379                 }
380         }
381         return 0;
382 }