Merge change I49b05da2
* changes:
Various XML fixes.
diff --git a/dexdump/OpCodeNames.c b/dexdump/OpCodeNames.c
index 378dcf7..6a1a52a 100644
--- a/dexdump/OpCodeNames.c
+++ b/dexdump/OpCodeNames.c
@@ -296,8 +296,8 @@
"UNUSED",
"UNUSED",
"UNUSED",
- "UNUSED",
- "UNUSED",
+ "^breakpoint", // does not appear in DEX files
+ "^throw-verification-error", // does not appear in DEX files
"+execute-inline",
"UNUSED",
diff --git a/libcore/tools/dalvik_jtreg/expectations/java.io.StreamTokenizer.Reset.expected b/libcore/tools/dalvik_jtreg/expectations/java.io.StreamTokenizer.Reset.expected
new file mode 100644
index 0000000..c09f0ce
--- /dev/null
+++ b/libcore/tools/dalvik_jtreg/expectations/java.io.StreamTokenizer.Reset.expected
@@ -0,0 +1,5 @@
+# The test is checking that the implementation doesn't read any characters
+# earlier than it absolutely needs to. This is a bogus requirement; streams
+# are allowed to buffer input as necessary.
+result=EXEC_FAILED
+pattern=.*should get token \\[\, but get -1.*
diff --git a/libcore/tools/dalvik_jtreg/java/dalvik/jtreg/JtregRunner.java b/libcore/tools/dalvik_jtreg/java/dalvik/jtreg/JtregRunner.java
index bfe2908..246d2d8 100644
--- a/libcore/tools/dalvik_jtreg/java/dalvik/jtreg/JtregRunner.java
+++ b/libcore/tools/dalvik_jtreg/java/dalvik/jtreg/JtregRunner.java
@@ -49,6 +49,7 @@
private final File localTemp = new File("/tmp/" + UUID.randomUUID());
private final File deviceTemp = new File("/data/jtreg" + UUID.randomUUID());
+ private final File testTemp = new File(deviceTemp, "/tests.tmp");
private final Adb adb = new Adb();
private final File directoryToScan;
@@ -98,7 +99,7 @@
int unsupportedTests = 0;
List<TestRun> runs = new ArrayList<TestRun>(tests.size());
- while (!builders.isTerminated() || !readyToRun.isEmpty()) {
+ for (int i = 0; i < tests.size(); i++) {
TestRun testRun = readyToRun.take();
runs.add(testRun);
@@ -132,6 +133,7 @@
*/
private void prepareDevice() {
adb.mkdir(deviceTemp);
+ adb.mkdir(testTemp);
File testRunnerJar = testToDex.writeTestRunnerJar();
adb.push(testRunnerJar, deviceTemp);
deviceTestRunner = new File(deviceTemp, testRunnerJar.getName());
@@ -187,6 +189,11 @@
builder.args("adb", "shell", "dalvikvm");
builder.args("-classpath", Command.path(testRun.getDeviceDex(), deviceTestRunner));
builder.args("-Duser.dir=" + testRun.getBase());
+ builder.args("-Duser.name=root");
+ builder.args("-Duser.language=en");
+ builder.args("-Duser.region=US");
+ builder.args("-Djavax.net.ssl.trustStore=/system/etc/security/cacerts.bks");
+ builder.args("-Djava.io.tmpdir=" + testTemp);
if (debugPort != null) {
builder.args("-Xrunjdwp:transport=dt_socket,address="
+ debugPort + ",server=y,suspend=y");
diff --git a/libdex/CmdUtils.c b/libdex/CmdUtils.c
index 7dfee87..102664c 100644
--- a/libdex/CmdUtils.c
+++ b/libdex/CmdUtils.c
@@ -162,7 +162,7 @@
goto bail;
}
- if (sysMapFileInShmem(fd, pMap) != 0) {
+ if (sysMapFileInShmemReadOnly(fd, pMap) != 0) {
fprintf(stderr, "ERROR: Unable to map %s\n", fileName);
close(fd);
goto bail;
diff --git a/libdex/InstrUtils.c b/libdex/InstrUtils.c
index 93e1f00..06e26bc 100644
--- a/libdex/InstrUtils.c
+++ b/libdex/InstrUtils.c
@@ -306,7 +306,7 @@
width = -3;
break;
- /* these should never appear */
+ /* these should never appear when scanning bytecode */
case OP_UNUSED_3E:
case OP_UNUSED_3F:
case OP_UNUSED_40:
@@ -325,7 +325,7 @@
case OP_UNUSED_E9:
case OP_UNUSED_EA:
case OP_UNUSED_EB:
- case OP_UNUSED_EC:
+ case OP_BREAKPOINT:
case OP_UNUSED_EF:
case OP_UNUSED_F1:
case OP_UNUSED_FC:
@@ -635,7 +635,7 @@
flags = kInstrCanContinue | kInstrCanThrow | kInstrInvoke;
break;
- /* these should never appear */
+ /* these should never appear when scanning code */
case OP_UNUSED_3E:
case OP_UNUSED_3F:
case OP_UNUSED_40:
@@ -654,7 +654,7 @@
case OP_UNUSED_E9:
case OP_UNUSED_EA:
case OP_UNUSED_EB:
- case OP_UNUSED_EC:
+ case OP_BREAKPOINT:
case OP_UNUSED_EF:
case OP_UNUSED_F1:
case OP_UNUSED_FC:
@@ -989,7 +989,7 @@
fmt = kFmt35c;
break;
- /* these should never appear */
+ /* these should never appear when scanning code */
case OP_UNUSED_3E:
case OP_UNUSED_3F:
case OP_UNUSED_40:
@@ -1008,7 +1008,7 @@
case OP_UNUSED_E9:
case OP_UNUSED_EA:
case OP_UNUSED_EB:
- case OP_UNUSED_EC:
+ case OP_BREAKPOINT:
case OP_UNUSED_EF:
case OP_UNUSED_F1:
case OP_UNUSED_FC:
diff --git a/libdex/OpCode.h b/libdex/OpCode.h
index 1272231..c3ed476 100644
--- a/libdex/OpCode.h
+++ b/libdex/OpCode.h
@@ -330,7 +330,15 @@
OP_UNUSED_E9 = 0xe9,
OP_UNUSED_EA = 0xea,
OP_UNUSED_EB = 0xeb,
- OP_UNUSED_EC = 0xec,
+
+ /*
+ * The "breakpoint" instruction is special, in that it should never
+ * be seen by anything but the debug interpreter. During debugging
+ * it takes the place of an arbitrary opcode, which means operations
+ * like "tell me the opcode width so I can find the next instruction"
+ * aren't possible. (This is correctable, but probably not useful.)
+ */
+ OP_BREAKPOINT = 0xec,
/* optimizer output -- these are never generated by "dx" */
OP_THROW_VERIFICATION_ERROR = 0xed,
@@ -358,6 +366,7 @@
#define kNumDalvikInstructions 256
+
/*
* Switch-statement signatures are a "NOP" followed by a code. (A true NOP
* is 0x0000.)
@@ -627,7 +636,7 @@
H(OP_UNUSED_E9), \
H(OP_UNUSED_EA), \
H(OP_UNUSED_EB), \
- H(OP_UNUSED_EC), \
+ H(OP_BREAKPOINT), \
H(OP_THROW_VERIFICATION_ERROR), \
H(OP_EXECUTE_INLINE), \
H(OP_UNUSED_EF), \
diff --git a/libdex/SysUtil.c b/libdex/SysUtil.c
index bf1be88..eaa612b 100644
--- a/libdex/SysUtil.c
+++ b/libdex/SysUtil.c
@@ -155,12 +155,12 @@
/*
* Map a file (from fd's current offset) into a shared, read-only memory
- * segment. The file offset must be a multiple of the page size.
+ * segment. The file offset must be a multiple of the system page size.
*
* On success, returns 0 and fills out "pMap". On failure, returns a nonzero
* value and does not disturb "pMap".
*/
-int sysMapFileInShmem(int fd, MemMapping* pMap)
+int sysMapFileInShmemReadOnly(int fd, MemMapping* pMap)
{
#ifdef HAVE_POSIX_FILEMAP
off_t start;
@@ -174,7 +174,7 @@
memPtr = mmap(NULL, length, PROT_READ, MAP_FILE | MAP_SHARED, fd, start);
if (memPtr == MAP_FAILED) {
- LOGW("mmap(%d, R, FILE|SHARED, %d, %d) failed: %s\n", (int) length,
+ LOGW("mmap(%d, RO, FILE|SHARED, %d, %d) failed: %s\n", (int) length,
fd, (int) start, strerror(errno));
return -1;
}
@@ -184,6 +184,60 @@
return 0;
#else
+ sysFakeMapFile(fd, pMap);
+#endif
+}
+
+/*
+ * Map a file (from fd's current offset) into a private, read-write memory
+ * segment that will be marked read-only (a/k/a "writable read-only"). The
+ * file offset must be a multiple of the system page size.
+ *
+ * In some cases the mapping will be fully writable (e.g. for files on
+ * FAT filesystems).
+ *
+ * On success, returns 0 and fills out "pMap". On failure, returns a nonzero
+ * value and does not disturb "pMap".
+ */
+int sysMapFileInShmemWritableReadOnly(int fd, MemMapping* pMap)
+{
+#ifdef HAVE_POSIX_FILEMAP
+ off_t start;
+ size_t length;
+ void* memPtr;
+
+ assert(pMap != NULL);
+
+ if (getFileStartAndLength(fd, &start, &length) < 0)
+ return -1;
+
+ memPtr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_FILE | MAP_PRIVATE,
+ fd, start);
+ if (memPtr == MAP_FAILED) {
+ LOGW("mmap(%d, R/W, FILE|PRIVATE, %d, %d) failed: %s\n", (int) length,
+ fd, (int) start, strerror(errno));
+ return -1;
+ }
+ if (mprotect(memPtr, length, PROT_READ) < 0) {
+ /* this fails with EACCESS on FAT filesystems, e.g. /sdcard */
+ int err = errno;
+ LOGV("mprotect(%p, %d, PROT_READ) failed: %s\n",
+ memPtr, length, strerror(err));
+ LOGD("mprotect(RO) failed (%d), file will remain read-write\n", err);
+ }
+
+ pMap->baseAddr = pMap->addr = memPtr;
+ pMap->baseLength = pMap->length = length;
+
+ return 0;
+#else
+ sysFakeMapFile(fd, pMap);
+#endif
+}
+
+#ifndef HAVE_POSIX_FILEMAP
+int sysFakeMapFile(int fd, MemMapping* pMap)
+{
/* No MMAP, just fake it by copying the bits.
For Win32 we could use MapViewOfFile if really necessary
(see libs/utils/FileMap.cpp).
@@ -208,8 +262,8 @@
pMap->baseLength = pMap->length = length;
return 0;
-#endif
}
+#endif
/*
* Map part of a file (from fd's current offset) into a shared, read-only
@@ -270,6 +324,45 @@
}
/*
+ * Change the access rights on one or more pages to read-only or read-write.
+ *
+ * Returns 0 on success.
+ */
+int sysChangeMapAccess(void* addr, size_t length, int wantReadWrite,
+ MemMapping* pMap)
+{
+ /*
+ * Verify that "addr" is part of this mapping file.
+ */
+ if (addr < pMap->baseAddr ||
+ (u1*)addr >= (u1*)pMap->baseAddr + pMap->baseLength)
+ {
+ LOGE("Attempted to change %p; map is %p - %p\n",
+ addr, pMap->baseAddr, (u1*)pMap->baseAddr + pMap->baseLength);
+ return -1;
+ }
+
+ /*
+ * Align "addr" to a page boundary and adjust "length" appropriately.
+ * (The address must be page-aligned, the length doesn't need to be,
+ * but we do need to ensure we cover the same range.)
+ */
+ u1* alignAddr = (u1*) ((int) addr & ~(SYSTEM_PAGE_SIZE-1));
+ size_t alignLength = length + ((u1*) addr - alignAddr);
+
+ //LOGI("%p/%zd --> %p/%zd\n", addr, length, alignAddr, alignLength);
+ int prot = wantReadWrite ? (PROT_READ|PROT_WRITE) : (PROT_READ);
+ if (mprotect(alignAddr, alignLength, prot) != 0) {
+ int err = errno;
+ LOGV("mprotect (%p,%zd,%d) failed: %s\n",
+ alignAddr, alignLength, prot, strerror(errno));
+ return (errno != 0) ? errno : -1;
+ }
+
+ return 0;
+}
+
+/*
* Release a memory mapping.
*/
void sysReleaseShmem(MemMapping* pMap)
diff --git a/libdex/SysUtil.h b/libdex/SysUtil.h
index 8b80503..b300a7b 100644
--- a/libdex/SysUtil.h
+++ b/libdex/SysUtil.h
@@ -23,6 +23,17 @@
#include <sys/types.h>
/*
+ * System page size. Normally you're expected to get this from
+ * sysconf(_SC_PAGESIZE) or some system-specific define (usually PAGESIZE
+ * or PAGE_SIZE). If we use a simple #define the compiler can generate
+ * appropriate masks directly, so we define it here and verify it as the
+ * VM is starting up.
+ *
+ * Must be a power of 2.
+ */
+#define SYSTEM_PAGE_SIZE 4096
+
+/*
* Use this to keep track of mapped segments.
*/
typedef struct MemMapping {
@@ -55,10 +66,19 @@
*
* On success, "pMap" is filled in, and zero is returned.
*/
-int sysMapFileInShmem(int fd, MemMapping* pMap);
+int sysMapFileInShmemReadOnly(int fd, MemMapping* pMap);
/*
- * Like sysMapFileInShmem, but on only part of a file.
+ * Map a file (from fd's current offset) into a shared, read-only memory
+ * segment that can be made writable. (In some cases, such as when
+ * mapping a file on a FAT filesystem, the result may be fully writable.)
+ *
+ * On success, "pMap" is filled in, and zero is returned.
+ */
+int sysMapFileInShmemWritableReadOnly(int fd, MemMapping* pMap);
+
+/*
+ * Like sysMapFileInShmemReadOnly, but on only part of a file.
*/
int sysMapFileSegmentInShmem(int fd, off_t start, long length,
MemMapping* pMap);
@@ -71,6 +91,15 @@
int sysCreatePrivateMap(size_t length, MemMapping* pMap);
/*
+ * Change the access rights on one or more pages. If "wantReadWrite" is
+ * zero, the pages will be made read-only; otherwise they will be read-write.
+ *
+ * Returns 0 on success.
+ */
+int sysChangeMapAccess(void* addr, size_t length, int wantReadWrite,
+ MemMapping* pmap);
+
+/*
* Release the pages associated with a shared memory segment.
*
* This does not free "pMap"; it just releases the memory.
diff --git a/libdex/ZipArchive.c b/libdex/ZipArchive.c
index 3f88e7d..7c7e18e 100644
--- a/libdex/ZipArchive.c
+++ b/libdex/ZipArchive.c
@@ -310,7 +310,7 @@
pArchive->mFd = fd;
- if (sysMapFileInShmem(pArchive->mFd, &map) != 0) {
+ if (sysMapFileInShmemReadOnly(pArchive->mFd, &map) != 0) {
err = -1;
LOGW("Map of '%s' failed\n", debugFileName);
goto bail;
diff --git a/tests/071-dexfile/expected.txt b/tests/071-dexfile/expected.txt
index 8aa4061..b7af75e 100644
--- a/tests/071-dexfile/expected.txt
+++ b/tests/071-dexfile/expected.txt
@@ -1,4 +1,3 @@
-My path is: test-ex.jar
Constructing another
Got expected ULE
done
diff --git a/tests/run-all-tests b/tests/run-all-tests
index fd61e03..ac86e1e 100755
--- a/tests/run-all-tests
+++ b/tests/run-all-tests
@@ -43,6 +43,9 @@
elif [ "x$1" = "x--reference" ]; then
run_args="${run_args} --reference"
shift
+ elif [ "x$1" = "x--jit" ]; then
+ run_args="${run_args} --jit"
+ shift
elif [ "x$1" = "x--fast" ]; then
run_args="${run_args} --fast"
shift
diff --git a/vm/Debugger.c b/vm/Debugger.c
index eec8176..97e7b00 100644
--- a/vm/Debugger.c
+++ b/vm/Debugger.c
@@ -97,6 +97,9 @@
*/
bool dvmDebuggerStartup(void)
{
+ if (!dvmBreakpointStartup())
+ return false;
+
gDvm.dbgRegistry = dvmHashTableCreate(1000, NULL);
return (gDvm.dbgRegistry != NULL);
}
@@ -108,6 +111,7 @@
{
dvmHashTableFree(gDvm.dbgRegistry);
gDvm.dbgRegistry = NULL;
+ dvmBreakpointShutdown();
}
diff --git a/vm/Debugger.h b/vm/Debugger.h
index 189e2be..eb9c439 100644
--- a/vm/Debugger.h
+++ b/vm/Debugger.h
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
/*
* Dalvik-specific side of debugger support. (The JDWP code is intended to
* be relatively generic.)
@@ -32,7 +33,7 @@
struct Thread;
/*
- * used by StepControl to track a set of addresses associated with
+ * Used by StepControl to track a set of addresses associated with
* a single line.
*/
typedef struct AddressSet {
diff --git a/vm/DvmDex.c b/vm/DvmDex.c
index 6740632..258d768 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.
*
@@ -128,7 +130,7 @@
goto bail;
}
- if (sysMapFileInShmem(fd, &memMap) != 0) {
+ if (sysMapFileInShmemWritableReadOnly(fd, &memMap) != 0) {
LOGE("Unable to map file\n");
goto bail;
}
@@ -217,3 +219,73 @@
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) {
+ LOGD("NOTE: DEX page access change (->RW) failed\n");
+ /* expected on files mounted from FAT; keep going (may crash) */
+ }
+
+ *addr = newVal;
+
+ if (sysChangeMapAccess(addr, 1, false, &pDvmDex->memMap) != 0) {
+ LOGD("NOTE: DEX page access change (->RO) failed\n");
+ /* expected on files mounted from FAT; 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) {
+ LOGD("NOTE: DEX page access change (->RW) failed\n");
+ /* expected on files mounted from FAT; keep going (may crash) */
+ }
+
+ *addr = newVal;
+
+ if (sysChangeMapAccess(addr, 2, false, &pDvmDex->memMap) != 0) {
+ LOGD("NOTE: DEX page access change (->RO) failed\n");
+ /* expected on files mounted from FAT; keep going */
+ }
+
+ return true;
+}
+
diff --git a/vm/DvmDex.h b/vm/DvmDex.h
index be31af3..9f3903a 100644
--- a/vm/DvmDex.h
+++ b/vm/DvmDex.h
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
/*
* The VM wraps some additional data structures around the DexFile. These
* are defined here.
@@ -81,6 +82,21 @@
void dvmDexFileFree(DvmDex* pDvmDex);
+/*
+ * Change the 1- or 2-byte value at the specified address to a new value. If
+ * the location already has the new value, do nothing.
+ *
+ * This does not make any synchronization guarantees. The caller must
+ * ensure exclusivity vs. other callers.
+ *
+ * For the 2-byte call, the pointer should have 16-bit alignment.
+ *
+ * Returns "true" on success.
+ */
+bool dvmDexChangeDex1(DvmDex* pDvmDex, u1* addr, u1 newVal);
+bool dvmDexChangeDex2(DvmDex* pDvmDex, u2* addr, u2 newVal);
+
+
#if DVM_RESOLVER_CACHE == DVM_RC_DISABLED
/* 1:1 mapping */
diff --git a/vm/Globals.h b/vm/Globals.h
index 152008e..0a983c3 100644
--- a/vm/Globals.h
+++ b/vm/Globals.h
@@ -34,8 +34,9 @@
#define MAX_BREAKPOINTS 20 /* used for a debugger optimization */
-// fwd
-typedef struct GcHeap GcHeap; /* heap internal structure */
+/* private structures */
+typedef struct GcHeap GcHeap;
+typedef struct BreakpointSet BreakpointSet;
/*
* One of these for each -ea/-da/-esa/-dsa on the command line.
@@ -528,12 +529,9 @@
HashTable* dbgRegistry;
/*
- * Breakpoint optimization table. This is global and NOT explicitly
- * synchronized, but all operations that modify the table are made
- * from relatively-synchronized functions. False-positives are
- * possible, false-negatives (i.e. missing a breakpoint) should not be.
+ * Debugger breakpoint table.
*/
- const u2* debugBreakAddr[MAX_BREAKPOINTS];
+ BreakpointSet* breakpointSet;
/*
* Single-step control struct. We currently only allow one thread to
diff --git a/vm/Init.c b/vm/Init.c
index f77dba6..66c1ad1 100644
--- a/vm/Init.c
+++ b/vm/Init.c
@@ -1135,6 +1135,13 @@
if (!gDvm.reduceSignals)
blockSignals();
+ /* verify system page size */
+ if (sysconf(_SC_PAGESIZE) != SYSTEM_PAGE_SIZE) {
+ LOGE("ERROR: expected page size %d, got %d\n",
+ SYSTEM_PAGE_SIZE, (int) sysconf(_SC_PAGESIZE));
+ goto fail;
+ }
+
/* mterp setup */
LOGV("Using executionMode %d\n", gDvm.executionMode);
dvmCheckAsmConstants();
diff --git a/vm/Jni.c b/vm/Jni.c
index 1dcea10..1029cdd 100644
--- a/vm/Jni.c
+++ b/vm/Jni.c
@@ -441,6 +441,7 @@
#else
dvmClearReferenceTable(&gDvm.jniGlobalRefTable);
#endif
+ dvmClearReferenceTable(&gDvm.jniPinRefTable);
}
diff --git a/vm/LinearAlloc.c b/vm/LinearAlloc.c
index 8a18af3..a8ed3ab 100644
--- a/vm/LinearAlloc.c
+++ b/vm/LinearAlloc.c
@@ -79,11 +79,6 @@
#define LENGTHFLAG_RW 0x40000000
#define LENGTHFLAG_MASK (~(LENGTHFLAG_FREE|LENGTHFLAG_RW))
-/* in case limits.h doesn't have it; must be a power of 2 */
-#ifndef PAGESIZE
-# define PAGESIZE 4096
-#endif
-
/* fwd */
static void checkAllFree(Object* classLoader);
@@ -130,7 +125,8 @@
* chunk of data will be properly aligned.
*/
assert(BLOCK_ALIGN >= HEADER_EXTRA);
- pHdr->curOffset = pHdr->firstOffset = (BLOCK_ALIGN-HEADER_EXTRA) + PAGESIZE;
+ pHdr->curOffset = pHdr->firstOffset =
+ (BLOCK_ALIGN-HEADER_EXTRA) + SYSTEM_PAGE_SIZE;
pHdr->mapLength = DEFAULT_MAX_LENGTH;
#ifdef USE_ASHMEM
@@ -168,7 +164,7 @@
#endif /*USE_ASHMEM*/
/* region expected to begin on a page boundary */
- assert(((int) pHdr->mapAddr & (PAGESIZE-1)) == 0);
+ assert(((int) pHdr->mapAddr & (SYSTEM_PAGE_SIZE-1)) == 0);
/* the system should initialize newly-mapped memory to zero */
assert(*(u4*) (pHdr->mapAddr + pHdr->curOffset) == 0);
@@ -195,7 +191,7 @@
free(pHdr);
return NULL;
}
- if (mprotect(pHdr->mapAddr + PAGESIZE, PAGESIZE,
+ if (mprotect(pHdr->mapAddr + SYSTEM_PAGE_SIZE, SYSTEM_PAGE_SIZE,
ENFORCE_READ_ONLY ? PROT_READ : PROT_READ|PROT_WRITE) != 0)
{
LOGW("LinearAlloc init mprotect #2 failed: %s\n", strerror(errno));
@@ -205,7 +201,7 @@
if (ENFORCE_READ_ONLY) {
/* allocate the per-page ref count */
- int numPages = (pHdr->mapLength+PAGESIZE-1) / PAGESIZE;
+ int numPages = (pHdr->mapLength+SYSTEM_PAGE_SIZE-1) / SYSTEM_PAGE_SIZE;
pHdr->writeRefCount = calloc(numPages, sizeof(short));
if (pHdr->writeRefCount == NULL) {
free(pHdr);
@@ -332,7 +328,7 @@
* See if we are starting on or have crossed into a new page. If so,
* call mprotect on the page(s) we're about to write to. We have to
* page-align the start address, but don't have to make the length a
- * PAGESIZE multiple (but we do it anyway).
+ * SYSTEM_PAGE_SIZE multiple (but we do it anyway).
*
* Note that "startOffset" is not the last *allocated* byte, but rather
* the offset of the first *unallocated* byte (which we are about to
@@ -341,9 +337,9 @@
* If ENFORCE_READ_ONLY is enabled, we have to call mprotect even if
* we've written to this page before, because it might be read-only.
*/
- lastGoodOff = (startOffset-1) & ~(PAGESIZE-1);
- firstWriteOff = startOffset & ~(PAGESIZE-1);
- lastWriteOff = (nextOffset-1) & ~(PAGESIZE-1);
+ lastGoodOff = (startOffset-1) & ~(SYSTEM_PAGE_SIZE-1);
+ firstWriteOff = startOffset & ~(SYSTEM_PAGE_SIZE-1);
+ lastWriteOff = (nextOffset-1) & ~(SYSTEM_PAGE_SIZE-1);
LOGVV("--- lastGood=0x%04x firstWrite=0x%04x lastWrite=0x%04x\n",
lastGoodOff, firstWriteOff, lastWriteOff);
if (lastGoodOff != lastWriteOff || ENFORCE_READ_ONLY) {
@@ -351,7 +347,7 @@
start = firstWriteOff;
assert(start <= nextOffset);
- len = (lastWriteOff - firstWriteOff) + PAGESIZE;
+ len = (lastWriteOff - firstWriteOff) + SYSTEM_PAGE_SIZE;
LOGVV("--- calling mprotect(start=%d len=%d RW)\n", start, len);
cc = mprotect(pHdr->mapAddr + start, len, PROT_READ | PROT_WRITE);
@@ -367,8 +363,8 @@
if (ENFORCE_READ_ONLY) {
int i, start, end;
- start = firstWriteOff / PAGESIZE;
- end = lastWriteOff / PAGESIZE;
+ start = firstWriteOff / SYSTEM_PAGE_SIZE;
+ end = lastWriteOff / SYSTEM_PAGE_SIZE;
LOGVV("--- marking pages %d-%d RW (alloc %d at %p)\n",
start, end, size, pHdr->mapAddr + startOffset + HEADER_EXTRA);
@@ -465,8 +461,8 @@
u4 len = *pLen & LENGTHFLAG_MASK;
int firstPage, lastPage;
- firstPage = ((u1*)pLen - (u1*)pHdr->mapAddr) / PAGESIZE;
- lastPage = ((u1*)mem - (u1*)pHdr->mapAddr + (len-1)) / PAGESIZE;
+ firstPage = ((u1*)pLen - (u1*)pHdr->mapAddr) / SYSTEM_PAGE_SIZE;
+ lastPage = ((u1*)mem - (u1*)pHdr->mapAddr + (len-1)) / SYSTEM_PAGE_SIZE;
LOGVV("--- updating pages %d-%d (%d)\n", firstPage, lastPage, direction);
int i, cc;
@@ -496,7 +492,8 @@
pHdr->writeRefCount[i]--;
if (pHdr->writeRefCount[i] == 0) {
LOGVV("--- prot page %d RO\n", i);
- cc = mprotect(pHdr->mapAddr + PAGESIZE * i, PAGESIZE, PROT_READ);
+ cc = mprotect(pHdr->mapAddr + SYSTEM_PAGE_SIZE * i,
+ SYSTEM_PAGE_SIZE, PROT_READ);
assert(cc == 0);
}
} else {
@@ -509,8 +506,8 @@
}
if (pHdr->writeRefCount[i] == 0) {
LOGVV("--- prot page %d RW\n", i);
- cc = mprotect(pHdr->mapAddr + PAGESIZE * i, PAGESIZE,
- PROT_READ | PROT_WRITE);
+ cc = mprotect(pHdr->mapAddr + SYSTEM_PAGE_SIZE * i,
+ SYSTEM_PAGE_SIZE, PROT_READ | PROT_WRITE);
assert(cc == 0);
}
pHdr->writeRefCount[i]++;
@@ -616,7 +613,7 @@
& ~(BLOCK_ALIGN-1));
LOGI(" %p (%3d): %clen=%d%s\n", pHdr->mapAddr + off + HEADER_EXTRA,
- (int) ((off + HEADER_EXTRA) / PAGESIZE),
+ (int) ((off + HEADER_EXTRA) / SYSTEM_PAGE_SIZE),
(rawLen & LENGTHFLAG_FREE) != 0 ? '*' : ' ',
rawLen & LENGTHFLAG_MASK,
(rawLen & LENGTHFLAG_RW) != 0 ? " [RW]" : "");
@@ -627,7 +624,7 @@
if (ENFORCE_READ_ONLY) {
LOGI("writeRefCount map:\n");
- int numPages = (pHdr->mapLength+PAGESIZE-1) / PAGESIZE;
+ int numPages = (pHdr->mapLength+SYSTEM_PAGE_SIZE-1) / SYSTEM_PAGE_SIZE;
int zstart = 0;
int i;
diff --git a/vm/LinearAlloc.h b/vm/LinearAlloc.h
index a390ee3..aa33fe1 100644
--- a/vm/LinearAlloc.h
+++ b/vm/LinearAlloc.h
@@ -22,7 +22,6 @@
/*
* If this is set, we create additional data structures and make many
* additional mprotect() calls.
- * (this breaks the debugger because the debugBreakpointCount cannot be updated)
*/
#define ENFORCE_READ_ONLY false
diff --git a/vm/Profile.c b/vm/Profile.c
index 7f39f1a..393440c 100644
--- a/vm/Profile.c
+++ b/vm/Profile.c
@@ -33,9 +33,6 @@
#ifdef HAVE_ANDROID_OS
# define UPDATE_MAGIC_PAGE 1
-# ifndef PAGESIZE
-# define PAGESIZE 4096
-# endif
#endif
/*
@@ -183,7 +180,7 @@
if (fd < 0) {
LOGV("Unable to open /dev/qemu_trace\n");
} else {
- gDvm.emulatorTracePage = mmap(0, PAGESIZE, PROT_READ|PROT_WRITE,
+ gDvm.emulatorTracePage = mmap(0, SYSTEM_PAGE_SIZE, PROT_READ|PROT_WRITE,
MAP_SHARED, fd, 0);
close(fd);
if (gDvm.emulatorTracePage == MAP_FAILED) {
@@ -207,7 +204,7 @@
{
#ifdef UPDATE_MAGIC_PAGE
if (gDvm.emulatorTracePage != NULL)
- munmap(gDvm.emulatorTracePage, PAGESIZE);
+ munmap(gDvm.emulatorTracePage, SYSTEM_PAGE_SIZE);
#endif
free(gDvm.executedInstrCounts);
}
diff --git a/vm/alloc/HeapBitmap.c b/vm/alloc/HeapBitmap.c
index 2c75678..778fd87 100644
--- a/vm/alloc/HeapBitmap.c
+++ b/vm/alloc/HeapBitmap.c
@@ -23,11 +23,8 @@
#define HB_ASHMEM_NAME "dalvik-heap-bitmap"
-#ifndef PAGE_SIZE
-#define PAGE_SIZE 4096
-#endif
#define ALIGN_UP_TO_PAGE_SIZE(p) \
- (((size_t)(p) + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1))
+ (((size_t)(p) + (SYSTEM_PAGE_SIZE - 1)) & ~(SYSTEM_PAGE_SIZE - 1))
#define LIKELY(exp) (__builtin_expect((exp) != 0, true))
#define UNLIKELY(exp) (__builtin_expect((exp) != 0, false))
diff --git a/vm/alloc/HeapSource.c b/vm/alloc/HeapSource.c
index 830e5d7..e792dca 100644
--- a/vm/alloc/HeapSource.c
+++ b/vm/alloc/HeapSource.c
@@ -32,13 +32,10 @@
static void snapIdealFootprint(void);
static void setIdealFootprint(size_t max);
-#ifndef PAGE_SIZE
-#define PAGE_SIZE 4096
-#endif
#define ALIGN_UP_TO_PAGE_SIZE(p) \
- (((size_t)(p) + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1))
+ (((size_t)(p) + (SYSTEM_PAGE_SIZE - 1)) & ~(SYSTEM_PAGE_SIZE - 1))
#define ALIGN_DOWN_TO_PAGE_SIZE(p) \
- ((size_t)(p) & ~(PAGE_SIZE - 1))
+ ((size_t)(p) & ~(SYSTEM_PAGE_SIZE - 1))
#define HEAP_UTILIZATION_MAX 1024
#define DEFAULT_HEAP_UTILIZATION 512 // Range 1..HEAP_UTILIZATION_MAX
@@ -1286,7 +1283,7 @@
* We also align the end address.
*/
start = (void *)ALIGN_UP_TO_PAGE_SIZE(start);
- end = (void *)((size_t)end & ~(PAGE_SIZE - 1));
+ end = (void *)((size_t)end & ~(SYSTEM_PAGE_SIZE - 1));
if (start < end) {
size_t length = (char *)end - (char *)start;
madvise(start, length, MADV_DONTNEED);
diff --git a/vm/alloc/MarkSweep.c b/vm/alloc/MarkSweep.c
index 634cfda..fda4e6d 100644
--- a/vm/alloc/MarkSweep.c
+++ b/vm/alloc/MarkSweep.c
@@ -68,11 +68,8 @@
#define LOGV_SWEEP(...) LOGVV_GC("SWEEP: " __VA_ARGS__)
#define LOGV_REF(...) LOGVV_GC("REF: " __VA_ARGS__)
-#ifndef PAGE_SIZE
-#define PAGE_SIZE 4096
-#endif
#define ALIGN_UP_TO_PAGE_SIZE(p) \
- (((size_t)(p) + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1))
+ (((size_t)(p) + (SYSTEM_PAGE_SIZE - 1)) & ~(SYSTEM_PAGE_SIZE - 1))
/* Do not cast the result of this to a boolean; the only set bit
* may be > 1<<8.
diff --git a/vm/analysis/CodeVerify.c b/vm/analysis/CodeVerify.c
index f945f23..911775c 100644
--- a/vm/analysis/CodeVerify.c
+++ b/vm/analysis/CodeVerify.c
@@ -2926,6 +2926,9 @@
* receive a "nop". The instruction's length will be left unchanged
* in "insnFlags".
*
+ * The verifier explicitly locks out breakpoint activity, so there should
+ * be no clashes with the debugger.
+ *
* IMPORTANT: this may replace meth->insns with a pointer to a new copy of
* the instructions.
*
@@ -2939,7 +2942,7 @@
u2 oldInsn = *oldInsns;
bool result = false;
- dvmMakeCodeReadWrite(meth);
+ //dvmMakeCodeReadWrite(meth);
//LOGD(" was 0x%04x\n", oldInsn);
u2* newInsns = (u2*) meth->insns + insnIdx;
@@ -3018,7 +3021,8 @@
/* nothing to do */
break;
case 3:
- newInsns[2] = OP_NOP;
+ dvmDexChangeDex2(meth->clazz->pDvmDex, newInsns+2, OP_NOP);
+ //newInsns[2] = OP_NOP;
break;
default:
/* whoops */
@@ -3028,13 +3032,15 @@
}
/* encode the opcode, with the failure code in the high byte */
- newInsns[0] = OP_THROW_VERIFICATION_ERROR |
+ u2 newVal = OP_THROW_VERIFICATION_ERROR |
(failure << 8) | (refType << (8 + kVerifyErrorRefTypeShift));
+ //newInsns[0] = newVal;
+ dvmDexChangeDex2(meth->clazz->pDvmDex, newInsns, newVal);
result = true;
bail:
- dvmMakeCodeReadOnly(meth);
+ //dvmMakeCodeReadOnly(meth);
return result;
}
@@ -5420,7 +5426,7 @@
failure = VERIFY_ERROR_GENERIC;
break;
- /* these should never appear */
+ /* these should never appear during verification */
case OP_UNUSED_3E:
case OP_UNUSED_3F:
case OP_UNUSED_40:
@@ -5439,7 +5445,7 @@
case OP_UNUSED_E9:
case OP_UNUSED_EA:
case OP_UNUSED_EB:
- case OP_UNUSED_EC:
+ case OP_BREAKPOINT:
case OP_UNUSED_EF:
case OP_UNUSED_F1:
case OP_UNUSED_FC:
diff --git a/vm/analysis/DexVerify.c b/vm/analysis/DexVerify.c
index 10251db..c490691 100644
--- a/vm/analysis/DexVerify.c
+++ b/vm/analysis/DexVerify.c
@@ -151,6 +151,67 @@
}
/*
+ * Temporarily "undo" any breakpoints found in this method. There is no risk
+ * of confusing the interpreter, because unverified code cannot be executed.
+ *
+ * Breakpoints can be set after a class is loaded but before it has been
+ * verified.
+ *
+ * The "breakpoint" opcode can replace any other opcode, leaving no
+ * indication of the original instruction's width or purpose in the
+ * instruction stream. We either have to quietly undo the breakpoints
+ * before verification, or look up the original opcode whenever we need it.
+ * The latter is more efficient since we only slow down on code that
+ * actually has breakpoints, but it requires explicit handling in every
+ * function that examines the instruction stream.
+ *
+ * We need to ensure that the debugger doesn't insert any additional
+ * breakpoints while we work. This either requires holding a lock on the
+ * breakpoint set throughout the verification of this method, or adding a
+ * "do not touch anything on these pages" list to the set. Either way,
+ * the caller of this method must ensure that it calls "redo" to release
+ * state.
+ *
+ * A debugger could connect while we work, so we return without doing
+ * anything if a debugger doesn't happen to be connected now. We can only
+ * avoid doing work if the debugger thread isn't running (dexopt, zygote,
+ * or debugging not configured).
+ *
+ * Returns "false" if we did nothing, "true" if we did stuff (and, hence,
+ * need to call "redo" at some point).
+ */
+static bool undoBreakpoints(Method* meth)
+{
+#ifdef WITH_DEBUGGER
+ if (gDvm.optimizing || gDvm.zygote || !gDvm.jdwpConfigured)
+ return false;
+ dvmUndoBreakpoints(meth);
+ return true;
+#else
+ return false;
+#endif
+}
+
+/*
+ * Restore any breakpoints we undid previously. Also has to update the
+ * stored "original opcode" value for any instruction that we replaced
+ * with a throw-verification-error op.
+ */
+static void redoBreakpoints(Method* meth)
+{
+#ifdef WITH_DEBUGGER
+ if (gDvm.optimizing || gDvm.zygote || !gDvm.jdwpConfigured) {
+ /* should not be here */
+ assert(false);
+ return;
+ }
+ dvmRedoBreakpoints(meth);
+#else
+ assert(false);
+#endif
+}
+
+/*
* Perform verification on a single method.
*
* We do this in three passes:
@@ -177,6 +238,9 @@
UninitInstanceMap* uninitMap = NULL;
InsnFlags* insnFlags = NULL;
int i, newInstanceCount;
+ bool undidBreakpoints;
+
+ undidBreakpoints = undoBreakpoints(meth);
/*
* If there aren't any instructions, make sure that's expected, then
@@ -258,6 +322,8 @@
result = true;
bail:
+ if (undidBreakpoints)
+ redoBreakpoints(meth);
dvmFreeUninitInstanceMap(uninitMap);
free(insnFlags);
return result;
diff --git a/vm/analysis/RegisterMap.c b/vm/analysis/RegisterMap.c
index d1f2600..bc314a2 100644
--- a/vm/analysis/RegisterMap.c
+++ b/vm/analysis/RegisterMap.c
@@ -3034,7 +3034,7 @@
case OP_UNUSED_E9:
case OP_UNUSED_EA:
case OP_UNUSED_EB:
- case OP_UNUSED_EC:
+ case OP_BREAKPOINT:
case OP_UNUSED_ED:
case OP_UNUSED_EF:
case OP_UNUSED_F1:
diff --git a/vm/compiler/Dataflow.c b/vm/compiler/Dataflow.c
index 8525540..fc5ecb9 100644
--- a/vm/compiler/Dataflow.c
+++ b/vm/compiler/Dataflow.c
@@ -737,7 +737,7 @@
// EB OP_UNUSED_EB
DF_NOP,
- // EC OP_UNUSED_EC
+ // EC OP_BREAKPOINT
DF_NOP,
// ED OP_THROW_VERIFICATION_ERROR
diff --git a/vm/compiler/codegen/arm/Codegen.c b/vm/compiler/codegen/arm/Codegen.c
index 5dea431..7ca905c 100644
--- a/vm/compiler/codegen/arm/Codegen.c
+++ b/vm/compiler/codegen/arm/Codegen.c
@@ -2316,7 +2316,7 @@
{
OpCode dalvikOpCode = mir->dalvikInsn.opCode;
if (((dalvikOpCode >= OP_UNUSED_3E) && (dalvikOpCode <= OP_UNUSED_43)) ||
- ((dalvikOpCode >= OP_UNUSED_E3) && (dalvikOpCode <= OP_UNUSED_EC))) {
+ ((dalvikOpCode >= OP_UNUSED_E3) && (dalvikOpCode <= OP_UNUSED_EB))) {
LOGE("Codegen: got unused opcode 0x%x\n",dalvikOpCode);
return true;
}
diff --git a/vm/compiler/template/armv5te/TEMPLATE_STRING_COMPARETO.S b/vm/compiler/template/armv5te/TEMPLATE_STRING_COMPARETO.S
index 8a07f14..75b6a90 100644
--- a/vm/compiler/template/armv5te/TEMPLATE_STRING_COMPARETO.S
+++ b/vm/compiler/template/armv5te/TEMPLATE_STRING_COMPARETO.S
@@ -59,8 +59,26 @@
* r3, r4, r7, r8, r9, r12 available for loading string data
*/
- sub r10, #3
+ subs r10, #2
+ blt do_remainder2
+
+ /*
+ * Unroll the first two checks so we can quickly catch early mismatch
+ * on long strings (but preserve incoming alignment)
+ */
+
+ ldrh r3, [r2, #2]!
+ ldrh r4, [r1, #2]!
+ ldrh r7, [r2, #2]!
+ ldrh r8, [r1, #2]!
+ subs r0, r3, r4
+ subeqs r0, r7, r8
+ bxne lr
+ cmp r10, #28
+ bgt do_memcmp16
+ subs r10, #3
blt do_remainder
+
loopback_triple:
ldrh r3, [r2, #2]!
ldrh r4, [r1, #2]!
@@ -91,3 +109,26 @@
mov r0, r11
bx lr
+do_remainder2:
+ adds r10, #2
+ bne loopback_single
+ mov r0, r11
+ bx lr
+
+ /* Long string case */
+do_memcmp16:
+ mov r4, lr
+ ldr lr, .Lmemcmp16
+ mov r7, r11
+ add r0, r2, #2
+ add r1, r1, #2
+ mov r2, r10
+ blx lr
+ cmp r0, #0
+ bxne r4
+ mov r0, r7
+ bx r4
+
+.Lmemcmp16:
+ .word __memcmp16
+
diff --git a/vm/compiler/template/out/CompilerTemplateAsm-armv5te-vfp.S b/vm/compiler/template/out/CompilerTemplateAsm-armv5te-vfp.S
index e04af58..c53af2b 100644
--- a/vm/compiler/template/out/CompilerTemplateAsm-armv5te-vfp.S
+++ b/vm/compiler/template/out/CompilerTemplateAsm-armv5te-vfp.S
@@ -1147,8 +1147,26 @@
* r3, r4, r7, r8, r9, r12 available for loading string data
*/
- sub r10, #3
+ subs r10, #2
+ blt do_remainder2
+
+ /*
+ * Unroll the first two checks so we can quickly catch early mismatch
+ * on long strings (but preserve incoming alignment)
+ */
+
+ ldrh r3, [r2, #2]!
+ ldrh r4, [r1, #2]!
+ ldrh r7, [r2, #2]!
+ ldrh r8, [r1, #2]!
+ subs r0, r3, r4
+ subeqs r0, r7, r8
+ bxne lr
+ cmp r10, #28
+ bgt do_memcmp16
+ subs r10, #3
blt do_remainder
+
loopback_triple:
ldrh r3, [r2, #2]!
ldrh r4, [r1, #2]!
@@ -1179,6 +1197,29 @@
mov r0, r11
bx lr
+do_remainder2:
+ adds r10, #2
+ bne loopback_single
+ mov r0, r11
+ bx lr
+
+ /* Long string case */
+do_memcmp16:
+ mov r4, lr
+ ldr lr, .Lmemcmp16
+ mov r7, r11
+ add r0, r2, #2
+ add r1, r1, #2
+ mov r2, r10
+ blx lr
+ cmp r0, #0
+ bxne r4
+ mov r0, r7
+ bx r4
+
+.Lmemcmp16:
+ .word __memcmp16
+
/* ------------------------------ */
.balign 4
diff --git a/vm/compiler/template/out/CompilerTemplateAsm-armv5te.S b/vm/compiler/template/out/CompilerTemplateAsm-armv5te.S
index eff456a..11689f2 100644
--- a/vm/compiler/template/out/CompilerTemplateAsm-armv5te.S
+++ b/vm/compiler/template/out/CompilerTemplateAsm-armv5te.S
@@ -872,8 +872,26 @@
* r3, r4, r7, r8, r9, r12 available for loading string data
*/
- sub r10, #3
+ subs r10, #2
+ blt do_remainder2
+
+ /*
+ * Unroll the first two checks so we can quickly catch early mismatch
+ * on long strings (but preserve incoming alignment)
+ */
+
+ ldrh r3, [r2, #2]!
+ ldrh r4, [r1, #2]!
+ ldrh r7, [r2, #2]!
+ ldrh r8, [r1, #2]!
+ subs r0, r3, r4
+ subeqs r0, r7, r8
+ bxne lr
+ cmp r10, #28
+ bgt do_memcmp16
+ subs r10, #3
blt do_remainder
+
loopback_triple:
ldrh r3, [r2, #2]!
ldrh r4, [r1, #2]!
@@ -904,6 +922,29 @@
mov r0, r11
bx lr
+do_remainder2:
+ adds r10, #2
+ bne loopback_single
+ mov r0, r11
+ bx lr
+
+ /* Long string case */
+do_memcmp16:
+ mov r4, lr
+ ldr lr, .Lmemcmp16
+ mov r7, r11
+ add r0, r2, #2
+ add r1, r1, #2
+ mov r2, r10
+ blx lr
+ cmp r0, #0
+ bxne r4
+ mov r0, r7
+ bx r4
+
+.Lmemcmp16:
+ .word __memcmp16
+
/* ------------------------------ */
.balign 4
diff --git a/vm/compiler/template/out/CompilerTemplateAsm-armv7-a.S b/vm/compiler/template/out/CompilerTemplateAsm-armv7-a.S
index 5e331ff..8e0e723 100644
--- a/vm/compiler/template/out/CompilerTemplateAsm-armv7-a.S
+++ b/vm/compiler/template/out/CompilerTemplateAsm-armv7-a.S
@@ -1147,8 +1147,26 @@
* r3, r4, r7, r8, r9, r12 available for loading string data
*/
- sub r10, #3
+ subs r10, #2
+ blt do_remainder2
+
+ /*
+ * Unroll the first two checks so we can quickly catch early mismatch
+ * on long strings (but preserve incoming alignment)
+ */
+
+ ldrh r3, [r2, #2]!
+ ldrh r4, [r1, #2]!
+ ldrh r7, [r2, #2]!
+ ldrh r8, [r1, #2]!
+ subs r0, r3, r4
+ subeqs r0, r7, r8
+ bxne lr
+ cmp r10, #28
+ bgt do_memcmp16
+ subs r10, #3
blt do_remainder
+
loopback_triple:
ldrh r3, [r2, #2]!
ldrh r4, [r1, #2]!
@@ -1179,6 +1197,29 @@
mov r0, r11
bx lr
+do_remainder2:
+ adds r10, #2
+ bne loopback_single
+ mov r0, r11
+ bx lr
+
+ /* Long string case */
+do_memcmp16:
+ mov r4, lr
+ ldr lr, .Lmemcmp16
+ mov r7, r11
+ add r0, r2, #2
+ add r1, r1, #2
+ mov r2, r10
+ blx lr
+ cmp r0, #0
+ bxne r4
+ mov r0, r7
+ bx r4
+
+.Lmemcmp16:
+ .word __memcmp16
+
/* ------------------------------ */
.balign 4
diff --git a/vm/interp/Interp.c b/vm/interp/Interp.c
index 658b771..10b9bb1 100644
--- a/vm/interp/Interp.c
+++ b/vm/interp/Interp.c
@@ -35,16 +35,322 @@
* ===========================================================================
*/
+// fwd
+static BreakpointSet* dvmBreakpointSetAlloc(void);
+static void dvmBreakpointSetFree(BreakpointSet* pSet);
+
/*
- * Initialize the breakpoint address lookup table when the debugger attaches.
+ * Initialize global breakpoint structures.
+ */
+bool dvmBreakpointStartup(void)
+{
+#ifdef WITH_DEBUGGER
+ gDvm.breakpointSet = dvmBreakpointSetAlloc();
+ return (gDvm.breakpointSet != NULL);
+#else
+ return true;
+#endif
+}
+
+/*
+ * Free resources.
+ */
+void dvmBreakpointShutdown(void)
+{
+#ifdef WITH_DEBUGGER
+ dvmBreakpointSetFree(gDvm.breakpointSet);
+#endif
+}
+
+
+#ifdef WITH_DEBUGGER
+/*
+ * This represents a breakpoint inserted in the instruction stream.
*
- * This shouldn't be necessary -- the global area is initially zeroed out,
- * and the events should be cleaning up after themselves.
+ * The debugger may ask us to create the same breakpoint multiple times.
+ * We only remove the breakpoint when the last instance is cleared.
+ */
+typedef struct {
+ u2* addr; /* absolute memory address */
+ u1 originalOpCode; /* original 8-bit opcode value */
+ int setCount; /* #of times this breakpoint was set */
+} Breakpoint;
+
+/*
+ * Set of breakpoints.
+ */
+struct BreakpointSet {
+ /* grab lock before reading or writing anything else in here */
+ pthread_mutex_t lock;
+
+ /* vector of breakpoint structures */
+ int alloc;
+ int count;
+ Breakpoint* breakpoints;
+};
+
+/*
+ * Initialize a BreakpointSet. Initially empty.
+ */
+static BreakpointSet* dvmBreakpointSetAlloc(void)
+{
+ BreakpointSet* pSet = (BreakpointSet*) calloc(1, sizeof(*pSet));
+
+ dvmInitMutex(&pSet->lock);
+ /* leave the rest zeroed -- will alloc on first use */
+
+ return pSet;
+}
+
+/*
+ * Free storage associated with a BreakpointSet.
+ */
+static void dvmBreakpointSetFree(BreakpointSet* pSet)
+{
+ if (pSet == NULL)
+ return;
+
+ free(pSet->breakpoints);
+ free(pSet);
+}
+
+/*
+ * Lock the breakpoint set.
+ */
+static void dvmBreakpointSetLock(BreakpointSet* pSet)
+{
+ dvmLockMutex(&pSet->lock);
+}
+
+/*
+ * Unlock the breakpoint set.
+ */
+static void dvmBreakpointSetUnlock(BreakpointSet* pSet)
+{
+ dvmUnlockMutex(&pSet->lock);
+}
+
+/*
+ * Return the #of breakpoints.
+ */
+static int dvmBreakpointSetCount(const BreakpointSet* pSet)
+{
+ return pSet->count;
+}
+
+/*
+ * See if we already have an entry for this address.
+ *
+ * The BreakpointSet's lock must be acquired before calling here.
+ *
+ * Returns the index of the breakpoint entry, or -1 if not found.
+ */
+static int dvmBreakpointSetFind(const BreakpointSet* pSet, const u2* addr)
+{
+ int i;
+
+ for (i = 0; i < pSet->count; i++) {
+ Breakpoint* pBreak = &pSet->breakpoints[i];
+ if (pBreak->addr == addr)
+ return i;
+ }
+
+ return -1;
+}
+
+/*
+ * Retrieve the opcode that was originally at the specified location.
+ *
+ * The BreakpointSet's lock must be acquired before calling here.
+ *
+ * Returns "true" with the opcode in *pOrig on success.
+ */
+static bool dvmBreakpointSetOriginalOpCode(const BreakpointSet* pSet,
+ const u2* addr, u1* pOrig)
+{
+ int idx = dvmBreakpointSetFind(pSet, addr);
+ if (idx < 0)
+ return false;
+
+ *pOrig = pSet->breakpoints[idx].originalOpCode;
+ return true;
+}
+
+/*
+ * Add a breakpoint at a specific address. If the address is already
+ * present in the table, this just increments the count.
+ *
+ * For a new entry, this will extract and preserve the current opcode from
+ * the instruction stream, and replace it with a breakpoint opcode.
+ *
+ * The BreakpointSet's lock must be acquired before calling here.
+ *
+ * Returns "true" on success.
+ */
+static bool dvmBreakpointSetAdd(BreakpointSet* pSet, Method* method,
+ unsigned int instrOffset)
+{
+ const int kBreakpointGrowth = 10;
+ const u2* addr = method->insns + instrOffset;
+ int idx = dvmBreakpointSetFind(pSet, addr);
+ Breakpoint* pBreak;
+
+ if (idx < 0) {
+ if (pSet->count == pSet->alloc) {
+ int newSize = pSet->alloc + kBreakpointGrowth;
+ Breakpoint* newVec;
+
+ LOGV("+++ increasing breakpoint set size to %d\n", newSize);
+
+ /* pSet->breakpoints will be NULL on first entry */
+ newVec = realloc(pSet->breakpoints, newSize * sizeof(Breakpoint));
+ if (newVec == NULL)
+ return false;
+
+ pSet->breakpoints = newVec;
+ pSet->alloc = newSize;
+ }
+
+ pBreak = &pSet->breakpoints[pSet->count++];
+ pBreak->addr = (u2*)addr;
+ pBreak->originalOpCode = *(u1*)addr;
+ pBreak->setCount = 1;
+
+ /*
+ * Change the opcode. We must ensure that the BreakpointSet
+ * updates happen before we change the opcode.
+ */
+ MEM_BARRIER();
+ assert(*(u1*)addr != OP_BREAKPOINT);
+ dvmDexChangeDex1(method->clazz->pDvmDex, (u1*)addr, OP_BREAKPOINT);
+ } else {
+ pBreak = &pSet->breakpoints[idx];
+ pBreak->setCount++;
+
+ /* verify instruction stream has break op */
+ assert(*(u1*)addr == OP_BREAKPOINT);
+ }
+
+ return true;
+}
+
+/*
+ * Remove one instance of the specified breakpoint. When the count
+ * reaches zero, the entry is removed from the table, and the original
+ * opcode is restored.
+ *
+ * The BreakpointSet's lock must be acquired before calling here.
+ */
+static void dvmBreakpointSetRemove(BreakpointSet* pSet, Method* method,
+ unsigned int instrOffset)
+{
+ const u2* addr = method->insns + instrOffset;
+ int idx = dvmBreakpointSetFind(pSet, addr);
+
+ if (idx < 0) {
+ /* breakpoint not found in set -- unexpected */
+ if (*(u1*)addr == OP_BREAKPOINT) {
+ LOGE("Unable to restore breakpoint opcode (%s.%s +%u)\n",
+ method->clazz->descriptor, method->name, instrOffset);
+ dvmAbort();
+ } else {
+ LOGW("Breakpoint was already restored? (%s.%s +%u)\n",
+ method->clazz->descriptor, method->name, instrOffset);
+ }
+ } else {
+ Breakpoint* pBreak = &pSet->breakpoints[idx];
+ if (pBreak->setCount == 1) {
+ /*
+ * Must restore opcode before removing set entry.
+ */
+ dvmDexChangeDex1(method->clazz->pDvmDex, (u1*)addr,
+ pBreak->originalOpCode);
+ MEM_BARRIER();
+
+ if (idx != pSet->count-1) {
+ /* shift down */
+ memmove(&pSet->breakpoints[idx], &pSet->breakpoints[idx+1],
+ (pSet->count-1 - idx) * sizeof(pSet->breakpoints[0]));
+ }
+ pSet->count--;
+ pSet->breakpoints[pSet->count].addr = (u2*) 0xdecadead; // debug
+ } else {
+ pBreak->setCount--;
+ assert(pBreak->setCount > 0);
+ }
+ }
+}
+
+/*
+ * Restore the original opcode on any breakpoints that are in the specified
+ * method. The breakpoints are NOT removed from the set.
+ *
+ * The BreakpointSet's lock must be acquired before calling here.
+ */
+static void dvmBreakpointSetUndo(BreakpointSet* pSet, Method* method)
+{
+ const u2* start = method->insns;
+ const u2* end = method->insns + dvmGetMethodInsnsSize(method);
+
+ int i;
+ for (i = 0; i < pSet->count; i++) {
+ Breakpoint* pBreak = &pSet->breakpoints[i];
+ if (pBreak->addr >= start && pBreak->addr < end) {
+ LOGV("UNDO %s.%s [%d]\n",
+ method->clazz->descriptor, method->name, i);
+ dvmDexChangeDex1(method->clazz->pDvmDex, (u1*)pBreak->addr,
+ pBreak->originalOpCode);
+ }
+ }
+}
+
+/*
+ * Put the breakpoint opcode back into the instruction stream, and check
+ * to see if the original opcode has changed.
+ *
+ * The BreakpointSet's lock must be acquired before calling here.
+ */
+static void dvmBreakpointSetRedo(BreakpointSet* pSet, Method* method)
+{
+ const u2* start = method->insns;
+ const u2* end = method->insns + dvmGetMethodInsnsSize(method);
+
+ int i;
+ for (i = 0; i < pSet->count; i++) {
+ Breakpoint* pBreak = &pSet->breakpoints[i];
+ if (pBreak->addr >= start && pBreak->addr < end) {
+ LOGV("REDO %s.%s [%d]\n",
+ method->clazz->descriptor, method->name, i);
+ u1 currentOpCode = *(u1*)pBreak->addr;
+ if (pBreak->originalOpCode != currentOpCode) {
+ /* verifier can drop in a throw-verification-error */
+ LOGD("NOTE: updating originalOpCode from 0x%02x to 0x%02x\n",
+ pBreak->originalOpCode, currentOpCode);
+ pBreak->originalOpCode = currentOpCode;
+ }
+ dvmDexChangeDex1(method->clazz->pDvmDex, (u1*)pBreak->addr,
+ OP_BREAKPOINT);
+ }
+ }
+}
+
+#endif /*WITH_DEBUGGER*/
+
+
+/*
+ * Do any debugger-attach-time initialization.
*/
void dvmInitBreakpoints(void)
{
#ifdef WITH_DEBUGGER
- memset(gDvm.debugBreakAddr, 0, sizeof(gDvm.debugBreakAddr));
+ /* quick sanity check */
+ BreakpointSet* pSet = gDvm.breakpointSet;
+ dvmBreakpointSetLock(pSet);
+ if (dvmBreakpointSetCount(pSet) != 0) {
+ LOGW("WARNING: %d leftover breakpoints\n", dvmBreakpointSetCount(pSet));
+ /* generally not good, but we can keep going */
+ }
+ dvmBreakpointSetUnlock(pSet);
#else
assert(false);
#endif
@@ -64,29 +370,13 @@
*
* "addr" is the absolute address of the breakpoint bytecode.
*/
-void dvmAddBreakAddr(Method* method, int instrOffset)
+void dvmAddBreakAddr(Method* method, unsigned int instrOffset)
{
#ifdef WITH_DEBUGGER
- const u2* addr = method->insns + instrOffset;
- const u2** ptr = gDvm.debugBreakAddr;
- int i;
-
- LOGV("BKP: add %p %s.%s (%s:%d)\n",
- addr, method->clazz->descriptor, method->name,
- dvmGetMethodSourceFile(method), dvmLineNumFromPC(method, instrOffset));
-
- method->debugBreakpointCount++;
- for (i = 0; i < MAX_BREAKPOINTS; i++, ptr++) {
- if (*ptr == NULL) {
- *ptr = addr;
- break;
- }
- }
- if (i == MAX_BREAKPOINTS) {
- /* no room; size is too small or we're not cleaning up properly */
- LOGE("ERROR: max breakpoints exceeded\n");
- assert(false);
- }
+ BreakpointSet* pSet = gDvm.breakpointSet;
+ dvmBreakpointSetLock(pSet);
+ dvmBreakpointSetAdd(pSet, method, instrOffset);
+ dvmBreakpointSetUnlock(pSet);
#else
assert(false);
#endif
@@ -102,35 +392,72 @@
* synchronized, so it should not be possible for two threads to be
* updating breakpoints at the same time.
*/
-void dvmClearBreakAddr(Method* method, int instrOffset)
+void dvmClearBreakAddr(Method* method, unsigned int instrOffset)
{
#ifdef WITH_DEBUGGER
- const u2* addr = method->insns + instrOffset;
- const u2** ptr = gDvm.debugBreakAddr;
- int i;
+ BreakpointSet* pSet = gDvm.breakpointSet;
+ dvmBreakpointSetLock(pSet);
+ dvmBreakpointSetRemove(pSet, method, instrOffset);
+ dvmBreakpointSetUnlock(pSet);
- LOGV("BKP: clear %p %s.%s (%s:%d)\n",
- addr, method->clazz->descriptor, method->name,
- dvmGetMethodSourceFile(method), dvmLineNumFromPC(method, instrOffset));
-
- method->debugBreakpointCount--;
- assert(method->debugBreakpointCount >= 0);
- for (i = 0; i < MAX_BREAKPOINTS; i++, ptr++) {
- if (*ptr == addr) {
- *ptr = NULL;
- break;
- }
- }
- if (i == MAX_BREAKPOINTS) {
- /* didn't find it */
- LOGE("ERROR: breakpoint on %p not found\n", addr);
- assert(false);
- }
#else
assert(false);
#endif
}
+#ifdef WITH_DEBUGGER
+/*
+ * Get the original opcode from under a breakpoint.
+ */
+u1 dvmGetOriginalOpCode(const u2* addr)
+{
+ BreakpointSet* pSet = gDvm.breakpointSet;
+ u1 orig = 0;
+
+ dvmBreakpointSetLock(pSet);
+ if (!dvmBreakpointSetOriginalOpCode(pSet, addr, &orig)) {
+ orig = *(u1*)addr;
+ if (orig == OP_BREAKPOINT) {
+ LOGE("GLITCH: can't find breakpoint, opcode is still set\n");
+ dvmAbort();
+ }
+ }
+ dvmBreakpointSetUnlock(pSet);
+
+ return orig;
+}
+
+/*
+ * Temporarily "undo" any breakpoints set in a specific method. Used
+ * during verification.
+ *
+ * Locks the breakpoint set, and leaves it locked.
+ */
+void dvmUndoBreakpoints(Method* method)
+{
+ BreakpointSet* pSet = gDvm.breakpointSet;
+
+ dvmBreakpointSetLock(pSet);
+ dvmBreakpointSetUndo(pSet, method);
+ /* lock remains held */
+}
+
+/*
+ * "Redo" the breakpoints cleared by a previous "undo", re-inserting the
+ * breakpoint opcodes and updating the "original opcode" values.
+ *
+ * Unlocks the breakpoint set, which must be held by a previous "undo".
+ */
+void dvmRedoBreakpoints(Method* method)
+{
+ BreakpointSet* pSet = gDvm.breakpointSet;
+
+ /* lock already held */
+ dvmBreakpointSetRedo(pSet, method);
+ dvmBreakpointSetUnlock(pSet);
+}
+#endif
+
/*
* Add a single step event. Currently this is a global item.
*
diff --git a/vm/interp/Interp.h b/vm/interp/Interp.h
index cd4c7ec..015c3db 100644
--- a/vm/interp/Interp.h
+++ b/vm/interp/Interp.h
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
/*
* Dalvik interpreter public definitions.
*/
@@ -34,12 +35,41 @@
void dvmThrowVerificationError(const Method* method, int kind, int ref);
/*
- * Breakpoint optimization table.
+ * One-time initialization and shutdown.
+ */
+bool dvmBreakpointStartup(void);
+void dvmBreakpointShutdown(void);
+
+/*
+ * Breakpoint implementation.
*/
void dvmInitBreakpoints();
-void dvmAddBreakAddr(Method* method, int instrOffset);
-void dvmClearBreakAddr(Method* method, int instrOffset);
+void dvmAddBreakAddr(Method* method, unsigned int instrOffset);
+void dvmClearBreakAddr(Method* method, unsigned int instrOffset);
bool dvmAddSingleStep(Thread* thread, int size, int depth);
void dvmClearSingleStep(Thread* thread);
+#ifdef WITH_DEBUGGER
+/*
+ * Recover the opcode that was replaced by a breakpoint.
+ */
+u1 dvmGetOriginalOpCode(const u2* addr);
+
+/*
+ * Temporarily "undo" any breakpoints set in a specific method. Used
+ * during verification.
+ *
+ * Locks the breakpoint set, and leaves it locked.
+ */
+void dvmUndoBreakpoints(Method* method);
+
+/*
+ * "Redo" the breakpoints cleared by a previous "undo", re-inserting the
+ * breakpoint opcodes and updating the "original opcode" values.
+ *
+ * Unlocks the breakpoint set, which must be held by a previous "undo".
+ */
+void dvmRedoBreakpoints(Method* method);
+#endif
+
#endif /*_DALVIK_INTERP_INTERP*/
diff --git a/vm/mterp/armv5te/OP_UNUSED_EC.S b/vm/mterp/armv5te/OP_BREAKPOINT.S
similarity index 100%
rename from vm/mterp/armv5te/OP_UNUSED_EC.S
rename to vm/mterp/armv5te/OP_BREAKPOINT.S
diff --git a/vm/mterp/c/OP_BREAKPOINT.c b/vm/mterp/c/OP_BREAKPOINT.c
new file mode 100644
index 0000000..5a1b543
--- /dev/null
+++ b/vm/mterp/c/OP_BREAKPOINT.c
@@ -0,0 +1,29 @@
+HANDLE_OPCODE(OP_BREAKPOINT)
+#if (INTERP_TYPE == INTERP_DBG) && defined(WITH_DEBUGGER)
+ {
+ /*
+ * Restart this instruction with the original opcode. We do
+ * this by simply jumping to the handler.
+ *
+ * It's probably not necessary to update "inst", but we do it
+ * for the sake of anything that needs to do disambiguation in a
+ * common handler with INST_INST.
+ *
+ * The breakpoint itself is handled over in updateDebugger(),
+ * because we need to detect other events (method entry, single
+ * step) and report them in the same event packet, and we're not
+ * yet handling those through breakpoint instructions. By the
+ * time we get here, the breakpoint has already been handled and
+ * the thread resumed.
+ */
+ u1 originalOpCode = dvmGetOriginalOpCode(pc);
+ LOGV("+++ break 0x%02x (0x%04x -> 0x%04x)\n", originalOpCode, inst,
+ INST_REPLACE_OP(inst, originalOpCode));
+ inst = INST_REPLACE_OP(inst, originalOpCode);
+ FINISH_BKPT(originalOpCode);
+ }
+#else
+ LOGE("Breakpoint hit in non-debug interpreter\n");
+ dvmAbort();
+#endif
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_EC.c b/vm/mterp/c/OP_UNUSED_EC.c
deleted file mode 100644
index fcb8c2e..0000000
--- a/vm/mterp/c/OP_UNUSED_EC.c
+++ /dev/null
@@ -1,2 +0,0 @@
-HANDLE_OPCODE(OP_UNUSED_EC)
-OP_END
diff --git a/vm/mterp/c/header.c b/vm/mterp/c/header.c
index 174c226..341d24b 100644
--- a/vm/mterp/c/header.c
+++ b/vm/mterp/c/header.c
@@ -295,6 +295,11 @@
#define INST_INST(_inst) ((_inst) & 0xff)
/*
+ * Replace the opcode (used when handling breakpoints). _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
* Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
*/
#define INST_A(_inst) (((_inst) >> 8) & 0x0f)
diff --git a/vm/mterp/out/InterpAsm-armv4t.S b/vm/mterp/out/InterpAsm-armv4t.S
index 56d0a26..a4e5c43 100644
--- a/vm/mterp/out/InterpAsm-armv4t.S
+++ b/vm/mterp/out/InterpAsm-armv4t.S
@@ -7618,8 +7618,8 @@
/* ------------------------------ */
.balign 64
-.L_OP_UNUSED_EC: /* 0xec */
-/* File: armv5te/OP_UNUSED_EC.S */
+.L_OP_BREAKPOINT: /* 0xec */
+/* File: armv5te/OP_BREAKPOINT.S */
/* File: armv5te/unused.S */
bl common_abort
diff --git a/vm/mterp/out/InterpAsm-armv5te-vfp.S b/vm/mterp/out/InterpAsm-armv5te-vfp.S
index c7926f6..3bb5409 100644
--- a/vm/mterp/out/InterpAsm-armv5te-vfp.S
+++ b/vm/mterp/out/InterpAsm-armv5te-vfp.S
@@ -7278,8 +7278,8 @@
/* ------------------------------ */
.balign 64
-.L_OP_UNUSED_EC: /* 0xec */
-/* File: armv5te/OP_UNUSED_EC.S */
+.L_OP_BREAKPOINT: /* 0xec */
+/* File: armv5te/OP_BREAKPOINT.S */
/* File: armv5te/unused.S */
bl common_abort
diff --git a/vm/mterp/out/InterpAsm-armv5te.S b/vm/mterp/out/InterpAsm-armv5te.S
index 3c5c496..52e536b 100644
--- a/vm/mterp/out/InterpAsm-armv5te.S
+++ b/vm/mterp/out/InterpAsm-armv5te.S
@@ -7618,8 +7618,8 @@
/* ------------------------------ */
.balign 64
-.L_OP_UNUSED_EC: /* 0xec */
-/* File: armv5te/OP_UNUSED_EC.S */
+.L_OP_BREAKPOINT: /* 0xec */
+/* File: armv5te/OP_BREAKPOINT.S */
/* File: armv5te/unused.S */
bl common_abort
diff --git a/vm/mterp/out/InterpAsm-armv7-a.S b/vm/mterp/out/InterpAsm-armv7-a.S
index aaf85d9..401bb96 100644
--- a/vm/mterp/out/InterpAsm-armv7-a.S
+++ b/vm/mterp/out/InterpAsm-armv7-a.S
@@ -7222,8 +7222,8 @@
/* ------------------------------ */
.balign 64
-.L_OP_UNUSED_EC: /* 0xec */
-/* File: armv5te/OP_UNUSED_EC.S */
+.L_OP_BREAKPOINT: /* 0xec */
+/* File: armv5te/OP_BREAKPOINT.S */
/* File: armv5te/unused.S */
bl common_abort
diff --git a/vm/mterp/out/InterpAsm-x86.S b/vm/mterp/out/InterpAsm-x86.S
index c25071e..4e6623c 100644
--- a/vm/mterp/out/InterpAsm-x86.S
+++ b/vm/mterp/out/InterpAsm-x86.S
@@ -5797,8 +5797,8 @@
/* ------------------------------ */
.balign 64
-.L_OP_UNUSED_EC: /* 0xec */
-/* File: x86/OP_UNUSED_EC.S */
+.L_OP_BREAKPOINT: /* 0xec */
+/* File: x86/OP_BREAKPOINT.S */
/* File: x86/unused.S */
jmp common_abort
diff --git a/vm/mterp/out/InterpC-allstubs.c b/vm/mterp/out/InterpC-allstubs.c
index 04d7c32..0ca466b 100644
--- a/vm/mterp/out/InterpC-allstubs.c
+++ b/vm/mterp/out/InterpC-allstubs.c
@@ -302,6 +302,11 @@
#define INST_INST(_inst) ((_inst) & 0xff)
/*
+ * Replace the opcode (used when handling breakpoints). _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
* Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
*/
#define INST_A(_inst) (((_inst) >> 8) & 0x0f)
@@ -2783,8 +2788,35 @@
HANDLE_OPCODE(OP_UNUSED_EB)
OP_END
-/* File: c/OP_UNUSED_EC.c */
-HANDLE_OPCODE(OP_UNUSED_EC)
+/* File: c/OP_BREAKPOINT.c */
+HANDLE_OPCODE(OP_BREAKPOINT)
+#if (INTERP_TYPE == INTERP_DBG) && defined(WITH_DEBUGGER)
+ {
+ /*
+ * Restart this instruction with the original opcode. We do
+ * this by simply jumping to the handler.
+ *
+ * It's probably not necessary to update "inst", but we do it
+ * for the sake of anything that needs to do disambiguation in a
+ * common handler with INST_INST.
+ *
+ * The breakpoint itself is handled over in updateDebugger(),
+ * because we need to detect other events (method entry, single
+ * step) and report them in the same event packet, and we're not
+ * yet handling those through breakpoint instructions. By the
+ * time we get here, the breakpoint has already been handled and
+ * the thread resumed.
+ */
+ u1 originalOpCode = dvmGetOriginalOpCode(pc);
+ LOGV("+++ break 0x%02x (0x%04x -> 0x%04x)\n", originalOpCode, inst,
+ INST_REPLACE_OP(inst, originalOpCode));
+ inst = INST_REPLACE_OP(inst, originalOpCode);
+ FINISH_BKPT(originalOpCode);
+ }
+#else
+ LOGE("Breakpoint hit in non-debug interpreter\n");
+ dvmAbort();
+#endif
OP_END
/* File: c/OP_THROW_VERIFICATION_ERROR.c */
diff --git a/vm/mterp/out/InterpC-armv4t.c b/vm/mterp/out/InterpC-armv4t.c
index 6b82bc8..6541684 100644
--- a/vm/mterp/out/InterpC-armv4t.c
+++ b/vm/mterp/out/InterpC-armv4t.c
@@ -302,6 +302,11 @@
#define INST_INST(_inst) ((_inst) & 0xff)
/*
+ * Replace the opcode (used when handling breakpoints). _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
* Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
*/
#define INST_A(_inst) (((_inst) >> 8) & 0x0f)
diff --git a/vm/mterp/out/InterpC-armv5te-vfp.c b/vm/mterp/out/InterpC-armv5te-vfp.c
index 7312700..0db6ce7 100644
--- a/vm/mterp/out/InterpC-armv5te-vfp.c
+++ b/vm/mterp/out/InterpC-armv5te-vfp.c
@@ -302,6 +302,11 @@
#define INST_INST(_inst) ((_inst) & 0xff)
/*
+ * Replace the opcode (used when handling breakpoints). _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
* Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
*/
#define INST_A(_inst) (((_inst) >> 8) & 0x0f)
diff --git a/vm/mterp/out/InterpC-armv5te.c b/vm/mterp/out/InterpC-armv5te.c
index ea11551..b2038f6 100644
--- a/vm/mterp/out/InterpC-armv5te.c
+++ b/vm/mterp/out/InterpC-armv5te.c
@@ -302,6 +302,11 @@
#define INST_INST(_inst) ((_inst) & 0xff)
/*
+ * Replace the opcode (used when handling breakpoints). _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
* Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
*/
#define INST_A(_inst) (((_inst) >> 8) & 0x0f)
diff --git a/vm/mterp/out/InterpC-armv7-a.c b/vm/mterp/out/InterpC-armv7-a.c
index 97799ec..e1872e1 100644
--- a/vm/mterp/out/InterpC-armv7-a.c
+++ b/vm/mterp/out/InterpC-armv7-a.c
@@ -302,6 +302,11 @@
#define INST_INST(_inst) ((_inst) & 0xff)
/*
+ * Replace the opcode (used when handling breakpoints). _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
* Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
*/
#define INST_A(_inst) (((_inst) >> 8) & 0x0f)
diff --git a/vm/mterp/out/InterpC-portdbg.c b/vm/mterp/out/InterpC-portdbg.c
index 4d15b45..062eadc 100644
--- a/vm/mterp/out/InterpC-portdbg.c
+++ b/vm/mterp/out/InterpC-portdbg.c
@@ -302,6 +302,11 @@
#define INST_INST(_inst) ((_inst) & 0xff)
/*
+ * Replace the opcode (used when handling breakpoints). _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
* Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
*/
#define INST_A(_inst) (((_inst) >> 8) & 0x0f)
@@ -448,6 +453,8 @@
*
* Assumes the existence of "const u2* pc" and (for threaded operation)
* "u2 inst".
+ *
+ * TODO: remove "switch" version.
*/
#ifdef THREADED_INTERP
# define H(_op) &&op_##_op
@@ -460,9 +467,13 @@
if (CHECK_JIT()) GOTO_bail_switch(); \
goto *handlerTable[INST_INST(inst)]; \
}
+# define FINISH_BKPT(_opcode) { \
+ goto *handlerTable[_opcode]; \
+ }
#else
# define HANDLE_OPCODE(_op) case _op:
# define FINISH(_offset) { ADJUST_PC(_offset); break; }
+# define FINISH_BKPT(opcode) { > not implemented < }
#endif
#define OP_END
@@ -1170,26 +1181,6 @@
/* code in here is only included in portable-debug interpreter */
/*
- * Determine if an address is "interesting" to the debugger. This allows
- * us to avoid scanning the entire event list before every instruction.
- *
- * The "debugBreakAddr" table is global and not synchronized.
- */
-static bool isInterestingAddr(const u2* pc)
-{
- const u2** ptr = gDvm.debugBreakAddr;
- int i;
-
- for (i = 0; i < MAX_BREAKPOINTS; i++, ptr++) {
- if (*ptr == pc) {
- LOGV("BKP: hit on %p\n", pc);
- return true;
- }
- }
- return false;
-}
-
-/*
* Update the debugger on interesting events, such as hitting a breakpoint
* or a single-step point. This is called from the top of the interpreter
* loop, before the current instruction is processed.
@@ -1235,14 +1226,10 @@
*
* Depending on the "mods" associated with event(s) on this address,
* we may or may not actually send a message to the debugger.
- *
- * Checking method->debugBreakpointCount is slower on the device than
- * just scanning the table (!). We could probably work something out
- * where we just check it on method entry/exit and remember the result,
- * but that's more fragile and requires passing more stuff around.
*/
#ifdef WITH_DEBUGGER
- if (method->debugBreakpointCount > 0 && isInterestingAddr(pc)) {
+ if (INST_INST(*pc) == OP_BREAKPOINT) {
+ LOGV("+++ breakpoint hit at %p\n", pc);
eventFlags |= DBG_BREAKPOINT;
}
#endif
@@ -3164,8 +3151,35 @@
HANDLE_OPCODE(OP_UNUSED_EB)
OP_END
-/* File: c/OP_UNUSED_EC.c */
-HANDLE_OPCODE(OP_UNUSED_EC)
+/* File: c/OP_BREAKPOINT.c */
+HANDLE_OPCODE(OP_BREAKPOINT)
+#if (INTERP_TYPE == INTERP_DBG) && defined(WITH_DEBUGGER)
+ {
+ /*
+ * Restart this instruction with the original opcode. We do
+ * this by simply jumping to the handler.
+ *
+ * It's probably not necessary to update "inst", but we do it
+ * for the sake of anything that needs to do disambiguation in a
+ * common handler with INST_INST.
+ *
+ * The breakpoint itself is handled over in updateDebugger(),
+ * because we need to detect other events (method entry, single
+ * step) and report them in the same event packet, and we're not
+ * yet handling those through breakpoint instructions. By the
+ * time we get here, the breakpoint has already been handled and
+ * the thread resumed.
+ */
+ u1 originalOpCode = dvmGetOriginalOpCode(pc);
+ LOGV("+++ break 0x%02x (0x%04x -> 0x%04x)\n", originalOpCode, inst,
+ INST_REPLACE_OP(inst, originalOpCode));
+ inst = INST_REPLACE_OP(inst, originalOpCode);
+ FINISH_BKPT(originalOpCode);
+ }
+#else
+ LOGE("Breakpoint hit in non-debug interpreter\n");
+ dvmAbort();
+#endif
OP_END
/* File: c/OP_THROW_VERIFICATION_ERROR.c */
diff --git a/vm/mterp/out/InterpC-portstd.c b/vm/mterp/out/InterpC-portstd.c
index 6ea6a56..52014f4 100644
--- a/vm/mterp/out/InterpC-portstd.c
+++ b/vm/mterp/out/InterpC-portstd.c
@@ -302,6 +302,11 @@
#define INST_INST(_inst) ((_inst) & 0xff)
/*
+ * Replace the opcode (used when handling breakpoints). _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
* Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
*/
#define INST_A(_inst) (((_inst) >> 8) & 0x0f)
@@ -443,6 +448,8 @@
*
* Assumes the existence of "const u2* pc" and (for threaded operation)
* "u2 inst".
+ *
+ * TODO: remove "switch" version.
*/
#ifdef THREADED_INTERP
# define H(_op) &&op_##_op
@@ -455,9 +462,13 @@
if (CHECK_JIT()) GOTO_bail_switch(); \
goto *handlerTable[INST_INST(inst)]; \
}
+# define FINISH_BKPT(_opcode) { \
+ goto *handlerTable[_opcode]; \
+ }
#else
# define HANDLE_OPCODE(_op) case _op:
# define FINISH(_offset) { ADJUST_PC(_offset); break; }
+# define FINISH_BKPT(opcode) { > not implemented < }
#endif
#define OP_END
@@ -2880,8 +2891,35 @@
HANDLE_OPCODE(OP_UNUSED_EB)
OP_END
-/* File: c/OP_UNUSED_EC.c */
-HANDLE_OPCODE(OP_UNUSED_EC)
+/* File: c/OP_BREAKPOINT.c */
+HANDLE_OPCODE(OP_BREAKPOINT)
+#if (INTERP_TYPE == INTERP_DBG) && defined(WITH_DEBUGGER)
+ {
+ /*
+ * Restart this instruction with the original opcode. We do
+ * this by simply jumping to the handler.
+ *
+ * It's probably not necessary to update "inst", but we do it
+ * for the sake of anything that needs to do disambiguation in a
+ * common handler with INST_INST.
+ *
+ * The breakpoint itself is handled over in updateDebugger(),
+ * because we need to detect other events (method entry, single
+ * step) and report them in the same event packet, and we're not
+ * yet handling those through breakpoint instructions. By the
+ * time we get here, the breakpoint has already been handled and
+ * the thread resumed.
+ */
+ u1 originalOpCode = dvmGetOriginalOpCode(pc);
+ LOGV("+++ break 0x%02x (0x%04x -> 0x%04x)\n", originalOpCode, inst,
+ INST_REPLACE_OP(inst, originalOpCode));
+ inst = INST_REPLACE_OP(inst, originalOpCode);
+ FINISH_BKPT(originalOpCode);
+ }
+#else
+ LOGE("Breakpoint hit in non-debug interpreter\n");
+ dvmAbort();
+#endif
OP_END
/* File: c/OP_THROW_VERIFICATION_ERROR.c */
diff --git a/vm/mterp/out/InterpC-x86.c b/vm/mterp/out/InterpC-x86.c
index ed42355..30a4e1c 100644
--- a/vm/mterp/out/InterpC-x86.c
+++ b/vm/mterp/out/InterpC-x86.c
@@ -302,6 +302,11 @@
#define INST_INST(_inst) ((_inst) & 0xff)
/*
+ * Replace the opcode (used when handling breakpoints). _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
* Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
*/
#define INST_A(_inst) (((_inst) >> 8) & 0x0f)
diff --git a/vm/mterp/portable/debug.c b/vm/mterp/portable/debug.c
index 449d49b..6716aba 100644
--- a/vm/mterp/portable/debug.c
+++ b/vm/mterp/portable/debug.c
@@ -1,26 +1,6 @@
/* code in here is only included in portable-debug interpreter */
/*
- * Determine if an address is "interesting" to the debugger. This allows
- * us to avoid scanning the entire event list before every instruction.
- *
- * The "debugBreakAddr" table is global and not synchronized.
- */
-static bool isInterestingAddr(const u2* pc)
-{
- const u2** ptr = gDvm.debugBreakAddr;
- int i;
-
- for (i = 0; i < MAX_BREAKPOINTS; i++, ptr++) {
- if (*ptr == pc) {
- LOGV("BKP: hit on %p\n", pc);
- return true;
- }
- }
- return false;
-}
-
-/*
* Update the debugger on interesting events, such as hitting a breakpoint
* or a single-step point. This is called from the top of the interpreter
* loop, before the current instruction is processed.
@@ -66,14 +46,10 @@
*
* Depending on the "mods" associated with event(s) on this address,
* we may or may not actually send a message to the debugger.
- *
- * Checking method->debugBreakpointCount is slower on the device than
- * just scanning the table (!). We could probably work something out
- * where we just check it on method entry/exit and remember the result,
- * but that's more fragile and requires passing more stuff around.
*/
#ifdef WITH_DEBUGGER
- if (method->debugBreakpointCount > 0 && isInterestingAddr(pc)) {
+ if (INST_INST(*pc) == OP_BREAKPOINT) {
+ LOGV("+++ breakpoint hit at %p\n", pc);
eventFlags |= DBG_BREAKPOINT;
}
#endif
diff --git a/vm/mterp/portable/stubdefs.c b/vm/mterp/portable/stubdefs.c
index 717e746..29258fc 100644
--- a/vm/mterp/portable/stubdefs.c
+++ b/vm/mterp/portable/stubdefs.c
@@ -19,6 +19,8 @@
*
* Assumes the existence of "const u2* pc" and (for threaded operation)
* "u2 inst".
+ *
+ * TODO: remove "switch" version.
*/
#ifdef THREADED_INTERP
# define H(_op) &&op_##_op
@@ -31,9 +33,13 @@
if (CHECK_JIT()) GOTO_bail_switch(); \
goto *handlerTable[INST_INST(inst)]; \
}
+# define FINISH_BKPT(_opcode) { \
+ goto *handlerTable[_opcode]; \
+ }
#else
# define HANDLE_OPCODE(_op) case _op:
# define FINISH(_offset) { ADJUST_PC(_offset); break; }
+# define FINISH_BKPT(opcode) { > not implemented < }
#endif
#define OP_END
diff --git a/vm/mterp/x86/OP_UNUSED_EC.S b/vm/mterp/x86/OP_BREAKPOINT.S
similarity index 100%
rename from vm/mterp/x86/OP_UNUSED_EC.S
rename to vm/mterp/x86/OP_BREAKPOINT.S
diff --git a/vm/oo/Class.c b/vm/oo/Class.c
index cc461e7..9565eb0 100644
--- a/vm/oo/Class.c
+++ b/vm/oo/Class.c
@@ -2117,6 +2117,7 @@
}
}
+#if 0 /* replaced with private/read-write mapping */
/*
* We usually map bytecode directly out of the DEX file, which is mapped
* shared read-only. If we want to be able to modify it, we have to make
@@ -2164,6 +2165,7 @@
LOGV("+++ marking %p read-only\n", methodDexCode);
dvmLinearReadOnly(meth->clazz->classLoader, methodDexCode);
}
+#endif
/*
diff --git a/vm/oo/Object.h b/vm/oo/Object.h
index 788ec13..298c230 100644
--- a/vm/oo/Object.h
+++ b/vm/oo/Object.h
@@ -553,9 +553,6 @@
#ifdef WITH_PROFILER
bool inProfile;
#endif
-#ifdef WITH_DEBUGGER
- short debugBreakpointCount;
-#endif
};
/*