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