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