xref: /minix3/minix/usr.bin/trace/format.c (revision b58e161ccb9bcc2008a44da1b8ee01a0a6f2990e)
1521fa314SDavid van Moolenbroek 
2521fa314SDavid van Moolenbroek #include "inc.h"
3521fa314SDavid van Moolenbroek 
4521fa314SDavid van Moolenbroek #include <stdarg.h>
5521fa314SDavid van Moolenbroek 
6521fa314SDavid van Moolenbroek /*
7521fa314SDavid van Moolenbroek  * The size of the formatting buffer, which in particular limits the maximum
8521fa314SDavid van Moolenbroek  * size of the output from the variadic functions.  All printer functions which
9521fa314SDavid van Moolenbroek  * are dealing with potentially large or even unbounded output, should be able
10521fa314SDavid van Moolenbroek  * to generate their output in smaller chunks.  In the end, nothing that is
11521fa314SDavid van Moolenbroek  * being printed as a unit should even come close to reaching this limit.
12521fa314SDavid van Moolenbroek  */
13521fa314SDavid van Moolenbroek #define FORMAT_BUFSZ	4096
14521fa314SDavid van Moolenbroek 
15521fa314SDavid van Moolenbroek /*
16521fa314SDavid van Moolenbroek  * The buffer which is used for all intermediate copying and/or formatting.
17521fa314SDavid van Moolenbroek  * Care must be taken that only one function uses this buffer at any time.
18521fa314SDavid van Moolenbroek  */
19521fa314SDavid van Moolenbroek static char formatbuf[FORMAT_BUFSZ];
20521fa314SDavid van Moolenbroek 
21521fa314SDavid van Moolenbroek /*
22521fa314SDavid van Moolenbroek  * Reset the line formatting for the given process.
23521fa314SDavid van Moolenbroek  */
24521fa314SDavid van Moolenbroek void
format_reset(struct trace_proc * proc)25521fa314SDavid van Moolenbroek format_reset(struct trace_proc * proc)
26521fa314SDavid van Moolenbroek {
27521fa314SDavid van Moolenbroek 
28521fa314SDavid van Moolenbroek 	proc->next_sep = NULL;
29521fa314SDavid van Moolenbroek 	proc->depth = -1;
30521fa314SDavid van Moolenbroek }
31521fa314SDavid van Moolenbroek 
32521fa314SDavid van Moolenbroek /*
33521fa314SDavid van Moolenbroek  * Set the next separator for the given process.  The given separator may be
34521fa314SDavid van Moolenbroek  * NULL.
35521fa314SDavid van Moolenbroek  */
36521fa314SDavid van Moolenbroek void
format_set_sep(struct trace_proc * proc,const char * sep)37521fa314SDavid van Moolenbroek format_set_sep(struct trace_proc * proc, const char * sep)
38521fa314SDavid van Moolenbroek {
39521fa314SDavid van Moolenbroek 
40521fa314SDavid van Moolenbroek 	proc->next_sep = sep;
41521fa314SDavid van Moolenbroek }
42521fa314SDavid van Moolenbroek 
43521fa314SDavid van Moolenbroek /*
44521fa314SDavid van Moolenbroek  * Print and clear the next separator for the process, if any.
45521fa314SDavid van Moolenbroek  */
46521fa314SDavid van Moolenbroek void
format_push_sep(struct trace_proc * proc)47521fa314SDavid van Moolenbroek format_push_sep(struct trace_proc * proc)
48521fa314SDavid van Moolenbroek {
49521fa314SDavid van Moolenbroek 
50521fa314SDavid van Moolenbroek 	if (proc->next_sep != NULL) {
51521fa314SDavid van Moolenbroek 		put_text(proc, proc->next_sep);
52521fa314SDavid van Moolenbroek 
53521fa314SDavid van Moolenbroek 		proc->next_sep = NULL;
54521fa314SDavid van Moolenbroek 	}
55521fa314SDavid van Moolenbroek }
56521fa314SDavid van Moolenbroek 
57521fa314SDavid van Moolenbroek /*
58521fa314SDavid van Moolenbroek  * Print a field, e.g. a parameter or a field from a structure, separated from
59521fa314SDavid van Moolenbroek  * other fields at the same nesting depth as appropriate.  If the given field
60521fa314SDavid van Moolenbroek  * name is not NULL, it may or may not be printed.  The given text is what will
61521fa314SDavid van Moolenbroek  * be printed for this field so far, but the caller is allowed to continue
62521fa314SDavid van Moolenbroek  * printing text for the same field with e.g. put_text().  As such, the given
63521fa314SDavid van Moolenbroek  * text may even be an empty string.
64521fa314SDavid van Moolenbroek  */
65521fa314SDavid van Moolenbroek void
put_field(struct trace_proc * proc,const char * name,const char * text)66521fa314SDavid van Moolenbroek put_field(struct trace_proc * proc, const char * name, const char * text)
67521fa314SDavid van Moolenbroek {
68521fa314SDavid van Moolenbroek 
69521fa314SDavid van Moolenbroek 	/*
70521fa314SDavid van Moolenbroek 	 * At depth -1 (the basic line level), names are not used.  A name
71521fa314SDavid van Moolenbroek 	 * should not be supplied by the caller in that case, but, it happens.
72521fa314SDavid van Moolenbroek 	 */
73521fa314SDavid van Moolenbroek 	if (proc->depth < 0)
74521fa314SDavid van Moolenbroek 		name = NULL;
75521fa314SDavid van Moolenbroek 
76521fa314SDavid van Moolenbroek 	format_push_sep(proc);
77521fa314SDavid van Moolenbroek 
78521fa314SDavid van Moolenbroek 	if (name != NULL && (proc->depths[proc->depth].name || allnames)) {
79521fa314SDavid van Moolenbroek 		put_text(proc, name);
80521fa314SDavid van Moolenbroek 		put_text(proc, "=");
81521fa314SDavid van Moolenbroek 	}
82521fa314SDavid van Moolenbroek 
83521fa314SDavid van Moolenbroek 	put_text(proc, text);
84521fa314SDavid van Moolenbroek 
85521fa314SDavid van Moolenbroek 	format_set_sep(proc, proc->depths[proc->depth].sep);
86521fa314SDavid van Moolenbroek }
87521fa314SDavid van Moolenbroek 
88521fa314SDavid van Moolenbroek /*
89521fa314SDavid van Moolenbroek  * Increase the nesting depth with a new block of fields, enclosed within
90521fa314SDavid van Moolenbroek  * parentheses, brackets, etcetera.  The given name, which may be NULL, is the
91521fa314SDavid van Moolenbroek  * name of the entire nested block.  In the flags field, PF_NONAME indicates
92521fa314SDavid van Moolenbroek  * that the fields within the block should have their names printed or not,
93521fa314SDavid van Moolenbroek  * although this may be overridden by setting the allnames variable.  The given
94521fa314SDavid van Moolenbroek  * string is the block opening string (e.g., an opening parenthesis).  The
95521fa314SDavid van Moolenbroek  * given separator is used to separate the fields within the nested block, and
96521fa314SDavid van Moolenbroek  * should generally be ", " to maintain output consistency.
97521fa314SDavid van Moolenbroek  */
98521fa314SDavid van Moolenbroek void
put_open(struct trace_proc * proc,const char * name,int flags,const char * string,const char * sep)99521fa314SDavid van Moolenbroek put_open(struct trace_proc * proc, const char * name, int flags,
100521fa314SDavid van Moolenbroek 	const char * string, const char * sep)
101521fa314SDavid van Moolenbroek {
102521fa314SDavid van Moolenbroek 
103521fa314SDavid van Moolenbroek 	put_field(proc, name, string);
104521fa314SDavid van Moolenbroek 
105521fa314SDavid van Moolenbroek 	proc->depth++;
106521fa314SDavid van Moolenbroek 
107521fa314SDavid van Moolenbroek 	assert(proc->depth < MAX_DEPTH);
108521fa314SDavid van Moolenbroek 
109521fa314SDavid van Moolenbroek 	proc->depths[proc->depth].sep = sep;
110521fa314SDavid van Moolenbroek 	proc->depths[proc->depth].name = !(flags & PF_NONAME);
111521fa314SDavid van Moolenbroek 
112521fa314SDavid van Moolenbroek 	format_set_sep(proc, NULL);
113521fa314SDavid van Moolenbroek }
114521fa314SDavid van Moolenbroek 
115521fa314SDavid van Moolenbroek /*
116521fa314SDavid van Moolenbroek  * Decrease the nesting depth by ending a nested block of fields.  The given
117521fa314SDavid van Moolenbroek  * string is the closing parenthesis, bracket, etcetera.
118521fa314SDavid van Moolenbroek  */
119521fa314SDavid van Moolenbroek void
put_close(struct trace_proc * proc,const char * string)120521fa314SDavid van Moolenbroek put_close(struct trace_proc * proc, const char * string)
121521fa314SDavid van Moolenbroek {
122521fa314SDavid van Moolenbroek 
123521fa314SDavid van Moolenbroek 	assert(proc->depth >= 0);
124521fa314SDavid van Moolenbroek 
125521fa314SDavid van Moolenbroek 	put_text(proc, string);
126521fa314SDavid van Moolenbroek 
127521fa314SDavid van Moolenbroek 	proc->depth--;
128521fa314SDavid van Moolenbroek 
129521fa314SDavid van Moolenbroek 	if (proc->depth >= 0)
130521fa314SDavid van Moolenbroek 		format_set_sep(proc, proc->depths[proc->depth].sep);
131521fa314SDavid van Moolenbroek 	else
132521fa314SDavid van Moolenbroek 		format_set_sep(proc, NULL);
133521fa314SDavid van Moolenbroek }
134521fa314SDavid van Moolenbroek 
135521fa314SDavid van Moolenbroek /*
136521fa314SDavid van Moolenbroek  * Version of put_text with variadic arguments.  The given process may be NULL.
137521fa314SDavid van Moolenbroek  */
138521fa314SDavid van Moolenbroek void
put_fmt(struct trace_proc * proc,const char * fmt,...)139521fa314SDavid van Moolenbroek put_fmt(struct trace_proc * proc, const char * fmt, ...)
140521fa314SDavid van Moolenbroek {
141521fa314SDavid van Moolenbroek 	va_list ap;
142521fa314SDavid van Moolenbroek 
143521fa314SDavid van Moolenbroek 	va_start(ap, fmt);
144521fa314SDavid van Moolenbroek 	(void)vsnprintf(formatbuf, sizeof(formatbuf), fmt, ap);
145521fa314SDavid van Moolenbroek 	va_end(ap);
146521fa314SDavid van Moolenbroek 
147521fa314SDavid van Moolenbroek 	put_text(proc, formatbuf);
148521fa314SDavid van Moolenbroek }
149521fa314SDavid van Moolenbroek 
150521fa314SDavid van Moolenbroek /*
151521fa314SDavid van Moolenbroek  * Version of put_field with variadic arguments.
152521fa314SDavid van Moolenbroek  */
153521fa314SDavid van Moolenbroek void
put_value(struct trace_proc * proc,const char * name,const char * fmt,...)154521fa314SDavid van Moolenbroek put_value(struct trace_proc * proc, const char * name, const char * fmt, ...)
155521fa314SDavid van Moolenbroek {
156521fa314SDavid van Moolenbroek 	va_list ap;
157521fa314SDavid van Moolenbroek 
158521fa314SDavid van Moolenbroek 	va_start(ap, fmt);
159521fa314SDavid van Moolenbroek 	(void)vsnprintf(formatbuf, sizeof(formatbuf), fmt, ap);
160521fa314SDavid van Moolenbroek 	va_end(ap);
161521fa314SDavid van Moolenbroek 
162521fa314SDavid van Moolenbroek 	put_field(proc, name, formatbuf);
163521fa314SDavid van Moolenbroek }
164521fa314SDavid van Moolenbroek 
165521fa314SDavid van Moolenbroek /*
166521fa314SDavid van Moolenbroek  * Start printing a structure.  In general, the function copies the contents of
167521fa314SDavid van Moolenbroek  * the structure of size 'size' from the traced process at 'addr' into the
168521fa314SDavid van Moolenbroek  * local 'ptr' structure, opens a nested block with name 'name' (which may
169521fa314SDavid van Moolenbroek  * be NULL) using an opening bracket, and returns TRUE to indicate that the
170521fa314SDavid van Moolenbroek  * caller should print fields from the structure.  However, if 'flags' contains
171521fa314SDavid van Moolenbroek  * PF_FAILED, the structure will be printed as a pointer, no copy will be made,
172521fa314SDavid van Moolenbroek  * and the call will return FALSE.  Similarly, if the remote copy fails, a
173521fa314SDavid van Moolenbroek  * pointer will be printed and the call will return FALSE.  If PF_LOCADDR is
174521fa314SDavid van Moolenbroek  * given, 'addr' is a local address, and an intraprocess copy will be made.
175521fa314SDavid van Moolenbroek  */
176521fa314SDavid van Moolenbroek int
put_open_struct(struct trace_proc * proc,const char * name,int flags,vir_bytes addr,void * ptr,size_t size)177521fa314SDavid van Moolenbroek put_open_struct(struct trace_proc * proc, const char * name, int flags,
178521fa314SDavid van Moolenbroek 	vir_bytes addr, void * ptr, size_t size)
179521fa314SDavid van Moolenbroek {
180521fa314SDavid van Moolenbroek 
181521fa314SDavid van Moolenbroek 	if ((flags & PF_FAILED) || valuesonly > 1 || addr == 0) {
182521fa314SDavid van Moolenbroek 		if (flags & PF_LOCADDR)
183521fa314SDavid van Moolenbroek 			put_field(proc, name, "&..");
184521fa314SDavid van Moolenbroek 		else
185521fa314SDavid van Moolenbroek 			put_ptr(proc, name, addr);
186521fa314SDavid van Moolenbroek 
187521fa314SDavid van Moolenbroek 		return FALSE;
188521fa314SDavid van Moolenbroek 	}
189521fa314SDavid van Moolenbroek 
190521fa314SDavid van Moolenbroek 	if (!(flags & PF_LOCADDR)) {
191521fa314SDavid van Moolenbroek 		if (mem_get_data(proc->pid, addr, ptr, size) < 0) {
192521fa314SDavid van Moolenbroek 			put_ptr(proc, name, addr);
193521fa314SDavid van Moolenbroek 
194521fa314SDavid van Moolenbroek 			return FALSE;
195521fa314SDavid van Moolenbroek 		}
196521fa314SDavid van Moolenbroek 	} else
197521fa314SDavid van Moolenbroek 		memcpy(ptr, (void *) addr, size);
198521fa314SDavid van Moolenbroek 
199521fa314SDavid van Moolenbroek 	put_open(proc, name, flags, "{", ", ");
200521fa314SDavid van Moolenbroek 
201521fa314SDavid van Moolenbroek 	return TRUE;
202521fa314SDavid van Moolenbroek }
203521fa314SDavid van Moolenbroek 
204521fa314SDavid van Moolenbroek /*
205521fa314SDavid van Moolenbroek  * End printing a structure.  This must be called only to match a successful
206521fa314SDavid van Moolenbroek  * call to put_open_struct.  The given 'all' flag indicates whether all fields
207521fa314SDavid van Moolenbroek  * of the structure have been printed; if not, a ".." continuation text is
208521fa314SDavid van Moolenbroek  * printed to show the user that some structure fields have not been printed.
209521fa314SDavid van Moolenbroek  */
210521fa314SDavid van Moolenbroek void
put_close_struct(struct trace_proc * proc,int all)211521fa314SDavid van Moolenbroek put_close_struct(struct trace_proc * proc, int all)
212521fa314SDavid van Moolenbroek {
213521fa314SDavid van Moolenbroek 
214521fa314SDavid van Moolenbroek 	if (!all)
215521fa314SDavid van Moolenbroek 		put_field(proc, NULL, "..");
216521fa314SDavid van Moolenbroek 
217521fa314SDavid van Moolenbroek 	put_close(proc, "}");
218521fa314SDavid van Moolenbroek }
219521fa314SDavid van Moolenbroek 
220521fa314SDavid van Moolenbroek /*
221521fa314SDavid van Moolenbroek  * Print a pointer.  NULL is treated as a special case.
222521fa314SDavid van Moolenbroek  */
223521fa314SDavid van Moolenbroek void
put_ptr(struct trace_proc * proc,const char * name,vir_bytes addr)224521fa314SDavid van Moolenbroek put_ptr(struct trace_proc * proc, const char * name, vir_bytes addr)
225521fa314SDavid van Moolenbroek {
226521fa314SDavid van Moolenbroek 
227521fa314SDavid van Moolenbroek 	if (addr == 0 && !valuesonly)
228521fa314SDavid van Moolenbroek 		put_field(proc, name, "NULL");
229521fa314SDavid van Moolenbroek 	else
230521fa314SDavid van Moolenbroek 		put_value(proc, name, "&0x%lx", addr);
231521fa314SDavid van Moolenbroek }
232521fa314SDavid van Moolenbroek 
233521fa314SDavid van Moolenbroek /*
234521fa314SDavid van Moolenbroek  * Print the contents of a buffer, at remote address 'addr' and of 'bytes'
235521fa314SDavid van Moolenbroek  * size, as a field using name 'name' (which may be NULL).  If the PF_FAILED
236521fa314SDavid van Moolenbroek  * flag is given, the buffer address is printed instead, since it is assumed
237521fa314SDavid van Moolenbroek  * that the actual buffer contains garbage.  If the PF_LOCADDR flag is given,
238521fa314SDavid van Moolenbroek  * the given address is a local address and no intraprocess copies are
239521fa314SDavid van Moolenbroek  * performed.  If the PF_STRING flag is given, the buffer is expected to
240521fa314SDavid van Moolenbroek  * contain a null terminator within its size, and the string will be printed
241521fa314SDavid van Moolenbroek  * only up to there.  Normally, the string is cut off beyond a number of bytes
242521fa314SDavid van Moolenbroek  * which depends on the verbosity level; if the PF_FULL flag is given, the full
243521fa314SDavid van Moolenbroek  * string will be printed no matter its size (used mainly for path names, which
244521fa314SDavid van Moolenbroek  * typically become useless once cut off).
245521fa314SDavid van Moolenbroek  */
246521fa314SDavid van Moolenbroek void
put_buf(struct trace_proc * proc,const char * name,int flags,vir_bytes addr,ssize_t size)247521fa314SDavid van Moolenbroek put_buf(struct trace_proc * proc, const char * name, int flags, vir_bytes addr,
248521fa314SDavid van Moolenbroek 	ssize_t size)
249521fa314SDavid van Moolenbroek {
250521fa314SDavid van Moolenbroek 	const char *escaped;
251521fa314SDavid van Moolenbroek 	size_t len, off, max, chunk;
252*b58e161cSDavid van Moolenbroek 	unsigned int i;
253*b58e161cSDavid van Moolenbroek 	int cutoff;
254521fa314SDavid van Moolenbroek 	char *p;
255521fa314SDavid van Moolenbroek 
256521fa314SDavid van Moolenbroek 	if ((flags & PF_FAILED) || valuesonly || addr == 0 || size < 0) {
257521fa314SDavid van Moolenbroek 		if (flags & PF_LOCADDR)
258521fa314SDavid van Moolenbroek 			put_field(proc, name, "&..");
259521fa314SDavid van Moolenbroek 		else
260521fa314SDavid van Moolenbroek 			put_ptr(proc, name, addr);
261521fa314SDavid van Moolenbroek 
262521fa314SDavid van Moolenbroek 		return;
263521fa314SDavid van Moolenbroek 	}
264521fa314SDavid van Moolenbroek 
265521fa314SDavid van Moolenbroek 	if (size == 0) {
266521fa314SDavid van Moolenbroek 		put_field(proc, name, "\"\"");
267521fa314SDavid van Moolenbroek 
268521fa314SDavid van Moolenbroek 		return;
269521fa314SDavid van Moolenbroek 	}
270521fa314SDavid van Moolenbroek 
271521fa314SDavid van Moolenbroek 	/*
272521fa314SDavid van Moolenbroek 	 * TODO: the maximum says nothing about the size of the printed text.
273521fa314SDavid van Moolenbroek 	 * Escaped-character printing can make the output much longer.  Does it
274521fa314SDavid van Moolenbroek 	 * make more sense to apply a limit after the escape transformation?
275521fa314SDavid van Moolenbroek 	 */
276521fa314SDavid van Moolenbroek 	if (verbose == 0) max = 32;
277521fa314SDavid van Moolenbroek 	else if (verbose == 1) max = 256;
278521fa314SDavid van Moolenbroek 	else max = SIZE_MAX;
279521fa314SDavid van Moolenbroek 
280521fa314SDavid van Moolenbroek 	/*
281521fa314SDavid van Moolenbroek 	 * If the output is cut off, we put two dots after the closing quote.
282521fa314SDavid van Moolenbroek 	 * For non-string buffers, the output is cut off if the size exceeds
283521fa314SDavid van Moolenbroek 	 * our limit or we run into a copying error somewhere in the middle.
284521fa314SDavid van Moolenbroek 	 * For strings, the output is cut off unless we find a null terminator.
285521fa314SDavid van Moolenbroek 	 */
286521fa314SDavid van Moolenbroek 	cutoff = !!(flags & PF_STRING);
287521fa314SDavid van Moolenbroek 	len = (size_t)size;
288521fa314SDavid van Moolenbroek 	if (!(flags & PF_FULL) && len > max) {
289521fa314SDavid van Moolenbroek 		len = max;
290521fa314SDavid van Moolenbroek 		cutoff = TRUE;
291521fa314SDavid van Moolenbroek 	}
292521fa314SDavid van Moolenbroek 
293521fa314SDavid van Moolenbroek 	for (off = 0; off < len; off += chunk) {
294521fa314SDavid van Moolenbroek 		chunk = len - off;
295521fa314SDavid van Moolenbroek 		if (chunk > sizeof(formatbuf) - 1)
296521fa314SDavid van Moolenbroek 			chunk = sizeof(formatbuf) - 1;
297521fa314SDavid van Moolenbroek 
298521fa314SDavid van Moolenbroek 		if (!(flags & PF_LOCADDR)) {
299521fa314SDavid van Moolenbroek 			if (mem_get_data(proc->pid, addr + off, formatbuf,
300521fa314SDavid van Moolenbroek 			    chunk) < 0) {
301521fa314SDavid van Moolenbroek 				if (off == 0) {
302521fa314SDavid van Moolenbroek 					put_ptr(proc, name, addr);
303521fa314SDavid van Moolenbroek 
304521fa314SDavid van Moolenbroek 					return;
305521fa314SDavid van Moolenbroek 				}
306521fa314SDavid van Moolenbroek 
307521fa314SDavid van Moolenbroek 				cutoff = TRUE;
308521fa314SDavid van Moolenbroek 				break;
309521fa314SDavid van Moolenbroek 			}
310521fa314SDavid van Moolenbroek 		} else
311521fa314SDavid van Moolenbroek 			memcpy(formatbuf, (void *)addr, chunk);
312521fa314SDavid van Moolenbroek 
313521fa314SDavid van Moolenbroek 		if (off == 0)
314521fa314SDavid van Moolenbroek 			put_field(proc, name, "\"");
315521fa314SDavid van Moolenbroek 
316521fa314SDavid van Moolenbroek 		/* In strings, look for the terminating null character. */
317521fa314SDavid van Moolenbroek 		if ((flags & PF_STRING) &&
318521fa314SDavid van Moolenbroek 		    (p = memchr(formatbuf, '\0', chunk)) != NULL) {
319521fa314SDavid van Moolenbroek 			chunk = (size_t)(p - formatbuf);
320521fa314SDavid van Moolenbroek 			cutoff = FALSE;
321521fa314SDavid van Moolenbroek 		}
322521fa314SDavid van Moolenbroek 
323521fa314SDavid van Moolenbroek 		/* Print the buffer contents using escaped characters. */
324521fa314SDavid van Moolenbroek 		for (i = 0; i < chunk; i++) {
325521fa314SDavid van Moolenbroek 			escaped = get_escape(formatbuf[i]);
326521fa314SDavid van Moolenbroek 
327521fa314SDavid van Moolenbroek 			put_text(proc, escaped);
328521fa314SDavid van Moolenbroek 		}
329521fa314SDavid van Moolenbroek 
330521fa314SDavid van Moolenbroek 		/* Stop if we found the end of the string. */
331521fa314SDavid van Moolenbroek 		if ((flags & PF_STRING) && !cutoff)
332521fa314SDavid van Moolenbroek 			break;
333521fa314SDavid van Moolenbroek 	}
334521fa314SDavid van Moolenbroek 
335521fa314SDavid van Moolenbroek 	if (cutoff)
336521fa314SDavid van Moolenbroek 		put_text(proc, "\"..");
337521fa314SDavid van Moolenbroek 	else
338521fa314SDavid van Moolenbroek 		put_text(proc, "\"");
339521fa314SDavid van Moolenbroek }
340521fa314SDavid van Moolenbroek 
341521fa314SDavid van Moolenbroek /*
342521fa314SDavid van Moolenbroek  * Print a flags field, using known flag names.  The name of the whole field is
343521fa314SDavid van Moolenbroek  * given as 'name' and may be NULL.  The caller must supply an array of known
344521fa314SDavid van Moolenbroek  * flags as 'fp' (with 'num' entries).  Each entry in the array has a mask, a
345521fa314SDavid van Moolenbroek  * value, and a name.  If the given flags 'value', bitwise-ANDed with the mask
346521fa314SDavid van Moolenbroek  * of an entry, yields the value of that entry, then the name is printed.  This
347521fa314SDavid van Moolenbroek  * means that certain zero bits may also be printed as actual flags, and that
348521fa314SDavid van Moolenbroek  * by supplying an all-bits-set mask can print a flag name for a zero value,
349521fa314SDavid van Moolenbroek  * for example F_OK for access().  See the FLAG macros and their usage for
350521fa314SDavid van Moolenbroek  * examples.  All matching flag names are printed with a "|" separator, and if
351521fa314SDavid van Moolenbroek  * after evaluating all 'num' entries in 'fp' there are still bits in 'value'
352521fa314SDavid van Moolenbroek  * for which nothing has been printed, the remaining bits will be printed with
353521fa314SDavid van Moolenbroek  * the 'fmt' format string for an integer (generally "%d" should be used).
354521fa314SDavid van Moolenbroek  */
355521fa314SDavid van Moolenbroek void
put_flags(struct trace_proc * proc,const char * name,const struct flags * fp,unsigned int num,const char * fmt,unsigned int value)356521fa314SDavid van Moolenbroek put_flags(struct trace_proc * proc, const char * name, const struct flags * fp,
357521fa314SDavid van Moolenbroek 	unsigned int num, const char * fmt, unsigned int value)
358521fa314SDavid van Moolenbroek {
359521fa314SDavid van Moolenbroek 	unsigned int left;
360521fa314SDavid van Moolenbroek 	int first;
361521fa314SDavid van Moolenbroek 
362521fa314SDavid van Moolenbroek 	if (valuesonly) {
363521fa314SDavid van Moolenbroek 		put_value(proc, name, fmt, value);
364521fa314SDavid van Moolenbroek 
365521fa314SDavid van Moolenbroek 		return;
366521fa314SDavid van Moolenbroek 	}
367521fa314SDavid van Moolenbroek 
368521fa314SDavid van Moolenbroek 	put_field(proc, name, "");
369521fa314SDavid van Moolenbroek 
370521fa314SDavid van Moolenbroek 	for (first = TRUE, left = value; num > 0; fp++, num--) {
371521fa314SDavid van Moolenbroek 		if ((value & fp->mask) == fp->value) {
372521fa314SDavid van Moolenbroek 			if (first)
373521fa314SDavid van Moolenbroek 				first = FALSE;
374521fa314SDavid van Moolenbroek 			else
375521fa314SDavid van Moolenbroek 				put_text(proc, "|");
376521fa314SDavid van Moolenbroek 			put_text(proc, fp->name);
377521fa314SDavid van Moolenbroek 
378521fa314SDavid van Moolenbroek 			left -= fp->value;
379521fa314SDavid van Moolenbroek 		}
380521fa314SDavid van Moolenbroek 	}
381521fa314SDavid van Moolenbroek 
382521fa314SDavid van Moolenbroek 	if (left != 0) {
383521fa314SDavid van Moolenbroek 		if (first)
384521fa314SDavid van Moolenbroek 			first = FALSE;
385521fa314SDavid van Moolenbroek 		else
386521fa314SDavid van Moolenbroek 			put_text(proc, "|");
387521fa314SDavid van Moolenbroek 
388521fa314SDavid van Moolenbroek 		put_fmt(proc, fmt, left);
389521fa314SDavid van Moolenbroek 	}
390521fa314SDavid van Moolenbroek 
391521fa314SDavid van Moolenbroek 	/*
392521fa314SDavid van Moolenbroek 	 * If nothing has been printed so far, simply print a zero.  Ignoring
393521fa314SDavid van Moolenbroek 	 * the given format in this case is intentional: a simple 0 looks
394521fa314SDavid van Moolenbroek 	 * better than 0x0 or 00 etc.
395521fa314SDavid van Moolenbroek 	 */
396521fa314SDavid van Moolenbroek 	if (first)
397521fa314SDavid van Moolenbroek 		put_text(proc, "0");
398521fa314SDavid van Moolenbroek }
399521fa314SDavid van Moolenbroek 
400521fa314SDavid van Moolenbroek /*
401521fa314SDavid van Moolenbroek  * Print a tail field at the end of an array.  The given 'count' value is the
402521fa314SDavid van Moolenbroek  * total number of elements in the array, or 0 to indicate that an error
403521fa314SDavid van Moolenbroek  * occurred.  The given 'printed' value is the number of fields printed so far.
404521fa314SDavid van Moolenbroek  * If some fields have been printed already, the number of fields not printed
405521fa314SDavid van Moolenbroek  * will be shown as "..(+N)".  If no fields have been printed already, the
406521fa314SDavid van Moolenbroek  * (total) number of fields not printed will be shown as "..(N)".  An error
407521fa314SDavid van Moolenbroek  * will print "..(?)".
408521fa314SDavid van Moolenbroek  *
409521fa314SDavid van Moolenbroek  * The rules for printing an array are as follows.  In principle, arrays should
410521fa314SDavid van Moolenbroek  * be enclosed in "[]".  However, if a copy error occurs immediately, a pointer
411521fa314SDavid van Moolenbroek  * to the array should be printed instead.  An empty array should be printed as
412521fa314SDavid van Moolenbroek  * "[]" (not "[..(0)]").  If a copy error occurs in the middle of the array,
413521fa314SDavid van Moolenbroek  * put_tail should be used with count == 0.  Only if not all fields in the
414521fa314SDavid van Moolenbroek  * array are printed, put_tail should be used with count > 0.  The value of
415521fa314SDavid van Moolenbroek  * 'printed' is typically the result of an arbitrary limit set based on the
416521fa314SDavid van Moolenbroek  * verbosity level.
417521fa314SDavid van Moolenbroek  */
418521fa314SDavid van Moolenbroek void
put_tail(struct trace_proc * proc,unsigned int count,unsigned int printed)419521fa314SDavid van Moolenbroek put_tail(struct trace_proc * proc, unsigned int count, unsigned int printed)
420521fa314SDavid van Moolenbroek {
421521fa314SDavid van Moolenbroek 
422521fa314SDavid van Moolenbroek 	if (count == 0)
423521fa314SDavid van Moolenbroek 		put_field(proc, NULL, "..(?)");
424521fa314SDavid van Moolenbroek 	else
425521fa314SDavid van Moolenbroek 		put_value(proc, NULL, "..(%s%u)",
426521fa314SDavid van Moolenbroek 		    (printed > 0) ? "+" : "", count - printed);
427521fa314SDavid van Moolenbroek }
428