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