Move V8 to external/v8

Change-Id: If68025d67453785a651c5dfb34fad298c16676a4
diff --git a/test/mjsunit/tools/codemap.js b/test/mjsunit/tools/codemap.js
new file mode 100644
index 0000000..06a91e8
--- /dev/null
+++ b/test/mjsunit/tools/codemap.js
@@ -0,0 +1,180 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Load Splay tree and CodeMap implementations from <project root>/tools.
+// Files: tools/splaytree.js tools/codemap.js
+
+
+function newCodeEntry(size, name) {
+  return new devtools.profiler.CodeMap.CodeEntry(size, name);
+};
+
+
+function assertEntry(codeMap, expected_name, addr) {
+  var entry = codeMap.findEntry(addr);
+  assertNotNull(entry, 'no entry at ' + addr.toString(16));
+  assertEquals(expected_name, entry.name, 'at ' + addr.toString(16));
+};
+
+
+function assertNoEntry(codeMap, addr) {
+  assertNull(codeMap.findEntry(addr), 'at ' + addr.toString(16));
+};
+
+
+(function testLibrariesAndStaticCode() {
+  var codeMap = new devtools.profiler.CodeMap();
+  codeMap.addLibrary(0x1500, newCodeEntry(0x3000, 'lib1'));
+  codeMap.addLibrary(0x15500, newCodeEntry(0x5000, 'lib2'));
+  codeMap.addLibrary(0x155500, newCodeEntry(0x10000, 'lib3'));
+  assertNoEntry(codeMap, 0);
+  assertNoEntry(codeMap, 0x1500 - 1);
+  assertEntry(codeMap, 'lib1', 0x1500);
+  assertEntry(codeMap, 'lib1', 0x1500 + 0x100);
+  assertEntry(codeMap, 'lib1', 0x1500 + 0x1000);
+  assertEntry(codeMap, 'lib1', 0x1500 + 0x3000 - 1);
+  assertNoEntry(codeMap, 0x1500 + 0x3000);
+  assertNoEntry(codeMap, 0x15500 - 1);
+  assertEntry(codeMap, 'lib2', 0x15500);
+  assertEntry(codeMap, 'lib2', 0x15500 + 0x100);
+  assertEntry(codeMap, 'lib2', 0x15500 + 0x1000);
+  assertEntry(codeMap, 'lib2', 0x15500 + 0x5000 - 1);
+  assertNoEntry(codeMap, 0x15500 + 0x5000);
+  assertNoEntry(codeMap, 0x155500 - 1);
+  assertEntry(codeMap, 'lib3', 0x155500);
+  assertEntry(codeMap, 'lib3', 0x155500 + 0x100);
+  assertEntry(codeMap, 'lib3', 0x155500 + 0x1000);
+  assertEntry(codeMap, 'lib3', 0x155500 + 0x10000 - 1);
+  assertNoEntry(codeMap, 0x155500 + 0x10000);
+  assertNoEntry(codeMap, 0xFFFFFFFF);
+
+  codeMap.addStaticCode(0x1510, newCodeEntry(0x30, 'lib1-f1'));
+  codeMap.addStaticCode(0x1600, newCodeEntry(0x50, 'lib1-f2'));
+  codeMap.addStaticCode(0x15520, newCodeEntry(0x100, 'lib2-f1'));
+  assertEntry(codeMap, 'lib1', 0x1500);
+  assertEntry(codeMap, 'lib1', 0x1510 - 1);
+  assertEntry(codeMap, 'lib1-f1', 0x1510);
+  assertEntry(codeMap, 'lib1-f1', 0x1510 + 0x15);
+  assertEntry(codeMap, 'lib1-f1', 0x1510 + 0x30 - 1);
+  assertEntry(codeMap, 'lib1', 0x1510 + 0x30);
+  assertEntry(codeMap, 'lib1', 0x1600 - 1);
+  assertEntry(codeMap, 'lib1-f2', 0x1600);
+  assertEntry(codeMap, 'lib1-f2', 0x1600 + 0x30);
+  assertEntry(codeMap, 'lib1-f2', 0x1600 + 0x50 - 1);
+  assertEntry(codeMap, 'lib1', 0x1600 + 0x50);
+  assertEntry(codeMap, 'lib2', 0x15500);
+  assertEntry(codeMap, 'lib2', 0x15520 - 1);
+  assertEntry(codeMap, 'lib2-f1', 0x15520);
+  assertEntry(codeMap, 'lib2-f1', 0x15520 + 0x80);
+  assertEntry(codeMap, 'lib2-f1', 0x15520 + 0x100 - 1);
+  assertEntry(codeMap, 'lib2', 0x15520 + 0x100);
+
+})();
+
+
+(function testDynamicCode() {
+  var codeMap = new devtools.profiler.CodeMap();
+  codeMap.addCode(0x1500, newCodeEntry(0x200, 'code1'));
+  codeMap.addCode(0x1700, newCodeEntry(0x100, 'code2'));
+  codeMap.addCode(0x1900, newCodeEntry(0x50, 'code3'));
+  codeMap.addCode(0x1950, newCodeEntry(0x10, 'code4'));
+  assertNoEntry(codeMap, 0);
+  assertNoEntry(codeMap, 0x1500 - 1);
+  assertEntry(codeMap, 'code1', 0x1500);
+  assertEntry(codeMap, 'code1', 0x1500 + 0x100);
+  assertEntry(codeMap, 'code1', 0x1500 + 0x200 - 1);
+  assertEntry(codeMap, 'code2', 0x1700);
+  assertEntry(codeMap, 'code2', 0x1700 + 0x50);
+  assertEntry(codeMap, 'code2', 0x1700 + 0x100 - 1);
+  assertNoEntry(codeMap, 0x1700 + 0x100);
+  assertNoEntry(codeMap, 0x1900 - 1);
+  assertEntry(codeMap, 'code3', 0x1900);
+  assertEntry(codeMap, 'code3', 0x1900 + 0x28);
+  assertEntry(codeMap, 'code4', 0x1950);
+  assertEntry(codeMap, 'code4', 0x1950 + 0x7);
+  assertEntry(codeMap, 'code4', 0x1950 + 0x10 - 1);
+  assertNoEntry(codeMap, 0x1950 + 0x10);
+  assertNoEntry(codeMap, 0xFFFFFFFF);
+})();
+
+
+(function testCodeMovesAndDeletions() {
+  var codeMap = new devtools.profiler.CodeMap();
+  codeMap.addCode(0x1500, newCodeEntry(0x200, 'code1'));
+  codeMap.addCode(0x1700, newCodeEntry(0x100, 'code2'));
+  assertEntry(codeMap, 'code1', 0x1500);
+  assertEntry(codeMap, 'code2', 0x1700);
+  codeMap.moveCode(0x1500, 0x1800);
+  assertNoEntry(codeMap, 0x1500);
+  assertEntry(codeMap, 'code2', 0x1700);
+  assertEntry(codeMap, 'code1', 0x1800);
+  codeMap.deleteCode(0x1700);
+  assertNoEntry(codeMap, 0x1700);
+  assertEntry(codeMap, 'code1', 0x1800);
+})();
+
+
+(function testDynamicNamesDuplicates() {
+  var codeMap = new devtools.profiler.CodeMap();
+  // Code entries with same names but different addresses.
+  codeMap.addCode(0x1500, newCodeEntry(0x200, 'code'));
+  codeMap.addCode(0x1700, newCodeEntry(0x100, 'code'));
+  assertEntry(codeMap, 'code', 0x1500);
+  assertEntry(codeMap, 'code {1}', 0x1700);
+  // Test name stability.
+  assertEntry(codeMap, 'code', 0x1500);
+  assertEntry(codeMap, 'code {1}', 0x1700);
+})();
+
+
+(function testStaticEntriesExport() {
+  var codeMap = new devtools.profiler.CodeMap();
+  codeMap.addStaticCode(0x1500, newCodeEntry(0x3000, 'lib1'));
+  codeMap.addStaticCode(0x15500, newCodeEntry(0x5000, 'lib2'));
+  codeMap.addStaticCode(0x155500, newCodeEntry(0x10000, 'lib3'));
+  var allStatics = codeMap.getAllStaticEntries();
+  allStatics.sort();
+  assertEquals(['lib1: 3000', 'lib2: 5000', 'lib3: 10000'], allStatics);
+})();
+
+
+(function testDynamicEntriesExport() {
+  var codeMap = new devtools.profiler.CodeMap();
+  codeMap.addCode(0x1500, newCodeEntry(0x200, 'code1'));
+  codeMap.addCode(0x1700, newCodeEntry(0x100, 'code2'));
+  codeMap.addCode(0x1900, newCodeEntry(0x50, 'code3'));
+  var allDynamics = codeMap.getAllDynamicEntries();
+  allDynamics.sort();
+  assertEquals(['code1: 200', 'code2: 100', 'code3: 50'], allDynamics);
+  codeMap.deleteCode(0x1700);
+  var allDynamics2 = codeMap.getAllDynamicEntries();
+  allDynamics2.sort();
+  assertEquals(['code1: 200', 'code3: 50'], allDynamics2);
+  codeMap.deleteCode(0x1500);
+  var allDynamics3 = codeMap.getAllDynamicEntries();
+  assertEquals(['code3: 50'], allDynamics3);
+})();
diff --git a/test/mjsunit/tools/consarray.js b/test/mjsunit/tools/consarray.js
new file mode 100644
index 0000000..8b2c59b
--- /dev/null
+++ b/test/mjsunit/tools/consarray.js
@@ -0,0 +1,60 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Load ConsArray implementation from <project root>/tools.
+// Files: tools/consarray.js
+
+
+var arr1 = new ConsArray();
+assertTrue(arr1.atEnd());
+
+arr1.concat([]);
+assertTrue(arr1.atEnd());
+
+arr1.concat([1]);
+assertFalse(arr1.atEnd());
+assertEquals(1, arr1.next());
+assertTrue(arr1.atEnd());
+
+arr1.concat([2, 3, 4]);
+arr1.concat([5]);
+arr1.concat([]);
+arr1.concat([6, 7]);
+
+assertFalse(arr1.atEnd());
+assertEquals(2, arr1.next());
+assertFalse(arr1.atEnd());
+assertEquals(3, arr1.next());
+assertFalse(arr1.atEnd());
+assertEquals(4, arr1.next());
+assertFalse(arr1.atEnd());
+assertEquals(5, arr1.next());
+assertFalse(arr1.atEnd());
+assertEquals(6, arr1.next());
+assertFalse(arr1.atEnd());
+assertEquals(7, arr1.next());
+assertTrue(arr1.atEnd());
diff --git a/test/mjsunit/tools/csvparser.js b/test/mjsunit/tools/csvparser.js
new file mode 100644
index 0000000..db3a2eb
--- /dev/null
+++ b/test/mjsunit/tools/csvparser.js
@@ -0,0 +1,79 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Load CSV parser implementation from <project root>/tools.
+// Files: tools/csvparser.js
+
+var parser = new devtools.profiler.CsvParser();
+
+assertEquals(
+    [],
+    parser.parseLine(''));
+
+assertEquals(
+    ['', ''],
+    parser.parseLine(','));
+
+assertEquals(
+    ['1997','Ford','E350'],
+    parser.parseLine('1997,Ford,E350'));
+
+assertEquals(
+    ['1997','Ford','E350'],
+    parser.parseLine('"1997","Ford","E350"'));
+
+assertEquals(
+    ['1997','Ford','E350','Super, luxurious truck'],
+    parser.parseLine('1997,Ford,E350,"Super, luxurious truck"'));
+
+assertEquals(
+    ['1997','Ford','E350','Super "luxurious" truck'],
+    parser.parseLine('1997,Ford,E350,"Super ""luxurious"" truck"'));
+
+assertEquals(
+    ['1997','Ford','E350','Super "luxurious" "truck"'],
+    parser.parseLine('1997,Ford,E350,"Super ""luxurious"" ""truck"""'));
+
+assertEquals(
+    ['1997','Ford','E350','Super "luxurious""truck"'],
+    parser.parseLine('1997,Ford,E350,"Super ""luxurious""""truck"""'));
+
+assertEquals(
+    ['shared-library','/lib/ld-2.3.6.so','0x489a2000','0x489b7000'],
+    parser.parseLine('shared-library,"/lib/ld-2.3.6.so",0x489a2000,0x489b7000'));
+
+assertEquals(
+    ['code-creation','LazyCompile','0xf6fe2d20','1201','APPLY_PREPARE native runtime.js:165'],
+    parser.parseLine('code-creation,LazyCompile,0xf6fe2d20,1201,"APPLY_PREPARE native runtime.js:165"'));
+
+assertEquals(
+    ['code-creation','LazyCompile','0xf6fe4bc0','282',' native v8natives.js:69'],
+    parser.parseLine('code-creation,LazyCompile,0xf6fe4bc0,282," native v8natives.js:69"'));
+
+assertEquals(
+    ['code-creation','RegExp','0xf6c21c00','826','NccyrJroXvg\\/([^,]*)'],
+    parser.parseLine('code-creation,RegExp,0xf6c21c00,826,"NccyrJroXvg\\/([^,]*)"'));
diff --git a/test/mjsunit/tools/logreader.js b/test/mjsunit/tools/logreader.js
new file mode 100644
index 0000000..8ed5ffd
--- /dev/null
+++ b/test/mjsunit/tools/logreader.js
@@ -0,0 +1,98 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Load CSV Parser and Log Reader implementations from <project root>/tools.
+// Files: tools/csvparser.js tools/logreader.js
+
+
+(function testAddressParser() {
+  var reader = new devtools.profiler.LogReader({});
+  var parser = reader.createAddressParser('test');
+
+  // Test that 0x values are parsed, and prevAddresses_ are untouched.
+  assertFalse('test' in reader.prevAddresses_);
+  assertEquals(0, parser('0x0'));
+  assertFalse('test' in reader.prevAddresses_);
+  assertEquals(0x100, parser('0x100'));
+  assertFalse('test' in reader.prevAddresses_);
+  assertEquals(0xffffffff, parser('0xffffffff'));
+  assertFalse('test' in reader.prevAddresses_);
+
+  // Test that values that has no '+' or '-' prefix are parsed
+  // and saved to prevAddresses_.
+  assertEquals(0, parser('0'));
+  assertEquals(0, reader.prevAddresses_.test);
+  assertEquals(0x100, parser('100'));
+  assertEquals(0x100, reader.prevAddresses_.test);
+  assertEquals(0xffffffff, parser('ffffffff'));
+  assertEquals(0xffffffff, reader.prevAddresses_.test);
+
+  // Test that values prefixed with '+' or '-' are treated as deltas,
+  // and prevAddresses_ is updated.
+  // Set base value.
+  assertEquals(0x100, parser('100'));
+  assertEquals(0x100, reader.prevAddresses_.test);
+  assertEquals(0x200, parser('+100'));
+  assertEquals(0x200, reader.prevAddresses_.test);
+  assertEquals(0x100, parser('-100'));
+  assertEquals(0x100, reader.prevAddresses_.test);
+})();
+
+
+(function testAddressParser() {
+  var reader = new devtools.profiler.LogReader({});
+
+  assertEquals([0x10000000, 0x10001000, 0xffff000, 0x10000000],
+               reader.processStack(0x10000000, ['overflow',
+                   '+1000', '-2000', '+1000']));
+})();
+
+
+(function testExpandBackRef() {
+  var reader = new devtools.profiler.LogReader({});
+
+  assertEquals('aaaaaaaa', reader.expandBackRef_('aaaaaaaa'));
+  assertEquals('aaaaaaaa', reader.expandBackRef_('#1'));
+  assertEquals('bbbbaaaa', reader.expandBackRef_('bbbb#2:4'));
+  assertEquals('"#1:1"', reader.expandBackRef_('"#1:1"'));
+})();
+
+
+// See http://code.google.com/p/v8/issues/detail?id=420
+(function testReadingTruncatedLog() {
+  // Having an incorrect event in the middle of a log should throw an exception.
+  var reader1 = new devtools.profiler.LogReader({});
+  assertThrows(function() {
+    reader1.processLogChunk('alias,a,b\nxxxx\nalias,c,d\n');
+  });
+
+  // But having it as the last record should not.
+  var reader2 = new devtools.profiler.LogReader({});
+  assertDoesNotThrow(function() {
+    reader2.processLogChunk('alias,a,b\nalias,c,d\nxxxx');
+  });
+})();
diff --git a/test/mjsunit/tools/profile.js b/test/mjsunit/tools/profile.js
new file mode 100644
index 0000000..9ed851b
--- /dev/null
+++ b/test/mjsunit/tools/profile.js
@@ -0,0 +1,348 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Load source code files from <project root>/tools.
+// Files: tools/splaytree.js tools/codemap.js tools/consarray.js tools/profile.js
+
+
+function stackToString(stack) {
+  return stack.join(' -> ');
+};
+
+
+function assertPathExists(root, path, opt_message) {
+  var message = opt_message ? ' (' + opt_message + ')' : '';
+  assertNotNull(root.descendToChild(path, function(node, pos) {
+    assertNotNull(node,
+      stackToString(path.slice(0, pos)) + ' has no child ' +
+                    path[pos] + message);
+  }), opt_message);
+};
+
+
+function assertNoPathExists(root, path, opt_message) {
+  var message = opt_message ? ' (' + opt_message + ')' : '';
+  assertNull(root.descendToChild(path), opt_message);
+};
+
+
+function countNodes(profile, traverseFunc) {
+  var count = 0;
+  traverseFunc.call(profile, function () { count++; });
+  return count;
+};
+
+
+function ProfileTestDriver() {
+  this.profile = new devtools.profiler.Profile();
+  this.stack_ = [];
+  this.addFunctions_();
+};
+
+
+// Addresses inside functions.
+ProfileTestDriver.prototype.funcAddrs_ = {
+    'lib1-f1': 0x11110, 'lib1-f2': 0x11210,
+    'lib2-f1': 0x21110, 'lib2-f2': 0x21210,
+    'T: F1': 0x50110, 'T: F2': 0x50210, 'T: F3': 0x50410 };
+
+
+ProfileTestDriver.prototype.addFunctions_ = function() {
+  this.profile.addLibrary('lib1', 0x11000, 0x12000);
+  this.profile.addStaticCode('lib1-f1', 0x11100, 0x11900);
+  this.profile.addStaticCode('lib1-f2', 0x11200, 0x11500);
+  this.profile.addLibrary('lib2', 0x21000, 0x22000);
+  this.profile.addStaticCode('lib2-f1', 0x21100, 0x21900);
+  this.profile.addStaticCode('lib2-f2', 0x21200, 0x21500);
+  this.profile.addCode('T', 'F1', 0x50100, 0x100);
+  this.profile.addCode('T', 'F2', 0x50200, 0x100);
+  this.profile.addCode('T', 'F3', 0x50400, 0x100);
+};
+
+
+ProfileTestDriver.prototype.enter = function(funcName) {
+  // Stack looks like this: [pc, caller, ..., main].
+  // Therefore, we are adding entries at the beginning.
+  this.stack_.unshift(this.funcAddrs_[funcName]);
+  this.profile.recordTick(this.stack_);
+};
+
+
+ProfileTestDriver.prototype.stay = function() {
+  this.profile.recordTick(this.stack_);
+};
+
+
+ProfileTestDriver.prototype.leave = function() {
+  this.stack_.shift();
+};
+
+
+ProfileTestDriver.prototype.execute = function() {
+  this.enter('lib1-f1');
+    this.enter('lib1-f2');
+      this.enter('T: F1');
+        this.enter('T: F2');
+        this.leave();
+      this.stay();
+        this.enter('lib2-f1');
+          this.enter('lib2-f1');
+          this.leave();
+        this.stay();
+        this.leave();
+        this.enter('T: F3');
+          this.enter('T: F3');
+            this.enter('T: F3');
+            this.leave();
+            this.enter('T: F2');
+            this.stay();
+            this.leave();
+          this.leave();
+        this.leave();
+      this.leave();
+      this.enter('lib2-f1');
+        this.enter('lib1-f1');
+        this.leave();
+      this.leave();
+    this.stay();
+  this.leave();
+};
+
+
+function Inherits(childCtor, parentCtor) {
+  function tempCtor() {};
+  tempCtor.prototype = parentCtor.prototype;
+  childCtor.superClass_ = parentCtor.prototype;
+  childCtor.prototype = new tempCtor();
+  childCtor.prototype.constructor = childCtor;
+};
+
+
+(function testCallTreeBuilding() {
+  function Driver() {
+    ProfileTestDriver.call(this);
+    this.namesTopDown = [];
+    this.namesBottomUp = [];
+  };
+  Inherits(Driver, ProfileTestDriver);
+
+  Driver.prototype.enter = function(func) {
+    this.namesTopDown.push(func);
+    this.namesBottomUp.unshift(func);
+    assertNoPathExists(this.profile.getTopDownProfile().getRoot(), this.namesTopDown,
+        'pre enter/topDown');
+    assertNoPathExists(this.profile.getBottomUpProfile().getRoot(), this.namesBottomUp,
+        'pre enter/bottomUp');
+    Driver.superClass_.enter.call(this, func);
+    assertPathExists(this.profile.getTopDownProfile().getRoot(), this.namesTopDown,
+        'post enter/topDown');
+    assertPathExists(this.profile.getBottomUpProfile().getRoot(), this.namesBottomUp,
+        'post enter/bottomUp');
+  };
+
+  Driver.prototype.stay = function() {
+    var preTopDownNodes = countNodes(this.profile, this.profile.traverseTopDownTree);
+    var preBottomUpNodes = countNodes(this.profile, this.profile.traverseBottomUpTree);
+    Driver.superClass_.stay.call(this);
+    var postTopDownNodes = countNodes(this.profile, this.profile.traverseTopDownTree);
+    var postBottomUpNodes = countNodes(this.profile, this.profile.traverseBottomUpTree);
+    // Must be no changes in tree layout.
+    assertEquals(preTopDownNodes, postTopDownNodes, 'stay/topDown');
+    assertEquals(preBottomUpNodes, postBottomUpNodes, 'stay/bottomUp');
+  };
+
+  Driver.prototype.leave = function() {
+    Driver.superClass_.leave.call(this);
+    this.namesTopDown.pop();
+    this.namesBottomUp.shift();
+  };
+
+  var testDriver = new Driver();
+  testDriver.execute();
+})();
+
+
+function assertNodeWeights(root, path, selfTicks, totalTicks) {
+  var node = root.descendToChild(path);
+  var stack = stackToString(path);
+  assertNotNull(node, 'node not found: ' + stack);
+  assertEquals(selfTicks, node.selfWeight, 'self of ' + stack);
+  assertEquals(totalTicks, node.totalWeight, 'total of ' + stack);
+};
+
+
+(function testTopDownRootProfileTicks() {
+  var testDriver = new ProfileTestDriver();
+  testDriver.execute();
+
+  var pathWeights = [
+    [['lib1-f1'], 1, 16],
+    [['lib1-f1', 'lib1-f2'], 2, 15],
+    [['lib1-f1', 'lib1-f2', 'T: F1'], 2, 11],
+    [['lib1-f1', 'lib1-f2', 'T: F1', 'T: F2'], 1, 1],
+    [['lib1-f1', 'lib1-f2', 'T: F1', 'lib2-f1'], 2, 3],
+    [['lib1-f1', 'lib1-f2', 'T: F1', 'lib2-f1', 'lib2-f1'], 1, 1],
+    [['lib1-f1', 'lib1-f2', 'T: F1', 'T: F3'], 1, 5],
+    [['lib1-f1', 'lib1-f2', 'T: F1', 'T: F3', 'T: F3'], 1, 4],
+    [['lib1-f1', 'lib1-f2', 'T: F1', 'T: F3', 'T: F3', 'T: F3'], 1, 1],
+    [['lib1-f1', 'lib1-f2', 'T: F1', 'T: F3', 'T: F3', 'T: F2'], 2, 2],
+    [['lib1-f1', 'lib1-f2', 'lib2-f1'], 1, 2],
+    [['lib1-f1', 'lib1-f2', 'lib2-f1', 'lib1-f1'], 1, 1]
+  ];
+
+  var root = testDriver.profile.getTopDownProfile().getRoot();
+  for (var i = 0; i < pathWeights.length; ++i) {
+    var data = pathWeights[i];
+    assertNodeWeights(root, data[0], data[1], data[2]);
+  }
+})();
+
+
+(function testRootFlatProfileTicks() {
+  function Driver() {
+    ProfileTestDriver.call(this);
+    this.namesTopDown = [''];
+    this.counters = {};
+    this.root = null;
+  };
+  Inherits(Driver, ProfileTestDriver);
+
+  Driver.prototype.increment = function(func, self, total) {
+    if (!(func in this.counters)) {
+      this.counters[func] = { self: 0, total: 0 };
+    }
+    this.counters[func].self += self;
+    this.counters[func].total += total;
+  };
+
+  Driver.prototype.incrementTotals = function() {
+    // Only count each function in the stack once.
+    var met = {};
+    for (var i = 0; i < this.namesTopDown.length; ++i) {
+      var name = this.namesTopDown[i];
+      if (!(name in met)) {
+        this.increment(name, 0, 1);
+      }
+      met[name] = true;
+    }
+  };
+
+  Driver.prototype.enter = function(func) {
+    Driver.superClass_.enter.call(this, func);
+    this.namesTopDown.push(func);
+    this.increment(func, 1, 0);
+    this.incrementTotals();
+  };
+
+  Driver.prototype.stay = function() {
+    Driver.superClass_.stay.call(this);
+    this.increment(this.namesTopDown[this.namesTopDown.length - 1], 1, 0);
+    this.incrementTotals();
+  };
+
+  Driver.prototype.leave = function() {
+    Driver.superClass_.leave.call(this);
+    this.namesTopDown.pop();
+  };
+
+  Driver.prototype.extractRoot = function() {
+    assertTrue('' in this.counters);
+    this.root = this.counters[''];
+    delete this.counters[''];
+  };
+
+  var testDriver = new Driver();
+  testDriver.execute();
+  testDriver.extractRoot();
+
+  var counted = 0;
+  for (var c in testDriver.counters) {
+    counted++;
+  }
+
+  var flatProfileRoot = testDriver.profile.getFlatProfile().getRoot();
+  assertEquals(testDriver.root.self, flatProfileRoot.selfWeight);
+  assertEquals(testDriver.root.total, flatProfileRoot.totalWeight);
+
+  var flatProfile = flatProfileRoot.exportChildren();
+  assertEquals(counted, flatProfile.length, 'counted vs. flatProfile');
+  for (var i = 0; i < flatProfile.length; ++i) {
+    var rec = flatProfile[i];
+    assertTrue(rec.label in testDriver.counters, 'uncounted: ' + rec.label);
+    var reference = testDriver.counters[rec.label];
+    assertEquals(reference.self, rec.selfWeight, 'self of ' + rec.label);
+    assertEquals(reference.total, rec.totalWeight, 'total of ' + rec.label);
+  }
+
+})();
+
+
+(function testFunctionCalleesProfileTicks() {
+  var testDriver = new ProfileTestDriver();
+  testDriver.execute();
+
+  var pathWeights = [
+    [['lib2-f1'], 3, 5],
+    [['lib2-f1', 'lib2-f1'], 1, 1],
+    [['lib2-f1', 'lib1-f1'], 1, 1]
+  ];
+
+  var profile = testDriver.profile.getTopDownProfile('lib2-f1');
+  var root = profile.getRoot();
+  for (var i = 0; i < pathWeights.length; ++i) {
+    var data = pathWeights[i];
+    assertNodeWeights(root, data[0], data[1], data[2]);
+  }
+})();
+
+
+(function testFunctionFlatProfileTicks() {
+  var testDriver = new ProfileTestDriver();
+  testDriver.execute();
+
+  var flatWeights = {
+    'lib2-f1': [1, 1],
+    'lib1-f1': [1, 1]
+  };
+
+  var flatProfileRoot =
+     testDriver.profile.getFlatProfile('lib2-f1').findOrAddChild('lib2-f1');
+  assertEquals(3, flatProfileRoot.selfWeight);
+  assertEquals(5, flatProfileRoot.totalWeight);
+
+  var flatProfile = flatProfileRoot.exportChildren();
+  assertEquals(2, flatProfile.length, 'counted vs. flatProfile');
+  for (var i = 0; i < flatProfile.length; ++i) {
+    var rec = flatProfile[i];
+    assertTrue(rec.label in flatWeights, 'uncounted: ' + rec.label);
+    var reference = flatWeights[rec.label];
+    assertEquals(reference[0], rec.selfWeight, 'self of ' + rec.label);
+    assertEquals(reference[1], rec.totalWeight, 'total of ' + rec.label);
+  }
+
+})();
+
diff --git a/test/mjsunit/tools/profile_view.js b/test/mjsunit/tools/profile_view.js
new file mode 100644
index 0000000..3ed1128
--- /dev/null
+++ b/test/mjsunit/tools/profile_view.js
@@ -0,0 +1,95 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Load source code files from <project root>/tools.
+// Files: tools/consarray.js tools/profile.js tools/profile_view.js
+
+
+function createNode(name, time, opt_parent) {
+  var node = new devtools.profiler.ProfileView.Node(name, time, time, null);
+  if (opt_parent) {
+    opt_parent.addChild(node);
+  }
+  return node;
+}
+
+
+(function testSorting() {
+   //
+   // Build a tree:
+   //   root             +--c/5
+   //    |               |
+   //    +--a/2  +--b/3--+--d/4
+   //    |       |       |
+   //    +--a/1--+--c/1  +--d/2
+   //    |       |
+   //    +--c/1  +--b/2
+   //
+   // So we can sort it using 2 fields: name and time.
+   var root = createNode('root', 0);
+   createNode('a', 2, root);
+   var a1 = createNode('a', 1, root);
+   createNode('c', 1, root);
+   var b3 = createNode('b', 3, a1);
+   createNode('c', 1, a1);
+   createNode('b', 2, a1);
+   createNode('c', 5, b3);
+   createNode('d', 4, b3);
+   createNode('d', 2, b3);
+
+   var view = new devtools.profiler.ProfileView(root);
+   var flatTree = [];
+
+   function fillFlatTree(node) {
+     flatTree.push(node.internalFuncName);
+     flatTree.push(node.selfTime);
+   }
+
+   view.traverse(fillFlatTree);
+   assertEquals(
+     ['root', 0,
+      'a', 2, 'a', 1, 'c', 1,
+      'b', 3, 'c', 1, 'b', 2,
+      'c', 5, 'd', 4, 'd', 2], flatTree);
+
+   function cmpStrs(s1, s2) {
+     return s1 == s2 ? 0 : (s1 < s2 ? -1 : 1);
+   }
+
+   view.sort(function(n1, n2) {
+     return cmpStrs(n1.internalFuncName, n2.internalFuncName) ||
+         (n1.selfTime - n2.selfTime);
+   });
+
+   flatTree = [];
+   view.traverse(fillFlatTree);
+   assertEquals(
+     ['root', 0,
+      'a', 1, 'a', 2, 'c', 1,
+      'b', 2, 'b', 3, 'c', 1,
+      'c', 5, 'd', 2, 'd', 4], flatTree);
+})();
diff --git a/test/mjsunit/tools/splaytree.js b/test/mjsunit/tools/splaytree.js
new file mode 100644
index 0000000..3beba0b
--- /dev/null
+++ b/test/mjsunit/tools/splaytree.js
@@ -0,0 +1,166 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Load the Splay tree implementation from <project root>/tools.
+// Files: tools/splaytree.js
+
+
+(function testIsEmpty() {
+  var tree = new goog.structs.SplayTree();
+  assertTrue(tree.isEmpty());
+  tree.insert(0, 'value');
+  assertFalse(tree.isEmpty());
+})();
+
+
+(function testExportValues() {
+  var tree = new goog.structs.SplayTree();
+  assertArrayEquals([], tree.exportValues());
+  tree.insert(0, 'value');
+  assertArrayEquals(['value'], tree.exportValues());
+  tree.insert(0, 'value');
+  assertArrayEquals(['value'], tree.exportValues());
+})();
+
+
+function createSampleTree() {
+  // Creates the following tree:
+  //           50
+  //          /  \
+  //         30  60
+  //        /  \   \
+  //       10  40  90
+  //         \    /  \
+  //         20  70 100
+  //        /      \
+  //       15      80
+  //
+  // We can't use the 'insert' method because it also uses 'splay_'.
+  return { key: 50, value: 50,
+      left: { key: 30, value: 30,
+              left: { key: 10, value: 10, left: null,
+                      right: { key: 20, value: 20,
+                               left: { key: 15, value: 15,
+                                       left: null, right: null },
+                               right: null } },
+              right: { key: 40, value: 40, left: null, right: null } },
+      right: { key: 60, value: 60, left: null,
+               right: { key: 90, value: 90,
+                        left: { key: 70, value: 70, left: null,
+                                right: { key: 80, value: 80,
+                                         left: null, right: null } },
+                        right: { key: 100, value: 100,
+                                 left: null, right: null } } } };
+};
+
+
+(function testSplay() {
+  var tree = new goog.structs.SplayTree();
+  tree.root_ = createSampleTree();
+  assertArrayEquals(['50', '30', '60', '10', '40', '90', '20', '70', '100', '15', '80'],
+                    tree.exportValues());
+  tree.splay_(50);
+  assertArrayEquals(['50', '30', '60', '10', '40', '90', '20', '70', '100', '15', '80'],
+                    tree.exportValues());
+  tree.splay_(80);
+  assertArrayEquals(['80', '60', '90', '50', '70', '100', '30', '10', '40', '20', '15'],
+                    tree.exportValues());
+})();
+
+
+(function testInsert() {
+  var tree = new goog.structs.SplayTree();
+  tree.insert(5, 'root');
+  tree.insert(3, 'left');
+  assertArrayEquals(['left', 'root'], tree.exportValues());
+  tree.insert(7, 'right');
+  assertArrayEquals(['right', 'root', 'left'], tree.exportValues());
+})();
+
+
+(function testFind() {
+  var tree = new goog.structs.SplayTree();
+  tree.insert(5, 'root');
+  tree.insert(3, 'left');
+  tree.insert(7, 'right');
+  assertEquals('root', tree.find(5).value);
+  assertEquals('left', tree.find(3).value);
+  assertEquals('right', tree.find(7).value);
+  assertEquals(null, tree.find(0));
+  assertEquals(null, tree.find(100));
+  assertEquals(null, tree.find(-100));
+})();
+
+
+(function testFindMin() {
+  var tree = new goog.structs.SplayTree();
+  assertEquals(null, tree.findMin());
+  tree.insert(5, 'root');
+  tree.insert(3, 'left');
+  tree.insert(7, 'right');
+  assertEquals('left', tree.findMin().value);
+})();
+
+
+(function testFindMax() {
+  var tree = new goog.structs.SplayTree();
+  assertEquals(null, tree.findMax());
+  tree.insert(5, 'root');
+  tree.insert(3, 'left');
+  tree.insert(7, 'right');
+  assertEquals('right', tree.findMax().value);
+})();
+
+
+(function testFindGreatestLessThan() {
+  var tree = new goog.structs.SplayTree();
+  assertEquals(null, tree.findGreatestLessThan(10));
+  tree.insert(5, 'root');
+  tree.insert(3, 'left');
+  tree.insert(7, 'right');
+  assertEquals('right', tree.findGreatestLessThan(10).value);
+  assertEquals('right', tree.findGreatestLessThan(7).value);
+  assertEquals('root', tree.findGreatestLessThan(6).value);
+  assertEquals('left', tree.findGreatestLessThan(4).value);
+  assertEquals(null, tree.findGreatestLessThan(2));
+})();
+
+
+(function testRemove() {
+  var tree = new goog.structs.SplayTree();
+  assertThrows('tree.remove(5)');
+  tree.insert(5, 'root');
+  tree.insert(3, 'left');
+  tree.insert(7, 'right');
+  assertThrows('tree.remove(1)');
+  assertThrows('tree.remove(6)');
+  assertThrows('tree.remove(10)');
+  assertEquals('root', tree.remove(5).value);
+  assertEquals('left', tree.remove(3).value);
+  assertEquals('right', tree.remove(7).value);
+  assertArrayEquals([], tree.exportValues());
+})();
diff --git a/test/mjsunit/tools/tickprocessor-test.default b/test/mjsunit/tools/tickprocessor-test.default
new file mode 100644
index 0000000..702f4bc
--- /dev/null
+++ b/test/mjsunit/tools/tickprocessor-test.default
@@ -0,0 +1,55 @@
+Statistical profiling result from v8.log, (13 ticks, 2 unaccounted, 0 excluded).
+
+ [Unknown]:
+   ticks  total  nonlib   name
+      2   15.4%
+
+ [Shared libraries]:
+   ticks  total  nonlib   name
+      3   23.1%    0.0%  /lib32/libm-2.7.so
+      1    7.7%    0.0%  ffffe000-fffff000
+
+ [JavaScript]:
+   ticks  total  nonlib   name
+      1    7.7%   11.1%  LazyCompile: exp native math.js:41
+
+ [C++]:
+   ticks  total  nonlib   name
+      2   15.4%   22.2%  v8::internal::Runtime_Math_exp(v8::internal::Arguments)
+      1    7.7%   11.1%  v8::internal::JSObject::LocalLookupRealNamedProperty(v8::internal::String*, v8::internal::LookupResult*)
+      1    7.7%   11.1%  v8::internal::HashTable<v8::internal::StringDictionaryShape, v8::internal::String*>::FindEntry(v8::internal::String*)
+      1    7.7%   11.1%  exp
+
+ [GC]:
+   ticks  total  nonlib   name
+      0    0.0%
+
+ [Bottom up (heavy) profile]:
+  Note: percentage shows a share of a particular caller in the total
+  amount of its parent calls.
+  Callers occupying less than 2.0% are not shown.
+
+   ticks parent  name
+      3   23.1%  /lib32/libm-2.7.so
+      3  100.0%    LazyCompile: exp native math.js:41
+      3  100.0%      Script: exp.js
+
+      2   15.4%  v8::internal::Runtime_Math_exp(v8::internal::Arguments)
+      2  100.0%    LazyCompile: exp native math.js:41
+      2  100.0%      Script: exp.js
+
+      1    7.7%  v8::internal::JSObject::LocalLookupRealNamedProperty(v8::internal::String*, v8::internal::LookupResult*)
+      1  100.0%    Script: exp.js
+
+      1    7.7%  v8::internal::HashTable<v8::internal::StringDictionaryShape, v8::internal::String*>::FindEntry(v8::internal::String*)
+      1  100.0%    Script: exp.js
+
+      1    7.7%  ffffe000-fffff000
+
+      1    7.7%  exp
+      1  100.0%    LazyCompile: exp native math.js:41
+      1  100.0%      Script: exp.js
+
+      1    7.7%  LazyCompile: exp native math.js:41
+      1  100.0%    Script: exp.js
+
diff --git a/test/mjsunit/tools/tickprocessor-test.gc-state b/test/mjsunit/tools/tickprocessor-test.gc-state
new file mode 100644
index 0000000..40f90db
--- /dev/null
+++ b/test/mjsunit/tools/tickprocessor-test.gc-state
@@ -0,0 +1,21 @@
+Statistical profiling result from v8.log, (13 ticks, 0 unaccounted, 13 excluded).
+
+ [Shared libraries]:
+   ticks  total  nonlib   name
+
+ [JavaScript]:
+   ticks  total  nonlib   name
+
+ [C++]:
+   ticks  total  nonlib   name
+
+ [GC]:
+   ticks  total  nonlib   name
+      0    0.0%
+
+ [Bottom up (heavy) profile]:
+  Note: percentage shows a share of a particular caller in the total
+  amount of its parent calls.
+  Callers occupying less than 2.0% are not shown.
+
+   ticks parent  name
diff --git a/test/mjsunit/tools/tickprocessor-test.ignore-unknown b/test/mjsunit/tools/tickprocessor-test.ignore-unknown
new file mode 100644
index 0000000..306d646
--- /dev/null
+++ b/test/mjsunit/tools/tickprocessor-test.ignore-unknown
@@ -0,0 +1,51 @@
+Statistical profiling result from v8.log, (13 ticks, 2 unaccounted, 0 excluded).
+
+ [Shared libraries]:
+   ticks  total  nonlib   name
+      3   27.3%    0.0%  /lib32/libm-2.7.so
+      1    9.1%    0.0%  ffffe000-fffff000
+
+ [JavaScript]:
+   ticks  total  nonlib   name
+      1    9.1%   14.3%  LazyCompile: exp native math.js:41
+
+ [C++]:
+   ticks  total  nonlib   name
+      2   18.2%   28.6%  v8::internal::Runtime_Math_exp(v8::internal::Arguments)
+      1    9.1%   14.3%  v8::internal::JSObject::LocalLookupRealNamedProperty(v8::internal::String*, v8::internal::LookupResult*)
+      1    9.1%   14.3%  v8::internal::HashTable<v8::internal::StringDictionaryShape, v8::internal::String*>::FindEntry(v8::internal::String*)
+      1    9.1%   14.3%  exp
+
+ [GC]:
+   ticks  total  nonlib   name
+      0    0.0%
+
+ [Bottom up (heavy) profile]:
+  Note: percentage shows a share of a particular caller in the total
+  amount of its parent calls.
+  Callers occupying less than 2.0% are not shown.
+
+   ticks parent  name
+      3   27.3%  /lib32/libm-2.7.so
+      3  100.0%    LazyCompile: exp native math.js:41
+      3  100.0%      Script: exp.js
+
+      2   18.2%  v8::internal::Runtime_Math_exp(v8::internal::Arguments)
+      2  100.0%    LazyCompile: exp native math.js:41
+      2  100.0%      Script: exp.js
+
+      1    9.1%  v8::internal::JSObject::LocalLookupRealNamedProperty(v8::internal::String*, v8::internal::LookupResult*)
+      1  100.0%    Script: exp.js
+
+      1    9.1%  v8::internal::HashTable<v8::internal::StringDictionaryShape, v8::internal::String*>::FindEntry(v8::internal::String*)
+      1  100.0%    Script: exp.js
+
+      1    9.1%  ffffe000-fffff000
+
+      1    9.1%  exp
+      1  100.0%    LazyCompile: exp native math.js:41
+      1  100.0%      Script: exp.js
+
+      1    9.1%  LazyCompile: exp native math.js:41
+      1  100.0%    Script: exp.js
+
diff --git a/test/mjsunit/tools/tickprocessor-test.log b/test/mjsunit/tools/tickprocessor-test.log
new file mode 100644
index 0000000..75daad6
--- /dev/null
+++ b/test/mjsunit/tools/tickprocessor-test.log
@@ -0,0 +1,24 @@
+shared-library,"shell",0x08048000,0x081ee000
+shared-library,"/lib32/libm-2.7.so",0xf7db6000,0xf7dd9000
+shared-library,"ffffe000-fffff000",0xffffe000,0xfffff000
+profiler,"begin",1
+code-creation,Stub,0xf540a100,474,"CEntryStub"
+code-creation,Script,0xf541cd80,736,"exp.js"
+code-creation,Stub,0xf541d0e0,47,"RuntimeStub_Math_exp"
+code-creation,LazyCompile,0xf541d120,145,"exp native math.js:41"
+code-creation,LoadIC,0xf541d280,117,"j"
+code-creation,LoadIC,0xf541d360,63,"i"
+tick,0x80f82d1,0xffdfe880,0,0xf541ce5c
+tick,0x80f89a1,0xffdfecf0,0,0xf541ce5c
+tick,0x8123b5c,0xffdff1a0,0,0xf541d1a1,0xf541ceea
+tick,0x8123b65,0xffdff1a0,0,0xf541d1a1,0xf541ceea
+tick,0xf541d2be,0xffdff1e4,0
+tick,0xf541d320,0xffdff1dc,0
+tick,0xf541d384,0xffdff1d8,0
+tick,0xf7db94da,0xffdff0ec,0,0xf541d1a1,0xf541ceea
+tick,0xf7db951c,0xffdff0f0,0,0xf541d1a1,0xf541ceea
+tick,0xf7dbc508,0xffdff14c,0,0xf541d1a1,0xf541ceea
+tick,0xf7dbff21,0xffdff198,0,0xf541d1a1,0xf541ceea
+tick,0xf7edec90,0xffdff0ec,0,0xf541d1a1,0xf541ceea
+tick,0xffffe402,0xffdff488,0
+profiler,"end"
diff --git a/test/mjsunit/tools/tickprocessor-test.separate-ic b/test/mjsunit/tools/tickprocessor-test.separate-ic
new file mode 100644
index 0000000..3a2041b
--- /dev/null
+++ b/test/mjsunit/tools/tickprocessor-test.separate-ic
@@ -0,0 +1,61 @@
+Statistical profiling result from v8.log, (13 ticks, 2 unaccounted, 0 excluded).
+
+ [Unknown]:
+   ticks  total  nonlib   name
+      2   15.4%
+
+ [Shared libraries]:
+   ticks  total  nonlib   name
+      3   23.1%    0.0%  /lib32/libm-2.7.so
+      1    7.7%    0.0%  ffffe000-fffff000
+
+ [JavaScript]:
+   ticks  total  nonlib   name
+      1    7.7%   11.1%  LoadIC: j
+      1    7.7%   11.1%  LoadIC: i
+      1    7.7%   11.1%  LazyCompile: exp native math.js:41
+
+ [C++]:
+   ticks  total  nonlib   name
+      2   15.4%   22.2%  v8::internal::Runtime_Math_exp(v8::internal::Arguments)
+      1    7.7%   11.1%  v8::internal::JSObject::LocalLookupRealNamedProperty(v8::internal::String*, v8::internal::LookupResult*)
+      1    7.7%   11.1%  v8::internal::HashTable<v8::internal::StringDictionaryShape, v8::internal::String*>::FindEntry(v8::internal::String*)
+      1    7.7%   11.1%  exp
+
+ [GC]:
+   ticks  total  nonlib   name
+      0    0.0%
+
+ [Bottom up (heavy) profile]:
+  Note: percentage shows a share of a particular caller in the total
+  amount of its parent calls.
+  Callers occupying less than 2.0% are not shown.
+
+   ticks parent  name
+      3   23.1%  /lib32/libm-2.7.so
+      3  100.0%    LazyCompile: exp native math.js:41
+      3  100.0%      Script: exp.js
+
+      2   15.4%  v8::internal::Runtime_Math_exp(v8::internal::Arguments)
+      2  100.0%    LazyCompile: exp native math.js:41
+      2  100.0%      Script: exp.js
+
+      1    7.7%  v8::internal::JSObject::LocalLookupRealNamedProperty(v8::internal::String*, v8::internal::LookupResult*)
+      1  100.0%    Script: exp.js
+
+      1    7.7%  v8::internal::HashTable<v8::internal::StringDictionaryShape, v8::internal::String*>::FindEntry(v8::internal::String*)
+      1  100.0%    Script: exp.js
+
+      1    7.7%  ffffe000-fffff000
+
+      1    7.7%  exp
+      1  100.0%    LazyCompile: exp native math.js:41
+      1  100.0%      Script: exp.js
+
+      1    7.7%  LoadIC: j
+
+      1    7.7%  LoadIC: i
+
+      1    7.7%  LazyCompile: exp native math.js:41
+      1  100.0%    Script: exp.js
+
diff --git a/test/mjsunit/tools/tickprocessor.js b/test/mjsunit/tools/tickprocessor.js
new file mode 100644
index 0000000..83bdac8
--- /dev/null
+++ b/test/mjsunit/tools/tickprocessor.js
@@ -0,0 +1,409 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Load implementations from <project root>/tools.
+// Files: tools/splaytree.js tools/codemap.js tools/csvparser.js
+// Files: tools/consarray.js tools/profile.js tools/profile_view.js
+// Files: tools/logreader.js tools/tickprocessor.js
+// Env: TEST_FILE_NAME
+
+
+(function testArgumentsProcessor() {
+  var p_default = new ArgumentsProcessor([]);
+  assertTrue(p_default.parse());
+  assertEquals(ArgumentsProcessor.DEFAULTS, p_default.result());
+
+  var p_logFile = new ArgumentsProcessor(['logfile.log']);
+  assertTrue(p_logFile.parse());
+  assertEquals('logfile.log', p_logFile.result().logFileName);
+
+  var p_platformAndLog = new ArgumentsProcessor(['--windows', 'winlog.log']);
+  assertTrue(p_platformAndLog.parse());
+  assertEquals('windows', p_platformAndLog.result().platform);
+  assertEquals('winlog.log', p_platformAndLog.result().logFileName);
+
+  var p_flags = new ArgumentsProcessor(['--gc', '--separate-ic']);
+  assertTrue(p_flags.parse());
+  assertEquals(TickProcessor.VmStates.GC, p_flags.result().stateFilter);
+  assertTrue(p_flags.result().separateIc);
+
+  var p_nmAndLog = new ArgumentsProcessor(['--nm=mn', 'nmlog.log']);
+  assertTrue(p_nmAndLog.parse());
+  assertEquals('mn', p_nmAndLog.result().nm);
+  assertEquals('nmlog.log', p_nmAndLog.result().logFileName);
+
+  var p_bad = new ArgumentsProcessor(['--unknown', 'badlog.log']);
+  assertFalse(p_bad.parse());
+})();
+
+
+(function testUnixCppEntriesProvider() {
+  var oldLoadSymbols = UnixCppEntriesProvider.prototype.loadSymbols;
+
+  // shell executable
+  UnixCppEntriesProvider.prototype.loadSymbols = function(libName) {
+    this.symbols = [[
+      '         U operator delete[](void*)@@GLIBCXX_3.4',
+      '08049790 T _init',
+      '08049f50 T _start',
+      '08139150 00000b4b t v8::internal::Runtime_StringReplaceRegExpWithString(v8::internal::Arguments)',
+      '08139ca0 000003f1 T v8::internal::Runtime::GetElementOrCharAt(v8::internal::Handle<v8::internal::Object>, unsigned int)',
+      '0813a0b0 00000855 t v8::internal::Runtime_DebugGetPropertyDetails(v8::internal::Arguments)',
+      '0818b220 00000036 W v8::internal::RegExpMacroAssembler::CheckPosition(int, v8::internal::Label*)',
+      '         w __gmon_start__',
+      '081f08a0 00000004 B stdout\n'
+    ].join('\n'), ''];
+  };
+
+  var shell_prov = new UnixCppEntriesProvider();
+  var shell_syms = [];
+  shell_prov.parseVmSymbols('shell', 0x08048000, 0x081ee000,
+      function (name, start, end) {
+        shell_syms.push(Array.prototype.slice.apply(arguments, [0]));
+      });
+  assertEquals(
+      [['_init', 0x08049790, 0x08049f50],
+       ['_start', 0x08049f50, 0x08139150],
+       ['v8::internal::Runtime_StringReplaceRegExpWithString(v8::internal::Arguments)', 0x08139150, 0x08139150 + 0xb4b],
+       ['v8::internal::Runtime::GetElementOrCharAt(v8::internal::Handle<v8::internal::Object>, unsigned int)', 0x08139ca0, 0x08139ca0 + 0x3f1],
+       ['v8::internal::Runtime_DebugGetPropertyDetails(v8::internal::Arguments)', 0x0813a0b0, 0x0813a0b0 + 0x855],
+       ['v8::internal::RegExpMacroAssembler::CheckPosition(int, v8::internal::Label*)', 0x0818b220, 0x0818b220 + 0x36]],
+      shell_syms);
+
+  // libc library
+  UnixCppEntriesProvider.prototype.loadSymbols = function(libName) {
+    this.symbols = [[
+        '000162a0 00000005 T __libc_init_first',
+        '0002a5f0 0000002d T __isnan',
+        '0002a5f0 0000002d W isnan',
+        '0002aaa0 0000000d W scalblnf',
+        '0002aaa0 0000000d W scalbnf',
+        '0011a340 00000048 T __libc_thread_freeres',
+        '00128860 00000024 R _itoa_lower_digits\n'].join('\n'), ''];
+  };
+  var libc_prov = new UnixCppEntriesProvider();
+  var libc_syms = [];
+  libc_prov.parseVmSymbols('libc', 0xf7c5c000, 0xf7da5000,
+      function (name, start, end) {
+        libc_syms.push(Array.prototype.slice.apply(arguments, [0]));
+      });
+  var libc_ref_syms = [['__libc_init_first', 0x000162a0, 0x000162a0 + 0x5],
+       ['__isnan', 0x0002a5f0, 0x0002a5f0 + 0x2d],
+       ['scalblnf', 0x0002aaa0, 0x0002aaa0 + 0xd],
+       ['__libc_thread_freeres', 0x0011a340, 0x0011a340 + 0x48]];
+  for (var i = 0; i < libc_ref_syms.length; ++i) {
+    libc_ref_syms[i][1] += 0xf7c5c000;
+    libc_ref_syms[i][2] += 0xf7c5c000;
+  }
+  assertEquals(libc_ref_syms, libc_syms);
+
+  UnixCppEntriesProvider.prototype.loadSymbols = oldLoadSymbols;
+})();
+
+
+(function testMacCppEntriesProvider() {
+  var oldLoadSymbols = MacCppEntriesProvider.prototype.loadSymbols;
+
+  // shell executable
+  MacCppEntriesProvider.prototype.loadSymbols = function(libName) {
+    this.symbols = [[
+      '         U operator delete[]',
+      '00001000 A __mh_execute_header',
+      '00001b00 T start',
+      '00001b40 t dyld_stub_binding_helper',
+      '0011b710 T v8::internal::RegExpMacroAssembler::CheckPosition',
+      '00134250 t v8::internal::Runtime_StringReplaceRegExpWithString',
+      '00137220 T v8::internal::Runtime::GetElementOrCharAt',
+      '00137400 t v8::internal::Runtime_DebugGetPropertyDetails',
+      '001c1a80 b _private_mem\n'
+    ].join('\n'), ''];
+  };
+
+  var shell_prov = new MacCppEntriesProvider();
+  var shell_syms = [];
+  shell_prov.parseVmSymbols('shell', 0x00001b00, 0x00163156,
+      function (name, start, end) {
+        shell_syms.push(Array.prototype.slice.apply(arguments, [0]));
+      });
+  assertEquals(
+      [['start', 0x00001b00, 0x00001b40],
+       ['dyld_stub_binding_helper', 0x00001b40, 0x0011b710],
+       ['v8::internal::RegExpMacroAssembler::CheckPosition', 0x0011b710, 0x00134250],
+       ['v8::internal::Runtime_StringReplaceRegExpWithString', 0x00134250, 0x00137220],
+       ['v8::internal::Runtime::GetElementOrCharAt', 0x00137220, 0x00137400],
+       ['v8::internal::Runtime_DebugGetPropertyDetails', 0x00137400, 0x00163156]],
+      shell_syms);
+
+  // stdc++ library
+  MacCppEntriesProvider.prototype.loadSymbols = function(libName) {
+    this.symbols = [[
+        '0000107a T __gnu_cxx::balloc::__mini_vector<std::pair<__gnu_cxx::bitmap_allocator<char>::_Alloc_block*, __gnu_cxx::bitmap_allocator<char>::_Alloc_block*> >::__mini_vector',
+        '0002c410 T std::basic_streambuf<char, std::char_traits<char> >::pubseekoff',
+        '0002c488 T std::basic_streambuf<char, std::char_traits<char> >::pubseekpos',
+        '000466aa T ___cxa_pure_virtual\n'].join('\n'), ''];
+  };
+  var stdc_prov = new MacCppEntriesProvider();
+  var stdc_syms = [];
+  stdc_prov.parseVmSymbols('stdc++', 0x95728fb4, 0x95770005,
+      function (name, start, end) {
+        stdc_syms.push(Array.prototype.slice.apply(arguments, [0]));
+      });
+  var stdc_ref_syms = [['__gnu_cxx::balloc::__mini_vector<std::pair<__gnu_cxx::bitmap_allocator<char>::_Alloc_block*, __gnu_cxx::bitmap_allocator<char>::_Alloc_block*> >::__mini_vector', 0x0000107a, 0x0002c410],
+       ['std::basic_streambuf<char, std::char_traits<char> >::pubseekoff', 0x0002c410, 0x0002c488],
+       ['std::basic_streambuf<char, std::char_traits<char> >::pubseekpos', 0x0002c488, 0x000466aa],
+       ['___cxa_pure_virtual', 0x000466aa, 0x95770005 - 0x95728fb4]];
+  for (var i = 0; i < stdc_ref_syms.length; ++i) {
+    stdc_ref_syms[i][1] += 0x95728fb4;
+    stdc_ref_syms[i][2] += 0x95728fb4;
+  }
+  assertEquals(stdc_ref_syms, stdc_syms);
+
+  MacCppEntriesProvider.prototype.loadSymbols = oldLoadSymbols;
+})();
+
+
+(function testWindowsCppEntriesProvider() {
+  var oldLoadSymbols = WindowsCppEntriesProvider.prototype.loadSymbols;
+
+  WindowsCppEntriesProvider.prototype.loadSymbols = function(libName) {
+    this.symbols = [
+      ' Start         Length     Name                   Class',
+      ' 0001:00000000 000ac902H .text                   CODE',
+      ' 0001:000ac910 000005e2H .text$yc                CODE',
+      '  Address         Publics by Value              Rva+Base       Lib:Object',
+      ' 0000:00000000       __except_list              00000000     <absolute>',
+      ' 0001:00000000       ?ReadFile@@YA?AV?$Handle@VString@v8@@@v8@@PBD@Z 00401000 f   shell.obj',
+      ' 0001:000000a0       ?Print@@YA?AV?$Handle@VValue@v8@@@v8@@ABVArguments@2@@Z 004010a0 f   shell.obj',
+      ' 0001:00001230       ??1UTF8Buffer@internal@v8@@QAE@XZ 00402230 f   v8_snapshot:scanner.obj',
+      ' 0001:00001230       ??1Utf8Value@String@v8@@QAE@XZ 00402230 f   v8_snapshot:api.obj',
+      ' 0001:000954ba       __fclose_nolock            004964ba f   LIBCMT:fclose.obj',
+      ' 0002:00000000       __imp__SetThreadPriority@8 004af000     kernel32:KERNEL32.dll',
+      ' 0003:00000418       ?in_use_list_@PreallocatedStorage@internal@v8@@0V123@A 00544418     v8_snapshot:allocation.obj',
+      ' Static symbols',
+      ' 0001:00000b70       ?DefaultFatalErrorHandler@v8@@YAXPBD0@Z 00401b70 f   v8_snapshot:api.obj',
+      ' 0001:000010b0       ?EnsureInitialized@v8@@YAXPBD@Z 004020b0 f   v8_snapshot:api.obj',
+      ' 0001:000ad17b       ??__Fnomem@?5???2@YAPAXI@Z@YAXXZ 004ae17b f   LIBCMT:new.obj'
+    ].join('\r\n');
+  };
+  var shell_prov = new WindowsCppEntriesProvider();
+  var shell_syms = [];
+  shell_prov.parseVmSymbols('shell.exe', 0x00400000, 0x0057c000,
+      function (name, start, end) {
+        shell_syms.push(Array.prototype.slice.apply(arguments, [0]));
+      });
+  assertEquals(
+      [['ReadFile', 0x00401000, 0x004010a0],
+       ['Print', 0x004010a0, 0x00402230],
+       ['v8::String::?1Utf8Value', 0x00402230, 0x004964ba],
+       ['v8::DefaultFatalErrorHandler', 0x00401b70, 0x004020b0],
+       ['v8::EnsureInitialized', 0x004020b0, 0x0057c000]],
+      shell_syms);
+
+  WindowsCppEntriesProvider.prototype.loadSymbols = oldLoadSymbols;
+})();
+
+
+// http://code.google.com/p/v8/issues/detail?id=427
+(function testWindowsProcessExeAndDllMapFile() {
+  function exeSymbols(exeName) {
+    return [
+      ' 0000:00000000       ___ImageBase               00400000     <linker-defined>',
+      ' 0001:00000780       ?RunMain@@YAHHQAPAD@Z      00401780 f   shell.obj',
+      ' 0001:00000ac0       _main                      00401ac0 f   shell.obj',
+      ''
+    ].join('\r\n');
+  }
+
+  function dllSymbols(dllName) {
+    return [
+      ' 0000:00000000       ___ImageBase               01c30000     <linker-defined>',
+      ' 0001:00000780       _DllMain@12                01c31780 f   libcmt:dllmain.obj',
+      ' 0001:00000ac0       ___DllMainCRTStartup       01c31ac0 f   libcmt:dllcrt0.obj',
+      ''
+    ].join('\r\n');
+  }
+
+  var oldRead = read;
+
+  read = exeSymbols;
+  var exe_exe_syms = [];
+  (new WindowsCppEntriesProvider()).parseVmSymbols(
+      'chrome.exe', 0x00400000, 0x00472000,
+      function (name, start, end) {
+        exe_exe_syms.push(Array.prototype.slice.apply(arguments, [0]));
+      });
+  assertEquals(
+      [['RunMain', 0x00401780, 0x00401ac0],
+       ['_main', 0x00401ac0, 0x00472000]],
+      exe_exe_syms, '.exe with .exe symbols');
+
+  read = dllSymbols;
+  var exe_dll_syms = [];
+  (new WindowsCppEntriesProvider()).parseVmSymbols(
+      'chrome.exe', 0x00400000, 0x00472000,
+      function (name, start, end) {
+        exe_dll_syms.push(Array.prototype.slice.apply(arguments, [0]));
+      });
+  assertEquals(
+      [],
+      exe_dll_syms, '.exe with .dll symbols');
+
+  read = dllSymbols;
+  var dll_dll_syms = [];
+  (new WindowsCppEntriesProvider()).parseVmSymbols(
+      'chrome.dll', 0x01c30000, 0x02b80000,
+      function (name, start, end) {
+        dll_dll_syms.push(Array.prototype.slice.apply(arguments, [0]));
+      });
+  assertEquals(
+      [['_DllMain@12', 0x01c31780, 0x01c31ac0],
+       ['___DllMainCRTStartup', 0x01c31ac0, 0x02b80000]],
+      dll_dll_syms, '.dll with .dll symbols');
+
+  read = exeSymbols;
+  var dll_exe_syms = [];
+  (new WindowsCppEntriesProvider()).parseVmSymbols(
+      'chrome.dll', 0x01c30000, 0x02b80000,
+      function (name, start, end) {
+        dll_exe_syms.push(Array.prototype.slice.apply(arguments, [0]));
+      });
+  assertEquals(
+      [],
+      dll_exe_syms, '.dll with .exe symbols');
+
+  read = oldRead;
+})();
+
+
+function CppEntriesProviderMock() {
+};
+
+
+CppEntriesProviderMock.prototype.parseVmSymbols = function(
+    name, startAddr, endAddr, symbolAdder) {
+  var symbols = {
+    'shell':
+        [['v8::internal::JSObject::LocalLookupRealNamedProperty(v8::internal::String*, v8::internal::LookupResult*)', 0x080f8800, 0x080f8d90],
+         ['v8::internal::HashTable<v8::internal::StringDictionaryShape, v8::internal::String*>::FindEntry(v8::internal::String*)', 0x080f8210, 0x080f8800],
+         ['v8::internal::Runtime_Math_exp(v8::internal::Arguments)', 0x08123b20, 0x08123b80]],
+    '/lib32/libm-2.7.so':
+        [['exp', startAddr + 0x00009e80, startAddr + 0x00009e80 + 0xa3],
+         ['fegetexcept', startAddr + 0x000061e0, startAddr + 0x000061e0 + 0x15]],
+    'ffffe000-fffff000': []};
+  assertTrue(name in symbols);
+  var syms = symbols[name];
+  for (var i = 0; i < syms.length; ++i) {
+    symbolAdder.apply(null, syms[i]);
+  }
+};
+
+
+function PrintMonitor(outputOrFileName) {
+  var expectedOut = typeof outputOrFileName == 'string' ?
+      this.loadExpectedOutput(outputOrFileName) : outputOrFileName;
+  var outputPos = 0;
+  var diffs = this.diffs = [];
+  var realOut = this.realOut = [];
+  var unexpectedOut = this.unexpectedOut = null;
+
+  this.oldPrint = print;
+  print = function(str) {
+    var strSplit = str.split('\n');
+    for (var i = 0; i < strSplit.length; ++i) {
+      s = strSplit[i];
+      realOut.push(s);
+      if (outputPos < expectedOut.length) {
+        if (expectedOut[outputPos] != s) {
+          diffs.push('line ' + outputPos + ': expected <' +
+                     expectedOut[outputPos] + '> found <' + s + '>\n');
+        }
+        outputPos++;
+      } else {
+        unexpectedOut = true;
+      }
+    }
+  };
+};
+
+
+PrintMonitor.prototype.loadExpectedOutput = function(fileName) {
+  var output = readFile(fileName);
+  return output.split('\n');
+};
+
+
+PrintMonitor.prototype.finish = function() {
+  print = this.oldPrint;
+  if (this.diffs.length > 0 || this.unexpectedOut != null) {
+    print(this.realOut.join('\n'));
+    assertEquals([], this.diffs);
+    assertNull(this.unexpectedOut);
+  }
+};
+
+
+function driveTickProcessorTest(
+    separateIc, ignoreUnknown, stateFilter, logInput, refOutput) {
+  // TEST_FILE_NAME must be provided by test runner.
+  assertEquals('string', typeof TEST_FILE_NAME);
+  var pathLen = TEST_FILE_NAME.lastIndexOf('/');
+  if (pathLen == -1) {
+    pathLen = TEST_FILE_NAME.lastIndexOf('\\');
+  }
+  assertTrue(pathLen != -1);
+  var testsPath = TEST_FILE_NAME.substr(0, pathLen + 1);
+  var tp = new TickProcessor(
+      new CppEntriesProviderMock(), separateIc, ignoreUnknown, stateFilter);
+  var pm = new PrintMonitor(testsPath + refOutput);
+  tp.processLogFile(testsPath + logInput);
+  // Hack file name to avoid dealing with platform specifics.
+  tp.lastLogFileName_ = 'v8.log';
+  tp.printStatistics();
+  pm.finish();
+};
+
+
+(function testProcessing() {
+  var testData = {
+    'Default': [
+      false, false, null,
+      'tickprocessor-test.log', 'tickprocessor-test.default'],
+    'SeparateIc': [
+      true, false, null,
+      'tickprocessor-test.log', 'tickprocessor-test.separate-ic'],
+    'IgnoreUnknown': [
+      false, true, null,
+      'tickprocessor-test.log', 'tickprocessor-test.ignore-unknown'],
+    'GcState': [
+      false, false, TickProcessor.VmStates.GC,
+      'tickprocessor-test.log', 'tickprocessor-test.gc-state']
+  };
+  for (var testName in testData) {
+    print('=== testProcessing-' + testName + ' ===');
+    driveTickProcessorTest.apply(null, testData[testName]);
+  }
+})();