Index: test/cctest/test-log.cc |
diff --git a/test/cctest/test-log.cc b/test/cctest/test-log.cc |
index 5884a419a9baede2ef1ce1a16881898b96ec3bf8..dafd3aaad59afd6b9122f304461f82c300e68920 100644 |
--- a/test/cctest/test-log.cc |
+++ b/test/cctest/test-log.cc |
@@ -5,12 +5,15 @@ |
#ifdef ENABLE_LOGGING_AND_PROFILING |
#ifdef __linux__ |
+#include <math.h> |
+#include <pthread.h> |
#include <signal.h> |
#include <unistd.h> |
-#endif |
+#endif // __linux__ |
#include "v8.h" |
#include "log.h" |
+#include "v8threads.h" |
#include "cctest.h" |
using v8::internal::Address; |
@@ -155,9 +158,10 @@ static bool was_sigprof_received = true; |
#ifdef __linux__ |
struct sigaction old_sigprof_handler; |
+pthread_t our_thread; |
static void SigProfSignalHandler(int signal, siginfo_t* info, void* context) { |
- if (signal != SIGPROF) return; |
+ if (signal != SIGPROF || !pthread_equal(pthread_self(), our_thread)) return; |
was_sigprof_received = true; |
old_sigprof_handler.sa_sigaction(signal, info, context); |
} |
@@ -185,6 +189,7 @@ static int CheckThatProfilerWorks(int log_pos) { |
// Intercept SIGPROF handler to make sure that the test process |
// had received it. Under load, system can defer it causing test failure. |
// It is important to execute this after 'ResumeProfiler'. |
+ our_thread = pthread_self(); |
was_sigprof_received = false; |
struct sigaction sa; |
sa.sa_sigaction = SigProfSignalHandler; |
@@ -280,6 +285,158 @@ TEST(ProfLazyMode) { |
} |
+// Profiling multiple threads that use V8 is currently only available on Linux. |
+#ifdef __linux__ |
+ |
+namespace { |
+ |
+class LoopingThread : public v8::internal::Thread { |
+ public: |
+ LoopingThread() |
+ : v8::internal::Thread(), |
+ semaphore_(v8::internal::OS::CreateSemaphore(0)), |
+ run_(true) { |
+ } |
+ |
+ virtual ~LoopingThread() { delete semaphore_; } |
+ |
+ void Run() { |
+ self_ = pthread_self(); |
+ RunLoop(); |
+ } |
+ |
+ void SendSigProf() { pthread_kill(self_, SIGPROF); } |
+ |
+ void Stop() { run_ = false; } |
+ |
+ bool WaitForRunning() { return semaphore_->Wait(1000000); } |
+ |
+ protected: |
+ bool IsRunning() { return run_; } |
+ |
+ virtual void RunLoop() = 0; |
+ |
+ void SetV8ThreadId() { |
+ v8_thread_id_ = v8::V8::GetCurrentThreadId(); |
+ } |
+ |
+ void SignalRunning() { semaphore_->Signal(); } |
+ |
+ private: |
+ v8::internal::Semaphore* semaphore_; |
+ bool run_; |
+ pthread_t self_; |
+ int v8_thread_id_; |
+}; |
+ |
+ |
+class LoopingJsThread : public LoopingThread { |
+ public: |
+ void RunLoop() { |
+ { |
+ v8::Locker locker; |
+ CHECK(v8::internal::ThreadManager::HasId()); |
+ SetV8ThreadId(); |
+ } |
+ while (IsRunning()) { |
+ v8::Locker locker; |
+ v8::HandleScope scope; |
+ v8::Persistent<v8::Context> context = v8::Context::New(); |
+ v8::Context::Scope context_scope(context); |
+ SignalRunning(); |
+ CompileAndRunScript( |
+ "var j; for (var i=0; i<10000; ++i) { j = Math.sin(i); }"); |
+ context.Dispose(); |
+ i::OS::Sleep(1); |
+ } |
+ } |
+}; |
+ |
+ |
+class LoopingNonJsThread : public LoopingThread { |
+ public: |
+ void RunLoop() { |
+ v8::Locker locker; |
+ v8::Unlocker unlocker; |
+ // Now thread has V8's id, but will not run VM code. |
+ CHECK(v8::internal::ThreadManager::HasId()); |
+ double i = 10; |
+ SignalRunning(); |
+ while (IsRunning()) { |
+ i = sin(i); |
+ i::OS::Sleep(1); |
+ } |
+ } |
+}; |
+ |
+ |
+class TestSampler : public v8::internal::Sampler { |
+ public: |
+ TestSampler() |
+ : Sampler(0, true), |
+ semaphore_(v8::internal::OS::CreateSemaphore(0)), |
+ was_sample_stack_called_(false) { |
+ } |
+ |
+ ~TestSampler() { delete semaphore_; } |
+ |
+ void SampleStack(v8::internal::TickSample*) { |
+ was_sample_stack_called_ = true; |
+ } |
+ |
+ void Tick(v8::internal::TickSample*) { semaphore_->Signal(); } |
+ |
+ bool WaitForTick() { return semaphore_->Wait(1000000); } |
+ |
+ void Reset() { was_sample_stack_called_ = false; } |
+ |
+ bool WasSampleStackCalled() { return was_sample_stack_called_; } |
+ |
+ private: |
+ v8::internal::Semaphore* semaphore_; |
+ bool was_sample_stack_called_; |
+}; |
+ |
+ |
+} // namespace |
+ |
+TEST(ProfMultipleThreads) { |
+ // V8 needs to be initialized before the first Locker |
+ // instantiation. Otherwise, Top::Initialize will reset |
+ // thread_id_ in ThreadTopLocal. |
+ v8::HandleScope scope; |
+ v8::Handle<v8::Context> env = v8::Context::New(); |
+ env->Enter(); |
+ |
+ LoopingJsThread jsThread; |
+ jsThread.Start(); |
+ LoopingNonJsThread nonJsThread; |
+ nonJsThread.Start(); |
+ |
+ TestSampler sampler; |
+ sampler.Start(); |
+ CHECK(!sampler.WasSampleStackCalled()); |
+ jsThread.WaitForRunning(); |
+ jsThread.SendSigProf(); |
+ CHECK(sampler.WaitForTick()); |
+ CHECK(sampler.WasSampleStackCalled()); |
+ sampler.Reset(); |
+ CHECK(!sampler.WasSampleStackCalled()); |
+ nonJsThread.WaitForRunning(); |
+ nonJsThread.SendSigProf(); |
+ CHECK(sampler.WaitForTick()); |
+ CHECK(!sampler.WasSampleStackCalled()); |
+ sampler.Stop(); |
+ |
+ jsThread.Stop(); |
+ nonJsThread.Stop(); |
+ jsThread.Join(); |
+ nonJsThread.Join(); |
+} |
+ |
+#endif // __linux__ |
+ |
+ |
static inline bool IsStringEqualTo(const char* r, const char* s) { |
return strncmp(r, s, strlen(r)) == 0; |
} |