OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 #include "platform/assert.h" | 5 #include "platform/assert.h" |
6 | 6 |
7 #include "vm/dart_api_impl.h" | 7 #include "vm/dart_api_impl.h" |
8 #include "vm/dart_api_state.h" | 8 #include "vm/dart_api_state.h" |
9 #include "vm/globals.h" | 9 #include "vm/globals.h" |
10 #include "vm/profiler.h" | 10 #include "vm/profiler.h" |
11 #include "vm/profiler_service.h" | 11 #include "vm/profiler_service.h" |
| 12 #include "vm/source_report.h" |
12 #include "vm/unit_test.h" | 13 #include "vm/unit_test.h" |
13 | 14 |
14 namespace dart { | 15 namespace dart { |
15 | 16 |
16 #ifndef PRODUCT | 17 #ifndef PRODUCT |
17 | 18 |
18 DECLARE_FLAG(bool, profile_vm); | 19 DECLARE_FLAG(bool, profile_vm); |
19 DECLARE_FLAG(int, max_profile_depth); | 20 DECLARE_FLAG(int, max_profile_depth); |
20 DECLARE_FLAG(bool, enable_inlining_annotations); | 21 DECLARE_FLAG(bool, enable_inlining_annotations); |
21 DECLARE_FLAG(int, optimization_counter_threshold); | 22 DECLARE_FLAG(int, optimization_counter_threshold); |
(...skipping 2270 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2292 EXPECT(walker.Down()); | 2293 EXPECT(walker.Down()); |
2293 EXPECT_STREQ("main", walker.CurrentName()); | 2294 EXPECT_STREQ("main", walker.CurrentName()); |
2294 EXPECT_EQ(1, walker.CurrentNodeTickCount()); | 2295 EXPECT_EQ(1, walker.CurrentNodeTickCount()); |
2295 EXPECT_EQ(1, walker.CurrentInclusiveTicks()); | 2296 EXPECT_EQ(1, walker.CurrentInclusiveTicks()); |
2296 EXPECT_EQ(0, walker.CurrentExclusiveTicks()); | 2297 EXPECT_EQ(0, walker.CurrentExclusiveTicks()); |
2297 EXPECT_STREQ("bacon", walker.CurrentToken()); | 2298 EXPECT_STREQ("bacon", walker.CurrentToken()); |
2298 EXPECT(!walker.Down()); | 2299 EXPECT(!walker.Down()); |
2299 } | 2300 } |
2300 } | 2301 } |
2301 | 2302 |
| 2303 |
| 2304 static void InsertFakeSample(SampleBuffer* sample_buffer, |
| 2305 uword* pc_offsets) { |
| 2306 ASSERT(sample_buffer != NULL); |
| 2307 Isolate* isolate = Isolate::Current(); |
| 2308 Sample* sample = sample_buffer->ReserveSample(); |
| 2309 ASSERT(sample != NULL); |
| 2310 sample->Init(isolate, |
| 2311 OS::GetCurrentMonotonicMicros(), |
| 2312 OSThread::Current()->trace_id()); |
| 2313 |
| 2314 intptr_t i = 0; |
| 2315 while (pc_offsets[i] != 0) { |
| 2316 // When we collect a real stack trace, all PCs collected aside from the |
| 2317 // executing one (i == 0) are actually return addresses. Return addresses |
| 2318 // are one byte beyond the call instruction that is executing. The profiler |
| 2319 // accounts for this and subtracts one from these addresses when querying |
| 2320 // inline and token position ranges. To be consistent with real stack |
| 2321 // traces, we add one byte to all PCs except the executing one. |
| 2322 // See OffsetForPC in profiler_service.cc for more context. |
| 2323 const intptr_t return_address_offset = i > 0 ? 1 : 0; |
| 2324 sample->SetAt(i, pc_offsets[i] + return_address_offset); |
| 2325 i++; |
| 2326 } |
| 2327 sample->SetAt(i, NULL); |
| 2328 } |
| 2329 |
| 2330 |
| 2331 static uword FindPCForTokenPosition(const Code& code, |
| 2332 const CodeSourceMap& code_source_map, |
| 2333 TokenPosition tp) { |
| 2334 CodeSourceMap::Iterator it(code_source_map); |
| 2335 |
| 2336 while (it.MoveNext()) { |
| 2337 if (it.TokenPos() == tp) { |
| 2338 return it.PcOffset() + code.EntryPoint(); |
| 2339 } |
| 2340 } |
| 2341 |
| 2342 return 0; |
| 2343 } |
| 2344 |
| 2345 |
| 2346 TEST_CASE(Profiler_GetSourceReport) { |
| 2347 const char* kScript = |
| 2348 "doWork(i) => i * i;\n" |
| 2349 "main() {\n" |
| 2350 " var sum = 0;\n" |
| 2351 " for (var i = 0; i < 100; i++) {\n" |
| 2352 " sum += doWork(i);\n" |
| 2353 " }\n" |
| 2354 " return sum;\n" |
| 2355 "}\n"; |
| 2356 |
| 2357 // Token position of * in `i * i`. |
| 2358 const TokenPosition squarePosition = TokenPosition(6); |
| 2359 |
| 2360 // Token position of the call to `doWork`. |
| 2361 const TokenPosition callPosition = TokenPosition(39); |
| 2362 |
| 2363 DisableNativeProfileScope dnps; |
| 2364 // Disable profiling for this thread. |
| 2365 DisableThreadInterruptsScope dtis(Thread::Current()); |
| 2366 |
| 2367 SampleBuffer* sample_buffer = Profiler::sample_buffer(); |
| 2368 EXPECT(sample_buffer != NULL); |
| 2369 |
| 2370 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
| 2371 EXPECT_VALID(lib); |
| 2372 Library& root_library = Library::Handle(); |
| 2373 root_library ^= Api::UnwrapHandle(lib); |
| 2374 |
| 2375 // Invoke main so that it gets compiled. |
| 2376 Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, NULL); |
| 2377 EXPECT_VALID(result); |
| 2378 |
| 2379 { |
| 2380 // Clear the profile for this isolate. |
| 2381 ClearProfileVisitor cpv(Isolate::Current()); |
| 2382 sample_buffer->VisitSamples(&cpv); |
| 2383 } |
| 2384 |
| 2385 // Query the code object for main and determine the PC at some token |
| 2386 // positions. |
| 2387 const Function& main = Function::Handle(GetFunction(root_library, "main")); |
| 2388 EXPECT(!main.IsNull()); |
| 2389 |
| 2390 const Function& do_work = |
| 2391 Function::Handle(GetFunction(root_library, "doWork")); |
| 2392 EXPECT(!do_work.IsNull()); |
| 2393 |
| 2394 const Script& script = Script::Handle(main.script()); |
| 2395 EXPECT(!script.IsNull()); |
| 2396 |
| 2397 const Code& main_code = Code::Handle(main.CurrentCode()); |
| 2398 EXPECT(!main_code.IsNull()); |
| 2399 |
| 2400 const Code& do_work_code = Code::Handle(do_work.CurrentCode()); |
| 2401 EXPECT(!do_work_code.IsNull()); |
| 2402 |
| 2403 const CodeSourceMap& main_code_source_map = |
| 2404 CodeSourceMap::Handle(main_code.code_source_map()); |
| 2405 EXPECT(!main_code_source_map.IsNull()); |
| 2406 |
| 2407 const CodeSourceMap& do_work_code_source_map = |
| 2408 CodeSourceMap::Handle(do_work_code.code_source_map()); |
| 2409 EXPECT(!do_work_code_source_map.IsNull()); |
| 2410 |
| 2411 // Dump code source map. |
| 2412 CodeSourceMap::Dump(do_work_code_source_map, do_work_code, main); |
| 2413 CodeSourceMap::Dump(main_code_source_map, main_code, main); |
| 2414 |
| 2415 // Look up some source token position's pc. |
| 2416 uword squarePositionPc = |
| 2417 FindPCForTokenPosition(do_work_code, |
| 2418 do_work_code_source_map, |
| 2419 squarePosition); |
| 2420 EXPECT(squarePositionPc != 0); |
| 2421 |
| 2422 uword callPositionPc = |
| 2423 FindPCForTokenPosition(main_code, main_code_source_map, callPosition); |
| 2424 EXPECT(callPositionPc != 0); |
| 2425 |
| 2426 // Look up some classifying token position's pc. |
| 2427 uword controlFlowPc = |
| 2428 FindPCForTokenPosition(do_work_code, |
| 2429 do_work_code_source_map, |
| 2430 TokenPosition::kControlFlow); |
| 2431 EXPECT(controlFlowPc != 0); |
| 2432 |
| 2433 uword tempMovePc = |
| 2434 FindPCForTokenPosition(main_code, |
| 2435 main_code_source_map, |
| 2436 TokenPosition::kTempMove); |
| 2437 EXPECT(tempMovePc != 0); |
| 2438 |
| 2439 // Insert fake samples. |
| 2440 |
| 2441 // Sample 1: |
| 2442 // squarePositionPc exclusive. |
| 2443 // callPositionPc inclusive. |
| 2444 uword sample1[] = { |
| 2445 squarePositionPc, // doWork. |
| 2446 callPositionPc, // main. |
| 2447 0 |
| 2448 }; |
| 2449 |
| 2450 // Sample 2: |
| 2451 // squarePositionPc exclusive. |
| 2452 uword sample2[] = { |
| 2453 squarePositionPc, // doWork. |
| 2454 0, |
| 2455 }; |
| 2456 |
| 2457 // Sample 3: |
| 2458 // controlFlowPc exclusive. |
| 2459 // callPositionPc inclusive. |
| 2460 uword sample3[] = { |
| 2461 controlFlowPc, // doWork. |
| 2462 callPositionPc, // main. |
| 2463 0 |
| 2464 }; |
| 2465 |
| 2466 // Sample 4: |
| 2467 // tempMovePc exclusive. |
| 2468 uword sample4[] = { |
| 2469 tempMovePc, // main. |
| 2470 0 |
| 2471 }; |
| 2472 |
| 2473 InsertFakeSample(sample_buffer, &sample1[0]); |
| 2474 InsertFakeSample(sample_buffer, &sample2[0]); |
| 2475 InsertFakeSample(sample_buffer, &sample3[0]); |
| 2476 InsertFakeSample(sample_buffer, &sample4[0]); |
| 2477 |
| 2478 // Generate source report for main. |
| 2479 SourceReport sourceReport(SourceReport::kProfile); |
| 2480 JSONStream js; |
| 2481 sourceReport.PrintJSON(&js, |
| 2482 script, |
| 2483 do_work.token_pos(), |
| 2484 main.end_token_pos()); |
| 2485 |
| 2486 // Verify positions in do_work. |
| 2487 EXPECT_SUBSTRING("\"positions\":[\"ControlFlow\",6]", js.ToCString()); |
| 2488 // Verify exclusive ticks in do_work. |
| 2489 EXPECT_SUBSTRING("\"exclusiveTicks\":[1,2]", js.ToCString()); |
| 2490 // Verify inclusive ticks in do_work. |
| 2491 EXPECT_SUBSTRING("\"inclusiveTicks\":[1,2]", js.ToCString()); |
| 2492 |
| 2493 // Verify positions in main. |
| 2494 EXPECT_SUBSTRING("\"positions\":[\"TempMove\",39]", js.ToCString()); |
| 2495 // Verify exclusive ticks in main. |
| 2496 EXPECT_SUBSTRING("\"exclusiveTicks\":[1,0]", js.ToCString()); |
| 2497 // Verify inclusive ticks in main. |
| 2498 EXPECT_SUBSTRING("\"inclusiveTicks\":[1,2]", js.ToCString()); |
| 2499 } |
| 2500 |
2302 #endif // !PRODUCT | 2501 #endif // !PRODUCT |
2303 | 2502 |
2304 } // namespace dart | 2503 } // namespace dart |
OLD | NEW |