Ah, thanks. I had missed the memcpy in Top::RestoreThread and
Top::ArchiveThread. I tried the profiler on linux but had some trouble with
deadlocks (before making any changes to the code).

Attached is a patch that makes the Mac version of Sampler sample the thread
that holds the global lock rather than always sampling the main thread. It
compiles on Linux but I haven't tested it elsewhere.

If there is any chance that you'd accept this patch I'd be happy to make
changes or corrections.

On Wed, Sep 22, 2010 at 10:57, Mikhail Naganov <[email protected]>wrote:

> Well, I believe, it is get restored, see ThreadManager::RestoreThread
> method.
>
> Anyway, there is a small hack you can exploit: in
> SafeStackFrameIterator constructor, change is_valid_top_ to be always
> false, and stack sampling will be always done using values from
> registers, not from top. However, this might result in incomplete
> stack traces.
>
> On Wed, Sep 22, 2010 at 21:43, malcolm handley
> <[email protected]> wrote:
> > Thanks. That's good to know. I'll give linux a try.
> > I fear, though, that it's not going to work completely.
> StackTracer::Trace
> > gets the JS SP for the thread identified by Top::GetCurrentThread. As far
> as
> > I can tell GetCurrentThread actually returns the main thread. (It returns
> > Top::thread_local_, which has a comment saying "The context that
> initiated
> > this JS execution" and does not seem to be adjusted when switching
> threads.)
> > I'll look into passing a thread to StackTracer::Trace.
> >
> > On Wed, Sep 22, 2010 at 10:20, Mikhail Naganov <[email protected]>
> > wrote:
> >>
> >> Hi Malcolm,
> >>
> >> Yes, no one wanted to profile multiple VM threads yet.
> >>
> >> On Linux, profiling multiple threads may even work already -- try it.
> >> There is a function 'IsVmThread' in platform-linux.cc which checks,
> >> whether the thread that a signal handler has been called upon is a VM
> >> thread or not.
> >>
> >> On Mac & Windows, things are more complicated, because we use a
> >> dedicated profiling thread that periodically stops and samples the VM
> >> thread. So to make this work for multiple threads case, one will need
> >> to track a list of VM threads in the sampler.
> >>
> >> On Wed, Sep 22, 2010 at 20:53, malcolm handley
> >> <[email protected]> wrote:
> >> > I have multiple threads using v8 (correctly guarded by v8::Locker as
> far
> >> > as
> >> > I can tell) and I'm interested in using v8's profiler. The comment
> above
> >> > the
> >> > Profiler class in log.cc states that "The Profiler samples pc and sp
> >> > values
> >> > for the main thread.". Is there a technical reason for that or is it
> >> > just
> >> > that no one has wanted to profile other threads yet? It seems to me
> that
> >> > profiling whichever thread holds the global lock could be productive
> but
> >> > I'd
> >> > love any feedback about fruitful ways to proceed (or reasons not to).
> >> >
> >> > --
> >> > v8-users mailing list
> >> > [email protected]
> >> > http://groups.google.com/group/v8-users
> >>
> >> --
> >> v8-users mailing list
> >> [email protected]
> >> http://groups.google.com/group/v8-users
> >
> > --
> > v8-users mailing list
> > [email protected]
> > http://groups.google.com/group/v8-users
>
> --
> v8-users mailing list
> [email protected]
> http://groups.google.com/group/v8-users
>

-- 
v8-users mailing list
[email protected]
http://groups.google.com/group/v8-users
Property changes on: .
___________________________________________________________________
Index: src/platform-openbsd.cc
===================================================================
--- src/platform-openbsd.cc     (revision 4400)
+++ src/platform-openbsd.cc     (working copy)
@@ -611,6 +611,27 @@
   active_ = false;
 }
 
+
+void Sampler::ArchiveThread() {
+}
+
+
+void Sampler::RestoreThread() {
+}
+
+
+void Sampler::AssertLockedByCurrentThread() {
+}
+
+
+void Sampler::Lock() {
+}
+
+
+void Sampler::Unlock() {
+}
+
+
 #endif  // ENABLE_LOGGING_AND_PROFILING
 
 } }  // namespace v8::internal
Index: src/v8threads.cc
===================================================================
--- src/v8threads.cc    (revision 4400)
+++ src/v8threads.cc    (working copy)
@@ -125,6 +125,10 @@
 
 
 bool ThreadManager::RestoreThread() {
+#ifdef ENABLE_LOGGING_AND_PROFILING
+  Sampler::Lock();
+#endif
+
   // First check whether the current thread has been 'lazily archived', ie
   // not archived at all.  If that is the case we put the state storage we
   // had prepared back in the free list, since we didn't need it after all.
@@ -136,6 +140,9 @@
     lazily_archived_thread_state_->LinkInto(ThreadState::FREE_LIST);
     lazily_archived_thread_state_ = NULL;
     Thread::SetThreadLocal(thread_state_key, NULL);
+#ifdef ENABLE_LOGGING_AND_PROFILING
+    Sampler::Unlock();
+#endif
     return true;
   }
 
