OSDN Git Service

Fix method tracing from command-line
authorSebastien Hertz <shertz@google.com>
Tue, 17 Dec 2013 09:42:03 +0000 (10:42 +0100)
committerSebastien Hertz <shertz@google.com>
Wed, 9 Jul 2014 14:10:45 +0000 (16:10 +0200)
Transitions current thread to the new kWaitingForMethodTracingStart thread
state when starting method tracing.

Ensures there is a current thread when method tracing is stopped due to runtime
shutdown. If the current thread has been detached, we now re-attach it.
Note: we only do this if method tracing has been activated from command-line.

Fixes instrumentation when forcing interpreter mode (-Xint) with method tracing
enabled.

Removes unused parameter from UnsafeLogFatalForThreadSuspendAllTimeout.

Bug: https://code.google.com/p/android/issues/detail?id=72094
Bug: 11683397
Change-Id: I70f000fb46ddd95d6ad51ea0a8eee77697a045e9

runtime/debugger.cc
runtime/instrumentation.cc
runtime/native/java_lang_Thread.cc
runtime/runtime.cc
runtime/thread_list.cc
runtime/thread_state.h
runtime/trace.h
test/304-method-tracing/expected.txt [new file with mode: 0644]
test/304-method-tracing/info.txt [new file with mode: 0644]
test/304-method-tracing/run [new file with mode: 0755]
test/304-method-tracing/src/Main.java [new file with mode: 0644]

