xref: /illumos-gate/usr/src/cmd/mdb/common/modules/libproc/libproc.c (revision bde334a8dbd66dfa70ce4d7fc9dcad6e1ae45fe4)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <libproc.h>
27 #include <Pcontrol.h>
28 #include <stddef.h>
29 
30 #include <mdb/mdb_modapi.h>
31 
32 typedef struct ps_prochandle ps_prochandle_t;
33 
34 /*
35  * addr::pr_symtab [-a | n]
36  *
37  *	-a	Sort symbols by address
38  *	-n	Sort symbols by name
39  *
40  * Given a sym_tbl_t, dump its contents in tabular form.  When given '-a' or
41  * '-n', we use the sorted tables 'sym_byaddr' or 'sym_byname', respectively.
42  */
43 static int
44 pr_symtab(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
45 {
46 	sym_tbl_t symtab;
47 	Elf_Data data_pri;
48 	Elf_Data data_aux;
49 	Elf_Data *data;
50 #ifdef _LP64
51 	Elf64_Sym sym;
52 	int width = 16;
53 #else
54 	Elf32_Sym sym;
55 	int width = 8;
56 #endif
57 	int i, idx, count;
58 	char name[128];
59 	int byaddr = FALSE;
60 	int byname = FALSE;
61 	uint_t *symlist;
62 	size_t symlistsz;
63 
64 	if (mdb_getopts(argc, argv,
65 	    'a', MDB_OPT_SETBITS, TRUE, &byaddr,
66 	    'n', MDB_OPT_SETBITS, TRUE, &byname,
67 	    NULL) != argc)
68 		return (DCMD_USAGE);
69 
70 	if (byaddr && byname) {
71 		mdb_warn("only one of '-a' or '-n' can be specified\n");
72 		return (DCMD_USAGE);
73 	}
74 
75 	if (!(flags & DCMD_ADDRSPEC))
76 		return (DCMD_USAGE);
77 
78 	if (mdb_vread(&symtab, sizeof (sym_tbl_t), addr) == -1) {
79 		mdb_warn("failed to read sym_tbl_t at %p", addr);
80 		return (DCMD_ERR);
81 	}
82 
83 	if (symtab.sym_count == 0) {
84 		mdb_warn("no symbols present\n");
85 		return (DCMD_ERR);
86 	}
87 
88 	/*
89 	 * As described in the libproc header Pcontrol.h, a sym_tbl_t
90 	 * contains a primary and an optional auxiliary symbol table.
91 	 * We treat the combination as a single table, with the auxiliary
92 	 * values coming before the primary ones.
93 	 *
94 	 * Read the primary and auxiliary Elf_Data structs.
95 	 */
96 	if (mdb_vread(&data_pri, sizeof (Elf_Data),
97 	    (uintptr_t)symtab.sym_data_pri) == -1) {
98 		mdb_warn("failed to read primary Elf_Data at %p",
99 		    symtab.sym_data_pri);
100 		return (DCMD_ERR);
101 	}
102 	if ((symtab.sym_symn_aux > 0) &&
103 	    (mdb_vread(&data_aux, sizeof (Elf_Data),
104 	    (uintptr_t)symtab.sym_data_aux) == -1)) {
105 		mdb_warn("failed to read auxiliary Elf_Data at %p",
106 		    symtab.sym_data_aux);
107 		return (DCMD_ERR);
108 	}
109 
110 	symlist = NULL;
111 	if (byaddr || byname) {
112 		uintptr_t src = byaddr ? (uintptr_t)symtab.sym_byaddr :
113 		    (uintptr_t)symtab.sym_byname;
114 
115 		symlistsz = symtab.sym_count * sizeof (uint_t);
116 		symlist = mdb_alloc(symlistsz, UM_SLEEP);
117 		if (mdb_vread(symlist, symlistsz, src) == -1) {
118 			mdb_warn("failed to read sorted symbols at %p", src);
119 			return (DCMD_ERR);
120 		}
121 		count = symtab.sym_count;
122 	} else {
123 		count = symtab.sym_symn;
124 	}
125 
126 	mdb_printf("%<u>%*s  %*s  %s%</u>\n", width, "ADDRESS", width,
127 	    "SIZE", "NAME");
128 
129 	for (i = 0; i < count; i++) {
130 		if (byaddr | byname)
131 			idx = symlist[i];
132 		else
133 			idx = i;
134 
135 		/* If index is in range of primary symtab, look it up there */
136 		if (idx >= symtab.sym_symn_aux) {
137 			data = &data_pri;
138 			idx -= symtab.sym_symn_aux;
139 		} else {	/* Look it up in the auxiliary symtab */
140 			data = &data_aux;
141 		}
142 
143 		if (mdb_vread(&sym, sizeof (sym), (uintptr_t)data->d_buf +
144 		    idx * sizeof (sym)) == -1) {
145 			mdb_warn("failed to read symbol at %p",
146 			    (uintptr_t)data->d_buf + idx * sizeof (sym));
147 			if (symlist)
148 				mdb_free(symlist, symlistsz);
149 			return (DCMD_ERR);
150 		}
151 
152 		if (mdb_readstr(name, sizeof (name),
153 		    (uintptr_t)symtab.sym_strs + sym.st_name) == -1) {
154 			mdb_warn("failed to read symbol name at %p",
155 			    symtab.sym_strs + sym.st_name);
156 			name[0] = '\0';
157 		}
158 
159 		mdb_printf("%0?p  %0?p  %s\n", sym.st_value, sym.st_size,
160 		    name);
161 	}
162 
163 	if (symlist)
164 		mdb_free(symlist, symlistsz);
165 
166 	return (DCMD_OK);
167 }
168 
169 /*
170  * addr::pr_addr2map search
171  *
172  * Given a ps_prochandle_t, convert the given address to the corresponding
173  * map_info_t.  Functionally equivalent to Paddr2mptr().
174  */
175 static int
176 pr_addr2map(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
177 {
178 	uintptr_t search;
179 	ps_prochandle_t psp;
180 	map_info_t *mp;
181 	int lo, hi, mid;
182 
183 	if (!(flags & DCMD_ADDRSPEC) || argc != 1)
184 		return (DCMD_USAGE);
185 
186 	if (argv[0].a_type == MDB_TYPE_IMMEDIATE)
187 		search = argv[0].a_un.a_val;
188 	else
189 		search = mdb_strtoull(argv[0].a_un.a_str);
190 
191 	if (mdb_vread(&psp, sizeof (ps_prochandle_t), addr) == -1) {
192 		mdb_warn("failed to read ps_prochandle at %p", addr);
193 		return (DCMD_ERR);
194 	}
195 
196 	lo = 0;
197 	hi = psp.map_count;
198 	while (lo <= hi) {
199 		mid = (lo + hi) / 2;
200 		mp = &psp.mappings[mid];
201 
202 		if ((addr - mp->map_pmap.pr_vaddr) < mp->map_pmap.pr_size) {
203 			mdb_printf("%#lr\n", addr + offsetof(ps_prochandle_t,
204 			    mappings) + (mp - psp.mappings) *
205 			    sizeof (map_info_t));
206 			return (DCMD_OK);
207 		}
208 
209 		if (addr < mp->map_pmap.pr_vaddr)
210 			hi = mid - 1;
211 		else
212 			lo = mid + 1;
213 	}
214 
215 	mdb_warn("no corresponding map for %p\n", search);
216 	return (DCMD_ERR);
217 }
218 
219 /*
220  * ::walk pr_file_info
221  *
222  * Given a ps_prochandle_t, walk all its file_info_t structures.
223  */
224 typedef struct {
225 	uintptr_t	fiw_next;
226 	int		fiw_count;
227 } file_info_walk_t;
228 
229 static int
230 pr_file_info_walk_init(mdb_walk_state_t *wsp)
231 {
232 	ps_prochandle_t psp;
233 	file_info_walk_t *fiw;
234 
235 	if (wsp->walk_addr == 0) {
236 		mdb_warn("pr_file_info doesn't support global walks\n");
237 		return (WALK_ERR);
238 	}
239 
240 	if (mdb_vread(&psp, sizeof (ps_prochandle_t), wsp->walk_addr) == -1) {
241 		mdb_warn("failed to read ps_prochandle at %p", wsp->walk_addr);
242 		return (WALK_ERR);
243 	}
244 
245 	fiw = mdb_alloc(sizeof (file_info_walk_t), UM_SLEEP);
246 
247 	fiw->fiw_next = (uintptr_t)psp.file_head.list_forw;
248 	fiw->fiw_count = psp.num_files;
249 	wsp->walk_data = fiw;
250 
251 	return (WALK_NEXT);
252 }
253 
254 static int
255 pr_file_info_walk_step(mdb_walk_state_t *wsp)
256 {
257 	file_info_walk_t *fiw = wsp->walk_data;
258 	file_info_t f;
259 	int status;
260 
261 	if (fiw->fiw_count == 0)
262 		return (WALK_DONE);
263 
264 	if (mdb_vread(&f, sizeof (file_info_t), fiw->fiw_next) == -1) {
265 		mdb_warn("failed to read file_info_t at %p", fiw->fiw_next);
266 		return (WALK_ERR);
267 	}
268 
269 	status = wsp->walk_callback(fiw->fiw_next, &f, wsp->walk_cbdata);
270 
271 	fiw->fiw_next = (uintptr_t)f.file_list.list_forw;
272 	fiw->fiw_count--;
273 
274 	return (status);
275 }
276 
277 static void
278 pr_file_info_walk_fini(mdb_walk_state_t *wsp)
279 {
280 	file_info_walk_t *fiw = wsp->walk_data;
281 	mdb_free(fiw, sizeof (file_info_walk_t));
282 }
283 
284 /*
285  * ::walk pr_map_info
286  *
287  * Given a ps_prochandle_t, walk all its map_info_t structures.
288  */
289 typedef struct {
290 	uintptr_t	miw_next;
291 	int		miw_count;
292 	int		miw_current;
293 } map_info_walk_t;
294 
295 static int
296 pr_map_info_walk_init(mdb_walk_state_t *wsp)
297 {
298 	ps_prochandle_t psp;
299 	map_info_walk_t *miw;
300 
301 	if (wsp->walk_addr == 0) {
302 		mdb_warn("pr_map_info doesn't support global walks\n");
303 		return (WALK_ERR);
304 	}
305 
306 	if (mdb_vread(&psp, sizeof (ps_prochandle_t), wsp->walk_addr) == -1) {
307 		mdb_warn("failed to read ps_prochandle at %p", wsp->walk_addr);
308 		return (WALK_ERR);
309 	}
310 
311 	miw = mdb_alloc(sizeof (map_info_walk_t), UM_SLEEP);
312 
313 	miw->miw_next = (uintptr_t)psp.mappings;
314 	miw->miw_count = psp.map_count;
315 	miw->miw_current = 0;
316 	wsp->walk_data = miw;
317 
318 	return (WALK_NEXT);
319 }
320 
321 static int
322 pr_map_info_walk_step(mdb_walk_state_t *wsp)
323 {
324 	map_info_walk_t *miw = wsp->walk_data;
325 	map_info_t m;
326 	int status;
327 
328 	if (miw->miw_current == miw->miw_count)
329 		return (WALK_DONE);
330 
331 	if (mdb_vread(&m, sizeof (map_info_t), miw->miw_next) == -1) {
332 		mdb_warn("failed to read map_info_t at %p", miw->miw_next);
333 		return (WALK_DONE);
334 	}
335 
336 	status = wsp->walk_callback(miw->miw_next, &m, wsp->walk_cbdata);
337 
338 	miw->miw_current++;
339 	miw->miw_next += sizeof (map_info_t);
340 
341 	return (status);
342 }
343 
344 static void
345 pr_map_info_walk_fini(mdb_walk_state_t *wsp)
346 {
347 	map_info_walk_t *miw = wsp->walk_data;
348 	mdb_free(miw, sizeof (map_info_walk_t));
349 }
350 
351 static const mdb_dcmd_t dcmds[] = {
352 	{ "pr_addr2map",  ":addr", "convert an adress into a map_info_t",
353 	    pr_addr2map },
354 	{ "pr_symtab",	":[-a | -n]", "print the contents of a sym_tbl_t",
355 	    pr_symtab },
356 	{ NULL }
357 };
358 
359 static const mdb_walker_t walkers[] = {
360 	{ "pr_file_info", "given a ps_prochandle, walk its file_info "
361 	    "structures", pr_file_info_walk_init, pr_file_info_walk_step,
362 	    pr_file_info_walk_fini },
363 	{ "pr_map_info", "given a ps_prochandle, walk its map_info structures",
364 	    pr_map_info_walk_init, pr_map_info_walk_step,
365 	    pr_map_info_walk_fini },
366 	{ NULL }
367 };
368 
369 static const mdb_modinfo_t modinfo = {
370 	MDB_API_VERSION, dcmds, walkers
371 };
372 
373 const mdb_modinfo_t *
374 _mdb_init(void)
375 {
376 	return (&modinfo);
377 }
378