1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "org_apache_harmony_dalvik_ddmc_DdmVmInternal.h"
18 
19 #include <android-base/logging.h>
20 
21 #include "base/file_utils.h"
22 #include "base/mutex.h"
23 #include "base/endian_utils.h"
24 #include "debugger.h"
25 #include "gc/heap.h"
26 #include "jni/jni_internal.h"
27 #include "native_util.h"
28 #include "nativehelper/jni_macros.h"
29 #include "nativehelper/scoped_local_ref.h"
30 #include "nativehelper/scoped_primitive_array.h"
31 #include "scoped_fast_native_object_access-inl.h"
32 #include "thread_list.h"
33 
34 namespace art {
35 
DdmVmInternal_setRecentAllocationsTrackingEnabled(JNIEnv *,jclass,jboolean enable)36 static void DdmVmInternal_setRecentAllocationsTrackingEnabled(JNIEnv*, jclass, jboolean enable) {
37   Dbg::SetAllocTrackingEnabled(enable);
38 }
39 
DdmVmInternal_setThreadNotifyEnabled(JNIEnv *,jclass,jboolean enable)40 static void DdmVmInternal_setThreadNotifyEnabled(JNIEnv*, jclass, jboolean enable) {
41   Dbg::DdmSetThreadNotification(enable);
42 }
43 
GetSelf(JNIEnv * env)44 static Thread* GetSelf(JNIEnv* env) {
45   return static_cast<JNIEnvExt*>(env)->GetSelf();
46 }
47 
48 /*
49  * Get a stack trace as an array of StackTraceElement objects.  Returns
50  * nullptr on failure, e.g. if the threadId couldn't be found.
51  */
DdmVmInternal_getStackTraceById(JNIEnv * env,jclass,jint thin_lock_id)52 static jobjectArray DdmVmInternal_getStackTraceById(JNIEnv* env, jclass, jint thin_lock_id) {
53   jobjectArray trace = nullptr;
54   Thread* const self = GetSelf(env);
55   if (static_cast<uint32_t>(thin_lock_id) == self->GetThreadId()) {
56     // No need to suspend ourself to build stacktrace.
57     ScopedObjectAccess soa(env);
58     jobject internal_trace = self->CreateInternalStackTrace(soa);
59     trace = Thread::InternalStackTraceToStackTraceElementArray(soa, internal_trace);
60   } else {
61     ThreadList* thread_list = Runtime::Current()->GetThreadList();
62     bool timed_out;
63 
64     // Check for valid thread
65     if (thin_lock_id == ThreadList::kInvalidThreadId) {
66       return nullptr;
67     }
68 
69     // Suspend thread to build stack trace.
70     Thread* thread = thread_list->SuspendThreadByThreadId(thin_lock_id,
71                                                           SuspendReason::kInternal,
72                                                           &timed_out);
73     if (thread != nullptr) {
74       {
75         ScopedObjectAccess soa(env);
76         jobject internal_trace = thread->CreateInternalStackTrace(soa);
77         trace = Thread::InternalStackTraceToStackTraceElementArray(soa, internal_trace);
78       }
79       // Restart suspended thread.
80       bool resumed = thread_list->Resume(thread, SuspendReason::kInternal);
81       DCHECK(resumed);
82     } else {
83       if (timed_out) {
84         LOG(ERROR) << "Trying to get thread's stack by id failed as the thread failed to suspend "
85             "within a generous timeout.";
86       }
87     }
88   }
89   return trace;
90 }
91 
ThreadCountCallback(Thread *,void * context)92 static void ThreadCountCallback(Thread*, void* context) {
93   uint16_t& count = *reinterpret_cast<uint16_t*>(context);
94   ++count;
95 }
96 
97 static const int kThstBytesPerEntry = 18;
98 static const int kThstHeaderLen = 4;
99 
ToJdwpThreadStatus(ThreadState state)100 static constexpr uint8_t ToJdwpThreadStatus(ThreadState state) {
101   /*
102   * ThreadStatus constants.
103   */
104   enum JdwpThreadStatus : uint8_t {
105     TS_ZOMBIE   = 0,
106     TS_RUNNING  = 1,  // RUNNING
107     TS_SLEEPING = 2,  // (in Thread.sleep())
108     TS_MONITOR  = 3,  // WAITING (monitor wait)
109     TS_WAIT     = 4,  // (in Object.wait())
110   };
111   switch (state) {
112     case kBlocked:
113       return TS_MONITOR;
114     case kNative:
115     case kRunnable:
116     case kSuspended:
117       return TS_RUNNING;
118     case kSleeping:
119       return TS_SLEEPING;
120     case kStarting:
121     case kTerminated:
122       return TS_ZOMBIE;
123     case kTimedWaiting:
124     case kWaitingForTaskProcessor:
125     case kWaitingForLockInflation:
126     case kWaitingForCheckPointsToRun:
127     case kWaitingForDebuggerSend:
128     case kWaitingForDebuggerSuspension:
129     case kWaitingForDebuggerToAttach:
130     case kWaitingForDeoptimization:
131     case kWaitingForGcToComplete:
132     case kWaitingForGetObjectsAllocated:
133     case kWaitingForJniOnLoad:
134     case kWaitingForMethodTracingStart:
135     case kWaitingForSignalCatcherOutput:
136     case kWaitingForVisitObjects:
137     case kWaitingInMainDebuggerLoop:
138     case kWaitingInMainSignalCatcherLoop:
139     case kWaitingPerformingGc:
140     case kWaitingWeakGcRootRead:
141     case kWaitingForGcThreadFlip:
142     case kNativeForAbort:
143     case kWaiting:
144       return TS_WAIT;
145       // Don't add a 'default' here so the compiler can spot incompatible enum changes.
146   }
147   LOG(FATAL) << "Unknown thread state: " << state;
148   UNREACHABLE();
149 }
150 
ThreadStatsGetterCallback(Thread * t,void * context)151 static void ThreadStatsGetterCallback(Thread* t, void* context) {
152   /*
153    * Generate the contents of a THST chunk.  The data encompasses all known
154    * threads.
155    *
156    * Response has:
157    *  (1b) header len
158    *  (1b) bytes per entry
159    *  (2b) thread count
160    * Then, for each thread:
161    *  (4b) thread id
162    *  (1b) thread status
163    *  (4b) tid
164    *  (4b) utime
165    *  (4b) stime
166    *  (1b) is daemon?
167    *
168    * The length fields exist in anticipation of adding additional fields
169    * without wanting to break ddms or bump the full protocol version.  I don't
170    * think it warrants full versioning.  They might be extraneous and could
171    * be removed from a future version.
172    */
173   char native_thread_state;
174   int utime;
175   int stime;
176   int task_cpu;
177   GetTaskStats(t->GetTid(), &native_thread_state, &utime, &stime, &task_cpu);
178 
179   std::vector<uint8_t>& bytes = *reinterpret_cast<std::vector<uint8_t>*>(context);
180   Append4BE(bytes, t->GetThreadId());
181   Append1BE(bytes, ToJdwpThreadStatus(t->GetState()));
182   Append4BE(bytes, t->GetTid());
183   Append4BE(bytes, utime);
184   Append4BE(bytes, stime);
185   Append1BE(bytes, t->IsDaemon());
186 }
187 
DdmVmInternal_getThreadStats(JNIEnv * env,jclass)188 static jbyteArray DdmVmInternal_getThreadStats(JNIEnv* env, jclass) {
189   std::vector<uint8_t> bytes;
190   Thread* self = GetSelf(env);
191   {
192     MutexLock mu(self, *Locks::thread_list_lock_);
193     ThreadList* thread_list = Runtime::Current()->GetThreadList();
194 
195     uint16_t thread_count = 0;
196     thread_list->ForEach(ThreadCountCallback, &thread_count);
197 
198     Append1BE(bytes, kThstHeaderLen);
199     Append1BE(bytes, kThstBytesPerEntry);
200     Append2BE(bytes, thread_count);
201 
202     thread_list->ForEach(ThreadStatsGetterCallback, &bytes);
203   }
204 
205   jbyteArray result = env->NewByteArray(bytes.size());
206   if (result != nullptr) {
207     env->SetByteArrayRegion(result, 0, bytes.size(), reinterpret_cast<const jbyte*>(&bytes[0]));
208   }
209   return result;
210 }
211 
212 static JNINativeMethod gMethods[] = {
213   NATIVE_METHOD(DdmVmInternal, setRecentAllocationsTrackingEnabled, "(Z)V"),
214   NATIVE_METHOD(DdmVmInternal, setThreadNotifyEnabled, "(Z)V"),
215   NATIVE_METHOD(DdmVmInternal, getStackTraceById, "(I)[Ljava/lang/StackTraceElement;"),
216   NATIVE_METHOD(DdmVmInternal, getThreadStats, "()[B"),
217 };
218 
register_org_apache_harmony_dalvik_ddmc_DdmVmInternal(JNIEnv * env)219 void register_org_apache_harmony_dalvik_ddmc_DdmVmInternal(JNIEnv* env) {
220   REGISTER_NATIVE_METHODS("org/apache/harmony/dalvik/ddmc/DdmVmInternal");
221 }
222 
223 }  // namespace art
224