• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/bin/bash
2# Loading... <!--
3# Copyright (C) 2017 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17cd $(dirname $0)
18python -m webbrowser -t "http://localhost:8000/$(basename $0)"
19python -m SimpleHTTPServer
20
21<<-EOF
22-->
23<body>
24<style>
25* {
26  box-sizing: border-box;
27}
28
29.main {
30  display: flex;
31  font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
32  font-weight: 300;
33}
34
35pre {
36  font-size: 12px;
37}
38
39ul {
40  margin: 0;
41  padding: 0;
42}
43
44li {
45  list-style: none;
46  border-radius: 3px;
47  border: solid rgba(0, 0, 0, 0) 1px;
48  padding: 3px;
49  margin-right: 5px 0;
50}
51
52li.selected {
53  border: solid rgba(0, 0, 0, 0.89) 1px;
54}
55
56h1 {
57  font-weight: 200;
58  margin-bottom: 0;
59}
60
61h2 {
62  font-size: smaller;
63}
64
65.focus {
66  flex: 1;
67  margin: 20px;
68}
69
70.context {
71  flex: 0 0 25%;
72}
73
74.green {
75  color: green;
76}
77
78.red {
79  color: red;
80}
81
82.files {
83  position: sticky;
84  top: 15px;
85}
86
87.file {
88  display: flex;
89  justify-content: flex-start;
90  flex-direction: row;
91}
92
93.file *:first-child {
94  flex: 0 0 300px;
95}
96
97.file *:last-child {
98  flex-grow: 1;
99}
100
101.version {
102  display: flex;
103  margin-bottom: 4px;
104}
105
106.version li {
107  margin-right: 20px;
108}
109
110input {
111  font-size: large;
112  margin: 20px 0;
113}
114
115</style>
116<script src="//unpkg.com/mithril"></script>
117<script src="//unpkg.com/diff"></script>
118
119<div id="content"></div>
120
121<script>
122// Remove hash bang.
123document.body.firstChild.remove();
124
125let THIS_URL = window.location.href;
126let gDirectoryToFormatFiles;
127let gNamesToRecords = new Map();
128let gFilterText = '';
129let gDisplayedRecords = null;
130let gDisplayedName = null;
131let gADevice = null;
132let gBDevice = null;
133let gDevices = []
134let gCache = new Map();
135
136function isdir(url) {
137  return url[url.length - 1] == '/';
138}
139
140function isfile(url) {
141  return !isdir(url);
142}
143
144function getdir(url) {
145  return url.slice(0, url.lastIndexOf('/')+1);
146}
147
148let getdirectories = url => listdir(url).then(xs => xs.filter(isdir));
149let getfiles = url => listdir(url).then(xs => xs.filter(isfile));
150
151function fetch(url) {
152  return new Promise(function(resolve, reject) {
153    let xhr = new XMLHttpRequest();
154    xhr.open("GET", url, true);
155    xhr.onload = e => resolve({
156      text: () => Promise.resolve(xhr.responseText),
157    });
158    xhr.onerror = e => reject(xhr.statusText);
159    xhr.send(null);
160  });
161}
162
163function geturl(url) {
164  console.log('Fetch:', url);
165  if (gCache.has(url)) return Promise.resolve(gCache.get(url));
166  return fetch(url).then(r => r.text()).then(text => {
167    gCache.set(url, text);
168    return text;
169  });
170}
171
172function listdir(url) {
173  return geturl(url).then(text => {
174    let re = new RegExp('<li><a href="(.+)">(.+)</a>', 'g');
175    if (window.location.href.indexOf('x20') != -1)
176      re = new RegExp('[^>]</td>\n<td>\n<a href="(.+)">(.+)</a>', 'g');
177    let match;
178    let matches = [];
179    while (match = re.exec(text)) {
180      matches.push(match[1]);
181    }
182    return matches;
183  });
184}
185
186function getfiletext(url) {
187  if (gCache.has(url)) return gCache.get(url);
188  geturl(url).then(() => m.redraw());
189  return "";
190}
191
192function makeFormatFileRecord(base_url, device, group_name, event_name) {
193  let url = base_url + device + 'events/' + group_name + event_name + 'format';
194  let group = group_name.replace('/', '');
195  let name = event_name.replace('/', '');
196  return new FormatFileRecord(device, group, name, url);
197}
198
199function findFormatFilesByDirectory() {
200  let url = getdir(THIS_URL) + 'data/';
201  let directoryToFormatFiles = new Map();
202  return getdirectories(url).then(directories => {
203    return Promise.all(directories.map(device => {
204      directoryToFormatFiles.set(device, []);
205      return getdirectories(url + device + 'events/').then(groups => {
206        return Promise.all(groups.map(group_name => {
207          let innerUrl = url + device + 'events/' + group_name;
208          return getdirectories(innerUrl).then(event_names => {
209            event_names.map(event_name => {
210              let record = makeFormatFileRecord(
211                  url,
212                  device,
213                  group_name,
214                  event_name);
215              directoryToFormatFiles.get(device).push(record);
216            });
217          });
218        }));
219      });
220    }));
221  }).then(_ => {
222    return directoryToFormatFiles
223  });
224}
225
226class FormatFileRecord {
227  constructor(device, group, name, url) {
228    this.device = device;
229    this.group = group;
230    this.name = name;
231    this.url = url;
232  }
233}
234
235function fuzzyMatch(query) {
236  let re = new RegExp(Array.from(query).join('.*'));
237  return text => text.match(re);
238}
239
240function contextView(filterText, namesToRecords) {
241  let matcher = fuzzyMatch(filterText);
242  return m('.context', [
243    m('h1', {class: 'title'}, 'Ftrace Format Explorer'),
244    m('input[type=text][placeholder=Filter]', {
245      oninput: m.withAttr('value', value => gFilterText = value),
246      value: filterText,
247    }),
248    m('ul',
249      Array.from(namesToRecords.entries())
250          .filter(e => matcher(e[0])).map(e => m('li[tabindex=0]', {
251        onfocus: () => { gDisplayedRecords = e[1]; gDisplayedName = e[0];
252      },
253      class: gDisplayedName == e[0] ? 'selected' : '',
254    }, e[0] + ' (' + e[1].length + ')' ))),
255  ]);
256}
257
258function focusView(records) {
259  if (records == null) {
260    return m('div.focus');
261  }
262
263  let r1 = records.filter(r => r.device == gADevice)[0];
264  let r2 = records.filter(r => r.device == gBDevice)[0];
265  if (!r1) r1 = records[0];
266  if (!r2) r2 = records[0];
267  let f1 = getfiletext(r1.url);
268  let f2 = getfiletext(r2.url);
269  let diff = JsDiff.diffChars(f1, f2);
270
271  let es = diff.map(part => {
272    let color = part.added ? 'green' : part.removed ? 'red' : 'grey';
273    let e = m('span.' + color, part.value);
274    return e;
275  });
276  return m('.focus', [
277    m('ul.version', gDevices.map(device => m('li', {
278      onclick: () => gADevice = device,
279      class: device == gADevice ? 'selected' : '',
280    }, device))),
281    m('ul.version', gDevices.map(device => m('li', {
282      onclick: () => gBDevice = device,
283      class: device == gBDevice ? 'selected' : '',
284    }, device))),
285    m('.files', [
286      m('.file', [m('h2', gADevice),  m('pre', f1)]),
287      gADevice == gBDevice ? undefined : [
288        m('.file', [m('h2', gBDevice),  m('pre', f2)]),
289        m('.file', [m('h2', 'Delta'), m('pre', es)]),
290      ]
291    ]),
292  ]);
293}
294
295let root = document.getElementById('content');
296let App = {
297  view: function() {
298    if (!gDirectoryToFormatFiles)
299      return m('.main', 'Loading...');
300    return m('.main', [
301      contextView(gFilterText, gNamesToRecords),
302      focusView(gDisplayedRecords),
303    ])
304  }
305}
306m.mount(root, App);
307
308findFormatFilesByDirectory().then(data => {
309  gDirectoryToFormatFiles = data;
310  gNamesToRecords = new Map();
311  gDevices = Array.from(gDirectoryToFormatFiles.keys());
312  for (let records of gDirectoryToFormatFiles.values()) {
313    for (let record of records) {
314      geturl(record.url);
315      if (gNamesToRecords.get(record.name) == null) {
316        gNamesToRecords.set(record.name, []);
317      }
318      gNamesToRecords.get(record.name).push(record);
319    }
320  }
321  [gADevice, gBDevice] = gDevices;
322  m.redraw();
323});
324
325</script>
326
327<!--
328EOF
329#-->
330