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