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