xref: /minix3/minix/usr.bin/trace/output.c (revision 521fa314e2aaec3c192c15f2aaa4c677a544e62a)
1*521fa314SDavid van Moolenbroek 
2*521fa314SDavid van Moolenbroek #include "inc.h"
3*521fa314SDavid van Moolenbroek 
4*521fa314SDavid van Moolenbroek #include <fcntl.h>
5*521fa314SDavid van Moolenbroek #include <unistd.h>
6*521fa314SDavid van Moolenbroek 
7*521fa314SDavid van Moolenbroek /*
8*521fa314SDavid van Moolenbroek  * The maximum number of bytes that may be buffered before writing the buffered
9*521fa314SDavid van Moolenbroek  * output to the underlying file.  This is a performance optimization only.
10*521fa314SDavid van Moolenbroek  * Writing more than this number of bytes at once will be handled correctly.
11*521fa314SDavid van Moolenbroek  */
12*521fa314SDavid van Moolenbroek #define OUTPUT_BUFSZ	512
13*521fa314SDavid van Moolenbroek 
14*521fa314SDavid van Moolenbroek static int out_fd;
15*521fa314SDavid van Moolenbroek static char out_buf[OUTPUT_BUFSZ];
16*521fa314SDavid van Moolenbroek static int out_len;
17*521fa314SDavid van Moolenbroek static int out_err;
18*521fa314SDavid van Moolenbroek 
19*521fa314SDavid van Moolenbroek static pid_t last_pid; /* not a trace_proc pointer; it could become invalid! */
20*521fa314SDavid van Moolenbroek static unsigned int line_off;
21*521fa314SDavid van Moolenbroek static unsigned int prefix_off;
22*521fa314SDavid van Moolenbroek static int print_pid;
23*521fa314SDavid van Moolenbroek static int print_susp;
24*521fa314SDavid van Moolenbroek static int add_space;
25*521fa314SDavid van Moolenbroek 
26*521fa314SDavid van Moolenbroek /*
27*521fa314SDavid van Moolenbroek  * Initialize the output channel.  Called before any other output functions,
28*521fa314SDavid van Moolenbroek  * but after a child process (to be traced) has already been spawned.  If the
29*521fa314SDavid van Moolenbroek  * given file string is not NULL, it is the path to a file that is to be used
30*521fa314SDavid van Moolenbroek  * to write output to.  If it is NULL, output is written to standard error.
31*521fa314SDavid van Moolenbroek  */
32*521fa314SDavid van Moolenbroek int
33*521fa314SDavid van Moolenbroek output_init(const char * file)
34*521fa314SDavid van Moolenbroek {
35*521fa314SDavid van Moolenbroek 
36*521fa314SDavid van Moolenbroek 	/* Initialize state. */
37*521fa314SDavid van Moolenbroek 	out_len = 0;
38*521fa314SDavid van Moolenbroek 	out_err = FALSE;
39*521fa314SDavid van Moolenbroek 
40*521fa314SDavid van Moolenbroek 	last_pid = 0;
41*521fa314SDavid van Moolenbroek 	line_off = 0;
42*521fa314SDavid van Moolenbroek 	prefix_off = 0;
43*521fa314SDavid van Moolenbroek 	print_pid = FALSE;
44*521fa314SDavid van Moolenbroek 	print_susp = FALSE;
45*521fa314SDavid van Moolenbroek 	add_space = FALSE;
46*521fa314SDavid van Moolenbroek 
47*521fa314SDavid van Moolenbroek 	/*
48*521fa314SDavid van Moolenbroek 	 * Ignore signals resulting from writing to a closed pipe.  We can
49*521fa314SDavid van Moolenbroek 	 * handle write errors properly ourselves.  Setting O_NOSIGPIPE is an
50*521fa314SDavid van Moolenbroek 	 * alternative, but that would affect other processes writing to the
51*521fa314SDavid van Moolenbroek 	 * same file object, even after we have terminated.
52*521fa314SDavid van Moolenbroek 	 */
53*521fa314SDavid van Moolenbroek 	signal(SIGPIPE, SIG_IGN);
54*521fa314SDavid van Moolenbroek 
55*521fa314SDavid van Moolenbroek 	/* Initialize the output file descriptor. */
56*521fa314SDavid van Moolenbroek 	if (file == NULL) {
57*521fa314SDavid van Moolenbroek 		/* No output file given?  Use standard error. */
58*521fa314SDavid van Moolenbroek 		out_fd = STDERR_FILENO;
59*521fa314SDavid van Moolenbroek 
60*521fa314SDavid van Moolenbroek 		return 0;
61*521fa314SDavid van Moolenbroek 	} else {
62*521fa314SDavid van Moolenbroek 		/*
63*521fa314SDavid van Moolenbroek 		 * Use a restrictive mask for the output file.  Traces may
64*521fa314SDavid van Moolenbroek 		 * contain sensitive information (for security and otherwise),
65*521fa314SDavid van Moolenbroek 		 * and the user might not always be careful about the location
66*521fa314SDavid van Moolenbroek 		 * of the file.
67*521fa314SDavid van Moolenbroek 		 */
68*521fa314SDavid van Moolenbroek 		/* The file descriptor is not closed explicitly. */
69*521fa314SDavid van Moolenbroek 		out_fd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_APPEND,
70*521fa314SDavid van Moolenbroek 		    0600);
71*521fa314SDavid van Moolenbroek 
72*521fa314SDavid van Moolenbroek 		return (out_fd < 0) ? -1 : 0;
73*521fa314SDavid van Moolenbroek 	}
74*521fa314SDavid van Moolenbroek }
75*521fa314SDavid van Moolenbroek 
76*521fa314SDavid van Moolenbroek /*
77*521fa314SDavid van Moolenbroek  * Write the given data to the given file descriptor, taking into account the
78*521fa314SDavid van Moolenbroek  * possibility of partial writes and write errors.
79*521fa314SDavid van Moolenbroek  */
80*521fa314SDavid van Moolenbroek static void
81*521fa314SDavid van Moolenbroek write_fd(int fd, const char *buf, size_t len)
82*521fa314SDavid van Moolenbroek {
83*521fa314SDavid van Moolenbroek 	ssize_t r;
84*521fa314SDavid van Moolenbroek 
85*521fa314SDavid van Moolenbroek 	/* If we got a write error before, do not try to write more. */
86*521fa314SDavid van Moolenbroek 	if (out_err)
87*521fa314SDavid van Moolenbroek 		return;
88*521fa314SDavid van Moolenbroek 
89*521fa314SDavid van Moolenbroek 	/* Write all output, in chunks if we have to. */
90*521fa314SDavid van Moolenbroek 	while (len > 0) {
91*521fa314SDavid van Moolenbroek 		r = write(fd, buf, len);
92*521fa314SDavid van Moolenbroek 
93*521fa314SDavid van Moolenbroek 		/*
94*521fa314SDavid van Moolenbroek 		 * A write error (and that includes EOF) causes the program to
95*521fa314SDavid van Moolenbroek 		 * terminate with an error code.  For obvious reasons we cannot
96*521fa314SDavid van Moolenbroek 		 * print an error about this.  Do not even report to standard
97*521fa314SDavid van Moolenbroek 		 * error if the output was redirected, because that may mess
98*521fa314SDavid van Moolenbroek 		 * with the actual programs being run right now.
99*521fa314SDavid van Moolenbroek 		 */
100*521fa314SDavid van Moolenbroek 		if (r <= 0) {
101*521fa314SDavid van Moolenbroek 			out_err = TRUE;
102*521fa314SDavid van Moolenbroek 
103*521fa314SDavid van Moolenbroek 			break;
104*521fa314SDavid van Moolenbroek 		}
105*521fa314SDavid van Moolenbroek 
106*521fa314SDavid van Moolenbroek 		len -= r;
107*521fa314SDavid van Moolenbroek 	}
108*521fa314SDavid van Moolenbroek }
109*521fa314SDavid van Moolenbroek 
110*521fa314SDavid van Moolenbroek /*
111*521fa314SDavid van Moolenbroek  * Return TRUE iff an output error occurred and the program should terminate.
112*521fa314SDavid van Moolenbroek  */
113*521fa314SDavid van Moolenbroek int
114*521fa314SDavid van Moolenbroek output_error(void)
115*521fa314SDavid van Moolenbroek {
116*521fa314SDavid van Moolenbroek 
117*521fa314SDavid van Moolenbroek 	return out_err;
118*521fa314SDavid van Moolenbroek }
119*521fa314SDavid van Moolenbroek 
120*521fa314SDavid van Moolenbroek /*
121*521fa314SDavid van Moolenbroek  * Print the given null-terminated string to the output channel.  Return the
122*521fa314SDavid van Moolenbroek  * number of characters printed, for alignment purposes.  In the future, this
123*521fa314SDavid van Moolenbroek  * number may end up being different from the number of bytes given to print,
124*521fa314SDavid van Moolenbroek  * due to multibyte encoding or colors or whatnot.
125*521fa314SDavid van Moolenbroek  */
126*521fa314SDavid van Moolenbroek static unsigned int
127*521fa314SDavid van Moolenbroek output_write(const char * text)
128*521fa314SDavid van Moolenbroek {
129*521fa314SDavid van Moolenbroek 	size_t len;
130*521fa314SDavid van Moolenbroek 
131*521fa314SDavid van Moolenbroek 	len = strlen(text);
132*521fa314SDavid van Moolenbroek 
133*521fa314SDavid van Moolenbroek 	if (out_len + len > sizeof(out_buf)) {
134*521fa314SDavid van Moolenbroek 		write_fd(out_fd, out_buf, out_len);
135*521fa314SDavid van Moolenbroek 
136*521fa314SDavid van Moolenbroek 		out_len = 0;
137*521fa314SDavid van Moolenbroek 
138*521fa314SDavid van Moolenbroek 		/* Write large buffers right away. */
139*521fa314SDavid van Moolenbroek 		if (len > sizeof(out_buf)) {
140*521fa314SDavid van Moolenbroek 			write_fd(out_fd, text, len);
141*521fa314SDavid van Moolenbroek 
142*521fa314SDavid van Moolenbroek 			return len;
143*521fa314SDavid van Moolenbroek 		}
144*521fa314SDavid van Moolenbroek 	}
145*521fa314SDavid van Moolenbroek 
146*521fa314SDavid van Moolenbroek 	memcpy(&out_buf[out_len], text, len);
147*521fa314SDavid van Moolenbroek 
148*521fa314SDavid van Moolenbroek 	out_len += len;
149*521fa314SDavid van Moolenbroek 
150*521fa314SDavid van Moolenbroek 	return len;
151*521fa314SDavid van Moolenbroek }
152*521fa314SDavid van Moolenbroek 
153*521fa314SDavid van Moolenbroek /*
154*521fa314SDavid van Moolenbroek  * Flush any pending output to the output channel.
155*521fa314SDavid van Moolenbroek  */
156*521fa314SDavid van Moolenbroek void
157*521fa314SDavid van Moolenbroek output_flush(void)
158*521fa314SDavid van Moolenbroek {
159*521fa314SDavid van Moolenbroek 
160*521fa314SDavid van Moolenbroek 	if (out_len > 0) {
161*521fa314SDavid van Moolenbroek 		write_fd(out_fd, out_buf, out_len);
162*521fa314SDavid van Moolenbroek 
163*521fa314SDavid van Moolenbroek 		out_len = 0;
164*521fa314SDavid van Moolenbroek 	}
165*521fa314SDavid van Moolenbroek }
166*521fa314SDavid van Moolenbroek 
167*521fa314SDavid van Moolenbroek /*
168*521fa314SDavid van Moolenbroek  * Print a PID prefix for the given process, or an info prefix if no process
169*521fa314SDavid van Moolenbroek  * (NULL) is given.  Prefixes are only relevant when multiple processes are
170*521fa314SDavid van Moolenbroek  * traced.  As long as there are multiple processes, each line is prefixed with
171*521fa314SDavid van Moolenbroek  * the PID of the process.  As soon as the number of processes has been reduced
172*521fa314SDavid van Moolenbroek  * back to one, one more line is prefixed with the PID of the remaining process
173*521fa314SDavid van Moolenbroek  * (with a "'" instead of a "|") to help the user identify which process is
174*521fa314SDavid van Moolenbroek  * left.  In addition, whenever a preempted call is about to be resumed, a "*"
175*521fa314SDavid van Moolenbroek  * is printed instead of a space, so as to show that it is a continuation of a
176*521fa314SDavid van Moolenbroek  * previous line.  An example of all these cases:
177*521fa314SDavid van Moolenbroek  *
178*521fa314SDavid van Moolenbroek  *   fork() = 3
179*521fa314SDavid van Moolenbroek  *       3| Tracing test (pid 3)
180*521fa314SDavid van Moolenbroek  *       3| fork() = 0
181*521fa314SDavid van Moolenbroek  *       3| read(0, <..>
182*521fa314SDavid van Moolenbroek  *       2| waitpid(-1, <..>
183*521fa314SDavid van Moolenbroek  *    INFO| This is an example info line.
184*521fa314SDavid van Moolenbroek  *       3|*read(0, "", 1024) = 0
185*521fa314SDavid van Moolenbroek  *       3| exit(1)
186*521fa314SDavid van Moolenbroek  *       3| Process exited normally with code 1
187*521fa314SDavid van Moolenbroek  *       2'*waitpid(-1, W_EXITED(1), 0) = 3
188*521fa314SDavid van Moolenbroek  *   exit(0)
189*521fa314SDavid van Moolenbroek  *   Process exited normally with code 0
190*521fa314SDavid van Moolenbroek  */
191*521fa314SDavid van Moolenbroek static void
192*521fa314SDavid van Moolenbroek put_prefix(struct trace_proc * proc, int resuming)
193*521fa314SDavid van Moolenbroek {
194*521fa314SDavid van Moolenbroek 	char prefix[32];
195*521fa314SDavid van Moolenbroek 	unsigned int count;
196*521fa314SDavid van Moolenbroek 
197*521fa314SDavid van Moolenbroek 	assert(line_off == 0);
198*521fa314SDavid van Moolenbroek 
199*521fa314SDavid van Moolenbroek 	count = proc_count();
200*521fa314SDavid van Moolenbroek 
201*521fa314SDavid van Moolenbroek 	/* TODO: add a command line option for always printing the pid. */
202*521fa314SDavid van Moolenbroek 	if (print_pid || count > 1 || proc == NULL) {
203*521fa314SDavid van Moolenbroek 		/*
204*521fa314SDavid van Moolenbroek 		 * TODO: we currently rely on the highest PID having at most
205*521fa314SDavid van Moolenbroek 		 * five digits, but this will eventually change.  There are
206*521fa314SDavid van Moolenbroek 		 * several ways to deal with that, but none are great.
207*521fa314SDavid van Moolenbroek 		 */
208*521fa314SDavid van Moolenbroek 		if (proc == NULL)
209*521fa314SDavid van Moolenbroek 			snprintf(prefix, sizeof(prefix), "%5s| ", "INFO");
210*521fa314SDavid van Moolenbroek 		else
211*521fa314SDavid van Moolenbroek 			snprintf(prefix, sizeof(prefix), "%5d%c%c",
212*521fa314SDavid van Moolenbroek 			    proc->pid, (count > 1) ? '|' : '\'',
213*521fa314SDavid van Moolenbroek 			    resuming ? '*' : ' ');
214*521fa314SDavid van Moolenbroek 
215*521fa314SDavid van Moolenbroek 		prefix_off = line_off = output_write(prefix);
216*521fa314SDavid van Moolenbroek 
217*521fa314SDavid van Moolenbroek 		last_pid = (proc != NULL ? proc->pid : 0);
218*521fa314SDavid van Moolenbroek 	} else {
219*521fa314SDavid van Moolenbroek 		assert(!resuming);
220*521fa314SDavid van Moolenbroek 
221*521fa314SDavid van Moolenbroek 		prefix_off = 0;
222*521fa314SDavid van Moolenbroek 	}
223*521fa314SDavid van Moolenbroek 
224*521fa314SDavid van Moolenbroek 	/* Remember whether the next line should get prefixed regardless. */
225*521fa314SDavid van Moolenbroek 	print_pid = (count > 1 || proc == NULL);
226*521fa314SDavid van Moolenbroek }
227*521fa314SDavid van Moolenbroek 
228*521fa314SDavid van Moolenbroek /*
229*521fa314SDavid van Moolenbroek  * Add a string to the end of the text recording for the given process.
230*521fa314SDavid van Moolenbroek  * This is used only to record the call-enter output of system calls.
231*521fa314SDavid van Moolenbroek  */
232*521fa314SDavid van Moolenbroek static void
233*521fa314SDavid van Moolenbroek record_add(struct trace_proc * proc, const char * text)
234*521fa314SDavid van Moolenbroek {
235*521fa314SDavid van Moolenbroek 	size_t len;
236*521fa314SDavid van Moolenbroek 
237*521fa314SDavid van Moolenbroek 	assert(proc->recording);
238*521fa314SDavid van Moolenbroek 
239*521fa314SDavid van Moolenbroek 	/* If the recording buffer is already full, do not record more. */
240*521fa314SDavid van Moolenbroek 	if (proc->outlen == sizeof(proc->outbuf))
241*521fa314SDavid van Moolenbroek 		return;
242*521fa314SDavid van Moolenbroek 
243*521fa314SDavid van Moolenbroek 	len = strlen(text);
244*521fa314SDavid van Moolenbroek 
245*521fa314SDavid van Moolenbroek 	/* If nonempty, the recording buffer is always null terminated. */
246*521fa314SDavid van Moolenbroek 	if (len < sizeof(proc->outbuf) - proc->outlen - 1) {
247*521fa314SDavid van Moolenbroek 		strcpy(&proc->outbuf[proc->outlen], text);
248*521fa314SDavid van Moolenbroek 
249*521fa314SDavid van Moolenbroek 		proc->outlen += len;
250*521fa314SDavid van Moolenbroek 	} else
251*521fa314SDavid van Moolenbroek 		proc->outlen = sizeof(proc->outbuf); /* buffer exhausted */
252*521fa314SDavid van Moolenbroek }
253*521fa314SDavid van Moolenbroek 
254*521fa314SDavid van Moolenbroek /*
255*521fa314SDavid van Moolenbroek  * Start recording text for the given process.  Since this marks the start of
256*521fa314SDavid van Moolenbroek  * a call, remember to print a preemption marker when the call gets preempted.
257*521fa314SDavid van Moolenbroek  */
258*521fa314SDavid van Moolenbroek void
259*521fa314SDavid van Moolenbroek record_start(struct trace_proc * proc)
260*521fa314SDavid van Moolenbroek {
261*521fa314SDavid van Moolenbroek 
262*521fa314SDavid van Moolenbroek 	proc->recording = TRUE;
263*521fa314SDavid van Moolenbroek 
264*521fa314SDavid van Moolenbroek 	print_susp = TRUE;
265*521fa314SDavid van Moolenbroek }
266*521fa314SDavid van Moolenbroek 
267*521fa314SDavid van Moolenbroek /*
268*521fa314SDavid van Moolenbroek  * Stop recording text for the given process.
269*521fa314SDavid van Moolenbroek  */
270*521fa314SDavid van Moolenbroek void
271*521fa314SDavid van Moolenbroek record_stop(struct trace_proc * proc)
272*521fa314SDavid van Moolenbroek {
273*521fa314SDavid van Moolenbroek 
274*521fa314SDavid van Moolenbroek 	proc->recording = FALSE;
275*521fa314SDavid van Moolenbroek }
276*521fa314SDavid van Moolenbroek 
277*521fa314SDavid van Moolenbroek /*
278*521fa314SDavid van Moolenbroek  * Clear recorded text for the given process.  Since this also marks the end of
279*521fa314SDavid van Moolenbroek  * the entire call, no longer print a supension marker before the next newline.
280*521fa314SDavid van Moolenbroek  */
281*521fa314SDavid van Moolenbroek void
282*521fa314SDavid van Moolenbroek record_clear(struct trace_proc * proc)
283*521fa314SDavid van Moolenbroek {
284*521fa314SDavid van Moolenbroek 
285*521fa314SDavid van Moolenbroek 	assert(!proc->recording);
286*521fa314SDavid van Moolenbroek 	proc->outlen = 0;
287*521fa314SDavid van Moolenbroek 
288*521fa314SDavid van Moolenbroek 	if (proc->pid == last_pid)
289*521fa314SDavid van Moolenbroek 		print_susp = FALSE;
290*521fa314SDavid van Moolenbroek }
291*521fa314SDavid van Moolenbroek 
292*521fa314SDavid van Moolenbroek /*
293*521fa314SDavid van Moolenbroek  * Replay the record for the given process on a new line, if the current line
294*521fa314SDavid van Moolenbroek  * does not already have output for this process.  If it does, do nothing.
295*521fa314SDavid van Moolenbroek  * If the process has no recorded output, just start a new line.  Return TRUE
296*521fa314SDavid van Moolenbroek  * iff the caller must print its own replay text due to a recording overflow.
297*521fa314SDavid van Moolenbroek  */
298*521fa314SDavid van Moolenbroek int
299*521fa314SDavid van Moolenbroek record_replay(struct trace_proc * proc)
300*521fa314SDavid van Moolenbroek {
301*521fa314SDavid van Moolenbroek 	int space;
302*521fa314SDavid van Moolenbroek 
303*521fa314SDavid van Moolenbroek 	assert(!proc->recording);
304*521fa314SDavid van Moolenbroek 
305*521fa314SDavid van Moolenbroek 	/*
306*521fa314SDavid van Moolenbroek 	 * If there is output on the current line, and it is for the current
307*521fa314SDavid van Moolenbroek 	 * process, we must assume that it is the original, recorded text, and
308*521fa314SDavid van Moolenbroek 	 * thus, we should do nothing.  If output on the current line is for
309*521fa314SDavid van Moolenbroek 	 * another process, we must force a new line before replaying.
310*521fa314SDavid van Moolenbroek 	 */
311*521fa314SDavid van Moolenbroek 	if (line_off > 0) {
312*521fa314SDavid van Moolenbroek 		if (proc->pid == last_pid)
313*521fa314SDavid van Moolenbroek 			return FALSE;
314*521fa314SDavid van Moolenbroek 
315*521fa314SDavid van Moolenbroek 		put_newline();
316*521fa314SDavid van Moolenbroek 	}
317*521fa314SDavid van Moolenbroek 
318*521fa314SDavid van Moolenbroek 	/*
319*521fa314SDavid van Moolenbroek 	 * If there is nothing to replay, do nothing further.  This case may
320*521fa314SDavid van Moolenbroek 	 * occur when printing signals, in which case the caller still expects
321*521fa314SDavid van Moolenbroek 	 * a new line to be started.  This line must not be prefixed with a
322*521fa314SDavid van Moolenbroek 	 * "resuming" marker though--after all, nothing is being resumed here.
323*521fa314SDavid van Moolenbroek 	 */
324*521fa314SDavid van Moolenbroek 	if (proc->outlen == 0)
325*521fa314SDavid van Moolenbroek 		return FALSE;
326*521fa314SDavid van Moolenbroek 
327*521fa314SDavid van Moolenbroek 	/*
328*521fa314SDavid van Moolenbroek 	 * If there is text to replay, then this does mean we are in effect
329*521fa314SDavid van Moolenbroek 	 * resuming the recorded call, even if it is just to print a signal.
330*521fa314SDavid van Moolenbroek 	 * Thus, we must print a prefix that shows the call is being resumed.
331*521fa314SDavid van Moolenbroek 	 * Similarly, unless the recording is cleared before a newline, we must
332*521fa314SDavid van Moolenbroek 	 * suspend the line again, too.
333*521fa314SDavid van Moolenbroek 	 */
334*521fa314SDavid van Moolenbroek 	put_prefix(proc, TRUE /*resuming*/);
335*521fa314SDavid van Moolenbroek 
336*521fa314SDavid van Moolenbroek 	print_susp = TRUE;
337*521fa314SDavid van Moolenbroek 
338*521fa314SDavid van Moolenbroek 	/*
339*521fa314SDavid van Moolenbroek 	 * If the recording buffer was exhausted during recording, the caller
340*521fa314SDavid van Moolenbroek 	 * must generate the replay text instead.
341*521fa314SDavid van Moolenbroek 	 */
342*521fa314SDavid van Moolenbroek 	if (proc->outlen == sizeof(proc->outbuf))
343*521fa314SDavid van Moolenbroek 		return TRUE;
344*521fa314SDavid van Moolenbroek 
345*521fa314SDavid van Moolenbroek 	/*
346*521fa314SDavid van Moolenbroek 	 * Replay the recording.  If it ends with a space, turn it into a soft
347*521fa314SDavid van Moolenbroek 	 * space, because the recording may be followed immediately by a
348*521fa314SDavid van Moolenbroek 	 * newline; an example of this is the exit() exception.
349*521fa314SDavid van Moolenbroek 	 */
350*521fa314SDavid van Moolenbroek 	space = proc->outbuf[proc->outlen - 1] == ' ';
351*521fa314SDavid van Moolenbroek 	if (space)
352*521fa314SDavid van Moolenbroek 		proc->outbuf[proc->outlen - 1] = 0;
353*521fa314SDavid van Moolenbroek 
354*521fa314SDavid van Moolenbroek 	put_text(proc, proc->outbuf);
355*521fa314SDavid van Moolenbroek 
356*521fa314SDavid van Moolenbroek 	if (space) {
357*521fa314SDavid van Moolenbroek 		put_space(proc);
358*521fa314SDavid van Moolenbroek 
359*521fa314SDavid van Moolenbroek 		/* Restore the space, in case another replay takes place. */
360*521fa314SDavid van Moolenbroek 		proc->outbuf[proc->outlen - 1] = ' ';
361*521fa314SDavid van Moolenbroek 	}
362*521fa314SDavid van Moolenbroek 
363*521fa314SDavid van Moolenbroek 	return FALSE;
364*521fa314SDavid van Moolenbroek }
365*521fa314SDavid van Moolenbroek 
366*521fa314SDavid van Moolenbroek /*
367*521fa314SDavid van Moolenbroek  * Start a new line, and adjust the local state accordingly.  If nothing has
368*521fa314SDavid van Moolenbroek  * been printed on the current line yet, this function is a no-op.  Otherwise,
369*521fa314SDavid van Moolenbroek  * the output so far may have to be marked as preempted with the "<..>"
370*521fa314SDavid van Moolenbroek  * preemption marker.
371*521fa314SDavid van Moolenbroek  */
372*521fa314SDavid van Moolenbroek void
373*521fa314SDavid van Moolenbroek put_newline(void)
374*521fa314SDavid van Moolenbroek {
375*521fa314SDavid van Moolenbroek 
376*521fa314SDavid van Moolenbroek 	if (line_off == 0)
377*521fa314SDavid van Moolenbroek 		return;
378*521fa314SDavid van Moolenbroek 
379*521fa314SDavid van Moolenbroek 	if (print_susp) {
380*521fa314SDavid van Moolenbroek 		if (add_space)
381*521fa314SDavid van Moolenbroek 			(void)output_write(" ");
382*521fa314SDavid van Moolenbroek 
383*521fa314SDavid van Moolenbroek 		(void)output_write("<..>");
384*521fa314SDavid van Moolenbroek 	}
385*521fa314SDavid van Moolenbroek 
386*521fa314SDavid van Moolenbroek #if DEBUG
387*521fa314SDavid van Moolenbroek 	(void)output_write("|");
388*521fa314SDavid van Moolenbroek #endif
389*521fa314SDavid van Moolenbroek 
390*521fa314SDavid van Moolenbroek 	(void)output_write("\n");
391*521fa314SDavid van Moolenbroek 	output_flush();
392*521fa314SDavid van Moolenbroek 
393*521fa314SDavid van Moolenbroek 	line_off = 0;
394*521fa314SDavid van Moolenbroek 	add_space = FALSE;
395*521fa314SDavid van Moolenbroek 	print_susp = FALSE;
396*521fa314SDavid van Moolenbroek 	last_pid = 0;
397*521fa314SDavid van Moolenbroek }
398*521fa314SDavid van Moolenbroek 
399*521fa314SDavid van Moolenbroek /*
400*521fa314SDavid van Moolenbroek  * Print a string as part of the output associated with a process.  If the
401*521fa314SDavid van Moolenbroek  * current line contains output for another process, a newline will be printed
402*521fa314SDavid van Moolenbroek  * first.  If the current line contains output for the same process, then the
403*521fa314SDavid van Moolenbroek  * text will simply continue on the same line.  If the current line is empty,
404*521fa314SDavid van Moolenbroek  * a process PID prefix may have to be printed first.  Either way, after this
405*521fa314SDavid van Moolenbroek  * operation, the current line will contain text for the given process.  If
406*521fa314SDavid van Moolenbroek  * requested, the text may also be recorded for the process, for later replay.
407*521fa314SDavid van Moolenbroek  * As an exception, proc may be NULL when printing general information lines.
408*521fa314SDavid van Moolenbroek  */
409*521fa314SDavid van Moolenbroek void
410*521fa314SDavid van Moolenbroek put_text(struct trace_proc * proc, const char * text)
411*521fa314SDavid van Moolenbroek {
412*521fa314SDavid van Moolenbroek 
413*521fa314SDavid van Moolenbroek 	if (line_off > 0 && (proc == NULL || proc->pid != last_pid)) {
414*521fa314SDavid van Moolenbroek 		/*
415*521fa314SDavid van Moolenbroek 		 * The current line has not been terminated with a newline yet.
416*521fa314SDavid van Moolenbroek 		 * Start a new line.  Note that this means that for lines not
417*521fa314SDavid van Moolenbroek 		 * associated to a process, the whole line must be printed at
418*521fa314SDavid van Moolenbroek 		 * once.  This can be fixed but is currently not an issue.
419*521fa314SDavid van Moolenbroek 		 */
420*521fa314SDavid van Moolenbroek 		put_newline();
421*521fa314SDavid van Moolenbroek 	}
422*521fa314SDavid van Moolenbroek 
423*521fa314SDavid van Moolenbroek 	/* See if we must add a prefix at the start of the line. */
424*521fa314SDavid van Moolenbroek 	if (line_off == 0)
425*521fa314SDavid van Moolenbroek 		put_prefix(proc, FALSE /*resuming*/);
426*521fa314SDavid van Moolenbroek 
427*521fa314SDavid van Moolenbroek 	/* If needed, record the given text. */
428*521fa314SDavid van Moolenbroek 	if (proc != NULL && proc->recording)
429*521fa314SDavid van Moolenbroek 		record_add(proc, text);
430*521fa314SDavid van Moolenbroek 
431*521fa314SDavid van Moolenbroek 	/*
432*521fa314SDavid van Moolenbroek 	 * If we delayed printing a space, print one now.  This is never part
433*521fa314SDavid van Moolenbroek 	 * of text that must be saved.  In fact, we support these soft spaces
434*521fa314SDavid van Moolenbroek 	 * for exactly one case; see put_space() for details.
435*521fa314SDavid van Moolenbroek 	 */
436*521fa314SDavid van Moolenbroek 	if (add_space) {
437*521fa314SDavid van Moolenbroek 		line_off += output_write(" ");
438*521fa314SDavid van Moolenbroek 
439*521fa314SDavid van Moolenbroek 		add_space = FALSE;
440*521fa314SDavid van Moolenbroek 	}
441*521fa314SDavid van Moolenbroek 
442*521fa314SDavid van Moolenbroek 	/* Finally, print the actual text. */
443*521fa314SDavid van Moolenbroek 	line_off += output_write(text);
444*521fa314SDavid van Moolenbroek 
445*521fa314SDavid van Moolenbroek 	last_pid = (proc != NULL) ? proc->pid : 0;
446*521fa314SDavid van Moolenbroek }
447*521fa314SDavid van Moolenbroek 
448*521fa314SDavid van Moolenbroek /*
449*521fa314SDavid van Moolenbroek  * Add a space to the output for the given process, but only if and once more
450*521fa314SDavid van Moolenbroek  * text is printed for the process afterwards.  The aim is to ensure that no
451*521fa314SDavid van Moolenbroek  * lines ever end with a space, to prevent needless line wrapping on terminals.
452*521fa314SDavid van Moolenbroek  * The space may have to be remembered for the current line (for preemption,
453*521fa314SDavid van Moolenbroek  * which does not have a process pointer to work with) as well as recorded for
454*521fa314SDavid van Moolenbroek  * later replay, if recording is enabled.  Consider the following example:
455*521fa314SDavid van Moolenbroek  *
456*521fa314SDavid van Moolenbroek  * [A]   3| execve(..) <..>
457*521fa314SDavid van Moolenbroek  *       2| getpid(0) = 2 (ppid=1)
458*521fa314SDavid van Moolenbroek  * [B]   3| execve(..) = -1 [ENOENT]
459*521fa314SDavid van Moolenbroek  * [A]   3| exit(1) <..>
460*521fa314SDavid van Moolenbroek  *       2| getpid(0) = 2 (ppid=1)
461*521fa314SDavid van Moolenbroek  *       3| exit(1)
462*521fa314SDavid van Moolenbroek  *       3| Process exited normally with code 1
463*521fa314SDavid van Moolenbroek  *
464*521fa314SDavid van Moolenbroek  * On the [A] lines, the space between the call's closing parenthesis and the
465*521fa314SDavid van Moolenbroek  * "<..>" preemption marker is the result of add_space being set to TRUE; on
466*521fa314SDavid van Moolenbroek  * the [B] line, the space between the closing parenthesis and the equals sign
467*521fa314SDavid van Moolenbroek  * is the result of the space being recorded.
468*521fa314SDavid van Moolenbroek  */
469*521fa314SDavid van Moolenbroek void
470*521fa314SDavid van Moolenbroek put_space(struct trace_proc * proc)
471*521fa314SDavid van Moolenbroek {
472*521fa314SDavid van Moolenbroek 
473*521fa314SDavid van Moolenbroek 	/* This call must only be used after output for the given process. */
474*521fa314SDavid van Moolenbroek 	assert(last_pid == proc->pid);
475*521fa314SDavid van Moolenbroek 
476*521fa314SDavid van Moolenbroek 	/* In case the call does not get preempted. */
477*521fa314SDavid van Moolenbroek 	add_space = TRUE;
478*521fa314SDavid van Moolenbroek 
479*521fa314SDavid van Moolenbroek 	/* In case the call does get preempted. */
480*521fa314SDavid van Moolenbroek 	if (proc->recording)
481*521fa314SDavid van Moolenbroek 		record_add(proc, " ");
482*521fa314SDavid van Moolenbroek }
483*521fa314SDavid van Moolenbroek 
484*521fa314SDavid van Moolenbroek /*
485*521fa314SDavid van Moolenbroek  * Indent the remainders of the text on the line for this process, such that
486*521fa314SDavid van Moolenbroek  * similar remainders are similarly aligned.  In particular, the remainder is
487*521fa314SDavid van Moolenbroek  * the equals sign of a call, and everything after it.  Of course, alignment
488*521fa314SDavid van Moolenbroek  * can only be used if the call has not already printed beyond the alignment
489*521fa314SDavid van Moolenbroek  * position.  Also, the prefix must not be counted toward the alignment, as it
490*521fa314SDavid van Moolenbroek  * is possible that a line without prefix may be preempted and later continued
491*521fa314SDavid van Moolenbroek  * with prefix.  All things considered, the result would look like this:
492*521fa314SDavid van Moolenbroek  *
493*521fa314SDavid van Moolenbroek  *   getuid()                      = 1 (euid=1)
494*521fa314SDavid van Moolenbroek  *   setuid(0)                     = -1 [EPERM]
495*521fa314SDavid van Moolenbroek  *   write(2, "Permission denied\n", 18) = 18
496*521fa314SDavid van Moolenbroek  *   fork()                        = 3
497*521fa314SDavid van Moolenbroek  *       3| Tracing test (pid 3)
498*521fa314SDavid van Moolenbroek  *       3| fork()                        = 0
499*521fa314SDavid van Moolenbroek  *       3| exit(0)
500*521fa314SDavid van Moolenbroek  *       3| Process exited normally with code 0
501*521fa314SDavid van Moolenbroek  *       2' waitpid(-1, W_EXITED(0), 0)   = 3
502*521fa314SDavid van Moolenbroek  *
503*521fa314SDavid van Moolenbroek  */
504*521fa314SDavid van Moolenbroek void put_align(struct trace_proc * __unused proc)
505*521fa314SDavid van Moolenbroek {
506*521fa314SDavid van Moolenbroek 
507*521fa314SDavid van Moolenbroek 	/*
508*521fa314SDavid van Moolenbroek 	 * TODO: add actual support for this.  The following code works,
509*521fa314SDavid van Moolenbroek 	 * although not so efficiently.  The difficulty is the default
510*521fa314SDavid van Moolenbroek 	 * configuration and corresponding options.
511*521fa314SDavid van Moolenbroek 
512*521fa314SDavid van Moolenbroek 	while (line_off - prefix_off < 20)
513*521fa314SDavid van Moolenbroek 		put_text(proc, " ");
514*521fa314SDavid van Moolenbroek 
515*521fa314SDavid van Moolenbroek 	 */
516*521fa314SDavid van Moolenbroek }
517