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