Page MenuHomeFreeBSD

D37899.id114629.diff
No OneTemporary

D37899.id114629.diff

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|DB_CMD_MEMSAFE),
};
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,16 @@
+#ifndef _DDB_DB_CTF_H_
+#define _DDB_DB_CTF_H_
+
+#include <sys/types.h>
+#include <sys/ctf.h>
+
+#define DB_CTF_OBJTOFF_INVALID 0xffffffff
+
+bool db_ctf_loaded(void);
+
+const ctf_header_t *db_ctf_fetch_cth(void);
+struct ctf_type_v3 *db_ctf_sym_to_type(const Elf_Sym *sym);
+struct ctf_type_v3 *db_ctf_typeid_to_type(uint32_t typeid);
+const char *db_ctf_stroff_to_str(uint32_t off);
+
+#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,245 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 1983, 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * 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 <ddb/db_ctf.h>
+#include <ddb/ddb.h>
+
+static struct ddb_ctf {
+ linker_ctf_t kernel_ctf;
+ bool loaded;
+} db_ctf;
+
+static void
+db_ctf_init(void *arg)
+{
+ int err;
+
+ memset((void *)&db_ctf, 0, sizeof(db_ctf));
+
+ err = linker_ctf_get_ddb(linker_kernel_file, &db_ctf.kernel_ctf);
+ if (err) {
+ printf("%s: linker_ctf_get_ddb error: %d\n", __func__, err);
+ return;
+ }
+
+ printf("%s: loaded kernel CTF info\n", __func__);
+
+ db_ctf.loaded = true;
+}
+
+SYSINIT(db_ctf, SI_SUB_KLD, SI_ORDER_FOURTH, db_ctf_init, NULL);
+
+bool
+db_ctf_loaded(void)
+{
+ return db_ctf.loaded;
+}
+
+const ctf_header_t *
+db_ctf_fetch_cth(void)
+{
+ return (const ctf_header_t *)db_ctf.kernel_ctf.ctftab;
+}
+
+static uint32_t
+sym_to_objtoff(const Elf_Sym *sym, const Elf_Sym *symtab,
+ const Elf_Sym *symtab_end)
+{
+ const ctf_header_t *hp = db_ctf_fetch_cth();
+ 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(uint32_t typeid)
+{
+ const ctf_header_t *hp = db_ctf_fetch_cth();
+ 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(uint32_t off)
+{
+ const ctf_header_t *hp = db_ctf_fetch_cth();
+ 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(const Elf_Sym *sym)
+{
+ uint32_t objtoff, typeid;
+ const Elf_Sym *symtab, *symtab_end;
+
+ if (sym == NULL) {
+ return (NULL);
+ }
+
+ symtab = db_ctf.kernel_ctf.symtab;
+ symtab_end = symtab + db_ctf.kernel_ctf.nsym;
+
+ objtoff = sym_to_objtoff(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 *)(db_ctf.kernel_ctf.ctftab +
+ sizeof(ctf_header_t) + objtoff);
+
+ return db_ctf_typeid_to_type(typeid);
+}
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,395 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 1983, 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * 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 <ddb/ddb.h>
+#include <ddb/db_access.h>
+#include <ddb/db_ctf.h>
+#include <ddb/db_lex.h>
+#include <ddb/db_sym.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 inline void
+db_pprint_int(db_addr_t addr, struct ctf_type_v3 *type)
+{
+ if (db_pager_quit) {
+ return;
+ }
+
+ 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 (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(
+ mp->ctm_type);
+ db_addr_t maddr = addr + mp->ctm_offset;
+
+ mname = db_ctf_stroff_to_str(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(
+ mp->ctlm_type);
+ db_addr_t maddr = addr + CTF_LMEM_OFFSET(mp);
+
+ mname = db_ctf_stroff_to_str(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(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(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(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(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(
+ 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(const Elf_Sym *sym)
+{
+ db_addr_t addr = 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);
+ if (!type) {
+ db_printf("Cant find CTF type info\n");
+ return -1;
+ }
+
+ db_symbol_values((c_db_sym_t)sym, &sym_name, &_val);
+ type_name = db_ctf_stroff_to_str(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;
+ Elf_Sym *sym;
+ db_expr_t off;
+
+ if (!db_ctf_loaded()) {
+ db_error("Kernel CTF data not present\n");
+ }
+
+ /* 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");
+ }
+
+ addr = db_tok_number;
+
+ sym = __DECONST(Elf_Sym *, db_search_symbol(addr, DB_STGY_ANY, &off));
+ if (sym == NULL) {
+ db_error("Symbol not found\n");
+ }
+
+ if (ELF_ST_TYPE(sym->st_info) != STT_OBJECT) {
+ db_error("Symbol is not a variable\n");
+ }
+
+ if (db_pprint_symbol(sym)) {
+ 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.

File Metadata

Mime Type
text/plain
Expires
Mon, Apr 20, 9:45 AM (18 h, 57 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
31831073
Default Alt Text
D37899.id114629.diff (16 KB)

Event Timeline