summaryrefslogtreecommitdiffstats
path: root/runtime/trace.cc
diff options
context:
space:
mode:
authorMathieu Chartier <mathieuc@google.com>2015-03-11 09:54:22 -0700
committerMathieu Chartier <mathieuc@google.com>2015-03-11 10:38:26 -0700
commit02e5f160d9cc02da20d9e47af30f948c462f7043 (patch)
tree3a9ab3cc6e1e9226ef1c5818f16db873e28c3ec6 /runtime/trace.cc
parent4fe8c2a9110916386e5fe8428410cd5795f57036 (diff)
downloadart-02e5f160d9cc02da20d9e47af30f948c462f7043.tar.gz
art-02e5f160d9cc02da20d9e47af30f948c462f7043.tar.bz2
art-02e5f160d9cc02da20d9e47af30f948c462f7043.zip
Fix sampling profiler race condition
Thread 1 is running RunSamplingThread and has just read trace into the_trace. Thread 2 is calling Trace::Stop and has just suspended all the threads. At this point thread 1 is blocked on the SuspendAll. Thread 2 goes and deletes the trace which Thread 1 still has a pointer to, calls ResumeAll(). At this point thread 1 suspends the threads and adds samples to the just deleted trace. The fix is to join the thread before we delete the trace. Bug: 18950006 Change-Id: I3090c4dac392a4e5d880c4dc8d9385aef53c7425
Diffstat (limited to 'runtime/trace.cc')
-rw-r--r--runtime/trace.cc33
1 files changed, 17 insertions, 16 deletions
diff --git a/runtime/trace.cc b/runtime/trace.cc
index a1296f4a5c..8833a85120 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -401,9 +401,8 @@ void Trace::Start(const char* trace_filename, int trace_fd, int buffer_size, int
void Trace::Stop() {
bool stop_alloc_counting = false;
- Runtime* runtime = Runtime::Current();
- runtime->GetThreadList()->SuspendAll();
- Trace* the_trace = NULL;
+ Runtime* const runtime = Runtime::Current();
+ Trace* the_trace = nullptr;
pthread_t sampling_pthread = 0U;
{
MutexLock mu(Thread::Current(), *Locks::trace_lock_);
@@ -415,19 +414,27 @@ void Trace::Stop() {
sampling_pthread = sampling_pthread_;
}
}
- if (the_trace != NULL) {
+ // Make sure that we join before we delete the trace since we don't want to have
+ // the sampling thread access a stale pointer. This finishes since the sampling thread exits when
+ // the_trace_ is null.
+ if (sampling_pthread != 0U) {
+ CHECK_PTHREAD_CALL(pthread_join, (sampling_pthread, NULL), "sampling thread shutdown");
+ sampling_pthread_ = 0U;
+ }
+ runtime->GetThreadList()->SuspendAll();
+ if (the_trace != nullptr) {
stop_alloc_counting = (the_trace->flags_ & kTraceCountAllocs) != 0;
the_trace->FinishTracing();
if (the_trace->sampling_enabled_) {
MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
- runtime->GetThreadList()->ForEach(ClearThreadStackTraceAndClockBase, NULL);
+ runtime->GetThreadList()->ForEach(ClearThreadStackTraceAndClockBase, nullptr);
} else {
runtime->GetInstrumentation()->DisableMethodTracing();
- runtime->GetInstrumentation()->RemoveListener(the_trace,
- instrumentation::Instrumentation::kMethodEntered |
- instrumentation::Instrumentation::kMethodExited |
- instrumentation::Instrumentation::kMethodUnwind);
+ runtime->GetInstrumentation()->RemoveListener(
+ the_trace, instrumentation::Instrumentation::kMethodEntered |
+ instrumentation::Instrumentation::kMethodExited |
+ instrumentation::Instrumentation::kMethodUnwind);
}
if (the_trace->trace_file_.get() != nullptr) {
// Do not try to erase, so flush and close explicitly.
@@ -441,15 +448,9 @@ void Trace::Stop() {
delete the_trace;
}
runtime->GetThreadList()->ResumeAll();
-
if (stop_alloc_counting) {
// Can be racy since SetStatsEnabled is not guarded by any locks.
- Runtime::Current()->SetStatsEnabled(false);
- }
-
- if (sampling_pthread != 0U) {
- CHECK_PTHREAD_CALL(pthread_join, (sampling_pthread, NULL), "sampling thread shutdown");
- sampling_pthread_ = 0U;
+ runtime->SetStatsEnabled(false);
}
}