From 3f52eafe5577b8489f90dc8ed5981b3455206147 Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Fri, 4 Apr 2014 17:50:18 +0200 Subject: Prepare field watchpoint support Adds field read/write events in the instrumentation. The debugger now registers as a listener for these events so JDWP field access and field modification events can be reported. This CL will be followed by another one to report these events from the interpreter. Therefore no JDWP field access and field modification events can be sent for now. Bug: 8267708 Change-Id: If2a93eb590805567d69015c83cce9cd2ab712cbd --- runtime/jdwp/jdwp.h | 11 +++++ runtime/jdwp/jdwp_event.cc | 99 ++++++++++++++++++++++++++++++++++++-------- runtime/jdwp/jdwp_handler.cc | 4 +- 3 files changed, 95 insertions(+), 19 deletions(-) (limited to 'runtime/jdwp') diff --git a/runtime/jdwp/jdwp.h b/runtime/jdwp/jdwp.h index 66ebb96..1477324 100644 --- a/runtime/jdwp/jdwp.h +++ b/runtime/jdwp/jdwp.h @@ -197,6 +197,17 @@ struct JdwpState { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); /* + * A field of interest has been accessed or modified. This is used for field access and field + * modification events. + * + * "fieldValue" is non-null for field modification events only. + * "is_modification" is true for field modification, false for field access. + */ + bool PostFieldEvent(const JdwpLocation* pLoc, RefTypeId typeId, FieldId fieldId, + ObjectId thisPtr, const JValue* fieldValue, bool is_modification) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + /* * An exception has been thrown. * * Pass in a zeroed-out "*pCatchLoc" if the exception wasn't caught. diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc index 9b3ea2e..9627802 100644 --- a/runtime/jdwp/jdwp_event.cc +++ b/runtime/jdwp/jdwp_event.cc @@ -121,26 +121,14 @@ struct ModBasket { /* nothing for StepOnly -- handled differently */ }; -/* - * Dump an event to the log file. - */ -static void dumpEvent(const JdwpEvent* pEvent) { - LOG(INFO) << StringPrintf("Event id=0x%4x %p (prev=%p next=%p):", pEvent->requestId, pEvent, pEvent->prev, pEvent->next); - LOG(INFO) << " kind=" << pEvent->eventKind << " susp=" << pEvent->suspend_policy << " modCount=" << pEvent->modCount; - - for (int i = 0; i < pEvent->modCount; i++) { - const JdwpEventMod* pMod = &pEvent->mods[i]; - LOG(INFO) << " " << pMod->modKind; - /* TODO - show details */ - } -} - static bool NeedsFullDeoptimization(JdwpEventKind eventKind) { switch (eventKind) { case EK_METHOD_ENTRY: case EK_METHOD_EXIT: case EK_METHOD_EXIT_WITH_RETURN_VALUE: case EK_SINGLE_STEP: + case EK_FIELD_ACCESS: + case EK_FIELD_MODIFICATION: return true; default: return false; @@ -177,9 +165,6 @@ JdwpError JdwpState::RegisterEvent(JdwpEvent* pEvent) { if (status != ERR_NONE) { return status; } - } else if (pMod->modKind == MK_FIELD_ONLY) { - /* should be for EK_FIELD_ACCESS or EK_FIELD_MODIFICATION */ - dumpEvent(pEvent); /* TODO - need for field watches */ } } if (NeedsFullDeoptimization(pEvent->eventKind)) { @@ -843,6 +828,86 @@ bool JdwpState::PostLocationEvent(const JdwpLocation* pLoc, ObjectId thisPtr, in return match_count != 0; } +bool JdwpState::PostFieldEvent(const JdwpLocation* pLoc, RefTypeId typeId, FieldId fieldId, + ObjectId thisPtr, const JValue* fieldValue, bool is_modification) { + ModBasket basket; + basket.pLoc = pLoc; + basket.classId = pLoc->class_id; + basket.thisPtr = thisPtr; + basket.threadId = Dbg::GetThreadSelfId(); + basket.className = Dbg::GetClassName(pLoc->class_id); + basket.field = fieldId; + + if (InvokeInProgress()) { + VLOG(jdwp) << "Not posting field event during invoke"; + return false; + } + + // Get field's reference type tag. + JDWP::JdwpTypeTag type_tag; + uint32_t class_status; // unused here. + JdwpError error = Dbg::GetClassInfo(typeId, &type_tag, &class_status, NULL); + if (error != ERR_NONE) { + return false; + } + + // Get instance type tag. + uint8_t tag; + error = Dbg::GetObjectTag(thisPtr, tag); + if (error != ERR_NONE) { + return false; + } + + int match_count = 0; + ExpandBuf* pReq = NULL; + JdwpSuspendPolicy suspend_policy = SP_NONE; + { + MutexLock mu(Thread::Current(), event_list_lock_); + JdwpEvent** match_list = AllocMatchList(event_list_size_); + + if (is_modification) { + FindMatchingEvents(EK_FIELD_MODIFICATION, &basket, match_list, &match_count); + } else { + FindMatchingEvents(EK_FIELD_ACCESS, &basket, match_list, &match_count); + } + if (match_count != 0) { + VLOG(jdwp) << "EVENT: " << match_list[0]->eventKind << "(" << match_count << " total) " + << basket.className << "." << Dbg::GetMethodName(pLoc->method_id) + << StringPrintf(" thread=%#" PRIx64 " dex_pc=%#" PRIx64 ")", + basket.threadId, pLoc->dex_pc); + + suspend_policy = scanSuspendPolicy(match_list, match_count); + VLOG(jdwp) << " suspend_policy=" << suspend_policy; + + pReq = eventPrep(); + expandBufAdd1(pReq, suspend_policy); + expandBufAdd4BE(pReq, match_count); + + for (int i = 0; i < match_count; i++) { + expandBufAdd1(pReq, match_list[i]->eventKind); + expandBufAdd4BE(pReq, match_list[i]->requestId); + expandBufAdd8BE(pReq, basket.threadId); + expandBufAddLocation(pReq, *pLoc); + expandBufAdd1(pReq, type_tag); + expandBufAddRefTypeId(pReq, typeId); + expandBufAddFieldId(pReq, fieldId); + expandBufAdd1(pReq, tag); + expandBufAddObjectId(pReq, thisPtr); + if (is_modification) { + Dbg::OutputFieldValue(fieldId, fieldValue, pReq); + } + } + } + + CleanupMatchList(match_list, match_count); + } + + Dbg::ManageDeoptimization(); + + SendRequestAndPossiblySuspend(pReq, suspend_policy, basket.threadId); + return match_count != 0; +} + /* * A thread is starting or stopping. * diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc index c2a2b54..8f224d2 100644 --- a/runtime/jdwp/jdwp_handler.cc +++ b/runtime/jdwp/jdwp_handler.cc @@ -353,8 +353,8 @@ static JdwpError VM_DisposeObjects(JdwpState*, Request& request, ExpandBuf*) static JdwpError VM_Capabilities(JdwpState*, Request&, ExpandBuf* reply) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - expandBufAdd1(reply, false); // canWatchFieldModification - expandBufAdd1(reply, false); // canWatchFieldAccess + expandBufAdd1(reply, true); // canWatchFieldModification + expandBufAdd1(reply, true); // canWatchFieldAccess expandBufAdd1(reply, true); // canGetBytecodes expandBufAdd1(reply, true); // canGetSyntheticAttribute expandBufAdd1(reply, true); // canGetOwnedMonitorInfo -- cgit v1.1