Processing of UDP events
[lttv.git] / lttv / lttv / sync / event_analysis_linreg.c
... / ...
CommitLineData
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// for INFINITY in math.h
20#define _ISOC99_SOURCE
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25
26#include <math.h>
27#include <stdio.h>
28#include <stdlib.h>
29
30#include "sync_chain_lttv.h"
31
32#include "event_analysis_linreg.h"
33
34
35#ifndef g_info
36#define g_info(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, format)
37#endif
38
39
40// Functions common to all analysis modules
41static void initAnalysisLinReg(SyncState* const syncState);
42static void destroyAnalysisLinReg(SyncState* const syncState);
43
44static void analyzeExchangeLinReg(SyncState* const syncState, Exchange* const exchange);
45static GArray* finalizeAnalysisLinReg(SyncState* const syncState);
46static void printAnalysisStatsLinReg(SyncState* const syncState);
47static void writeAnalysisGraphsPlotsLinReg(FILE* stream, SyncState* const
48 syncState, const unsigned int i, const unsigned int j);
49
50// Functions specific to this module
51static void registerAnalysisLinReg() __attribute__((constructor (102)));
52
53static void finalizeLSA(SyncState* const syncState);
54static void doGraphProcessing(SyncState* const syncState);
55static GArray* calculateFactors(SyncState* const syncState);
56static void shortestPath(Fit* const* const fitArray, const unsigned int
57 traceNum, const unsigned int traceNb, double* const distances,
58 unsigned int* const previousVertex);
59static double sumDistances(const double* const distances, const unsigned int
60 traceNb);
61static void reduceFactors(Fit* const* const fitArray, const unsigned int* const
62 previousVertex, const unsigned int traceNum, double* const drift,
63 double* const offset, double* const stDev);
64
65// Graph-related Glib functions
66static void gfGraphDestroy(gpointer data, gpointer user_data);
67static gint gcfGraphTraceCompare(gconstpointer a, gconstpointer b);
68
69
70static AnalysisModule analysisModuleLinReg= {
71 .name= "linreg",
72 .initAnalysis= &initAnalysisLinReg,
73 .destroyAnalysis= &destroyAnalysisLinReg,
74 .analyzeMessage= NULL,
75 .analyzeExchange= &analyzeExchangeLinReg,
76 .analyzeBroadcast= NULL,
77 .finalizeAnalysis= &finalizeAnalysisLinReg,
78 .printAnalysisStats= &printAnalysisStatsLinReg,
79 .writeAnalysisGraphsPlots= &writeAnalysisGraphsPlotsLinReg,
80 .writeAnalysisGraphsOptions= NULL,
81};
82
83
84/*
85 * Analysis module registering function
86 */
87static void registerAnalysisLinReg()
88{
89 g_queue_push_tail(&analysisModules, &analysisModuleLinReg);
90}
91
92
93/*
94 * Analysis init function
95 *
96 * This function is called at the beginning of a synchronization run for a set
97 * of traces.
98 *
99 * Allocate some of the analysis specific data structures
100 *
101 * Args:
102 * syncState container for synchronization data.
103 * This function allocates these analysisData members:
104 * fitArray
105 * stDev
106 */
107static void initAnalysisLinReg(SyncState* const syncState)
108{
109 unsigned int i;
110 AnalysisDataLinReg* analysisData;
111
112 analysisData= malloc(sizeof(AnalysisDataLinReg));
113 syncState->analysisData= analysisData;
114
115 analysisData->fitArray= malloc(syncState->traceNb * sizeof(Fit*));
116 for (i= 0; i < syncState->traceNb; i++)
117 {
118 analysisData->fitArray[i]= calloc(syncState->traceNb, sizeof(Fit));
119 }
120
121 if (syncState->stats)
122 {
123 analysisData->stDev= malloc(sizeof(double) * syncState->traceNb);
124 }
125}
126
127
128/*
129 * Analysis destroy function
130 *
131 * Free the analysis specific data structures
132 *
133 * Args:
134 * syncState container for synchronization data.
135 * This function deallocates these analysisData members:
136 * fitArray
137 * graphList
138 * stDev
139 */
140static void destroyAnalysisLinReg(SyncState* const syncState)
141{
142 unsigned int i;
143 AnalysisDataLinReg* analysisData;
144
145 analysisData= (AnalysisDataLinReg*) syncState->analysisData;
146
147 if (analysisData == NULL)
148 {
149 return;
150 }
151
152 for (i= 0; i < syncState->traceNb; i++)
153 {
154 free(analysisData->fitArray[i]);
155 }
156 free(analysisData->fitArray);
157
158 g_queue_foreach(analysisData->graphList, &gfGraphDestroy, NULL);
159 g_queue_free(analysisData->graphList);
160
161 if (syncState->stats)
162 {
163 free(analysisData->stDev);
164 }
165
166 free(syncState->analysisData);
167 syncState->analysisData= NULL;
168}
169
170
171/*
172 * Perform analysis on a series of event pairs.
173 *
174 * Args:
175 * syncState container for synchronization data
176 * exchange structure containing the many events
177 */
178static void analyzeExchangeLinReg(SyncState* const syncState, Exchange* const exchange)
179{
180 unsigned int ni, nj;
181 double dji, eji;
182 double timoy;
183 Fit* fit;
184 Message* ackedMessage;
185 AnalysisDataLinReg* analysisData;
186
187 g_debug("Synchronization calculation, ");
188 g_debug("%d acked packets - using last one, ",
189 g_queue_get_length(exchange->acks));
190
191 analysisData= (AnalysisDataLinReg*) syncState->analysisData;
192 ackedMessage= g_queue_peek_tail(exchange->acks);
193
194 // Calculate the intermediate values for the
195 // least-squares analysis
196 dji= ((double) ackedMessage->inE->time - (double) ackedMessage->outE->time
197 + (double) exchange->message->outE->time - (double)
198 exchange->message->inE->time) / 2;
199 eji= fabs((double) ackedMessage->inE->time - (double)
200 ackedMessage->outE->time - (double) exchange->message->outE->time +
201 (double) exchange->message->inE->time) / 2;
202 timoy= ((double) ackedMessage->outE->time + (double)
203 exchange->message->inE->time) / 2;
204 ni= ackedMessage->outE->traceNum;
205 nj= ackedMessage->inE->traceNum;
206 fit= &analysisData->fitArray[nj][ni];
207
208 fit->n++;
209 fit->st+= timoy;
210 fit->st2+= pow(timoy, 2);
211 fit->sd+= dji;
212 fit->sd2+= pow(dji, 2);
213 fit->std+= timoy * dji;
214
215 g_debug("intermediate values: dji= %f ti moy= %f "
216 "ni= %u nj= %u fit: n= %u st= %f st2= %f sd= %f "
217 "sd2= %f std= %f, ", dji, timoy, ni, nj, fit->n,
218 fit->st, fit->st2, fit->sd, fit->sd2, fit->std);
219}
220
221
222/*
223 * Finalize the factor calculations
224 *
225 * The least squares analysis is finalized and finds the factors directly
226 * between each pair of traces that had events together. The traces are
227 * aranged in a graph, a reference trace is chosen and the factors between
228 * this reference and every other trace is calculated. Sometimes it is
229 * necessary to use many graphs when there are "islands" of independent
230 * traces.
231 *
232 * Args:
233 * syncState container for synchronization data.
234 *
235 * Returns:
236 * Factors[traceNb] synchronization factors for each trace
237 */
238static GArray* finalizeAnalysisLinReg(SyncState* const syncState)
239{
240 GArray* factors;
241
242 // Finalize the processing
243 finalizeLSA(syncState);
244
245 // Find a reference node and structure nodes in a graph
246 doGraphProcessing(syncState);
247
248 /* Calculate the resulting offset and drift between each trace and its
249 * reference
250 */
251 factors= calculateFactors(syncState);
252
253 return factors;
254}
255
256
257/*
258 * Print statistics related to analysis. Must be called after
259 * finalizeAnalysis.
260 *
261 * Args:
262 * syncState container for synchronization data.
263 */
264static void printAnalysisStatsLinReg(SyncState* const syncState)
265{
266 unsigned int i, j;
267 AnalysisDataLinReg* analysisData;
268
269 if (!syncState->stats)
270 {
271 return;
272 }
273
274 analysisData= (AnalysisDataLinReg*) syncState->analysisData;
275
276 printf("Linear regression analysis stats:\n");
277
278 printf("\tIndividual synchronization factors:\n");
279
280 for (j= 0; j < syncState->traceNb; j++)
281 {
282 for (i= 0; i < j; i++)
283 {
284 Fit* fit;
285
286 fit= &analysisData->fitArray[j][i];
287 printf("\t\t%3d - %-3d: ", i, j);
288 printf("a0= % 7g a1= 1 %c %7g accuracy %7g\n", fit->d0, fit->x <
289 0. ? '-' : '+', fabs(fit->x), fit->e);
290
291 fit= &analysisData->fitArray[i][j];
292 printf("\t\t%3d - %-3d: ", j, i);
293 printf("a0= % 7g a1= 1 %c %7g accuracy %7g\n", fit->d0, fit->x <
294 0. ? '-' : '+', fabs(fit->x), fit->e);
295 }
296 }
297
298 printf("\tTree:\n");
299 for (i= 0; i < syncState->traceNb; i++)
300 {
301 GList* result;
302
303 result= g_queue_find_custom(analysisData->graphList, &i,
304 &gcfGraphTraceCompare);
305 if (result != NULL)
306 {
307 Graph* graph;
308
309 graph= (Graph*) result->data;
310
311 printf("\t\ttrace %u reference %u previous vertex ", i,
312 graph->reference);
313
314 if (i == graph->reference)
315 {
316 printf("- ");
317 }
318 else
319 {
320 printf("%u ", graph->previousVertex[i]);
321 }
322
323 printf("stdev= %g\n", analysisData->stDev[i]);
324 }
325 else
326 {
327 g_error("Trace %d not part of a graph\n", i);
328 }
329 }
330}
331
332
333/*
334 * Finalize the least-squares analysis. The intermediate values in the fit
335 * array are used to calculate the drift and the offset between each pair of
336 * nodes based on their exchanges.
337 *
338 * Args:
339 * syncState: container for synchronization data.
340 */
341static void finalizeLSA(SyncState* const syncState)
342{
343 unsigned int i, j;
344 AnalysisDataLinReg* analysisData;
345
346 analysisData= (AnalysisDataLinReg*) syncState->analysisData;
347
348 for (i= 0; i < syncState->traceNb; i++)
349 {
350 for (j= 0; j < syncState->traceNb; j++)
351 {
352 if (i != j)
353 {
354 Fit* fit;
355 double delta;
356
357 fit= &analysisData->fitArray[i][j];
358
359 delta= fit->n * fit->st2 - pow(fit->st, 2);
360 fit->x= (fit->n * fit->std - fit->st * fit->sd) / delta;
361 fit->d0= (fit->st2 * fit->sd - fit->st * fit->std) / delta;
362 fit->e= sqrt((fit->sd2 - (fit->n * pow(fit->std, 2) +
363 pow(fit->sd, 2) * fit->st2 - 2 * fit->st * fit->sd
364 * fit->std) / delta) / (fit->n - 2));
365
366 g_debug("[i= %u j= %u]\n", i, j);
367 g_debug("n= %d st= %g st2= %g sd= %g sd2= %g std= %g\n",
368 fit->n, fit->st, fit->st2, fit->sd, fit->sd2, fit->std);
369 g_debug("xij= %g d0ij= %g e= %g\n", fit->x, fit->d0, fit->e);
370 g_debug("(xji= %g d0ji= %g)\n", -fit->x / (1 + fit->x),
371 -fit->d0 / (1 + fit->x));
372 }
373 }
374 }
375}
376
377
378/*
379 * Structure nodes in graphs of nodes that had exchanges. Each graph has a
380 * reference node, the one that can reach the others with the smallest
381 * cummulative error.
382 *
383 * Args:
384 * syncState: container for synchronization data.
385 * This function allocates these analysisData members:
386 * graphList
387 */
388static void doGraphProcessing(SyncState* const syncState)
389{
390 unsigned int i, j;
391 double* distances;
392 unsigned int* previousVertex;
393 AnalysisDataLinReg* analysisData;
394
395 analysisData= (AnalysisDataLinReg*) syncState->analysisData;
396
397 distances= malloc(syncState->traceNb * sizeof(double));
398 previousVertex= malloc(syncState->traceNb * sizeof(unsigned int));
399 analysisData->graphList= g_queue_new();
400
401 for (i= 0; i < syncState->traceNb; i++)
402 {
403 double errorSum;
404 GList* result;
405
406 // Perform shortest path search
407 g_debug("shortest path trace %d\ndistances: ", i);
408 shortestPath(analysisData->fitArray, i, syncState->traceNb, distances,
409 previousVertex);
410
411 for (j= 0; j < syncState->traceNb; j++)
412 {
413 g_debug("%g, ", distances[j]);
414 }
415 g_debug("\npreviousVertex: ");
416 for (j= 0; j < syncState->traceNb; j++)
417 {
418 g_debug("%u, ", previousVertex[j]);
419 }
420 g_debug("\n");
421
422 // Group in graphs nodes that have exchanges
423 errorSum= sumDistances(distances, syncState->traceNb);
424 result= g_queue_find_custom(analysisData->graphList, &i,
425 &gcfGraphTraceCompare);
426 if (result != NULL)
427 {
428 Graph* graph;
429
430 g_debug("found graph\n");
431 graph= (Graph*) result->data;
432 if (errorSum < graph->errorSum)
433 {
434 g_debug("adding to graph\n");
435 graph->errorSum= errorSum;
436 free(graph->previousVertex);
437 graph->previousVertex= previousVertex;
438 graph->reference= i;
439 previousVertex= malloc(syncState->traceNb * sizeof(unsigned
440 int));
441 }
442 }
443 else
444 {
445 Graph* newGraph;
446
447 g_debug("creating new graph\n");
448 newGraph= malloc(sizeof(Graph));
449 newGraph->errorSum= errorSum;
450 newGraph->previousVertex= previousVertex;
451 newGraph->reference= i;
452 previousVertex= malloc(syncState->traceNb * sizeof(unsigned int));
453
454 g_queue_push_tail(analysisData->graphList, newGraph);
455 }
456 }
457
458 free(previousVertex);
459 free(distances);
460}
461
462
463/*
464 * Calculate the resulting offset and drift between each trace and its
465 * reference.
466 *
467 * Args:
468 * syncState: container for synchronization data.
469 *
470 * Returns:
471 * Factors[traceNb] synchronization factors for each trace
472 */
473static GArray* calculateFactors(SyncState* const syncState)
474{
475 unsigned int i;
476 AnalysisDataLinReg* analysisData;
477 GArray* factors;
478
479 analysisData= (AnalysisDataLinReg*) syncState->analysisData;
480 factors= g_array_sized_new(FALSE, FALSE, sizeof(Factors),
481 syncState->traceNb);
482 g_array_set_size(factors, syncState->traceNb);
483
484 // Calculate the resulting offset and drift between each trace and its
485 // reference
486 for (i= 0; i < syncState->traceNb; i++)
487 {
488 GList* result;
489
490 result= g_queue_find_custom(analysisData->graphList, &i,
491 &gcfGraphTraceCompare);
492 if (result != NULL)
493 {
494 Graph* graph;
495 double stDev;
496 Factors* traceFactors;
497
498 graph= (Graph*) result->data;
499 traceFactors= &g_array_index(factors, Factors, i);
500
501 reduceFactors(analysisData->fitArray, graph->previousVertex, i,
502 &traceFactors->drift, &traceFactors->offset, &stDev);
503
504 if (syncState->stats)
505 {
506 analysisData->stDev[i]= stDev;
507 }
508 }
509 else
510 {
511 g_error("Trace %d not part of a graph\n", i);
512 }
513 }
514
515 return factors;
516}
517
518
519/*
520 * Single-source shortest path search to find the path with the lowest error to
521 * convert one node's time to another.
522 * Uses Dijkstra's algorithm
523 *
524 * Args:
525 * fitArray: array with the regression parameters
526 * traceNum: reference node
527 * traceNb: number of traces = number of nodes
528 * distances: array of computed distance from source node to node i,
529 * INFINITY if i is unreachable, preallocated to the number of
530 * nodes
531 * previousVertex: previous vertex from a node i on the way to the source,
532 * UINT_MAX if i is not on the way or is the source,
533 * preallocated to the number of nodes
534 */
535static void shortestPath(Fit* const* const fitArray, const unsigned int
536 traceNum, const unsigned int traceNb, double* const distances, unsigned
537 int* const previousVertex)
538{
539 bool* visited;
540 unsigned int i, j;
541
542 visited= malloc(traceNb * sizeof(bool));
543
544 for (i= 0; i < traceNb; i++)
545 {
546 const Fit* fit;
547
548 visited[i]= false;
549
550 fit= &fitArray[traceNum][i];
551 g_debug("fitArray[traceNum= %u][i= %u]->n = %u\n", traceNum, i, fit->n);
552 if (fit->n > 0)
553 {
554 distances[i]= fit->e;
555 previousVertex[i]= traceNum;
556 }
557 else
558 {
559 distances[i]= INFINITY;
560 previousVertex[i]= UINT_MAX;
561 }
562 }
563 visited[traceNum]= true;
564
565 for (j= 0; j < traceNb; j++)
566 {
567 g_debug("(%d, %u, %g), ", visited[j], previousVertex[j], distances[j]);
568 }
569 g_debug("\n");
570
571 for (i= 0; i < traceNb - 2; i++)
572 {
573 unsigned int v;
574 double dvMin;
575
576 dvMin= INFINITY;
577 for (j= 0; j < traceNb; j++)
578 {
579 if (visited[j] == false && distances[j] < dvMin)
580 {
581 v= j;
582 dvMin= distances[j];
583 }
584 }
585
586 g_debug("v= %u dvMin= %g\n", v, dvMin);
587
588 if (dvMin != INFINITY)
589 {
590 visited[v]= true;
591
592 for (j= 0; j < traceNb; j++)
593 {
594 const Fit* fit;
595
596 fit= &fitArray[v][j];
597 if (visited[j] == false && fit->n > 0 && distances[v] + fit->e
598 < distances[j])
599 {
600 distances[j]= distances[v] + fit->e;
601 previousVertex[j]= v;
602 }
603 }
604 }
605 else
606 {
607 break;
608 }
609
610 for (j= 0; j < traceNb; j++)
611 {
612 g_debug("(%d, %u, %g), ", visited[j], previousVertex[j], distances[j]);
613 }
614 g_debug("\n");
615 }
616
617 free(visited);
618}
619
620
621/*
622 * Cummulate the distances between a reference node and the other nodes
623 * reachable from it in a graph.
624 *
625 * Args:
626 * distances: array of shortest path distances, with UINT_MAX for
627 * unreachable nodes
628 * traceNb: number of nodes = number of traces
629 */
630static double sumDistances(const double* const distances, const unsigned int traceNb)
631{
632 unsigned int i;
633 double result;
634
635 result= 0;
636 for (i= 0; i < traceNb; i++)
637 {
638 if (distances[i] != INFINITY)
639 {
640 result+= distances[i];
641 }
642 }
643
644 return result;
645}
646
647
648/*
649 * Cummulate the time correction factors between two nodes accross a graph
650 *
651 * With traceNum i, reference node r:
652 * tr= (1 + Xri) * ti + D0ri
653 * = drift * ti + offset
654 *
655 * Args:
656 * fitArray: array with the regression parameters
657 * previousVertex: previous vertex from a node i on the way to the source,
658 * UINT_MAX if i is not on the way or is the source,
659 * preallocated to the number of nodes
660 * traceNum: end node, the reference depends on previousVertex
661 * drift: drift factor
662 * offset: offset factor
663 */
664static void reduceFactors(Fit* const* const fitArray, const unsigned int* const
665 previousVertex, const unsigned int traceNum, double* const drift, double*
666 const offset, double* const stDev)
667{
668 if (previousVertex[traceNum] == UINT_MAX)
669 {
670 *drift= 1.;
671 *offset= 0.;
672 *stDev= 0.;
673 }
674 else
675 {
676 const Fit* fit;
677 double cummDrift, cummOffset, cummStDev;
678 unsigned int pv;
679
680 pv= previousVertex[traceNum];
681
682 fit= &fitArray[pv][traceNum];
683 reduceFactors(fitArray, previousVertex, pv, &cummDrift, &cummOffset,
684 &cummStDev);
685
686 *drift= cummDrift * (1 + fit->x);
687 *offset= cummDrift * fit->d0 + cummOffset;
688 *stDev= fit->x * cummStDev + fit->e;
689 }
690}
691
692
693/*
694 * A GFunc for g_queue_foreach()
695 *
696 * Args:
697 * data Graph*, graph to destroy
698 * user_data NULL
699 */
700static void gfGraphDestroy(gpointer data, gpointer user_data)
701{
702 Graph* graph;
703
704 graph= (Graph*) data;
705
706 free(graph->previousVertex);
707 free(graph);
708}
709
710
711/*
712 * A GCompareFunc for g_queue_find_custom()
713 *
714 * Args:
715 * a: Graph* graph
716 * b: unsigned int* traceNum
717 *
718 * Returns:
719 * 0 if graph contains traceNum
720 */
721static gint gcfGraphTraceCompare(gconstpointer a, gconstpointer b)
722{
723 Graph* graph;
724 unsigned int traceNum;
725
726 graph= (Graph*) a;
727 traceNum= *(unsigned int *) b;
728
729 if (graph->previousVertex[traceNum] != UINT_MAX)
730 {
731 return 0;
732 }
733 else if (graph->reference == traceNum)
734 {
735 return 0;
736 }
737 else
738 {
739 return 1;
740 }
741}
742
743
744/*
745 * Write the analysis-specific graph lines in the gnuplot script.
746 *
747 * Args:
748 * stream: stream where to write the data
749 * syncState: container for synchronization data
750 * i: first trace number, on the x axis
751 * j: second trace number, garanteed to be larger than i
752 */
753void writeAnalysisGraphsPlotsLinReg(FILE* stream, SyncState* const syncState,
754 const unsigned int i, const unsigned int j)
755{
756 AnalysisDataLinReg* analysisData;
757 Fit* fit;
758
759 analysisData= (AnalysisDataLinReg*) syncState->analysisData;
760 fit= &analysisData->fitArray[j][i];
761
762 fprintf(stream,
763 "\t%7g + %7g * x "
764 "title \"Linreg conversion\" with lines "
765 "linecolor rgb \"gray60\" linetype 1, \\\n",
766 fit->d0, 1. + fit->x);
767}
This page took 0.05953 seconds and 4 git commands to generate.