xref: /openbmc/openbmc-tools/dbus-vis/dbus_pcap_loader.js (revision c403b03712808a0fa8deca1a90327dea67f8937e)
1b65280ffSSui Chen// This file performs the file reading step
2b65280ffSSui Chen// Actual preprocessing is done in dbus_timeline_vis.js
3b65280ffSSui Chen
4b65280ffSSui Chenfunction MyFloatMillisToBigIntUsec(x) {
5b65280ffSSui Chen  x = ('' + x).split('.');
6b65280ffSSui Chen  ret = BigInt(x[0]) * BigInt(1000);
7b65280ffSSui Chen  return ret;
8b65280ffSSui Chen}
9b65280ffSSui Chen
10b65280ffSSui Chen// When the Open File dialog is completed, the name of the opened file will be passed
11b65280ffSSui Chen// to this routine. Then the program will do the following:
12b65280ffSSui Chen// 1. Launch "linecount.py" to get the total packet count in the PCAP file for
13b65280ffSSui Chen//    progress
14b65280ffSSui Chen// 2. Launch "dbus-pcap" to get the timestamps of each DBus message
15b65280ffSSui Chen// 3. Launch "dbus-pcap" to get the JSON representation of each DBus message
16b65280ffSSui Chen//
17b65280ffSSui Chenfunction OpenDBusPcapFile(file_name) {
18b65280ffSSui Chen  // First try to parse using dbus-pcap
19b65280ffSSui Chen
20b65280ffSSui Chen  ShowBlocker('Determining the number of packets in the pcap file ...');
21b65280ffSSui Chen  const num_lines_py =
22b65280ffSSui Chen      spawn('python3', ['linecount.py', file_name]);
23b65280ffSSui Chen  let stdout_num_lines = '';
24b65280ffSSui Chen  num_lines_py.stdout.on('data', (data) => {
25b65280ffSSui Chen    stdout_num_lines += data;
26b65280ffSSui Chen  });
27b65280ffSSui Chen
28b65280ffSSui Chen  num_lines_py.on('close', (code) => {
29b65280ffSSui Chen    let num_packets = parseInt(stdout_num_lines.trim());
30b65280ffSSui Chen    ShowBlocker('Running dbus-pcap (Pass 1/2, packet timestamps) ...');
31b65280ffSSui Chen    const dbus_pcap1 =
32b65280ffSSui Chen        //spawn('python3', ['dbus-pcap', file_name, '--json', '--progress']);
33b65280ffSSui Chen        spawn('python3', ['dbus-pcap', file_name]);
34b65280ffSSui Chen    let stdout1 = '';
35b65280ffSSui Chen    let timestamps = [];
36b65280ffSSui Chen    let count1 = 0; // In the first phase, consecutive newlines indicate a new entry
37b65280ffSSui Chen    //const r = new RegExp('([0-9]+/[0-9]+) [0-9]+\.[0-9]+:.*');
38b65280ffSSui Chen    const r = new RegExp('([0-9]+\.[0-9]+):.*');
39b65280ffSSui Chen
40b65280ffSSui Chen    let is_last_newline = false;  // Whether the last scanned character is a newline
41b65280ffSSui Chen    let last_update_millis = 0;
42b65280ffSSui Chen    dbus_pcap1.stdout.on('data', (data) => {
43b65280ffSSui Chen      const s = data.toString();
44b65280ffSSui Chen      stdout1 += s;
45b65280ffSSui Chen      for (let i=0; i<s.length; i++) {
46b65280ffSSui Chen        const ch = s[i];
47b65280ffSSui Chen        let is_new_line = false;
48b65280ffSSui Chen        if (ch == '\n' || ch == '\r') {
49b65280ffSSui Chen          is_new_line = true;
50b65280ffSSui Chen        }
51b65280ffSSui Chen        if (!is_last_newline && is_new_line) {
52b65280ffSSui Chen          count1 ++;
53b65280ffSSui Chen        }
54b65280ffSSui Chen        is_last_newline = is_new_line;
55b65280ffSSui Chen      }
56b65280ffSSui Chen      const millis = Date.now();
57b65280ffSSui Chen      if (millis - last_update_millis > 100) { // Refresh at most 10 times per second
58b65280ffSSui Chen        let pct = parseInt(count1 * 100 / num_packets);
59b65280ffSSui Chen        ShowBlocker('Running dbus-pcap (Pass 1/2, packet timestamps): ' + count1 + '/' + num_packets + ' (' + pct + '%)');
60b65280ffSSui Chen        last_update_millis = millis;
61b65280ffSSui Chen      }
62b65280ffSSui Chen    });
63b65280ffSSui Chen
6427cf9332SSui Chen    dbus_pcap1.stderr.on('data', (data) => {
65e8c12087SSui Chen      console.error(data.toString());
6627cf9332SSui Chen    });
6727cf9332SSui Chen
68b65280ffSSui Chen    dbus_pcap1.on('close', (code) => {
69b65280ffSSui Chen      ShowBlocker('Running dbus-pcap (Pass 2/2, packet contents) ...');
70b65280ffSSui Chen      let stdout2 = '';
71b65280ffSSui Chen      let count2 = 0;
72b65280ffSSui Chen      is_last_newline = false;
73b65280ffSSui Chen      const dbus_pcap2 =
74b65280ffSSui Chen          spawn('python3', ['dbus-pcap', file_name, '--json']);
75b65280ffSSui Chen      dbus_pcap2.stdout.on('data', (data) => {
76b65280ffSSui Chen        const s = data.toString();
77b65280ffSSui Chen        stdout2 += s;
78b65280ffSSui Chen        for (let i=0; i<s.length; i++) {
79b65280ffSSui Chen          const ch = s[i];
80b65280ffSSui Chen          let is_new_line = false;
81b65280ffSSui Chen          if (ch == '\n' || ch == '\r') {
82b65280ffSSui Chen            is_new_line = true;
83b65280ffSSui Chen          }
84b65280ffSSui Chen          if (!is_last_newline && is_new_line) {
85b65280ffSSui Chen            count2 ++;
86b65280ffSSui Chen          }
87b65280ffSSui Chen          is_last_newline = is_new_line;
88b65280ffSSui Chen        }
89b65280ffSSui Chen        const millis = Date.now();
90b65280ffSSui Chen        if (millis - last_update_millis > 100) { // Refresh at most 10 times per second
91b65280ffSSui Chen          let pct = parseInt(count2 * 100 / num_packets);
92b65280ffSSui Chen          ShowBlocker('Running dbus-pcap (Pass 2/2, packet contents): ' + count2 + '/' + num_packets + ' (' + pct + '%)');
93b65280ffSSui Chen          last_update_millis = millis;
94b65280ffSSui Chen        }
95b65280ffSSui Chen      });
96b65280ffSSui Chen
9727cf9332SSui Chen      dbus_pcap2.stderr.on('data', (data) => {
98e8c12087SSui Chen        console.error(data.toString());
9927cf9332SSui Chen      });
10027cf9332SSui Chen
101b65280ffSSui Chen      dbus_pcap2.on('close', (code) => {
102b65280ffSSui Chen        { ShowBlocker('Processing dbus-pcap output ... '); }
103b65280ffSSui Chen
104b65280ffSSui Chen        let packets = [];
105b65280ffSSui Chen        // Parse timestamps
106b65280ffSSui Chen        let lines = stdout1.split('\n');
107b65280ffSSui Chen        for (let i=0; i<lines.length; i++) {
108b65280ffSSui Chen          let l = lines[i].trim();
109b65280ffSSui Chen          if (l.length > 0) {
110b65280ffSSui Chen            // Timestamp
111b65280ffSSui Chen            l = l.substr(0, l.indexOf(':'));
112b65280ffSSui Chen            const ts_usec = parseFloat(l) * 1000.0;
113b65280ffSSui Chen            if (!isNaN(ts_usec)) {
114b65280ffSSui Chen              timestamps.push(ts_usec);
115b65280ffSSui Chen            } else {
116b65280ffSSui Chen              console.log('not a number: ' + l);
117b65280ffSSui Chen            }
118b65280ffSSui Chen          }
119b65280ffSSui Chen        }
120b65280ffSSui Chen
121b65280ffSSui Chen        // JSON
122b65280ffSSui Chen        lines = stdout2.split('\n');
123b65280ffSSui Chen        for (let i=0; i<lines.length; i++) {
124b65280ffSSui Chen          let l = lines[i].trim();
125b65280ffSSui Chen          let parsed = undefined;
126b65280ffSSui Chen          if (l.length > 0) {
127b65280ffSSui Chen            try {
128b65280ffSSui Chen              parsed = JSON.parse(l);
129b65280ffSSui Chen            } catch (e) {
130b65280ffSSui Chen              console.log(e);
131b65280ffSSui Chen            }
132b65280ffSSui Chen
133b65280ffSSui Chen            if (parsed == undefined) {
134b65280ffSSui Chen              try {
135b65280ffSSui Chen                l = l.replace("NaN", "null");
136b65280ffSSui Chen                parsed = JSON.parse(l);
137b65280ffSSui Chen              } catch (e) {
138b65280ffSSui Chen                console.log(e);
139b65280ffSSui Chen              }
140b65280ffSSui Chen            }
141b65280ffSSui Chen
142b65280ffSSui Chen            if (parsed != undefined) {
143b65280ffSSui Chen              packets.push(parsed);
144b65280ffSSui Chen            }
145b65280ffSSui Chen          }
146b65280ffSSui Chen        }
147b65280ffSSui Chen
148b65280ffSSui Chen        Timestamps_DBus = timestamps;
149b65280ffSSui Chen
150b65280ffSSui Chen        Data_DBus = packets.slice();
151b65280ffSSui Chen        OnGroupByConditionChanged_DBus();
152b65280ffSSui Chen        const v = dbus_timeline_view;
153b65280ffSSui Chen
154b65280ffSSui Chen        // Will return 2 packages
155b65280ffSSui Chen        // 1) sensor PropertyChagned signal emissions
156b65280ffSSui Chen        // 2) everything else
157b65280ffSSui Chen        let preproc = Preprocess_DBusPcap(packets, timestamps);
158b65280ffSSui Chen
159b65280ffSSui Chen        let grouped = Group_DBus(preproc, v.GroupBy);
160b65280ffSSui Chen        GenerateTimeLine_DBus(grouped);
161b65280ffSSui Chen
162b65280ffSSui Chen        dbus_timeline_view.IsCanvasDirty = true;
163b65280ffSSui Chen        if (dbus_timeline_view.IsEmpty() == false ||
164b65280ffSSui Chen            ipmi_timeline_view.IsEmpty() == false) {
165b65280ffSSui Chen          dbus_timeline_view.CurrentFileName = file_name;
166b65280ffSSui Chen          ipmi_timeline_view.CurrentFileName = file_name;
167b65280ffSSui Chen          HideWelcomeScreen();
168b65280ffSSui Chen          ShowDBusTimeline();
169b65280ffSSui Chen          ShowIPMITimeline();
170b65280ffSSui Chen          ShowNavigation();
171b65280ffSSui Chen          UpdateFileNamesString();
172b65280ffSSui Chen        }
173b65280ffSSui Chen        HideBlocker();
174b65280ffSSui Chen
175b65280ffSSui Chen        g_btn_zoom_reset.click(); // Zoom to capture time range
176b65280ffSSui Chen      });
177b65280ffSSui Chen    });
178b65280ffSSui Chen  })
179b65280ffSSui Chen}
180b65280ffSSui Chen
181b65280ffSSui Chen// Input: data and timestamps obtained from
182b65280ffSSui Chen// Output: Two arrays
183b65280ffSSui Chen//   The first is sensor PropertyChanged emissions only
184b65280ffSSui Chen//   The second is all other DBus message types
185b65280ffSSui Chen//
186b65280ffSSui Chen// This function also determines the starting timestamp of the capture
187b65280ffSSui Chen//
188b65280ffSSui Chenfunction Preprocess_DBusPcap(data, timestamps) {
189b65280ffSSui Chen  // Also clear IPMI entries
190b65280ffSSui Chen  g_ipmi_parsed_entries = [];
191b65280ffSSui Chen
192b65280ffSSui Chen  let ret = [];
193b65280ffSSui Chen
194b65280ffSSui Chen  let in_flight = {};
195b65280ffSSui Chen  let in_flight_ipmi = {};
196b65280ffSSui Chen
197b65280ffSSui Chen  for (let i = 0; i < data.length; i++) {
198b65280ffSSui Chen    const packet = data[i];
199b65280ffSSui Chen
200b65280ffSSui Chen    // Fields we are interested in
201b65280ffSSui Chen    const fixed_header = packet[0];  // is an [Array(5), Array(6)]
202b65280ffSSui Chen
203b65280ffSSui Chen    if (fixed_header == undefined) {  // for hacked dbus-pcap
204b65280ffSSui Chen      console.log(packet);
205b65280ffSSui Chen      continue;
206b65280ffSSui Chen    }
207b65280ffSSui Chen
208b65280ffSSui Chen    const payload = packet[1];
209b65280ffSSui Chen    const ty = fixed_header[0][1];
210b65280ffSSui Chen    let timestamp = timestamps[i];
211b65280ffSSui Chen    let timestamp_end = undefined;
212b65280ffSSui Chen    const IDX_TIMESTAMP_END = 8;
213b65280ffSSui Chen    const IDX_MC_OUTCOME = 9;  // Outcome of method call
214b65280ffSSui Chen
215b65280ffSSui Chen    let serial, path, member, iface, destination, sender, signature = '';
216b65280ffSSui Chen    // Same as the format of the Dummy data set
217b65280ffSSui Chen
218b65280ffSSui Chen    switch (ty) {
219b65280ffSSui Chen      case 4: {  // signal
220b65280ffSSui Chen        serial = fixed_header[0][5];
221b65280ffSSui Chen        path = fixed_header[1][0][1];
222b65280ffSSui Chen        iface = fixed_header[1][1][1];
223b65280ffSSui Chen        member = fixed_header[1][2][1];
224b65280ffSSui Chen        // signature = fixed_header[1][3][1];
225b65280ffSSui Chen        // fixed_header[1] can have variable length.
226b65280ffSSui Chen        // For example: signal from org.freedesktop.PolicyKit1.Authority can
227b65280ffSSui Chen        // have only 4 elements, while most others are 5
228b65280ffSSui Chen        const idx = fixed_header[1].length - 1;
229b65280ffSSui Chen        sender = fixed_header[1][idx][1];
230b65280ffSSui Chen
231b65280ffSSui Chen        // Ugly fix for:
232b65280ffSSui Chen        if (sender == "s" || sender == "sss") {
233b65280ffSSui Chen          sender = packet[1][0];
234b65280ffSSui Chen          if (fixed_header[1].length == 6) {
235b65280ffSSui Chen            // Example: fixed_header is
236b65280ffSSui Chen            // 0: (2) [7, "org.freedesktop.DBus"]
237b65280ffSSui Chen            // 1: (2) [6, ":1.1440274"]
238b65280ffSSui Chen            // 2: (2) [1, "/org/freedesktop/DBus"]
239b65280ffSSui Chen            // 3: (2) [2, "org.freedesktop.DBus"]
240b65280ffSSui Chen            // 4: (2) [3, "NameLost"]
241b65280ffSSui Chen            // 5: (2) [8, "s"]
242b65280ffSSui Chen            path = fixed_header[1][2][1];
243b65280ffSSui Chen            iface = fixed_header[1][3][1];
244b65280ffSSui Chen            member = fixed_header[1][4][1];
245b65280ffSSui Chen          } else if (fixed_header[1].length == 5) {
246b65280ffSSui Chen            // Example:  fixed_header is
247b65280ffSSui Chen            // 0: (2) [7, "org.freedesktop.DBus"]
248b65280ffSSui Chen            // 1: (2) [1, "/org/freedesktop/DBus"]
249b65280ffSSui Chen            // 2: (2) [2, "org.freedesktop.DBus"]
250b65280ffSSui Chen            // 3: (2) [3, "NameOwnerChanged"]
251b65280ffSSui Chen            // 4: (2) [8, "sss"]
252b65280ffSSui Chen            path = fixed_header[1][1][1];
253b65280ffSSui Chen            iface = fixed_header[1][2][1];
254b65280ffSSui Chen            member = fixed_header[1][3][1];
255b65280ffSSui Chen          }
256b65280ffSSui Chen        }
257b65280ffSSui Chen
258b65280ffSSui Chen
259b65280ffSSui Chen        destination = '<none>';
260b65280ffSSui Chen        timestamp_end = timestamp;
261b65280ffSSui Chen        let entry = [
262b65280ffSSui Chen          'sig', timestamp, serial, sender, destination, path, iface, member,
263b65280ffSSui Chen          timestamp_end, payload
264b65280ffSSui Chen        ];
265b65280ffSSui Chen
266b65280ffSSui Chen        // Legacy IPMI interface uses signal for IPMI request
267b65280ffSSui Chen        if (iface == 'org.openbmc.HostIpmi' && member == 'ReceivedMessage') {
268b65280ffSSui Chen          console.log('Legacy IPMI interface, request');
269b65280ffSSui Chen        }
270b65280ffSSui Chen
271b65280ffSSui Chen        ret.push(entry);
272b65280ffSSui Chen        break;
273b65280ffSSui Chen      }
274b65280ffSSui Chen      case 1: {  // method call
275b65280ffSSui Chen        serial = fixed_header[0][5];
276b65280ffSSui Chen        path = fixed_header[1][0][1];
277b65280ffSSui Chen        member = fixed_header[1][1][1];
278b65280ffSSui Chen        iface = fixed_header[1][2][1];
279b65280ffSSui Chen        destination = fixed_header[1][3][1];
280b65280ffSSui Chen        if (fixed_header[1].length > 5) {
281b65280ffSSui Chen          sender = fixed_header[1][5][1];
282b65280ffSSui Chen          signature = fixed_header[1][4][1];
283b65280ffSSui Chen        } else {
284b65280ffSSui Chen          sender = fixed_header[1][4][1];
285b65280ffSSui Chen        }
286b65280ffSSui Chen        let entry = [
287b65280ffSSui Chen          'mc', timestamp, serial, sender, destination, path, iface, member,
288*c403b037SSui Chen          timestamp_end, payload, packet, ''
289b65280ffSSui Chen        ];
290b65280ffSSui Chen
291b65280ffSSui Chen        // Legacy IPMI interface uses method call for IPMI response
292b65280ffSSui Chen        if (iface == 'org.openbmc.HostIpmi' && member == 'sendMessge') {
293b65280ffSSui Chen          console.log('Legacy IPMI interface, response')
294b65280ffSSui Chen        } else if (
295b65280ffSSui Chen            iface == 'xyz.openbmc_project.Ipmi.Server' && member == 'execute') {
296b65280ffSSui Chen          let ipmi_entry = {
297b65280ffSSui Chen            netfn: payload[0],
298b65280ffSSui Chen            lun: payload[1],
299b65280ffSSui Chen            cmd: payload[2],
300b65280ffSSui Chen            request: payload[3],
301b65280ffSSui Chen            start_usec: MyFloatMillisToBigIntUsec(timestamp),
302b65280ffSSui Chen            end_usec: 0,
303b65280ffSSui Chen            response: []
304b65280ffSSui Chen          };
305b65280ffSSui Chen          in_flight_ipmi[serial] = (ipmi_entry);
306b65280ffSSui Chen        }
307b65280ffSSui Chen
308b65280ffSSui Chen
309b65280ffSSui Chen        ret.push(entry);
310b65280ffSSui Chen        in_flight[serial] = (entry);
311b65280ffSSui Chen        break;
312b65280ffSSui Chen      }
313b65280ffSSui Chen      case 2: {  // method reply
314b65280ffSSui Chen        let reply_serial = fixed_header[1][0][1];
315b65280ffSSui Chen        if (reply_serial in in_flight) {
316b65280ffSSui Chen          let x = in_flight[reply_serial];
317b65280ffSSui Chen          delete in_flight[reply_serial];
318b65280ffSSui Chen          x[IDX_TIMESTAMP_END] = timestamp;
319b65280ffSSui Chen          x[IDX_MC_OUTCOME] = 'ok';
320b65280ffSSui Chen        }
321b65280ffSSui Chen
322b65280ffSSui Chen        if (reply_serial in in_flight_ipmi) {
323b65280ffSSui Chen          let x = in_flight_ipmi[reply_serial];
324b65280ffSSui Chen          delete in_flight_ipmi[reply_serial];
325b65280ffSSui Chen          if (payload[0] != undefined && payload[0][4] != undefined) {
326b65280ffSSui Chen            x.response = payload[0][4];
327b65280ffSSui Chen          }
328b65280ffSSui Chen          x.end_usec = MyFloatMillisToBigIntUsec(timestamp);
329b65280ffSSui Chen          g_ipmi_parsed_entries.push(x);
330b65280ffSSui Chen        }
331b65280ffSSui Chen        break;
332b65280ffSSui Chen      }
333b65280ffSSui Chen      case 3: {  // error reply
334b65280ffSSui Chen        let reply_serial = fixed_header[1][0][1];
335b65280ffSSui Chen        if (reply_serial in in_flight) {
336b65280ffSSui Chen          let x = in_flight[reply_serial];
337b65280ffSSui Chen          delete in_flight[reply_serial];
338b65280ffSSui Chen          x[IDX_TIMESTAMP_END] = timestamp;
339b65280ffSSui Chen          x[IDX_MC_OUTCOME] = 'error';
340b65280ffSSui Chen        }
341b65280ffSSui Chen      }
342b65280ffSSui Chen    }
343b65280ffSSui Chen  }
344b65280ffSSui Chen
345b65280ffSSui Chen  if (g_ipmi_parsed_entries.length > 0) UpdateLayout();
346b65280ffSSui Chen  return ret;
347b65280ffSSui Chen}
348