xref: /minix3/minix/usr.bin/trace/format.c (revision 08cbf5a04d9d252f1eec34cb0fea3101e5fa9b88)
1 
2 #include "inc.h"
3 
4 #include <stdarg.h>
5 
6 /*
7  * The size of the formatting buffer, which in particular limits the maximum
8  * size of the output from the variadic functions.  All printer functions which
9  * are dealing with potentially large or even unbounded output, should be able
10  * to generate their output in smaller chunks.  In the end, nothing that is
11  * being printed as a unit should even come close to reaching this limit.
12  */
13 #define FORMAT_BUFSZ	4096
14 
15 /*
16  * The buffer which is used for all intermediate copying and/or formatting.
17  * Care must be taken that only one function uses this buffer at any time.
18  */
19 static char formatbuf[FORMAT_BUFSZ];
20 
21 /*
22  * Reset the line formatting for the given process.
23  */
24 void
25 format_reset(struct trace_proc * proc)
26 {
27 
28 	proc->next_sep = NULL;
29 	proc->depth = -1;
30 }
31 
32 /*
33  * Set the next separator for the given process.  The given separator may be
34  * NULL.
35  */
36 void
37 format_set_sep(struct trace_proc * proc, const char * sep)
38 {
39 
40 	proc->next_sep = sep;
41 }
42 
43 /*
44  * Print and clear the next separator for the process, if any.
45  */
46 void
47 format_push_sep(struct trace_proc * proc)
48 {
49 
50 	if (proc->next_sep != NULL) {
51 		put_text(proc, proc->next_sep);
52 
53 		proc->next_sep = NULL;
54 	}
55 }
56 
57 /*
58  * Print a field, e.g. a parameter or a field from a structure, separated from
59  * other fields at the same nesting depth as appropriate.  If the given field
60  * name is not NULL, it may or may not be printed.  The given text is what will
61  * be printed for this field so far, but the caller is allowed to continue
62  * printing text for the same field with e.g. put_text().  As such, the given
63  * text may even be an empty string.
64  */
65 void
66 put_field(struct trace_proc * proc, const char * name, const char * text)
67 {
68 
69 	/*
70 	 * At depth -1 (the basic line level), names are not used.  A name
71 	 * should not be supplied by the caller in that case, but, it happens.
72 	 */
73 	if (proc->depth < 0)
74 		name = NULL;
75 
76 	format_push_sep(proc);
77 
78 	if (name != NULL && (proc->depths[proc->depth].name || allnames)) {
79 		put_text(proc, name);
80 		put_text(proc, "=");
81 	}
82 
83 	put_text(proc, text);
84 
85 	format_set_sep(proc, proc->depths[proc->depth].sep);
86 }
87 
88 /*
89  * Increase the nesting depth with a new block of fields, enclosed within
90  * parentheses, brackets, etcetera.  The given name, which may be NULL, is the
91  * name of the entire nested block.  In the flags field, PF_NONAME indicates
92  * that the fields within the block should have their names printed or not,
93  * although this may be overridden by setting the allnames variable.  The given
94  * string is the block opening string (e.g., an opening parenthesis).  The
95  * given separator is used to separate the fields within the nested block, and
96  * should generally be ", " to maintain output consistency.
97  */
98 void
99 put_open(struct trace_proc * proc, const char * name, int flags,
100 	const char * string, const char * sep)
101 {
102 
103 	put_field(proc, name, string);
104 
105 	proc->depth++;
106 
107 	assert(proc->depth < MAX_DEPTH);
108 
109 	proc->depths[proc->depth].sep = sep;
110 	proc->depths[proc->depth].name = !(flags & PF_NONAME);
111 
112 	format_set_sep(proc, NULL);
113 }
114 
115 /*
116  * Decrease the nesting depth by ending a nested block of fields.  The given
117  * string is the closing parenthesis, bracket, etcetera.
118  */
119 void
120 put_close(struct trace_proc * proc, const char * string)
121 {
122 
123 	assert(proc->depth >= 0);
124 
125 	put_text(proc, string);
126 
127 	proc->depth--;
128 
129 	if (proc->depth >= 0)
130 		format_set_sep(proc, proc->depths[proc->depth].sep);
131 	else
132 		format_set_sep(proc, NULL);
133 }
134 
135 /*
136  * Version of put_text with variadic arguments.  The given process may be NULL.
137  */
138 void
139 put_fmt(struct trace_proc * proc, const char * fmt, ...)
140 {
141 	va_list ap;
142 
143 	va_start(ap, fmt);
144 	(void)vsnprintf(formatbuf, sizeof(formatbuf), fmt, ap);
145 	va_end(ap);
146 
147 	put_text(proc, formatbuf);
148 }
149 
150 /*
151  * Version of put_field with variadic arguments.
152  */
153 void
154 put_value(struct trace_proc * proc, const char * name, const char * fmt, ...)
155 {
156 	va_list ap;
157 
158 	va_start(ap, fmt);
159 	(void)vsnprintf(formatbuf, sizeof(formatbuf), fmt, ap);
160 	va_end(ap);
161 
162 	put_field(proc, name, formatbuf);
163 }
164 
165 /*
166  * Start printing a structure.  In general, the function copies the contents of
167  * the structure of size 'size' from the traced process at 'addr' into the
168  * local 'ptr' structure, opens a nested block with name 'name' (which may
169  * be NULL) using an opening bracket, and returns TRUE to indicate that the
170  * caller should print fields from the structure.  However, if 'flags' contains
171  * PF_FAILED, the structure will be printed as a pointer, no copy will be made,
172  * and the call will return FALSE.  Similarly, if the remote copy fails, a
173  * pointer will be printed and the call will return FALSE.  If PF_LOCADDR is
174  * given, 'addr' is a local address, and an intraprocess copy will be made.
175  */
176 int
177 put_open_struct(struct trace_proc * proc, const char * name, int flags,
178 	vir_bytes addr, void * ptr, size_t size)
179 {
180 
181 	if ((flags & PF_FAILED) || valuesonly > 1 || addr == 0) {
182 		if (flags & PF_LOCADDR)
183 			put_field(proc, name, "&..");
184 		else
185 			put_ptr(proc, name, addr);
186 
187 		return FALSE;
188 	}
189 
190 	if (!(flags & PF_LOCADDR)) {
191 		if (mem_get_data(proc->pid, addr, ptr, size) < 0) {
192 			put_ptr(proc, name, addr);
193 
194 			return FALSE;
195 		}
196 	} else
197 		memcpy(ptr, (void *) addr, size);
198 
199 	put_open(proc, name, flags, "{", ", ");
200 
201 	return TRUE;
202 }
203 
204 /*
205  * End printing a structure.  This must be called only to match a successful
206  * call to put_open_struct.  The given 'all' flag indicates whether all fields
207  * of the structure have been printed; if not, a ".." continuation text is
208  * printed to show the user that some structure fields have not been printed.
209  */
210 void
211 put_close_struct(struct trace_proc * proc, int all)
212 {
213 
214 	if (!all)
215 		put_field(proc, NULL, "..");
216 
217 	put_close(proc, "}");
218 }
219 
220 /*
221  * Print a pointer.  NULL is treated as a special case.
222  */
223 void
224 put_ptr(struct trace_proc * proc, const char * name, vir_bytes addr)
225 {
226 
227 	if (addr == 0 && !valuesonly)
228 		put_field(proc, name, "NULL");
229 	else
230 		put_value(proc, name, "&0x%lx", addr);
231 }
232 
233 /*
234  * Print the contents of a buffer, at remote address 'addr' and of 'bytes'
235  * size, as a field using name 'name' (which may be NULL).  If the PF_FAILED
236  * flag is given, the buffer address is printed instead, since it is assumed
237  * that the actual buffer contains garbage.  If the PF_LOCADDR flag is given,
238  * the given address is a local address and no intraprocess copies are
239  * performed.  If the PF_STRING flag is given, the buffer is expected to
240  * contain a null terminator within its size, and the string will be printed
241  * only up to there.  Normally, the string is cut off beyond a number of bytes
242  * which depends on the verbosity level; if the PF_FULL flag is given, the full
243  * string will be printed no matter its size (used mainly for path names, which
244  * typically become useless once cut off).
245  */
246 void
247 put_buf(struct trace_proc * proc, const char * name, int flags, vir_bytes addr,
248 	ssize_t size)
249 {
250 	const char *escaped;
251 	size_t len, off, max, chunk;
252 	unsigned int i;
253 	int cutoff;
254 	char *p;
255 
256 	if ((flags & PF_FAILED) || valuesonly || addr == 0 || size < 0) {
257 		if (flags & PF_LOCADDR)
258 			put_field(proc, name, "&..");
259 		else
260 			put_ptr(proc, name, addr);
261 
262 		return;
263 	}
264 
265 	if (size == 0) {
266 		put_field(proc, name, "\"\"");
267 
268 		return;
269 	}
270 
271 	/*
272 	 * TODO: the maximum says nothing about the size of the printed text.
273 	 * Escaped-character printing can make the output much longer.  Does it
274 	 * make more sense to apply a limit after the escape transformation?
275 	 */
276 	if (verbose == 0) max = 32;
277 	else if (verbose == 1) max = 256;
278 	else max = SIZE_MAX;
279 
280 	/*
281 	 * If the output is cut off, we put two dots after the closing quote.
282 	 * For non-string buffers, the output is cut off if the size exceeds
283 	 * our limit or we run into a copying error somewhere in the middle.
284 	 * For strings, the output is cut off unless we find a null terminator.
285 	 */
286 	cutoff = !!(flags & PF_STRING);
287 	len = (size_t)size;
288 	if (!(flags & PF_FULL) && len > max) {
289 		len = max;
290 		cutoff = TRUE;
291 	}
292 
293 	for (off = 0; off < len; off += chunk) {
294 		chunk = len - off;
295 		if (chunk > sizeof(formatbuf) - 1)
296 			chunk = sizeof(formatbuf) - 1;
297 
298 		if (!(flags & PF_LOCADDR)) {
299 			if (mem_get_data(proc->pid, addr + off, formatbuf,
300 			    chunk) < 0) {
301 				if (off == 0) {
302 					put_ptr(proc, name, addr);
303 
304 					return;
305 				}
306 
307 				cutoff = TRUE;
308 				break;
309 			}
310 		} else
311 			memcpy(formatbuf, (void *)addr, chunk);
312 
313 		if (off == 0)
314 			put_field(proc, name, "\"");
315 
316 		/* In strings, look for the terminating null character. */
317 		if ((flags & PF_STRING) &&
318 		    (p = memchr(formatbuf, '\0', chunk)) != NULL) {
319 			chunk = (size_t)(p - formatbuf);
320 			cutoff = FALSE;
321 		}
322 
323 		/* Print the buffer contents using escaped characters. */
324 		for (i = 0; i < chunk; i++) {
325 			escaped = get_escape(formatbuf[i]);
326 
327 			put_text(proc, escaped);
328 		}
329 
330 		/* Stop if we found the end of the string. */
331 		if ((flags & PF_STRING) && !cutoff)
332 			break;
333 	}
334 
335 	if (cutoff)
336 		put_text(proc, "\"..");
337 	else
338 		put_text(proc, "\"");
339 }
340 
341 /*
342  * Print a flags field, using known flag names.  The name of the whole field is
343  * given as 'name' and may be NULL.  The caller must supply an array of known
344  * flags as 'fp' (with 'num' entries).  Each entry in the array has a mask, a
345  * value, and a name.  If the given flags 'value', bitwise-ANDed with the mask
346  * of an entry, yields the value of that entry, then the name is printed.  This
347  * means that certain zero bits may also be printed as actual flags, and that
348  * by supplying an all-bits-set mask can print a flag name for a zero value,
349  * for example F_OK for access().  See the FLAG macros and their usage for
350  * examples.  All matching flag names are printed with a "|" separator, and if
351  * after evaluating all 'num' entries in 'fp' there are still bits in 'value'
352  * for which nothing has been printed, the remaining bits will be printed with
353  * the 'fmt' format string for an integer (generally "%d" should be used).
354  */
355 void
356 put_flags(struct trace_proc * proc, const char * name, const struct flags * fp,
357 	unsigned int num, const char * fmt, unsigned int value)
358 {
359 	unsigned int left;
360 	int first;
361 
362 	if (valuesonly) {
363 		put_value(proc, name, fmt, value);
364 
365 		return;
366 	}
367 
368 	put_field(proc, name, "");
369 
370 	for (first = TRUE, left = value; num > 0; fp++, num--) {
371 		if ((value & fp->mask) == fp->value) {
372 			if (first)
373 				first = FALSE;
374 			else
375 				put_text(proc, "|");
376 			put_text(proc, fp->name);
377 
378 			left -= fp->value;
379 		}
380 	}
381 
382 	if (left != 0) {
383 		if (first)
384 			first = FALSE;
385 		else
386 			put_text(proc, "|");
387 
388 		put_fmt(proc, fmt, left);
389 	}
390 
391 	/*
392 	 * If nothing has been printed so far, simply print a zero.  Ignoring
393 	 * the given format in this case is intentional: a simple 0 looks
394 	 * better than 0x0 or 00 etc.
395 	 */
396 	if (first)
397 		put_text(proc, "0");
398 }
399 
400 /*
401  * Print a tail field at the end of an array.  The given 'count' value is the
402  * total number of elements in the array, or 0 to indicate that an error
403  * occurred.  The given 'printed' value is the number of fields printed so far.
404  * If some fields have been printed already, the number of fields not printed
405  * will be shown as "..(+N)".  If no fields have been printed already, the
406  * (total) number of fields not printed will be shown as "..(N)".  An error
407  * will print "..(?)".
408  *
409  * The rules for printing an array are as follows.  In principle, arrays should
410  * be enclosed in "[]".  However, if a copy error occurs immediately, a pointer
411  * to the array should be printed instead.  An empty array should be printed as
412  * "[]" (not "[..(0)]").  If a copy error occurs in the middle of the array,
413  * put_tail should be used with count == 0.  Only if not all fields in the
414  * array are printed, put_tail should be used with count > 0.  The value of
415  * 'printed' is typically the result of an arbitrary limit set based on the
416  * verbosity level.
417  */
418 void
419 put_tail(struct trace_proc * proc, unsigned int count, unsigned int printed)
420 {
421 
422 	if (count == 0)
423 		put_field(proc, NULL, "..(?)");
424 	else
425 		put_value(proc, NULL, "..(%s%u)",
426 		    (printed > 0) ? "+" : "", count - printed);
427 }
428