Add sanity test for CPU profiler
The new test checks full CPU profiling cycle: using public V8 API it starts profiling, executes a script, stops profiling and analyzes collected profile to check that its top-down tree has expected strutcture. The script that is being profiled is guaranteed to run > 200ms to make sure enough samples are collected. To avoid possible flakiness due to non-deterministic time required to start new thread on varios OSs when Sampler and ProfilerEventsProcessor threads are being started the main thread is blocked until the threads are running. Also I removed the heuristic in profile-generator.cc where we try to figure out if the value on top of the sampled stack is return address of some frameless stub invocation. The code periodically gives false positive with the new test ending up in an extra node in the collected cpu profile. After discussion with jkummerow@ we concluded that the logic is too fragile and that we can address frameless stub invocations in a more reliable way later should they have a noticeable effect on cpu profiling. BUG=None Review URL: https://codereview.chromium.org/13627002 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14205 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
dca3698584
commit
c7ce87f865
@ -445,7 +445,7 @@ void CpuProfiler::StartProcessorIfNotStarted() {
|
|||||||
generator_ = new ProfileGenerator(profiles_);
|
generator_ = new ProfileGenerator(profiles_);
|
||||||
processor_ = new ProfilerEventsProcessor(generator_);
|
processor_ = new ProfilerEventsProcessor(generator_);
|
||||||
is_profiling_ = true;
|
is_profiling_ = true;
|
||||||
processor_->Start();
|
processor_->StartSynchronously();
|
||||||
// Enumerate stuff we already have in the heap.
|
// Enumerate stuff we already have in the heap.
|
||||||
if (isolate_->heap()->HasBeenSetUp()) {
|
if (isolate_->heap()->HasBeenSetUp()) {
|
||||||
if (!FLAG_prof_browser_mode) {
|
if (!FLAG_prof_browser_mode) {
|
||||||
@ -459,11 +459,11 @@ void CpuProfiler::StartProcessorIfNotStarted() {
|
|||||||
}
|
}
|
||||||
// Enable stack sampling.
|
// Enable stack sampling.
|
||||||
Sampler* sampler = reinterpret_cast<Sampler*>(isolate_->logger()->ticker_);
|
Sampler* sampler = reinterpret_cast<Sampler*>(isolate_->logger()->ticker_);
|
||||||
|
sampler->IncreaseProfilingDepth();
|
||||||
if (!sampler->IsActive()) {
|
if (!sampler->IsActive()) {
|
||||||
sampler->Start();
|
sampler->Start();
|
||||||
need_to_stop_sampler_ = true;
|
need_to_stop_sampler_ = true;
|
||||||
}
|
}
|
||||||
sampler->IncreaseProfilingDepth();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,7 +381,8 @@ class Thread::PlatformData : public Malloced {
|
|||||||
|
|
||||||
Thread::Thread(const Options& options)
|
Thread::Thread(const Options& options)
|
||||||
: data_(new PlatformData()),
|
: data_(new PlatformData()),
|
||||||
stack_size_(options.stack_size()) {
|
stack_size_(options.stack_size()),
|
||||||
|
start_semaphore_(NULL) {
|
||||||
set_name(options.name());
|
set_name(options.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -398,7 +399,7 @@ static void* ThreadEntry(void* arg) {
|
|||||||
// one) so we initialize it here too.
|
// one) so we initialize it here too.
|
||||||
thread->data()->thread_ = pthread_self();
|
thread->data()->thread_ = pthread_self();
|
||||||
ASSERT(thread->data()->thread_ != kNoThread);
|
ASSERT(thread->data()->thread_ != kNoThread);
|
||||||
thread->Run();
|
thread->NotifyStartedAndRun();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -639,7 +640,7 @@ class SamplerThread : public Thread {
|
|||||||
SamplerRegistry::AddActiveSampler(sampler);
|
SamplerRegistry::AddActiveSampler(sampler);
|
||||||
if (instance_ == NULL) {
|
if (instance_ == NULL) {
|
||||||
instance_ = new SamplerThread(sampler->interval());
|
instance_ = new SamplerThread(sampler->interval());
|
||||||
instance_->Start();
|
instance_->StartSynchronously();
|
||||||
} else {
|
} else {
|
||||||
ASSERT(instance_->interval_ == sampler->interval());
|
ASSERT(instance_->interval_ == sampler->interval());
|
||||||
}
|
}
|
||||||
|
@ -500,7 +500,8 @@ class Thread::PlatformData : public Malloced {
|
|||||||
|
|
||||||
Thread::Thread(const Options& options)
|
Thread::Thread(const Options& options)
|
||||||
: data_(new PlatformData),
|
: data_(new PlatformData),
|
||||||
stack_size_(options.stack_size()) {
|
stack_size_(options.stack_size()),
|
||||||
|
start_semaphore_(NULL) {
|
||||||
set_name(options.name());
|
set_name(options.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -517,7 +518,7 @@ static void* ThreadEntry(void* arg) {
|
|||||||
// one) so we initialize it here too.
|
// one) so we initialize it here too.
|
||||||
thread->data()->thread_ = pthread_self();
|
thread->data()->thread_ = pthread_self();
|
||||||
ASSERT(thread->data()->thread_ != kNoThread);
|
ASSERT(thread->data()->thread_ != kNoThread);
|
||||||
thread->Run();
|
thread->NotifyStartedAndRun();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -765,7 +766,7 @@ class SignalSender : public Thread {
|
|||||||
|
|
||||||
// Start a thread that sends SIGPROF signal to VM threads.
|
// Start a thread that sends SIGPROF signal to VM threads.
|
||||||
instance_ = new SignalSender(sampler->interval());
|
instance_ = new SignalSender(sampler->interval());
|
||||||
instance_->Start();
|
instance_->StartSynchronously();
|
||||||
} else {
|
} else {
|
||||||
ASSERT(instance_->interval_ == sampler->interval());
|
ASSERT(instance_->interval_ == sampler->interval());
|
||||||
}
|
}
|
||||||
|
@ -785,7 +785,8 @@ class Thread::PlatformData : public Malloced {
|
|||||||
|
|
||||||
Thread::Thread(const Options& options)
|
Thread::Thread(const Options& options)
|
||||||
: data_(new PlatformData()),
|
: data_(new PlatformData()),
|
||||||
stack_size_(options.stack_size()) {
|
stack_size_(options.stack_size()),
|
||||||
|
start_semaphore_(NULL) {
|
||||||
set_name(options.name());
|
set_name(options.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -807,7 +808,7 @@ static void* ThreadEntry(void* arg) {
|
|||||||
#endif
|
#endif
|
||||||
thread->data()->thread_ = pthread_self();
|
thread->data()->thread_ = pthread_self();
|
||||||
ASSERT(thread->data()->thread_ != kNoThread);
|
ASSERT(thread->data()->thread_ != kNoThread);
|
||||||
thread->Run();
|
thread->NotifyStartedAndRun();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1183,7 +1184,7 @@ class SignalSender : public Thread {
|
|||||||
// Start a thread that will send SIGPROF signal to VM threads,
|
// Start a thread that will send SIGPROF signal to VM threads,
|
||||||
// when CPU profiling will be enabled.
|
// when CPU profiling will be enabled.
|
||||||
instance_ = new SignalSender(sampler->interval());
|
instance_ = new SignalSender(sampler->interval());
|
||||||
instance_->Start();
|
instance_->StartSynchronously();
|
||||||
} else {
|
} else {
|
||||||
ASSERT(instance_->interval_ == sampler->interval());
|
ASSERT(instance_->interval_ == sampler->interval());
|
||||||
}
|
}
|
||||||
|
@ -495,7 +495,8 @@ class Thread::PlatformData : public Malloced {
|
|||||||
|
|
||||||
Thread::Thread(const Options& options)
|
Thread::Thread(const Options& options)
|
||||||
: data_(new PlatformData),
|
: data_(new PlatformData),
|
||||||
stack_size_(options.stack_size()) {
|
stack_size_(options.stack_size()),
|
||||||
|
start_semaphore_(NULL) {
|
||||||
set_name(options.name());
|
set_name(options.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -530,7 +531,7 @@ static void* ThreadEntry(void* arg) {
|
|||||||
thread->data()->thread_ = pthread_self();
|
thread->data()->thread_ = pthread_self();
|
||||||
SetThreadName(thread->name());
|
SetThreadName(thread->name());
|
||||||
ASSERT(thread->data()->thread_ != kNoThread);
|
ASSERT(thread->data()->thread_ != kNoThread);
|
||||||
thread->Run();
|
thread->NotifyStartedAndRun();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -776,7 +777,7 @@ class SamplerThread : public Thread {
|
|||||||
SamplerRegistry::AddActiveSampler(sampler);
|
SamplerRegistry::AddActiveSampler(sampler);
|
||||||
if (instance_ == NULL) {
|
if (instance_ == NULL) {
|
||||||
instance_ = new SamplerThread(sampler->interval());
|
instance_ = new SamplerThread(sampler->interval());
|
||||||
instance_->Start();
|
instance_->StartSynchronously();
|
||||||
} else {
|
} else {
|
||||||
ASSERT(instance_->interval_ == sampler->interval());
|
ASSERT(instance_->interval_ == sampler->interval());
|
||||||
}
|
}
|
||||||
|
@ -369,7 +369,8 @@ class Thread::PlatformData : public Malloced {
|
|||||||
|
|
||||||
Thread::Thread(const Options& options)
|
Thread::Thread(const Options& options)
|
||||||
: data_(new PlatformData()),
|
: data_(new PlatformData()),
|
||||||
stack_size_(options.stack_size) {
|
stack_size_(options.stack_size),
|
||||||
|
start_semaphore_(NULL) {
|
||||||
set_name(options.name);
|
set_name(options.name);
|
||||||
UNIMPLEMENTED();
|
UNIMPLEMENTED();
|
||||||
}
|
}
|
||||||
|
@ -529,7 +529,8 @@ class Thread::PlatformData : public Malloced {
|
|||||||
|
|
||||||
Thread::Thread(const Options& options)
|
Thread::Thread(const Options& options)
|
||||||
: data_(new PlatformData()),
|
: data_(new PlatformData()),
|
||||||
stack_size_(options.stack_size()) {
|
stack_size_(options.stack_size()),
|
||||||
|
start_semaphore_(NULL) {
|
||||||
set_name(options.name());
|
set_name(options.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -551,7 +552,7 @@ static void* ThreadEntry(void* arg) {
|
|||||||
#endif
|
#endif
|
||||||
thread->data()->thread_ = pthread_self();
|
thread->data()->thread_ = pthread_self();
|
||||||
ASSERT(thread->data()->thread_ != kNoThread);
|
ASSERT(thread->data()->thread_ != kNoThread);
|
||||||
thread->Run();
|
thread->NotifyStartedAndRun();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -827,7 +828,7 @@ class SignalSender : public Thread {
|
|||||||
// Start a thread that will send SIGPROF signal to VM threads,
|
// Start a thread that will send SIGPROF signal to VM threads,
|
||||||
// when CPU profiling will be enabled.
|
// when CPU profiling will be enabled.
|
||||||
instance_ = new SignalSender(sampler->interval());
|
instance_ = new SignalSender(sampler->interval());
|
||||||
instance_->Start();
|
instance_->StartSynchronously();
|
||||||
} else {
|
} else {
|
||||||
ASSERT(instance_->interval_ == sampler->interval());
|
ASSERT(instance_->interval_ == sampler->interval());
|
||||||
}
|
}
|
||||||
|
@ -470,7 +470,8 @@ class Thread::PlatformData : public Malloced {
|
|||||||
|
|
||||||
Thread::Thread(const Options& options)
|
Thread::Thread(const Options& options)
|
||||||
: data_(new PlatformData()),
|
: data_(new PlatformData()),
|
||||||
stack_size_(options.stack_size()) {
|
stack_size_(options.stack_size()),
|
||||||
|
start_semaphore_(NULL) {
|
||||||
set_name(options.name());
|
set_name(options.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -487,7 +488,7 @@ static void* ThreadEntry(void* arg) {
|
|||||||
// one) so we initialize it here too.
|
// one) so we initialize it here too.
|
||||||
thread->data()->thread_ = pthread_self();
|
thread->data()->thread_ = pthread_self();
|
||||||
ASSERT(thread->data()->thread_ != kNoThread);
|
ASSERT(thread->data()->thread_ != kNoThread);
|
||||||
thread->Run();
|
thread->NotifyStartedAndRun();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -743,7 +744,7 @@ class SignalSender : public Thread {
|
|||||||
// Start a thread that will send SIGPROF signal to VM threads,
|
// Start a thread that will send SIGPROF signal to VM threads,
|
||||||
// when CPU profiling will be enabled.
|
// when CPU profiling will be enabled.
|
||||||
instance_ = new SignalSender(sampler->interval());
|
instance_ = new SignalSender(sampler->interval());
|
||||||
instance_->Start();
|
instance_->StartSynchronously();
|
||||||
} else {
|
} else {
|
||||||
ASSERT(instance_->interval_ == sampler->interval());
|
ASSERT(instance_->interval_ == sampler->interval());
|
||||||
}
|
}
|
||||||
|
@ -1605,7 +1605,7 @@ static const HANDLE kNoThread = INVALID_HANDLE_VALUE;
|
|||||||
// convention.
|
// convention.
|
||||||
static unsigned int __stdcall ThreadEntry(void* arg) {
|
static unsigned int __stdcall ThreadEntry(void* arg) {
|
||||||
Thread* thread = reinterpret_cast<Thread*>(arg);
|
Thread* thread = reinterpret_cast<Thread*>(arg);
|
||||||
thread->Run();
|
thread->NotifyStartedAndRun();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1622,7 +1622,8 @@ class Thread::PlatformData : public Malloced {
|
|||||||
// handle until it is started.
|
// handle until it is started.
|
||||||
|
|
||||||
Thread::Thread(const Options& options)
|
Thread::Thread(const Options& options)
|
||||||
: stack_size_(options.stack_size()) {
|
: stack_size_(options.stack_size()),
|
||||||
|
start_semaphore_(NULL) {
|
||||||
data_ = new PlatformData(kNoThread);
|
data_ = new PlatformData(kNoThread);
|
||||||
set_name(options.name());
|
set_name(options.name());
|
||||||
}
|
}
|
||||||
@ -2016,7 +2017,7 @@ class SamplerThread : public Thread {
|
|||||||
SamplerRegistry::AddActiveSampler(sampler);
|
SamplerRegistry::AddActiveSampler(sampler);
|
||||||
if (instance_ == NULL) {
|
if (instance_ == NULL) {
|
||||||
instance_ = new SamplerThread(sampler->interval());
|
instance_ = new SamplerThread(sampler->interval());
|
||||||
instance_->Start();
|
instance_->StartSynchronously();
|
||||||
} else {
|
} else {
|
||||||
ASSERT(instance_->interval_ == sampler->interval());
|
ASSERT(instance_->interval_ == sampler->interval());
|
||||||
}
|
}
|
||||||
|
123
src/platform.h
123
src/platform.h
@ -452,6 +452,59 @@ class VirtualMemory {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Semaphore
|
||||||
|
//
|
||||||
|
// A semaphore object is a synchronization object that maintains a count. The
|
||||||
|
// count is decremented each time a thread completes a wait for the semaphore
|
||||||
|
// object and incremented each time a thread signals the semaphore. When the
|
||||||
|
// count reaches zero, threads waiting for the semaphore blocks until the
|
||||||
|
// count becomes non-zero.
|
||||||
|
|
||||||
|
class Semaphore {
|
||||||
|
public:
|
||||||
|
virtual ~Semaphore() {}
|
||||||
|
|
||||||
|
// Suspends the calling thread until the semaphore counter is non zero
|
||||||
|
// and then decrements the semaphore counter.
|
||||||
|
virtual void Wait() = 0;
|
||||||
|
|
||||||
|
// Suspends the calling thread until the counter is non zero or the timeout
|
||||||
|
// time has passed. If timeout happens the return value is false and the
|
||||||
|
// counter is unchanged. Otherwise the semaphore counter is decremented and
|
||||||
|
// true is returned. The timeout value is specified in microseconds.
|
||||||
|
virtual bool Wait(int timeout) = 0;
|
||||||
|
|
||||||
|
// Increments the semaphore counter.
|
||||||
|
virtual void Signal() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <int InitialValue>
|
||||||
|
struct CreateSemaphoreTrait {
|
||||||
|
static Semaphore* Create() {
|
||||||
|
return OS::CreateSemaphore(InitialValue);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// POD Semaphore initialized lazily (i.e. the first time Pointer() is called).
|
||||||
|
// Usage:
|
||||||
|
// // The following semaphore starts at 0.
|
||||||
|
// static LazySemaphore<0>::type my_semaphore = LAZY_SEMAPHORE_INITIALIZER;
|
||||||
|
//
|
||||||
|
// void my_function() {
|
||||||
|
// // Do something with my_semaphore.Pointer().
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
template <int InitialValue>
|
||||||
|
struct LazySemaphore {
|
||||||
|
typedef typename LazyDynamicInstance<
|
||||||
|
Semaphore, CreateSemaphoreTrait<InitialValue>,
|
||||||
|
ThreadSafeInitOnceTrait>::type type;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define LAZY_SEMAPHORE_INITIALIZER LAZY_DYNAMIC_INSTANCE_INITIALIZER
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Thread
|
// Thread
|
||||||
//
|
//
|
||||||
@ -489,9 +542,18 @@ class Thread {
|
|||||||
explicit Thread(const Options& options);
|
explicit Thread(const Options& options);
|
||||||
virtual ~Thread();
|
virtual ~Thread();
|
||||||
|
|
||||||
// Start new thread by calling the Run() method in the new thread.
|
// Start new thread by calling the Run() method on the new thread.
|
||||||
void Start();
|
void Start();
|
||||||
|
|
||||||
|
// Start new thread and wait until Run() method is called on the new thread.
|
||||||
|
void StartSynchronously() {
|
||||||
|
start_semaphore_ = OS::CreateSemaphore(0);
|
||||||
|
Start();
|
||||||
|
start_semaphore_->Wait();
|
||||||
|
delete start_semaphore_;
|
||||||
|
start_semaphore_ = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
// Wait until thread terminates.
|
// Wait until thread terminates.
|
||||||
void Join();
|
void Join();
|
||||||
|
|
||||||
@ -541,6 +603,11 @@ class Thread {
|
|||||||
class PlatformData;
|
class PlatformData;
|
||||||
PlatformData* data() { return data_; }
|
PlatformData* data() { return data_; }
|
||||||
|
|
||||||
|
void NotifyStartedAndRun() {
|
||||||
|
if (start_semaphore_) start_semaphore_->Signal();
|
||||||
|
Run();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void set_name(const char* name);
|
void set_name(const char* name);
|
||||||
|
|
||||||
@ -548,6 +615,7 @@ class Thread {
|
|||||||
|
|
||||||
char name_[kMaxThreadNameLength];
|
char name_[kMaxThreadNameLength];
|
||||||
int stack_size_;
|
int stack_size_;
|
||||||
|
Semaphore* start_semaphore_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(Thread);
|
DISALLOW_COPY_AND_ASSIGN(Thread);
|
||||||
};
|
};
|
||||||
@ -619,59 +687,6 @@ class ScopedLock {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Semaphore
|
|
||||||
//
|
|
||||||
// A semaphore object is a synchronization object that maintains a count. The
|
|
||||||
// count is decremented each time a thread completes a wait for the semaphore
|
|
||||||
// object and incremented each time a thread signals the semaphore. When the
|
|
||||||
// count reaches zero, threads waiting for the semaphore blocks until the
|
|
||||||
// count becomes non-zero.
|
|
||||||
|
|
||||||
class Semaphore {
|
|
||||||
public:
|
|
||||||
virtual ~Semaphore() {}
|
|
||||||
|
|
||||||
// Suspends the calling thread until the semaphore counter is non zero
|
|
||||||
// and then decrements the semaphore counter.
|
|
||||||
virtual void Wait() = 0;
|
|
||||||
|
|
||||||
// Suspends the calling thread until the counter is non zero or the timeout
|
|
||||||
// time has passed. If timeout happens the return value is false and the
|
|
||||||
// counter is unchanged. Otherwise the semaphore counter is decremented and
|
|
||||||
// true is returned. The timeout value is specified in microseconds.
|
|
||||||
virtual bool Wait(int timeout) = 0;
|
|
||||||
|
|
||||||
// Increments the semaphore counter.
|
|
||||||
virtual void Signal() = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <int InitialValue>
|
|
||||||
struct CreateSemaphoreTrait {
|
|
||||||
static Semaphore* Create() {
|
|
||||||
return OS::CreateSemaphore(InitialValue);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// POD Semaphore initialized lazily (i.e. the first time Pointer() is called).
|
|
||||||
// Usage:
|
|
||||||
// // The following semaphore starts at 0.
|
|
||||||
// static LazySemaphore<0>::type my_semaphore = LAZY_SEMAPHORE_INITIALIZER;
|
|
||||||
//
|
|
||||||
// void my_function() {
|
|
||||||
// // Do something with my_semaphore.Pointer().
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
template <int InitialValue>
|
|
||||||
struct LazySemaphore {
|
|
||||||
typedef typename LazyDynamicInstance<
|
|
||||||
Semaphore, CreateSemaphoreTrait<InitialValue>,
|
|
||||||
ThreadSafeInitOnceTrait>::type type;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define LAZY_SEMAPHORE_INITIALIZER LAZY_DYNAMIC_INSTANCE_INITIALIZER
|
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Socket
|
// Socket
|
||||||
//
|
//
|
||||||
|
@ -900,14 +900,6 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) {
|
|||||||
// that a callback calls itself.
|
// that a callback calls itself.
|
||||||
*(entries.start()) = NULL;
|
*(entries.start()) = NULL;
|
||||||
*entry++ = code_map_.FindEntry(sample.external_callback);
|
*entry++ = code_map_.FindEntry(sample.external_callback);
|
||||||
} else if (sample.tos != NULL) {
|
|
||||||
// Find out, if top of stack was pointing inside a JS function
|
|
||||||
// meaning that we have encountered a frameless invocation.
|
|
||||||
*entry = code_map_.FindEntry(sample.tos);
|
|
||||||
if (*entry != NULL && !(*entry)->is_js_function()) {
|
|
||||||
*entry = NULL;
|
|
||||||
}
|
|
||||||
entry++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const Address* stack_pos = sample.stack,
|
for (const Address* stack_pos = sample.stack,
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
#include "v8.h"
|
#include "v8.h"
|
||||||
#include "cpu-profiler-inl.h"
|
#include "cpu-profiler-inl.h"
|
||||||
#include "cctest.h"
|
#include "cctest.h"
|
||||||
|
#include "utils.h"
|
||||||
#include "../include/v8-profiler.h"
|
#include "../include/v8-profiler.h"
|
||||||
|
|
||||||
using i::CodeEntry;
|
using i::CodeEntry;
|
||||||
@ -39,7 +40,9 @@ using i::CpuProfilesCollection;
|
|||||||
using i::ProfileGenerator;
|
using i::ProfileGenerator;
|
||||||
using i::ProfileNode;
|
using i::ProfileNode;
|
||||||
using i::ProfilerEventsProcessor;
|
using i::ProfilerEventsProcessor;
|
||||||
|
using i::ScopedVector;
|
||||||
using i::TokenEnumerator;
|
using i::TokenEnumerator;
|
||||||
|
using i::Vector;
|
||||||
|
|
||||||
|
|
||||||
TEST(StartStop) {
|
TEST(StartStop) {
|
||||||
@ -391,3 +394,145 @@ TEST(DeleteCpuProfileDifferentTokens) {
|
|||||||
CHECK_EQ(0, cpu_profiler->GetProfileCount());
|
CHECK_EQ(0, cpu_profiler->GetProfileCount());
|
||||||
CHECK_EQ(NULL, cpu_profiler->FindCpuProfile(uid3));
|
CHECK_EQ(NULL, cpu_profiler->FindCpuProfile(uid3));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool ContainsString(v8::Handle<v8::String> string,
|
||||||
|
const Vector<v8::Handle<v8::String> >& vector) {
|
||||||
|
for (int i = 0; i < vector.length(); i++) {
|
||||||
|
if (string->Equals(vector[i]))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void CheckChildrenNames(const v8::CpuProfileNode* node,
|
||||||
|
const Vector<v8::Handle<v8::String> >& names) {
|
||||||
|
int count = node->GetChildrenCount();
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
v8::Handle<v8::String> name = node->GetChild(i)->GetFunctionName();
|
||||||
|
CHECK(ContainsString(name, names));
|
||||||
|
// Check that there are no duplicates.
|
||||||
|
for (int j = 0; j < count; j++) {
|
||||||
|
if (j == i) continue;
|
||||||
|
CHECK_NE(name, node->GetChild(j)->GetFunctionName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const v8::CpuProfileNode* FindChild(const v8::CpuProfileNode* node,
|
||||||
|
const char* name) {
|
||||||
|
int count = node->GetChildrenCount();
|
||||||
|
v8::Handle<v8::String> nameHandle = v8::String::New(name);
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
const v8::CpuProfileNode* child = node->GetChild(i);
|
||||||
|
if (nameHandle->Equals(child->GetFunctionName())) return child;
|
||||||
|
}
|
||||||
|
CHECK(false);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void CheckSimpleBranch(const v8::CpuProfileNode* node,
|
||||||
|
const char* names[], int length) {
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
const char* name = names[i];
|
||||||
|
node = FindChild(node, name);
|
||||||
|
CHECK(node);
|
||||||
|
int expectedChildrenCount = (i == length - 1) ? 0 : 1;
|
||||||
|
CHECK_EQ(expectedChildrenCount, node->GetChildrenCount());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const char* cpu_profiler_test_source = "function loop(timeout) {\n"
|
||||||
|
" this.mmm = 0;\n"
|
||||||
|
" var start = Date.now();\n"
|
||||||
|
" while (Date.now() - start < timeout) {\n"
|
||||||
|
" var n = 100*1000;\n"
|
||||||
|
" while(n > 1) {\n"
|
||||||
|
" n--;\n"
|
||||||
|
" this.mmm += n * n * n;\n"
|
||||||
|
" }\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n"
|
||||||
|
"function delay() { try { loop(10); } catch(e) { } }\n"
|
||||||
|
"function bar() { delay(); }\n"
|
||||||
|
"function baz() { delay(); }\n"
|
||||||
|
"function foo() {\n"
|
||||||
|
" try {\n"
|
||||||
|
" delay();\n"
|
||||||
|
" bar();\n"
|
||||||
|
" delay();\n"
|
||||||
|
" baz();\n"
|
||||||
|
" } catch (e) { }\n"
|
||||||
|
"}\n"
|
||||||
|
"function start() {\n"
|
||||||
|
" var start = Date.now();\n"
|
||||||
|
" do {\n"
|
||||||
|
" foo();\n"
|
||||||
|
" var duration = Date.now() - start;\n"
|
||||||
|
" } while (duration < 200);\n"
|
||||||
|
" return duration;\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
|
||||||
|
// Check that the profile tree for the script above will look like the
|
||||||
|
// following:
|
||||||
|
//
|
||||||
|
// [Top down]:
|
||||||
|
// 1062 0 (root) [-1]
|
||||||
|
// 1054 0 start [-1]
|
||||||
|
// 1054 1 foo [-1]
|
||||||
|
// 265 0 baz [-1]
|
||||||
|
// 265 1 delay [-1]
|
||||||
|
// 264 264 loop [-1]
|
||||||
|
// 525 3 delay [-1]
|
||||||
|
// 522 522 loop [-1]
|
||||||
|
// 263 0 bar [-1]
|
||||||
|
// 263 1 delay [-1]
|
||||||
|
// 262 262 loop [-1]
|
||||||
|
// 2 2 (program) [-1]
|
||||||
|
// 6 6 (garbage collector) [-1]
|
||||||
|
TEST(CollectCpuProfile) {
|
||||||
|
LocalContext env;
|
||||||
|
v8::HandleScope scope(env->GetIsolate());
|
||||||
|
|
||||||
|
v8::Script::Compile(v8::String::New(cpu_profiler_test_source))->Run();
|
||||||
|
v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
|
||||||
|
env->Global()->Get(v8::String::New("start")));
|
||||||
|
|
||||||
|
v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
|
||||||
|
v8::Local<v8::String> profile_name = v8::String::New("my_profile");
|
||||||
|
|
||||||
|
cpu_profiler->StartCpuProfiling(profile_name);
|
||||||
|
function->Call(env->Global(), 0, 0);
|
||||||
|
const v8::CpuProfile* profile = cpu_profiler->StopCpuProfiling(profile_name);
|
||||||
|
|
||||||
|
CHECK_NE(NULL, profile);
|
||||||
|
// Dump collected profile to have a better diagnostic in case of failure.
|
||||||
|
reinterpret_cast<i::CpuProfile*>(
|
||||||
|
const_cast<v8::CpuProfile*>(profile))->Print();
|
||||||
|
|
||||||
|
const v8::CpuProfileNode* root = profile->GetTopDownRoot();
|
||||||
|
|
||||||
|
ScopedVector<v8::Handle<v8::String> > names(3);
|
||||||
|
names[0] = v8::String::New(ProfileGenerator::kGarbageCollectorEntryName);
|
||||||
|
names[1] = v8::String::New(ProfileGenerator::kProgramEntryName);
|
||||||
|
names[2] = v8::String::New("start");
|
||||||
|
CheckChildrenNames(root, names);
|
||||||
|
|
||||||
|
const v8::CpuProfileNode* startNode = FindChild(root, "start");
|
||||||
|
CHECK_EQ(1, startNode->GetChildrenCount());
|
||||||
|
|
||||||
|
const v8::CpuProfileNode* fooNode = FindChild(startNode, "foo");
|
||||||
|
CHECK_EQ(3, fooNode->GetChildrenCount());
|
||||||
|
|
||||||
|
const char* barBranch[] = { "bar", "delay", "loop" };
|
||||||
|
CheckSimpleBranch(fooNode, barBranch, ARRAY_SIZE(barBranch));
|
||||||
|
const char* bazBranch[] = { "baz", "delay", "loop" };
|
||||||
|
CheckSimpleBranch(fooNode, bazBranch, ARRAY_SIZE(bazBranch));
|
||||||
|
const char* delayBranch[] = { "delay", "loop" };
|
||||||
|
CheckSimpleBranch(fooNode, delayBranch, ARRAY_SIZE(delayBranch));
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user