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