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 #include <sys/stdarg.h>
33
34 #include <ddb/ddb.h>
35 #include <ddb/db_ctf.h>
36 #include <ddb/db_lex.h>
37 #include <ddb/db_sym.h>
38 #include <ddb/db_access.h>
39
40 #define DB_PPRINT_DEFAULT_DEPTH 1
41
42 static void db_pprint_type(db_addr_t addr, struct ctf_type_v3 *type,
43 u_int depth);
44
45 static u_int max_depth = DB_PPRINT_DEFAULT_DEPTH;
46 static struct db_ctf_sym_data sym_data;
47 static const char *asteriskstr = "*****";
48
49 /*
50 * Pretty-prints a CTF_INT type.
51 */
52 static inline void
db_pprint_int(db_addr_t addr,struct ctf_type_v3 * type,u_int depth)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
db_pprint_struct(db_addr_t addr,struct ctf_type_v3 * type,u_int depth)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 / NBBY);
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) / NBBY);
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
db_pprint_arr(db_addr_t addr,struct ctf_type_v3 * type,u_int depth)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
db_pprint_enum(db_addr_t addr,struct ctf_type_v3 * type,u_int depth)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
db_pprint_ptr(db_addr_t addr,struct ctf_type_v3 * type,u_int depth)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 uint32_t tid;
252 u_int kind;
253 int ptrcnt;
254
255 ptrcnt = 1;
256 tid = type->ctt_type;
257 again:
258 ref_type = db_ctf_typeid_to_type(&sym_data, tid);
259 kind = CTF_V3_INFO_KIND(ref_type->ctt_info);
260 switch (kind) {
261 case CTF_K_STRUCT:
262 qual = "struct ";
263 break;
264 case CTF_K_VOLATILE:
265 qual = "volatile ";
266 tid = ref_type->ctt_type;
267 goto again;
268 case CTF_K_CONST:
269 qual = "const ";
270 tid = ref_type->ctt_type;
271 goto again;
272 case CTF_K_RESTRICT:
273 qual = "restrict ";
274 tid = ref_type->ctt_type;
275 goto again;
276 case CTF_K_POINTER:
277 ptrcnt++;
278 tid = ref_type->ctt_type;
279 goto again;
280 case CTF_K_TYPEDEF:
281 tid = ref_type->ctt_type;
282 goto again;
283 default:
284 break;
285 }
286
287 ptrcnt = min(ptrcnt, strlen(asteriskstr));
288 val = (addr != 0) ? db_get_value(addr, sizeof(db_addr_t), false) : 0;
289 if (depth < max_depth && (val != 0)) {
290 /* Print contents of memory pointed to by this pointer. */
291 db_pprint_type(val, ref_type, depth + 1);
292 } else {
293 name = db_ctf_stroff_to_str(&sym_data, ref_type->ctt_name);
294 db_indent = depth;
295 if (name != NULL)
296 db_printf("(%s%s %.*s) 0x%lx", qual, name, ptrcnt,
297 asteriskstr, (long)val);
298 else
299 db_printf("(%s %.*s) 0x%lx", qual, ptrcnt, asteriskstr,
300 (long)val);
301 }
302 }
303
304 /*
305 * Pretty-print dispatching function.
306 */
307 static void
db_pprint_type(db_addr_t addr,struct ctf_type_v3 * type,u_int depth)308 db_pprint_type(db_addr_t addr, struct ctf_type_v3 *type, u_int depth)
309 {
310
311 if (db_pager_quit) {
312 return;
313 }
314 if (type == NULL) {
315 db_printf("unknown type");
316 return;
317 }
318
319 switch (CTF_V3_INFO_KIND(type->ctt_info)) {
320 case CTF_K_INTEGER:
321 db_pprint_int(addr, type, depth);
322 break;
323 case CTF_K_UNION:
324 case CTF_K_STRUCT:
325 db_pprint_struct(addr, type, depth);
326 break;
327 case CTF_K_FUNCTION:
328 case CTF_K_FLOAT:
329 db_indent = depth;
330 db_iprintf("0x%lx", (long)addr);
331 break;
332 case CTF_K_POINTER:
333 db_pprint_ptr(addr, type, depth);
334 break;
335 case CTF_K_TYPEDEF:
336 case CTF_K_VOLATILE:
337 case CTF_K_RESTRICT:
338 case CTF_K_CONST: {
339 struct ctf_type_v3 *ref_type = db_ctf_typeid_to_type(&sym_data,
340 type->ctt_type);
341 db_pprint_type(addr, ref_type, depth);
342 break;
343 }
344 case CTF_K_ENUM:
345 db_pprint_enum(addr, type, depth);
346 break;
347 case CTF_K_ARRAY:
348 db_pprint_arr(addr, type, depth);
349 break;
350 case CTF_K_UNKNOWN:
351 case CTF_K_FORWARD:
352 default:
353 break;
354 }
355 }
356
357 /*
358 * Symbol pretty-printing command.
359 * Syntax: pprint [/d depth] <sym_name>
360 */
361 static void
db_pprint_symbol_cmd(const char * name)362 db_pprint_symbol_cmd(const char *name)
363 {
364 db_addr_t addr;
365 int db_indent_old;
366 const char *type_name = NULL;
367 struct ctf_type_v3 *type = NULL;
368
369 if (db_pager_quit) {
370 return;
371 }
372 /* Clear symbol and CTF info */
373 memset(&sym_data, 0, sizeof(struct db_ctf_sym_data));
374 if (db_ctf_find_symbol(name, &sym_data) != 0) {
375 db_error("Symbol not found\n");
376 }
377 if (ELF_ST_TYPE(sym_data.sym->st_info) != STT_OBJECT) {
378 db_error("Symbol is not a variable\n");
379 }
380 addr = sym_data.sym->st_value;
381 type = db_ctf_sym_to_type(&sym_data);
382 if (type == NULL) {
383 db_error("Can't find CTF type info\n");
384 }
385 type_name = db_ctf_stroff_to_str(&sym_data, type->ctt_name);
386 if (type_name != NULL)
387 db_printf("%s ", type_name);
388 db_printf("%s = ", name);
389
390 db_indent_old = db_indent;
391 db_pprint_type(addr, type, 0);
392 db_indent = db_indent_old;
393 }
394
395 /*
396 * Command for pretty-printing arbitrary addresses.
397 * Syntax: pprint [/d depth] struct <struct_name> <addr>
398 */
399 static void
db_pprint_struct_cmd(db_expr_t addr,const char * struct_name)400 db_pprint_struct_cmd(db_expr_t addr, const char *struct_name)
401 {
402 int db_indent_old;
403 struct ctf_type_v3 *type = NULL;
404
405 type = db_ctf_find_typename(&sym_data, struct_name);
406 if (type == NULL) {
407 db_error("Can't find CTF type info\n");
408 return;
409 }
410
411 db_printf("struct %s ", struct_name);
412 db_printf("%p = ", (void *)addr);
413
414 db_indent_old = db_indent;
415 db_pprint_type(addr, type, 0);
416 db_indent = db_indent_old;
417 }
418
419 /*
420 * Pretty print an address or a symbol.
421 */
422 void
db_pprint_cmd(db_expr_t addr,bool have_addr,db_expr_t count,char * modif)423 db_pprint_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif)
424 {
425 int t = 0;
426 const char *name;
427
428 /* Set default depth */
429 max_depth = DB_PPRINT_DEFAULT_DEPTH;
430 /* Parse print modifiers */
431 t = db_read_token();
432 if (t == tSLASH) {
433 t = db_read_token();
434 if (t != tIDENT) {
435 db_error("Invalid flag passed\n");
436 }
437 /* Parse desired depth level */
438 if (strcmp(db_tok_string, "d") == 0) {
439 t = db_read_token();
440 if (t != tNUMBER) {
441 db_error("Invalid depth provided\n");
442 }
443 max_depth = db_tok_number;
444 } else {
445 db_error("Invalid flag passed\n");
446 }
447 /* Fetch next token */
448 t = db_read_token();
449 }
450 /* Parse subcomannd */
451 if (t == tIDENT) {
452 if (strcmp(db_tok_string, "struct") == 0) {
453 t = db_read_token();
454
455 if (t != tIDENT) {
456 db_error("Invalid struct type name provided\n");
457 }
458 name = db_tok_string;
459
460 if (db_expression(&addr) == 0) {
461 db_error("Address not provided\n");
462 }
463 db_pprint_struct_cmd(addr, name);
464 } else {
465 name = db_tok_string;
466 db_pprint_symbol_cmd(name);
467 }
468 } else {
469 db_error("Invalid subcommand\n");
470 }
471 db_skip_to_eol();
472 }
473