1 /* This file is part of the Linux Trace Toolkit viewer
2 * Copyright (C) 2009, 2010 Benjamin Poirier <benjamin.poirier@polymtl.ca>
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.
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.
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/>.
30 #include <sys/resource.h>
33 #include <sys/types.h>
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 "factor_reduction_accuracy.h"
45 #include "sync_chain.h"
51 GString
* optionString
;
53 GHashTable
* shortIndex
;
57 const char* processOptions(const int argc
, char* const argv
[]);
58 static void usage(const char* const programName
);
59 static void gfPrintModuleOption(gpointer data
, gpointer user_data
);
60 static void nullLog(const gchar
*log_domain
, GLogLevelFlags log_level
, const
61 gchar
*message
, gpointer user_data
);
62 static void gfAddModuleOption(gpointer data
, gpointer user_data
);
63 static guint
ghfCharHash(gconstpointer key
);
64 static gboolean
gefCharEqual(gconstpointer a
, gconstpointer b
);
67 static ModuleOption optionSyncStats
= {
69 .longName
= "sync-stats",
71 .optionHelp
= "Print statistics and debug messages",
73 static char graphsDir
[20];
74 static ModuleOption optionSyncGraphs
= {
76 .longName
= "sync-graphs",
77 .hasArg
= OPTIONAL_ARG
,
78 .optionHelp
= "Output gnuplot graph showing synchronization points",
80 static ModuleOption optionSyncAnalysis
= {
82 .longName
= "sync-analysis",
83 .hasArg
= REQUIRED_ARG
,
84 .optionHelp
= "Specify which algorithm to use for event analysis",
86 static ModuleOption optionSyncReduction
= {
88 .longName
= "sync-reduction",
89 .hasArg
= REQUIRED_ARG
,
90 .optionHelp
= "Specify which algorithm to use for factor reduction",
95 * Implement a sync chain, it is mostly for unittest and it does not depend on
99 * argc, argv: standard argument arrays
102 * exit status from main() is always EXIT_SUCCESS
104 int main(const int argc
, char* const argv
[])
106 SyncState
* syncState
;
107 struct timeval startTime
, endTime
;
108 struct rusage startUsage
, endUsage
;
113 const char* testCaseName
;
114 GString
* analysisModulesNames
;
115 GString
* reductionModulesNames
;
117 AllFactors
* allFactors
;
120 * Initialize event modules
121 * Call the "constructor" or initialization function of each event module
122 * so it can register itself. This must be done before elements in
123 * processingModules, matchingModules, analysisModules or moduleOptions
126 registerProcessingText();
128 registerMatchingTCP();
129 registerMatchingBroadcast();
130 registerMatchingDistributor();
132 registerAnalysisCHull();
133 registerAnalysisLinReg();
134 registerAnalysisEval();
136 registerReductionAccuracy();
138 // Initialize data structures
139 syncState
= malloc(sizeof(SyncState
));
141 // Process command line arguments
142 g_assert(g_queue_get_length(&analysisModules
) > 0);
143 optionSyncAnalysis
.arg
= ((AnalysisModule
*)
144 g_queue_peek_head(&analysisModules
))->name
;
145 analysisModulesNames
= g_string_new("Available modules: ");
146 g_queue_foreach(&analysisModules
, &gfAppendAnalysisName
,
147 analysisModulesNames
);
148 // remove the last ", "
149 g_string_truncate(analysisModulesNames
, analysisModulesNames
->len
- 2);
150 optionSyncAnalysis
.argHelp
= analysisModulesNames
->str
;
152 g_assert(g_queue_get_length(&reductionModules
) > 0);
153 optionSyncReduction
.arg
= ((ReductionModule
*)
154 g_queue_peek_head(&reductionModules
))->name
;
155 reductionModulesNames
= g_string_new("Available modules: ");
156 g_queue_foreach(&reductionModules
, &gfAppendReductionName
,
157 reductionModulesNames
);
158 // remove the last ", "
159 g_string_truncate(reductionModulesNames
, reductionModulesNames
->len
- 2);
160 optionSyncReduction
.argHelp
= reductionModulesNames
->str
;
162 retval
= snprintf(graphsDir
, sizeof(graphsDir
), "graphs-%d", getpid());
163 if (retval
> sizeof(graphsDir
) - 1)
165 graphsDir
[sizeof(graphsDir
) - 1]= '\0';
167 optionSyncGraphs
.arg
= graphsDir
;
169 g_queue_push_head(&moduleOptions
, &optionSyncReduction
);
170 g_queue_push_head(&moduleOptions
, &optionSyncAnalysis
);
171 g_queue_push_head(&moduleOptions
, &optionSyncGraphs
);
172 g_queue_push_head(&moduleOptions
, &optionSyncStats
);
174 testCaseName
= processOptions(argc
, argv
);
176 g_string_free(analysisModulesNames
, TRUE
);
177 g_string_free(reductionModulesNames
, TRUE
);
179 if (optionSyncStats
.present
)
181 syncState
->stats
= true;
182 gettimeofday(&startTime
, 0);
183 getrusage(RUSAGE_SELF
, &startUsage
);
187 syncState
->stats
= false;
188 id
= g_log_set_handler(NULL
, G_LOG_LEVEL_DEBUG
, nullLog
, NULL
);
191 if (optionSyncGraphs
.present
)
193 // Create the graph directory right away in case the module initialization
194 // functions have something to write in it.
195 syncState
->graphsDir
= optionSyncGraphs
.arg
;
196 syncState
->graphsStream
= createGraphsDir(syncState
->graphsDir
);
200 syncState
->graphsStream
= NULL
;
201 syncState
->graphsDir
= NULL
;
205 syncState
->processingData
= NULL
;
206 result
= g_queue_find_custom(&processingModules
, "text",
207 &gcfCompareProcessing
);
208 g_assert(result
!= NULL
);
209 syncState
->processingModule
= (ProcessingModule
*) result
->data
;
211 syncState
->matchingData
= NULL
;
212 result
= g_queue_find_custom(&matchingModules
, "TCP", &gcfCompareMatching
);
213 g_assert(result
!= NULL
);
214 syncState
->matchingModule
= (MatchingModule
*) result
->data
;
216 syncState
->analysisData
= NULL
;
217 result
= g_queue_find_custom(&analysisModules
, optionSyncAnalysis
.arg
,
218 &gcfCompareAnalysis
);
221 syncState
->analysisModule
= (AnalysisModule
*) result
->data
;
225 g_error("Analysis module '%s' not found", optionSyncAnalysis
.arg
);
228 syncState
->reductionData
= NULL
;
229 result
= g_queue_find_custom(&reductionModules
, optionSyncReduction
.arg
,
230 &gcfCompareReduction
);
233 syncState
->reductionModule
= (ReductionModule
*) result
->data
;
237 g_error("Reduction module '%s' not found", optionSyncReduction
.arg
);
240 // Initialize modules
241 syncState
->processingModule
->initProcessing(syncState
, testCaseName
);
242 syncState
->matchingModule
->initMatching(syncState
);
243 syncState
->analysisModule
->initAnalysis(syncState
);
244 syncState
->reductionModule
->initReduction(syncState
);
247 allFactors
= syncState
->processingModule
->finalizeProcessing(syncState
);
248 factors
= syncState
->reductionModule
->finalizeReduction(syncState
,
250 freeAllFactors(allFactors
, syncState
->traceNb
);
253 if (syncState
->graphsStream
)
255 writeGraphsScript(syncState
);
257 if (fclose(syncState
->graphsStream
) != 0)
259 g_error("%s", strerror(errno
));
264 if (optionSyncStats
.present
)
268 printStats(syncState
);
270 printf("Resulting synchronization factors:\n");
271 for (i
= 0; i
< factors
->len
; i
++)
273 Factors
* traceFactors
= &g_array_index(factors
, Factors
, i
);
274 printf("\ttrace %u drift= %g offset= %g\n", i
,
275 traceFactors
->drift
, traceFactors
->offset
);
279 // Destroy modules and clean up
280 syncState
->processingModule
->destroyProcessing(syncState
);
281 syncState
->matchingModule
->destroyMatching(syncState
);
282 syncState
->analysisModule
->destroyAnalysis(syncState
);
283 syncState
->reductionModule
->destroyReduction(syncState
);
285 stats
= syncState
->stats
;
290 gettimeofday(&endTime
, 0);
291 retval
= getrusage(RUSAGE_SELF
, &endUsage
);
293 timeDiff(&endTime
, &startTime
);
294 timeDiff(&endUsage
.ru_utime
, &startUsage
.ru_utime
);
295 timeDiff(&endUsage
.ru_stime
, &startUsage
.ru_stime
);
297 printf("Synchronization time:\n");
298 printf("\treal time: %ld.%06ld\n", endTime
.tv_sec
, endTime
.tv_usec
);
299 printf("\tuser time: %ld.%06ld\n", endUsage
.ru_utime
.tv_sec
,
300 endUsage
.ru_utime
.tv_usec
);
301 printf("\tsystem time: %ld.%06ld\n", endUsage
.ru_stime
.tv_sec
,
302 endUsage
.ru_stime
.tv_usec
);
305 if (!optionSyncStats
.present
)
307 g_log_remove_handler(NULL
, id
);
315 * Read program arguments and update ModuleOptions structures
318 * argc, argv: standard argument arrays
321 * Name of the test case file (first parameter)
323 const char* processOptions(const int argc
, char* const argv
[])
327 extern int optind
, opterr
, optopt
;
329 GString
* optionString
;
332 GHashTable
* shortIndex
;
334 longOptions
= g_array_sized_new(TRUE
, FALSE
, sizeof(struct option
),
335 g_queue_get_length(&moduleOptions
));
336 optionString
= g_string_new("");
337 longIndex
= g_queue_new();
338 shortIndex
= g_hash_table_new(&ghfCharHash
, &gefCharEqual
);
340 g_queue_foreach(&moduleOptions
, &gfAddModuleOption
, &(struct OptionsInfo
)
341 {longOptions
, optionString
, longIndex
, shortIndex
});
346 ModuleOption
* moduleOption
;
349 c
= getopt_long(argc
, argv
, optionString
->str
, (struct option
*)
350 longOptions
->data
, &optionIndex
);
352 if (longOption
>= 0 && longOption
< g_queue_get_length(longIndex
))
354 moduleOption
= g_queue_peek_nth(longIndex
, longOption
);
356 else if ((moduleOption
= g_hash_table_lookup(shortIndex
, &c
)) != NULL
)
370 g_error("Option parse error");
373 moduleOption
->present
= true;
375 if (moduleOption
->hasArg
== REQUIRED_ARG
)
377 moduleOption
->arg
= optarg
;
379 if (moduleOption
->hasArg
== OPTIONAL_ARG
&& optarg
)
381 moduleOption
->arg
= optarg
;
385 g_array_free(longOptions
, TRUE
);
386 g_string_free(optionString
, TRUE
);
387 g_queue_free(longIndex
);
388 g_hash_table_destroy(shortIndex
);
392 fprintf(stderr
, "Test file unspecified\n");
402 * Print information about program options and arguments.
405 * programName: name of the program, as contained in argv[0] for example
407 static void usage(const char* const programName
)
410 "%s [options] <test file>\n"
411 "Options:\n", programName
);
413 g_queue_foreach(&moduleOptions
, &gfPrintModuleOption
, NULL
);
418 * A GFunc for g_queue_foreach()
420 * Print analysis module names.
423 * data: ModuleOption*, option
426 static void gfPrintModuleOption(gpointer data
, gpointer user_data
)
428 ModuleOption
* option
= data
;
429 int width
= 0, sum
= 0;
430 const int colWidth
= 27;
434 if (option
->shortName
)
436 printf("-%c, %n", option
->shortName
, &width
);
440 printf("--%-s%n", option
->longName
, &width
);
443 if (option
->hasArg
== REQUIRED_ARG
|| option
->hasArg
== OPTIONAL_ARG
)
445 printf("=[..]%n", &width
);
449 if (option
->optionHelp
)
451 printf("%*s%s\n", colWidth
- sum
> 0 ? colWidth
- sum
: 0, "", option
->optionHelp
);
456 printf("\t%*s%s\n", colWidth
, "", option
->argHelp
);
459 if ((option
->hasArg
== REQUIRED_ARG
|| option
->hasArg
== OPTIONAL_ARG
) && option
->arg
)
461 printf("\t%*sDefault value: %s\n", colWidth
, "", option
->arg
);
467 * A Glib log function which does nothing.
469 static void nullLog(const gchar
*log_domain
, GLogLevelFlags log_level
, const
470 gchar
*message
, gpointer user_data
)
475 * A GFunc for g_queue_foreach()
478 * data: ModuleOption*, option
479 * user_data: struct OptionsInfo*, add option to this array of struct option
481 static void gfAddModuleOption(gpointer data
, gpointer user_data
)
483 ModuleOption
* option
= data
;
484 struct OptionsInfo
* optionsInfo
= user_data
;
485 struct option newOption
;
486 // "[mixing enumerations] can still be considered bad style even though it
487 // is not strictly illegal" c.faq 2.22
488 const int conversion
[]= {
489 [NO_ARG
]= no_argument
,
490 [OPTIONAL_ARG
]= optional_argument
,
491 [REQUIRED_ARG
]= required_argument
,
493 const char* colons
[]= {
495 [OPTIONAL_ARG
]= "::",
499 newOption
.name
= option
->longName
;
500 newOption
.has_arg
= conversion
[option
->hasArg
];
501 newOption
.flag
= NULL
;
502 newOption
.val
= g_queue_get_length(optionsInfo
->longIndex
);
504 g_array_append_val(optionsInfo
->longOptions
, newOption
);
505 if (option
->shortName
)
507 g_string_append_c(optionsInfo
->optionString
, option
->shortName
);
508 g_string_append(optionsInfo
->optionString
, colons
[option
->hasArg
]);
510 g_hash_table_insert(optionsInfo
->shortIndex
, &option
->shortName
,
513 g_queue_push_tail(optionsInfo
->longIndex
, option
);
518 * A GHashFunc for g_hash_table_new()
521 * key char*, just one character
523 static guint
ghfCharHash(gconstpointer key
)
530 * A GEqualFunc for g_hash_table_new()
533 * a, b char*, just one character each
536 * TRUE if both values are equal
538 static gboolean
gefCharEqual(gconstpointer a
, gconstpointer b
)
540 if (*(char*) a
== *(char*) b
)