1 /* This file is part of the Linux Trace Toolkit viewer
2 * Copyright (C) 2009 Benjamin Poirier <benjamin.poirier@polymtl.ca>
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;
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.
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,
27 #include <sys/resource.h>
29 #include <sys/types.h>
33 #include <lttv/module.h>
34 #include <lttv/option.h>
36 #include "sync_chain.h"
40 #define g_info(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, format)
45 static void destroy();
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
);
51 GQueue processingModules
= G_QUEUE_INIT
;
52 GQueue matchingModules
= G_QUEUE_INIT
;
53 GQueue analysisModules
= G_QUEUE_INIT
;
54 GQueue moduleOptions
= G_QUEUE_INIT
;
56 static char* argHelpNone
= "none";
57 static ModuleOption optionSync
= {
61 .optionHelp
= "synchronize the time between the traces",
63 static char graphsDir
[20];
64 static ModuleOption optionSyncStats
= {
65 .longName
= "sync-stats",
68 .optionHelp
= "print statistics about the time synchronization",
70 static ModuleOption optionSyncNull
= {
71 .longName
= "sync-null",
74 .optionHelp
= "read the events but do not perform any processing",
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",
82 static ModuleOption optionSyncGraphs
= {
83 .longName
= "sync-graphs",
86 .optionHelp
= "output gnuplot graph showing synchronization points",
88 static ModuleOption optionSyncGraphsDir
= {
89 .longName
= "sync-graphs-dir",
90 .hasArg
= REQUIRED_ARG
,
91 .optionHelp
= "specify the directory where to store the graphs",
95 * Module init function
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.
109 g_debug("\t\t\tXXXX sync init\n");
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
;
121 retval
= snprintf(graphsDir
, sizeof(graphsDir
), "graphs-%d", getpid());
122 if (retval
> sizeof(graphsDir
) - 1)
124 graphsDir
[sizeof(graphsDir
) - 1]= '\0';
126 optionSyncGraphsDir
.arg
= graphsDir
;
127 optionSyncGraphsDir
.argHelp
= graphsDir
;
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
);
136 g_queue_foreach(&moduleOptions
, &gfAddModuleOption
, NULL
);
142 * Module unload function
144 static void destroy()
146 g_debug("\t\t\tXXXX sync destroy\n");
148 g_queue_foreach(&moduleOptions
, &gfRemoveModuleOption
, NULL
);
149 g_string_free(analysisModulesNames
, TRUE
);
151 g_queue_clear(&processingModules
);
152 g_queue_clear(&matchingModules
);
153 g_queue_clear(&analysisModules
);
154 g_queue_clear(&moduleOptions
);
159 * Calculate a traceset's drift and offset values based on network events
161 * The individual correction factors are written out to each trace.
164 * traceSetContext: traceset
166 void syncTraceset(LttvTracesetContext
* const traceSetContext
)
168 SyncState
* syncState
;
169 struct timeval startTime
, endTime
;
170 struct rusage startUsage
, endUsage
;
175 if (!optionSync
.present
)
177 g_debug("Not synchronizing traceset because option is disabled");
181 if (optionSyncStats
.present
)
183 gettimeofday(&startTime
, 0);
184 getrusage(RUSAGE_SELF
, &startUsage
);
187 // Initialize data structures
188 syncState
= malloc(sizeof(SyncState
));
189 syncState
->traceNb
= lttv_traceset_number(traceSetContext
->ts
);
191 if (optionSyncStats
.present
)
193 syncState
->stats
= true;
197 syncState
->stats
= false;
200 if (optionSyncGraphs
.present
)
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
);
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)
214 g_error(strerror(errno
));
216 if ((syncState
->graphsStream
= fdopen(graphsFp
, "w")) == NULL
)
218 g_error(strerror(errno
));
224 g_error(strerror(errno
));
230 syncState
->graphsStream
= NULL
;
231 syncState
->graphsDir
= NULL
;
234 // Identify and initialize modules
235 syncState
->processingData
= NULL
;
236 if (optionSyncNull
.present
)
238 result
= g_queue_find_custom(&processingModules
, "LTTV-null",
239 &gcfCompareProcessing
);
243 result
= g_queue_find_custom(&processingModules
, "LTTV-standard",
244 &gcfCompareProcessing
);
246 g_assert(result
!= NULL
);
247 syncState
->processingModule
= (ProcessingModule
*) result
->data
;
249 syncState
->matchingData
= NULL
;
250 result
= g_queue_find_custom(&matchingModules
, "TCP", &gcfCompareMatching
);
251 g_assert(result
!= NULL
);
252 syncState
->matchingModule
= (MatchingModule
*) result
->data
;
254 syncState
->analysisData
= NULL
;
255 result
= g_queue_find_custom(&analysisModules
, optionSyncAnalysis
.arg
,
256 &gcfCompareAnalysis
);
259 syncState
->analysisModule
= (AnalysisModule
*) result
->data
;
263 g_error("Analysis module '%s' not found", optionSyncAnalysis
.arg
);
266 if (!optionSyncNull
.present
)
268 syncState
->analysisModule
->initAnalysis(syncState
);
269 syncState
->matchingModule
->initMatching(syncState
);
271 syncState
->processingModule
->initProcessing(syncState
, traceSetContext
);
274 lttv_process_traceset_seek_time(traceSetContext
, ltt_time_zero
);
275 lttv_process_traceset_middle(traceSetContext
, ltt_time_infinite
,
277 lttv_process_traceset_seek_time(traceSetContext
, ltt_time_zero
);
279 syncState
->processingModule
->finalizeProcessing(syncState
);
282 if (optionSyncGraphs
.present
)
284 fprintf(syncState
->graphsStream
,
285 "#!/usr/bin/gnuplot\n\n"
286 "set terminal postscript eps color size 8in,6in\n");
288 // Cover the upper triangular matrix, i is the reference node.
289 for (i
= 0; i
< syncState
->traceNb
; i
++)
291 for (j
= i
+ 1; j
< syncState
->traceNb
; j
++)
293 long pos1
, pos2
, trunc
;
295 fprintf(syncState
->graphsStream
,
296 "\nset output \"%03d-%03d.eps\"\n"
299 if (syncState
->processingModule
->writeProcessingGraphsPlots
)
301 syncState
->processingModule
->writeProcessingGraphsPlots(syncState
,
304 if (syncState
->matchingModule
->writeMatchingGraphsPlots
)
306 syncState
->matchingModule
->writeMatchingGraphsPlots(syncState
,
309 if (syncState
->analysisModule
->writeAnalysisGraphsPlots
)
311 syncState
->analysisModule
->writeAnalysisGraphsPlots(syncState
,
315 fflush(syncState
->graphsStream
);
316 pos2
= ftell(syncState
->graphsStream
);
319 // Remove the ", \\\n" from the last graph plot line
324 // Remove the "plot \\\n" line to avoid creating an invalid
329 if (ftruncate(fileno(syncState
->graphsStream
), trunc
) == -1)
331 g_error(strerror(errno
));
333 if (fseek(syncState
->graphsStream
, 0, SEEK_END
) == -1)
335 g_error(strerror(errno
));
338 fprintf(syncState
->graphsStream
,
339 "\nset output \"%03d-%03d.eps\"\n"
340 "set title \"\"\n", i
, j
);
342 if (syncState
->processingModule
->writeProcessingGraphsOptions
)
344 syncState
->processingModule
->writeProcessingGraphsOptions(syncState
,
347 if (syncState
->matchingModule
->writeMatchingGraphsOptions
)
349 syncState
->matchingModule
->writeMatchingGraphsOptions(syncState
,
352 if (syncState
->analysisModule
->writeAnalysisGraphsOptions
)
354 syncState
->analysisModule
->writeAnalysisGraphsOptions(syncState
,
360 fprintf(syncState
->graphsStream
, "replot\n");
365 if (fclose(syncState
->graphsStream
) != 0)
367 g_error(strerror(errno
));
371 if (syncState
->processingModule
->printProcessingStats
!= NULL
)
373 syncState
->processingModule
->printProcessingStats(syncState
);
375 if (syncState
->matchingModule
->printMatchingStats
!= NULL
)
377 syncState
->matchingModule
->printMatchingStats(syncState
);
379 if (syncState
->analysisModule
->printAnalysisStats
!= NULL
)
381 syncState
->analysisModule
->printAnalysisStats(syncState
);
384 if (optionSyncStats
.present
)
386 printf("Resulting synchronization factors:\n");
387 for (i
= 0; i
< syncState
->traceNb
; i
++)
391 t
= traceSetContext
->traces
[i
]->t
;
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
);
401 syncState
->processingModule
->destroyProcessing(syncState
);
402 if (syncState
->matchingModule
!= NULL
)
404 syncState
->matchingModule
->destroyMatching(syncState
);
406 if (syncState
->analysisModule
!= NULL
)
408 syncState
->analysisModule
->destroyAnalysis(syncState
);
413 if (optionSyncStats
.present
)
415 gettimeofday(&endTime
, 0);
416 retval
= getrusage(RUSAGE_SELF
, &endUsage
);
418 timeDiff(&endTime
, &startTime
);
419 timeDiff(&endUsage
.ru_utime
, &startUsage
.ru_utime
);
420 timeDiff(&endUsage
.ru_stime
, &startUsage
.ru_stime
);
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
);
433 * Calculate the elapsed time between two timeval values
436 * end: end time, result is also stored in this structure
439 void timeDiff(struct timeval
* const end
, const struct timeval
* const start
)
441 if (end
->tv_usec
>= start
->tv_usec
)
443 end
->tv_sec
-= start
->tv_sec
;
444 end
->tv_usec
-= start
->tv_usec
;
448 end
->tv_sec
= end
->tv_sec
- start
->tv_sec
- 1;
449 end
->tv_usec
= end
->tv_usec
- start
->tv_usec
+ 1e6
;
455 * A GCompareFunc for g_slist_find_custom()
458 * a: ProcessingModule*, element's data
459 * b: char*, user data to compare against
462 * 0 if the processing module a's name is b
464 gint
gcfCompareProcessing(gconstpointer a
, gconstpointer b
)
466 const ProcessingModule
* processingModule
;
469 processingModule
= (const ProcessingModule
*) a
;
470 name
= (const char*) b
;
472 return strncmp(processingModule
->name
, name
,
473 strlen(processingModule
->name
) + 1);
478 * A GCompareFunc for g_slist_find_custom()
481 * a: MatchingModule*, element's data
482 * b: char*, user data to compare against
485 * 0 if the matching module a's name is b
487 gint
gcfCompareMatching(gconstpointer a
, gconstpointer b
)
489 const MatchingModule
* matchingModule
;
492 matchingModule
= (const MatchingModule
*) a
;
493 name
= (const char*) b
;
495 return strncmp(matchingModule
->name
, name
, strlen(matchingModule
->name
) +
501 * A GCompareFunc for g_slist_find_custom()
504 * a: AnalysisModule*, element's data
505 * b: char*, user data to compare against
508 * 0 if the analysis module a's name is b
510 gint
gcfCompareAnalysis(gconstpointer a
, gconstpointer b
)
512 const AnalysisModule
* analysisModule
;
515 analysisModule
= (const AnalysisModule
*) a
;
516 name
= (const char*) b
;
518 return strncmp(analysisModule
->name
, name
, strlen(analysisModule
->name
) +
524 * A GFunc for g_queue_foreach()
526 * Concatenate analysis module names.
529 * data: AnalysisModule*
530 * user_data: GString*, concatenated names
532 static void gfAppendAnalysisName(gpointer data
, gpointer user_data
)
534 g_string_append((GString
*) user_data
, ((AnalysisModule
*) data
)->name
);
535 g_string_append((GString
*) user_data
, ", ");
540 * Change to the directory used to hold graphs. Create it if necessary.
543 * graph: name of directory
546 * The current working directory before the execution of the function. The
547 * string must be free'd by the caller.
549 char* changeToGraphDir(const char* const graphs
)
554 cwd
= getcwd(NULL
, 0);
557 g_error(strerror(errno
));
559 while ((retval
= chdir(graphs
)) != 0)
563 retval
= mkdir(graphs
, S_IRUSR
| S_IWUSR
| S_IXUSR
| S_IRGRP
|
564 S_IWGRP
| S_IXGRP
| S_IROTH
| S_IWOTH
| S_IXOTH
);
567 g_error(strerror(errno
));
572 g_error(strerror(errno
));
581 * A GFunc for g_queue_foreach()
584 * data: ModuleOption*
587 static void gfAddModuleOption(gpointer data
, gpointer user_data
)
589 ModuleOption
* option
;
590 LttvOptionType conversion
[]= {
591 [NO_ARG
]= LTTV_OPT_NONE
,
592 [REQUIRED_ARG
]= LTTV_OPT_STRING
,
595 g_assert_cmpuint(sizeof(conversion
) / sizeof(*conversion
), ==,
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
);
605 * A GFunc for g_queue_foreach()
608 * data: ModuleOption*
611 static void gfRemoveModuleOption(gpointer data
, gpointer user_data
)
613 lttv_option_remove(((ModuleOption
*) data
)->longName
);
617 LTTV_MODULE("sync", "Synchronize traces", \
618 "Synchronizes a traceset based on the correspondance of network events", \
619 init
, destroy
, "option")