diff --git a/sys/conf/files b/sys/conf/files --- a/sys/conf/files +++ b/sys/conf/files @@ -735,6 +735,8 @@ ddb/db_variables.c optional ddb ddb/db_watch.c optional ddb ddb/db_write_cmd.c optional ddb +ddb/db_pprint.c optional ddb +ddb/db_ctf.c optional ddb dev/aac/aac.c optional aac dev/aac/aac_cam.c optional aacp aac dev/aac/aac_debug.c optional aac diff --git a/sys/ddb/db_command.c b/sys/ddb/db_command.c --- a/sys/ddb/db_command.c +++ b/sys/ddb/db_command.c @@ -166,6 +166,7 @@ DB_CMD("capture", db_capture_cmd, CS_OWN|DB_CMD_MEMSAFE), DB_CMD("textdump", db_textdump_cmd, CS_OWN|DB_CMD_MEMSAFE), DB_CMD("findstack", db_findstack_cmd, 0), + DB_CMD("pprint", db_pprint_cmd, CS_OWN), }; struct db_command_table db_cmd_table = LIST_HEAD_INITIALIZER(db_cmd_table); diff --git a/sys/ddb/db_ctf.h b/sys/ddb/db_ctf.h new file mode 100644 --- /dev/null +++ b/sys/ddb/db_ctf.h @@ -0,0 +1,59 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2022 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _DDB_DB_CTF_H_ +#define _DDB_DB_CTF_H_ + +#include +#include +#include + +#include +#include + +#define DB_CTF_OBJTOFF_INVALID 0xffffffff + +int db_ctf_register(linker_file_t module); +int db_ctf_unregister(linker_file_t module); + +struct db_ctf_sym_data { + linker_ctf_t *lc; + Elf_Sym *sym; +}; + +typedef struct db_ctf_sym_data *db_ctf_sym_data_t; + +struct ctf_type_v3 *db_ctf_sym_to_type(db_ctf_sym_data_t sd); +struct ctf_type_v3 *db_ctf_typeid_to_type(db_ctf_sym_data_t sd, + uint32_t typeid); +const char *db_ctf_stroff_to_str(db_ctf_sym_data_t sd, uint32_t off); +int db_ctf_find_symbol(db_expr_t addr, db_ctf_sym_data_t sd); +void db_ctf_init_kctf(vm_offset_t ksymtab, vm_offset_t kstrtab, + vm_offset_t ksymtab_size); +linker_ctf_t *db_ctf_fetch_kctf(void); + +#endif /* !_DDB_DB_CTF_H_ */ diff --git a/sys/ddb/db_ctf.c b/sys/ddb/db_ctf.c new file mode 100644 --- /dev/null +++ b/sys/ddb/db_ctf.c @@ -0,0 +1,375 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2022 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +struct db_ctf { + linker_ctf_t lc; + char *modname; + LIST_ENTRY(db_ctf) link; +}; + +static LIST_HEAD(, db_ctf) ctf_table = SLIST_HEAD_INITIALIZER(ctf_table); +static struct mtx db_ctf_mtx; +MTX_SYSINIT(db_ctf, &db_ctf_mtx, "ddb module CTF data registry", MTX_DEF); + +static MALLOC_DEFINE(M_DBCTF, "ddb ctf", "ddb module ctf data"); + +/* Used to register kernel CTF data before SUB_KLD. */ +static struct db_ctf kctf; + +static struct db_ctf * +db_ctf_lookup(const char *modname) +{ + struct db_ctf *dcp; + + LIST_FOREACH (dcp, &ctf_table, link) { + if (dcp->modname != NULL && strcmp(modname, dcp->modname) == 0) + break; + } + + return (dcp); +} + +int +db_ctf_register(linker_file_t mod) +{ + struct db_ctf *dcp; + char *modname = mod->filename; + + mtx_lock(&db_ctf_mtx); + if (db_ctf_lookup(modname) != NULL) { + mtx_unlock(&db_ctf_mtx); + printf("%s: ddb CTF data for module %s already loaded!\n", + __func__, modname); + + return (EINVAL); + } + mtx_unlock(&db_ctf_mtx); + + dcp = malloc(sizeof(struct db_ctf), M_DBCTF, M_WAITOK); + if (linker_ctf_get(mod, &dcp->lc) != 0) { + free(dcp, M_DBCTF); + return (EINVAL); + } + dcp->modname = strdup(modname, M_DBCTF); + + mtx_lock(&db_ctf_mtx); + LIST_INSERT_HEAD(&ctf_table, dcp, link); + mtx_unlock(&db_ctf_mtx); + + return (0); +} + +int +db_ctf_unregister(linker_file_t mod) +{ + struct db_ctf *dcp; + char *modname = mod->filename; + + mtx_lock(&db_ctf_mtx); + dcp = db_ctf_lookup(modname); + if (dcp == NULL) { + mtx_unlock(&db_ctf_mtx); + printf("%s: ddb CTF data for module %s already loaded!\n", + __func__, modname); + + return (EINVAL); + } + mtx_unlock(&db_ctf_mtx); + + mtx_lock(&db_ctf_mtx); + LIST_REMOVE(dcp, link); + mtx_unlock(&db_ctf_mtx); + + free(dcp->modname, M_TEMP); + free(dcp, M_DBCTF); + + return (0); +} + +static const ctf_header_t * +db_ctf_fetch_cth(linker_ctf_t *lc) +{ + return (const ctf_header_t *)lc->ctftab; +} + +static uint32_t +sym_to_objtoff(linker_ctf_t *lc, const Elf_Sym *sym, const Elf_Sym *symtab, + const Elf_Sym *symtab_end) +{ + const ctf_header_t *hp = db_ctf_fetch_cth(lc); + uint32_t objtoff = hp->cth_objtoff; + const size_t idwidth = 4; + + /* Ignore non-object symbols */ + if (ELF_ST_TYPE(sym->st_info) != STT_OBJECT) { + return DB_CTF_OBJTOFF_INVALID; + } + + /* Sanity check */ + if (!(sym >= symtab && sym <= symtab_end)) { + return DB_CTF_OBJTOFF_INVALID; + } + + for (const Elf_Sym *symp = symtab; symp < symtab_end; symp++) { + /* Make sure we do not go beyond the objtoff section */ + if (objtoff >= hp->cth_funcoff) { + objtoff = DB_CTF_OBJTOFF_INVALID; + break; + } + + if (symp->st_name == 0 || symp->st_shndx == SHN_UNDEF) { + continue; + } + + if ((symp->st_shndx == SHN_ABS && symp->st_value == 0)) { + continue; + } + + /* Skip non-object symbols */ + if (ELF_ST_TYPE(symp->st_info) != STT_OBJECT) { + continue; + } + + if (symp == sym) { + break; + } + + objtoff += idwidth; + } + + return objtoff; +} + +struct ctf_type_v3 * +db_ctf_typeid_to_type(db_ctf_sym_data_t sd, uint32_t typeid) +{ + const ctf_header_t *hp = db_ctf_fetch_cth(sd->lc); + const uint8_t *ctfstart = (const uint8_t *)hp + sizeof(ctf_header_t); + + uint32_t typeoff = hp->cth_typeoff; + uint32_t stroff = hp->cth_stroff; + /* CTF typeids start at 0x1 */ + size_t cur_typeid = 1; + + /* Find corresponding type */ + while (typeoff < stroff) { + u_int vlen, kind, size; + size_t skiplen, type_struct_size; + struct ctf_type_v3 *t = + (struct ctf_type_v3 *)(__DECONST(uint8_t *, ctfstart) + + typeoff); + + vlen = CTF_V3_INFO_VLEN(t->ctt_info); + kind = CTF_V3_INFO_KIND(t->ctt_info); + size = ((t->ctt_size == CTF_V3_LSIZE_SENT) ? CTF_TYPE_LSIZE(t) : + t->ctt_size); + type_struct_size = ((t->ctt_size == CTF_V3_LSIZE_SENT) ? + sizeof(struct ctf_type_v3) : + sizeof(struct ctf_stype_v3)); + + switch (kind) { + case CTF_K_INTEGER: + case CTF_K_FLOAT: + skiplen = sizeof(uint32_t); + break; + case CTF_K_ARRAY: + skiplen = sizeof(struct ctf_array_v3); + break; + case CTF_K_UNION: + case CTF_K_STRUCT: + skiplen = vlen * + ((size < CTF_V3_LSTRUCT_THRESH) ? + sizeof(struct ctf_member_v3) : + sizeof(struct ctf_lmember_v3)); + break; + case CTF_K_ENUM: + skiplen = vlen * sizeof(struct ctf_enum); + break; + case CTF_K_FUNCTION: + skiplen = vlen * sizeof(uint32_t); + break; + case CTF_K_UNKNOWN: + case CTF_K_FORWARD: + case CTF_K_POINTER: + case CTF_K_TYPEDEF: + case CTF_K_VOLATILE: + case CTF_K_CONST: + case CTF_K_RESTRICT: + skiplen = 0; + break; + default: + db_printf("Error: invalid CTF type kind encountered\n"); + return (NULL); + } + + /* We found the type struct */ + if (cur_typeid == typeid) { + break; + } + + cur_typeid++; + typeoff += type_struct_size + skiplen; + } + + if (typeoff < stroff) { + return (struct ctf_type_v3 *)(__DECONST(uint8_t *, ctfstart) + + typeoff); + } else { /* A type struct was not found */ + return (NULL); + } +} + +const char * +db_ctf_stroff_to_str(db_ctf_sym_data_t sd, uint32_t off) +{ + const ctf_header_t *hp = db_ctf_fetch_cth(sd->lc); + uint32_t stroff = hp->cth_stroff + off; + + if (stroff >= (hp->cth_stroff + hp->cth_strlen)) { + return "invalid"; + } + + const char *ret = ((const char *)hp + sizeof(ctf_header_t)) + stroff; + if (*ret == '\0') { + return NULL; + } + + return ret; +} + +struct ctf_type_v3 * +db_ctf_sym_to_type(db_ctf_sym_data_t sd) +{ + uint32_t objtoff, typeid; + const Elf_Sym *symtab, *symtab_end; + + if (sd->sym == NULL) { + return (NULL); + } + + symtab = sd->lc->symtab; + symtab_end = symtab + sd->lc->nsym; + + objtoff = sym_to_objtoff(sd->lc, sd->sym, symtab, symtab_end); + /* Sanity check - should not happen */ + if (objtoff == DB_CTF_OBJTOFF_INVALID) { + db_printf("Could not find CTF object offset."); + return (NULL); + } + + typeid = *( + const uint32_t *)(sd->lc->ctftab + sizeof(ctf_header_t) + objtoff); + + return db_ctf_typeid_to_type(sd, typeid); +} + +int +db_ctf_find_symbol(db_expr_t addr, db_ctf_sym_data_t sd) +{ + db_expr_t off; + struct db_ctf *dcp; + + sd->sym = __DECONST(Elf_Sym *, + db_search_symbol(addr, DB_STGY_ANY, &off)); + if (sd->sym == NULL) { + return (ENOENT); + } + + dcp = db_ctf_lookup(linker_kernel_file->filename); + if (dcp == NULL) { + return (ENOENT); + } + + sd->lc = &dcp->lc; + + return (0); +} + +void +db_ctf_init_kctf(vm_offset_t ksymtab, vm_offset_t kstrtab, + vm_offset_t ksymtab_size) +{ + const ctf_header_t *hp; + uint8_t *ctf_start; + size_t size; + void *mod; + + mod = preload_search_by_type("ddb_kctf"); + if (mod == NULL) { + return; + } + + ctf_start = preload_fetch_addr(mod); + size = preload_fetch_size(mod); + bzero(&kctf.lc, sizeof(kctf.lc)); + hp = (const ctf_header_t *)ctf_start; + + /* Sanity check. */ + if (hp->cth_magic != CTF_MAGIC) { + printf("%s: bad kernel CTF magic value\n", __func__); + return; + } + + if (hp->cth_version != CTF_VERSION_3) { + printf("%s: CTF V2 data encountered\n", __func__); + return; + } + + /* We only deal with uncompressed data */ + if (hp->cth_flags & CTF_F_COMPRESS) { + printf("%s: kernel CTF data is compressed\n", __func__); + return; + } + + kctf.lc.ctftab = ctf_start; + kctf.lc.ctfcnt = size; + kctf.lc.symtab = (const Elf_Sym *)ksymtab; + kctf.lc.nsym = ksymtab_size / sizeof(Elf_Sym); + kctf.lc.strtab = (const char *)kstrtab; + kctf.modname = "kernel"; + + LIST_INSERT_HEAD(&ctf_table, &kctf, link); +} + +linker_ctf_t * +db_ctf_fetch_kctf(void) +{ + return (kctf.modname != NULL ? &kctf.lc : NULL); +} diff --git a/sys/ddb/db_main.c b/sys/ddb/db_main.c --- a/sys/ddb/db_main.c +++ b/sys/ddb/db_main.c @@ -32,9 +32,9 @@ #include #include #include -#include #include #include +#include #include #include #include @@ -44,9 +44,10 @@ #include #include -#include #include +#include #include +#include struct db_private { char* strtab; @@ -228,6 +229,7 @@ ksymtab_private.relbase = ksymtab_relbase; db_add_symbol_table((char *)ksymtab, (char *)(ksymtab + ksymtab_size), "elf", (char *)&ksymtab_private); + db_ctf_init_kctf(ksymtab, kstrtab, ksymtab_size); } db_add_symbol_table(NULL, NULL, "kld", NULL); return (1); /* We're the default debugger. */ diff --git a/sys/ddb/db_pprint.c b/sys/ddb/db_pprint.c new file mode 100644 --- /dev/null +++ b/sys/ddb/db_pprint.c @@ -0,0 +1,391 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2022 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define DB_PPRINT_DEFAULT_DEPTH 1 + +static void db_pprint_type(db_addr_t addr, struct ctf_type_v3 *type, + u_int depth); + +static u_int max_depth = DB_PPRINT_DEFAULT_DEPTH; +static struct db_ctf_sym_data sym_data; + +static inline void +db_pprint_int(db_addr_t addr, struct ctf_type_v3 *type) +{ + size_t type_struct_size = ((type->ctt_size == CTF_V3_LSIZE_SENT) ? + sizeof(struct ctf_type_v3) : + sizeof(struct ctf_stype_v3)); + uint32_t data = db_get_value((db_expr_t)type + type_struct_size, + sizeof(uint32_t), 0); + + u_int bits = CTF_INT_BITS(data); + boolean_t sign = !!(CTF_INT_ENCODING(data) & CTF_INT_SIGNED); + + if (db_pager_quit) { + return; + } + + if (bits > 64) { + db_printf("Invalid size '%d' found for integer type\n", bits); + return; + } + + int nbytes = (bits / 8) ? (bits / 8) : 1; + db_printf("0x%lx", db_get_value(addr, nbytes, sign)); +} + +static inline void +db_pprint_struct(db_addr_t addr, struct ctf_type_v3 *type, u_int depth) +{ + const char *mname; + + size_t type_struct_size = ((type->ctt_size == CTF_V3_LSIZE_SENT) ? + sizeof(struct ctf_type_v3) : + sizeof(struct ctf_stype_v3)); + size_t struct_size = ((type->ctt_size == CTF_V3_LSIZE_SENT) ? + CTF_TYPE_LSIZE(type) : + type->ctt_size); + u_int vlen = CTF_V3_INFO_VLEN(type->ctt_info); + + if (db_pager_quit) { + return; + } + + if (depth > max_depth) { + db_printf("{ ... }, "); + return; + } + + db_printf("{\n"); + + if (struct_size < CTF_V3_LSTRUCT_THRESH) { + struct ctf_member_v3 *mp, *endp; + + mp = (struct ctf_member_v3 *)((db_addr_t)type + + type_struct_size); + endp = mp + vlen; + + for (; mp < endp; mp++) { + if (db_pager_quit) { + return; + } + + struct ctf_type_v3 *mtype = + db_ctf_typeid_to_type(&sym_data, mp->ctm_type); + db_addr_t maddr = addr + mp->ctm_offset; + + mname = db_ctf_stroff_to_str(&sym_data, mp->ctm_name); + if (mname) { + db_printf("%s = ", mname); + } + + db_pprint_type(maddr, mtype, depth + 1); + db_printf(", "); + } + } else { + struct ctf_lmember_v3 *mp, *endp; + mp = (struct ctf_lmember_v3 *)((db_addr_t)type + + type_struct_size); + endp = mp + vlen; + + for (; mp < endp; mp++) { + if (db_pager_quit) { + return; + } + + struct ctf_type_v3 *mtype = + db_ctf_typeid_to_type(&sym_data, mp->ctlm_type); + db_addr_t maddr = addr + CTF_LMEM_OFFSET(mp); + + mname = db_ctf_stroff_to_str(&sym_data, mp->ctlm_name); + if (mname) { + db_printf("%s = ", mname); + } + + db_pprint_type(maddr, mtype, depth + 1); + db_printf(", "); + } + } + + db_printf("\n}"); +} + +static inline void +db_pprint_arr(db_addr_t addr, struct ctf_type_v3 *type, u_int depth) +{ + struct ctf_array_v3 *arr; + struct ctf_type_v3 *elem_type; + size_t elem_size; + size_t type_struct_size = ((type->ctt_size == CTF_V3_LSIZE_SENT) ? + sizeof(struct ctf_type_v3) : + sizeof(struct ctf_stype_v3)); + + arr = (struct ctf_array_v3 *)((db_addr_t)type + type_struct_size); + elem_type = db_ctf_typeid_to_type(&sym_data, arr->cta_contents); + elem_size = ((elem_type->ctt_size == CTF_V3_LSIZE_SENT) ? + CTF_TYPE_LSIZE(elem_type) : + elem_type->ctt_size); + + db_addr_t elem_addr = addr; + db_addr_t end = addr + (arr->cta_nelems * elem_size); + + db_printf("["); + for (; elem_addr < end; elem_addr += elem_size) { + if (db_pager_quit) { + return; + } + + db_pprint_type(elem_addr, elem_type, depth); + + if ((elem_addr + elem_size) < end) { + db_printf(", "); + } + } + db_printf("]\n"); +} + +static inline void +db_pprint_enum(db_addr_t addr, struct ctf_type_v3 *type) +{ + struct ctf_enum *ep, *endp; + const char *valname; + u_int vlen = CTF_V3_INFO_VLEN(type->ctt_info); + db_expr_t val = db_get_value(addr, sizeof(int), 0); + size_t type_struct_size = ((type->ctt_size == CTF_V3_LSIZE_SENT) ? + sizeof(struct ctf_type_v3) : + sizeof(struct ctf_stype_v3)); + + if (db_pager_quit) { + return; + } + + ep = (struct ctf_enum *)((db_addr_t)type + type_struct_size); + endp = ep + vlen; + + for (; ep < endp; ep++) { + if (val == ep->cte_value) { + valname = db_ctf_stroff_to_str(&sym_data, ep->cte_name); + if (valname) { + db_printf("%s ", valname); + } + + db_printf("(0x%lx)", val); + break; + } + } +} + +static inline void +db_pprint_ptr(db_addr_t addr, struct ctf_type_v3 *type, u_int depth) +{ + const char *qual = ""; + const char *name; + struct ctf_type_v3 *ref_type; + u_int kind; + db_addr_t val; + + ref_type = db_ctf_typeid_to_type(&sym_data, type->ctt_type); + kind = CTF_V3_INFO_KIND(ref_type->ctt_info); + + switch (kind) { + case CTF_K_STRUCT: + qual = "struct "; + break; + case CTF_K_VOLATILE: + qual = "volatile "; + break; + case CTF_K_CONST: + qual = "const "; + break; + default: + break; + } + + val = db_get_value(addr, sizeof(db_addr_t), false); + + if (depth < max_depth) { + db_pprint_type(addr, ref_type, depth + 1); + } else { + + name = db_ctf_stroff_to_str(&sym_data, ref_type->ctt_name); + if (name) { + db_printf("(%s%s *)", qual, name); + } + + db_printf("0x%lx", val); + } +} + +static void +db_pprint_type(db_addr_t addr, struct ctf_type_v3 *type, u_int depth) +{ + + if (db_pager_quit) { + return; + } + + if (type == NULL) { + db_printf("unknown type"); + return; + } + + switch (CTF_V3_INFO_KIND(type->ctt_info)) { + case CTF_K_INTEGER: + db_pprint_int(addr, type); + break; + case CTF_K_UNION: + case CTF_K_STRUCT: + db_pprint_struct(addr, type, depth); + break; + case CTF_K_FUNCTION: + case CTF_K_FLOAT: + db_printf("0x%lx", addr); + break; + case CTF_K_POINTER: + db_pprint_ptr(addr, type, depth); + break; + case CTF_K_TYPEDEF: + case CTF_K_VOLATILE: + case CTF_K_RESTRICT: + case CTF_K_CONST: { + struct ctf_type_v3 *ref_type = db_ctf_typeid_to_type(&sym_data, + type->ctt_type); + db_pprint_type(addr, ref_type, depth); + break; + } + case CTF_K_ENUM: + db_pprint_enum(addr, type); + break; + case CTF_K_ARRAY: + db_pprint_arr(addr, type, depth); + break; + case CTF_K_UNKNOWN: + case CTF_K_FORWARD: + default: + break; + } +} + +static int +db_pprint_symbol(void) +{ + db_addr_t addr = sym_data.sym->st_value; + struct ctf_type_v3 *type = NULL; + db_expr_t _val; + + const char *sym_name = NULL; + const char *type_name = NULL; + + if (db_pager_quit) { + return -1; + } + + type = db_ctf_sym_to_type(&sym_data); + if (!type) { + db_printf("Cant find CTF type info\n"); + return -1; + } + + db_symbol_values((c_db_sym_t)sym_data.sym, &sym_name, &_val); + type_name = db_ctf_stroff_to_str(&sym_data, type->ctt_name); + + if (type_name) { + db_printf("%s ", type_name); + } + if (sym_name) { + db_printf("%s = ", sym_name); + } + + db_pprint_type(addr, type, 0); + + return 0; +} + +/* + * Pretty print an address. + * Syntax: pprint [/d depth] addr + */ +void +db_pprint_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif) +{ + int t = 0; + + /* Set default depth */ + max_depth = DB_PPRINT_DEFAULT_DEPTH; + + /* Parse print modifiers */ + t = db_read_token(); + if (t == tSLASH) { + t = db_read_token(); + if (t != tIDENT) { + db_error("Invalid flag passed\n"); + } + /* Fetch desired depth level */ + if (!strcmp(db_tok_string, "d")) { + t = db_read_token(); + if (t != tNUMBER) { + db_error("Invalid depth provided\n"); + } + max_depth = db_tok_number; + } else { + db_error("Invalid flag passed\n"); + } + /* Fetch next token */ + t = db_read_token(); + } + + if (t != tNUMBER) { + db_error("No address supplied\n"); + } + + bzero(&sym_data, sizeof(sym_data)); + addr = db_tok_number; + if (db_ctf_find_symbol(addr, &sym_data)) { + db_error("Symbol not found\n"); + } + + if (ELF_ST_TYPE(sym_data.sym->st_info) != STT_OBJECT) { + db_error("Symbol is not a variable\n"); + } + + if (db_pprint_symbol()) { + db_error(""); + } +} diff --git a/sys/ddb/ddb.h b/sys/ddb/ddb.h --- a/sys/ddb/ddb.h +++ b/sys/ddb/ddb.h @@ -275,6 +275,7 @@ db_cmdfcn_t db_unscript_cmd; db_cmdfcn_t db_watchpoint_cmd; db_cmdfcn_t db_write_cmd; +db_cmdfcn_t db_pprint_cmd; /* * Interface between DDB and the DDB output capture facility. diff --git a/sys/kern/link_elf.c b/sys/kern/link_elf.c --- a/sys/kern/link_elf.c +++ b/sys/kern/link_elf.c @@ -70,6 +70,10 @@ #include "linker_if.h" +#ifdef DDB_CTF +#include +#endif + #define MAXSEGS 4 typedef struct elf_file { @@ -498,6 +502,16 @@ (void)link_elf_link_common_finish(linker_kernel_file); linker_kernel_file->flags |= LINKER_FILE_LINKED; + +#ifdef DDB_CTF + /* Check if ddb already loaded kernel CTF data. */ + linker_ctf_t *lc = db_ctf_fetch_kctf(); + if (lc != NULL) { + ef->ctftab = __DECONST(uint8_t *, lc->ctftab); + ef->ctfcnt = lc->ctfcnt; + } +#endif + TAILQ_INIT(&set_pcpu_list); #ifdef VIMAGE TAILQ_INIT(&set_vnet_list);