/* Release tracking of an object by ID */ void commonRef_release(JNIEnv *env, jlong id) { debugMonitorEnter(gdata->refLock); { deleteNodeByID(env, id, 1); } debugMonitorExit(gdata->refLock); }
void commonRef_releaseMultiple(JNIEnv *env, jlong id, jint refCount) { debugMonitorEnter(gdata->refLock); { deleteNodeByID(env, id, refCount); } debugMonitorExit(gdata->refLock); }
/* * Given an object ID obtained from the debugger front end, return a * strong, global reference to that object (or NULL if the object * has been collected). The reference can then be used for JNI and * JVMTI calls. Caller is resposible for deleting the returned reference. */ jobject commonRef_idToRef(JNIEnv *env, jlong id) { jobject ref; ref = NULL; debugMonitorEnter(gdata->refLock); { RefNode *node; node = findNodeByID(env, id); if (node != NULL) { if (node->isStrong) { saveGlobalRef(env, node->ref, &ref); } 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); JNI_FUNC_PTR(env,DeleteLocalRef)(env, lref); } } } } debugMonitorExit(gdata->refLock); return ref; }
/* Prevent garbage collection of an object */ jvmtiError commonRef_pin(jlong id) { jvmtiError error; error = JVMTI_ERROR_NONE; if (id == NULL_OBJECT_ID) { return error; } debugMonitorEnter(gdata->refLock); { JNIEnv *env; RefNode *node; env = getEnv(); node = findNodeByID(env, id); if (node == NULL) { error = AGENT_ERROR_INVALID_OBJECT; } else { jobject strongRef; strongRef = strengthenNode(env, node); if (strongRef == NULL) { /* * Referent has been collected, clean up now. */ error = AGENT_ERROR_INVALID_OBJECT; deleteNodeByID(env, id, ALL_REFS); } } } debugMonitorExit(gdata->refLock); return error; }
/* Reset the commonRefs usage */ void commonRef_reset(JNIEnv *env) { debugMonitorEnter(gdata->refLock); { int i; for (i = 0; i < gdata->objectsByIDsize; i++) { RefNode *node; node = gdata->objectsByID[i]; while (node != NULL) { RefNode *next; next = node->next; deleteNode(env, node); node = next; } gdata->objectsByID[i] = NULL; } /* Toss entire hash table and re-create a new one */ jvmtiDeallocate(gdata->objectsByID); gdata->objectsByID = NULL; gdata->nextSeqNum = 1; /* 0 used for error indication */ initializeObjectsByID(HASH_INIT_SIZE); } debugMonitorExit(gdata->refLock); }
/* * Given a reference obtained from JNI or JVMTI, return an object * id suitable for sending to the debugger front end. */ jlong commonRef_refToID(JNIEnv *env, jobject ref) { jlong id; if (ref == NULL) { return NULL_OBJECT_ID; } id = NULL_OBJECT_ID; debugMonitorEnter(gdata->refLock); { RefNode *node; node = findNodeByRef(env, ref); if (node == NULL) { node = newCommonRef(env, ref); if ( node != NULL ) { id = node->seqNum; } } else { id = node->seqNum; node->count++; } } debugMonitorExit(gdata->refLock); return id; }
static void notifyTransportError(void) { debugMonitorEnter(cmdQueueLock); transportError = JNI_TRUE; debugMonitorNotify(cmdQueueLock); debugMonitorExit(cmdQueueLock); }
static void enqueue(jdwpPacket *packet) { struct PacketList *pL; struct PacketList *walker; pL = jvmtiAllocate((jint)sizeof(struct PacketList)); if (pL == NULL) { EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"packet list"); } pL->packet = *packet; pL->next = NULL; debugMonitorEnter(cmdQueueLock); if (cmdQueue == NULL) { cmdQueue = pL; debugMonitorNotify(cmdQueueLock); } else { walker = (struct PacketList *)cmdQueue; while (walker->next != NULL) walker = walker->next; walker->next = pL; } debugMonitorExit(cmdQueueLock); }
static void enqueueCommand(HelperCommand *command, jboolean wait, jboolean reportingVMDeath) { static jboolean vmDeathReported = JNI_FALSE; CommandQueue *queue = &commandQueue; jint size = commandSize(command); command->done = JNI_FALSE; command->waiting = wait; command->next = NULL; debugMonitorEnter(commandQueueLock); while (size + currentQueueSize > maxQueueSize) { debugMonitorWait(commandQueueLock); } log_debugee_location("enqueueCommand(): HelperCommand being processed", NULL, NULL, 0); if (vmDeathReported) { /* send no more events after VMDeath and don't wait */ wait = JNI_FALSE; } else { currentQueueSize += size; if (queue->head == NULL) { queue->head = command; } else { queue->tail->next = command; } queue->tail = command; if (reportingVMDeath) { vmDeathReported = JNI_TRUE; } } debugMonitorNotifyAll(commandQueueLock); debugMonitorExit(commandQueueLock); if (wait) { debugMonitorEnter(commandCompleteLock); while (!command->done) { log_debugee_location("enqueueCommand(): HelperCommand wait", NULL, NULL, 0); debugMonitorWait(commandCompleteLock); } freeCommand(command); debugMonitorExit(commandCompleteLock); } }
/* * We wait for either ThreadReferenceImpl.c: resume() or * VirtualMachineImpl.c: resume() to be called. */ static void doBlockCommandLoop(void) { debugMonitorEnter(blockCommandLoopLock); while (blockCommandLoop == JNI_TRUE) { debugMonitorWait(blockCommandLoopLock); } debugMonitorExit(blockCommandLoopLock); }
/* * Wait for all initialization to complete. */ void debugInit_waitInitComplete(void) { debugMonitorEnter(initMonitor); while (!initComplete) { debugMonitorWait(initMonitor); } debugMonitorExit(initMonitor); }
void eventHelper_reset(jbyte newSessionID) { debugMonitorEnter(commandQueueLock); currentSessionID = newSessionID; holdEvents = JNI_FALSE; debugMonitorNotifyAll(commandQueueLock); debugMonitorExit(commandQueueLock); }
void commonRef_release(jlong id) { JNIEnv *env = getEnv(); debugMonitorEnter(refLock); deleteNodeByID(env, id, 1); debugMonitorExit(refLock); }
void commonRef_releaseMultiple(jlong id, jint refCount) { JNIEnv *env = getEnv(); debugMonitorEnter(refLock); deleteNodeByID(env, id, refCount); debugMonitorExit(refLock); }
static void signalInitComplete(void) { /* * Initialization is complete */ LOG_MISC(("signal initialization complete")); debugMonitorEnter(initMonitor); initComplete = JNI_TRUE; debugMonitorNotifyAll(initMonitor); debugMonitorExit(initMonitor); }
static void completeCommand(HelperCommand *command) { if (command->waiting) { debugMonitorEnter(commandCompleteLock); command->done = JNI_TRUE; log_debugee_location("completeCommand(): HelperCommand done waiting", NULL, NULL, 0); debugMonitorNotifyAll(commandCompleteLock); debugMonitorExit(commandCompleteLock); } else { freeCommand(command); } }
/* * If the command that we are about to execute has a suspend-all * policy, then prepare for either ThreadReferenceImpl.c: resume() * or VirtualMachineImpl.c: resume() to be called. */ static jboolean needBlockCommandLoop(HelperCommand *cmd) { if (cmd->commandKind == COMMAND_REPORT_EVENT_COMPOSITE && cmd->u.reportEventComposite.suspendPolicy == JDWP_SUSPEND_POLICY(ALL)) { debugMonitorEnter(blockCommandLoopLock); blockCommandLoop = JNI_TRUE; debugMonitorExit(blockCommandLoopLock); return JNI_TRUE; } return JNI_FALSE; }
static HelperCommand * dequeueCommand(void) { HelperCommand *command = NULL; CommandQueue *queue = &commandQueue; jint size; debugMonitorEnter(commandQueueLock); while (command == NULL) { while (holdEvents || (queue->head == NULL)) { debugMonitorWait(commandQueueLock); } JDI_ASSERT(queue->head); command = queue->head; queue->head = command->next; if (queue->tail == command) { queue->tail = NULL; } log_debugee_location("dequeueCommand(): command being dequeued", NULL, NULL, 0); size = commandSize(command); /* * Immediately close out any commands enqueued from a * previously attached debugger. */ if (command->sessionID != currentSessionID) { log_debugee_location("dequeueCommand(): command session removal", NULL, NULL, 0); completeCommand(command); command = NULL; } /* * There's room in the queue for more. */ currentQueueSize -= size; debugMonitorNotifyAll(commandQueueLock); } debugMonitorExit(commandQueueLock); return command; }
/* * Given an object ID obtained from the debugger front end, return a * strong, global reference to that object (or NULL if the object * has been collected). The reference can then be used for JNI and * JVMTI calls. Caller is resposible for deleting the returned reference. */ jobject commonRef_idToRef(jlong id) { JNIEnv *env = getEnv(); jobject ref = NULL; RefNode *node; debugMonitorEnter(refLock); node = findNodeByID(env, id); if (node != NULL) { saveGlobalRef(env, node->ref, &ref); } debugMonitorExit(refLock); return ref; }
/* Get rid of RefNodes for objects that no longer exist */ 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); }
static void connectionInitiated(jdwpTransportEnv *t) { jint isValid = JNI_FALSE; debugMonitorEnter(listenerLock); /* * Don't allow a connection until initialization is complete */ debugInit_waitInitComplete(); /* Are we the first transport to get a connection? */ if (transport == NULL) { transport = t; isValid = JNI_TRUE; } else { if (transport == t) { /* connected with the same transport as before */ isValid = JNI_TRUE; } else { /* * Another transport got a connection - multiple transports * not fully supported yet so shouldn't get here. */ (*t)->Close(t); JDI_ASSERT(JNI_FALSE); } } if (isValid) { debugMonitorNotifyAll(listenerLock); } debugMonitorExit(listenerLock); if (isValid) { debugLoop_run(); } }
void transport_waitForConnection(void) { /* * If the VM is suspended on debugger initialization, we wait * for a connection before continuing. This ensures that all * events are delivered to the debugger. (We might as well do this * this since the VM won't continue until a remote debugger attaches * and resumes it.) If not suspending on initialization, we must * just drop any packets (i.e. events) so that the VM can continue * to run. The debugger may not attach until much later. */ if (debugInit_suspendOnInit()) { debugMonitorEnter(listenerLock); while (transport == NULL) { debugMonitorWait(listenerLock); } debugMonitorExit(listenerLock); } }
/* Permit garbage collection of an object */ jvmtiError commonRef_unpin(jlong id) { JNIEnv *env = getEnv(); jvmtiError error = JVMTI_ERROR_NONE; RefNode *node; debugMonitorEnter(refLock); node = findNodeByID(env, id); if (node != NULL) { jweak weakRef = weakenNode(env, node); if (weakRef == NULL) { error = JVMTI_ERROR_OUT_OF_MEMORY; } } debugMonitorExit(refLock); return error; }
static jboolean dequeue(jdwpPacket *packet) { struct PacketList *node = NULL; debugMonitorEnter(cmdQueueLock); while (!transportError && (cmdQueue == NULL)) { debugMonitorWait(cmdQueueLock); } if (cmdQueue != NULL) { node = (struct PacketList *)cmdQueue; cmdQueue = node->next; } debugMonitorExit(cmdQueueLock); if (node != NULL) { *packet = node->packet; jvmtiDeallocate(node); } return (node != NULL); }
void commonRef_reset(void) { JNIEnv *env = getEnv(); int i; RefNode *node; debugMonitorEnter(refLock); for (i = 0; i < CR_HASH_SLOT_COUNT; i++) { node = objectsByID[i]; while (node != NULL) { RefNode *temp = node->nextByID; deleteNode(env, node); node = temp; } objectsByID[i] = NULL; objectsByRef[i] = NULL; } nextSeqNum = 1; debugMonitorExit(refLock); }
void commonRef_unlock(void) { debugMonitorExit(refLock); }
/* * Get rid of RefNodes for objects that no longer exist */ void commonRef_compact(void) { JNIEnv *env = getEnv(); RefNode *node; RefNode *prev; int i; /* printRefTables(); */ debugMonitorEnter(refLock); /* * Part 1: Walk through the id-based hash table. Detach any nodes * for which the ref has been collected. Mark the node instead of * deleting it so that the second part through the ref-based hash * table can find it. */ for (i = 0; i < CR_HASH_SLOT_COUNT; i++) { node = objectsByID[i]; prev = NULL; while (node != NULL) { /* Has the object been collected? */ if (isSameObject(env, node->ref, NULL)) { /* Mark it to be freed by part 2 */ node->count = 0; /* Detach from the ID list */ if (prev == NULL) { objectsByID[i] = node->nextByID; } else { prev->nextByID = node->nextByID; } } else { prev = node; } node = node->nextByID; } } /* * Part 2: Walk through the ref-based hash table. Detach and * delete all nodes that were marked in pass 1. */ for (i = 0; i < CR_HASH_SLOT_COUNT; i++) { node = objectsByRef[i]; prev = NULL; while (node != NULL) { /* Has the object been marked? */ if (node->count == 0) { RefNode *freed = node; /* Detach from the ref list */ if (prev == NULL) { objectsByRef[i] = freed->nextByRef; } else { prev->nextByRef = freed->nextByRef; } node = node->nextByRef; deleteNode(env, freed); } else { prev = node; node = node->nextByRef; } } } debugMonitorExit(refLock); /* printRefTables(); */ }
void debugLoop_sync(void) { debugMonitorEnter(resumeLock); debugMonitorExit(resumeLock); }
void debugLoop_run(void) { jboolean shouldListen; jdwpPacket p; jvmtiStartFunction func; /* Initialize all statics */ /* We may be starting a new connection after an error */ cmdQueue = NULL; cmdQueueLock = debugMonitorCreate("JDWP Command Queue Lock"); transportError = JNI_FALSE; shouldListen = JNI_TRUE; func = &reader; (void)spawnNewThread(func, NULL, "JDWP Command Reader"); standardHandlers_onConnect(); threadControl_onConnect(); /* Okay, start reading cmds! */ while (shouldListen) { if (!dequeue(&p)) { break; } if (p.type.cmd.flags & JDWPTRANSPORT_FLAGS_REPLY) { /* * Its a reply packet. */ continue; } else { /* * Its a cmd packet. */ jdwpCmdPacket *cmd = &p.type.cmd; PacketInputStream in; PacketOutputStream out; CommandHandler func; /* Should reply be sent to sender. * For error handling, assume yes, since * only VM/exit does not reply */ jboolean replyToSender = JNI_TRUE; /* * For VirtualMachine.Resume commands we hold the resumeLock * while executing and replying to the command. This ensures * that a Resume after VM_DEATH will be allowed to complete * before the thread posting the VM_DEATH continues VM * termination. */ if (resumeCommand(cmd)) { debugMonitorEnter(resumeLock); } /* Initialize the input and output streams */ inStream_init(&in, p); outStream_initReply(&out, inStream_id(&in)); LOG_MISC(("Command set %d, command %d", cmd->cmdSet, cmd->cmd)); func = debugDispatch_getHandler(cmd->cmdSet,cmd->cmd); if (func == NULL) { /* we've never heard of this, so I guess we * haven't implemented it. * Handle gracefully for future expansion * and platform / vendor expansion. */ outStream_setError(&out, JDWP_ERROR(NOT_IMPLEMENTED)); } else if (gdata->vmDead && ((cmd->cmdSet) != JDWP_COMMAND_SET(VirtualMachine))) { /* Protect the VM from calls while dead. * VirtualMachine cmdSet quietly ignores some cmds * after VM death, so, it sends it's own errors. */ outStream_setError(&out, JDWP_ERROR(VM_DEAD)); } else { /* Call the command handler */ replyToSender = func(&in, &out); } /* Reply to the sender */ if (replyToSender) { if (inStream_error(&in)) { outStream_setError(&out, inStream_error(&in)); } outStream_sendReply(&out); } /* * Release the resumeLock as the reply has been posted. */ if (resumeCommand(cmd)) { debugMonitorExit(resumeLock); } inStream_destroy(&in); outStream_destroy(&out); shouldListen = !lastCommand(cmd); } } threadControl_onDisconnect(); standardHandlers_onDisconnect(); /* * Cut off the transport immediately. This has the effect of * cutting off any events that the eventHelper thread might * be trying to send. */ transport_close(); debugMonitorDestroy(cmdQueueLock); /* Reset for a new connection to this VM if it's still alive */ if ( ! gdata->vmDead ) { debugInit_reset(getEnv()); } }
void eventHelper_unlock(void) { debugMonitorExit(commandCompleteLock); debugMonitorExit(commandQueueLock); }