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 "base/logging.h" 18 #include "base/mutex.h" 19 #include "debugger.h" 20 #include "jni_internal.h" 21 #include "scoped_thread_state_change.h" 22 #include "ScopedLocalRef.h" 23 #include "ScopedPrimitiveArray.h" 24 #include "stack.h" 25 #include "thread_list.h" 26 27 namespace art { 28 29 static void DdmVmInternal_enableRecentAllocations(JNIEnv*, jclass, jboolean enable) { 30 Dbg::SetAllocTrackingEnabled(enable); 31 } 32 33 static jbyteArray DdmVmInternal_getRecentAllocations(JNIEnv* env, jclass) { 34 ScopedObjectAccess soa(env); 35 return Dbg::GetRecentAllocations(); 36 } 37 38 static jboolean DdmVmInternal_getRecentAllocationStatus(JNIEnv*, jclass) { 39 return Dbg::IsAllocTrackingEnabled(); 40 } 41 42 /* 43 * Get a stack trace as an array of StackTraceElement objects. Returns 44 * NULL on failure, e.g. if the threadId couldn't be found. 45 */ 46 static jobjectArray DdmVmInternal_getStackTraceById(JNIEnv* env, jclass, jint thin_lock_id) { 47 ScopedLocalRef<jobject> peer(env, NULL); 48 { 49 Thread* t = Runtime::Current()->GetThreadList()->FindThreadByThinLockId(thin_lock_id); 50 if (t == NULL) { 51 return NULL; 52 } 53 ScopedObjectAccess soa(env); 54 peer.reset(soa.AddLocalReference<jobject>(t->GetPeer())); 55 } 56 if (peer.get() == NULL) { 57 return NULL; 58 } 59 60 // Suspend thread to build stack trace. 61 bool timed_out; 62 Thread* thread = Thread::SuspendForDebugger(peer.get(), true, &timed_out); 63 if (thread != NULL) { 64 jobject trace; 65 { 66 ScopedObjectAccess soa(env); 67 trace = thread->CreateInternalStackTrace(soa); 68 } 69 // Restart suspended thread. 70 Runtime::Current()->GetThreadList()->Resume(thread, true); 71 return Thread::InternalStackTraceToStackTraceElementArray(env, trace); 72 } else { 73 if (timed_out) { 74 LOG(ERROR) << "Trying to get thread's stack by id failed as the thread failed to suspend " 75 "within a generous timeout."; 76 } 77 return NULL; 78 } 79 } 80 81 static void ThreadCountCallback(Thread*, void* context) { 82 uint16_t& count = *reinterpret_cast<uint16_t*>(context); 83 ++count; 84 } 85 86 static const int kThstBytesPerEntry = 18; 87 static const int kThstHeaderLen = 4; 88 89 static void ThreadStatsGetterCallback(Thread* t, void* context) { 90 /* 91 * Generate the contents of a THST chunk. The data encompasses all known 92 * threads. 93 * 94 * Response has: 95 * (1b) header len 96 * (1b) bytes per entry 97 * (2b) thread count 98 * Then, for each thread: 99 * (4b) thread id 100 * (1b) thread status 101 * (4b) tid 102 * (4b) utime 103 * (4b) stime 104 * (1b) is daemon? 105 * 106 * The length fields exist in anticipation of adding additional fields 107 * without wanting to break ddms or bump the full protocol version. I don't 108 * think it warrants full versioning. They might be extraneous and could 109 * be removed from a future version. 110 */ 111 char native_thread_state; 112 int utime, stime, task_cpu; 113 GetTaskStats(t->GetTid(), native_thread_state, utime, stime, task_cpu); 114 115 std::vector<uint8_t>& bytes = *reinterpret_cast<std::vector<uint8_t>*>(context); 116 JDWP::Append4BE(bytes, t->GetThinLockId()); 117 JDWP::Append1BE(bytes, Dbg::ToJdwpThreadStatus(t->GetState())); 118 JDWP::Append4BE(bytes, t->GetTid()); 119 JDWP::Append4BE(bytes, utime); 120 JDWP::Append4BE(bytes, stime); 121 JDWP::Append1BE(bytes, t->IsDaemon()); 122 } 123 124 static jbyteArray DdmVmInternal_getThreadStats(JNIEnv* env, jclass) { 125 std::vector<uint8_t> bytes; 126 Thread* self = static_cast<JNIEnvExt*>(env)->self; 127 { 128 MutexLock mu(self, *Locks::thread_list_lock_); 129 ThreadList* thread_list = Runtime::Current()->GetThreadList(); 130 131 uint16_t thread_count = 0; 132 thread_list->ForEach(ThreadCountCallback, &thread_count); 133 134 JDWP::Append1BE(bytes, kThstHeaderLen); 135 JDWP::Append1BE(bytes, kThstBytesPerEntry); 136 JDWP::Append2BE(bytes, thread_count); 137 138 thread_list->ForEach(ThreadStatsGetterCallback, &bytes); 139 } 140 141 jbyteArray result = env->NewByteArray(bytes.size()); 142 if (result != NULL) { 143 env->SetByteArrayRegion(result, 0, bytes.size(), reinterpret_cast<const jbyte*>(&bytes[0])); 144 } 145 return result; 146 } 147 148 static jint DdmVmInternal_heapInfoNotify(JNIEnv* env, jclass, jint when) { 149 ScopedObjectAccess soa(env); 150 return Dbg::DdmHandleHpifChunk(static_cast<Dbg::HpifWhen>(when)); 151 } 152 153 static jboolean DdmVmInternal_heapSegmentNotify(JNIEnv*, jclass, jint when, jint what, jboolean native) { 154 return Dbg::DdmHandleHpsgNhsgChunk(static_cast<Dbg::HpsgWhen>(when), static_cast<Dbg::HpsgWhat>(what), native); 155 } 156 157 static void DdmVmInternal_threadNotify(JNIEnv*, jclass, jboolean enable) { 158 Dbg::DdmSetThreadNotification(enable); 159 } 160 161 static JNINativeMethod gMethods[] = { 162 NATIVE_METHOD(DdmVmInternal, enableRecentAllocations, "(Z)V"), 163 NATIVE_METHOD(DdmVmInternal, getRecentAllocations, "()[B"), 164 NATIVE_METHOD(DdmVmInternal, getRecentAllocationStatus, "()Z"), 165 NATIVE_METHOD(DdmVmInternal, getStackTraceById, "(I)[Ljava/lang/StackTraceElement;"), 166 NATIVE_METHOD(DdmVmInternal, getThreadStats, "()[B"), 167 NATIVE_METHOD(DdmVmInternal, heapInfoNotify, "(I)Z"), 168 NATIVE_METHOD(DdmVmInternal, heapSegmentNotify, "(IIZ)Z"), 169 NATIVE_METHOD(DdmVmInternal, threadNotify, "(Z)V"), 170 }; 171 172 void register_org_apache_harmony_dalvik_ddmc_DdmVmInternal(JNIEnv* env) { 173 REGISTER_NATIVE_METHODS("org/apache/harmony/dalvik/ddmc/DdmVmInternal"); 174 } 175 176 } // namespace art 177