1<!DOCTYPE html>
2<!--
3Copyright (c) 2012 The Chromium Authors. All rights reserved.
4Use of this source code is governed by a BSD-style license that can be
5found in the LICENSE file.
6-->
7
8<link rel="import" href="/tracing/extras/importer/linux_perf/parser.html">
9<link rel="import" href="/tracing/model/counter_series.html">
10
11<script>
12'use strict';
13
14/**
15 * @fileoverview Parses trace_marker events that were inserted in the trace by
16 * userland.
17 */
18tr.exportTo('tr.e.importer.linux_perf', function() {
19  var ColorScheme = tr.b.ColorScheme;
20  var Parser = tr.e.importer.linux_perf.Parser;
21
22  /**
23   * Parses linux trace mark events that were inserted in the trace by userland.
24   * @constructor
25   */
26  function AndroidParser(importer) {
27    Parser.call(this, importer);
28
29    importer.registerEventHandler('tracing_mark_write:android',
30        AndroidParser.prototype.traceMarkWriteAndroidEvent.bind(this));
31    importer.registerEventHandler('0:android',
32        AndroidParser.prototype.traceMarkWriteAndroidEvent.bind(this));
33
34    this.model_ = importer.model_;
35    this.ppids_ = {};
36  }
37
38  function parseArgs(argsString) {
39    var args = {};
40    if (argsString) {
41      var argsArray = argsString.split(';');
42      for (var i = 0; i < argsArray.length; ++i) {
43        var parts = argsArray[i].split('=');
44        if (parts[0])
45          args[parts.shift()] = parts.join('=');
46      }
47    }
48    return args;
49  }
50
51  AndroidParser.prototype = {
52    __proto__: Parser.prototype,
53
54    openAsyncSlice: function(thread, category, name, cookie, ts, args) {
55      var asyncSliceConstructor =
56         tr.model.AsyncSlice.getConstructor(
57            category, name);
58      var slice = new asyncSliceConstructor(
59          category, name,
60          ColorScheme.getColorIdForGeneralPurposeString(name), ts, args);
61      var key = category + ':' + name + ':' + cookie;
62      slice.id = cookie;
63      slice.startThread = thread;
64
65      if (!this.openAsyncSlices) {
66        this.openAsyncSlices = { };
67      }
68      this.openAsyncSlices[key] = slice;
69    },
70
71    closeAsyncSlice: function(thread, category, name, cookie, ts, args) {
72      if (!this.openAsyncSlices) {
73        // No async slices have been started.
74        return;
75      }
76
77      var key = category + ':' + name + ':' + cookie;
78      var slice = this.openAsyncSlices[key];
79      if (!slice) {
80        // No async slices w/ this key have been started.
81        return;
82      }
83
84      for (var arg in args) {
85        if (slice.args[arg] !== undefined) {
86          this.model_.importWarning({
87            type: 'parse_error',
88            message: 'Both the S and F events of ' + slice.title +
89                ' provided values for argument ' + arg + '.' +
90                ' The value of the F event will be used.'
91          });
92        }
93        slice.args[arg] = args[arg];
94      }
95
96      slice.endThread = thread;
97      slice.duration = ts - slice.start;
98      slice.startThread.asyncSliceGroup.push(slice);
99      slice.subSlices = [new tr.model.AsyncSlice(slice.category,
100          slice.title, slice.colorId, slice.start, slice.args, slice.duration)];
101      delete this.openAsyncSlices[key];
102    },
103
104    traceMarkWriteAndroidEvent: function(eventName, cpuNumber, pid, ts,
105                                  eventBase) {
106      var eventData = eventBase.details.split('|');
107      switch (eventData[0]) {
108        case 'B':
109          var ppid = parseInt(eventData[1]);
110          var title = eventData[2];
111          var args = parseArgs(eventData[3]);
112          var category = eventData[4];
113          if (category === undefined)
114            category = 'android';
115
116          var thread = this.model_.getOrCreateProcess(ppid)
117              .getOrCreateThread(pid);
118          thread.name = eventBase.threadName;
119          if (!thread.sliceGroup.isTimestampValidForBeginOrEnd(ts)) {
120            this.model_.importWarning({
121              type: 'parse_error',
122              message: 'Timestamps are moving backward.'
123            });
124            return false;
125          }
126
127          this.ppids_[pid] = ppid;
128          thread.sliceGroup.beginSlice(category, title, ts, args);
129
130          break;
131
132        case 'E':
133          var ppid = this.ppids_[pid];
134          if (ppid === undefined) {
135            // Silently ignore unmatched E events.
136            break;
137          }
138
139          var thread = this.model_.getOrCreateProcess(ppid)
140              .getOrCreateThread(pid);
141          if (!thread.sliceGroup.openSliceCount) {
142            // Silently ignore unmatched E events.
143            break;
144          }
145
146          var slice = thread.sliceGroup.endSlice(ts);
147
148          var args = parseArgs(eventData[3]);
149          for (var arg in args) {
150            if (slice.args[arg] !== undefined) {
151              this.model_.importWarning({
152                type: 'parse_error',
153                message: 'Both the B and E events of ' + slice.title +
154                    ' provided values for argument ' + arg + '.' +
155                    ' The value of the E event will be used.'
156              });
157            }
158            slice.args[arg] = args[arg];
159          }
160
161          break;
162
163        case 'C':
164          var ppid = parseInt(eventData[1]);
165          var name = eventData[2];
166          var value = parseInt(eventData[3]);
167          var category = eventData[4];
168          if (category === undefined)
169            category = 'android';
170
171          var ctr = this.model_.getOrCreateProcess(ppid)
172              .getOrCreateCounter(category, name);
173          // Initialize the counter's series fields if needed.
174          if (ctr.numSeries === 0) {
175            ctr.addSeries(new tr.model.CounterSeries(value,
176                ColorScheme.getColorIdForGeneralPurposeString(
177                    ctr.name + '.' + 'value')));
178          }
179
180          ctr.series.forEach(function(series) {
181            series.addCounterSample(ts, value);
182          });
183
184          break;
185
186        case 'S':
187          var ppid = parseInt(eventData[1]);
188          var name = eventData[2];
189          var cookie = parseInt(eventData[3]);
190          var args = parseArgs(eventData[4]);
191          var category = eventData[5];
192          if (category === undefined)
193            category = 'android';
194
195
196          var thread = this.model_.getOrCreateProcess(ppid)
197            .getOrCreateThread(pid);
198          thread.name = eventBase.threadName;
199
200          this.ppids_[pid] = ppid;
201          this.openAsyncSlice(thread, category, name, cookie, ts, args);
202
203          break;
204
205        case 'F':
206          // Note: An async slice may end on a different thread from the one
207          // that started it so this thread may not have been seen yet.
208          var ppid = parseInt(eventData[1]);
209
210          var name = eventData[2];
211          var cookie = parseInt(eventData[3]);
212          var args = parseArgs(eventData[4]);
213          var category = eventData[5];
214          if (category === undefined)
215            category = 'android';
216
217          var thread = this.model_.getOrCreateProcess(ppid)
218            .getOrCreateThread(pid);
219          thread.name = eventBase.threadName;
220
221          this.ppids_[pid] = ppid;
222          this.closeAsyncSlice(thread, category, name, cookie, ts, args);
223
224          break;
225
226        default:
227          return false;
228      }
229
230      return true;
231    }
232  };
233
234  Parser.register(AndroidParser);
235
236  return {
237    AndroidParser: AndroidParser
238  };
239});
240</script>
241