1 /* PTTRACE.C    (c) Copyright Greg Smith, 2003-2010                  */
2 /*              pthreads trace debugger                              */
3 
4 /*-------------------------------------------------------------------*/
5 /* Trace threading calls                                             */
6 /*-------------------------------------------------------------------*/
7 
8 #include "hstdinc.h"
9 
10 #define _PTTRACE_C_
11 #define _HUTIL_DLL_
12 
13 #include "hercules.h"
14 
15 #ifdef OPTION_PTTRACE
16 
17 PTT_TRACE *pttrace;                     /* Pthreads trace table      */
18 int        pttracex;                    /* Pthreads trace index      */
19 int        pttracen;                    /* Pthreads trace entries    */
20 LOCK       pttlock;                     /* Pthreads trace lock       */
21 int        pttnolock;                   /* 1=no PTT locking          */
22 int        pttnotod;                    /* 1=don't call gettimeofday */
23 int        pttnowrap;                   /* 1=don't wrap              */
24 int        pttto;                       /* timeout in seconds        */
25 TID        ptttotid;                    /* timeout thread id         */
26 LOCK       ptttolock;                   /* timeout thread lock       */
27 COND       ptttocond;                   /* timeout thread condition  */
28 
ptt_trace_init(int n,int init)29 DLL_EXPORT void ptt_trace_init (int n, int init)
30 {
31     if (n > 0)
32         pttrace = calloc (n, PTT_TRACE_SIZE);
33     else
34         pttrace = NULL;
35 
36     if (pttrace)
37         pttracen = n;
38     else
39         pttracen = 0;
40 
41     pttracex = 0;
42 
43     if (init)
44     {
45 #if defined(OPTION_FTHREADS)
46         fthread_mutex_init (&pttlock, NULL);
47 #else
48         pthread_mutex_init (&pttlock, NULL);
49 #endif
50         pttnolock = 0;
51         pttnotod = 0;
52         pttnowrap = 0;
53         pttto = 0;
54         ptttotid = 0;
55 #if defined(OPTION_FTHREADS)
56         fthread_mutex_init (&ptttolock, NULL);
57         fthread_cond_init (&ptttocond);
58 #else
59         pthread_mutex_init (&ptttolock, NULL);
60         pthread_cond_init (&ptttocond, NULL);
61 #endif
62     }
63 }
64 
ptt_cmd(int argc,char * argv[],char * cmdline)65 DLL_EXPORT int ptt_cmd(int argc, char *argv[], char* cmdline)
66 {
67     int  rc = 0;
68     int  n, to = -1;
69     char c;
70 
71     UNREFERENCED(cmdline);
72 
73     if (argc > 1)
74     {
75         /* process arguments; last arg can be trace table size */
76         for (--argc, argv++; argc; --argc, ++argv)
77         {
78             if (strcasecmp("opts", argv[0]) == 0)
79                 continue;
80             else if (strcasecmp("error", argv[0]) == 0)
81             {
82                 pttclass |= PTT_CL_ERR;
83                 continue;
84             }
85             else if (strcasecmp("noerror", argv[0]) == 0)
86             {
87                 pttclass &= ~PTT_CL_ERR;
88                 continue;
89             }
90             else if (strcasecmp("control", argv[0]) == 0)
91             {
92                 pttclass |= PTT_CL_INF;
93                 continue;
94             }
95             else if (strcasecmp("nocontrol", argv[0]) == 0)
96             {
97                 pttclass &= ~PTT_CL_INF;
98                 continue;
99             }
100             else if (strcasecmp("prog", argv[0]) == 0)
101             {
102                 pttclass |= PTT_CL_PGM;
103                 continue;
104             }
105             else if (strcasecmp("noprog", argv[0]) == 0)
106             {
107                 pttclass &= ~PTT_CL_PGM;
108                 continue;
109             }
110             else if (strcasecmp("inter", argv[0]) == 0)
111             {
112                 pttclass |= PTT_CL_CSF;
113                 continue;
114             }
115             else if (strcasecmp("nointer", argv[0]) == 0)
116             {
117                 pttclass &= ~PTT_CL_CSF;
118                 continue;
119             }
120             else if (strcasecmp("sie", argv[0]) == 0)
121             {
122                 pttclass |= PTT_CL_SIE;
123                 continue;
124             }
125             else if (strcasecmp("nosie", argv[0]) == 0)
126             {
127                 pttclass &= ~PTT_CL_SIE;
128                 continue;
129             }
130             else if (strcasecmp("signal", argv[0]) == 0)
131             {
132                 pttclass |= PTT_CL_SIG;
133                 continue;
134             }
135             else if (strcasecmp("nosignal", argv[0]) == 0)
136             {
137                 pttclass &= ~PTT_CL_SIG;
138                 continue;
139             }
140             else if (strcasecmp("io", argv[0]) == 0)
141             {
142                 pttclass |= PTT_CL_IO;
143                 continue;
144             }
145             else if (strcasecmp("noio", argv[0]) == 0)
146             {
147                 pttclass &= ~PTT_CL_IO;
148                 continue;
149             }
150             else if (strcasecmp("timer", argv[0]) == 0)
151             {
152                 pttclass |= PTT_CL_TMR;
153                 continue;
154             }
155             else if (strcasecmp("notimer", argv[0]) == 0)
156             {
157                 pttclass &= ~PTT_CL_TMR;
158                 continue;
159             }
160             else if (strcasecmp("logger", argv[0]) == 0)
161             {
162                 pttclass |= PTT_CL_LOG;
163                 continue;
164             }
165             else if (strcasecmp("nologger", argv[0]) == 0)
166             {
167                 pttclass &= ~PTT_CL_LOG;
168                 continue;
169             }
170             else if (strcasecmp("nothreads", argv[0]) == 0)
171             {
172                 pttclass &= ~PTT_CL_THR;
173                 continue;
174             }
175             else if (strcasecmp("threads", argv[0]) == 0)
176             {
177                 pttclass |= PTT_CL_THR;
178                 continue;
179             }
180             else if (strcasecmp("nolock", argv[0]) == 0)
181             {
182                 pttnolock = 1;
183                 continue;
184             }
185             else if (strcasecmp("lock", argv[0]) == 0)
186             {
187                 pttnolock = 0;
188                 continue;
189             }
190             else if (strcasecmp("notod", argv[0]) == 0)
191             {
192                 pttnotod = 1;
193                 continue;
194             }
195             else if (strcasecmp("tod", argv[0]) == 0)
196             {
197                 pttnotod = 0;
198                 continue;
199             }
200             else if (strcasecmp("nowrap", argv[0]) == 0)
201             {
202                 pttnowrap = 1;
203                 continue;
204             }
205             else if (strcasecmp("wrap", argv[0]) == 0)
206             {
207                 pttnowrap = 0;
208                 continue;
209             }
210             else if (strncasecmp("to=", argv[0], 3) == 0 && strlen(argv[0]) > 3
211                   && (sscanf(&argv[0][3], "%d%c", &to, &c) == 1 && to >= 0))
212             {
213                 pttto = to;
214                 continue;
215             }
216             else if (argc == 1 && sscanf(argv[0], "%d%c", &n, &c) == 1 && n >= 0)
217             {
218                 OBTAIN_PTTLOCK;
219                 if (pttracen == 0)
220                 {
221                     if (pttrace != NULL)
222                     {
223                         RELEASE_PTTLOCK;
224                         logmsg( _("HHCPT002E Trace is busy\n"));
225                         return -1;
226                     }
227                 }
228                 else if (pttrace)
229                 {
230                     pttracen = 0;
231                     RELEASE_PTTLOCK;
232                     usleep(1000);
233                     OBTAIN_PTTLOCK;
234                     free (pttrace);
235                     pttrace = NULL;
236                 }
237                 ptt_trace_init (n, 0);
238                 RELEASE_PTTLOCK;
239             }
240             else
241             {
242                 logmsg( _("HHCPT001E Invalid value: %s\n"), argv[0]);
243                 rc = -1;
244                 break;
245             }
246         } /* for each ptt argument */
247 
248         /* wakeup timeout thread if to= specified */
249         if (to >= 0 && ptttotid)
250         {
251             obtain_lock (&ptttolock);
252             ptttotid = 0;
253             signal_condition (&ptttocond);
254             release_lock (&ptttolock);
255         }
256 
257         /* start timeout thread if positive to= specified */
258         if (to > 0)
259         {
260             obtain_lock (&ptttolock);
261             ptttotid = 0;
262             create_thread (&ptttotid, NULL, ptt_timeout, NULL, "ptt_timeout");
263             release_lock (&ptttolock);
264         }
265     }
266     else
267     {
268         if (pttracen)
269             rc = ptt_pthread_print();
270 
271         logmsg( _("HHCPT003I ptt %s%s%s%s%s%s%s%s%s%s%s %s %s to=%d %d\n"),
272                (pttclass & PTT_CL_INF) ? "control " : "",
273                (pttclass & PTT_CL_ERR) ? "error " : "",
274                (pttclass & PTT_CL_PGM) ? "prog " : "",
275                (pttclass & PTT_CL_CSF) ? "inter " : "",
276                (pttclass & PTT_CL_SIE) ? "sie " : "",
277                (pttclass & PTT_CL_SIG) ? "signal " : "",
278                (pttclass & PTT_CL_IO) ? "io " : "",
279                (pttclass & PTT_CL_TMR) ? "timer " : "",
280                (pttclass & PTT_CL_THR) ? "threads " : "",
281                (pttclass & PTT_CL_LOG) ? "logger " : "",
282                pttnolock ? "nolock" : "lock",
283                pttnotod ? "notod" : "tod",
284                pttnowrap ? "nowrap" : "wrap",
285                pttto,
286                pttracen);
287     }
288 
289     return rc;
290 }
291 
292 /* thread to print trace after timeout */
ptt_timeout()293 void *ptt_timeout()
294 {
295     struct timeval  now;
296     struct timespec tm;
297 
298     obtain_lock (&ptttolock);
299     gettimeofday (&now, NULL);
300     tm.tv_sec = now.tv_sec + pttto;
301     tm.tv_nsec = now.tv_usec * 1000;
302     timed_wait_condition (&ptttocond, &ptttolock, &tm);
303     if (thread_id() == ptttotid)
304     {
305         ptt_pthread_print();
306         pttto = 0;
307         ptttotid = 0;
308     }
309     release_lock (&ptttolock);
310     return NULL;
311 }
312 
313 #ifndef OPTION_FTHREADS
ptt_pthread_mutex_init(LOCK * mutex,pthread_mutexattr_t * attr,char * loc)314 DLL_EXPORT int ptt_pthread_mutex_init(LOCK *mutex, pthread_mutexattr_t *attr, char *loc)
315 {
316     PTTRACE ("lock init", mutex, attr, loc, PTT_MAGIC);
317     return pthread_mutex_init(mutex, attr);
318 }
319 
ptt_pthread_mutex_lock(LOCK * mutex,char * loc)320 DLL_EXPORT int ptt_pthread_mutex_lock(LOCK *mutex, char *loc)
321 {
322 int result;
323 
324     PTTRACE ("lock before", mutex, NULL, loc, PTT_MAGIC);
325     result = pthread_mutex_lock(mutex);
326     PTTRACE ("lock after", mutex, NULL, loc, result);
327     return result;
328 }
329 
ptt_pthread_mutex_trylock(LOCK * mutex,char * loc)330 DLL_EXPORT int ptt_pthread_mutex_trylock(LOCK *mutex, char *loc)
331 {
332 int result;
333 
334     PTTRACE ("try before", mutex, NULL, loc, PTT_MAGIC);
335     result = pthread_mutex_trylock(mutex);
336     PTTRACE ("try after", mutex, NULL, loc, result);
337     return result;
338 }
339 
ptt_pthread_mutex_unlock(LOCK * mutex,char * loc)340 DLL_EXPORT int ptt_pthread_mutex_unlock(LOCK *mutex, char *loc)
341 {
342 int result;
343 
344     result = pthread_mutex_unlock(mutex);
345     PTTRACE ("unlock", mutex, NULL, loc, result);
346     return result;
347 }
348 
ptt_pthread_cond_init(COND * cond,pthread_condattr_t * attr,char * loc)349 DLL_EXPORT int ptt_pthread_cond_init(COND *cond, pthread_condattr_t *attr, char *loc)
350 {
351     PTTRACE ("cond init", NULL, cond, loc, PTT_MAGIC);
352     return pthread_cond_init(cond, attr);
353 }
354 
ptt_pthread_cond_signal(COND * cond,char * loc)355 DLL_EXPORT int ptt_pthread_cond_signal(COND *cond, char *loc)
356 {
357 int result;
358 
359     result = pthread_cond_signal(cond);
360     PTTRACE ("signal", NULL, cond, loc, result);
361     return result;
362 }
363 
ptt_pthread_cond_broadcast(COND * cond,char * loc)364 DLL_EXPORT int ptt_pthread_cond_broadcast(COND *cond, char *loc)
365 {
366 int result;
367 
368     result = pthread_cond_broadcast(cond);
369     PTTRACE ("broadcast", NULL, cond, loc, result);
370     return result;
371 }
372 
ptt_pthread_cond_wait(COND * cond,LOCK * mutex,char * loc)373 DLL_EXPORT int ptt_pthread_cond_wait(COND *cond, LOCK *mutex, char *loc)
374 {
375 int result;
376 
377     PTTRACE ("wait before", mutex, cond, loc, PTT_MAGIC);
378     result = pthread_cond_wait(cond, mutex);
379     PTTRACE ("wait after", mutex, cond, loc, result);
380     return result;
381 }
382 
ptt_pthread_cond_timedwait(COND * cond,LOCK * mutex,const struct timespec * time,char * loc)383 DLL_EXPORT int ptt_pthread_cond_timedwait(COND *cond, LOCK *mutex,
384                           const struct timespec *time, char *loc)
385 {
386 int result;
387 
388     PTTRACE ("tw before", mutex, cond, loc, PTT_MAGIC);
389     result = pthread_cond_timedwait(cond, mutex, time);
390     PTTRACE ("tw after", mutex, cond, loc, result);
391     return result;
392 }
393 
ptt_pthread_create(pthread_t * tid,ATTR * attr,void * (* start)(),void * arg,char * nm,char * loc)394 DLL_EXPORT int ptt_pthread_create(pthread_t *tid, ATTR *attr,
395                        void *(*start)(), void *arg, char *nm, char *loc)
396 {
397 int result;
398     UNREFERENCED(nm);
399 
400     result = pthread_create(tid, attr, start, arg);
401     PTTRACE ("create", (void *)*tid, NULL, loc, result);
402     return result;
403 }
404 
ptt_pthread_join(pthread_t tid,void ** value,char * loc)405 DLL_EXPORT int ptt_pthread_join(pthread_t tid, void **value, char *loc)
406 {
407 int result;
408 
409     PTTRACE ("join before", (void *)tid, value ? *value : NULL, loc, PTT_MAGIC);
410     result = pthread_join(tid,value);
411     PTTRACE ("join after", (void *)tid, value ? *value : NULL, loc, result);
412     return result;
413 }
414 
ptt_pthread_detach(pthread_t tid,char * loc)415 DLL_EXPORT int ptt_pthread_detach(pthread_t tid, char *loc)
416 {
417 int result;
418 
419     PTTRACE ("dtch before", (void *)tid, NULL, loc, PTT_MAGIC);
420     result = pthread_detach(tid);
421     PTTRACE ("dtch after", (void *)tid, NULL, loc, result);
422     return result;
423 }
424 
ptt_pthread_kill(pthread_t tid,int sig,char * loc)425 DLL_EXPORT int ptt_pthread_kill(pthread_t tid, int sig, char *loc)
426 {
427     PTTRACE ("kill", (void *)tid, (void *)(long)sig, loc, PTT_MAGIC);
428     return pthread_kill(tid, sig);
429 }
430 #else /* OPTION_FTHREADS */
ptt_pthread_mutex_init(LOCK * mutex,void * attr,char * loc)431 DLL_EXPORT int ptt_pthread_mutex_init(LOCK *mutex, void *attr, char *loc)
432 {
433     PTTRACE ("lock init", mutex, attr, loc, PTT_MAGIC);
434     return fthread_mutex_init(mutex,attr);
435 }
436 
ptt_pthread_mutex_lock(LOCK * mutex,char * loc)437 DLL_EXPORT int ptt_pthread_mutex_lock(LOCK *mutex, char *loc)
438 {
439 int result;
440 
441     PTTRACE ("lock before", mutex, NULL, loc, PTT_MAGIC);
442     result = fthread_mutex_lock(mutex);
443     PTTRACE ("lock after", mutex, NULL, loc, result);
444     return result;
445 }
446 
ptt_pthread_mutex_trylock(LOCK * mutex,char * loc)447 DLL_EXPORT int ptt_pthread_mutex_trylock(LOCK *mutex, char *loc)
448 {
449 int result;
450 
451     PTTRACE ("try before", mutex, NULL, loc, PTT_MAGIC);
452     result = fthread_mutex_trylock(mutex);
453     PTTRACE ("try after", mutex, NULL, loc, result);
454     return result;
455 }
456 
ptt_pthread_mutex_unlock(LOCK * mutex,char * loc)457 DLL_EXPORT int ptt_pthread_mutex_unlock(LOCK *mutex, char *loc)
458 {
459 int result;
460 
461     result = fthread_mutex_unlock(mutex);
462     PTTRACE ("unlock", mutex, NULL, loc, result);
463     return result;
464 }
465 
ptt_pthread_cond_init(COND * cond,void * attr,char * loc)466 DLL_EXPORT int ptt_pthread_cond_init(COND *cond, void *attr, char *loc)
467 {
468     UNREFERENCED(attr);
469     PTTRACE ("cond init", NULL, cond, loc, PTT_MAGIC);
470     return fthread_cond_init(cond);
471 }
472 
ptt_pthread_cond_signal(COND * cond,char * loc)473 DLL_EXPORT int ptt_pthread_cond_signal(COND *cond, char *loc)
474 {
475 int result;
476 
477     result = fthread_cond_signal(cond);
478     PTTRACE ("signal", NULL, cond, loc, result);
479     return result;
480 }
481 
ptt_pthread_cond_broadcast(COND * cond,char * loc)482 DLL_EXPORT int ptt_pthread_cond_broadcast(COND *cond, char *loc)
483 {
484 int result;
485 
486     result = fthread_cond_broadcast(cond);
487     PTTRACE ("broadcast", NULL, cond, loc, result);
488     return result;
489 }
490 
ptt_pthread_cond_wait(COND * cond,LOCK * mutex,char * loc)491 DLL_EXPORT int ptt_pthread_cond_wait(COND *cond, LOCK *mutex, char *loc)
492 {
493 int result;
494 
495     PTTRACE ("wait before", mutex, cond, loc, PTT_MAGIC);
496     result = fthread_cond_wait(cond, mutex);
497     PTTRACE ("wait after", mutex, cond, loc, result);
498     return result;
499 }
500 
ptt_pthread_cond_timedwait(COND * cond,LOCK * mutex,struct timespec * time,char * loc)501 DLL_EXPORT int ptt_pthread_cond_timedwait(COND *cond, LOCK *mutex,
502                                 struct timespec *time, char *loc)
503 {
504 int result;
505 
506     PTTRACE ("tw before", mutex, cond, loc, PTT_MAGIC);
507     result = fthread_cond_timedwait(cond, mutex, time);
508     PTTRACE ("tw after", mutex, cond, loc, result);
509     return result;
510 }
511 
ptt_pthread_create(fthread_t * tid,ATTR * attr,PFT_THREAD_FUNC start,void * arg,char * nm,char * loc)512 DLL_EXPORT int ptt_pthread_create(fthread_t *tid, ATTR *attr,
513                        PFT_THREAD_FUNC start, void *arg, char *nm, char *loc)
514 {
515 int result;
516 
517     result = fthread_create(tid, attr, start, arg, nm);
518     PTTRACE ("create", (void *)(uintptr_t)(*tid), NULL, loc, result);
519     return result;
520 }
521 
ptt_pthread_join(fthread_t tid,void ** value,char * loc)522 DLL_EXPORT int ptt_pthread_join(fthread_t tid, void **value, char *loc)
523 {
524 int result;
525 
526     PTTRACE ("join before", (void *)(uintptr_t)tid, value ? *value : NULL, loc, PTT_MAGIC);
527     result = fthread_join(tid,value);
528     PTTRACE ("join after", (void *)(uintptr_t)tid, value ? *value : NULL, loc, result);
529     return result;
530 }
531 
ptt_pthread_detach(fthread_t tid,char * loc)532 DLL_EXPORT int ptt_pthread_detach(fthread_t tid, char *loc)
533 {
534 int result;
535 
536     PTTRACE ("dtch before", (void *)(uintptr_t)tid, NULL, loc, PTT_MAGIC);
537     result = fthread_detach(tid);
538     PTTRACE ("dtch after", (void *)(uintptr_t)tid, NULL, loc, result);
539     return result;
540 }
541 
ptt_pthread_kill(fthread_t tid,int sig,char * loc)542 DLL_EXPORT int ptt_pthread_kill(fthread_t tid, int sig, char *loc)
543 {
544     PTTRACE ("kill", (void *)(uintptr_t)tid, (void *)(uintptr_t)sig, loc, PTT_MAGIC);
545     return fthread_kill(tid, sig);
546 }
547 #endif
548 
ptt_pthread_trace(int class,char * type,void * data1,void * data2,char * loc,int result)549 DLL_EXPORT void ptt_pthread_trace (int class, char * type, void *data1, void *data2,
550                         char *loc, int result)
551 {
552 int i, n;
553 
554     if (pttrace == NULL || pttracen == 0 || !(pttclass & class) ) return;
555 
556     /*
557     ** Fish debug: it appears MSVC sometimes sets the __FILE__ macro
558     ** to a full path filename (rather than just the filename only)
559     ** under certain circumstances. (I think maybe it's only for .h
560     ** files since vstore.h is the one that's messing up). Therefore
561     ** for MSVC we need to convert it to just the filename. ((sigh))
562     */
563 #if defined( _MSVC_ )   // fish debug; appears to be vstore.h
564                         // maybe all *.h files are this way??
565     {
566         char* p = strrchr( loc, '\\' );
567         if (!p) p = strrchr( loc, '/' );
568         if (p)
569             loc = p+1;
570     }
571 #endif
572 
573     /*
574      * Messages from timer.c, clock.c and/or logger.c are not usually
575      * that interesting and take up table space.  Check the flags to
576      * see if we want to trace them.
577      */
578     if (!strncasecmp(loc, "timer.c:", 8)  && !(pttclass & PTT_CL_TMR)) return;
579     if (!strncasecmp(loc, "clock.c:", 8)  && !(pttclass & PTT_CL_TMR)) return;
580     if (!strncasecmp(loc, "logger.c:", 9) && !(pttclass & PTT_CL_LOG)) return;
581 
582     /* check for `nowrap' */
583     if (pttnowrap && pttracex + 1 >= pttracen) return;
584 
585     OBTAIN_PTTLOCK;
586     if (pttrace == NULL || (n = pttracen) == 0)
587     {
588         RELEASE_PTTLOCK;
589         return;
590     }
591     i = pttracex++;
592     if (pttracex >= n) pttracex = 0;
593     RELEASE_PTTLOCK;
594     pttrace[i].tid   = thread_id();
595     pttrace[i].class = class;
596     pttrace[i].type  = type;
597     pttrace[i].data1 = data1;
598     pttrace[i].data2 = data2;
599     pttrace[i].loc  = loc;
600     if (pttnotod == 0)
601         gettimeofday(&pttrace[i].tv,NULL);
602     pttrace[i].result = result;
603 }
604 
ptt_pthread_print()605 DLL_EXPORT int ptt_pthread_print ()
606 {
607 int   i, n, count = 0;
608 char  result[32]; // (result is 'int'; if 64-bits, 19 digits or more!)
609 char  tbuf[256];
610 time_t tt;
611 const char dot = '.';
612 
613     if (pttrace == NULL || pttracen == 0) return count;
614     OBTAIN_PTTLOCK;
615     n = pttracen;
616     pttracen = 0;
617     RELEASE_PTTLOCK;
618 
619     i = pttracex;
620     do
621     {
622         if (pttrace[i].tid)
623         {
624             tt = pttrace[i].tv.tv_sec; strcpy(tbuf, ctime(&tt)); tbuf[19] = '\0';
625 
626             if (pttrace[i].result == PTT_MAGIC && (pttrace[i].class & PTT_CL_THR))
627                 result[0] = '\0';
628             else
629                 if((pttrace[i].class & ~PTT_CL_THR))
630                     sprintf(result, "%8.8x", pttrace[i].result);
631                 else
632                     sprintf(result, "%d", pttrace[i].result);
633 
634             logmsg
635             (
636                 "%8.8"I32_FMT"x "             // Thread id (low 32 bits)
637                 "%-12.12s "                   // Trace type (string; 12 chars)
638                 PTR_FMTx" "                   // Data value 1
639                 PTR_FMTx" "                   // Data value 2
640                 "%-18.18s "                   // File name
641                 "%s%c%6.6ld "                 // Time of day (HH:MM:SS.usecs)
642                 "%s\n"                        // Numeric result (or empty string)
643 
644                 ,(U32)(uintptr_t)(pttrace[i].tid) // Thread id (low 32 bits)
645                 ,pttrace[i].type              // Trace type (string; 12 chars)
646                 ,(uintptr_t)pttrace[i].data1  // Data value 1
647                 ,(uintptr_t)pttrace[i].data2  // Data value 2
648                 ,pttrace[i].loc               // File name
649                 ,tbuf + 11                    // Time of day (HH:MM:SS)
650                 ,dot                          // Time of day (decimal point)
651                 ,pttrace[i].tv.tv_usec        // Time of day (microseconds)
652                 ,result                       // Numeric result (or empty string)
653             );
654             count++;
655         }
656         if (++i >= n) i = 0;
657     } while (i != pttracex);
658     memset (pttrace, 0, PTT_TRACE_SIZE * n);
659     pttracex = 0;
660     pttracen = n;
661     return count;
662 }
663 
664 #endif
665