Re: git: c21bc6f3c242 - main - ddb: Add CTF-based pretty printing
- In reply to: Bojan Novković : "git: c21bc6f3c242 - main - ddb: Add CTF-based pretty printing"
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 22 Mar 2024 15:17:28 UTC
Am Fri, 22 Mar 2024 03:56:19 GMT
Bojan Novković <bnovkov@FreeBSD.org> schrieb:
This commit breaks buildkernel of GENERIC and custom configs, when either
options KDB
or
options DDB
or both are commented out:
[...]
--- kernel ---
linking kernel
ld: error: undefined symbol: db_ctf_lookup_typename
>>> referenced by link_elf_obj.c
>>> link_elf_obj.o:(link_elf_ctf_lookup_typename)
>>> referenced by link_elf.c
>>> link_elf.o:(link_elf_ctf_lookup_typename)
*** [kernel] Error code 1
make[2]: stopped in /usr/obj/usr/src/amd64.amd64/sys/THOR
make[2]: 1 error
make[2]: stopped in /usr/obj/usr/src/amd64.amd64/sys/THOR
605.46 real 2122.47 user 197.81 sys
make[1]: stopped in /usr/src
make: stopped in /usr/src
> The branch main has been updated by bnovkov:
>
> URL: https://cgit.FreeBSD.org/src/commit/?id=c21bc6f3c2425de74141bfee07b609bf65b5a6b3
>
> commit c21bc6f3c2425de74141bfee07b609bf65b5a6b3
> Author: Bojan Novković <bnovkov@FreeBSD.org>
> AuthorDate: 2024-03-22 03:01:34 +0000
> Commit: Bojan Novković <bnovkov@FreeBSD.org>
> CommitDate: 2024-03-22 03:03:33 +0000
>
> ddb: Add CTF-based pretty printing
>
> Add basic CTF support and a CTF-powered pretty-printer to ddb.
>
> The db_ctf.* files expose a basic interface for fetching type
> data for ELF symbols, interacting with the CTF string table,
> and translating type identifiers to type data.
>
> The db_pprint.c file uses those interfaces to implement
> a pretty-printer for all kernel ELF symbols.
> The pretty-printer works with symbol names and arbitrary addresses:
> pprint struct thread 0xffffffff8194ad90
>
> Pretty-printing currently only works after the root filesystem
> gets mounted because the CTF info is not available during
> early boot.
>
> Differential Revision: https://reviews.freebsd.org/D37899
> Approved by: markj (mentor)
> ---
> share/man/man4/ddb.4 | 26 +++
> sys/conf/files | 2 +
> sys/ddb/db_command.c | 1 +
> sys/ddb/db_ctf.c | 326 +++++++++++++++++++++++++++++++++++
> sys/ddb/db_ctf.h | 64 +++++++
> sys/ddb/db_pprint.c | 450 ++++++++++++++++++++++++++++++++++++++++++++++++
> sys/ddb/ddb.h | 1 +
> sys/kern/kern_ctf.c | 40 +++++
> sys/kern/kern_linker.c | 68 +++++++-
> sys/kern/link_elf.c | 37 ++++
> sys/kern/link_elf_obj.c | 14 ++
> sys/kern/linker_if.m | 23 +++
> sys/sys/linker.h | 3 +
> 13 files changed, 1054 insertions(+), 1 deletion(-)
>
> diff --git a/share/man/man4/ddb.4 b/share/man/man4/ddb.4
> index 3648c9ca58cb..f3443cbac127 100644
> --- a/share/man/man4/ddb.4
> +++ b/share/man/man4/ddb.4
> @@ -289,6 +289,32 @@ eax = xxxxxx
> ecx = yyyyyy
> .Ed
> .Pp
> +.It Ic pprint Ns Oo Li / Ns Cm d depth Oc Oo Ar name Oc
> +Pretty-print symbol specified by
> +.Ar name
> +using CTF debugging data. Works for all symbols exported by the kernel and loaded kernel
> modules. +.Pp
> +If the
> +.Cm d
> +modifier has been specified, contents of structs nested up to
> +.Ar depth
> +levels deep will also be included in the output.
> +.Ed
> +.Pp
> +.It Ic pprint struct Ns Oo Li / Ns Cm d depth Ic Oc Oo Ar name Oc Ns Op Ns Ar addr
> +Print memory at
> +.Ar addr
> +as struct
> +.Ar name Ns .
> +Works for all structs defined by the kernel and loaded kernel modules.
> +.Pp
> +If the
> +.Cm d
> +modifier has been specified, contents of structs nested up to
> +.Ar depth
> +levels deep will also be included in the output.
> +.Ed
> +.Pp
> .It Xo
> .Ic write Ns Op Li / Ns Cm bhl
> .Ar addr expr1 Op Ar expr2 ...
> diff --git a/sys/conf/files b/sys/conf/files
> index c902bcfdbd52..021829408c0f 100644
> --- a/sys/conf/files
> +++ b/sys/conf/files
> @@ -718,12 +718,14 @@ ddb/db_access.c optional ddb
> ddb/db_break.c optional ddb
> ddb/db_capture.c optional ddb
> ddb/db_command.c optional ddb
> +ddb/db_ctf.c optional ddb
> ddb/db_examine.c optional ddb
> ddb/db_expr.c optional ddb
> ddb/db_input.c optional ddb
> ddb/db_lex.c optional ddb
> ddb/db_main.c optional ddb
> ddb/db_output.c optional ddb
> +ddb/db_pprint.c optional ddb
> ddb/db_print.c optional ddb
> ddb/db_ps.c optional ddb
> ddb/db_run.c optional ddb
> diff --git a/sys/ddb/db_command.c b/sys/ddb/db_command.c
> index 9d79e3b2a6d3..0c88d496f6b8 100644
> --- a/sys/ddb/db_command.c
> +++ b/sys/ddb/db_command.c
> @@ -163,6 +163,7 @@ static struct db_command db_cmds[] = {
> 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.c b/sys/ddb/db_ctf.c
> new file mode 100644
> index 000000000000..03145064885c
> --- /dev/null
> +++ b/sys/ddb/db_ctf.c
> @@ -0,0 +1,326 @@
> +/*-
> + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
> + *
> + * Copyright (c) 2023 Bojan Novković <bnovkov@freebsd.org>
> + *
> + * 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 <sys/cdefs.h>
> +#include <sys/types.h>
> +#include <sys/param.h>
> +#include <sys/systm.h>
> +#include <sys/ctype.h>
> +#include <sys/linker.h>
> +#include <sys/malloc.h>
> +#include <sys/mutex.h>
> +
> +#include <ddb/ddb.h>
> +#include <ddb/db_ctf.h>
> +
> +static const ctf_header_t *
> +db_ctf_fetch_cth(linker_ctf_t *lc)
> +{
> + return (const ctf_header_t *)lc->ctftab;
> +}
> +
> +/*
> + * Tries to look up the ELF symbol -> CTF type identifier mapping by scanning
> + * the CTF object section.
> + */
> +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_INVALID_OFF);
> + }
> + /* Sanity check */
> + if (!(sym >= symtab && sym <= symtab_end)) {
> + return (DB_CTF_INVALID_OFF);
> + }
> +
> + 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_INVALID_OFF;
> + 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);
> +}
> +
> +/*
> + * Returns the size of CTF type 't'.
> + */
> +static u_int
> +db_ctf_type_size(struct ctf_type_v3 *t)
> +{
> + u_int vlen, kind, ssize;
> + u_int type_struct_size, kind_size;
> +
> + vlen = CTF_V3_INFO_VLEN(t->ctt_info);
> + kind = CTF_V3_INFO_KIND(t->ctt_info);
> + ssize = ((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:
> + kind_size = sizeof(uint32_t);
> + break;
> + case CTF_K_ARRAY:
> + kind_size = sizeof(struct ctf_array_v3);
> + break;
> + case CTF_K_UNION:
> + case CTF_K_STRUCT:
> + kind_size = vlen *
> + ((ssize < CTF_V3_LSTRUCT_THRESH) ?
> + sizeof(struct ctf_member_v3) :
> + sizeof(struct ctf_lmember_v3));
> + break;
> + case CTF_K_ENUM:
> + kind_size = vlen * sizeof(struct ctf_enum);
> + break;
> + case CTF_K_FUNCTION:
> + kind_size = 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:
> + kind_size = 0;
> + break;
> + default:
> + db_printf("Error: invalid CTF type kind encountered\n");
> + return (-1);
> + }
> +
> + return (type_struct_size + kind_size);
> +}
> +
> +/*
> + * Looks up type name 'name' in the CTF string table and returns the
> + * corresponding CTF type struct, if any.
> + */
> +struct ctf_type_v3 *
> +db_ctf_typename_to_type(linker_ctf_t *lc, const char *name)
> +{
> + const ctf_header_t *hp = db_ctf_fetch_cth(lc);
> + char *start, *cur, *end;
> + uint32_t stroff = hp->cth_stroff;
> + uint32_t typeoff = hp->cth_typeoff;
> + uint32_t name_stroff;
> + const uint8_t *ctfstart = (const uint8_t *)hp + sizeof(ctf_header_t);
> +
> + u_int skiplen;
> +
> + /* Scan ctf strtab for typename. */
> + start = cur = __DECONST(char *, hp) + sizeof(ctf_header_t) +
> + hp->cth_stroff;
> + end = cur + hp->cth_strlen;
> + while (cur < end) {
> + if (strcmp(cur, name) == 0)
> + break;
> + cur += strlen(cur) + 1;
> + }
> + if (cur >= end)
> + return (NULL);
> + name_stroff = (uint32_t)(cur - start);
> +
> + /* Scan for type containing the found stroff. */
> + while (typeoff < stroff) {
> + struct ctf_type_v3 *t =
> + (struct ctf_type_v3 *)(__DECONST(uint8_t *, ctfstart) +
> + typeoff);
> + /* We found the type struct */
> + if (t->ctt_name == name_stroff) {
> + break;
> + }
> + if ((skiplen = db_ctf_type_size(t)) == -1) {
> + return (NULL);
> + }
> + typeoff += skiplen;
> + }
> + if (typeoff < stroff) {
> + return (struct ctf_type_v3 *)(__DECONST(uint8_t *, ctfstart) +
> + typeoff);
> + } else { /* A type struct was not found */
> + return (NULL);
> + }
> +}
> +
> +/*
> + * Wrapper used by the kernel linker CTF routines.
> + * Currently used to implement lookup of CTF types accross all loaded kernel
> + * modules.
> + */
> +bool
> +db_ctf_lookup_typename(linker_ctf_t *lc, const char *typename)
> +{
> + return (db_ctf_typename_to_type(lc, typename) != NULL);
> +}
> +
> +/*
> + * Returns the type corresponding to the 'typeid' parameter from the CTF type
> + * section.
> + */
> +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;
> + u_int skiplen;
> +
> + /* Find corresponding type */
> + while (typeoff < stroff) {
> + struct ctf_type_v3 *t =
> + (struct ctf_type_v3 *)(__DECONST(uint8_t *, ctfstart) +
> + typeoff);
> +
> + /* We found the type struct */
> + if (cur_typeid == typeid) {
> + break;
> + }
> + cur_typeid++;
> + if ((skiplen = db_ctf_type_size(t)) == -1) {
> + return (NULL);
> + }
> + typeoff += 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;
> + const char *ret;
> +
> + if (stroff >= (hp->cth_stroff + hp->cth_strlen)) {
> + return ("invalid");
> + }
> + ret = ((const char *)hp + sizeof(ctf_header_t)) + stroff;
> + if (*ret == '\0') {
> + return (NULL);
> + }
> +
> + return (ret);
> +}
> +
> +/*
> + * Tries to find the type of the symbol specified in 'sd->sym'.
> + */
> +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_INVALID_OFF) {
> + db_printf("Could not find CTF object offset.\n");
> + return (NULL);
> + }
> +
> + typeid = *(
> + const uint32_t *)(sd->lc.ctftab + sizeof(ctf_header_t) + objtoff);
> +
> + return (db_ctf_typeid_to_type(sd, typeid));
> +}
> +
> +/*
> + * Scans the kernel file and all loaded module for symbol 'name'.
> + */
> +int
> +db_ctf_find_symbol(const char *name, db_ctf_sym_data_t sd)
> +{
> + int error;
> + c_linker_sym_t lsym = NULL;
> +
> + error = linker_ctf_lookup_sym_ddb(name, &lsym, &sd->lc);
> + if (error != 0) {
> + db_printf(
> + "failed to look up symbol and CTF info for %s: error %d\n",
> + name, error);
> + return (error);
> + }
> + sd->sym = __DECONST(Elf_Sym *, lsym);
> +
> + return (0);
> +}
> +
> +/*
> + * Scans the kernel file and all loaded module for type specified by 'typename'.
> + */
> +struct ctf_type_v3 *
> +db_ctf_find_typename(db_ctf_sym_data_t sd, const char *typename)
> +{
> + if (linker_ctf_lookup_typename_ddb(&sd->lc, typename) != 0) {
> + return (NULL);
> + }
> + return (db_ctf_typename_to_type(&sd->lc, typename));
> +}
> diff --git a/sys/ddb/db_ctf.h b/sys/ddb/db_ctf.h
> new file mode 100644
> index 000000000000..6da5f76b6cf6
> --- /dev/null
> +++ b/sys/ddb/db_ctf.h
> @@ -0,0 +1,64 @@
> +/*-
> + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
> + *
> + * Copyright (c) 2023 Bojan Novković <bnovkov@freebsd.org>
> + *
> + * 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 <sys/types.h>
> +#include <sys/ctf.h>
> +#include <sys/linker.h>
> +
> +#include <ddb/ddb.h>
> +#include <ddb/db_sym.h>
> +
> +#define DB_CTF_INVALID_OFF 0xffffffff
> +
> +struct db_ctf_sym_data {
> + linker_ctf_t lc;
> + Elf_Sym *sym;
> +};
> +
> +typedef struct db_ctf_sym_data *db_ctf_sym_data_t;
> +
> +/*
> + * Routines for finding symbols and CTF info accross all loaded linker files.
> + */
> +int db_ctf_find_symbol(const char *name, db_ctf_sym_data_t sd);
> +struct ctf_type_v3 *db_ctf_find_typename(db_ctf_sym_data_t sd,
> + const char *typename);
> +bool db_ctf_lookup_typename(linker_ctf_t *lc, const char *typename);
> +
> +/*
> + * Routines for working with CTF data.
> + */
> +struct ctf_type_v3 *db_ctf_sym_to_type(db_ctf_sym_data_t sd);
> +const char *db_ctf_stroff_to_str(db_ctf_sym_data_t sd, uint32_t off);
> +struct ctf_type_v3 *db_ctf_typename_to_type(linker_ctf_t *lc, const char *name);
> +struct ctf_type_v3 *db_ctf_typeid_to_type(db_ctf_sym_data_t sd,
> + uint32_t typeid);
> +
> +#endif /* !_DDB_DB_CTF_H_ */
> diff --git a/sys/ddb/db_pprint.c b/sys/ddb/db_pprint.c
> new file mode 100644
> index 000000000000..dc7582864957
> --- /dev/null
> +++ b/sys/ddb/db_pprint.c
> @@ -0,0 +1,450 @@
> +/*-
> + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
> + *
> + * Copyright (c) 2022 Bojan Novković <bnovkov@freebsd.org>
> + *
> + * 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 <sys/param.h>
> +#include <sys/systm.h>
> +#include <sys/ctype.h>
> +#include <sys/linker.h>
> +
> +#include <machine/stdarg.h>
> +
> +#include <ddb/ddb.h>
> +#include <ddb/db_ctf.h>
> +#include <ddb/db_lex.h>
> +#include <ddb/db_sym.h>
> +#include <ddb/db_access.h>
> +
> +#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;
> +
> +/*
> + * Pretty-prints a CTF_INT type.
> + */
> +static inline void
> +db_pprint_int(db_addr_t addr, struct ctf_type_v3 *type, u_int depth)
> +{
> + uint32_t data;
> + size_t type_struct_size;
> +
> + type_struct_size = (type->ctt_size == CTF_V3_LSIZE_SENT) ?
> + sizeof(struct ctf_type_v3) :
> + sizeof(struct ctf_stype_v3);
> +
> + 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;
> + }
> + db_printf("0x%lx",
> + db_get_value(addr, (bits / 8) ? (bits / 8) : 1, sign));
> +}
> +
> +/*
> + * Pretty-prints a struct. Nested structs are pretty-printed up 'depth' nested
> + * levels.
> + */
> +static inline void
> +db_pprint_struct(db_addr_t addr, struct ctf_type_v3 *type, u_int depth)
> +{
> + size_t type_struct_size;
> + size_t struct_size;
> + struct ctf_type_v3 *mtype;
> + const char *mname;
> + db_addr_t maddr;
> + u_int vlen;
> +
> + type_struct_size = (type->ctt_size == CTF_V3_LSIZE_SENT) ?
> + sizeof(struct ctf_type_v3) :
> + sizeof(struct ctf_stype_v3);
> + struct_size = ((type->ctt_size == CTF_V3_LSIZE_SENT) ?
> + CTF_TYPE_LSIZE(type) :
> + type->ctt_size);
> + 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;
> + }
> + mtype = db_ctf_typeid_to_type(&sym_data, mp->ctm_type);
> + maddr = addr + mp->ctm_offset;
> + mname = db_ctf_stroff_to_str(&sym_data, mp->ctm_name);
> + db_indent = depth;
> + if (mname != NULL) {
> + db_iprintf("%s = ", mname);
> + } else {
> + db_iprintf("");
> + }
> +
> + db_pprint_type(maddr, mtype, depth + 1);
> + db_printf(",\n");
> + }
> + } 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;
> + }
> + mtype = db_ctf_typeid_to_type(&sym_data, mp->ctlm_type);
> + maddr = addr + CTF_LMEM_OFFSET(mp);
> + mname = db_ctf_stroff_to_str(&sym_data, mp->ctlm_name);
> + db_indent = depth;
> + if (mname != NULL) {
> + db_iprintf("%s = ", mname);
> + } else {
> + db_iprintf("");
> + }
> +
> + db_pprint_type(maddr, mtype, depth + 1);
> + db_printf(",");
> + }
> + }
> + db_indent = depth - 1;
> + db_iprintf("}");
> +}
> +
> +/*
> + * Pretty-prints an array. Each array member is printed out in a separate line
> + * indented with 'depth' spaces.
> + */
> +static inline void
> +db_pprint_arr(db_addr_t addr, struct ctf_type_v3 *type, u_int depth)
> +{
> + struct ctf_type_v3 *elem_type;
> + struct ctf_array_v3 *arr;
> + db_addr_t elem_addr, end;
> + size_t type_struct_size;
> + size_t elem_size;
> +
> + 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);
> + elem_addr = addr;
> + end = addr + (arr->cta_nelems * elem_size);
> +
> + db_indent = depth;
> + db_printf("[\n");
> + /* Loop through and print individual elements. */
> + for (; elem_addr < end; elem_addr += elem_size) {
> + if (db_pager_quit) {
> + return;
> + }
> + db_iprintf("");
> + db_pprint_type(elem_addr, elem_type, depth);
> + if ((elem_addr + elem_size) < end) {
> + db_printf(",\n");
> + }
> + }
> + db_printf("\n");
> + db_indent = depth - 1;
> + db_iprintf("]");
> +}
> +
> +/*
> + * Pretty-prints an enum value. Also prints out symbolic name of value, if any.
> + */
> +static inline void
> +db_pprint_enum(db_addr_t addr, struct ctf_type_v3 *type, u_int depth)
> +{
> + struct ctf_enum *ep, *endp;
> + size_t type_struct_size;
> + const char *valname;
> + db_expr_t val;
> + u_int vlen;
> +
> + type_struct_size = (type->ctt_size == CTF_V3_LSIZE_SENT) ?
> + sizeof(struct ctf_type_v3) :
> + sizeof(struct ctf_stype_v3);
> + vlen = CTF_V3_INFO_VLEN(type->ctt_info);
> + val = db_get_value(addr, sizeof(int), 0);
> +
> + 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 != NULL)
> + db_printf("%s (0x%lx)", valname, val);
> + else
> + db_printf("(0x%lx)", val);
> + break;
> + }
> + }
> +}
> +
> +/*
> + * Pretty-prints a pointer. If the 'depth' parameter is less than the
> + * 'max_depth' global var, the pointer is "dereference", i.e. the contents of
> + * the memory it points to are also printed. The value of the pointer is printed
> + * otherwise.
> + */
> +static inline void
> +db_pprint_ptr(db_addr_t addr, struct ctf_type_v3 *type, u_int depth)
> +{
> + struct ctf_type_v3 *ref_type;
> + const char *qual = "";
> + const char *name;
> + db_addr_t val;
> + u_int kind;
> +
> + 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) {
> + /* Print contents of memory pointed to by this pointer. */
> + db_pprint_type(addr, ref_type, depth + 1);
> + } else {
> + name = db_ctf_stroff_to_str(&sym_data, ref_type->ctt_name);
> + db_indent = depth;
> + if (name != NULL)
> + db_printf("(%s%s *) 0x%lx", qual, name, val);
> + else
> + db_printf("0x%lx", val);
> + }
> +}
> +
> +/*
> + * Pretty-print dispatching function.
> + */
> +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, depth);
> + 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_indent = depth;
> + db_iprintf("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, depth);
> + break;
> + case CTF_K_ARRAY:
> + db_pprint_arr(addr, type, depth);
> + break;
> + case CTF_K_UNKNOWN:
> + case CTF_K_FORWARD:
> + default:
> + break;
> + }
> +}
> +
> +/*
> + * Symbol pretty-printing command.
> + * Syntax: pprint [/d depth] <sym_name>
> + */
> +static void
> +db_pprint_symbol_cmd(const char *name)
> +{
> + db_addr_t addr;
> + int db_indent_old;
> + const char *type_name = NULL;
> + struct ctf_type_v3 *type = NULL;
> +
> + if (db_pager_quit) {
> + return;
> + }
> + /* Clear symbol and CTF info */
> + memset(&sym_data, 0, sizeof(struct db_ctf_sym_data));
> + if (db_ctf_find_symbol(name, &sym_data) != 0) {
> + 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");
> + }
> + addr = sym_data.sym->st_value;
> + type = db_ctf_sym_to_type(&sym_data);
> + if (type == NULL) {
> + db_error("Can't find CTF type info\n");
> + }
> + type_name = db_ctf_stroff_to_str(&sym_data, type->ctt_name);
> + if (type_name != NULL)
> + db_printf("%s ", type_name);
> + db_printf("%s = ", name);
> +
> + db_indent_old = db_indent;
> + db_pprint_type(addr, type, 0);
> + db_indent = db_indent_old;
> +}
> +
> +/*
> + * Command for pretty-printing arbitrary addresses.
> + * Syntax: pprint [/d depth] struct <struct_name> <addr>
> + */
> +static void
> +db_pprint_struct_cmd(db_expr_t addr, const char *struct_name)
> +{
> + int db_indent_old;
> + struct ctf_type_v3 *type = NULL;
> +
> + type = db_ctf_find_typename(&sym_data, struct_name);
> + if (type == NULL) {
> + db_error("Can't find CTF type info\n");
> + return;
> + }
> +
> + db_printf("struct %s ", struct_name);
> + db_printf("%p = ", (void *)addr);
> +
> + db_indent_old = db_indent;
> + db_pprint_type(addr, type, 0);
> + db_indent = db_indent_old;
> +}
> +
> +/*
> + * Pretty print an address or a symbol.
> + */
> +void
> +db_pprint_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif)
> +{
> + int t = 0;
> + const char *name;
> +
> + /* 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");
> + }
> + /* Parse desired depth level */
> + if (strcmp(db_tok_string, "d") == 0) {
> + 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();
> + }
> + /* Parse subcomannd */
> + if (t == tIDENT) {
> + if (strcmp(db_tok_string, "struct") == 0) {
> + t = db_read_token();
> +
> + if (t != tIDENT) {
> + db_error("Invalid struct type name provided\n");
> + }
> + name = db_tok_string;
> +
> + if (db_expression(&addr) == 0) {
> + db_error("Address not provided\n");
> + }
> + db_pprint_struct_cmd(addr, name);
> + } else {
> + name = db_tok_string;
> + db_pprint_symbol_cmd(name);
> + }
> + } else {
> + db_error("Invalid subcommand\n");
> + }
> + db_skip_to_eol();
> +}
> diff --git a/sys/ddb/ddb.h b/sys/ddb/ddb.h
> index 1f388efcb389..bb92fef63e94 100644
> --- a/sys/ddb/ddb.h
> +++ b/sys/ddb/ddb.h
> @@ -297,6 +297,7 @@ db_cmdfcn_t db_trace_until_matching_cmd;
> 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/kern_ctf.c b/sys/kern/kern_ctf.c
> index 748622653eb3..8a2106a15308 100644
> --- a/sys/kern/kern_ctf.c
> *** 331 LINES SKIPPED ***
>
--
O. Hartmann