Add a unittest program for clock synchronization modules
[lttv.git] / lttv / lttv / sync / event_processing_lttv_standard.c
CommitLineData
70407e86
BP
1/* This file is part of the Linux Trace Toolkit viewer
2 * Copyright (C) 2009 Benjamin Poirier <benjamin.poirier@polymtl.ca>
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#define _ISOC99_SOURCE
20
21#ifdef HAVE_CONFIG_H
22#include <config.h>
23#endif
24
25#include <linux/if_ether.h>
26#include <math.h>
27#include <netinet/in.h>
28#include <stdint.h>
29#include <stdlib.h>
30
31#include "sync_chain.h"
32#include "event_processing_lttv_common.h"
33
34#include "event_processing_lttv_standard.h"
35
36
37#ifndef g_info
38#define g_info(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, format)
39#endif
40
41
08365995 42// Functions common to all processing modules
70407e86
BP
43static void initProcessingLTTVStandard(SyncState* const syncState,
44 LttvTracesetContext* const traceSetContext);
45static void destroyProcessingLTTVStandard(SyncState* const syncState);
46
47static void finalizeProcessingLTTVStandard(SyncState* const syncState);
48static void printProcessingStatsLTTVStandard(SyncState* const syncState);
08365995
BP
49static void writeProcessingGraphsPlotsLTTVStandard(FILE* stream, SyncState*
50 const syncState, const unsigned int i, const unsigned int j);
51static void writeProcessingGraphsOptionsLTTVStandard(FILE* stream, SyncState*
52 const syncState, const unsigned int i, const unsigned int j);
70407e86
BP
53
54// Functions specific to this module
55static void registerProcessingLTTVStandard() __attribute__((constructor (102)));
56static gboolean processEventLTTVStandard(void* hookData, void* callData);
57static void partialDestroyProcessingLTTVStandard(SyncState* const syncState);
58
59
60static ProcessingModule processingModuleLTTVStandard = {
61 .name= "LTTV-standard",
62 .initProcessing= &initProcessingLTTVStandard,
63 .destroyProcessing= &destroyProcessingLTTVStandard,
64 .finalizeProcessing= &finalizeProcessingLTTVStandard,
65 .printProcessingStats= &printProcessingStatsLTTVStandard,
08365995
BP
66 .writeProcessingGraphsPlots= &writeProcessingGraphsPlotsLTTVStandard,
67 .writeProcessingGraphsOptions= &writeProcessingGraphsOptionsLTTVStandard,
70407e86
BP
68};
69
70
71
72/*
73 * Processing Module registering function
74 */
75static void registerProcessingLTTVStandard()
76{
77 g_queue_push_tail(&processingModules, &processingModuleLTTVStandard);
78
79 createQuarks();
80}
81
82
83/*
84 * Allocate and initialize data structures for synchronizing a traceset.
85 * Register event hooks.
86 *
87 * Args:
88 * syncState: container for synchronization data.
89 * This function allocates these processingData members:
90 * traceNumTable
91 * pendingRecv
92 * hookListList
93 * stats
94 * traceSetContext: set of LTTV traces
95 */
96static void initProcessingLTTVStandard(SyncState* const syncState, LttvTracesetContext*
97 const traceSetContext)
98{
99 unsigned int i;
100 ProcessingDataLTTVStandard* processingData;
101
102 processingData= malloc(sizeof(ProcessingDataLTTVStandard));
103 syncState->processingData= processingData;
104 processingData->traceSetContext= traceSetContext;
105
106 if (syncState->stats)
107 {
108 processingData->stats= calloc(1, sizeof(ProcessingStatsLTTVStandard));
109 }
110 else
111 {
112 processingData->stats= NULL;
113 }
114
115 processingData->traceNumTable= g_hash_table_new(&g_direct_hash, NULL);
116 processingData->hookListList= g_array_sized_new(FALSE, FALSE,
117 sizeof(GArray*), syncState->traceNb);
118 processingData->pendingRecv= malloc(sizeof(GHashTable*) *
119 syncState->traceNb);
120
121 for(i= 0; i < syncState->traceNb; i++)
122 {
123 g_hash_table_insert(processingData->traceNumTable,
124 processingData->traceSetContext->traces[i]->t, (gpointer) i);
125 }
126
127 for(i= 0; i < syncState->traceNb; i++)
128 {
129 processingData->pendingRecv[i]= g_hash_table_new_full(&g_direct_hash,
130 NULL, NULL, &gdnDestroyNetEvent);
131 }
132
133 registerHooks(processingData->hookListList, traceSetContext,
08365995 134 &processEventLTTVStandard, syncState);
70407e86
BP
135}
136
137
138/*
139 * Call the partial processing destroyer, obtain and adjust the factors from
140 * downstream
141 *
142 * Args:
143 * syncState container for synchronization data.
144 */
145static void finalizeProcessingLTTVStandard(SyncState* const syncState)
146{
147 unsigned int i;
148 GArray* factors;
149 double minOffset, minDrift;
150 unsigned int refFreqTrace;
151 ProcessingDataLTTVStandard* processingData;
152
153 processingData= (ProcessingDataLTTVStandard*) syncState->processingData;
154
155 partialDestroyProcessingLTTVStandard(syncState);
156
157 factors= syncState->matchingModule->finalizeMatching(syncState);
158
159 /* The offsets are adjusted so the lowest one is 0. This is done because
160 * of a Lttv specific limitation: events cannot have negative times. By
161 * having non-negative offsets, events cannot be moved backwards to
162 * negative times.
163 */
164 minOffset= 0;
165 for (i= 0; i < syncState->traceNb; i++)
166 {
167 minOffset= MIN(g_array_index(factors, Factors, i).offset, minOffset);
168 }
169
170 for (i= 0; i < syncState->traceNb; i++)
171 {
172 g_array_index(factors, Factors, i).offset-= minOffset;
173 }
174
175 /* Because the timestamps are corrected at the TSC level (not at the
176 * LttTime level) all trace frequencies must be made equal. We choose to
177 * use the frequency of the system with the lowest drift
178 */
179 minDrift= INFINITY;
180 refFreqTrace= 0;
181 for (i= 0; i < syncState->traceNb; i++)
182 {
183 if (g_array_index(factors, Factors, i).drift < minDrift)
184 {
185 minDrift= g_array_index(factors, Factors, i).drift;
186 refFreqTrace= i;
187 }
188 }
189 g_assert(syncState->traceNb == 0 || minDrift != INFINITY);
190
191 // Write the factors to the LttTrace structures
192 for (i= 0; i < syncState->traceNb; i++)
193 {
194 LttTrace* t;
195 Factors* traceFactors;
196
197 t= processingData->traceSetContext->traces[i]->t;
198 traceFactors= &g_array_index(factors, Factors, i);
199
200 t->drift= traceFactors->drift;
201 t->offset= traceFactors->offset;
202 t->start_freq=
203 processingData->traceSetContext->traces[refFreqTrace]->t->start_freq;
204 t->freq_scale=
205 processingData->traceSetContext->traces[refFreqTrace]->t->freq_scale;
206 t->start_time_from_tsc =
207 ltt_time_from_uint64(tsc_to_uint64(t->freq_scale, t->start_freq,
208 t->drift * t->start_tsc + t->offset));
209 }
210
211 g_array_free(factors, TRUE);
212
213 lttv_traceset_context_compute_time_span(processingData->traceSetContext,
214 &processingData->traceSetContext->time_span);
215
216 g_debug("traceset start %ld.%09ld end %ld.%09ld\n",
217 processingData->traceSetContext->time_span.start_time.tv_sec,
218 processingData->traceSetContext->time_span.start_time.tv_nsec,
219 processingData->traceSetContext->time_span.end_time.tv_sec,
220 processingData->traceSetContext->time_span.end_time.tv_nsec);
221
222 return;
223}
224
225
226/*
227 * Print statistics related to processing and downstream modules. Must be
228 * called after finalizeProcessing.
229 *
230 * Args:
231 * syncState container for synchronization data.
232 */
233static void printProcessingStatsLTTVStandard(SyncState* const syncState)
234{
235 unsigned int i;
236 ProcessingDataLTTVStandard* processingData;
237
238 if (!syncState->stats)
239 {
240 return;
241 }
242
243 processingData= (ProcessingDataLTTVStandard*) syncState->processingData;
244
245 printf("LTTV processing stats:\n");
246 printf("\treceived frames: %d\n", processingData->stats->totRecv);
247 printf("\treceived frames that are IP: %d\n",
248 processingData->stats->totRecvIp);
249 printf("\treceived and processed packets that are TCP: %d\n",
250 processingData->stats->totInE);
251 printf("\tsent packets that are TCP: %d\n",
252 processingData->stats->totOutE);
253
254 if (syncState->matchingModule->printMatchingStats != NULL)
255 {
256 syncState->matchingModule->printMatchingStats(syncState);
257 }
258
259 printf("Resulting synchronization factors:\n");
260 for (i= 0; i < syncState->traceNb; i++)
261 {
262 LttTrace* t;
263
264 t= processingData->traceSetContext->traces[i]->t;
265
266 printf("\ttrace %u drift= %g offset= %g (%f) start time= %ld.%09ld\n",
267 i, t->drift, t->offset, (double) tsc_to_uint64(t->freq_scale,
268 t->start_freq, t->offset) / NANOSECONDS_PER_SECOND,
269 t->start_time_from_tsc.tv_sec, t->start_time_from_tsc.tv_nsec);
270 }
271}
272
273
274/*
275 * Unregister event hooks. Deallocate processingData.
276 *
277 * Args:
278 * syncState: container for synchronization data.
279 * This function deallocates these processingData members:
280 * stats
281 */
282static void destroyProcessingLTTVStandard(SyncState* const syncState)
283{
284 ProcessingDataLTTVStandard* processingData;
285
286 processingData= (ProcessingDataLTTVStandard*) syncState->processingData;
287
288 if (processingData == NULL)
289 {
290 return;
291 }
292
293 partialDestroyProcessingLTTVStandard(syncState);
294
295 if (syncState->stats)
296 {
297 free(processingData->stats);
298 }
299
300 free(syncState->processingData);
301 syncState->processingData= NULL;
302}
303
304
305/*
306 * Unregister event hooks. Deallocate some of processingData.
307 *
308 * This function can be called right after the events have been processed to
309 * free some data structures that are not needed for finalization.
310 *
311 * Args:
312 * syncState: container for synchronization data.
313 * This function deallocates these members:
314 * traceNumTable
315 * hookListList
316 * pendingRecv
317 */
318static void partialDestroyProcessingLTTVStandard(SyncState* const syncState)
319{
320 unsigned int i;
321 ProcessingDataLTTVStandard* processingData;
322
323 processingData= (ProcessingDataLTTVStandard*) syncState->processingData;
324
325 if (processingData == NULL || processingData->traceNumTable == NULL)
326 {
327 return;
328 }
329
330 g_hash_table_destroy(processingData->traceNumTable);
331 processingData->traceNumTable= NULL;
332
333 for(i= 0; i < syncState->traceNb; i++)
334 {
335
336 g_debug("Cleaning up pendingRecv list\n");
337 g_hash_table_destroy(processingData->pendingRecv[i]);
338 }
339 free(processingData->pendingRecv);
340
341 unregisterHooks(processingData->hookListList,
08365995 342 processingData->traceSetContext);
70407e86
BP
343}
344
345
346/*
347 * Lttv hook function that will be called for network events
348 *
349 * Args:
350 * hookData: LttvTraceHook* for the type of event that generated the call
351 * callData: LttvTracefileContext* at the moment of the event
352 *
353 * Returns:
354 * FALSE Always returns FALSE, meaning to keep processing hooks for
355 * this event
356 */
357static gboolean processEventLTTVStandard(void* hookData, void* callData)
358{
359 LttvTraceHook* traceHook;
360 LttvTracefileContext* tfc;
361 LttEvent* event;
362 LttTime time;
363 LttCycleCount tsc;
364 LttTrace* trace;
365 unsigned long traceNum;
366 struct marker_info* info;
367 SyncState* syncState;
368 ProcessingDataLTTVStandard* processingData;
369
370 traceHook= (LttvTraceHook*) hookData;
371 tfc= (LttvTracefileContext*) callData;
372 syncState= (SyncState*) traceHook->hook_data;
373 processingData= (ProcessingDataLTTVStandard*) syncState->processingData;
374 event= ltt_tracefile_get_event(tfc->tf);
375 time= ltt_event_time(event);
376 tsc= ltt_event_cycle_count(event);
377 trace= tfc->t_context->t;
378 info= marker_get_info_from_id(tfc->tf->mdata, event->event_id);
379
380 g_assert(g_hash_table_lookup_extended(processingData->traceNumTable,
381 trace, NULL, (gpointer*) &traceNum));
382
383 g_debug("XXXX process event: time: %ld.%09ld trace: %ld (%p) name: %s ",
384 (long) time.tv_sec, time.tv_nsec, traceNum, trace,
385 g_quark_to_string(info->name));
386
387 if (info->name == LTT_EVENT_DEV_XMIT)
388 {
389 NetEvent* outE;
390
391 if (!ltt_event_get_unsigned(event,
392 lttv_trace_get_hook_field(traceHook, 1)) == ETH_P_IP ||
393 !ltt_event_get_unsigned(event,
394 lttv_trace_get_hook_field(traceHook, 2)) == IPPROTO_TCP)
395 {
396 return FALSE;
397 }
398
399 if (syncState->stats)
400 {
401 processingData->stats->totOutE++;
402 }
403
404 outE= malloc(sizeof(NetEvent));
405 outE->packetKey= malloc(sizeof(PacketKey));
406
407 outE->traceNum= traceNum;
408 outE->tsc= tsc;
409 outE->skb= NULL;
410 outE->packetKey->connectionKey.saddr= ltt_event_get_unsigned(event,
411 lttv_trace_get_hook_field(traceHook, 3));
412 outE->packetKey->connectionKey.daddr= ltt_event_get_unsigned(event,
413 lttv_trace_get_hook_field(traceHook, 4));
414 outE->packetKey->tot_len= ltt_event_get_unsigned(event,
415 lttv_trace_get_hook_field(traceHook, 5));
416 outE->packetKey->ihl= ltt_event_get_unsigned(event,
417 lttv_trace_get_hook_field(traceHook, 6));
418 outE->packetKey->connectionKey.source= ltt_event_get_unsigned(event,
419 lttv_trace_get_hook_field(traceHook, 7));
420 outE->packetKey->connectionKey.dest= ltt_event_get_unsigned(event,
421 lttv_trace_get_hook_field(traceHook, 8));
422 outE->packetKey->seq= ltt_event_get_unsigned(event,
423 lttv_trace_get_hook_field(traceHook, 9));
424 outE->packetKey->ack_seq= ltt_event_get_unsigned(event,
425 lttv_trace_get_hook_field(traceHook, 10));
426 outE->packetKey->doff= ltt_event_get_unsigned(event,
427 lttv_trace_get_hook_field(traceHook, 11));
428 outE->packetKey->ack= ltt_event_get_unsigned(event,
429 lttv_trace_get_hook_field(traceHook, 12));
430 outE->packetKey->rst= ltt_event_get_unsigned(event,
431 lttv_trace_get_hook_field(traceHook, 13));
432 outE->packetKey->syn= ltt_event_get_unsigned(event,
433 lttv_trace_get_hook_field(traceHook, 14));
434 outE->packetKey->fin= ltt_event_get_unsigned(event,
435 lttv_trace_get_hook_field(traceHook, 15));
436
437 syncState->matchingModule->matchEvent(syncState, outE, OUT);
438
439 g_debug("Output event done\n");
440 }
441 else if (info->name == LTT_EVENT_DEV_RECEIVE)
442 {
443 guint32 protocol;
444
445 if (syncState->stats)
446 {
447 processingData->stats->totRecv++;
448 }
449
450 protocol= ltt_event_get_unsigned(event,
451 lttv_trace_get_hook_field(traceHook, 1));
452
453 if (protocol == ETH_P_IP)
454 {
455 NetEvent* inE;
456
457 if (syncState->stats)
458 {
459 processingData->stats->totRecvIp++;
460 }
461
462 inE= malloc(sizeof(NetEvent));
463
464 inE->traceNum= traceNum;
465 inE->tsc= tsc;
466 inE->skb= (void*) (long) ltt_event_get_long_unsigned(event,
467 lttv_trace_get_hook_field(traceHook, 0));
468 inE->packetKey= NULL;
469
85c9417b 470 g_hash_table_replace(processingData->pendingRecv[traceNum],
70407e86
BP
471 inE->skb, inE);
472
473 g_debug("Adding inE %p for skb %p to pendingRecv\n", inE, inE->skb);
474 }
475 else
476 {
477 g_debug("\n");
478 }
479 }
480 else if (info->name == LTT_EVENT_TCPV4_RCV)
481 {
482 NetEvent* inE;
483 void* skb;
484
485 // Search pendingRecv for an event with the same skb
486 skb= (void*) (long) ltt_event_get_long_unsigned(event,
487 lttv_trace_get_hook_field(traceHook, 0));
488
489 inE= (NetEvent*)
490 g_hash_table_lookup(processingData->pendingRecv[traceNum], skb);
491 if (inE == NULL)
492 {
493 // This should only happen in case of lost events
494 g_debug("No matching pending receive event found\n");
495 }
496 else
497 {
498 if (syncState->stats)
499 {
500 processingData->stats->totInE++;
501 }
502
503 // If it's there, remove it and proceed with a receive event
504 g_hash_table_steal(processingData->pendingRecv[traceNum], skb);
505
506 inE->packetKey= malloc(sizeof(PacketKey));
507
508 inE->packetKey->connectionKey.saddr= ltt_event_get_unsigned(event,
509 lttv_trace_get_hook_field(traceHook, 1));
510 inE->packetKey->connectionKey.daddr= ltt_event_get_unsigned(event,
511 lttv_trace_get_hook_field(traceHook, 2));
512 inE->packetKey->tot_len= ltt_event_get_unsigned(event,
513 lttv_trace_get_hook_field(traceHook, 3));
514 inE->packetKey->ihl= ltt_event_get_unsigned(event,
515 lttv_trace_get_hook_field(traceHook, 4));
516 inE->packetKey->connectionKey.source= ltt_event_get_unsigned(event,
517 lttv_trace_get_hook_field(traceHook, 5));
518 inE->packetKey->connectionKey.dest= ltt_event_get_unsigned(event,
519 lttv_trace_get_hook_field(traceHook, 6));
520 inE->packetKey->seq= ltt_event_get_unsigned(event,
521 lttv_trace_get_hook_field(traceHook, 7));
522 inE->packetKey->ack_seq= ltt_event_get_unsigned(event,
523 lttv_trace_get_hook_field(traceHook, 8));
524 inE->packetKey->doff= ltt_event_get_unsigned(event,
525 lttv_trace_get_hook_field(traceHook, 9));
526 inE->packetKey->ack= ltt_event_get_unsigned(event,
527 lttv_trace_get_hook_field(traceHook, 10));
528 inE->packetKey->rst= ltt_event_get_unsigned(event,
529 lttv_trace_get_hook_field(traceHook, 11));
530 inE->packetKey->syn= ltt_event_get_unsigned(event,
531 lttv_trace_get_hook_field(traceHook, 12));
532 inE->packetKey->fin= ltt_event_get_unsigned(event,
533 lttv_trace_get_hook_field(traceHook, 13));
534
535 syncState->matchingModule->matchEvent(syncState, inE, IN);
536
537 g_debug("Input event %p for skb %p done\n", inE, skb);
538 }
539 }
70407e86
BP
540 else if (info->name == LTT_EVENT_NETWORK_IPV4_INTERFACE)
541 {
542 char* name;
543 guint64 address;
544 gint64 up;
545 char addressString[17];
546
547 address= ltt_event_get_long_unsigned(event,
548 lttv_trace_get_hook_field(traceHook, 1));
549 up= ltt_event_get_long_int(event, lttv_trace_get_hook_field(traceHook,
550 2));
551 /* name must be the last field to get or else copy the string, see the
552 * doc for ltt_event_get_string()
553 */
554 name= ltt_event_get_string(event, lttv_trace_get_hook_field(traceHook,
555 0));
556
557 convertIP(addressString, address);
558
559 g_debug("name \"%s\" address %s up %lld\n", name, addressString, up);
560 }
561 else
562 {
563 g_assert_not_reached();
564 }
565
566 return FALSE;
567}
08365995
BP
568
569
570/*
571 * Write the processing-specific graph lines in the gnuplot script (none at
572 * the moment). Call the downstream module's graph function.
573 *
574 * Args:
575 * stream: stream where to write the data
576 * syncState: container for synchronization data
577 * i: first trace number
578 * j: second trace number, garanteed to be larger than i
579 */
580static void writeProcessingGraphsPlotsLTTVStandard(FILE* stream, SyncState*
581 const syncState, const unsigned int i, const unsigned int j)
582{
583 if (syncState->matchingModule->writeMatchingGraphsPlots != NULL)
584 {
585 syncState->matchingModule->writeMatchingGraphsPlots(stream, syncState,
586 i, j);
587 }
588}
589
590
591/*
592 * Write the processing-specific options in the gnuplot script. Call the
593 * downstream module's options function.
594 *
595 * Args:
596 * stream: stream where to write the data
597 * syncState: container for synchronization data
598 * i: first trace number
599 * j: second trace number, garanteed to be larger than i
600 */
601static void writeProcessingGraphsOptionsLTTVStandard(FILE* stream, SyncState*
602 const syncState, const unsigned int i, const unsigned int j)
603{
604 ProcessingDataLTTVStandard* processingData;
605 LttTrace* traceI, * traceJ;
606
607 processingData= (ProcessingDataLTTVStandard*) syncState->processingData;
608
609 traceI= processingData->traceSetContext->traces[i]->t;
610 traceJ= processingData->traceSetContext->traces[j]->t;
611
612 fprintf(stream,
613 "set x2label \"Clock %1$d (s)\"\n"
614 "set x2range [GPVAL_X_MIN / %2$.1f : GPVAL_X_MAX / %2$.1f]\n"
615 "set x2tics\n"
616 "set y2label \"Clock %3$d (s)\"\n"
617 "set y2range [GPVAL_Y_MIN / %4$.1f : GPVAL_Y_MAX / %4$.1f]\n"
618 "set y2tics\n", i, (double) traceI->start_freq / traceI->freq_scale,
619 j, (double) traceJ->start_freq / traceJ->freq_scale);
620
621 if (syncState->matchingModule->writeMatchingGraphsOptions != NULL)
622 {
623 syncState->matchingModule->writeMatchingGraphsOptions(stream,
624 syncState, i, j);
625 }
626}
This page took 0.045307 seconds and 4 git commands to generate.