+
+ 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));
+ }
+ }
+}
+
+
+/*
+ * Write the analysis-specific plot in the gnuplot script.
+ *
+ * Args:
+ * graphsStream: write to this file
+ * rttKey: must be sorted such that saddr < daddr
+ * minRtt: if available, else NULL
+ */
+static void writeHistogram(FILE* graphsStream, const struct RttKey* rttKey,
+ double* minRtt)
+{
+ char saddr[16], daddr[16];
+
+ convertIP(saddr, rttKey->saddr);
+ convertIP(daddr, rttKey->daddr);
+
+ fprintf(graphsStream,
+ "reset\n"
+ "set output \"histogram-%s-%s.eps\"\n"
+ "set title \"\"\n"
+ "set xlabel \"Message Latency (s)\"\n"
+ "set ylabel \"Proportion of messages per second\"\n", saddr, daddr);
+
+ if (minRtt != NULL)
+ {
+ fprintf(graphsStream,
+ "set arrow from %.9f, 0 rto 0, graph 1 "
+ "nohead linetype 3 linewidth 3 linecolor rgb \"black\"\n", *minRtt / 2);
+ }
+
+ fprintf(graphsStream,
+ "plot \\\n"
+ "\t\"analysis_eval_hrtt-%1$s_and_%2$s.data\" "
+ "title \"RTT/2\" with linespoints linetype 1 linewidth 2 "
+ "linecolor rgb \"black\" pointtype 6 pointsize 1,\\\n"
+ "\t\"analysis_eval_tt-%1$s_to_%2$s.data\" "
+ "title \"%1$s to %2$s\" with linespoints linetype 4 linewidth 2 "
+ "linecolor rgb \"gray60\" pointtype 6 pointsize 1,\\\n"
+ "\t\"analysis_eval_tt-%2$s_to_%1$s.data\" "
+ "title \"%2$s to %1$s\" with linespoints linetype 4 linewidth 2 "
+ "linecolor rgb \"gray30\" pointtype 6 pointsize 1\n", saddr, daddr);