diff --git a/winsup/utils/ChangeLog b/winsup/utils/ChangeLog index cbbfc3a86..5a7d61ff3 100644 --- a/winsup/utils/ChangeLog +++ b/winsup/utils/ChangeLog @@ -1,3 +1,20 @@ +2001-11-15 Gary R. Van Sickle + + * strace.cc (main): Change getopt() to getopt_long(). + Add support for help and version info. + Use new parse_mask() function for -m/--mask option. + (longopts): Add long options structure. + (opts): Move options string from getopts call to static var. + (usage): Print usage information. + (SCCSid): Version info. + (version): New function for displaying version info. + (parse_mask): New function supporting parsing of mnemonics, + hex, and basic expressions in masks. + (mnemonic2ul): New mnemonic parsing function. + (tag_mask_mnemonic): New type. + (mnemonic_table): New table of mnemonics for mnemonic2ul() to + search through. + 2001-11-12 Corinna Vinschen * cygcheck.cc (dump_sysinfo): Redefine output format slightly. diff --git a/winsup/utils/strace.cc b/winsup/utils/strace.cc index bd68d4a98..c88b40673 100644 --- a/winsup/utils/strace.cc +++ b/winsup/utils/strace.cc @@ -30,6 +30,9 @@ int _impure_ptr; /* we *know* we're being built with GCC */ #define alloca __builtin_alloca +// Version string. +static char *SCCSid = "@(#)strace V1.0, Copyright (C) 2001 Red Hat Inc., " __DATE__ "\n"; + static const char *pgm; static int forkdebug = 0; static int numerror = 1; @@ -602,6 +605,196 @@ dostrace (unsigned mask, FILE *ofile, char **argv) return; } +typedef struct tag_mask_mnemonic +{ + unsigned long val; + const char *text; +} +mask_mnemonic; + +static const mask_mnemonic mnemonic_table[] = { + {_STRACE_ALL, "all"}, + {_STRACE_FLUSH, "flush"}, + {_STRACE_INHERIT, "inherit"}, + {_STRACE_UHOH, "uhoh"}, + {_STRACE_SYSCALL, "syscall"}, + {_STRACE_STARTUP, "startup"}, + {_STRACE_DEBUG, "debug"}, + {_STRACE_PARANOID, "paranoid"}, + {_STRACE_TERMIOS, "termios"}, + {_STRACE_SELECT, "select"}, + {_STRACE_WM, "wm"}, + {_STRACE_SIGP, "sigp"}, + {_STRACE_MINIMAL, "minimal"}, + {_STRACE_EXITDUMP, "exitdump"}, + {_STRACE_SYSTEM, "system"}, + {_STRACE_NOMUTEX, "nomutex"}, + {_STRACE_MALLOC, "malloc"}, + {_STRACE_THREAD, "thread"}, + {0, NULL} +}; + +static unsigned long +mnemonic2ul (const char *nptr, char **endptr) +{ + // Look up mnemonic in table, return value. + // *endptr = ptr to char that breaks match. + const mask_mnemonic *mnp = mnemonic_table; + + while (mnp->text != NULL) + { + if (strcmp (mnp->text, nptr) == 0) + { + // Found a match. + if (endptr != NULL) + { + *endptr = ((char *) nptr) + strlen (mnp->text); + } + return mnp->val; + } + mnp++; + } + + // Didn't find it. + if (endptr != NULL) + { + *endptr = (char *) nptr; + } + return 0; +} + +static unsigned long +parse_mask (const char *ms, char **endptr) +{ + const char *p = ms; + char *newp; + unsigned long retval = 0, thisval; + const size_t bufsize = 16; + char buffer[bufsize]; + size_t len; + + while (*p != '\0') + { + // First extract the term, terminate it, and lowercase it. + strncpy (buffer, p, bufsize); + buffer[bufsize - 1] = '\0'; + len = strcspn (buffer, "+,\0"); + buffer[len] = '\0'; + strlwr (buffer); + + // Check if this is a mnemonic. We have to do this first or strtoul() + // will false-trigger on anything starting with "a" through "f". + thisval = mnemonic2ul (buffer, &newp); + if (buffer == newp) + { + // This term isn't mnemonic, check if it's hex. + thisval = strtoul (buffer, &newp, 16); + if (newp != buffer + len) + { + // Not hex either, syntax error. + *endptr = (char *) p; + return 0; + } + } + + p += len; + retval += thisval; + + // Handle operators + if (*p == '\0') + break; + if ((*p == '+') || (*p == ',')) + { + // For now these both equate to addition/ORing. Until we get + // fancy and add things like "all-", all we need do is + // continue the looping. + p++; + continue; + } + else + { + // Syntax error + *endptr = (char *) p; + return 0; + } + } + + *endptr = (char *) p; + return retval; +} + +static void +usage () +{ + fprintf (stderr, "\ +Usage: strace [OPTIONS] \n\ + -b, --buffer-size=SIZE Set size of output file buffer.\n\ + -m, --mask=MASK Set message filter mask.\n\ +\n\ + MASK can be any combination of the following mnemonics and/or hex values\n\ + (0x is optional). Combine masks with '+' or ',' like so:\n\ +\n\ + --mask=wm+system,malloc+0x00800\n\ +\n\ + Mnemonic Hex Corresponding Def Description\n\ + =========================================================================\n\ + all 0x00001 (_STRACE_ALL) All strace messages.\n\ + flush 0x00002 (_STRACE_FLUSH) Flush output buffer after each message.\n\ + inherit 0x00004 (_STRACE_INHERIT) Children inherit mask from parent.\n\ + uhoh 0x00008 (_STRACE_UHOH) Unusual or weird phenomenon.\n\ + syscall 0x00010 (_STRACE_SYSCALL) System calls.\n\ + startup 0x00020 (_STRACE_STARTUP) argc/envp printout at startup.\n\ + debug 0x00040 (_STRACE_DEBUG) Info to help debugging. \n\ + paranoid 0x00080 (_STRACE_PARANOID) Paranoid info.\n\ + termios 0x00100 (_STRACE_TERMIOS) Info for debugging termios stuff.\n\ + select 0x00200 (_STRACE_SELECT) Info on ugly select internals.\n\ + wm 0x00400 (_STRACE_WM) Trace Windows msgs (enable _strace_wm).\n\ + sigp 0x00800 (_STRACE_SIGP) Trace signal and process handling.\n\ + minimal 0x01000 (_STRACE_MINIMAL) Very minimal strace output.\n\ + exitdump 0x04000 (_STRACE_EXITDUMP) Dump strace cache on exit.\n\ + system 0x08000 (_STRACE_SYSTEM) Cache strace messages.\n\ + nomutex 0x10000 (_STRACE_NOMUTEX) Don't use mutex for synchronization.\n\ + malloc 0x20000 (_STRACE_MALLOC) Trace malloc calls.\n\ + thread 0x40000 (_STRACE_THREAD) Thread-locking calls.\n\ +\n\ + -o, --output=FILENAME Set output file to FILENAME.\n\ + -f, --trace-children Also trace forked child processes.\n\ + -n, --crack-error-numbers Output descriptive text instead of error\n\ + numbers for Windows errors.\n\ + -d, --no-delta Don't display the delta-t microsecond timestamp.\n\ + -t, --timestamp Use an absolute hh:mm:ss timestamp insted of the\n\ + default microsecond timestamp. Implies -d.\n\ + -w, --new-window Spawn program under test in a new window.\n\ + -S, --flush-period=PERIOD Flush buffered strace output every PERIOD secs.\n\ + -v, --version Display version info.\n\ + -h, --help Display this help info.\n\ +"); +} + +static void +version () +{ + fprintf (stderr, SCCSid+4); +} + +struct option longopts[] = { + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'v'}, + {"buffer-size", required_argument, NULL, 'b'}, + {"mask", required_argument, NULL, 'm'}, + {"output", required_argument, NULL, 'o'}, + {"trace-children", no_argument, NULL, 'f'}, + {"crack-error-numbers", no_argument, NULL, 'n'}, + {"no-delta", no_argument, NULL, 'd'}, + {"usecs", no_argument, NULL, 'u'}, + {"timestamp", no_argument, NULL, 't'}, + {"new-window", no_argument, NULL, 'w'}, + {"flush-period", required_argument, NULL, 'S'}, + {NULL, 0, NULL, 0} +}; + +static const char *const opts = "hvb:m:o:fndutwS:"; + int main (int argc, char **argv) { @@ -614,9 +807,19 @@ main (int argc, char **argv) else pgm++; - while ((opt = getopt (argc, argv, "b:m:o:fndutwS:")) != EOF) + while ((opt = getopt_long (argc, argv, opts, longopts, NULL)) != EOF) switch (opt) { + case 'h': + // Print help and exit + usage (); + return 1; + break; + case 'v': + // Print version info and exit + version (); + return 1; + break; case 'f': forkdebug ^= 1; break; @@ -624,8 +827,17 @@ main (int argc, char **argv) bufsize = atoi (optarg); break; case 'm': - mask = strtoul (optarg, NULL, 16); + { + char *endptr; + mask = parse_mask (optarg, &endptr); + if (*endptr != '\0') + { + // Bad mask expression. + error (0, "syntax error in mask expression \"%s\" near \ +character #%d.\n", optarg, (int) (endptr - optarg), endptr); + } break; + } case 'o': if ((ofile = fopen (optarg, "w")) == NULL) error (1, "can't open %s", optarg); @@ -643,6 +855,9 @@ main (int argc, char **argv) delta ^= 1; break; case 'u': + // FIXME: This option isn't handled properly/at all by the + // program's logic. It seems to be the default, does it + // need to just be removed? usecs ^= 1; break; case 'w':