Merge pi-platform-release to stage-aosp-master - DO NOT MERGE
Change-Id: Ie7e1c8de0bdc6b28d19ed92570216ce2d18488c3
diff --git a/src/share/back/VirtualMachineImpl.c b/src/share/back/VirtualMachineImpl.c
index dfcdfb2..fde53c3 100644
--- a/src/share/back/VirtualMachineImpl.c
+++ b/src/share/back/VirtualMachineImpl.c
@@ -55,6 +55,12 @@
if (vmVersion == NULL) {
vmVersion = "<unknown>";
}
+ // ANDROID-CHANGED: The runtime value of the java.version property has always been "0" on
+ // android but the old debugger just sent a different value. Simply sending "0"
+ // can confuse some JDWP clients so we will simply say that we are version "8".
+ if (strcmp(gdata->property_java_vm_name, "Dalvik") == 0 && strcmp(vmVersion, "0") == 0) {
+ vmVersion = "8";
+ }
vmName = gdata->property_java_vm_name;
if (vmName == NULL) {
vmName = "<unknown>";
diff --git a/src/share/back/classTrack.c b/src/share/back/classTrack.c
index aea139a..64a0733 100644
--- a/src/share/back/classTrack.c
+++ b/src/share/back/classTrack.c
@@ -113,7 +113,7 @@
static void JNICALL
cbTrackingObjectFree(jvmtiEnv* jvmti_env, jlong tag)
{
- debugMonitorEnter(deletedTagLock);
+ debugMonitorEnterNoSuspend(deletedTagLock);
*(jlong*)bagAdd(deletedTagBag) = tag;
debugMonitorExit(deletedTagLock);
}
diff --git a/src/share/back/commonRef.c b/src/share/back/commonRef.c
index 259d4d8..f00766f 100644
--- a/src/share/back/commonRef.c
+++ b/src/share/back/commonRef.c
@@ -30,23 +30,31 @@
#include "util.h"
#include "commonRef.h"
-#define ALL_REFS -1
-
/*
+ * ANDROID-CHANGED: This was modified for android to avoid any use of weak
+ * global (jweak) references. On Android hosts the number of jweak
+ * references active at any one time is limited. By using jweaks to keep
+ * track of objects here we could hit the jweak limit on some very large
+ * apps. The implementation is compatible with any JVMTI implementation
+ * that provides the 'can_tag_objects' and
+ * 'can_generate_object_free_events' capabilities. This works by watching
+ * for the ObjectFree events on tagged objects and storing them in a list
+ * of things that have been deleted.
+ *
* Each object sent to the front end is tracked with the RefNode struct
* (see util.h).
* External to this module, objects are identified by a jlong id which is
- * simply the sequence number. A weak reference is usually used so that
+ * simply the sequence number. A JVMTI tag is usually used so that
* the presence of a debugger-tracked object will not prevent
* its collection. Once an object is collected, its RefNode may be
- * deleted and the weak ref inside may be reused (these may happen in
- * either order). Using the sequence number
+ * deleted (these may happen in * either order). Using the sequence number
* as the object id prevents ambiguity in the object id when the weak ref
* is reused. The RefNode* is stored with the object as it's JVMTI Tag.
+ * This tag also provides the weak-reference behavior.
*
- * The ref member is changed from weak to strong when
- * gc of the object is to be prevented.
- * Whether or not it is strong, it is never exported from this module.
+ * The ref member is changed from weak to strong when gc of the object is
+ * to be prevented. Whether or not it is strong, it is never exported
+ * from this module.
*
* A reference count of each jobject is also maintained here. It tracks
* the number times an object has been referenced through
@@ -54,9 +62,9 @@
* count is decremented to 0 (with commonRef_release*), even if the
* corresponding object has not been collected.
*
- * One hash table is maintained. The mapping of ID to jobject (or RefNode*)
- * is handled with one hash table that will re-size itself as the number
- * of RefNode's grow.
+ * One hash table is maintained. The mapping of ID to RefNode* is handled
+ * with one hash table that will re-size itself as the number of RefNode's
+ * grow.
*/
/* Initial hash table size (must be power of 2) */
@@ -82,38 +90,97 @@
return gdata->nextSeqNum++;
}
-/* Create a fresh RefNode structure, create a weak ref and tag the object */
+/* ANDROID-CHANGED: This helper function is unique to android.
+ * This function gets a local-ref to object the node is pointing to. If the node's object has been
+ * collected it will return NULL. The caller is responsible for calling env->DeleteLocalRef or
+ * env->PopLocalFrame to clean up the reference. This function makes no changes to the passed in
+ * node.
+ */
+static jobject
+getLocalRef(JNIEnv *env, const RefNode* node) {
+ if (node->isStrong) {
+ return JNI_FUNC_PTR(env,NewLocalRef)(env, node->ref);
+ }
+ jint count = -1;
+ jobject *objects = NULL;
+ jlong tag = ptr_to_jlong(node);
+ jvmtiError error = JVMTI_FUNC_PTR(gdata->jvmti,GetObjectsWithTags)
+ (gdata->jvmti, 1, &tag, &count, &objects, NULL);
+ if (error != JVMTI_ERROR_NONE) {
+ EXIT_ERROR(error,"GetObjectsWithTags");
+ }
+ if (count != 1 && count != 0) {
+ EXIT_ERROR(AGENT_ERROR_INTERNAL,
+ "GetObjectsWithTags returned multiple objects unexpectedly");
+ }
+ jobject res = (count == 0) ? NULL : objects[0];
+ JVMTI_FUNC_PTR(gdata->jvmti,Deallocate)(gdata->jvmti,(unsigned char*)objects);
+ return res;
+}
+
+/* ANDROID-CHANGED: Handler function for objects being freed. */
+void commonRef_handleFreedObject(jlong tag) {
+ RefNode* node = (RefNode*)jlong_to_ptr(tag);
+ debugMonitorEnterNoSuspend(gdata->refLock); {
+ // Delete the node and remove it from the hashmap.
+ // If we raced with a deleteNode call and lost the next and prev will be null but we will
+ // not be at the start of the bucket. This is fine.
+ jint slot = hashBucket(node->seqNum);
+ if (node->next != NULL ||
+ node->prev != NULL ||
+ gdata->objectsByID[slot] == node) {
+ /* Detach from id hash table */
+ if (node->prev == NULL) {
+ gdata->objectsByID[slot] = node->next;
+ } else {
+ node->prev->next = node->next;
+ }
+ /* Also fixup back links. */
+ if (node->next != NULL) {
+ node->next->prev = node->prev;
+ }
+ gdata->objectsByIDcount--;
+ }
+ jvmtiDeallocate(node);
+ } debugMonitorExit(gdata->refLock);
+}
+
+/* Create a fresh RefNode structure, and tag the object (creating a weak-ref to it).
+ * ANDROID-CHANGED: The definition of RefNode was changed slightly so that node->ref is only for
+ * a strong reference. For weak references we use the node as a tag on the object to keep track if
+ * it.
+ * ANDROID-CHANGED: ref must be a local-reference held live for the duration of this method until it
+ * is fully in the objectByID map.
+ */
static RefNode *
createNode(JNIEnv *env, jobject ref)
{
RefNode *node;
- jobject weakRef;
jvmtiError error;
+ if (ref == NULL) {
+ return NULL;
+ }
+
/* Could allocate RefNode's in blocks, not sure it would help much */
node = (RefNode*)jvmtiAllocate((int)sizeof(RefNode));
if (node == NULL) {
return NULL;
}
- /* Create weak reference to make sure we have a reference */
- weakRef = JNI_FUNC_PTR(env,NewWeakGlobalRef)(env, ref);
- if (weakRef == NULL) {
- jvmtiDeallocate(node);
- return NULL;
- }
-
- /* Set tag on weakRef */
- error = JVMTI_FUNC_PTR(gdata->jvmti, SetTag)
- (gdata->jvmti, weakRef, ptr_to_jlong(node));
+ /* ANDROID-CHANGED: Use local reference to make sure we have a reference. We will use this
+ * reference to set a tag to the node to use as a weak-reference and keep track of the ref.
+ * ANDROID-CHANGED: Set node tag on the ref. This tag now functions as the weak-reference to the
+ * object.
+ */
+ error = JVMTI_FUNC_PTR(gdata->jvmti, SetTag)(gdata->jvmti, ref, ptr_to_jlong(node));
if ( error != JVMTI_ERROR_NONE ) {
- JNI_FUNC_PTR(env,DeleteWeakGlobalRef)(env, weakRef);
jvmtiDeallocate(node);
return NULL;
}
/* Fill in RefNode */
- node->ref = weakRef;
+ node->ref = NULL;
node->isStrong = JNI_FALSE;
node->count = 1;
node->seqNum = newSeqNum();
@@ -127,20 +194,41 @@
static void
deleteNode(JNIEnv *env, RefNode *node)
{
- LOG_MISC(("Freeing %d (%x)\n", (int)node->seqNum, node->ref));
+ /* ANDROID-CHANGED: use getLocalRef to get a local reference to the node. */
+ WITH_LOCAL_REFS(env, 1) {
+ jobject localRef = getLocalRef(env, node);
+ LOG_MISC(("Freeing %d\n", (int)node->seqNum));
- if ( node->ref != NULL ) {
- /* Clear tag */
- (void)JVMTI_FUNC_PTR(gdata->jvmti,SetTag)
- (gdata->jvmti, node->ref, NULL_OBJECT_ID);
- if (node->isStrong) {
- JNI_FUNC_PTR(env,DeleteGlobalRef)(env, node->ref);
+ /* Detach from id hash table */
+ if (node->prev == NULL) {
+ gdata->objectsByID[hashBucket(node->seqNum)] = node->next;
} else {
- JNI_FUNC_PTR(env,DeleteWeakGlobalRef)(env, node->ref);
+ node->prev->next = node->next;
}
- }
- gdata->objectsByIDcount--;
- jvmtiDeallocate(node);
+ /* Also fixup back links. */
+ if (node->next != NULL) {
+ node->next->prev = node->prev;
+ }
+
+ // If we don't get the localref that means the ObjectFree event is being called and the
+ // node will be deleted there.
+ if ( localRef != NULL ) {
+ /* Clear tag */
+ (void)JVMTI_FUNC_PTR(gdata->jvmti,SetTag)
+ (gdata->jvmti, localRef, NULL_OBJECT_ID);
+ if (node->isStrong) {
+ JNI_FUNC_PTR(env,DeleteGlobalRef)(env, node->ref);
+ }
+
+ jvmtiDeallocate(node);
+ } else {
+ // We are going to let the object-free do the final work. Mark this node as not in the
+ // list with both null links but not in the bucket.
+ node->prev = NULL;
+ node->next = NULL;
+ }
+ gdata->objectsByIDcount--;
+ } END_WITH_LOCAL_REFS(env);
}
/* Change a RefNode to have a strong reference */
@@ -148,44 +236,35 @@
strengthenNode(JNIEnv *env, RefNode *node)
{
if (!node->isStrong) {
- jobject strongRef;
-
- strongRef = JNI_FUNC_PTR(env,NewGlobalRef)(env, node->ref);
- /*
- * NewGlobalRef on a weak ref will return NULL if the weak
- * reference has been collected or if out of memory.
- * We need to distinguish those two occurrences.
- */
- if ((strongRef == NULL) && !isSameObject(env, node->ref, NULL)) {
- EXIT_ERROR(AGENT_ERROR_NULL_POINTER,"NewGlobalRef");
- }
- if (strongRef != NULL) {
- JNI_FUNC_PTR(env,DeleteWeakGlobalRef)(env, node->ref);
- node->ref = strongRef;
- node->isStrong = JNI_TRUE;
- }
- return strongRef;
- } else {
- return node->ref;
+ /* ANDROID-CHANGED: We need to find and fill in the node->ref when we strengthen a node. */
+ WITH_LOCAL_REFS(env, 1) {
+ /* getLocalRef will return NULL if the referent has been collected. */
+ jobject localRef = getLocalRef(env, node);
+ if (localRef != NULL) {
+ node->ref = JNI_FUNC_PTR(env,NewGlobalRef)(env, localRef);
+ if (node->ref == NULL) {
+ EXIT_ERROR(AGENT_ERROR_NULL_POINTER,"NewGlobalRef");
+ }
+ node->isStrong = JNI_TRUE;
+ }
+ } END_WITH_LOCAL_REFS(env);
}
+ return node->ref;
}
-/* Change a RefNode to have a weak reference */
-static jweak
+/* Change a RefNode to have a weak reference
+ * ANDROID-CHANGED: This is done by deleting the strong reference. We already have a tag in
+ * to the node from when we created the node. Since this is never removed we can simply delete the
+ * global ref, reset node->isStrong & node->ref, and return. Since no part of this can fail we can
+ * change this function to be void too.
+ */
+static void
weakenNode(JNIEnv *env, RefNode *node)
{
if (node->isStrong) {
- jweak weakRef;
-
- weakRef = JNI_FUNC_PTR(env,NewWeakGlobalRef)(env, node->ref);
- if (weakRef != NULL) {
- JNI_FUNC_PTR(env,DeleteGlobalRef)(env, node->ref);
- node->ref = weakRef;
- node->isStrong = JNI_FALSE;
- }
- return weakRef;
- } else {
- return node->ref;
+ JNI_FUNC_PTR(env,DeleteGlobalRef)(env, node->ref);
+ node->ref = NULL;
+ node->isStrong = JNI_FALSE;
}
}
@@ -216,36 +295,25 @@
static void
deleteNodeByID(JNIEnv *env, jlong id, jint refCount)
{
+ /* ANDROID-CHANGED: Rewrite for double-linked list. Also remove ALL_REFS since it's not needed
+ * since the free-callback will do the work of cleaning up when an object gets collected. */
jint slot;
RefNode *node;
- RefNode *prev;
slot = hashBucket(id);
node = gdata->objectsByID[slot];
- prev = NULL;
while (node != NULL) {
if (id == node->seqNum) {
- if (refCount != ALL_REFS) {
- node->count -= refCount;
- } else {
- node->count = 0;
- }
+ node->count -= refCount;
if (node->count <= 0) {
if ( node->count < 0 ) {
EXIT_ERROR(AGENT_ERROR_INTERNAL,"RefNode count < 0");
}
- /* Detach from id hash table */
- if (prev == NULL) {
- gdata->objectsByID[slot] = node->next;
- } else {
- prev->next = node->next;
- }
deleteNode(env, node);
}
break;
}
- prev = node;
node = node->next;
}
}
@@ -263,20 +331,22 @@
static RefNode *
findNodeByID(JNIEnv *env, jlong id)
{
+ /* ANDROID-CHANGED: Rewrite for double-linked list */
jint slot;
RefNode *node;
- RefNode *prev;
slot = hashBucket(id);
node = gdata->objectsByID[slot];
- prev = NULL;
while (node != NULL) {
if ( id == node->seqNum ) {
- if ( prev != NULL ) {
+ if ( node->prev != NULL ) {
/* Re-order hash list so this one is up front */
- prev->next = node->next;
+ node->prev->next = node->next;
+ node->prev->prev = node->prev;
node->next = gdata->objectsByID[slot];
+ node->next->prev = node;
+ node->prev = NULL;
gdata->objectsByID[slot] = node;
}
break;
@@ -302,15 +372,21 @@
static void
hashIn(RefNode *node)
{
+ /* ANDROID-CHANGED: Modify for double-linked list */
jint slot;
/* Add to id hashtable */
slot = hashBucket(node->seqNum);
node->next = gdata->objectsByID[slot];
+ node->prev = NULL;
+ if (node->next != NULL) {
+ node->next->prev = node;
+ }
gdata->objectsByID[slot] = node;
}
-/* Allocate and add RefNode to hash table */
+/* Allocate and add RefNode to hash table
+ * ANDROID-CHANGED: Requires that ref be a held-live local ref.*/
static RefNode *
newCommonRef(JNIEnv *env, jobject ref)
{
@@ -378,13 +454,8 @@
for (i = 0; i < gdata->objectsByIDsize; i++) {
RefNode *node;
- node = gdata->objectsByID[i];
- while (node != NULL) {
- RefNode *next;
-
- next = node->next;
+ for (node = gdata->objectsByID[i]; node != NULL; node = gdata->objectsByID[i]) {
deleteNode(env, node);
- node = next;
}
gdata->objectsByID[i] = NULL;
}
@@ -417,10 +488,12 @@
node = findNodeByRef(env, ref);
if (node == NULL) {
- node = newCommonRef(env, ref);
- if ( node != NULL ) {
- id = node->seqNum;
- }
+ WITH_LOCAL_REFS(env, 1) {
+ node = newCommonRef(env, JNI_FUNC_PTR(env,NewLocalRef)(env, ref));
+ if ( node != NULL ) {
+ id = node->seqNum;
+ }
+ } END_WITH_LOCAL_REFS(env);
} else {
id = node->seqNum;
node->count++;
@@ -451,14 +524,20 @@
} else {
jobject lref;
- lref = JNI_FUNC_PTR(env,NewLocalRef)(env, node->ref);
- if ( lref == NULL ) {
- /* Object was GC'd shortly after we found the node */
- deleteNodeByID(env, node->seqNum, ALL_REFS);
- } else {
- saveGlobalRef(env, node->ref, &ref);
+ /* ANDROID-CHANGED: Use getLocalRef helper to get a local-reference to the object
+ * this node weakly points to. It will return NULL if the object has been GCd
+ */
+ lref = getLocalRef(env, node);
+ if ( lref != NULL ) {
+ /* ANDROID-CHANGED: Use lref to save the global ref since that is the only real
+ * jobject we have.
+ */
+ saveGlobalRef(env, lref, &ref);
JNI_FUNC_PTR(env,DeleteLocalRef)(env, lref);
}
+ /* ANDROID-CHANGED: Otherwise the object was GC'd shortly after we found the node.
+ * The free callback will deal with cleanup once we return.
+ */
}
}
} debugMonitorExit(gdata->refLock);
@@ -501,9 +580,9 @@
if (strongRef == NULL) {
/*
* Referent has been collected, clean up now.
+ * ANDROID-CHANGED: The node will be cleaned up by the object-free callback.
*/
error = AGENT_ERROR_INVALID_OBJECT;
- deleteNodeByID(env, id, ALL_REFS);
}
}
} debugMonitorExit(gdata->refLock);
@@ -524,12 +603,8 @@
env = getEnv();
node = findNodeByID(env, id);
if (node != NULL) {
- jweak weakRef;
-
- weakRef = weakenNode(env, node);
- if (weakRef == NULL) {
- error = AGENT_ERROR_OUT_OF_MEMORY;
- }
+ // ANDROID-CHANGED: weakenNode was changed to never fail.
+ weakenNode(env, node);
}
} debugMonitorExit(gdata->refLock);
return error;
@@ -556,44 +631,7 @@
void
commonRef_compact(void)
{
- JNIEnv *env;
- RefNode *node;
- RefNode *prev;
- int i;
-
- env = getEnv();
- debugMonitorEnter(gdata->refLock); {
- if ( gdata->objectsByIDsize > 0 ) {
- /*
- * Walk through the id-based hash table. Detach any nodes
- * for which the ref has been collected.
- */
- for (i = 0; i < gdata->objectsByIDsize; i++) {
- node = gdata->objectsByID[i];
- prev = NULL;
- while (node != NULL) {
- /* Has the object been collected? */
- if ( (!node->isStrong) &&
- isSameObject(env, node->ref, NULL)) {
- RefNode *freed;
-
- /* Detach from the ID list */
- if (prev == NULL) {
- gdata->objectsByID[i] = node->next;
- } else {
- prev->next = node->next;
- }
- freed = node;
- node = node->next;
- deleteNode(env, freed);
- } else {
- prev = node;
- node = node->next;
- }
- }
- }
- }
- } debugMonitorExit(gdata->refLock);
+ // NO-OP.
}
/* Lock the commonRef tables */
diff --git a/src/share/back/commonRef.h b/src/share/back/commonRef.h
index 7c76667..b84ca96 100644
--- a/src/share/back/commonRef.h
+++ b/src/share/back/commonRef.h
@@ -38,6 +38,9 @@
void commonRef_release(JNIEnv *env, jlong id);
void commonRef_compact(void);
+/* ANDROID-CHANGED: Called when an object is freed. This is called without any synchronization. */
+void commonRef_handleFreedObject(jlong tag);
+
void commonRef_lock(void);
void commonRef_unlock(void);
diff --git a/src/share/back/debugInit.c b/src/share/back/debugInit.c
index a11a9bc..66d5282 100644
--- a/src/share/back/debugInit.c
+++ b/src/share/back/debugInit.c
@@ -89,6 +89,8 @@
static jboolean suspendOnInit = JNI_TRUE; /* suspend all app threads after init */
static jboolean dopause = JNI_FALSE; /* pause for debugger attach */
static jboolean docoredump = JNI_FALSE; /* core dump on exit */
+/* ANDROID-CHANGED: Added directlog option */
+static jboolean directlog = JNI_FALSE; /* Don't add pid to logfile. */
static char *logfile = NULL; /* Name of logfile (if logging) */
static unsigned logflags = 0; /* Log flags */
@@ -202,10 +204,11 @@
minor_runtime >= minor_compiletime;
}
-// ANDROID-CHANGED: Function to get and set the com.android.art.internal.ddm.process_chunk extension
-// function. This returns JNI_ERR if something went wrong with searching. If the extension is not
-// found we return JNI_OK and don't bother updating the gdata pointer.
-static jint find_ddm_process_chunk()
+// ANDROID-CHANGED: Function to get and set the com.android.art.internal.ddm.process_chunk and
+// com.android.art.concurrent.raw_monitor_enter_no_suspend extension functions. This returns JNI_ERR
+// if something went wrong with searching. If the extension is not found we return JNI_OK and don't
+// bother updating the gdata pointer.
+static jint find_extension_functions()
{
jvmtiError error;
jvmtiExtensionFunctionInfo* extension_info;
@@ -228,6 +231,10 @@
if (strcmp("com.android.art.internal.ddm.process_chunk", extension_info[i].id) == 0) {
gdata->ddm_process_chunk = (DdmProcessChunk) extension_info[i].func;
}
+ if (strcmp("com.android.art.concurrent.raw_monitor_enter_no_suspend",
+ extension_info[i].id) == 0) {
+ gdata->raw_monitor_enter_no_suspend = (RawMonitorEnterNoSuspend) extension_info[i].func;
+ }
jvmtiDeallocate(extension_info[i].id);
jvmtiDeallocate(extension_info[i].short_description);
for (j = 0; j < extension_info[i].param_count; j++) {
@@ -382,6 +389,8 @@
needed_capabilities.can_maintain_original_method_order = 1;
needed_capabilities.can_generate_monitor_events = 1;
needed_capabilities.can_tag_objects = 1;
+ /* ANDROID-CHANGED: Needed for how we implement commonRef tracking */
+ needed_capabilities.can_generate_object_free_events = 1;
/* And what potential ones that would be nice to have */
needed_capabilities.can_force_early_return
@@ -459,7 +468,7 @@
}
// ANDROID-CHANGED: Find com.android.art.internal.ddm.process_chunk function if it exists.
- if (find_ddm_process_chunk() != JNI_OK) {
+ if (find_extension_functions() != JNI_OK || gdata->raw_monitor_enter_no_suspend == NULL) {
ERROR_MESSAGE(("Fatal error while attempting to find the "
"com.android.art.internal.ddm.process_chunk extension function"));
return JNI_ERR;
@@ -1003,6 +1012,8 @@
"pause=y|n pause to debug PID n\n"
"coredump=y|n coredump at exit n\n"
"errorexit=y|n exit on any error n\n"
+ /* ANDROID-CHANGED: Added directlog */
+ "directlog do not add pid to name of logfile n\n"
"logfile=filename name of log file none\n"
"logflags=flags log flags (bitmask) none\n"
" JVM calls = 0x001\n"
@@ -1108,6 +1119,8 @@
/* Set defaults */
gdata->assertOn = DEFAULT_ASSERT_ON;
gdata->assertFatal = DEFAULT_ASSERT_FATAL;
+ /* ANDROID-CHANGED: Add directlog */
+ directlog = JNI_FALSE;
logfile = DEFAULT_LOGFILE;
// ANDROID-CHANGED: By default we assume ddms is off initially.
gdata->ddmInitiallyActive = JNI_FALSE;
@@ -1263,6 +1276,12 @@
} else if (strcmp(buf, "precrash") == 0) {
errmsg = "The precrash option removed, use -XX:OnError";
goto bad_option_with_errmsg;
+ } else if (strcmp(buf, "directlog") == 0) {
+ /* ANDROID-CHANGED: Added directlog */
+ /*LINTED*/
+ if ( !get_boolean(&str, &directlog) ) {
+ goto syntax_error;
+ }
} else if (strcmp(buf, "logfile") == 0) {
/*LINTED*/
if (!get_tok(&str, current, (int)(end - current), ',')) {
@@ -1324,7 +1343,8 @@
/* Setup logging now */
if ( logfile!=NULL ) {
- setup_logging(logfile, logflags);
+ /* ANDROID-CHANGED: Add directlog */
+ setup_logging(logfile, logflags, directlog);
(void)atexit(&atexit_finish_logging);
}
diff --git a/src/share/back/eventHandler.c b/src/share/back/eventHandler.c
index 5f5bf24..25da37c 100644
--- a/src/share/back/eventHandler.c
+++ b/src/share/back/eventHandler.c
@@ -743,6 +743,16 @@
return clazz;
}
+/* ANDROID-CHANGED: Android keeps track of object unloads by watching this event instead of looking
+ * through jweaks since there are a limited number of those. This does not cause any corresponding
+ * jdwp event and is merely passed on to the commonRef system.
+ */
+static void JNICALL
+cbObjectFree(jvmtiEnv* jvmti_env, jlong tag)
+{
+ commonRef_handleFreedObject(tag);
+}
+
/* Event callback for JVMTI_EVENT_SINGLE_STEP */
static void JNICALL
cbSingleStep(jvmtiEnv *jvmti_env, JNIEnv *env,
@@ -883,6 +893,13 @@
cbClassPrepare(jvmtiEnv *jvmti_env, JNIEnv *env,
jthread thread, jclass klass)
{
+ /* ANDROID-CHANGED: b/111394423 Android sends ClassPrepare events for arrays too. We don't
+ * really care about these though and they can cause deadlocks since they may be sent on jit
+ * threads so just ignore them.
+ */
+ if (isArrayClass(klass)) {
+ return;
+ }
EventInfo info;
LOG_CB(("cbClassPrepare: thread=%p", thread));
@@ -912,6 +929,13 @@
cbClassLoad(jvmtiEnv *jvmti_env, JNIEnv *env,
jthread thread, jclass klass)
{
+ /* ANDROID-CHANGED: b/111394423 Android sends ClassLoad events for arrays too. We don't really
+ * care about these though and they can cause deadlocks since they may be sent on jit threads so
+ * just ignore them.
+ */
+ if (isArrayClass(klass)) {
+ return;
+ }
EventInfo info;
LOG_CB(("cbClassLoad: thread=%p", thread));
@@ -1485,8 +1509,16 @@
if (error != JVMTI_ERROR_NONE) {
EXIT_ERROR(error,"Can't enable garbage collection finish events");
}
+ /* ANDROID-CHANGED: Permanently enable object free for common-ref tracking */
+ error = JVMTI_FUNC_PTR(gdata->jvmti,SetEventNotificationMode)
+ (gdata->jvmti, JVMTI_ENABLE, JVMTI_EVENT_OBJECT_FREE, NULL);
+ if (error != JVMTI_ERROR_NONE) {
+ EXIT_ERROR(error,"Can't enable object free events");
+ }
(void)memset(&(gdata->callbacks),0,sizeof(gdata->callbacks));
+ /* ANDROID-CHANGED: Event callback for common-ref tracking */
+ gdata->callbacks.ObjectFree = &cbObjectFree;
/* Event callback for JVMTI_EVENT_SINGLE_STEP */
gdata->callbacks.SingleStep = &cbSingleStep;
/* Event callback for JVMTI_EVENT_BREAKPOINT */
diff --git a/src/share/back/log_messages.c b/src/share/back/log_messages.c
index 32bea29..78b21c2 100644
--- a/src/share/back/log_messages.c
+++ b/src/share/back/log_messages.c
@@ -202,8 +202,9 @@
#endif
/* Set up the logging with the name of a logging file. */
+/* ANDROID-CHANGED: Added directlog */
void
-setup_logging(const char *filename, unsigned flags)
+setup_logging(const char *filename, unsigned flags, int directlog)
{
#ifdef JDWP_LOGGING
FILE *fp = NULL;
@@ -217,9 +218,14 @@
return;
/* Create potential filename for logging */
- processPid = GETPID();
- (void)snprintf(logging_filename, sizeof(logging_filename),
- "%s.%d", filename, (int)processPid);
+ /* ANDROID-CHANGED: Add directlog */
+ if (directlog) {
+ strncpy(logging_filename, filename, sizeof(logging_filename));
+ } else {
+ processPid = GETPID();
+ (void)snprintf(logging_filename, sizeof(logging_filename),
+ "%s.%d", filename, (int)processPid);
+ }
/* Turn on logging (do this last) */
logging = 1;
diff --git a/src/share/back/log_messages.h b/src/share/back/log_messages.h
index 129fab6..23833bf 100644
--- a/src/share/back/log_messages.h
+++ b/src/share/back/log_messages.h
@@ -28,7 +28,8 @@
/* LOG: Must be called like: LOG_category(("anything")) or LOG_category((format,args)) */
-void setup_logging(const char *, unsigned);
+/* ANDROID-CHANGED: Added directlog argument */
+void setup_logging(const char *, unsigned, int);
void finish_logging();
#define LOG_NULL ((void)0)
diff --git a/src/share/back/util.c b/src/share/back/util.c
index 060c452..dcd195d 100644
--- a/src/share/back/util.c
+++ b/src/share/back/util.c
@@ -40,9 +40,11 @@
/* Forward declarations */
static jboolean isInterface(jclass clazz);
-static jboolean isArrayClass(jclass clazz);
static char * getPropertyUTF8(JNIEnv *env, char *propertyName);
+static jvmtiError (JNICALL *ext_RawMonitorEnterNoSuspend) (jvmtiEnv* env, jrawMonitorID monitor);
+static jvmtiError (JNICALL *ext_RawMonitorExitNoSuspend) (jvmtiEnv* env, jrawMonitorID monitor);
+
// ANDROID-CHANGED: Implement a helper to get the current time in milliseconds according to
// CLOCK_MONOTONIC.
jlong
@@ -1093,6 +1095,24 @@
}
}
+/* ANDROID-CHANGED: Add suspension ignoring raw-monitor enter. */
+void debugMonitorEnterNoSuspend(jrawMonitorID monitor)
+{
+ jvmtiError error;
+ while (JNI_TRUE) {
+ error = FUNC_PTR(&gdata,raw_monitor_enter_no_suspend)(gdata->jvmti, monitor);
+ error = ignore_vm_death(error);
+ if (error == JVMTI_ERROR_INTERRUPT) {
+ handleInterrupt();
+ } else {
+ break;
+ }
+ }
+ if (error != JVMTI_ERROR_NONE) {
+ EXIT_ERROR(error, "on raw monitor enter no suspend");
+ }
+}
+
void
debugMonitorWait(jrawMonitorID monitor)
{
@@ -1312,7 +1332,8 @@
return status;
}
-static jboolean
+/* ANDROID-CHANGED: Make isArrayClass public */
+jboolean
isArrayClass(jclass clazz)
{
jboolean isArray = JNI_FALSE;
diff --git a/src/share/back/util.h b/src/share/back/util.h
index dde7495..b02ad7b 100644
--- a/src/share/back/util.h
+++ b/src/share/back/util.h
@@ -56,13 +56,20 @@
/* Get access to Native Platform Toolkit functions */
#include "npt.h"
+/* ANDROID-CHANGED: We want to avoid allocating jweaks on android so if !isStrong we will use the
+ * node-pointer tag as the weak-reference.
+ */
/* Definition of a CommonRef tracked by the backend for the frontend */
typedef struct RefNode {
jlong seqNum; /* ID of reference, also key for hash table */
- jobject ref; /* could be strong or weak */
+ jobject ref; /* ANDROID-CHANGED: Always the strong reference if isStrong, NULL
+ * otherwise.
+ */
struct RefNode *next; /* next RefNode* in bucket chain */
+ struct RefNode *prev; /* ANDROID-CHANGED: Previous RefNode* in bucket chain. Used to allow
+ * us to remove arbitrary elements. */
jint count; /* count of references */
- unsigned isStrong : 1; /* 1 means this is a string reference */
+ unsigned isStrong : 1; /* 1 means this is a strong reference */
} RefNode;
/* Value of a NULL ID */
@@ -82,6 +89,7 @@
jint* type_out,
jint* length_out,
jbyte** data_out);
+typedef jvmtiError (*RawMonitorEnterNoSuspend)(jvmtiEnv* env, jrawMonitorID mon);
typedef struct {
jvmtiEnv *jvmti;
@@ -147,6 +155,7 @@
/* ANDROID-CHANGED: com.android.art.internal.ddm.process_chunk extension function */
DdmProcessChunk ddm_process_chunk;
+ RawMonitorEnterNoSuspend raw_monitor_enter_no_suspend;
/* ANDROID-CHANGED: Need to keep track of if ddm is initially active. */
jboolean ddmInitiallyActive;
@@ -380,6 +389,12 @@
jrawMonitorID debugMonitorCreate(char *name);
void debugMonitorEnter(jrawMonitorID theLock);
void debugMonitorExit(jrawMonitorID theLock);
+
+/* ANDROID-CHANGED: extension functions that will enter and exit a mutex without allowing suspension
+ * to occur. Caller must not use monitor-wait.
+ */
+void debugMonitorEnterNoSuspend(jrawMonitorID theLock);
+
void debugMonitorWait(jrawMonitorID theLock);
void debugMonitorTimedWait(jrawMonitorID theLock, jlong millis);
void debugMonitorNotify(jrawMonitorID theLock);
@@ -390,6 +405,9 @@
void threadGroupInfo(jthreadGroup, jvmtiThreadGroupInfo *info);
+/* ANDROID-CHANGED: Add isArrayClass */
+jboolean isArrayClass(jclass);
+
char *getClassname(jclass);
jvmtiError classSignature(jclass, char**, char**);
jint classStatus(jclass);