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