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