source: gpfs_3.1_ker2.6.20/lpp/mmfs/src/gpl-linux/lxtrace.c @ 211

Last change on this file since 211 was 16, checked in by rock, 17 years ago
File size: 63.0 KB
RevLine 
[16]1/***************************************************************************
2 *
3 * Copyright (C) 2001 International Business Machines
4 * All rights reserved.
5 *
6 * This file is part of the GPFS mmfslinux kernel module.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 *  1. Redistributions of source code must retain the above copyright notice,
13 *     this list of conditions and the following disclaimer.
14 *  2. Redistributions in binary form must reproduce the above copyright
15 *     notice, this list of conditions and the following disclaimer in the
16 *     documentation and/or other materials provided with the distribution.
17 *  3. The name of the author may not be used to endorse or promote products
18 *     derived from this software without specific prior written
19 *     permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
30 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 *************************************************************************** */
33static char sccsid[] = "@(#)37  1.36  src/avs/fs/mmfs/ts/kernext/gpl-linux/lxtrace.c, mmfs, avs_rgpfs24, rgpfs240610b 10/31/05 10:36:03";
34/**************************************************************************
35 *
36 * lxtrace : the command.  Turns tracing on/off and formats trace files.
37 *
38 *  "lxtrace {on hw[,hw[,hw...]] [trcFile [filesize [bufsize]]]" }
39 *           {off                                               }
40 *           {format [-T] [-v] [-t formatFile] [-o outFile] [trcFile] }
41 *
42 *           on   - begins tracing
43 *           off  - stops tracing
44 *           hw   - hookword (identifies traces to be captured)
45 *                  This is a comma-separated list (up to LXTRACE_MAX_HW)
46 *           trcFile  - path to the file where output is written
47 *           filesize - maximum size (bytes) of the wrap-around file
48 *                      A filesize of 0 will cause the file NOT to wrap
49 *           bufsize  - the size of buffer to used by the device
50 *                      to record trace records
51 *
52 *   For debug/testing purposes, the following additional options
53 *   are defined:
54 *   "lxtrace dump              // returns state information in a buffer
55 *           fsync              // flush buffered records to the read buffer
56 *
57 **************************************************************************/
58
59#include <Shark-gpl.h>
60#include <sys/types.h>
61#include <sys/time.h>
62#include <sys/ioctl.h>
63
64#include <errno.h>
65#include <stdio.h>
66#include <unistd.h>
67#include <string.h>
68#include <stdlib.h>
69#include <malloc.h>
70#include <signal.h>
71#include <fcntl.h>
72#include <ctype.h>
73
74#include <Trace.h>
75#include <lxtrace.h>
76
77/* Convenience macros for printing error and debug messages */
78#define DP(fmt,args...) fprintf(stderr, fmt, ## args)
79#ifdef LXTRACE_DEBUG
80#define DFP(fmt,args...) fprintf(stderr, fmt, ## args)
81#else
82#define DFP(fmt,args...) ((void)0)
83#endif
84
85#define ROUNDUP(_addr, _align) \
86   (((long)_addr + (long)_align - 1L) & ~((long)_align - 1L))
87
88/* Examples of formatted trace output:
89
90Default options:
91
92 Timestamp   Pid  COMPONENT_TAG: application trace record
93----------- ----- ---------------------------------------
94   0.000000 21499 TRACE_VNODE: gpfs_i_revalidate enter:
95
96-v option:
97
98 Timestamp   Pid  P Hookword COMPONENT_TAG: application trace record
99----------- ----- - -------- ---------------------------------------
100   0.000000 21499 1 307004B3 TRACE_VNODE: gpfs_i_revalidate enter:
101
102-T option:
103
104   Timestamp      Pid  COMPONENT_TAG: application trace record
105---------------- ----- ---------------------------------------
106970706704.711164 21499 TRACE_VNODE: gpfs_i_revalidate enter:
107
108-T and -v options:
109
110   Timestamp      Pid  P Hookword COMPONENT_TAG: application trace record
111---------------- ----- - -------- ---------------------------------------
112970706704.711164 21499 1 307004B3 TRACE_VNODE: gpfs_i_revalidate enter:
113
114*/
115
116/* Define fragments of header lines and the corresponding trace printf format
117   strings for each field on a trace output line */
118#define H1_TS       " Timestamp  "
119#define H2_TS       "----------- "
120#define FMT_TS      "%4d.%06d "
121#define H1_TS_T     "    Timestamp     "
122#define H2_TS_T     "----------------- "
123#define FMT_TS_T    "%10d.%06d "
124
125#define H1_PID      "  Pid  "
126#define H2_PID      "------ "
127#define FMT_PID     "%6d "
128
129#define H1_P_HW     ""
130#define H2_P_HW     ""
131#define FMT_P_HW    ""
132#define H1_P_HW_V   "P B Hookword "
133#define H2_P_HW_V   "- - -------- "
134#define FMT_P_HW_V  "%1d %1d %08X "
135
136#define H1_TAG      "COMPONENT_TAG: application trace record\n"
137#define H2_TAG      "---------------------------------------\n"
138#define FMT_TAG     "%s: "
139
140/* Bytes in an "_id2name" formatting substitution */
141#define TRC_MAX_ID2NAME 128
142
143#ifndef O_DIRECT
144#ifdef GPFS_ARCH_PPC64
145#define O_DIRECT 0400000
146#else
147#define O_DIRECT 040000
148#endif
149#endif
150
151#ifdef __64BIT__
152typedef long long argType;
153#define ARGLEN sizeof(long long)
154#define TRC_ARG(N) (((long long *)&application_data)[N])
155#else
156typedef int argType;
157#define ARGLEN sizeof(int)
158#define TRC_ARG(N) (((int *)&application_data)[N])
159#endif // __64BIT__
160
161#define TRC_ARGLIST argP[0] , argP[1] , argP[2] , argP[3] , argP[4] , argP[5], \
162                    argP[6] , argP[7] , argP[8] , argP[9] , argP[10], argP[11],\
163                    argP[12], argP[13], argP[14], argP[15], argP[16], argP[17],\
164                    argP[18], argP[19]
165
166
167/* Offsets of the various fields in the formatting file */
168#define GFR_TRCID 0
169#define GFR_HOOKID 15
170#define GFR_COMPID 20
171#define GFR_FORMAT 40
172
173/* Max length of COMPID in the formatting file */
174# define MAX_COMPID_LEN (GFR_FORMAT - GFR_COMPID - 1)
175
176/* Thread IDs greater than this are really message handlers.
177   FirstMsgHandlerThread is also defined in ThreadNames.h. */
178#define FirstMsgHandlerThread 0x120
179
180/* Size of the hash tables for in-memory format file and its hash functions.
181   HOOK_HASH is for looking up format strings by hook id.
182   I2N_HASH is for looking up id2name conversions by conversion name and id
183   value; the I2NHASH hash function uses the first 4 chars of the conversion
184   name, relying on the fact that these names are at least 4 chars long,
185   because they all end in "_id2name". */
186#define HOOK_HASH_TABLE_SIZE 0x10000
187#define HOOKHASH(H) ((H) & 0xFFFF)
188#define I2N_HASH_TABLE_SIZE 0x4000
189#ifndef ALIGNED_DATA_ACCESS
190#define I2NHASH(F,I) ((*(int*)(F) + (I)) & 0x3FFF)
191#else
192static inline int I2NHASH(char *F, int I)
193{
194  int tmp;
195
196  memcpy(&tmp, F, sizeof(int));
197  return (tmp + I) & 0x3FFF;
198}
199#endif
200
201/* combined device and application trace data headers */
202typedef struct trc_hdrs_t
203{
204  trc_header_t hdr;  /* device header */
205  trc_datahdr_t rec; /* application header */
206} trc_hdrs_t;
207
208/* id2name conversion specifier. */
209typedef struct i2nSpec_t
210{
211  int argNum;       // number of the argument to be converted
212  char *i2nNameP;   // conversion function name
213} i2nSpec_t;
214
215/* Each format record has one of these headers describing it. */
216typedef struct fmtDesc_t
217{
218  int       hookid;             // hookid or id2name value
219  char      *cidP;              // component identifier or id2name tag
220  char      *fmtP;              // format string or id2name string
221  unsigned int llmask;          // bitvector indicating %ll arguments
222  i2nSpec_t *i2nSpecP;          // array of id2name conversion specifiers
223  struct fmtDesc_t *fmtNextP;   // next in the hash chain
224} fmtDesc_t;
225
226/* Hash tables for finding format records. */
227fmtDesc_t * hookHashP[HOOK_HASH_TABLE_SIZE];
228fmtDesc_t * i2nHashP[I2N_HASH_TABLE_SIZE];
229
230/* Hash lookup statistics */
231static int nHookLookup = 0;
232static int nHookCheck = 0;
233static int nI2NLookup = 0;
234static int nI2NCheck = 0;
235static int nI2NFail = 0;
236
237/* static state information */
238static int TraceHandle = -1;// file descriptor for the trace device
239static int out_fd;        // file descriptor for the (raw trace) output file
240static int outSize;       // out_fd filesize
241static int bufSize;       // size of the trace buffers (device and daemon)
242static char *bufP;        // the daemon trace read buffer
243
244#if LINUX_KERNEL_VERSION >= 2060000
245#define LXTRACE_USE_DIO
246#endif
247
248#ifdef LXTRACE_USE_DIO
249#define MAXAIOBUFS  10
250static char *aiobufP[MAXAIOBUFS];        // the daemon 4k aligned trace write buffer
251static char *wbufP[MAXAIOBUFS];        // the daemon orginal trace write buffer
252static int wrapBytes=0;    // bytes leftover after last DIO write
253#endif
254static int nWraps;        // number of times the output file has wrapped
255static off_t currentPos;  // position of next write to output file
256static off_t fileEnd=0;  // position of next write to output file
257
258/* The previous time is used to validate a wrap point when we
259   think we've encountered one. */
260static struct timeval prevTime;
261
262/* Keep track of start/end times in the trace file */
263static time_t startTime, stopTime;
264
265static char *formatFileP;     // malloc'ed buffer holding formatting data
266static char *fmtDescSpaceP;   // fmtData_t space for each format record
267
268static int allowed_usec_wobble = 2; // allowed timestamp wobble before declaring a wrap.
269
270/* Convert ascii representation of a hexadecimal to int */
271int axtoi(char s[])
272{
273  int i, n;
274
275  n = 0;
276  for (i = 0; s[i] != '\0'; i++)
277  {
278    if (s[i] >= '0' && s[i] <= '9')
279      n = 16 * n + (s[i] - '0');
280    else if (s[i] >= 'A' && s[i] <= 'F')
281      n = 16 * n + (s[i] - 'A' + 10);
282    else if (s[i] >= 'a' && s[i] <= 'f')
283      n = 16 * n + (s[i] - 'a' + 10);
284    else
285      break;
286  }
287  return n;
288}
289
290/* Convert timestamp to string format. */
291extern char *ctime_r(const time_t *, char *);
292char *fmtTime(time_t time, char *timeStr)
293{
294  /* ctime_r returns a null-terminated, readable string that includes
295   * a newline char which we remove.  When ctime_r cannot convert the
296   * supplied timestamp, it returns NULL.
297   */
298  if (!ctime_r(&time, timeStr))
299    return "unknown";
300
301  timeStr[strlen(timeStr) - 1] = '\0';
302  return timeStr;
303}
304
305#define TIMEVAL_ZERO ((struct timeval){0,0})
306static struct timeval wrap_prev = TIMEVAL_ZERO;
307static struct timeval wrap_next = TIMEVAL_ZERO;
308
309/* isWrapPoint:
310 * See if consecutive timestamps are backwards (indicates a wrap-point).
311 */
312int isWrapPoint(struct timeval prev, struct timeval next)
313{
314  /* Detect timestamps going backwards.  This could be the wrap-point
315   * or just wobble.
316   */
317  if ((prev.tv_sec > next.tv_sec) ||
318      ((prev.tv_sec == next.tv_sec)
319       && (prev.tv_usec - next.tv_usec > allowed_usec_wobble)))
320  {
321    /* Prepare for simple subtraction (do any necessary borrowing) */
322    if (prev.tv_usec < next.tv_usec)
323      {
324  prev.tv_usec += 1000000;
325  prev.tv_sec  -= 1;
326      }
327 
328    DFP("isWrapPoint: wrap delta %d.%06d (prev %d.06d, next %d.06d)\n", 
329        (prev.tv_sec - next.tv_sec), (prev.tv_usec - next.tv_usec),
330         prev.tv_sec, prev.tv_usec, next.tv_sec, next.tv_usec);
331
332    /* Detect and warn if multiple wrap points are detected. */
333    if (((wrap_prev.tv_sec != 0) && 
334        ((wrap_prev.tv_sec != prev.tv_sec) || (wrap_prev.tv_usec != prev.tv_usec))) ||
335        ((wrap_next.tv_sec != 0) && 
336        ((wrap_next.tv_sec != next.tv_sec) || (wrap_next.tv_usec != next.tv_usec))))
337    {
338      DP("Multiple wrap-points found:\n");
339      DP("\t%d.%06d-%d.%06d (delta %d.%06d)\n",
340         wrap_prev.tv_sec, wrap_prev.tv_usec, wrap_next.tv_sec, wrap_next.tv_usec,
341        (wrap_prev.tv_sec - wrap_next.tv_sec), (wrap_prev.tv_usec - wrap_next.tv_usec));
342      DP("\t%d.%06d-%d.%06d (delta %d.%06d)\n",
343         prev.tv_sec, prev.tv_usec, next.tv_sec, next.tv_usec,
344        (prev.tv_sec - next.tv_sec), (prev.tv_usec - next.tv_usec));
345    }
346 
347    /* Keep track of what we have now declared to be a wrap point */
348    wrap_prev = prev;
349    wrap_next = next;
350 
351    return true;
352  }
353
354  return false;
355}
356
357/* Parse a quoted format string: removes enclosing quotes, removes trailing
358   white-space, and converts escape sequences \n, \r, \t, \", etc.  Also
359   examines '%' format arguments and computes a bitvector that indicates which
360   of the arguments are long longs (%ll..).  The converted string is placed in
361   outP; the i'th bit in *llmaskP is set if the i'th argument is a long long.
362   It's ok to call this function to convert a string in-place (outP == inP),
363   since the conversion only makes the string shorter (removes surrounding
364   quotes and reduces escape sequences to a single char).
365   Returns a pointer to the next character in inP following the ending quotes,
366   or NULL if there are no more characters following the quoted string. */
367char *ParseFormatString(char *inP, char *outP, unsigned int *llmaskP)
368{
369  char *initialOutP = outP;
370  int b = 1;
371
372  /* skip initial quote */
373  if (*inP == '"')
374    inP++;
375
376  /* copy string until ending quote */
377  *llmaskP = 0;
378  while (*inP && *inP != '"')
379  {
380    if (*inP == '\\' && *(inP + 1))
381    {
382      inP++;
383      switch(*inP)
384      {
385        case 'n': *outP++ = '\n'; inP++; break;
386        case 'r': *outP++ = '\r'; inP++; break;
387        case 't': *outP++ = '\t'; inP++; break;
388        default:  *outP++ = *inP++;
389      }
390    }
391    else if (*inP == '%')
392    {
393      *outP++ = *inP++;
394      if (*inP == '%')
395        *outP++ = *inP++;
396      else
397      {
398        if (*inP == '+' | *inP == '-')
399          *outP++ = *inP++;
400        while (isdigit(*inP))
401          *outP++ = *inP++;
402        if (*inP == 'l' && *(inP+1) == 'l')
403          *llmaskP |= b;
404        b <<= 1;
405      }
406    }
407    else
408      *outP++ = *inP++;
409  }
410
411  /* remove trailing white space from the string */
412  while (outP > initialOutP && isspace(outP[-1]))
413    outP--;
414  *outP = '\0';
415
416  /* Return pointer to next character after the string or NULL if no ending
417     quote was found */
418  return *inP? inP + 1: NULL;
419}
420
421
422/* Parse a string containing a list of id2name conversion specifiers of the
423   form "$n=xxx_id2name $m=yyy_id2name ...".  Returns a "null-terminted" array
424   of i2nSpec_t structures, i.e., the array contains one more entry than the
425   number of conversion specifiers found, and argNum/i2nNameP in the last
426   array entry are set to 0/NULL. */
427i2nSpec_t *ParseI2N(char *i2nP, unsigned int llmask)
428{
429  i2nSpec_t i2n[LXTRACE_MAX_FORMAT_SUBS];
430  i2nSpec_t *i2nSpecP;
431  char *p;
432  int n, argNum, a, nll;
433  unsigned int m;
434
435  for (n = 0, p = strtok(i2nP, " \t\n");
436       p && n < LXTRACE_MAX_FORMAT_SUBS;
437       n++, p = strtok(NULL, " \t\n"))
438  {
439    if (*p != '$')
440      break;
441    p++;
442    argNum = atoi(p);
443    p = strchr(p, '=');
444    if (argNum <= 0 || p == NULL)
445      break;
446
447#ifndef __64BIT__
448    /* The "n" in "$n=" signifies that the "n"th parameter needs substitution.
449       However, we need to account for any double-word ("%ll") values (will be
450       taking two positions) in the arglist and adjust accordingly.  The
451       llmask parameter indicates which arguments are of the %ll form.  Count
452       the bits in llmask before the point of the substitution and add the
453       count to the argNum value. */
454    if (llmask)
455    {
456      nll = 0;
457      for (a = 1, m = llmask;
458           a < argNum && m;
459           a++, m >>= 1)
460      {
461        if (m & 1)
462          nll++;
463      }
464      argNum += nll;
465    }
466#endif // __64BIT__
467
468    i2n[n].argNum = argNum;
469    i2n[n].i2nNameP = p + 1;
470  }
471  if (n == 0)
472    return NULL;  // ?? print msg about bad id2name specifiers
473
474  i2nSpecP = (i2nSpec_t *)malloc((n + 1)*sizeof(i2nSpec_t));
475  if (i2nSpecP != NULL)
476  {
477    memcpy(i2nSpecP, i2n, n*sizeof(i2nSpec_t));
478    i2nSpecP[n].argNum = 0;
479    i2nSpecP[n].i2nNameP = NULL;
480  }
481  return i2nSpecP;
482}
483
484
485/* The formatting records are read into memory and parsed.  A fmtDesc_t header
486   is constructed for each and is then inserted into a hash table. */
487int BuildFormatTable(FILE *fP)
488{
489  char buf[2048];
490  long fileSize;   /* size (bytes) of the formatting file */
491  int  nRecords;   /* Number of records in the formatting file */
492  int  hookid;     /* hookid of current record */
493  int  hashVal;    /* hash value */
494  struct fmtDesc_t *nextDescP; 
495  char *curFmtRecordP, *nextFmtRecordP;
496  char *trcidP;
497  char *hookP;
498  char *cidP;
499  char *fmtP;
500  int i;
501#ifdef DBGASSERTS
502  char *formatFileEndP;
503  char *fmtDescSpaceEndP;
504#endif // DBGASSERTS
505
506  /* Clear all entries in the hash table */
507  memset(hookHashP, 0, sizeof(hookHashP));
508  memset(i2nHashP,  0, sizeof(i2nHashP));
509
510  rewind(fP);
511
512  /* Count the number of formatting records */
513  nRecords = 0;
514  while (fgets(buf, sizeof(buf), fP) != NULL)
515    nRecords += 1;
516
517  /* Now that we are at the end of the file, see how big it is */
518  fileSize = ftell(fP);
519  if (fileSize < 0)
520    return errno;
521
522  /* Allocate the buffer for the file contents.
523     Add 1 for possible trailing zero. */
524  if (NULL == (formatFileP = (char *)malloc(fileSize+1)))
525    return ENOMEM;
526
527  /* Allocate space for the fmtData_t for all of the records. */
528  if (NULL == (fmtDescSpaceP = (char *)malloc(nRecords * sizeof(fmtDesc_t))))
529    return ENOMEM;
530  nextDescP = (fmtDesc_t *)fmtDescSpaceP; 
531
532#ifdef DBGASSERTS
533  formatFileEndP = formatFileP + fileSize;
534  fmtDescSpaceEndP = fmtDescSpaceP + (nRecords * sizeof(fmtDesc_t)); 
535#endif // DBGASSERTS
536
537  rewind(fP); /* back to the beginning of the file */
538
539  /* Read each record in one-at-a-time, parse the record, and insert it into
540     the right hash table. */
541  curFmtRecordP = formatFileP;
542  while (fgets(curFmtRecordP, sizeof(buf), fP) != NULL)
543  {
544    nextFmtRecordP = curFmtRecordP + strlen(curFmtRecordP);
545
546    /* The TRCID identifier, hookid, component id, and format string are at
547       fixed offsets */
548    trcidP = curFmtRecordP + GFR_TRCID;
549    hookP  = curFmtRecordP + GFR_HOOKID;
550    cidP   = curFmtRecordP + GFR_COMPID;
551    fmtP   = curFmtRecordP + GFR_FORMAT;
552
553    /* parse the format record. */
554    hookid = axtoi(hookP) & 0x0000FFFF;
555    nextDescP->hookid = hookid;
556    nextDescP->cidP = strtok(cidP, " ");
557    nextDescP->fmtP = fmtP;
558
559    /* find the end of the format string */
560    fmtP = ParseFormatString(fmtP, fmtP, &nextDescP->llmask);
561
562    /* check for id2name conversion specifiers following the format string */
563    nextDescP->i2nSpecP = NULL;
564    if (fmtP)
565    {
566      while (isspace(*fmtP))
567        fmtP++;
568      if (*fmtP)
569        nextDescP->i2nSpecP = ParseI2N(fmtP, nextDescP->llmask);
570    }
571
572    /* Place the format record descriptor into the right hash table. */
573    if (strncmp(trcidP, "TRCID_", 6) == 0)
574    {
575      /* this record contains a format string */
576      strcat(nextDescP->fmtP, "\n");
577      hashVal = HOOKHASH(hookid);
578      nextDescP->fmtNextP = hookHashP[hashVal];
579      hookHashP[hashVal] = nextDescP;
580    }
581    else
582    {
583      /* this record contains an id2name conversion value */
584      hashVal = I2NHASH(nextDescP->cidP, hookid);
585      nextDescP->fmtNextP = i2nHashP[hashVal];
586      i2nHashP[hashVal] = nextDescP;
587    }
588
589    /* Advance to the next fmtDesc and format record */
590    nextDescP++; 
591    curFmtRecordP = nextFmtRecordP;
592  }
593
594#ifdef DBGASSERTS
595  /* verify that we didn't overrun our buffers */
596  if ((char *)nextDescP > fmtDescSpaceEndP)
597  {
598    DP("lxtrace format: overwrote format descriptor memory!\n");
599    return -1;
600  }
601  if (nextFmtRecordP > formatFileEndP)
602  {
603    DP("lxtrace format: overwrote format file memory!\n");
604    return -1;
605  }
606#endif // DBGASSERTS
607
608  return 0;
609}
610
611
612/* Find a format record by hookid. */
613int GetFormatRecord(FILE *fP, int hookid, char **cidPP, char **fmtPP,
614                    unsigned int *llmaskP, i2nSpec_t **i2nPP)
615{
616  fmtDesc_t *nextP;
617
618  DFP("GetFormatRecord(0x%X, 0x%X, 0x%X, 0x%X, 0x%X)\n",
619      fP, hookid, bufSize, bufP, cidPP);
620
621  nHookLookup++;
622
623  /* search hash chain for the specified hookid */
624  for (nextP = hookHashP[HOOKHASH(hookid)];
625       nextP != NULL;
626       nextP = nextP->fmtNextP)
627  {
628    nHookCheck++;
629
630    /* Check whether hookid matches. */
631    if (nextP->hookid == hookid)
632    {
633      /* Hookid and the record type match.  Copy to work area. */
634      *cidPP = nextP->cidP;
635      *fmtPP = nextP->fmtP;
636      *llmaskP = nextP->llmask;
637      *i2nPP = nextP->i2nSpecP;
638      DFP("GetFormatRecord for id %X is: %s\n", hookid, *fmtPP);
639      return 0;
640    }
641  }
642
643  /* not found */
644  return -1;
645}
646
647
648/* Find an id2name string by conversion name ("tag") and id value */
649int GetID2NameRecord(FILE *fP, char *tagP,
650                     int id, int bufSize, char *bufP)
651{
652  fmtDesc_t *nextP;
653
654  DFP("GetID2NameRecord(0x%X, 0x%X, 0x%X, 0x%X, 0x%X)\n",
655      fP, tagP, id, bufSize, bufP);
656
657  nI2NLookup++;
658
659  /* search hash chain for the specified hookid and tag */
660  for (nextP = i2nHashP[I2NHASH(tagP, id)];
661       nextP != NULL;
662       nextP = nextP->fmtNextP)
663  {
664    nI2NCheck++;
665
666    /* Check whether hookid and tag match. */
667    if (nextP->hookid == id &&
668        strncmp(nextP->cidP, tagP, MAX_COMPID_LEN) == 0)
669    {
670      DFP("GetID2NameRecord for id %X is: %s\n", id, nextP->fmtP);
671      strncpy(bufP, nextP->fmtP, bufSize);
672      return 0;
673    }
674  }
675
676  /* not found */
677  nI2NFail++;
678  return -1;
679}
680
681
682/* Read the next trace header from the raw trace file.  Normally, this just
683   involves the read() for the specified file.  However, it also needs to
684   check for wrapping of the file and make the necessary adjustments when we
685   reach the wrap point. */
686int ReadNextTrcHeader(FILE *raw_fp, trc_hdrs_t *hdrP)
687{
688  int nRead;
689  off_t off1, off2;
690
691  if (0 < (nRead = fread(hdrP, 1, sizeof(trc_hdrs_t), raw_fp)))
692  {
693    /* Successful read.  Validate the header. */
694    if (hdrP->hdr.trMagic != LXTRACE_MAGIC)
695    {
696      /* Maybe we've reached the wrap point.  See if we can get oriented.
697         Backup and try 1 byte further until we find a valid header.  This
698         should be the wrap point.  If we can verify this, return the header.
699       */
700      fseek(raw_fp, 1-nRead, SEEK_CUR);
701      off1 = ftell(raw_fp) - 1;
702      while (0 < (nRead = fread(hdrP, 1, sizeof(trc_hdrs_t), raw_fp)))
703      {
704        /* Keep inching forward looking for a valid device header that
705         * includes timestamps that also indicate a wrap ocurred. */
706        if ((hdrP->hdr.trMagic == LXTRACE_MAGIC) &&
707            isWrapPoint(prevTime, hdrP->hdr.trTime))
708        {
709          DFP("ReadNextTrcHeader: wrap point between %d.%d and %d.%d\n", 
710                  hdrP->hdr.trTime.tv_sec, hdrP->hdr.trTime.tv_usec, 
711                  prevTime.tv_sec, prevTime.tv_usec);
712
713          /* We've found what appears to be a valid header. */
714          prevTime = hdrP->hdr.trTime; /* timestamp of the last valid record */
715          nWraps += 1;
716          DFP("ReadNextTrcHeader: wrap point at %d gap %d\n", off1, off2-off1);
717
718          /* Save the oldest timestamp that we've found */
719          if (hdrP->hdr.trTime.tv_sec < startTime)
720            startTime = hdrP->hdr.trTime.tv_sec;
721
722          return nRead;
723        }
724        else if (hdrP->hdr.trMagic == LXTRACE_MAGIC)
725        {
726          /* We've found a valid header some ways from the beginning of
727             the file, but it's not a wrap point.  This could be due to
728             some garbage in the file.  Try to skip garbage data. */
729
730          /* timestamp of the last valid record */
731          prevTime = hdrP->hdr.trTime;
732                                                                               
733          /* Save the latest timestamp we've found */
734          stopTime = hdrP->hdr.trTime.tv_sec;
735                                                                               
736          /* Save the oldest timestamp that we've found */
737          if (hdrP->hdr.trTime.tv_sec < startTime)
738            startTime = hdrP->hdr.trTime.tv_sec;
739
740          return nRead;
741        }
742        fseek(raw_fp, 1-nRead, SEEK_CUR);
743        off2 = ftell(raw_fp);
744      }
745      DP("lxtrace format: ReadNextTrcHeader failed to find a valid record!\n");
746    }
747    else
748    {
749      /* Valid header found, but this could still be a wrap-point if the
750       * alignment worked out exactly. 
751       */
752
753      if (isWrapPoint(prevTime, hdrP->hdr.trTime))
754      {
755        DFP("ReadNextTrcHeader: wrap point between %d.%d and %d.%d\n", 
756                hdrP->hdr.trTime.tv_sec, hdrP->hdr.trTime.tv_usec, 
757                prevTime.tv_sec, prevTime.tv_usec);
758
759        nWraps += 1;
760        DFP("ReadNextTrcHeader: wrap point at %d gap %d\n", off1, off2-off1);
761
762        /* timestamp of the last valid record */
763        prevTime = hdrP->hdr.trTime;
764
765        /* Save the oldest timestamp that we've found */
766        if (hdrP->hdr.trTime.tv_sec < startTime)
767          startTime = hdrP->hdr.trTime.tv_sec;
768      }
769      else
770      {
771        /* timestamp of the last valid record */
772        prevTime = hdrP->hdr.trTime; 
773
774        /* Save the latest timestamp we've found */
775        stopTime = hdrP->hdr.trTime.tv_sec;
776
777        /* Save the oldest timestamp that we've found */
778        if (hdrP->hdr.trTime.tv_sec < startTime)
779          startTime = hdrP->hdr.trTime.tv_sec;
780      }
781    }
782
783  }
784  return nRead;
785}
786
787#ifdef LXTRACE_USE_DIO
788#include <aio.h>
789static struct aiocb aiocb[MAXAIOBUFS] = {0};
790static int aioStarted[MAXAIOBUFS] = {0};
791static int numBuf = 0;
792static char wrapBuf[4096];   // bytes that didn't make the last dio write.
793
794inline void aioWait(int numBuf)
795{
796     int i,rc;
797     const struct aiocb *aioP[]={&aiocb[numBuf]};
798
799  if(aioStarted[numBuf])
800  {
801           rc=aio_suspend(aioP, 1, NULL);
802     if(rc==0)
803     {
804      size_t lrc = aio_return(&aiocb[numBuf]);
805      aioStarted[numBuf]=0;
806      DFP("aioWait aio_return[%d] %d\n", numBuf, rc);
807     }
808  }
809}
810inline void aioWrite(char* bufP, int wBytes)
811{
812      int rc;
813      bzero(&aiocb[numBuf], sizeof(struct aiocb));
814      aiocb[numBuf].aio_buf=bufP;
815      aiocb[numBuf].aio_nbytes=wBytes;
816      aiocb[numBuf].aio_offset=currentPos;
817      aiocb[numBuf].aio_fildes=out_fd;
818      aiocb[numBuf].aio_sigevent.sigev_notify=SIGEV_NONE;
819      rc=aio_write(&aiocb[numBuf]);
820      DFP("aioWrite %d bytes at offset %d numBuf %d \n", wBytes, currentPos, numBuf);
821      aioStarted[numBuf]=1;
822      numBuf = (numBuf + 1) % MAXAIOBUFS;
823}
824
825#endif
826
827/* ProcessBuffer reads from the trace device and writes to the output file.
828   This is a function used by the trace daemon when handling SIGIO from the
829   device or after fsync() as the device is being closed. */
830void ProcessBuffer()
831{
832  int rc, nBytes;
833 
834retry:
835
836#ifdef LXTRACE_USE_DIO
837  aioWait(numBuf);
838#endif
839
840  /* Issue a read for one buffer to the trace device */
841  DFP("trace daemon about to read %d bytes\n", bufSize);
842  nBytes = read(TraceHandle, bufP, bufSize);
843  DFP("trace daemon read %d bytes\n", nBytes);
844  if (nBytes == -1)
845  {
846    DP("trace daemon read error %d\n", errno);
847    close(TraceHandle);
848    close(out_fd);
849    _exit(1);
850  }
851  if (nBytes > 0)
852  {
853    /* Check the output file wrap-size and handle if it has been reached. */
854    if ((out_fd > 2) && (outSize > 0) && ((currentPos + nBytes) > outSize))
855    {
856#ifdef LXTRACE_USE_DIO
857      /* Write any leftover bytes as a full block and setup to truncate on exit
858   any data that may have extended past this point from the last
859         time we wrapped. */
860      memset(aiobufP[numBuf], 0, 4096);
861      memcpy(aiobufP[numBuf], wrapBuf, wrapBytes);
862      fileEnd=currentPos + wrapBytes;
863      wrapBytes = 0;
864      aioWrite(aiobufP[numBuf], 4096);
865      aioWait(numBuf);
866#else
867      /* Truncate any data that may have extended past this point from the last
868         time we wrapped.  Seek back to the top of the file and continue. */
869      DFP("trace daemon wrapping file at length %d\n", currentPos);
870      lseek(out_fd, 0, SEEK_SET);
871      ftruncate(out_fd, currentPos);
872#endif
873      currentPos = 0;
874    }
875
876    /* Every trace record starts with a header (trc_hdrs_t) that includes
877     * a magic number.  If we ever get a buffer that doesn't start
878     * properly, write some debug information and refuse to write it
879     * to the trace file.
880     */
881    if (((trc_hdrs_t *)bufP)->hdr.trMagic != LXTRACE_MAGIC)
882    {
883      DP("lxtrace: bad trace buffer from tracedev!\n");
884      DP("lxtrace: bufP 0x%X %d bytes at offset %d data 0x%X\n", 
885          bufP, nBytes, currentPos, ((trc_hdrs_t *)bufP)->hdr.trMagic);
886    }
887    else
888    {
889
890#ifdef LXTRACE_USE_DIO
891      /* dio nees 4k aligned buffers */
892      int totalBytes = nBytes + wrapBytes;
893      int aioBytes = totalBytes & 0xfffff000;
894      int extraBytes = totalBytes - aioBytes;
895
896      DFP("trace daemon writing %d bytes at offset %d, wb %d, ab %d eb %d\n", 
897          totalBytes, currentPos, wrapBytes, aioBytes, extraBytes);
898      if (aioBytes > 0)
899      {
900        /* Write the buffer to the trace output file */
901        memcpy(aiobufP[numBuf], wrapBuf, wrapBytes);
902        memcpy((aiobufP[numBuf] + wrapBytes), bufP, aioBytes - wrapBytes);
903        aioWrite(aiobufP[numBuf], aioBytes);
904
905        if(extraBytes > 0)
906          memcpy(wrapBuf, bufP + aioBytes - wrapBytes, extraBytes);
907        wrapBytes = extraBytes;
908      }
909      else 
910      {
911        /* Add data to the existing wrapBuf.  There must be room, or
912         * we wouldn't be here.  */
913        DFP("trace daemon adding %d bytes to wrapBuf\n", nBytes);
914        memcpy(wrapBuf + wrapBytes, bufP, nBytes);
915        wrapBytes += nBytes;
916      }
917
918      currentPos += aioBytes;
919#else
920      /* Write the buffer to the trace output file */
921      DFP("trace daemon writing %d bytes at offset %d\n", nBytes, currentPos);
922      rc = write(out_fd, bufP, nBytes);
923      currentPos += nBytes;
924#endif
925
926    }
927    goto retry;
928  }
929
930}
931
932/* The daemon signal handler.  This is the main part of the daemon which (after
933   initializing) does nothing except wait for signals.  SIGIO indicates that
934   a trace buffer is ready to be processed, any other signal stops tracing and
935   terminates the daemon. */
936void SignalHandler(int signum)
937{
938  int rc;
939  struct kArgs args;
940
941  DFP("trace daemon got signal %d\n", signum);
942  switch (signum)
943  {
944    case SIGIO:
945      /* This is the main daemon function.  Handle SIGIO signals from the
946         device and transfer the data from the device to the output file
947         when received.  */
948      ProcessBuffer();
949      break;
950
951    default:
952      /* SIGTERM (or other) will terminate the daemon and end the trace. */
953      // DP("trace daemon: terminating due to signal %d\n", signum);
954      if (TraceHandle > 0)
955      {
956        /* Get any records that are buffered in the device. */
957        ProcessBuffer();
958
959#ifdef LXTRACE_USE_DIO
960        for (numBuf=0; numBuf < MAXAIOBUFS ; numBuf++)
961          aioWait(numBuf);
962        numBuf=0;
963        rc = fcntl(out_fd,F_SETFL,0);
964        rc = lseek(out_fd, currentPos, SEEK_SET);
965        rc = write(out_fd, wrapBuf, wrapBytes);
966        if(fileEnd)
967        {
968           lseek(out_fd, 0, SEEK_SET);
969           ftruncate(out_fd, fileEnd);
970  }
971        for (numBuf=0; numBuf < MAXAIOBUFS ; numBuf++)
972           if (wbufP[numBuf])
973     {
974              free(wbufP[numBuf]);
975        wbufP[numBuf]=NULL;
976           }
977        numBuf=0;
978#endif
979
980        if (nWraps)
981          DP("lxtrace: Trace file wrapped %d times.\n", nWraps);
982
983        /* close everything */
984        close(TraceHandle);
985        close(out_fd);
986      }
987
988      /* daemon terminates */
989      if (bufP)
990        free(bufP);
991
992      _exit(0);
993  }
994}
995
996
997/* Print command syntax and exit */
998void Usage()
999{
1000  DP("\n\
1001Usage: lxtrace { on on-args | off | format format-args | dump | fsync }\n\
1002\n\
1003  Command line format for lxtrace on:\n\
1004    lxtrace on [trcFile [filesize [bufsize]]]\n\
1005      trcFile    Name of raw trace file to be created.  Default is\n\
1006                   /tmp/lxtrace.trc.\n\
1007      filesize   Optional maximum size of wrap-around trace file.  A value\n\
1008                   of 0 means that the trace file grows indefinitely.  Default\n\
1009                   is 16M.\n\
1010      bufsize    Optional size of each buffer.  Default is 64K.\n\
1011\n\
1012  Command line format for lxtrace format:\n\
1013    lxtrace format [-T] [-v] [-t formatFile] [-w wobble] [-o outFile] [trcFile]\n\
1014      -T         Display timestamps as seconds since 1/1/1970.  Default\n\
1015                   is to display the first timestamp as 0.0 and other\n\
1016                   timestamps as elapsed time since the first trace record.\n\
1017      -v         Verbose mode.  Include CPU number, buffer number, and hook\n\
1018                  word in trace output.  Default is to not include these.\n\
1019      wobble     Number of microseconds wobble allowed in timestamps.\n\
1020                 An apparent wrap within the wobble limit will be ignored.\n\
1021      formatFile Name of file containing trace formats.  Default is\n\
1022                   /usr/lpp/mmfs/mmfs.trcfmt.\n\
1023      outFile    Name of file to receive formatted trace output.  Default\n\
1024                   is standard output.\n\
1025      trcFile    Name of input raw trace file.  Default is /tmp/lxtrace.trc.\n");
1026  if (TraceHandle != -1)
1027    close(TraceHandle);
1028  exit(1);
1029}
1030
1031
1032/* The lxtrace command.  */
1033int main(int argc, char *argv[])
1034{
1035  int rc = 0;
1036  struct kArgs args; // command arguments
1037  int i;
1038  int nTrcHdrs;
1039
1040  if (argc < 2)
1041    Usage();
1042
1043  /* A new trace is being started.  Validate the options, open the files,
1044     start the daemon and configure the device. */
1045  if (strcmp(argv[1], "on") == 0)
1046  {
1047    int hw, dpid;
1048    struct sigaction action;
1049    char* trcFileNameP;
1050
1051    /* Initialize local state fields. */
1052    out_fd = -1;
1053    bufP = NULL;
1054    outSize = DEF_TRC_FILESIZE;
1055    bufSize = DEF_TRC_BUFSIZE & 0xfffff000;
1056    currentPos = 0;
1057    nWraps = 0;
1058
1059    /* Get output file name */
1060    if (argc >= 3)
1061      trcFileNameP = argv[2];
1062    else
1063      trcFileNameP = "/tmp/lxtrace.trc";
1064
1065    /* Special cases of specifying stdout or stderr to receive the trace output. */
1066    if (strcmp(trcFileNameP, "stdout") == 0)
1067      out_fd = 1; //stdout;
1068    else if (strcmp(trcFileNameP, "stderr") == 0)
1069      out_fd = 2; //stderr;
1070    else
1071    {
1072      /* Open and trunc the output file. */
1073      if (0 > (out_fd = open(trcFileNameP, O_RDWR|O_CREAT|O_TRUNC, 0644)))
1074      {
1075        DP("lxtrace on: could not open file %s, errno %d\n", trcFileNameP, errno);
1076        rc = errno;
1077        goto exit_on;
1078      }
1079    }
1080#ifdef LXTRACE_USE_DIO
1081    rc=fcntl(out_fd,F_SETFL,O_DIRECT);
1082#endif
1083
1084    /* Save the output filesize.  If the size is explicitly given as zero,
1085       do not wrap. */
1086    if (argc >= 4)
1087    {
1088      outSize = atoi(argv[3]);
1089      if (((outSize < MIN_TRC_FILESIZE) || (outSize > MAX_TRC_FILESIZE)) &&
1090          (outSize != 0))
1091      {
1092        DP("Output file size must be 0 or between %d and %d\n",
1093            MIN_TRC_FILESIZE, MAX_TRC_FILESIZE);
1094        Usage();
1095      }
1096    }
1097
1098    /* If specified, set the trace device buffer size (otherwise use default) */
1099    if (argc >= 5)
1100    {
1101      bufSize = atoi(argv[4]) & 0xfffff000;
1102      if ((bufSize < MIN_TRC_BUFSIZE) || (bufSize > MAX_TRC_BUFSIZE))
1103      {
1104        DP("Buffer size must be between %d and %d\n",
1105            MIN_TRC_BUFSIZE, MAX_TRC_BUFSIZE);
1106        Usage();
1107      }
1108    }
1109
1110    /* The output filesize must be at least as big as the selected buffer size. */
1111    if ((outSize != 0) && (outSize < bufSize))
1112    {
1113      DP("lxtrace: the filesize (%d) must be at least as large as the bufsize (%d)\n",
1114         outSize, bufSize);
1115      Usage();
1116    }
1117
1118    /* The trace daemon does all the work.  It is created here ("lxtrace on") and
1119       lives until "trace off" sends it a SIGTERM signal. */
1120    dpid = fork();
1121
1122    if (dpid < 0)
1123    {
1124      DP("lxtrace on: unable to fork trace daemon");
1125      rc = EAGAIN;
1126      goto exit;
1127    }
1128
1129    else if (dpid != 0)
1130    { /* Parent */
1131      return(0);   /* parent's work is done. */
1132    }
1133
1134    else
1135    { /* Child/Daemon */
1136
1137      /* At this point, we are in the trace daemon.  Complete device
1138         configuration and then wait on its signals. */
1139
1140      if (!(bufP = (char *)malloc(bufSize)))
1141      {
1142        DP("trace daemon: unable to malloc trace buffer");
1143        rc = ENOMEM;
1144        goto exit_on;
1145      }
1146      memset(bufP, 0, bufSize);     /* Force page faults to occur now */
1147#ifdef LXTRACE_USE_DIO
1148      for (i=0; i < MAXAIOBUFS; i++)
1149      {
1150        /* dio needs 4k aligned buffers */
1151        wbufP[i] = malloc(bufSize + 4095);
1152        if(wbufP[i] == NULL)
1153        {
1154           DP("trace daemon: unable to malloc trace buffer");
1155           rc = ENOMEM;
1156           goto exit_on;
1157        }
1158        aiobufP[i] = (char*)ROUNDUP(wbufP[i], 4096);
1159        memset(aiobufP[i], 0, bufSize);     /* Force page faults to occur now */
1160  aioStarted[i] = 0;
1161      }
1162#endif
1163
1164      /* Initialize the signal handler */
1165      action.sa_handler = SignalHandler;
1166      sigfillset(&action.sa_mask);  /* block all signals while the handler is running */
1167      action.sa_flags = 0;
1168
1169      if ((sigaction(SIGIO  , &action, NULL)) ||
1170          (sigaction(SIGTERM, &action, NULL)))
1171      {
1172        DP("trace daemon: unable to create signal handler\n");
1173        rc = EINVAL;
1174        goto exit_on;
1175      }
1176
1177      /* Initialize the trace device */
1178
1179      TraceHandle = open(TRC_DEVICE, O_RDWR);
1180      if (TraceHandle < 0)
1181      {
1182        /* Device does not exist, wrong type, or already open, ... */
1183        DP("trace daemon: device open failed with errno %d\n", errno);
1184        rc = ENODEV;
1185        goto exit_on;
1186      }
1187
1188      /* Configure trace device buffer size. */
1189      args.arg1 = bufSize;
1190      rc = ioctl(TraceHandle, trc_bufSize, &args);
1191      if (rc < 0)
1192      {
1193        DP("trace daemon: device buffer request failed with errno %d\n", errno);
1194        rc = ENOMEM;
1195        goto exit_on;
1196      }
1197
1198      /* Mark as ready for tracing. */
1199      rc = ioctl(TraceHandle, trc_begin, NULL);
1200      if (rc < 0)
1201      {
1202        DP("trace daemon: device begin failed with errno %d\n", errno);
1203        rc = ENODEV;
1204        goto exit_on;
1205      }
1206
1207      /* wait here forever (until SIGTERM is handled and exits) */
1208      while (1)
1209      {
1210        DFP("trace daemon about to wait\n");
1211        pause();
1212      }
1213      _exit(-1);
1214    } /* child/daemon */
1215
1216exit_on:
1217    /* exit here for failures during daemon initialization. */
1218    DFP("trace daemon exitting\n");
1219    if (TraceHandle > 0)
1220      close(TraceHandle);
1221  }
1222
1223
1224  /* Stopping a previously activated trace by notifying to the device. */
1225  else if (strcmp(argv[1], "off") == 0)
1226  {
1227    int fd;
1228
1229    fd = open(TRC_DEVICE, O_RDONLY);
1230    if (fd < 0)
1231    {
1232      DP("lxtrace off: device open failed rc %d errno %d\n", fd, errno);
1233      rc = ENODEV;
1234      goto exit;
1235    }
1236
1237    /* trc_end terminates tracing. */
1238    args.arg1 = 0;
1239    rc = ioctl(fd, trc_end, &args);
1240    if (rc < 0)
1241    {
1242      DP("lxtrace off: device end failed with errno %d\n", errno);
1243      rc = EINVAL;
1244      goto exit_off;
1245    }
1246
1247exit_off:
1248    /* When we close here, the daemon will be signalled to terminate. */
1249    close(fd);
1250  }
1251
1252
1253  /* Format the trace file. */
1254  else if (strcmp(argv[1], "format") == 0)
1255  {
1256    FILE *raw_fp; /* raw trace data file */
1257    FILE *fmt_fp; /* the application formatting file */
1258    FILE *out_fp; /* output file */
1259    Boolean hasWrapped; /* the raw trace file became full and wrapped */
1260    struct timeval tzero = { 0, 0 };
1261    trc_hdrs_t h;
1262    int useTOD = 0;    /* true if -T option given */
1263    int verbose = 0;   /* true if -v option given */
1264    char* formatFileNameP = "/usr/lpp/mmfs/mmfs.trcfmt";
1265    char* outputFileNameP = NULL;
1266    char* rawTraceFileNameP = "/tmp/lxtrace.trc";
1267    char *compP, *fmtP;
1268    unsigned int llmask;
1269    i2nSpec_t *i2nP;
1270    int nRecordsProcessed = 0;  /* useful when running under gdb */
1271    int i, id;
1272    Boolean msgHandler;
1273    char timeBufP[26]; /* timestamp in string format */
1274    FILE *version_fp;
1275
1276    /* work buffers */
1277    char application_data[LXTRACE_MAX_DATA];   /* raw trace data */
1278
1279    /* Array of id2name substitutions to be made */
1280    typedef struct { char s[TRC_MAX_ID2NAME]; } idname_t;
1281    idname_t id2nameP[LXTRACE_MAX_FORMAT_SUBS];
1282    char fmtString[256];
1283
1284    /* Array of pointers to the args for the fprintf that formats the output */
1285    argType argP[LXTRACE_MAX_FORMAT_SUBS];
1286
1287    raw_fp = fmt_fp = out_fp = NULL;
1288    formatFileP = NULL;
1289    fmtDescSpaceP = NULL;
1290
1291#if defined(__64BIT__)
1292    startTime = 9223372036854775807;
1293#else
1294    startTime = 2147483647;
1295#endif
1296    stopTime = 0;
1297
1298
1299    /* Parse arguments */
1300    for (i = 2; i < argc ; i++)
1301    {
1302      if (strcmp(argv[i], "-T") == 0)
1303        useTOD = 1;
1304      else if (strcmp(argv[i], "-v") == 0)
1305        verbose = 1;
1306      else if (strcmp(argv[i], "-w") == 0  &&  i+1<argc)
1307      {
1308  char *endptr;
1309        i += 1;
1310        allowed_usec_wobble = strtol(argv[i],&endptr,10);
1311  if ((*endptr != 0) || (allowed_usec_wobble < 0))
1312  {
1313    DP("lxtrace format: -w requires an integer argument greater than or equal to zero.\n");
1314    rc = 12;
1315    goto exit_format;
1316  }
1317      }
1318      else if (strcmp(argv[i], "-t") == 0  &&  i+1<argc)
1319      {
1320        i += 1;
1321        formatFileNameP = argv[i];
1322      }
1323      else if (strcmp(argv[i], "-o") == 0  &&  i+1<argc)
1324      {
1325        i += 1;
1326        outputFileNameP = argv[i];
1327      }
1328      else
1329        rawTraceFileNameP = argv[i];
1330    }
1331
1332    /* Open the trace file. */
1333    if (NULL == (raw_fp = fopen(rawTraceFileNameP, "r")))
1334    {
1335      DP("lxtrace format: could not open file %s, errno %d\n",
1336         rawTraceFileNameP, errno);
1337      rc = errno;
1338      goto exit_format;
1339    }
1340
1341    /* Open the formatting file. */
1342    if (NULL == (fmt_fp = fopen(formatFileNameP, "r")))
1343    {
1344      DP("lxtrace format: could not open file %s, errno %d\n", formatFileNameP, errno);
1345      rc = errno;
1346      goto exit_format;
1347    }
1348
1349    if (0 != (rc = BuildFormatTable(fmt_fp)))
1350    {
1351      DP("lxtrace format: could not parse file: %s errno %d\n",
1352         formatFileNameP, errno);
1353      rc = errno;
1354      goto exit_format;
1355    }
1356
1357    /* Open and truncate the output file. (defaults to stdout) */
1358    if (outputFileNameP == NULL)
1359      out_fp = stdout;
1360    else
1361    {
1362      out_fp = fopen(outputFileNameP, "w");
1363      if (out_fp == NULL)
1364      {
1365        DP("lxtrace format: could not open file %s, errno %d\n",
1366           outputFileNameP, errno);
1367        rc = errno;
1368        goto exit_format;
1369      }
1370    }
1371
1372    /* The trace file (raw_fp) is a wrap-around file.  We need to read it to
1373       see if it ever wrapped.  If it never wrapped, rewind and process it
1374       top-to-bottom.  If the file did wrap, set the file pointer to the oldest
1375       record and begin processing from there.  ReadNextTrcHeader detects the
1376       wrap point.  When formatting of a wrapped file reaches end-of-file, it
1377       must then go back and read starting at the beginning and stop when
1378       the wrap point is again reached. */
1379
1380    hasWrapped = false; /* Signal format processing below we have a wrap file */
1381    nWraps = 0;         /* ReadNextTrcHeader counts wrap points it encounters.*/
1382
1383    nTrcHdrs = 0;
1384    while (0 < (rc = ReadNextTrcHeader(raw_fp, &h)))
1385    {
1386      nTrcHdrs++;
1387
1388      /* If ReadNextTrcHeader detects a wrap, leave and begin formating. */
1389      if (nWraps)
1390        break;
1391
1392      /* skip over trace data */
1393      fseek(raw_fp, h.hdr.trLength - sizeof(trc_datahdr_t), SEEK_CUR);
1394    }
1395
1396    DFP("wrap analysis: nTrcHdrs %d nWraps %d seekpos %d\n",
1397        nTrcHdrs, nWraps, ftell(raw_fp) - sizeof(trc_hdrs_t));
1398
1399    /* Write The first and last timestamps found in the trace */
1400    fprintf(out_fp, "Trace begins: %s\n", fmtTime(startTime, timeBufP));
1401    fprintf(out_fp, "Trace ends  : %s%s\n", fmtTime(stopTime, timeBufP),
1402            nWraps? " (file wrapped)":"");
1403
1404    /* Show the point we interpreted as the wrap-point */
1405    if (nWraps)
1406    {
1407      fprintf(out_fp,"Wrap point  : %d.%06d-%d.%06d (delta %d.%06d)\n",
1408           wrap_prev.tv_sec, wrap_prev.tv_usec, wrap_next.tv_sec, wrap_next.tv_usec,
1409          (wrap_prev.tv_sec-wrap_next.tv_sec), (wrap_prev.tv_usec-wrap_next.tv_usec));
1410    }
1411
1412    /* Write kernel version to the trace file.  This is the machine
1413     * where the format was done, but this would normally be the
1414     * same machine that generated the raw trace as well.
1415     */
1416    if (version_fp = fopen("/proc/version", "r"))
1417    {
1418      int verLen;
1419      const int verStrLen = 512;
1420      char version_str[verStrLen];
1421
1422      memset(version_str, 0, verStrLen);
1423      if (0 < (verLen = fread(version_str, 1, verStrLen, version_fp)))
1424      {
1425        version_str[verStrLen] = '\0';
1426        fprintf(out_fp, "Formatted on: %s\n\n", version_str);
1427      }
1428
1429      fclose(version_fp);
1430    }
1431
1432    /* Reset to avoid reporting a wrap on the first record from the next
1433     * call to ReadNextTrcHeader.
1434     */
1435    prevTime.tv_sec = 0;
1436
1437    /* If a wrap point was located, back-up so the header can be read again
1438       as formatting begins with this record.  If the file never wrapped,
1439       reset to the top of the file and process top-to-bottom. */
1440    if (nWraps)
1441    {
1442      fseek(raw_fp, -sizeof(trc_hdrs_t), SEEK_CUR);
1443      hasWrapped = true; /* Signal format procesing below to handle the wrap */
1444    }
1445    else
1446      rewind(raw_fp);
1447
1448    /* Print first line of header and build trace format string */
1449    fmtString[0] = '\0';
1450    if (useTOD)
1451    {
1452      fprintf(out_fp, H1_TS_T);
1453      strcat(fmtString, FMT_TS_T);
1454    }
1455    else
1456    {
1457      fprintf(out_fp, H1_TS);
1458      strcat(fmtString, FMT_TS);
1459    }
1460    fprintf(out_fp, H1_PID);
1461    strcat(fmtString, FMT_PID);
1462    if (verbose)
1463    {
1464      fprintf(out_fp, H1_P_HW_V);
1465      strcat(fmtString, FMT_P_HW_V);
1466    }
1467    fprintf(out_fp, H1_TAG);
1468    strcat(fmtString, FMT_TAG);
1469
1470    /* Print second line of header */
1471    if (useTOD)
1472      fprintf(out_fp, H2_TS_T);
1473    else
1474      fprintf(out_fp, H2_TS);
1475    fprintf(out_fp, H2_PID);
1476    if (verbose)
1477      fprintf(out_fp, H2_P_HW_V);
1478    fprintf(out_fp, H2_TAG);
1479
1480start_formatting:
1481    nWraps = 0; /* Reset the ReadNextTrcHeader wrap-found indicator. */
1482
1483    /* Process raw trace records. Start by reading the device header. */
1484    while (0 < (rc = ReadNextTrcHeader(raw_fp, &h)))
1485    {
1486      DFP("hdr.trTime %X:%X hdr.trProcess %X hdr.trCPU %X hdr.trBuf %d hdr.trLength %X\n",
1487          h.hdr.trTime.tv_sec, h.hdr.trTime.tv_usec, h.hdr.trProcess,
1488          h.hdr.trCPU, h.hdr.trBuf, h.hdr.trLength);
1489
1490      /* Since we always start AFTER the wrap point, we should only see a
1491         wrap if we had reached the end of the file and then gone back to
1492         the beginning to get the records from there up to the wrap point.
1493         So, if we see this, we have formatted all the records and are done. */
1494
1495      if (nWraps) // ReadNextTrcHeader detected and processed a wrap-point
1496      {
1497        DFP("lxtrace format: ending after processing records 0 to wrap-point\n");
1498        break;
1499      }
1500
1501      DFP("rec.trHook 0x%X rec.trNArgs %d rec.trSPos %d rec.trSLen %d\n",
1502          h.rec.trHook, h.rec.trNArgs, h.rec.trSPos, h.rec.trSLen);
1503
1504      if (h.hdr.trLength < sizeof(trc_datahdr_t))
1505      {
1506        DP("lxtrace format: bad record length in %s at offset %d, trLength %d\n",
1507           rawTraceFileNameP, ftell(raw_fp), h.hdr.trLength);
1508        rc = EINVAL;
1509        if (hasWrapped)
1510        {
1511          DP("lxtrace format: restart from wrap-point\n");
1512          fprintf(out_fp, "*** BAD trace record header ***\n");
1513          goto do_wrap;
1514        }
1515        goto exit_format;
1516      }
1517
1518      /* application_data[] holds the raw trace data that follows the headers. */
1519      if (0 > (rc = fread(application_data, 1,
1520                          h.hdr.trLength - sizeof(trc_datahdr_t), raw_fp)))
1521      {
1522        DP("lxtrace format: could not read data from %s at offset %d, rc %d errno %d\n",
1523           rawTraceFileNameP, ftell(raw_fp), rc, errno);
1524        rc = errno;
1525        goto exit_format;
1526      }
1527
1528      DFP("read %d bytes of application trace data\n", rc);
1529
1530      /* If this is the first trace record, consider this time zero,
1531         unless the -T option was specified */
1532      if (tzero.tv_sec == 0  &&  !useTOD)
1533        tzero = h.hdr.trTime;
1534
1535      /* Convert trace time to time since first trace record unless
1536         the -T option was specified */
1537      if (!useTOD)
1538      {
1539        h.hdr.trTime.tv_sec -= tzero.tv_sec;
1540        if (h.hdr.trTime.tv_usec >= tzero.tv_usec)
1541          h.hdr.trTime.tv_usec -= tzero.tv_usec;
1542        else
1543        {
1544          h.hdr.trTime.tv_sec--;
1545          h.hdr.trTime.tv_usec += 1000000 - tzero.tv_usec;
1546        }
1547      }
1548
1549      /* Get the formatting record for the trace record with this hookid */
1550      if (0 != GetFormatRecord(fmt_fp, (int)(h.rec.trHook & 0x0000FFFF),
1551                               &compP, &fmtP, &llmask, &i2nP))
1552      {
1553        DP("lxtrace format: No formatting record found for hook %X\n",
1554           h.rec.trHook);
1555        fprintf(out_fp, "Unknown hook id 0x%08X\n", h.rec.trHook);
1556      }
1557      else
1558      {
1559        /* Found the formatting information for the current trace record. */
1560
1561        /* Write leading information before the formatted trace. */
1562        if (verbose)
1563          fprintf(out_fp, fmtString,
1564                  h.hdr.trTime.tv_sec, h.hdr.trTime.tv_usec, h.hdr.trProcess,
1565                  h.hdr.trCPU, h.hdr.trBuf, h.rec.trHook,
1566                  compP);
1567        else
1568          fprintf(out_fp, fmtString,
1569                  h.hdr.trTime.tv_sec, h.hdr.trTime.tv_usec, h.hdr.trProcess,
1570                  compP);
1571
1572        DFP("format string: %s\n", fmtP);
1573        DFP("raw data: %X %X %X %X %X\n",
1574            TRC_ARG(0), TRC_ARG(1), TRC_ARG(2), TRC_ARG(3), TRC_ARG(4));
1575
1576        /* Format-specific processing. */
1577        if (h.rec.trSPos == _TR_FORMAT_I)
1578        {
1579          /* Trace data consists of n integer values */
1580          for (i=0; i < h.rec.trNArgs; i++)
1581            argP[i] = TRC_ARG(i);
1582        }
1583        else if (h.rec.trSPos < LXTRACE_MAX_FORMAT_SUBS)
1584        {
1585          /* Trace data contains null terminated string at TRC_ARG(rec.trSPos);
1586             the length of the string is given by rec.trSLen (rounded up to a
1587             multiple of ARGLEN).  Get a pointer to the string, make sure it's
1588             null-terminated (so we don't blow up if the trace data is trash),
1589             and compute how many extra TRC_ARG words are occupied by the
1590             string. */
1591          // ?? should validate trSLen!
1592          char *s = (char *)&TRC_ARG(h.rec.trSPos);
1593          int xi = h.rec.trSLen/ARGLEN - 1;
1594          s[h.rec.trSLen - 1] = '\0';
1595
1596          for (i = 0; i < h.rec.trSPos; i++)
1597            argP[i] = TRC_ARG(i);
1598          argP[i++] = (argType)s;
1599          for (; i <= h.rec.trNArgs; i++)
1600            argP[i] = TRC_ARG(i + xi);
1601        }
1602        else if (h.rec.trSPos == _TR_FORMAT_F)
1603        {
1604          /* First n words are integer values; a double value is stored at
1605             TRC_ARG(rec.trNArgs) */
1606          double *d = (double *)&TRC_ARG(h.rec.trNArgs);
1607
1608          for (i = 0; i < h.rec.trNArgs; i++)
1609            argP[i] = TRC_ARG(i);
1610          memcpy(&argP[i], d, sizeof(double));
1611        }
1612        else if (h.rec.trSPos == _TR_FORMAT_X)
1613        {
1614          /* XTrace type (preformatted) record. */
1615          fputs(application_data, out_fp);
1616          i = strlen(application_data);
1617          if (i == 0 || application_data[i-1] != '\n')
1618      putc('\n', out_fp);
1619          continue;
1620        }
1621        else
1622        {
1623          DP("lxtrace format: format %d not supported\n", h.rec.trSPos);
1624          fprintf(out_fp, "Unsupported format %d\n", h.rec.trSPos);
1625          goto exit_format;
1626        }
1627
1628#if defined(GPFS_LITTLE_ENDIAN) && !defined(__64BIT__)
1629        /* On a 32 bit machine, 64 bit values are stored with the high-order
1630           word first, followed by the low-order word.  The problem is that
1631           the reverse is expected by printf formatting.  For each "%ll" in
1632           the formatting string, swap the words in the argP array so that
1633           the words are swapped into the expected order.  The llmask
1634           bitvector indicates which arguments are of the %ll form. */
1635        if (llmask)
1636        {
1637          int tmp;
1638          int argPos = 0;
1639
1640          DFP("\nlong long formatting: %s\n", fmtP);
1641
1642          /* Loop through the bits in llmask */
1643          while (llmask)
1644          {
1645            if (llmask & 1)
1646            {
1647              DFP("switching argP[%d] and argP[%d]\n", argPos, argPos+1);
1648              tmp = argP[argPos];
1649              argP[argPos] = argP[argPos+1];
1650              argP[argPos+1]  = tmp;
1651              argPos += 1; /* account for the extra arg for this 2-word sub */
1652            }
1653            argPos += 1;
1654            llmask >>= 1;
1655          }
1656        }
1657#endif // GPFS_LITTLE_ENDIAN and NOT __64BIT__
1658
1659#ifdef DBGASSERTS
1660        if (i2nP == NULL &&
1661            (h.rec.trSPos==_TR_FORMAT_I || h.rec.trSPos==_TR_FORMAT_F) &&
1662            strstr(fmtP, "%s"))
1663        {
1664          DP("lxtrace format: hook %X is %s format record containing a string specification.\n",
1665             (h.rec.trHook&0x0000FFFF),
1666             (h.rec.trSPos==_TR_FORMAT_I) ? (char *) "an integer" :
1667               (h.rec.trSPos==_TR_FORMAT_F)? (char *) "a float":
1668               (char *) "an unknown");
1669          DP("               The formatting information for this record does not provide any substitutions.\n");
1670          fprintf(out_fp, "%s\n", fmtP);
1671          goto exit_format;
1672        }
1673#endif // DBGASSERTS
1674
1675        /* Update the argP[] array as necessary to do the substitutions */
1676        if (i2nP != NULL)
1677        {
1678          /* This loop proceses each of the substitution strings */
1679          for (i = 0; i2nP[i].i2nNameP != NULL; i++)
1680          {
1681            int whichSub = i2nP[i].argNum - 1;
1682            char *argtP  = i2nP[i].i2nNameP;
1683
1684            id = argP[whichSub];
1685
1686            /* Thread_id2name conversion need special treatment */
1687            msgHandler = false;
1688            if (strcmp(argtP, "Thread_id2name") == 0)
1689            {
1690              /* Thread IDs >= FirstMsgHandlerThread are really message
1691                 handler IDs, so look for a tscMsg_id2name entry in that case.
1692                 Thread IDs < FirstMsgHandlerThread are "standard" thread IDs,
1693                 so look for StdThread_id2name in that case. */
1694              if (id >= FirstMsgHandlerThread)
1695              {
1696                /* Adjust id value and conversion function.  Set a flag to
1697                   remember to insert "Msg handler" in front of the name that
1698                   is found. */
1699                id -= FirstMsgHandlerThread;
1700                argtP = "tscMsg_id2name";
1701                msgHandler = true;
1702              }
1703              else
1704                argtP = "StdThread_id2name";
1705            }
1706           
1707            /* Now look-up the substitution for argtP (xx_id2name) that is
1708               associated with the appropriate traced value (id). */
1709            if (0 != GetID2NameRecord(fmt_fp, argtP, id, TRC_MAX_ID2NAME,
1710                                      id2nameP[whichSub].s))
1711            {
1712              /* Unable to find the formatting (substitution) information */
1713
1714              /* ip addresses are formatted as such */
1715              if ((0 == strcmp(argtP, "nodeaddr_id2name"))  ||
1716                  (0 == strcmp(argtP, "ipaddr_id2name")) )
1717              {
1718                union { int i; unsigned char c[4]; } a;
1719
1720                a.i = id;
1721                sprintf(id2nameP[whichSub].s, "%u.%u.%u.%u",
1722                        a.c[0], a.c[1], a.c[2], a.c[3]);
1723              }
1724
1725              /* lockState to string conversions. */
1726              else if ((0 == strcmp(argtP, "lockstateHigh_id2name")))
1727              {
1728                #define lsAcqPend  0x80000000
1729                #define lsLxLock   0x40000000
1730                #define lsXwLock   0x20000000
1731                #define lsWaLock   0x10000000
1732                #define lsWwLock   0x08000000
1733                #define lsRvkLock  0x04000000
1734                #define lsRoCount  0x003FF800
1735                #define lsRoUnit   0x00000800
1736                #define lsWoCount  0x000007FF
1737                #define lsWoUnit   0x00000001
1738                char buf1[32], buf2[32];
1739                buf1[0]='\0';
1740                buf2[0]='\0';
1741                if (id & lsRoCount)
1742                  sprintf(buf1, " ro:%d", (id & lsRoCount)/lsRoUnit);
1743                if (id & lsWoCount)
1744                  sprintf(buf2, " wo:%d", (id & lsWoCount)/lsWoUnit);
1745                sprintf(id2nameP[whichSub].s, "[%s%s%s%s%s%s%s%s",
1746                        (id & lsAcqPend) ? " acq" : "",
1747                        (id & lsLxLock) ? " lx" : "",
1748                        (id & lsXwLock) ? " xw" : "",
1749                        (id & lsWaLock) ? " wa" : "",
1750                        (id & lsWwLock) ? " ww" : "",
1751                        (id & lsRvkLock) ? " rvk" : "",
1752                        buf1, buf2);
1753
1754              }
1755
1756              /* lockState to string conversions. */
1757              else if ((0 == strcmp(argtP, "lockstateLow_id2name")))
1758              {
1759                #define lsRfCount  0x7FF00000
1760                #define lsRfUnit   0x00100000
1761                #define lsWfCount  0x000FFE00
1762                #define lsWfUnit   0x00000200
1763                #define lsRsCount  0x000001FF
1764                #define lsRsUnit   0x00000001
1765                char buf1[32], buf2[32], buf3[32];
1766                buf1[0]='\0';
1767                buf2[0]='\0';
1768                buf3[0]='\0';
1769                if (id & lsRfCount)
1770                  sprintf(buf1, " rf:%d", (id & lsRfCount)/lsRfUnit);
1771                if (id & lsWfCount)
1772                  sprintf(buf2, " wf:%d", (id & lsWfCount)/lsWfUnit);
1773                if (id & lsRsCount)
1774                  sprintf(buf3, " rs:%d", (id & lsRoCount)/lsRoUnit);
1775                sprintf(id2nameP[whichSub].s, "%s%s%s ]", buf1, buf2, buf3);
1776              }
1777
1778              /* lockFlags to string conversions. */
1779              else if ((0 == strcmp(argtP, "lockflags_id2name")))
1780              {
1781                #define lfDaemonWriter      0x0001
1782                #define lfRevokePending     0x0002
1783                #define lfRevokeProcessing  0x0004
1784                sprintf(id2nameP[whichSub].s, "[%s%s%s ]",
1785                        (id & lfDaemonWriter) ? " dmn" : "",
1786                        (id & lfRevokePending) ? " rvk" : "",
1787                        (id & lfRevokeProcessing) ? " pro" : "");
1788              }
1789
1790              /* reason is optional so handle the resulting value of zero. */
1791              else if ((0 == strcmp(argtP, "reason_id2name")) &&
1792                       (id == 0))
1793                strcpy(id2nameP[whichSub].s, "no reason given");
1794
1795              /* Any other case is a lack of proper formatting information
1796                 for the generated trace record.  Insert "unknown" into the
1797                 formatted output and continue. */
1798              else
1799              {
1800#ifdef DBGASSERTS
1801                DP("lxtrace format: %s substitution %d (value=%d) not found (used in hook %04X).\n",
1802                   argtP, whichSub+1, argP[whichSub], (h.rec.trHook&0x0000FFFF));
1803                // To debug one of these, exit here.
1804                // goto exit_format;
1805#endif // DBGASSERTS
1806                sprintf(id2nameP[whichSub].s, "unknown(%d)", argP[whichSub]);
1807              }
1808            }
1809
1810            /* If we are converting the thread ID of a message handler,
1811               insert "Msg handler" in front of the name. */
1812            if (msgHandler)
1813            {
1814              char tmpBuff[TRC_MAX_ID2NAME];
1815              strncpy(tmpBuff, id2nameP[whichSub].s, sizeof(tmpBuff));
1816              snprintf(id2nameP[whichSub].s, TRC_MAX_ID2NAME,
1817                       "Msg handler %s", tmpBuff);
1818            }
1819
1820            /* Alter the TRC_ARGLIST for this substituted parameter */
1821            argP[whichSub] = (argType)id2nameP[whichSub].s;
1822          } /* for each substitution string */
1823
1824        } /* End-substitutions */
1825
1826        /* Output the completed trace record */
1827#if !defined(GPFS_ARCH_POWER) || !defined(GPFS_LINUX) || defined(__64BIT__)
1828        fprintf(out_fp, fmtP, TRC_ARGLIST);
1829#else
1830        /* On Linux 32-bit Power architecture (and maybe others?), the
1831           va_list calling convention does not allow passing a mixture of
1832           longs and long longs in successive memory words.  If long longs
1833           are present, break up the format string at each "%" and print
1834           the arguments separately, using llmask to determine which ones
1835           are long long. */
1836        if (llmask == 0)
1837          fprintf(out_fp, fmtP, TRC_ARGLIST);
1838        else
1839        {
1840          int n = 0, ll = llmask;
1841          unsigned long long bign;
1842          char *ptr, *newptr;
1843          char *newfmtP = malloc(strlen(fmtP) + 1);
1844          if (newfmtP == NULL)
1845          {
1846            DP("lxtrace format: unable to malloc format buffer");
1847            rc = ENOMEM;
1848            goto exit_format;
1849          }
1850          ptr = fmtP;
1851          newptr = newfmtP;
1852          while (*ptr)
1853          {
1854            if (*ptr != '%')
1855              *newptr++ = *ptr++;
1856            else
1857            {
1858              do
1859                *newptr++ = *ptr++;
1860              while (*ptr && *ptr != '%');
1861              *newptr = '\0';
1862              if (ll & 1)
1863              {
1864                bign = ((unsigned long long) argP[n] << 32) +
1865                  (unsigned long)argP[n+1];
1866                fprintf(out_fp, newfmtP, bign);
1867                n += 2;
1868              }
1869              else
1870              {
1871                fprintf(out_fp, newfmtP, argP[n]);
1872                n++;
1873              }
1874              ll >>= 1;
1875              newptr = newfmtP;
1876            }
1877          }
1878          *newptr = '\0';
1879          if (newptr != newfmtP)
1880            fprintf(out_fp, newfmtP);
1881          free(newfmtP);
1882        }
1883#endif
1884        nRecordsProcessed += 1;
1885
1886      } /* end processing this trace record */
1887    } /* while there are more unformatted trace records to process */
1888
1889    if (hasWrapped)
1890    {
1891do_wrap:
1892      rewind(raw_fp); /* On a wrapped file, need to go back to the top. */
1893      hasWrapped = false;  /* Clear this so we don't do this more than once. */
1894      DFP("lxtrace format: done with wrap-point to EOF.  Going back to record 0\n");
1895      goto start_formatting;
1896    }
1897
1898    rc = 0;
1899
1900exit_format:
1901    /* print hash lookup statistics */
1902    DFP("Stats: hook lookup %6d check %7d (%.1f)\n",
1903        nHookLookup, nHookCheck, (double)nHookCheck/(double)nHookLookup);
1904    DFP("        i2n lookup %6d check %7d (%.1f) fail %d\n",
1905        nI2NLookup, nI2NCheck, (double)nI2NCheck/(double)nI2NLookup, nI2NFail);
1906
1907    DFP("Wrap point: %d.%06d-%d.%06d (delta %d.%06d)\n",
1908         wrap_prev.tv_sec, wrap_prev.tv_usec, wrap_next.tv_sec, wrap_next.tv_usec,
1909        (wrap_prev.tv_sec-wrap_next.tv_sec), (wrap_prev.tv_usec-wrap_next.tv_usec));
1910
1911    /* cleanup */
1912    if (formatFileP) free(formatFileP);
1913    if (fmtDescSpaceP) free(fmtDescSpaceP);
1914    if (raw_fp != NULL) fclose(raw_fp);
1915    if (fmt_fp != NULL) fclose(fmt_fp);
1916    if (out_fp != NULL) fclose(out_fp);
1917  } /* "format" option */
1918
1919
1920  /* Display the state information associated with the device. */
1921  else if (strcmp(argv[1], "dump") == 0)
1922  {
1923    int fd;
1924#define DUMP_LEN 512
1925    char dumpBufP[DUMP_LEN];
1926
1927    fd = open(TRC_DEVICE, O_RDONLY);
1928    if (fd < 0)
1929    {
1930      DP("lxtrace dump: device open failed errno %d\n", errno);
1931      rc = ENODEV;
1932      goto exit;
1933    }
1934
1935    /* Dump the trace device information */
1936    args.arg1 = DUMP_LEN;
1937    args.arg2 = (long)dumpBufP;
1938
1939    if (0 != (rc = ioctl(fd, trc_dump, &args)))
1940    {
1941      DP("lxtrace dump: device dump returns rc %d errno %d \n", rc, errno);
1942    }
1943    else
1944    {
1945      printf("%s\n", dumpBufP);
1946    }
1947
1948exit_dump:
1949    close(fd);
1950  }
1951
1952  /* Flush any data that is currently buffered in the device. */
1953  else if (strcmp(argv[1], "fsync") == 0)
1954  {
1955    int fd;
1956
1957    fd = open(TRC_DEVICE, O_RDONLY);
1958    if (fd < 0)
1959    {
1960      DP("lxtrace fsync: device open failed with errno %d\n", errno);
1961      rc = ENODEV;
1962      goto exit;
1963    }
1964
1965    if (0 != (rc = fsync(fd)))
1966    {
1967      DP("lxtrace fsync: device fsync returns rc %d errno %d\n", rc, errno);
1968    }
1969
1970exit_fsync:
1971    close(fd);
1972  }
1973
1974  else
1975    Usage();
1976
1977exit:
1978  return rc;
1979}
1980
1981
Note: See TracBrowser for help on using the repository browser.