|
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 * nssilock.c - NSS lock instrumentation wrapper functions |
|
7 * |
|
8 * NOTE - These are not public interfaces |
|
9 * |
|
10 * Implementation Notes: |
|
11 * I've tried to make the instrumentation relatively non-intrusive. |
|
12 * To do this, I have used a single PR_LOG() call in each |
|
13 * instrumented function. There's room for improvement. |
|
14 * |
|
15 * |
|
16 */ |
|
17 |
|
18 #include "prinit.h" |
|
19 #include "prerror.h" |
|
20 #include "prlock.h" |
|
21 #include "prmem.h" |
|
22 #include "prenv.h" |
|
23 #include "prcvar.h" |
|
24 #include "prio.h" |
|
25 |
|
26 #if defined(NEED_NSS_ILOCK) |
|
27 #include "prlog.h" |
|
28 #include "nssilock.h" |
|
29 |
|
30 /* |
|
31 ** Declare the instrumented PZLock |
|
32 */ |
|
33 struct pzlock_s { |
|
34 PRLock *lock; /* the PZLock to be instrumented */ |
|
35 PRIntervalTime time; /* timestamp when the lock was aquired */ |
|
36 nssILockType ltype; |
|
37 }; |
|
38 |
|
39 /* |
|
40 ** Declare the instrumented PZMonitor |
|
41 */ |
|
42 struct pzmonitor_s { |
|
43 PRMonitor *mon; /* the PZMonitor to be instrumented */ |
|
44 PRIntervalTime time; /* timestamp when the monitor was aquired */ |
|
45 nssILockType ltype; |
|
46 }; |
|
47 |
|
48 /* |
|
49 ** Declare the instrumented PZCondVar |
|
50 */ |
|
51 struct pzcondvar_s { |
|
52 PRCondVar *cvar; /* the PZCondVar to be instrumented */ |
|
53 nssILockType ltype; |
|
54 }; |
|
55 |
|
56 |
|
57 /* |
|
58 ** Define a CallOnce type to ensure serialized self-initialization |
|
59 */ |
|
60 static PRCallOnceType coNssILock; /* CallOnce type */ |
|
61 static PRIntn nssILockInitialized; /* initialization done when 1 */ |
|
62 static PRLogModuleInfo *nssILog; /* Log instrumentation to this handle */ |
|
63 |
|
64 |
|
65 #define NUM_TT_ENTRIES 6000000 |
|
66 static PRInt32 traceIndex = -1; /* index into trace table */ |
|
67 static struct pzTrace_s *tt; /* pointer to trace table */ |
|
68 static PRInt32 ttBufSize = (NUM_TT_ENTRIES * sizeof(struct pzTrace_s )); |
|
69 static PRCondVar *ttCVar; |
|
70 static PRLock *ttLock; |
|
71 static PRFileDesc *ttfd; /* trace table file */ |
|
72 |
|
73 /* |
|
74 ** Vtrace() -- Trace events, write events to external media |
|
75 ** |
|
76 ** Vtrace() records traced events in an in-memory trace table |
|
77 ** when the trace table fills, Vtrace writes the entire table |
|
78 ** to a file. |
|
79 ** |
|
80 ** data can be lost! |
|
81 ** |
|
82 */ |
|
83 static void Vtrace( |
|
84 nssILockOp op, |
|
85 nssILockType ltype, |
|
86 PRIntervalTime callTime, |
|
87 PRIntervalTime heldTime, |
|
88 void *lock, |
|
89 PRIntn line, |
|
90 char *file |
|
91 ) { |
|
92 PRInt32 idx; |
|
93 struct pzTrace_s *tp; |
|
94 |
|
95 RetryTrace: |
|
96 idx = PR_ATOMIC_INCREMENT( &traceIndex ); |
|
97 while( NUM_TT_ENTRIES <= idx || op == FlushTT ) { |
|
98 if( NUM_TT_ENTRIES == idx || op == FlushTT ) { |
|
99 int writeSize = idx * sizeof(struct pzTrace_s); |
|
100 PR_Lock(ttLock); |
|
101 PR_Write( ttfd, tt, writeSize ); |
|
102 traceIndex = -1; |
|
103 PR_NotifyAllCondVar( ttCVar ); |
|
104 PR_Unlock(ttLock); |
|
105 goto RetryTrace; |
|
106 } else { |
|
107 PR_Lock(ttLock); |
|
108 while( NUM_TT_ENTRIES < idx ) |
|
109 PR_WaitCondVar(ttCVar, PR_INTERVAL_NO_WAIT); |
|
110 PR_Unlock(ttLock); |
|
111 goto RetryTrace; |
|
112 } |
|
113 } /* end while() */ |
|
114 |
|
115 /* create the trace entry */ |
|
116 tp = tt + idx; |
|
117 tp->threadID = PR_GetThreadID(PR_GetCurrentThread()); |
|
118 tp->op = op; |
|
119 tp->ltype = ltype; |
|
120 tp->callTime = callTime; |
|
121 tp->heldTime = heldTime; |
|
122 tp->lock = lock; |
|
123 tp ->line = line; |
|
124 strcpy(tp->file, file ); |
|
125 return; |
|
126 } /* --- end Vtrace() --- */ |
|
127 |
|
128 /* |
|
129 ** pz_TraceFlush() -- Force trace table write to file |
|
130 ** |
|
131 */ |
|
132 extern void pz_TraceFlush( void ) |
|
133 { |
|
134 Vtrace( FlushTT, nssILockSelfServ, 0, 0, NULL, 0, "" ); |
|
135 return; |
|
136 } /* --- end pz_TraceFlush() --- */ |
|
137 |
|
138 /* |
|
139 ** nssILockInit() -- Initialization for nssilock |
|
140 ** |
|
141 ** This function is called from the CallOnce mechanism. |
|
142 */ |
|
143 static PRStatus |
|
144 nssILockInit( void ) |
|
145 { |
|
146 int i; |
|
147 nssILockInitialized = 1; |
|
148 |
|
149 /* new log module */ |
|
150 nssILog = PR_NewLogModule("nssilock"); |
|
151 if ( NULL == nssILog ) { |
|
152 return(PR_FAILURE); |
|
153 } |
|
154 |
|
155 tt = PR_Calloc( NUM_TT_ENTRIES, sizeof(struct pzTrace_s)); |
|
156 if (NULL == tt ) { |
|
157 fprintf(stderr, "nssilock: can't allocate trace table\n"); |
|
158 exit(1); |
|
159 } |
|
160 |
|
161 ttfd = PR_Open( "xxxTTLog", PR_CREATE_FILE | PR_WRONLY, 0666 ); |
|
162 if ( NULL == ttfd ) { |
|
163 fprintf( stderr, "Oh Drat! Can't open 'xxxTTLog'\n"); |
|
164 exit(1); |
|
165 } |
|
166 |
|
167 ttLock = PR_NewLock(); |
|
168 ttCVar = PR_NewCondVar(ttLock); |
|
169 |
|
170 return(PR_SUCCESS); |
|
171 } /* --- end nssILockInit() --- */ |
|
172 |
|
173 extern PZLock * pz_NewLock( |
|
174 nssILockType ltype, |
|
175 char *file, |
|
176 PRIntn line ) |
|
177 { |
|
178 PRStatus rc; |
|
179 PZLock *lock; |
|
180 |
|
181 /* Self Initialize the nssILock feature */ |
|
182 if (!nssILockInitialized) { |
|
183 rc = PR_CallOnce( &coNssILock, nssILockInit ); |
|
184 if ( PR_FAILURE == rc ) { |
|
185 PR_SetError( PR_UNKNOWN_ERROR, 0 ); |
|
186 return( NULL ); |
|
187 } |
|
188 } |
|
189 |
|
190 lock = PR_NEWZAP( PZLock ); |
|
191 if ( NULL != lock ) { |
|
192 lock->ltype = ltype; |
|
193 lock->lock = PR_NewLock(); |
|
194 if ( NULL == lock->lock ) { |
|
195 PR_DELETE( lock ); |
|
196 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
197 } |
|
198 } else { |
|
199 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
200 } |
|
201 |
|
202 Vtrace( NewLock, ltype, 0, 0, lock, line, file ); |
|
203 return(lock); |
|
204 } /* --- end pz_NewLock() --- */ |
|
205 |
|
206 extern void |
|
207 pz_Lock( |
|
208 PZLock *lock, |
|
209 char *file, |
|
210 PRIntn line |
|
211 ) |
|
212 { |
|
213 PRIntervalTime callTime; |
|
214 |
|
215 callTime = PR_IntervalNow(); |
|
216 PR_Lock( lock->lock ); |
|
217 lock->time = PR_IntervalNow(); |
|
218 callTime = lock->time - callTime; |
|
219 |
|
220 Vtrace( Lock, lock->ltype, callTime, 0, lock, line, file ); |
|
221 return; |
|
222 } /* --- end pz_Lock() --- */ |
|
223 |
|
224 extern PRStatus |
|
225 pz_Unlock( |
|
226 PZLock *lock, |
|
227 char *file, |
|
228 PRIntn line |
|
229 ) |
|
230 { |
|
231 PRStatus rc; |
|
232 PRIntervalTime callTime, now, heldTime; |
|
233 |
|
234 callTime = PR_IntervalNow(); |
|
235 rc = PR_Unlock( lock->lock ); |
|
236 now = PR_IntervalNow(); |
|
237 callTime = now - callTime; |
|
238 heldTime = now - lock->time; |
|
239 Vtrace( Unlock, lock->ltype, callTime, heldTime, lock, line, file ); |
|
240 return( rc ); |
|
241 } /* --- end pz_Unlock() --- */ |
|
242 |
|
243 extern void |
|
244 pz_DestroyLock( |
|
245 PZLock *lock, |
|
246 char *file, |
|
247 PRIntn line |
|
248 ) |
|
249 { |
|
250 Vtrace( DestroyLock, lock->ltype, 0, 0, lock, line, file ); |
|
251 PR_DestroyLock( lock->lock ); |
|
252 PR_DELETE( lock ); |
|
253 return; |
|
254 } /* --- end pz_DestroyLock() --- */ |
|
255 |
|
256 |
|
257 |
|
258 extern PZCondVar * |
|
259 pz_NewCondVar( |
|
260 PZLock *lock, |
|
261 char *file, |
|
262 PRIntn line |
|
263 ) |
|
264 { |
|
265 PZCondVar *cvar; |
|
266 |
|
267 cvar = PR_NEWZAP( PZCondVar ); |
|
268 if ( NULL == cvar ) { |
|
269 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
270 } else { |
|
271 cvar->ltype = lock->ltype; |
|
272 cvar->cvar = PR_NewCondVar( lock->lock ); |
|
273 if ( NULL == cvar->cvar ) { |
|
274 PR_DELETE( cvar ); |
|
275 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
276 } |
|
277 |
|
278 } |
|
279 Vtrace( NewCondVar, lock->ltype, 0, 0, cvar, line, file ); |
|
280 return( cvar ); |
|
281 } /* --- end pz_NewCondVar() --- */ |
|
282 |
|
283 extern void |
|
284 pz_DestroyCondVar( |
|
285 PZCondVar *cvar, |
|
286 char *file, |
|
287 PRIntn line |
|
288 ) |
|
289 { |
|
290 Vtrace( DestroyCondVar, cvar->ltype, 0, 0, cvar, line, file ); |
|
291 PR_DestroyCondVar( cvar->cvar ); |
|
292 PR_DELETE( cvar ); |
|
293 } /* --- end pz_DestroyCondVar() --- */ |
|
294 |
|
295 extern PRStatus |
|
296 pz_WaitCondVar( |
|
297 PZCondVar *cvar, |
|
298 PRIntervalTime timeout, |
|
299 char *file, |
|
300 PRIntn line |
|
301 ) |
|
302 { |
|
303 PRStatus rc; |
|
304 PRIntervalTime callTime; |
|
305 |
|
306 callTime = PR_IntervalNow(); |
|
307 rc = PR_WaitCondVar( cvar->cvar, timeout ); |
|
308 callTime = PR_IntervalNow() - callTime; |
|
309 |
|
310 Vtrace( WaitCondVar, cvar->ltype, callTime, 0, cvar, line, file ); |
|
311 return(rc); |
|
312 } /* --- end pz_WaitCondVar() --- */ |
|
313 |
|
314 extern PRStatus |
|
315 pz_NotifyCondVar( |
|
316 PZCondVar *cvar, |
|
317 char *file, |
|
318 PRIntn line |
|
319 ) |
|
320 { |
|
321 PRStatus rc; |
|
322 |
|
323 rc = PR_NotifyCondVar( cvar->cvar ); |
|
324 |
|
325 Vtrace( NotifyCondVar, cvar->ltype, 0, 0, cvar, line, file ); |
|
326 return(rc); |
|
327 } /* --- end pz_NotifyCondVar() --- */ |
|
328 |
|
329 extern PRStatus |
|
330 pz_NotifyAllCondVar( |
|
331 PZCondVar *cvar, |
|
332 char *file, |
|
333 PRIntn line |
|
334 ) |
|
335 { |
|
336 PRStatus rc; |
|
337 |
|
338 rc = PR_NotifyAllCondVar( cvar->cvar ); |
|
339 |
|
340 Vtrace( NotifyAllCondVar, cvar->ltype, 0, 0, cvar, line, file ); |
|
341 return(rc); |
|
342 } /* --- end pz_NotifyAllCondVar() --- */ |
|
343 |
|
344 extern PZMonitor * |
|
345 pz_NewMonitor( |
|
346 nssILockType ltype, |
|
347 char *file, |
|
348 PRIntn line |
|
349 ) |
|
350 { |
|
351 PRStatus rc; |
|
352 PZMonitor *mon; |
|
353 |
|
354 /* Self Initialize the nssILock feature */ |
|
355 if (!nssILockInitialized) { |
|
356 rc = PR_CallOnce( &coNssILock, nssILockInit ); |
|
357 if ( PR_FAILURE == rc ) { |
|
358 PR_SetError( PR_UNKNOWN_ERROR, 0 ); |
|
359 return( NULL ); |
|
360 } |
|
361 } |
|
362 |
|
363 mon = PR_NEWZAP( PZMonitor ); |
|
364 if ( NULL != mon ) { |
|
365 mon->ltype = ltype; |
|
366 mon->mon = PR_NewMonitor(); |
|
367 if ( NULL == mon->mon ) { |
|
368 PR_DELETE( mon ); |
|
369 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
370 } |
|
371 } else { |
|
372 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
373 } |
|
374 |
|
375 Vtrace( NewMonitor, ltype, 0, 0, mon, line, file ); |
|
376 return(mon); |
|
377 } /* --- end pz_NewMonitor() --- */ |
|
378 |
|
379 extern void |
|
380 pz_DestroyMonitor( |
|
381 PZMonitor *mon, |
|
382 char *file, |
|
383 PRIntn line |
|
384 ) |
|
385 { |
|
386 Vtrace( DestroyMonitor, mon->ltype, 0, 0, mon, line, file ); |
|
387 PR_DestroyMonitor( mon->mon ); |
|
388 PR_DELETE( mon ); |
|
389 return; |
|
390 } /* --- end pz_DestroyMonitor() --- */ |
|
391 |
|
392 extern void |
|
393 pz_EnterMonitor( |
|
394 PZMonitor *mon, |
|
395 char *file, |
|
396 PRIntn line |
|
397 ) |
|
398 { |
|
399 PRIntervalTime callTime, now; |
|
400 |
|
401 callTime = PR_IntervalNow(); |
|
402 PR_EnterMonitor( mon->mon ); |
|
403 now = PR_IntervalNow(); |
|
404 callTime = now - callTime; |
|
405 if ( PR_GetMonitorEntryCount(mon->mon) == 1 ) { |
|
406 mon->time = now; |
|
407 } |
|
408 Vtrace( EnterMonitor, mon->ltype, callTime, 0, mon, line, file ); |
|
409 return; |
|
410 } /* --- end pz_EnterMonitor() --- */ |
|
411 |
|
412 extern PRStatus |
|
413 pz_ExitMonitor( |
|
414 PZMonitor *mon, |
|
415 char *file, |
|
416 PRIntn line |
|
417 ) |
|
418 { |
|
419 PRStatus rc; |
|
420 PRIntervalTime callTime, now, heldTime; |
|
421 PRIntn mec = PR_GetMonitorEntryCount( mon->mon ); |
|
422 |
|
423 heldTime = (PRIntervalTime)-1; |
|
424 callTime = PR_IntervalNow(); |
|
425 rc = PR_ExitMonitor( mon->mon ); |
|
426 now = PR_IntervalNow(); |
|
427 callTime = now - callTime; |
|
428 if ( mec == 1 ) |
|
429 heldTime = now - mon->time; |
|
430 Vtrace( ExitMonitor, mon->ltype, callTime, heldTime, mon, line, file ); |
|
431 return( rc ); |
|
432 } /* --- end pz_ExitMonitor() --- */ |
|
433 |
|
434 extern PRIntn |
|
435 pz_GetMonitorEntryCount( |
|
436 PZMonitor *mon, |
|
437 char *file, |
|
438 PRIntn line |
|
439 ) |
|
440 { |
|
441 return( PR_GetMonitorEntryCount(mon->mon)); |
|
442 } /* --- end pz_GetMonitorEntryCount() --- */ |
|
443 |
|
444 |
|
445 extern PRStatus |
|
446 pz_Wait( |
|
447 PZMonitor *mon, |
|
448 PRIntervalTime ticks, |
|
449 char *file, |
|
450 PRIntn line |
|
451 ) |
|
452 { |
|
453 PRStatus rc; |
|
454 PRIntervalTime callTime; |
|
455 |
|
456 callTime = PR_IntervalNow(); |
|
457 rc = PR_Wait( mon->mon, ticks ); |
|
458 callTime = PR_IntervalNow() - callTime; |
|
459 Vtrace( Wait, mon->ltype, callTime, 0, mon, line, file ); |
|
460 return( rc ); |
|
461 } /* --- end pz_Wait() --- */ |
|
462 |
|
463 extern PRStatus |
|
464 pz_Notify( |
|
465 PZMonitor *mon, |
|
466 char *file, |
|
467 PRIntn line |
|
468 ) |
|
469 { |
|
470 PRStatus rc; |
|
471 PRIntervalTime callTime; |
|
472 |
|
473 callTime = PR_IntervalNow(); |
|
474 rc = PR_Notify( mon->mon ); |
|
475 callTime = PR_IntervalNow() - callTime; |
|
476 Vtrace( Notify, mon->ltype, callTime, 0, mon, line, file ); |
|
477 return( rc ); |
|
478 } /* --- end pz_Notify() --- */ |
|
479 |
|
480 extern PRStatus |
|
481 pz_NotifyAll( |
|
482 PZMonitor *mon, |
|
483 char *file, |
|
484 PRIntn line |
|
485 ) |
|
486 { |
|
487 PRStatus rc; |
|
488 PRIntervalTime callTime; |
|
489 |
|
490 callTime = PR_IntervalNow(); |
|
491 rc = PR_NotifyAll( mon->mon ); |
|
492 callTime = PR_IntervalNow() - callTime; |
|
493 Vtrace( NotifyAll, mon->ltype, callTime, 0, mon, line, file ); |
|
494 return( rc ); |
|
495 } /* --- end pz_NotifyAll() --- */ |
|
496 |
|
497 #endif /* NEED_NSS_ILOCK */ |
|
498 /* --- end nssilock.c --------------------------------- */ |