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
43static void db_pprint_type(db_addr_t addr, struct ctf_type_v3 *type,
44    u_int depth);
45
46static u_int max_depth = DB_PPRINT_DEFAULT_DEPTH;
47static struct db_ctf_sym_data sym_data;
48
49/*
50 * Pretty-prints a CTF_INT type.
51 */
52static inline void
53db_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 */
82static inline void
83db_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 */
164static inline void
165db_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 */
205static inline void
206db_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 */
244static inline void
245db_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 */
286static void
287db_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 */
340static void
341db_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 */
378static void
379db_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 */
401void
402db_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