index f19c353..4474e71 100644 (file)
@@ -1982,13 +1982,14 @@ JDWP::JdwpThreadStatus Dbg::ToJdwpThreadStatus(ThreadState state) {
     case kTerminated:
       return JDWP::TS_ZOMBIE;
     case kTimedWaiting:
+    case kWaitingForCheckPointsToRun:
     case kWaitingForDebuggerSend:
     case kWaitingForDebuggerSuspension:
     case kWaitingForDebuggerToAttach:
     case kWaitingForDeoptimization:
     case kWaitingForGcToComplete:
-    case kWaitingForCheckPointsToRun:
     case kWaitingForJniOnLoad:
+    case kWaitingForMethodTracingStart:
     case kWaitingForSignalCatcherOutput:
     case kWaitingInMainDebuggerLoop:
     case kWaitingInMainSignalCatcherLoop:
index 8f5da83..f459b59 100644 (file)
@@ -137,7 +137,8 @@ void Instrumentation::InstallStubsForMethod(mirror::ArtMethod* method) {
       new_quick_code = GetQuickResolutionTrampoline(class_linker);
     }
   } else {  // !uninstall
-    if ((interpreter_stubs_installed_ || IsDeoptimized(method)) && !method->IsNative()) {
+    if ((interpreter_stubs_installed_ || forced_interpret_only_ || IsDeoptimized(method)) &&
+        !method->IsNative()) {
       new_portable_code = GetPortableToInterpreterBridge();
       new_quick_code = GetQuickToInterpreterBridge();
     } else {
@@ -150,7 +151,9 @@ void Instrumentation::InstallStubsForMethod(mirror::ArtMethod* method) {
         new_quick_code = class_linker->GetQuickOatCodeFor(method);
         DCHECK(new_quick_code != GetQuickToInterpreterBridgeTrampoline(class_linker));
         if (entry_exit_stubs_installed_ && new_quick_code != GetQuickToInterpreterBridge()) {
-          DCHECK(new_portable_code != GetPortableToInterpreterBridge());
+          // TODO: portable to quick bridge. Bug: 8196384. We cannot enable the check below as long
+          // as GetPortableToQuickBridge() == GetPortableToInterpreterBridge().
+          // DCHECK(new_portable_code != GetPortableToInterpreterBridge());
           new_portable_code = GetPortableToInterpreterBridge();
           new_quick_code = GetQuickInstrumentationEntryPoint();
         }
index 86db893..bae67f2 100644 (file)
@@ -85,6 +85,7 @@ static jint Thread_nativeGetStatus(JNIEnv* env, jobject java_thread, jboolean ha
     case kWaitingForJniOnLoad:            return kJavaWaiting;
     case kWaitingForSignalCatcherOutput:  return kJavaWaiting;
     case kWaitingInMainSignalCatcherLoop: return kJavaWaiting;
+    case kWaitingForMethodTracingStart:   return kJavaWaiting;
     case kSuspended:                      return kJavaRunnable;
     // Don't add a 'default' here so the compiler can spot incompatible enum changes.
   }
index 8aa7ea1..9bcbf13 100644 (file)
@@ -147,6 +147,13 @@ Runtime::Runtime()
 }
 
 Runtime::~Runtime() {
+  if (method_trace_ && Thread::Current() == nullptr) {
+    // We need a current thread to shutdown method tracing: re-attach it now.
+    JNIEnv* unused_env;
+    if (GetJavaVM()->AttachCurrentThread(&unused_env, nullptr) != JNI_OK) {
+      LOG(ERROR) << "Could not attach current thread before runtime shutdown.";
+    }
+  }
   if (dump_gc_performance_on_shutdown_) {
     // This can't be called from the Heap destructor below because it
     // could call RosAlloc::InspectAll() which needs the thread_list
@@ -675,6 +682,7 @@ bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) {
   Trace::SetDefaultClockSource(options->profile_clock_source_);
 
   if (options->method_trace_) {
+    ScopedThreadStateChange tsc(self, kWaitingForMethodTracingStart);
     Trace::Start(options->method_trace_file_.c_str(), -1, options->method_trace_file_size_, 0,
                  false, false, 0);
   }
index d20a459..54732fa 100644 (file)
@@ -153,8 +153,8 @@ void ThreadList::AssertThreadsAreSuspended(Thread* self, Thread* ignore1, Thread
 
 #if HAVE_TIMED_RWLOCK
 // Attempt to rectify locks so that we dump thread list with required locks before exiting.
-static void UnsafeLogFatalForThreadSuspendAllTimeout(Thread* self) NO_THREAD_SAFETY_ANALYSIS __attribute__((noreturn));
-static void UnsafeLogFatalForThreadSuspendAllTimeout(Thread* self) {
+static void UnsafeLogFatalForThreadSuspendAllTimeout() NO_THREAD_SAFETY_ANALYSIS __attribute__((noreturn));
+static void UnsafeLogFatalForThreadSuspendAllTimeout() {
   Runtime* runtime = Runtime::Current();
   std::ostringstream ss;
   ss << "Thread suspend timeout\n";
@@ -332,7 +332,7 @@ void ThreadList::SuspendAll() {
 #if HAVE_TIMED_RWLOCK
   // Timeout if we wait more than 30 seconds.
   if (!Locks::mutator_lock_->ExclusiveLockWithTimeout(self, 30 * 1000, 0)) {
-    UnsafeLogFatalForThreadSuspendAllTimeout(self);
+    UnsafeLogFatalForThreadSuspendAllTimeout();
   }
 #else
   Locks::mutator_lock_->ExclusiveLock(self);
@@ -351,6 +351,7 @@ void ThreadList::SuspendAll() {
 
 void ThreadList::ResumeAll() {
   Thread* self = Thread::Current();
+  DCHECK(self != nullptr);
 
   VLOG(threads) << *self << " ResumeAll starting";
 
@@ -587,7 +588,7 @@ void ThreadList::SuspendAllForDebugger() {
 #if HAVE_TIMED_RWLOCK
   // Timeout if we wait more than 30 seconds.
   if (!Locks::mutator_lock_->ExclusiveLockWithTimeout(self, 30 * 1000, 0)) {
-    UnsafeLogFatalForThreadSuspendAllTimeout(self);
+    UnsafeLogFatalForThreadSuspendAllTimeout();
   } else {
     Locks::mutator_lock_->ExclusiveUnlock(self);
   }
index 57bf4f1..0e47d21 100644 (file)
@@ -38,6 +38,7 @@ enum ThreadState {
   kWaitingForSignalCatcherOutput,   // WAITING        TS_WAIT      waiting for signal catcher IO to complete
   kWaitingInMainSignalCatcherLoop,  // WAITING        TS_WAIT      blocking/reading/processing signals
   kWaitingForDeoptimization,        // WAITING        TS_WAIT      waiting for deoptimization suspend all
+  kWaitingForMethodTracingStart,    // WAITING        TS_WAIT      waiting for method tracing to start
   kStarting,                        // NEW            TS_WAIT      native thread started, not yet ready to run managed code
   kNative,                          // RUNNABLE       TS_RUNNING   running in a JNI native method
   kSuspended,                       // RUNNABLE       TS_RUNNING   suspended by GC or debugger
index 08da16f..3f5d80a 100644 (file)
@@ -65,11 +65,14 @@ class Trace FINAL : public instrumentation::InstrumentationListener {
 
   static void Start(const char* trace_filename, int trace_fd, int buffer_size, int flags,
                     bool direct_to_ddms, bool sampling_enabled, int interval_us)
-  LOCKS_EXCLUDED(Locks::mutator_lock_,
-                 Locks::thread_list_lock_,
-                 Locks::thread_suspend_count_lock_,
-                 Locks::trace_lock_);
-  static void Stop() LOCKS_EXCLUDED(Locks::trace_lock_);
+      LOCKS_EXCLUDED(Locks::mutator_lock_,
+                     Locks::thread_list_lock_,
+                     Locks::thread_suspend_count_lock_,
+                     Locks::trace_lock_);
+  static void Stop()
+      LOCKS_EXCLUDED(Locks::mutator_lock_,
+                     Locks::thread_list_lock_,
+                     Locks::trace_lock_);
   static void Shutdown() LOCKS_EXCLUDED(Locks::trace_lock_);
   static TracingMode GetMethodTracingMode() LOCKS_EXCLUDED(Locks::trace_lock_);
 
diff --git a/test/304-method-tracing/expected.txt b/test/304-method-tracing/expected.txt
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/304-method-tracing/info.txt b/test/304-method-tracing/info.txt
new file mode 100644 (file)
index 0000000..d3154e6
--- /dev/null
@@ -0,0 +1 @@
+Test method tracing from command-line.
diff --git a/test/304-method-tracing/run b/test/304-method-tracing/run
new file mode 100755 (executable)
index 0000000..7bd1895
--- /dev/null
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Runs the test with method tracing enabled.
+exec ${RUN} "$@" --runtime-option -Xmethod-trace --runtime-option -Xmethod-trace-file:${DEX_LOCATION}/trace.bin
diff --git a/test/304-method-tracing/src/Main.java b/test/304-method-tracing/src/Main.java
new file mode 100644 (file)
index 0000000..25cee6d
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.ArrayList;
+
+public class Main {
+    static class ThreadRunnable implements Runnable {
+        public void run() {
+            for (int i = 0; i < 1000; ++i) {
+                doNothing();
+            }
+        }
+
+        private void doNothing() {}
+    }
+
+    public static void main(String[] args) {
+        ArrayList<Thread> threads = new ArrayList<Thread>();
+        for (int i = 0; i < 10; ++i) {
+            threads.add(new Thread(new ThreadRunnable(), "TestThread-" + i));
+        }
+
+        for (Thread t : threads) {
+            t.start();
+        }
+
+        for (Thread t : threads) {
+            try {
+                t.join();
+            } catch (InterruptedException e) {
+                System.out.println("Thread " + t.getName() + " has been interrupted");
+            }
+        }
+    }
+}