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() {