| Index: tools/tracemerge.dart
 | 
| diff --git a/pkg/collection_helpers/lib/algorithms.dart b/tools/tracemerge.dart
 | 
| similarity index 66%
 | 
| copy from pkg/collection_helpers/lib/algorithms.dart
 | 
| copy to tools/tracemerge.dart
 | 
| index 057111a664625e3d4ab28a0f9421f03b340c73bb..6c7d5ddd45dcf2b9d2cca9aede54f084293ae6d5 100644
 | 
| --- a/pkg/collection_helpers/lib/algorithms.dart
 | 
| +++ b/tools/tracemerge.dart
 | 
| @@ -2,99 +2,10 @@
 | 
|  // for details. All rights reserved. Use of this source code is governed by a
 | 
|  // BSD-style license that can be found in the LICENSE file.
 | 
|  
 | 
| -/**
 | 
| - * Operations on collections.
 | 
| - */
 | 
| -library dart.collection_helpers.algorithms;
 | 
| -
 | 
| -import "dart:math" show Random;
 | 
| -
 | 
| -/** Version of [binarySearch] optimized for comparable keys */
 | 
| -int _comparableBinarySearch(List<Comparable> list, Comparable key) {
 | 
| -  int min = 0;
 | 
| -  int max = list.length;
 | 
| -  while (min < max) {
 | 
| -    int mid = min + ((max - min) >> 1);
 | 
| -    var element = list[mid];
 | 
| -    int comp = element.compareTo(key);
 | 
| -    if (comp == 0) return mid;
 | 
| -    if (comp < 0) {
 | 
| -      min = mid + 1;
 | 
| -    } else {
 | 
| -      max = mid;
 | 
| -    }
 | 
| -  }
 | 
| -  return -1;
 | 
| -}
 | 
| -
 | 
| -/**
 | 
| - * Returns a position of the [key] in [sortedList], if it is there.
 | 
| - *
 | 
| - * If the list isn't sorted according to the [compare] function, the result
 | 
| - * is unpredictable.
 | 
| - *
 | 
| - * If [compare] is omitted, it defaults to calling [Comparable.compareTo] on
 | 
| - * the objects.
 | 
| - *
 | 
| - * Returns -1 if [key] is not in the list by default.
 | 
| - */
 | 
| -int binarySearch(List sortedList, var key,
 | 
| -                 { int compare(var a, var b) }) {
 | 
| -  if (compare == null) {
 | 
| -    return _comparableBinarySearch(sortedList, key);
 | 
| -  }
 | 
| -  int min = 0;
 | 
| -  int max = sortedList.length;
 | 
| -  while (min < max) {
 | 
| -    int mid = min + ((max - min) >> 1);
 | 
| -    var element = sortedList[mid];
 | 
| -    int comp = compare(element, key);
 | 
| -    if (comp == 0) return mid;
 | 
| -    if (comp < 0) {
 | 
| -      min = mid + 1;
 | 
| -    } else {
 | 
| -      max = mid;
 | 
| -    }
 | 
| -  }
 | 
| -  return -1;
 | 
| -}
 | 
| -
 | 
| -
 | 
| -/**
 | 
| - * Shuffles a list randomly.
 | 
| - *
 | 
| - * A sub-range of a list can be shuffled by providing [start] and [end].
 | 
| - */
 | 
| -void shuffle(List list, [int start = 0, int end = null]) {
 | 
| -  Random random = new Random();
 | 
| -  if (end == null) end = list.length;
 | 
| -  int length = end - start;
 | 
| -  while (length > 1) {
 | 
| -    int pos = random.nextInt(length);
 | 
| -    length--;
 | 
| -    var tmp1 = list[start + pos];
 | 
| -    list[start + pos] = list[start + length];
 | 
| -    list[start + length] = tmp1;
 | 
| -  }
 | 
| -}
 | 
| -
 | 
| +// Merge multiple isolate profiler tracing dumps into one.
 | 
|  
 | 
| -/**
 | 
| - * Reverses a list, or a part of a list, in-place.
 | 
| - */
 | 
| -void reverse(List list, [int start = 0, int end = null]) {
 | 
| -  if (end == null) end = list.length;
 | 
| -  _reverse(list, start, end);
 | 
| -}
 | 
| -
 | 
| -// Internal helper function that assumes valid arguments.
 | 
| -void _reverse(List list, int start, int end) {
 | 
| -  for (int i = start, j = end - 1; i < j; i++, j--) {
 | 
| -    var tmp = list[i];
 | 
| -    list[i] = list[j];
 | 
| -    list[j] = tmp;
 | 
| -  }
 | 
| -}
 | 
| +import 'dart:convert';
 | 
| +import 'dart:io';
 | 
|  
 | 
