trace(1): add basic support for timestamps

This patch adds strace-like support for a -t command line option,
which causes a timestamp to be printed at the beginning of each line.
If the option is given more than once, the output will also include
microseconds.

Change-Id: I8cda581651859448c154b01815cc49d915b7b354
This commit is contained in:
David van Moolenbroek 2016-12-15 18:05:51 +00:00
parent 1bb466dd36
commit 10a44c0ee2
4 changed files with 56 additions and 17 deletions

View File

@ -165,15 +165,18 @@ output_flush(void)
} }
/* /*
* Print a PID prefix for the given process, or an info prefix if no process * Print a prefix for a line of output. Possibly print a timestamp first.
* (NULL) is given. Prefixes are only relevant when multiple processes are * Then, if applicable, print a PID prefix for the given process, or an info
* traced. As long as there are multiple processes, each line is prefixed with * prefix if no process (NULL) is given.
* the PID of the process. As soon as the number of processes has been reduced *
* back to one, one more line is prefixed with the PID of the remaining process * PIDs are relevant only when multiple processes are traced. As long as there
* (with a "'" instead of a "|") to help the user identify which process is * are multiple processes, each line is prefixed with the PID of the process.
* left. In addition, whenever a preempted call is about to be resumed, a "*" * As soon as the number of processes has been reduced back to one, one more
* is printed instead of a space, so as to show that it is a continuation of a * line is prefixed with the PID of the remaining process (with a "'" instead
* previous line. An example of all these cases: * of a "|") to help the user identify which process remains. In addition,
* whenever a preempted call is about to be resumed, a "*" is printed instead
* of a space, so as to show that it is a continuation of a previous line. An
* example of all these cases:
* *
* fork() = 3 * fork() = 3
* 3| Tracing test (pid 3) * 3| Tracing test (pid 3)
@ -191,11 +194,33 @@ output_flush(void)
static void static void
put_prefix(struct trace_proc * proc, int resuming) put_prefix(struct trace_proc * proc, int resuming)
{ {
struct timeval tv;
struct tm *tm;
char prefix[32]; char prefix[32];
unsigned int count; unsigned int off, count;
assert(line_off == 0); assert(line_off == 0);
off = 0;
if (timestamps > 0) {
gettimeofday(&tv, NULL);
tm = gmtime(&tv.tv_sec);
off = strftime(prefix, sizeof(prefix), "%T", tm);
if (timestamps > 1)
off += snprintf(&prefix[off], sizeof(prefix) - off,
".%06u", tv.tv_usec);
assert(off < sizeof(prefix) - 2);
prefix[off++] = ' ';
prefix[off] = '\0';
off = output_write(prefix);
}
count = proc_count(); count = proc_count();
/* TODO: add a command line option for always printing the pid. */ /* TODO: add a command line option for always printing the pid. */
@ -212,14 +237,14 @@ put_prefix(struct trace_proc * proc, int resuming)
proc->pid, (count > 1) ? '|' : '\'', proc->pid, (count > 1) ? '|' : '\'',
resuming ? '*' : ' '); resuming ? '*' : ' ');
prefix_off = line_off = output_write(prefix); off += output_write(prefix);
last_pid = (proc != NULL ? proc->pid : 0); last_pid = (proc != NULL ? proc->pid : 0);
} else { } else
assert(!resuming); assert(!resuming);
prefix_off = 0; prefix_off = off;
} line_off += off;
/* Remember whether the next line should get prefixed regardless. */ /* Remember whether the next line should get prefixed regardless. */
print_pid = (count > 1 || proc == NULL); print_pid = (count > 1 || proc == NULL);

View File

@ -96,6 +96,7 @@ unsigned int proc_count(void);
const char *get_signal_name(int sig); const char *get_signal_name(int sig);
/* trace.c */ /* trace.c */
extern int timestamps;
extern int allnames; extern int allnames;
extern unsigned int verbose; extern unsigned int verbose;
extern unsigned int valuesonly; extern unsigned int valuesonly;

View File

@ -6,7 +6,7 @@
.Nd print process system calls and signals .Nd print process system calls and signals
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm .Nm
.Op Fl fgNsVv .Op Fl fgNstVv
.Op Fl o Ar file .Op Fl o Ar file
.Op Fl p Ar pid .Op Fl p Ar pid
.Op Ar command .Op Ar command
@ -58,6 +58,14 @@ preceded by a line showing the process's current stack trace.
For signals blocked by the target process, the stack trace may not be For signals blocked by the target process, the stack trace may not be
meaningful. meaningful.
Stack traces may not be supported on all platforms. Stack traces may not be supported on all platforms.
.It Fl t
Print timestamps.
By default, no timestamps are printed.
If this flag is given once, each line will be prefixed by the current time.
If this flag is given twice, the time will also include microseconds.
The printed timestamp corresponds to the time at which printing of the rest of
the line was initiated, which in the case of call resumption may not be the
same as the time that the system call was initiated.
.It Fl V .It Fl V
Print values only. Print values only.
If this flag is given once, numerical values will be printed instead of If this flag is given once, numerical values will be printed instead of

View File

@ -8,6 +8,7 @@
#include <err.h> #include <err.h>
/* Global variables, used only for a subset of the command line options. */ /* Global variables, used only for a subset of the command line options. */
int timestamps; /* 0 = none, 1 = time w/o usecs, 2 = time w/usecs */
int allnames; /* FALSE = structure field names, TRUE = all names */ int allnames; /* FALSE = structure field names, TRUE = all names */
unsigned int valuesonly; /* 0 = normal, 1 = no symbols, 2 = no structures */ unsigned int valuesonly; /* 0 = normal, 1 = no symbols, 2 = no structures */
unsigned int verbose; /* 0 = essentials, 1 = elaborate, 2 = everything */ unsigned int verbose; /* 0 = essentials, 1 = elaborate, 2 = everything */
@ -444,7 +445,7 @@ static void __dead
usage(void) usage(void)
{ {
(void)fprintf(stderr, "usage: %s [-fgNsVv] [-o file] [-p pid] " (void)fprintf(stderr, "usage: %s [-fgNstVv] [-o file] [-p pid] "
"[command]\n", getprogname()); "[command]\n", getprogname());
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
@ -471,11 +472,12 @@ main(int argc, char * argv[])
grouping = FALSE; grouping = FALSE;
output_file = NULL; output_file = NULL;
timestamps = 0;
allnames = FALSE; allnames = FALSE;
verbose = 0; verbose = 0;
valuesonly = 0; valuesonly = 0;
while ((c = getopt(argc, argv, "fgNsVvo:p:")) != -1) { while ((c = getopt(argc, argv, "fgNstVvo:p:")) != -1) {
switch (c) { switch (c) {
case 'f': case 'f':
follow_fork = TRUE; follow_fork = TRUE;
@ -489,6 +491,9 @@ main(int argc, char * argv[])
case 's': case 's':
show_stack = TRUE; show_stack = TRUE;
break; break;
case 't':
timestamps++;
break;
case 'V': case 'V':
valuesonly++; valuesonly++;
break; break;