| Index: runtime/vm/profiler_test.cc
|
| diff --git a/runtime/vm/profiler_test.cc b/runtime/vm/profiler_test.cc
|
| index 35338515d72ce136f2b84980f8cf35e66228e8cc..63ac759d663f9ef4f976a8cad0496adee1d9176d 100644
|
| --- a/runtime/vm/profiler_test.cc
|
| +++ b/runtime/vm/profiler_test.cc
|
| @@ -34,6 +34,22 @@ class DisableNativeProfileScope : public ValueObject {
|
| };
|
|
|
|
|
| +class DisableBackgroundCompilationScope : public ValueObject {
|
| + public:
|
| + DisableBackgroundCompilationScope()
|
| + : FLAG_background_compilation_(FLAG_background_compilation) {
|
| + FLAG_background_compilation = false;
|
| + }
|
| +
|
| + ~DisableBackgroundCompilationScope() {
|
| + FLAG_background_compilation = FLAG_background_compilation_;
|
| + }
|
| +
|
| + private:
|
| + const bool FLAG_background_compilation_;
|
| +};
|
| +
|
| +
|
| // Temporarily adjust the maximum profile depth.
|
| class MaxProfileDepthScope : public ValueObject {
|
| public:
|
| @@ -139,6 +155,7 @@ TEST_CASE(Profiler_AllocationSampleTest) {
|
| delete sample_buffer;
|
| }
|
|
|
| +
|
| static RawClass* GetClass(const Library& lib, const char* name) {
|
| const Class& cls = Class::Handle(
|
| lib.LookupClassAllowPrivate(String::Handle(Symbols::New(name))));
|
| @@ -147,6 +164,14 @@ static RawClass* GetClass(const Library& lib, const char* name) {
|
| }
|
|
|
|
|
| +static RawFunction* GetFunction(const Library& lib, const char* name) {
|
| + const Function& func = Function::Handle(
|
| + lib.LookupFunctionAllowPrivate(String::Handle(Symbols::New(name))));
|
| + EXPECT(!func.IsNull()); // No ambiguity error expected.
|
| + return func.raw();
|
| +}
|
| +
|
| +
|
| class AllocationFilter : public SampleFilter {
|
| public:
|
| AllocationFilter(Isolate* isolate,
|
| @@ -1154,6 +1179,8 @@ TEST_CASE(Profiler_StringInterpolation) {
|
|
|
| TEST_CASE(Profiler_FunctionInline) {
|
| DisableNativeProfileScope dnps;
|
| + DisableBackgroundCompilationScope dbcs;
|
| +
|
| const char* kScript =
|
| "class A {\n"
|
| " var a;\n"
|
| @@ -1180,8 +1207,6 @@ TEST_CASE(Profiler_FunctionInline) {
|
| " B.boo(true);\n"
|
| "}\n";
|
|
|
| - const bool old_flag = FLAG_background_compilation;
|
| - FLAG_background_compilation = false;
|
| Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL);
|
| EXPECT_VALID(lib);
|
| Library& root_library = Library::Handle();
|
| @@ -1398,7 +1423,133 @@ TEST_CASE(Profiler_FunctionInline) {
|
| EXPECT_STREQ("[Inline End]", walker.CurrentName());
|
| EXPECT(!walker.Down());
|
| }
|
| - FLAG_background_compilation = old_flag;
|
| +}
|
| +
|
| +
|
| +TEST_CASE(Profiler_InliningIntervalBoundry) {
|
| + // The PC of frames below the top frame is a call's return address,
|
| + // which can belong to a different inlining interval than the call.
|
| + // This test checks the profiler service takes this into account; see
|
| + // ProfileBuilder::ProcessFrame.
|
| +
|
| + DisableNativeProfileScope dnps;
|
| + DisableBackgroundCompilationScope dbcs;
|
| + const char* kScript =
|
| + "class A {\n"
|
| + "}\n"
|
| + "bool alloc = false;"
|
| + "maybeAlloc() {\n"
|
| + " try {\n"
|
| + " if (alloc) new A();\n"
|
| + " } catch (e) {\n"
|
| + " }\n"
|
| + "}\n"
|
| + "right() => maybeAlloc();\n"
|
| + "doNothing() {\n"
|
| + " try {\n"
|
| + " } catch (e) {\n"
|
| + " }\n"
|
| + "}\n"
|
| + "wrong() => doNothing();\n"
|
| + "a() {\n"
|
| + " try {\n"
|
| + " right();\n"
|
| + " wrong();\n"
|
| + " } catch (e) {\n"
|
| + " }\n"
|
| + "}\n"
|
| + "mainNoAlloc() {\n"
|
| + " for (var i = 0; i < 20000; i++) {\n"
|
| + " a();\n"
|
| + " }\n"
|
| + "}\n"
|
| + "mainAlloc() {\n"
|
| + " alloc = true;\n"
|
| + " a();\n"
|
| + "}\n";
|
| +
|
| + Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL);
|
| + EXPECT_VALID(lib);
|
| + Library& root_library = Library::Handle();
|
| + root_library ^= Api::UnwrapHandle(lib);
|
| +
|
| + const Class& class_a = Class::Handle(GetClass(root_library, "A"));
|
| + EXPECT(!class_a.IsNull());
|
| +
|
| + // Compile and optimize.
|
| + Dart_Handle result = Dart_Invoke(lib, NewString("mainNoAlloc"), 0, NULL);
|
| + EXPECT_VALID(result);
|
| + result = Dart_Invoke(lib, NewString("mainAlloc"), 0, NULL);
|
| + EXPECT_VALID(result);
|
| +
|
| + // At this point a should be optimized and have inlined both right and wrong,
|
| + // but not maybeAllocate or doNothing.
|
| + Function& func = Function::Handle();
|
| + func = GetFunction(root_library, "a");
|
| + EXPECT(!func.is_inlinable());
|
| + EXPECT(func.HasOptimizedCode());
|
| + func = GetFunction(root_library, "right");
|
| + EXPECT(func.is_inlinable());
|
| + func = GetFunction(root_library, "wrong");
|
| + EXPECT(func.is_inlinable());
|
| + func = GetFunction(root_library, "doNothing");
|
| + EXPECT(!func.is_inlinable());
|
| + func = GetFunction(root_library, "maybeAlloc");
|
| + EXPECT(!func.is_inlinable());
|
| +
|
| + {
|
| + Thread* thread = Thread::Current();
|
| + Isolate* isolate = thread->isolate();
|
| + StackZone zone(thread);
|
| + HANDLESCOPE(thread);
|
| + Profile profile(isolate);
|
| + AllocationFilter filter(isolate, class_a.id());
|
| + profile.Build(thread, &filter, Profile::kNoTags);
|
| + // We should have no allocation samples.
|
| + EXPECT_EQ(0, profile.sample_count());
|
| + }
|
| +
|
| + // Turn on allocation tracing for A.
|
| + class_a.SetTraceAllocation(true);
|
| +
|
| + result = Dart_Invoke(lib, NewString("mainAlloc"), 0, NULL);
|
| + EXPECT_VALID(result);
|
| +
|
| + {
|
| + Thread* thread = Thread::Current();
|
| + Isolate* isolate = thread->isolate();
|
| + StackZone zone(thread);
|
| + HANDLESCOPE(thread);
|
| + Profile profile(isolate);
|
| + AllocationFilter filter(isolate, class_a.id());
|
| + profile.Build(thread, &filter, Profile::kNoTags);
|
| + EXPECT_EQ(1, profile.sample_count());
|
| + ProfileTrieWalker walker(&profile);
|
| +
|
| + // Inline expansion should show us the complete call chain:
|
| + walker.Reset(Profile::kExclusiveFunction);
|
| + EXPECT(walker.Down());
|
| + EXPECT_STREQ("maybeAlloc", walker.CurrentName());
|
| + EXPECT(walker.Down());
|
| + EXPECT_STREQ("right", walker.CurrentName());
|
| + EXPECT(walker.Down());
|
| + EXPECT_STREQ("a", walker.CurrentName());
|
| + EXPECT(walker.Down());
|
| + EXPECT_STREQ("mainAlloc", walker.CurrentName());
|
| + EXPECT(!walker.Down());
|
| +
|
| + // Inline expansion should show us the complete call chain:
|
| + walker.Reset(Profile::kInclusiveFunction);
|
| + EXPECT(walker.Down());
|
| + EXPECT_STREQ("mainAlloc", walker.CurrentName());
|
| + EXPECT(walker.Down());
|
| + EXPECT_STREQ("a", walker.CurrentName());
|
| + EXPECT(walker.Down());
|
| + EXPECT_STREQ("right", walker.CurrentName());
|
| + EXPECT(walker.Down());
|
| + EXPECT_STREQ("maybeAlloc", walker.CurrentName());
|
| + EXPECT(!walker.Down());
|
| + }
|
| }
|
|
|
|
|
|
|