Merge "Refactor allocation entrypoints." into dalvik-dev
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index c5c1dfb..7e02978 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -95,20 +95,6 @@
return os;
}
-struct SingleStepControl {
- // Are we single-stepping right now?
- bool is_active;
- Thread* thread;
-
- JDWP::JdwpStepSize step_size;
- JDWP::JdwpStepDepth step_depth;
-
- const mirror::ArtMethod* method;
- int32_t line_number; // Or -1 for native methods.
- std::set<uint32_t> dex_pcs;
- int stack_depth;
-};
-
class DebugInstrumentationListener : public instrumentation::InstrumentationListener {
public:
DebugInstrumentationListener() {}
@@ -121,19 +107,18 @@
// TODO: post location events is a suspension point and native method entry stubs aren't.
return;
}
- Dbg::PostLocationEvent(method, 0, this_object, Dbg::kMethodEntry);
+ Dbg::PostLocationEvent(method, 0, this_object, Dbg::kMethodEntry, nullptr);
}
virtual void MethodExited(Thread* thread, mirror::Object* this_object,
const mirror::ArtMethod* method,
uint32_t dex_pc, const JValue& return_value)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- UNUSED(return_value);
if (method->IsNative()) {
// TODO: post location events is a suspension point and native method entry stubs aren't.
return;
}
- Dbg::PostLocationEvent(method, dex_pc, this_object, Dbg::kMethodExit);
+ Dbg::PostLocationEvent(method, dex_pc, this_object, Dbg::kMethodExit, &return_value);
}
virtual void MethodUnwind(Thread* thread, mirror::Object* this_object,
@@ -193,7 +178,6 @@
// Breakpoints and single-stepping.
static std::vector<Breakpoint> gBreakpoints GUARDED_BY(Locks::breakpoint_lock_);
-static SingleStepControl gSingleStepControl GUARDED_BY(Locks::breakpoint_lock_);
static bool IsBreakpoint(const mirror::ArtMethod* m, uint32_t dex_pc)
LOCKS_EXCLUDED(Locks::breakpoint_lock_)
@@ -1393,6 +1377,13 @@
JDWP::Set4BE(expandBufGetBuffer(pReply) + variable_count_offset, context.variable_count);
}
+void Dbg::OutputMethodReturnValue(JDWP::MethodId method_id, const JValue* return_value,
+ JDWP::ExpandBuf* pReply) {
+ mirror::ArtMethod* m = FromMethodId(method_id);
+ JDWP::JdwpTag tag = BasicTagFromDescriptor(MethodHelper(m).GetShorty());
+ OutputJValue(tag, return_value, pReply);
+}
+
JDWP::JdwpError Dbg::GetBytecodes(JDWP::RefTypeId, JDWP::MethodId method_id,
std::vector<uint8_t>& bytecodes)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -1461,25 +1452,18 @@
}
JDWP::JdwpTag tag = BasicTagFromDescriptor(FieldHelper(f).GetTypeDescriptor());
-
- if (IsPrimitiveTag(tag)) {
- expandBufAdd1(pReply, tag);
- if (tag == JDWP::JT_BOOLEAN || tag == JDWP::JT_BYTE) {
- expandBufAdd1(pReply, f->Get32(o));
- } else if (tag == JDWP::JT_CHAR || tag == JDWP::JT_SHORT) {
- expandBufAdd2BE(pReply, f->Get32(o));
- } else if (tag == JDWP::JT_FLOAT || tag == JDWP::JT_INT) {
- expandBufAdd4BE(pReply, f->Get32(o));
- } else if (tag == JDWP::JT_DOUBLE || tag == JDWP::JT_LONG) {
- expandBufAdd8BE(pReply, f->Get64(o));
- } else {
- LOG(FATAL) << "Unknown tag: " << tag;
- }
+ JValue field_value;
+ if (tag == JDWP::JT_VOID) {
+ LOG(FATAL) << "Unknown tag: " << tag;
+ } else if (!IsPrimitiveTag(tag)) {
+ field_value.SetL(f->GetObject(o));
+ } else if (tag == JDWP::JT_DOUBLE || tag == JDWP::JT_LONG) {
+ field_value.SetJ(f->Get64(o));
} else {
- mirror::Object* value = f->GetObject(o);
- expandBufAdd1(pReply, TagFromObject(value));
- expandBufAddObjectId(pReply, gRegistry->Add(value));
+ field_value.SetI(f->Get32(o));
}
+ Dbg::OutputJValue(tag, &field_value, pReply);
+
return JDWP::ERR_NONE;
}
@@ -1557,6 +1541,27 @@
return s->ToModifiedUtf8();
}
+void Dbg::OutputJValue(JDWP::JdwpTag tag, const JValue* return_value, JDWP::ExpandBuf* pReply) {
+ if (IsPrimitiveTag(tag)) {
+ expandBufAdd1(pReply, tag);
+ if (tag == JDWP::JT_BOOLEAN || tag == JDWP::JT_BYTE) {
+ expandBufAdd1(pReply, return_value->GetI());
+ } else if (tag == JDWP::JT_CHAR || tag == JDWP::JT_SHORT) {
+ expandBufAdd2BE(pReply, return_value->GetI());
+ } else if (tag == JDWP::JT_FLOAT || tag == JDWP::JT_INT) {
+ expandBufAdd4BE(pReply, return_value->GetI());
+ } else if (tag == JDWP::JT_DOUBLE || tag == JDWP::JT_LONG) {
+ expandBufAdd8BE(pReply, return_value->GetJ());
+ } else {
+ CHECK_EQ(tag, JDWP::JT_VOID);
+ }
+ } else {
+ mirror::Object* value = return_value->GetL();
+ expandBufAdd1(pReply, TagFromObject(value));
+ expandBufAddObjectId(pReply, gRegistry->Add(value));
+ }
+}
+
JDWP::JdwpError Dbg::GetThreadName(JDWP::ObjectId thread_id, std::string& name) {
ScopedObjectAccessUnchecked soa(Thread::Current());
MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
@@ -2226,8 +2231,8 @@
visitor.WalkStack();
}
-void Dbg::PostLocationEvent(const mirror::ArtMethod* m, int dex_pc,
- mirror::Object* this_object, int event_flags) {
+void Dbg::PostLocationEvent(const mirror::ArtMethod* m, int dex_pc, mirror::Object* this_object,
+ int event_flags, const JValue* return_value) {
mirror::Class* c = m->GetDeclaringClass();
JDWP::JdwpLocation location;
@@ -2242,7 +2247,7 @@
if (gRegistry->Contains(this_object)) {
this_id = gRegistry->Add(this_object);
}
- gJdwpState->PostLocationEvent(&location, this_id, event_flags);
+ gJdwpState->PostLocationEvent(&location, this_id, event_flags, return_value);
}
void Dbg::PostException(Thread* thread, const ThrowLocation& throw_location,
@@ -2292,63 +2297,62 @@
event_flags |= kBreakpoint;
}
- {
- // If the debugger is single-stepping one of our threads, check to
- // see if we're that thread and we've reached a step point.
- MutexLock mu(Thread::Current(), *Locks::breakpoint_lock_);
- if (gSingleStepControl.is_active && gSingleStepControl.thread == thread) {
- CHECK(!m->IsNative());
- if (gSingleStepControl.step_depth == JDWP::SD_INTO) {
- // Step into method calls. We break when the line number
- // or method pointer changes. If we're in SS_MIN mode, we
- // always stop.
- if (gSingleStepControl.method != m) {
- event_flags |= kSingleStep;
- VLOG(jdwp) << "SS new method";
- } else if (gSingleStepControl.step_size == JDWP::SS_MIN) {
+ // If the debugger is single-stepping one of our threads, check to
+ // see if we're that thread and we've reached a step point.
+ const SingleStepControl* single_step_control = thread->GetSingleStepControl();
+ DCHECK(single_step_control != nullptr);
+ if (single_step_control->is_active) {
+ CHECK(!m->IsNative());
+ if (single_step_control->step_depth == JDWP::SD_INTO) {
+ // Step into method calls. We break when the line number
+ // or method pointer changes. If we're in SS_MIN mode, we
+ // always stop.
+ if (single_step_control->method != m) {
+ event_flags |= kSingleStep;
+ VLOG(jdwp) << "SS new method";
+ } else if (single_step_control->step_size == JDWP::SS_MIN) {
+ event_flags |= kSingleStep;
+ VLOG(jdwp) << "SS new instruction";
+ } else if (single_step_control->dex_pcs.find(dex_pc) == single_step_control->dex_pcs.end()) {
+ event_flags |= kSingleStep;
+ VLOG(jdwp) << "SS new line";
+ }
+ } else if (single_step_control->step_depth == JDWP::SD_OVER) {
+ // Step over method calls. We break when the line number is
+ // different and the frame depth is <= the original frame
+ // depth. (We can't just compare on the method, because we
+ // might get unrolled past it by an exception, and it's tricky
+ // to identify recursion.)
+
+ int stack_depth = GetStackDepth(thread);
+
+ if (stack_depth < single_step_control->stack_depth) {
+ // Popped up one or more frames, always trigger.
+ event_flags |= kSingleStep;
+ VLOG(jdwp) << "SS method pop";
+ } else if (stack_depth == single_step_control->stack_depth) {
+ // Same depth, see if we moved.
+ if (single_step_control->step_size == JDWP::SS_MIN) {
event_flags |= kSingleStep;
VLOG(jdwp) << "SS new instruction";
- } else if (gSingleStepControl.dex_pcs.find(dex_pc) == gSingleStepControl.dex_pcs.end()) {
+ } else if (single_step_control->dex_pcs.find(dex_pc) == single_step_control->dex_pcs.end()) {
event_flags |= kSingleStep;
VLOG(jdwp) << "SS new line";
}
- } else if (gSingleStepControl.step_depth == JDWP::SD_OVER) {
- // Step over method calls. We break when the line number is
- // different and the frame depth is <= the original frame
- // depth. (We can't just compare on the method, because we
- // might get unrolled past it by an exception, and it's tricky
- // to identify recursion.)
+ }
+ } else {
+ CHECK_EQ(single_step_control->step_depth, JDWP::SD_OUT);
+ // Return from the current method. We break when the frame
+ // depth pops up.
- int stack_depth = GetStackDepth(thread);
+ // This differs from the "method exit" break in that it stops
+ // with the PC at the next instruction in the returned-to
+ // function, rather than the end of the returning function.
- if (stack_depth < gSingleStepControl.stack_depth) {
- // popped up one or more frames, always trigger
- event_flags |= kSingleStep;
- VLOG(jdwp) << "SS method pop";
- } else if (stack_depth == gSingleStepControl.stack_depth) {
- // same depth, see if we moved
- if (gSingleStepControl.step_size == JDWP::SS_MIN) {
- event_flags |= kSingleStep;
- VLOG(jdwp) << "SS new instruction";
- } else if (gSingleStepControl.dex_pcs.find(dex_pc) == gSingleStepControl.dex_pcs.end()) {
- event_flags |= kSingleStep;
- VLOG(jdwp) << "SS new line";
- }
- }
- } else {
- CHECK_EQ(gSingleStepControl.step_depth, JDWP::SD_OUT);
- // Return from the current method. We break when the frame
- // depth pops up.
-
- // This differs from the "method exit" break in that it stops
- // with the PC at the next instruction in the returned-to
- // function, rather than the end of the returning function.
-
- int stack_depth = GetStackDepth(thread);
- if (stack_depth < gSingleStepControl.stack_depth) {
- event_flags |= kSingleStep;
- VLOG(jdwp) << "SS method pop";
- }
+ int stack_depth = GetStackDepth(thread);
+ if (stack_depth < single_step_control->stack_depth) {
+ event_flags |= kSingleStep;
+ VLOG(jdwp) << "SS method pop";
}
}
}
@@ -2356,7 +2360,7 @@
// If there's something interesting going on, see if it matches one
// of the debugger filters.
if (event_flags != 0) {
- Dbg::PostLocationEvent(m, dex_pc, this_object, event_flags);
+ Dbg::PostLocationEvent(m, dex_pc, this_object, event_flags, nullptr);
}
}
@@ -2444,50 +2448,50 @@
return sts.GetError();
}
- MutexLock mu2(self, *Locks::breakpoint_lock_);
- // TODO: there's no theoretical reason why we couldn't support single-stepping
- // of multiple threads at once, but we never did so historically.
- if (gSingleStepControl.thread != NULL && sts.GetThread() != gSingleStepControl.thread) {
- LOG(WARNING) << "single-step already active for " << *gSingleStepControl.thread
- << "; switching to " << *sts.GetThread();
- }
-
//
// Work out what Method* we're in, the current line number, and how deep the stack currently
// is for step-out.
//
struct SingleStepStackVisitor : public StackVisitor {
- explicit SingleStepStackVisitor(Thread* thread)
- EXCLUSIVE_LOCKS_REQUIRED(Locks::breakpoint_lock_)
+ explicit SingleStepStackVisitor(Thread* thread, SingleStepControl* single_step_control,
+ int32_t* line_number)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : StackVisitor(thread, NULL) {
- gSingleStepControl.method = NULL;
- gSingleStepControl.stack_depth = 0;
+ : StackVisitor(thread, NULL), single_step_control_(single_step_control),
+ line_number_(line_number) {
+ DCHECK_EQ(single_step_control_, thread->GetSingleStepControl());
+ single_step_control_->method = NULL;
+ single_step_control_->stack_depth = 0;
}
// TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses
// annotalysis.
bool VisitFrame() NO_THREAD_SAFETY_ANALYSIS {
- Locks::breakpoint_lock_->AssertHeld(Thread::Current());
- const mirror::ArtMethod* m = GetMethod();
+ mirror::ArtMethod* m = GetMethod();
if (!m->IsRuntimeMethod()) {
- ++gSingleStepControl.stack_depth;
- if (gSingleStepControl.method == NULL) {
+ ++single_step_control_->stack_depth;
+ if (single_step_control_->method == NULL) {
const mirror::DexCache* dex_cache = m->GetDeclaringClass()->GetDexCache();
- gSingleStepControl.method = m;
- gSingleStepControl.line_number = -1;
+ single_step_control_->method = m;
+ *line_number_ = -1;
if (dex_cache != NULL) {
const DexFile& dex_file = *dex_cache->GetDexFile();
- gSingleStepControl.line_number = dex_file.GetLineNumFromPC(m, GetDexPc());
+ *line_number_ = dex_file.GetLineNumFromPC(m, GetDexPc());
}
}
}
return true;
}
+
+ SingleStepControl* const single_step_control_;
+ int32_t* const line_number_;
};
- SingleStepStackVisitor visitor(sts.GetThread());
+ Thread* const thread = sts.GetThread();
+ SingleStepControl* const single_step_control = thread->GetSingleStepControl();
+ DCHECK(single_step_control != nullptr);
+ int32_t line_number = -1;
+ SingleStepStackVisitor visitor(thread, single_step_control, &line_number);
visitor.WalkStack();
//
@@ -2495,17 +2499,14 @@
//
struct DebugCallbackContext {
- DebugCallbackContext() EXCLUSIVE_LOCKS_REQUIRED(Locks::breakpoint_lock_) {
- last_pc_valid = false;
- last_pc = 0;
+ explicit DebugCallbackContext(SingleStepControl* single_step_control, int32_t line_number)
+ : single_step_control_(single_step_control), line_number_(line_number),
+ last_pc_valid(false), last_pc(0) {
}
- // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses
- // annotalysis.
- static bool Callback(void* raw_context, uint32_t address, uint32_t line_number) NO_THREAD_SAFETY_ANALYSIS {
- Locks::breakpoint_lock_->AssertHeld(Thread::Current());
+ static bool Callback(void* raw_context, uint32_t address, uint32_t line_number) {
DebugCallbackContext* context = reinterpret_cast<DebugCallbackContext*>(raw_context);
- if (static_cast<int32_t>(line_number) == gSingleStepControl.line_number) {
+ if (static_cast<int32_t>(line_number) == context->line_number_) {
if (!context->last_pc_valid) {
// Everything from this address until the next line change is ours.
context->last_pc = address;
@@ -2516,35 +2517,32 @@
} else if (context->last_pc_valid) { // and the line number is new
// Add everything from the last entry up until here to the set
for (uint32_t dex_pc = context->last_pc; dex_pc < address; ++dex_pc) {
- gSingleStepControl.dex_pcs.insert(dex_pc);
+ context->single_step_control_->dex_pcs.insert(dex_pc);
}
context->last_pc_valid = false;
}
return false; // There may be multiple entries for any given line.
}
- // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses
- // annotalysis.
- ~DebugCallbackContext() NO_THREAD_SAFETY_ANALYSIS {
- Locks::breakpoint_lock_->AssertHeld(Thread::Current());
+ ~DebugCallbackContext() {
// If the line number was the last in the position table...
if (last_pc_valid) {
- size_t end = MethodHelper(gSingleStepControl.method).GetCodeItem()->insns_size_in_code_units_;
+ size_t end = MethodHelper(single_step_control_->method).GetCodeItem()->insns_size_in_code_units_;
for (uint32_t dex_pc = last_pc; dex_pc < end; ++dex_pc) {
- gSingleStepControl.dex_pcs.insert(dex_pc);
+ single_step_control_->dex_pcs.insert(dex_pc);
}
}
}
+ SingleStepControl* const single_step_control_;
+ const int32_t line_number_;
bool last_pc_valid;
uint32_t last_pc;
};
- gSingleStepControl.dex_pcs.clear();
- const mirror::ArtMethod* m = gSingleStepControl.method;
- if (m->IsNative()) {
- gSingleStepControl.line_number = -1;
- } else {
- DebugCallbackContext context;
+ single_step_control->dex_pcs.clear();
+ const mirror::ArtMethod* m = single_step_control->method;
+ if (!m->IsNative()) {
+ DebugCallbackContext context(single_step_control, line_number);
MethodHelper mh(m);
mh.GetDexFile().DecodeDebugInfo(mh.GetCodeItem(), m->IsStatic(), m->GetDexMethodIndex(),
DebugCallbackContext::Callback, NULL, &context);
@@ -2554,20 +2552,19 @@
// Everything else...
//
- gSingleStepControl.thread = sts.GetThread();
- gSingleStepControl.step_size = step_size;
- gSingleStepControl.step_depth = step_depth;
- gSingleStepControl.is_active = true;
+ single_step_control->step_size = step_size;
+ single_step_control->step_depth = step_depth;
+ single_step_control->is_active = true;
if (VLOG_IS_ON(jdwp)) {
- VLOG(jdwp) << "Single-step thread: " << *gSingleStepControl.thread;
- VLOG(jdwp) << "Single-step step size: " << gSingleStepControl.step_size;
- VLOG(jdwp) << "Single-step step depth: " << gSingleStepControl.step_depth;
- VLOG(jdwp) << "Single-step current method: " << PrettyMethod(gSingleStepControl.method);
- VLOG(jdwp) << "Single-step current line: " << gSingleStepControl.line_number;
- VLOG(jdwp) << "Single-step current stack depth: " << gSingleStepControl.stack_depth;
+ VLOG(jdwp) << "Single-step thread: " << *thread;
+ VLOG(jdwp) << "Single-step step size: " << single_step_control->step_size;
+ VLOG(jdwp) << "Single-step step depth: " << single_step_control->step_depth;
+ VLOG(jdwp) << "Single-step current method: " << PrettyMethod(single_step_control->method);
+ VLOG(jdwp) << "Single-step current line: " << line_number;
+ VLOG(jdwp) << "Single-step current stack depth: " << single_step_control->stack_depth;
VLOG(jdwp) << "Single-step dex_pc values:";
- for (std::set<uint32_t>::iterator it = gSingleStepControl.dex_pcs.begin() ; it != gSingleStepControl.dex_pcs.end(); ++it) {
+ for (std::set<uint32_t>::iterator it = single_step_control->dex_pcs.begin(); it != single_step_control->dex_pcs.end(); ++it) {
VLOG(jdwp) << StringPrintf(" %#x", *it);
}
}
@@ -2575,12 +2572,17 @@
return JDWP::ERR_NONE;
}
-void Dbg::UnconfigureStep(JDWP::ObjectId /*thread_id*/) {
- MutexLock mu(Thread::Current(), *Locks::breakpoint_lock_);
-
- gSingleStepControl.is_active = false;
- gSingleStepControl.thread = NULL;
- gSingleStepControl.dex_pcs.clear();
+void Dbg::UnconfigureStep(JDWP::ObjectId thread_id) {
+ ScopedObjectAccessUnchecked soa(Thread::Current());
+ MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
+ Thread* thread;
+ JDWP::JdwpError error = DecodeThread(soa, thread_id, thread);
+ if (error != JDWP::ERR_NONE) {
+ SingleStepControl* single_step_control = thread->GetSingleStepControl();
+ DCHECK(single_step_control != nullptr);
+ single_step_control->is_active = false;
+ single_step_control->dex_pcs.clear();
+ }
}
static char JdwpTagToShortyChar(JDWP::JdwpTag tag) {
diff --git a/runtime/debugger.h b/runtime/debugger.h
index 8574a33..0a7cf5a 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -23,6 +23,7 @@
#include <pthread.h>
+#include <set>
#include <string>
#include "jdwp/jdwp.h"
@@ -79,6 +80,39 @@
/* condition variable to wait on while the method executes */
Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
ConditionVariable cond_ GUARDED_BY(lock_);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DebugInvokeReq);
+};
+
+// Thread local data-structure that holds fields for controlling single-stepping.
+struct SingleStepControl {
+ SingleStepControl()
+ : is_active(false), step_size(JDWP::SS_MIN), step_depth(JDWP::SD_INTO),
+ method(nullptr), stack_depth(0) {
+ }
+
+ // Are we single-stepping right now?
+ bool is_active;
+
+ // See JdwpStepSize and JdwpStepDepth for details.
+ JDWP::JdwpStepSize step_size;
+ JDWP::JdwpStepDepth step_depth;
+
+ // The location this single-step was initiated from.
+ // A single-step is initiated in a suspended thread. We save here the current method and the
+ // set of DEX pcs associated to the source line number where the suspension occurred.
+ // This is used to support SD_INTO and SD_OVER single-step depths so we detect when a single-step
+ // causes the execution of an instruction in a different method or at a different line number.
+ mirror::ArtMethod* method;
+ std::set<uint32_t> dex_pcs;
+
+ // The stack depth when this single-step was initiated. This is used to support SD_OVER and SD_OUT
+ // single-step depth.
+ int stack_depth;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SingleStepControl);
};
class Dbg {
@@ -230,6 +264,9 @@
static void OutputVariableTable(JDWP::RefTypeId ref_type_id, JDWP::MethodId id, bool with_generic,
JDWP::ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static void OutputMethodReturnValue(JDWP::MethodId method_id, const JValue* return_value,
+ JDWP::ExpandBuf* pReply)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static JDWP::JdwpError GetBytecodes(JDWP::RefTypeId class_id, JDWP::MethodId method_id,
std::vector<uint8_t>& bytecodes)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -254,6 +291,8 @@
static std::string StringToUtf8(JDWP::ObjectId string_id)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static void OutputJValue(JDWP::JdwpTag tag, const JValue* return_value, JDWP::ExpandBuf* pReply)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
/*
* Thread, ThreadGroup, Frame
@@ -327,7 +366,8 @@
kMethodExit = 0x08,
};
static void PostLocationEvent(const mirror::ArtMethod* method, int pcOffset,
- mirror::Object* thisPtr, int eventFlags)
+ mirror::Object* thisPtr, int eventFlags,
+ const JValue* return_value)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static void PostException(Thread* thread, const ThrowLocation& throw_location,
mirror::ArtMethod* catch_method,
@@ -353,9 +393,9 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static JDWP::JdwpError ConfigureStep(JDWP::ObjectId thread_id, JDWP::JdwpStepSize size,
JDWP::JdwpStepDepth depth)
- LOCKS_EXCLUDED(Locks::breakpoint_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static void UnconfigureStep(JDWP::ObjectId thread_id) LOCKS_EXCLUDED(Locks::breakpoint_lock_);
+ static void UnconfigureStep(JDWP::ObjectId thread_id)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static JDWP::JdwpError InvokeMethod(JDWP::ObjectId thread_id, JDWP::ObjectId object_id,
JDWP::RefTypeId class_id, JDWP::MethodId method_id,
diff --git a/runtime/jdwp/jdwp.h b/runtime/jdwp/jdwp.h
index a1657d0..fd78bf2 100644
--- a/runtime/jdwp/jdwp.h
+++ b/runtime/jdwp/jdwp.h
@@ -31,6 +31,7 @@
struct iovec;
namespace art {
+ union JValue;
namespace mirror {
class ArtMethod;
} // namespace mirror
@@ -185,8 +186,11 @@
* issuing a MethodEntry on a native method.
*
* "eventFlags" indicates the types of events that have occurred.
+ *
+ * "returnValue" is non-null for MethodExit events only.
*/
- bool PostLocationEvent(const JdwpLocation* pLoc, ObjectId thisPtr, int eventFlags)
+ bool PostLocationEvent(const JdwpLocation* pLoc, ObjectId thisPtr, int eventFlags,
+ const JValue* returnValue)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
/*
diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc
index 345549d..61bd1ed 100644
--- a/runtime/jdwp/jdwp_event.cc
+++ b/runtime/jdwp/jdwp_event.cc
@@ -719,7 +719,8 @@
* - Single-step to a line with a breakpoint. Should get a single
* event message with both events in it.
*/
-bool JdwpState::PostLocationEvent(const JdwpLocation* pLoc, ObjectId thisPtr, int eventFlags) {
+bool JdwpState::PostLocationEvent(const JdwpLocation* pLoc, ObjectId thisPtr, int eventFlags,
+ const JValue* returnValue) {
ModBasket basket;
basket.pLoc = pLoc;
basket.classId = pLoc->class_id;
@@ -771,9 +772,7 @@
}
if ((eventFlags & Dbg::kMethodExit) != 0) {
FindMatchingEvents(EK_METHOD_EXIT, &basket, match_list, &match_count);
-
- // TODO: match EK_METHOD_EXIT_WITH_RETURN_VALUE too; we need to include the 'value', though.
- // FindMatchingEvents(EK_METHOD_EXIT_WITH_RETURN_VALUE, &basket, match_list, &match_count);
+ FindMatchingEvents(EK_METHOD_EXIT_WITH_RETURN_VALUE, &basket, match_list, &match_count);
}
if (match_count != 0) {
VLOG(jdwp) << "EVENT: " << match_list[0]->eventKind << "(" << match_count << " total) "
@@ -792,6 +791,9 @@
expandBufAdd4BE(pReq, match_list[i]->requestId);
expandBufAdd8BE(pReq, basket.threadId);
expandBufAddLocation(pReq, *pLoc);
+ if (match_list[i]->eventKind == EK_METHOD_EXIT_WITH_RETURN_VALUE) {
+ Dbg::OutputMethodReturnValue(pLoc->method_id, returnValue, pReq);
+ }
}
}
diff --git a/runtime/thread.cc b/runtime/thread.cc
index e55c35f..1add507 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -917,6 +917,7 @@
throwing_OutOfMemoryError_(false),
debug_suspend_count_(0),
debug_invoke_req_(new DebugInvokeReq),
+ single_step_control_(new SingleStepControl),
deoptimization_shadow_frame_(NULL),
instrumentation_stack_(new std::deque<instrumentation::InstrumentationStackFrame>),
name_(new std::string(kThreadNameDuringStartup)),
@@ -1019,6 +1020,7 @@
}
delete debug_invoke_req_;
+ delete single_step_control_;
delete instrumentation_stack_;
delete name_;
delete stack_trace_sample_;
diff --git a/runtime/thread.h b/runtime/thread.h
index f4b2ae5..db2f7b4 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -68,6 +68,7 @@
class ScopedObjectAccess;
class ScopedObjectAccessUnchecked;
class ShadowFrame;
+struct SingleStepControl;
class Thread;
class ThreadList;
@@ -513,6 +514,10 @@
return debug_invoke_req_;
}
+ SingleStepControl* GetSingleStepControl() const {
+ return single_step_control_;
+ }
+
void SetDeoptimizationShadowFrame(ShadowFrame* sf);
void SetDeoptimizationReturnValue(const JValue& ret_val);
@@ -746,6 +751,9 @@
// JDWP invoke-during-breakpoint support.
DebugInvokeReq* debug_invoke_req_;
+ // JDWP single-stepping support.
+ SingleStepControl* single_step_control_;
+
// Shadow frame that is used temporarily during the deoptimization of a method.
ShadowFrame* deoptimization_shadow_frame_;
JValue deoptimization_return_value_;