Show the latency histograms by IP address rather than by trace
[lttv.git] / lttv / modules / text / sync_chain_batch.c
CommitLineData
9a9ca632
BP
1/* This file is part of the Linux Trace Toolkit viewer
2 * Copyright (C) 2009 Benjamin Poirier
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 <glib.h>
26#include <inttypes.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <sys/resource.h>
31#include <sys/time.h>
32#include <sys/types.h>
33#include <sys/stat.h>
34#include <unistd.h>
35
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>
2bd4b3e4 44#include <lttv/sync/sync_chain.h>
9a9ca632
BP
45#include <ltt/ltt.h>
46#include <ltt/event.h>
47#include <ltt/trace.h>
48
49
50struct TracesetChainState {
51 // uint64_t* eventNbs[LttvTraceContext*]
52 GHashTable* eventNbs;
53
54 SyncState* syncState;
55 struct timeval startTime;
56 struct rusage startUsage;
9a9ca632
BP
57};
58
59static LttvHooks* before_traceset, * before_trace, * event_hook, * after_traceset;
60
61
62static void init();
63static void destroy();
64
65static gboolean tracesetStart(void *hook_data, void *call_data);
66static gboolean traceStart(void *hook_data, void *call_data);
67static int processEveryEvent(void *hook_data, void *call_data);
68static gboolean tracesetEnd(void *hook_data, void *call_data);
69
70static void setupSyncChain(LttvTracesetContext* const traceSetContext);
71static void teardownSyncChain(LttvTracesetContext* const traceSetContext);
72
73void ghfPrintEventCount(gpointer key, gpointer value, gpointer user_data);
74void gdnDestroyUint64(gpointer data);
75
76// struct TracesetChainState* tracesetChainStates[LttvTracesetContext*]
77static GHashTable* tracesetChainStates;
78
79static LttvHooks* before_traceset, * before_trace, * event_hook, * after_traceset;
80static const struct {
81 const char const *path;
82 LttvHooks **hook;
83 LttvHook function;
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},
89};
90
91static gboolean optionEvalGraphs;
92static char* optionEvalGraphsDir;
93static char graphsDir[20];
94
95
96/*
97 * Module init function
98 */
99static void init()
100{
101 gboolean result;
102 unsigned int i;
103 LttvAttributeValue value;
104 int retval;
105 LttvIAttribute* attributes= LTTV_IATTRIBUTE(lttv_global_attributes());
106
107 tracesetChainStates= g_hash_table_new(NULL, NULL);
108
109 for (i= 0; i < sizeof(batchAnalysisHooks) / sizeof(*batchAnalysisHooks);
110 i++)
111 {
112 result= lttv_iattribute_find_by_path(attributes,
113 batchAnalysisHooks[i].path, LTTV_POINTER, &value);
114 g_assert(result);
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);
119 }
120
121 optionEvalGraphs= FALSE;
122 lttv_option_add("eval-graphs", '\0', "output gnuplot graph showing "
123 "synchronization points", "none", LTTV_OPT_NONE, &optionEvalGraphs,
124 NULL, NULL);
125
d6ee5003 126 retval= snprintf(graphsDir, sizeof(graphsDir), "eval-graphs-%d", getpid());
9a9ca632
BP
127 if (retval > sizeof(graphsDir) - 1)
128 {
129 graphsDir[sizeof(graphsDir) - 1]= '\0';
130 }
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,
134 NULL, NULL);
135}
136
137
138/*
139 * Module destroy function
140 */
141static void destroy()
142{
143 unsigned int i;
144
145 g_assert_cmpuint(g_hash_table_size(tracesetChainStates), ==, 0);
146 g_hash_table_destroy(tracesetChainStates);
147
148 for (i= 0; i < sizeof(batchAnalysisHooks) / sizeof(*batchAnalysisHooks);
149 i++)
150 {
151 lttv_hooks_remove_data(*batchAnalysisHooks[i].hook,
152 batchAnalysisHooks[i].function, NULL);
153 }
154
155 lttv_option_remove("eval-graphs");
156 lttv_option_remove("eval-graphs-dir");
157}
158
159
160/*
161 * Lttv hook function that will be called before a traceset is processed
162 *
163 * Args:
164 * hookData: NULL
165 * callData: LttvTracesetContext* at that moment
166 *
167 * Returns:
168 * FALSE Always returns FALSE, meaning to keep processing hooks
169 */
170static gboolean tracesetStart(void *hook_data, void *call_data)
171{
172 struct TracesetChainState* tracesetChainState;
173 LttvTracesetContext *tsc= (LttvTracesetContext *) call_data;
174
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);
179
180 gettimeofday(&tracesetChainState->startTime, 0);
181 getrusage(RUSAGE_SELF, &tracesetChainState->startUsage);
182
183 setupSyncChain(tsc);
184
185 return FALSE;
186}
187
188
189/*
190 * Lttv hook function that will be called before a trace is processed
191 *
192 * Args:
193 * hookData: NULL
194 * callData: LttvTraceContext* at that moment
195 *
196 * Returns:
197 * FALSE Always returns FALSE, meaning to keep processing hooks
198 */
199static gboolean traceStart(void *hook_data, void *call_data)
200{
201 struct TracesetChainState* tracesetChainState;
202 uint64_t* eventNb;
203 LttvTraceContext* tc= (LttvTraceContext*) call_data;
204 LttvTracesetContext* tsc= tc->ts_context;
205
206 tracesetChainState= g_hash_table_lookup(tracesetChainStates, tsc);
207 eventNb= malloc(sizeof(uint64_t));
208 *eventNb= 0;
209 g_hash_table_insert(tracesetChainState->eventNbs, tc, eventNb);
210
211 return FALSE;
212}
213
214
215/*
216 * Lttv hook function that is called for every event
217 *
218 * Args:
219 * hookData: NULL
220 * callData: LttvTracefileContext* at the moment of the event
221 *
222 * Returns:
223 * FALSE Always returns FALSE, meaning to keep processing hooks for
224 * this event
225 */
226static int processEveryEvent(void *hook_data, void *call_data)
227{
228 LttvTracefileContext* tfc= (LttvTracefileContext*) call_data;
229 LttvTraceContext* tc= tfc->t_context;
230 LttvTracesetContext* tsc= tc->ts_context;
231 struct TracesetChainState* tracesetChainState;
232 uint64_t* eventNb;
233
234 tracesetChainState= g_hash_table_lookup(tracesetChainStates, tsc);
235 eventNb= g_hash_table_lookup(tracesetChainState->eventNbs, tc);
236
237 (*eventNb)++;
238
239 return FALSE;
240}
241
242
243/*
244 * Lttv hook function that is called after a traceset has been processed
245 *
246 * Args:
247 * hookData: NULL
248 * callData: LttvTracefileContext* at that moment
249 *
250 * Returns:
251 * FALSE Always returns FALSE, meaning to keep processing hooks
252 */
253static gboolean tracesetEnd(void *hook_data, void *call_data)
254{
255 struct TracesetChainState* tracesetChainState;
256 LttvTracesetContext* tsc= (LttvTracesetContext*) call_data;
257 uint64_t sum= 0;
258
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,
263 &sum);
264 printf("\ttotal events: %" PRIu64 "\n", sum);
265 g_hash_table_destroy(tracesetChainState->eventNbs);
266
267 teardownSyncChain(tsc);
268
269 g_hash_table_remove(tracesetChainStates, tsc);
270
271 return FALSE;
272}
273
274
275/*
276 * Initialize modules in a sync chain. Use modules that will check
277 * the precision of time synchronization between a group of traces.
278 *
279 * Args:
280 * traceSetContext: traceset
281 */
282void setupSyncChain(LttvTracesetContext* const traceSetContext)
283{
284 struct TracesetChainState* tracesetChainState;
285 SyncState* syncState;
286 GList* result;
287 int retval;
288
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);
293
294 syncState->stats= true;
295
296 if (optionEvalGraphs)
9a9ca632
BP
297 {
298 char* cwd;
299 int graphsFp;
300
301 // Create the graph directory right away in case the module initialization
302 // functions have something to write in it.
8d7d16dd
BP
303 syncState->graphsDir= optionEvalGraphsDir;
304 cwd= changeToGraphDir(optionEvalGraphsDir);
9a9ca632
BP
305
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)
309 {
310 g_error(strerror(errno));
311 }
8d7d16dd 312 if ((syncState->graphsStream= fdopen(graphsFp, "w")) == NULL)
9a9ca632
BP
313 {
314 g_error(strerror(errno));
315 }
316
e072e1ab
BP
317 fprintf(syncState->graphsStream,
318 "#!/usr/bin/gnuplot\n\n"
319 "set terminal postscript eps color size 8in,6in\n");
320
9a9ca632
BP
321 retval= chdir(cwd);
322 if (retval == -1)
323 {
324 g_error(strerror(errno));
325 }
326 free(cwd);
327 }
8d7d16dd
BP
328 else
329 {
330 syncState->graphsStream= NULL;
331 syncState->graphsDir= NULL;
332 }
9a9ca632 333
9a9ca632 334 syncState->analysisData= NULL;
cdce23b3
BP
335 result= g_queue_find_custom(&analysisModules, "eval",
336 &gcfCompareAnalysis);
9a9ca632 337 syncState->analysisModule= (AnalysisModule*) result->data;
d4721e1a 338 syncState->analysisModule->initAnalysis(syncState);
9a9ca632 339
d4721e1a 340 syncState->matchingData= NULL;
d6ee5003
BP
341 result= g_queue_find_custom(&matchingModules, "distributor",
342 &gcfCompareMatching);
d4721e1a 343 syncState->matchingModule= (MatchingModule*) result->data;
9a9ca632 344 syncState->matchingModule->initMatching(syncState);
d4721e1a
BP
345
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);
9a9ca632
BP
351}
352
353
354/*
355 * Destroy modules in a sync chain
356 *
357 * Args:
358 * traceSetContext: traceset
359 */
360void teardownSyncChain(LttvTracesetContext* const traceSetContext)
361{
362 struct TracesetChainState* tracesetChainState;
363 SyncState* syncState;
364 struct timeval endTime;
365 struct rusage endUsage;
d6ee5003 366 unsigned int i, j;
9a9ca632
BP
367 int retval;
368
369 tracesetChainState= g_hash_table_lookup(tracesetChainStates, traceSetContext);
370 syncState= tracesetChainState->syncState;
371
372 syncState->processingModule->finalizeProcessing(syncState);
373
374 // Write graphs file
8d7d16dd 375 if (optionEvalGraphs)
9a9ca632 376 {
9a9ca632
BP
377 // Cover the upper triangular matrix, i is the reference node.
378 for (i= 0; i < syncState->traceNb; i++)
379 {
380 for (j= i + 1; j < syncState->traceNb; j++)
381 {
d6ee5003 382 long pos1, pos2, trunc;
9a9ca632 383
8d7d16dd 384 fprintf(syncState->graphsStream,
e072e1ab
BP
385 "\nreset\n"
386 "set output \"%03d-%03d.eps\"\n"
9a9ca632 387 "plot \\\n", i, j);
8d7d16dd 388 pos1= ftell(syncState->graphsStream);
9a9ca632 389
e072e1ab
BP
390 if (syncState->processingModule->writeProcessingGraphsPlots)
391 {
392 syncState->processingModule->writeProcessingGraphsPlots(syncState,
393 i, j);
394 }
395 if (syncState->matchingModule->writeMatchingGraphsPlots)
396 {
397 syncState->matchingModule->writeMatchingGraphsPlots(syncState,
398 i, j);
399 }
d6ee5003
BP
400 if (syncState->analysisModule->writeAnalysisGraphsPlots)
401 {
8d7d16dd
BP
402 syncState->analysisModule->writeAnalysisGraphsPlots(syncState,
403 i, j);
d6ee5003 404 }
9a9ca632 405
8d7d16dd
BP
406 fflush(syncState->graphsStream);
407 pos2= ftell(syncState->graphsStream);
d6ee5003
BP
408 if (pos1 != pos2)
409 {
410 // Remove the ", \\\n" from the last graph plot line
411 trunc= pos2 - 4;
412 }
413 else
414 {
415 // Remove the "plot \\\n" line to avoid creating an invalid
416 // gnuplot script
417 trunc= pos2 - 7;
418 }
419
8d7d16dd 420 if (ftruncate(fileno(syncState->graphsStream), trunc) == -1)
9a9ca632
BP
421 {
422 g_error(strerror(errno));
423 }
8d7d16dd 424 if (fseek(syncState->graphsStream, 0, SEEK_END) == -1)
9a9ca632
BP
425 {
426 g_error(strerror(errno));
427 }
428
8d7d16dd 429 fprintf(syncState->graphsStream,
9a9ca632 430 "\nset output \"%1$03d-%2$03d.eps\"\n"
d6ee5003 431 "set title \"\"\n", i, j);
9a9ca632 432
e072e1ab
BP
433 if (syncState->processingModule->writeProcessingGraphsOptions)
434 {
435 syncState->processingModule->writeProcessingGraphsOptions(syncState,
436 i, j);
437 }
438 if (syncState->matchingModule->writeMatchingGraphsOptions)
439 {
440 syncState->matchingModule->writeMatchingGraphsOptions(syncState,
441 i, j);
442 }
d6ee5003
BP
443 if (syncState->analysisModule->writeAnalysisGraphsOptions)
444 {
8d7d16dd
BP
445 syncState->analysisModule->writeAnalysisGraphsOptions(syncState,
446 i, j);
d6ee5003 447 }
9a9ca632 448
d6ee5003
BP
449 if (pos1 != pos2)
450 {
8d7d16dd 451 fprintf(syncState->graphsStream, "replot\n");
d6ee5003 452 }
9a9ca632
BP
453 }
454 }
455
8d7d16dd 456 if (fclose(syncState->graphsStream) != 0)
9a9ca632
BP
457 {
458 g_error(strerror(errno));
459 }
460 }
461
462 if (syncState->processingModule->printProcessingStats != NULL)
463 {
464 syncState->processingModule->printProcessingStats(syncState);
465 }
d6ee5003
BP
466 if (syncState->matchingModule->printMatchingStats != NULL)
467 {
468 syncState->matchingModule->printMatchingStats(syncState);
469 }
470 if (syncState->analysisModule->printAnalysisStats != NULL)
471 {
472 syncState->analysisModule->printAnalysisStats(syncState);
473 }
474
475 printf("Resulting synchronization factors:\n");
476 for (i= 0; i < syncState->traceNb; i++)
477 {
478 LttTrace* t;
479
480 t= traceSetContext->traces[i]->t;
481
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);
486 }
9a9ca632
BP
487
488 syncState->processingModule->destroyProcessing(syncState);
489 if (syncState->matchingModule != NULL)
490 {
491 syncState->matchingModule->destroyMatching(syncState);
492 }
493 if (syncState->analysisModule != NULL)
494 {
495 syncState->analysisModule->destroyAnalysis(syncState);
496 }
497
498 free(syncState);
499
500 gettimeofday(&endTime, 0);
501 retval= getrusage(RUSAGE_SELF, &endUsage);
502
503 timeDiff(&endTime, &tracesetChainState->startTime);
504 timeDiff(&endUsage.ru_utime, &tracesetChainState->startUsage.ru_utime);
505 timeDiff(&endUsage.ru_stime, &tracesetChainState->startUsage.ru_stime);
506
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);
513
514 g_hash_table_remove(tracesetChainStates, traceSetContext);
515 free(tracesetChainState);
516}
517
518
519
520/*
521 * A GHFunc function for g_hash_table_foreach()
522 *
523 * Args:
524 * key: LttvTraceContext *
525 * value: uint64_t *, event count for this trace
526 * user_data: uint64_t *, sum of the event counts
527 *
528 * Returns:
529 * Updates the sum in user_data
530 */
531void ghfPrintEventCount(gpointer key, gpointer value, gpointer user_data)
532{
533 LttvTraceContext *tc = (LttvTraceContext *) key;
534 uint64_t *eventNb = (uint64_t *)value;
535 uint64_t *sum = (uint64_t *)user_data;
536
537 printf("\t%s: %" PRIu64 "\n", g_quark_to_string(ltt_trace_name(tc->t)),
538 *eventNb);
539 *sum += *eventNb;
540}
541
542
543/*
544 * A GDestroyNotify function for g_hash_table_new_full()
545 *
546 * Args:
547 * data: TsetStats *
548 */
549void gdnDestroyUint64(gpointer data)
550{
551 free((uint64_t *) data);
552}
553
554
555LTTV_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")
This page took 0.05439 seconds and 4 git commands to generate.