/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * tracker.c * * This file contains the code used by the pointer-tracking calls used * in the debug builds to catch bad pointers. The entire contents are * only available in debug builds (both internal and external builds). */ #ifndef BASE_H #include "base.h" #endif /* BASE_H */ #ifdef DEBUG /* * identity_hash * * This static callback is a PLHashFunction as defined in plhash.h * It merely returns the value of the object pointer as its hash. * There are no possible errors. */ static PLHashNumber PR_CALLBACK identity_hash(const void *key) { return (PLHashNumber)((char *)key - (char *)NULL); } /* * trackerOnceFunc * * This function is called once, using the nssCallOnce function above. * It creates a new pointer tracker object; initialising its hash * table and protective lock. */ static PRStatus trackerOnceFunc(void *arg) { nssPointerTracker *tracker = (nssPointerTracker *)arg; tracker->lock = PZ_NewLock(nssILockOther); if ((PZLock *)NULL == tracker->lock) { return PR_FAILURE; } tracker->table = PL_NewHashTable(0, identity_hash, PL_CompareValues, PL_CompareValues, (PLHashAllocOps *)NULL, (void *)NULL); if ((PLHashTable *)NULL == tracker->table) { PZ_DestroyLock(tracker->lock); tracker->lock = (PZLock *)NULL; return PR_FAILURE; } return PR_SUCCESS; } /* * nssPointerTracker_initialize * * This method is only present in debug builds. * * This routine initializes an nssPointerTracker object. Note that * the object must have been declared *static* to guarantee that it * is in a zeroed state initially. This routine is idempotent, and * may even be safely called by multiple threads simultaneously with * the same argument. This routine returns a PRStatus value; if * successful, it will return PR_SUCCESS. On failure it will set an * error on the error stack and return PR_FAILURE. * * The error may be one of the following values: * NSS_ERROR_NO_MEMORY * * Return value: * PR_SUCCESS * PR_FAILURE */ NSS_IMPLEMENT PRStatus nssPointerTracker_initialize(nssPointerTracker *tracker) { PRStatus rv = PR_CallOnceWithArg(&tracker->once, trackerOnceFunc, tracker); if (PR_SUCCESS != rv) { nss_SetError(NSS_ERROR_NO_MEMORY); } return rv; } #ifdef DONT_DESTROY_EMPTY_TABLES /* See same #ifdef below */ /* * count_entries * * This static routine is a PLHashEnumerator, as defined in plhash.h. * It merely causes the enumeration function to count the number of * entries. */ static PRIntn PR_CALLBACK count_entries(PLHashEntry *he, PRIntn index, void *arg) { return HT_ENUMERATE_NEXT; } #endif /* DONT_DESTROY_EMPTY_TABLES */ /* * zero_once * * This is a guaranteed zeroed once block. It's used to help clear * the tracker. */ static const PRCallOnceType zero_once; /* * nssPointerTracker_finalize * * This method is only present in debug builds. * * This routine returns the nssPointerTracker object to the pre- * initialized state, releasing all resources used by the object. * It will *NOT* destroy the objects being tracked by the pointer * (should any remain), and therefore cannot be used to "sweep up" * remaining objects. This routine returns a PRStatus value; if * successful, it will return PR_SUCCES. On failure it will set an * error on the error stack and return PR_FAILURE. If any objects * remain in the tracker when it is finalized, that will be treated * as an error. * * The error may be one of the following values: * NSS_ERROR_INVALID_POINTER * NSS_ERROR_TRACKER_NOT_INITIALIZED * NSS_ERROR_TRACKER_NOT_EMPTY * * Return value: * PR_SUCCESS * PR_FAILURE */ NSS_IMPLEMENT PRStatus nssPointerTracker_finalize(nssPointerTracker *tracker) { PZLock *lock; if ((nssPointerTracker *)NULL == tracker) { nss_SetError(NSS_ERROR_INVALID_POINTER); return PR_FAILURE; } if ((PZLock *)NULL == tracker->lock) { nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); return PR_FAILURE; } lock = tracker->lock; PZ_Lock(lock); if ((PLHashTable *)NULL == tracker->table) { PZ_Unlock(lock); nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); return PR_FAILURE; } #ifdef DONT_DESTROY_EMPTY_TABLES /* * I changed my mind; I think we don't want this after all. * Comments? */ count = PL_HashTableEnumerateEntries(tracker->table, count_entries, (void *)NULL); if (0 != count) { PZ_Unlock(lock); nss_SetError(NSS_ERROR_TRACKER_NOT_EMPTY); return PR_FAILURE; } #endif /* DONT_DESTROY_EMPTY_TABLES */ PL_HashTableDestroy(tracker->table); /* memset(tracker, 0, sizeof(nssPointerTracker)); */ tracker->once = zero_once; tracker->lock = (PZLock *)NULL; tracker->table = (PLHashTable *)NULL; PZ_Unlock(lock); PZ_DestroyLock(lock); return PR_SUCCESS; } /* * nssPointerTracker_add * * This method is only present in debug builds. * * This routine adds the specified pointer to the nssPointerTracker * object. It should be called in constructor objects to register * new valid objects. The nssPointerTracker is threadsafe, but this * call is not idempotent. This routine returns a PRStatus value; * if successful it will return PR_SUCCESS. On failure it will set * an error on the error stack and return PR_FAILURE. * * The error may be one of the following values: * NSS_ERROR_INVALID_POINTER * NSS_ERROR_NO_MEMORY * NSS_ERROR_TRACKER_NOT_INITIALIZED * NSS_ERROR_DUPLICATE_POINTER * * Return value: * PR_SUCCESS * PR_FAILURE */ NSS_IMPLEMENT PRStatus nssPointerTracker_add(nssPointerTracker *tracker, const void *pointer) { void *check; PLHashEntry *entry; if ((nssPointerTracker *)NULL == tracker) { nss_SetError(NSS_ERROR_INVALID_POINTER); return PR_FAILURE; } if ((PZLock *)NULL == tracker->lock) { nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); return PR_FAILURE; } PZ_Lock(tracker->lock); if ((PLHashTable *)NULL == tracker->table) { PZ_Unlock(tracker->lock); nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); return PR_FAILURE; } check = PL_HashTableLookup(tracker->table, pointer); if ((void *)NULL != check) { PZ_Unlock(tracker->lock); nss_SetError(NSS_ERROR_DUPLICATE_POINTER); return PR_FAILURE; } entry = PL_HashTableAdd(tracker->table, pointer, (void *)pointer); PZ_Unlock(tracker->lock); if ((PLHashEntry *)NULL == entry) { nss_SetError(NSS_ERROR_NO_MEMORY); return PR_FAILURE; } return PR_SUCCESS; } /* * nssPointerTracker_remove * * This method is only present in debug builds. * * This routine removes the specified pointer from the * nssPointerTracker object. It does not call any destructor for the * object; rather, this should be called from the object's destructor. * The nssPointerTracker is threadsafe, but this call is not * idempotent. This routine returns a PRStatus value; if successful * it will return PR_SUCCESS. On failure it will set an error on the * error stack and return PR_FAILURE. * * The error may be one of the following values: * NSS_ERROR_INVALID_POINTER * NSS_ERROR_TRACKER_NOT_INITIALIZED * NSS_ERROR_POINTER_NOT_REGISTERED * * Return value: * PR_SUCCESS * PR_FAILURE */ NSS_IMPLEMENT PRStatus nssPointerTracker_remove(nssPointerTracker *tracker, const void *pointer) { PRBool registered; if ((nssPointerTracker *)NULL == tracker) { nss_SetError(NSS_ERROR_INVALID_POINTER); return PR_FAILURE; } if ((PZLock *)NULL == tracker->lock) { nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); return PR_FAILURE; } PZ_Lock(tracker->lock); if ((PLHashTable *)NULL == tracker->table) { PZ_Unlock(tracker->lock); nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); return PR_FAILURE; } registered = PL_HashTableRemove(tracker->table, pointer); PZ_Unlock(tracker->lock); if (!registered) { nss_SetError(NSS_ERROR_POINTER_NOT_REGISTERED); return PR_FAILURE; } return PR_SUCCESS; } /* * nssPointerTracker_verify * * This method is only present in debug builds. * * This routine verifies that the specified pointer has been registered * with the nssPointerTracker object. The nssPointerTracker object is * threadsafe, and this call may be safely called from multiple threads * simultaneously with the same arguments. This routine returns a * PRStatus value; if the pointer is registered this will return * PR_SUCCESS. Otherwise it will set an error on the error stack and * return PR_FAILURE. Although the error is suitable for leaving on * the stack, callers may wish to augment the information available by * placing a more type-specific error on the stack. * * The error may be one of the following values: * NSS_ERROR_INVALID_POINTER * NSS_ERROR_TRACKER_NOT_INITIALIZED * NSS_ERROR_POINTER_NOT_REGISTERED * * Return value: * PR_SUCCESS * PR_FAILRUE */ NSS_IMPLEMENT PRStatus nssPointerTracker_verify(nssPointerTracker *tracker, const void *pointer) { void *check; if ((nssPointerTracker *)NULL == tracker) { nss_SetError(NSS_ERROR_INVALID_POINTER); return PR_FAILURE; } if ((PZLock *)NULL == tracker->lock) { nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); return PR_FAILURE; } PZ_Lock(tracker->lock); if ((PLHashTable *)NULL == tracker->table) { PZ_Unlock(tracker->lock); nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); return PR_FAILURE; } check = PL_HashTableLookup(tracker->table, pointer); PZ_Unlock(tracker->lock); if ((void *)NULL == check) { nss_SetError(NSS_ERROR_POINTER_NOT_REGISTERED); return PR_FAILURE; } return PR_SUCCESS; } #endif /* DEBUG */