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