tracker.c (10278B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /* 6 * tracker.c 7 * 8 * This file contains the code used by the pointer-tracking calls used 9 * in the debug builds to catch bad pointers. The entire contents are 10 * only available in debug builds (both internal and external builds). 11 */ 12 13 #ifndef BASE_H 14 #include "base.h" 15 #endif /* BASE_H */ 16 17 #ifdef DEBUG 18 /* 19 * identity_hash 20 * 21 * This static callback is a PLHashFunction as defined in plhash.h 22 * It merely returns the value of the object pointer as its hash. 23 * There are no possible errors. 24 */ 25 26 static PLHashNumber PR_CALLBACK 27 identity_hash(const void *key) 28 { 29 return (PLHashNumber)((char *)key - (char *)NULL); 30 } 31 32 /* 33 * trackerOnceFunc 34 * 35 * This function is called once, using the nssCallOnce function above. 36 * It creates a new pointer tracker object; initialising its hash 37 * table and protective lock. 38 */ 39 40 static PRStatus 41 trackerOnceFunc(void *arg) 42 { 43 nssPointerTracker *tracker = (nssPointerTracker *)arg; 44 45 tracker->lock = PZ_NewLock(nssILockOther); 46 if ((PZLock *)NULL == tracker->lock) { 47 return PR_FAILURE; 48 } 49 50 tracker->table = 51 PL_NewHashTable(0, identity_hash, PL_CompareValues, PL_CompareValues, 52 (PLHashAllocOps *)NULL, (void *)NULL); 53 if ((PLHashTable *)NULL == tracker->table) { 54 PZ_DestroyLock(tracker->lock); 55 tracker->lock = (PZLock *)NULL; 56 return PR_FAILURE; 57 } 58 59 return PR_SUCCESS; 60 } 61 62 /* 63 * nssPointerTracker_initialize 64 * 65 * This method is only present in debug builds. 66 * 67 * This routine initializes an nssPointerTracker object. Note that 68 * the object must have been declared *static* to guarantee that it 69 * is in a zeroed state initially. This routine is idempotent, and 70 * may even be safely called by multiple threads simultaneously with 71 * the same argument. This routine returns a PRStatus value; if 72 * successful, it will return PR_SUCCESS. On failure it will set an 73 * error on the error stack and return PR_FAILURE. 74 * 75 * The error may be one of the following values: 76 * NSS_ERROR_NO_MEMORY 77 * 78 * Return value: 79 * PR_SUCCESS 80 * PR_FAILURE 81 */ 82 83 NSS_IMPLEMENT PRStatus 84 nssPointerTracker_initialize(nssPointerTracker *tracker) 85 { 86 PRStatus rv = PR_CallOnceWithArg(&tracker->once, trackerOnceFunc, tracker); 87 if (PR_SUCCESS != rv) { 88 nss_SetError(NSS_ERROR_NO_MEMORY); 89 } 90 91 return rv; 92 } 93 94 #ifdef DONT_DESTROY_EMPTY_TABLES 95 /* See same #ifdef below */ 96 /* 97 * count_entries 98 * 99 * This static routine is a PLHashEnumerator, as defined in plhash.h. 100 * It merely causes the enumeration function to count the number of 101 * entries. 102 */ 103 104 static PRIntn PR_CALLBACK 105 count_entries(PLHashEntry *he, PRIntn index, void *arg) 106 { 107 return HT_ENUMERATE_NEXT; 108 } 109 #endif /* DONT_DESTROY_EMPTY_TABLES */ 110 111 /* 112 * zero_once 113 * 114 * This is a guaranteed zeroed once block. It's used to help clear 115 * the tracker. 116 */ 117 118 static const PRCallOnceType zero_once; 119 120 /* 121 * nssPointerTracker_finalize 122 * 123 * This method is only present in debug builds. 124 * 125 * This routine returns the nssPointerTracker object to the pre- 126 * initialized state, releasing all resources used by the object. 127 * It will *NOT* destroy the objects being tracked by the pointer 128 * (should any remain), and therefore cannot be used to "sweep up" 129 * remaining objects. This routine returns a PRStatus value; if 130 * successful, it will return PR_SUCCES. On failure it will set an 131 * error on the error stack and return PR_FAILURE. If any objects 132 * remain in the tracker when it is finalized, that will be treated 133 * as an error. 134 * 135 * The error may be one of the following values: 136 * NSS_ERROR_INVALID_POINTER 137 * NSS_ERROR_TRACKER_NOT_INITIALIZED 138 * NSS_ERROR_TRACKER_NOT_EMPTY 139 * 140 * Return value: 141 * PR_SUCCESS 142 * PR_FAILURE 143 */ 144 145 NSS_IMPLEMENT PRStatus 146 nssPointerTracker_finalize(nssPointerTracker *tracker) 147 { 148 PZLock *lock; 149 150 if ((nssPointerTracker *)NULL == tracker) { 151 nss_SetError(NSS_ERROR_INVALID_POINTER); 152 return PR_FAILURE; 153 } 154 155 if ((PZLock *)NULL == tracker->lock) { 156 nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); 157 return PR_FAILURE; 158 } 159 160 lock = tracker->lock; 161 PZ_Lock(lock); 162 163 if ((PLHashTable *)NULL == tracker->table) { 164 PZ_Unlock(lock); 165 nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); 166 return PR_FAILURE; 167 } 168 169 #ifdef DONT_DESTROY_EMPTY_TABLES 170 /* 171 * I changed my mind; I think we don't want this after all. 172 * Comments? 173 */ 174 count = PL_HashTableEnumerateEntries(tracker->table, count_entries, 175 (void *)NULL); 176 177 if (0 != count) { 178 PZ_Unlock(lock); 179 nss_SetError(NSS_ERROR_TRACKER_NOT_EMPTY); 180 return PR_FAILURE; 181 } 182 #endif /* DONT_DESTROY_EMPTY_TABLES */ 183 184 PL_HashTableDestroy(tracker->table); 185 /* memset(tracker, 0, sizeof(nssPointerTracker)); */ 186 tracker->once = zero_once; 187 tracker->lock = (PZLock *)NULL; 188 tracker->table = (PLHashTable *)NULL; 189 190 PZ_Unlock(lock); 191 PZ_DestroyLock(lock); 192 193 return PR_SUCCESS; 194 } 195 196 /* 197 * nssPointerTracker_add 198 * 199 * This method is only present in debug builds. 200 * 201 * This routine adds the specified pointer to the nssPointerTracker 202 * object. It should be called in constructor objects to register 203 * new valid objects. The nssPointerTracker is threadsafe, but this 204 * call is not idempotent. This routine returns a PRStatus value; 205 * if successful it will return PR_SUCCESS. On failure it will set 206 * an error on the error stack and return PR_FAILURE. 207 * 208 * The error may be one of the following values: 209 * NSS_ERROR_INVALID_POINTER 210 * NSS_ERROR_NO_MEMORY 211 * NSS_ERROR_TRACKER_NOT_INITIALIZED 212 * NSS_ERROR_DUPLICATE_POINTER 213 * 214 * Return value: 215 * PR_SUCCESS 216 * PR_FAILURE 217 */ 218 219 NSS_IMPLEMENT PRStatus 220 nssPointerTracker_add(nssPointerTracker *tracker, const void *pointer) 221 { 222 void *check; 223 PLHashEntry *entry; 224 225 if ((nssPointerTracker *)NULL == tracker) { 226 nss_SetError(NSS_ERROR_INVALID_POINTER); 227 return PR_FAILURE; 228 } 229 230 if ((PZLock *)NULL == tracker->lock) { 231 nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); 232 return PR_FAILURE; 233 } 234 235 PZ_Lock(tracker->lock); 236 237 if ((PLHashTable *)NULL == tracker->table) { 238 PZ_Unlock(tracker->lock); 239 nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); 240 return PR_FAILURE; 241 } 242 243 check = PL_HashTableLookup(tracker->table, pointer); 244 if ((void *)NULL != check) { 245 PZ_Unlock(tracker->lock); 246 nss_SetError(NSS_ERROR_DUPLICATE_POINTER); 247 return PR_FAILURE; 248 } 249 250 entry = PL_HashTableAdd(tracker->table, pointer, (void *)pointer); 251 252 PZ_Unlock(tracker->lock); 253 254 if ((PLHashEntry *)NULL == entry) { 255 nss_SetError(NSS_ERROR_NO_MEMORY); 256 return PR_FAILURE; 257 } 258 259 return PR_SUCCESS; 260 } 261 262 /* 263 * nssPointerTracker_remove 264 * 265 * This method is only present in debug builds. 266 * 267 * This routine removes the specified pointer from the 268 * nssPointerTracker object. It does not call any destructor for the 269 * object; rather, this should be called from the object's destructor. 270 * The nssPointerTracker is threadsafe, but this call is not 271 * idempotent. This routine returns a PRStatus value; if successful 272 * it will return PR_SUCCESS. On failure it will set an error on the 273 * error stack and return PR_FAILURE. 274 * 275 * The error may be one of the following values: 276 * NSS_ERROR_INVALID_POINTER 277 * NSS_ERROR_TRACKER_NOT_INITIALIZED 278 * NSS_ERROR_POINTER_NOT_REGISTERED 279 * 280 * Return value: 281 * PR_SUCCESS 282 * PR_FAILURE 283 */ 284 285 NSS_IMPLEMENT PRStatus 286 nssPointerTracker_remove(nssPointerTracker *tracker, const void *pointer) 287 { 288 PRBool registered; 289 290 if ((nssPointerTracker *)NULL == tracker) { 291 nss_SetError(NSS_ERROR_INVALID_POINTER); 292 return PR_FAILURE; 293 } 294 295 if ((PZLock *)NULL == tracker->lock) { 296 nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); 297 return PR_FAILURE; 298 } 299 300 PZ_Lock(tracker->lock); 301 302 if ((PLHashTable *)NULL == tracker->table) { 303 PZ_Unlock(tracker->lock); 304 nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); 305 return PR_FAILURE; 306 } 307 308 registered = PL_HashTableRemove(tracker->table, pointer); 309 PZ_Unlock(tracker->lock); 310 311 if (!registered) { 312 nss_SetError(NSS_ERROR_POINTER_NOT_REGISTERED); 313 return PR_FAILURE; 314 } 315 316 return PR_SUCCESS; 317 } 318 319 /* 320 * nssPointerTracker_verify 321 * 322 * This method is only present in debug builds. 323 * 324 * This routine verifies that the specified pointer has been registered 325 * with the nssPointerTracker object. The nssPointerTracker object is 326 * threadsafe, and this call may be safely called from multiple threads 327 * simultaneously with the same arguments. This routine returns a 328 * PRStatus value; if the pointer is registered this will return 329 * PR_SUCCESS. Otherwise it will set an error on the error stack and 330 * return PR_FAILURE. Although the error is suitable for leaving on 331 * the stack, callers may wish to augment the information available by 332 * placing a more type-specific error on the stack. 333 * 334 * The error may be one of the following values: 335 * NSS_ERROR_INVALID_POINTER 336 * NSS_ERROR_TRACKER_NOT_INITIALIZED 337 * NSS_ERROR_POINTER_NOT_REGISTERED 338 * 339 * Return value: 340 * PR_SUCCESS 341 * PR_FAILRUE 342 */ 343 344 NSS_IMPLEMENT PRStatus 345 nssPointerTracker_verify(nssPointerTracker *tracker, const void *pointer) 346 { 347 void *check; 348 349 if ((nssPointerTracker *)NULL == tracker) { 350 nss_SetError(NSS_ERROR_INVALID_POINTER); 351 return PR_FAILURE; 352 } 353 354 if ((PZLock *)NULL == tracker->lock) { 355 nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); 356 return PR_FAILURE; 357 } 358 359 PZ_Lock(tracker->lock); 360 361 if ((PLHashTable *)NULL == tracker->table) { 362 PZ_Unlock(tracker->lock); 363 nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); 364 return PR_FAILURE; 365 } 366 367 check = PL_HashTableLookup(tracker->table, pointer); 368 PZ_Unlock(tracker->lock); 369 370 if ((void *)NULL == check) { 371 nss_SetError(NSS_ERROR_POINTER_NOT_REGISTERED); 372 return PR_FAILURE; 373 } 374 375 return PR_SUCCESS; 376 } 377 378 #endif /* DEBUG */