xref: /freebsd/sys/ddb/db_pprint.c (revision 5f757f3f)
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 
49 /*
50  * Pretty-prints a CTF_INT type.
51  */
52 static inline void
53 db_pprint_int(db_addr_t addr, struct ctf_type_v3 *type, u_int depth)
54 {
55 	uint32_t data;
56 	size_t type_struct_size;
57 
58 	type_struct_size = (type->ctt_size == CTF_V3_LSIZE_SENT) ?
59 		sizeof(struct ctf_type_v3) :
60 		sizeof(struct ctf_stype_v3);
61 
62 	data = db_get_value((db_expr_t)type + type_struct_size,
63 		sizeof(uint32_t), 0);
64 	u_int bits = CTF_INT_BITS(data);
65 	boolean_t sign = !!(CTF_INT_ENCODING(data) & CTF_INT_SIGNED);
66 
67 	if (db_pager_quit) {
68 		return;
69 	}
70 	if (bits > 64) {
71 		db_printf("Invalid size '%d' found for integer type\n", bits);
72 		return;
73 	}
74 	db_printf("0x%lx",
75 	    (long)db_get_value(addr, (bits / 8) ? (bits / 8) : 1, sign));
76 }
77 
78 /*
79  * Pretty-prints a struct. Nested structs are pretty-printed up 'depth' nested
80  * levels.
81  */
82 static inline void
83 db_pprint_struct(db_addr_t addr, struct ctf_type_v3 *type, u_int depth)
84 {
85 	size_t type_struct_size;
86 	size_t struct_size;
87 	struct ctf_type_v3 *mtype;
88 	const char *mname;
89 	db_addr_t maddr;
90 	u_int vlen;
91 
92 	type_struct_size = (type->ctt_size == CTF_V3_LSIZE_SENT) ?
93 		sizeof(struct ctf_type_v3) :
94 		sizeof(struct ctf_stype_v3);
95 	struct_size = ((type->ctt_size == CTF_V3_LSIZE_SENT) ?
96 		CTF_TYPE_LSIZE(type) :
97 		type->ctt_size);
98 	vlen = CTF_V3_INFO_VLEN(type->ctt_info);
99 
100 	if (db_pager_quit) {
101 		return;
102 	}
103 	if (depth > max_depth) {
104 		db_printf("{ ... }");
105 		return;
106 	}
107 	db_printf("{\n");
108 
109 	if (struct_size < CTF_V3_LSTRUCT_THRESH) {
110 		struct ctf_member_v3 *mp, *endp;
111 
112 		mp = (struct ctf_member_v3 *)((db_addr_t)type +
113 		    type_struct_size);
114 		endp = mp + vlen;
115 		for (; mp < endp; mp++) {
116 			if (db_pager_quit) {
117 				return;
118 			}
119 			mtype = db_ctf_typeid_to_type(&sym_data, mp->ctm_type);
120 			maddr = addr + mp->ctm_offset;
121 			mname = db_ctf_stroff_to_str(&sym_data, mp->ctm_name);
122 			db_indent = depth;
123 			if (mname != NULL) {
124 				db_iprintf("%s = ", mname);
125 			} else {
126 				db_iprintf("");
127 			}
128 
129 			db_pprint_type(maddr, mtype, depth + 1);
130 			db_printf(",\n");
131 		}
132 	} else {
133 		struct ctf_lmember_v3 *mp, *endp;
134 
135 		mp = (struct ctf_lmember_v3 *)((db_addr_t)type +
136 		    type_struct_size);
137 		endp = mp + vlen;
138 		for (; mp < endp; mp++) {
139 			if (db_pager_quit) {
140 				return;
141 			}
142 			mtype = db_ctf_typeid_to_type(&sym_data, mp->ctlm_type);
143 			maddr = addr + CTF_LMEM_OFFSET(mp);
144 			mname = db_ctf_stroff_to_str(&sym_data, mp->ctlm_name);
145 			db_indent = depth;
146 			if (mname != NULL) {
147 				db_iprintf("%s = ", mname);
148 			} else {
149 				db_iprintf("");
150 			}
151 
152 			db_pprint_type(maddr, mtype, depth + 1);
153 			db_printf(",");
154 		}
155 	}
156 	db_indent = depth - 1;
157 	db_iprintf("}");
158 }
159 
160 /*
161  * Pretty-prints an array. Each array member is printed out in a separate line
162  * indented with 'depth' spaces.
163  */
164 static inline void
165 db_pprint_arr(db_addr_t addr, struct ctf_type_v3 *type, u_int depth)
166 {
167 	struct ctf_type_v3 *elem_type;
168 	struct ctf_array_v3 *arr;
169 	db_addr_t elem_addr, end;
170 	size_t type_struct_size;
171 	size_t elem_size;
172 
173 	type_struct_size = (type->ctt_size == CTF_V3_LSIZE_SENT) ?
174 		sizeof(struct ctf_type_v3) :
175 		sizeof(struct ctf_stype_v3);
176 	arr = (struct ctf_array_v3 *)((db_addr_t)type + type_struct_size);
177 	elem_type = db_ctf_typeid_to_type(&sym_data, arr->cta_contents);
178 	elem_size = ((elem_type->ctt_size == CTF_V3_LSIZE_SENT) ?
179 		CTF_TYPE_LSIZE(elem_type) :
180 		elem_type->ctt_size);
181 	elem_addr = addr;
182 	end = addr + (arr->cta_nelems * elem_size);
183 
184 	db_indent = depth;
185 	db_printf("[\n");
186 	/* Loop through and print individual elements. */
187 	for (; elem_addr < end; elem_addr += elem_size) {
188 		if (db_pager_quit) {
189 			return;
190 		}
191 		db_iprintf("");
192 		db_pprint_type(elem_addr, elem_type, depth);
193 		if ((elem_addr + elem_size) < end) {
194 			db_printf(",\n");
195 		}
196 	}
197 	db_printf("\n");
198 	db_indent = depth - 1;
199 	db_iprintf("]");
200 }
201 
202 /*
203  * Pretty-prints an enum value. Also prints out symbolic name of value, if any.
204  */
205 static inline void
206 db_pprint_enum(db_addr_t addr, struct ctf_type_v3 *type, u_int depth)
207 {
208 	struct ctf_enum *ep, *endp;
209 	size_t type_struct_size;
210 	const char *valname;
211 	db_expr_t val;
212 	u_int vlen;
213 
214 	type_struct_size = (type->ctt_size == CTF_V3_LSIZE_SENT) ?
215 		sizeof(struct ctf_type_v3) :
216 		sizeof(struct ctf_stype_v3);
217 	vlen = CTF_V3_INFO_VLEN(type->ctt_info);
218 	val = db_get_value(addr, sizeof(int), 0);
219 
220 	if (db_pager_quit) {
221 		return;
222 	}
223 	ep = (struct ctf_enum *)((db_addr_t)type + type_struct_size);
224 	endp = ep + vlen;
225 	for (; ep < endp; ep++) {
226 		if (val == ep->cte_value) {
227 			valname = db_ctf_stroff_to_str(&sym_data, ep->cte_name);
228 			if (valname != NULL) {
229 				db_printf("%s (0x%lx)", valname, (long)val);
230 				break;
231 			}
232 		}
233 	}
234 	if (ep == endp)
235 		db_printf("0x%lx", (long)val);
236 }
237 
238 /*
239  * Pretty-prints a pointer. If the 'depth' parameter is less than the
240  * 'max_depth' global var, the pointer is "dereference", i.e. the contents of
241  * the memory it points to are also printed. The value of the pointer is printed
242  * otherwise.
243  */
244 static inline void
245 db_pprint_ptr(db_addr_t addr, struct ctf_type_v3 *type, u_int depth)
246 {
247 	struct ctf_type_v3 *ref_type;
248 	const char *qual = "";
249 	const char *name;
250 	db_addr_t val;
251 	u_int kind;
252 
253 	ref_type = db_ctf_typeid_to_type(&sym_data, type->ctt_type);
254 	kind = CTF_V3_INFO_KIND(ref_type->ctt_info);
255 	switch (kind) {
256 	case CTF_K_STRUCT:
257 		qual = "struct ";
258 		break;
259 	case CTF_K_VOLATILE:
260 		qual = "volatile ";
261 		break;
262 	case CTF_K_CONST:
263 		qual = "const ";
264 		break;
265 	default:
266 		break;
267 	}
268 
269 	val = db_get_value(addr, sizeof(db_addr_t), false);
270 	if (depth < max_depth) {
271 		/* Print contents of memory pointed to by this pointer. */
272 		db_pprint_type(addr, ref_type, depth + 1);
273 	} else {
274 		name = db_ctf_stroff_to_str(&sym_data, ref_type->ctt_name);
275 		db_indent = depth;
276 		if (name != NULL)
277 			db_printf("(%s%s *) 0x%lx", qual, name, (long)val);
278 		else
279 			db_printf("(%s *) 0x%lx", qual, (long)val);
280 	}
281 }
282 
283 /*
284  * Pretty-print dispatching function.
285  */
286 static void
287 db_pprint_type(db_addr_t addr, struct ctf_type_v3 *type, u_int depth)
288 {
289 
290 	if (db_pager_quit) {
291 		return;
292 	}
293 	if (type == NULL) {
294 		db_printf("unknown type");
295 		return;
296 	}
297 
298 	switch (CTF_V3_INFO_KIND(type->ctt_info)) {
299 	case CTF_K_INTEGER:
300 		db_pprint_int(addr, type, depth);
301 		break;
302 	case CTF_K_UNION:
303 	case CTF_K_STRUCT:
304 		db_pprint_struct(addr, type, depth);
305 		break;
306 	case CTF_K_FUNCTION:
307 	case CTF_K_FLOAT:
308 		db_indent = depth;
309 		db_iprintf("0x%lx", (long)addr);
310 		break;
311 	case CTF_K_POINTER:
312 		db_pprint_ptr(addr, type, depth);
313 		break;
314 	case CTF_K_TYPEDEF:
315 	case CTF_K_VOLATILE:
316 	case CTF_K_RESTRICT:
317 	case CTF_K_CONST: {
318 		struct ctf_type_v3 *ref_type = db_ctf_typeid_to_type(&sym_data,
319 		    type->ctt_type);
320 		db_pprint_type(addr, ref_type, depth);
321 		break;
322 	}
323 	case CTF_K_ENUM:
324 		db_pprint_enum(addr, type, depth);
325 		break;
326 	case CTF_K_ARRAY:
327 		db_pprint_arr(addr, type, depth);
328 		break;
329 	case CTF_K_UNKNOWN:
330 	case CTF_K_FORWARD:
331 	default:
332 		break;
333 	}
334 }
335 
336 /*
337  * Symbol pretty-printing command.
338  * Syntax: pprint [/d depth] <sym_name>
339  */
340 static void
341 db_pprint_symbol_cmd(const char *name)
342 {
343 	db_addr_t addr;
344 	int db_indent_old;
345 	const char *type_name = NULL;
346 	struct ctf_type_v3 *type = NULL;
347 
348 	if (db_pager_quit) {
349 		return;
350 	}
351 	/* Clear symbol and CTF info */
352 	memset(&sym_data, 0, sizeof(struct db_ctf_sym_data));
353 	if (db_ctf_find_symbol(name, &sym_data) != 0) {
354 		db_error("Symbol not found\n");
355 	}
356 	if (ELF_ST_TYPE(sym_data.sym->st_info) != STT_OBJECT) {
357 		db_error("Symbol is not a variable\n");
358 	}
359 	addr = sym_data.sym->st_value;
360 	type = db_ctf_sym_to_type(&sym_data);
361 	if (type == NULL) {
362 		db_error("Can't find CTF type info\n");
363 	}
364 	type_name = db_ctf_stroff_to_str(&sym_data, type->ctt_name);
365 	if (type_name != NULL)
366 		db_printf("%s ", type_name);
367 	db_printf("%s = ", name);
368 
369 	db_indent_old = db_indent;
370 	db_pprint_type(addr, type, 0);
371 	db_indent = db_indent_old;
372 }
373 
374 /*
375  * Command for pretty-printing arbitrary addresses.
376  * Syntax: pprint [/d depth] struct <struct_name> <addr>
377  */
378 static void
379 db_pprint_struct_cmd(db_expr_t addr, const char *struct_name)
380 {
381 	int db_indent_old;
382 	struct ctf_type_v3 *type = NULL;
383 
384 	type = db_ctf_find_typename(&sym_data, struct_name);
385 	if (type == NULL) {
386 		db_error("Can't find CTF type info\n");
387 		return;
388 	}
389 
390 	db_printf("struct %s ", struct_name);
391 	db_printf("%p = ", (void *)addr);
392 
393 	db_indent_old = db_indent;
394 	db_pprint_type(addr, type, 0);
395 	db_indent = db_indent_old;
396 }
397 
398 /*
399  * Pretty print an address or a symbol.
400  */
401 void
402 db_pprint_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif)
403 {
404 	int t = 0;
405 	const char *name;
406 
407 	/* Set default depth */
408 	max_depth = DB_PPRINT_DEFAULT_DEPTH;
409 	/* Parse print modifiers */
410 	t = db_read_token();
411 	if (t == tSLASH) {
412 		t = db_read_token();
413 		if (t != tIDENT) {
414 			db_error("Invalid flag passed\n");
415 		}
416 		/* Parse desired depth level */
417 		if (strcmp(db_tok_string, "d") == 0) {
418 			t = db_read_token();
419 			if (t != tNUMBER) {
420 				db_error("Invalid depth provided\n");
421 			}
422 			max_depth = db_tok_number;
423 		} else {
424 			db_error("Invalid flag passed\n");
425 		}
426 		/* Fetch next token */
427 		t = db_read_token();
428 	}
429 	/* Parse subcomannd */
430 	if (t == tIDENT) {
431 		if (strcmp(db_tok_string, "struct") == 0) {
432 			t = db_read_token();
433 
434 			if (t != tIDENT) {
435 				db_error("Invalid struct type name provided\n");
436 			}
437 			name = db_tok_string;
438 
439 			if (db_expression(&addr) == 0) {
440 				db_error("Address not provided\n");
441 			}
442 			db_pprint_struct_cmd(addr, name);
443 		} else {
444 			name = db_tok_string;
445 			db_pprint_symbol_cmd(name);
446 		}
447 	} else {
448 		db_error("Invalid subcommand\n");
449 	}
450 	db_skip_to_eol();
451 }
452