summaryrefslogtreecommitdiffstats
path: root/vm/DvmDex.c
diff options
context:
space:
mode:
authorAndy McFadden <fadden@android.com>2009-10-28 17:39:02 -0700
committerAndy McFadden <fadden@android.com>2009-11-16 12:37:25 -0800
commit96516932f1557d8f48a8b2dbbb885af01a11ef6e (patch)
treea4bac9fd87ab5e88dfaaa92c84d99b6195fdc0fe /vm/DvmDex.c
parent62beb8b347e9e578de1844d56ce08f1d536e9c28 (diff)
downloadandroid_dalvik-96516932f1557d8f48a8b2dbbb885af01a11ef6e.tar.gz
android_dalvik-96516932f1557d8f48a8b2dbbb885af01a11ef6e.tar.bz2
android_dalvik-96516932f1557d8f48a8b2dbbb885af01a11ef6e.zip
Change the way breakpoints work.
This replaces the breakpoint mechanism with a more efficient approach. We now insert breakpoint instructions into the bytecode stream instead of maintaining a table. This requires mapping DEX files as private instead of shared, which allows copy-on-write to work. mprotect() is used to guard the pages against inadvertent writes. Unused opcode EC is now OP_BREAKPOINT. It's not recognized by dexdump or any interpreter except portdbg, but it can be encountered by the bytecode verifier (the debugger can request breakpoints in unverified code). Breakpoint changes are blocked while the verifier runs to avoid races. This eliminates method->debugBreakpointCount, which is no longer needed. (Also, it clashed with LinearAlloc's read-only mode.) The deferred verification error mechanism was using a code-copying approach to modify the bytecode stream. That has been changed to use the same copy-on-write modification mechanism. Also, normalized all PAGE_SIZE/PAGESIZE references to a single SYSTEM_PAGE_SIZE define. Simple Fibonacci computation test times (opal-eng): JIT, no debugger: 10.6ms Fast interp, no debugger: 36ms Portable interp, no debugger: 43.8ms ORIG debug interp, no breakpoints set: 458ms ORIG debug interp, breakpoint set nearby: 697ms NEW debug interp, no breakpoints set: 341ms NEW debug interp, breakpoints set nearby: 341ms Where "nearby" means there's a breakpoint in the method doing the computation that isn't actually hit -- the VM had an optimization where it flagged methods with breakpoints and skipped some of the processing when possible. The bottom line is that code should run noticeably faster while a debugger is attached.
Diffstat (limited to 'vm/DvmDex.c')
-rw-r--r--vm/DvmDex.c72
1 files changed, 72 insertions, 0 deletions
diff --git a/vm/DvmDex.c b/vm/DvmDex.c
index 6740632b3..20a4376e6 100644
--- a/vm/DvmDex.c
+++ b/vm/DvmDex.c
@@ -13,11 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
/*
* VM-specific state associated with a DEX file.
*/
#include "Dalvik.h"
+
/*
* Create auxillary data structures.
*
@@ -217,3 +219,73 @@ void dvmDexFileFree(DvmDex* pDvmDex)
free(pDvmDex);
}
+
+/*
+ * Change the byte at the specified address to a new value. If the location
+ * already has the new value, do nothing.
+ *
+ * This requires changing the access permissions to read-write, updating
+ * the value, and then resetting the permissions.
+ *
+ * This does not make any synchronization guarantees. It's important for the
+ * caller(s) to work out mutual exclusion, at least on a page granularity,
+ * to avoid a race where one threads sets read-write, another thread sets
+ * read-only, and then the first thread does a write.
+ *
+ * TODO: if we're back to the original state of the page, use
+ * madvise(MADV_DONTNEED) to release the private/dirty copy.
+ *
+ * Returns "true" on success.
+ */
+bool dvmDexChangeDex1(DvmDex* pDvmDex, u1* addr, u1 newVal)
+{
+ if (*addr == newVal) {
+ LOGV("+++ byte at %p is already 0x%02x\n", addr, newVal);
+ return true;
+ }
+
+ LOGV("+++ change byte at %p from 0x%02x to 0x%02x\n", addr, *addr, newVal);
+ if (sysChangeMapAccess(addr, 1, true, &pDvmDex->memMap) < 0) {
+ LOGE("access change failed\n");
+ return false;
+ }
+
+ *addr = newVal;
+
+ if (sysChangeMapAccess(addr, 1, false, &pDvmDex->memMap) < 0) {
+ LOGW("WARNING: unable to restore read-only access on mapping\n");
+ /* not fatal, keep going */
+ }
+
+ return true;
+}
+
+/*
+ * Change the 2-byte value at the specified address to a new value. If the
+ * location already has the new value, do nothing.
+ *
+ * Otherwise works like dvmDexChangeDex1.
+ */
+bool dvmDexChangeDex2(DvmDex* pDvmDex, u2* addr, u2 newVal)
+{
+ if (*addr == newVal) {
+ LOGV("+++ value at %p is already 0x%04x\n", addr, newVal);
+ return true;
+ }
+
+ LOGV("+++ change 2byte at %p from 0x%04x to 0x%04x\n", addr, *addr, newVal);
+ if (sysChangeMapAccess(addr, 2, true, &pDvmDex->memMap) < 0) {
+ LOGE("access change failed\n");
+ return false;
+ }
+
+ *addr = newVal;
+
+ if (sysChangeMapAccess(addr, 2, false, &pDvmDex->memMap) < 0) {
+ LOGW("WARNING: unable to restore read-only access on mapping\n");
+ /* not fatal, keep going */
+ }
+
+ return true;
+}
+