|  /**
 | 
|   * Sort a list using insertion sort.
 | 
| @@ -299,3 +210,152 @@ void _merge(int compare(a, b),
 | 
|    target.setRange(targetOffset, targetOffset + (secondEnd - cursor2),
 | 
|        secondList, cursor2);
 | 
|  }
 | 
| +
 | 
| +class TraceMerge {
 | 
| +  Map _processes = {};
 | 
| +  List _metaEvents = [];
 | 
| +
 | 
| +  void _processEventsFromFile(String name) {
 | 
| +    var file = new File(name);
 | 
| +    var events = [];
 | 
| +    try {
 | 
| +      var contents = file.readAsStringSync();
 | 
| +      events = JSON.decode(contents);
 | 
| +    } catch (e) {
 | 
| +      print('Exception for $name $e');
 | 
| +    }
 | 
| +    _processEvents(events);
 | 
| +  }
 | 
| +
 | 
| +  List _findOrAddProcessThread(pid, tid) {
 | 
| +    var process = _processes[pid];
 | 
| +    if (process == null) {
 | 
| +      process = {};
 | 
| +      _processes[pid] = process;
 | 
| +    }
 | 
| +    var thread = process[tid];
 | 
| +    if (thread == null) {
 | 
| +      thread = [];
 | 
| +      process[tid] = thread;
 | 
| +    }
 | 
| +    return thread;
 | 
| +  }
 | 
| +
 | 
| +  void _processEvents(List events) {
 | 
| +    for (var i = 0; i < events.length; i++) {
 | 
| +      Map event = events[i];
 | 
| +      if (event['ph'] == 'M') {
 | 
| +        _metaEvents.add(event);
 | 
| +      } else {
 | 
| +        var pid = event['pid'];
 | 
| +        if (pid == null) {
 | 
| +          throw "No pid in ${event}";
 | 
| +        }
 | 
| +        var tid = event['tid'];
 | 
| +        if (tid == null) {
 | 
| +          throw "No tid in ${event}";
 | 
| +        }
 | 
| +        var thread = _findOrAddProcessThread(pid, tid);
 | 
| +        if (thread == null) {
 | 
| +          throw "No thread list returned.";
 | 
| +        }
 | 
| +        thread.add(event);
 | 
| +      }
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  int _compare(Map a, Map b) {
 | 
| +    if (a['ts'] > b['ts']) {
 | 
| +      return 1;
 | 
| +    } else if (a['ts'] < b['ts']) {
 | 
| +      return -1;
 | 
| +    }
 | 
| +    return 0;
 | 
| +  }
 | 
| +
 | 
| +  void _sortEvents() {
 | 
| +    _processes.forEach((k, Map process) {
 | 
| +      process.forEach((k, List thread) {
 | 
| +        mergeSort(thread, compare:_compare);
 | 
| +      });
 | 
| +    });
 | 
| +  }
 | 
| +
 | 
| +  void _mergeEventsForThread(List thread) {
 | 
| +    List<Map> stack = [];
 | 
| +    int stackDepth = 0;
 | 
| +    thread.forEach((event) {
 | 
| +      if (event['ph'] == 'B') {
 | 
| +        if (stackDepth == stack.length) {
 | 
| +          stack.add(null);
 | 
| +        }
 | 
| +        stackDepth++;
 | 
| +        var end_event = stack[stackDepth - 1];
 | 
| +        if (end_event != null) {
 | 
| +          if (end_event['name'] == event['name'] && stackDepth > 1) {
 | 
| +            // Kill these events.
 | 
| +            // event['dead'] = true;
 | 
| +            // end_event['dead'] = true;
 | 
| +          }
 | 
| +        }
 | 
| +      } else {
 | 
| +        if (event['ph'] != 'E') {
 | 
| +          throw 'Expected E event: ${event}';
 | 
| +        }
 | 
| +        if (stackDepth <= 0) {
 | 
| +          throw 'Stack out of sync ${event}.';
 | 
| +        }
 | 
| +        stackDepth--;
 | 
| +        stack[stackDepth] = event;
 | 
| +      }
 | 
| +    });
 | 
| +  }
 | 
| +
 | 
| +  void _mergeEvents() {
 | 
| +    _processes.forEach((k, Map process) {
 | 
| +      process.forEach((k, List thread) {
 | 
| +        _mergeEventsForThread(thread);
 | 
| +      });
 | 
| +    });
 | 
| +  }
 | 
| +
 | 
| +  void writeEventsToFile(String name) {
 | 
| +    var file = new File(name);
 | 
| +    List final_events = _metaEvents;
 | 
| +    _processes.forEach((pid, Map process) {
 | 
| +      process.forEach((tid, List thread) {
 | 
| +        thread.forEach((event) {
 | 
| +          if (event['dead'] == null) {
 | 
| +            // Not dead.
 | 
| +            final_events.add(event);
 | 
| +          }
 | 
| +        });
 | 
| +      });
 | 
| +    });
 | 
| +    file.writeAsStringSync(JSON.encode(final_events));
 | 
| +  }
 | 
| +
 | 
| +  void merge(List<String> inputs) {
 | 
| +    for (var i = 0; i < inputs.length; i++) {
 | 
| +      _processEventsFromFile(inputs[i]);
 | 
| +    }
 | 
| +    _sortEvents();
 | 
| +    _mergeEvents();
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +main(List<String> arguments) {
 | 
| +  if (arguments.length < 2) {
 | 
| +    print('${Platform.executable} ${Platform.script} <output> <inputs>');
 | 
| +    return;
 | 
| +  }
 | 
| +  String output = arguments[0];
 | 
| +  List<String> inputs = new List<String>();
 | 
| +  for (var i = 1; i < arguments.length; i++) {
 | 
| +    inputs.add(arguments[i]);
 | 
| +  }
 | 
| +  print('Merging $inputs into $output.');
 | 
| +  TraceMerge tm = new TraceMerge();
 | 
| +  tm.merge(inputs);
 | 
| +  tm.writeEventsToFile(output);
 | 
| +}
 | 
| 
 |