c2ec05def08908d92221c089041a55999e9b439f
[lttv.git] / lttv / lttv / sync / sync_chain_lttv.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 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <sys/resource.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <unistd.h>
32
33 #include <lttv/module.h>
34 #include <lttv/option.h>
35
36 #include "sync_chain.h"
37
38
39 static void init();
40 static void destroy();
41
42 static void gfAppendAnalysisName(gpointer data, gpointer user_data);
43 static void gfAddModuleOption(gpointer data, gpointer user_data);
44 static void gfRemoveModuleOption(gpointer data, gpointer user_data);
45
46 GQueue processingModules= G_QUEUE_INIT;
47 GQueue matchingModules= G_QUEUE_INIT;
48 GQueue analysisModules= G_QUEUE_INIT;
49 GQueue moduleOptions= G_QUEUE_INIT;
50
51 static char* argHelpNone= "none";
52 static ModuleOption optionSync= {
53 .longName= "sync",
54 .hasArg= NO_ARG,
55 {.present= false},
56 .optionHelp= "synchronize the time between the traces",
57 };
58 static char graphsDir[20];
59 static ModuleOption optionSyncStats= {
60 .longName= "sync-stats",
61 .hasArg= NO_ARG,
62 {.present= false},
63 .optionHelp= "print statistics about the time synchronization",
64 };
65 static ModuleOption optionSyncNull= {
66 .longName= "sync-null",
67 .hasArg= NO_ARG,
68 {.present= false},
69 .optionHelp= "read the events but do not perform any processing",
70 };
71 static GString* analysisModulesNames;
72 static ModuleOption optionSyncAnalysis= {
73 .longName= "sync-analysis",
74 .hasArg= REQUIRED_ARG,
75 .optionHelp= "specify the algorithm to use for event analysis",
76 };
77 static ModuleOption optionSyncGraphs= {
78 .longName= "sync-graphs",
79 .hasArg= NO_ARG,
80 {.present= false},
81 .optionHelp= "output gnuplot graph showing synchronization points",
82 };
83 static ModuleOption optionSyncGraphsDir= {
84 .longName= "sync-graphs-dir",
85 .hasArg= REQUIRED_ARG,
86 .optionHelp= "specify the directory where to store the graphs",
87 };
88
89 /*
90 * Module init function
91 *
92 * This function is declared to be the module initialization function. Event
93 * modules are registered with a "constructor (102)" attribute except one in
94 * each class (processing, matching, analysis) which is chosen to be the
95 * default and which is registered with a "constructor (101)" attribute.
96 * Constructors with no priority are called after constructors with
97 * priorities. The result is that the list of event modules is known when this
98 * function is executed.
99 */
100 static void init()
101 {
102 int retval;
103
104 g_debug("Sync init");
105
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("");
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 optionSyncGraphsDir.arg= graphsDir;
122 optionSyncGraphsDir.argHelp= graphsDir;
123
124 g_queue_push_head(&moduleOptions, &optionSyncGraphsDir);
125 g_queue_push_head(&moduleOptions, &optionSyncGraphs);
126 g_queue_push_head(&moduleOptions, &optionSyncAnalysis);
127 g_queue_push_head(&moduleOptions, &optionSyncNull);
128 g_queue_push_head(&moduleOptions, &optionSyncStats);
129 g_queue_push_head(&moduleOptions, &optionSync);
130
131 g_queue_foreach(&moduleOptions, &gfAddModuleOption, NULL);
132
133 }
134
135
136 /*
137 * Module unload function
138 */
139 static void destroy()
140 {
141 g_debug("Sync destroy");
142
143 g_queue_foreach(&moduleOptions, &gfRemoveModuleOption, NULL);
144 g_string_free(analysisModulesNames, TRUE);
145
146 g_queue_clear(&processingModules);
147 g_queue_clear(&matchingModules);
148 g_queue_clear(&analysisModules);
149 g_queue_clear(&moduleOptions);
150 }
151
152
153 /*
154 * Calculate a traceset's drift and offset values based on network events
155 *
156 * The individual correction factors are written out to each trace.
157 *
158 * Args:
159 * traceSetContext: traceset
160 */
161 void syncTraceset(LttvTracesetContext* const traceSetContext)
162 {
163 SyncState* syncState;
164 struct timeval startTime, endTime;
165 struct rusage startUsage, endUsage;
166 GList* result;
167 unsigned int i;
168 int retval;
169
170 if (!optionSync.present)
171 {
172 g_debug("Not synchronizing traceset because option is disabled");
173 return;
174 }
175
176 if (optionSyncStats.present)
177 {
178 gettimeofday(&startTime, 0);
179 getrusage(RUSAGE_SELF, &startUsage);
180 }
181
182 // Initialize data structures
183 syncState= malloc(sizeof(SyncState));
184 syncState->traceNb= lttv_traceset_number(traceSetContext->ts);
185
186 if (optionSyncStats.present)
187 {
188 syncState->stats= true;
189 }
190 else
191 {
192 syncState->stats= false;
193 }
194
195 if (optionSyncGraphs.present)
196 {
197 char* cwd;
198 int graphsFp;
199
200 // Create the graph directory right away in case the module initialization
201 // functions have something to write in it.
202 syncState->graphsDir= optionSyncGraphsDir.arg;
203 cwd= changeToGraphDir(optionSyncGraphsDir.arg);
204
205 if ((graphsFp= open("graphs.gnu", O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR |
206 S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH
207 | S_IWOTH | S_IXOTH)) == -1)
208 {
209 g_error(strerror(errno));
210 }
211 if ((syncState->graphsStream= fdopen(graphsFp, "w")) == NULL)
212 {
213 g_error(strerror(errno));
214 }
215
216 fprintf(syncState->graphsStream,
217 "#!/usr/bin/gnuplot\n\n"
218 "set terminal postscript eps color size 8in,6in\n");
219
220 retval= chdir(cwd);
221 if (retval == -1)
222 {
223 g_error(strerror(errno));
224 }
225 free(cwd);
226 }
227 else
228 {
229 syncState->graphsStream= NULL;
230 syncState->graphsDir= NULL;
231 }
232
233 // Identify and initialize modules
234 syncState->processingData= NULL;
235 if (optionSyncNull.present)
236 {
237 result= g_queue_find_custom(&processingModules, "LTTV-null",
238 &gcfCompareProcessing);
239 }
240 else
241 {
242 result= g_queue_find_custom(&processingModules, "LTTV-standard",
243 &gcfCompareProcessing);
244 }
245 g_assert(result != NULL);
246 syncState->processingModule= (ProcessingModule*) result->data;
247
248 syncState->matchingData= NULL;
249 result= g_queue_find_custom(&matchingModules, "TCP", &gcfCompareMatching);
250 g_assert(result != NULL);
251 syncState->matchingModule= (MatchingModule*) result->data;
252
253 syncState->analysisData= NULL;
254 result= g_queue_find_custom(&analysisModules, optionSyncAnalysis.arg,
255 &gcfCompareAnalysis);
256 if (result != NULL)
257 {
258 syncState->analysisModule= (AnalysisModule*) result->data;
259 }
260 else
261 {
262 g_error("Analysis module '%s' not found", optionSyncAnalysis.arg);
263 }
264
265 if (!optionSyncNull.present)
266 {
267 syncState->analysisModule->initAnalysis(syncState);
268 syncState->matchingModule->initMatching(syncState);
269 }
270 syncState->processingModule->initProcessing(syncState, traceSetContext);
271
272 // Process traceset
273 lttv_process_traceset_seek_time(traceSetContext, ltt_time_zero);
274 lttv_process_traceset_middle(traceSetContext, ltt_time_infinite,
275 G_MAXULONG, NULL);
276 lttv_process_traceset_seek_time(traceSetContext, ltt_time_zero);
277
278 syncState->processingModule->finalizeProcessing(syncState);
279
280 // Write graphs file
281 if (optionSyncGraphs.present)
282 {
283 writeGraphsScript(syncState);
284
285 if (fclose(syncState->graphsStream) != 0)
286 {
287 g_error(strerror(errno));
288 }
289 }
290
291 if (syncState->processingModule->printProcessingStats != NULL)
292 {
293 syncState->processingModule->printProcessingStats(syncState);
294 }
295 if (syncState->matchingModule->printMatchingStats != NULL)
296 {
297 syncState->matchingModule->printMatchingStats(syncState);
298 }
299 if (syncState->analysisModule->printAnalysisStats != NULL)
300 {
301 syncState->analysisModule->printAnalysisStats(syncState);
302 }
303
304 if (optionSyncStats.present)
305 {
306 printf("Resulting synchronization factors:\n");
307 for (i= 0; i < syncState->traceNb; i++)
308 {
309 LttTrace* t;
310
311 t= traceSetContext->traces[i]->t;
312
313 printf("\ttrace %u drift= %g offset= %g (%f) start time= %ld.%09ld\n",
314 i, t->drift, t->offset, (double) tsc_to_uint64(t->freq_scale,
315 t->start_freq, t->offset) / NANOSECONDS_PER_SECOND,
316 t->start_time_from_tsc.tv_sec,
317 t->start_time_from_tsc.tv_nsec);
318 }
319 }
320
321 syncState->processingModule->destroyProcessing(syncState);
322 if (syncState->matchingModule != NULL)
323 {
324 syncState->matchingModule->destroyMatching(syncState);
325 }
326 if (syncState->analysisModule != NULL)
327 {
328 syncState->analysisModule->destroyAnalysis(syncState);
329 }
330
331 free(syncState);
332
333 if (optionSyncStats.present)
334 {
335 gettimeofday(&endTime, 0);
336 retval= getrusage(RUSAGE_SELF, &endUsage);
337
338 timeDiff(&endTime, &startTime);
339 timeDiff(&endUsage.ru_utime, &startUsage.ru_utime);
340 timeDiff(&endUsage.ru_stime, &startUsage.ru_stime);
341
342 printf("Synchronization time:\n");
343 printf("\treal time: %ld.%06ld\n", endTime.tv_sec, endTime.tv_usec);
344 printf("\tuser time: %ld.%06ld\n", endUsage.ru_utime.tv_sec,
345 endUsage.ru_utime.tv_usec);
346 printf("\tsystem time: %ld.%06ld\n", endUsage.ru_stime.tv_sec,
347 endUsage.ru_stime.tv_usec);
348 }
349 }
350
351
352 /*
353 * Calculate the elapsed time between two timeval values
354 *
355 * Args:
356 * end: end time, result is also stored in this structure
357 * start: start time
358 */
359 void timeDiff(struct timeval* const end, const struct timeval* const start)
360 {
361 if (end->tv_usec >= start->tv_usec)
362 {
363 end->tv_sec-= start->tv_sec;
364 end->tv_usec-= start->tv_usec;
365 }
366 else
367 {
368 end->tv_sec= end->tv_sec - start->tv_sec - 1;
369 end->tv_usec= end->tv_usec - start->tv_usec + 1e6;
370 }
371 }
372
373
374 /*
375 * A GCompareFunc for g_slist_find_custom()
376 *
377 * Args:
378 * a: ProcessingModule*, element's data
379 * b: char*, user data to compare against
380 *
381 * Returns:
382 * 0 if the processing module a's name is b
383 */
384 gint gcfCompareProcessing(gconstpointer a, gconstpointer b)
385 {
386 const ProcessingModule* processingModule;
387 const char* name;
388
389 processingModule= (const ProcessingModule*) a;
390 name= (const char*) b;
391
392 return strncmp(processingModule->name, name,
393 strlen(processingModule->name) + 1);
394 }
395
396
397 /*
398 * A GCompareFunc for g_slist_find_custom()
399 *
400 * Args:
401 * a: MatchingModule*, element's data
402 * b: char*, user data to compare against
403 *
404 * Returns:
405 * 0 if the matching module a's name is b
406 */
407 gint gcfCompareMatching(gconstpointer a, gconstpointer b)
408 {
409 const MatchingModule* matchingModule;
410 const char* name;
411
412 matchingModule= (const MatchingModule*) a;
413 name= (const char*) b;
414
415 return strncmp(matchingModule->name, name, strlen(matchingModule->name) +
416 1);
417 }
418
419
420 /*
421 * A GCompareFunc for g_slist_find_custom()
422 *
423 * Args:
424 * a: AnalysisModule*, element's data
425 * b: char*, user data to compare against
426 *
427 * Returns:
428 * 0 if the analysis module a's name is b
429 */
430 gint gcfCompareAnalysis(gconstpointer a, gconstpointer b)
431 {
432 const AnalysisModule* analysisModule;
433 const char* name;
434
435 analysisModule= (const AnalysisModule*) a;
436 name= (const char*) b;
437
438 return strncmp(analysisModule->name, name, strlen(analysisModule->name) +
439 1);
440 }
441
442
443 /*
444 * A GFunc for g_queue_foreach()
445 *
446 * Concatenate analysis module names.
447 *
448 * Args:
449 * data: AnalysisModule*
450 * user_data: GString*, concatenated names
451 */
452 static void gfAppendAnalysisName(gpointer data, gpointer user_data)
453 {
454 g_string_append((GString*) user_data, ((AnalysisModule*) data)->name);
455 g_string_append((GString*) user_data, ", ");
456 }
457
458
459 /*
460 * Change to the directory used to hold graphs. Create it if necessary.
461 *
462 * Args:
463 * graph: name of directory
464 *
465 * Returns:
466 * The current working directory before the execution of the function. The
467 * string must be free'd by the caller.
468 */
469 char* changeToGraphDir(const char* const graphs)
470 {
471 int retval;
472 char* cwd;
473
474 cwd= getcwd(NULL, 0);
475 if (cwd == NULL)
476 {
477 g_error(strerror(errno));
478 }
479 while ((retval= chdir(graphs)) != 0)
480 {
481 if (errno == ENOENT)
482 {
483 retval= mkdir(graphs, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP |
484 S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH);
485 if (retval != 0)
486 {
487 g_error(strerror(errno));
488 }
489 }
490 else
491 {
492 g_error(strerror(errno));
493 }
494 }
495
496 return cwd;
497 }
498
499
500 /*
501 * A GFunc for g_queue_foreach()
502 *
503 * Args:
504 * data: ModuleOption*
505 * user_data: NULL
506 */
507 static void gfAddModuleOption(gpointer data, gpointer user_data)
508 {
509 ModuleOption* option;
510 LttvOptionType conversion[]= {
511 [NO_ARG]= LTTV_OPT_NONE,
512 [REQUIRED_ARG]= LTTV_OPT_STRING,
513 };
514
515 g_assert_cmpuint(sizeof(conversion) / sizeof(*conversion), ==,
516 HAS_ARG_COUNT);
517 option= (ModuleOption*) data;
518 lttv_option_add(option->longName, '\0', option->optionHelp,
519 option->argHelp ? option->argHelp : argHelpNone,
520 conversion[option->hasArg], &option->arg, NULL, NULL);
521 }
522
523
524 /*
525 * A GFunc for g_queue_foreach()
526 *
527 * Args:
528 * data: ModuleOption*
529 * user_data: NULL
530 */
531 static void gfRemoveModuleOption(gpointer data, gpointer user_data)
532 {
533 lttv_option_remove(((ModuleOption*) data)->longName);
534 }
535
536
537 LTTV_MODULE("sync", "Synchronize traces", \
538 "Synchronizes a traceset based on the correspondance of network events", \
539 init, destroy, "option")
This page took 0.044856 seconds and 3 git commands to generate.