[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [metrics-db/master] Merge Torperf files into new .tpf file format.
commit 8b45d2fb00af489e4ff9ebb74a892e5f91485844
Author: Karsten Loesing <karsten.loesing@xxxxxxx>
Date: Thu May 24 09:34:26 2012 +0200
Merge Torperf files into new .tpf file format.
Implements part of #3036.
---
config.template | 12 +-
src/org/torproject/ernie/db/Configuration.java | 39 +--
src/org/torproject/ernie/db/Main.java | 3 +-
src/org/torproject/ernie/db/TorperfDownloader.java | 484 ++++++++++++++++++--
4 files changed, 467 insertions(+), 71 deletions(-)
diff --git a/config.template b/config.template
index 5034e12..aecc67d 100644
--- a/config.template
+++ b/config.template
@@ -136,11 +136,9 @@
## times)
#TorperfSource torperf http://torperf.torproject.org/
#
-## Torperf .data files available on a given source (option can be
-## contained multiple times)
-#TorperfDataFiles torperf 50kb.data 1mb.data 5mb.data
-#
-## Torperf .extradata files available on a given source (option can be
-## contained multiple times)
-#TorperfExtradataFiles torperf 50kb.extradata 1mb.extradata 5mb.extradata
+## Torperf measurement file size in bytes, .data file, and .extradata file
+## available on a given source (option can be contained multiple times)
+#TorperfFiles torperf 51200 50kb.data 50kb.extradata
+#TorperfFiles torperf 1048576 1mb.data 1mb.extradata
+#TorperfFiles torperf 5242880 5mb.data 5mb.extradata
diff --git a/src/org/torproject/ernie/db/Configuration.java b/src/org/torproject/ernie/db/Configuration.java
index 6ab9f44..63349c8 100644
--- a/src/org/torproject/ernie/db/Configuration.java
+++ b/src/org/torproject/ernie/db/Configuration.java
@@ -59,8 +59,7 @@ public class Configuration {
private boolean processTorperfFiles = false;
private String torperfOutputDirectory = "torperf/";
private SortedMap<String, String> torperfSources = null;
- private SortedMap<String, List<String>> torperfDataFiles = null;
- private SortedMap<String, List<String>> torperfExtradataFiles = null;
+ private List<String> torperfFiles = null;
private boolean provideFilesViaRsync = false;
private String rsyncDirectory = "rsync";
public Configuration() {
@@ -194,29 +193,18 @@ public class Configuration {
String sourceName = parts[1];
String baseUrl = parts[2];
this.torperfSources.put(sourceName, baseUrl);
- } else if (line.startsWith("TorperfDataFiles")) {
- if (this.torperfDataFiles == null) {
- this.torperfDataFiles = new TreeMap<String, List<String>>();
+ } else if (line.startsWith("TorperfFiles")) {
+ if (this.torperfFiles == null) {
+ this.torperfFiles = new ArrayList<String>();
}
String[] parts = line.split(" ");
- String sourceName = parts[1];
- List<String> dataFiles = new ArrayList<String>();
- for (int i = 2; i < parts.length; i++) {
- dataFiles.add(parts[i]);
- }
- this.torperfDataFiles.put(sourceName, dataFiles);
- } else if (line.startsWith("TorperfExtradataFiles")) {
- if (this.torperfExtradataFiles == null) {
- this.torperfExtradataFiles =
- new TreeMap<String, List<String>>();
- }
- String[] parts = line.split(" ");
- String sourceName = parts[1];
- List<String> extradataFiles = new ArrayList<String>();
- for (int i = 2; i < parts.length; i++) {
- extradataFiles.add(parts[i]);
+ if (parts.length != 5) {
+ logger.severe("Configuration file contains TorperfFiles "
+ + "option with wrong number of values in line '" + line
+ + "'! Exiting!");
+ System.exit(1);
}
- this.torperfExtradataFiles.put(sourceName, extradataFiles);
+ this.torperfFiles.add(line);
} else if (line.startsWith("ProvideFilesViaRsync")) {
this.provideFilesViaRsync = Integer.parseInt(
line.split(" ")[1]) != 0;
@@ -381,11 +369,8 @@ public class Configuration {
public SortedMap<String, String> getTorperfSources() {
return this.torperfSources;
}
- public SortedMap<String, List<String>> getTorperfDataFiles() {
- return this.torperfDataFiles;
- }
- public SortedMap<String, List<String>> getTorperfExtradataFiles() {
- return this.torperfExtradataFiles;
+ public List<String> getTorperfFiles() {
+ return this.torperfFiles;
}
public boolean getProvideFilesViaRsync() {
return this.provideFilesViaRsync;
diff --git a/src/org/torproject/ernie/db/Main.java b/src/org/torproject/ernie/db/Main.java
index e44eac0..4fa9e37 100644
--- a/src/org/torproject/ernie/db/Main.java
+++ b/src/org/torproject/ernie/db/Main.java
@@ -139,8 +139,7 @@ public class Main {
// Process Torperf files
if (config.getProcessTorperfFiles()) {
new TorperfDownloader(new File(config.getTorperfOutputDirectory()),
- config.getTorperfSources(), config.getTorperfDataFiles(),
- config.getTorperfExtradataFiles());
+ config.getTorperfSources(), config.getTorperfFiles());
}
// Copy recently published files to a local directory that can then
diff --git a/src/org/torproject/ernie/db/TorperfDownloader.java b/src/org/torproject/ernie/db/TorperfDownloader.java
index 8ee0fe9..e7c55a1 100644
--- a/src/org/torproject/ernie/db/TorperfDownloader.java
+++ b/src/org/torproject/ernie/db/TorperfDownloader.java
@@ -11,65 +11,172 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
+import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
+import java.util.TimeZone;
+import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
/* Download possibly truncated Torperf .data and .extradata files from
- * configured sources and append them to the files we already have. */
+ * configured sources, append them to the files we already have, and merge
+ * the two files into the .tpf format. */
public class TorperfDownloader {
private File torperfOutputDirectory = null;
private SortedMap<String, String> torperfSources = null;
- private SortedMap<String, List<String>> torperfDataFiles = null;
- private SortedMap<String, List<String>> torperfExtradataFiles = null;
+ private List<String> torperfFilesLines = null;
private Logger logger = null;
+ private SimpleDateFormat dateFormat;
public TorperfDownloader(File torperfOutputDirectory,
SortedMap<String, String> torperfSources,
- SortedMap<String, List<String>> torperfDataFiles,
- SortedMap<String, List<String>> torperfExtradataFiles) {
+ List<String> torperfFilesLines) {
if (torperfOutputDirectory == null) {
throw new IllegalArgumentException();
}
this.torperfOutputDirectory = torperfOutputDirectory;
this.torperfSources = torperfSources;
- this.torperfDataFiles = torperfDataFiles;
- this.torperfExtradataFiles = torperfExtradataFiles;
+ this.torperfFilesLines = torperfFilesLines;
if (!this.torperfOutputDirectory.exists()) {
this.torperfOutputDirectory.mkdirs();
}
this.logger = Logger.getLogger(TorperfDownloader.class.getName());
- this.downloadAndMergeFiles(this.torperfDataFiles, true);
- this.downloadAndMergeFiles(this.torperfExtradataFiles, false);
+ this.dateFormat = new SimpleDateFormat("yyyy-MM-dd");
+ this.dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ this.readLastMergedTimestamps();
+ for (String torperfFilesLine : this.torperfFilesLines) {
+ this.downloadAndMergeFiles(torperfFilesLine);
+ }
+ this.writeLastMergedTimestamps();
}
- private void downloadAndMergeFiles(
- SortedMap<String, List<String>> dataOrExtradataFiles,
- boolean isDataFile) {
- for (Map.Entry<String, List<String>> e :
- dataOrExtradataFiles.entrySet()) {
- String sourceName = e.getKey();
- String sourceBaseUrl = torperfSources.get(sourceName);
- List<String> files = e.getValue();
- for (String file : files) {
- String url = sourceBaseUrl + file;
- File outputFile = new File(torperfOutputDirectory,
- sourceName + "-" + file);
- this.downloadAndMergeFile(url, outputFile, isDataFile);
+ private File torperfLastMergedFile =
+ new File("stats/torperf-last-merged");
+ SortedMap<String, String> lastMergedTimestamps =
+ new TreeMap<String, String>();
+ private void readLastMergedTimestamps() {
+ if (!this.torperfLastMergedFile.exists()) {
+ return;
+ }
+ try {
+ BufferedReader br = new BufferedReader(new FileReader(
+ this.torperfLastMergedFile));
+ String line;
+ while ((line = br.readLine()) != null) {
+ String[] parts = line.split(" ");
+ String fileName = null, timestamp = null;
+ if (parts.length == 2) {
+ try {
+ Double.parseDouble(parts[1]);
+ fileName = parts[0];
+ timestamp = parts[1];
+ } catch (NumberFormatException e) {
+ /* Handle below. */
+ }
+ }
+ if (fileName == null || timestamp == null) {
+ this.logger.log(Level.WARNING, "Invalid line '" + line + "' in "
+ + this.torperfLastMergedFile.getAbsolutePath() + ". "
+ + "Ignoring past history of merging .data and .extradata "
+ + "files.");
+ this.lastMergedTimestamps.clear();
+ break;
+ }
+ this.lastMergedTimestamps.put(fileName, timestamp);
}
+ br.close();
+ } catch (IOException e) {
+ this.logger.log(Level.WARNING, "Error while reading '"
+ + this.torperfLastMergedFile.getAbsolutePath() + ". Ignoring "
+ + "past history of merging .data and .extradata files.");
+ this.lastMergedTimestamps.clear();
+ }
+ }
+
+ private void writeLastMergedTimestamps() {
+ try {
+ BufferedWriter bw = new BufferedWriter(new FileWriter(
+ this.torperfLastMergedFile));
+ for (Map.Entry<String, String> e :
+ this.lastMergedTimestamps.entrySet()) {
+ String fileName = e.getKey();
+ String timestamp = e.getValue();
+ bw.write(fileName + " " + timestamp + "\n");
+ }
+ bw.close();
+ } catch (IOException e) {
+ this.logger.log(Level.WARNING, "Error while writing '"
+ + this.torperfLastMergedFile.getAbsolutePath() + ". This may "
+ + "result in ignoring history of merging .data and .extradata "
+ + "files in the next execution.", e);
+ }
+ }
+
+ private void downloadAndMergeFiles(String torperfFilesLine) {
+ String[] parts = torperfFilesLine.split(" ");
+ String sourceName = parts[1];
+ int fileSize = -1;
+ try {
+ fileSize = Integer.parseInt(parts[2]);
+ } catch (NumberFormatException e) {
+ this.logger.log(Level.WARNING, "Could not parse file size in "
+ + "TorperfFiles configuration line '" + torperfFilesLine
+ + "'.");
+ return;
+ }
+
+ /* Download and append the .data file. */
+ String dataFileName = parts[3];
+ String sourceBaseUrl = torperfSources.get(sourceName);
+ String dataUrl = sourceBaseUrl + dataFileName;
+ String dataOutputFileName = sourceName + "-" + dataFileName;
+ File dataOutputFile = new File(torperfOutputDirectory,
+ dataOutputFileName);
+ boolean downloadedDataFile = this.downloadAndAppendFile(dataUrl,
+ dataOutputFile, true);
+
+ /* Download and append the .extradata file. */
+ String extradataFileName = parts[4];
+ String extradataUrl = sourceBaseUrl + extradataFileName;
+ String extradataOutputFileName = sourceName + "-" + extradataFileName;
+ File extradataOutputFile = new File(torperfOutputDirectory,
+ extradataOutputFileName);
+ boolean downloadedExtradataFile = this.downloadAndAppendFile(
+ extradataUrl, extradataOutputFile, false);
+
+ /* Merge both files into .tpf format. */
+ if (!downloadedDataFile && !downloadedExtradataFile) {
+ return;
+ }
+ String skipUntil = null;
+ if (this.lastMergedTimestamps.containsKey(dataOutputFileName)) {
+ skipUntil = this.lastMergedTimestamps.get(dataOutputFileName);
+ }
+ try {
+ skipUntil = this.mergeFiles(dataOutputFile, extradataOutputFile,
+ sourceName, fileSize, skipUntil);
+ } catch (IOException e) {
+ this.logger.log(Level.WARNING, "Failed merging " + dataOutputFile
+ + " and " + extradataOutputFile + ".", e);
+ }
+ if (skipUntil != null) {
+ this.lastMergedTimestamps.put(dataOutputFileName, skipUntil);
}
}
- private void downloadAndMergeFile(String url, File outputFile,
+ private boolean downloadAndAppendFile(String url, File outputFile,
boolean isDataFile) {
+
+ /* Read an existing output file to determine which line will be the
+ * first to append to it. */
String lastTimestampLine = null;
int linesAfterLastTimestampLine = 0;
if (outputFile.exists() && outputFile.lastModified() >
System.currentTimeMillis() - 330L * 60L * 1000L) {
- return;
+ return false;
} else if (outputFile.exists()) {
try {
BufferedReader br = new BufferedReader(new FileReader(
@@ -85,10 +192,10 @@ public class TorperfDownloader {
}
br.close();
} catch (IOException e) {
- logger.log(Level.WARNING, "Failed reading '"
- + outputFile.getAbsolutePath() + "' to find the last line to "
- + "append to.", e);
- return;
+ this.logger.log(Level.WARNING, "Failed reading '"
+ + outputFile.getAbsolutePath() + "' to determine the first "
+ + "line to append to it.", e);
+ return false;
}
}
try {
@@ -120,14 +227,14 @@ public class TorperfDownloader {
bw.close();
br.close();
} catch (IOException e) {
- logger.log(Level.WARNING, "Failed downloading and merging '" + url
- + "'.", e);
- return;
+ this.logger.log(Level.WARNING, "Failed downloading and/or merging '"
+ + url + "'.", e);
+ return false;
}
if (lastTimestampLine == null) {
- logger.warning("'" + outputFile.getAbsolutePath() + "' doesn't "
- + "contain any timestamp lines. Unable to check whether that "
- + "file is stale or not.");
+ this.logger.warning("'" + outputFile.getAbsolutePath()
+ + "' doesn't contain any timestamp lines. Unable to check "
+ + "whether that file is stale or not.");
} else {
long lastTimestampMillis = -1L;
if (isDataFile) {
@@ -141,11 +248,318 @@ public class TorperfDownloader {
}
if (lastTimestampMillis < System.currentTimeMillis()
- 330L * 60L * 1000L) {
- logger.warning("The last timestamp in '"
+ this.logger.warning("The last timestamp in '"
+ outputFile.getAbsolutePath() + "' is more than 5:30 hours "
+ "old: " + lastTimestampMillis);
}
}
+ return true;
+ }
+
+ private String mergeFiles(File dataFile, File extradataFile,
+ String source, int fileSize, String skipUntil) throws IOException {
+ SortedMap<String, String> config = new TreeMap<String, String>();
+ config.put("SOURCE", source);
+ config.put("FILESIZE", String.valueOf(fileSize));
+ if (!dataFile.exists() || !extradataFile.exists()) {
+ this.logger.warning("File " + dataFile.getAbsolutePath() + " or "
+ + extradataFile.getAbsolutePath() + " is missing.");
+ return null;
+ }
+ this.logger.fine("Merging " + dataFile.getAbsolutePath() + " and "
+ + extradataFile.getAbsolutePath() + " into .tpf format.");
+ BufferedReader brD = new BufferedReader(new FileReader(dataFile)),
+ brE = new BufferedReader(new FileReader(extradataFile));
+ String lineD = brD.readLine(), lineE = brE.readLine();
+ int d = 1, e = 1;
+ String maxDataComplete = null, maxUsedAt = null;
+ while (lineD != null) {
+
+ /* Parse .data line. Every valid .data line will go into the .tpf
+ * format, either with additional information from the .extradata
+ * file or without it. */
+ if (lineD.isEmpty()) {
+ this.logger.finer("Skipping empty line " + dataFile.getName() + ":"
+ + d++ + ".");
+ lineD = brD.readLine();
+ continue;
+ }
+ SortedMap<String, String> data = this.parseDataLine(lineD);
+ if (data == null) {
+ this.logger.warning("Skipping illegal line .data:" + d++ + " '"
+ + lineD + "'.");
+ lineD = brD.readLine();
+ continue;
+ }
+ String dataComplete = data.get("DATACOMPLETE");
+ double dataCompleteSeconds = Double.parseDouble(dataComplete);
+ if (skipUntil != null && dataComplete.compareTo(skipUntil) < 0) {
+ this.logger.finer("Skipping " + dataFile.getName() + ":"
+ + d++ + " which we already processed before.");
+ lineD = brD.readLine();
+ continue;
+ }
+ maxDataComplete = dataComplete;
+
+ /* Parse .extradata line if available and try to find the one that
+ * matches the .data line. */
+ SortedMap<String, String> extradata = null;
+ while (lineE != null) {
+ if (lineE.isEmpty()) {
+ this.logger.finer("Skipping " + extradataFile.getName() + ":"
+ + e++ + " which is empty.");
+ lineE = brE.readLine();
+ continue;
+ }
+ if (lineE.startsWith("BUILDTIMEOUT_SET ")) {
+ this.logger.finer("Skipping " + extradataFile.getName() + ":"
+ + e++ + " which is a BUILDTIMEOUT_SET line.");
+ lineE = brE.readLine();
+ continue;
+ } else if (lineE.startsWith("ok ") ||
+ lineE.startsWith("error ")) {
+ this.logger.finer("Skipping " + extradataFile.getName() + ":"
+ + e++ + " which is in the old format.");
+ lineE = brE.readLine();
+ continue;
+ }
+ extradata = this.parseExtradataLine(lineE);
+ if (extradata == null) {
+ this.logger.warning("Skipping Illegal line .extradata:" + e++
+ + " '" + lineE + "'.");
+ lineE = brE.readLine();
+ continue;
+ }
+ if (!extradata.containsKey("USED_AT")) {
+ this.logger.finer("Skipping " + extradataFile.getName() + ":"
+ + e++ + " which doesn't contain a USED_AT element.");
+ lineE = brE.readLine();
+ continue;
+ }
+ String usedAt = extradata.get("USED_AT");
+ double usedAtSeconds = Double.parseDouble(usedAt);
+ if (skipUntil != null && usedAt.compareTo(skipUntil) < 0) {
+ this.logger.finer("Skipping " + extradataFile.getName() + ":"
+ + e++ + " which we already processed before.");
+ lineE = brE.readLine();
+ continue;
+ }
+ maxUsedAt = usedAt;
+ if (Math.abs(usedAtSeconds - dataCompleteSeconds) <= 1.0) {
+ this.logger.fine("Merging " + extradataFile.getName() + ":"
+ + e++ + " into the current .data line.");
+ lineE = brE.readLine();
+ break;
+ } else if (usedAtSeconds > dataCompleteSeconds) {
+ this.logger.finer("Comparing " + extradataFile.getName()
+ + " to the next .data line.");
+ extradata = null;
+ break;
+ } else {
+ this.logger.finer("Skipping " + extradataFile.getName() + ":"
+ + e++ + " which is too old to be merged with .data:" + d
+ + ".");
+ lineE = brE.readLine();
+ continue;
+ }
+ }
+
+ /* Write output line to .tpf file. */
+ SortedMap<String, String> keysAndValues =
+ new TreeMap<String, String>();
+ if (extradata != null) {
+ keysAndValues.putAll(extradata);
+ }
+ keysAndValues.putAll(data);
+ keysAndValues.putAll(config);
+ this.logger.fine("Writing " + dataFile.getName() + ":" + d++ + ".");
+ lineD = brD.readLine();
+ try {
+ this.writeTpfLine(source, fileSize, keysAndValues);
+ } catch (IOException ex) {
+ this.logger.log(Level.WARNING, "Error writing output line. "
+ + "Aborting to merge " + dataFile.getName() + " and "
+ + extradataFile.getName() + ".", e);
+ break;
+ }
+ }
+ brD.close();
+ brE.close();
+ this.writeCachedTpfLines();
+ if (maxDataComplete == null) {
+ return maxUsedAt;
+ } else if (maxUsedAt == null) {
+ return maxDataComplete;
+ } else if (maxDataComplete.compareTo(maxUsedAt) > 0) {
+ return maxUsedAt;
+ } else {
+ return maxDataComplete;
+ }
+ }
+
+ private SortedMap<Integer, String> dataTimestamps;
+ private SortedMap<String, String> parseDataLine(String line) {
+ String[] parts = line.trim().split(" ");
+ if (line.length() == 0 || parts.length < 20) {
+ return null;
+ }
+ if (this.dataTimestamps == null) {
+ this.dataTimestamps = new TreeMap<Integer, String>();
+ this.dataTimestamps.put(0, "START");
+ this.dataTimestamps.put(2, "SOCKET");
+ this.dataTimestamps.put(4, "CONNECT");
+ this.dataTimestamps.put(6, "NEGOTIATE");
+ this.dataTimestamps.put(8, "REQUEST");
+ this.dataTimestamps.put(10, "RESPONSE");
+ this.dataTimestamps.put(12, "DATAREQUEST");
+ this.dataTimestamps.put(14, "DATARESPONSE");
+ this.dataTimestamps.put(16, "DATACOMPLETE");
+ this.dataTimestamps.put(21, "DATAPERC10");
+ this.dataTimestamps.put(23, "DATAPERC20");
+ this.dataTimestamps.put(25, "DATAPERC30");
+ this.dataTimestamps.put(27, "DATAPERC40");
+ this.dataTimestamps.put(29, "DATAPERC50");
+ this.dataTimestamps.put(31, "DATAPERC60");
+ this.dataTimestamps.put(33, "DATAPERC70");
+ this.dataTimestamps.put(35, "DATAPERC80");
+ this.dataTimestamps.put(37, "DATAPERC90");
+ }
+ SortedMap<String, String> data = new TreeMap<String, String>();
+ try {
+ for (Map.Entry<Integer, String> e : this.dataTimestamps.entrySet()) {
+ int i = e.getKey();
+ if (parts.length > i + 1) {
+ String key = e.getValue();
+ String value = String.format("%s.%02d", parts[i],
+ Integer.parseInt(parts[i + 1]) / 10000);
+ data.put(key, value);
+ }
+ }
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ data.put("WRITEBYTES", parts[18]);
+ data.put("READBYTES", parts[19]);
+ if (parts.length >= 21) {
+ data.put("DIDTIMEOUT", parts[20]);
+ }
+ return data;
+ }
+
+ private SortedMap<String, String> parseExtradataLine(String line) {
+ String[] parts = line.split(" ");
+ SortedMap<String, String> extradata = new TreeMap<String, String>();
+ String previousKey = null;
+ for (String part : parts) {
+ String[] keyAndValue = part.split("=", -1);
+ if (keyAndValue.length == 2) {
+ String key = keyAndValue[0];
+ previousKey = key;
+ String value = keyAndValue[1];
+ if (value.contains(".") && value.lastIndexOf(".") ==
+ value.length() - 2) {
+ /* Make sure that all floats have two trailing digits. */
+ value += "0";
+ }
+ extradata.put(key, value);
+ } else if (keyAndValue.length == 1 && previousKey != null) {
+ String value = keyAndValue[0];
+ if (previousKey.equals("STREAM_FAIL_REASONS") &&
+ (value.equals("MISC") || value.equals("EXITPOLICY") ||
+ value.equals("RESOURCELIMIT") ||
+ value.equals("RESOLVEFAILED"))) {
+ extradata.put(previousKey, extradata.get(previousKey) + ":"
+ + value);
+ } else {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+ return extradata;
+ }
+
+ private String cachedSource;
+ private int cachedFileSize;
+ private String cachedStartDate;
+ private SortedMap<String, String> cachedTpfLines;
+ private void writeTpfLine(String source, int fileSize,
+ SortedMap<String, String> keysAndValues) throws IOException {
+ StringBuilder sb = new StringBuilder();
+ int written = 0;
+ for (Map.Entry<String, String> keyAndValue :
+ keysAndValues.entrySet()) {
+ String key = keyAndValue.getKey();
+ String value = keyAndValue.getValue();
+ sb.append((written++ > 0 ? " " : "") + key + "=" + value);
+ }
+ String line = sb.toString();
+ String startString = keysAndValues.get("START");
+ long startMillis = Long.parseLong(startString.substring(0,
+ startString.indexOf("."))) * 1000L;
+ String startDate = dateFormat.format(startMillis);
+ if (this.cachedTpfLines == null || !source.equals(this.cachedSource) ||
+ fileSize != this.cachedFileSize ||
+ !startDate.equals(this.cachedStartDate)) {
+ this.writeCachedTpfLines();
+ this.readTpfLinesToCache(source, fileSize, startDate);
+ }
+ if (!this.cachedTpfLines.containsKey(startString) ||
+ line.length() > this.cachedTpfLines.get(startString).length()) {
+ this.cachedTpfLines.put(startString, line);
+ }
+ }
+
+ private void readTpfLinesToCache(String source, int fileSize,
+ String startDate) throws IOException {
+ this.cachedTpfLines = new TreeMap<String, String>();
+ this.cachedSource = source;
+ this.cachedFileSize = fileSize;
+ this.cachedStartDate = startDate;
+ File tpfFile = new File(torperfOutputDirectory,
+ startDate.replaceAll("-", "/") + "/"
+ + source + "-" + String.valueOf(fileSize) + "-" + startDate
+ + ".tpf");
+ if (!tpfFile.exists()) {
+ return;
+ }
+ BufferedReader br = new BufferedReader(new FileReader(tpfFile));
+ String line;
+ while ((line = br.readLine()) != null) {
+ if (line.startsWith("@type ")) {
+ continue;
+ }
+ if (line.contains("START=")) {
+ String startString = line.substring(line.indexOf("START=")
+ + "START=".length()).split(" ")[0];
+ this.cachedTpfLines.put(startString, line);
+ }
+ }
+ br.close();
+ }
+
+ private void writeCachedTpfLines() throws IOException {
+ if (this.cachedSource == null || this.cachedFileSize == 0 ||
+ this.cachedStartDate == null || this.cachedTpfLines == null) {
+ return;
+ }
+ File tpfFile = new File(torperfOutputDirectory,
+ this.cachedStartDate.replaceAll("-", "/")
+ + "/" + this.cachedSource + "-"
+ + String.valueOf(this.cachedFileSize) + "-"
+ + this.cachedStartDate + ".tpf");
+ tpfFile.getParentFile().mkdirs();
+ BufferedWriter bw = new BufferedWriter(new FileWriter(tpfFile));
+ bw.write("@type torperf 1.0\n");
+ for (String line : this.cachedTpfLines.values()) {
+ bw.write(line + "\n");
+ }
+ bw.close();
+ this.cachedSource = null;
+ this.cachedFileSize = 0;
+ this.cachedStartDate = null;
+ this.cachedTpfLines = null;
}
}
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits