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