generator_ = new ProfileGenerator(profiles_);
processor_ = new ProfilerEventsProcessor(generator_);
is_profiling_ = true;
- processor_->Start();
+ processor_->StartSynchronously();
// Enumerate stuff we already have in the heap.
if (isolate_->heap()->HasBeenSetUp()) {
if (!FLAG_prof_browser_mode) {
}
// Enable stack sampling.
Sampler* sampler = reinterpret_cast<Sampler*>(isolate_->logger()->ticker_);
+ sampler->IncreaseProfilingDepth();
if (!sampler->IsActive()) {
sampler->Start();
need_to_stop_sampler_ = true;
}
- sampler->IncreaseProfilingDepth();
}
}
Thread::Thread(const Options& options)
: data_(new PlatformData()),
- stack_size_(options.stack_size()) {
+ stack_size_(options.stack_size()),
+ start_semaphore_(NULL) {
set_name(options.name());
}
// one) so we initialize it here too.
thread->data()->thread_ = pthread_self();
ASSERT(thread->data()->thread_ != kNoThread);
- thread->Run();
+ thread->NotifyStartedAndRun();
return NULL;
}
SamplerRegistry::AddActiveSampler(sampler);
if (instance_ == NULL) {
instance_ = new SamplerThread(sampler->interval());
- instance_->Start();
+ instance_->StartSynchronously();
} else {
ASSERT(instance_->interval_ == sampler->interval());
}
Thread::Thread(const Options& options)
: data_(new PlatformData),
- stack_size_(options.stack_size()) {
+ stack_size_(options.stack_size()),
+ start_semaphore_(NULL) {
set_name(options.name());
}
// one) so we initialize it here too.
thread->data()->thread_ = pthread_self();
ASSERT(thread->data()->thread_ != kNoThread);
- thread->Run();
+ thread->NotifyStartedAndRun();
return NULL;
}
// Start a thread that sends SIGPROF signal to VM threads.
instance_ = new SignalSender(sampler->interval());
- instance_->Start();
+ instance_->StartSynchronously();
} else {
ASSERT(instance_->interval_ == sampler->interval());
}
Thread::Thread(const Options& options)
: data_(new PlatformData()),
- stack_size_(options.stack_size()) {
+ stack_size_(options.stack_size()),
+ start_semaphore_(NULL) {
set_name(options.name());
}
#endif
thread->data()->thread_ = pthread_self();
ASSERT(thread->data()->thread_ != kNoThread);
- thread->Run();
+ thread->NotifyStartedAndRun();
return NULL;
}
// Start a thread that will send SIGPROF signal to VM threads,
// when CPU profiling will be enabled.
instance_ = new SignalSender(sampler->interval());
- instance_->Start();
+ instance_->StartSynchronously();
} else {
ASSERT(instance_->interval_ == sampler->interval());
}
Thread::Thread(const Options& options)
: data_(new PlatformData),
- stack_size_(options.stack_size()) {
+ stack_size_(options.stack_size()),
+ start_semaphore_(NULL) {
set_name(options.name());
}
thread->data()->thread_ = pthread_self();
SetThreadName(thread->name());
ASSERT(thread->data()->thread_ != kNoThread);
- thread->Run();
+ thread->NotifyStartedAndRun();
return NULL;
}
SamplerRegistry::AddActiveSampler(sampler);
if (instance_ == NULL) {
instance_ = new SamplerThread(sampler->interval());
- instance_->Start();
+ instance_->StartSynchronously();
} else {
ASSERT(instance_->interval_ == sampler->interval());
}
Thread::Thread(const Options& options)
: data_(new PlatformData()),
- stack_size_(options.stack_size) {
+ stack_size_(options.stack_size),
+ start_semaphore_(NULL) {
set_name(options.name);
UNIMPLEMENTED();
}
Thread::Thread(const Options& options)
: data_(new PlatformData()),
- stack_size_(options.stack_size()) {
+ stack_size_(options.stack_size()),
+ start_semaphore_(NULL) {
set_name(options.name());
}
#endif
thread->data()->thread_ = pthread_self();
ASSERT(thread->data()->thread_ != kNoThread);
- thread->Run();
+ thread->NotifyStartedAndRun();
return NULL;
}
// Start a thread that will send SIGPROF signal to VM threads,
// when CPU profiling will be enabled.
instance_ = new SignalSender(sampler->interval());
- instance_->Start();
+ instance_->StartSynchronously();
} else {
ASSERT(instance_->interval_ == sampler->interval());
}
Thread::Thread(const Options& options)
: data_(new PlatformData()),
- stack_size_(options.stack_size()) {
+ stack_size_(options.stack_size()),
+ start_semaphore_(NULL) {
set_name(options.name());
}
// one) so we initialize it here too.
thread->data()->thread_ = pthread_self();
ASSERT(thread->data()->thread_ != kNoThread);
- thread->Run();
+ thread->NotifyStartedAndRun();
return NULL;
}
// Start a thread that will send SIGPROF signal to VM threads,
// when CPU profiling will be enabled.
instance_ = new SignalSender(sampler->interval());
- instance_->Start();
+ instance_->StartSynchronously();
} else {
ASSERT(instance_->interval_ == sampler->interval());
}
// convention.
static unsigned int __stdcall ThreadEntry(void* arg) {
Thread* thread = reinterpret_cast<Thread*>(arg);
- thread->Run();
+ thread->NotifyStartedAndRun();
return 0;
}
// handle until it is started.
Thread::Thread(const Options& options)
- : stack_size_(options.stack_size()) {
+ : stack_size_(options.stack_size()),
+ start_semaphore_(NULL) {
data_ = new PlatformData(kNoThread);
set_name(options.name());
}
SamplerRegistry::AddActiveSampler(sampler);
if (instance_ == NULL) {
instance_ = new SamplerThread(sampler->interval());
- instance_->Start();
+ instance_->StartSynchronously();
} else {
ASSERT(instance_->interval_ == sampler->interval());
}
};
+// ----------------------------------------------------------------------------
+// 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
//
explicit Thread(const Options& options);
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();
+ // 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.
void Join();
class PlatformData;
PlatformData* data() { return data_; }
+ void NotifyStartedAndRun() {
+ if (start_semaphore_) start_semaphore_->Signal();
+ Run();
+ }
+
private:
void set_name(const char* name);
char name_[kMaxThreadNameLength];
int stack_size_;
+ Semaphore* start_semaphore_;
DISALLOW_COPY_AND_ASSIGN(Thread);
};
};
-// ----------------------------------------------------------------------------
-// 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
//
// that a callback calls itself.
*(entries.start()) = NULL;
*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,
#include "v8.h"
#include "cpu-profiler-inl.h"
#include "cctest.h"
+#include "utils.h"
#include "../include/v8-profiler.h"
using i::CodeEntry;
using i::ProfileGenerator;
using i::ProfileNode;
using i::ProfilerEventsProcessor;
+using i::ScopedVector;
using i::TokenEnumerator;
+using i::Vector;
TEST(StartStop) {
CHECK_EQ(0, cpu_profiler->GetProfileCount());
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));
+}