@@ -153,6 +160,9 @@
   if (state == NULL) {
     // This is a new thread.
     StackGuard::InitThread(access);
+#ifdef ENABLE_LOGGING_AND_PROFILING
+    Sampler::Unlock();
+#endif
     return false;
   }
   char* from = state->data();
@@ -162,6 +172,7 @@
 #ifdef ENABLE_DEBUGGER_SUPPORT
   from = Debug::RestoreDebug(from);
 #endif
+  Logger::RestoreThread();
   from = StackGuard::RestoreStackGuard(from);
   from = RegExpStack::RestoreStack(from);
   from = Bootstrapper::RestoreState(from);
@@ -173,6 +184,9 @@
   state->set_id(kInvalidId);
   state->Unlink();
   state->LinkInto(ThreadState::FREE_LIST);
+#ifdef ENABLE_LOGGING_AND_PROFILING
+  Sampler::Unlock();
+#endif
   return true;
 }
 
@@ -282,6 +296,9 @@
 
 
 void ThreadManager::EagerlyArchiveThread() {
+#ifdef ENABLE_LOGGING_AND_PROFILING
+  Sampler::Lock();
+#endif
   ThreadState* state = lazily_archived_thread_state_;
   state->LinkInto(ThreadState::IN_USE_LIST);
   char* to = state->data();
@@ -293,11 +310,15 @@
 #ifdef ENABLE_DEBUGGER_SUPPORT
   to = Debug::ArchiveDebug(to);
 #endif
+  Logger::ArchiveThread();
   to = StackGuard::ArchiveStackGuard(to);
   to = RegExpStack::ArchiveStack(to);
   to = Bootstrapper::ArchiveState(to);
   lazily_archived_thread_.Initialize(ThreadHandle::INVALID);
   lazily_archived_thread_state_ = NULL;
+#ifdef ENABLE_LOGGING_AND_PROFILING
+  Sampler::Unlock();
+#endif
 }
 
 
Index: src/platform-win32.cc
===================================================================
--- src/platform-win32.cc       (revision 4400)
+++ src/platform-win32.cc       (working copy)
@@ -1905,6 +1905,26 @@
 }
 
 
+void Sampler::ArchiveThread() {
+}
+
+
+void Sampler::RestoreThread() {
+}
+
+
+void Sampler::AssertLockedByCurrentThread() {
+}
+
+
+void Sampler::Lock() {
+}
+
+
+void Sampler::Unlock() {
+}
+
+
 #endif  // ENABLE_LOGGING_AND_PROFILING
 
 } }  // namespace v8::internal
Index: src/platform-linux.cc
===================================================================
--- src/platform-linux.cc       (revision 4400)
+++ src/platform-linux.cc       (working copy)
@@ -840,6 +840,26 @@
 }
 
 
+void Sampler::ArchiveThread() {
+}
+
+
+void Sampler::RestoreThread() {
+}
+
+
+void Sampler::AssertLockedByCurrentThread() {
+}
+
+
+void Sampler::Lock() {
+}
+
+
+void Sampler::Unlock() {
+}
+
+
 #endif  // ENABLE_LOGGING_AND_PROFILING
 
 } }  // namespace v8::internal
Index: src/platform.h
===================================================================
--- src/platform.h      (revision 4400)
+++ src/platform.h      (working copy)
@@ -556,6 +556,21 @@
   // Whether the sampler is running (that is, consumes resources).
   inline bool IsActive() { return active_; }
 
+  // Indicates that the current thread is releasing or acquiring the global
+  // lock. The sampler must be locked for all of the work involved in 
archiving/
+  // resuming a thread.
+  void ArchiveThread();
+  void RestoreThread();
+  // We provide a method for asserting the lock status rather than testing it 
so
+  // that platforms that do not care about this do not have to track the lock
+  // status.
+  static void AssertLockedByCurrentThread();
+  // The sampler must be locked when the current thread is changing (being
+  // restored or archived) so that we do not try to take data structures are
+  // being updated to reflect the change.
+  static void Lock();
+  static void Unlock();
+
   class PlatformData;
 
  private:
Index: src/platform-solaris.cc
===================================================================
--- src/platform-solaris.cc     (revision 4400)
+++ src/platform-solaris.cc     (working copy)
@@ -602,6 +602,27 @@
   active_ = false;
 }
 
+
+void Sampler::ArchiveThread() {
+}
+
+
+void Sampler::RestoreThread() {
+}
+
+
+void Sampler::AssertLockedByCurrentThread() {
+}
+
+
+void Sampler::Lock() {
+}
+
+
+void Sampler::Unlock() {
+}
+
+
 #endif  // ENABLE_LOGGING_AND_PROFILING
 
 } }  // namespace v8::internal
Index: src/log.h
===================================================================
--- src/log.h   (revision 4400)
+++ src/log.h   (working copy)
@@ -258,6 +258,10 @@
   static void ResumeProfiler(int flags, int tag);
   static int GetActiveProfilerModules();
 
+  // Called when the current thread is being archived or restored.
+  static void ArchiveThread();
+  static void RestoreThread();
+
   // If logging is performed into a memory buffer, allows to
   // retrieve previously written messages. See v8.h.
   static int GetLogLines(int from_pos, char* dest_buf, int max_size);
