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