binder_parser.html revision 4a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724
1<!DOCTYPE html>
2<!--
3Copyright (c) 2015 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
10<script>
11'use strict';
12
13/**
14 * @fileoverview Parses Binder events
15 */
16tr.exportTo('tr.e.importer.linux_perf', function() {
17  var Parser = tr.e.importer.linux_perf.Parser;
18
19  // Matches binder transactions:
20  // transaction=%d dest_node=%d dest_proc=%d dest_thread=%d reply=%d flags=0x%x
21  // code=0x%x
22  var binderTransRE = new RegExp('transaction=(\\d+) dest_node=(\\d+) ' +
23                                 'dest_proc=(\\d+) dest_thread=(\\d+) ' +
24                                 'reply=(\\d+) flags=(0x[0-9a-fA-F]+) ' +
25                                 'code=(0x[0-9a-fA-F]+)');
26
27  var binderTransReceivedRE = /transaction=(\d+)/;
28
29  function isBinderThread(name) {
30    return (name.indexOf('Binder') > -1);
31  }
32
33  // Taken from kernel source: include/uapi/linux/android/binder.h.
34  var TF_ONE_WAY = 0x01;
35  var TF_ROOT_OBJECT = 0x04;
36  var TF_STATUS_CODE = 0x08;
37  var TF_ACCEPT_FDS = 0x10;
38  var NO_FLAGS = 0;
39
40  function binderFlagsToHuman(num) {
41    var flag = parseInt(num, 16);
42    var str = '';
43
44    if (flag & TF_ONE_WAY)
45      str += 'this is a one-way call: async, no return; ';
46    if (flag & TF_ROOT_OBJECT)
47      str += 'contents are the components root object; ';
48    if (flag & TF_STATUS_CODE)
49      str += 'contents are a 32-bit status code; ';
50    if (flag & TF_ACCEPT_FDS)
51      str += 'allow replies with file descriptors; ';
52    if (flag === NO_FLAGS)
53      str += 'No Flags Set';
54
55    return str;
56  }
57
58  function isReplyToOrigin(calling, called) {
59    return (called.dest_proc === calling.calling_pid ||
60            called.dest_thread === calling.calling_pid);
61  }
62
63  function binderCodeToHuman(code) {
64    return 'Java Layer Dependent';
65  }
66
67  function doInternalSlice(trans, slice, ts) {
68    if (slice.subSlices.length !== 0) {
69      /* We want to make sure we keep moving the small slice to the end of
70         the big slice or else the arrows will not point to the end.
71      */
72      slice.subSlices[0].start = ts;
73      return slice.subSlices[0];
74    }
75    var kthread = trans.calling_kthread.thread;
76    var internal_slice = kthread.sliceGroup.pushCompleteSlice('binder',
77                             slice.title,
78                             ts, .001, 0, 0,
79                             slice.args);
80
81    internal_slice.title = slice.title;
82    internal_slice.id = slice.id;
83    internal_slice.colorId = slice.colorId;
84    slice.subSlices.push(internal_slice);
85    return internal_slice;
86  }
87
88  function generateBinderArgsForSlice(trans, c_threadName) {
89    return {
90      'Transaction Id': trans.transaction_key,
91      'Destination Node': trans.dest_node,
92      'Destination Process': trans.dest_proc,
93      'Destination Thread': trans.dest_thread,
94      'Destination Name': c_threadName,
95      'Reply transaction?': trans.is_reply_transaction,
96      'Flags': trans.flags + ' ' +
97           binderFlagsToHuman(trans.flags),
98
99      'Code': trans.code + ' ' +
100           binderCodeToHuman(trans.code),
101
102      'Calling PID': trans.calling_pid,
103      'Calling tgid': trans.calling_kthread.thread.parent.pid
104    };
105  }
106
107  /** @constructor */
108  function BinderTransaction(events, calling_pid, calling_ts, calling_kthread) {
109    this.transaction_key = parseInt(events[1]);
110    this.dest_node = parseInt(events[2]);
111    this.dest_proc = parseInt(events[3]);
112    this.dest_thread = parseInt(events[4]);
113    this.is_reply_transaction = parseInt(events[5]) === 1 ? true : false;
114    this.expect_reply = ((this.is_reply_transaction === false) &&
115        (parseInt(events[6], 16) & TF_ONE_WAY) === 0);
116
117    this.flags = events[6];
118    this.code = events[7];
119    this.calling_pid = calling_pid;
120    this.calling_ts = calling_ts;
121    this.calling_kthread = calling_kthread;
122  }
123
124
125  /** @constructor */
126  function BinderParser(importer) {
127    Parser.call(this, importer);
128    importer.registerEventHandler('binder_locked',
129        BinderParser.prototype.
130        binderLocked.bind(this));
131    importer.registerEventHandler('binder_unlock',
132        BinderParser.prototype.
133        binderUnlock.bind(this));
134    importer.registerEventHandler('binder_lock',
135        BinderParser.prototype.binderLock.bind(this));
136    importer.registerEventHandler('binder_transaction',
137        BinderParser.prototype.
138        binderTransaction.bind(this));
139    importer.registerEventHandler('binder_transaction_received',
140        BinderParser.prototype.
141        binderTransactionReceived.bind(this));
142
143    this.model_ = importer.model;
144    this.kthreadlookup = {};
145    this.importer_ = importer;
146    this.transWaitingRecv = {};
147    this.syncTransWaitingCompletion = {};
148    this.recursiveSyncTransWaitingCompletion_ByPID = {};
149    this.receivedTransWaitingConversion = {};
150  }
151
152  BinderParser.prototype = {
153    __proto__: Parser.prototype,
154
155    binderLock: function(eventName, cpuNumber, pid, ts, eventBase) {
156      var tgid = parseInt(eventBase.tgid);
157      this.doNameMappings(pid, tgid, eventName.threadName);
158
159      var kthread = this.importer_.
160          getOrCreateBinderKernelThread(eventBase.threadName, tgid, pid);
161
162      kthread.binderAttemptLockTS = ts;
163      kthread.binderOpenTsA = ts;
164      return true;
165    },
166
167    binderLocked: function(eventName, cpuNumber, pid, ts, eventBase) {
168      var binder_thread = isBinderThread(eventBase.threadName);
169      var tgid, name;
170      var as_slice;
171      var need_push = false;
172      var kthread, rthread;
173
174      tgid = parseInt(eventBase.tgid);
175      name = eventBase.threadName;
176
177      kthread = this.importer_.
178          getOrCreateBinderKernelThread(eventBase.threadName, tgid, pid);
179
180      this.doNameMappings(pid, tgid, name);
181
182      rthread = kthread.thread;
183      kthread.binderLockAquiredTS = ts;
184
185      if (kthread.binderAttemptLockTS === undefined)
186        return false;
187
188      var args = this.generateArgsForSlice(tgid, pid, name, kthread);
189      rthread.sliceGroup.pushCompleteSlice('binder', 'binder lock waiting',
190          kthread.binderAttemptLockTS,
191          ts - kthread.binderAttemptLockTS,
192          0, 0, args);
193
194      kthread.binderAttemptLockTS = undefined;
195      return true;
196    },
197
198    binderUnlock: function(eventName, cpuNumber, pid, ts, eventBase) {
199      var tgid = parseInt(eventBase.tgid);
200      var kthread = this.importer_.
201          getOrCreateBinderKernelThread(
202          eventBase.threadName, tgid, pid);
203
204      if (kthread.binderLockAquiredTS === undefined)
205        return false;
206
207      args = this.generateArgsForSlice(tgid, pid, eventBase.threadName,
208          kthread);
209      kthread.thread.sliceGroup.pushCompleteSlice(
210          'binder',
211          'binder lock held',
212           kthread.binderLockAquiredTS,
213           ts - kthread.binderLockAquiredTS,
214           0, 0, args);
215
216      kthread.binderLockAquiredTS = undefined;
217      return true;
218    },
219
220    /** There are a few transaction status changes that signify
221     *  progress through a binder transaction:
222     *
223     * Case One: Sync transaction.
224     *  Thread A calls a blocking function on Thread B. We receive a
225     *  binder_transaction msg From thread A stating that it is going to Call
226     *  thread B. We create a slice and a binder object for this transaction and
227     *  add it to addTransactionWaitingForRecv(transaction key, binder object)
228     *  This notifies thread B and passes the slice, binder object and time
229     *  stamp.
230     *
231     * Case Two: Async transaction.
232     *  Thread A calls an async function on Thread B. Like above we receive a
233     *  binder_transaction message, but the flags differ from above. The
234     *  TF_ONEWAY flags are set so we know that when Thread B gets the
235     *  binder_transaction_received with the same transaciton key the total
236     *  transaction is complete.
237     *
238     * Case Three: 'Prior_receive'
239     *  Prior_receive occurs when the thread being called (think A calls B),
240     *  receives a binder_transaction_received message, but cannot correlate it
241     *  to any current outstanding recursive transactions. That means the
242     *  message it just received is the start of some communication, not some
243     *  ongoing communication.
244     *  Once the execution flow has been passed to thread B, from A:
245     *  Thread B enters binder_transaction_received() we see that Thread A
246     *  had notified us that it sent us a message by calling
247     *  getTransactionWaitingForRecv(transaction key);
248     *  What can happen now is either this was a simple Call reply,
249     *  or this is a call -> recursion -> reply. We call modelPriorReceive()
250     *  which sets up the slices accordingly.
251     *  If this is a call -> recursion -> reply
252     *  we will go to case 4 by calling addRecursiveSyncTransNeedingCompletion()
253     *  The function takes B's PID, the binder object from A and the current
254     *  binder object from B. This function adds outstanding non-complete
255     *  transactions to a stack on thread B.
256     *
257     * Case Four: 'recursive_trans'
258     *  This case follows Like above:
259     *  A sent binder_transaction
260     *  B got binder_transaction_received
261     *  B instead of replying to A can Call C or call 'into' A, ie recursion
262     *  Case four also deals with setting up a large slice to 'contain'
263     *  all the recursive transactions that happen until B finally replies to
264     *  A.
265     *
266     *
267     * An example: A-> B-> C-> B-> A
268     *
269     * (1) A starts a synchronous transaction to B.
270     * (2) A enters binderTransaction() event handler, hits the else statement
271     * (3) A calls addTransactionWaitingForRecv(trans key, object) to notify
272     *     Thread B.
273     * (4) B Enters binderTransactionReceived().
274     * (5) B hits the second if after calling
275     *     getTransactionWaitingForRecv(trans key)
276     *     This function returns us the object set up in step (3).
277     * (6) This is not an async transaction, B calls
278     *      setCurrentReceiveOnPID(B's PID, [ts for (4), object from (3)]).
279     *
280     * (7) B enters binderTransaction() event handler, first if triggers after
281     *     calling getPriorReceiveOnPID(B's PID) the tuple from (6) is returned.
282     *
283     * (8) Execution enters modelPriorReceive().
284     * (8a) A slice is setup for this binder transaction in B's timeline.
285     * (9) This is not a reply to thread A, B is going to call Thread C.
286     * (10) else statement is hit.
287     * (11) We set the tile from (8a) to be binder_reply this is the
288     *     'containg slice' for the recursion
289     * (12) We create a new slice 'binder_transaction' this slice is the
290     *      recursive slice that will have arrows to Thread C's slice.
291     * (13) addRecursiveSyncTransNeedingCompletion(B's PID,
292     *                                            [obj from (3), obj from 7])
293     *      this sets up notification that B's pid has outstanding recursive
294     *      transactions that need to be completed.
295     * (14) B notifies C that a transaction is waiting for it by calling
296     *      addTransactionWaitingForRecv like in step (3).
297     * (15) C enters binderTransactionReceived() step 5 6 7 8 8a happen, but in
298     *      the context of Thread C.
299     * (16) C is in modelPriorReceive(), it hits the first if statement,
300     *      this transaction _IS_ a reply, and it is a reply to B.
301     * (17) C calls addSyncTransNeedingCompletion(trans key,
302     *                                       [object from(3), object from 15-5])
303     * (18) B enters binderTransactionReceived() hits the first if after calling
304     *      getSyncTransNeedingCompletion(trans key from (17)) the tuple from
305     *     (17) gets returned.
306     *
307     * (19) B scales up the slice created in (12) and sets up flows from 15-8a
308     *      slice.
309     * (20) B enters BinderTransaction() event handler and the second if is hit
310     *      after calling getRecursiveTransactionNeedingCompletion(B's pid).
311     * (21) modelRecursiveTransactions() gets called, first if executes.
312     * (22) slice durations are fixed up.
313     * (23) B notifies A via
314     *      addSyncTransNeedingCompletion(trans key, binder obj from 8a).
315     * (24) B deletes the outstanding asynctrans via
316     (      removeRecursiveTransaction(B's pid).
317     * (25) A enters binderTransactionReceived() event handler and finishes up
318     *      some flows, and slices.
319     */
320    binderTransaction: function(eventName, cpuNumber, pid, ts, eventBase) {
321      var event = binderTransRE.exec(eventBase.details);
322      if (event === undefined)
323        return false;
324
325      var tgid = parseInt(eventBase.tgid);
326
327      this.doNameMappings(pid, tgid, eventBase.threadName);
328
329      var kthread;
330      kthread = this.importer_.
331          getOrCreateBinderKernelThread(eventBase.threadName, tgid, pid);
332
333      var trans = new BinderTransaction(event, pid, ts, kthread);
334      var args = generateBinderArgsForSlice(trans, eventBase.threadName);
335      /**
336       * This thread previously ack'd the transaction with a
337       * transaction_received. That means someone sent us a message we processed
338       * it and are now sending a transaction.
339       * The transaction could be a response, or it could be recursive.
340       */
341      var prior_receive = this.getPriorReceiveOnPID(pid);
342
343      if (prior_receive !== false) {
344        return this.modelPriorReceive(prior_receive, ts, pid, tgid, kthread,
345            trans, args, event);
346      }
347      /**
348       * This Thread has an already established recursive slice. We will now
349       * either complete the entire transaction, OR do more recursive calls.
350       */
351      var recursive_trans = this.getRecursiveTransactionNeedingCompletion(pid);
352
353      if (recursive_trans !== false)
354        return this.modelRecursiveTransactions(recursive_trans, ts, pid,
355            kthread, trans, args);
356
357      /**
358       * Start of a Transaction. This thread is the initiator of either a call
359       * response, an async call -> ack, or a call -> recursion -> response.
360       * Note, we put a fake duration into this slice and patch it up later.
361       */
362      var slice = kthread.thread.sliceGroup.pushCompleteSlice('binder',
363          '', ts, .03, 0, 0, args);
364
365      slice.colorId = tr.ui.b.
366          getColorIdForGeneralPurposeString(ts.toString());
367      trans.slice = slice;
368
369      if (trans.expect_reply)
370        slice.title = 'binder transaction';
371      else
372        slice.title = 'binder transaction async';
373
374      this.addTransactionWaitingForRecv(trans.transaction_key, trans);
375
376      return true;
377    },
378
379    binderTransactionReceived: function(eventName, cpuNumber, pid, ts,
380                                        eventBase) {
381      var event = binderTransReceivedRE.exec(eventBase.details);
382
383      if (event === undefined)
384        return false;
385
386      var transactionkey = parseInt(event[1]);
387      var tgid = parseInt(eventBase.tgid);
388      var kthread;
389      kthread = this.importer_.
390          getOrCreateBinderKernelThread(eventBase.threadName, tgid, pid);
391
392      var syncComplete = this.getSyncTransNeedsCompletion(transactionkey);
393
394      if (syncComplete !== false) {
395        /* This recv is the completion of a synchronous transaction.
396         * We need to scale the slice up to the current ts and finish
397         * creating some flows.
398         */
399        var sync_trans = syncComplete[0];
400        var sync_slice = sync_trans.slice;
401        var response_trans = syncComplete[1];
402        var response_slice = response_trans.slice;
403
404        sync_slice.duration = ts - sync_slice.start;
405        /** These calls are a little hack that places a very small slice at
406         *  the end of the sync slice and the response slice. This allows us
407         *  to hook flow events (arrows) from the start to the end of the
408         *  slices.
409         */
410        var sync_internal = doInternalSlice(sync_trans, sync_slice, ts);
411        var response_ts = response_slice.start + response_slice.duration;
412        var response_internal = doInternalSlice(response_trans,
413            response_slice, response_ts);
414
415        if (response_slice.outFlowEvents.length === 0 ||
416            sync_slice.inFlowEvents.length === 0) {
417          var flow = this.generateFlow(response_internal, sync_internal,
418              response_trans, sync_trans);
419
420          sync_slice.inFlowEvents.push(flow);
421          response_slice.outFlowEvents.push(flow);
422          this.model_.flowEvents.push(flow);
423        }
424        // Move flow arrows -- but not the first one.
425        for (var i = 1; i < sync_slice.inFlowEvents.length; i++) {
426          sync_slice.inFlowEvents[i].duration =
427              ts - sync_slice.inFlowEvents[i].start;
428        }
429        return true;
430      }
431
432      var tr_for_recv = this.getTransactionWaitingForRecv(transactionkey);
433
434      if (tr_for_recv !== false) {
435        if (!tr_for_recv.expect_reply) {
436          // This is an async call place an Async slice.
437          var args = generateBinderArgsForSlice(tr_for_recv,
438              eventBase.threadName);
439          var slice = kthread.thread.sliceGroup.
440              pushCompleteSlice('binder',
441              'binder Async recv',
442              ts, .03, 0, 0,
443              args);
444
445          var fake_event = [0, 0, 0, 0, 0, 0, 0];
446          var fake_trans = new BinderTransaction(fake_event, pid, ts, kthread);
447          var flow = this.generateFlow(tr_for_recv.slice, slice,
448              tr_for_recv, fake_trans);
449
450          this.model_.flowEvents.push(flow);
451          tr_for_recv.slice.title = 'binder transaction async';
452          tr_for_recv.slice.duration = .03;
453          return true;
454        }
455        // Setup prior receive.
456        tr_for_recv.slice.title = 'binder transaction';
457        this.setCurrentReceiveOnPID(pid, [ts, tr_for_recv]);
458        return true;
459      }
460      /** This case is when we received an ack for a transaction we have
461       *  never seen before. This usually happens at the start of a trace.
462       *  We will get incomplete transactions that started before started
463       *  tracing. Just discard them.
464       */
465      return false;
466    },
467
468    // helper functions
469    modelRecursiveTransactions: function(recursive_trans, ts, pid, kthread,
470                                         trans, args) {
471
472      var recursive_slice = recursive_trans[1].slice;
473      var orig_slice = recursive_trans[0].slice;
474      recursive_slice.duration = ts - recursive_slice.start;
475      trans.slice = recursive_slice;
476
477      if (trans.is_reply_transaction) {
478        /* Case one:
479         * This transaction is finally the reply of the recursion.
480         */
481        orig_slice.duration = ts - orig_slice.start;
482        this.addSyncTransNeedingCompletion(trans.transaction_key,
483             recursive_trans);
484
485        if (isReplyToOrigin(recursive_trans[0], trans))
486          this.removeRecursiveTransaction(pid);
487      } else {
488        /**
489         *  Case two:
490         *  This transaction is more recursive calls.
491         *  This is a nested call within an already started transaction,
492         *  it can either be a async or a normal sync transaction.
493         */
494        var slice = kthread.thread.sliceGroup.pushCompleteSlice('binder',
495            '' , ts, .03, 0,
496            0, args);
497
498        trans.slice = slice;
499        this.addTransactionWaitingForRecv(trans.transaction_key, trans);
500      }
501      return true;
502    },
503
504    modelPriorReceive: function(prior_receive, ts, pid, tgid, kthread, trans,
505                                args, event) {
506      var callee_slice = prior_receive[1].slice;
507      var callee_trans = prior_receive[1];
508      var recv_ts = prior_receive[0];
509      var slice = kthread.thread.sliceGroup.pushCompleteSlice('binder',
510          '', recv_ts, ts - recv_ts, 0, 0, args);
511
512      var flow = this.generateFlow(callee_slice, slice, callee_trans, trans);
513      this.model_.flowEvents.push(flow);
514      trans.slice = slice;
515
516      if (trans.is_reply_transaction) {
517        /* This is a response to a synchronous or a recursive sync
518         * transaction.
519         */
520        slice.title = 'binder reply';
521        /* Notify this transaction key that when it recv's it is completing
522         * a sync transaction.
523         */
524        this.addSyncTransNeedingCompletion(trans.transaction_key,
525            [callee_trans, trans]);
526      } else {
527        /**
528         * Recursive calls and or calls around, either way it's not
529         * going to complete a transaction.
530         */
531          slice.title = 'binder reply';
532          /* Since this is a recursive transaction we want to create the main
533           * large slice which will contain all these recursive transactions.
534           * For that we created the main slice above and this is a recursive
535           * transaction that will be placed right below it. Note, that this
536           * is only for the first recursive transaction. If more come they will
537           * be handled below in the getRecursiveTransactionNeedingCompletion
538           */
539          var trans1 = new BinderTransaction(event, pid, ts, kthread);
540
541          slice = kthread.thread.sliceGroup.
542          pushCompleteSlice('binder',
543              'binder transaction',
544              recv_ts,
545              (ts - recv_ts), 0,
546              0, args);
547
548          /* could be a async trans if so set the length to be a small one */
549          if (!trans.expect_reply) {
550            slice.title = 'binder transaction async';
551            slice.duration = .03;
552          } else {
553            /* stupid hack to stop merging of AIDL slices and
554             * this slice. This is currently disabled, if AIDL tracing is on we
555             * will see merging of this slice and the AIDL slice. Once upstream
556             * has a solution for flow events to be placed in the middle of
557             * slices this part can be fixed.
558             *
559             * This is commented out because AIDL tracing doesn't exit yet.
560             */
561            //slice.start += .15;
562          }
563          trans1.slice = slice;
564          this.addRecursiveSyncTransNeedingCompletion(pid,
565              [callee_trans, trans]);
566          this.addTransactionWaitingForRecv(trans.transaction_key, trans1);
567      }
568      return true;
569    },
570
571    getRecursiveTransactionNeedingCompletion: function(pid) {
572      if (this.recursiveSyncTransWaitingCompletion_ByPID[pid] === undefined)
573        return false;
574
575      var len = this.recursiveSyncTransWaitingCompletion_ByPID[pid].length;
576      if (len === 0)
577        return false;
578
579      return this.recursiveSyncTransWaitingCompletion_ByPID[pid][len - 1];
580    },
581
582    addRecursiveSyncTransNeedingCompletion: function(pid, tuple) {
583      if (this.recursiveSyncTransWaitingCompletion_ByPID[pid] === undefined)
584        this.recursiveSyncTransWaitingCompletion_ByPID[pid] = [];
585
586      this.recursiveSyncTransWaitingCompletion_ByPID[pid].push(tuple);
587    },
588
589    removeRecursiveTransaction: function(pid) {
590      var len = this.recursiveSyncTransWaitingCompletion_ByPID[pid].length;
591      if (len === 0) {
592        delete this.recursiveSyncTransWaitingCompletion_ByPID[pid];
593        return;
594      }
595
596      this.recursiveSyncTransWaitingCompletion_ByPID[pid].splice(len - 1, 1);
597    },
598
599    setCurrentReceiveOnPID: function(pid, tuple) {
600      if (this.receivedTransWaitingConversion[pid] === undefined) {
601        this.receivedTransWaitingConversion[pid] = [];
602      }
603      this.receivedTransWaitingConversion[pid].push(tuple);
604    },
605
606    getPriorReceiveOnPID: function(pid) {
607      if (this.receivedTransWaitingConversion[pid] === undefined)
608        return false;
609
610      var len = this.receivedTransWaitingConversion[pid].length;
611      if (len === 0)
612        return false;
613
614      return this.receivedTransWaitingConversion[pid].splice(len - 1, 1)[0];
615    },
616
617    addSyncTransNeedingCompletion: function(transactionkey, tuple) {
618      var dict = this.syncTransWaitingCompletion;
619      dict[transactionkey] = tuple;
620    },
621
622    getSyncTransNeedsCompletion: function(transactionkey) {
623      var ret = this.syncTransWaitingCompletion[transactionkey];
624      if (ret === undefined)
625        return false;
626
627      delete this.syncTransWaitingCompletion[transactionkey];
628      return ret;
629    },
630
631    getTransactionWaitingForRecv: function(transactionkey) {
632      var ret = this.transWaitingRecv[transactionkey];
633      if (ret === undefined)
634        return false;
635
636      delete this.transWaitingRecv[transactionkey];
637      return ret;
638    },
639
640    addTransactionWaitingForRecv: function(transactionkey, transaction) {
641      this.transWaitingRecv[transactionkey] = transaction;
642    },
643
644    generateFlow: function(from, to, from_trans, to_trans) {
645      var title = 'Transaction from : ' +
646        this.pid2name(from_trans.calling_pid) +
647        ' From PID: ' + from_trans.calling_pid + ' to pid: ' +
648        to_trans.calling_pid +
649        ' Thread Name: ' + this.pid2name(to_trans.calling_pid);
650
651      var ts = from.start;
652      var flow = new tr.model.FlowEvent('binder', 'binder',
653          title, 1, ts, []);
654      flow.startSlice = from;
655      flow.endSlice = to;
656      flow.start = from.start;
657      flow.duration = to.start - ts;
658
659      from.outFlowEvents.push(flow);
660      to.inFlowEvents.push(flow);
661
662      return flow;
663    },
664
665    generateArgsForSlice: function(tgid, pid, name, kthread) {
666      return {
667        'Thread Name': name,
668        'pid': pid,
669        'gid': tgid
670      };
671    },
672
673    pid2name: function(pid) {
674      return this.kthreadlookup[pid];
675    },
676
677    doNameMappings: function(pid, tgid, name) {
678      this.registerPidName(pid, name);
679      this.registerPidName(tgid, name);
680    },
681
682    registerPidName: function(pid, name) {
683      if (this.pid2name(pid) === undefined)
684        this.kthreadlookup[pid] = name;
685    }
686  };
687
688  Parser.register(BinderParser);
689  return {
690    BinderParser: BinderParser
691  };
692});
693</script>
694