/* * Mark this event as disabled. Any future attempts to add a hook for this event * will result in an error. * * This function should not be called directly. It should be called through the hook interface * * Returns 0 on success, or non-zero if the event is already hooked or reserved */ static intptr_t J9HookDisable(struct J9HookInterface **hookInterface, uintptr_t taggedEventNum) { J9CommonHookInterface *commonInterface = (J9CommonHookInterface *)hookInterface; uintptr_t eventNum = taggedEventNum & J9HOOK_EVENT_NUM_MASK; /* try to answer without using the lock, first */ if (HOOK_FLAGS(commonInterface, eventNum) & J9HOOK_FLAG_RESERVED) { return -1; } else if (HOOK_FLAGS(commonInterface, eventNum) & J9HOOK_FLAG_DISABLED) { return 0; } else { intptr_t rc = 0; omrthread_monitor_enter(commonInterface->lock); if (HOOK_FLAGS(commonInterface, eventNum) & (J9HOOK_FLAG_RESERVED | J9HOOK_FLAG_HOOKED)) { rc = -1; } else { HOOK_FLAGS(commonInterface, eventNum) |= J9HOOK_FLAG_DISABLED; } omrthread_monitor_exit(commonInterface->lock); return rc; } }
/* * Inform all registered listeners that the specified event has occurred. Details about the * event should be available through eventData. * * If the J9HOOK_TAG_ONCE bit is set in the taggedEventNum, then the event is disabled * before the listeners are informed. Any attempts to add listeners to a TAG_ONCE event * once it has been reported will fail. * * This function should not be called directly. It should be called through the hook interface * */ static void J9HookDispatch(struct J9HookInterface **hookInterface, uintptr_t taggedEventNum, void *eventData) { uintptr_t eventNum = taggedEventNum & J9HOOK_EVENT_NUM_MASK; J9CommonHookInterface *commonInterface = (J9CommonHookInterface *)hookInterface; J9HookRecord *record = HOOK_RECORD(commonInterface, eventNum); if (taggedEventNum & J9HOOK_TAG_ONCE) { uint8_t oldFlags; omrthread_monitor_enter(commonInterface->lock); oldFlags = HOOK_FLAGS(commonInterface, eventNum); /* clear the HOOKED and RESERVED flags and set the DISABLED flag */ HOOK_FLAGS(commonInterface, eventNum) = (oldFlags | J9HOOK_FLAG_DISABLED) & ~(J9HOOK_FLAG_RESERVED | J9HOOK_FLAG_HOOKED); omrthread_monitor_exit(commonInterface->lock); if (oldFlags & J9HOOK_FLAG_DISABLED) { /* already reported */ return; } } while (record) { J9HookFunction function; void *userData; uintptr_t id; /* ensure that the id is read before any other fields */ id = record->id; if (HOOK_IS_VALID_ID(id)) { VM_AtomicSupport::readBarrier(); function = record->function; userData = record->userData; /* now read the id again to make sure that nothing has changed */ VM_AtomicSupport::readBarrier(); if (record->id == id) { function(hookInterface, eventNum, eventData, userData); } else { /* this record has been updated while we were reading it. Skip it. */ } } record = record->next; } }
/* * Remove the specified function from the listeners for eventNum. * If userData is NULL, all functions matching function are removed. * If userData is non-NULL, only functions with matching userData are removed. * * This function should not be called directly. It should be called through the hook interface */ static void J9HookUnregister(struct J9HookInterface **hookInterface, uintptr_t taggedEventNum, J9HookFunction function, void *userData) { J9CommonHookInterface *commonInterface = (J9CommonHookInterface *)hookInterface; J9HookRecord *record; J9HookRegistrationEvent eventStruct; uintptr_t hooksRemaining = 0; uintptr_t hooksRemoved = 0; uintptr_t eventNum = taggedEventNum & J9HOOK_EVENT_NUM_MASK; eventStruct.eventNum = eventNum; eventStruct.function = function; eventStruct.isRegistration = 0; eventStruct.userData = NULL; eventStruct.agentID = J9HOOK_AGENTID_DEFAULT; omrthread_monitor_enter(commonInterface->lock); record = HOOK_RECORD(commonInterface, eventNum); while (record) { if (record->function == function) { if ((userData == NULL) || (record->userData == userData)) { if (taggedEventNum & J9HOOK_TAG_COUNTED) { if (--(record->count) != 0) { omrthread_monitor_exit(commonInterface->lock); return; } } if (userData != NULL) { /* copy data from the event record so that we can report it once we've released the mutex */ eventStruct.userData = record->userData; eventStruct.agentID = record->agentID; } /* mark the record as invalid so that it can be recycled */ record->id = HOOK_INVALID_ID(record->id); hooksRemoved++; } } if (HOOK_IS_VALID_ID(record->id)) { hooksRemaining++; } record = record->next; } if (hooksRemaining == 0) { HOOK_FLAGS(commonInterface, eventNum) &= ~J9HOOK_FLAG_HOOKED; } omrthread_monitor_exit(commonInterface->lock); if (hooksRemoved != 0) { /* report the unregistration event */ (*hookInterface)->J9HookDispatch(hookInterface, J9HOOK_REGISTRATION_EVENT, &eventStruct); } }
/** * Determine if the specified event has been disabled. This function does not modify the * hook interface in any way. Note that there's no atomic protection of any kind -- just * because the event wasn't disabled when you called this function doesn't mean that it * won't be by the time you try to register for it. * * @return 0 (false) if the event is disabled, non-zero (true) if the event is not disabled. */ static intptr_t J9HookIsEnabled(struct J9HookInterface **hookInterface, uintptr_t taggedEventNum) { J9CommonHookInterface *commonInterface = (J9CommonHookInterface *)hookInterface; uintptr_t eventNum = taggedEventNum & J9HOOK_EVENT_NUM_MASK; if (HOOK_FLAGS(commonInterface, eventNum) & J9HOOK_FLAG_DISABLED) { return 0; } #if 0 /* at some point, we may wish to distinguish between 'not disabled' and reserved. */ if (HOOK_FLAGS(commonInterface, eventNum) & J9HOOK_FLAG_RESERVED) { return 2; } #endif return 1; }
/* * Use J9HookReserve to indicate your intention to hook an event in the future. * Certain events must be reserved before the providing module initializes completely, * or it may make decisions which make it impossible to add the hooks later. * Once the point of no-return is reached, the providing module will disable the * event if it has not been registered or reserved. * * This function should not be called directly. It should be called through the hook interface * * returns 0 on success * non-zero if the event has been disabled */ static intptr_t J9HookReserve(struct J9HookInterface **hookInterface, uintptr_t taggedEventNum) { J9CommonHookInterface *commonInterface = (J9CommonHookInterface *)hookInterface; intptr_t rc = 0; uintptr_t eventNum = taggedEventNum & J9HOOK_EVENT_NUM_MASK; omrthread_monitor_enter(commonInterface->lock); if (HOOK_FLAGS(commonInterface, eventNum) & J9HOOK_FLAG_DISABLED) { rc = -1; } else { HOOK_FLAGS(commonInterface, eventNum) |= J9HOOK_FLAG_RESERVED; } omrthread_monitor_exit(commonInterface->lock); return rc; }
static intptr_t J9HookRegisterWithCallSitePrivate(struct J9HookInterface **hookInterface, uintptr_t taggedEventNum, J9HookFunction function, const char *callsite, void *userData, uintptr_t agentID) { J9CommonHookInterface *commonInterface = (J9CommonHookInterface *)hookInterface; J9HookRegistrationEvent eventStruct; intptr_t rc = 0; uintptr_t eventNum = taggedEventNum & J9HOOK_EVENT_NUM_MASK; omrthread_monitor_enter(commonInterface->lock); if (HOOK_FLAGS(commonInterface, eventNum) & J9HOOK_FLAG_DISABLED) { rc = -1; } else { J9HookRecord *insertionPoint = NULL; J9HookRecord *emptyRecord = NULL; J9HookRecord *record = HOOK_RECORD(commonInterface, eventNum); /* at the end of the loop, insertionPoint will point to the last record which should be triggered before the one we're adding */ while (record) { if ((taggedEventNum & J9HOOK_TAG_REVERSE_ORDER) ? record->agentID >= agentID : record->agentID <= agentID) { insertionPoint = record; } if (!HOOK_IS_VALID_ID(record->id)) { if ((taggedEventNum & J9HOOK_TAG_REVERSE_ORDER) ? record->agentID >= agentID : record->agentID <= agentID) { emptyRecord = record; } } else if ((record->function == function) && (record->userData == userData)) { /* this listener is already registered */ ++(record->count); omrthread_monitor_exit(commonInterface->lock); return 0; } record = record->next; } /* * Re-use the empty record if it is in a legitimate position for the requested agent. * (It is a legitimate position if all records before this position have the same or lower agent IDs (tested previously) * and all records after this position have the same or higher IDs) */ if ((emptyRecord != NULL) && ((emptyRecord->next == NULL) || ((taggedEventNum & J9HOOK_TAG_REVERSE_ORDER) ? (emptyRecord->next->agentID <= agentID) : (emptyRecord->next->agentID >= agentID))) ) { emptyRecord->function = function; emptyRecord->callsite = callsite; emptyRecord->userData = userData; emptyRecord->count = 1; emptyRecord->agentID = agentID; VM_AtomicSupport::writeBarrier(); emptyRecord->id = HOOK_VALID_ID(emptyRecord->id); HOOK_FLAGS(commonInterface, eventNum) |= J9HOOK_FLAG_HOOKED | J9HOOK_FLAG_RESERVED; } else { record = (J9HookRecord *)pool_newElement(commonInterface->pool); if (record == NULL) { rc = -1; } else { if (insertionPoint == NULL) { record->next = HOOK_RECORD(commonInterface, eventNum); } else { record->next = insertionPoint->next; } record->function = function; record->callsite = callsite; record->userData = userData; record->count = 1; record->id = HOOK_INITIAL_ID; record->agentID = agentID; VM_AtomicSupport::writeBarrier(); if (insertionPoint == NULL) { HOOK_RECORD(commonInterface, eventNum) = record; } else { insertionPoint->next = record; } HOOK_FLAGS(commonInterface, eventNum) |= J9HOOK_FLAG_HOOKED | J9HOOK_FLAG_RESERVED; } } } omrthread_monitor_exit(commonInterface->lock); /* report the registration event */ eventStruct.eventNum = eventNum; eventStruct.function = function; eventStruct.userData = userData; eventStruct.isRegistration = 1; eventStruct.agentID = agentID; (*hookInterface)->J9HookDispatch(hookInterface, J9HOOK_REGISTRATION_EVENT, &eventStruct); return rc; }
/* * Inform all registered listeners that the specified event has occurred. Details about the * event should be available through eventData. * * If the J9HOOK_TAG_ONCE bit is set in the taggedEventNum, then the event is disabled * before the listeners are informed. Any attempts to add listeners to a TAG_ONCE event * once it has been reported will fail. * * This function should not be called directly. It should be called through the hook interface * */ static void J9HookDispatch(struct J9HookInterface **hookInterface, uintptr_t taggedEventNum, void *eventData) { uintptr_t eventNum = taggedEventNum & J9HOOK_EVENT_NUM_MASK; J9CommonHookInterface *commonInterface = (J9CommonHookInterface *)hookInterface; J9HookRecord *record = HOOK_RECORD(commonInterface, eventNum); OMREventInfo4Dump *eventDump = J9HOOK_DUMPINFO(commonInterface, eventNum); uintptr_t samplingInterval = (taggedEventNum & J9HOOK_TAG_SAMPLING_MASK) >> 16; bool sampling = false; if (taggedEventNum & J9HOOK_TAG_ONCE) { uint8_t oldFlags; omrthread_monitor_enter(commonInterface->lock); oldFlags = HOOK_FLAGS(commonInterface, eventNum); /* clear the HOOKED and RESERVED flags and set the DISABLED flag */ HOOK_FLAGS(commonInterface, eventNum) = (oldFlags | J9HOOK_FLAG_DISABLED) & ~(J9HOOK_FLAG_RESERVED | J9HOOK_FLAG_HOOKED); omrthread_monitor_exit(commonInterface->lock); if (oldFlags & J9HOOK_FLAG_DISABLED) { /* already reported */ return; } } while (record) { J9HookFunction function; void *userData; uintptr_t id; /* ensure that the id is read before any other fields */ id = record->id; if (HOOK_IS_VALID_ID(id)) { VM_AtomicSupport::readBarrier(); function = record->function; userData = record->userData; /* now read the id again to make sure that nothing has changed */ VM_AtomicSupport::readBarrier(); if (record->id == id) { uint64_t startTime = 0; uintptr_t count = 0; if (NULL != eventDump) { count = VM_AtomicSupport::add((volatile uintptr_t *)&eventDump->count, 1); sampling = (1 >= samplingInterval) || ((100 >= samplingInterval) && (0 == (count % samplingInterval))); } else { sampling = false; } OMRPORT_ACCESS_FROM_OMRPORT(commonInterface->portLib); if (sampling) { startTime = omrtime_current_time_millis(); } function(hookInterface, eventNum, eventData, userData); if (sampling) { uint64_t timeDelta = omrtime_current_time_millis() - startTime; eventDump->lastHook.startTime = startTime; eventDump->lastHook.callsite = record->callsite; eventDump->lastHook.func_ptr = (void *)record->function; eventDump->lastHook.duration = timeDelta; if ((eventDump->longestHook.duration < timeDelta) || (0 == eventDump->longestHook.startTime)) { eventDump->longestHook.startTime = startTime; eventDump->longestHook.callsite = record->callsite; eventDump->longestHook.func_ptr = (void *)record->function; eventDump->longestHook.duration = timeDelta; } if (commonInterface->threshold4Trace <= timeDelta) { const char *callsite = "UNKNOWN"; char buffer[32]; if (NULL != record->callsite) { callsite = record->callsite; } else { /* if the callsite info can not be retrieved, use callback function pointer instead */ omrstr_printf(buffer, sizeof(buffer), "0x%p", record->function); callsite = buffer; } Trc_Hook_Dispatch_Exceed_Threshold_Event(callsite, timeDelta); } } } else { /* this record has been updated while we were reading it. Skip it. */ } } record = record->next; } }