1// Copyright 2009 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6//     * Redistributions of source code must retain the above copyright
7//       notice, this list of conditions and the following disclaimer.
8//     * Redistributions in binary form must reproduce the above
9//       copyright notice, this list of conditions and the following
10//       disclaimer in the documentation and/or other materials provided
11//       with the distribution.
12//     * Neither the name of Google Inc. nor the names of its
13//       contributors may be used to endorse or promote products derived
14//       from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28// This test case is not compatible with optimization stress because the
29// generated profile will look vastly different when more is optimized.
30// Flags: --nostress-opt --noalways-opt
31
32// Load implementations from <project root>/tools.
33// Files: tools/splaytree.js tools/codemap.js tools/csvparser.js
34// Files: tools/consarray.js tools/profile.js tools/profile_view.js
35// Files: tools/logreader.js tools/tickprocessor.js
36// Env: TEST_FILE_NAME
37
38
39(function testArgumentsProcessor() {
40  var p_default = new ArgumentsProcessor([]);
41  assertTrue(p_default.parse());
42  assertEquals(ArgumentsProcessor.DEFAULTS, p_default.result());
43
44  var p_logFile = new ArgumentsProcessor(['logfile.log']);
45  assertTrue(p_logFile.parse());
46  assertEquals('logfile.log', p_logFile.result().logFileName);
47
48  var p_platformAndLog = new ArgumentsProcessor(['--windows', 'winlog.log']);
49  assertTrue(p_platformAndLog.parse());
50  assertEquals('windows', p_platformAndLog.result().platform);
51  assertEquals('winlog.log', p_platformAndLog.result().logFileName);
52
53  var p_flags = new ArgumentsProcessor(['--gc', '--separate-ic']);
54  assertTrue(p_flags.parse());
55  assertEquals(TickProcessor.VmStates.GC, p_flags.result().stateFilter);
56  assertTrue(p_flags.result().separateIc);
57
58  var p_nmAndLog = new ArgumentsProcessor(['--nm=mn', 'nmlog.log']);
59  assertTrue(p_nmAndLog.parse());
60  assertEquals('mn', p_nmAndLog.result().nm);
61  assertEquals('nmlog.log', p_nmAndLog.result().logFileName);
62
63  var p_bad = new ArgumentsProcessor(['--unknown', 'badlog.log']);
64  assertFalse(p_bad.parse());
65})();
66
67
68(function testUnixCppEntriesProvider() {
69  var oldLoadSymbols = UnixCppEntriesProvider.prototype.loadSymbols;
70
71  // shell executable
72  UnixCppEntriesProvider.prototype.loadSymbols = function(libName) {
73    this.symbols = [[
74      '         U operator delete[](void*)@@GLIBCXX_3.4',
75      '08049790 T _init',
76      '08049f50 T _start',
77      '08139150 00000b4b t v8::internal::Runtime_StringReplaceRegExpWithString(v8::internal::Arguments)',
78      '08139ca0 000003f1 T v8::internal::Runtime::GetElementOrCharAt(v8::internal::Handle<v8::internal::Object>, unsigned int)',
79      '0813a0b0 00000855 t v8::internal::Runtime_DebugGetPropertyDetails(v8::internal::Arguments)',
80      '0818b220 00000036 W v8::internal::RegExpMacroAssembler::CheckPosition(int, v8::internal::Label*)',
81      '         w __gmon_start__',
82      '081f08a0 00000004 B stdout\n'
83    ].join('\n'), ''];
84  };
85
86  var shell_prov = new UnixCppEntriesProvider();
87  var shell_syms = [];
88  shell_prov.parseVmSymbols('shell', 0x08048000, 0x081ee000,
89      function (name, start, end) {
90        shell_syms.push(Array.prototype.slice.apply(arguments, [0]));
91      });
92  assertEquals(
93      [['_init', 0x08049790, 0x08049f50],
94       ['_start', 0x08049f50, 0x08139150],
95       ['v8::internal::Runtime_StringReplaceRegExpWithString(v8::internal::Arguments)', 0x08139150, 0x08139150 + 0xb4b],
96       ['v8::internal::Runtime::GetElementOrCharAt(v8::internal::Handle<v8::internal::Object>, unsigned int)', 0x08139ca0, 0x08139ca0 + 0x3f1],
97       ['v8::internal::Runtime_DebugGetPropertyDetails(v8::internal::Arguments)', 0x0813a0b0, 0x0813a0b0 + 0x855],
98       ['v8::internal::RegExpMacroAssembler::CheckPosition(int, v8::internal::Label*)', 0x0818b220, 0x0818b220 + 0x36]],
99      shell_syms);
100
101  // libc library
102  UnixCppEntriesProvider.prototype.loadSymbols = function(libName) {
103    this.symbols = [[
104        '000162a0 00000005 T __libc_init_first',
105        '0002a5f0 0000002d T __isnan',
106        '0002a5f0 0000002d W isnan',
107        '0002aaa0 0000000d W scalblnf',
108        '0002aaa0 0000000d W scalbnf',
109        '0011a340 00000048 T __libc_thread_freeres',
110        '00128860 00000024 R _itoa_lower_digits\n'].join('\n'), ''];
111  };
112  var libc_prov = new UnixCppEntriesProvider();
113  var libc_syms = [];
114  libc_prov.parseVmSymbols('libc', 0xf7c5c000, 0xf7da5000,
115      function (name, start, end) {
116        libc_syms.push(Array.prototype.slice.apply(arguments, [0]));
117      });
118  var libc_ref_syms = [['__libc_init_first', 0x000162a0, 0x000162a0 + 0x5],
119       ['__isnan', 0x0002a5f0, 0x0002a5f0 + 0x2d],
120       ['scalblnf', 0x0002aaa0, 0x0002aaa0 + 0xd],
121       ['__libc_thread_freeres', 0x0011a340, 0x0011a340 + 0x48]];
122  for (var i = 0; i < libc_ref_syms.length; ++i) {
123    libc_ref_syms[i][1] += 0xf7c5c000;
124    libc_ref_syms[i][2] += 0xf7c5c000;
125  }
126  assertEquals(libc_ref_syms, libc_syms);
127
128  UnixCppEntriesProvider.prototype.loadSymbols = oldLoadSymbols;
129})();
130
131
132(function testMacCppEntriesProvider() {
133  var oldLoadSymbols = MacCppEntriesProvider.prototype.loadSymbols;
134
135  // shell executable
136  MacCppEntriesProvider.prototype.loadSymbols = function(libName) {
137    this.symbols = [[
138      '         U operator delete[]',
139      '00001000 A __mh_execute_header',
140      '00001b00 T start',
141      '00001b40 t dyld_stub_binding_helper',
142      '0011b710 T v8::internal::RegExpMacroAssembler::CheckPosition',
143      '00134250 t v8::internal::Runtime_StringReplaceRegExpWithString',
144      '00137220 T v8::internal::Runtime::GetElementOrCharAt',
145      '00137400 t v8::internal::Runtime_DebugGetPropertyDetails',
146      '001c1a80 b _private_mem\n'
147    ].join('\n'), ''];
148  };
149
150  var shell_prov = new MacCppEntriesProvider();
151  var shell_syms = [];
152  shell_prov.parseVmSymbols('shell', 0x00001b00, 0x00163156,
153      function (name, start, end) {
154        shell_syms.push(Array.prototype.slice.apply(arguments, [0]));
155      });
156  assertEquals(
157      [['start', 0x00001b00, 0x00001b40],
158       ['dyld_stub_binding_helper', 0x00001b40, 0x0011b710],
159       ['v8::internal::RegExpMacroAssembler::CheckPosition', 0x0011b710, 0x00134250],
160       ['v8::internal::Runtime_StringReplaceRegExpWithString', 0x00134250, 0x00137220],
161       ['v8::internal::Runtime::GetElementOrCharAt', 0x00137220, 0x00137400],
162       ['v8::internal::Runtime_DebugGetPropertyDetails', 0x00137400, 0x00163156]],
163      shell_syms);
164
165  // stdc++ library
166  MacCppEntriesProvider.prototype.loadSymbols = function(libName) {
167    this.symbols = [[
168        '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',
169        '0002c410 T std::basic_streambuf<char, std::char_traits<char> >::pubseekoff',
170        '0002c488 T std::basic_streambuf<char, std::char_traits<char> >::pubseekpos',
171        '000466aa T ___cxa_pure_virtual\n'].join('\n'), ''];
172  };
173  var stdc_prov = new MacCppEntriesProvider();
174  var stdc_syms = [];
175  stdc_prov.parseVmSymbols('stdc++', 0x95728fb4, 0x95770005,
176      function (name, start, end) {
177        stdc_syms.push(Array.prototype.slice.apply(arguments, [0]));
178      });
179  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],
180       ['std::basic_streambuf<char, std::char_traits<char> >::pubseekoff', 0x0002c410, 0x0002c488],
181       ['std::basic_streambuf<char, std::char_traits<char> >::pubseekpos', 0x0002c488, 0x000466aa],
182       ['___cxa_pure_virtual', 0x000466aa, 0x95770005 - 0x95728fb4]];
183  for (var i = 0; i < stdc_ref_syms.length; ++i) {
184    stdc_ref_syms[i][1] += 0x95728fb4;
185    stdc_ref_syms[i][2] += 0x95728fb4;
186  }
187  assertEquals(stdc_ref_syms, stdc_syms);
188
189  MacCppEntriesProvider.prototype.loadSymbols = oldLoadSymbols;
190})();
191
192
193(function testWindowsCppEntriesProvider() {
194  var oldLoadSymbols = WindowsCppEntriesProvider.prototype.loadSymbols;
195
196  WindowsCppEntriesProvider.prototype.loadSymbols = function(libName) {
197    this.symbols = [
198      ' Start         Length     Name                   Class',
199      ' 0001:00000000 000ac902H .text                   CODE',
200      ' 0001:000ac910 000005e2H .text$yc                CODE',
201      '  Address         Publics by Value              Rva+Base       Lib:Object',
202      ' 0000:00000000       __except_list              00000000     <absolute>',
203      ' 0001:00000000       ?ReadFile@@YA?AV?$Handle@VString@v8@@@v8@@PBD@Z 00401000 f   shell.obj',
204      ' 0001:000000a0       ?Print@@YA?AV?$Handle@VValue@v8@@@v8@@ABVArguments@2@@Z 004010a0 f   shell.obj',
205      ' 0001:00001230       ??1UTF8Buffer@internal@v8@@QAE@XZ 00402230 f   v8_snapshot:scanner.obj',
206      ' 0001:00001230       ??1Utf8Value@String@v8@@QAE@XZ 00402230 f   v8_snapshot:api.obj',
207      ' 0001:000954ba       __fclose_nolock            004964ba f   LIBCMT:fclose.obj',
208      ' 0002:00000000       __imp__SetThreadPriority@8 004af000     kernel32:KERNEL32.dll',
209      ' 0003:00000418       ?in_use_list_@PreallocatedStorage@internal@v8@@0V123@A 00544418     v8_snapshot:allocation.obj',
210      ' Static symbols',
211      ' 0001:00000b70       ?DefaultFatalErrorHandler@v8@@YAXPBD0@Z 00401b70 f   v8_snapshot:api.obj',
212      ' 0001:000010b0       ?EnsureInitialized@v8@@YAXPBD@Z 004020b0 f   v8_snapshot:api.obj',
213      ' 0001:000ad17b       ??__Fnomem@?5???2@YAPAXI@Z@YAXXZ 004ae17b f   LIBCMT:new.obj'
214    ].join('\r\n');
215  };
216  var shell_prov = new WindowsCppEntriesProvider();
217  var shell_syms = [];
218  shell_prov.parseVmSymbols('shell.exe', 0x00400000, 0x0057c000,
219      function (name, start, end) {
220        shell_syms.push(Array.prototype.slice.apply(arguments, [0]));
221      });
222  assertEquals(
223      [['ReadFile', 0x00401000, 0x004010a0],
224       ['Print', 0x004010a0, 0x00402230],
225       ['v8::String::?1Utf8Value', 0x00402230, 0x004964ba],
226       ['v8::DefaultFatalErrorHandler', 0x00401b70, 0x004020b0],
227       ['v8::EnsureInitialized', 0x004020b0, 0x0057c000]],
228      shell_syms);
229
230  WindowsCppEntriesProvider.prototype.loadSymbols = oldLoadSymbols;
231})();
232
233
234// http://code.google.com/p/v8/issues/detail?id=427
235(function testWindowsProcessExeAndDllMapFile() {
236  function exeSymbols(exeName) {
237    return [
238      ' 0000:00000000       ___ImageBase               00400000     <linker-defined>',
239      ' 0001:00000780       ?RunMain@@YAHHQAPAD@Z      00401780 f   shell.obj',
240      ' 0001:00000ac0       _main                      00401ac0 f   shell.obj',
241      ''
242    ].join('\r\n');
243  }
244
245  function dllSymbols(dllName) {
246    return [
247      ' 0000:00000000       ___ImageBase               01c30000     <linker-defined>',
248      ' 0001:00000780       _DllMain@12                01c31780 f   libcmt:dllmain.obj',
249      ' 0001:00000ac0       ___DllMainCRTStartup       01c31ac0 f   libcmt:dllcrt0.obj',
250      ''
251    ].join('\r\n');
252  }
253
254  var oldRead = read;
255
256  read = exeSymbols;
257  var exe_exe_syms = [];
258  (new WindowsCppEntriesProvider()).parseVmSymbols(
259      'chrome.exe', 0x00400000, 0x00472000,
260      function (name, start, end) {
261        exe_exe_syms.push(Array.prototype.slice.apply(arguments, [0]));
262      });
263  assertEquals(
264      [['RunMain', 0x00401780, 0x00401ac0],
265       ['_main', 0x00401ac0, 0x00472000]],
266      exe_exe_syms, '.exe with .exe symbols');
267
268  read = dllSymbols;
269  var exe_dll_syms = [];
270  (new WindowsCppEntriesProvider()).parseVmSymbols(
271      'chrome.exe', 0x00400000, 0x00472000,
272      function (name, start, end) {
273        exe_dll_syms.push(Array.prototype.slice.apply(arguments, [0]));
274      });
275  assertEquals(
276      [],
277      exe_dll_syms, '.exe with .dll symbols');
278
279  read = dllSymbols;
280  var dll_dll_syms = [];
281  (new WindowsCppEntriesProvider()).parseVmSymbols(
282      'chrome.dll', 0x01c30000, 0x02b80000,
283      function (name, start, end) {
284        dll_dll_syms.push(Array.prototype.slice.apply(arguments, [0]));
285      });
286  assertEquals(
287      [['_DllMain@12', 0x01c31780, 0x01c31ac0],
288       ['___DllMainCRTStartup', 0x01c31ac0, 0x02b80000]],
289      dll_dll_syms, '.dll with .dll symbols');
290
291  read = exeSymbols;
292  var dll_exe_syms = [];
293  (new WindowsCppEntriesProvider()).parseVmSymbols(
294      'chrome.dll', 0x01c30000, 0x02b80000,
295      function (name, start, end) {
296        dll_exe_syms.push(Array.prototype.slice.apply(arguments, [0]));
297      });
298  assertEquals(
299      [],
300      dll_exe_syms, '.dll with .exe symbols');
301
302  read = oldRead;
303})();
304
305
306function CppEntriesProviderMock() {
307};
308
309
310CppEntriesProviderMock.prototype.parseVmSymbols = function(
311    name, startAddr, endAddr, symbolAdder) {
312  var symbols = {
313    'shell':
314        [['v8::internal::JSObject::LocalLookupRealNamedProperty(v8::internal::String*, v8::internal::LookupResult*)', 0x080f8800, 0x080f8d90],
315         ['v8::internal::HashTable<v8::internal::StringDictionaryShape, v8::internal::String*>::FindEntry(v8::internal::String*)', 0x080f8210, 0x080f8800],
316         ['v8::internal::Runtime_Math_exp(v8::internal::Arguments)', 0x08123b20, 0x08123b80]],
317    '/lib32/libm-2.7.so':
318        [['exp', startAddr + 0x00009e80, startAddr + 0x00009e80 + 0xa3],
319         ['fegetexcept', startAddr + 0x000061e0, startAddr + 0x000061e0 + 0x15]],
320    'ffffe000-fffff000': []};
321  assertTrue(name in symbols);
322  var syms = symbols[name];
323  for (var i = 0; i < syms.length; ++i) {
324    symbolAdder.apply(null, syms[i]);
325  }
326};
327
328
329function PrintMonitor(outputOrFileName) {
330  var expectedOut = typeof outputOrFileName == 'string' ?
331      this.loadExpectedOutput(outputOrFileName) : outputOrFileName;
332  var outputPos = 0;
333  var diffs = this.diffs = [];
334  var realOut = this.realOut = [];
335  var unexpectedOut = this.unexpectedOut = null;
336
337  this.oldPrint = print;
338  print = function(str) {
339    var strSplit = str.split('\n');
340    for (var i = 0; i < strSplit.length; ++i) {
341      var s = strSplit[i];
342      realOut.push(s);
343      if (outputPos < expectedOut.length) {
344        if (expectedOut[outputPos] != s) {
345          diffs.push('line ' + outputPos + ': expected <' +
346                     expectedOut[outputPos] + '> found <' + s + '>\n');
347        }
348        outputPos++;
349      } else {
350        unexpectedOut = true;
351      }
352    }
353  };
354};
355
356
357PrintMonitor.prototype.loadExpectedOutput = function(fileName) {
358  var output = readFile(fileName);
359  return output.split('\n');
360};
361
362
363PrintMonitor.prototype.finish = function() {
364  print = this.oldPrint;
365  if (this.diffs.length > 0 || this.unexpectedOut != null) {
366    print(this.realOut.join('\n'));
367    assertEquals([], this.diffs);
368    assertNull(this.unexpectedOut);
369  }
370};
371
372
373function driveTickProcessorTest(
374    separateIc, ignoreUnknown, stateFilter, logInput, refOutput) {
375  // TEST_FILE_NAME must be provided by test runner.
376  assertEquals('string', typeof TEST_FILE_NAME);
377  var pathLen = TEST_FILE_NAME.lastIndexOf('/');
378  if (pathLen == -1) {
379    pathLen = TEST_FILE_NAME.lastIndexOf('\\');
380  }
381  assertTrue(pathLen != -1);
382  var testsPath = TEST_FILE_NAME.substr(0, pathLen + 1);
383  var tp = new TickProcessor(new CppEntriesProviderMock(),
384                             separateIc,
385                             TickProcessor.CALL_GRAPH_SIZE,
386                             ignoreUnknown,
387                             stateFilter,
388                             undefined,
389                             "0",
390                             "auto,auto");
391  var pm = new PrintMonitor(testsPath + refOutput);
392  tp.processLogFileInTest(testsPath + logInput);
393  tp.printStatistics();
394  pm.finish();
395};
396
397
398(function testProcessing() {
399  var testData = {
400    'Default': [
401      false, false, null,
402      'tickprocessor-test.log', 'tickprocessor-test.default'],
403    'SeparateIc': [
404      true, false, null,
405      'tickprocessor-test.log', 'tickprocessor-test.separate-ic'],
406    'IgnoreUnknown': [
407      false, true, null,
408      'tickprocessor-test.log', 'tickprocessor-test.ignore-unknown'],
409    'GcState': [
410      false, false, TickProcessor.VmStates.GC,
411      'tickprocessor-test.log', 'tickprocessor-test.gc-state'],
412    'FunctionInfo': [
413      false, false, null,
414      'tickprocessor-test-func-info.log', 'tickprocessor-test.func-info']
415  };
416  for (var testName in testData) {
417    print('=== testProcessing-' + testName + ' ===');
418    driveTickProcessorTest.apply(null, testData[testName]);
419  }
420})();
421