0180e3b01a0ee54e1c2c3de64a6eafb66d0120c2
[lttv.git] / lttv / lttv / sync / sync_chain_lttv.c
1 /* This file is part of the Linux Trace Toolkit viewer
2 * Copyright (C) 2009, 2010 Benjamin Poirier <benjamin.poirier@polymtl.ca>
3 *
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.
8 *
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.
13 *
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/>.
16 */
17
18 #define _ISOC99_SOURCE
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <math.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <sys/resource.h>
30 #include <sys/stat.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <unistd.h>
34
35 #include <lttv/module.h>
36 #include <lttv/option.h>
37
38
39 #include "event_processing_lttng_standard.h"
40 #include "event_processing_lttng_null.h"
41 #include "event_matching_tcp.h"
42 #include "event_matching_broadcast.h"
43 #include "event_matching_distributor.h"
44 #include "event_analysis_chull.h"
45 #include "event_analysis_linreg.h"
46 #include "event_analysis_eval.h"
47 #include "sync_chain.h"
48 #include "sync_chain_lttv.h"
49
50
51 static void init();
52 static void destroy();
53
54 static void gfAddModuleOption(gpointer data, gpointer user_data);
55 static void gfRemoveModuleOption(gpointer data, gpointer user_data);
56
57 static ModuleOption optionSync= {
58 .longName= "sync",
59 .hasArg= NO_ARG,
60 .optionHelp= "synchronize the time between the traces",
61 };
62 static ModuleOption optionSyncStats= {
63 .longName= "sync-stats",
64 .hasArg= NO_ARG,
65 .optionHelp= "print statistics about the time synchronization",
66 };
67 static ModuleOption optionSyncNull= {
68 .longName= "sync-null",
69 .hasArg= NO_ARG,
70 .optionHelp= "read the events but do not perform any processing",
71 };
72 static GString* analysisModulesNames;
73 static ModuleOption optionSyncAnalysis= {
74 .longName= "sync-analysis",
75 .hasArg= REQUIRED_ARG,
76 .optionHelp= "specify the algorithm to use for event analysis",
77 };
78 static ModuleOption optionSyncGraphs= {
79 .longName= "sync-graphs",
80 .hasArg= NO_ARG,
81 .optionHelp= "output gnuplot graph showing synchronization points",
82 };
83 static char graphsDir[20];
84 static ModuleOption optionSyncGraphsDir= {
85 .longName= "sync-graphs-dir",
86 .hasArg= REQUIRED_ARG,
87 .optionHelp= "specify the directory where to store the graphs",
88 };
89
90
91 /*
92 * Module init function
93 *
94 * This function is declared to be the module initialization function.
95 */
96 static void init()
97 {
98 int retval;
99
100 g_debug("Sync init");
101
102 /*
103 * Initialize event modules
104 * Call the "constructor" or initialization function of each event module
105 * so it can register itself. This must be done before elements in
106 * processingModules, matchingModules, analysisModules or moduleOptions
107 * are accessed.
108 */
109 registerProcessingLTTVStandard();
110 registerProcessingLTTVNull();
111
112 registerMatchingTCP();
113 registerMatchingBroadcast();
114 registerMatchingDistributor();
115
116 registerAnalysisCHull();
117 registerAnalysisLinReg();
118 registerAnalysisEval();
119
120 g_assert(g_queue_get_length(&analysisModules) > 0);
121 optionSyncAnalysis.arg= ((AnalysisModule*)
122 g_queue_peek_head(&analysisModules))->name;
123 analysisModulesNames= g_string_new("");
124 g_queue_foreach(&analysisModules, &gfAppendAnalysisName,
125 analysisModulesNames);
126 // remove the last ", "
127 g_string_truncate(analysisModulesNames, analysisModulesNames->len - 2);
128 optionSyncAnalysis.argHelp= analysisModulesNames->str;
129
130 retval= snprintf(graphsDir, sizeof(graphsDir), "graphs-%d", getpid());
131 if (retval > sizeof(graphsDir) - 1)
132 {
133 graphsDir[sizeof(graphsDir) - 1]= '\0';
134 }
135 optionSyncGraphsDir.arg= graphsDir;
136 optionSyncGraphsDir.argHelp= graphsDir;
137
138 g_queue_push_head(&moduleOptions, &optionSyncGraphsDir);
139 g_queue_push_head(&moduleOptions, &optionSyncGraphs);
140 g_queue_push_head(&moduleOptions, &optionSyncAnalysis);
141 g_queue_push_head(&moduleOptions, &optionSyncNull);
142 g_queue_push_head(&moduleOptions, &optionSyncStats);
143 g_queue_push_head(&moduleOptions, &optionSync);
144
145 g_queue_foreach(&moduleOptions, &gfAddModuleOption, NULL);
146 }
147
148
149 /*
150 * Module unload function
151 */
152 static void destroy()
153 {
154 g_debug("Sync destroy");
155
156 g_queue_foreach(&moduleOptions, &gfRemoveModuleOption, NULL);
157 g_string_free(analysisModulesNames, TRUE);
158
159 g_queue_clear(&processingModules);
160 g_queue_clear(&matchingModules);
161 g_queue_clear(&analysisModules);
162 g_queue_clear(&moduleOptions);
163 }
164
165
166 /*
167 * Calculate a traceset's drift and offset values based on network events
168 *
169 * The individual correction factors are written out to each trace.
170 *
171 * Args:
172 * traceSetContext: traceset
173 *
174 * Returns:
175 * false if synchronization was not performed, true otherwise
176 */
177 bool syncTraceset(LttvTracesetContext* const traceSetContext)
178 {
179 SyncState* syncState;
180 struct timeval startTime, endTime;
181 struct rusage startUsage, endUsage;
182 GList* result;
183 unsigned int i;
184 GArray* factors;
185 double minOffset, minDrift;
186 unsigned int refFreqTrace;
187 int retval;
188
189 if (!optionSync.present)
190 {
191 g_debug("Not synchronizing traceset because option is disabled");
192 return false;
193 }
194
195 if (optionSyncStats.present)
196 {
197 gettimeofday(&startTime, 0);
198 getrusage(RUSAGE_SELF, &startUsage);
199 }
200
201 // Initialize data structures
202 syncState= malloc(sizeof(SyncState));
203
204 if (optionSyncStats.present)
205 {
206 syncState->stats= true;
207 }
208 else
209 {
210 syncState->stats= false;
211 }
212
213 if (!optionSyncNull.present && optionSyncGraphs.present)
214 {
215 // Create the graph directory right away in case the module initialization
216 // functions have something to write in it.
217 syncState->graphsDir= optionSyncGraphsDir.arg;
218 syncState->graphsStream= createGraphsDir(syncState->graphsDir);
219 }
220 else
221 {
222 syncState->graphsStream= NULL;
223 syncState->graphsDir= NULL;
224 }
225
226 // Identify and initialize modules
227 syncState->processingData= NULL;
228 if (optionSyncNull.present)
229 {
230 result= g_queue_find_custom(&processingModules, "LTTV-null",
231 &gcfCompareProcessing);
232 }
233 else
234 {
235 result= g_queue_find_custom(&processingModules, "LTTV-standard",
236 &gcfCompareProcessing);
237 }
238 g_assert(result != NULL);
239 syncState->processingModule= (ProcessingModule*) result->data;
240
241 syncState->matchingData= NULL;
242 result= g_queue_find_custom(&matchingModules, "TCP", &gcfCompareMatching);
243 g_assert(result != NULL);
244 syncState->matchingModule= (MatchingModule*) result->data;
245
246 syncState->analysisData= NULL;
247 result= g_queue_find_custom(&analysisModules, optionSyncAnalysis.arg,
248 &gcfCompareAnalysis);
249 if (result != NULL)
250 {
251 syncState->analysisModule= (AnalysisModule*) result->data;
252 }
253 else
254 {
255 g_error("Analysis module '%s' not found", optionSyncAnalysis.arg);
256 }
257
258 syncState->processingModule->initProcessing(syncState, traceSetContext);
259 if (!optionSyncNull.present)
260 {
261 syncState->matchingModule->initMatching(syncState);
262 syncState->analysisModule->initAnalysis(syncState);
263 }
264
265 // Process traceset
266 lttv_process_traceset_seek_time(traceSetContext, ltt_time_zero);
267 lttv_process_traceset_middle(traceSetContext, ltt_time_infinite,
268 G_MAXULONG, NULL);
269 lttv_process_traceset_seek_time(traceSetContext, ltt_time_zero);
270
271 // Obtain, adjust and set correction factors
272 factors= syncState->processingModule->finalizeProcessing(syncState);
273
274 /* The offsets are adjusted so the lowest one is 0. This is done because
275 * of a Lttv specific limitation: events cannot have negative times. By
276 * having non-negative offsets, events cannot be moved backwards to
277 * negative times.
278 */
279 minOffset= 0;
280 for (i= 0; i < syncState->traceNb; i++)
281 {
282 minOffset= MIN(g_array_index(factors, Factors, i).offset, minOffset);
283 }
284
285 for (i= 0; i < syncState->traceNb; i++)
286 {
287 g_array_index(factors, Factors, i).offset-= minOffset;
288 }
289
290 /* Because the timestamps are corrected at the TSC level (not at the
291 * LttTime level) all trace frequencies must be made equal. We use the
292 * frequency of the system with the lowest drift
293 */
294 minDrift= INFINITY;
295 refFreqTrace= 0;
296 for (i= 0; i < syncState->traceNb; i++)
297 {
298 if (g_array_index(factors, Factors, i).drift < minDrift)
299 {
300 minDrift= g_array_index(factors, Factors, i).drift;
301 refFreqTrace= i;
302 }
303 }
304 g_assert(syncState->traceNb == 0 || minDrift != INFINITY);
305
306 // Write the factors to the LttTrace structures
307 for (i= 0; i < syncState->traceNb; i++)
308 {
309 LttTrace* t;
310 Factors* traceFactors;
311
312 t= traceSetContext->traces[i]->t;
313 traceFactors= &g_array_index(factors, Factors, i);
314
315 t->drift= traceFactors->drift;
316 t->offset= traceFactors->offset;
317 t->start_freq= traceSetContext->traces[refFreqTrace]->t->start_freq;
318 t->freq_scale= traceSetContext->traces[refFreqTrace]->t->freq_scale;
319 t->start_time_from_tsc =
320 ltt_time_from_uint64(tsc_to_uint64(t->freq_scale, t->start_freq,
321 t->drift * t->start_tsc + t->offset));
322 }
323
324 g_array_free(factors, TRUE);
325
326 lttv_traceset_context_compute_time_span(traceSetContext,
327 &traceSetContext->time_span);
328
329 g_debug("traceset start %ld.%09ld end %ld.%09ld",
330 traceSetContext->time_span.start_time.tv_sec,
331 traceSetContext->time_span.start_time.tv_nsec,
332 traceSetContext->time_span.end_time.tv_sec,
333 traceSetContext->time_span.end_time.tv_nsec);
334
335 // Write graphs file
336 if (!optionSyncNull.present && optionSyncGraphs.present)
337 {
338 writeGraphsScript(syncState);
339
340 if (fclose(syncState->graphsStream) != 0)
341 {
342 g_error(strerror(errno));
343 }
344 }
345
346 if (!optionSyncNull.present && optionSyncStats.present)
347 {
348 printStats(syncState);
349
350 printf("Resulting synchronization factors:\n");
351 for (i= 0; i < syncState->traceNb; i++)
352 {
353 LttTrace* t;
354
355 t= traceSetContext->traces[i]->t;
356
357 printf("\ttrace %u drift= %g offset= %g (%f) start time= %ld.%09ld\n",
358 i, t->drift, t->offset, (double) tsc_to_uint64(t->freq_scale,
359 t->start_freq, t->offset) / NANOSECONDS_PER_SECOND,
360 t->start_time_from_tsc.tv_sec,
361 t->start_time_from_tsc.tv_nsec);
362 }
363 }
364
365 syncState->processingModule->destroyProcessing(syncState);
366 if (syncState->matchingModule != NULL)
367 {
368 syncState->matchingModule->destroyMatching(syncState);
369 }
370 if (syncState->analysisModule != NULL)
371 {
372 syncState->analysisModule->destroyAnalysis(syncState);
373 }
374
375 free(syncState);
376
377 if (optionSyncStats.present)
378 {
379 gettimeofday(&endTime, 0);
380 retval= getrusage(RUSAGE_SELF, &endUsage);
381
382 timeDiff(&endTime, &startTime);
383 timeDiff(&endUsage.ru_utime, &startUsage.ru_utime);
384 timeDiff(&endUsage.ru_stime, &startUsage.ru_stime);
385
386 printf("Synchronization time:\n");
387 printf("\treal time: %ld.%06ld\n", endTime.tv_sec, endTime.tv_usec);
388 printf("\tuser time: %ld.%06ld\n", endUsage.ru_utime.tv_sec,
389 endUsage.ru_utime.tv_usec);
390 printf("\tsystem time: %ld.%06ld\n", endUsage.ru_stime.tv_sec,
391 endUsage.ru_stime.tv_usec);
392 }
393
394 return true;
395 }
396
397
398 /*
399 * A GFunc for g_queue_foreach()
400 *
401 * Args:
402 * data: ModuleOption*
403 * user_data: NULL
404 */
405 static void gfAddModuleOption(gpointer data, gpointer user_data)
406 {
407 ModuleOption* option= data;
408 LttvOptionType conversion[]= {
409 [NO_ARG]= LTTV_OPT_NONE,
410 [OPTIONAL_ARG]= LTTV_OPT_NONE,
411 [REQUIRED_ARG]= LTTV_OPT_STRING,
412 };
413 size_t fieldOffset[]= {
414 [NO_ARG]= offsetof(ModuleOption, present),
415 [REQUIRED_ARG]= offsetof(ModuleOption, arg),
416 };
417 static const char* argHelpNone= "none";
418
419 g_assert_cmpuint(sizeof(conversion) / sizeof(*conversion), ==,
420 HAS_ARG_COUNT);
421 if (option->hasArg == OPTIONAL_ARG)
422 {
423 g_warning("Parameters with optional arguments not supported by the "
424 "lttv option scheme, parameter '%s' will not be available",
425 option->longName);
426 }
427 else
428 {
429 lttv_option_add(option->longName, '\0', option->optionHelp,
430 option->argHelp ? option->argHelp : argHelpNone,
431 conversion[option->hasArg], (void*) option + fieldOffset[option->hasArg],
432 NULL, NULL);
433 }
434 }
435
436
437 /*
438 * A GFunc for g_queue_foreach()
439 *
440 * Args:
441 * data: ModuleOption*
442 * user_data: NULL
443 */
444 static void gfRemoveModuleOption(gpointer data, gpointer user_data)
445 {
446 lttv_option_remove(((ModuleOption*) data)->longName);
447 }
448
449
450 LTTV_MODULE("sync", "Synchronize traces", \
451 "Synchronizes a traceset based on the correspondance of network events", \
452 init, destroy, "option")
This page took 0.041786 seconds and 3 git commands to generate.