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