xref: /freebsd/sys/ddb/db_pprint.c (revision 7fdf597e96a02165cfe22ff357b857d5fa15ed8a)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2022 Bojan Novković <bnovkov@freebsd.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/param.h>
29 #include <sys/systm.h>
30 #include <sys/ctype.h>
31 #include <sys/linker.h>
32 
33 #include <machine/stdarg.h>
34 
35 #include <ddb/ddb.h>
36 #include <ddb/db_ctf.h>
37 #include <ddb/db_lex.h>
38 #include <ddb/db_sym.h>
39 #include <ddb/db_access.h>
40 
41 #define DB_PPRINT_DEFAULT_DEPTH 1
42 
43 static void db_pprint_type(db_addr_t addr, struct ctf_type_v3 *type,
44     u_int depth);
45 
46 static u_int max_depth = DB_PPRINT_DEFAULT_DEPTH;
47 static struct db_ctf_sym_data sym_data;
48 static const char *asteriskstr = "*****";
49 
50 /*
51  * Pretty-prints a CTF_INT type.
52  */
53 static inline void
54 db_pprint_int(db_addr_t addr, struct ctf_type_v3 *type, u_int depth)
55 {
56 	uint32_t data;
57 	size_t type_struct_size;
58 
59 	type_struct_size = (type->ctt_size == CTF_V3_LSIZE_SENT) ?
60 		sizeof(struct ctf_type_v3) :
61 		sizeof(struct ctf_stype_v3);
62 
63 	data = db_get_value((db_expr_t)type + type_struct_size,
64 		sizeof(uint32_t), 0);
65 	u_int bits = CTF_INT_BITS(data);
66 	boolean_t sign = !!(CTF_INT_ENCODING(data) & CTF_INT_SIGNED);
67 
68 	if (db_pager_quit) {
69 		return;
70 	}
71 	if (bits > 64) {
72 		db_printf("Invalid size '%d' found for integer type\n", bits);
73 		return;
74 	}
75 	db_printf("0x%lx",
76 	    (long)db_get_value(addr, (bits / 8) ? (bits / 8) : 1, sign));
77 }
78 
79 /*
80  * Pretty-prints a struct. Nested structs are pretty-printed up 'depth' nested
81  * levels.
82  */
83 static inline void
84 db_pprint_struct(db_addr_t addr, struct ctf_type_v3 *type, u_int depth)
85 {
86 	size_t type_struct_size;
87 	size_t struct_size;
88 	struct ctf_type_v3 *mtype;
89 	const char *mname;
90 	db_addr_t maddr;
91 	u_int vlen;
92 
93 	type_struct_size = (type->ctt_size == CTF_V3_LSIZE_SENT) ?
94 		sizeof(struct ctf_type_v3) :
95 		sizeof(struct ctf_stype_v3);
96 	struct_size = ((type->ctt_size == CTF_V3_LSIZE_SENT) ?
97 		CTF_TYPE_LSIZE(type) :
98 		type->ctt_size);
99 	vlen = CTF_V3_INFO_VLEN(type->ctt_info);
100 
101 	if (db_pager_quit) {
102 		return;
103 	}
104 	if (depth > max_depth) {
105 		db_printf("{ ... }");
106 		return;
107 	}
108 	db_printf("{\n");
109 
110 	if (struct_size < CTF_V3_LSTRUCT_THRESH) {
111 		struct ctf_member_v3 *mp, *endp;
112 
113 		mp = (struct ctf_member_v3 *)((db_addr_t)type +
114 		    type_struct_size);
115 		endp = mp + vlen;
116 		for (; mp < endp; mp++) {
117 			if (db_pager_quit) {
118 				return;
119 			}
120 			mtype = db_ctf_typeid_to_type(&sym_data, mp->ctm_type);
121 			maddr = addr + (mp->ctm_offset / NBBY);
122 			mname = db_ctf_stroff_to_str(&sym_data, mp->ctm_name);
123 			db_indent = depth;
124 			if (mname != NULL) {
125 				db_iprintf("%s = ", mname);
126 			} else {
127 				db_iprintf("");
128 			}
129 
130 			db_pprint_type(maddr, mtype, depth + 1);
131 			db_printf(",\n");
132 		}
133 	} else {
134 		struct ctf_lmember_v3 *mp, *endp;
135 
136 		mp = (struct ctf_lmember_v3 *)((db_addr_t)type +
137 		    type_struct_size);
138 		endp = mp + vlen;
139 		for (; mp < endp; mp++) {
140 			if (db_pager_quit) {
141 				return;
142 			}
143 			mtype = db_ctf_typeid_to_type(&sym_data, mp->ctlm_type);
144 			maddr = addr + (CTF_LMEM_OFFSET(mp) / NBBY);
145 			mname = db_ctf_stroff_to_str(&sym_data, mp->ctlm_name);
146 			db_indent = depth;
147 			if (mname != NULL) {
148 				db_iprintf("%s = ", mname);
149 			} else {
150 				db_iprintf("");
151 			}
152 
153 			db_pprint_type(maddr, mtype, depth + 1);
154 			db_printf(",");
155 		}
156 	}
157 	db_indent = depth - 1;
158 	db_iprintf("}");
159 }
160 
161 /*
162  * Pretty-prints an array. Each array member is printed out in a separate line
163  * indented with 'depth' spaces.
164  */
165 static inline void
166 db_pprint_arr(db_addr_t addr, struct ctf_type_v3 *type, u_int depth)
167 {
168 	struct ctf_type_v3 *elem_type;
169 	struct ctf_array_v3 *arr;
170 	db_addr_t elem_addr, end;
171 	size_t type_struct_size;
172 	size_t elem_size;
173 
174 	type_struct_size = (type->ctt_size == CTF_V3_LSIZE_SENT) ?
175 		sizeof(struct ctf_type_v3) :
176 		sizeof(struct ctf_stype_v3);
177 	arr = (struct ctf_array_v3 *)((db_addr_t)type + type_struct_size);
178 	elem_type = db_ctf_typeid_to_type(&sym_data, arr->cta_contents);
179 	elem_size = ((elem_type->ctt_size == CTF_V3_LSIZE_SENT) ?
180 		CTF_TYPE_LSIZE(elem_type) :
181 		elem_type->ctt_size);
182 	elem_addr = addr;
183 	end = addr + (arr->cta_nelems * elem_size);
184 
185 	db_indent = depth;
186 	db_printf("[\n");
187 	/* Loop through and print individual elements. */
188 	for (; elem_addr < end; elem_addr += elem_size) {
189 		if (db_pager_quit) {
190 			return;
191 		}
192 		db_iprintf("");
193 		db_pprint_type(elem_addr, elem_type, depth);
194 		if ((elem_addr + elem_size) < end) {
195 			db_printf(",\n");
196 		}
197 	}
198 	db_printf("\n");
199 	db_indent = depth - 1;
200 	db_iprintf("]");
201 }
202 
203 /*
204  * Pretty-prints an enum value. Also prints out symbolic name of value, if any.
205  */
206 static inline void
207 db_pprint_enum(db_addr_t addr, struct ctf_type_v3 *type, u_int depth)
208 {
209 	struct ctf_enum *ep, *endp;
210 	size_t type_struct_size;
211 	const char *valname;
212 	db_expr_t val;
213 	u_int vlen;
214 
215 	type_struct_size = (type->ctt_size == CTF_V3_LSIZE_SENT) ?
216 		sizeof(struct ctf_type_v3) :
217 		sizeof(struct ctf_stype_v3);
218 	vlen = CTF_V3_INFO_VLEN(type->ctt_info);
219 	val = db_get_value(addr, sizeof(int), 0);
220 
221 	if (db_pager_quit) {
222 		return;
223 	}
224 	ep = (struct ctf_enum *)((db_addr_t)type + type_struct_size);
225 	endp = ep + vlen;
226 	for (; ep < endp; ep++) {
227 		if (val == ep->cte_value) {
228 			valname = db_ctf_stroff_to_str(&sym_data, ep->cte_name);
229 			if (valname != NULL) {
230 				db_printf("%s (0x%lx)", valname, (long)val);
231 				break;
232 			}
233 		}
234 	}
235 	if (ep == endp)
236 		db_printf("0x%lx", (long)val);
237 }
238 
239 /*
240  * Pretty-prints a pointer. If the 'depth' parameter is less than the
241  * 'max_depth' global var, the pointer is "dereference", i.e. the contents of
242  * the memory it points to are also printed. The value of the pointer is printed
243  * otherwise.
244  */
245 static inline void
246 db_pprint_ptr(db_addr_t addr, struct ctf_type_v3 *type, u_int depth)
247 {
248 	struct ctf_type_v3 *ref_type;
249 	const char *qual = "";
250 	const char *name;
251 	db_addr_t val;
252 	uint32_t tid;
253 	u_int kind;
254 	int ptrcnt;
255 
256 	ptrcnt = 1;
257 	tid = type->ctt_type;
258 again:
259 	ref_type = db_ctf_typeid_to_type(&sym_data, tid);
260 	kind = CTF_V3_INFO_KIND(ref_type->ctt_info);
261 	switch (kind) {
262 	case CTF_K_STRUCT:
263 		qual = "struct ";
264 		break;
265 	case CTF_K_VOLATILE:
266 		qual = "volatile ";
267 		tid = ref_type->ctt_type;
268 		goto again;
269 	case CTF_K_CONST:
270 		qual = "const ";
271 		tid = ref_type->ctt_type;
272 		goto again;
273 	case CTF_K_RESTRICT:
274 		qual = "restrict ";
275 		tid = ref_type->ctt_type;
276 		goto again;
277 	case CTF_K_POINTER:
278 		ptrcnt++;
279 		tid = ref_type->ctt_type;
280 		goto again;
281 	case CTF_K_TYPEDEF:
282 		tid = ref_type->ctt_type;
283 		goto again;
284 	default:
285 		break;
286 	}
287 
288 	ptrcnt = min(ptrcnt, strlen(asteriskstr));
289 	val = (addr != 0) ? db_get_value(addr, sizeof(db_addr_t), false) : 0;
290 	if (depth < max_depth && (val != 0)) {
291 		/* Print contents of memory pointed to by this pointer. */
292 		db_pprint_type(val, ref_type, depth + 1);
293 	} else {
294 		name = db_ctf_stroff_to_str(&sym_data, ref_type->ctt_name);
295 		db_indent = depth;
296 		if (name != NULL)
297 			db_printf("(%s%s %.*s) 0x%lx", qual, name, ptrcnt,
298 			    asteriskstr, (long)val);
299 		else
300 			db_printf("(%s %.*s) 0x%lx", qual, ptrcnt, asteriskstr,
301 			    (long)val);
302 	}
303 }
304 
305 /*
306  * Pretty-print dispatching function.
307  */
308 static void
309 db_pprint_type(db_addr_t addr, struct ctf_type_v3 *type, u_int depth)
310 {
311 
312 	if (db_pager_quit) {
313 		return;
314 	}
315 	if (type == NULL) {
316 		db_printf("unknown type");
317 		return;
318 	}
319 
320 	switch (CTF_V3_INFO_KIND(type->ctt_info)) {
321 	case CTF_K_INTEGER:
322 		db_pprint_int(addr, type, depth);
323 		break;
324 	case CTF_K_UNION:
325 	case CTF_K_STRUCT:
326 		db_pprint_struct(addr, type, depth);
327 		break;
328 	case CTF_K_FUNCTION:
329 	case CTF_K_FLOAT:
330 		db_indent = depth;
331 		db_iprintf("0x%lx", (long)addr);
332 		break;
333 	case CTF_K_POINTER:
334 		db_pprint_ptr(addr, type, depth);
335 		break;
336 	case CTF_K_TYPEDEF:
337 	case CTF_K_VOLATILE:
338 	case CTF_K_RESTRICT:
339 	case CTF_K_CONST: {
340 		struct ctf_type_v3 *ref_type = db_ctf_typeid_to_type(&sym_data,
341 		    type->ctt_type);
342 		db_pprint_type(addr, ref_type, depth);
343 		break;
344 	}
345 	case CTF_K_ENUM:
346 		db_pprint_enum(addr, type, depth);
347 		break;
348 	case CTF_K_ARRAY:
349 		db_pprint_arr(addr, type, depth);
350 		break;
351 	case CTF_K_UNKNOWN:
352 	case CTF_K_FORWARD:
353 	default:
354 		break;
355 	}
356 }
357 
358 /*
359  * Symbol pretty-printing command.
360  * Syntax: pprint [/d depth] <sym_name>
361  */
362 static void
363 db_pprint_symbol_cmd(const char *name)
364 {
365 	db_addr_t addr;
366 	int db_indent_old;
367 	const char *type_name = NULL;
368 	struct ctf_type_v3 *type = NULL;
369 
370 	if (db_pager_quit) {
371 		return;
372 	}
373 	/* Clear symbol and CTF info */
374 	memset(&sym_data, 0, sizeof(struct db_ctf_sym_data));
375 	if (db_ctf_find_symbol(name, &sym_data) != 0) {
376 		db_error("Symbol not found\n");
377 	}
378 	if (ELF_ST_TYPE(sym_data.sym->st_info) != STT_OBJECT) {
379 		db_error("Symbol is not a variable\n");
380 	}
381 	addr = sym_data.sym->st_value;
382 	type = db_ctf_sym_to_type(&sym_data);
383 	if (type == NULL) {
384 		db_error("Can't find CTF type info\n");
385 	}
386 	type_name = db_ctf_stroff_to_str(&sym_data, type->ctt_name);
387 	if (type_name != NULL)
388 		db_printf("%s ", type_name);
389 	db_printf("%s = ", name);
390 
391 	db_indent_old = db_indent;
392 	db_pprint_type(addr, type, 0);
393 	db_indent = db_indent_old;
394 }
395 
396 /*
397  * Command for pretty-printing arbitrary addresses.
398  * Syntax: pprint [/d depth] struct <struct_name> <addr>
399  */
400 static void
401 db_pprint_struct_cmd(db_expr_t addr, const char *struct_name)
402 {
403 	int db_indent_old;
404 	struct ctf_type_v3 *type = NULL;
405 
406 	type = db_ctf_find_typename(&sym_data, struct_name);
407 	if (type == NULL) {
408 		db_error("Can't find CTF type info\n");
409 		return;
410 	}
411 
412 	db_printf("struct %s ", struct_name);
413 	db_printf("%p = ", (void *)addr);
414 
415 	db_indent_old = db_indent;
416 	db_pprint_type(addr, type, 0);
417 	db_indent = db_indent_old;
418 }
419 
420 /*
421  * Pretty print an address or a symbol.
422  */
423 void
424 db_pprint_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif)
425 {
426 	int t = 0;
427 	const char *name;
428 
429 	/* Set default depth */
430 	max_depth = DB_PPRINT_DEFAULT_DEPTH;
431 	/* Parse print modifiers */
432 	t = db_read_token();
433 	if (t == tSLASH) {
434 		t = db_read_token();
435 		if (t != tIDENT) {
436 			db_error("Invalid flag passed\n");
437 		}
438 		/* Parse desired depth level */
439 		if (strcmp(db_tok_string, "d") == 0) {
440 			t = db_read_token();
441 			if (t != tNUMBER) {
442 				db_error("Invalid depth provided\n");
443 			}
444 			max_depth = db_tok_number;
445 		} else {
446 			db_error("Invalid flag passed\n");
447 		}
448 		/* Fetch next token */
449 		t = db_read_token();
450 	}
451 	/* Parse subcomannd */
452 	if (t == tIDENT) {
453 		if (strcmp(db_tok_string, "struct") == 0) {
454 			t = db_read_token();
455 
456 			if (t != tIDENT) {
457 				db_error("Invalid struct type name provided\n");
458 			}
459 			name = db_tok_string;
460 
461 			if (db_expression(&addr) == 0) {
462 				db_error("Address not provided\n");
463 			}
464 			db_pprint_struct_cmd(addr, name);
465 		} else {
466 			name = db_tok_string;
467 			db_pprint_symbol_cmd(name);
468 		}
469 	} else {
470 		db_error("Invalid subcommand\n");
471 	}
472 	db_skip_to_eol();
473 }
474