Esempio n. 1
0
/*
 * 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;
	}
}
Esempio n. 2
0
/*
 * 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;
	}
}
Esempio n. 3
0
/*
 * 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);
	}
}
Esempio n. 4
0
/**
 * 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;
}
Esempio n. 5
0
/*
 * 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;
}
Esempio n. 6
0
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;
}
Esempio n. 7
0
/*
 * 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;
	}
}