Change synchronization code license to LGPLv2.1
[lttv.git] / lttv / lttv / sync / sync_chain_unittest.c
CommitLineData
b670bb7c 1/* This file is part of the Linux Trace Toolkit viewer
277e5b53 2 * Copyright (C) 2009, 2010 Benjamin Poirier <benjamin.poirier@polymtl.ca>
b670bb7c 3 *
277e5b53
BP
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation, either version 2.1 of the License, or (at
7 * your option) any later version.
b670bb7c 8 *
277e5b53
BP
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
12 * License for more details.
b670bb7c 13 *
277e5b53
BP
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
b670bb7c
BP
16 */
17
18#define _GNU_SOURCE
19
20#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
24#include <errno.h>
25#include <fcntl.h>
26#include <getopt.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <sys/resource.h>
31#include <sys/stat.h>
32#include <sys/time.h>
33#include <sys/types.h>
34#include <sys/stat.h>
35#include <unistd.h>
36
2f961b65
BP
37#include "event_processing_text.h"
38#include "event_matching_tcp.h"
39#include "event_matching_broadcast.h"
40#include "event_matching_distributor.h"
41#include "event_analysis_chull.h"
42#include "event_analysis_linreg.h"
43#include "event_analysis_eval.h"
b670bb7c
BP
44#include "sync_chain.h"
45
46
47struct OptionsInfo
48{
49 GArray* longOptions;
50 GString* optionString;
48b641c1
BP
51 GQueue* longIndex;
52 GHashTable* shortIndex;
b670bb7c
BP
53};
54
55
56const char* processOptions(const int argc, char* const argv[]);
57static void usage(const char* const programName);
58static void gfPrintModuleOption(gpointer data, gpointer user_data);
59static void nullLog(const gchar *log_domain, GLogLevelFlags log_level, const
60 gchar *message, gpointer user_data);
61static void gfAddModuleOption(gpointer data, gpointer user_data);
48b641c1
BP
62static guint ghfCharHash(gconstpointer key);
63static gboolean gefCharEqual(gconstpointer a, gconstpointer b);
b670bb7c
BP
64
65
66static ModuleOption optionSyncStats= {
67 .shortName= 's',
68 .longName= "sync-stats",
69 .hasArg= NO_ARG,
70 .optionHelp= "Print statistics and debug messages",
71};
72static char graphsDir[20];
73static ModuleOption optionSyncGraphs= {
74 .shortName= 'g',
75 .longName= "sync-graphs",
76 .hasArg= OPTIONAL_ARG,
77 .optionHelp= "Output gnuplot graph showing synchronization points",
78};
79static ModuleOption optionSyncAnalysis= {
80 .shortName= 'a',
81 .longName= "sync-analysis",
82 .hasArg= REQUIRED_ARG,
83 .optionHelp= "Specify which algorithm to use for event analysis",
84};
85
86
87/*
88 * Implement a sync chain, it is mostly for unittest and it does not depend on
89 * lttv
90 *
91 * Args:
92 * argc, argv: standard argument arrays
93 *
94 * Returns:
95 * exit status from main() is always EXIT_SUCCESS
96 */
97int main(const int argc, char* const argv[])
98{
99 SyncState* syncState;
100 struct timeval startTime, endTime;
101 struct rusage startUsage, endUsage;
102 GList* result;
103 int retval;
104 bool stats;
105 const char* testCaseName;
106 GString* analysisModulesNames;
6ce8ceac 107 unsigned int id;
b670bb7c 108
2f961b65
BP
109 /*
110 * Initialize event modules
111 * Call the "constructor" or initialization function of each event module
112 * so it can register itself. This must be done before elements in
113 * processingModules, matchingModules, analysisModules or moduleOptions
114 * are accessed.
115 */
116 registerProcessingText();
117
118 registerMatchingTCP();
119 registerMatchingBroadcast();
120 registerMatchingDistributor();
121
122 registerAnalysisCHull();
123 registerAnalysisLinReg();
124 registerAnalysisEval();
125
b670bb7c
BP
126 // Initialize data structures
127 syncState= malloc(sizeof(SyncState));
128
129 // Process command line arguments
130 g_assert(g_queue_get_length(&analysisModules) > 0);
131 optionSyncAnalysis.arg= ((AnalysisModule*)
132 g_queue_peek_head(&analysisModules))->name;
133 analysisModulesNames= g_string_new("Available modules: ");
134 g_queue_foreach(&analysisModules, &gfAppendAnalysisName,
135 analysisModulesNames);
136 // remove the last ", "
137 g_string_truncate(analysisModulesNames, analysisModulesNames->len - 2);
138 optionSyncAnalysis.argHelp= analysisModulesNames->str;
139
140 retval= snprintf(graphsDir, sizeof(graphsDir), "graphs-%d", getpid());
141 if (retval > sizeof(graphsDir) - 1)
142 {
143 graphsDir[sizeof(graphsDir) - 1]= '\0';
144 }
145 optionSyncGraphs.arg= graphsDir;
146
147 g_queue_push_head(&moduleOptions, &optionSyncAnalysis);
148 g_queue_push_head(&moduleOptions, &optionSyncGraphs);
149 g_queue_push_head(&moduleOptions, &optionSyncStats);
150
151 testCaseName= processOptions(argc, argv);
152
153 g_string_free(analysisModulesNames, TRUE);
154
155 if (optionSyncStats.present)
156 {
157 syncState->stats= true;
158 gettimeofday(&startTime, 0);
159 getrusage(RUSAGE_SELF, &startUsage);
160 }
161 else
162 {
163 syncState->stats= false;
6ce8ceac 164 id= g_log_set_handler(NULL, G_LOG_LEVEL_DEBUG, nullLog, NULL);
b670bb7c
BP
165 }
166
167 if (optionSyncGraphs.present)
168 {
169 // Create the graph directory right away in case the module initialization
170 // functions have something to write in it.
171 syncState->graphsDir= optionSyncGraphs.arg;
172 syncState->graphsStream= createGraphsDir(syncState->graphsDir);
173 }
174 else
175 {
176 syncState->graphsStream= NULL;
177 syncState->graphsDir= NULL;
178 }
179
180 // Identify modules
181 syncState->processingData= NULL;
182 result= g_queue_find_custom(&processingModules, "text",
183 &gcfCompareProcessing);
184 g_assert(result != NULL);
185 syncState->processingModule= (ProcessingModule*) result->data;
186
187 syncState->matchingData= NULL;
188 result= g_queue_find_custom(&matchingModules, "TCP", &gcfCompareMatching);
189 g_assert(result != NULL);
190 syncState->matchingModule= (MatchingModule*) result->data;
191
192 syncState->analysisData= NULL;
193 result= g_queue_find_custom(&analysisModules, optionSyncAnalysis.arg,
194 &gcfCompareAnalysis);
195 if (result != NULL)
196 {
197 syncState->analysisModule= (AnalysisModule*) result->data;
198 }
199 else
200 {
201 g_error("Analysis module '%s' not found", optionSyncAnalysis.arg);
202 }
203
204 // Initialize modules
205 syncState->processingModule->initProcessing(syncState, testCaseName);
206 syncState->matchingModule->initMatching(syncState);
207 syncState->analysisModule->initAnalysis(syncState);
208
209 // Process traceset
210 syncState->processingModule->finalizeProcessing(syncState);
211
212 // Write graphs file
213 if (syncState->graphsStream)
214 {
215 writeGraphsScript(syncState);
216
217 if (fclose(syncState->graphsStream) != 0)
218 {
219 g_error(strerror(errno));
220 }
221 }
222
223 // Print statistics
224 if (syncState->stats)
225 {
226 printStats(syncState);
227 }
228
229 // Destroy modules and clean up
230 syncState->processingModule->destroyProcessing(syncState);
231 syncState->matchingModule->destroyMatching(syncState);
232 syncState->analysisModule->destroyAnalysis(syncState);
233
234 stats= syncState->stats;
235 free(syncState);
236
237 if (stats)
238 {
239 gettimeofday(&endTime, 0);
240 retval= getrusage(RUSAGE_SELF, &endUsage);
241
242 timeDiff(&endTime, &startTime);
243 timeDiff(&endUsage.ru_utime, &startUsage.ru_utime);
244 timeDiff(&endUsage.ru_stime, &startUsage.ru_stime);
245
246 printf("Synchronization time:\n");
247 printf("\treal time: %ld.%06ld\n", endTime.tv_sec, endTime.tv_usec);
248 printf("\tuser time: %ld.%06ld\n", endUsage.ru_utime.tv_sec,
249 endUsage.ru_utime.tv_usec);
250 printf("\tsystem time: %ld.%06ld\n", endUsage.ru_stime.tv_sec,
251 endUsage.ru_stime.tv_usec);
252 }
253
6ce8ceac
BP
254 if (!optionSyncStats.present)
255 {
256 g_log_remove_handler(NULL, id);
257 }
258
b670bb7c
BP
259 return EXIT_SUCCESS;
260}
261
262
263/*
264 * Read program arguments dans update ModuleOptions structures
265 *
266 * Args:
267 * argc, argv: standard argument arrays
268 *
269 * Returns:
270 * Name of the test case file (first parameter)
271 */
272const char* processOptions(const int argc, char* const argv[])
273{
274 int c;
275 extern char* optarg;
276 extern int optind, opterr, optopt;
277 GArray* longOptions;
278 GString* optionString;
48b641c1
BP
279 GQueue* longIndex;
280 int longOption;
281 GHashTable* shortIndex;
b670bb7c
BP
282
283 longOptions= g_array_sized_new(TRUE, FALSE, sizeof(struct option),
284 g_queue_get_length(&moduleOptions));
285 optionString= g_string_new("");
48b641c1
BP
286 longIndex= g_queue_new();
287 shortIndex= g_hash_table_new(&ghfCharHash, &gefCharEqual);
b670bb7c
BP
288
289 g_queue_foreach(&moduleOptions, &gfAddModuleOption, &(struct OptionsInfo)
48b641c1 290 {longOptions, optionString, longIndex, shortIndex});
b670bb7c
BP
291
292 do
293 {
294 int optionIndex= 0;
48b641c1 295 ModuleOption* moduleOption;
b670bb7c 296
48b641c1 297 longOption= -1;
b670bb7c
BP
298 c= getopt_long(argc, argv, optionString->str, (struct option*)
299 longOptions->data, &optionIndex);
300
48b641c1
BP
301 if (longOption >= 0 && longOption < g_queue_get_length(longIndex))
302 {
303 moduleOption= g_queue_peek_nth(longIndex, longOption);
304 }
305 else if ((moduleOption= g_hash_table_lookup(shortIndex, &c)) != NULL)
b670bb7c 306 {
b670bb7c
BP
307 }
308 else if (c == -1)
309 {
310 break;
311 }
312 else if (c == '?')
313 {
314 usage(argv[0]);
315 abort();
316 }
317 else
318 {
319 g_error("Option parse error");
320 }
48b641c1
BP
321
322 moduleOption->present= true;
323
324 if (moduleOption->hasArg == REQUIRED_ARG)
325 {
326 moduleOption->arg= optarg;
327 }
328 if (moduleOption->hasArg == OPTIONAL_ARG && optarg)
329 {
330 moduleOption->arg= optarg;
331 }
b670bb7c
BP
332 } while (c != -1);
333
334 g_array_free(longOptions, TRUE);
335 g_string_free(optionString, TRUE);
48b641c1
BP
336 g_queue_free(longIndex);
337 g_hash_table_destroy(shortIndex);
b670bb7c
BP
338
339 if (argc <= optind)
340 {
341 fprintf(stderr, "Test file unspecified\n");
342 usage(argv[0]);
343 abort();
344 }
345
346 return argv[optind];
347}
348
349
350/*
351 * Print information about program options and arguments.
352 *
353 * Args:
354 * programName: name of the program, as contained in argv[0] for example
355 */
356static void usage(const char* const programName)
357{
358 printf(
359 "%s [options] <test file>\n"
360 "Options:\n", programName);
361
362 g_queue_foreach(&moduleOptions, &gfPrintModuleOption, NULL);
363}
364
365
366/*
367 * A GFunc for g_queue_foreach()
368 *
369 * Print analysis module names.
370 *
371 * Args:
372 * data: ModuleOption*, option
373 * user_data: NULL
374 */
375static void gfPrintModuleOption(gpointer data, gpointer user_data)
376{
377 ModuleOption* option= data;
378 int width= 0, sum= 0;
379 const int colWidth= 27;
380
381 printf("\t");
382
383 if (option->shortName)
384 {
385 printf("-%c, %n", option->shortName, &width);
386 sum+= width;
387 }
388
389 printf("--%-s%n", option->longName, &width);
390 sum+= width;
391
392 if (option->hasArg == REQUIRED_ARG || option->hasArg == OPTIONAL_ARG)
393 {
394 printf("=[..]%n", &width);
395 sum+= width;
396 }
397
398 if (option->optionHelp)
399 {
400 printf("%*s%s\n", colWidth - sum > 0 ? colWidth - sum : 0, "", option->optionHelp);
401 }
402
403 if (option->argHelp)
404 {
405 printf("\t%*s%s\n", colWidth, "", option->argHelp);
406 }
407
408 if ((option->hasArg == REQUIRED_ARG || option->hasArg == OPTIONAL_ARG) && option->arg)
409 {
410 printf("\t%*sDefault value: %s\n", colWidth, "", option->arg);
411 }
412}
413
414
415/*
416 * A Glib log function which does nothing.
417 */
418static void nullLog(const gchar *log_domain, GLogLevelFlags log_level, const
419 gchar *message, gpointer user_data)
420{}
421
422
423/*
424 * A GFunc for g_queue_foreach()
425 *
426 * Args:
427 * data: ModuleOption*, option
428 * user_data: struct OptionsInfo*, add option to this array of struct option
429 */
430static void gfAddModuleOption(gpointer data, gpointer user_data)
431{
432 ModuleOption* option= data;
433 struct OptionsInfo* optionsInfo= user_data;
434 struct option newOption;
435 // "[mixing enumerations] can still be considered bad style even though it
436 // is not strictly illegal" c.faq 2.22
437 const int conversion[]= {
438 [NO_ARG]= no_argument,
439 [OPTIONAL_ARG]= optional_argument,
440 [REQUIRED_ARG]= required_argument,
441 };
442 const char* colons[]= {
443 [NO_ARG]= "",
444 [OPTIONAL_ARG]= "::",
445 [REQUIRED_ARG]= ":",
446 };
447
448 newOption.name= option->longName;
449 newOption.has_arg= conversion[option->hasArg];
450 newOption.flag= NULL;
48b641c1 451 newOption.val= g_queue_get_length(optionsInfo->longIndex);
b670bb7c
BP
452
453 g_array_append_val(optionsInfo->longOptions, newOption);
48b641c1
BP
454 if (option->shortName)
455 {
456 g_string_append_c(optionsInfo->optionString, option->shortName);
457 g_string_append(optionsInfo->optionString, colons[option->hasArg]);
458
459 g_hash_table_insert(optionsInfo->shortIndex, &option->shortName,
460 option);
461 }
462 g_queue_push_tail(optionsInfo->longIndex, option);
463}
464
465
466/*
467 * A GHashFunc for g_hash_table_new()
468 *
469 * Args:
470 * key char*, just one character
471 */
472static guint ghfCharHash(gconstpointer key)
473{
474 return *(char*) key;
475}
476
477
478/*
479 * A GEqualFunc for g_hash_table_new()
480 *
481 * Args:
482 * a, b char*, just one character each
483 *
484 * Returns:
485 * TRUE if both values are equal
486 */
487static gboolean gefCharEqual(gconstpointer a, gconstpointer b)
488{
489 if (*(char*) a == *(char*) b)
490 {
491 return TRUE;
492 }
493 else
494 {
495 return FALSE;
496 }
b670bb7c 497}
This page took 0.067638 seconds and 4 git commands to generate.