blob: 081ec2043aace3ac54ad2d4ee64623bae8c37581 [file] [log] [blame]
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "org_apache_harmony_dalvik_ddmc_DdmVmInternal.h"
#include <android-base/logging.h>
#include "base/file_utils.h"
#include "base/mutex.h"
#include "base/endian_utils.h"
#include "debugger.h"
#include "gc/heap.h"
#include "jni/jni_internal.h"
#include "native_util.h"
#include "nativehelper/jni_macros.h"
#include "nativehelper/scoped_local_ref.h"
#include "nativehelper/scoped_primitive_array.h"
#include "scoped_fast_native_object_access-inl.h"
#include "thread_list.h"
namespace art {
static void DdmVmInternal_setRecentAllocationsTrackingEnabled(JNIEnv*, jclass, jboolean enable) {
Dbg::SetAllocTrackingEnabled(enable);
}
static void DdmVmInternal_setThreadNotifyEnabled(JNIEnv*, jclass, jboolean enable) {
Dbg::DdmSetThreadNotification(enable);
}
static Thread* GetSelf(JNIEnv* env) {
return static_cast<JNIEnvExt*>(env)->GetSelf();
}
/*
* Get a stack trace as an array of StackTraceElement objects. Returns
* nullptr on failure, e.g. if the threadId couldn't be found.
*/
static jobjectArray DdmVmInternal_getStackTraceById(JNIEnv* env, jclass, jint thin_lock_id) {
jobjectArray trace = nullptr;
Thread* const self = GetSelf(env);
if (static_cast<uint32_t>(thin_lock_id) == self->GetThreadId()) {
// No need to suspend ourself to build stacktrace.
ScopedObjectAccess soa(env);
jobject internal_trace = self->CreateInternalStackTrace(soa);
trace = Thread::InternalStackTraceToStackTraceElementArray(soa, internal_trace);
} else {
ThreadList* thread_list = Runtime::Current()->GetThreadList();
bool timed_out;
// Check for valid thread
if (thin_lock_id == ThreadList::kInvalidThreadId) {
return nullptr;
}
// Suspend thread to build stack trace.
Thread* thread = thread_list->SuspendThreadByThreadId(thin_lock_id,
SuspendReason::kInternal,
&timed_out);
if (thread != nullptr) {
{
ScopedObjectAccess soa(env);
jobject internal_trace = thread->CreateInternalStackTrace(soa);
trace = Thread::InternalStackTraceToStackTraceElementArray(soa, internal_trace);
}
// Restart suspended thread.
bool resumed = thread_list->Resume(thread, SuspendReason::kInternal);
DCHECK(resumed);
} else {
if (timed_out) {
LOG(ERROR) << "Trying to get thread's stack by id failed as the thread failed to suspend "
"within a generous timeout.";
}
}
}
return trace;
}
static void ThreadCountCallback(Thread*, void* context) {
uint16_t& count = *reinterpret_cast<uint16_t*>(context);
++count;
}
static const int kThstBytesPerEntry = 18;
static const int kThstHeaderLen = 4;
static constexpr uint8_t ToJdwpThreadStatus(ThreadState state) {
/*
* ThreadStatus constants.
*/
enum JdwpThreadStatus : uint8_t {
TS_ZOMBIE = 0,
TS_RUNNING = 1, // RUNNING
TS_SLEEPING = 2, // (in Thread.sleep())
TS_MONITOR = 3, // WAITING (monitor wait)
TS_WAIT = 4, // (in Object.wait())
};
switch (state) {
case ThreadState::kBlocked:
return TS_MONITOR;
case ThreadState::kNative:
case ThreadState::kRunnable:
case ThreadState::kSuspended:
return TS_RUNNING;
case ThreadState::kObsoleteRunnable:
break; // Obsolete value.
case ThreadState::kSleeping:
return TS_SLEEPING;
case ThreadState::kStarting:
case ThreadState::kTerminated:
return TS_ZOMBIE;
case ThreadState::kTimedWaiting:
case ThreadState::kWaitingForTaskProcessor:
case ThreadState::kWaitingForLockInflation:
case ThreadState::kWaitingForCheckPointsToRun:
case ThreadState::kWaitingForDebuggerSend:
case ThreadState::kWaitingForDebuggerSuspension:
case ThreadState::kWaitingForDebuggerToAttach:
case ThreadState::kWaitingForDeoptimization:
case ThreadState::kWaitingForGcToComplete:
case ThreadState::kWaitingForGetObjectsAllocated:
case ThreadState::kWaitingForJniOnLoad:
case ThreadState::kWaitingForMethodTracingStart:
case ThreadState::kWaitingForSignalCatcherOutput:
case ThreadState::kWaitingForVisitObjects:
case ThreadState::kWaitingInMainDebuggerLoop:
case ThreadState::kWaitingInMainSignalCatcherLoop:
case ThreadState::kWaitingPerformingGc:
case ThreadState::kWaitingWeakGcRootRead:
case ThreadState::kWaitingForGcThreadFlip:
case ThreadState::kNativeForAbort:
case ThreadState::kWaiting:
return TS_WAIT;
// Don't add a 'default' here so the compiler can spot incompatible enum changes.
}
LOG(FATAL) << "Unknown thread state: " << state;
UNREACHABLE();
}
static void ThreadStatsGetterCallback(Thread* t, void* context) {
/*
* Generate the contents of a THST chunk. The data encompasses all known
* threads.
*
* Response has:
* (1b) header len
* (1b) bytes per entry
* (2b) thread count
* Then, for each thread:
* (4b) thread id
* (1b) thread status
* (4b) tid
* (4b) utime
* (4b) stime
* (1b) is daemon?
*
* The length fields exist in anticipation of adding additional fields
* without wanting to break ddms or bump the full protocol version. I don't
* think it warrants full versioning. They might be extraneous and could
* be removed from a future version.
*/
char native_thread_state;
int utime;
int stime;
int task_cpu;
GetTaskStats(t->GetTid(), &native_thread_state, &utime, &stime, &task_cpu);
std::vector<uint8_t>& bytes = *reinterpret_cast<std::vector<uint8_t>*>(context);
Append4BE(bytes, t->GetThreadId());
Append1BE(bytes, ToJdwpThreadStatus(t->GetState()));
Append4BE(bytes, t->GetTid());
Append4BE(bytes, utime);
Append4BE(bytes, stime);
Append1BE(bytes, t->IsDaemon());
}
static jbyteArray DdmVmInternal_getThreadStats(JNIEnv* env, jclass) {
std::vector<uint8_t> bytes;
Thread* self = GetSelf(env);
{
MutexLock mu(self, *Locks::thread_list_lock_);
ThreadList* thread_list = Runtime::Current()->GetThreadList();
uint16_t thread_count = 0;
thread_list->ForEach(ThreadCountCallback, &thread_count);
Append1BE(bytes, kThstHeaderLen);
Append1BE(bytes, kThstBytesPerEntry);
Append2BE(bytes, thread_count);
thread_list->ForEach(ThreadStatsGetterCallback, &bytes);
}
jbyteArray result = env->NewByteArray(bytes.size());
if (result != nullptr) {
env->SetByteArrayRegion(result, 0, bytes.size(), reinterpret_cast<const jbyte*>(&bytes[0]));
}
return result;
}
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(DdmVmInternal, setRecentAllocationsTrackingEnabled, "(Z)V"),
NATIVE_METHOD(DdmVmInternal, setThreadNotifyEnabled, "(Z)V"),
NATIVE_METHOD(DdmVmInternal, getStackTraceById, "(I)[Ljava/lang/StackTraceElement;"),
NATIVE_METHOD(DdmVmInternal, getThreadStats, "()[B"),
};
void register_org_apache_harmony_dalvik_ddmc_DdmVmInternal(JNIEnv* env) {
REGISTER_NATIVE_METHODS("org/apache/harmony/dalvik/ddmc/DdmVmInternal");
}
} // namespace art