1// This script deals with Boost ASIO handlers
2// Will need to add code to visualize one-shot events
3
4// Fields: HandlerNumber, Level,
5// Creation time, Enter time, Exit time, EnterDescription,
6//    [ [Operation, time] ]
7
8// Will use g_StartingSec as the starting of epoch
9
10var ASIO_Data = [];
11var ASIO_Timestamp = [];
12
13function FindFirstEntrySlot(slots) {
14  let i = 0;
15  for (; i < slots.length; i++) {
16    if (slots[i] == undefined) break;
17  }
18  if (i >= slots.length) slots.push(undefined);
19  return i;
20}
21
22function SimplifyDesc(desc) {
23  const idx0 = desc.indexOf('0x');
24  if (idx0 == -1)
25    return desc;
26  else {
27    const d1 = desc.substr(idx0 + 2);
28    let idx1 = 0;
29    while (idx1 + 1 < d1.length &&
30           ((d1[idx1] >= '0' && d1[idx1] <= '9') ||
31            (d1[idx1] >= 'A' && d1[idx1] <= 'F') ||
32            (d1[idx1] >= 'a' && d1[idx1] <= 'f'))) {
33      idx1++;
34    }
35    return desc.substr(0, idx0) + d1.substr(idx1)
36  }
37}
38
39function ParseBoostHandlerTimeline(content) {
40  let parsed_entries = [];
41  const lines = content.split('\n');
42  let slots = [];               // In-flight handlers
43  let in_flight_id2level = {};  // In-flight ID to level
44
45  for (let lidx = 0; lidx < lines.length; lidx++) {
46    const line = lines[lidx];
47    if (line.startsWith('@asio|') == false) continue;
48    const sp = line.split('|');
49
50    const tag = sp[0], ts = sp[1], action = sp[2], desc = sp[3];
51    let handler_id = -999;
52    let ts_sec = parseFloat(ts);
53    const simp_desc = SimplifyDesc(desc);
54
55    if (action.indexOf('*') != -1) {
56      const idx = action.indexOf('*');
57      const handler_id = parseInt(action.substr(idx + 1));
58      const level = FindFirstEntrySlot(slots);
59
60      // Create an entry here
61      let entry = [
62        handler_id, level, ts_sec, undefined, undefined, desc, simp_desc, []
63      ];
64
65      slots[level] = entry;
66      in_flight_id2level[handler_id] = level;
67    } else if (action[0] == '>') {  // The program enters handler number X
68      handler_id = parseInt(action.substr(1));
69      if (handler_id in in_flight_id2level) {
70        const level = in_flight_id2level[handler_id];
71        let entry = slots[level];
72        entry[3] = ts_sec;
73      }
74    } else if (action[0] == '<') {
75      handler_id = parseInt(action.substr(1));
76      if (handler_id in in_flight_id2level) {
77        const level = in_flight_id2level[handler_id];
78        let entry = slots[level];
79        entry[4] = ts_sec;
80        slots[level] = undefined;
81        parsed_entries.push(entry);
82        delete in_flight_id2level[handler_id];
83      }
84    } else if (action[0] == '.') {  // syscalls
85    }
86  }
87
88  console.log(
89      'Boost handler log: ' + parsed_entries.length + ' entries' +
90      ', ' + slots.length + ' levels');
91  ASIO_Data = parsed_entries;
92  return parsed_entries;
93}
94
95function Group_ASIO(preprocessed, group_by) {
96  let grouped = {};
97  const IDXES = {'Layout Level': 1, 'Description': 5, 'Description1': 6};
98  for (var n = 0; n < preprocessed.length; n++) {
99    var key = ''
100    for (var i = 0; i < group_by.length; i++) {
101      if (i > 0) key += ' ';
102      key += ('' + preprocessed[n][IDXES[group_by[i]]]);
103    }
104    if (grouped[key] == undefined) grouped[key] = [];
105    grouped[key].push(preprocessed[n]);
106  }
107  return grouped;
108}
109
110function OnGroupByConditionChanged_ASIO() {
111  var tags = ['bah1', 'bah2', 'bah3'];
112  const v = boost_asio_handler_timeline_view;
113  v.GroupBy = [];
114  v.GroupByStr = '';
115  for (let i = 0; i < tags.length; i++) {
116    let cb = document.getElementById(tags[i]);
117    if (cb.checked) {
118      v.GroupBy.push(cb.value);
119      if (v.GroupByStr.length > 0) {
120        v.GroupByStr += ', ';
121      }
122      v.GroupByStr += cb.value;
123    }
124  }
125  let preproc = ASIO_Data;
126  let grouped = Group_ASIO(preproc, v.GroupBy);
127  GenerateTimeLine_ASIO(grouped);
128  boost_asio_handler_timeline_view.IsCanvasDirty = true;
129}
130
131function GenerateTimeLine_ASIO(grouped) {
132  const keys = Object.keys(grouped);
133  let sortedKeys = keys.slice();
134  let intervals = [];
135  let titles = [];
136
137  const was_starting_time_undefined = (g_StartingSec == undefined);
138
139  for (let i = 0; i < sortedKeys.length; i++) {
140    titles.push({"header":false, "title":sortedKeys[i], "intervals_idxes":[i] });
141    line = [];
142    for (let j = 0; j < grouped[sortedKeys[i]].length; j++) {
143      let entry = grouped[sortedKeys[i]][j];
144      let t0 = parseFloat(entry[3]);
145      let t1 = parseFloat(entry[4]);
146
147      if (was_starting_time_undefined) {
148        if (g_StartingSec == undefined) {
149          g_StartingSec = t0;
150        }
151        g_StartingSec = Math.min(g_StartingSec, t0);
152      }
153
154      line.push([t0, t1, entry, 'ok', 0]);
155    }
156    intervals.push(line);
157  }
158
159  // Time shift
160  for (let i = 0; i < intervals.length; i++) {
161    for (let j = 0; j < intervals[i].length; j++) {
162      let x = intervals[i][j];
163      x[0] -= g_StartingSec;
164      x[1] -= g_StartingSec;
165    }
166  }
167
168  boost_asio_handler_timeline_view.Intervals = intervals.slice();
169  boost_asio_handler_timeline_view.Titles = titles.slice();
170  boost_asio_handler_timeline_view.LayoutForOverlappingIntervals();
171}
172
173// Main view object for Boost handler timeline
174
175boost_asio_handler_timeline_view = new BoostASIOHandlerTimelineView();
176boost_asio_handler_timeline_view.IsCanvasDirty = true;
177
178function draw_timeline_boost_asio_handler(ctx) {
179  boost_asio_handler_timeline_view.Render(ctx);
180}
181
182let Canvas_Asio = document.getElementById('my_canvas_boost_asio_handler');
183
184Canvas_Asio.onmousemove = function(event) {
185  const v = boost_asio_handler_timeline_view;
186  v.MouseState.x = event.pageX - this.offsetLeft;
187  v.MouseState.y = event.pageY - this.offsetTop;
188  if (v.MouseState.pressed == true &&
189      v.MouseState.hoveredSide == 'timeline') {  // Update highlighted area
190    v.HighlightedRegion.t1 = v.MouseXToTimestamp(v.MouseState.x);
191  }
192  v.OnMouseMove();
193  v.IsCanvasDirty = true;
194
195  v.linked_views.forEach(function(u) {
196    u.MouseState.x = event.pageX - Canvas_Asio.offsetLeft;
197    u.MouseState.y = 0;                  // Do not highlight any entry
198    if (u.MouseState.pressed == true &&
199        u.MouseState.hoveredSide == 'timeline') {  // Update highlighted area
200      u.HighlightedRegion.t1 = u.MouseXToTimestamp(u.MouseState.x);
201    }
202    u.OnMouseMove();
203    u.IsCanvasDirty = true;
204  });
205};
206
207Canvas_Asio.onmousedown = function(event) {
208  if (event.button == 0) {
209    boost_asio_handler_timeline_view.OnMouseDown();
210  }
211};
212
213Canvas_Asio.onmouseup = function(event) {
214  if (event.button == 0) {
215    boost_asio_handler_timeline_view.OnMouseUp();
216  }
217};
218
219Canvas_Asio.onwheel = function(event) {
220  boost_asio_handler_timeline_view.OnMouseWheel(event);
221}
222