v8/test/cctest/test-cpu-profiler.cc
mikhail.naganov@gmail.com 56788625b6 Fix CPU profiling for Crankshaft.
The main issue was due to multiple recompilations of functions.  Now
code objects are grouped by function using SFI object address.
JSFunction objects are no longer tracked, instead we track SFI object
moves. To pick a correct code version, we now sample return addresses
instead of JSFunction addresses.

tools/{linux|mac|windows}-tickprocessor scripts differentiate
between code optimization states for the same function
(using * and ~ prefixes introduced earlier).

DevTools CPU profiler treats all variants of function code as
a single function.

ll_prof treats each optimized variant as a separate entry, because
it can disassemble each one of them.

tickprocessor.py not updated -- it is deprecated and will be removed.

BUG=v8/1087,b/3178160
TEST=all existing tests pass, including Chromium layout tests

Review URL: http://codereview.chromium.org/6551011

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@6902 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2011-02-22 16:31:24 +00:00

240 lines
7.9 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"
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();
while (!processor.running()) {
i::Thread::YieldCPU();
}
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();
while (!processor.running()) {
i::Thread::YieldCPU();
}
// Enqueue code creation events.
i::HandleScope scope;
const char* aaa_str = "aaa";
i::Handle<i::String> aaa_name = i::Factory::NewStringFromAscii(
i::Vector<const char>(aaa_str, i::StrLength(aaa_str)));
processor.CodeCreateEvent(i::Logger::FUNCTION_TAG,
*aaa_name,
i::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();
while (!processor.running()) {
i::Thread::YieldCPU();
}
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();
}
#endif // ENABLE_LOGGING_AND_PROFILING