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 
GetSelf(JNIEnv * env)36 static Thread* GetSelf(JNIEnv* env) {
37   return static_cast<JNIEnvExt*>(env)->GetSelf();
38 }
39 
DdmVmInternal_enableRecentAllocations(JNIEnv *,jclass,jboolean enable)40 static void DdmVmInternal_enableRecentAllocations(JNIEnv*, jclass, jboolean enable) {
41   Dbg::SetAllocTrackingEnabled(enable);
42 }
43 
DdmVmInternal_getRecentAllocations(JNIEnv * env,jclass)44 static jbyteArray DdmVmInternal_getRecentAllocations(JNIEnv* env, jclass) {
45   ScopedFastNativeObjectAccess soa(env);
46   return Dbg::GetRecentAllocations();
47 }
48 
DdmVmInternal_getRecentAllocationStatus(JNIEnv *,jclass)49 static jboolean DdmVmInternal_getRecentAllocationStatus(JNIEnv*, jclass) {
50   return Runtime::Current()->GetHeap()->IsAllocTrackingEnabled();
51 }
52 
53 /*
54  * Get a stack trace as an array of StackTraceElement objects.  Returns
55  * nullptr on failure, e.g. if the threadId couldn't be found.
56  */
DdmVmInternal_getStackTraceById(JNIEnv * env,jclass,jint thin_lock_id)57 static jobjectArray DdmVmInternal_getStackTraceById(JNIEnv* env, jclass, jint thin_lock_id) {
58   jobjectArray trace = nullptr;
59   Thread* const self = GetSelf(env);
60   if (static_cast<uint32_t>(thin_lock_id) == self->GetThreadId()) {
61     // No need to suspend ourself to build stacktrace.
62     ScopedObjectAccess soa(env);
63     jobject internal_trace = self->CreateInternalStackTrace<false>(soa);
64     trace = Thread::InternalStackTraceToStackTraceElementArray(soa, internal_trace);
65   } else {
66     ThreadList* thread_list = Runtime::Current()->GetThreadList();
67     bool timed_out;
68 
69     // Check for valid thread
70     if (thin_lock_id == ThreadList::kInvalidThreadId) {
71       return nullptr;
72     }
73 
74     // Suspend thread to build stack trace.
75     Thread* thread = thread_list->SuspendThreadByThreadId(thin_lock_id,
76                                                           SuspendReason::kInternal,
77                                                           &timed_out);
78     if (thread != nullptr) {
79       {
80         ScopedObjectAccess soa(env);
81         jobject internal_trace = thread->CreateInternalStackTrace<false>(soa);
82         trace = Thread::InternalStackTraceToStackTraceElementArray(soa, internal_trace);
83       }
84       // Restart suspended thread.
85       bool resumed = thread_list->Resume(thread, SuspendReason::kInternal);
86       DCHECK(resumed);
87     } else {
88       if (timed_out) {
89         LOG(ERROR) << "Trying to get thread's stack by id failed as the thread failed to suspend "
90             "within a generous timeout.";
91       }
92     }
93   }
94   return trace;
95 }
96 
ThreadCountCallback(Thread *,void * context)97 static void ThreadCountCallback(Thread*, void* context) {
98   uint16_t& count = *reinterpret_cast<uint16_t*>(context);
99   ++count;
100 }
101 
102 static const int kThstBytesPerEntry = 18;
103 static const int kThstHeaderLen = 4;
104 
ToJdwpThreadStatus(ThreadState state)105 static constexpr uint8_t ToJdwpThreadStatus(ThreadState state) {
106   /*
107   * ThreadStatus constants.
108   */
109   enum JdwpThreadStatus : uint8_t {
110     TS_ZOMBIE   = 0,
111     TS_RUNNING  = 1,  // RUNNING
112     TS_SLEEPING = 2,  // (in Thread.sleep())
113     TS_MONITOR  = 3,  // WAITING (monitor wait)
114     TS_WAIT     = 4,  // (in Object.wait())
115   };
116   switch (state) {
117     case kBlocked:
118       return TS_MONITOR;
119     case kNative:
120     case kRunnable:
121     case kSuspended:
122       return TS_RUNNING;
123     case kSleeping:
124       return TS_SLEEPING;
125     case kStarting:
126     case kTerminated:
127       return TS_ZOMBIE;
128     case kTimedWaiting:
129     case kWaitingForTaskProcessor:
130     case kWaitingForLockInflation:
131     case kWaitingForCheckPointsToRun:
132     case kWaitingForDebuggerSend:
133     case kWaitingForDebuggerSuspension:
134     case kWaitingForDebuggerToAttach:
135     case kWaitingForDeoptimization:
136     case kWaitingForGcToComplete:
137     case kWaitingForGetObjectsAllocated:
138     case kWaitingForJniOnLoad:
139     case kWaitingForMethodTracingStart:
140     case kWaitingForSignalCatcherOutput:
141     case kWaitingForVisitObjects:
142     case kWaitingInMainDebuggerLoop:
143     case kWaitingInMainSignalCatcherLoop:
144     case kWaitingPerformingGc:
145     case kWaitingWeakGcRootRead:
146     case kWaitingForGcThreadFlip:
147     case kNativeForAbort:
148     case kWaiting:
149       return TS_WAIT;
150       // Don't add a 'default' here so the compiler can spot incompatible enum changes.
151   }
152   LOG(FATAL) << "Unknown thread state: " << state;
153   UNREACHABLE();
154 }
155 
ThreadStatsGetterCallback(Thread * t,void * context)156 static void ThreadStatsGetterCallback(Thread* t, void* context) {
157   /*
158    * Generate the contents of a THST chunk.  The data encompasses all known
159    * threads.
160    *
161    * Response has:
162    *  (1b) header len
163    *  (1b) bytes per entry
164    *  (2b) thread count
165    * Then, for each thread:
166    *  (4b) thread id
167    *  (1b) thread status
168    *  (4b) tid
169    *  (4b) utime
170    *  (4b) stime
171    *  (1b) is daemon?
172    *
173    * The length fields exist in anticipation of adding additional fields
174    * without wanting to break ddms or bump the full protocol version.  I don't
175    * think it warrants full versioning.  They might be extraneous and could
176    * be removed from a future version.
177    */
178   char native_thread_state;
179   int utime;
180   int stime;
181   int task_cpu;
182   GetTaskStats(t->GetTid(), &native_thread_state, &utime, &stime, &task_cpu);
183 
184   std::vector<uint8_t>& bytes = *reinterpret_cast<std::vector<uint8_t>*>(context);
185   Append4BE(bytes, t->GetThreadId());
186   Append1BE(bytes, ToJdwpThreadStatus(t->GetState()));
187   Append4BE(bytes, t->GetTid());
188   Append4BE(bytes, utime);
189   Append4BE(bytes, stime);
190   Append1BE(bytes, t->IsDaemon());
191 }
192 
DdmVmInternal_getThreadStats(JNIEnv * env,jclass)193 static jbyteArray DdmVmInternal_getThreadStats(JNIEnv* env, jclass) {
194   std::vector<uint8_t> bytes;
195   Thread* self = GetSelf(env);
196   {
197     MutexLock mu(self, *Locks::thread_list_lock_);
198     ThreadList* thread_list = Runtime::Current()->GetThreadList();
199 
200     uint16_t thread_count = 0;
201     thread_list->ForEach(ThreadCountCallback, &thread_count);
202 
203     Append1BE(bytes, kThstHeaderLen);
204     Append1BE(bytes, kThstBytesPerEntry);
205     Append2BE(bytes, thread_count);
206 
207     thread_list->ForEach(ThreadStatsGetterCallback, &bytes);
208   }
209 
210   jbyteArray result = env->NewByteArray(bytes.size());
211   if (result != nullptr) {
212     env->SetByteArrayRegion(result, 0, bytes.size(), reinterpret_cast<const jbyte*>(&bytes[0]));
213   }
214   return result;
215 }
216 
DdmVmInternal_heapInfoNotify(JNIEnv * env,jclass,jint when)217 static jboolean DdmVmInternal_heapInfoNotify(JNIEnv* env, jclass, jint when) {
218   ScopedFastNativeObjectAccess soa(env);
219   return Dbg::DdmHandleHpifChunk(static_cast<Dbg::HpifWhen>(when));
220 }
221 
DdmVmInternal_heapSegmentNotify(JNIEnv *,jclass,jint when,jint what,jboolean native)222 static jboolean DdmVmInternal_heapSegmentNotify(JNIEnv*, jclass, jint when, jint what, jboolean native) {
223   return Dbg::DdmHandleHpsgNhsgChunk(static_cast<Dbg::HpsgWhen>(when), static_cast<Dbg::HpsgWhat>(what), native);
224 }
225 
DdmVmInternal_threadNotify(JNIEnv *,jclass,jboolean enable)226 static void DdmVmInternal_threadNotify(JNIEnv*, jclass, jboolean enable) {
227   Dbg::DdmSetThreadNotification(enable);
228 }
229 
230 static JNINativeMethod gMethods[] = {
231   NATIVE_METHOD(DdmVmInternal, enableRecentAllocations, "(Z)V"),
232   FAST_NATIVE_METHOD(DdmVmInternal, getRecentAllocations, "()[B"),
233   FAST_NATIVE_METHOD(DdmVmInternal, getRecentAllocationStatus, "()Z"),
234   NATIVE_METHOD(DdmVmInternal, getStackTraceById, "(I)[Ljava/lang/StackTraceElement;"),
235   NATIVE_METHOD(DdmVmInternal, getThreadStats, "()[B"),
236   FAST_NATIVE_METHOD(DdmVmInternal, heapInfoNotify, "(I)Z"),
237   NATIVE_METHOD(DdmVmInternal, heapSegmentNotify, "(IIZ)Z"),
238   NATIVE_METHOD(DdmVmInternal, threadNotify, "(Z)V"),
239 };
240 
register_org_apache_harmony_dalvik_ddmc_DdmVmInternal(JNIEnv * env)241 void register_org_apache_harmony_dalvik_ddmc_DdmVmInternal(JNIEnv* env) {
242   REGISTER_NATIVE_METHODS("org/apache/harmony/dalvik/ddmc/DdmVmInternal");
243 }
244 
245 }  // namespace art
246