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 306 function CppEntriesProviderMock() { 307 }; 308 309 310 CppEntriesProviderMock.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 329 function 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 357 PrintMonitor.prototype.loadExpectedOutput = function(fileName) { 358 var output = readFile(fileName); 359 return output.split('\n'); 360 }; 361 362 363 PrintMonitor.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 373 function 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