2013-11-19 18:26:10 +00:00
|
|
|
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
#include "platform/assert.h"
|
|
|
|
|
|
|
|
#include "vm/dart_api_impl.h"
|
|
|
|
#include "vm/dart_api_state.h"
|
|
|
|
#include "vm/globals.h"
|
|
|
|
#include "vm/profiler.h"
|
2015-06-26 19:22:08 +00:00
|
|
|
#include "vm/profiler_service.h"
|
2016-03-14 14:04:23 +00:00
|
|
|
#include "vm/source_report.h"
|
2018-10-10 18:00:21 +00:00
|
|
|
#include "vm/symbols.h"
|
2013-11-19 18:26:10 +00:00
|
|
|
#include "vm/unit_test.h"
|
|
|
|
|
|
|
|
namespace dart {
|
|
|
|
|
2016-02-05 17:55:51 +00:00
|
|
|
#ifndef PRODUCT
|
|
|
|
|
2015-08-06 21:21:55 +00:00
|
|
|
DECLARE_FLAG(bool, profile_vm);
|
2018-11-05 18:26:02 +00:00
|
|
|
DECLARE_FLAG(bool, profile_vm_allocation);
|
2015-08-24 18:29:44 +00:00
|
|
|
DECLARE_FLAG(int, max_profile_depth);
|
2016-03-02 20:02:28 +00:00
|
|
|
DECLARE_FLAG(int, optimization_counter_threshold);
|
|
|
|
|
2015-08-06 21:21:55 +00:00
|
|
|
// Some tests are written assuming native stack trace profiling is disabled.
|
2015-08-24 18:29:44 +00:00
|
|
|
class DisableNativeProfileScope : public ValueObject {
|
2015-08-06 21:31:57 +00:00
|
|
|
public:
|
2018-11-05 18:26:02 +00:00
|
|
|
DisableNativeProfileScope()
|
|
|
|
: FLAG_profile_vm_(FLAG_profile_vm),
|
|
|
|
FLAG_profile_vm_allocation_(FLAG_profile_vm_allocation) {
|
2015-08-06 21:21:55 +00:00
|
|
|
FLAG_profile_vm = false;
|
2018-11-05 18:26:02 +00:00
|
|
|
FLAG_profile_vm_allocation = false;
|
2015-08-06 21:21:55 +00:00
|
|
|
}
|
|
|
|
|
2018-11-05 18:26:02 +00:00
|
|
|
~DisableNativeProfileScope() {
|
|
|
|
FLAG_profile_vm = FLAG_profile_vm_;
|
|
|
|
FLAG_profile_vm_allocation = FLAG_profile_vm_allocation_;
|
|
|
|
}
|
2015-08-06 21:21:55 +00:00
|
|
|
|
|
|
|
private:
|
2015-08-06 21:31:57 +00:00
|
|
|
const bool FLAG_profile_vm_;
|
2018-11-05 18:26:02 +00:00
|
|
|
const bool FLAG_profile_vm_allocation_;
|
2015-08-06 21:21:55 +00:00
|
|
|
};
|
|
|
|
|
2016-01-13 00:45:00 +00:00
|
|
|
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_;
|
|
|
|
};
|
|
|
|
|
2015-08-24 18:29:44 +00:00
|
|
|
// Temporarily adjust the maximum profile depth.
|
|
|
|
class MaxProfileDepthScope : public ValueObject {
|
|
|
|
public:
|
|
|
|
explicit MaxProfileDepthScope(intptr_t new_max_depth)
|
|
|
|
: FLAG_max_profile_depth_(FLAG_max_profile_depth) {
|
|
|
|
Profiler::SetSampleDepth(new_max_depth);
|
|
|
|
}
|
|
|
|
|
2016-11-08 21:54:47 +00:00
|
|
|
~MaxProfileDepthScope() { Profiler::SetSampleDepth(FLAG_max_profile_depth_); }
|
2015-08-24 18:29:44 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
const intptr_t FLAG_max_profile_depth_;
|
|
|
|
};
|
|
|
|
|
2013-11-19 18:26:10 +00:00
|
|
|
class ProfileSampleBufferTestHelper {
|
|
|
|
public:
|
2017-02-24 21:56:06 +00:00
|
|
|
static intptr_t IterateCount(const Dart_Port port,
|
2013-12-16 18:52:15 +00:00
|
|
|
const SampleBuffer& sample_buffer) {
|
2013-11-19 18:26:10 +00:00
|
|
|
intptr_t c = 0;
|
2013-12-16 18:52:15 +00:00
|
|
|
for (intptr_t i = 0; i < sample_buffer.capacity(); i++) {
|
2014-02-20 21:10:07 +00:00
|
|
|
Sample* sample = sample_buffer.At(i);
|
2017-02-24 21:56:06 +00:00
|
|
|
if (sample->port() != port) {
|
2013-12-16 18:52:15 +00:00
|
|
|
continue;
|
|
|
|
}
|
2013-11-19 18:26:10 +00:00
|
|
|
c++;
|
|
|
|
}
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
2017-02-24 21:56:06 +00:00
|
|
|
static intptr_t IterateSumPC(const Dart_Port port,
|
2013-12-16 18:52:15 +00:00
|
|
|
const SampleBuffer& sample_buffer) {
|
2013-11-19 18:26:10 +00:00
|
|
|
intptr_t c = 0;
|
2013-12-16 18:52:15 +00:00
|
|
|
for (intptr_t i = 0; i < sample_buffer.capacity(); i++) {
|
2014-02-20 21:10:07 +00:00
|
|
|
Sample* sample = sample_buffer.At(i);
|
2017-02-24 21:56:06 +00:00
|
|
|
if (sample->port() != port) {
|
2013-12-16 18:52:15 +00:00
|
|
|
continue;
|
|
|
|
}
|
2014-01-17 16:32:51 +00:00
|
|
|
c += sample->At(0);
|
2013-11-19 18:26:10 +00:00
|
|
|
}
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-06-23 13:47:39 +00:00
|
|
|
TEST_CASE(Profiler_SampleBufferWrapTest) {
|
2013-11-19 18:26:10 +00:00
|
|
|
SampleBuffer* sample_buffer = new SampleBuffer(3);
|
2017-02-24 21:56:06 +00:00
|
|
|
Dart_Port i = 123;
|
2013-12-16 18:52:15 +00:00
|
|
|
EXPECT_EQ(0, ProfileSampleBufferTestHelper::IterateSumPC(i, *sample_buffer));
|
2013-11-19 18:26:10 +00:00
|
|
|
Sample* s;
|
|
|
|
s = sample_buffer->ReserveSample();
|
2014-02-20 21:10:07 +00:00
|
|
|
s->Init(i, 0, 0);
|
2014-01-17 16:32:51 +00:00
|
|
|
s->SetAt(0, 2);
|
2013-12-16 18:52:15 +00:00
|
|
|
EXPECT_EQ(2, ProfileSampleBufferTestHelper::IterateSumPC(i, *sample_buffer));
|
2013-11-19 18:26:10 +00:00
|
|
|
s = sample_buffer->ReserveSample();
|
2014-02-20 21:10:07 +00:00
|
|
|
s->Init(i, 0, 0);
|
2014-01-17 16:32:51 +00:00
|
|
|
s->SetAt(0, 4);
|
2013-12-16 18:52:15 +00:00
|
|
|
EXPECT_EQ(6, ProfileSampleBufferTestHelper::IterateSumPC(i, *sample_buffer));
|
2013-11-19 18:26:10 +00:00
|
|
|
s = sample_buffer->ReserveSample();
|
2014-02-20 21:10:07 +00:00
|
|
|
s->Init(i, 0, 0);
|
2014-01-17 16:32:51 +00:00
|
|
|
s->SetAt(0, 6);
|
2013-12-16 18:52:15 +00:00
|
|
|
EXPECT_EQ(12, ProfileSampleBufferTestHelper::IterateSumPC(i, *sample_buffer));
|
2013-11-19 18:26:10 +00:00
|
|
|
s = sample_buffer->ReserveSample();
|
2014-02-20 21:10:07 +00:00
|
|
|
s->Init(i, 0, 0);
|
2014-01-17 16:32:51 +00:00
|
|
|
s->SetAt(0, 8);
|
2013-12-16 18:52:15 +00:00
|
|
|
EXPECT_EQ(18, ProfileSampleBufferTestHelper::IterateSumPC(i, *sample_buffer));
|
2013-11-19 18:26:10 +00:00
|
|
|
delete sample_buffer;
|
|
|
|
}
|
|
|
|
|
2015-06-23 13:47:39 +00:00
|
|
|
TEST_CASE(Profiler_SampleBufferIterateTest) {
|
2013-11-19 18:26:10 +00:00
|
|
|
SampleBuffer* sample_buffer = new SampleBuffer(3);
|
2017-02-24 21:56:06 +00:00
|
|
|
Dart_Port i = 123;
|
2013-12-16 18:52:15 +00:00
|
|
|
EXPECT_EQ(0, ProfileSampleBufferTestHelper::IterateCount(i, *sample_buffer));
|
|
|
|
Sample* s;
|
|
|
|
s = sample_buffer->ReserveSample();
|
2014-02-20 21:10:07 +00:00
|
|
|
s->Init(i, 0, 0);
|
2013-12-16 18:52:15 +00:00
|
|
|
EXPECT_EQ(1, ProfileSampleBufferTestHelper::IterateCount(i, *sample_buffer));
|
|
|
|
s = sample_buffer->ReserveSample();
|
2014-02-20 21:10:07 +00:00
|
|
|
s->Init(i, 0, 0);
|
2013-12-16 18:52:15 +00:00
|
|
|
EXPECT_EQ(2, ProfileSampleBufferTestHelper::IterateCount(i, *sample_buffer));
|
|
|
|
s = sample_buffer->ReserveSample();
|
2014-02-20 21:10:07 +00:00
|
|
|
s->Init(i, 0, 0);
|
2013-12-16 18:52:15 +00:00
|
|
|
EXPECT_EQ(3, ProfileSampleBufferTestHelper::IterateCount(i, *sample_buffer));
|
|
|
|
s = sample_buffer->ReserveSample();
|
2014-02-20 21:10:07 +00:00
|
|
|
s->Init(i, 0, 0);
|
2013-12-16 18:52:15 +00:00
|
|
|
EXPECT_EQ(3, ProfileSampleBufferTestHelper::IterateCount(i, *sample_buffer));
|
2013-11-19 18:26:10 +00:00
|
|
|
delete sample_buffer;
|
|
|
|
}
|
|
|
|
|
2015-06-23 13:47:39 +00:00
|
|
|
TEST_CASE(Profiler_AllocationSampleTest) {
|
|
|
|
Isolate* isolate = Isolate::Current();
|
|
|
|
SampleBuffer* sample_buffer = new SampleBuffer(3);
|
|
|
|
Sample* sample = sample_buffer->ReserveSample();
|
2017-02-24 21:56:06 +00:00
|
|
|
sample->Init(isolate->main_port(), 0, 0);
|
2015-06-23 13:47:39 +00:00
|
|
|
sample->set_metadata(99);
|
|
|
|
sample->set_is_allocation_sample(true);
|
|
|
|
EXPECT_EQ(99, sample->allocation_cid());
|
|
|
|
delete sample_buffer;
|
|
|
|
}
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
static RawLibrary* LoadTestScript(const char* script) {
|
|
|
|
Dart_Handle api_lib;
|
|
|
|
{
|
|
|
|
TransitionVMToNative transition(Thread::Current());
|
|
|
|
api_lib = TestCase::LoadTestScript(script, NULL);
|
2019-08-16 20:39:15 +00:00
|
|
|
EXPECT_VALID(api_lib);
|
2018-10-31 19:51:52 +00:00
|
|
|
}
|
|
|
|
Library& lib = Library::Handle();
|
|
|
|
lib ^= Api::UnwrapHandle(api_lib);
|
|
|
|
return lib.raw();
|
|
|
|
}
|
|
|
|
|
2015-06-24 20:41:12 +00:00
|
|
|
static RawClass* GetClass(const Library& lib, const char* name) {
|
2018-09-04 20:02:47 +00:00
|
|
|
Thread* thread = Thread::Current();
|
|
|
|
const Class& cls = Class::Handle(
|
|
|
|
lib.LookupClassAllowPrivate(String::Handle(Symbols::New(thread, name))));
|
2015-06-24 20:41:12 +00:00
|
|
|
EXPECT(!cls.IsNull()); // No ambiguity error expected.
|
|
|
|
return cls.raw();
|
|
|
|
}
|
|
|
|
|
2016-01-13 00:45:00 +00:00
|
|
|
static RawFunction* GetFunction(const Library& lib, const char* name) {
|
2018-09-04 20:02:47 +00:00
|
|
|
Thread* thread = Thread::Current();
|
2016-04-11 23:28:29 +00:00
|
|
|
const Function& func = Function::Handle(lib.LookupFunctionAllowPrivate(
|
2018-09-04 20:02:47 +00:00
|
|
|
String::Handle(Symbols::New(thread, name))));
|
2016-01-13 00:45:00 +00:00
|
|
|
EXPECT(!func.IsNull()); // No ambiguity error expected.
|
|
|
|
return func.raw();
|
|
|
|
}
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
static void Invoke(const Library& lib,
|
|
|
|
const char* name,
|
|
|
|
intptr_t argc = 0,
|
|
|
|
Dart_Handle* argv = NULL) {
|
|
|
|
Thread* thread = Thread::Current();
|
|
|
|
Dart_Handle api_lib = Api::NewHandle(thread, lib.raw());
|
|
|
|
TransitionVMToNative transition(thread);
|
|
|
|
Dart_Handle result = Dart_Invoke(api_lib, NewString(name), argc, argv);
|
|
|
|
EXPECT_VALID(result);
|
|
|
|
}
|
|
|
|
|
2015-06-26 19:22:08 +00:00
|
|
|
class AllocationFilter : public SampleFilter {
|
|
|
|
public:
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter(Dart_Port port,
|
2015-12-21 22:07:49 +00:00
|
|
|
intptr_t cid,
|
|
|
|
int64_t time_origin_micros = -1,
|
|
|
|
int64_t time_extent_micros = -1)
|
2017-02-24 21:56:06 +00:00
|
|
|
: SampleFilter(port,
|
2016-03-22 15:42:06 +00:00
|
|
|
Thread::kMutatorTask,
|
2015-12-21 22:07:49 +00:00
|
|
|
time_origin_micros,
|
|
|
|
time_extent_micros),
|
2015-08-20 20:08:57 +00:00
|
|
|
cid_(cid),
|
2016-11-08 21:54:47 +00:00
|
|
|
enable_vm_ticks_(false) {}
|
2015-06-26 19:22:08 +00:00
|
|
|
|
|
|
|
bool FilterSample(Sample* sample) {
|
2016-11-08 21:54:47 +00:00
|
|
|
if (!enable_vm_ticks_ && (sample->vm_tag() == VMTag::kVMTagId)) {
|
2015-08-20 20:08:57 +00:00
|
|
|
// We don't want to see embedder ticks in the test.
|
|
|
|
return false;
|
|
|
|
}
|
2016-11-08 21:54:47 +00:00
|
|
|
return sample->is_allocation_sample() && (sample->allocation_cid() == cid_);
|
2015-08-20 20:08:57 +00:00
|
|
|
}
|
|
|
|
|
2016-11-08 21:54:47 +00:00
|
|
|
void set_enable_vm_ticks(bool enable) { enable_vm_ticks_ = enable; }
|
2015-06-26 19:22:08 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
intptr_t cid_;
|
2015-11-24 23:36:55 +00:00
|
|
|
bool enable_vm_ticks_;
|
2015-06-26 19:22:08 +00:00
|
|
|
};
|
|
|
|
|
2017-08-09 18:20:55 +00:00
|
|
|
static void EnableProfiler() {
|
|
|
|
if (!FLAG_profiler) {
|
|
|
|
FLAG_profiler = true;
|
2018-09-28 23:18:59 +00:00
|
|
|
Profiler::Init();
|
2017-08-09 18:20:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-07 01:04:08 +00:00
|
|
|
class ProfileStackWalker {
|
|
|
|
public:
|
|
|
|
explicit ProfileStackWalker(Profile* profile, bool as_func = false)
|
|
|
|
: profile_(profile),
|
|
|
|
as_functions_(as_func),
|
|
|
|
index_(0),
|
|
|
|
sample_(profile->SampleAt(0)) {
|
|
|
|
ClearInliningData();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Down() {
|
|
|
|
if (as_functions_) {
|
|
|
|
return UpdateFunctionIndex();
|
|
|
|
} else {
|
|
|
|
++index_;
|
|
|
|
return (index_ < sample_->length());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* CurrentName() {
|
|
|
|
if (as_functions_) {
|
|
|
|
ProfileFunction* func = GetFunction();
|
|
|
|
EXPECT(func != NULL);
|
|
|
|
return func->Name();
|
|
|
|
} else {
|
|
|
|
ProfileCode* code = GetCode();
|
|
|
|
EXPECT(code != NULL);
|
|
|
|
return code->name();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* CurrentToken() {
|
|
|
|
if (!as_functions_) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
ProfileFunction* func = GetFunction();
|
|
|
|
const Function& function = *(func->function());
|
|
|
|
if (function.IsNull()) {
|
|
|
|
// No function.
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
Zone* zone = Thread::Current()->zone();
|
|
|
|
const Script& script = Script::Handle(zone, function.script());
|
|
|
|
if (script.IsNull()) {
|
|
|
|
// No script.
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
ProfileFunctionSourcePosition pfsp(TokenPosition::kNoSource);
|
|
|
|
if (!func->GetSinglePosition(&pfsp)) {
|
|
|
|
// Not exactly one source position.
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
TokenPosition token_pos = pfsp.token_pos();
|
|
|
|
if (!token_pos.IsSourcePosition()) {
|
|
|
|
// Not a location in a script.
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (token_pos.IsSynthetic()) {
|
|
|
|
token_pos = token_pos.FromSynthetic();
|
|
|
|
}
|
|
|
|
|
|
|
|
String& str = String::Handle(zone);
|
|
|
|
if (script.kind() == RawScript::kKernelTag) {
|
|
|
|
intptr_t line = 0, column = 0, token_len = 0;
|
|
|
|
script.GetTokenLocation(token_pos, &line, &column, &token_len);
|
|
|
|
str = script.GetSnippet(line, column, line, column + token_len);
|
|
|
|
} else {
|
|
|
|
UNREACHABLE();
|
|
|
|
}
|
|
|
|
return str.IsNull() ? NULL : str.ToCString();
|
|
|
|
}
|
|
|
|
|
|
|
|
intptr_t CurrentInclusiveTicks() {
|
|
|
|
if (as_functions_) {
|
|
|
|
ProfileFunction* func = GetFunction();
|
|
|
|
EXPECT(func != NULL);
|
|
|
|
return func->inclusive_ticks();
|
|
|
|
} else {
|
|
|
|
ProfileCode* code = GetCode();
|
|
|
|
ASSERT(code != NULL);
|
|
|
|
return code->inclusive_ticks();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
intptr_t CurrentExclusiveTicks() {
|
|
|
|
if (as_functions_) {
|
|
|
|
ProfileFunction* func = GetFunction();
|
|
|
|
EXPECT(func != NULL);
|
|
|
|
return func->exclusive_ticks();
|
|
|
|
} else {
|
|
|
|
ProfileCode* code = GetCode();
|
|
|
|
ASSERT(code != NULL);
|
|
|
|
return code->exclusive_ticks();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* VMTagName() { return VMTag::TagName(sample_->vm_tag()); }
|
|
|
|
|
|
|
|
private:
|
|
|
|
ProfileCode* GetCode() {
|
|
|
|
uword pc = sample_->At(index_);
|
|
|
|
int64_t timestamp = sample_->timestamp();
|
|
|
|
return profile_->GetCodeFromPC(pc, timestamp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const intptr_t kInvalidInlinedIndex = -1;
|
|
|
|
|
|
|
|
bool UpdateFunctionIndex() {
|
|
|
|
if (inlined_index_ != kInvalidInlinedIndex) {
|
|
|
|
if (inlined_index_ - 1 >= 0) {
|
|
|
|
--inlined_index_;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
ClearInliningData();
|
|
|
|
}
|
|
|
|
++index_;
|
|
|
|
return (index_ < sample_->length());
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClearInliningData() {
|
|
|
|
inlined_index_ = kInvalidInlinedIndex;
|
|
|
|
inlined_functions_ = NULL;
|
|
|
|
inlined_token_positions_ = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ProfileFunction* GetFunction() {
|
|
|
|
// Check to see if we're currently processing inlined functions. If so,
|
|
|
|
// return the next inlined function.
|
|
|
|
ProfileFunction* function = GetInlinedFunction();
|
|
|
|
if (function != NULL) {
|
|
|
|
return function;
|
|
|
|
}
|
|
|
|
|
|
|
|
const uword pc = sample_->At(index_);
|
|
|
|
ProfileCode* profile_code =
|
|
|
|
profile_->GetCodeFromPC(pc, sample_->timestamp());
|
|
|
|
ASSERT(profile_code != NULL);
|
|
|
|
function = profile_code->function();
|
|
|
|
ASSERT(function != NULL);
|
|
|
|
|
|
|
|
TokenPosition token_position = TokenPosition::kNoSource;
|
|
|
|
Code& code = Code::ZoneHandle();
|
|
|
|
if (profile_code->code().IsCode()) {
|
|
|
|
code ^= profile_code->code().raw();
|
|
|
|
inlined_functions_cache_.Get(pc, code, sample_, index_,
|
|
|
|
&inlined_functions_,
|
|
|
|
&inlined_token_positions_, &token_position);
|
|
|
|
} else if (profile_code->code().IsBytecode()) {
|
|
|
|
// No inlining in bytecode.
|
|
|
|
const Bytecode& bc = Bytecode::CheckedHandle(Thread::Current()->zone(),
|
|
|
|
profile_code->code().raw());
|
|
|
|
token_position = bc.GetTokenIndexOfPC(pc);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (code.IsNull() || (inlined_functions_ == NULL) ||
|
|
|
|
(inlined_functions_->length() <= 1)) {
|
|
|
|
ClearInliningData();
|
|
|
|
// No inlined functions.
|
|
|
|
return function;
|
|
|
|
}
|
|
|
|
|
|
|
|
ASSERT(code.is_optimized());
|
|
|
|
inlined_index_ = inlined_functions_->length() - 1;
|
|
|
|
function = GetInlinedFunction();
|
|
|
|
ASSERT(function != NULL);
|
|
|
|
return function;
|
|
|
|
}
|
|
|
|
|
|
|
|
ProfileFunction* GetInlinedFunction() {
|
|
|
|
if ((inlined_index_ != kInvalidInlinedIndex) &&
|
|
|
|
(inlined_index_ < inlined_functions_->length())) {
|
|
|
|
return profile_->FindFunction(*(*inlined_functions_)[inlined_index_]);
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
Profile* profile_;
|
|
|
|
bool as_functions_;
|
|
|
|
intptr_t index_;
|
|
|
|
ProcessedSample* sample_;
|
|
|
|
ProfileCodeInlinedFunctionsCache inlined_functions_cache_;
|
|
|
|
GrowableArray<const Function*>* inlined_functions_;
|
|
|
|
GrowableArray<TokenPosition>* inlined_token_positions_;
|
|
|
|
intptr_t inlined_index_;
|
|
|
|
};
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
ISOLATE_UNIT_TEST_CASE(Profiler_TrivialRecordAllocation) {
|
2017-08-09 18:20:55 +00:00
|
|
|
EnableProfiler();
|
2015-08-06 21:21:55 +00:00
|
|
|
DisableNativeProfileScope dnps;
|
2018-11-05 18:26:02 +00:00
|
|
|
DisableBackgroundCompilationScope dbcs;
|
2015-06-24 20:41:12 +00:00
|
|
|
const char* kScript =
|
|
|
|
"class A {\n"
|
|
|
|
" var a;\n"
|
|
|
|
" var b;\n"
|
|
|
|
"}\n"
|
2015-06-26 19:22:08 +00:00
|
|
|
"class B {\n"
|
|
|
|
" static boo() {\n"
|
|
|
|
" return new A();\n"
|
|
|
|
" }\n"
|
|
|
|
"}\n"
|
2015-06-24 20:41:12 +00:00
|
|
|
"main() {\n"
|
2015-06-26 19:22:08 +00:00
|
|
|
" return B.boo();\n"
|
2015-06-24 20:41:12 +00:00
|
|
|
"}\n";
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
const Library& root_library = Library::Handle(LoadTestScript(kScript));
|
2015-06-24 20:41:12 +00:00
|
|
|
|
2015-12-21 22:07:49 +00:00
|
|
|
const int64_t before_allocations_micros = Dart_TimelineGetMicros();
|
2015-06-24 20:41:12 +00:00
|
|
|
const Class& class_a = Class::Handle(GetClass(root_library, "A"));
|
|
|
|
EXPECT(!class_a.IsNull());
|
|
|
|
class_a.SetTraceAllocation(true);
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "main");
|
2015-06-26 19:22:08 +00:00
|
|
|
|
2015-12-21 22:07:49 +00:00
|
|
|
const int64_t after_allocations_micros = Dart_TimelineGetMicros();
|
|
|
|
const int64_t allocation_extent_micros =
|
|
|
|
after_allocations_micros - before_allocations_micros;
|
2015-06-26 19:22:08 +00:00
|
|
|
{
|
2015-08-24 18:29:44 +00:00
|
|
|
Thread* thread = Thread::Current();
|
|
|
|
Isolate* isolate = thread->isolate();
|
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
2015-06-26 19:22:08 +00:00
|
|
|
Profile profile(isolate);
|
2015-12-21 22:07:49 +00:00
|
|
|
// Filter for the class in the time range.
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), class_a.id(),
|
|
|
|
before_allocations_micros,
|
2015-12-21 22:07:49 +00:00
|
|
|
allocation_extent_micros);
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2015-06-26 19:22:08 +00:00
|
|
|
// We should have 1 allocation sample.
|
|
|
|
EXPECT_EQ(1, profile.sample_count());
|
2019-09-07 01:04:08 +00:00
|
|
|
ProfileStackWalker walker(&profile);
|
2015-06-26 19:22:08 +00:00
|
|
|
|
|
|
|
// Move down from the root.
|
2019-09-07 01:04:08 +00:00
|
|
|
EXPECT_STREQ("DRT_AllocateObject", walker.VMTagName());
|
2019-06-28 16:44:57 +00:00
|
|
|
if (FLAG_enable_interpreter) {
|
|
|
|
EXPECT_STREQ("[Bytecode] B.boo", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Bytecode] main", walker.CurrentName());
|
|
|
|
EXPECT(!walker.Down());
|
|
|
|
} else {
|
|
|
|
EXPECT_STREQ("[Stub] Allocate A", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Unoptimized] B.boo", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Unoptimized] main", walker.CurrentName());
|
|
|
|
EXPECT(!walker.Down());
|
|
|
|
}
|
2015-06-26 19:22:08 +00:00
|
|
|
}
|
2015-12-21 22:07:49 +00:00
|
|
|
|
|
|
|
// Query with a time filter where no allocations occurred.
|
|
|
|
{
|
|
|
|
Thread* thread = Thread::Current();
|
|
|
|
Isolate* isolate = thread->isolate();
|
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), class_a.id(),
|
|
|
|
Dart_TimelineGetMicros(), 16000);
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2015-12-21 22:07:49 +00:00
|
|
|
// We should have no allocation samples because none occured within
|
|
|
|
// the specified time range.
|
|
|
|
EXPECT_EQ(0, profile.sample_count());
|
|
|
|
}
|
2015-06-24 20:41:12 +00:00
|
|
|
}
|
|
|
|
|
2017-03-27 20:25:26 +00:00
|
|
|
#if defined(DART_USE_TCMALLOC) && defined(HOST_OS_LINUX) && defined(DEBUG) && \
|
2019-09-10 11:31:46 +00:00
|
|
|
defined(HOST_ARCH_X64)
|
2017-03-27 20:25:26 +00:00
|
|
|
|
|
|
|
DART_NOINLINE static void NativeAllocationSampleHelper(char** result) {
|
|
|
|
ASSERT(result != NULL);
|
|
|
|
*result = static_cast<char*>(malloc(sizeof(char) * 1024));
|
|
|
|
}
|
|
|
|
|
|
|
|
ISOLATE_UNIT_TEST_CASE(Profiler_NativeAllocation) {
|
2017-04-19 22:50:59 +00:00
|
|
|
bool enable_malloc_hooks_saved = FLAG_profiler_native_memory;
|
|
|
|
FLAG_profiler_native_memory = true;
|
2017-03-27 20:25:26 +00:00
|
|
|
|
2019-09-10 11:31:46 +00:00
|
|
|
EnableProfiler();
|
|
|
|
|
|
|
|
MallocHooks::Init();
|
2017-03-27 20:25:26 +00:00
|
|
|
MallocHooks::ResetStats();
|
|
|
|
bool stack_trace_collection_enabled =
|
|
|
|
MallocHooks::stack_trace_collection_enabled();
|
|
|
|
MallocHooks::set_stack_trace_collection_enabled(true);
|
|
|
|
|
|
|
|
char* result = NULL;
|
|
|
|
const int64_t before_allocations_micros = Dart_TimelineGetMicros();
|
|
|
|
NativeAllocationSampleHelper(&result);
|
|
|
|
|
|
|
|
// Disable stack allocation stack trace collection to avoid muddying up
|
|
|
|
// results.
|
|
|
|
MallocHooks::set_stack_trace_collection_enabled(false);
|
|
|
|
const int64_t after_allocations_micros = Dart_TimelineGetMicros();
|
|
|
|
const int64_t allocation_extent_micros =
|
|
|
|
after_allocations_micros - before_allocations_micros;
|
|
|
|
|
|
|
|
// Walk the trie and do a sanity check of the allocation values associated
|
|
|
|
// with each node.
|
|
|
|
{
|
|
|
|
Thread* thread = Thread::Current();
|
|
|
|
Isolate* isolate = thread->isolate();
|
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
|
|
|
Profile profile(isolate);
|
|
|
|
|
|
|
|
// Filter for the class in the time range.
|
|
|
|
NativeAllocationSampleFilter filter(before_allocations_micros,
|
|
|
|
allocation_extent_micros);
|
2019-09-10 11:31:46 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::allocation_sample_buffer());
|
2017-03-27 20:25:26 +00:00
|
|
|
// We should have 1 allocation sample.
|
|
|
|
EXPECT_EQ(1, profile.sample_count());
|
2019-09-10 11:31:46 +00:00
|
|
|
ProfileStackWalker walker(&profile);
|
2017-03-27 20:25:26 +00:00
|
|
|
|
2019-09-10 11:31:46 +00:00
|
|
|
// Move down from the root.
|
2017-03-27 20:25:26 +00:00
|
|
|
EXPECT_SUBSTRING("[Native]", walker.CurrentName());
|
2019-09-10 11:31:46 +00:00
|
|
|
EXPECT_EQ(1024ul, profile.SampleAt(0)->native_allocation_size_bytes());
|
2017-03-27 20:25:26 +00:00
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("dart::Dart_TestProfiler_NativeAllocation()",
|
|
|
|
walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("dart::TestCase::Run()", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("dart::TestCaseBase::RunTest()", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("dart::TestCaseBase::RunAll()", walker.CurrentName());
|
2019-09-10 11:31:46 +00:00
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_SUBSTRING("[Native]", walker.CurrentName());
|
2017-03-27 20:25:26 +00:00
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("main", walker.CurrentName());
|
|
|
|
EXPECT(!walker.Down());
|
|
|
|
}
|
|
|
|
|
|
|
|
MallocHooks::set_stack_trace_collection_enabled(true);
|
|
|
|
free(result);
|
|
|
|
MallocHooks::set_stack_trace_collection_enabled(false);
|
|
|
|
|
|
|
|
// Check to see that the native allocation sample associated with the memory
|
|
|
|
// freed above is marked as free and is no longer reported.
|
|
|
|
{
|
|
|
|
Thread* thread = Thread::Current();
|
|
|
|
Isolate* isolate = thread->isolate();
|
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
|
|
|
Profile profile(isolate);
|
|
|
|
|
|
|
|
// Filter for the class in the time range.
|
|
|
|
NativeAllocationSampleFilter filter(before_allocations_micros,
|
|
|
|
allocation_extent_micros);
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2017-03-27 20:25:26 +00:00
|
|
|
// We should have 0 allocation samples since we freed the memory.
|
|
|
|
EXPECT_EQ(0, profile.sample_count());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Query with a time filter where no allocations occurred.
|
|
|
|
{
|
|
|
|
Thread* thread = Thread::Current();
|
|
|
|
Isolate* isolate = thread->isolate();
|
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
|
|
|
Profile profile(isolate);
|
|
|
|
NativeAllocationSampleFilter filter(Dart_TimelineGetMicros(), 16000);
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2017-03-27 20:25:26 +00:00
|
|
|
// We should have no allocation samples because none occured within
|
|
|
|
// the specified time range.
|
|
|
|
EXPECT_EQ(0, profile.sample_count());
|
|
|
|
}
|
|
|
|
|
|
|
|
MallocHooks::set_stack_trace_collection_enabled(
|
|
|
|
stack_trace_collection_enabled);
|
2017-04-19 22:50:59 +00:00
|
|
|
FLAG_profiler_native_memory = enable_malloc_hooks_saved;
|
2017-03-27 20:25:26 +00:00
|
|
|
}
|
2019-09-10 11:31:46 +00:00
|
|
|
#endif // defined(DART_USE_TCMALLOC) && defined(HOST_OS_LINUX) && \
|
|
|
|
// defined(DEBUG) && defined(HOST_ARCH_X64)
|
2017-03-27 20:25:26 +00:00
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
ISOLATE_UNIT_TEST_CASE(Profiler_ToggleRecordAllocation) {
|
2017-08-09 18:20:55 +00:00
|
|
|
EnableProfiler();
|
|
|
|
|
2015-08-06 21:21:55 +00:00
|
|
|
DisableNativeProfileScope dnps;
|
2018-11-05 18:26:02 +00:00
|
|
|
DisableBackgroundCompilationScope dbcs;
|
2015-06-26 19:35:54 +00:00
|
|
|
const char* kScript =
|
|
|
|
"class A {\n"
|
|
|
|
" var a;\n"
|
|
|
|
" var b;\n"
|
|
|
|
"}\n"
|
|
|
|
"class B {\n"
|
|
|
|
" static boo() {\n"
|
|
|
|
" return new A();\n"
|
|
|
|
" }\n"
|
|
|
|
"}\n"
|
|
|
|
"main() {\n"
|
|
|
|
" return B.boo();\n"
|
|
|
|
"}\n";
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
const Library& root_library = Library::Handle(LoadTestScript(kScript));
|
2015-06-26 19:35:54 +00:00
|
|
|
|
|
|
|
const Class& class_a = Class::Handle(GetClass(root_library, "A"));
|
|
|
|
EXPECT(!class_a.IsNull());
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "main");
|
2015-06-26 19:35:54 +00:00
|
|
|
|
|
|
|
{
|
2015-08-24 18:29:44 +00:00
|
|
|
Thread* thread = Thread::Current();
|
|
|
|
Isolate* isolate = thread->isolate();
|
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
2015-06-26 19:35:54 +00:00
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2015-06-26 19:35:54 +00:00
|
|
|
// We should have no allocation samples.
|
|
|
|
EXPECT_EQ(0, profile.sample_count());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Turn on allocation tracing for A.
|
|
|
|
class_a.SetTraceAllocation(true);
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "main");
|
2015-06-26 19:35:54 +00:00
|
|
|
|
|
|
|
{
|
2015-08-24 18:29:44 +00:00
|
|
|
Thread* thread = Thread::Current();
|
|
|
|
Isolate* isolate = thread->isolate();
|
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
2015-06-26 19:35:54 +00:00
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2015-06-26 19:35:54 +00:00
|
|
|
// We should have one allocation sample.
|
|
|
|
EXPECT_EQ(1, profile.sample_count());
|
2019-09-07 01:04:08 +00:00
|
|
|
ProfileStackWalker walker(&profile);
|
2015-06-26 19:35:54 +00:00
|
|
|
|
2019-09-07 01:04:08 +00:00
|
|
|
EXPECT_STREQ("DRT_AllocateObject", walker.VMTagName());
|
2019-06-28 16:44:57 +00:00
|
|
|
if (FLAG_enable_interpreter) {
|
|
|
|
EXPECT_STREQ("[Bytecode] B.boo", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Bytecode] main", walker.CurrentName());
|
|
|
|
EXPECT(!walker.Down());
|
|
|
|
} else {
|
|
|
|
EXPECT_STREQ("[Stub] Allocate A", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Unoptimized] B.boo", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Unoptimized] main", walker.CurrentName());
|
|
|
|
EXPECT(!walker.Down());
|
|
|
|
}
|
2015-06-26 19:35:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Turn off allocation tracing for A.
|
|
|
|
class_a.SetTraceAllocation(false);
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "main");
|
2015-06-26 19:35:54 +00:00
|
|
|
|
|
|
|
{
|
2015-08-24 18:29:44 +00:00
|
|
|
Thread* thread = Thread::Current();
|
|
|
|
Isolate* isolate = thread->isolate();
|
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
2015-06-26 19:35:54 +00:00
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2015-06-26 19:35:54 +00:00
|
|
|
// We should still only have one allocation sample.
|
|
|
|
EXPECT_EQ(1, profile.sample_count());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
ISOLATE_UNIT_TEST_CASE(Profiler_CodeTicks) {
|
2017-08-09 18:20:55 +00:00
|
|
|
EnableProfiler();
|
2015-08-06 21:21:55 +00:00
|
|
|
DisableNativeProfileScope dnps;
|
2018-11-05 18:26:02 +00:00
|
|
|
DisableBackgroundCompilationScope dbcs;
|
2015-07-28 17:21:38 +00:00
|
|
|
const char* kScript =
|
|
|
|
"class A {\n"
|
|
|
|
" var a;\n"
|
|
|
|
" var b;\n"
|
|
|
|
"}\n"
|
|
|
|
"class B {\n"
|
|
|
|
" static boo() {\n"
|
|
|
|
" return new A();\n"
|
|
|
|
" }\n"
|
|
|
|
"}\n"
|
|
|
|
"main() {\n"
|
2016-10-31 22:04:49 +00:00
|
|
|
" return B.boo();\n"
|
2015-07-28 17:21:38 +00:00
|
|
|
"}\n";
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
const Library& root_library = Library::Handle(LoadTestScript(kScript));
|
2015-07-28 17:21:38 +00:00
|
|
|
|
|
|
|
const Class& class_a = Class::Handle(GetClass(root_library, "A"));
|
|
|
|
EXPECT(!class_a.IsNull());
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "main");
|
2015-07-28 17:21:38 +00:00
|
|
|
|
|
|
|
{
|
2015-08-24 18:29:44 +00:00
|
|
|
Thread* thread = Thread::Current();
|
|
|
|
Isolate* isolate = thread->isolate();
|
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
2015-07-28 17:21:38 +00:00
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2015-07-28 17:21:38 +00:00
|
|
|
// We should have no allocation samples.
|
|
|
|
EXPECT_EQ(0, profile.sample_count());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Turn on allocation tracing for A.
|
|
|
|
class_a.SetTraceAllocation(true);
|
|
|
|
|
|
|
|
// Allocate three times.
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "main");
|
|
|
|
Invoke(root_library, "main");
|
|
|
|
Invoke(root_library, "main");
|
2015-07-28 17:21:38 +00:00
|
|
|
|
|
|
|
{
|
2015-08-24 18:29:44 +00:00
|
|
|
Thread* thread = Thread::Current();
|
|
|
|
Isolate* isolate = thread->isolate();
|
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
2015-07-28 17:21:38 +00:00
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2015-07-28 22:18:29 +00:00
|
|
|
// We should have three allocation samples.
|
2015-07-28 17:21:38 +00:00
|
|
|
EXPECT_EQ(3, profile.sample_count());
|
2019-09-07 01:04:08 +00:00
|
|
|
ProfileStackWalker walker(&profile);
|
2015-07-28 17:21:38 +00:00
|
|
|
|
|
|
|
// Move down from the root.
|
2019-09-07 01:04:08 +00:00
|
|
|
EXPECT_STREQ("DRT_AllocateObject", walker.VMTagName());
|
2019-06-28 16:44:57 +00:00
|
|
|
if (FLAG_enable_interpreter) {
|
|
|
|
EXPECT_STREQ("[Bytecode] B.boo", walker.CurrentName());
|
|
|
|
EXPECT_EQ(3, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Bytecode] main", walker.CurrentName());
|
|
|
|
EXPECT_EQ(3, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT_EQ(0, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT(!walker.Down());
|
|
|
|
} else {
|
|
|
|
EXPECT_STREQ("[Stub] Allocate A", walker.CurrentName());
|
|
|
|
EXPECT_EQ(3, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Unoptimized] B.boo", walker.CurrentName());
|
|
|
|
EXPECT_EQ(3, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Unoptimized] main", walker.CurrentName());
|
|
|
|
EXPECT_EQ(3, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT_EQ(0, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT(!walker.Down());
|
|
|
|
}
|
2015-07-28 21:28:44 +00:00
|
|
|
}
|
|
|
|
}
|
2018-10-31 19:51:52 +00:00
|
|
|
ISOLATE_UNIT_TEST_CASE(Profiler_FunctionTicks) {
|
2017-08-09 18:20:55 +00:00
|
|
|
EnableProfiler();
|
2015-08-06 21:21:55 +00:00
|
|
|
DisableNativeProfileScope dnps;
|
2018-11-05 18:26:02 +00:00
|
|
|
DisableBackgroundCompilationScope dbcs;
|
2015-07-28 21:28:44 +00:00
|
|
|
const char* kScript =
|
|
|
|
"class A {\n"
|
|
|
|
" var a;\n"
|
|
|
|
" var b;\n"
|
|
|
|
"}\n"
|
|
|
|
"class B {\n"
|
|
|
|
" static boo() {\n"
|
|
|
|
" return new A();\n"
|
|
|
|
" }\n"
|
|
|
|
"}\n"
|
|
|
|
"main() {\n"
|
2016-10-31 22:04:49 +00:00
|
|
|
" return B.boo();\n"
|
2015-07-28 21:28:44 +00:00
|
|
|
"}\n";
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
const Library& root_library = Library::Handle(LoadTestScript(kScript));
|
2015-07-28 21:28:44 +00:00
|
|
|
|
|
|
|
const Class& class_a = Class::Handle(GetClass(root_library, "A"));
|
|
|
|
EXPECT(!class_a.IsNull());
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "main");
|
2015-07-28 21:28:44 +00:00
|
|
|
|
|
|
|
{
|
2015-08-24 18:29:44 +00:00
|
|
|
Thread* thread = Thread::Current();
|
|
|
|
Isolate* isolate = thread->isolate();
|
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
2015-07-28 21:28:44 +00:00
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2015-07-28 21:28:44 +00:00
|
|
|
// We should have no allocation samples.
|
|
|
|
EXPECT_EQ(0, profile.sample_count());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Turn on allocation tracing for A.
|
|
|
|
class_a.SetTraceAllocation(true);
|
|
|
|
|
|
|
|
// Allocate three times.
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "main");
|
|
|
|
Invoke(root_library, "main");
|
|
|
|
Invoke(root_library, "main");
|
2015-07-28 21:28:44 +00:00
|
|
|
|
|
|
|
{
|
2015-08-24 18:29:44 +00:00
|
|
|
Thread* thread = Thread::Current();
|
|
|
|
Isolate* isolate = thread->isolate();
|
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
2015-07-28 21:28:44 +00:00
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2015-07-28 22:18:29 +00:00
|
|
|
// We should have three allocation samples.
|
2015-07-28 21:28:44 +00:00
|
|
|
EXPECT_EQ(3, profile.sample_count());
|
2019-09-07 01:04:08 +00:00
|
|
|
ProfileStackWalker walker(&profile, true);
|
|
|
|
|
|
|
|
EXPECT_STREQ("DRT_AllocateObject", walker.VMTagName());
|
2015-07-28 21:28:44 +00:00
|
|
|
|
2019-06-28 16:44:57 +00:00
|
|
|
if (!FLAG_enable_interpreter) {
|
|
|
|
EXPECT_STREQ("[Stub] Allocate A", walker.CurrentName());
|
|
|
|
EXPECT_EQ(3, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
}
|
2015-07-28 21:28:44 +00:00
|
|
|
EXPECT_STREQ("B.boo", walker.CurrentName());
|
|
|
|
EXPECT_EQ(3, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("main", walker.CurrentName());
|
|
|
|
EXPECT_EQ(3, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT_EQ(0, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT(!walker.Down());
|
2015-07-28 17:21:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
ISOLATE_UNIT_TEST_CASE(Profiler_IntrinsicAllocation) {
|
2017-08-09 18:20:55 +00:00
|
|
|
EnableProfiler();
|
2015-08-06 21:21:55 +00:00
|
|
|
DisableNativeProfileScope dnps;
|
2018-11-05 18:26:02 +00:00
|
|
|
DisableBackgroundCompilationScope dbcs;
|
2015-07-10 19:19:01 +00:00
|
|
|
const char* kScript = "double foo(double a, double b) => a + b;";
|
2018-10-31 19:51:52 +00:00
|
|
|
const Library& root_library = Library::Handle(LoadTestScript(kScript));
|
2015-09-02 00:18:55 +00:00
|
|
|
Isolate* isolate = thread->isolate();
|
2015-07-10 19:19:01 +00:00
|
|
|
|
|
|
|
const Class& double_class =
|
|
|
|
Class::Handle(isolate->object_store()->double_class());
|
|
|
|
EXPECT(!double_class.IsNull());
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
Dart_Handle args[2];
|
|
|
|
{
|
|
|
|
TransitionVMToNative transition(thread);
|
|
|
|
args[0] = Dart_NewDouble(1.0);
|
|
|
|
args[1] = Dart_NewDouble(2.0);
|
|
|
|
}
|
2015-07-10 19:19:01 +00:00
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "foo", 2, &args[0]);
|
2015-07-10 19:19:01 +00:00
|
|
|
|
|
|
|
{
|
2015-09-02 00:18:55 +00:00
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
2015-07-10 19:19:01 +00:00
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), double_class.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2015-07-10 19:19:01 +00:00
|
|
|
// We should have no allocation samples.
|
|
|
|
EXPECT_EQ(0, profile.sample_count());
|
|
|
|
}
|
|
|
|
|
|
|
|
double_class.SetTraceAllocation(true);
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "foo", 2, &args[0]);
|
2015-07-10 19:19:01 +00:00
|
|
|
|
|
|
|
{
|
2015-09-02 00:18:55 +00:00
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
2015-07-10 19:19:01 +00:00
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), double_class.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2015-07-10 19:19:01 +00:00
|
|
|
// We should have one allocation sample.
|
|
|
|
EXPECT_EQ(1, profile.sample_count());
|
2019-09-07 01:04:08 +00:00
|
|
|
ProfileStackWalker walker(&profile);
|
2015-07-10 19:19:01 +00:00
|
|
|
|
2019-06-28 16:44:57 +00:00
|
|
|
if (FLAG_enable_interpreter) {
|
2019-09-07 01:04:08 +00:00
|
|
|
EXPECT_STREQ("DRT_AllocateObject", walker.VMTagName());
|
2019-06-28 16:44:57 +00:00
|
|
|
EXPECT_STREQ("[Bytecode] foo", walker.CurrentName());
|
|
|
|
EXPECT(!walker.Down());
|
|
|
|
} else {
|
2019-09-07 01:04:08 +00:00
|
|
|
EXPECT_STREQ("Double_add", walker.VMTagName());
|
2019-08-15 21:10:47 +00:00
|
|
|
EXPECT_STREQ("[Unoptimized] double._add", walker.CurrentName());
|
2019-06-28 16:44:57 +00:00
|
|
|
EXPECT(walker.Down());
|
2019-08-15 21:10:47 +00:00
|
|
|
EXPECT_STREQ("[Unoptimized] double.+", walker.CurrentName());
|
2019-06-28 16:44:57 +00:00
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Unoptimized] foo", walker.CurrentName());
|
|
|
|
EXPECT(!walker.Down());
|
|
|
|
}
|
2015-07-10 19:19:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
double_class.SetTraceAllocation(false);
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "foo", 2, &args[0]);
|
2015-07-10 19:19:01 +00:00
|
|
|
|
|
|
|
{
|
2015-09-02 00:18:55 +00:00
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
2015-07-10 19:19:01 +00:00
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), double_class.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2015-07-10 19:19:01 +00:00
|
|
|
// We should still only have one allocation sample.
|
|
|
|
EXPECT_EQ(1, profile.sample_count());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
ISOLATE_UNIT_TEST_CASE(Profiler_ArrayAllocation) {
|
2017-08-09 18:20:55 +00:00
|
|
|
EnableProfiler();
|
2015-08-06 21:21:55 +00:00
|
|
|
DisableNativeProfileScope dnps;
|
2018-11-05 18:26:02 +00:00
|
|
|
DisableBackgroundCompilationScope dbcs;
|
2015-07-10 20:06:17 +00:00
|
|
|
const char* kScript =
|
|
|
|
"List foo() => new List(4);\n"
|
|
|
|
"List bar() => new List();\n";
|
2018-10-31 19:51:52 +00:00
|
|
|
const Library& root_library = Library::Handle(LoadTestScript(kScript));
|
2015-09-02 00:18:55 +00:00
|
|
|
Isolate* isolate = thread->isolate();
|
2015-07-10 20:06:17 +00:00
|
|
|
|
|
|
|
const Class& array_class =
|
|
|
|
Class::Handle(isolate->object_store()->array_class());
|
|
|
|
EXPECT(!array_class.IsNull());
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "foo");
|
2015-07-10 20:06:17 +00:00
|
|
|
|
|
|
|
{
|
2015-09-02 00:18:55 +00:00
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
2015-07-10 20:06:17 +00:00
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), array_class.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2015-07-10 20:06:17 +00:00
|
|
|
// We should have no allocation samples.
|
|
|
|
EXPECT_EQ(0, profile.sample_count());
|
|
|
|
}
|
|
|
|
|
|
|
|
array_class.SetTraceAllocation(true);
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "foo");
|
2015-07-10 20:06:17 +00:00
|
|
|
|
|
|
|
{
|
2015-09-02 00:18:55 +00:00
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
2015-07-10 20:06:17 +00:00
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), array_class.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2015-07-10 20:06:17 +00:00
|
|
|
// We should have one allocation sample.
|
|
|
|
EXPECT_EQ(1, profile.sample_count());
|
2019-09-07 01:04:08 +00:00
|
|
|
ProfileStackWalker walker(&profile);
|
2015-07-10 20:06:17 +00:00
|
|
|
|
2019-09-07 01:04:08 +00:00
|
|
|
EXPECT_STREQ("DRT_AllocateArray", walker.VMTagName());
|
2019-06-28 16:44:57 +00:00
|
|
|
if (FLAG_enable_interpreter) {
|
|
|
|
EXPECT_STREQ("[Bytecode] new _List", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Bytecode] foo", walker.CurrentName());
|
|
|
|
EXPECT(!walker.Down());
|
|
|
|
} else {
|
|
|
|
EXPECT_STREQ("[Stub] AllocateArray", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Unoptimized] new _List", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Unoptimized] foo", walker.CurrentName());
|
|
|
|
EXPECT(!walker.Down());
|
|
|
|
}
|
2015-07-10 20:06:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
array_class.SetTraceAllocation(false);
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "foo");
|
2015-07-10 20:06:17 +00:00
|
|
|
|
|
|
|
{
|
2015-09-02 00:18:55 +00:00
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
2015-07-10 20:06:17 +00:00
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), array_class.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2015-07-10 20:06:17 +00:00
|
|
|
// We should still only have one allocation sample.
|
|
|
|
EXPECT_EQ(1, profile.sample_count());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clear the samples.
|
|
|
|
ProfilerService::ClearSamples();
|
|
|
|
|
|
|
|
// Compile bar (many List objects allocated).
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "bar");
|
2015-07-10 20:06:17 +00:00
|
|
|
|
|
|
|
// Enable again.
|
|
|
|
array_class.SetTraceAllocation(true);
|
|
|
|
|
|
|
|
// Run bar.
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "bar");
|
2015-07-10 20:06:17 +00:00
|
|
|
|
|
|
|
{
|
2015-09-02 00:18:55 +00:00
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
2015-07-10 20:06:17 +00:00
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), array_class.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
The current growth strategy for growable arrays allocates a backing array of size 2 at (empty) creation and doubles the size whenever the capacity is insufficient while adding elements.
I collected statistics for the sizes and capacities of growable arrays which are promoted to old-space or survive an old-space gc when running dart2js and Fasta. For these applications, the vast majority of arrays stay empty. More than half of the total object size of promoted backing arrays is backing for empty growable arrays.
Furthermore, since the overhead for an array is 3 words (header, type parameters and length), and object sizes are rounded up to an even number of words, we waste one word for all even-sized arrays.
This CL changes the growth strategy so that empty growable arrays are created with a shared, zero-sized array as backing, avoiding the allocation of a backing array if no elements are added. When the array needs to grow, it starts out at 3 and grows to double size plus one each time: 7, 15, 31, ...
A few places in the VM code need to handle these shared, zero-sized arrays specially. In particular, the Array::MakeArray function needs to allocate a new, empty array if its result is to be returned to Dart code.
Benchmarks suggest that the change improves memory usage by a few percent overall and does not significantly affect run time.
BUG=
R=erikcorry@google.com
Review-Url: https://codereview.chromium.org/2949803002 .
2017-06-22 08:51:53 +00:00
|
|
|
// We should have no allocation samples, since empty
|
|
|
|
// growable lists use a shared backing.
|
|
|
|
EXPECT_EQ(0, profile.sample_count());
|
2015-07-10 20:06:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
ISOLATE_UNIT_TEST_CASE(Profiler_ContextAllocation) {
|
2017-08-09 18:20:55 +00:00
|
|
|
EnableProfiler();
|
2015-08-20 20:08:57 +00:00
|
|
|
DisableNativeProfileScope dnps;
|
2018-11-05 18:26:02 +00:00
|
|
|
DisableBackgroundCompilationScope dbcs;
|
2015-08-20 20:08:57 +00:00
|
|
|
const char* kScript =
|
|
|
|
"var msg1 = 'a';\n"
|
|
|
|
"foo() {\n"
|
|
|
|
" var msg = msg1 + msg1;\n"
|
|
|
|
" return (x) { return '$msg + $msg'; }(msg);\n"
|
|
|
|
"}\n";
|
2018-10-31 19:51:52 +00:00
|
|
|
const Library& root_library = Library::Handle(LoadTestScript(kScript));
|
2015-09-02 00:18:55 +00:00
|
|
|
Isolate* isolate = thread->isolate();
|
2015-08-20 20:08:57 +00:00
|
|
|
|
2016-11-08 21:54:47 +00:00
|
|
|
const Class& context_class = Class::Handle(Object::context_class());
|
2015-08-20 20:08:57 +00:00
|
|
|
EXPECT(!context_class.IsNull());
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "foo");
|
2015-08-20 20:08:57 +00:00
|
|
|
|
|
|
|
{
|
2015-09-02 00:18:55 +00:00
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
2015-08-20 20:08:57 +00:00
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), context_class.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2015-08-20 20:08:57 +00:00
|
|
|
// We should have no allocation samples.
|
|
|
|
EXPECT_EQ(0, profile.sample_count());
|
|
|
|
}
|
|
|
|
|
|
|
|
context_class.SetTraceAllocation(true);
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "foo");
|
2015-08-20 20:08:57 +00:00
|
|
|
|
|
|
|
{
|
2015-09-02 00:18:55 +00:00
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
2015-08-20 20:08:57 +00:00
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), context_class.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2015-08-20 20:08:57 +00:00
|
|
|
// We should have one allocation sample.
|
|
|
|
EXPECT_EQ(1, profile.sample_count());
|
2019-09-07 01:04:08 +00:00
|
|
|
ProfileStackWalker walker(&profile);
|
2015-08-20 20:08:57 +00:00
|
|
|
|
2019-09-07 01:04:08 +00:00
|
|
|
EXPECT_STREQ("DRT_AllocateContext", walker.VMTagName());
|
2019-06-28 16:44:57 +00:00
|
|
|
if (FLAG_enable_interpreter) {
|
|
|
|
EXPECT_STREQ("[Bytecode] foo", walker.CurrentName());
|
|
|
|
EXPECT(!walker.Down());
|
|
|
|
} else {
|
|
|
|
EXPECT_STREQ("[Stub] AllocateContext", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Unoptimized] foo", walker.CurrentName());
|
|
|
|
EXPECT(!walker.Down());
|
|
|
|
}
|
2015-08-20 20:08:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
context_class.SetTraceAllocation(false);
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "foo");
|
2015-08-20 20:08:57 +00:00
|
|
|
|
|
|
|
{
|
2015-09-02 00:18:55 +00:00
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
2015-08-20 20:08:57 +00:00
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), context_class.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2015-08-20 20:08:57 +00:00
|
|
|
// We should still only have one allocation sample.
|
|
|
|
EXPECT_EQ(1, profile.sample_count());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
ISOLATE_UNIT_TEST_CASE(Profiler_ClosureAllocation) {
|
2017-08-09 18:20:55 +00:00
|
|
|
EnableProfiler();
|
2015-08-20 20:08:57 +00:00
|
|
|
DisableNativeProfileScope dnps;
|
2018-11-05 18:26:02 +00:00
|
|
|
DisableBackgroundCompilationScope dbcs;
|
2015-08-20 20:08:57 +00:00
|
|
|
const char* kScript =
|
|
|
|
"var msg1 = 'a';\n"
|
|
|
|
"\n"
|
|
|
|
"foo() {\n"
|
|
|
|
" var msg = msg1 + msg1;\n"
|
|
|
|
" var msg2 = msg + msg;\n"
|
|
|
|
" return (x, y, z, w) { return '$x + $y + $z'; }(msg, msg2, msg, msg);\n"
|
|
|
|
"}\n"
|
|
|
|
"bar() {\n"
|
|
|
|
" var msg = msg1 + msg1;\n"
|
|
|
|
" var msg2 = msg + msg;\n"
|
|
|
|
" return (x, y) { return '$x + $y'; }(msg, msg2);\n"
|
|
|
|
"}\n";
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
const Library& root_library = Library::Handle(LoadTestScript(kScript));
|
2015-09-02 00:18:55 +00:00
|
|
|
Isolate* isolate = thread->isolate();
|
2015-08-20 20:08:57 +00:00
|
|
|
|
2016-01-20 00:32:59 +00:00
|
|
|
const Class& closure_class =
|
|
|
|
Class::Handle(Isolate::Current()->object_store()->closure_class());
|
|
|
|
EXPECT(!closure_class.IsNull());
|
|
|
|
closure_class.SetTraceAllocation(true);
|
2015-08-20 20:08:57 +00:00
|
|
|
|
2016-01-20 00:32:59 +00:00
|
|
|
// Invoke "foo" which during compilation, triggers a closure allocation.
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "foo");
|
2015-08-20 20:08:57 +00:00
|
|
|
|
|
|
|
{
|
2015-09-02 00:18:55 +00:00
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
2015-08-20 20:08:57 +00:00
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), closure_class.id());
|
2015-11-24 23:36:55 +00:00
|
|
|
filter.set_enable_vm_ticks(true);
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2015-08-20 20:08:57 +00:00
|
|
|
// We should have one allocation sample.
|
|
|
|
EXPECT_EQ(1, profile.sample_count());
|
2019-09-07 01:04:08 +00:00
|
|
|
ProfileStackWalker walker(&profile);
|
2015-08-20 20:08:57 +00:00
|
|
|
|
2019-09-07 01:04:08 +00:00
|
|
|
EXPECT_SUBSTRING("DRT_AllocateObject", walker.VMTagName());
|
2019-06-28 16:44:57 +00:00
|
|
|
if (!FLAG_enable_interpreter) {
|
|
|
|
EXPECT_STREQ("[Stub] Allocate _Closure", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
}
|
2016-01-20 00:32:59 +00:00
|
|
|
EXPECT_SUBSTRING("foo", walker.CurrentName());
|
2015-08-20 20:08:57 +00:00
|
|
|
EXPECT(!walker.Down());
|
|
|
|
}
|
|
|
|
|
2016-01-20 00:32:59 +00:00
|
|
|
// Disable allocation tracing for Closure.
|
|
|
|
closure_class.SetTraceAllocation(false);
|
2015-08-20 20:08:57 +00:00
|
|
|
|
2016-01-20 00:32:59 +00:00
|
|
|
// Invoke "bar" which during compilation, triggers a closure allocation.
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "bar");
|
2015-08-20 20:08:57 +00:00
|
|
|
|
|
|
|
{
|
2015-09-02 00:18:55 +00:00
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
2015-08-20 20:08:57 +00:00
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), closure_class.id());
|
2015-11-24 23:36:55 +00:00
|
|
|
filter.set_enable_vm_ticks(true);
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2015-08-20 20:08:57 +00:00
|
|
|
// We should still only have one allocation sample.
|
|
|
|
EXPECT_EQ(1, profile.sample_count());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
ISOLATE_UNIT_TEST_CASE(Profiler_TypedArrayAllocation) {
|
2017-08-09 18:20:55 +00:00
|
|
|
EnableProfiler();
|
2015-08-06 21:21:55 +00:00
|
|
|
DisableNativeProfileScope dnps;
|
2018-11-05 18:26:02 +00:00
|
|
|
DisableBackgroundCompilationScope dbcs;
|
2015-07-10 20:50:55 +00:00
|
|
|
const char* kScript =
|
|
|
|
"import 'dart:typed_data';\n"
|
|
|
|
"List foo() => new Float32List(4);\n";
|
2018-10-31 19:51:52 +00:00
|
|
|
const Library& root_library = Library::Handle(LoadTestScript(kScript));
|
2015-09-02 00:18:55 +00:00
|
|
|
Isolate* isolate = thread->isolate();
|
2015-07-10 20:50:55 +00:00
|
|
|
|
|
|
|
const Library& typed_data_library =
|
|
|
|
Library::Handle(isolate->object_store()->typed_data_library());
|
|
|
|
|
|
|
|
const Class& float32_list_class =
|
2016-12-22 09:57:21 +00:00
|
|
|
Class::Handle(GetClass(typed_data_library, "_Float32List"));
|
2015-07-10 20:50:55 +00:00
|
|
|
EXPECT(!float32_list_class.IsNull());
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "foo");
|
2015-07-10 20:50:55 +00:00
|
|
|
|
|
|
|
{
|
2015-09-02 00:18:55 +00:00
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
2015-07-10 20:50:55 +00:00
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), float32_list_class.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2015-07-10 20:50:55 +00:00
|
|
|
// We should have no allocation samples.
|
|
|
|
EXPECT_EQ(0, profile.sample_count());
|
|
|
|
}
|
|
|
|
|
|
|
|
float32_list_class.SetTraceAllocation(true);
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "foo");
|
2015-07-10 20:50:55 +00:00
|
|
|
|
|
|
|
{
|
2015-09-02 00:18:55 +00:00
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
2015-07-10 20:50:55 +00:00
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), float32_list_class.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2015-07-10 20:50:55 +00:00
|
|
|
// We should have one allocation sample.
|
|
|
|
EXPECT_EQ(1, profile.sample_count());
|
2019-09-07 01:04:08 +00:00
|
|
|
ProfileStackWalker walker(&profile);
|
2015-07-10 20:50:55 +00:00
|
|
|
|
2019-09-07 01:04:08 +00:00
|
|
|
EXPECT_STREQ("TypedData_Float32Array_new", walker.VMTagName());
|
2019-06-28 16:44:57 +00:00
|
|
|
if (FLAG_enable_interpreter) {
|
|
|
|
EXPECT_STREQ("[Bytecode] new Float32List", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Bytecode] foo", walker.CurrentName());
|
|
|
|
EXPECT(!walker.Down());
|
|
|
|
} else {
|
|
|
|
EXPECT_STREQ("[Unoptimized] new Float32List", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Unoptimized] foo", walker.CurrentName());
|
|
|
|
EXPECT(!walker.Down());
|
|
|
|
}
|
2015-07-10 20:50:55 +00:00
|
|
|
}
|
2015-07-10 21:00:16 +00:00
|
|
|
|
|
|
|
float32_list_class.SetTraceAllocation(false);
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "foo");
|
2015-07-10 21:00:16 +00:00
|
|
|
|
|
|
|
{
|
2015-09-02 00:18:55 +00:00
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
2015-07-10 21:00:16 +00:00
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), float32_list_class.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2015-07-10 21:00:16 +00:00
|
|
|
// We should still only have one allocation sample.
|
|
|
|
EXPECT_EQ(1, profile.sample_count());
|
|
|
|
}
|
|
|
|
|
|
|
|
float32_list_class.SetTraceAllocation(true);
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "foo");
|
2015-07-10 21:00:16 +00:00
|
|
|
|
|
|
|
{
|
2015-09-02 00:18:55 +00:00
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
2015-07-10 21:00:16 +00:00
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), float32_list_class.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2015-07-10 21:00:16 +00:00
|
|
|
// We should now have two allocation samples.
|
|
|
|
EXPECT_EQ(2, profile.sample_count());
|
|
|
|
}
|
2015-07-10 20:50:55 +00:00
|
|
|
}
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
ISOLATE_UNIT_TEST_CASE(Profiler_StringAllocation) {
|
2017-08-09 18:20:55 +00:00
|
|
|
EnableProfiler();
|
2015-08-06 21:21:55 +00:00
|
|
|
DisableNativeProfileScope dnps;
|
2018-11-05 18:26:02 +00:00
|
|
|
DisableBackgroundCompilationScope dbcs;
|
2015-07-10 21:30:04 +00:00
|
|
|
const char* kScript = "String foo(String a, String b) => a + b;";
|
2018-10-31 19:51:52 +00:00
|
|
|
const Library& root_library = Library::Handle(LoadTestScript(kScript));
|
2015-09-02 00:18:55 +00:00
|
|
|
Isolate* isolate = thread->isolate();
|
2015-07-10 21:30:04 +00:00
|
|
|
|
|
|
|
const Class& one_byte_string_class =
|
|
|
|
Class::Handle(isolate->object_store()->one_byte_string_class());
|
|
|
|
EXPECT(!one_byte_string_class.IsNull());
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
Dart_Handle args[2];
|
|
|
|
{
|
|
|
|
TransitionVMToNative transition(thread);
|
|
|
|
args[0] = NewString("a");
|
|
|
|
args[1] = NewString("b");
|
|
|
|
}
|
2015-07-10 21:30:04 +00:00
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "foo", 2, &args[0]);
|
2015-07-10 21:30:04 +00:00
|
|
|
|
|
|
|
{
|
2015-09-02 00:18:55 +00:00
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
2015-07-10 21:30:04 +00:00
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2015-07-10 21:30:04 +00:00
|
|
|
// We should have no allocation samples.
|
|
|
|
EXPECT_EQ(0, profile.sample_count());
|
|
|
|
}
|
|
|
|
|
|
|
|
one_byte_string_class.SetTraceAllocation(true);
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "foo", 2, &args[0]);
|
2015-07-10 21:30:04 +00:00
|
|
|
|
|
|
|
{
|
2015-09-02 00:18:55 +00:00
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
2015-07-10 21:30:04 +00:00
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2015-07-10 21:30:04 +00:00
|
|
|
// We should still only have one allocation sample.
|
|
|
|
EXPECT_EQ(1, profile.sample_count());
|
2019-09-07 01:04:08 +00:00
|
|
|
ProfileStackWalker walker(&profile);
|
2015-07-10 21:30:04 +00:00
|
|
|
|
2019-09-07 01:04:08 +00:00
|
|
|
EXPECT_STREQ("String_concat", walker.VMTagName());
|
2019-06-28 16:44:57 +00:00
|
|
|
if (FLAG_enable_interpreter) {
|
|
|
|
EXPECT_STREQ("[Bytecode] _StringBase.+", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Bytecode] foo", walker.CurrentName());
|
|
|
|
EXPECT(!walker.Down());
|
|
|
|
} else {
|
|
|
|
EXPECT_STREQ("[Unoptimized] _StringBase.+", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Unoptimized] foo", walker.CurrentName());
|
|
|
|
EXPECT(!walker.Down());
|
|
|
|
}
|
2015-07-10 21:30:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
one_byte_string_class.SetTraceAllocation(false);
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "foo", 2, &args[0]);
|
2015-07-10 21:30:04 +00:00
|
|
|
|
|
|
|
{
|
2015-09-02 00:18:55 +00:00
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
2015-07-10 21:30:04 +00:00
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2015-07-10 21:30:04 +00:00
|
|
|
// We should still only have one allocation sample.
|
|
|
|
EXPECT_EQ(1, profile.sample_count());
|
|
|
|
}
|
|
|
|
|
|
|
|
one_byte_string_class.SetTraceAllocation(true);
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "foo", 2, &args[0]);
|
2015-07-10 21:30:04 +00:00
|
|
|
|
|
|
|
{
|
2015-09-02 00:18:55 +00:00
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
2015-07-10 21:30:04 +00:00
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2015-07-10 21:30:04 +00:00
|
|
|
// We should now have two allocation samples.
|
|
|
|
EXPECT_EQ(2, profile.sample_count());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
ISOLATE_UNIT_TEST_CASE(Profiler_StringInterpolation) {
|
2017-08-09 18:20:55 +00:00
|
|
|
EnableProfiler();
|
2015-08-06 21:21:55 +00:00
|
|
|
DisableNativeProfileScope dnps;
|
2016-03-30 21:24:08 +00:00
|
|
|
DisableBackgroundCompilationScope dbcs;
|
2015-07-10 21:30:04 +00:00
|
|
|
const char* kScript = "String foo(String a, String b) => '$a | $b';";
|
2018-10-31 19:51:52 +00:00
|
|
|
const Library& root_library = Library::Handle(LoadTestScript(kScript));
|
2015-09-02 00:18:55 +00:00
|
|
|
Isolate* isolate = thread->isolate();
|
2015-07-10 21:30:04 +00:00
|
|
|
|
|
|
|
const Class& one_byte_string_class =
|
|
|
|
Class::Handle(isolate->object_store()->one_byte_string_class());
|
|
|
|
EXPECT(!one_byte_string_class.IsNull());
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
Dart_Handle args[2];
|
|
|
|
{
|
|
|
|
TransitionVMToNative transition(thread);
|
|
|
|
args[0] = NewString("a");
|
|
|
|
args[1] = NewString("b");
|
|
|
|
}
|
2015-07-10 21:30:04 +00:00
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "foo", 2, &args[0]);
|
2015-07-10 21:30:04 +00:00
|
|
|
|
|
|
|
{
|
2015-09-02 00:18:55 +00:00
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
2015-07-10 21:30:04 +00:00
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2015-07-10 21:30:04 +00:00
|
|
|
// We should have no allocation samples.
|
|
|
|
EXPECT_EQ(0, profile.sample_count());
|
|
|
|
}
|
|
|
|
|
|
|
|
one_byte_string_class.SetTraceAllocation(true);
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "foo", 2, &args[0]);
|
2015-07-10 21:30:04 +00:00
|
|
|
|
|
|
|
{
|
2015-09-02 00:18:55 +00:00
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
2015-07-10 21:30:04 +00:00
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2015-07-10 21:30:04 +00:00
|
|
|
// We should still only have one allocation sample.
|
|
|
|
EXPECT_EQ(1, profile.sample_count());
|
2019-09-07 01:04:08 +00:00
|
|
|
ProfileStackWalker walker(&profile);
|
2015-07-10 21:30:04 +00:00
|
|
|
|
2019-09-07 01:04:08 +00:00
|
|
|
EXPECT_STREQ("OneByteString_allocate", walker.VMTagName());
|
2019-06-28 16:44:57 +00:00
|
|
|
if (FLAG_enable_interpreter) {
|
|
|
|
EXPECT_STREQ("[Bytecode] _OneByteString._allocate", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Bytecode] _OneByteString._concatAll",
|
|
|
|
walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Bytecode] _StringBase._interpolate", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Bytecode] foo", walker.CurrentName());
|
|
|
|
EXPECT(!walker.Down());
|
|
|
|
} else {
|
2019-08-15 21:10:47 +00:00
|
|
|
EXPECT_STREQ("[Unoptimized] String._allocate", walker.CurrentName());
|
2019-06-28 16:44:57 +00:00
|
|
|
EXPECT(walker.Down());
|
2019-08-15 21:10:47 +00:00
|
|
|
EXPECT_STREQ("[Unoptimized] String._concatAll", walker.CurrentName());
|
2019-06-28 16:44:57 +00:00
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Unoptimized] _StringBase._interpolate",
|
|
|
|
walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Unoptimized] foo", walker.CurrentName());
|
|
|
|
EXPECT(!walker.Down());
|
|
|
|
}
|
2015-07-10 21:30:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
one_byte_string_class.SetTraceAllocation(false);
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "foo", 2, &args[0]);
|
2015-07-10 21:30:04 +00:00
|
|
|
|
|
|
|
{
|
2015-09-02 00:18:55 +00:00
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
2015-07-10 21:30:04 +00:00
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2015-07-10 21:30:04 +00:00
|
|
|
// We should still only have one allocation sample.
|
|
|
|
EXPECT_EQ(1, profile.sample_count());
|
|
|
|
}
|
|
|
|
|
|
|
|
one_byte_string_class.SetTraceAllocation(true);
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "foo", 2, &args[0]);
|
2015-07-10 21:30:04 +00:00
|
|
|
|
|
|
|
{
|
2015-09-02 00:18:55 +00:00
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
2015-07-10 21:30:04 +00:00
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2015-07-10 21:30:04 +00:00
|
|
|
// We should now have two allocation samples.
|
|
|
|
EXPECT_EQ(2, profile.sample_count());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
ISOLATE_UNIT_TEST_CASE(Profiler_FunctionInline) {
|
2017-08-09 18:20:55 +00:00
|
|
|
EnableProfiler();
|
2015-08-06 21:21:55 +00:00
|
|
|
DisableNativeProfileScope dnps;
|
2016-01-13 00:45:00 +00:00
|
|
|
DisableBackgroundCompilationScope dbcs;
|
2018-11-05 18:26:02 +00:00
|
|
|
SetFlagScope<int> sfs(&FLAG_optimization_counter_threshold, 30000);
|
2019-08-16 22:39:52 +00:00
|
|
|
SetFlagScope<bool> sfs2(&FLAG_enable_interpreter, false);
|
2016-01-13 00:45:00 +00:00
|
|
|
|
2015-07-29 17:21:36 +00:00
|
|
|
const char* kScript =
|
|
|
|
"class A {\n"
|
|
|
|
" var a;\n"
|
|
|
|
" var b;\n"
|
|
|
|
"}\n"
|
|
|
|
"class B {\n"
|
|
|
|
" static choo(bool alloc) {\n"
|
|
|
|
" if (alloc) return new A();\n"
|
|
|
|
" return alloc && alloc && !alloc;\n"
|
|
|
|
" }\n"
|
|
|
|
" static foo(bool alloc) {\n"
|
|
|
|
" choo(alloc);\n"
|
|
|
|
" }\n"
|
|
|
|
" static boo(bool alloc) {\n"
|
|
|
|
" for (var i = 0; i < 50000; i++) {\n"
|
|
|
|
" foo(alloc);\n"
|
|
|
|
" }\n"
|
|
|
|
" }\n"
|
|
|
|
"}\n"
|
|
|
|
"main() {\n"
|
|
|
|
" B.boo(false);\n"
|
|
|
|
"}\n"
|
|
|
|
"mainA() {\n"
|
|
|
|
" B.boo(true);\n"
|
|
|
|
"}\n";
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
const Library& root_library = Library::Handle(LoadTestScript(kScript));
|
2015-07-29 17:21:36 +00:00
|
|
|
|
|
|
|
const Class& class_a = Class::Handle(GetClass(root_library, "A"));
|
|
|
|
EXPECT(!class_a.IsNull());
|
|
|
|
|
|
|
|
// Compile "main".
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "main");
|
2015-07-29 17:21:36 +00:00
|
|
|
// Compile "mainA".
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "mainA");
|
2015-07-29 17:21:36 +00:00
|
|
|
// At this point B.boo should be optimized and inlined B.foo and B.choo.
|
|
|
|
|
|
|
|
{
|
2015-08-24 18:29:44 +00:00
|
|
|
Thread* thread = Thread::Current();
|
|
|
|
Isolate* isolate = thread->isolate();
|
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
2015-07-29 17:21:36 +00:00
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2015-07-29 17:21:36 +00:00
|
|
|
// We should have no allocation samples.
|
|
|
|
EXPECT_EQ(0, profile.sample_count());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Turn on allocation tracing for A.
|
|
|
|
class_a.SetTraceAllocation(true);
|
|
|
|
|
|
|
|
// Allocate 50,000 instances of A.
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "mainA");
|
2015-07-29 17:21:36 +00:00
|
|
|
|
|
|
|
{
|
2015-08-24 18:29:44 +00:00
|
|
|
Thread* thread = Thread::Current();
|
|
|
|
Isolate* isolate = thread->isolate();
|
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
2015-07-29 17:21:36 +00:00
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2015-08-04 22:02:51 +00:00
|
|
|
// We should have 50,000 allocation samples.
|
|
|
|
EXPECT_EQ(50000, profile.sample_count());
|
2019-09-07 01:04:08 +00:00
|
|
|
{
|
|
|
|
ProfileStackWalker walker(&profile);
|
|
|
|
// We have two code objects: mainA and B.boo.
|
|
|
|
EXPECT_STREQ("DRT_AllocateObject", walker.VMTagName());
|
|
|
|
EXPECT_STREQ("[Stub] Allocate A", walker.CurrentName());
|
|
|
|
EXPECT_EQ(50000, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Optimized] B.boo", walker.CurrentName());
|
|
|
|
EXPECT_EQ(50000, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Unoptimized] mainA", walker.CurrentName());
|
|
|
|
EXPECT_EQ(50000, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT_EQ(0, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT(!walker.Down());
|
|
|
|
}
|
|
|
|
{
|
|
|
|
ProfileStackWalker walker(&profile, true);
|
|
|
|
// Inline expansion should show us the complete call chain:
|
|
|
|
// mainA -> B.boo -> B.foo -> B.choo.
|
|
|
|
EXPECT_STREQ("DRT_AllocateObject", walker.VMTagName());
|
|
|
|
EXPECT_STREQ("[Stub] Allocate A", walker.CurrentName());
|
|
|
|
EXPECT_EQ(50000, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("B.choo", walker.CurrentName());
|
|
|
|
EXPECT_EQ(50000, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("B.foo", walker.CurrentName());
|
|
|
|
EXPECT_EQ(50000, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT_EQ(0, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("B.boo", walker.CurrentName());
|
|
|
|
EXPECT_EQ(50000, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT_EQ(0, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("mainA", walker.CurrentName());
|
|
|
|
EXPECT_EQ(50000, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT_EQ(0, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT(!walker.Down());
|
|
|
|
}
|
2015-08-04 22:02:51 +00:00
|
|
|
}
|
2016-01-13 00:45:00 +00:00
|
|
|
}
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
ISOLATE_UNIT_TEST_CASE(Profiler_InliningIntervalBoundry) {
|
2016-01-13 00:45:00 +00:00
|
|
|
// 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.
|
|
|
|
|
2017-08-09 18:20:55 +00:00
|
|
|
EnableProfiler();
|
2016-01-13 00:45:00 +00:00
|
|
|
DisableNativeProfileScope dnps;
|
|
|
|
DisableBackgroundCompilationScope dbcs;
|
2018-11-05 18:26:02 +00:00
|
|
|
SetFlagScope<int> sfs(&FLAG_optimization_counter_threshold, 30000);
|
2019-08-16 22:39:52 +00:00
|
|
|
SetFlagScope<bool> sfs2(&FLAG_enable_interpreter, false);
|
2018-11-05 18:26:02 +00:00
|
|
|
|
2016-01-13 00:45:00 +00:00
|
|
|
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";
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
const Library& root_library = Library::Handle(LoadTestScript(kScript));
|
2016-01-13 00:45:00 +00:00
|
|
|
|
|
|
|
const Class& class_a = Class::Handle(GetClass(root_library, "A"));
|
|
|
|
EXPECT(!class_a.IsNull());
|
|
|
|
|
|
|
|
// Compile and optimize.
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "mainNoAlloc");
|
|
|
|
Invoke(root_library, "mainAlloc");
|
2016-01-13 00:45:00 +00:00
|
|
|
|
|
|
|
// 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);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2016-01-13 00:45:00 +00:00
|
|
|
// We should have no allocation samples.
|
|
|
|
EXPECT_EQ(0, profile.sample_count());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Turn on allocation tracing for A.
|
|
|
|
class_a.SetTraceAllocation(true);
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "mainAlloc");
|
2016-01-13 00:45:00 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
Thread* thread = Thread::Current();
|
|
|
|
Isolate* isolate = thread->isolate();
|
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2016-01-13 00:45:00 +00:00
|
|
|
EXPECT_EQ(1, profile.sample_count());
|
2019-09-07 01:04:08 +00:00
|
|
|
ProfileStackWalker walker(&profile, true);
|
2016-01-13 00:45:00 +00:00
|
|
|
|
|
|
|
// Inline expansion should show us the complete call chain:
|
2019-09-07 01:04:08 +00:00
|
|
|
EXPECT_STREQ("DRT_AllocateObject", walker.VMTagName());
|
2016-11-23 23:40:26 +00:00
|
|
|
EXPECT_STREQ("[Stub] Allocate A", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
2016-01-13 00:45:00 +00:00
|
|
|
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());
|
|
|
|
}
|
2015-07-29 17:21:36 +00:00
|
|
|
}
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
ISOLATE_UNIT_TEST_CASE(Profiler_ChainedSamples) {
|
2017-08-09 18:20:55 +00:00
|
|
|
EnableProfiler();
|
2015-08-24 18:29:44 +00:00
|
|
|
MaxProfileDepthScope mpds(32);
|
2015-08-24 19:02:31 +00:00
|
|
|
DisableNativeProfileScope dnps;
|
|
|
|
|
2015-08-24 18:29:44 +00:00
|
|
|
// Each sample holds 8 stack frames.
|
|
|
|
// This chain is 20 stack frames deep.
|
|
|
|
const char* kScript =
|
|
|
|
"class A {\n"
|
|
|
|
" var a;\n"
|
|
|
|
" var b;\n"
|
|
|
|
"}\n"
|
|
|
|
"class B {\n"
|
|
|
|
" static boo() {\n"
|
|
|
|
" return new A();\n"
|
|
|
|
" }\n"
|
|
|
|
"}\n"
|
|
|
|
"go() => init();\n"
|
|
|
|
"init() => secondInit();\n"
|
|
|
|
"secondInit() => apple();\n"
|
|
|
|
"apple() => banana();\n"
|
|
|
|
"banana() => cantaloupe();\n"
|
|
|
|
"cantaloupe() => dog();\n"
|
|
|
|
"dog() => elephant();\n"
|
|
|
|
"elephant() => fred();\n"
|
|
|
|
"fred() => granola();\n"
|
|
|
|
"granola() => haystack();\n"
|
|
|
|
"haystack() => ice();\n"
|
|
|
|
"ice() => jeep();\n"
|
|
|
|
"jeep() => kindle();\n"
|
|
|
|
"kindle() => lemon();\n"
|
|
|
|
"lemon() => mayo();\n"
|
|
|
|
"mayo() => napkin();\n"
|
|
|
|
"napkin() => orange();\n"
|
|
|
|
"orange() => B.boo();\n"
|
|
|
|
"main() {\n"
|
|
|
|
" return go();\n"
|
|
|
|
"}\n";
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
const Library& root_library = Library::Handle(LoadTestScript(kScript));
|
2015-08-24 18:29:44 +00:00
|
|
|
|
|
|
|
const Class& class_a = Class::Handle(GetClass(root_library, "A"));
|
|
|
|
EXPECT(!class_a.IsNull());
|
|
|
|
class_a.SetTraceAllocation(true);
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "main");
|
2015-08-24 18:29:44 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
Thread* thread = Thread::Current();
|
|
|
|
Isolate* isolate = thread->isolate();
|
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2015-08-24 18:29:44 +00:00
|
|
|
// We should have 1 allocation sample.
|
|
|
|
EXPECT_EQ(1, profile.sample_count());
|
2019-09-07 01:04:08 +00:00
|
|
|
ProfileStackWalker walker(&profile);
|
2015-08-24 18:29:44 +00:00
|
|
|
|
2019-09-07 01:04:08 +00:00
|
|
|
EXPECT_STREQ("DRT_AllocateObject", walker.VMTagName());
|
2019-06-28 16:44:57 +00:00
|
|
|
if (FLAG_enable_interpreter) {
|
|
|
|
EXPECT_STREQ("[Bytecode] B.boo", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Bytecode] orange", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Bytecode] napkin", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Bytecode] mayo", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Bytecode] lemon", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Bytecode] kindle", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Bytecode] jeep", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Bytecode] ice", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Bytecode] haystack", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Bytecode] granola", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Bytecode] fred", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Bytecode] elephant", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Bytecode] dog", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Bytecode] cantaloupe", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Bytecode] banana", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Bytecode] apple", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Bytecode] secondInit", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Bytecode] init", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Bytecode] go", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Bytecode] main", walker.CurrentName());
|
|
|
|
EXPECT(!walker.Down());
|
|
|
|
} else {
|
|
|
|
EXPECT_STREQ("[Stub] Allocate A", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Unoptimized] B.boo", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Unoptimized] orange", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Unoptimized] napkin", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Unoptimized] mayo", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Unoptimized] lemon", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Unoptimized] kindle", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Unoptimized] jeep", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Unoptimized] ice", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Unoptimized] haystack", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Unoptimized] granola", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Unoptimized] fred", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Unoptimized] elephant", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Unoptimized] dog", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Unoptimized] cantaloupe", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Unoptimized] banana", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Unoptimized] apple", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Unoptimized] secondInit", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Unoptimized] init", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Unoptimized] go", walker.CurrentName());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("[Unoptimized] main", walker.CurrentName());
|
|
|
|
EXPECT(!walker.Down());
|
|
|
|
}
|
2015-08-24 18:29:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
ISOLATE_UNIT_TEST_CASE(Profiler_BasicSourcePosition) {
|
2017-08-09 18:20:55 +00:00
|
|
|
EnableProfiler();
|
2016-03-02 20:02:28 +00:00
|
|
|
DisableNativeProfileScope dnps;
|
|
|
|
DisableBackgroundCompilationScope dbcs;
|
|
|
|
const char* kScript =
|
|
|
|
"class A {\n"
|
|
|
|
" var a;\n"
|
|
|
|
" var b;\n"
|
2019-07-30 12:01:06 +00:00
|
|
|
" @pragma('vm:never-inline') A() { }\n"
|
2016-03-02 20:02:28 +00:00
|
|
|
"}\n"
|
|
|
|
"class B {\n"
|
2019-07-30 12:01:06 +00:00
|
|
|
" @pragma('vm:prefer-inline')\n"
|
2016-03-02 20:02:28 +00:00
|
|
|
" static boo() {\n"
|
|
|
|
" return new A();\n"
|
|
|
|
" }\n"
|
|
|
|
"}\n"
|
|
|
|
"main() {\n"
|
|
|
|
" B.boo();\n"
|
|
|
|
"}\n";
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
const Library& root_library = Library::Handle(LoadTestScript(kScript));
|
2016-03-02 20:02:28 +00:00
|
|
|
|
|
|
|
const Class& class_a = Class::Handle(GetClass(root_library, "A"));
|
|
|
|
EXPECT(!class_a.IsNull());
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "main");
|
2016-03-02 20:02:28 +00:00
|
|
|
|
|
|
|
// Turn on allocation tracing for A.
|
|
|
|
class_a.SetTraceAllocation(true);
|
|
|
|
|
|
|
|
// Allocate one time.
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "main");
|
2016-03-02 20:02:28 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
Thread* thread = Thread::Current();
|
|
|
|
Isolate* isolate = thread->isolate();
|
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2016-03-02 20:02:28 +00:00
|
|
|
// We should have one allocation samples.
|
|
|
|
EXPECT_EQ(1, profile.sample_count());
|
2019-09-07 01:04:08 +00:00
|
|
|
ProfileStackWalker walker(&profile, true);
|
2016-03-02 20:02:28 +00:00
|
|
|
|
2019-09-07 01:04:08 +00:00
|
|
|
EXPECT_STREQ("DRT_AllocateObject", walker.VMTagName());
|
2019-06-28 16:44:57 +00:00
|
|
|
if (!FLAG_enable_interpreter) {
|
|
|
|
EXPECT_STREQ("[Stub] Allocate A", walker.CurrentName());
|
|
|
|
EXPECT_EQ(1, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
}
|
2016-03-02 20:02:28 +00:00
|
|
|
EXPECT_STREQ("B.boo", walker.CurrentName());
|
|
|
|
EXPECT_EQ(1, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT_STREQ("A", walker.CurrentToken());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("main", walker.CurrentName());
|
|
|
|
EXPECT_EQ(1, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT_EQ(0, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT_STREQ("boo", walker.CurrentToken());
|
|
|
|
EXPECT(!walker.Down());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
ISOLATE_UNIT_TEST_CASE(Profiler_BasicSourcePositionOptimized) {
|
2017-08-09 18:20:55 +00:00
|
|
|
EnableProfiler();
|
2016-03-02 20:02:28 +00:00
|
|
|
DisableNativeProfileScope dnps;
|
|
|
|
DisableBackgroundCompilationScope dbcs;
|
|
|
|
// Optimize quickly.
|
2019-08-16 22:39:52 +00:00
|
|
|
SetFlagScope<int> sfs2(&FLAG_optimization_counter_threshold, 5);
|
|
|
|
SetFlagScope<bool> sfs3(&FLAG_enable_interpreter, false);
|
2016-03-02 20:02:28 +00:00
|
|
|
const char* kScript =
|
|
|
|
"class A {\n"
|
|
|
|
" var a;\n"
|
|
|
|
" var b;\n"
|
2019-07-30 12:01:06 +00:00
|
|
|
" @pragma('vm:never-inline') A() { }\n"
|
2016-03-02 20:02:28 +00:00
|
|
|
"}\n"
|
|
|
|
"class B {\n"
|
2019-07-30 12:01:06 +00:00
|
|
|
" @pragma('vm:prefer-inline')\n"
|
2016-03-02 20:02:28 +00:00
|
|
|
" static boo() {\n"
|
|
|
|
" return new A();\n"
|
|
|
|
" }\n"
|
|
|
|
"}\n"
|
|
|
|
"main() {\n"
|
|
|
|
" B.boo();\n"
|
|
|
|
"}\n";
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
const Library& root_library = Library::Handle(LoadTestScript(kScript));
|
2016-03-02 20:02:28 +00:00
|
|
|
|
|
|
|
const Class& class_a = Class::Handle(GetClass(root_library, "A"));
|
|
|
|
EXPECT(!class_a.IsNull());
|
|
|
|
|
|
|
|
const Function& main = Function::Handle(GetFunction(root_library, "main"));
|
|
|
|
EXPECT(!main.IsNull());
|
|
|
|
|
|
|
|
// Warm up function.
|
|
|
|
while (true) {
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "main");
|
2016-03-02 20:02:28 +00:00
|
|
|
const Code& code = Code::Handle(main.CurrentCode());
|
|
|
|
if (code.is_optimized()) {
|
|
|
|
// Warmed up.
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Turn on allocation tracing for A.
|
|
|
|
class_a.SetTraceAllocation(true);
|
|
|
|
|
|
|
|
// Allocate one time.
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "main");
|
2016-03-02 20:02:28 +00:00
|
|
|
|
|
|
|
// Still optimized.
|
|
|
|
const Code& code = Code::Handle(main.CurrentCode());
|
|
|
|
EXPECT(code.is_optimized());
|
|
|
|
|
|
|
|
{
|
|
|
|
Thread* thread = Thread::Current();
|
|
|
|
Isolate* isolate = thread->isolate();
|
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2016-03-02 20:02:28 +00:00
|
|
|
// We should have one allocation samples.
|
|
|
|
EXPECT_EQ(1, profile.sample_count());
|
2019-09-07 01:04:08 +00:00
|
|
|
ProfileStackWalker walker(&profile, true);
|
2016-03-02 20:02:28 +00:00
|
|
|
|
|
|
|
// Move down from the root.
|
2019-09-07 01:04:08 +00:00
|
|
|
EXPECT_STREQ("DRT_AllocateObject", walker.VMTagName());
|
2016-11-23 23:40:26 +00:00
|
|
|
EXPECT_STREQ("[Stub] Allocate A", walker.CurrentName());
|
|
|
|
EXPECT_EQ(1, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT(walker.Down());
|
2016-03-02 20:02:28 +00:00
|
|
|
EXPECT_STREQ("B.boo", walker.CurrentName());
|
|
|
|
EXPECT_EQ(1, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT_STREQ("A", walker.CurrentToken());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("main", walker.CurrentName());
|
|
|
|
EXPECT_EQ(1, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT_EQ(0, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT_STREQ("boo", walker.CurrentToken());
|
|
|
|
EXPECT(!walker.Down());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
ISOLATE_UNIT_TEST_CASE(Profiler_SourcePosition) {
|
2017-08-09 18:20:55 +00:00
|
|
|
EnableProfiler();
|
2016-03-02 20:02:28 +00:00
|
|
|
DisableNativeProfileScope dnps;
|
|
|
|
DisableBackgroundCompilationScope dbcs;
|
|
|
|
const char* kScript =
|
|
|
|
"class A {\n"
|
|
|
|
" var a;\n"
|
|
|
|
" var b;\n"
|
2019-07-30 12:01:06 +00:00
|
|
|
" @pragma('vm:never-inline') A() { }\n"
|
2016-03-02 20:02:28 +00:00
|
|
|
"}\n"
|
|
|
|
"class B {\n"
|
2019-07-30 12:01:06 +00:00
|
|
|
" @pragma('vm:never-inline')\n"
|
2016-03-02 20:02:28 +00:00
|
|
|
" static oats() {\n"
|
|
|
|
" return boo();\n"
|
|
|
|
" }\n"
|
2019-07-30 12:01:06 +00:00
|
|
|
" @pragma('vm:prefer-inline')\n"
|
2016-03-02 20:02:28 +00:00
|
|
|
" static boo() {\n"
|
|
|
|
" return new A();\n"
|
|
|
|
" }\n"
|
|
|
|
"}\n"
|
|
|
|
"class C {\n"
|
2019-07-30 12:01:06 +00:00
|
|
|
" @pragma('vm:never-inline') bacon() {\n"
|
2016-03-02 20:02:28 +00:00
|
|
|
" return fox();\n"
|
|
|
|
" }\n"
|
2019-07-30 12:01:06 +00:00
|
|
|
" @pragma('vm:prefer-inline') fox() {\n"
|
2016-03-02 20:02:28 +00:00
|
|
|
" return B.oats();\n"
|
|
|
|
" }\n"
|
|
|
|
"}\n"
|
|
|
|
"main() {\n"
|
|
|
|
" new C()..bacon();\n"
|
|
|
|
"}\n";
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
const Library& root_library = Library::Handle(LoadTestScript(kScript));
|
2016-03-02 20:02:28 +00:00
|
|
|
|
|
|
|
const Class& class_a = Class::Handle(GetClass(root_library, "A"));
|
|
|
|
EXPECT(!class_a.IsNull());
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "main");
|
2016-03-02 20:02:28 +00:00
|
|
|
|
|
|
|
// Turn on allocation tracing for A.
|
|
|
|
class_a.SetTraceAllocation(true);
|
|
|
|
|
|
|
|
// Allocate one time.
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "main");
|
2016-03-02 20:02:28 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
Thread* thread = Thread::Current();
|
|
|
|
Isolate* isolate = thread->isolate();
|
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2016-03-02 20:02:28 +00:00
|
|
|
// We should have one allocation samples.
|
|
|
|
EXPECT_EQ(1, profile.sample_count());
|
2019-09-07 01:04:08 +00:00
|
|
|
ProfileStackWalker walker(&profile, true);
|
2016-03-02 20:02:28 +00:00
|
|
|
|
2019-09-07 01:04:08 +00:00
|
|
|
EXPECT_STREQ("DRT_AllocateObject", walker.VMTagName());
|
2019-06-28 16:44:57 +00:00
|
|
|
if (!FLAG_enable_interpreter) {
|
|
|
|
EXPECT_STREQ("[Stub] Allocate A", walker.CurrentName());
|
|
|
|
EXPECT_EQ(1, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
}
|
2016-03-02 20:02:28 +00:00
|
|
|
EXPECT_STREQ("B.boo", walker.CurrentName());
|
|
|
|
EXPECT_EQ(1, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT_STREQ("A", walker.CurrentToken());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("B.oats", walker.CurrentName());
|
|
|
|
EXPECT_EQ(1, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT_EQ(0, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT_STREQ("boo", walker.CurrentToken());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("C.fox", walker.CurrentName());
|
|
|
|
EXPECT_EQ(1, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT_EQ(0, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT_STREQ("oats", walker.CurrentToken());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("C.bacon", walker.CurrentName());
|
|
|
|
EXPECT_EQ(1, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT_EQ(0, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT_STREQ("fox", walker.CurrentToken());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("main", walker.CurrentName());
|
|
|
|
EXPECT_EQ(1, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT_EQ(0, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT_STREQ("bacon", walker.CurrentToken());
|
|
|
|
EXPECT(!walker.Down());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
ISOLATE_UNIT_TEST_CASE(Profiler_SourcePositionOptimized) {
|
2017-08-09 18:20:55 +00:00
|
|
|
EnableProfiler();
|
2016-03-02 20:02:28 +00:00
|
|
|
DisableNativeProfileScope dnps;
|
|
|
|
DisableBackgroundCompilationScope dbcs;
|
|
|
|
// Optimize quickly.
|
2019-08-16 22:39:52 +00:00
|
|
|
SetFlagScope<int> sfs2(&FLAG_optimization_counter_threshold, 5);
|
|
|
|
SetFlagScope<bool> sfs3(&FLAG_enable_interpreter, false);
|
2016-03-02 20:02:28 +00:00
|
|
|
|
|
|
|
const char* kScript =
|
|
|
|
"class A {\n"
|
|
|
|
" var a;\n"
|
|
|
|
" var b;\n"
|
2019-07-30 12:01:06 +00:00
|
|
|
" @pragma('vm:never-inline') A() { }\n"
|
2016-03-02 20:02:28 +00:00
|
|
|
"}\n"
|
|
|
|
"class B {\n"
|
2019-07-30 12:01:06 +00:00
|
|
|
" @pragma('vm:never-inline')\n"
|
2016-03-02 20:02:28 +00:00
|
|
|
" static oats() {\n"
|
|
|
|
" return boo();\n"
|
|
|
|
" }\n"
|
2019-07-30 12:01:06 +00:00
|
|
|
" @pragma('vm:prefer-inline')\n"
|
2016-03-02 20:02:28 +00:00
|
|
|
" static boo() {\n"
|
|
|
|
" return new A();\n"
|
|
|
|
" }\n"
|
|
|
|
"}\n"
|
|
|
|
"class C {\n"
|
2019-07-30 12:01:06 +00:00
|
|
|
" @pragma('vm:never-inline') bacon() {\n"
|
2016-03-02 20:02:28 +00:00
|
|
|
" return fox();\n"
|
|
|
|
" }\n"
|
2019-07-30 12:01:06 +00:00
|
|
|
" @pragma('vm:prefer-inline') fox() {\n"
|
2016-03-02 20:02:28 +00:00
|
|
|
" return B.oats();\n"
|
|
|
|
" }\n"
|
|
|
|
"}\n"
|
|
|
|
"main() {\n"
|
|
|
|
" new C()..bacon();\n"
|
|
|
|
"}\n";
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
const Library& root_library = Library::Handle(LoadTestScript(kScript));
|
2016-03-02 20:02:28 +00:00
|
|
|
|
|
|
|
const Class& class_a = Class::Handle(GetClass(root_library, "A"));
|
|
|
|
EXPECT(!class_a.IsNull());
|
|
|
|
|
|
|
|
const Function& main = Function::Handle(GetFunction(root_library, "main"));
|
|
|
|
EXPECT(!main.IsNull());
|
|
|
|
|
|
|
|
// Warm up function.
|
|
|
|
while (true) {
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "main");
|
2016-03-02 20:02:28 +00:00
|
|
|
const Code& code = Code::Handle(main.CurrentCode());
|
|
|
|
if (code.is_optimized()) {
|
|
|
|
// Warmed up.
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Turn on allocation tracing for A.
|
|
|
|
class_a.SetTraceAllocation(true);
|
|
|
|
|
|
|
|
// Allocate one time.
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "main");
|
2016-03-02 20:02:28 +00:00
|
|
|
|
|
|
|
// Still optimized.
|
|
|
|
const Code& code = Code::Handle(main.CurrentCode());
|
|
|
|
EXPECT(code.is_optimized());
|
|
|
|
|
|
|
|
{
|
|
|
|
Thread* thread = Thread::Current();
|
|
|
|
Isolate* isolate = thread->isolate();
|
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2016-03-02 20:02:28 +00:00
|
|
|
// We should have one allocation samples.
|
|
|
|
EXPECT_EQ(1, profile.sample_count());
|
2019-09-07 01:04:08 +00:00
|
|
|
ProfileStackWalker walker(&profile, true);
|
2016-03-02 20:02:28 +00:00
|
|
|
|
2019-09-07 01:04:08 +00:00
|
|
|
EXPECT_STREQ("DRT_AllocateObject", walker.VMTagName());
|
2016-11-23 23:40:26 +00:00
|
|
|
EXPECT_STREQ("[Stub] Allocate A", walker.CurrentName());
|
|
|
|
EXPECT_EQ(1, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT(walker.Down());
|
2016-03-02 20:02:28 +00:00
|
|
|
EXPECT_STREQ("B.boo", walker.CurrentName());
|
|
|
|
EXPECT_EQ(1, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT_STREQ("A", walker.CurrentToken());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("B.oats", walker.CurrentName());
|
|
|
|
EXPECT_EQ(1, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT_EQ(0, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT_STREQ("boo", walker.CurrentToken());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("C.fox", walker.CurrentName());
|
|
|
|
EXPECT_EQ(1, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT_EQ(0, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT_STREQ("oats", walker.CurrentToken());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("C.bacon", walker.CurrentName());
|
|
|
|
EXPECT_EQ(1, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT_EQ(0, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT_STREQ("fox", walker.CurrentToken());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("main", walker.CurrentName());
|
|
|
|
EXPECT_EQ(1, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT_EQ(0, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT_STREQ("bacon", walker.CurrentToken());
|
|
|
|
EXPECT(!walker.Down());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
ISOLATE_UNIT_TEST_CASE(Profiler_BinaryOperatorSourcePosition) {
|
2017-08-09 18:20:55 +00:00
|
|
|
EnableProfiler();
|
2016-03-02 20:02:28 +00:00
|
|
|
DisableNativeProfileScope dnps;
|
|
|
|
DisableBackgroundCompilationScope dbcs;
|
|
|
|
const char* kScript =
|
|
|
|
"class A {\n"
|
|
|
|
" var a;\n"
|
|
|
|
" var b;\n"
|
2019-07-30 12:01:06 +00:00
|
|
|
" @pragma('vm:never-inline') A() { }\n"
|
2016-03-02 20:02:28 +00:00
|
|
|
"}\n"
|
|
|
|
"class B {\n"
|
2019-07-30 12:01:06 +00:00
|
|
|
" @pragma('vm:never-inline')\n"
|
2016-03-02 20:02:28 +00:00
|
|
|
" static oats() {\n"
|
|
|
|
" return boo();\n"
|
|
|
|
" }\n"
|
2019-07-30 12:01:06 +00:00
|
|
|
" @pragma('vm:prefer-inline')\n"
|
2016-03-02 20:02:28 +00:00
|
|
|
" static boo() {\n"
|
|
|
|
" return new A();\n"
|
|
|
|
" }\n"
|
|
|
|
"}\n"
|
|
|
|
"class C {\n"
|
2019-07-30 12:01:06 +00:00
|
|
|
" @pragma('vm:never-inline') bacon() {\n"
|
2016-03-02 20:02:28 +00:00
|
|
|
" return this + this;\n"
|
|
|
|
" }\n"
|
2019-07-30 12:01:06 +00:00
|
|
|
" @pragma('vm:prefer-inline') operator+(C other) {\n"
|
2016-03-02 20:02:28 +00:00
|
|
|
" return fox();\n"
|
|
|
|
" }\n"
|
2019-07-30 12:01:06 +00:00
|
|
|
" @pragma('vm:prefer-inline') fox() {\n"
|
2016-03-02 20:02:28 +00:00
|
|
|
" return B.oats();\n"
|
|
|
|
" }\n"
|
|
|
|
"}\n"
|
|
|
|
"main() {\n"
|
|
|
|
" new C()..bacon();\n"
|
|
|
|
"}\n";
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
const Library& root_library = Library::Handle(LoadTestScript(kScript));
|
2016-03-02 20:02:28 +00:00
|
|
|
|
|
|
|
const Class& class_a = Class::Handle(GetClass(root_library, "A"));
|
|
|
|
EXPECT(!class_a.IsNull());
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "main");
|
2016-03-02 20:02:28 +00:00
|
|
|
|
|
|
|
// Turn on allocation tracing for A.
|
|
|
|
class_a.SetTraceAllocation(true);
|
|
|
|
|
|
|
|
// Allocate one time.
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "main");
|
2016-03-02 20:02:28 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
Thread* thread = Thread::Current();
|
|
|
|
Isolate* isolate = thread->isolate();
|
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2016-03-02 20:02:28 +00:00
|
|
|
// We should have one allocation samples.
|
|
|
|
EXPECT_EQ(1, profile.sample_count());
|
2019-09-07 01:04:08 +00:00
|
|
|
ProfileStackWalker walker(&profile, true);
|
2016-03-02 20:02:28 +00:00
|
|
|
|
2019-09-07 01:04:08 +00:00
|
|
|
EXPECT_STREQ("DRT_AllocateObject", walker.VMTagName());
|
2019-06-28 16:44:57 +00:00
|
|
|
if (!FLAG_enable_interpreter) {
|
|
|
|
EXPECT_STREQ("[Stub] Allocate A", walker.CurrentName());
|
|
|
|
EXPECT_EQ(1, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
}
|
2016-03-02 20:02:28 +00:00
|
|
|
EXPECT_STREQ("B.boo", walker.CurrentName());
|
|
|
|
EXPECT_EQ(1, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT_STREQ("A", walker.CurrentToken());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("B.oats", walker.CurrentName());
|
|
|
|
EXPECT_EQ(1, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT_EQ(0, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT_STREQ("boo", walker.CurrentToken());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("C.fox", walker.CurrentName());
|
|
|
|
EXPECT_EQ(1, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT_EQ(0, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT_STREQ("oats", walker.CurrentToken());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("C.+", walker.CurrentName());
|
|
|
|
EXPECT_EQ(1, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT_EQ(0, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT_STREQ("fox", walker.CurrentToken());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("C.bacon", walker.CurrentName());
|
|
|
|
EXPECT_EQ(1, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT_EQ(0, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT_STREQ("+", walker.CurrentToken());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("main", walker.CurrentName());
|
|
|
|
EXPECT_EQ(1, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT_EQ(0, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT_STREQ("bacon", walker.CurrentToken());
|
|
|
|
EXPECT(!walker.Down());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
ISOLATE_UNIT_TEST_CASE(Profiler_BinaryOperatorSourcePositionOptimized) {
|
2017-08-09 18:20:55 +00:00
|
|
|
EnableProfiler();
|
2016-03-02 20:02:28 +00:00
|
|
|
DisableNativeProfileScope dnps;
|
|
|
|
DisableBackgroundCompilationScope dbcs;
|
|
|
|
// Optimize quickly.
|
2019-08-16 22:39:52 +00:00
|
|
|
SetFlagScope<int> sfs2(&FLAG_optimization_counter_threshold, 5);
|
|
|
|
SetFlagScope<bool> sfs3(&FLAG_enable_interpreter, false);
|
2016-03-02 20:02:28 +00:00
|
|
|
|
|
|
|
const char* kScript =
|
|
|
|
"class A {\n"
|
|
|
|
" var a;\n"
|
|
|
|
" var b;\n"
|
2019-07-30 12:01:06 +00:00
|
|
|
" @pragma('vm:never-inline') A() { }\n"
|
2016-03-02 20:02:28 +00:00
|
|
|
"}\n"
|
|
|
|
"class B {\n"
|
2019-07-30 12:01:06 +00:00
|
|
|
" @pragma('vm:never-inline')\n"
|
2016-03-02 20:02:28 +00:00
|
|
|
" static oats() {\n"
|
|
|
|
" return boo();\n"
|
|
|
|
" }\n"
|
2019-07-30 12:01:06 +00:00
|
|
|
" @pragma('vm:prefer-inline')\n"
|
2016-03-02 20:02:28 +00:00
|
|
|
" static boo() {\n"
|
|
|
|
" return new A();\n"
|
|
|
|
" }\n"
|
|
|
|
"}\n"
|
|
|
|
"class C {\n"
|
2019-07-30 12:01:06 +00:00
|
|
|
" @pragma('vm:never-inline') bacon() {\n"
|
2016-03-02 20:02:28 +00:00
|
|
|
" return this + this;\n"
|
|
|
|
" }\n"
|
2019-07-30 12:01:06 +00:00
|
|
|
" @pragma('vm:prefer-inline') operator+(C other) {\n"
|
2016-03-02 20:02:28 +00:00
|
|
|
" return fox();\n"
|
|
|
|
" }\n"
|
2019-07-30 12:01:06 +00:00
|
|
|
" @pragma('vm:prefer-inline') fox() {\n"
|
2016-03-02 20:02:28 +00:00
|
|
|
" return B.oats();\n"
|
|
|
|
" }\n"
|
|
|
|
"}\n"
|
|
|
|
"main() {\n"
|
|
|
|
" new C()..bacon();\n"
|
|
|
|
"}\n";
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
const Library& root_library = Library::Handle(LoadTestScript(kScript));
|
2016-03-02 20:02:28 +00:00
|
|
|
|
|
|
|
const Class& class_a = Class::Handle(GetClass(root_library, "A"));
|
|
|
|
EXPECT(!class_a.IsNull());
|
|
|
|
|
|
|
|
const Function& main = Function::Handle(GetFunction(root_library, "main"));
|
|
|
|
EXPECT(!main.IsNull());
|
|
|
|
|
|
|
|
// Warm up function.
|
|
|
|
while (true) {
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "main");
|
2016-03-02 20:02:28 +00:00
|
|
|
const Code& code = Code::Handle(main.CurrentCode());
|
|
|
|
if (code.is_optimized()) {
|
|
|
|
// Warmed up.
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Turn on allocation tracing for A.
|
|
|
|
class_a.SetTraceAllocation(true);
|
|
|
|
|
|
|
|
// Allocate one time.
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "main");
|
2016-03-02 20:02:28 +00:00
|
|
|
|
|
|
|
// Still optimized.
|
|
|
|
const Code& code = Code::Handle(main.CurrentCode());
|
|
|
|
EXPECT(code.is_optimized());
|
|
|
|
|
|
|
|
{
|
|
|
|
Thread* thread = Thread::Current();
|
|
|
|
Isolate* isolate = thread->isolate();
|
|
|
|
StackZone zone(thread);
|
|
|
|
HANDLESCOPE(thread);
|
|
|
|
Profile profile(isolate);
|
2017-02-24 21:56:06 +00:00
|
|
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
2019-09-07 01:04:08 +00:00
|
|
|
profile.Build(thread, &filter, Profiler::sample_buffer());
|
2016-03-02 20:02:28 +00:00
|
|
|
// We should have one allocation samples.
|
|
|
|
EXPECT_EQ(1, profile.sample_count());
|
2019-09-07 01:04:08 +00:00
|
|
|
ProfileStackWalker walker(&profile, true);
|
2016-03-02 20:02:28 +00:00
|
|
|
|
2019-09-07 01:04:08 +00:00
|
|
|
EXPECT_STREQ("DRT_AllocateObject", walker.VMTagName());
|
2019-08-16 22:39:52 +00:00
|
|
|
if (!FLAG_enable_interpreter) {
|
|
|
|
EXPECT_STREQ("[Stub] Allocate A", walker.CurrentName());
|
|
|
|
EXPECT_EQ(1, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
}
|
2016-03-02 20:02:28 +00:00
|
|
|
EXPECT_STREQ("B.boo", walker.CurrentName());
|
|
|
|
EXPECT_EQ(1, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT_STREQ("A", walker.CurrentToken());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("B.oats", walker.CurrentName());
|
|
|
|
EXPECT_EQ(1, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT_EQ(0, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT_STREQ("boo", walker.CurrentToken());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("C.fox", walker.CurrentName());
|
|
|
|
EXPECT_EQ(1, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT_EQ(0, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT_STREQ("oats", walker.CurrentToken());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("C.+", walker.CurrentName());
|
|
|
|
EXPECT_EQ(1, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT_EQ(0, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT_STREQ("fox", walker.CurrentToken());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("C.bacon", walker.CurrentName());
|
|
|
|
EXPECT_EQ(1, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT_EQ(0, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT_STREQ("+", walker.CurrentToken());
|
|
|
|
EXPECT(walker.Down());
|
|
|
|
EXPECT_STREQ("main", walker.CurrentName());
|
|
|
|
EXPECT_EQ(1, walker.CurrentInclusiveTicks());
|
|
|
|
EXPECT_EQ(0, walker.CurrentExclusiveTicks());
|
|
|
|
EXPECT_STREQ("bacon", walker.CurrentToken());
|
|
|
|
EXPECT(!walker.Down());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-08 21:54:47 +00:00
|
|
|
static void InsertFakeSample(SampleBuffer* sample_buffer, uword* pc_offsets) {
|
2016-03-14 14:04:23 +00:00
|
|
|
ASSERT(sample_buffer != NULL);
|
|
|
|
Isolate* isolate = Isolate::Current();
|
|
|
|
Sample* sample = sample_buffer->ReserveSample();
|
|
|
|
ASSERT(sample != NULL);
|
2017-02-24 21:56:06 +00:00
|
|
|
sample->Init(isolate->main_port(), OS::GetCurrentMonotonicMicros(),
|
2016-03-14 14:04:23 +00:00
|
|
|
OSThread::Current()->trace_id());
|
2016-03-22 15:42:06 +00:00
|
|
|
sample->set_thread_task(Thread::kMutatorTask);
|
2016-03-14 14:04:23 +00:00
|
|
|
|
|
|
|
intptr_t i = 0;
|
|
|
|
while (pc_offsets[i] != 0) {
|
|
|
|
// When we collect a real stack trace, all PCs collected aside from the
|
|
|
|
// executing one (i == 0) are actually return addresses. Return addresses
|
|
|
|
// are one byte beyond the call instruction that is executing. The profiler
|
|
|
|
// accounts for this and subtracts one from these addresses when querying
|
|
|
|
// inline and token position ranges. To be consistent with real stack
|
|
|
|
// traces, we add one byte to all PCs except the executing one.
|
|
|
|
// See OffsetForPC in profiler_service.cc for more context.
|
|
|
|
const intptr_t return_address_offset = i > 0 ? 1 : 0;
|
|
|
|
sample->SetAt(i, pc_offsets[i] + return_address_offset);
|
|
|
|
i++;
|
|
|
|
}
|
2016-03-16 18:17:53 +00:00
|
|
|
sample->SetAt(i, 0);
|
2016-03-14 14:04:23 +00:00
|
|
|
}
|
|
|
|
|
2017-07-13 15:08:33 +00:00
|
|
|
static uword FindPCForTokenPosition(const Code& code, TokenPosition tp) {
|
2017-02-07 20:52:21 +00:00
|
|
|
GrowableArray<const Function*> functions;
|
|
|
|
GrowableArray<TokenPosition> token_positions;
|
|
|
|
for (intptr_t pc_offset = 0; pc_offset < code.Size(); pc_offset++) {
|
2017-02-10 02:03:41 +00:00
|
|
|
code.GetInlinedFunctionsAtInstruction(pc_offset, &functions,
|
|
|
|
&token_positions);
|
2017-02-07 20:52:21 +00:00
|
|
|
if (token_positions[0] == tp) {
|
|
|
|
return code.PayloadStart() + pc_offset;
|
2016-03-14 14:04:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
ISOLATE_UNIT_TEST_CASE(Profiler_GetSourceReport) {
|
2017-08-09 18:20:55 +00:00
|
|
|
EnableProfiler();
|
2016-03-14 14:04:23 +00:00
|
|
|
const char* kScript =
|
|
|
|
"doWork(i) => i * i;\n"
|
|
|
|
"main() {\n"
|
|
|
|
" var sum = 0;\n"
|
|
|
|
" for (var i = 0; i < 100; i++) {\n"
|
|
|
|
" sum += doWork(i);\n"
|
|
|
|
" }\n"
|
|
|
|
" return sum;\n"
|
|
|
|
"}\n";
|
|
|
|
|
|
|
|
// Token position of * in `i * i`.
|
2018-11-05 18:26:02 +00:00
|
|
|
const TokenPosition squarePosition = TokenPosition(15);
|
2016-03-14 14:04:23 +00:00
|
|
|
|
|
|
|
// Token position of the call to `doWork`.
|
2018-11-05 18:26:02 +00:00
|
|
|
const TokenPosition callPosition = TokenPosition(90);
|
2016-03-14 14:04:23 +00:00
|
|
|
|
|
|
|
DisableNativeProfileScope dnps;
|
|
|
|
// Disable profiling for this thread.
|
|
|
|
DisableThreadInterruptsScope dtis(Thread::Current());
|
|
|
|
|
2016-04-07 16:12:56 +00:00
|
|
|
DisableBackgroundCompilationScope dbcs;
|
|
|
|
|
2016-03-14 14:04:23 +00:00
|
|
|
SampleBuffer* sample_buffer = Profiler::sample_buffer();
|
|
|
|
EXPECT(sample_buffer != NULL);
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
const Library& root_library = Library::Handle(LoadTestScript(kScript));
|
2016-03-14 14:04:23 +00:00
|
|
|
|
|
|
|
// Invoke main so that it gets compiled.
|
2018-10-31 19:51:52 +00:00
|
|
|
Invoke(root_library, "main");
|
2016-03-14 14:04:23 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
// Clear the profile for this isolate.
|
|
|
|
ClearProfileVisitor cpv(Isolate::Current());
|
|
|
|
sample_buffer->VisitSamples(&cpv);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Query the code object for main and determine the PC at some token
|
|
|
|
// positions.
|
|
|
|
const Function& main = Function::Handle(GetFunction(root_library, "main"));
|
|
|
|
EXPECT(!main.IsNull());
|
|
|
|
|
|
|
|
const Function& do_work =
|
|
|
|
Function::Handle(GetFunction(root_library, "doWork"));
|
|
|
|
EXPECT(!do_work.IsNull());
|
|
|
|
|
|
|
|
const Script& script = Script::Handle(main.script());
|
|
|
|
EXPECT(!script.IsNull());
|
|
|
|
|
|
|
|
const Code& main_code = Code::Handle(main.CurrentCode());
|
|
|
|
EXPECT(!main_code.IsNull());
|
|
|
|
|
|
|
|
const Code& do_work_code = Code::Handle(do_work.CurrentCode());
|
|
|
|
EXPECT(!do_work_code.IsNull());
|
|
|
|
|
|
|
|
// Dump code source map.
|
2017-02-07 20:52:21 +00:00
|
|
|
do_work_code.DumpSourcePositions();
|
|
|
|
main_code.DumpSourcePositions();
|
2016-03-14 14:04:23 +00:00
|
|
|
|
|
|
|
// Look up some source token position's pc.
|
2017-02-07 20:52:21 +00:00
|
|
|
uword squarePositionPc = FindPCForTokenPosition(do_work_code, squarePosition);
|
2016-03-14 14:04:23 +00:00
|
|
|
EXPECT(squarePositionPc != 0);
|
|
|
|
|
2017-02-07 20:52:21 +00:00
|
|
|
uword callPositionPc = FindPCForTokenPosition(main_code, callPosition);
|
2016-03-14 14:04:23 +00:00
|
|
|
EXPECT(callPositionPc != 0);
|
|
|
|
|
|
|
|
// Look up some classifying token position's pc.
|
2017-02-07 20:52:21 +00:00
|
|
|
uword controlFlowPc =
|
|
|
|
FindPCForTokenPosition(do_work_code, TokenPosition::kControlFlow);
|
2016-03-14 14:04:23 +00:00
|
|
|
EXPECT(controlFlowPc != 0);
|
|
|
|
|
|
|
|
// Insert fake samples.
|
|
|
|
|
|
|
|
// Sample 1:
|
|
|
|
// squarePositionPc exclusive.
|
|
|
|
// callPositionPc inclusive.
|
2016-11-08 21:54:47 +00:00
|
|
|
uword sample1[] = {squarePositionPc, // doWork.
|
|
|
|
callPositionPc, // main.
|
|
|
|
0};
|
2016-03-14 14:04:23 +00:00
|
|
|
|
|
|
|
// Sample 2:
|
|
|
|
// squarePositionPc exclusive.
|
|
|
|
uword sample2[] = {
|
2016-11-08 21:54:47 +00:00
|
|
|
squarePositionPc, // doWork.
|
|
|
|
0,
|
2016-03-14 14:04:23 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Sample 3:
|
|
|
|
// controlFlowPc exclusive.
|
|
|
|
// callPositionPc inclusive.
|
2016-11-08 21:54:47 +00:00
|
|
|
uword sample3[] = {controlFlowPc, // doWork.
|
|
|
|
callPositionPc, // main.
|
|
|
|
0};
|
2016-03-14 14:04:23 +00:00
|
|
|
|
|
|
|
InsertFakeSample(sample_buffer, &sample1[0]);
|
|
|
|
InsertFakeSample(sample_buffer, &sample2[0]);
|
|
|
|
InsertFakeSample(sample_buffer, &sample3[0]);
|
|
|
|
|
|
|
|
// Generate source report for main.
|
|
|
|
JSONStream js;
|
2017-08-08 00:51:21 +00:00
|
|
|
{
|
|
|
|
SourceReport sourceReport(SourceReport::kProfile);
|
|
|
|
sourceReport.PrintJSON(&js, script, do_work.token_pos(),
|
|
|
|
main.end_token_pos());
|
|
|
|
}
|
2016-03-14 14:04:23 +00:00
|
|
|
|
|
|
|
// Verify positions in do_work.
|
2018-11-05 18:26:02 +00:00
|
|
|
EXPECT_SUBSTRING("\"positions\":[\"ControlFlow\",15]", js.ToCString());
|
2016-03-14 14:04:23 +00:00
|
|
|
// Verify exclusive ticks in do_work.
|
|
|
|
EXPECT_SUBSTRING("\"exclusiveTicks\":[1,2]", js.ToCString());
|
|
|
|
// Verify inclusive ticks in do_work.
|
|
|
|
EXPECT_SUBSTRING("\"inclusiveTicks\":[1,2]", js.ToCString());
|
|
|
|
|
|
|
|
// Verify positions in main.
|
2018-11-05 18:26:02 +00:00
|
|
|
EXPECT_SUBSTRING("\"positions\":[90]", js.ToCString());
|
2016-03-14 14:04:23 +00:00
|
|
|
// Verify exclusive ticks in main.
|
2018-11-05 18:26:02 +00:00
|
|
|
EXPECT_SUBSTRING("\"exclusiveTicks\":[0]", js.ToCString());
|
2016-03-14 14:04:23 +00:00
|
|
|
// Verify inclusive ticks in main.
|
2018-11-05 18:26:02 +00:00
|
|
|
EXPECT_SUBSTRING("\"inclusiveTicks\":[2]", js.ToCString());
|
2016-03-14 14:04:23 +00:00
|
|
|
}
|
|
|
|
|
2018-10-31 19:51:52 +00:00
|
|
|
ISOLATE_UNIT_TEST_CASE(Profiler_ProfileCodeTableTest) {
|
2017-06-14 18:14:19 +00:00
|
|
|
Zone* Z = Thread::Current()->zone();
|
|
|
|
|
|
|
|
ProfileCodeTable* table = new (Z) ProfileCodeTable();
|
|
|
|
EXPECT_EQ(table->length(), 0);
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(42), static_cast<ProfileCode*>(NULL));
|
|
|
|
|
|
|
|
int64_t timestamp = 0;
|
2018-11-15 17:42:13 +00:00
|
|
|
const AbstractCode null_code(Code::null());
|
2017-06-14 18:14:19 +00:00
|
|
|
|
|
|
|
ProfileCode* code1 = new (Z)
|
|
|
|
ProfileCode(ProfileCode::kNativeCode, 50, 60, timestamp, null_code);
|
|
|
|
EXPECT_EQ(table->InsertCode(code1), 0);
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(0), static_cast<ProfileCode*>(NULL));
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(100), static_cast<ProfileCode*>(NULL));
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(50), code1);
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(55), code1);
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(59), code1);
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(60), static_cast<ProfileCode*>(NULL));
|
|
|
|
|
|
|
|
// Insert below all.
|
|
|
|
ProfileCode* code2 = new (Z)
|
|
|
|
ProfileCode(ProfileCode::kNativeCode, 10, 20, timestamp, null_code);
|
|
|
|
EXPECT_EQ(table->InsertCode(code2), 0);
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(0), static_cast<ProfileCode*>(NULL));
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(100), static_cast<ProfileCode*>(NULL));
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(50), code1);
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(10), code2);
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(19), code2);
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(20), static_cast<ProfileCode*>(NULL));
|
|
|
|
|
|
|
|
// Insert above all.
|
|
|
|
ProfileCode* code3 = new (Z)
|
|
|
|
ProfileCode(ProfileCode::kNativeCode, 80, 90, timestamp, null_code);
|
|
|
|
EXPECT_EQ(table->InsertCode(code3), 2);
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(0), static_cast<ProfileCode*>(NULL));
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(100), static_cast<ProfileCode*>(NULL));
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(50), code1);
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(10), code2);
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(80), code3);
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(89), code3);
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(90), static_cast<ProfileCode*>(NULL));
|
|
|
|
|
|
|
|
// Insert between.
|
|
|
|
ProfileCode* code4 = new (Z)
|
|
|
|
ProfileCode(ProfileCode::kNativeCode, 65, 75, timestamp, null_code);
|
|
|
|
EXPECT_EQ(table->InsertCode(code4), 2);
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(0), static_cast<ProfileCode*>(NULL));
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(100), static_cast<ProfileCode*>(NULL));
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(50), code1);
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(10), code2);
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(80), code3);
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(65), code4);
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(74), code4);
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(75), static_cast<ProfileCode*>(NULL));
|
|
|
|
|
|
|
|
// Insert overlapping left.
|
|
|
|
ProfileCode* code5 = new (Z)
|
|
|
|
ProfileCode(ProfileCode::kNativeCode, 15, 25, timestamp, null_code);
|
|
|
|
EXPECT_EQ(table->InsertCode(code5), 0);
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(0), static_cast<ProfileCode*>(NULL));
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(100), static_cast<ProfileCode*>(NULL));
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(50), code1);
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(10), code2);
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(80), code3);
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(65), code4);
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(15), code2); // Merged left.
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(24), code2); // Merged left.
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(25), static_cast<ProfileCode*>(NULL));
|
|
|
|
|
|
|
|
// Insert overlapping right.
|
|
|
|
ProfileCode* code6 = new (Z)
|
|
|
|
ProfileCode(ProfileCode::kNativeCode, 45, 55, timestamp, null_code);
|
|
|
|
EXPECT_EQ(table->InsertCode(code6), 1);
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(0), static_cast<ProfileCode*>(NULL));
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(100), static_cast<ProfileCode*>(NULL));
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(50), code1);
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(10), code2);
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(80), code3);
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(65), code4);
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(15), code2); // Merged left.
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(24), code2); // Merged left.
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(45), code1); // Merged right.
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(54), code1); // Merged right.
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(55), code1);
|
|
|
|
|
|
|
|
// Insert overlapping both.
|
|
|
|
ProfileCode* code7 = new (Z)
|
|
|
|
ProfileCode(ProfileCode::kNativeCode, 20, 50, timestamp, null_code);
|
|
|
|
EXPECT_EQ(table->InsertCode(code7), 0);
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(0), static_cast<ProfileCode*>(NULL));
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(100), static_cast<ProfileCode*>(NULL));
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(50), code1);
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(10), code2);
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(80), code3);
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(65), code4);
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(15), code2); // Merged left.
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(24), code2); // Merged left.
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(45), code1); // Merged right.
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(54), code1); // Merged right.
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(20), code2); // Merged left.
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(49), code1); // Truncated.
|
|
|
|
EXPECT_EQ(table->FindCodeForPC(50), code1);
|
|
|
|
}
|
|
|
|
|
2016-02-05 17:55:51 +00:00
|
|
|
#endif // !PRODUCT
|
2015-08-04 22:02:51 +00:00
|
|
|
|
2016-02-05 17:55:51 +00:00
|
|
|
} // namespace dart
|