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 ** Define a CallOnce type to ensure serialized self-initialization
58 */
59 static PRCallOnceType coNssILock;  /* CallOnce type */
60 static PRIntn nssILockInitialized; /* initialization done when 1 */
61 static PRLogModuleInfo *nssILog;   /* Log instrumentation to this handle */
62 
63 #define NUM_TT_ENTRIES 6000000
64 static PRInt32 traceIndex = -1; /* index into trace table */
65 static struct pzTrace_s *tt;    /* pointer to trace table */
66 static PRInt32 ttBufSize = (NUM_TT_ENTRIES * sizeof(struct pzTrace_s));
67 static PRCondVar *ttCVar;
68 static PRLock *ttLock;
69 static PRFileDesc *ttfd; /* trace table file */
70 
71 /*
72 ** Vtrace() -- Trace events, write events to external media
73 **
74 ** Vtrace() records traced events in an in-memory trace table
75 ** when the trace table fills, Vtrace writes the entire table
76 ** to a file.
77 **
78 ** data can be lost!
79 **
80 */
81 static void
Vtrace(nssILockOp op,nssILockType ltype,PRIntervalTime callTime,PRIntervalTime heldTime,void * lock,PRIntn line,char * file)82 Vtrace(
83     nssILockOp op,
84     nssILockType ltype,
85     PRIntervalTime callTime,
86     PRIntervalTime heldTime,
87     void *lock,
88     PRIntn line,
89     char *file)
90 {
91     PRInt32 idx;
92     struct pzTrace_s *tp;
93 
94 RetryTrace:
95     idx = PR_ATOMIC_INCREMENT(&traceIndex);
96     while (NUM_TT_ENTRIES <= idx || op == FlushTT) {
97         if (NUM_TT_ENTRIES == idx || op == FlushTT) {
98             int writeSize = idx * sizeof(struct pzTrace_s);
99             PR_Lock(ttLock);
100             PR_Write(ttfd, tt, writeSize);
101             traceIndex = -1;
102             PR_NotifyAllCondVar(ttCVar);
103             PR_Unlock(ttLock);
104             goto RetryTrace;
105         } else {
106             PR_Lock(ttLock);
107             while (NUM_TT_ENTRIES < idx)
108                 PR_WaitCondVar(ttCVar, PR_INTERVAL_NO_WAIT);
109             PR_Unlock(ttLock);
110             goto RetryTrace;
111         }
112     } /* end while() */
113 
114     /* create the trace entry */
115     tp = tt + idx;
116     tp->threadID = PR_GetThreadID(PR_GetCurrentThread());
117     tp->op = op;
118     tp->ltype = ltype;
119     tp->callTime = callTime;
120     tp->heldTime = heldTime;
121     tp->lock = lock;
122     tp->line = line;
123     strcpy(tp->file, file);
124     return;
125 } /* --- end Vtrace() --- */
126 
127 /*
128 ** pz_TraceFlush() -- Force trace table write to file
129 **
130 */
131 extern void
pz_TraceFlush(void)132 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
nssILockInit(void)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(nssILockType ltype,char * file,PRIntn line)174 pz_NewLock(
175     nssILockType ltype,
176     char *file,
177     PRIntn line)
178 {
179     PRStatus rc;
180     PZLock *lock;
181 
182     /* Self Initialize the nssILock feature */
183     if (!nssILockInitialized) {
184         rc = PR_CallOnce(&coNssILock, nssILockInit);
185         if (PR_FAILURE == rc) {
186             PR_SetError(PR_UNKNOWN_ERROR, 0);
187             return (NULL);
188         }
189     }
190 
191     lock = PR_NEWZAP(PZLock);
192     if (NULL != lock) {
193         lock->ltype = ltype;
194         lock->lock = PR_NewLock();
195         if (NULL == lock->lock) {
196             PR_DELETE(lock);
197             PORT_SetError(SEC_ERROR_NO_MEMORY);
198         }
199     } else {
200         PORT_SetError(SEC_ERROR_NO_MEMORY);
201     }
202 
203     Vtrace(NewLock, ltype, 0, 0, lock, line, file);
204     return (lock);
205 } /* --- end pz_NewLock() --- */
206 
207 extern void
pz_Lock(PZLock * lock,char * file,PRIntn line)208 pz_Lock(
209     PZLock *lock,
210     char *file,
211     PRIntn line)
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
pz_Unlock(PZLock * lock,char * file,PRIntn line)225 pz_Unlock(
226     PZLock *lock,
227     char *file,
228     PRIntn line)
229 {
230     PRStatus rc;
231     PRIntervalTime callTime, now, heldTime;
232 
233     callTime = PR_IntervalNow();
234     rc = PR_Unlock(lock->lock);
235     now = PR_IntervalNow();
236     callTime = now - callTime;
237     heldTime = now - lock->time;
238     Vtrace(Unlock, lock->ltype, callTime, heldTime, lock, line, file);
239     return (rc);
240 } /* --- end  pz_Unlock() --- */
241 
242 extern void
pz_DestroyLock(PZLock * lock,char * file,PRIntn line)243 pz_DestroyLock(
244     PZLock *lock,
245     char *file,
246     PRIntn line)
247 {
248     Vtrace(DestroyLock, lock->ltype, 0, 0, lock, line, file);
249     PR_DestroyLock(lock->lock);
250     PR_DELETE(lock);
251     return;
252 } /* --- end  pz_DestroyLock() --- */
253 
254 extern PZCondVar *
pz_NewCondVar(PZLock * lock,char * file,PRIntn line)255 pz_NewCondVar(
256     PZLock *lock,
257     char *file,
258     PRIntn line)
259 {
260     PZCondVar *cvar;
261 
262     cvar = PR_NEWZAP(PZCondVar);
263     if (NULL == cvar) {
264         PORT_SetError(SEC_ERROR_NO_MEMORY);
265     } else {
266         cvar->ltype = lock->ltype;
267         cvar->cvar = PR_NewCondVar(lock->lock);
268         if (NULL == cvar->cvar) {
269             PR_DELETE(cvar);
270             PORT_SetError(SEC_ERROR_NO_MEMORY);
271         }
272     }
273     Vtrace(NewCondVar, lock->ltype, 0, 0, cvar, line, file);
274     return (cvar);
275 } /* --- end  pz_NewCondVar() --- */
276 
277 extern void
pz_DestroyCondVar(PZCondVar * cvar,char * file,PRIntn line)278 pz_DestroyCondVar(
279     PZCondVar *cvar,
280     char *file,
281     PRIntn line)
282 {
283     Vtrace(DestroyCondVar, cvar->ltype, 0, 0, cvar, line, file);
284     PR_DestroyCondVar(cvar->cvar);
285     PR_DELETE(cvar);
286 } /* --- end  pz_DestroyCondVar() --- */
287 
288 extern PRStatus
pz_WaitCondVar(PZCondVar * cvar,PRIntervalTime timeout,char * file,PRIntn line)289 pz_WaitCondVar(
290     PZCondVar *cvar,
291     PRIntervalTime timeout,
292     char *file,
293     PRIntn line)
294 {
295     PRStatus rc;
296     PRIntervalTime callTime;
297 
298     callTime = PR_IntervalNow();
299     rc = PR_WaitCondVar(cvar->cvar, timeout);
300     callTime = PR_IntervalNow() - callTime;
301 
302     Vtrace(WaitCondVar, cvar->ltype, callTime, 0, cvar, line, file);
303     return (rc);
304 } /* --- end  pz_WaitCondVar() --- */
305 
306 extern PRStatus
pz_NotifyCondVar(PZCondVar * cvar,char * file,PRIntn line)307 pz_NotifyCondVar(
308     PZCondVar *cvar,
309     char *file,
310     PRIntn line)
311 {
312     PRStatus rc;
313 
314     rc = PR_NotifyCondVar(cvar->cvar);
315 
316     Vtrace(NotifyCondVar, cvar->ltype, 0, 0, cvar, line, file);
317     return (rc);
318 } /* --- end  pz_NotifyCondVar() --- */
319 
320 extern PRStatus
pz_NotifyAllCondVar(PZCondVar * cvar,char * file,PRIntn line)321 pz_NotifyAllCondVar(
322     PZCondVar *cvar,
323     char *file,
324     PRIntn line)
325 {
326     PRStatus rc;
327 
328     rc = PR_NotifyAllCondVar(cvar->cvar);
329 
330     Vtrace(NotifyAllCondVar, cvar->ltype, 0, 0, cvar, line, file);
331     return (rc);
332 } /* --- end  pz_NotifyAllCondVar() --- */
333 
334 extern PZMonitor *
pz_NewMonitor(nssILockType ltype,char * file,PRIntn line)335 pz_NewMonitor(
336     nssILockType ltype,
337     char *file,
338     PRIntn line)
339 {
340     PRStatus rc;
341     PZMonitor *mon;
342 
343     /* Self Initialize the nssILock feature */
344     if (!nssILockInitialized) {
345         rc = PR_CallOnce(&coNssILock, nssILockInit);
346         if (PR_FAILURE == rc) {
347             PR_SetError(PR_UNKNOWN_ERROR, 0);
348             return (NULL);
349         }
350     }
351 
352     mon = PR_NEWZAP(PZMonitor);
353     if (NULL != mon) {
354         mon->ltype = ltype;
355         mon->mon = PR_NewMonitor();
356         if (NULL == mon->mon) {
357             PR_DELETE(mon);
358             PORT_SetError(SEC_ERROR_NO_MEMORY);
359         }
360     } else {
361         PORT_SetError(SEC_ERROR_NO_MEMORY);
362     }
363 
364     Vtrace(NewMonitor, ltype, 0, 0, mon, line, file);
365     return (mon);
366 } /* --- end  pz_NewMonitor() --- */
367 
368 extern void
pz_DestroyMonitor(PZMonitor * mon,char * file,PRIntn line)369 pz_DestroyMonitor(
370     PZMonitor *mon,
371     char *file,
372     PRIntn line)
373 {
374     Vtrace(DestroyMonitor, mon->ltype, 0, 0, mon, line, file);
375     PR_DestroyMonitor(mon->mon);
376     PR_DELETE(mon);
377     return;
378 } /* --- end  pz_DestroyMonitor() --- */
379 
380 extern void
pz_EnterMonitor(PZMonitor * mon,char * file,PRIntn line)381 pz_EnterMonitor(
382     PZMonitor *mon,
383     char *file,
384     PRIntn line)
385 {
386     PRIntervalTime callTime, now;
387 
388     callTime = PR_IntervalNow();
389     PR_EnterMonitor(mon->mon);
390     now = PR_IntervalNow();
391     callTime = now - callTime;
392     if (PR_GetMonitorEntryCount(mon->mon) == 1) {
393         mon->time = now;
394     }
395     Vtrace(EnterMonitor, mon->ltype, callTime, 0, mon, line, file);
396     return;
397 } /* --- end  pz_EnterMonitor() --- */
398 
399 extern PRStatus
pz_ExitMonitor(PZMonitor * mon,char * file,PRIntn line)400 pz_ExitMonitor(
401     PZMonitor *mon,
402     char *file,
403     PRIntn line)
404 {
405     PRStatus rc;
406     PRIntervalTime callTime, now, heldTime;
407     PRIntn mec = PR_GetMonitorEntryCount(mon->mon);
408 
409     heldTime = (PRIntervalTime)-1;
410     callTime = PR_IntervalNow();
411     rc = PR_ExitMonitor(mon->mon);
412     now = PR_IntervalNow();
413     callTime = now - callTime;
414     if (mec == 1)
415         heldTime = now - mon->time;
416     Vtrace(ExitMonitor, mon->ltype, callTime, heldTime, mon, line, file);
417     return (rc);
418 } /* --- end  pz_ExitMonitor() --- */
419 
420 extern PRIntn
pz_GetMonitorEntryCount(PZMonitor * mon,char * file,PRIntn line)421 pz_GetMonitorEntryCount(
422     PZMonitor *mon,
423     char *file,
424     PRIntn line)
425 {
426     return (PR_GetMonitorEntryCount(mon->mon));
427 } /* --- end pz_GetMonitorEntryCount() --- */
428 
429 extern PRStatus
pz_Wait(PZMonitor * mon,PRIntervalTime ticks,char * file,PRIntn line)430 pz_Wait(
431     PZMonitor *mon,
432     PRIntervalTime ticks,
433     char *file,
434     PRIntn line)
435 {
436     PRStatus rc;
437     PRIntervalTime callTime;
438 
439     callTime = PR_IntervalNow();
440     rc = PR_Wait(mon->mon, ticks);
441     callTime = PR_IntervalNow() - callTime;
442     Vtrace(Wait, mon->ltype, callTime, 0, mon, line, file);
443     return (rc);
444 } /* --- end  pz_Wait() --- */
445 
446 extern PRStatus
pz_Notify(PZMonitor * mon,char * file,PRIntn line)447 pz_Notify(
448     PZMonitor *mon,
449     char *file,
450     PRIntn line)
451 {
452     PRStatus rc;
453     PRIntervalTime callTime;
454 
455     callTime = PR_IntervalNow();
456     rc = PR_Notify(mon->mon);
457     callTime = PR_IntervalNow() - callTime;
458     Vtrace(Notify, mon->ltype, callTime, 0, mon, line, file);
459     return (rc);
460 } /* --- end  pz_Notify() --- */
461 
462 extern PRStatus
pz_NotifyAll(PZMonitor * mon,char * file,PRIntn line)463 pz_NotifyAll(
464     PZMonitor *mon,
465     char *file,
466     PRIntn line)
467 {
468     PRStatus rc;
469     PRIntervalTime callTime;
470 
471     callTime = PR_IntervalNow();
472     rc = PR_NotifyAll(mon->mon);
473     callTime = PR_IntervalNow() - callTime;
474     Vtrace(NotifyAll, mon->ltype, callTime, 0, mon, line, file);
475     return (rc);
476 } /* --- end  pz_NotifyAll() --- */
477 
478 #endif /* NEED_NSS_ILOCK */
479 /* --- end nssilock.c --------------------------------- */
480