xref: /illumos-gate/usr/src/cmd/mdb/common/mdb/mdb_stack.c (revision 3350c9c925acb5854315e9d992703db756886095)
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