1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2025 Oxide Computer Company
14 */
15
16 /*
17 * Common code to help printing stack frames in a consistent way, and with
18 * options to include frame size and type data where it can be retrieved from
19 * CTF data.
20 */
21
22 #include <sys/types.h>
23
24 #include <mdb/mdb_string.h>
25 #include <mdb/mdb_modapi.h>
26 #include <mdb/mdb_debug.h>
27 #include <mdb/mdb_ctf.h>
28 #include <mdb/mdb_isautil.h>
29 #include <mdb/mdb_stack.h>
30 #include <mdb/mdb.h>
31
32 typedef struct {
33 mdb_tgt_t *msfd_tgt;
34 uint_t msfd_arglim;
35 mdb_stack_frame_flags_t msfd_flags;
36 uintptr_t msfd_lastbp;
37 boolean_t (*msfd_callcheck)(uintptr_t);
38 char *msfd_buf;
39 size_t msfd_buflen;
40 } mdb_stack_frame_data_t;
41
42 mdb_stack_frame_hdl_t *
mdb_stack_frame_init(mdb_tgt_t * tgt,uint_t arglim,mdb_stack_frame_flags_t flags)43 mdb_stack_frame_init(mdb_tgt_t *tgt, uint_t arglim,
44 mdb_stack_frame_flags_t flags)
45 {
46 mdb_stack_frame_data_t *data;
47
48 data = mdb_alloc(sizeof (*data), UM_SLEEP | UM_GC);
49 if (data == NULL)
50 return (NULL);
51 data->msfd_tgt = tgt;
52 data->msfd_arglim = arglim;
53 data->msfd_flags = flags;
54 data->msfd_lastbp = 0;
55 data->msfd_buf = NULL;
56 data->msfd_buflen = 0;
57
58 return (data);
59 }
60
61 uint_t
mdb_stack_frame_arglim(mdb_stack_frame_hdl_t * datap)62 mdb_stack_frame_arglim(mdb_stack_frame_hdl_t *datap)
63 {
64 mdb_stack_frame_data_t *data = datap;
65
66 return (data->msfd_arglim);
67 }
68
69 void
mdb_stack_frame_flags_set(mdb_stack_frame_hdl_t * datap,mdb_stack_frame_flags_t flags)70 mdb_stack_frame_flags_set(mdb_stack_frame_hdl_t *datap,
71 mdb_stack_frame_flags_t flags)
72 {
73 mdb_stack_frame_data_t *data = datap;
74
75 ASSERT((flags & ~MSF_ALL) == 0);
76 data->msfd_flags |= flags;
77 }
78
79 static char *
mdb_stack_typename(mdb_ctf_id_t id,char ** bufp,size_t * lenp)80 mdb_stack_typename(mdb_ctf_id_t id, char **bufp, size_t *lenp)
81 {
82 char *buf = *bufp;
83 size_t len = *lenp;
84 ssize_t newlen;
85
86 if (mdb_ctf_type_name(id, buf, len) != NULL)
87 return (buf);
88
89 /*
90 * Retrieve the buffer size required to store this type.
91 */
92 newlen = mdb_ctf_type_lname(id, NULL, 0) + 1;
93 /*
94 * To avoid reallocations in most cases, we always allocate at least
95 * space for 32 characters and the NUL terminator. This will
96 * accommodate most types.
97 */
98 newlen = MAX(newlen, 33);
99 if (newlen > len) {
100 char *newbuf = mdb_alloc(newlen, UM_SLEEP | UM_GC);
101 if (newbuf == NULL)
102 return (NULL);
103 mdb_free(buf, len);
104 *bufp = newbuf;
105 *lenp = newlen;
106 return (mdb_ctf_type_name(id, newbuf, newlen));
107 }
108
109 return (NULL);
110 }
111
112 void
mdb_stack_frame(mdb_stack_frame_hdl_t * datap,uintptr_t pc,uintptr_t bp,uint_t argc,const long * argv)113 mdb_stack_frame(mdb_stack_frame_hdl_t *datap, uintptr_t pc, uintptr_t bp,
114 uint_t argc, const long *argv)
115 {
116 mdb_stack_frame_data_t *data = datap;
117 uint_t nargc = MIN(argc, data->msfd_arglim);
118 mdb_ctf_id_t argtypes[nargc];
119 mdb_ctf_funcinfo_t mcfi;
120 boolean_t ctf;
121 mdb_syminfo_t msi;
122 uintptr_t npc;
123 GElf_Sym sym;
124 uint_t i;
125 int ret;
126
127 ctf = B_FALSE;
128 npc = pc;
129
130 ret = mdb_tgt_lookup_by_addr(data->msfd_tgt, pc, MDB_TGT_SYM_FUZZY,
131 NULL, 0, &sym, &msi);
132
133 if (ret != 0 || sym.st_value == pc) {
134 /*
135 * One of two things is going on here. Either:
136 *
137 * - this address is not covered by a symbol, or
138 * - there is a symbol but our address points directly to the
139 * start of it.
140 *
141 * Both cases can arise when the return address is from a call
142 * to a function that the compiler knows will never return. In
143 * these cases the compiler may elide the caller’s epilogue,
144 * leaving the return address pointing just past the end of the
145 * callee; either into the next function or into padding
146 * between functions.
147 *
148 * If the previous address is covered by a symbol, we use that
149 * symbol instead and mark it as approximate with a tilde (~)
150 * in the output. The platform must provide a callback that
151 * uses heuristics to to determine whether the preceding
152 * instruction could plausibly represent a function call that
153 * would result in the current return address. For example, an
154 * unconditional jump is typically not valid as it would not
155 * preserve the return address.
156 */
157 if (pc > 0) {
158 ret = mdb_tgt_lookup_by_addr(data->msfd_tgt, pc - 1,
159 MDB_TGT_SYM_FUZZY, NULL, 0, &sym, &msi);
160 if (ret == 0 && mdb_isa_prev_callcheck(pc))
161 npc = pc - 1;
162 }
163 }
164
165 if (ret == 0 && (data->msfd_flags & MSF_TYPES)) {
166 if (mdb_ctf_func_info(&sym, &msi, &mcfi) == 0)
167 ctf = B_TRUE;
168 }
169
170 if (data->msfd_flags & MSF_SIZES) {
171 if (data->msfd_lastbp != 0)
172 mdb_printf("[%4lr] ", bp - data->msfd_lastbp);
173 else
174 mdb_printf("%7s", "");
175 data->msfd_lastbp = bp;
176 }
177
178 if (data->msfd_flags & MSF_VERBOSE)
179 mdb_printf("%0?lr ", bp);
180
181 if (ctf) {
182 if (mdb_stack_typename(mcfi.mtf_return,
183 &data->msfd_buf, &data->msfd_buflen) != NULL) {
184 mdb_printf("%s ", data->msfd_buf);
185 }
186 }
187
188 if (data->msfd_flags & MSF_ADDR) {
189 mdb_printf("%0?lr(", pc);
190 } else {
191 if (npc != pc)
192 mdb_printf("~");
193 mdb_printf("%a(", npc);
194 }
195
196 if (ctf && mdb_ctf_func_args(&mcfi, nargc, argtypes) != 0)
197 ctf = B_FALSE;
198
199 for (i = 0; i < nargc; i++) {
200 if (i > 0)
201 mdb_printf(", ");
202 if (ctf && mdb_stack_typename(argtypes[i],
203 &data->msfd_buf, &data->msfd_buflen) != NULL) {
204 const char *type = data->msfd_buf;
205
206 switch (mdb_ctf_type_kind(argtypes[i])) {
207 case CTF_K_POINTER:
208 if (argv[i] == 0)
209 mdb_printf("(%s)NULL", type);
210 else
211 mdb_printf("(%s)%lr", type, argv[i]);
212 break;
213 case CTF_K_ENUM: {
214 const char *cp;
215
216 cp = mdb_ctf_enum_name(argtypes[i], argv[i]);
217 if (cp != NULL)
218 mdb_printf("(%s)%s", type, cp);
219 else
220 mdb_printf("(%s)%lr", type, argv[i]);
221 break;
222 }
223 default:
224 mdb_printf("(%s)%lr", type, argv[i]);
225 }
226 } else {
227 mdb_printf("%lr", argv[i]);
228 }
229 }
230
231 mdb_printf(")\n");
232 }
233