xref: /openbmc/openbmc-tools/dbus-vis/dbus_timeline_vis.js (revision e8c12087fb285a501f2ac857abd19ab076918e82)
1b65280ffSSui Chen// This file deals with preprocessing the parsed DBus timeline data.
2b65280ffSSui Chen// Data and Timestamps are separate b/c dbus-pcap does not include
3b65280ffSSui Chen// timestamps in JSON output so we need to export both formats
4b65280ffSSui Chen// (JSON and text)
5b65280ffSSui Chenvar Data_DBus = [];
6b65280ffSSui Chenvar Timestamps_DBus = [];
7b65280ffSSui Chen
8b65280ffSSui Chen// Main view object
9b65280ffSSui Chenvar dbus_timeline_view = new DBusTimelineView();
10b65280ffSSui Chenvar sensors_timeline_view = new DBusTimelineView(); // Same DBusTimelineView type, just that it will have only sensor propertieschanged events
11b65280ffSSui Chen
12b65280ffSSui Chen// group-by condition changes
13b65280ffSSui Chen{
14b65280ffSSui Chen  const tags = [
15b65280ffSSui Chen    'dbus_column1', 'dbus_column2', 'dbus_column3', 'dbus_column4',
16b65280ffSSui Chen    'dbus_column5', 'dbus_column6', 'dbus_column7'
17b65280ffSSui Chen  ];
18b65280ffSSui Chen  for (let i = 0; i < 7; i++) {
19b65280ffSSui Chen    document.getElementById(tags[i]).addEventListener(
20b65280ffSSui Chen        'click', OnGroupByConditionChanged_DBus);
21b65280ffSSui Chen  }
22b65280ffSSui Chen}
23b65280ffSSui Chen
24b65280ffSSui Chen// Called from renderer.Render()
25b65280ffSSui Chenfunction draw_timeline_sensors(ctx) {
26b65280ffSSui Chen  sensors_timeline_view.Render(ctx);
27b65280ffSSui Chen}
28b65280ffSSui Chen
29b65280ffSSui Chen// Called from renderer.Render()
30b65280ffSSui Chenfunction draw_timeline_dbus(ctx) {
31b65280ffSSui Chen  dbus_timeline_view.Render(ctx);
32b65280ffSSui Chen}
33b65280ffSSui Chen
34b65280ffSSui Chenlet Canvas_DBus = document.getElementById('my_canvas_dbus');
35b65280ffSSui Chen
36b65280ffSSui Chenconst IDXES = {
37b65280ffSSui Chen  'Type': 0,
38b65280ffSSui Chen  'Timestamp': 1,
39b65280ffSSui Chen  'Serial': 2,
40b65280ffSSui Chen  'Sender': 3,
41b65280ffSSui Chen  'Destination': 4,
42b65280ffSSui Chen  'Path': 5,
43b65280ffSSui Chen  'Interface': 6,
44b65280ffSSui Chen  'Member': 7
45b65280ffSSui Chen};
46b65280ffSSui Chen
47b65280ffSSui Chen// This "group" is based on the content of the DBus
48b65280ffSSui Chen// It is independent of the "group_by" of the meta-data (sender/destination/
49b65280ffSSui Chen// path/interface/member) of a DBus message
50b65280ffSSui Chen//
51b65280ffSSui Chen// Input is processed message and some basic statistics needed for categorizing
52b65280ffSSui Chen//
53b65280ffSSui Chenconst DBusMessageContentKey = function(msg, cxn_occ) {
54b65280ffSSui Chen  let ret = undefined;
55b65280ffSSui Chen  const type = msg[IDXES["Type"]];
56b65280ffSSui Chen  const dest = msg[IDXES["Destination"]];
57b65280ffSSui Chen  const path = msg[IDXES["Path"]];
58b65280ffSSui Chen  const iface = msg[IDXES["Interface"]];
59b65280ffSSui Chen  const member = msg[IDXES["Member"]];
60b65280ffSSui Chen  const sender = msg[IDXES["Sender"]];
61b65280ffSSui Chen
62b65280ffSSui Chen  if (sender == "s" || sender == "sss") {
63b65280ffSSui Chen    console.log(msg)
64b65280ffSSui Chen  }
65b65280ffSSui Chen
66b65280ffSSui Chen  if (type == "sig") {
67b65280ffSSui Chen    if (path.indexOf("/xyz/openbmc_project/sensors/") != -1 &&
68b65280ffSSui Chen        iface == "org.freedesktop.DBus.Properties" &&
69b65280ffSSui Chen        member == "PropertiesChanged") {
70b65280ffSSui Chen      ret = "Sensor PropertiesChanged Signals";
71b65280ffSSui Chen    }
72b65280ffSSui Chen  } else if (type == "mc") {
73b65280ffSSui Chen    if (dest == "xyz.openbmc_project.Ipmi.Host" &&
74b65280ffSSui Chen        path == "/xyz/openbmc_project/Ipmi" &&
75b65280ffSSui Chen        iface == "xyz.openbmc_project.Ipmi.Server" &&
76b65280ffSSui Chen        member == "execute") {
77b65280ffSSui Chen      ret = "IPMI Daemon";
78b65280ffSSui Chen    }
79b65280ffSSui Chen  }
80b65280ffSSui Chen
81b65280ffSSui Chen  if (ret == undefined && cxn_occ[sender] <= 10) {
82b65280ffSSui Chen    ret = "Total 10 messages or less"
83b65280ffSSui Chen  }
84b65280ffSSui Chen
85b65280ffSSui Chen  if (ret == undefined && type == "mc") {
86b65280ffSSui Chen    if (path.indexOf("/xyz/openbmc_project/sensors/") == 0 &&
87b65280ffSSui Chen    iface == "org.freedesktop.DBus.Properties" &&
88b65280ffSSui Chen    (member.startsWith("Get") || member.startsWith("Set"))) {
89b65280ffSSui Chen      ret = "Sensor Get/Set";
90b65280ffSSui Chen    }
91b65280ffSSui Chen  }
92b65280ffSSui Chen
93b65280ffSSui Chen  if (ret == undefined) {
94b65280ffSSui Chen    ret = "Uncategorized";
95b65280ffSSui Chen  }
96b65280ffSSui Chen
97b65280ffSSui Chen  return ret;
98b65280ffSSui Chen}
99b65280ffSSui Chen
100b65280ffSSui Chenfunction Group_DBus(preprocessed, group_by) {
101b65280ffSSui Chen  let grouped = {};  // [content key][sort key] -> packet
102b65280ffSSui Chen
103b65280ffSSui Chen  let cxn_occ = {}; // How many times have a specific service appeared?
104b65280ffSSui Chen  preprocessed.forEach((pp) => {
105b65280ffSSui Chen    const cxn = pp[IDXES["Sender"]];
106b65280ffSSui Chen    if (cxn_occ[cxn] == undefined) {
107b65280ffSSui Chen      cxn_occ[cxn] = 0;
108b65280ffSSui Chen    }
109b65280ffSSui Chen    cxn_occ[cxn]++;
110b65280ffSSui Chen  });
111b65280ffSSui Chen
112b65280ffSSui Chen  for (var n = 0; n < preprocessed.length; n++) {
113b65280ffSSui Chen    var key = '';
114b65280ffSSui Chen    for (var i = 0; i < group_by.length; i++) {
115b65280ffSSui Chen      if (i > 0) key += ' ';
116b65280ffSSui Chen      key += ('' + preprocessed[n][IDXES[group_by[i]]]);
117b65280ffSSui Chen    }
118b65280ffSSui Chen
119b65280ffSSui Chen    // "Content Key" is displayed on the "Column Headers"
120b65280ffSSui Chen    const content_group = DBusMessageContentKey(preprocessed[n], cxn_occ);
121b65280ffSSui Chen
122b65280ffSSui Chen    // Initialize the "Collapsed" array here
123b65280ffSSui Chen    // TODO: this should ideally not be specific to the dbus_interface_view instance
124b65280ffSSui Chen    if (dbus_timeline_view.HeaderCollapsed[content_group] == undefined) {
125b65280ffSSui Chen      dbus_timeline_view.HeaderCollapsed[content_group] = false;  // Not collapsed by default
126b65280ffSSui Chen    }
127b65280ffSSui Chen
128b65280ffSSui Chen    if (grouped[content_group] == undefined) {
129b65280ffSSui Chen      grouped[content_group] = [];
130b65280ffSSui Chen    }
131b65280ffSSui Chen    let grouped1 = grouped[content_group];
132b65280ffSSui Chen
133b65280ffSSui Chen    if (grouped1[key] == undefined) grouped1[key] = [];
134b65280ffSSui Chen    grouped1[key].push(preprocessed[n]);
135b65280ffSSui Chen  }
136b65280ffSSui Chen  return grouped;
137b65280ffSSui Chen}
138b65280ffSSui Chen
139b65280ffSSui Chenfunction OnGroupByConditionChanged_DBus() {
140b65280ffSSui Chen  var tags = [
141b65280ffSSui Chen    'dbus_column1', 'dbus_column2', 'dbus_column3', 'dbus_column4',
142b65280ffSSui Chen    'dbus_column5', 'dbus_column6', 'dbus_column7'
143b65280ffSSui Chen  ];
144b65280ffSSui Chen  const v = dbus_timeline_view;
145b65280ffSSui Chen  v.GroupBy = [];
146b65280ffSSui Chen  v.GroupByStr = '';
147b65280ffSSui Chen  for (let i = 0; i < tags.length; i++) {
148b65280ffSSui Chen    let cb = document.getElementById(tags[i]);
149b65280ffSSui Chen    if (cb.checked) {
150b65280ffSSui Chen      v.GroupBy.push(cb.value);
151b65280ffSSui Chen      if (v.GroupByStr.length > 0) {
152b65280ffSSui Chen        v.GroupByStr += ', ';
153b65280ffSSui Chen      }
154b65280ffSSui Chen      v.GroupByStr += cb.value;
155b65280ffSSui Chen    }
156b65280ffSSui Chen  }
157b65280ffSSui Chen  let preproc = Preprocess_DBusPcap(
158b65280ffSSui Chen      Data_DBus, Timestamps_DBus);  // should be from dbus_pcap
159b65280ffSSui Chen  let grouped = Group_DBus(preproc, v.GroupBy);
160b65280ffSSui Chen  GenerateTimeLine_DBus(grouped);
161b65280ffSSui Chen  dbus_timeline_view.IsCanvasDirty = true;
162b65280ffSSui Chen}
163b65280ffSSui Chen
164b65280ffSSui Chen// Todo: put g_StartingSec somewhere that's common between sensors and non-sensors
165b65280ffSSui Chenfunction GenerateTimeLine_DBus(grouped) {
166b65280ffSSui Chen  let intervals = [];
167b65280ffSSui Chen  let titles = [];
168b65280ffSSui Chen  g_StartingSec = undefined;
169b65280ffSSui Chen
170b65280ffSSui Chen  // First, turn "content keys" into headers in the flattened layout
171b65280ffSSui Chen  const content_keys = Object.keys(grouped);
172b65280ffSSui Chen
173b65280ffSSui Chen  const keys = Object.keys(grouped);
174b65280ffSSui Chen  let sortedKeys = keys.slice();
175b65280ffSSui Chen
176b65280ffSSui Chen  let interval_idx = 0;  // The overall index into the intervals array
177b65280ffSSui Chen
178b65280ffSSui Chen  for (let x=0; x<content_keys.length; x++) {
179b65280ffSSui Chen    const content_key = content_keys[x];
180b65280ffSSui Chen    // Per-content key
181b65280ffSSui Chen    const grouped1 = grouped[content_key];
182b65280ffSSui Chen    const keys1 = Object.keys(grouped1);
183b65280ffSSui Chen
184b65280ffSSui Chen    let the_header = { "header":true, "title":content_key, "intervals_idxes":[] };
185b65280ffSSui Chen    titles.push(the_header);
186b65280ffSSui Chen    // TODO: this currently depends on the dbus_timeline_view instance
187b65280ffSSui Chen    const collapsed = dbus_timeline_view.HeaderCollapsed[content_key];
188b65280ffSSui Chen
189b65280ffSSui Chen    for (let i = 0; i < keys1.length; i++) {
190b65280ffSSui Chen      // The Title array controls which lines are drawn. If we con't push the header
191b65280ffSSui Chen      // it will not be drawn (thus giving a "collapsed" visual effect.)
192b65280ffSSui Chen      if (!collapsed) {
193b65280ffSSui Chen        titles.push({ "header":false, "title":keys1[i], "intervals_idxes":[interval_idx] });
194b65280ffSSui Chen      }
195b65280ffSSui Chen
196b65280ffSSui Chen
197b65280ffSSui Chen      line = [];
198b65280ffSSui Chen      for (let j = 0; j < grouped1[keys1[i]].length; j++) {
199b65280ffSSui Chen        let entry = grouped1[keys1[i]][j];
200b65280ffSSui Chen        let t0 = parseFloat(entry[1]) / 1000.0;
201b65280ffSSui Chen        let t1 = parseFloat(entry[8]) / 1000.0;
202b65280ffSSui Chen
203b65280ffSSui Chen        // Modify time shift delta if IPMI dataset is loaded first
204b65280ffSSui Chen        if (g_StartingSec == undefined) {
205b65280ffSSui Chen          g_StartingSec = t0;
206b65280ffSSui Chen        }
207b65280ffSSui Chen        g_StartingSec = Math.min(g_StartingSec, t0);
208b65280ffSSui Chen        const outcome = entry[9];
209b65280ffSSui Chen        line.push([t0, t1, entry, outcome, 0]);
210b65280ffSSui Chen      }
211b65280ffSSui Chen
212b65280ffSSui Chen      the_header.intervals_idxes.push(interval_idx);  // Keep the indices into the intervals array for use in rendering
213b65280ffSSui Chen      intervals.push(line);
214b65280ffSSui Chen      interval_idx ++;
215b65280ffSSui Chen    }
216b65280ffSSui Chen
217b65280ffSSui Chen    // Compute a set of "merged intervals" for each content_key
218b65280ffSSui Chen    let rise_fall_edges = [];
219b65280ffSSui Chen    the_header.intervals_idxes.forEach((i) => {
220b65280ffSSui Chen      intervals[i].forEach((t0t1) => {
221*e8c12087SSui Chen        if (t0t1[0] <= t0t1[1]) {  // For errored-out method calls, the end time will be set to a value smaller than the start time
222b65280ffSSui Chen          rise_fall_edges.push([t0t1[0], 0]);  // 0 is a rising edge
223b65280ffSSui Chen          rise_fall_edges.push([t0t1[1], 1]);  // 1 is a falling edge
224b65280ffSSui Chen        }
225b65280ffSSui Chen      })
226b65280ffSSui Chen    });
227b65280ffSSui Chen
228b65280ffSSui Chen    let merged_intervals = [],
229b65280ffSSui Chen        current_interval = [undefined, undefined, 0];  // start, end, weight
230b65280ffSSui Chen    rise_fall_edges.sort();
231b65280ffSSui Chen    let i = 0, level = 0;
232b65280ffSSui Chen    while (i<rise_fall_edges.length) {
233b65280ffSSui Chen      let timestamp = rise_fall_edges[i][0];
234b65280ffSSui Chen      while (i < rise_fall_edges.length && timestamp == rise_fall_edges[i][0]) {
235b65280ffSSui Chen        switch (rise_fall_edges[i][1]) {
236b65280ffSSui Chen          case 0: {  // rising edge
237b65280ffSSui Chen            if (level == 0) {
238b65280ffSSui Chen              current_interval[0] = timestamp;
239b65280ffSSui Chen              current_interval[2] ++;
240b65280ffSSui Chen            }
241b65280ffSSui Chen            level ++;
242b65280ffSSui Chen            break;
243b65280ffSSui Chen          }
244b65280ffSSui Chen          case 1: {  // falling edge
245b65280ffSSui Chen            level --;
246b65280ffSSui Chen            if (level == 0) {
247b65280ffSSui Chen              current_interval[1] = timestamp;
248b65280ffSSui Chen              merged_intervals.push(current_interval);
249b65280ffSSui Chen              current_interval = [undefined, undefined, 0];
250b65280ffSSui Chen            }
251b65280ffSSui Chen            break;
252b65280ffSSui Chen          }
253b65280ffSSui Chen        }
254b65280ffSSui Chen        i++;
255b65280ffSSui Chen      }
256b65280ffSSui Chen    }
257b65280ffSSui Chen    the_header.merged_intervals = merged_intervals;
258b65280ffSSui Chen  }
259b65280ffSSui Chen
260b65280ffSSui Chen  // Time shift
261b65280ffSSui Chen  for (let i = 0; i < intervals.length; i++) {
262b65280ffSSui Chen    for (let j = 0; j < intervals[i].length; j++) {
263b65280ffSSui Chen      let x = intervals[i][j];
264b65280ffSSui Chen      x[0] -= g_StartingSec;
265b65280ffSSui Chen      x[1] -= g_StartingSec;
266b65280ffSSui Chen    }
267b65280ffSSui Chen  }
268b65280ffSSui Chen  // merged intervals should be time-shifted as well
269b65280ffSSui Chen  titles.forEach((t) => {
270b65280ffSSui Chen    if (t.header == true) {
271b65280ffSSui Chen      t.merged_intervals.forEach((mi) => {
272b65280ffSSui Chen        mi[0] -= g_StartingSec;
273b65280ffSSui Chen        mi[1] -= g_StartingSec;
274b65280ffSSui Chen      })
275b65280ffSSui Chen    }
276b65280ffSSui Chen  })
277b65280ffSSui Chen
278b65280ffSSui Chen  dbus_timeline_view.Intervals = intervals.slice();
279b65280ffSSui Chen  dbus_timeline_view.Titles    = titles.slice();
280b65280ffSSui Chen  dbus_timeline_view.LayoutForOverlappingIntervals();
281b65280ffSSui Chen}
282b65280ffSSui Chen
283b65280ffSSui ChenCanvas_DBus.onmousemove =
284b65280ffSSui Chen    function(event) {
285b65280ffSSui Chen  const v = dbus_timeline_view;
286b65280ffSSui Chen  v.MouseState.x = event.pageX - this.offsetLeft;
287b65280ffSSui Chen  v.MouseState.y = event.pageY - this.offsetTop;
288b65280ffSSui Chen  if (v.MouseState.pressed == true &&
289b65280ffSSui Chen    v.MouseState.hoveredSide == 'timeline') {  // Update highlighted area
290b65280ffSSui Chen    v.HighlightedRegion.t1 = v.MouseXToTimestamp(v.MouseState.x);
291b65280ffSSui Chen  }
292b65280ffSSui Chen  v.OnMouseMove();
293b65280ffSSui Chen  v.IsCanvasDirty = true;
294b65280ffSSui Chen
295b65280ffSSui Chen  v.linked_views.forEach(function(u) {
296b65280ffSSui Chen    u.MouseState.x = event.pageX - Canvas_DBus.offsetLeft;
297b65280ffSSui Chen    u.MouseState.y = undefined;                  // Do not highlight any entry or the horizontal scroll bars
298b65280ffSSui Chen    if (u.MouseState.pressed == true &&
299b65280ffSSui Chen      v.MouseState.hoveredSide == 'timeline') {  // Update highlighted area
300b65280ffSSui Chen      u.HighlightedRegion.t1 = u.MouseXToTimestamp(u.MouseState.x);
301b65280ffSSui Chen    }
302b65280ffSSui Chen    u.OnMouseMove();
303b65280ffSSui Chen    u.IsCanvasDirty = true;
304b65280ffSSui Chen  });
305b65280ffSSui Chen}
306b65280ffSSui Chen
307b65280ffSSui ChenCanvas_DBus.onmousedown = function(event) {
308b65280ffSSui Chen  if (event.button == 0) {
309b65280ffSSui Chen    dbus_timeline_view.OnMouseDown();
310b65280ffSSui Chen  }
311b65280ffSSui Chen};
312b65280ffSSui Chen
313b65280ffSSui ChenCanvas_DBus.onmouseup =
314b65280ffSSui Chen    function(event) {
315b65280ffSSui Chen  if (event.button == 0) {
316b65280ffSSui Chen    dbus_timeline_view.OnMouseUp();
317b65280ffSSui Chen  }
318b65280ffSSui Chen}
319b65280ffSSui Chen
320b65280ffSSui ChenCanvas_DBus.onmouseleave =
321b65280ffSSui Chen    function(event) {
322b65280ffSSui Chen  dbus_timeline_view.OnMouseLeave();
323b65280ffSSui Chen}
324b65280ffSSui Chen
325b65280ffSSui ChenCanvas_DBus.onwheel = function(event) {
326b65280ffSSui Chen  dbus_timeline_view.OnMouseWheel(event);
327b65280ffSSui Chen}
328