1b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor#!/usr/bin/python
2b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor#
3b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor# Copyright (C) 2010 The Android Open Source Project
4b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor#
5b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor# Licensed under the Apache License, Version 2.0 (the "License");
6b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor# you may not use this file except in compliance with the License.
7b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor# You may obtain a copy of the License at
8b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor#
9b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor#      http://www.apache.org/licenses/LICENSE-2.0
10b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor#
11b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor# Unless required by applicable law or agreed to in writing, software
12b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor# distributed under the License is distributed on an "AS IS" BASIS,
13b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor# See the License for the specific language governing permissions and
15b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor# limitations under the License.
16b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
17b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnorimport cgi
18b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnorimport csv
19b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnorimport json
20b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnorimport math
21b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnorimport os
22b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnorimport re
23b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnorimport sys
24b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnorimport time
25b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnorimport urllib
26b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
27b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor"""Interpret output from procstatlog and write an HTML report file."""
28b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
29b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
30b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor# TODO: Rethink dygraph-combined.js source URL?
31b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan EgnorPAGE_BEGIN = """
32b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor<html><head>
33b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor<title>%(filename)s</title>
34b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor<script type="text/javascript" src="http://www.corp.google.com/~egnor/no_crawl/dygraph-combined.js"></script>
35b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor<script>
36b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnorvar allCharts = [];
37b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnorvar inDrawCallback = false;
38b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
39b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan EgnorOnDraw = function(me, initial) {
40b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    if (inDrawCallback || initial) return;
41b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    inDrawCallback = true;
42b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    var range = me.xAxisRange();
43b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    for (var j = 0; j < allCharts.length; j++) {
44b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        if (allCharts[j] == me) continue;
45b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        allCharts[j].updateOptions({dateWindow: range});
46b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    }
47b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    inDrawCallback = false;
48b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor}
49b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
50b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan EgnorMakeChart = function(id, filename, options) {
51b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    options.width = "75%%";
52b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    options.xTicker = Dygraph.dateTicker;
53b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    options.xValueFormatter = Dygraph.dateString_;
54b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    options.xAxisLabelFormatter = Dygraph.dateAxisFormatter;
55b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    options.drawCallback = OnDraw;
56b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    allCharts.push(new Dygraph(document.getElementById(id), filename, options));
57b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor}
58b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor</script>
59b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor</head><body>
60b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor<p>
61b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor<span style="font-size: 150%%">%(filename)s</span>
62b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor- stat report generated by %(user)s on %(date)s</p>
63b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor<table cellpadding=0 cellspacing=0 margin=0 border=0>
64b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor"""
65b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
66b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan EgnorCHART = """
67b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor<tr>
68b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor<td valign=top width=25%%>%(label_html)s</td>
69b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor<td id="%(id)s"> </td>
70b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor</tr>
71b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor<script>
72b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan EgnorMakeChart(%(id_js)s, %(filename_js)s, %(options_js)s)
73b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
74b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor</script>
75b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor"""
76b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
77b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan EgnorSPACER = """
78b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor<tr><td colspan=2 height=20> </td></tr>
79b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor"""
80b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
81b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan EgnorTOTAL_CPU_LABEL = """
82b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor<b style="font-size: 150%%">Total CPU</b><br>
83b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnorjiffies: <nobr>%(sys)d sys</nobr>, <nobr>%(user)d user</nobr>
84b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor"""
85b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
86376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan EgnorCPU_SPEED_LABEL = """
87376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor<nobr>average CPU speed</nobr>
88376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor"""
89376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
90b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan EgnorCONTEXT_LABEL = """
91b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnorcontext: <nobr>%(switches)d switches</nobr>
92b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor"""
93b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
94b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan EgnorFAULTS_LABEL = """
95b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor<nobr>page faults:</nobr> <nobr>%(major)d major</nobr>
96b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor"""
97b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
98376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan EgnorBINDER_LABEL = """
99376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnorbinder: <nobr>%(calls)d calls</nobr>
100376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor"""
101376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
102b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan EgnorPROC_CPU_LABEL = """
103b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor<span style="font-size: 150%%">%(process)s</span> (%(pid)d)<br>
104b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnorjiffies: <nobr>%(sys)d sys</nobr>, <nobr>%(user)d user</nobr>
105b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor</div>
106b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor"""
107b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
108b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan EgnorYAFFS_LABEL = """
109376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor<span style="font-size: 150%%">yaffs: %(partition)s</span><br>
110b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnorpages: <nobr>%(nPageReads)d read</nobr>,
111b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor<nobr>%(nPageWrites)d written</nobr><br>
112b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnorblocks: <nobr>%(nBlockErasures)d erased</nobr>
113b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor"""
114b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
115b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan EgnorDISK_LABEL = """
116b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor<span style="font-size: 150%%">disk: %(device)s</span><br>
117b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnorsectors: <nobr>%(reads)d read</nobr>, <nobr>%(writes)d written</nobr>
118b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor"""
119b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor
120b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan EgnorDISK_TIME_LABEL = """
121b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnormsec: <nobr>%(msec)d waiting</nobr>
122b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor"""
123b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor
124376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan EgnorNET_LABEL = """
125376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor<span style="font-size: 150%%">net: %(interface)s</span><br>
126376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnorbytes: <nobr>%(tx)d tx</nobr>,
127b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor<nobr>%(rx)d rx</nobr>
128376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor"""
129376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
130b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan EgnorPAGE_END = """
131b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor</table></body></html>
132b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor"""
133b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
134b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
135b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnordef WriteChartData(titles, datasets, filename):
136b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    writer = csv.writer(file(filename, "w"))
137b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    writer.writerow(["Time"] + titles)
138b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
139b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    merged_rows = {}
140b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    for set_num, data in enumerate(datasets):
141b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        for when, datum in data.iteritems():
142b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            if type(datum) == tuple: datum = "%d/%d" % datum
143b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            merged_rows.setdefault(when, {})[set_num] = datum
144b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
145b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    num_cols = len(datasets)
146b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    for when, values in sorted(merged_rows.iteritems()):
147b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        msec = "%d" % (when * 1000)
148b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        writer.writerow([msec] + [values.get(n, "") for n in range(num_cols)])
149b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
150b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
151b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnordef WriteOutput(history, log_filename, filename):
152b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    out = []
153b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
154b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    out.append(PAGE_BEGIN % {
155b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        "filename": cgi.escape(log_filename),
156b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        "user": cgi.escape(os.environ.get("USER", "unknown")),
157b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        "date": cgi.escape(time.ctime()),
158b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    })
159b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
160b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    files_dir = "%s_files" % os.path.splitext(filename)[0]
161b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    files_url = os.path.basename(files_dir)
162b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    if not os.path.isdir(files_dir): os.makedirs(files_dir)
163b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
164b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    sorted_history = sorted(history.iteritems())
165376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    date_window = [1000 * sorted_history[1][0], 1000 * sorted_history[-1][0]]
166b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
167b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    #
168b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    # Output total CPU statistics
169b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    #
170b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
171b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    sys_jiffies = {}
172b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    sys_user_jiffies = {}
173b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    all_jiffies = {}
174b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    total_sys = total_user = 0
175b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
176b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    last_state = {}
177b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    for when, state in sorted_history:
178b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        last = last_state.get("/proc/stat:cpu", "").split()
179b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        next = state.get("/proc/stat:cpu", "").split()
180b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        if last and next:
181b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            stime = sum([int(next[x]) - int(last[x]) for x in [2, 5, 6]])
182b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            utime = sum([int(next[x]) - int(last[x]) for x in [0, 1]])
183b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            idle = sum([int(next[x]) - int(last[x]) for x in [3, 4]])
184b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            all = stime + utime + idle
185b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            total_sys += stime
186b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            total_user += utime
187b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
188b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            sys_jiffies[when] = (stime, all)
189b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            sys_user_jiffies[when] = (stime + utime, all)
190b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            all_jiffies[when] = all
191b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
192b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        last_state = state
193b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
194b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    WriteChartData(
195b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        ["sys", "sys+user"],
196b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        [sys_jiffies, sys_user_jiffies],
197b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        os.path.join(files_dir, "total_cpu.csv"))
198b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
199b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    out.append(CHART % {
200b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        "id": cgi.escape("total_cpu"),
201b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        "id_js": json.write("total_cpu"),
202b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        "label_html": TOTAL_CPU_LABEL % {"sys": total_sys, "user": total_user},
203b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        "filename_js": json.write(files_url + "/total_cpu.csv"),
204b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        "options_js": json.write({
205b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            "colors": ["blue", "green"],
206b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            "dateWindow": date_window,
207b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            "fillGraph": True,
208b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            "fractions": True,
209b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            "height": 100,
210b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            "valueRange": [0, 110],
211b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        }),
212b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    })
213b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
214b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    #
215376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    # Output CPU speed statistics
216376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    #
217376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
218376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    cpu_speed = {}
219376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    speed_key = "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state:"
220376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
221376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    last_state = {}
222376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    for when, state in sorted_history:
223376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        total_time = total_cycles = 0
224376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        for key in state:
225376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            if not key.startswith(speed_key): continue
226376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
227376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            last = int(last_state.get(key, -1))
228376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            next = int(state.get(key, -1))
229376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            if last != -1 and next != -1:
230376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor                speed = int(key[len(speed_key):])
231376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor                total_time += next - last
232376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor                total_cycles += (next - last) * speed
233376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
234376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        if total_time > 0: cpu_speed[when] = total_cycles / total_time
235376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        last_state = state
236376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
237376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    WriteChartData(
238376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        ["kHz"], [cpu_speed],
239376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        os.path.join(files_dir, "cpu_speed.csv"))
240376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
241376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    out.append(CHART % {
242376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        "id": cgi.escape("cpu_speed"),
243376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        "id_js": json.write("cpu_speed"),
244376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        "label_html": CPU_SPEED_LABEL,
245376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        "filename_js": json.write(files_url + "/cpu_speed.csv"),
246376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        "options_js": json.write({
247376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            "colors": ["navy"],
248376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            "dateWindow": date_window,
249376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            "fillGraph": True,
250376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            "height": 50,
251376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            "includeZero": True,
252376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        }),
253376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    })
254376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
255376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    #
256b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    # Output total context switch statistics
257b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    #
258b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
259376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    context_switches = {}
260b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
261b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    last_state = {}
262b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    for when, state in sorted_history:
263b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        last = int(last_state.get("/proc/stat:ctxt", -1))
264b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        next = int(state.get("/proc/stat:ctxt", -1))
265376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        if last != -1 and next != -1: context_switches[when] = next - last
266b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        last_state = state
267b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
268b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    WriteChartData(
269376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        ["switches"], [context_switches],
270b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        os.path.join(files_dir, "context_switches.csv"))
271b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
272376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    total_switches = sum(context_switches.values())
273b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    out.append(CHART % {
274b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        "id": cgi.escape("context_switches"),
275b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        "id_js": json.write("context_switches"),
276376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        "label_html": CONTEXT_LABEL % {"switches": total_switches},
277b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        "filename_js": json.write(files_url + "/context_switches.csv"),
278b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        "options_js": json.write({
279b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            "colors": ["blue"],
280b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            "dateWindow": date_window,
281b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            "fillGraph": True,
282b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            "height": 50,
283376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            "includeZero": True,
284b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        }),
285b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    })
286b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
287b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    #
288b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    # Collect (no output yet) per-process CPU and major faults
289b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    #
290b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
291b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    process_name = {}
292b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    process_start = {}
293b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    process_sys = {}
294b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    process_sys_user = {}
295b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
296b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    process_faults = {}
297b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    total_faults = {}
298b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    max_faults = 0
299b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
300b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    last_state = {}
301b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    zero_stat = "0 (zero) Z 0 0 0 0 0 0 0 0 0 0 0 0"
302b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    for when, state in sorted_history:
303b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        for key in state:
304b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            if not key.endswith("/stat"): continue
305b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
306b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            last = last_state.get(key, zero_stat).split()
307b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            next = state.get(key, "").split()
308b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            if not next: continue
309b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
310b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            pid = int(next[0])
311b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            process_start.setdefault(pid, when)
312b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            process_name[pid] = next[1][1:-1]
313b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
314b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            all = all_jiffies.get(when, 0)
315b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            if not all: continue
316b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
317b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            faults = int(next[11]) - int(last[11])
318b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            process_faults.setdefault(pid, {})[when] = faults
319b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            tf = total_faults[when] = total_faults.get(when, 0) + faults
320b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            max_faults = max(max_faults, tf)
321b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
322b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            stime = int(next[14]) - int(last[14])
323b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            utime = int(next[13]) - int(last[13])
324b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            process_sys.setdefault(pid, {})[when] = (stime, all)
325b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            process_sys_user.setdefault(pid, {})[when] = (stime + utime, all)
326b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
327b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        last_state = state
328b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
329b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    #
330b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    # Output total major faults (sum over all processes)
331b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    #
332b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
333b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    WriteChartData(
334376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        ["major"], [total_faults],
335b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        os.path.join(files_dir, "total_faults.csv"))
336b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
337b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    out.append(CHART % {
338b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        "id": cgi.escape("total_faults"),
339b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        "id_js": json.write("total_faults"),
340b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        "label_html": FAULTS_LABEL % {"major": sum(total_faults.values())},
341b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        "filename_js": json.write(files_url + "/total_faults.csv"),
342b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        "options_js": json.write({
343b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            "colors": ["gray"],
344b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            "dateWindow": date_window,
345b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            "fillGraph": True,
346b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            "height": 50,
347b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            "valueRange": [0, max_faults * 11 / 10],
348b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        }),
349b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    })
350b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
351b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    #
352376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    # Output binder transaactions
353b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    #
354b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
355376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    binder_calls = {}
356376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
357376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    last_state = {}
358376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    for when, state in sorted_history:
359376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        last = int(last_state.get("/proc/binder/stats:BC_TRANSACTION", -1))
360376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        next = int(state.get("/proc/binder/stats:BC_TRANSACTION", -1))
361376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        if last != -1 and next != -1: binder_calls[when] = next - last
362376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        last_state = state
363376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
364376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    WriteChartData(
365376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        ["calls"], [binder_calls],
366376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        os.path.join(files_dir, "binder_calls.csv"))
367376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
368376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    out.append(CHART % {
369376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        "id": cgi.escape("binder_calls"),
370376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        "id_js": json.write("binder_calls"),
371376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        "label_html": BINDER_LABEL % {"calls": sum(binder_calls.values())},
372376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        "filename_js": json.write(files_url + "/binder_calls.csv"),
373376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        "options_js": json.write({
374376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            "colors": ["green"],
375376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            "dateWindow": date_window,
376376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            "fillGraph": True,
377376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            "height": 50,
378376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            "includeZero": True,
379376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        })
380376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    })
381376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
382376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    #
383376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    # Output network interface statistics
384376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    #
385376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
386376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    if out[-1] != SPACER: out.append(SPACER)
387376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
388376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    interface_rx = {}
389376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    interface_tx = {}
390376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    max_bytes = 0
391376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
392376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    last_state = {}
393376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    for when, state in sorted_history:
394376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        for key in state:
395376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            if not key.startswith("/proc/net/dev:"): continue
396376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
397376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            last = last_state.get(key, "").split()
398376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            next = state.get(key, "").split()
399376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            if not (last and next): continue
400376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
401376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            rx = int(next[0]) - int(last[0])
402376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            tx = int(next[8]) - int(last[8])
403376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            max_bytes = max(max_bytes, rx, tx)
404376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
405376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            net, interface = key.split(":", 1)
406376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            interface_rx.setdefault(interface, {})[when] = rx
407376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            interface_tx.setdefault(interface, {})[when] = tx
408376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
409376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        last_state = state
410376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
411376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    for num, interface in enumerate(sorted(interface_rx.keys())):
412376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        rx, tx = interface_rx[interface], interface_tx[interface]
413376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        total_rx, total_tx = sum(rx.values()), sum(tx.values())
414376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        if not (total_rx or total_tx): continue
415376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
416376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        WriteChartData(
417376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            ["rx", "tx"], [rx, tx],
418376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            os.path.join(files_dir, "net%d.csv" % num))
419376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
420376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        out.append(CHART % {
421376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            "id": cgi.escape("net%d" % num),
422376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            "id_js": json.write("net%d" % num),
423376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            "label_html": NET_LABEL % {
424376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor                "interface": cgi.escape(interface),
425376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor                "rx": total_rx,
426376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor                "tx": total_tx
427376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            },
428376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            "filename_js": json.write("%s/net%d.csv" % (files_url, num)),
429376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            "options_js": json.write({
430376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor                "colors": ["black", "purple"],
431376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor                "dateWindow": date_window,
432376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor                "fillGraph": True,
433376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor                "height": 75,
434376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor                "valueRange": [0, max_bytes * 11 / 10],
435376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            })
436376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        })
437376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
438376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    #
439376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    # Output YAFFS statistics
440376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    #
441376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
442376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    if out[-1] != SPACER: out.append(SPACER)
443b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
444b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    yaffs_vars = ["nBlockErasures", "nPageReads", "nPageWrites"]
445b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    partition_ops = {}
446b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
447b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    last_state = {}
448b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    for when, state in sorted_history:
449b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        for key in state:
450b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            if not key.startswith("/proc/yaffs:"): continue
451b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
452b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            last = int(last_state.get(key, -1))
453b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            next = int(state.get(key, -1))
454b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            if last == -1 or next == -1: continue
455b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
456b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            value = next - last
457376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            yaffs, partition, var = key.split(":", 2)
458b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            ops = partition_ops.setdefault(partition, {})
459b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            if var in yaffs_vars:
460b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor                ops.setdefault(var, {})[when] = value
461b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
462b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        last_state = state
463b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
464b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    for num, (partition, ops) in enumerate(sorted(partition_ops.iteritems())):
465b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        totals = [sum(ops.get(var, {}).values()) for var in yaffs_vars]
466b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        if not sum(totals): continue
467b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
468b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        WriteChartData(
469b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            yaffs_vars,
470b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            [ops.get(var, {}) for var in yaffs_vars],
471b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            os.path.join(files_dir, "yaffs%d.csv" % num))
472b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
473b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        values = {"partition": partition}
474b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        values.update(zip(yaffs_vars, totals))
475b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        out.append(CHART % {
476b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            "id": cgi.escape("yaffs%d" % num),
477b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            "id_js": json.write("yaffs%d" % num),
478b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            "label_html": YAFFS_LABEL % values,
479b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            "filename_js": json.write("%s/yaffs%d.csv" % (files_url, num)),
480b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            "options_js": json.write({
481b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor                "colors": ["maroon", "gray", "teal"],
482b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor                "dateWindow": date_window,
483b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor                "fillGraph": True,
484b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor                "height": 75,
485376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor                "includeZero": True,
486b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            })
487b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        })
488b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
489b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    #
490b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor    # Output non-YAFFS statistics
491b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor    #
492b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor
493b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor    disk_reads = {}
494b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor    disk_writes = {}
495b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor    disk_msec = {}
496b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor    total_io = max_io = max_msec = 0
497b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor
498b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor    last_state = {}
499b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor    for when, state in sorted_history:
500b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor        for key in state:
501b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor            if not key.startswith("/proc/diskstats:"): continue
502b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor
503b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor            last = last_state.get(key, "").split()
504b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor            next = state.get(key, "").split()
505b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor            if not (last and next): continue
506b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor
507b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor            reads = int(next[2]) - int(last[2])
508b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor            writes = int(next[6]) - int(last[6])
509b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor            msec = int(next[10]) - int(last[10])
510b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor            total_io += reads + writes
511b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor            max_io = max(max_io, reads, writes)
512b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor            max_msec = max(max_msec, msec)
513b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor
514b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor            diskstats, device = key.split(":", 1)
515b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor            disk_reads.setdefault(device, {})[when] = reads
516b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor            disk_writes.setdefault(device, {})[when] = writes
517b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor            disk_msec.setdefault(device, {})[when] = msec
518b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor
519b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor        last_state = state
520b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor
521b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor    io_cutoff = total_io / 100
522b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor    for num, device in enumerate(sorted(disk_reads.keys())):
523b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor        if [d for d in disk_reads.keys()
524b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor            if d.startswith(device) and d != device]: continue
525b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor
526b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor        reads, writes = disk_reads[device], disk_writes[device]
527b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor        total_reads, total_writes = sum(reads.values()), sum(writes.values())
528b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor        if total_reads + total_writes <= io_cutoff: continue
529b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor
530b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor        WriteChartData(
531b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor            ["reads", "writes"], [reads, writes],
532b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor            os.path.join(files_dir, "disk%d.csv" % num))
533b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor
534b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor        out.append(CHART % {
535b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor            "id": cgi.escape("disk%d" % num),
536b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor            "id_js": json.write("disk%d" % num),
537b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor            "label_html": DISK_LABEL % {
538b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor                "device": cgi.escape(device),
539b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor                "reads": total_reads,
540b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor                "writes": total_writes,
541b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor            },
542b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor            "filename_js": json.write("%s/disk%d.csv" % (files_url, num)),
543b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor            "options_js": json.write({
544b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor                "colors": ["gray", "teal"],
545b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor                "dateWindow": date_window,
546b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor                "fillGraph": True,
547b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor                "height": 75,
548b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor                "valueRange": [0, max_io * 11 / 10],
549b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor            }),
550b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor        })
551b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor
552b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor        msec = disk_msec[device]
553b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor
554b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor        WriteChartData(
555b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor            ["msec"], [msec],
556b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor            os.path.join(files_dir, "disk%d_time.csv" % num))
557b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor
558b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor        out.append(CHART % {
559b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor            "id": cgi.escape("disk%d_time" % num),
560b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor            "id_js": json.write("disk%d_time" % num),
561b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor            "label_html": DISK_TIME_LABEL % {"msec": sum(msec.values())},
562b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor            "filename_js": json.write("%s/disk%d_time.csv" % (files_url, num)),
563b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor            "options_js": json.write({
564b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor                "colors": ["blue"],
565b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor                "dateWindow": date_window,
566b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor                "fillGraph": True,
567b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor                "height": 50,
568b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor                "valueRange": [0, max_msec * 11 / 10],
569b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor            }),
570b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor        })
571b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor
572b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor    #
573b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    # Output per-process CPU and page faults collected earlier
574b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    #
575b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
576b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor    cpu_cutoff = (total_sys + total_user) / 200
577b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor    faults_cutoff = sum(total_faults.values()) / 100
578b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    for start, pid in sorted([(s, p) for p, s in process_start.iteritems()]):
579b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        sys = sum([n for n, d in process_sys.get(pid, {}).values()])
580b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        sys_user = sum([n for n, d in process_sys_user.get(pid, {}).values()])
581b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        if sys_user <= cpu_cutoff: continue
582b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
583376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        if out[-1] != SPACER: out.append(SPACER)
584b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
585b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        WriteChartData(
586b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            ["sys", "sys+user"],
587b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            [process_sys.get(pid, {}), process_sys_user.get(pid, {})],
588b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            os.path.join(files_dir, "proc%d.csv" % pid))
589b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
590b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        out.append(CHART % {
591b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            "id": cgi.escape("proc%d" % pid),
592b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            "id_js": json.write("proc%d" % pid),
593b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            "label_html": PROC_CPU_LABEL % {
594b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor                "pid": pid,
595b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor                "process": cgi.escape(process_name.get(pid, "(unknown)")),
596b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor                "sys": sys,
597b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor                "user": sys_user - sys,
598b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            },
599b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            "filename_js": json.write("%s/proc%d.csv" % (files_url, pid)),
600b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            "options_js": json.write({
601b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor                "colors": ["blue", "green"],
602b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor                "dateWindow": date_window,
603b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor                "fillGraph": True,
604b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor                "fractions": True,
605b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor                "height": 75,
606b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor                "valueRange": [0, 110],
607b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            }),
608b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        })
609b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
610b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        faults = sum(process_faults.get(pid, {}).values())
611b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        if faults <= faults_cutoff: continue
612b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
613b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        WriteChartData(
614376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            ["major"], [process_faults.get(pid, {})],
615b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            os.path.join(files_dir, "proc%d_faults.csv" % pid))
616b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
617b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        out.append(CHART % {
618b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            "id": cgi.escape("proc%d_faults" % pid),
619b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            "id_js": json.write("proc%d_faults" % pid),
620b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            "label_html": FAULTS_LABEL % {"major": faults},
621b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            "filename_js": json.write("%s/proc%d_faults.csv" % (files_url, pid)),
622b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            "options_js": json.write({
623b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor                "colors": ["gray"],
624b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor                "dateWindow": date_window,
625b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor                "fillGraph": True,
626b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor                "height": 50,
627b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor                "valueRange": [0, max_faults * 11 / 10],
628b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            }),
629b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        })
630b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
631b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    out.append(PAGE_END)
632b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    file(filename, "w").write("\n".join(out))
633b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
634b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
635b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnordef main(argv):
636b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    if len(argv) != 3:
637b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        print >>sys.stderr, "usage: procstatreport.py procstat.log output.html"
638376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        return 2
639b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
640b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    history = {}
641b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    current_state = {}
642b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    scan_time = 0.0
643b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
644b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    for line in file(argv[1]):
645376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        if not line.endswith("\n"): continue
646376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
647b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        parts = line.split(None, 2)
648b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        if len(parts) < 2 or parts[1] not in "+-=":
649b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            print >>sys.stderr, "Invalid input:", line
650b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            sys.exit(1)
651b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
652b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        name, op = parts[:2]
653b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
654b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        if name == "T" and op == "+":  # timestamp: scan about to begin
655b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            scan_time = float(line[4:])
656b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            continue
657b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
658b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        if name == "T" and op == "-":  # timestamp: scan complete
659b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            time = (scan_time + float(line[4:])) / 2.0
660b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            history[time] = dict(current_state)
661b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
662b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        elif op == "-":
663b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            if name in current_state: del current_state[name]
664b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
665b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor        else:
666b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor            current_state[name] = "".join(parts[2:]).strip()
667b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
668376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    if len(history) < 2:
669376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        print >>sys.stderr, "error: insufficient history to chart"
670376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        return 1
671376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
672b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    WriteOutput(history, argv[1], argv[2])
673b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
674b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor
675b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnorif __name__ == "__main__":
676b1ccdd258e9efdd9dfa9e2c110e70348d20d3a31Dan Egnor    sys.exit(main(sys.argv))
677