1 /* This file is part of the Linux Trace Toolkit viewer
2 * Copyright (C) 2009 Benjamin Poirier
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,
30 #include <sys/resource.h>
32 #include <sys/types.h>
36 #include <lttv/attribute.h>
37 #include <lttv/filter.h>
38 #include <lttv/hook.h>
39 #include <lttv/iattribute.h>
40 #include <lttv/lttv.h>
41 #include <lttv/module.h>
42 #include <lttv/option.h>
43 #include <lttv/print.h>
44 #include <lttv/sync/sync_chain.h>
46 #include <ltt/event.h>
47 #include <ltt/trace.h>
50 struct TracesetChainState
{
51 // uint64_t* eventNbs[LttvTraceContext*]
55 struct timeval startTime
;
56 struct rusage startUsage
;
59 static LttvHooks
* before_traceset
, * before_trace
, * event_hook
, * after_traceset
;
63 static void destroy();
65 static gboolean
tracesetStart(void *hook_data
, void *call_data
);
66 static gboolean
traceStart(void *hook_data
, void *call_data
);
67 static int processEveryEvent(void *hook_data
, void *call_data
);
68 static gboolean
tracesetEnd(void *hook_data
, void *call_data
);
70 static void setupSyncChain(LttvTracesetContext
* const traceSetContext
);
71 static void teardownSyncChain(LttvTracesetContext
* const traceSetContext
);
73 void ghfPrintEventCount(gpointer key
, gpointer value
, gpointer user_data
);
74 void gdnDestroyUint64(gpointer data
);
76 // struct TracesetChainState* tracesetChainStates[LttvTracesetContext*]
77 static GHashTable
* tracesetChainStates
;
79 static LttvHooks
* before_traceset
, * before_trace
, * event_hook
, * after_traceset
;
81 const char const *path
;
84 } batchAnalysisHooks
[] = {
85 {"hooks/traceset/before", &before_traceset
, &tracesetStart
},
86 {"hooks/trace/before", &before_trace
, &traceStart
},
87 {"hooks/event", &event_hook
, &processEveryEvent
},
88 {"hooks/traceset/after", &after_traceset
, &tracesetEnd
},
91 static gboolean optionEvalGraphs
;
92 static char* optionEvalGraphsDir
;
93 static char graphsDir
[20];
97 * Module init function
103 LttvAttributeValue value
;
105 LttvIAttribute
* attributes
= LTTV_IATTRIBUTE(lttv_global_attributes());
107 tracesetChainStates
= g_hash_table_new(NULL
, NULL
);
109 for (i
= 0; i
< sizeof(batchAnalysisHooks
) / sizeof(*batchAnalysisHooks
);
112 result
= lttv_iattribute_find_by_path(attributes
,
113 batchAnalysisHooks
[i
].path
, LTTV_POINTER
, &value
);
115 *batchAnalysisHooks
[i
].hook
= *(value
.v_pointer
);
116 g_assert(*batchAnalysisHooks
[i
].hook
);
117 lttv_hooks_add(*batchAnalysisHooks
[i
].hook
,
118 batchAnalysisHooks
[i
].function
, NULL
, LTTV_PRIO_DEFAULT
);
121 optionEvalGraphs
= FALSE
;
122 lttv_option_add("eval-graphs", '\0', "output gnuplot graph showing "
123 "synchronization points", "none", LTTV_OPT_NONE
, &optionEvalGraphs
,
126 retval
= snprintf(graphsDir
, sizeof(graphsDir
), "eval-graphs-%d", getpid());
127 if (retval
> sizeof(graphsDir
) - 1)
129 graphsDir
[sizeof(graphsDir
) - 1]= '\0';
131 optionEvalGraphsDir
= graphsDir
;
132 lttv_option_add("eval-graphs-dir", '\0', "specify the directory where to"
133 " store the graphs", graphsDir
, LTTV_OPT_STRING
, &optionEvalGraphsDir
,
139 * Module destroy function
141 static void destroy()
145 g_assert_cmpuint(g_hash_table_size(tracesetChainStates
), ==, 0);
146 g_hash_table_destroy(tracesetChainStates
);
148 for (i
= 0; i
< sizeof(batchAnalysisHooks
) / sizeof(*batchAnalysisHooks
);
151 lttv_hooks_remove_data(*batchAnalysisHooks
[i
].hook
,
152 batchAnalysisHooks
[i
].function
, NULL
);
155 lttv_option_remove("eval-graphs");
156 lttv_option_remove("eval-graphs-dir");
161 * Lttv hook function that will be called before a traceset is processed
165 * callData: LttvTracesetContext* at that moment
168 * FALSE Always returns FALSE, meaning to keep processing hooks
170 static gboolean
tracesetStart(void *hook_data
, void *call_data
)
172 struct TracesetChainState
* tracesetChainState
;
173 LttvTracesetContext
*tsc
= (LttvTracesetContext
*) call_data
;
175 tracesetChainState
= malloc(sizeof(struct TracesetChainState
));
176 g_hash_table_insert(tracesetChainStates
, tsc
, tracesetChainState
);
177 tracesetChainState
->eventNbs
= g_hash_table_new_full(&g_direct_hash
,
178 &g_direct_equal
, NULL
, &gdnDestroyUint64
);
180 gettimeofday(&tracesetChainState
->startTime
, 0);
181 getrusage(RUSAGE_SELF
, &tracesetChainState
->startUsage
);
190 * Lttv hook function that will be called before a trace is processed
194 * callData: LttvTraceContext* at that moment
197 * FALSE Always returns FALSE, meaning to keep processing hooks
199 static gboolean
traceStart(void *hook_data
, void *call_data
)
201 struct TracesetChainState
* tracesetChainState
;
203 LttvTraceContext
* tc
= (LttvTraceContext
*) call_data
;
204 LttvTracesetContext
* tsc
= tc
->ts_context
;
206 tracesetChainState
= g_hash_table_lookup(tracesetChainStates
, tsc
);
207 eventNb
= malloc(sizeof(uint64_t));
209 g_hash_table_insert(tracesetChainState
->eventNbs
, tc
, eventNb
);
216 * Lttv hook function that is called for every event
220 * callData: LttvTracefileContext* at the moment of the event
223 * FALSE Always returns FALSE, meaning to keep processing hooks for
226 static int processEveryEvent(void *hook_data
, void *call_data
)
228 LttvTracefileContext
* tfc
= (LttvTracefileContext
*) call_data
;
229 LttvTraceContext
* tc
= tfc
->t_context
;
230 LttvTracesetContext
* tsc
= tc
->ts_context
;
231 struct TracesetChainState
* tracesetChainState
;
234 tracesetChainState
= g_hash_table_lookup(tracesetChainStates
, tsc
);
235 eventNb
= g_hash_table_lookup(tracesetChainState
->eventNbs
, tc
);
244 * Lttv hook function that is called after a traceset has been processed
248 * callData: LttvTracefileContext* at that moment
251 * FALSE Always returns FALSE, meaning to keep processing hooks
253 static gboolean
tracesetEnd(void *hook_data
, void *call_data
)
255 struct TracesetChainState
* tracesetChainState
;
256 LttvTracesetContext
* tsc
= (LttvTracesetContext
*) call_data
;
259 tracesetChainState
= g_hash_table_lookup(tracesetChainStates
, tsc
);
260 printf("Event count (%u traces):\n",
261 g_hash_table_size(tracesetChainState
->eventNbs
));
262 g_hash_table_foreach(tracesetChainState
->eventNbs
, &ghfPrintEventCount
,
264 printf("\ttotal events: %" PRIu64
"\n", sum
);
265 g_hash_table_destroy(tracesetChainState
->eventNbs
);
267 teardownSyncChain(tsc
);
269 g_hash_table_remove(tracesetChainStates
, tsc
);
276 * Initialize modules in a sync chain. Use modules that will check
277 * the precision of time synchronization between a group of traces.
280 * traceSetContext: traceset
282 void setupSyncChain(LttvTracesetContext
* const traceSetContext
)
284 struct TracesetChainState
* tracesetChainState
;
285 SyncState
* syncState
;
289 tracesetChainState
= g_hash_table_lookup(tracesetChainStates
, traceSetContext
);
290 syncState
= malloc(sizeof(SyncState
));
291 tracesetChainState
->syncState
= syncState
;
292 syncState
->traceNb
= lttv_traceset_number(traceSetContext
->ts
);
294 syncState
->stats
= true;
296 if (optionEvalGraphs
)
301 // Create the graph directory right away in case the module initialization
302 // functions have something to write in it.
303 syncState
->graphsDir
= optionEvalGraphsDir
;
304 cwd
= changeToGraphDir(optionEvalGraphsDir
);
306 if ((graphsFp
= open("graphs.gnu", O_WRONLY
| O_CREAT
| O_TRUNC
, S_IRUSR
|
307 S_IWUSR
| S_IXUSR
| S_IRGRP
| S_IWGRP
| S_IXGRP
| S_IROTH
308 | S_IWOTH
| S_IXOTH
)) == -1)
310 g_error(strerror(errno
));
312 if ((syncState
->graphsStream
= fdopen(graphsFp
, "w")) == NULL
)
314 g_error(strerror(errno
));
317 fprintf(syncState
->graphsStream
,
318 "#!/usr/bin/gnuplot\n\n"
319 "set terminal postscript eps color size 8in,6in\n");
324 g_error(strerror(errno
));
330 syncState
->graphsStream
= NULL
;
331 syncState
->graphsDir
= NULL
;
334 syncState
->analysisData
= NULL
;
335 result
= g_queue_find_custom(&analysisModules
, "eval",
336 &gcfCompareAnalysis
);
337 syncState
->analysisModule
= (AnalysisModule
*) result
->data
;
338 syncState
->analysisModule
->initAnalysis(syncState
);
340 syncState
->matchingData
= NULL
;
341 result
= g_queue_find_custom(&matchingModules
, "distributor",
342 &gcfCompareMatching
);
343 syncState
->matchingModule
= (MatchingModule
*) result
->data
;
344 syncState
->matchingModule
->initMatching(syncState
);
346 syncState
->processingData
= NULL
;
347 result
= g_queue_find_custom(&processingModules
, "LTTV-standard",
348 &gcfCompareProcessing
);
349 syncState
->processingModule
= (ProcessingModule
*) result
->data
;
350 syncState
->processingModule
->initProcessing(syncState
, traceSetContext
);
355 * Destroy modules in a sync chain
358 * traceSetContext: traceset
360 void teardownSyncChain(LttvTracesetContext
* const traceSetContext
)
362 struct TracesetChainState
* tracesetChainState
;
363 SyncState
* syncState
;
364 struct timeval endTime
;
365 struct rusage endUsage
;
369 tracesetChainState
= g_hash_table_lookup(tracesetChainStates
, traceSetContext
);
370 syncState
= tracesetChainState
->syncState
;
372 syncState
->processingModule
->finalizeProcessing(syncState
);
375 if (optionEvalGraphs
)
377 // Cover the upper triangular matrix, i is the reference node.
378 for (i
= 0; i
< syncState
->traceNb
; i
++)
380 for (j
= i
+ 1; j
< syncState
->traceNb
; j
++)
382 long pos1
, pos2
, trunc
;
384 fprintf(syncState
->graphsStream
,
386 "set output \"%03d-%03d.eps\"\n"
388 pos1
= ftell(syncState
->graphsStream
);
390 if (syncState
->processingModule
->writeProcessingGraphsPlots
)
392 syncState
->processingModule
->writeProcessingGraphsPlots(syncState
,
395 if (syncState
->matchingModule
->writeMatchingGraphsPlots
)
397 syncState
->matchingModule
->writeMatchingGraphsPlots(syncState
,
400 if (syncState
->analysisModule
->writeAnalysisGraphsPlots
)
402 syncState
->analysisModule
->writeAnalysisGraphsPlots(syncState
,
406 fflush(syncState
->graphsStream
);
407 pos2
= ftell(syncState
->graphsStream
);
410 // Remove the ", \\\n" from the last graph plot line
415 // Remove the "plot \\\n" line to avoid creating an invalid
420 if (ftruncate(fileno(syncState
->graphsStream
), trunc
) == -1)
422 g_error(strerror(errno
));
424 if (fseek(syncState
->graphsStream
, 0, SEEK_END
) == -1)
426 g_error(strerror(errno
));
429 fprintf(syncState
->graphsStream
,
430 "\nset output \"%1$03d-%2$03d.eps\"\n"
431 "set title \"\"\n", i
, j
);
433 if (syncState
->processingModule
->writeProcessingGraphsOptions
)
435 syncState
->processingModule
->writeProcessingGraphsOptions(syncState
,
438 if (syncState
->matchingModule
->writeMatchingGraphsOptions
)
440 syncState
->matchingModule
->writeMatchingGraphsOptions(syncState
,
443 if (syncState
->analysisModule
->writeAnalysisGraphsOptions
)
445 syncState
->analysisModule
->writeAnalysisGraphsOptions(syncState
,
451 fprintf(syncState
->graphsStream
, "replot\n");
456 if (fclose(syncState
->graphsStream
) != 0)
458 g_error(strerror(errno
));
462 if (syncState
->processingModule
->printProcessingStats
!= NULL
)
464 syncState
->processingModule
->printProcessingStats(syncState
);
466 if (syncState
->matchingModule
->printMatchingStats
!= NULL
)
468 syncState
->matchingModule
->printMatchingStats(syncState
);
470 if (syncState
->analysisModule
->printAnalysisStats
!= NULL
)
472 syncState
->analysisModule
->printAnalysisStats(syncState
);
475 printf("Resulting synchronization factors:\n");
476 for (i
= 0; i
< syncState
->traceNb
; i
++)
480 t
= traceSetContext
->traces
[i
]->t
;
482 printf("\ttrace %u drift= %g offset= %g (%f) start time= %ld.%09ld\n",
483 i
, t
->drift
, t
->offset
, (double) tsc_to_uint64(t
->freq_scale
,
484 t
->start_freq
, t
->offset
) / NANOSECONDS_PER_SECOND
,
485 t
->start_time_from_tsc
.tv_sec
, t
->start_time_from_tsc
.tv_nsec
);
488 syncState
->processingModule
->destroyProcessing(syncState
);
489 if (syncState
->matchingModule
!= NULL
)
491 syncState
->matchingModule
->destroyMatching(syncState
);
493 if (syncState
->analysisModule
!= NULL
)
495 syncState
->analysisModule
->destroyAnalysis(syncState
);
500 gettimeofday(&endTime
, 0);
501 retval
= getrusage(RUSAGE_SELF
, &endUsage
);
503 timeDiff(&endTime
, &tracesetChainState
->startTime
);
504 timeDiff(&endUsage
.ru_utime
, &tracesetChainState
->startUsage
.ru_utime
);
505 timeDiff(&endUsage
.ru_stime
, &tracesetChainState
->startUsage
.ru_stime
);
507 printf("Evaluation time:\n");
508 printf("\treal time: %ld.%06ld\n", endTime
.tv_sec
, endTime
.tv_usec
);
509 printf("\tuser time: %ld.%06ld\n", endUsage
.ru_utime
.tv_sec
,
510 endUsage
.ru_utime
.tv_usec
);
511 printf("\tsystem time: %ld.%06ld\n", endUsage
.ru_stime
.tv_sec
,
512 endUsage
.ru_stime
.tv_usec
);
514 g_hash_table_remove(tracesetChainStates
, traceSetContext
);
515 free(tracesetChainState
);
521 * A GHFunc function for g_hash_table_foreach()
524 * key: LttvTraceContext *
525 * value: uint64_t *, event count for this trace
526 * user_data: uint64_t *, sum of the event counts
529 * Updates the sum in user_data
531 void ghfPrintEventCount(gpointer key
, gpointer value
, gpointer user_data
)
533 LttvTraceContext
*tc
= (LttvTraceContext
*) key
;
534 uint64_t *eventNb
= (uint64_t *)value
;
535 uint64_t *sum
= (uint64_t *)user_data
;
537 printf("\t%s: %" PRIu64
"\n", g_quark_to_string(ltt_trace_name(tc
->t
)),
544 * A GDestroyNotify function for g_hash_table_new_full()
549 void gdnDestroyUint64(gpointer data
)
551 free((uint64_t *) data
);
555 LTTV_MODULE("sync_chain_batch", "Execute synchronization modules in a "\
556 "post-processing step.", "This can be used to quantify the precision "\
557 "with which a group of trace is synchronized.", init
, destroy
,\
558 "batchAnalysis", "option", "sync")