Index: src/platform-nullos.cc
===================================================================
--- src/platform-nullos.cc      (revision 4400)
+++ src/platform-nullos.cc      (working copy)
@@ -449,6 +449,27 @@
   UNIMPLEMENTED();
 }
 
+
+void Sampler::ArchiveThread() {
+}
+
+
+void Sampler::RestoreThread() {
+}
+
+
+void Sampler::AssertLockedByCurrentThread() {
+}
+
+
+void Sampler::Lock() {
+}
+
+
+void Sampler::Unlock() {
+}
+
+
 #endif  // ENABLE_LOGGING_AND_PROFILING
 
 } }  // namespace v8::internal
Index: src/platform-freebsd.cc
===================================================================
--- src/platform-freebsd.cc     (revision 4400)
+++ src/platform-freebsd.cc     (working copy)
@@ -659,6 +659,27 @@
   active_ = false;
 }
 
+
+void Sampler::ArchiveThread() {
+}
+
+
+void Sampler::RestoreThread() {
+}
+
+
+void Sampler::AssertLockedByCurrentThread() {
+}
+
+
+void Sampler::Lock() {
+}
+
+
+void Sampler::Unlock() {
+}
+
+
 #endif  // ENABLE_LOGGING_AND_PROFILING
 
 } }  // namespace v8::internal
Index: src/platform-macos.cc
===================================================================
--- src/platform-macos.cc       (revision 4400)
+++ src/platform-macos.cc       (working copy)
@@ -539,6 +539,7 @@
   // because the latter doesn't provide thread manipulation primitives 
required.
   // For details, consult "Mac OS X Internals" book, Section 7.3.
   mach_port_t task_self_;
+  thread_act_t main_thread_;
   thread_act_t profiled_thread_;
   pthread_t sampler_thread_;
 
@@ -546,6 +547,7 @@
   void Runner() {
     // Loop until the sampler is disengaged, keeping the specified samling 
freq.
     for ( ; sampler_->IsActive(); OS::Sleep(sampler_->interval_)) {
+      Lock();
       TickSample sample_obj;
       TickSample* sample = NULL;
 #ifdef ENABLE_CPP_PROFILES_PROCESSOR
@@ -594,6 +596,7 @@
 
       // Invoke tick handler with program counter and stack pointer.
       sampler_->Tick(sample);
+      Unlock();
     }
   }
 };
@@ -610,6 +613,10 @@
 }
 
 
+static Mutex* sampler_mutex = OS::CreateMutex();
+static ThreadHandle sampler_mutex_owner(ThreadHandle::INVALID);
+
+
 Sampler::Sampler(int interval, bool profiling)
     : interval_(interval), profiling_(profiling), active_(false) {
   data_ = new PlatformData(this);
@@ -626,6 +633,7 @@
   // thread.
   if (IsProfiling()) {
     data_->profiled_thread_ = mach_thread_self();
+    data_->main_thread_ = data_->profiled_thread_;
   }
 
   // Create sampler thread with high priority.
@@ -658,6 +666,45 @@
   }
 }
 
+
+void Sampler::ArchiveThread() {
+  // The sampler must be locked before switching threads.
+  AssertLockedByCurrentThread();
+  if (IsProfiling()) {
+    // After a thread releases the lock we can't be sure that it still exists.
+    // Instead sample the main thread until another thread gets the lock.
+    data_->profiled_thread_ = data_->main_thread_;
+  }
+}
+
+
+void Sampler::RestoreThread() {
+  // The sampler must be locked before switching threads.
+  AssertLockedByCurrentThread();
+  if (IsProfiling()) {
+    data_->profiled_thread_ = mach_thread_self();
+  }
+}
+
+
+void Sampler::AssertLockedByCurrentThread() {
+  ASSERT(sampler_mutex_owner.IsSelf());
+}
+
+
+void Sampler::Lock() {
+  sampler_mutex->Lock();
+  sampler_mutex_owner.Initialize(ThreadHandle::SELF);
+  AssertLockedByCurrentThread();
+}
+
+
+void Sampler::Unlock() {
+  sampler_mutex->Unlock();
+  sampler_mutex_owner.Initialize(ThreadHandle::INVALID);
+}
+
+
 #endif  // ENABLE_LOGGING_AND_PROFILING
 
 } }  // namespace v8::internal
Index: src/log.cc
===================================================================
--- src/log.cc  (revision 4400)
+++ src/log.cc  (working copy)
@@ -1218,6 +1218,20 @@
 }
 
 
+void Logger::ArchiveThread() {
+#ifdef ENABLE_LOGGING_AND_PROFILING
+  ticker_->ArchiveThread();
+#endif
+}
+
+
+void Logger::RestoreThread() {
+#ifdef ENABLE_LOGGING_AND_PROFILING
+  ticker_->RestoreThread();
+#endif
+}
+
+
 // This function can be called when Log's mutex is acquired,
 // either from main or Profiler's thread.
 void Logger::StopLoggingAndProfiling() {

Reply via email to