Factor debug locals into liveness.
If the debugger asks for a reference held in a local variable, we know
that the reference is valid because the stack is part of the root set.
With liveness analysis, it's possible for a local to be considered dead
and get GCed. Since we don't currently have a way to deal with that
in the debugging code, we just mark debugger-visible locals as live.
Bug 2534655
Change-Id: I5b217f20a76364f1f6a89499058de6ec4eb07b7d
diff --git a/vm/analysis/Liveness.c b/vm/analysis/Liveness.c
index c6040b5..4712953 100644
--- a/vm/analysis/Liveness.c
+++ b/vm/analysis/Liveness.c
@@ -15,7 +15,7 @@
*/
/*
- * Backward analysis for Dalvik bytecode.
+ * Liveness analysis for Dalvik bytecode.
*/
#include "Dalvik.h"
#include "analysis/Liveness.h"
@@ -23,6 +23,7 @@
static bool processInstruction(VerifierData* vdata, u4 curIdx,
BitVector* workBits);
+static bool markDebugLocals(VerifierData* vdata);
static void dumpLiveState(const VerifierData* vdata, u4 curIdx,
const BitVector* workBits);
@@ -265,6 +266,12 @@
}
#endif
+ /*
+ * Factor in the debug info, if any.
+ */
+ if (!markDebugLocals(vdata))
+ goto bail;
+
result = true;
bail:
@@ -724,6 +731,67 @@
return true;
}
+/*
+ * This is a dexDecodeDebugInfo callback, used by markDebugLocals().
+ */
+static void markLocalsCb(void* ctxt, u2 reg, u4 startAddress, u4 endAddress,
+ const char* name, const char* descriptor, const char* signature)
+{
+ VerifierData* vdata = (VerifierData*) ctxt;
+ bool verbose = dvmWantVerboseVerification(vdata->method);
+
+ if (verbose) {
+ LOGI("%04x-%04x %2d (%s %s)\n",
+ startAddress, endAddress, reg, name, descriptor);
+ }
+
+ bool wide = (descriptor[0] == 'D' || descriptor[0] == 'J');
+ assert(reg <= vdata->insnRegCount + (wide ? 1 : 0));
+
+ /*
+ * Set the bit in all GC point instructions in the range
+ * [startAddress, endAddress).
+ */
+ unsigned int idx;
+ for (idx = startAddress; idx < endAddress; idx++) {
+ BitVector* liveRegs = vdata->registerLines[idx].liveRegs;
+ if (liveRegs != NULL) {
+ if (wide) {
+ GENW(liveRegs, reg);
+ } else {
+ GEN(liveRegs, reg);
+ }
+ }
+ }
+}
+
+/*
+ * Mark all debugger-visible locals as live.
+ *
+ * The "locals" table describes the positions of the various locals in the
+ * stack frame based on the current execution address. If the debugger
+ * wants to display one, it issues a request by "slot number". We need
+ * to ensure that references in stack slots that might be queried by the
+ * debugger aren't GCed.
+ *
+ * (If the GC had some way to mark the slot as invalid we wouldn't have
+ * to do this. We could also have the debugger interface check the
+ * register map and simply refuse to return a "dead" value, but that's
+ * potentially confusing since the referred-to object might actually be
+ * alive, and being able to see it without having to hunt around for a
+ * "live" stack frame is useful.)
+ */
+static bool markDebugLocals(VerifierData* vdata)
+{
+ const Method* meth = vdata->method;
+
+ dexDecodeDebugInfo(meth->clazz->pDvmDex->pDexFile, dvmGetMethodCode(meth),
+ meth->clazz->descriptor, meth->prototype.protoIdx, meth->accessFlags,
+ NULL, markLocalsCb, vdata);
+
+ return true;
+}
+
/*
* Dump the liveness bits to the log.
diff --git a/vm/analysis/Liveness.h b/vm/analysis/Liveness.h
index f17217e..6436f66 100644
--- a/vm/analysis/Liveness.h
+++ b/vm/analysis/Liveness.h
@@ -15,13 +15,13 @@
*/
/*
- * Backward flow analysis.
+ * Liveness analysis.
*/
-#ifndef _DALVIK_BACKWARDFLOW
-#define _DALVIK_BACKWARDFLOW
+#ifndef _DALVIK_LIVENESS
+#define _DALVIK_LIVENESS
struct VerifierData;
bool dvmComputeLiveness(struct VerifierData* vdata);
-#endif /*_DALVIK_BACKWARDFLOW*/
+#endif /*_DALVIK_LIVENESS*/