508b22c436
Thread class was receiving an isolate parameter by default. This approact violates the assumption that only VM threads can have an associated isolate, and can lead to troubles, because accessing the same isolate from different threads leads to race conditions. This was found by investigating mysterious failures of the CPU profiler layout test on Linux Chromium. As almost all threads were associated with some isolate, the sampler was trying to sample them. As a side effect, we have also fixed the DebuggerAgent test. Thanks to Vitaly for help in fixing isolates handling! R=vitalyr@chromium.org BUG=none TEST=none git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@8259 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
406 lines
14 KiB
C++
406 lines
14 KiB
C++
// Copyright 2010 the V8 project authors. All rights reserved.
|
|
//
|
|
// Tests of profiles generator and utilities.
|
|
|
|
#ifdef ENABLE_LOGGING_AND_PROFILING
|
|
|
|
#include "v8.h"
|
|
#include "cpu-profiler-inl.h"
|
|
#include "cctest.h"
|
|
#include "../include/v8-profiler.h"
|
|
|
|
namespace i = v8::internal;
|
|
|
|
using i::CodeEntry;
|
|
using i::CpuProfile;
|
|
using i::CpuProfiler;
|
|
using i::CpuProfilesCollection;
|
|
using i::ProfileGenerator;
|
|
using i::ProfileNode;
|
|
using i::ProfilerEventsProcessor;
|
|
using i::TokenEnumerator;
|
|
|
|
|
|
TEST(StartStop) {
|
|
CpuProfilesCollection profiles;
|
|
ProfileGenerator generator(&profiles);
|
|
ProfilerEventsProcessor processor(&generator);
|
|
processor.Start();
|
|
processor.Stop();
|
|
processor.Join();
|
|
}
|
|
|
|
static v8::Persistent<v8::Context> env;
|
|
|
|
static void InitializeVM() {
|
|
if (env.IsEmpty()) env = v8::Context::New();
|
|
v8::HandleScope scope;
|
|
env->Enter();
|
|
}
|
|
|
|
static inline i::Address ToAddress(int n) {
|
|
return reinterpret_cast<i::Address>(n);
|
|
}
|
|
|
|
static void EnqueueTickSampleEvent(ProfilerEventsProcessor* proc,
|
|
i::Address frame1,
|
|
i::Address frame2 = NULL,
|
|
i::Address frame3 = NULL) {
|
|
i::TickSample* sample = proc->TickSampleEvent();
|
|
sample->pc = frame1;
|
|
sample->tos = frame1;
|
|
sample->frames_count = 0;
|
|
if (frame2 != NULL) {
|
|
sample->stack[0] = frame2;
|
|
sample->frames_count = 1;
|
|
}
|
|
if (frame3 != NULL) {
|
|
sample->stack[1] = frame3;
|
|
sample->frames_count = 2;
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
class TestSetup {
|
|
public:
|
|
TestSetup()
|
|
: old_flag_prof_browser_mode_(i::FLAG_prof_browser_mode) {
|
|
i::FLAG_prof_browser_mode = false;
|
|
}
|
|
|
|
~TestSetup() {
|
|
i::FLAG_prof_browser_mode = old_flag_prof_browser_mode_;
|
|
}
|
|
|
|
private:
|
|
bool old_flag_prof_browser_mode_;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
TEST(CodeEvents) {
|
|
InitializeVM();
|
|
TestSetup test_setup;
|
|
CpuProfilesCollection profiles;
|
|
profiles.StartProfiling("", 1);
|
|
ProfileGenerator generator(&profiles);
|
|
ProfilerEventsProcessor processor(&generator);
|
|
processor.Start();
|
|
|
|
// Enqueue code creation events.
|
|
i::HandleScope scope;
|
|
const char* aaa_str = "aaa";
|
|
i::Handle<i::String> aaa_name = FACTORY->NewStringFromAscii(
|
|
i::Vector<const char>(aaa_str, i::StrLength(aaa_str)));
|
|
processor.CodeCreateEvent(i::Logger::FUNCTION_TAG,
|
|
*aaa_name,
|
|
HEAP->empty_string(),
|
|
0,
|
|
ToAddress(0x1000),
|
|
0x100,
|
|
ToAddress(0x10000));
|
|
processor.CodeCreateEvent(i::Logger::BUILTIN_TAG,
|
|
"bbb",
|
|
ToAddress(0x1200),
|
|
0x80);
|
|
processor.CodeCreateEvent(i::Logger::STUB_TAG, 5, ToAddress(0x1300), 0x10);
|
|
processor.CodeCreateEvent(i::Logger::BUILTIN_TAG,
|
|
"ddd",
|
|
ToAddress(0x1400),
|
|
0x80);
|
|
processor.CodeMoveEvent(ToAddress(0x1400), ToAddress(0x1500));
|
|
processor.CodeCreateEvent(i::Logger::STUB_TAG, 3, ToAddress(0x1600), 0x10);
|
|
processor.CodeDeleteEvent(ToAddress(0x1600));
|
|
// Enqueue a tick event to enable code events processing.
|
|
EnqueueTickSampleEvent(&processor, ToAddress(0x1000));
|
|
|
|
processor.Stop();
|
|
processor.Join();
|
|
|
|
// Check the state of profile generator.
|
|
CodeEntry* entry1 = generator.code_map()->FindEntry(ToAddress(0x1000));
|
|
CHECK_NE(NULL, entry1);
|
|
CHECK_EQ(aaa_str, entry1->name());
|
|
CodeEntry* entry2 = generator.code_map()->FindEntry(ToAddress(0x1200));
|
|
CHECK_NE(NULL, entry2);
|
|
CHECK_EQ("bbb", entry2->name());
|
|
CodeEntry* entry3 = generator.code_map()->FindEntry(ToAddress(0x1300));
|
|
CHECK_NE(NULL, entry3);
|
|
CHECK_EQ("5", entry3->name());
|
|
CHECK_EQ(NULL, generator.code_map()->FindEntry(ToAddress(0x1400)));
|
|
CodeEntry* entry4 = generator.code_map()->FindEntry(ToAddress(0x1500));
|
|
CHECK_NE(NULL, entry4);
|
|
CHECK_EQ("ddd", entry4->name());
|
|
CHECK_EQ(NULL, generator.code_map()->FindEntry(ToAddress(0x1600)));
|
|
}
|
|
|
|
|
|
template<typename T>
|
|
static int CompareProfileNodes(const T* p1, const T* p2) {
|
|
return strcmp((*p1)->entry()->name(), (*p2)->entry()->name());
|
|
}
|
|
|
|
TEST(TickEvents) {
|
|
TestSetup test_setup;
|
|
CpuProfilesCollection profiles;
|
|
profiles.StartProfiling("", 1);
|
|
ProfileGenerator generator(&profiles);
|
|
ProfilerEventsProcessor processor(&generator);
|
|
processor.Start();
|
|
|
|
processor.CodeCreateEvent(i::Logger::BUILTIN_TAG,
|
|
"bbb",
|
|
ToAddress(0x1200),
|
|
0x80);
|
|
processor.CodeCreateEvent(i::Logger::STUB_TAG, 5, ToAddress(0x1300), 0x10);
|
|
processor.CodeCreateEvent(i::Logger::BUILTIN_TAG,
|
|
"ddd",
|
|
ToAddress(0x1400),
|
|
0x80);
|
|
EnqueueTickSampleEvent(&processor, ToAddress(0x1210));
|
|
EnqueueTickSampleEvent(&processor, ToAddress(0x1305), ToAddress(0x1220));
|
|
EnqueueTickSampleEvent(&processor,
|
|
ToAddress(0x1404),
|
|
ToAddress(0x1305),
|
|
ToAddress(0x1230));
|
|
|
|
processor.Stop();
|
|
processor.Join();
|
|
CpuProfile* profile =
|
|
profiles.StopProfiling(TokenEnumerator::kNoSecurityToken, "", 1);
|
|
CHECK_NE(NULL, profile);
|
|
|
|
// Check call trees.
|
|
const i::List<ProfileNode*>* top_down_root_children =
|
|
profile->top_down()->root()->children();
|
|
CHECK_EQ(1, top_down_root_children->length());
|
|
CHECK_EQ("bbb", top_down_root_children->last()->entry()->name());
|
|
const i::List<ProfileNode*>* top_down_bbb_children =
|
|
top_down_root_children->last()->children();
|
|
CHECK_EQ(1, top_down_bbb_children->length());
|
|
CHECK_EQ("5", top_down_bbb_children->last()->entry()->name());
|
|
const i::List<ProfileNode*>* top_down_stub_children =
|
|
top_down_bbb_children->last()->children();
|
|
CHECK_EQ(1, top_down_stub_children->length());
|
|
CHECK_EQ("ddd", top_down_stub_children->last()->entry()->name());
|
|
const i::List<ProfileNode*>* top_down_ddd_children =
|
|
top_down_stub_children->last()->children();
|
|
CHECK_EQ(0, top_down_ddd_children->length());
|
|
|
|
const i::List<ProfileNode*>* bottom_up_root_children_unsorted =
|
|
profile->bottom_up()->root()->children();
|
|
CHECK_EQ(3, bottom_up_root_children_unsorted->length());
|
|
i::List<ProfileNode*> bottom_up_root_children(3);
|
|
bottom_up_root_children.AddAll(*bottom_up_root_children_unsorted);
|
|
bottom_up_root_children.Sort(&CompareProfileNodes);
|
|
CHECK_EQ("5", bottom_up_root_children[0]->entry()->name());
|
|
CHECK_EQ("bbb", bottom_up_root_children[1]->entry()->name());
|
|
CHECK_EQ("ddd", bottom_up_root_children[2]->entry()->name());
|
|
const i::List<ProfileNode*>* bottom_up_stub_children =
|
|
bottom_up_root_children[0]->children();
|
|
CHECK_EQ(1, bottom_up_stub_children->length());
|
|
CHECK_EQ("bbb", bottom_up_stub_children->last()->entry()->name());
|
|
const i::List<ProfileNode*>* bottom_up_bbb_children =
|
|
bottom_up_root_children[1]->children();
|
|
CHECK_EQ(0, bottom_up_bbb_children->length());
|
|
const i::List<ProfileNode*>* bottom_up_ddd_children =
|
|
bottom_up_root_children[2]->children();
|
|
CHECK_EQ(1, bottom_up_ddd_children->length());
|
|
CHECK_EQ("5", bottom_up_ddd_children->last()->entry()->name());
|
|
const i::List<ProfileNode*>* bottom_up_ddd_stub_children =
|
|
bottom_up_ddd_children->last()->children();
|
|
CHECK_EQ(1, bottom_up_ddd_stub_children->length());
|
|
CHECK_EQ("bbb", bottom_up_ddd_stub_children->last()->entry()->name());
|
|
}
|
|
|
|
|
|
// http://crbug/51594
|
|
// This test must not crash.
|
|
TEST(CrashIfStoppingLastNonExistentProfile) {
|
|
InitializeVM();
|
|
TestSetup test_setup;
|
|
CpuProfiler::Setup();
|
|
CpuProfiler::StartProfiling("1");
|
|
CpuProfiler::StopProfiling("2");
|
|
CpuProfiler::StartProfiling("1");
|
|
CpuProfiler::StopProfiling("");
|
|
CpuProfiler::TearDown();
|
|
}
|
|
|
|
|
|
// http://code.google.com/p/v8/issues/detail?id=1398
|
|
// Long stacks (exceeding max frames limit) must not be erased.
|
|
TEST(Issue1398) {
|
|
TestSetup test_setup;
|
|
CpuProfilesCollection profiles;
|
|
profiles.StartProfiling("", 1);
|
|
ProfileGenerator generator(&profiles);
|
|
ProfilerEventsProcessor processor(&generator);
|
|
processor.Start();
|
|
|
|
processor.CodeCreateEvent(i::Logger::BUILTIN_TAG,
|
|
"bbb",
|
|
ToAddress(0x1200),
|
|
0x80);
|
|
|
|
i::TickSample* sample = processor.TickSampleEvent();
|
|
sample->pc = ToAddress(0x1200);
|
|
sample->tos = 0;
|
|
sample->frames_count = i::TickSample::kMaxFramesCount;
|
|
for (int i = 0; i < sample->frames_count; ++i) {
|
|
sample->stack[i] = ToAddress(0x1200);
|
|
}
|
|
|
|
processor.Stop();
|
|
processor.Join();
|
|
CpuProfile* profile =
|
|
profiles.StopProfiling(TokenEnumerator::kNoSecurityToken, "", 1);
|
|
CHECK_NE(NULL, profile);
|
|
|
|
int actual_depth = 0;
|
|
const ProfileNode* node = profile->top_down()->root();
|
|
while (node->children()->length() > 0) {
|
|
node = node->children()->last();
|
|
++actual_depth;
|
|
}
|
|
|
|
CHECK_EQ(1 + i::TickSample::kMaxFramesCount, actual_depth); // +1 for PC.
|
|
}
|
|
|
|
|
|
TEST(DeleteAllCpuProfiles) {
|
|
InitializeVM();
|
|
TestSetup test_setup;
|
|
CpuProfiler::Setup();
|
|
CHECK_EQ(0, CpuProfiler::GetProfilesCount());
|
|
CpuProfiler::DeleteAllProfiles();
|
|
CHECK_EQ(0, CpuProfiler::GetProfilesCount());
|
|
|
|
CpuProfiler::StartProfiling("1");
|
|
CpuProfiler::StopProfiling("1");
|
|
CHECK_EQ(1, CpuProfiler::GetProfilesCount());
|
|
CpuProfiler::DeleteAllProfiles();
|
|
CHECK_EQ(0, CpuProfiler::GetProfilesCount());
|
|
CpuProfiler::StartProfiling("1");
|
|
CpuProfiler::StartProfiling("2");
|
|
CpuProfiler::StopProfiling("2");
|
|
CpuProfiler::StopProfiling("1");
|
|
CHECK_EQ(2, CpuProfiler::GetProfilesCount());
|
|
CpuProfiler::DeleteAllProfiles();
|
|
CHECK_EQ(0, CpuProfiler::GetProfilesCount());
|
|
|
|
// Test profiling cancellation by the 'delete' command.
|
|
CpuProfiler::StartProfiling("1");
|
|
CpuProfiler::StartProfiling("2");
|
|
CHECK_EQ(0, CpuProfiler::GetProfilesCount());
|
|
CpuProfiler::DeleteAllProfiles();
|
|
CHECK_EQ(0, CpuProfiler::GetProfilesCount());
|
|
|
|
CpuProfiler::TearDown();
|
|
}
|
|
|
|
|
|
TEST(DeleteCpuProfile) {
|
|
v8::HandleScope scope;
|
|
LocalContext env;
|
|
|
|
CHECK_EQ(0, v8::CpuProfiler::GetProfilesCount());
|
|
v8::Local<v8::String> name1 = v8::String::New("1");
|
|
v8::CpuProfiler::StartProfiling(name1);
|
|
const v8::CpuProfile* p1 = v8::CpuProfiler::StopProfiling(name1);
|
|
CHECK_NE(NULL, p1);
|
|
CHECK_EQ(1, v8::CpuProfiler::GetProfilesCount());
|
|
unsigned uid1 = p1->GetUid();
|
|
CHECK_EQ(p1, v8::CpuProfiler::FindProfile(uid1));
|
|
const_cast<v8::CpuProfile*>(p1)->Delete();
|
|
CHECK_EQ(0, CpuProfiler::GetProfilesCount());
|
|
CHECK_EQ(NULL, v8::CpuProfiler::FindProfile(uid1));
|
|
|
|
v8::Local<v8::String> name2 = v8::String::New("2");
|
|
v8::CpuProfiler::StartProfiling(name2);
|
|
const v8::CpuProfile* p2 = v8::CpuProfiler::StopProfiling(name2);
|
|
CHECK_NE(NULL, p2);
|
|
CHECK_EQ(1, v8::CpuProfiler::GetProfilesCount());
|
|
unsigned uid2 = p2->GetUid();
|
|
CHECK_NE(static_cast<int>(uid1), static_cast<int>(uid2));
|
|
CHECK_EQ(p2, v8::CpuProfiler::FindProfile(uid2));
|
|
CHECK_EQ(NULL, v8::CpuProfiler::FindProfile(uid1));
|
|
v8::Local<v8::String> name3 = v8::String::New("3");
|
|
v8::CpuProfiler::StartProfiling(name3);
|
|
const v8::CpuProfile* p3 = v8::CpuProfiler::StopProfiling(name3);
|
|
CHECK_NE(NULL, p3);
|
|
CHECK_EQ(2, v8::CpuProfiler::GetProfilesCount());
|
|
unsigned uid3 = p3->GetUid();
|
|
CHECK_NE(static_cast<int>(uid1), static_cast<int>(uid3));
|
|
CHECK_EQ(p3, v8::CpuProfiler::FindProfile(uid3));
|
|
CHECK_EQ(NULL, v8::CpuProfiler::FindProfile(uid1));
|
|
const_cast<v8::CpuProfile*>(p2)->Delete();
|
|
CHECK_EQ(1, v8::CpuProfiler::GetProfilesCount());
|
|
CHECK_EQ(NULL, v8::CpuProfiler::FindProfile(uid2));
|
|
CHECK_EQ(p3, v8::CpuProfiler::FindProfile(uid3));
|
|
const_cast<v8::CpuProfile*>(p3)->Delete();
|
|
CHECK_EQ(0, CpuProfiler::GetProfilesCount());
|
|
CHECK_EQ(NULL, v8::CpuProfiler::FindProfile(uid3));
|
|
CHECK_EQ(NULL, v8::CpuProfiler::FindProfile(uid2));
|
|
CHECK_EQ(NULL, v8::CpuProfiler::FindProfile(uid1));
|
|
}
|
|
|
|
|
|
TEST(DeleteCpuProfileDifferentTokens) {
|
|
v8::HandleScope scope;
|
|
LocalContext env;
|
|
|
|
CHECK_EQ(0, v8::CpuProfiler::GetProfilesCount());
|
|
v8::Local<v8::String> name1 = v8::String::New("1");
|
|
v8::CpuProfiler::StartProfiling(name1);
|
|
const v8::CpuProfile* p1 = v8::CpuProfiler::StopProfiling(name1);
|
|
CHECK_NE(NULL, p1);
|
|
CHECK_EQ(1, v8::CpuProfiler::GetProfilesCount());
|
|
unsigned uid1 = p1->GetUid();
|
|
CHECK_EQ(p1, v8::CpuProfiler::FindProfile(uid1));
|
|
v8::Local<v8::String> token1 = v8::String::New("token1");
|
|
const v8::CpuProfile* p1_t1 = v8::CpuProfiler::FindProfile(uid1, token1);
|
|
CHECK_NE(NULL, p1_t1);
|
|
CHECK_NE(p1, p1_t1);
|
|
CHECK_EQ(1, v8::CpuProfiler::GetProfilesCount());
|
|
const_cast<v8::CpuProfile*>(p1)->Delete();
|
|
CHECK_EQ(0, CpuProfiler::GetProfilesCount());
|
|
CHECK_EQ(NULL, v8::CpuProfiler::FindProfile(uid1));
|
|
CHECK_EQ(NULL, v8::CpuProfiler::FindProfile(uid1, token1));
|
|
const_cast<v8::CpuProfile*>(p1_t1)->Delete();
|
|
CHECK_EQ(0, CpuProfiler::GetProfilesCount());
|
|
|
|
v8::Local<v8::String> name2 = v8::String::New("2");
|
|
v8::CpuProfiler::StartProfiling(name2);
|
|
v8::Local<v8::String> token2 = v8::String::New("token2");
|
|
const v8::CpuProfile* p2_t2 = v8::CpuProfiler::StopProfiling(name2, token2);
|
|
CHECK_NE(NULL, p2_t2);
|
|
CHECK_EQ(1, v8::CpuProfiler::GetProfilesCount());
|
|
unsigned uid2 = p2_t2->GetUid();
|
|
CHECK_NE(static_cast<int>(uid1), static_cast<int>(uid2));
|
|
const v8::CpuProfile* p2 = v8::CpuProfiler::FindProfile(uid2);
|
|
CHECK_NE(p2_t2, p2);
|
|
v8::Local<v8::String> name3 = v8::String::New("3");
|
|
v8::CpuProfiler::StartProfiling(name3);
|
|
const v8::CpuProfile* p3 = v8::CpuProfiler::StopProfiling(name3);
|
|
CHECK_NE(NULL, p3);
|
|
CHECK_EQ(2, v8::CpuProfiler::GetProfilesCount());
|
|
unsigned uid3 = p3->GetUid();
|
|
CHECK_NE(static_cast<int>(uid1), static_cast<int>(uid3));
|
|
CHECK_EQ(p3, v8::CpuProfiler::FindProfile(uid3));
|
|
const_cast<v8::CpuProfile*>(p2_t2)->Delete();
|
|
CHECK_EQ(1, v8::CpuProfiler::GetProfilesCount());
|
|
CHECK_EQ(NULL, v8::CpuProfiler::FindProfile(uid2));
|
|
CHECK_EQ(p3, v8::CpuProfiler::FindProfile(uid3));
|
|
const_cast<v8::CpuProfile*>(p2)->Delete();
|
|
CHECK_EQ(1, v8::CpuProfiler::GetProfilesCount());
|
|
CHECK_EQ(NULL, v8::CpuProfiler::FindProfile(uid2));
|
|
CHECK_EQ(p3, v8::CpuProfiler::FindProfile(uid3));
|
|
const_cast<v8::CpuProfile*>(p3)->Delete();
|
|
CHECK_EQ(0, CpuProfiler::GetProfilesCount());
|
|
CHECK_EQ(NULL, v8::CpuProfiler::FindProfile(uid3));
|
|
}
|
|
|
|
#endif // ENABLE_LOGGING_AND_PROFILING
|