xref: /minix3/minix/usr.bin/trace/output.c (revision 10a44c0ee2297b10de47c333d88880b5ccc8208e)
1521fa314SDavid van Moolenbroek 
2521fa314SDavid van Moolenbroek #include "inc.h"
3521fa314SDavid van Moolenbroek 
4521fa314SDavid van Moolenbroek #include <fcntl.h>
5521fa314SDavid van Moolenbroek #include <unistd.h>
6521fa314SDavid van Moolenbroek 
7521fa314SDavid van Moolenbroek /*
8521fa314SDavid van Moolenbroek  * The maximum number of bytes that may be buffered before writing the buffered
9521fa314SDavid van Moolenbroek  * output to the underlying file.  This is a performance optimization only.
10521fa314SDavid van Moolenbroek  * Writing more than this number of bytes at once will be handled correctly.
11521fa314SDavid van Moolenbroek  */
12521fa314SDavid van Moolenbroek #define OUTPUT_BUFSZ	512
13521fa314SDavid van Moolenbroek 
14521fa314SDavid van Moolenbroek static int out_fd;
15521fa314SDavid van Moolenbroek static char out_buf[OUTPUT_BUFSZ];
16521fa314SDavid van Moolenbroek static int out_len;
17521fa314SDavid van Moolenbroek static int out_err;
18521fa314SDavid van Moolenbroek 
19521fa314SDavid van Moolenbroek static pid_t last_pid; /* not a trace_proc pointer; it could become invalid! */
20521fa314SDavid van Moolenbroek static unsigned int line_off;
21521fa314SDavid van Moolenbroek static unsigned int prefix_off;
22521fa314SDavid van Moolenbroek static int print_pid;
23521fa314SDavid van Moolenbroek static int print_susp;
24521fa314SDavid van Moolenbroek static int add_space;
25521fa314SDavid van Moolenbroek 
26521fa314SDavid van Moolenbroek /*
27521fa314SDavid van Moolenbroek  * Initialize the output channel.  Called before any other output functions,
28521fa314SDavid van Moolenbroek  * but after a child process (to be traced) has already been spawned.  If the
29521fa314SDavid van Moolenbroek  * given file string is not NULL, it is the path to a file that is to be used
30521fa314SDavid van Moolenbroek  * to write output to.  If it is NULL, output is written to standard error.
31521fa314SDavid van Moolenbroek  */
32521fa314SDavid van Moolenbroek int
output_init(const char * file)33521fa314SDavid van Moolenbroek output_init(const char * file)
34521fa314SDavid van Moolenbroek {
35521fa314SDavid van Moolenbroek 
36521fa314SDavid van Moolenbroek 	/* Initialize state. */
37521fa314SDavid van Moolenbroek 	out_len = 0;
38521fa314SDavid van Moolenbroek 	out_err = FALSE;
39521fa314SDavid van Moolenbroek 
40521fa314SDavid van Moolenbroek 	last_pid = 0;
41521fa314SDavid van Moolenbroek 	line_off = 0;
42521fa314SDavid van Moolenbroek 	prefix_off = 0;
43521fa314SDavid van Moolenbroek 	print_pid = FALSE;
44521fa314SDavid van Moolenbroek 	print_susp = FALSE;
45521fa314SDavid van Moolenbroek 	add_space = FALSE;
46521fa314SDavid van Moolenbroek 
47521fa314SDavid van Moolenbroek 	/*
48521fa314SDavid van Moolenbroek 	 * Ignore signals resulting from writing to a closed pipe.  We can
49521fa314SDavid van Moolenbroek 	 * handle write errors properly ourselves.  Setting O_NOSIGPIPE is an
50521fa314SDavid van Moolenbroek 	 * alternative, but that would affect other processes writing to the
51521fa314SDavid van Moolenbroek 	 * same file object, even after we have terminated.
52521fa314SDavid van Moolenbroek 	 */
53521fa314SDavid van Moolenbroek 	signal(SIGPIPE, SIG_IGN);
54521fa314SDavid van Moolenbroek 
55521fa314SDavid van Moolenbroek 	/* Initialize the output file descriptor. */
56521fa314SDavid van Moolenbroek 	if (file == NULL) {
57521fa314SDavid van Moolenbroek 		/* No output file given?  Use standard error. */
58521fa314SDavid van Moolenbroek 		out_fd = STDERR_FILENO;
59521fa314SDavid van Moolenbroek 
60521fa314SDavid van Moolenbroek 		return 0;
61521fa314SDavid van Moolenbroek 	} else {
62521fa314SDavid van Moolenbroek 		/*
63521fa314SDavid van Moolenbroek 		 * Use a restrictive mask for the output file.  Traces may
64521fa314SDavid van Moolenbroek 		 * contain sensitive information (for security and otherwise),
65521fa314SDavid van Moolenbroek 		 * and the user might not always be careful about the location
66521fa314SDavid van Moolenbroek 		 * of the file.
67521fa314SDavid van Moolenbroek 		 */
68521fa314SDavid van Moolenbroek 		/* The file descriptor is not closed explicitly. */
69521fa314SDavid van Moolenbroek 		out_fd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_APPEND,
70521fa314SDavid van Moolenbroek 		    0600);
71521fa314SDavid van Moolenbroek 
72521fa314SDavid van Moolenbroek 		return (out_fd < 0) ? -1 : 0;
73521fa314SDavid van Moolenbroek 	}
74521fa314SDavid van Moolenbroek }
75521fa314SDavid van Moolenbroek 
76521fa314SDavid van Moolenbroek /*
77521fa314SDavid van Moolenbroek  * Write the given data to the given file descriptor, taking into account the
78521fa314SDavid van Moolenbroek  * possibility of partial writes and write errors.
79521fa314SDavid van Moolenbroek  */
80521fa314SDavid van Moolenbroek static void
write_fd(int fd,const char * buf,size_t len)81521fa314SDavid van Moolenbroek write_fd(int fd, const char *buf, size_t len)
82521fa314SDavid van Moolenbroek {
83521fa314SDavid van Moolenbroek 	ssize_t r;
84521fa314SDavid van Moolenbroek 
85521fa314SDavid van Moolenbroek 	/* If we got a write error before, do not try to write more. */
86521fa314SDavid van Moolenbroek 	if (out_err)
87521fa314SDavid van Moolenbroek 		return;
88521fa314SDavid van Moolenbroek 
89521fa314SDavid van Moolenbroek 	/* Write all output, in chunks if we have to. */
90521fa314SDavid van Moolenbroek 	while (len > 0) {
91521fa314SDavid van Moolenbroek 		r = write(fd, buf, len);
92521fa314SDavid van Moolenbroek 
93521fa314SDavid van Moolenbroek 		/*
94521fa314SDavid van Moolenbroek 		 * A write error (and that includes EOF) causes the program to
95521fa314SDavid van Moolenbroek 		 * terminate with an error code.  For obvious reasons we cannot
96521fa314SDavid van Moolenbroek 		 * print an error about this.  Do not even report to standard
97521fa314SDavid van Moolenbroek 		 * error if the output was redirected, because that may mess
98521fa314SDavid van Moolenbroek 		 * with the actual programs being run right now.
99521fa314SDavid van Moolenbroek 		 */
100521fa314SDavid van Moolenbroek 		if (r <= 0) {
101521fa314SDavid van Moolenbroek 			out_err = TRUE;
102521fa314SDavid van Moolenbroek 
103521fa314SDavid van Moolenbroek 			break;
104521fa314SDavid van Moolenbroek 		}
105521fa314SDavid van Moolenbroek 
106521fa314SDavid van Moolenbroek 		len -= r;
107521fa314SDavid van Moolenbroek 	}
108521fa314SDavid van Moolenbroek }
109521fa314SDavid van Moolenbroek 
110521fa314SDavid van Moolenbroek /*
111521fa314SDavid van Moolenbroek  * Return TRUE iff an output error occurred and the program should terminate.
112521fa314SDavid van Moolenbroek  */
113521fa314SDavid van Moolenbroek int
output_error(void)114521fa314SDavid van Moolenbroek output_error(void)
115521fa314SDavid van Moolenbroek {
116521fa314SDavid van Moolenbroek 
117521fa314SDavid van Moolenbroek 	return out_err;
118521fa314SDavid van Moolenbroek }
119521fa314SDavid van Moolenbroek 
120521fa314SDavid van Moolenbroek /*
121521fa314SDavid van Moolenbroek  * Print the given null-terminated string to the output channel.  Return the
122521fa314SDavid van Moolenbroek  * number of characters printed, for alignment purposes.  In the future, this
123521fa314SDavid van Moolenbroek  * number may end up being different from the number of bytes given to print,
124521fa314SDavid van Moolenbroek  * due to multibyte encoding or colors or whatnot.
125521fa314SDavid van Moolenbroek  */
126521fa314SDavid van Moolenbroek static unsigned int
output_write(const char * text)127521fa314SDavid van Moolenbroek output_write(const char * text)
128521fa314SDavid van Moolenbroek {
129521fa314SDavid van Moolenbroek 	size_t len;
130521fa314SDavid van Moolenbroek 
131521fa314SDavid van Moolenbroek 	len = strlen(text);
132521fa314SDavid van Moolenbroek 
133521fa314SDavid van Moolenbroek 	if (out_len + len > sizeof(out_buf)) {
134521fa314SDavid van Moolenbroek 		write_fd(out_fd, out_buf, out_len);
135521fa314SDavid van Moolenbroek 
136521fa314SDavid van Moolenbroek 		out_len = 0;
137521fa314SDavid van Moolenbroek 
138521fa314SDavid van Moolenbroek 		/* Write large buffers right away. */
139521fa314SDavid van Moolenbroek 		if (len > sizeof(out_buf)) {
140521fa314SDavid van Moolenbroek 			write_fd(out_fd, text, len);
141521fa314SDavid van Moolenbroek 
142521fa314SDavid van Moolenbroek 			return len;
143521fa314SDavid van Moolenbroek 		}
144521fa314SDavid van Moolenbroek 	}
145521fa314SDavid van Moolenbroek 
146521fa314SDavid van Moolenbroek 	memcpy(&out_buf[out_len], text, len);
147521fa314SDavid van Moolenbroek 
148521fa314SDavid van Moolenbroek 	out_len += len;
149521fa314SDavid van Moolenbroek 
150521fa314SDavid van Moolenbroek 	return len;
151521fa314SDavid van Moolenbroek }
152521fa314SDavid van Moolenbroek 
153521fa314SDavid van Moolenbroek /*
154521fa314SDavid van Moolenbroek  * Flush any pending output to the output channel.
155521fa314SDavid van Moolenbroek  */
156521fa314SDavid van Moolenbroek void
output_flush(void)157521fa314SDavid van Moolenbroek output_flush(void)
158521fa314SDavid van Moolenbroek {
159521fa314SDavid van Moolenbroek 
160521fa314SDavid van Moolenbroek 	if (out_len > 0) {
161521fa314SDavid van Moolenbroek 		write_fd(out_fd, out_buf, out_len);
162521fa314SDavid van Moolenbroek 
163521fa314SDavid van Moolenbroek 		out_len = 0;
164521fa314SDavid van Moolenbroek 	}
165521fa314SDavid van Moolenbroek }
166521fa314SDavid van Moolenbroek 
167521fa314SDavid van Moolenbroek /*
168*10a44c0eSDavid van Moolenbroek  * Print a prefix for a line of output.  Possibly print a timestamp first.
169*10a44c0eSDavid van Moolenbroek  * Then, if applicable, print a PID prefix for the given process, or an info
170*10a44c0eSDavid van Moolenbroek  * prefix if no process (NULL) is given.
171*10a44c0eSDavid van Moolenbroek  *
172*10a44c0eSDavid van Moolenbroek  * PIDs are relevant only when multiple processes are traced.  As long as there
173*10a44c0eSDavid van Moolenbroek  * are multiple processes, each line is prefixed with the PID of the process.
174*10a44c0eSDavid van Moolenbroek  * As soon as the number of processes has been reduced back to one, one more
175*10a44c0eSDavid van Moolenbroek  * line is prefixed with the PID of the remaining process (with a "'" instead
176*10a44c0eSDavid van Moolenbroek  * of a "|") to help the user identify which process remains.  In addition,
177*10a44c0eSDavid van Moolenbroek  * whenever a preempted call is about to be resumed, a "*" is printed instead
178*10a44c0eSDavid van Moolenbroek  * of a space, so as to show that it is a continuation of a previous line.  An
179*10a44c0eSDavid van Moolenbroek  * example of all these cases:
180521fa314SDavid van Moolenbroek  *
181521fa314SDavid van Moolenbroek  *   fork() = 3
182521fa314SDavid van Moolenbroek  *       3| Tracing test (pid 3)
183521fa314SDavid van Moolenbroek  *       3| fork() = 0
184521fa314SDavid van Moolenbroek  *       3| read(0, <..>
185521fa314SDavid van Moolenbroek  *       2| waitpid(-1, <..>
186521fa314SDavid van Moolenbroek  *    INFO| This is an example info line.
187521fa314SDavid van Moolenbroek  *       3|*read(0, "", 1024) = 0
188521fa314SDavid van Moolenbroek  *       3| exit(1)
189521fa314SDavid van Moolenbroek  *       3| Process exited normally with code 1
190521fa314SDavid van Moolenbroek  *       2'*waitpid(-1, W_EXITED(1), 0) = 3
191521fa314SDavid van Moolenbroek  *   exit(0)
192521fa314SDavid van Moolenbroek  *   Process exited normally with code 0
193521fa314SDavid van Moolenbroek  */
194521fa314SDavid van Moolenbroek static void
put_prefix(struct trace_proc * proc,int resuming)195521fa314SDavid van Moolenbroek put_prefix(struct trace_proc * proc, int resuming)
196521fa314SDavid van Moolenbroek {
197*10a44c0eSDavid van Moolenbroek 	struct timeval tv;
198*10a44c0eSDavid van Moolenbroek 	struct tm *tm;
199521fa314SDavid van Moolenbroek 	char prefix[32];
200*10a44c0eSDavid van Moolenbroek 	unsigned int off, count;
201521fa314SDavid van Moolenbroek 
202521fa314SDavid van Moolenbroek 	assert(line_off == 0);
203521fa314SDavid van Moolenbroek 
204*10a44c0eSDavid van Moolenbroek 	off = 0;
205*10a44c0eSDavid van Moolenbroek 
206*10a44c0eSDavid van Moolenbroek 	if (timestamps > 0) {
207*10a44c0eSDavid van Moolenbroek 		gettimeofday(&tv, NULL);
208*10a44c0eSDavid van Moolenbroek 
209*10a44c0eSDavid van Moolenbroek 		tm = gmtime(&tv.tv_sec);
210*10a44c0eSDavid van Moolenbroek 
211*10a44c0eSDavid van Moolenbroek 		off = strftime(prefix, sizeof(prefix), "%T", tm);
212*10a44c0eSDavid van Moolenbroek 
213*10a44c0eSDavid van Moolenbroek 		if (timestamps > 1)
214*10a44c0eSDavid van Moolenbroek 			off += snprintf(&prefix[off], sizeof(prefix) - off,
215*10a44c0eSDavid van Moolenbroek 			    ".%06u", tv.tv_usec);
216*10a44c0eSDavid van Moolenbroek 
217*10a44c0eSDavid van Moolenbroek 		assert(off < sizeof(prefix) - 2);
218*10a44c0eSDavid van Moolenbroek 		prefix[off++] = ' ';
219*10a44c0eSDavid van Moolenbroek 		prefix[off] = '\0';
220*10a44c0eSDavid van Moolenbroek 
221*10a44c0eSDavid van Moolenbroek 		off = output_write(prefix);
222*10a44c0eSDavid van Moolenbroek 	}
223*10a44c0eSDavid van Moolenbroek 
224521fa314SDavid van Moolenbroek 	count = proc_count();
225521fa314SDavid van Moolenbroek 
226521fa314SDavid van Moolenbroek 	/* TODO: add a command line option for always printing the pid. */
227521fa314SDavid van Moolenbroek 	if (print_pid || count > 1 || proc == NULL) {
228521fa314SDavid van Moolenbroek 		/*
229521fa314SDavid van Moolenbroek 		 * TODO: we currently rely on the highest PID having at most
230521fa314SDavid van Moolenbroek 		 * five digits, but this will eventually change.  There are
231521fa314SDavid van Moolenbroek 		 * several ways to deal with that, but none are great.
232521fa314SDavid van Moolenbroek 		 */
233521fa314SDavid van Moolenbroek 		if (proc == NULL)
234521fa314SDavid van Moolenbroek 			snprintf(prefix, sizeof(prefix), "%5s| ", "INFO");
235521fa314SDavid van Moolenbroek 		else
236521fa314SDavid van Moolenbroek 			snprintf(prefix, sizeof(prefix), "%5d%c%c",
237521fa314SDavid van Moolenbroek 			    proc->pid, (count > 1) ? '|' : '\'',
238521fa314SDavid van Moolenbroek 			    resuming ? '*' : ' ');
239521fa314SDavid van Moolenbroek 
240*10a44c0eSDavid van Moolenbroek 		off += output_write(prefix);
241521fa314SDavid van Moolenbroek 
242521fa314SDavid van Moolenbroek 		last_pid = (proc != NULL ? proc->pid : 0);
243*10a44c0eSDavid van Moolenbroek 	} else
244521fa314SDavid van Moolenbroek 		assert(!resuming);
245521fa314SDavid van Moolenbroek 
246*10a44c0eSDavid van Moolenbroek 	prefix_off = off;
247*10a44c0eSDavid van Moolenbroek 	line_off += off;
248521fa314SDavid van Moolenbroek 
249521fa314SDavid van Moolenbroek 	/* Remember whether the next line should get prefixed regardless. */
250521fa314SDavid van Moolenbroek 	print_pid = (count > 1 || proc == NULL);
251521fa314SDavid van Moolenbroek }
252521fa314SDavid van Moolenbroek 
253521fa314SDavid van Moolenbroek /*
254521fa314SDavid van Moolenbroek  * Add a string to the end of the text recording for the given process.
255521fa314SDavid van Moolenbroek  * This is used only to record the call-enter output of system calls.
256521fa314SDavid van Moolenbroek  */
257521fa314SDavid van Moolenbroek static void
record_add(struct trace_proc * proc,const char * text)258521fa314SDavid van Moolenbroek record_add(struct trace_proc * proc, const char * text)
259521fa314SDavid van Moolenbroek {
260521fa314SDavid van Moolenbroek 	size_t len;
261521fa314SDavid van Moolenbroek 
262521fa314SDavid van Moolenbroek 	assert(proc->recording);
263521fa314SDavid van Moolenbroek 
264521fa314SDavid van Moolenbroek 	/* If the recording buffer is already full, do not record more. */
265521fa314SDavid van Moolenbroek 	if (proc->outlen == sizeof(proc->outbuf))
266521fa314SDavid van Moolenbroek 		return;
267521fa314SDavid van Moolenbroek 
268521fa314SDavid van Moolenbroek 	len = strlen(text);
269521fa314SDavid van Moolenbroek 
270521fa314SDavid van Moolenbroek 	/* If nonempty, the recording buffer is always null terminated. */
271521fa314SDavid van Moolenbroek 	if (len < sizeof(proc->outbuf) - proc->outlen - 1) {
272521fa314SDavid van Moolenbroek 		strcpy(&proc->outbuf[proc->outlen], text);
273521fa314SDavid van Moolenbroek 
274521fa314SDavid van Moolenbroek 		proc->outlen += len;
275521fa314SDavid van Moolenbroek 	} else
276521fa314SDavid van Moolenbroek 		proc->outlen = sizeof(proc->outbuf); /* buffer exhausted */
277521fa314SDavid van Moolenbroek }
278521fa314SDavid van Moolenbroek 
279521fa314SDavid van Moolenbroek /*
280521fa314SDavid van Moolenbroek  * Start recording text for the given process.  Since this marks the start of
281521fa314SDavid van Moolenbroek  * a call, remember to print a preemption marker when the call gets preempted.
282521fa314SDavid van Moolenbroek  */
283521fa314SDavid van Moolenbroek void
record_start(struct trace_proc * proc)284521fa314SDavid van Moolenbroek record_start(struct trace_proc * proc)
285521fa314SDavid van Moolenbroek {
286521fa314SDavid van Moolenbroek 
287521fa314SDavid van Moolenbroek 	proc->recording = TRUE;
288521fa314SDavid van Moolenbroek 
289521fa314SDavid van Moolenbroek 	print_susp = TRUE;
290521fa314SDavid van Moolenbroek }
291521fa314SDavid van Moolenbroek 
292521fa314SDavid van Moolenbroek /*
293521fa314SDavid van Moolenbroek  * Stop recording text for the given process.
294521fa314SDavid van Moolenbroek  */
295521fa314SDavid van Moolenbroek void
record_stop(struct trace_proc * proc)296521fa314SDavid van Moolenbroek record_stop(struct trace_proc * proc)
297521fa314SDavid van Moolenbroek {
298521fa314SDavid van Moolenbroek 
299521fa314SDavid van Moolenbroek 	proc->recording = FALSE;
300521fa314SDavid van Moolenbroek }
301521fa314SDavid van Moolenbroek 
302521fa314SDavid van Moolenbroek /*
303521fa314SDavid van Moolenbroek  * Clear recorded text for the given process.  Since this also marks the end of
304521fa314SDavid van Moolenbroek  * the entire call, no longer print a supension marker before the next newline.
305521fa314SDavid van Moolenbroek  */
306521fa314SDavid van Moolenbroek void
record_clear(struct trace_proc * proc)307521fa314SDavid van Moolenbroek record_clear(struct trace_proc * proc)
308521fa314SDavid van Moolenbroek {
309521fa314SDavid van Moolenbroek 
310521fa314SDavid van Moolenbroek 	assert(!proc->recording);
311521fa314SDavid van Moolenbroek 	proc->outlen = 0;
312521fa314SDavid van Moolenbroek 
313521fa314SDavid van Moolenbroek 	if (proc->pid == last_pid)
314521fa314SDavid van Moolenbroek 		print_susp = FALSE;
315521fa314SDavid van Moolenbroek }
316521fa314SDavid van Moolenbroek 
317521fa314SDavid van Moolenbroek /*
318521fa314SDavid van Moolenbroek  * Replay the record for the given process on a new line, if the current line
319521fa314SDavid van Moolenbroek  * does not already have output for this process.  If it does, do nothing.
320521fa314SDavid van Moolenbroek  * If the process has no recorded output, just start a new line.  Return TRUE
321521fa314SDavid van Moolenbroek  * iff the caller must print its own replay text due to a recording overflow.
322521fa314SDavid van Moolenbroek  */
323521fa314SDavid van Moolenbroek int
record_replay(struct trace_proc * proc)324521fa314SDavid van Moolenbroek record_replay(struct trace_proc * proc)
325521fa314SDavid van Moolenbroek {
326521fa314SDavid van Moolenbroek 	int space;
327521fa314SDavid van Moolenbroek 
328521fa314SDavid van Moolenbroek 	assert(!proc->recording);
329521fa314SDavid van Moolenbroek 
330521fa314SDavid van Moolenbroek 	/*
331521fa314SDavid van Moolenbroek 	 * If there is output on the current line, and it is for the current
332521fa314SDavid van Moolenbroek 	 * process, we must assume that it is the original, recorded text, and
333521fa314SDavid van Moolenbroek 	 * thus, we should do nothing.  If output on the current line is for
334521fa314SDavid van Moolenbroek 	 * another process, we must force a new line before replaying.
335521fa314SDavid van Moolenbroek 	 */
336521fa314SDavid van Moolenbroek 	if (line_off > 0) {
337521fa314SDavid van Moolenbroek 		if (proc->pid == last_pid)
338521fa314SDavid van Moolenbroek 			return FALSE;
339521fa314SDavid van Moolenbroek 
340521fa314SDavid van Moolenbroek 		put_newline();
341521fa314SDavid van Moolenbroek 	}
342521fa314SDavid van Moolenbroek 
343521fa314SDavid van Moolenbroek 	/*
344521fa314SDavid van Moolenbroek 	 * If there is nothing to replay, do nothing further.  This case may
345521fa314SDavid van Moolenbroek 	 * occur when printing signals, in which case the caller still expects
346521fa314SDavid van Moolenbroek 	 * a new line to be started.  This line must not be prefixed with a
347521fa314SDavid van Moolenbroek 	 * "resuming" marker though--after all, nothing is being resumed here.
348521fa314SDavid van Moolenbroek 	 */
349521fa314SDavid van Moolenbroek 	if (proc->outlen == 0)
350521fa314SDavid van Moolenbroek 		return FALSE;
351521fa314SDavid van Moolenbroek 
352521fa314SDavid van Moolenbroek 	/*
353521fa314SDavid van Moolenbroek 	 * If there is text to replay, then this does mean we are in effect
354521fa314SDavid van Moolenbroek 	 * resuming the recorded call, even if it is just to print a signal.
355521fa314SDavid van Moolenbroek 	 * Thus, we must print a prefix that shows the call is being resumed.
356521fa314SDavid van Moolenbroek 	 * Similarly, unless the recording is cleared before a newline, we must
357521fa314SDavid van Moolenbroek 	 * suspend the line again, too.
358521fa314SDavid van Moolenbroek 	 */
359521fa314SDavid van Moolenbroek 	put_prefix(proc, TRUE /*resuming*/);
360521fa314SDavid van Moolenbroek 
361521fa314SDavid van Moolenbroek 	print_susp = TRUE;
362521fa314SDavid van Moolenbroek 
363521fa314SDavid van Moolenbroek 	/*
364521fa314SDavid van Moolenbroek 	 * If the recording buffer was exhausted during recording, the caller
365521fa314SDavid van Moolenbroek 	 * must generate the replay text instead.
366521fa314SDavid van Moolenbroek 	 */
367521fa314SDavid van Moolenbroek 	if (proc->outlen == sizeof(proc->outbuf))
368521fa314SDavid van Moolenbroek 		return TRUE;
369521fa314SDavid van Moolenbroek 
370521fa314SDavid van Moolenbroek 	/*
371521fa314SDavid van Moolenbroek 	 * Replay the recording.  If it ends with a space, turn it into a soft
372521fa314SDavid van Moolenbroek 	 * space, because the recording may be followed immediately by a
373521fa314SDavid van Moolenbroek 	 * newline; an example of this is the exit() exception.
374521fa314SDavid van Moolenbroek 	 */
375521fa314SDavid van Moolenbroek 	space = proc->outbuf[proc->outlen - 1] == ' ';
376521fa314SDavid van Moolenbroek 	if (space)
377521fa314SDavid van Moolenbroek 		proc->outbuf[proc->outlen - 1] = 0;
378521fa314SDavid van Moolenbroek 
379521fa314SDavid van Moolenbroek 	put_text(proc, proc->outbuf);
380521fa314SDavid van Moolenbroek 
381521fa314SDavid van Moolenbroek 	if (space) {
382521fa314SDavid van Moolenbroek 		put_space(proc);
383521fa314SDavid van Moolenbroek 
384521fa314SDavid van Moolenbroek 		/* Restore the space, in case another replay takes place. */
385521fa314SDavid van Moolenbroek 		proc->outbuf[proc->outlen - 1] = ' ';
386521fa314SDavid van Moolenbroek 	}
387521fa314SDavid van Moolenbroek 
388521fa314SDavid van Moolenbroek 	return FALSE;
389521fa314SDavid van Moolenbroek }
390521fa314SDavid van Moolenbroek 
391521fa314SDavid van Moolenbroek /*
392521fa314SDavid van Moolenbroek  * Start a new line, and adjust the local state accordingly.  If nothing has
393521fa314SDavid van Moolenbroek  * been printed on the current line yet, this function is a no-op.  Otherwise,
394521fa314SDavid van Moolenbroek  * the output so far may have to be marked as preempted with the "<..>"
395521fa314SDavid van Moolenbroek  * preemption marker.
396521fa314SDavid van Moolenbroek  */
397521fa314SDavid van Moolenbroek void
put_newline(void)398521fa314SDavid van Moolenbroek put_newline(void)
399521fa314SDavid van Moolenbroek {
400521fa314SDavid van Moolenbroek 
401521fa314SDavid van Moolenbroek 	if (line_off == 0)
402521fa314SDavid van Moolenbroek 		return;
403521fa314SDavid van Moolenbroek 
404521fa314SDavid van Moolenbroek 	if (print_susp) {
405521fa314SDavid van Moolenbroek 		if (add_space)
406521fa314SDavid van Moolenbroek 			(void)output_write(" ");
407521fa314SDavid van Moolenbroek 
408521fa314SDavid van Moolenbroek 		(void)output_write("<..>");
409521fa314SDavid van Moolenbroek 	}
410521fa314SDavid van Moolenbroek 
411521fa314SDavid van Moolenbroek #if DEBUG
412521fa314SDavid van Moolenbroek 	(void)output_write("|");
413521fa314SDavid van Moolenbroek #endif
414521fa314SDavid van Moolenbroek 
415521fa314SDavid van Moolenbroek 	(void)output_write("\n");
416521fa314SDavid van Moolenbroek 	output_flush();
417521fa314SDavid van Moolenbroek 
418521fa314SDavid van Moolenbroek 	line_off = 0;
419521fa314SDavid van Moolenbroek 	add_space = FALSE;
420521fa314SDavid van Moolenbroek 	print_susp = FALSE;
421521fa314SDavid van Moolenbroek 	last_pid = 0;
422521fa314SDavid van Moolenbroek }
423521fa314SDavid van Moolenbroek 
424521fa314SDavid van Moolenbroek /*
425521fa314SDavid van Moolenbroek  * Print a string as part of the output associated with a process.  If the
426521fa314SDavid van Moolenbroek  * current line contains output for another process, a newline will be printed
427521fa314SDavid van Moolenbroek  * first.  If the current line contains output for the same process, then the
428521fa314SDavid van Moolenbroek  * text will simply continue on the same line.  If the current line is empty,
429521fa314SDavid van Moolenbroek  * a process PID prefix may have to be printed first.  Either way, after this
430521fa314SDavid van Moolenbroek  * operation, the current line will contain text for the given process.  If
431521fa314SDavid van Moolenbroek  * requested, the text may also be recorded for the process, for later replay.
432521fa314SDavid van Moolenbroek  * As an exception, proc may be NULL when printing general information lines.
433521fa314SDavid van Moolenbroek  */
434521fa314SDavid van Moolenbroek void
put_text(struct trace_proc * proc,const char * text)435521fa314SDavid van Moolenbroek put_text(struct trace_proc * proc, const char * text)
436521fa314SDavid van Moolenbroek {
437521fa314SDavid van Moolenbroek 
438521fa314SDavid van Moolenbroek 	if (line_off > 0 && (proc == NULL || proc->pid != last_pid)) {
439521fa314SDavid van Moolenbroek 		/*
440521fa314SDavid van Moolenbroek 		 * The current line has not been terminated with a newline yet.
441521fa314SDavid van Moolenbroek 		 * Start a new line.  Note that this means that for lines not
442521fa314SDavid van Moolenbroek 		 * associated to a process, the whole line must be printed at
443521fa314SDavid van Moolenbroek 		 * once.  This can be fixed but is currently not an issue.
444521fa314SDavid van Moolenbroek 		 */
445521fa314SDavid van Moolenbroek 		put_newline();
446521fa314SDavid van Moolenbroek 	}
447521fa314SDavid van Moolenbroek 
448521fa314SDavid van Moolenbroek 	/* See if we must add a prefix at the start of the line. */
449521fa314SDavid van Moolenbroek 	if (line_off == 0)
450521fa314SDavid van Moolenbroek 		put_prefix(proc, FALSE /*resuming*/);
451521fa314SDavid van Moolenbroek 
452521fa314SDavid van Moolenbroek 	/* If needed, record the given text. */
453521fa314SDavid van Moolenbroek 	if (proc != NULL && proc->recording)
454521fa314SDavid van Moolenbroek 		record_add(proc, text);
455521fa314SDavid van Moolenbroek 
456521fa314SDavid van Moolenbroek 	/*
457521fa314SDavid van Moolenbroek 	 * If we delayed printing a space, print one now.  This is never part
458521fa314SDavid van Moolenbroek 	 * of text that must be saved.  In fact, we support these soft spaces
459521fa314SDavid van Moolenbroek 	 * for exactly one case; see put_space() for details.
460521fa314SDavid van Moolenbroek 	 */
461521fa314SDavid van Moolenbroek 	if (add_space) {
462521fa314SDavid van Moolenbroek 		line_off += output_write(" ");
463521fa314SDavid van Moolenbroek 
464521fa314SDavid van Moolenbroek 		add_space = FALSE;
465521fa314SDavid van Moolenbroek 	}
466521fa314SDavid van Moolenbroek 
467521fa314SDavid van Moolenbroek 	/* Finally, print the actual text. */
468521fa314SDavid van Moolenbroek 	line_off += output_write(text);
469521fa314SDavid van Moolenbroek 
470521fa314SDavid van Moolenbroek 	last_pid = (proc != NULL) ? proc->pid : 0;
471521fa314SDavid van Moolenbroek }
472521fa314SDavid van Moolenbroek 
473521fa314SDavid van Moolenbroek /*
474521fa314SDavid van Moolenbroek  * Add a space to the output for the given process, but only if and once more
475521fa314SDavid van Moolenbroek  * text is printed for the process afterwards.  The aim is to ensure that no
476521fa314SDavid van Moolenbroek  * lines ever end with a space, to prevent needless line wrapping on terminals.
477521fa314SDavid van Moolenbroek  * The space may have to be remembered for the current line (for preemption,
478521fa314SDavid van Moolenbroek  * which does not have a process pointer to work with) as well as recorded for
479521fa314SDavid van Moolenbroek  * later replay, if recording is enabled.  Consider the following example:
480521fa314SDavid van Moolenbroek  *
481521fa314SDavid van Moolenbroek  * [A]   3| execve(..) <..>
482521fa314SDavid van Moolenbroek  *       2| getpid(0) = 2 (ppid=1)
483521fa314SDavid van Moolenbroek  * [B]   3| execve(..) = -1 [ENOENT]
484521fa314SDavid van Moolenbroek  * [A]   3| exit(1) <..>
485521fa314SDavid van Moolenbroek  *       2| getpid(0) = 2 (ppid=1)
486521fa314SDavid van Moolenbroek  *       3| exit(1)
487521fa314SDavid van Moolenbroek  *       3| Process exited normally with code 1
488521fa314SDavid van Moolenbroek  *
489521fa314SDavid van Moolenbroek  * On the [A] lines, the space between the call's closing parenthesis and the
490521fa314SDavid van Moolenbroek  * "<..>" preemption marker is the result of add_space being set to TRUE; on
491521fa314SDavid van Moolenbroek  * the [B] line, the space between the closing parenthesis and the equals sign
492521fa314SDavid van Moolenbroek  * is the result of the space being recorded.
493521fa314SDavid van Moolenbroek  */
494521fa314SDavid van Moolenbroek void
put_space(struct trace_proc * proc)495521fa314SDavid van Moolenbroek put_space(struct trace_proc * proc)
496521fa314SDavid van Moolenbroek {
497521fa314SDavid van Moolenbroek 
498521fa314SDavid van Moolenbroek 	/* This call must only be used after output for the given process. */
499521fa314SDavid van Moolenbroek 	assert(last_pid == proc->pid);
500521fa314SDavid van Moolenbroek 
501521fa314SDavid van Moolenbroek 	/* In case the call does not get preempted. */
502521fa314SDavid van Moolenbroek 	add_space = TRUE;
503521fa314SDavid van Moolenbroek 
504521fa314SDavid van Moolenbroek 	/* In case the call does get preempted. */
505521fa314SDavid van Moolenbroek 	if (proc->recording)
506521fa314SDavid van Moolenbroek 		record_add(proc, " ");
507521fa314SDavid van Moolenbroek }
508521fa314SDavid van Moolenbroek 
509521fa314SDavid van Moolenbroek /*
510521fa314SDavid van Moolenbroek  * Indent the remainders of the text on the line for this process, such that
511521fa314SDavid van Moolenbroek  * similar remainders are similarly aligned.  In particular, the remainder is
512521fa314SDavid van Moolenbroek  * the equals sign of a call, and everything after it.  Of course, alignment
513521fa314SDavid van Moolenbroek  * can only be used if the call has not already printed beyond the alignment
514521fa314SDavid van Moolenbroek  * position.  Also, the prefix must not be counted toward the alignment, as it
515521fa314SDavid van Moolenbroek  * is possible that a line without prefix may be preempted and later continued
516521fa314SDavid van Moolenbroek  * with prefix.  All things considered, the result would look like this:
517521fa314SDavid van Moolenbroek  *
518521fa314SDavid van Moolenbroek  *   getuid()                      = 1 (euid=1)
519521fa314SDavid van Moolenbroek  *   setuid(0)                     = -1 [EPERM]
520521fa314SDavid van Moolenbroek  *   write(2, "Permission denied\n", 18) = 18
521521fa314SDavid van Moolenbroek  *   fork()                        = 3
522521fa314SDavid van Moolenbroek  *       3| Tracing test (pid 3)
523521fa314SDavid van Moolenbroek  *       3| fork()                        = 0
524521fa314SDavid van Moolenbroek  *       3| exit(0)
525521fa314SDavid van Moolenbroek  *       3| Process exited normally with code 0
526521fa314SDavid van Moolenbroek  *       2' waitpid(-1, W_EXITED(0), 0)   = 3
527521fa314SDavid van Moolenbroek  *
528521fa314SDavid van Moolenbroek  */
put_align(struct trace_proc * __unused proc)529521fa314SDavid van Moolenbroek void put_align(struct trace_proc * __unused proc)
530521fa314SDavid van Moolenbroek {
531521fa314SDavid van Moolenbroek 
532521fa314SDavid van Moolenbroek 	/*
533521fa314SDavid van Moolenbroek 	 * TODO: add actual support for this.  The following code works,
534521fa314SDavid van Moolenbroek 	 * although not so efficiently.  The difficulty is the default
535521fa314SDavid van Moolenbroek 	 * configuration and corresponding options.
536521fa314SDavid van Moolenbroek 
537521fa314SDavid van Moolenbroek 	while (line_off - prefix_off < 20)
538521fa314SDavid van Moolenbroek 		put_text(proc, " ");
539521fa314SDavid van Moolenbroek 
540521fa314SDavid van Moolenbroek 	 */
541521fa314SDavid van Moolenbroek }
542