+
+ analysisData->stats->exchangeRtt=
+ g_hash_table_new_full(&ghfRttKeyHash, &gefRttKeyEqual,
+ &gdnDestroyRttKey, &gdnDestroyDouble);
+ }
+
+ if (syncState->graphsStream)
+ {
+ binBase= exp10(6. / (BIN_NB - 3));
+ analysisData->graphs= g_hash_table_new_full(&ghfRttKeyHash,
+ &gefRttKeyEqual, &gdnDestroyRttKey, &gdnDestroyAnalysisGraphEval);
+ }
+}
+
+
+/*
+ * Create and open files used to store histogram points to generate graphs.
+ * Create data structures to store histogram points during analysis.
+ *
+ * Args:
+ * graphsDir: folder where to write files
+ * rttKey: host pair, make sure saddr < daddr
+ */
+static AnalysisGraphEval* constructAnalysisGraphEval(const char* const
+ graphsDir, const struct RttKey* const rttKey)
+{
+ int retval;
+ unsigned int i;
+ char* cwd;
+ char name[60], saddr[16], daddr[16];
+ AnalysisGraphEval* graph= calloc(1, sizeof(*graph));
+ const struct {
+ size_t pointsOffset;
+ const char* fileName;
+ const char* host1, *host2;
+ } loopValues[]= {
+ {offsetof(AnalysisGraphEval, ttSendPoints), "analysis_eval_tt-%s_to_%s.data",
+ saddr, daddr},
+ {offsetof(AnalysisGraphEval, ttRecvPoints), "analysis_eval_tt-%s_to_%s.data",
+ daddr, saddr},
+ {offsetof(AnalysisGraphEval, hrttPoints), "analysis_eval_hrtt-%s_and_%s.data",
+ saddr, daddr},
+ };
+
+ graph->ttSendBins.max= BIN_NB - 1;
+ graph->ttRecvBins.max= BIN_NB - 1;
+ graph->hrttBins.max= BIN_NB - 1;
+
+ convertIP(saddr, rttKey->saddr);
+ convertIP(daddr, rttKey->daddr);
+
+ cwd= changeToGraphDir(graphsDir);
+
+ for (i= 0; i < sizeof(loopValues) / sizeof(*loopValues); i++)
+ {
+ retval= snprintf(name, sizeof(name), loopValues[i].fileName,
+ loopValues[i].host1, loopValues[i].host2);
+ if (retval > sizeof(name) - 1)
+ {
+ name[sizeof(name) - 1]= '\0';
+ }
+ if ((*(FILE**)((void*) graph + loopValues[i].pointsOffset)=
+ fopen(name, "w")) == NULL)
+ {
+ g_error(strerror(errno));
+ }
+ }
+
+ retval= chdir(cwd);
+ if (retval == -1)
+ {
+ g_error(strerror(errno));
+ }
+ free(cwd);
+
+ return graph;
+}
+
+
+/*
+ * Close files used to store histogram points to generate graphs.
+ *
+ * Args:
+ * graphsDir: folder where to write files
+ * rttKey: host pair, make sure saddr < daddr
+ */
+static void destroyAnalysisGraphEval(AnalysisGraphEval* const graph)
+{
+ unsigned int i;
+ int retval;
+ const struct {
+ size_t pointsOffset;
+ } loopValues[]= {
+ {offsetof(AnalysisGraphEval, ttSendPoints)},
+ {offsetof(AnalysisGraphEval, ttRecvPoints)},
+ {offsetof(AnalysisGraphEval, hrttPoints)},
+ };
+
+ for (i= 0; i < sizeof(loopValues) / sizeof(*loopValues); i++)
+ {
+ retval= fclose(*(FILE**)((void*) graph + loopValues[i].pointsOffset));
+ if (retval != 0)
+ {
+ g_error(strerror(errno));
+ }
+ }
+}
+
+
+/*
+ * A GDestroyNotify function for g_hash_table_new_full()
+ *
+ * Args:
+ * data: AnalysisGraphEval*
+ */
+static void gdnDestroyAnalysisGraphEval(gpointer data)
+{
+ destroyAnalysisGraphEval(data);
+}
+
+
+/*
+ * A GHFunc for g_hash_table_foreach()
+ *
+ * Args:
+ * key: RttKey* where saddr < daddr
+ * value: AnalysisGraphEval*
+ * user_data struct WriteGraphInfo*
+ */
+static void ghfWriteGraph(gpointer key, gpointer value, gpointer user_data)
+{
+ double* rtt1, * rtt2;
+ struct RttKey* rttKey= key;
+ struct RttKey oppositeRttKey= {.saddr= rttKey->daddr, .daddr=
+ rttKey->saddr};
+ AnalysisGraphEval* graph= value;
+ struct WriteGraphInfo* info= user_data;
+
+ rtt1= g_hash_table_lookup(info->rttInfo, rttKey);
+ rtt2= g_hash_table_lookup(info->rttInfo, &oppositeRttKey);
+
+ if (rtt1 == NULL)
+ {
+ rtt1= rtt2;
+ }
+ else if (rtt2 != NULL)
+ {
+ rtt1= MIN(rtt1, rtt2);
+ }
+
+ dumpBinToFile(&graph->ttSendBins, graph->ttSendPoints);
+ dumpBinToFile(&graph->ttRecvBins, graph->ttRecvPoints);
+ dumpBinToFile(&graph->hrttBins, graph->hrttPoints);
+ writeHistogram(info->graphsStream, rttKey, rtt1);
+}
+
+
+/*
+ * Write the content of one bin in a histogram point file
+ *
+ * Args:
+ * bin: array of values that make up a histogram
+ * file: FILE*, write to this file
+ */
+static void dumpBinToFile(const struct Bins* const bins, FILE* const file)
+{
+ unsigned int i;
+
+ // The first and last bins are skipped, see struct Bins
+ for (i= 1; i < BIN_NB - 1; i++)
+ {
+ if (bins->bin[i] > 0)
+ {
+ fprintf(file, "%20.9f %20.9f %20.9f\n", (binStart(i) + binEnd(i))
+ / 2., (double) bins->bin[i] / ((binEnd(i) - binStart(i)) *
+ bins->total), binEnd(i) - binStart(i));
+ }