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