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 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <libproc.h> 29 #include <Pcontrol.h> 30 #include <stddef.h> 31 32 #include <mdb/mdb_modapi.h> 33 34 typedef struct ps_prochandle ps_prochandle_t; 35 36 /* 37 * addr::pr_symtab [-a | n] 38 * 39 * -a Sort symbols by address 40 * -n Sort symbols by name 41 * 42 * Given a sym_tbl_t, dump its contents in tabular form. When given '-a' or 43 * '-n', we use the sorted tables 'sym_byaddr' or 'sym_byname', respectively. 44 */ 45 static int 46 pr_symtab(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 47 { 48 sym_tbl_t symtab; 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 if (mdb_vread(&data, sizeof (Elf_Data), 89 (uintptr_t)symtab.sym_data) == -1) { 90 mdb_warn("failed to read Elf_Data at %p", symtab.sym_data); 91 return (DCMD_ERR); 92 } 93 94 symlist = NULL; 95 if (byaddr || byname) { 96 uintptr_t src = byaddr ? (uintptr_t)symtab.sym_byaddr : 97 (uintptr_t)symtab.sym_byname; 98 99 symlistsz = symtab.sym_count * sizeof (uint_t); 100 symlist = mdb_alloc(symlistsz, UM_SLEEP); 101 if (mdb_vread(symlist, symlistsz, src) == -1) { 102 mdb_warn("failed to read sorted symbols at %p", src); 103 return (DCMD_ERR); 104 } 105 count = symtab.sym_count; 106 } else { 107 count = symtab.sym_symn; 108 } 109 110 mdb_printf("%<u>%*s %*s %s%</u>\n", width, "ADDRESS", width, 111 "SIZE", "NAME"); 112 113 for (i = 0; i < count; i++) { 114 if (byaddr | byname) 115 idx = symlist[i]; 116 else 117 idx = i; 118 119 if (mdb_vread(&sym, sizeof (sym), (uintptr_t)data.d_buf + 120 idx * sizeof (sym)) == -1) { 121 mdb_warn("failed to read symbol at %p", 122 (uintptr_t)data.d_buf + idx * sizeof (sym)); 123 if (symlist) 124 mdb_free(symlist, symlistsz); 125 return (DCMD_ERR); 126 } 127 128 if (mdb_readstr(name, sizeof (name), 129 (uintptr_t)symtab.sym_strs + sym.st_name) == -1) { 130 mdb_warn("failed to read symbol name at %p", 131 symtab.sym_strs + sym.st_name); 132 name[0] = '\0'; 133 } 134 135 mdb_printf("%0?p %0?p %s\n", sym.st_value, sym.st_size, 136 name); 137 } 138 139 if (symlist) 140 mdb_free(symlist, symlistsz); 141 142 return (DCMD_OK); 143 } 144 145 /* 146 * addr::pr_addr2map search 147 * 148 * Given a ps_prochandle_t, convert the given address to the corresponding 149 * map_info_t. Functionally equivalent to Paddr2mptr(). 150 */ 151 static int 152 pr_addr2map(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 153 { 154 uintptr_t search; 155 ps_prochandle_t psp; 156 map_info_t *mp; 157 int lo, hi, mid; 158 159 if (!(flags & DCMD_ADDRSPEC) || argc != 1) 160 return (DCMD_USAGE); 161 162 if (argv[0].a_type == MDB_TYPE_IMMEDIATE) 163 search = argv[0].a_un.a_val; 164 else 165 search = mdb_strtoull(argv[0].a_un.a_str); 166 167 if (mdb_vread(&psp, sizeof (ps_prochandle_t), addr) == -1) { 168 mdb_warn("failed to read ps_prochandle at %p", addr); 169 return (DCMD_ERR); 170 } 171 172 lo = 0; 173 hi = psp.map_count; 174 while (lo <= hi) { 175 mid = (lo + hi) / 2; 176 mp = &psp.mappings[mid]; 177 178 if ((addr - mp->map_pmap.pr_vaddr) < mp->map_pmap.pr_size) { 179 mdb_printf("%#lr\n", addr + offsetof(ps_prochandle_t, 180 mappings) + (mp - psp.mappings) * 181 sizeof (map_info_t)); 182 return (DCMD_OK); 183 } 184 185 if (addr < mp->map_pmap.pr_vaddr) 186 hi = mid - 1; 187 else 188 lo = mid + 1; 189 } 190 191 mdb_warn("no corresponding map for %p\n", search); 192 return (DCMD_ERR); 193 } 194 195 /* 196 * ::walk pr_file_info 197 * 198 * Given a ps_prochandle_t, walk all its file_info_t structures. 199 */ 200 typedef struct { 201 uintptr_t fiw_next; 202 int fiw_count; 203 } file_info_walk_t; 204 205 static int 206 pr_file_info_walk_init(mdb_walk_state_t *wsp) 207 { 208 ps_prochandle_t psp; 209 file_info_walk_t *fiw; 210 211 if (wsp->walk_addr == NULL) { 212 mdb_warn("pr_file_info doesn't support global walks\n"); 213 return (WALK_ERR); 214 } 215 216 if (mdb_vread(&psp, sizeof (ps_prochandle_t), wsp->walk_addr) == -1) { 217 mdb_warn("failed to read ps_prochandle at %p", wsp->walk_addr); 218 return (WALK_ERR); 219 } 220 221 fiw = mdb_alloc(sizeof (file_info_walk_t), UM_SLEEP); 222 223 fiw->fiw_next = (uintptr_t)psp.file_head.list_forw; 224 fiw->fiw_count = psp.num_files; 225 wsp->walk_data = fiw; 226 227 return (WALK_NEXT); 228 } 229 230 static int 231 pr_file_info_walk_step(mdb_walk_state_t *wsp) 232 { 233 file_info_walk_t *fiw = wsp->walk_data; 234 file_info_t f; 235 int status; 236 237 if (fiw->fiw_count == 0) 238 return (WALK_DONE); 239 240 if (mdb_vread(&f, sizeof (file_info_t), fiw->fiw_next) == -1) { 241 mdb_warn("failed to read file_info_t at %p", fiw->fiw_next); 242 return (WALK_ERR); 243 } 244 245 status = wsp->walk_callback(fiw->fiw_next, &f, wsp->walk_cbdata); 246 247 fiw->fiw_next = (uintptr_t)f.file_list.list_forw; 248 fiw->fiw_count--; 249 250 return (status); 251 } 252 253 static void 254 pr_file_info_walk_fini(mdb_walk_state_t *wsp) 255 { 256 file_info_walk_t *fiw = wsp->walk_data; 257 mdb_free(fiw, sizeof (file_info_walk_t)); 258 } 259 260 /* 261 * ::walk pr_map_info 262 * 263 * Given a ps_prochandle_t, walk all its map_info_t structures. 264 */ 265 typedef struct { 266 uintptr_t miw_next; 267 int miw_count; 268 int miw_current; 269 } map_info_walk_t; 270 271 static int 272 pr_map_info_walk_init(mdb_walk_state_t *wsp) 273 { 274 ps_prochandle_t psp; 275 map_info_walk_t *miw; 276 277 if (wsp->walk_addr == NULL) { 278 mdb_warn("pr_map_info doesn't support global walks\n"); 279 return (WALK_ERR); 280 } 281 282 if (mdb_vread(&psp, sizeof (ps_prochandle_t), wsp->walk_addr) == -1) { 283 mdb_warn("failed to read ps_prochandle at %p", wsp->walk_addr); 284 return (WALK_ERR); 285 } 286 287 miw = mdb_alloc(sizeof (map_info_walk_t), UM_SLEEP); 288 289 miw->miw_next = (uintptr_t)psp.mappings; 290 miw->miw_count = psp.map_count; 291 miw->miw_current = 0; 292 wsp->walk_data = miw; 293 294 return (WALK_NEXT); 295 } 296 297 static int 298 pr_map_info_walk_step(mdb_walk_state_t *wsp) 299 { 300 map_info_walk_t *miw = wsp->walk_data; 301 map_info_t m; 302 int status; 303 304 if (miw->miw_current == miw->miw_count) 305 return (WALK_DONE); 306 307 if (mdb_vread(&m, sizeof (map_info_t), miw->miw_next) == -1) { 308 mdb_warn("failed to read map_info_t at %p", miw->miw_next); 309 return (WALK_DONE); 310 } 311 312 status = wsp->walk_callback(miw->miw_next, &m, wsp->walk_cbdata); 313 314 miw->miw_current++; 315 miw->miw_next += sizeof (map_info_t); 316 317 return (status); 318 } 319 320 static void 321 pr_map_info_walk_fini(mdb_walk_state_t *wsp) 322 { 323 map_info_walk_t *miw = wsp->walk_data; 324 mdb_free(miw, sizeof (map_info_walk_t)); 325 } 326 327 static const mdb_dcmd_t dcmds[] = { 328 { "pr_addr2map", ":addr", "convert an adress into a map_info_t", 329 pr_addr2map }, 330 { "pr_symtab", ":[-a | -n]", "print the contents of a sym_tbl_t", 331 pr_symtab }, 332 { NULL } 333 }; 334 335 static const mdb_walker_t walkers[] = { 336 { "pr_file_info", "given a ps_prochandle, walk its file_info " 337 "structures", pr_file_info_walk_init, pr_file_info_walk_step, 338 pr_file_info_walk_fini }, 339 { "pr_map_info", "given a ps_prochandle, walk its map_info structures", 340 pr_map_info_walk_init, pr_map_info_walk_step, 341 pr_map_info_walk_fini }, 342 { NULL } 343 }; 344 345 static const mdb_modinfo_t modinfo = { 346 MDB_API_VERSION, dcmds, walkers 347 }; 348 349 const mdb_modinfo_t * 350 _mdb_init(void) 351 { 352 return (&modinfo); 353 } 354