Index: script/bootchartd
===================================================================
RCS file: /cvsroot/bootchart/bootchart/script/bootchartd,v
retrieving revision 1.12
diff -u -r1.12 bootchartd
--- script/bootchartd 5 Jul 2005 20:13:51 -0000 1.12
+++ script/bootchartd 11 Oct 2005 10:37:07 -0000
@@ -20,11 +20,15 @@
VERSION="0.8"
# Read configuration.
-if [ -f /etc/bootchartd.conf ]; then
- . /etc/bootchartd.conf
+if [ -f $PWD/bootchartd.conf ]; then
+ . $PWD/bootchartd.conf
else
- echo "/etc/bootchartd.conf missing"
- exit 1
+ if [ -f /etc/bootchartd.conf ]; then
+ . /etc/bootchartd.conf
+ else
+ echo "/etc/bootchartd.conf missing" >&2
+ exit 1
+ fi
fi
# The path to GNU accounting utilities (optional)
@@ -71,6 +75,7 @@
elif [ "$#" -gt 0 ]; then
# If a command was passed, run it
# (used for profiling specific applications)
+ sleep $SAMPLE_PERIOD
$@
echo "profile.process = $@" >> "$TMPFS_MOUNT"/header
stop
@@ -87,12 +92,18 @@
local logfile="$2"
while [ -f "$BOOTLOG_LOCK" ]; do
# Write the time (in jiffies).
- read cpustat < /proc/stat
- get_uptime $cpustat
+ read uptime < /proc/uptime
+ uptime=${uptime%% [0-9]*}
+ uptime=${uptime/./}
+ echo $uptime
# Log the command output
if [ "$cmd" = "__proc_stat__" ]; then
- cat /proc/*/stat 2>/dev/null
+ if [ "$SINGLE_UID" = "yes" ]; then
+ find /proc/[0-9]* -noleaf -mindepth 1 -maxdepth 1 -user $EUID -name stat 2> /dev/null | xargs cat 2> /dev/null
+ else
+ cat /proc/*/stat 2>/dev/null
+ fi
else
$cmd 2>/dev/null
fi
@@ -135,10 +146,9 @@
done;
}
-
# Stop the boot logger. The lock file is removed to force the loggers in
# background to exit. Some final log files are created and then all log files
-# from the tmpfs are packaged and stored in /var/log.
+# from the tmpfs are packaged and stored in $LOG_DIR.
stop()
{
if [ ! -f "$BOOTLOG_LOCK" ]; then
@@ -165,7 +175,7 @@
# Render the chart if configured (and the renderer is installed)
[ "$AUTO_RENDER" = "yes" -a -x /usr/bin/bootchart ] && \
- /usr/bin/bootchart -o /var/log/ -f $AUTO_RENDER_FORMAT
+ /usr/bin/bootchart -o $LOG_DIR/ -f $AUTO_RENDER_FORMAT
}
@@ -181,7 +191,7 @@
elif [ -f /etc/SuSE-release ]; then
echo "system.release = $( sed q /etc/SuSE-release )"
elif [ -f /etc/debian_version ]; then
- echo "system.release = Debian GNU/$( uname -s ) $( cat /etc/debian_version )" >> "$HEADER"
+ echo "system.release = Debian GNU/$( uname -s ) $( cat /etc/debian_version )"
else
echo "system.release = $( sed 's/\\.//g;q' /etc/issue )"
fi
Index: src/org/bootchart/Main.java
===================================================================
RCS file: /cvsroot/bootchart/bootchart/src/org/bootchart/Main.java,v
retrieving revision 1.13
diff -u -r1.13 Main.java
--- src/org/bootchart/Main.java 5 Jul 2005 20:07:03 -0000 1.13
+++ src/org/bootchart/Main.java 11 Oct 2005 10:37:07 -0000
@@ -47,8 +47,10 @@
import org.bootchart.common.BootStats;
import org.bootchart.common.Common;
import org.bootchart.common.ProcessTree;
+import org.bootchart.common.PsStats;
import org.bootchart.common.Stats;
import org.bootchart.parser.HeaderParser;
+import org.bootchart.parser.MilestonesParser;
import org.bootchart.parser.linux.PacctParser;
import org.bootchart.parser.linux.PidNameParser;
import org.bootchart.parser.linux.ProcDiskStatParser;
@@ -138,6 +140,8 @@
inputFiles.add(logTarball);
} else if (logDir.exists()) {
inputFiles.add(logDir);
+ } else {
+ System.err.println("/var/log/bootchart not found");
}
}
@@ -199,12 +203,13 @@
Properties headers = new Properties();
Map pidNameMap = null;
- List processList = null;
int samplePeriod = -1;
Map forkMap = null;
Stats cpuStats = null;
Stats diskStats = null;
+ PsStats psStats = null;
BootStats bootStats = null;
+ List milestonesList = null;
int numCpu = 1;
String monitoredApp = null; // used for profiling specific processes
@@ -243,17 +248,11 @@
} else if (logName.equals("ps.log")) {
// read ps log file
- PsParser.PsStats psStats =
- PsParser.parseLog(is, pidNameMap, forkMap);
- processList = psStats.processList;
- samplePeriod = psStats.samplePeriod;
-
+ psStats = PsParser.parseLog(is, pidNameMap, forkMap);
+
} else if (logName.equals("proc_ps.log")) {
// read the /proc/[PID]/stat log file
- ProcPsParser.PsStats psStats =
- ProcPsParser.parseLog(is, pidNameMap, forkMap);
- processList = psStats.processList;
- samplePeriod = psStats.samplePeriod;
+ psStats = ProcPsParser.parseLog(is, pidNameMap, forkMap);
} else if (logName.equals("proc_stat.log")) {
// read the /proc/stat log file
@@ -267,6 +266,10 @@
// parse process forking PID mappings
forkMap = PacctParser.parseLog(is);
+ } else if (logName.equals("milestones.log")) {
+ // parse boot process milestones
+ milestonesList = MilestonesParser.parseLog(is);
+
} else if (logName.equals("init_pidname.log")) {
// map pids to command names (useful for Gentoo, where most init
// processes are sourced and thus shown as "rc boot" or
@@ -280,13 +283,12 @@
long opTime = 0;
if (bootStats == null) {
- ProcessTree procTree =
- new ProcessTree(Collections.EMPTY_LIST, 0, monitoredApp, prune);
- if (processList == null || processList.size() == 0) {
+ ProcessTree procTree = new ProcessTree(null, monitoredApp, prune);
+ if (psStats == null || psStats.processList.size() == 0) {
log.warning("No process samples");
} else {
opTime = System.currentTimeMillis();
- procTree = new ProcessTree(processList, samplePeriod, monitoredApp, prune);
+ procTree = new ProcessTree(psStats, monitoredApp, prune);
opTime = System.currentTimeMillis() - opTime;
log.fine("Tree generation and pruning took " + opTime + " ms");
@@ -295,7 +297,8 @@
}
}
//log.fine(procTree.toString());
- bootStats = new BootStats(cpuStats, diskStats, procTree);
+ MilestonesParser.adjustOffset(milestonesList, procTree.startTime);
+ bootStats = new BootStats(cpuStats, diskStats, procTree, milestonesList);
}
Renderer renderer = null;
@@ -345,6 +348,7 @@
options.addOption("n", "no-prune", false,
"do not prune the process tree");
+
return options;
}
Index: src/org/bootchart/common/BootStats.java
===================================================================
RCS file: /cvsroot/bootchart/bootchart/src/org/bootchart/common/BootStats.java,v
retrieving revision 1.1
diff -u -r1.1 BootStats.java
--- src/org/bootchart/common/BootStats.java 21 Jan 2005 00:24:19 -0000 1.1
+++ src/org/bootchart/common/BootStats.java 11 Oct 2005 10:37:07 -0000
@@ -19,6 +19,8 @@
*/
package org.bootchart.common;
+import java.util.List;
+
/**
* BootStats encapsulates boot statistics. This includes global CPU and
* disk I/O statistics and a process tree with process accounting.
@@ -30,6 +32,8 @@
public Stats diskStats;
/** The process tree.*/
public ProcessTree procTree;
+ /** The milestone list.*/
+ public List milestonesList;
/**
* Creates a new boot statistics instance.
@@ -38,9 +42,10 @@
* @param diskStats disk utilization and throughput I/O statistics
* @param procTree the process tree
*/
- public BootStats(Stats cpuStats, Stats diskStats, ProcessTree procTree) {
+ public BootStats(Stats cpuStats, Stats diskStats, ProcessTree procTree, List milestonesList) {
this.cpuStats = cpuStats;
this.diskStats = diskStats;
this.procTree = procTree;
+ this.milestonesList = milestonesList;
}
}
Index: src/org/bootchart/common/ProcessTree.java
===================================================================
RCS file: /cvsroot/bootchart/bootchart/src/org/bootchart/common/ProcessTree.java,v
retrieving revision 1.10
diff -u -r1.10 ProcessTree.java
--- src/org/bootchart/common/ProcessTree.java 10 Apr 2005 13:33:46 -0000 1.10
+++ src/org/bootchart/common/ProcessTree.java 11 Oct 2005 10:37:07 -0000
@@ -68,8 +68,9 @@
private static final SimpleDateFormat TIME_FORMAT =
new SimpleDateFormat("mm:ss.SSS", Common.LOCALE);
- /** The start time of the first process in the tree. */
+ /** The start time of the graph */
public Date startTime;
+ public Date endTime;
/**
* The duration of the process tree (measured from the start time of
@@ -78,15 +79,14 @@
*/
public long duration;
- /** Statistics sampling period. */
+ /** The process statistics */
+ private PsStats psStats;
+ private List processList;
public int samplePeriod;
/** The number of all processes in the tree. */
public int numProc;
-
- /** List of {@link Process} instances. */
- private List processList;
-
+
/** The {@link Process} tree. */
public List processTree;
@@ -94,23 +94,37 @@
* Creates a new process tree from the specified list of
* Process instances.
*
- * @param processList list of process instances
- * @param samplePeriod sampling period
+ * @param psStats process statistics
* @param monitoredApp monitored application (or null if
* the boot process is monitored)
* @param prune whether to prune the tree by removing sleepy and
* short-living processes and merging threads
*/
- public ProcessTree(List processList, int samplePeriod, String monitoredApp,
- boolean prune) {
- this.processList = processList;
- this.samplePeriod = samplePeriod;
-
+ public ProcessTree(PsStats psStats, String monitoredApp, boolean prune) {
+ this.psStats = psStats;
+ if(psStats == null || psStats.processList.size() == 0) {
+ this.processList = null;
+ return;
+ }
+
+ this.processList = psStats.processList;
+ this.samplePeriod = psStats.samplePeriod;
+
if (processList == null || processList.size() == 0) {
return;
}
-
+
+ /**
+ * If we're monitoring an application, remove all processes that
+ * existed before the app started
+ */
+ if(monitoredApp != null) {
+ int preExistingProcsRemoved = removePreExisting();
+ log.fine("Removing " + preExistingProcsRemoved + " pre-existing processes");
+ }
+
build();
+System.out.println("***** PROCESS TREE IS" + processTree + "\n*****");
log.fine("Number of all processes: " + numNodes(processTree));
/*
@@ -147,15 +161,11 @@
build();
}
- // compute the process tree times
- startTime = getStartTime(processTree);
- Date endTime = getEndTime(processTree);
- duration = endTime.getTime() - startTime.getTime();
-
/* Merge the logger's processes, since it forks lots of sleeps and
* other processes.
*/
- int numRemoved = mergeLogger(processTree, LOGGER_PROC, monitoredApp);
+int numRemoved = 0;
+// int numRemoved = mergeLogger(processTree, LOGGER_PROC, monitoredApp);
if (monitoredApp != null) { // profiling specific applications
if (prune) {
@@ -187,10 +197,20 @@
//log.fine(toString());
// update process tree times
- startTime = getStartTime(processTree);
- endTime = getEndTime(processTree);
+ if(monitoredApp == null) {
+ // Start time is time at which first process was started
+ startTime = getStartTime(processTree);
+ endTime = getEndTime(processTree);
+ } else {
+ // Start time is when stat collection started
+ startTime = psStats.startTime;
+ endTime = psStats.endTime;
+ }
+
duration = endTime.getTime() - startTime.getTime();
log.fine("Boot time: " + duration);
+System.out.println(processList);
+System.out.println(this);
}
/**
@@ -201,7 +221,7 @@
for (Iterator i=processList.iterator(); i.hasNext(); ) {
Process proc = (Process)i.next();
// find the parent process
- if (proc.parent != null) {
+ if (proc.parent != null && processList.contains(proc.parent)) {
proc.parent.childList.add(proc);
} else {
// a root process
@@ -445,6 +465,7 @@
}
private static void mergeProcesses(Process p1, Process p2) {
+System.out.println("merging process " + p1.pid + "/" + p1.cmd + " with " + p2.pid + "/" + p2.cmd);
p1.samples.addAll(p2.samples);
long p1time = p1.startTime.getTime();
long p2time = p2.startTime.getTime();
@@ -487,6 +508,26 @@
}
return numRemoved;
}
+
+ /**
+ * Removes processes that already existed when data collection started.
+ * Useful for when a specific application is being monitored.
+ * @return the number of removed processes
+ */
+ private int removePreExisting() {
+ int numRemoved = 0;
+
+ for(Iterator i = processList.iterator(); i.hasNext(); ) {
+ Process p = (Process) i.next();
+ if(p.startTime.getTime() < psStats.startTime.getTime()) {
+System.out.println("Removing process " + p.cmd);
+ i.remove();
+ numRemoved++;
+ }
+ }
+
+ return numRemoved;
+ }
/**
* Sort process tree.
Index: src/org/bootchart/parser/linux/ProcPsParser.java
===================================================================
RCS file: /cvsroot/bootchart/bootchart/src/org/bootchart/parser/linux/ProcPsParser.java,v
retrieving revision 1.11
diff -u -r1.11 ProcPsParser.java
--- src/org/bootchart/parser/linux/ProcPsParser.java 10 Apr 2005 13:33:54 -0000 1.11
+++ src/org/bootchart/parser/linux/ProcPsParser.java 11 Oct 2005 10:37:07 -0000
@@ -35,6 +35,7 @@
import org.bootchart.common.Common;
import org.bootchart.common.Process;
import org.bootchart.common.ProcessSample;
+import org.bootchart.common.PsStats;
import org.bootchart.common.Sample;
@@ -46,14 +47,6 @@
*/
public class ProcPsParser {
private static final Logger log = Logger.getLogger(ProcPsParser.class.getName());
-
- /** Process statistics. */
- public static class PsStats {
- /** A list of processes (with enclosing CPU samples). */
- public List processList;
- /** Statistics sampling period. */
- public int samplePeriod;
- }
/**
* Parses the proc_ps.log file. The output from
@@ -269,6 +262,8 @@
PsStats psStats = new PsStats();
psStats.processList = new ArrayList(processMap.values());
psStats.samplePeriod = samplePeriod;
+ psStats.startTime = startTime;
+ psStats.endTime = time;
return psStats;
}
Index: src/org/bootchart/parser/linux/PsParser.java
===================================================================
RCS file: /cvsroot/bootchart/bootchart/src/org/bootchart/parser/linux/PsParser.java,v
retrieving revision 1.6
diff -u -r1.6 PsParser.java
--- src/org/bootchart/parser/linux/PsParser.java 13 Feb 2005 21:57:06 -0000 1.6
+++ src/org/bootchart/parser/linux/PsParser.java 11 Oct 2005 10:37:07 -0000
@@ -35,6 +35,7 @@
import org.bootchart.common.Common;
import org.bootchart.common.Process;
import org.bootchart.common.ProcessSample;
+import org.bootchart.common.PsStats;
import org.bootchart.common.Sample;
@@ -44,14 +45,6 @@
public class PsParser {
private static final Logger log = Logger.getLogger(PsParser.class.getName());
- /** Process statistics. */
- public static class PsStats {
- /** A list of processes (with enclosing CPU samples). */
- public List processList;
- /** Statistics sampling period. */
- public int samplePeriod;
- }
-
/** The mapping between ps column types and Java objects. */
private static Map COLUMN_TYPES = new HashMap();
@@ -247,6 +240,8 @@
}
PsStats psStats = new PsStats();
psStats.processList = new ArrayList(processMap.values());
+ psStats.startTime = startTime;
+ psStats.endTime = time;
psStats.samplePeriod = samplePeriod;
return psStats;
}
Index: src/org/bootchart/renderer/ImageRenderer.java
===================================================================
RCS file: /cvsroot/bootchart/bootchart/src/org/bootchart/renderer/ImageRenderer.java,v
retrieving revision 1.15
diff -u -r1.15 ImageRenderer.java
--- src/org/bootchart/renderer/ImageRenderer.java 4 Jul 2005 12:27:19 -0000 1.15
+++ src/org/bootchart/renderer/ImageRenderer.java 11 Oct 2005 10:37:07 -0000
@@ -48,6 +48,7 @@
import org.bootchart.common.DiskTPutSample;
import org.bootchart.common.DiskUtilSample;
import org.bootchart.common.FileOpenSample;
+import org.bootchart.common.Milestone;
import org.bootchart.common.Process;
import org.bootchart.common.ProcessSample;
import org.bootchart.common.ProcessTree;
@@ -129,6 +130,9 @@
private static final Stroke DEP_STROKE =
new BasicStroke(1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL,
10.0f, new float[]{2.0f, 2.0f}, 0.0f);
+
+ /* How far the milestone is from the milestone text */
+ private static final int MILESTONE_SEP = 16;
/** Boot duration time format. */
private static final DateFormat BOOT_TIME_FORMAT =
@@ -163,6 +167,7 @@
Stats diskStats = bootStats.diskStats;
Stats cpuStats = bootStats.cpuStats;
ProcessTree procTree = bootStats.procTree;
+ List milestonesList = bootStats.milestonesList;
int headerH = 280;
int barH = 55;
@@ -170,10 +175,16 @@
int offX = 10;
int offY = 10;
+ // Milestones
+ // HACK
+ int milestonesH = 0;
+ if(milestonesList != null)
+ milestonesH = milestonesList.size() * 16;
+
int secW = 25; // the width of a second
int w = (int)(procTree.duration * secW / 1000) + 2*offX;
int procH = 16; // the height of a process
- int h = procH * procTree.numProc + headerH + 2*offY;
+ int h = procH * procTree.numProc + headerH + milestonesH + 2*offY;
while (w > MAX_IMG_DIM && secW > 1) {
secW /= 2;
@@ -181,7 +192,7 @@
}
while (h > MAX_IMG_DIM) {
procH = procH * 3 / 4;
- h = procH * procTree.numProc + headerH + 2*offY;
+ h = procH * procTree.numProc + headerH + milestonesH + 2*offY;
}
w = Math.min(w, MAX_IMG_DIM);
@@ -615,6 +626,9 @@
setColor(g, BORDER_COLOR);
g.drawRect(rectX, rectY, rectW, rectH);
+
+ if(milestonesList != null)
+ drawMilestones(milestonesList, offX, procH * (procTree.numProc + 1) + headerH, secW);
setColor(g, SIG_COLOR);
g.setFont(SIG_FONT);
@@ -631,7 +645,7 @@
img = img.getSubimage(0, 0, Math.max(w, titleW), h);
}
}
-
+
private void drawHeader(Properties headers, int offX, long duration) {
setColor(g, TEXT_COLOR);
g.setFont(TITLE_FONT);
@@ -792,6 +806,36 @@
g.drawString(label, sx, y + procH - 2);
}
+ private void drawMilestones(List milestonesList, int startX, int startY, int secW) {
+ int currX, currY;
+ int textHeight = g.getFontMetrics(TEXT_FONT).getMaxAscent();
+
+ setColor(g, BORDER_COLOR);
+ currY = startY + textHeight / 2;
+ for(Iterator i = milestonesList.iterator(); i.hasNext(); ) {
+ Milestone m = (Milestone) i.next();
+ float secs = (float) (m.timestamp.getTime() / 1000.0);
+ currX = (int) (startX + secs * secW);
+
+ g.drawLine(currX, 280, currX, currY);
+ g.drawLine(currX, currY, currX + MILESTONE_SEP, currY);
+ currY += 16;
+ }
+
+ setColor(g, TEXT_COLOR);
+ g.setFont(TEXT_FONT);
+ currY = startY + textHeight;
+ for(Iterator i = milestonesList.iterator(); i.hasNext(); ) {
+ Milestone m = (Milestone) i.next();
+ String str = m.toString();
+ float secs = (float) (m.timestamp.getTime() / 1000.0);
+ currX = (int) (startX + secs * secW);
+
+ g.drawString(str, currX + MILESTONE_SEP, currY);
+ currY += 16;
+ }
+ }
+
/**
* Sets the current color. If allowAlpha is not set (e.g.
* for the EPS renderer), an opaque color is used.
--- /dev/null 2005-10-10 14:55:46.672593000 +0200
+++ src/org/bootchart/common/Milestone.java 2005-08-21 15:27:33.000000000 +0200
@@ -0,0 +1,41 @@
+/*
+ * Bootchart -- Boot Process Visualization
+ *
+ * Copyright (C) 2004 Lorenzo Colitti
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+package org.bootchart.common;
+
+import java.util.Date;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+
+/** Milestone in the boot process */
+public class Milestone {
+ public Date timestamp;
+ public String description;
+ protected DateFormat dateFormat;
+
+ public Milestone(Date timestamp, String description) {
+ this.timestamp = timestamp;
+ this.description = description;
+ this.dateFormat = new SimpleDateFormat("m:ss.S");
+ }
+
+ public String toString() {
+ return dateFormat.format(timestamp) + ": " + description;
+ }
+}
--- /dev/null 2005-10-10 14:55:46.672593000 +0200
+++ src/org/bootchart/common/PsStats.java 2005-08-12 21:20:07.000000000 +0200
@@ -0,0 +1,34 @@
+/*
+ * Bootchart -- Boot Process Visualization
+ *
+ * Copyright (C) 2004 Ziga Mahkovec
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+package org.bootchart.common;
+
+import java.util.Date;
+import java.util.List;
+
+/** Process statistics. */
+public class PsStats {
+ /** A list of processes (with enclosing CPU samples). */
+ public List processList;
+ /** Statistics sampling period. */
+ public int samplePeriod;
+ /** Start and end of sampling period. */
+ public Date startTime;
+ public Date endTime;
+}
--- /dev/null 2005-10-10 14:55:46.672593000 +0200
+++ src/org/bootchart/parser/MilestonesParser.java 2005-08-21 20:30:23.000000000 +0200
@@ -0,0 +1,91 @@
+/*
+ * Bootchart -- Boot Process Visualization
+ *
+ * Copyright (C) 2005 Lorenzo Colitti
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+package org.bootchart.parser;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.logging.Logger;
+
+import org.bootchart.common.Milestone;
+
+/**
+ * MilestonesParser parses the milestones log file, which contains
+ * information about chart milestones.
+ */
+public class MilestonesParser {
+ private static final Logger log = Logger.getLogger(MilestonesParser.class.getName());
+
+ /**
+ * Parses the milestones file. The file contains one line per milestone in the form:
+ * ssssssssss.NNNNNN milestone description string
+ * where ssssssssss.NNNNNN is a unix timestamp made up of seconds and nanoseconds.
+ * e.g:
+ * 1124551705.570623 starting boot process
+ * 1124551710.86122 network up
+ *
+ * @param reader the BufferedReader to read from
+ * @return a List of milestones
+ * @throws IOException if an I/O error occurs
+ */
+ public static List parseLog(InputStream is) throws IOException {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+ List milestonesList = new LinkedList();
+ String line = reader.readLine();
+ while(line != null) {
+ try {
+ /* Split timestamp from description */
+ String dateStr = line.substring(0, line.indexOf(" "));
+ String eventStr = line.substring(line.indexOf(" ") + 1);
+
+ /* Split seconds from nanoseconds */
+ float uptime = Float.parseFloat(dateStr);
+ long millis = (long) (uptime * 1000.0);
+
+ Date date = new Date(millis);
+
+ Milestone m = new Milestone(date, eventStr);
+ milestonesList.add(m);
+ log.fine("milestone at " + date + ": " + eventStr);
+ } catch(Exception e) {
+ } finally {
+ line = reader.readLine();
+ }
+ }
+ return milestonesList;
+ }
+
+ public static void adjustOffset(List milestonesList, Date date) {
+ if(milestonesList == null)
+ return;
+
+ Iterator i = milestonesList.iterator();
+
+ while(i.hasNext()) {
+ Milestone m = (Milestone) i.next();
+ m.timestamp = new Date( (m.timestamp.getTime() - date.getTime()));
+ }
+ }
+}