am 64b928e: AI 148694: Manually copied from cupcake_dcm CL 148669-p9. W
Merge commit '64b928e82a3b91b339ff9ee980c4a0c020daa251' into donut
* commit '64b928e82a3b91b339ff9ee980c4a0c020daa251':
AI 148694: Manually copied from cupcake_dcm CL 148669-p9. When resetting the
diff --git a/dexdump/DexDump.c b/dexdump/DexDump.c
index a4d97eb..c5714ea 100644
--- a/dexdump/DexDump.c
+++ b/dexdump/DexDump.c
@@ -13,11 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
/*
* The "dexdump" tool is intended to mimic "objdump". When possible, use
* similar command-line arguments.
*
- * TODO: rework the output format to be more regexp-friendly
+ * TODO: rework the "plain" output format to be more regexp-friendly
+ *
+ * Differences between XML output and the "current.xml" file:
+ * - classes in same package are not all grouped together; generally speaking
+ * nothing is sorted
+ * - no "deprecated" on fields and methods
+ * - no "value" on fields
+ * - no parameter names
+ * - no generic signatures on parameters, e.g. type="java.lang.Class<?>"
+ * - class shows declared fields and methods; does not show inherited fields
*/
#include "libdex/DexFile.h"
#include "libdex/DexCatch.h"
@@ -43,12 +53,21 @@
static InstructionWidth* gInstrWidth;
static InstructionFormat* gInstrFormat;
+typedef enum OutputFormat {
+ OUTPUT_PLAIN = 0, /* default */
+ OUTPUT_XML, /* fancy */
+} OutputFormat;
+
/* command-line options */
struct {
bool disassemble;
bool showFileHeaders;
bool showSectionHeaders;
+ bool ignoreBadChecksum;
+ OutputFormat outputFormat;
const char* tempFileName;
+ bool exportsOnly;
+ bool verbose;
} gOptions;
/* basic info about a field or method */
@@ -67,34 +86,134 @@
}
/*
- * Return a newly-allocated string for the "dot version" of the class
- * name for the given type descriptor. That is, The initial "L" and
- * final ";" (if any) have been removed and all occurrences of '/'
- * have been changed to '.'.
+ * Converts a single-character primitive type into its human-readable
+ * equivalent.
+ */
+static const char* primitiveTypeLabel(char typeChar)
+{
+ switch (typeChar) {
+ case 'B': return "byte";
+ case 'C': return "char";
+ case 'D': return "double";
+ case 'F': return "float";
+ case 'I': return "int";
+ case 'J': return "long";
+ case 'S': return "short";
+ case 'V': return "void";
+ case 'Z': return "boolean";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+/*
+ * Converts a type descriptor to human-readable "dotted" form. For
+ * example, "Ljava/lang/String;" becomes "java.lang.String", and
+ * "[I" becomes "int[]". Also converts '$' to '.', which means this
+ * form can't be converted back to a descriptor.
*/
static char* descriptorToDot(const char* str)
{
- size_t at = strlen(str);
+ int targetLen = strlen(str);
+ int offset = 0;
+ int arrayDepth = 0;
char* newStr;
- if (str[0] == 'L') {
- assert(str[at - 1] == ';');
- at -= 2; /* Two fewer chars to copy. */
- str++; /* Skip the 'L'. */
+ /* strip leading [s; will be added to end */
+ while (targetLen > 1 && str[offset] == '[') {
+ offset++;
+ targetLen--;
+ }
+ arrayDepth = offset;
+
+ if (targetLen == 1) {
+ /* primitive type */
+ str = primitiveTypeLabel(str[offset]);
+ offset = 0;
+ targetLen = strlen(str);
+ } else {
+ /* account for leading 'L' and trailing ';' */
+ if (targetLen >= 2 && str[offset] == 'L' &&
+ str[offset+targetLen-1] == ';')
+ {
+ targetLen -= 2;
+ offset++;
+ }
}
- newStr = malloc(at + 1); /* Add one for the '\0'. */
- newStr[at] = '\0';
+ newStr = malloc(targetLen + arrayDepth * 2 +1);
- while (at > 0) {
- at--;
- newStr[at] = (str[at] == '/') ? '.' : str[at];
+ /* copy class name over */
+ int i;
+ for (i = 0; i < targetLen; i++) {
+ char ch = str[offset + i];
+ newStr[i] = (ch == '/' || ch == '$') ? '.' : ch;
+ }
+
+ /* add the appropriate number of brackets for arrays */
+ while (arrayDepth-- > 0) {
+ newStr[i++] = '[';
+ newStr[i++] = ']';
+ }
+ newStr[i] = '\0';
+ assert(i == targetLen + arrayDepth * 2);
+
+ return newStr;
+}
+
+/*
+ * Converts the class name portion of a type descriptor to human-readable
+ * "dotted" form.
+ *
+ * Returns a newly-allocated string.
+ */
+static char* descriptorClassToDot(const char* str)
+{
+ const char* lastSlash;
+ char* newStr;
+ char* cp;
+
+ /* reduce to just the class name, trimming trailing ';' */
+ lastSlash = strrchr(str, '/');
+ if (lastSlash == NULL)
+ lastSlash = str + 1; /* start past 'L' */
+ else
+ lastSlash++; /* start past '/' */
+
+ newStr = strdup(lastSlash);
+ newStr[strlen(lastSlash)-1] = '\0';
+ for (cp = newStr; *cp != '\0'; cp++) {
+ if (*cp == '$')
+ *cp = '.';
}
return newStr;
}
/*
+ * Returns a quoted string representing the boolean value.
+ */
+static const char* quotedBool(bool val)
+{
+ if (val)
+ return "\"true\"";
+ else
+ return "\"false\"";
+}
+
+static const char* quotedVisibility(u4 accessFlags)
+{
+ if ((accessFlags & ACC_PUBLIC) != 0)
+ return "\"public\"";
+ else if ((accessFlags & ACC_PROTECTED) != 0)
+ return "\"protected\"";
+ else if ((accessFlags & ACC_PRIVATE) != 0)
+ return "\"private\"";
+ else
+ return "\"package\"";
+}
+
+/*
* Count the number of '1' bits in a word.
*
* Having completed this, I'm ready for an interview at Google.
@@ -114,7 +233,6 @@
return count;
}
-
/*
* Flag for use with createAccessFlagStr().
*/
@@ -310,7 +428,7 @@
}
/*
- * Dump an interface.
+ * Dump an interface that a class declares to implement.
*/
void dumpInterface(const DexFile* pDexFile, const DexTypeItem* pTypeItem,
int i)
@@ -318,7 +436,13 @@
const char* interfaceName =
dexStringByTypeIdx(pDexFile, pTypeItem->typeIdx);
- printf(" #%d : '%s'\n", i, interfaceName);
+ if (gOptions.outputFormat == OUTPUT_PLAIN) {
+ printf(" #%d : '%s'\n", i, interfaceName);
+ } else {
+ char* dotted = descriptorToDot(interfaceName);
+ printf("<implements name=\"%s\">\n</implements>\n", dotted);
+ free(dotted);
+ }
}
/*
@@ -870,8 +994,14 @@
const DexMethodId* pMethodId;
const char* backDescriptor;
const char* name;
- char* typeDescriptor;
- char* accessStr;
+ char* typeDescriptor = NULL;
+ char* accessStr = NULL;
+
+ if (gOptions.exportsOnly &&
+ (pDexMethod->accessFlags & (ACC_PUBLIC | ACC_PROTECTED)) == 0)
+ {
+ return;
+ }
pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
name = dexStringById(pDexFile, pMethodId->nameIdx);
@@ -882,22 +1012,119 @@
accessStr = createAccessFlagStr(pDexMethod->accessFlags,
kAccessForMethod);
- printf(" #%d : (in %s)\n", i, backDescriptor);
- printf(" name : '%s'\n", name);
- printf(" type : '%s'\n", typeDescriptor);
- printf(" access : 0x%04x (%s)\n",
- pDexMethod->accessFlags, accessStr);
+ if (gOptions.outputFormat == OUTPUT_PLAIN) {
+ printf(" #%d : (in %s)\n", i, backDescriptor);
+ printf(" name : '%s'\n", name);
+ printf(" type : '%s'\n", typeDescriptor);
+ printf(" access : 0x%04x (%s)\n",
+ pDexMethod->accessFlags, accessStr);
- if (pDexMethod->codeOff == 0) {
- printf(" code : (none)\n");
- } else {
- printf(" code -\n");
- dumpCode(pDexFile, pDexMethod);
+ if (pDexMethod->codeOff == 0) {
+ printf(" code : (none)\n");
+ } else {
+ printf(" code -\n");
+ dumpCode(pDexFile, pDexMethod);
+ }
+
+ if (gOptions.disassemble)
+ putchar('\n');
+ } else if (gOptions.outputFormat == OUTPUT_XML) {
+ bool constructor = (name[0] == '<');
+
+ if (constructor) {
+ char* tmp;
+
+ tmp = descriptorClassToDot(backDescriptor);
+ printf("<constructor name=\"%s\"\n", tmp);
+ free(tmp);
+
+ tmp = descriptorToDot(backDescriptor);
+ printf(" type=\"%s\"\n", tmp);
+ free(tmp);
+ } else {
+ printf("<method name=\"%s\"\n", name);
+
+ const char* returnType = strrchr(typeDescriptor, ')');
+ if (returnType == NULL) {
+ fprintf(stderr, "bad method type descriptor '%s'\n",
+ typeDescriptor);
+ goto bail;
+ }
+
+ char* tmp = descriptorToDot(returnType+1);
+ printf(" return=\"%s\"\n", tmp);
+ free(tmp);
+
+ printf(" abstract=%s\n",
+ quotedBool((pDexMethod->accessFlags & ACC_ABSTRACT) != 0));
+ printf(" native=%s\n",
+ quotedBool((pDexMethod->accessFlags & ACC_NATIVE) != 0));
+
+ bool isSync =
+ (pDexMethod->accessFlags & ACC_SYNCHRONIZED) != 0 ||
+ (pDexMethod->accessFlags & ACC_DECLARED_SYNCHRONIZED) != 0;
+ printf(" synchronized=%s\n", quotedBool(isSync));
+ }
+
+ printf(" static=%s\n",
+ quotedBool((pDexMethod->accessFlags & ACC_STATIC) != 0));
+ printf(" final=%s\n",
+ quotedBool((pDexMethod->accessFlags & ACC_FINAL) != 0));
+ // "deprecated=" not knowable w/o parsing annotations
+ printf(" visibility=%s\n",
+ quotedVisibility(pDexMethod->accessFlags));
+
+ printf(">\n");
+
+ /*
+ * Parameters.
+ */
+ if (typeDescriptor[0] != '(') {
+ fprintf(stderr, "ERROR: bad descriptor '%s'\n", typeDescriptor);
+ goto bail;
+ }
+
+ char tmpBuf[strlen(typeDescriptor)+1]; /* more than big enough */
+ int argNum = 0;
+
+ const char* base = typeDescriptor+1;
+
+ while (*base != ')') {
+ char* cp = tmpBuf;
+
+ while (*base == '[')
+ *cp++ = *base++;
+
+ if (*base == 'L') {
+ /* copy through ';' */
+ do {
+ *cp = *base++;
+ } while (*cp++ != ';');
+ } else {
+ /* primitive char, copy it */
+ if (strchr("ZBCSIFJD", *base) == NULL) {
+ fprintf(stderr, "ERROR: bad method signature '%s'\n", base);
+ goto bail;
+ }
+ *cp++ = *base++;
+ }
+
+ /* null terminate and display */
+ *cp++ = '\0';
+
+ char* tmp = descriptorToDot(tmpBuf);
+ printf("<parameter name=\"arg%d\" type=\"%s\">\n</parameter>\n",
+ argNum++, tmp);
+ free(tmp);
+ }
+
+ if (constructor)
+ printf("</constructor>\n");
+ else
+ printf("</method>\n");
}
- if (gOptions.disassemble)
- putchar('\n');
-
+bail:
free(typeDescriptor);
free(accessStr);
}
@@ -913,6 +1140,12 @@
const char* typeDescriptor;
char* accessStr;
+ if (gOptions.exportsOnly &&
+ (pSField->accessFlags & (ACC_PUBLIC | ACC_PROTECTED)) == 0)
+ {
+ return;
+ }
+
pFieldId = dexGetFieldId(pDexFile, pSField->fieldIdx);
name = dexStringById(pDexFile, pFieldId->nameIdx);
typeDescriptor = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
@@ -920,11 +1153,35 @@
accessStr = createAccessFlagStr(pSField->accessFlags, kAccessForField);
- printf(" #%d : (in %s)\n", i, backDescriptor);
- printf(" name : '%s'\n", name);
- printf(" type : '%s'\n", typeDescriptor);
- printf(" access : 0x%04x (%s)\n",
- pSField->accessFlags, accessStr);
+ if (gOptions.outputFormat == OUTPUT_PLAIN) {
+ printf(" #%d : (in %s)\n", i, backDescriptor);
+ printf(" name : '%s'\n", name);
+ printf(" type : '%s'\n", typeDescriptor);
+ printf(" access : 0x%04x (%s)\n",
+ pSField->accessFlags, accessStr);
+ } else if (gOptions.outputFormat == OUTPUT_XML) {
+ char* tmp;
+
+ printf("<field name=\"%s\"\n", name);
+
+ tmp = descriptorToDot(typeDescriptor);
+ printf(" type=\"%s\"\n", tmp);
+ free(tmp);
+
+ printf(" transient=%s\n",
+ quotedBool((pSField->accessFlags & ACC_TRANSIENT) != 0));
+ printf(" volatile=%s\n",
+ quotedBool((pSField->accessFlags & ACC_VOLATILE) != 0));
+ // "value=" not knowable w/o parsing annotations
+ printf(" static=%s\n",
+ quotedBool((pSField->accessFlags & ACC_STATIC) != 0));
+ printf(" final=%s\n",
+ quotedBool((pSField->accessFlags & ACC_FINAL) != 0));
+ // "deprecated=" not knowable w/o parsing annotations
+ printf(" visibility=%s\n",
+ quotedVisibility(pSField->accessFlags));
+ printf(">\n</field>\n");
+ }
free(accessStr);
}
@@ -934,92 +1191,158 @@
*/
void dumpIField(const DexFile* pDexFile, const DexField* pIField, int i)
{
- const DexFieldId* pFieldId;
- const char* backDescriptor;
- const char* name;
- const char* typeDescriptor;
- char* accessStr;
-
- pFieldId = dexGetFieldId(pDexFile, pIField->fieldIdx);
- name = dexStringById(pDexFile, pFieldId->nameIdx);
- typeDescriptor = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
- backDescriptor = dexStringByTypeIdx(pDexFile, pFieldId->classIdx);
-
- accessStr = createAccessFlagStr(pIField->accessFlags, kAccessForField);
-
- printf(" #%d : (in %s)\n", i, backDescriptor);
- printf(" name : '%s'\n", name);
- printf(" type : '%s'\n", typeDescriptor);
- printf(" access : 0x%04x (%s)\n",
- pIField->accessFlags, accessStr);
-
- free(accessStr);
+ dumpSField(pDexFile, pIField, i);
}
/*
* Dump the class.
+ *
+ * If "*pLastPackage" is NULL or does not match the current class' package,
+ * the value will be replaced with a newly-allocated string.
*/
-void dumpClass(DexFile* pDexFile, int idx)
+void dumpClass(DexFile* pDexFile, int idx, char** pLastPackage)
{
const DexTypeList* pInterfaces;
const DexClassDef* pClassDef;
- DexClassData* pClassData;
+ DexClassData* pClassData = NULL;
const u1* pEncodedData;
const char* fileName;
const char* classDescriptor;
const char* superclassDescriptor;
- char* accessStr;
+ char* accessStr = NULL;
int i;
pClassDef = dexGetClassDef(pDexFile, idx);
- printf("Class #%d -\n", idx);
+
+ if (gOptions.exportsOnly && (pClassDef->accessFlags & ACC_PUBLIC) == 0) {
+ //printf("<!-- omitting non-public class %s -->\n",
+ // classDescriptor);
+ goto bail;
+ }
pEncodedData = dexGetClassData(pDexFile, pClassDef);
pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);
if (pClassData == NULL) {
- printf("Trouble reading class data\n");
- return;
+ printf("Trouble reading class data (#%d)\n", idx);
+ goto bail;
}
classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
- printf(" Class descriptor : '%s'\n", classDescriptor);
- accessStr = createAccessFlagStr(pClassDef->accessFlags, kAccessForClass);
- printf(" Access flags : 0x%04x (%s)\n",
- pClassDef->accessFlags, accessStr);
+ /*
+ * For the XML output, show the package name. Ideally we'd gather
+ * up the classes, sort them, and dump them alphabetically so the
+ * package name wouldn't jump around, but that's not a great plan
+ * for something that needs to run on the device.
+ */
+ if (!(classDescriptor[0] == 'L' &&
+ classDescriptor[strlen(classDescriptor)-1] == ';'))
+ {
+ /* arrays and primitives should not be defined explicitly */
+ fprintf(stderr, "Malformed class name '%s'\n", classDescriptor);
+ /* keep going? */
+ } else if (gOptions.outputFormat == OUTPUT_XML) {
+ char* mangle;
+ char* lastSlash;
+ char* cp;
- if (pClassDef->superclassIdx == kDexNoIndex)
- superclassDescriptor = "(none)";
- else {
- superclassDescriptor =
- dexStringByTypeIdx(pDexFile, pClassDef->superclassIdx);
- printf(" Superclass : '%s'\n", superclassDescriptor);
+ mangle = strdup(classDescriptor + 1);
+ mangle[strlen(mangle)-1] = '\0';
+
+ /* reduce to just the package name */
+ lastSlash = strrchr(mangle, '/');
+ if (lastSlash != NULL) {
+ *lastSlash = '\0';
+ } else {
+ *mangle = '\0';
+ }
+
+ for (cp = mangle; *cp != '\0'; cp++) {
+ if (*cp == '/')
+ *cp = '.';
+ }
+
+ if (*pLastPackage == NULL || strcmp(mangle, *pLastPackage) != 0) {
+ /* start of a new package */
+ if (*pLastPackage != NULL)
+ printf("</package>\n");
+ printf("<package name=\"%s\"\n>\n", mangle);
+ free(*pLastPackage);
+ *pLastPackage = mangle;
+ } else {
+ free(mangle);
+ }
}
- printf(" Interfaces -\n");
+ accessStr = createAccessFlagStr(pClassDef->accessFlags, kAccessForClass);
+
+ if (pClassDef->superclassIdx == kDexNoIndex) {
+ superclassDescriptor = NULL;
+ } else {
+ superclassDescriptor =
+ dexStringByTypeIdx(pDexFile, pClassDef->superclassIdx);
+ }
+
+ if (gOptions.outputFormat == OUTPUT_PLAIN) {
+ printf("Class #%d -\n", idx);
+ printf(" Class descriptor : '%s'\n", classDescriptor);
+ printf(" Access flags : 0x%04x (%s)\n",
+ pClassDef->accessFlags, accessStr);
+
+ if (superclassDescriptor != NULL)
+ printf(" Superclass : '%s'\n", superclassDescriptor);
+
+ printf(" Interfaces -\n");
+ } else {
+ char* tmp;
+
+ tmp = descriptorClassToDot(classDescriptor);
+ printf("<class name=\"%s\"\n", tmp);
+ free(tmp);
+
+ if (superclassDescriptor != NULL) {
+ tmp = descriptorToDot(superclassDescriptor);
+ printf(" extends=\"%s\"\n", tmp);
+ free(tmp);
+ }
+ printf(" abstract=%s\n",
+ quotedBool((pClassDef->accessFlags & ACC_ABSTRACT) != 0));
+ printf(" static=%s\n",
+ quotedBool((pClassDef->accessFlags & ACC_STATIC) != 0));
+ printf(" final=%s\n",
+ quotedBool((pClassDef->accessFlags & ACC_FINAL) != 0));
+ // "deprecated=" not knowable w/o parsing annotations
+ printf(" visibility=%s\n",
+ quotedVisibility(pClassDef->accessFlags));
+ printf(">\n");
+ }
pInterfaces = dexGetInterfacesList(pDexFile, pClassDef);
if (pInterfaces != NULL) {
for (i = 0; i < (int) pInterfaces->size; i++)
dumpInterface(pDexFile, dexGetTypeItem(pInterfaces, i), i);
}
- printf(" Static fields -\n");
+ if (gOptions.outputFormat == OUTPUT_PLAIN)
+ printf(" Static fields -\n");
for (i = 0; i < (int) pClassData->header.staticFieldsSize; i++) {
dumpSField(pDexFile, &pClassData->staticFields[i], i);
}
- printf(" Instance fields -\n");
+ if (gOptions.outputFormat == OUTPUT_PLAIN)
+ printf(" Instance fields -\n");
for (i = 0; i < (int) pClassData->header.instanceFieldsSize; i++) {
dumpIField(pDexFile, &pClassData->instanceFields[i], i);
}
- printf(" Direct methods -\n");
+ if (gOptions.outputFormat == OUTPUT_PLAIN)
+ printf(" Direct methods -\n");
for (i = 0; i < (int) pClassData->header.directMethodsSize; i++) {
dumpMethod(pDexFile, &pClassData->directMethods[i], i);
}
- printf(" Virtual methods -\n");
+ if (gOptions.outputFormat == OUTPUT_PLAIN)
+ printf(" Virtual methods -\n");
for (i = 0; i < (int) pClassData->header.virtualMethodsSize; i++) {
dumpMethod(pDexFile, &pClassData->virtualMethods[i], i);
}
@@ -1030,11 +1353,18 @@
fileName = dexStringById(pDexFile, pClassDef->sourceFileIdx);
else
fileName = "unknown";
- printf(" source_file_idx : %d (%s)\n",
- pClassDef->sourceFileIdx, fileName);
- printf("\n");
+ if (gOptions.outputFormat == OUTPUT_PLAIN) {
+ printf(" source_file_idx : %d (%s)\n",
+ pClassDef->sourceFileIdx, fileName);
+ printf("\n");
+ }
+ if (gOptions.outputFormat == OUTPUT_XML) {
+ printf("</class>\n");
+ }
+
+bail:
free(pClassData);
free(accessStr);
}
@@ -1044,20 +1374,35 @@
*/
void processDexFile(const char* fileName, DexFile* pDexFile)
{
+ char* package = NULL;
int i;
- printf("Opened '%s', DEX version '%.3s'\n", fileName,
- pDexFile->pHeader->magic +4);
+ if (gOptions.verbose) {
+ printf("Opened '%s', DEX version '%.3s'\n", fileName,
+ pDexFile->pHeader->magic +4);
+ }
if (gOptions.showFileHeaders)
dumpFileHeader(pDexFile);
+ if (gOptions.outputFormat == OUTPUT_XML)
+ printf("<api>\n");
+
for (i = 0; i < (int) pDexFile->pHeader->classDefsSize; i++) {
if (gOptions.showSectionHeaders)
dumpClassDef(pDexFile, i);
- dumpClass(pDexFile, i);
+ dumpClass(pDexFile, i, &package);
}
+
+ /* free the last one allocated */
+ if (package != NULL) {
+ printf("</package>\n");
+ free(package);
+ }
+
+ if (gOptions.outputFormat == OUTPUT_XML)
+ printf("</api>\n");
}
@@ -1071,14 +1416,18 @@
bool mapped = false;
int result = -1;
- printf("Processing '%s'...\n", fileName);
+ if (gOptions.verbose)
+ printf("Processing '%s'...\n", fileName);
if (dexOpenAndMap(fileName, gOptions.tempFileName, &map, false) != 0)
goto bail;
mapped = true;
- pDexFile = dexFileParse(map.addr, map.length,
- kDexParseVerifyChecksum | kDexParseContinueOnError);
+ int flags = kDexParseVerifyChecksum;
+ if (gOptions.ignoreBadChecksum)
+ flags |= kDexParseContinueOnError;
+
+ pDexFile = dexFileParse(map.addr, map.length, flags);
if (pDexFile == NULL) {
fprintf(stderr, "ERROR: DEX parse failed\n");
goto bail;
@@ -1103,11 +1452,15 @@
void usage(void)
{
fprintf(stderr, "Copyright (C) 2007 The Android Open Source Project\n\n");
- fprintf(stderr, "%s: [-d] [-f] [-h] [-t tempfile] dexfile...\n", gProgName);
+ fprintf(stderr,
+ "%s: [-d] [-f] [-h] [-i] [-l layout] [-t tempfile] dexfile...\n",
+ gProgName);
fprintf(stderr, "\n");
fprintf(stderr, " -d : disassemble code sections\n");
fprintf(stderr, " -f : display summary information from file header\n");
fprintf(stderr, " -h : display file header details\n");
+ fprintf(stderr, " -i : ignore checksum failures\n");
+ fprintf(stderr, " -l : output layout, either 'plain' or 'xml'\n");
fprintf(stderr, " -t : temp file name (defaults to /sdcard/dex-temp-*)\n");
}
@@ -1122,9 +1475,10 @@
int ic;
memset(&gOptions, 0, sizeof(gOptions));
+ gOptions.verbose = true;
while (1) {
- ic = getopt(argc, argv, "dfht:");
+ ic = getopt(argc, argv, "dfhil:t:");
if (ic < 0)
break;
@@ -1138,8 +1492,22 @@
case 'h': // dump section headers, i.e. all meta-data
gOptions.showSectionHeaders = true;
break;
+ case 'i': // continue even if checksum is bad
+ gOptions.ignoreBadChecksum = true;
+ break;
+ case 'l': // layout
+ if (strcmp(optarg, "plain") == 0) {
+ gOptions.outputFormat = OUTPUT_PLAIN;
+ } else if (strcmp(optarg, "xml") == 0) {
+ gOptions.outputFormat = OUTPUT_XML;
+ gOptions.verbose = false;
+ gOptions.exportsOnly = true;
+ } else {
+ wantUsage = true;
+ }
+ break;
case 't': // temp file, used when opening compressed Jar
- gOptions.tempFileName = argv[optind];
+ gOptions.tempFileName = optarg;
break;
default:
wantUsage = true;
@@ -1169,3 +1537,4 @@
return 0;
}
+
diff --git a/libcore/dalvik/src/main/java/dalvik/system/VMDebug.java b/libcore/dalvik/src/main/java/dalvik/system/VMDebug.java
index 2d09edd..d9f11ce 100644
--- a/libcore/dalvik/src/main/java/dalvik/system/VMDebug.java
+++ b/libcore/dalvik/src/main/java/dalvik/system/VMDebug.java
@@ -153,6 +153,12 @@
int bufferSize, int flags);
/**
+ * Determine whether method tracing is currently active.
+ * @hide
+ */
+ public static native boolean isMethodTracingActive();
+
+ /**
* Stops method tracing.
*/
public static native void stopMethodTracing();
diff --git a/libcore/luni-kernel/src/main/java/java/lang/ClassLoader.java b/libcore/luni-kernel/src/main/java/java/lang/ClassLoader.java
index 822fade..3c2e911 100644
--- a/libcore/luni-kernel/src/main/java/java/lang/ClassLoader.java
+++ b/libcore/luni-kernel/src/main/java/java/lang/ClassLoader.java
@@ -634,7 +634,7 @@
*/
final boolean isAncestorOf(ClassLoader child) {
for (ClassLoader current = child; current != null;
- current = child.parent) {
+ current = current.parent) {
if (current == this) {
return true;
}
diff --git a/libcore/luni/src/main/native/java_net_InetAddress.cpp b/libcore/luni/src/main/native/java_net_InetAddress.cpp
index cf026bc..0eb2753 100644
--- a/libcore/luni/src/main/native/java_net_InetAddress.cpp
+++ b/libcore/luni/src/main/native/java_net_InetAddress.cpp
@@ -258,14 +258,17 @@
memset(sin, 0, sizeof(struct sockaddr_in));
sin->sin_family = AF_INET;
memcpy(&sin->sin_addr.s_addr, rawAddress, 4);
+ env->ReleaseByteArrayElements(javaAddress, rawAddress, JNI_ABORT);
break;
case 16:
socklen = sizeof(struct sockaddr_in6);
memset(sin6, 0, sizeof(struct sockaddr_in6));
sin6->sin6_family = AF_INET6;
memcpy(&sin6->sin6_addr.s6_addr, rawAddress, 4);
+ env->ReleaseByteArrayElements(javaAddress, rawAddress, JNI_ABORT);
break;
default:
+ env->ReleaseByteArrayElements(javaAddress, rawAddress, JNI_ABORT);
jniThrowException(env, "java/net/UnknownHostException",
"Invalid address length");
return NULL;
diff --git a/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp b/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp
index de01295..f79c019 100755
--- a/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp
+++ b/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp
@@ -3084,7 +3084,12 @@
if ((anOption >> 16) & BROKEN_MULTICAST_IF) {
return;
}
- result = setsockopt(handle, IPPROTO_IP, IP_MULTICAST_IF, &sockVal, sockSize);
+ struct ip_mreqn mcast_req;
+ memset(&mcast_req, 0, sizeof(mcast_req));
+ memcpy(&(mcast_req.imr_address), &(sockVal.sin_addr),
+ sizeof(struct in_addr));
+ result = setsockopt(handle, IPPROTO_IP, IP_MULTICAST_IF,
+ &mcast_req, sizeof(mcast_req));
if (0 != result) {
throwSocketException(env, convertError(errno));
return;
diff --git a/libcore/sql/src/main/native/sqlite_jni.c b/libcore/sql/src/main/native/sqlite_jni.c
index c8a76e4..4923869 100644
--- a/libcore/sql/src/main/native/sqlite_jni.c
+++ b/libcore/sql/src/main/native/sqlite_jni.c
@@ -29,6 +29,8 @@
#define HAVE_BOTH_SQLITE 1
#endif
+#define CANT_PASS_VALIST_AS_CHARPTR
+
#include "sqlite_jni.h"
#if defined(_WIN32) || !defined(CANT_PASS_VALIST_AS_CHARPTR)
diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java
index e985908..36282e1 100644
--- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java
+++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java
@@ -146,10 +146,10 @@
ArrayList<String> array = new ArrayList<String>();
if ((ssl_op_no & SSL_OP_NO_SSLv3) == 0x00000000L) {
- array.add(supportedProtocols[1]);
+ array.add(supportedProtocols[0]);
}
if ((ssl_op_no & SSL_OP_NO_TLSv1) == 0x00000000L) {
- array.add(supportedProtocols[2]);
+ array.add(supportedProtocols[1]);
}
return array.toArray(new String[array.size()]);
}
diff --git a/libdex/CmdUtils.c b/libdex/CmdUtils.c
index ca1054c..7dfee87 100644
--- a/libdex/CmdUtils.c
+++ b/libdex/CmdUtils.c
@@ -97,14 +97,14 @@
*
* If "quiet" is set, don't report common errors.
*
- * Returns 0 on success.
+ * Returns 0 (kUTFRSuccess) on success.
*/
UnzipToFileResult dexOpenAndMap(const char* fileName, const char* tempFileName,
MemMapping* pMap, bool quiet)
{
- UnzipToFileResult result = kUTFRSuccess;
+ UnzipToFileResult result = kUTFRGenericFailure;
int len = strlen(fileName);
- char tempName[32];
+ char tempNameBuf[32];
bool removeTemp = false;
int fd = -1;
@@ -125,18 +125,18 @@
* data to a temp file, the location of which varies.
*/
if (access("/tmp", W_OK) == 0)
- sprintf(tempName, "/tmp/dex-temp-%d", getpid());
+ sprintf(tempNameBuf, "/tmp/dex-temp-%d", getpid());
else
- sprintf(tempName, "/sdcard/dex-temp-%d", getpid());
+ sprintf(tempNameBuf, "/sdcard/dex-temp-%d", getpid());
- tempFileName = tempName;
+ tempFileName = tempNameBuf;
}
- result = dexUnzipToFile(fileName, tempName, quiet);
+ result = dexUnzipToFile(fileName, tempFileName, quiet);
if (result == kUTFRSuccess) {
- //printf("+++ Good unzip to '%s'\n", tempName);
- fileName = tempName;
+ //printf("+++ Good unzip to '%s'\n", tempFileName);
+ fileName = tempFileName;
removeTemp = true;
} else if (result == kUTFRNotZip) {
if (!quiet) {
@@ -177,8 +177,10 @@
if (fd >= 0)
close(fd);
if (removeTemp) {
- if (unlink(tempName) != 0)
- fprintf(stderr, "Warning: unable to remove temp '%s'\n", tempName);
+ if (unlink(tempFileName) != 0) {
+ fprintf(stderr, "Warning: unable to remove temp '%s'\n",
+ tempFileName);
+ }
}
return result;
}
diff --git a/libdex/CmdUtils.h b/libdex/CmdUtils.h
index fa354a9..e0b0105 100644
--- a/libdex/CmdUtils.h
+++ b/libdex/CmdUtils.h
@@ -34,6 +34,7 @@
/* encode the result of unzipping to a file */
typedef enum UnzipToFileResult {
kUTFRSuccess = 0,
+ kUTFRGenericFailure,
kUTFRBadArgs,
kUTFRNotZip,
kUTFRNoClassesDex,
diff --git a/tests/042-new-instance/expected.txt b/tests/042-new-instance/expected.txt
index 1ae2d3b..53447db 100644
--- a/tests/042-new-instance/expected.txt
+++ b/tests/042-new-instance/expected.txt
@@ -1,3 +1,8 @@
LocalClass succeeded
Got expected PackageAccess complaint
-LocalClass2 succeeded
+LocalClass3 succeeded
+Got expected InstantationError
+Cons LocalClass failed as expected
+Cons LocalClass2 succeeded
+Cons got expected PackageAccess complaint
+Cons got expected InstantationException
diff --git a/tests/042-new-instance/info.txt b/tests/042-new-instance/info.txt
index 08127da..49c9e02 100644
--- a/tests/042-new-instance/info.txt
+++ b/tests/042-new-instance/info.txt
@@ -1,6 +1,2 @@
-This is a miscellaneous test that was imported into the new-at-the-time
-runtime test framework. The test is intended to exercise basic features,
-and as such cannot be build on top of junit, since failure of such basic
-features might disrupt junit.
-
-TODO: Real description goes here.
+Test various permutations of Class.newInstance and Constructor.newInstance,
+looking for correct handling of access rights and abstract classes.
diff --git a/tests/042-new-instance/src/Main.java b/tests/042-new-instance/src/Main.java
index c77eb48..49894fe 100644
--- a/tests/042-new-instance/src/Main.java
+++ b/tests/042-new-instance/src/Main.java
@@ -1,10 +1,20 @@
// Copyright 2007 The Android Open Source Project
+import java.lang.reflect.Constructor;
+
/**
* Test instance creation.
*/
public class Main {
public static void main(String[] args) {
+ testClassNewInstance();
+ testConstructorNewInstance();
+ }
+
+ /**
+ * Tests Class.newInstance().
+ */
+ static void testClassNewInstance() {
// should succeed
try {
Class c = Class.forName("LocalClass");
@@ -27,22 +37,89 @@
ex.printStackTrace();
}
- LocalClass2.main();
+ LocalClass3.main();
+
+ try {
+ MaybeAbstract ma = new MaybeAbstract();
+ System.err.println("ERROR: MaybeAbstract succeeded unexpectedly");
+ } catch (InstantiationError ie) {
+ System.out.println("Got expected InstantationError");
+ } catch (Exception ex) {
+ System.err.println("Got unexpected MaybeAbstract failure");
+ }
+ }
+
+ /**
+ * Tests Constructor.newInstance().
+ */
+ static void testConstructorNewInstance() {
+ // should fail -- getConstructor only returns public constructors
+ try {
+ Class c = Class.forName("LocalClass");
+ Constructor cons = c.getConstructor(new Class[0] /*(Class[])null*/);
+ System.err.println("Cons LocalClass succeeded unexpectedly");
+ } catch (NoSuchMethodException nsme) {
+ System.out.println("Cons LocalClass failed as expected");
+ } catch (Exception ex) {
+ System.err.println("Cons LocalClass failed strangely");
+ ex.printStackTrace();
+ }
+
+ // should succeed
+ try {
+ Class c = Class.forName("LocalClass2");
+ Constructor cons = c.getConstructor((Class[]) null);
+ Object obj = cons.newInstance();
+ System.out.println("Cons LocalClass2 succeeded");
+ } catch (Exception ex) {
+ System.err.println("Cons LocalClass2 failed");
+ ex.printStackTrace();
+ }
+
+ // should fail
+ try {
+ Class c = Class.forName("otherpackage.PackageAccess");
+ Constructor cons = c.getConstructor(new Class[0] /*(Class[])null*/);
+ System.err.println("ERROR: Cons PackageAccess succeeded unexpectedly");
+ } catch (NoSuchMethodException nsme) {
+ System.out.println("Cons got expected PackageAccess complaint");
+ } catch (Exception ex) {
+ System.err.println("Cons got unexpected PackageAccess failure");
+ ex.printStackTrace();
+ }
+
+ // should fail
+ try {
+ Class c = Class.forName("MaybeAbstract");
+ Constructor cons = c.getConstructor(new Class[0] /*(Class[])null*/);
+ Object obj = cons.newInstance();
+ System.err.println("ERROR: Cons MaybeAbstract succeeded unexpectedly");
+ } catch (InstantiationException ie) {
+ // note InstantiationException vs. InstantiationError
+ System.out.println("Cons got expected InstantationException");
+ } catch (Exception ex) {
+ System.err.println("Cons got unexpected MaybeAbstract failure");
+ ex.printStackTrace();
+ }
}
}
class LocalClass {
- // this class has a default constructor with package visibility
+ // this class has a default constructor with package visibility
+}
+
+class LocalClass2 {
+ public LocalClass2() {}
}
-class LocalClass2 {
+class LocalClass3 {
public static void main() {
try {
CC.newInstance();
- System.out.println("LocalClass2 succeeded");
+ System.out.println("LocalClass3 succeeded");
} catch (Exception ex) {
- System.err.println("Got unexpected LocalClass2 failure");
+ System.err.println("Got unexpected LocalClass3 failure");
ex.printStackTrace();
}
}
diff --git a/tests/042-new-instance/src/MaybeAbstract.java b/tests/042-new-instance/src/MaybeAbstract.java
new file mode 100644
index 0000000..43c002b
--- /dev/null
+++ b/tests/042-new-instance/src/MaybeAbstract.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public /*abstract*/ class MaybeAbstract {
+ public MaybeAbstract() {}
+ int foo() { return 0; }
+}
+
diff --git a/tests/042-new-instance/src2/MaybeAbstract.java b/tests/042-new-instance/src2/MaybeAbstract.java
new file mode 100644
index 0000000..bfbfd45
--- /dev/null
+++ b/tests/042-new-instance/src2/MaybeAbstract.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public abstract class MaybeAbstract {
+ public MaybeAbstract() {}
+ int foo() { return 0; }
+}
+
diff --git a/vm/Android.mk b/vm/Android.mk
index 25a03fd..f3eed3f 100644
--- a/vm/Android.mk
+++ b/vm/Android.mk
@@ -41,42 +41,46 @@
LOCAL_CFLAGS += -DWITH_MONITOR_TRACKING
endif
-#
-# "Debug" profile:
-# - debugger enabled
-# - profiling enabled
-# - tracked-reference verification enabled
-# - allocation limits enabled
-# - GDB helpers enabled
-# - LOGV
-# - assert() (NDEBUG is handled in the build system)
-#
-ifeq ($(TARGET_BUILD_TYPE),debug)
-LOCAL_CFLAGS += -DWITH_INSTR_CHECKS -DWITH_EXTRA_OBJECT_VALIDATION
-LOCAL_CFLAGS += -DWITH_TRACKREF_CHECKS
-LOCAL_CFLAGS += -DWITH_ALLOC_LIMITS
-#LOCAL_CFLAGS += -DCHECK_MUTEX
-#LOCAL_CFLAGS += -DPROFILE_FIELD_ACCESS
-LOCAL_CFLAGS += -DDVM_SHOW_EXCEPTION=3
-# add some extra stuff to make it easier to examine with GDB
-LOCAL_CFLAGS += -DEASY_GDB
+# Make DEBUG_DALVIK_VM default to true when building the simulator.
+ifeq ($(TARGET_SIMULATOR),true)
+ ifeq ($(strip $(DEBUG_DALVIK_VM)),)
+ DEBUG_DALVIK_VM := true
+ endif
endif
-
-#
-# "Performance" profile:
-# - all development features disabled
-# - compiler optimizations enabled (redundant for "release" builds)
-# - (debugging and profiling still enabled)
-#
-ifeq ($(TARGET_BUILD_TYPE),release)
-#LOCAL_CFLAGS += -DNDEBUG -DLOG_NDEBUG=1
-# "-O2" is redundant for device (release) but useful for sim (debug)
-#LOCAL_CFLAGS += -O2 -Winline
-LOCAL_CFLAGS += -DDVM_SHOW_EXCEPTION=1
-# if you want to try with assertions on the device, add:
-#LOCAL_CFLAGS += -UNDEBUG -DDEBUG=1 -DLOG_NDEBUG=1 -DWITH_DALVIK_ASSERT
-endif
+ifeq ($(strip $(DEBUG_DALVIK_VM)),true)
+ #
+ # "Debug" profile:
+ # - debugger enabled
+ # - profiling enabled
+ # - tracked-reference verification enabled
+ # - allocation limits enabled
+ # - GDB helpers enabled
+ # - LOGV
+ # - assert() (NDEBUG is handled in the build system)
+ #
+ LOCAL_CFLAGS += -DWITH_INSTR_CHECKS -DWITH_EXTRA_OBJECT_VALIDATION
+ LOCAL_CFLAGS += -DWITH_TRACKREF_CHECKS
+ LOCAL_CFLAGS += -DWITH_ALLOC_LIMITS
+ #LOCAL_CFLAGS += -DCHECK_MUTEX
+ #LOCAL_CFLAGS += -DPROFILE_FIELD_ACCESS
+ LOCAL_CFLAGS += -DDVM_SHOW_EXCEPTION=3
+ # add some extra stuff to make it easier to examine with GDB
+ LOCAL_CFLAGS += -DEASY_GDB
+else # !DALVIK_VM_DEBUG
+ #
+ # "Performance" profile:
+ # - all development features disabled
+ # - compiler optimizations enabled (redundant for "release" builds)
+ # - (debugging and profiling still enabled)
+ #
+ #LOCAL_CFLAGS += -DNDEBUG -DLOG_NDEBUG=1
+ # "-O2" is redundant for device (release) but useful for sim (debug)
+ #LOCAL_CFLAGS += -O2 -Winline
+ LOCAL_CFLAGS += -DDVM_SHOW_EXCEPTION=1
+ # if you want to try with assertions on the device, add:
+ #LOCAL_CFLAGS += -UNDEBUG -DDEBUG=1 -DLOG_NDEBUG=1 -DWITH_DALVIK_ASSERT
+endif # !DALVIK_VM_DEBUG
# bug hunting: checksum and verify interpreted stack when making JNI calls
#LOCAL_CFLAGS += -DWITH_JNI_STACK_CHECK
diff --git a/vm/CheckJni.c b/vm/CheckJni.c
index 4791a01..0601e72 100644
--- a/vm/CheckJni.c
+++ b/vm/CheckJni.c
@@ -84,6 +84,8 @@
#define CHECK_FIELD_TYPE(_obj, _fieldid, _prim, _isstatic) \
checkFieldType(_obj, _fieldid, _prim, _isstatic, __FUNCTION__)
+#define CHECK_INST_FIELD_ID(_env, _obj, _fieldid) \
+ checkInstanceFieldID(_env, _obj, _fieldid, __FUNCTION__)
#define CHECK_CLASS(_env, _clazz) \
checkClass(_env, _clazz, __FUNCTION__)
#define CHECK_STRING(_env, _str) \
@@ -638,8 +640,15 @@
/*
* Verify that this instance field ID is valid for this object.
*/
-static void checkInstanceFieldID(JNIEnv* env, jobject obj, jfieldID fieldID)
+static void checkInstanceFieldID(JNIEnv* env, jobject obj, jfieldID fieldID,
+ const char* func)
{
+ if (obj == NULL) {
+ LOGW("JNI WARNING: invalid null object (%s)\n", func);
+ abortMaybe();
+ return;
+ }
+
ClassObject* clazz = ((Object*)obj)->clazz;
/*
@@ -736,22 +745,35 @@
/*
* Verify the guard area and, if "modOkay" is false, that the data itself
* has not been altered.
+ *
+ * The caller has already checked that "dataBuf" is non-NULL.
*/
static bool checkGuardedCopy(const void* dataBuf, bool modOkay)
{
+ static const u4 kMagicCmp = kGuardMagic;
const u1* fullBuf = ((const u1*) dataBuf) - kGuardLen / 2;
const GuardExtra* pExtra = getGuardExtra(dataBuf);
- size_t len = pExtra->originalLen;
+ size_t len;
const u2* pat;
int i;
- if (pExtra->magic != kGuardMagic) {
- LOGE("JNI: guard magic does not match (found 0x%08x) "
+ /*
+ * Before we do anything with "pExtra", check the magic number. We
+ * do the check with memcmp rather than "==" in case the pointer is
+ * unaligned. If it points to completely bogus memory we're going
+ * to crash, but there's no easy way around that.
+ */
+ if (memcmp(&pExtra->magic, &kMagicCmp, 4) != 0) {
+ u1 buf[4];
+ memcpy(buf, &pExtra->magic, 4);
+ LOGE("JNI: guard magic does not match (found 0x%02x%02x%02x%02x) "
"-- incorrect data pointer %p?\n",
- pExtra->magic, dataBuf);
+ buf[3], buf[2], buf[1], buf[0], dataBuf); /* assume little endian */
return false;
}
+ len = pExtra->originalLen;
+
/* check bottom half of guard; skip over optional checksum storage */
pat = (u2*) fullBuf;
for (i = kGuardExtra / 2; i < (int) (kGuardLen / 2 - kGuardExtra) / 2; i++)
@@ -889,6 +911,7 @@
if (!checkGuardedCopy(dataBuf, true)) {
LOGE("JNI: failed guarded copy check in releaseGuardedPACopy\n");
abortMaybe();
+ return NULL;
}
switch (mode) {
@@ -1328,7 +1351,7 @@
CHECK_ENTER(env, kFlag_Default); \
CHECK_OBJECT(env, obj); \
_ctype result; \
- checkInstanceFieldID(env, obj, fieldID); \
+ CHECK_INST_FIELD_ID(env, obj, fieldID); \
result = BASE_ENV(env)->Get##_jname##Field(env, obj, fieldID); \
CHECK_EXIT(env); \
return result; \
@@ -1349,7 +1372,7 @@
{ \
CHECK_ENTER(env, kFlag_Default); \
CHECK_OBJECT(env, obj); \
- checkInstanceFieldID(env, obj, fieldID); \
+ CHECK_INST_FIELD_ID(env, obj, fieldID); \
CHECK_FIELD_TYPE((jobject)(u4) value, fieldID, _ftype, false); \
BASE_ENV(env)->Set##_jname##Field(env, obj, fieldID, value); \
CHECK_EXIT(env); \
@@ -1571,6 +1594,7 @@
if (!checkGuardedCopy(chars, false)) {
LOGE("JNI: failed guarded copy check in ReleaseStringChars\n");
abortMaybe();
+ return;
}
chars = (const jchar*) freeGuardedCopy((jchar*)chars);
}
@@ -1626,6 +1650,7 @@
if (!checkGuardedCopy(utf, false)) {
LOGE("JNI: failed guarded copy check in ReleaseStringUTFChars\n");
abortMaybe();
+ return;
}
utf = (const char*) freeGuardedCopy((char*)utf);
}
@@ -1892,6 +1917,7 @@
if (!checkGuardedCopy(carray, false)) {
LOGE("JNI: failed guarded copy check in ReleaseStringCritical\n");
abortMaybe();
+ return;
}
carray = (const jchar*) freeGuardedCopy((jchar*)carray);
}
@@ -1945,6 +1971,7 @@
LOGW("JNI WARNING: invalid values for address (%p) or capacity (%ld)\n",
address, (long) capacity);
abortMaybe();
+ return NULL;
}
result = BASE_ENV(env)->NewDirectByteBuffer(env, address, capacity);
CHECK_EXIT(env);
diff --git a/vm/Globals.h b/vm/Globals.h
index 2ac73ce..79b9e91 100644
--- a/vm/Globals.h
+++ b/vm/Globals.h
@@ -144,6 +144,13 @@
volatile int classSerialNumber;
/*
+ * Classes with a low classSerialNumber are probably in the zygote, and
+ * their InitiatingLoaderList is not used, to promote sharing. The list is
+ * kept here instead.
+ */
+ InitiatingLoaderList* initiatingLoaderList;
+
+ /*
* Interned strings.
*/
HashTable* internedStrings;
diff --git a/vm/Profile.c b/vm/Profile.c
index 9b47885..6055580 100644
--- a/vm/Profile.c
+++ b/vm/Profile.c
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
/*
* Android's method call profiling goodies.
*/
@@ -28,10 +29,10 @@
#include <sys/mman.h>
#include <sched.h>
#include <errno.h>
+#include <fcntl.h>
#ifdef HAVE_ANDROID_OS
# define UPDATE_MAGIC_PAGE 1
-# define MAGIC_PAGE_BASE_ADDR 0x08000000
# ifndef PAGESIZE
# define PAGESIZE 4096
# endif
@@ -176,13 +177,20 @@
* We could key this off of the "ro.kernel.qemu" property, but there's
* no real harm in doing this on a real device.
*/
- gDvm.emulatorTracePage = mmap((void*) MAGIC_PAGE_BASE_ADDR,
- PAGESIZE, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_SHARED|MAP_ANON, -1, 0);
- if (gDvm.emulatorTracePage == MAP_FAILED) {
- LOGE("Unable to mmap magic page (0x%08x)\n", MAGIC_PAGE_BASE_ADDR);
- return false;
+ int fd = open("/dev/qemu_trace", O_RDWR);
+ if (fd < 0) {
+ LOGV("Unable to open /dev/qemu_trace\n");
+ } else {
+ gDvm.emulatorTracePage = mmap(0, PAGESIZE, PROT_READ|PROT_WRITE,
+ MAP_SHARED, fd, 0);
+ close(fd);
+ if (gDvm.emulatorTracePage == MAP_FAILED) {
+ LOGE("Unable to mmap /dev/qemu_trace\n");
+ gDvm.emulatorTracePage = NULL;
+ } else {
+ *(u4*) gDvm.emulatorTracePage = 0;
+ }
}
- *(u4*) gDvm.emulatorTracePage = 0;
#else
assert(gDvm.emulatorTracePage == NULL);
#endif
@@ -388,6 +396,7 @@
return;
fail:
+ updateActiveProfilers(-1);
if (state->traceFile != NULL) {
fclose(state->traceFile);
state->traceFile = NULL;
@@ -448,6 +457,15 @@
}
/*
+ * Returns "true" if method tracing is currently active.
+ */
+bool dvmIsMethodTraceActive(void)
+{
+ const MethodTraceState* state = &gDvm.methodTrace;
+ return state->traceEnabled;
+}
+
+/*
* Stop method tracing. We write the buffer to disk and generate a key
* file so we can interpret it.
*/
@@ -464,6 +482,7 @@
if (!state->traceEnabled) {
/* somebody already stopped it, or it was never started */
+ LOGD("TRACE stop requested, but not running\n");
dvmUnlockMutex(&state->startStopLock);
return;
} else {
@@ -627,7 +646,7 @@
return;
assert(method->insns != NULL);
- u4* pMagic = ((u4*) MAGIC_PAGE_BASE_ADDR) +1;
+ u4* pMagic = (u4*) gDvm.emulatorTracePage;
/*
* The dexlist output shows the &DexCode.insns offset value, which
@@ -690,6 +709,10 @@
*/
void dvmEmulatorTraceStart(void)
{
+ /* If we could not map the emulator trace page, then do not enable tracing */
+ if (gDvm.emulatorTracePage == NULL)
+ return;
+
updateActiveProfilers(1);
/* in theory we should make this an atomic inc; in practice not important */
@@ -705,7 +728,7 @@
{
if (gDvm.emulatorTraceEnableCount == 0) {
LOGE("ERROR: emulator tracing not enabled\n");
- dvmAbort();
+ return;
}
updateActiveProfilers(-1);
/* in theory we should make this an atomic inc; in practice not important */
diff --git a/vm/Profile.h b/vm/Profile.h
index f762974..cdaf027 100644
--- a/vm/Profile.h
+++ b/vm/Profile.h
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
/*
* Android's method call profiling goodies.
*/
@@ -96,6 +97,7 @@
* Start/stop method tracing.
*/
void dvmMethodTraceStart(const char* traceFileName, int bufferSize, int flags);
+bool dvmIsMethodTraceActive(void);
void dvmMethodTraceStop(void);
/*
diff --git a/vm/Thread.c b/vm/Thread.c
index 42b527e..acfc7a9 100644
--- a/vm/Thread.c
+++ b/vm/Thread.c
@@ -43,6 +43,9 @@
#undef __KERNEL__
#endif
+// Change this to enable logging on cgroup errors
+#define ENABLE_CGROUP_ERR_LOGGING 0
+
// change this to LOGV/LOGD to debug thread activity
#define LOG_THREAD LOGVV
@@ -624,6 +627,7 @@
/*
* Finish preparing the main thread, allocating some objects to represent
* it. As part of doing so, we finish initializing Thread and ThreadGroup.
+ * This will execute some interpreted code (e.g. class initializers).
*/
bool dvmPrepMainThread(void)
{
@@ -711,7 +715,8 @@
/*
* Stuff the VMThread back into the Thread. From this point on, other
- * Threads will see that this Thread is running.
+ * Threads will see that this Thread is running (at least, they would,
+ * if there were any).
*/
dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread,
vmThreadObj);
@@ -719,6 +724,24 @@
thread->threadObj = threadObj;
/*
+ * Set the context class loader. This invokes a ClassLoader method,
+ * which could conceivably call Thread.currentThread(), so we want the
+ * Thread to be fully configured before we do this.
+ */
+ Object* systemLoader = dvmGetSystemClassLoader();
+ if (systemLoader == NULL) {
+ LOGW("WARNING: system class loader is NULL (setting main ctxt)\n");
+ /* keep going */
+ }
+ int ctxtClassLoaderOffset = dvmFindFieldOffset(gDvm.classJavaLangThread,
+ "contextClassLoader", "Ljava/lang/ClassLoader;");
+ if (ctxtClassLoaderOffset < 0) {
+ LOGE("Unable to find contextClassLoader field in Thread\n");
+ return false;
+ }
+ dvmSetFieldObject(threadObj, ctxtClassLoaderOffset, systemLoader);
+
+ /*
* Finish our thread prep.
*/
@@ -2745,6 +2768,41 @@
};
/*
+ * Change the scheduler cgroup of a pid
+ */
+int dvmChangeThreadSchedulerGroup(const char *cgroup)
+{
+#ifdef HAVE_ANDROID_OS
+ FILE *fp;
+ char path[255];
+ int rc;
+
+ sprintf(path, "/dev/cpuctl/%s/tasks", (cgroup ? cgroup : ""));
+
+ if (!(fp = fopen(path, "w"))) {
+#if ENABLE_CGROUP_ERR_LOGGING
+ LOGW("Unable to open %s (%s)\n", path, strerror(errno));
+#endif
+ return -errno;
+ }
+
+ rc = fprintf(fp, "0");
+ fclose(fp);
+
+ if (rc < 0) {
+#if ENABLE_CGROUP_ERR_LOGGING
+ LOGW("Unable to move pid %d to cgroup %s (%s)\n", getpid(),
+ (cgroup ? cgroup : "<default>"), strerror(errno));
+#endif
+ }
+
+ return (rc < 0) ? errno : 0;
+#else // HAVE_ANDROID_OS
+ return 0;
+#endif
+}
+
+/*
* Change the priority of a system thread to match that of the Thread object.
*
* We map a priority value from 1-10 to Linux "nice" values, where lower
@@ -2761,6 +2819,12 @@
}
newNice = kNiceValues[newPriority-1];
+ if (newPriority == ANDROID_PRIORITY_BACKGROUND) {
+ dvmChangeThreadSchedulerGroup("bg_non_interactive");
+ } else if (getpriority(PRIO_PROCESS, pid) == ANDROID_PRIORITY_BACKGROUND) {
+ dvmChangeThreadSchedulerGroup(NULL);
+ }
+
if (setpriority(PRIO_PROCESS, pid, newNice) != 0) {
char* str = dvmGetThreadName(thread);
LOGI("setPriority(%d) '%s' to prio=%d(n=%d) failed: %s\n",
diff --git a/vm/Thread.h b/vm/Thread.h
index b64f9b7..86a7845 100644
--- a/vm/Thread.h
+++ b/vm/Thread.h
@@ -398,6 +398,11 @@
INLINE void dvmSetThreadJNIEnv(Thread* self, JNIEnv* env) { self->jniEnv = env;}
/*
+ * Change the scheduler group of the current process
+ */
+int dvmChangeThreadSchedulerGroup(const char *group);
+
+/*
* Update the priority value of the underlying pthread.
*/
void dvmChangeThreadPriority(Thread* thread, int newPriority);
diff --git a/vm/alloc/Heap.c b/vm/alloc/Heap.c
index 9ddc8be..09954ea 100644
--- a/vm/alloc/Heap.c
+++ b/vm/alloc/Heap.c
@@ -774,6 +774,11 @@
/* Current value is numerically greater than "normal", which
* in backward UNIX terms means lower priority.
*/
+
+ if (priorityResult == ANDROID_PRIORITY_BACKGROUND) {
+ dvmChangeThreadSchedulerGroup(NULL);
+ }
+
if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_NORMAL) != 0) {
LOGI_HEAP("Unable to elevate priority from %d to %d\n",
priorityResult, ANDROID_PRIORITY_NORMAL);
@@ -831,6 +836,8 @@
if (gcHeap->hprofDumpOnGc) {
char nameBuf[128];
+ gcHeap->hprofResult = -1;
+
if (gcHeap->hprofFileName == NULL) {
/* no filename was provided; invent one */
sprintf(nameBuf, "/data/misc/heap-dump-tm%d-pid%d.hprof",
@@ -982,7 +989,8 @@
if (gcHeap->hprofContext != NULL) {
hprofFinishHeapDump(gcHeap->hprofContext);
//TODO: write a HEAP_SUMMARY record
- hprofShutdown(gcHeap->hprofContext);
+ if (hprofShutdown(gcHeap->hprofContext))
+ gcHeap->hprofResult = 0; /* indicate success */
gcHeap->hprofContext = NULL;
}
#endif
@@ -1016,6 +1024,10 @@
} else {
LOGD_HEAP("Reset priority to %d\n", oldThreadPriority);
}
+
+ if (oldThreadPriority == ANDROID_PRIORITY_BACKGROUND) {
+ dvmChangeThreadSchedulerGroup("bg_non_interactive");
+ }
}
gcElapsedTime = (dvmGetRelativeTimeUsec() - gcHeap->gcStartTime) / 1000;
if (gcElapsedTime < 10000) {
@@ -1046,16 +1058,23 @@
* Perform garbage collection, writing heap information to the specified file.
*
* If "fileName" is NULL, a suitable name will be generated automatically.
+ *
+ * Returns 0 on success, or an error code on failure.
*/
-void hprofDumpHeap(const char* fileName)
+int hprofDumpHeap(const char* fileName)
{
+ int result;
+
dvmLockMutex(&gDvm.gcHeapLock);
gDvm.gcHeap->hprofDumpOnGc = true;
gDvm.gcHeap->hprofFileName = fileName;
dvmCollectGarbageInternal(false);
+ result = gDvm.gcHeap->hprofResult;
dvmUnlockMutex(&gDvm.gcHeapLock);
+
+ return result;
}
void dvmHeapSetHprofGcScanState(hprof_heap_tag_t state, u4 threadSerialNumber)
diff --git a/vm/alloc/HeapInternal.h b/vm/alloc/HeapInternal.h
index 7851983..fafb87a 100644
--- a/vm/alloc/HeapInternal.h
+++ b/vm/alloc/HeapInternal.h
@@ -189,6 +189,7 @@
bool hprofDumpOnGc;
const char* hprofFileName;
hprof_context_t *hprofContext;
+ int hprofResult;
#endif
};
diff --git a/vm/analysis/CodeVerify.c b/vm/analysis/CodeVerify.c
index 65aa833..8c5e8d2 100644
--- a/vm/analysis/CodeVerify.c
+++ b/vm/analysis/CodeVerify.c
@@ -5163,23 +5163,20 @@
if (!checkMoveException(meth, insnIdx+insnWidth, "next"))
goto bail;
- /*
- * We want to update the registers and set the "changed" flag on the
- * next instruction (if necessary). We may not be storing register
- * changes for all addresses, so for non-branch targets we just
- * compare "entry" vs. "work" to see if we've changed anything.
- */
if (getRegisterLine(regTable, insnIdx+insnWidth) != NULL) {
+ /*
+ * Merge registers into what we have for the next instruction,
+ * and set the "changed" flag if needed.
+ */
updateRegisters(meth, insnFlags, regTable, insnIdx+insnWidth,
workRegs);
} else {
- /* if not yet visited, or regs were updated, set "changed" */
- if (!dvmInsnIsVisited(insnFlags, insnIdx+insnWidth) ||
- compareRegisters(workRegs, entryRegs,
- insnRegCount + kExtraRegs) != 0)
- {
- dvmInsnSetChanged(insnFlags, insnIdx+insnWidth, true);
- }
+ /*
+ * We're not recording register data for the next instruction,
+ * so we don't know what the prior state was. We have to
+ * assume that something has changed and re-evaluate it.
+ */
+ dvmInsnSetChanged(insnFlags, insnIdx+insnWidth, true);
}
}
diff --git a/vm/hprof/Hprof.c b/vm/hprof/Hprof.c
index 66b46f4..2e6f7c9 100644
--- a/vm/hprof/Hprof.c
+++ b/vm/hprof/Hprof.c
@@ -111,7 +111,10 @@
}
}
-void
+/*
+ * Finish up the hprof dump. Returns true on success.
+ */
+bool
hprofShutdown(hprof_context_t *ctx)
{
FILE *tempFp = ctx->fp;
@@ -131,7 +134,7 @@
fclose(tempFp);
free(ctx->fileName);
free(ctx);
- return;
+ return false;
}
hprofContextInit(ctx, ctx->fileName, fp, true);
@@ -179,4 +182,5 @@
/* throw out a log message for the benefit of "runhat" */
LOGI("hprof: heap dump completed, temp file removed\n");
+ return true;
}
diff --git a/vm/hprof/Hprof.h b/vm/hprof/Hprof.h
index e0e2d4b..696b0a7 100644
--- a/vm/hprof/Hprof.h
+++ b/vm/hprof/Hprof.h
@@ -235,7 +235,7 @@
*/
hprof_context_t *hprofStartup(const char *outputFileName);
-void hprofShutdown(hprof_context_t *ctx);
+bool hprofShutdown(hprof_context_t *ctx);
/*
* Heap.c functions
@@ -244,7 +244,7 @@
* the heap implementation; these functions require heap knowledge,
* so they are implemented in Heap.c.
*/
-void hprofDumpHeap(const char* fileName);
+int hprofDumpHeap(const char* fileName);
void dvmHeapSetHprofGcScanState(hprof_heap_tag_t state, u4 threadSerialNumber);
#endif // _DALVIK_HPROF_HPROF
diff --git a/vm/mterp/armv4/OP_AGET_WIDE.S b/vm/mterp/armv4t/OP_AGET_WIDE.S
similarity index 100%
rename from vm/mterp/armv4/OP_AGET_WIDE.S
rename to vm/mterp/armv4t/OP_AGET_WIDE.S
diff --git a/vm/mterp/armv4/OP_APUT_WIDE.S b/vm/mterp/armv4t/OP_APUT_WIDE.S
similarity index 100%
rename from vm/mterp/armv4/OP_APUT_WIDE.S
rename to vm/mterp/armv4t/OP_APUT_WIDE.S
diff --git a/vm/mterp/armv4/OP_IGET_WIDE.S b/vm/mterp/armv4t/OP_IGET_WIDE.S
similarity index 100%
rename from vm/mterp/armv4/OP_IGET_WIDE.S
rename to vm/mterp/armv4t/OP_IGET_WIDE.S
diff --git a/vm/mterp/armv4/OP_IGET_WIDE_QUICK.S b/vm/mterp/armv4t/OP_IGET_WIDE_QUICK.S
similarity index 100%
rename from vm/mterp/armv4/OP_IGET_WIDE_QUICK.S
rename to vm/mterp/armv4t/OP_IGET_WIDE_QUICK.S
diff --git a/vm/mterp/armv4/OP_IPUT_WIDE.S b/vm/mterp/armv4t/OP_IPUT_WIDE.S
similarity index 100%
rename from vm/mterp/armv4/OP_IPUT_WIDE.S
rename to vm/mterp/armv4t/OP_IPUT_WIDE.S
diff --git a/vm/mterp/armv4/OP_IPUT_WIDE_QUICK.S b/vm/mterp/armv4t/OP_IPUT_WIDE_QUICK.S
similarity index 100%
rename from vm/mterp/armv4/OP_IPUT_WIDE_QUICK.S
rename to vm/mterp/armv4t/OP_IPUT_WIDE_QUICK.S
diff --git a/vm/mterp/armv4/OP_SGET_WIDE.S b/vm/mterp/armv4t/OP_SGET_WIDE.S
similarity index 100%
rename from vm/mterp/armv4/OP_SGET_WIDE.S
rename to vm/mterp/armv4t/OP_SGET_WIDE.S
diff --git a/vm/mterp/armv4/OP_SPUT_WIDE.S b/vm/mterp/armv4t/OP_SPUT_WIDE.S
similarity index 100%
rename from vm/mterp/armv4/OP_SPUT_WIDE.S
rename to vm/mterp/armv4t/OP_SPUT_WIDE.S
diff --git a/vm/mterp/armv4/platform.S b/vm/mterp/armv4t/platform.S
similarity index 100%
rename from vm/mterp/armv4/platform.S
rename to vm/mterp/armv4t/platform.S
diff --git a/vm/mterp/config-armv4 b/vm/mterp/config-armv4t
similarity index 80%
rename from vm/mterp/config-armv4
rename to vm/mterp/config-armv4t
index 4f64c11..01eddb2 100644
--- a/vm/mterp/config-armv4
+++ b/vm/mterp/config-armv4t
@@ -13,8 +13,8 @@
# limitations under the License.
#
-# Configuration for ARMv4 architecture targets. This is largely pulled
-# from the ARMv5 sources, but we can't use certain instructions introduced
+# Configuration for ARMv4T architecture targets. This is largely pulled
+# from the ARMv5TE sources, but we can't use certain instructions introduced
# in ARMv5 (BLX, CLZ, LDC2, MCR2, MRC2, STC2) or ARMv5TE (PLD, LDRD, MCRR,
# MRRC, QADD, QDADD, QDSUB, QSUB, SMLA, SMLAL, SMLAW, SMUL, SMULW, STRD).
#
@@ -42,14 +42,14 @@
# opcode list; argument to op-start is default directory
op-start armv5te
- op OP_AGET_WIDE armv4
- op OP_APUT_WIDE armv4
- op OP_IGET_WIDE armv4
- op OP_IGET_WIDE_QUICK armv4
- op OP_IPUT_WIDE armv4
- op OP_IPUT_WIDE_QUICK armv4
- op OP_SGET_WIDE armv4
- op OP_SPUT_WIDE armv4
+ op OP_AGET_WIDE armv4t
+ op OP_APUT_WIDE armv4t
+ op OP_IGET_WIDE armv4t
+ op OP_IGET_WIDE_QUICK armv4t
+ op OP_IPUT_WIDE armv4t
+ op OP_IPUT_WIDE_QUICK armv4t
+ op OP_SGET_WIDE armv4t
+ op OP_SPUT_WIDE armv4t
op-end
# "helper" code for C; include if you use any of the C stubs (this generates
diff --git a/vm/mterp/out/InterpAsm-armv4.S b/vm/mterp/out/InterpAsm-armv4t.S
similarity index 99%
rename from vm/mterp/out/InterpAsm-armv4.S
rename to vm/mterp/out/InterpAsm-armv4t.S
index 3de7aea..d60571b 100644
--- a/vm/mterp/out/InterpAsm-armv4.S
+++ b/vm/mterp/out/InterpAsm-armv4t.S
@@ -1,5 +1,5 @@
/*
- * This file was generated automatically by gen-mterp.py for 'armv4'.
+ * This file was generated automatically by gen-mterp.py for 'armv4t'.
*
* --> DO NOT EDIT <--
*/
@@ -1855,7 +1855,7 @@
/* ------------------------------ */
.balign 64
.L_OP_AGET_WIDE: /* 0x45 */
-/* File: armv4/OP_AGET_WIDE.S */
+/* File: armv4t/OP_AGET_WIDE.S */
/*
* Array get, 64 bits. vAA <- vBB[vCC].
*
@@ -2078,7 +2078,7 @@
/* ------------------------------ */
.balign 64
.L_OP_APUT_WIDE: /* 0x4c */
-/* File: armv4/OP_APUT_WIDE.S */
+/* File: armv4t/OP_APUT_WIDE.S */
/*
* Array put, 64 bits. vBB[vCC] <- vAA.
*/
@@ -2289,7 +2289,7 @@
/* ------------------------------ */
.balign 64
.L_OP_IGET_WIDE: /* 0x53 */
-/* File: armv4/OP_IGET_WIDE.S */
+/* File: armv4t/OP_IGET_WIDE.S */
/*
* Wide 32-bit instance field get.
*/
@@ -2483,7 +2483,7 @@
/* ------------------------------ */
.balign 64
.L_OP_IPUT_WIDE: /* 0x5a */
-/* File: armv4/OP_IPUT_WIDE.S */
+/* File: armv4t/OP_IPUT_WIDE.S */
/* iput-wide vA, vB, field@CCCC */
mov r0, rINST, lsr #12 @ r0<- B
ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
@@ -2672,7 +2672,7 @@
/* ------------------------------ */
.balign 64
.L_OP_SGET_WIDE: /* 0x61 */
-/* File: armv4/OP_SGET_WIDE.S */
+/* File: armv4t/OP_SGET_WIDE.S */
/*
* 64-bit SGET handler.
*/
@@ -2850,7 +2850,7 @@
/* ------------------------------ */
.balign 64
.L_OP_SPUT_WIDE: /* 0x68 */
-/* File: armv4/OP_SPUT_WIDE.S */
+/* File: armv4t/OP_SPUT_WIDE.S */
/*
* 64-bit SPUT handler.
*/
@@ -7522,7 +7522,7 @@
/* ------------------------------ */
.balign 64
.L_OP_IGET_WIDE_QUICK: /* 0xf3 */
-/* File: armv4/OP_IGET_WIDE_QUICK.S */
+/* File: armv4t/OP_IGET_WIDE_QUICK.S */
/* iget-wide-quick vA, vB, offset@CCCC */
mov r2, rINST, lsr #12 @ r2<- B
GET_VREG(r3, r2) @ r3<- object we're operating on
@@ -7585,7 +7585,7 @@
/* ------------------------------ */
.balign 64
.L_OP_IPUT_WIDE_QUICK: /* 0xf6 */
-/* File: armv4/OP_IPUT_WIDE_QUICK.S */
+/* File: armv4t/OP_IPUT_WIDE_QUICK.S */
/* iput-wide-quick vA, vB, offset@CCCC */
mov r0, rINST, lsr #8 @ r0<- A(+)
mov r1, rINST, lsr #12 @ r1<- B
diff --git a/vm/mterp/out/InterpC-armv4.c b/vm/mterp/out/InterpC-armv4t.c
similarity index 99%
rename from vm/mterp/out/InterpC-armv4.c
rename to vm/mterp/out/InterpC-armv4t.c
index 2fcdcab..7f101a9 100644
--- a/vm/mterp/out/InterpC-armv4.c
+++ b/vm/mterp/out/InterpC-armv4t.c
@@ -1,5 +1,5 @@
/*
- * This file was generated automatically by gen-mterp.py for 'armv4'.
+ * This file was generated automatically by gen-mterp.py for 'armv4t'.
*
* --> DO NOT EDIT <--
*/
diff --git a/vm/mterp/rebuild.sh b/vm/mterp/rebuild.sh
index d1c6983..1380f69 100755
--- a/vm/mterp/rebuild.sh
+++ b/vm/mterp/rebuild.sh
@@ -19,7 +19,7 @@
# generated as part of the build.
#
set -e
-for arch in portstd portdbg allstubs armv4 armv5te x86; do TARGET_ARCH_EXT=$arch make -f Makefile-mterp; done
+for arch in portstd portdbg allstubs armv4t armv5te x86; do TARGET_ARCH_EXT=$arch make -f Makefile-mterp; done
# These aren't actually used, so just go ahead and remove them. The correct
# approach is to prevent them from being generated in the first place, but
diff --git a/vm/native/dalvik_system_VMDebug.c b/vm/native/dalvik_system_VMDebug.c
index 9eccb76..ec6d92e 100644
--- a/vm/native/dalvik_system_VMDebug.c
+++ b/vm/native/dalvik_system_VMDebug.c
@@ -244,6 +244,23 @@
}
/*
+ * static boolean isMethodTracingActive()
+ *
+ * Determine whether method tracing is currently active.
+ */
+static void Dalvik_dalvik_system_VMDebug_isMethodTracingActive(const u4* args,
+ JValue* pResult)
+{
+ UNUSED_PARAMETER(args);
+
+#ifdef WITH_PROFILER
+ RETURN_BOOLEAN(dvmIsMethodTraceActive());
+#else
+ RETURN_BOOLEAN(false);
+#endif
+}
+
+/*
* static void stopMethodTracing()
*
* Stop method tracing.
@@ -527,6 +544,7 @@
#ifdef WITH_HPROF
StringObject* fileNameStr = (StringObject*) args[0];
char* fileName;
+ int result;
if (fileNameStr == NULL) {
dvmThrowException("Ljava/lang/NullPointerException;", NULL);
@@ -540,8 +558,15 @@
RETURN_VOID();
}
- hprofDumpHeap(fileName);
+ result = hprofDumpHeap(fileName);
free(fileName);
+
+ if (result != 0) {
+ /* ideally we'd throw something more specific based on actual failure */
+ dvmThrowException("Ljava/lang/RuntimeException;",
+ "Failure during heap dump -- check log output for details");
+ RETURN_VOID();
+ }
#else
dvmThrowException("Ljava/lang/UnsupportedOperationException;", NULL);
#endif
@@ -562,6 +587,8 @@
Dalvik_dalvik_system_VMDebug_stopAllocCounting },
{ "startMethodTracing", "(Ljava/lang/String;II)V",
Dalvik_dalvik_system_VMDebug_startMethodTracing },
+ { "isMethodTracingActive", "()Z",
+ Dalvik_dalvik_system_VMDebug_isMethodTracingActive },
{ "stopMethodTracing", "()V",
Dalvik_dalvik_system_VMDebug_stopMethodTracing },
{ "startEmulatorTracing", "()V",
diff --git a/vm/native/java_lang_reflect_Constructor.c b/vm/native/java_lang_reflect_Constructor.c
index 82b72ee..6878f7b 100644
--- a/vm/native/java_lang_reflect_Constructor.c
+++ b/vm/native/java_lang_reflect_Constructor.c
@@ -39,6 +39,12 @@
/*
* public int constructNative(Object[] args, Class declaringClass,
* Class[] parameterTypes, int slot, boolean noAccessCheck)
+ *
+ * We get here through Constructor.newInstance(). The Constructor object
+ * would not be available if the constructor weren't public (per the
+ * definition of Class.getConstructor), so we can skip the method access
+ * check. We can also safely assume the constructor isn't associated
+ * with an interface, array, or primitive class.
*/
static void Dalvik_java_lang_reflect_Constructor_constructNative(
const u4* args, JValue* pResult)
@@ -52,6 +58,22 @@
Object* newObj;
Method* meth;
+ if (dvmIsAbstractClass(declaringClass)) {
+ dvmThrowExceptionWithClassMessage("Ljava/lang/InstantiationException;",
+ declaringClass->descriptor);
+ RETURN_VOID();
+ }
+
+ /* initialize the class if it hasn't been already */
+ if (!dvmIsClassInitialized(declaringClass)) {
+ if (!dvmInitClass(declaringClass)) {
+ LOGW("Class init failed in Constructor.constructNative (%s)\n",
+ declaringClass->descriptor);
+ assert(dvmCheckException(dvmThreadSelf()));
+ RETURN_VOID();
+ }
+ }
+
newObj = dvmAllocObject(declaringClass, ALLOC_DEFAULT);
if (newObj == NULL)
RETURN_PTR(NULL);
diff --git a/vm/oo/Class.c b/vm/oo/Class.c
index c7ab763..47e2a87 100644
--- a/vm/oo/Class.c
+++ b/vm/oo/Class.c
@@ -151,6 +151,9 @@
may not be worth the performance hit.
*/
+#define INITIAL_CLASS_SERIAL_NUMBER 0x50000000
+#define ZYGOTE_CLASS_CUTOFF 2000
+
static ClassPathEntry* processClassPath(const char* pathStr, bool isBootstrap);
static void freeCpeArray(ClassPathEntry* cpe);
@@ -293,8 +296,15 @@
* Class serial number. We start with a high value to make it distinct
* in binary dumps (e.g. hprof).
*/
- gDvm.classSerialNumber = 0x50000000;
+ gDvm.classSerialNumber = INITIAL_CLASS_SERIAL_NUMBER;
+ /* Set up the table we'll use for tracking initiating loaders for
+ * early classes.
+ * If it's NULL, we just fall back to the InitiatingLoaderList in the
+ * ClassObject, so it's not fatal to fail this allocation.
+ */
+ gDvm.initiatingLoaderList =
+ calloc(ZYGOTE_CLASS_CUTOFF, sizeof(InitiatingLoaderList));
/* This placeholder class is used while a ClassObject is
* loading/linking so those not in the know can still say
@@ -346,6 +356,8 @@
gDvm.bootClassPath = NULL;
dvmLinearAllocDestroy(NULL);
+
+ free(gDvm.initiatingLoaderList);
}
@@ -797,6 +809,18 @@
#define kInitLoaderInc 4 /* must be power of 2 */
+static InitiatingLoaderList *dvmGetInitiatingLoaderList(ClassObject* clazz)
+{
+ assert(clazz->serialNumber > INITIAL_CLASS_SERIAL_NUMBER);
+ int classIndex = clazz->serialNumber-INITIAL_CLASS_SERIAL_NUMBER;
+ if (gDvm.initiatingLoaderList != NULL &&
+ classIndex < ZYGOTE_CLASS_CUTOFF) {
+ return &(gDvm.initiatingLoaderList[classIndex]);
+ } else {
+ return &(clazz->initiatingLoaderList);
+ }
+}
+
/*
* Determine if "loader" appears in clazz' initiating loader list.
*
@@ -820,9 +844,13 @@
/*
* Scan the list for a match. The list is expected to be short.
*/
+ /* Cast to remove the const from clazz, but use const loaderList */
+ ClassObject* nonConstClazz = (ClassObject*) clazz;
+ const InitiatingLoaderList *loaderList =
+ dvmGetInitiatingLoaderList(nonConstClazz);
int i;
- for (i = clazz->initiatingLoaderCount-1; i >= 0; --i) {
- if (clazz->initiatingLoaders[i] == loader) {
+ for (i = loaderList->initiatingLoaderCount-1; i >= 0; --i) {
+ if (loaderList->initiatingLoaders[i] == loader) {
//LOGI("+++ found initiating match %p in %s\n",
// loader, clazz->descriptor);
return true;
@@ -854,10 +882,10 @@
* pretty minor, and probably outweighs the O(n^2) hit for
* checking before every add, so we may not want to do this.
*/
- if (false && dvmLoaderInInitiatingList(clazz, loader)) {
- LOGW("WOW: simultaneous add of initiating class loader\n");
- goto bail_unlock;
- }
+ //if (dvmLoaderInInitiatingList(clazz, loader)) {
+ // LOGW("WOW: simultaneous add of initiating class loader\n");
+ // goto bail_unlock;
+ //}
/*
* The list never shrinks, so we just keep a count of the
@@ -867,25 +895,26 @@
* The pointer is initially NULL, so we *do* want to call realloc
* when count==0.
*/
- if ((clazz->initiatingLoaderCount & (kInitLoaderInc-1)) == 0) {
+ InitiatingLoaderList *loaderList = dvmGetInitiatingLoaderList(clazz);
+ if ((loaderList->initiatingLoaderCount & (kInitLoaderInc-1)) == 0) {
Object** newList;
- newList = (Object**) realloc(clazz->initiatingLoaders,
- (clazz->initiatingLoaderCount + kInitLoaderInc)
+ newList = (Object**) realloc(loaderList->initiatingLoaders,
+ (loaderList->initiatingLoaderCount + kInitLoaderInc)
* sizeof(Object*));
if (newList == NULL) {
/* this is mainly a cache, so it's not the EotW */
assert(false);
goto bail_unlock;
}
- clazz->initiatingLoaders = newList;
+ loaderList->initiatingLoaders = newList;
//LOGI("Expanded init list to %d (%s)\n",
- // clazz->initiatingLoaderCount+kInitLoaderInc,
+ // loaderList->initiatingLoaderCount+kInitLoaderInc,
// clazz->descriptor);
}
-
- clazz->initiatingLoaders[clazz->initiatingLoaderCount++] = loader;
+ loaderList->initiatingLoaders[loaderList->initiatingLoaderCount++] =
+ loader;
bail_unlock:
dvmHashTableUnlock(gDvm.loadedClasses);
@@ -1889,8 +1918,9 @@
dvmLinearFree(clazz->classLoader, virtualMethods);
}
- clazz->initiatingLoaderCount = -1;
- NULL_AND_FREE(clazz->initiatingLoaders);
+ InitiatingLoaderList *loaderList = dvmGetInitiatingLoaderList(clazz);
+ loaderList->initiatingLoaderCount = -1;
+ NULL_AND_FREE(loaderList->initiatingLoaders);
clazz->interfaceCount = -1;
NULL_AND_LINEAR_FREE(clazz->interfaces);
diff --git a/vm/oo/Object.h b/vm/oo/Object.h
index 7ef8dac..18fbb36 100644
--- a/vm/oo/Object.h
+++ b/vm/oo/Object.h
@@ -24,6 +24,7 @@
/* fwd decl */
struct DataObject;
+struct InitiatingLoaderList;
struct ClassObject;
struct StringObject;
struct ArrayObject;
@@ -35,6 +36,7 @@
struct Field;
struct RegisterMap;
typedef struct DataObject DataObject;
+typedef struct InitiatingLoaderList InitiatingLoaderList;
typedef struct ClassObject ClassObject;
typedef struct StringObject StringObject;
typedef struct ArrayObject ArrayObject;
@@ -248,6 +250,18 @@
};
/*
+ * For classes created early and thus probably in the zygote, the
+ * InitiatingLoaderList is kept in gDvm. Later classes use the structure in
+ * Object Class. This helps keep zygote pages shared.
+ */
+struct InitiatingLoaderList {
+ /* a list of initiating loader Objects; grown and initialized on demand */
+ Object** initiatingLoaders;
+ /* count of loaders in the above list */
+ int initiatingLoaderCount;
+};
+
+/*
* Class objects have many additional fields. This is used for both
* classes and interfaces, including synthesized classes (arrays and
* primitive types).
@@ -318,8 +332,9 @@
Object* classLoader;
/* initiating class loader list */
- Object** initiatingLoaders;
- int initiatingLoaderCount;
+ /* NOTE: for classes with low serialNumber, these are unused, and the
+ values are kept in a table in gDvm. */
+ InitiatingLoaderList initiatingLoaderList;
/* array of interfaces this class implements directly */
int interfaceCount;