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