From nobody Fri Mar 22 03:56:19 2024 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4V17mM244Hz5Fb1y; Fri, 22 Mar 2024 03:56:19 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4V17mM1httz41R3; Fri, 22 Mar 2024 03:56:19 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1711079779; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=UKx55LseTG/xsPZXSAiJ0UbpQJxdfiWm1CNP0Lo/H6Q=; b=Efu7xBnY/ESoe8qh2F09GnI6+QkvuVc3Lbe7kOzHMYaxK7JWEIVtWhjCY3kJDZgvMBlXf3 KwWYKZ6VkzgXko8NzLPETmCPDSywGCJqrW3+MPbPNwGrcXqsPOU4+gr90Gqmvw70/se8iA sOYqDfefzRCqcuKssCpWReOuAtypVguErGVYLmzT6eFUx0dE0/i2WoIZCv3fGBpKKvlHT6 XdCU9awn2vRsBvUi6hS+7FXTtBOPKmgoxEJRWunhMLIw8r8ZZA8YLHnES2g/iukFSNnOR8 VvgovzmC72c3VxGaKsl9TykKjHWddeSvKIgWx+YMyFxCM4dJF1yFBTgxqMxtLw== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1711079779; a=rsa-sha256; cv=none; b=bq+SEkFGffpSWPfdgteElx+IJROJ75TiLceQv4NUYBjseFNyDtAWMA+H7ZxWeJQehfsPzA TarlvH92gxrMQYFhGka0asRJoinG/SO10vnEufn+VJYXdun9YvbmkHoPTnosuSddW3z3MV 2B4ChNJ7YbLYEfN7N5AYJjC4e8FYNzf7Tt6aoW9FANhxg2kjAbmVJSjXoZ1r3CS/A6G1+f dHmjX04nrFPmo/6elLd4UdXaxTI+cYyJgbB8nC68UXXaRxj+xBUu4WepmNEUUR2AVA70Qk QgpIAfUHakUv13vacoEMOugTZBIUvP/XS6CY5JQ5jD+MdVoAX+zLCwyCu3UkcA== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1711079779; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=UKx55LseTG/xsPZXSAiJ0UbpQJxdfiWm1CNP0Lo/H6Q=; b=vHNb+HaF7Xvu6TrmtkclQWhX7kMlnQ0DcDu3lrRxK1I/PL2tpPnCQsVql9PtYeK+1gCcaN d8z7ir8wMErC1KQ5vKD86nti7DdxfZ/aQQgN2zh2LhVrtsPDpThk2o8HLAiS1bf2XJ9fls 1C+D7cah6DCzNSvv0qDhlaMj2gSSicqPorimlTfOCh9u43f0/9gH8JLxKbeNj43sk9dbO9 acRSyUO8R5KCu6JvgUcvdC+K+m+GOqQcC3dKcJVCIdV4xjD9NQRnKo6HmepQxRomoJDlNJ eEVyVtrXpoON7sLuPyjN4MZFUsnpxzJExNK/JqUoNYNh6b5jgPXNBFiN02wv3Q== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4V17mM1Hwhz13Xy; Fri, 22 Mar 2024 03:56:19 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.17.1/8.17.1) with ESMTP id 42M3uJRn021419; Fri, 22 Mar 2024 03:56:19 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.17.1/8.17.1/Submit) id 42M3uJh3021416; Fri, 22 Mar 2024 03:56:19 GMT (envelope-from git) Date: Fri, 22 Mar 2024 03:56:19 GMT Message-Id: <202403220356.42M3uJh3021416@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Bojan =?utf-8?Q?Novkovi=C4=87?= Subject: git: c21bc6f3c242 - main - ddb: Add CTF-based pretty printing List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-main@freebsd.org X-BeenThere: dev-commits-src-main@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: bnovkov X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: c21bc6f3c2425de74141bfee07b609bf65b5a6b3 Auto-Submitted: auto-generated The branch main has been updated by bnovkov: URL: https://cgit.FreeBSD.org/src/commit/?id=c21bc6f3c2425de74141bfee07b609bf65b5a6b3 commit c21bc6f3c2425de74141bfee07b609bf65b5a6b3 Author: Bojan Novković AuthorDate: 2024-03-22 03:01:34 +0000 Commit: Bojan Novković 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ć + * + * 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 + +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ć + * + * 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_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ć + * + * 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 + +#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] + */ +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 + */ +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 ***