1139747Simp/*- 24Srgrimes * Mach Operating System 34Srgrimes * Copyright (c) 1991,1990 Carnegie Mellon University 44Srgrimes * All Rights Reserved. 58876Srgrimes * 64Srgrimes * Permission to use, copy, modify and distribute this software and its 74Srgrimes * documentation is hereby granted, provided that both the copyright 84Srgrimes * notice and this permission notice appear in all copies of the 94Srgrimes * software, derivative works or modified versions, and any portions 104Srgrimes * thereof, and that both notices appear in supporting documentation. 118876Srgrimes * 128876Srgrimes * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS 134Srgrimes * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 144Srgrimes * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 158876Srgrimes * 164Srgrimes * Carnegie Mellon requests users of this software to return to 178876Srgrimes * 184Srgrimes * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 194Srgrimes * School of Computer Science 204Srgrimes * Carnegie Mellon University 214Srgrimes * Pittsburgh PA 15213-3890 228876Srgrimes * 234Srgrimes * any improvements or extensions that they make and grant Carnegie the 244Srgrimes * rights to redistribute these changes. 254Srgrimes */ 264Srgrimes/* 274Srgrimes * Author: David B. Golub, Carnegie Mellon University 284Srgrimes * Date: 7/90 294Srgrimes */ 30116176Sobrien 31116176Sobrien#include <sys/cdefs.h> 32116176Sobrien__FBSDID("$FreeBSD$"); 33116176Sobrien 342056Swollman#include <sys/param.h> 35196019Srwatson#include <sys/pcpu.h> 36195699Srwatson#include <sys/smp.h> 372056Swollman#include <sys/systm.h> 3812734Sbde 39195699Srwatson#include <net/vnet.h> 40195699Srwatson 412056Swollman#include <ddb/ddb.h> 424Srgrimes#include <ddb/db_sym.h> 43195699Srwatson#include <ddb/db_variables.h> 444Srgrimes 45126204Sphk#include <opt_ddb.h> 46126204Sphk 474Srgrimes/* 484Srgrimes * Multiple symbol tables 494Srgrimes */ 50923Sdg#ifndef MAXNOSYMTABS 514Srgrimes#define MAXNOSYMTABS 3 /* mach, ux, emulator */ 52923Sdg#endif 534Srgrimes 5412515Sphkstatic db_symtab_t db_symtabs[MAXNOSYMTABS] = {{0,},}; 5512515Sphkstatic int db_nsymtab = 0; 564Srgrimes 5727122Sbdestatic db_symtab_t *db_last_symtab; /* where last symbol was found */ 584Srgrimes 5992756Salfredstatic c_db_sym_t db_lookup( const char *symstr); 6092756Salfredstatic char *db_qualify(c_db_sym_t sym, char *symtabname); 6192756Salfredstatic boolean_t db_symbol_is_ambiguous(c_db_sym_t sym); 6292756Salfredstatic boolean_t db_line_at_pc(c_db_sym_t, char **, int *, db_expr_t); 634Srgrimes 64195699Srwatsonstatic int db_cpu = -1; 65195699Srwatson 66195699Srwatson#ifdef VIMAGE 67195699Srwatsonstatic void *db_vnet = NULL; 68195699Srwatson#endif 69195699Srwatson 704Srgrimes/* 71195699Srwatson * Validate the CPU number used to interpret per-CPU variables so we can 72195699Srwatson * avoid later confusion if an invalid CPU is requested. 73195699Srwatson */ 74195699Srwatsonint 75195699Srwatsondb_var_db_cpu(struct db_variable *vp, db_expr_t *valuep, int op) 76195699Srwatson{ 77195699Srwatson 78195699Srwatson switch (op) { 79195699Srwatson case DB_VAR_GET: 80195699Srwatson *valuep = db_cpu; 81195699Srwatson return (1); 82195699Srwatson 83195699Srwatson case DB_VAR_SET: 84195699Srwatson if (*(int *)valuep < -1 && *(int *)valuep > mp_maxid) { 85195699Srwatson db_printf("Invalid value: %d", *(int*)valuep); 86195699Srwatson return (0); 87195699Srwatson } 88195699Srwatson db_cpu = *(int *)valuep; 89195699Srwatson return (1); 90195699Srwatson 91195699Srwatson default: 92195699Srwatson db_printf("db_var_db_cpu: unknown operation\n"); 93195699Srwatson return (0); 94195699Srwatson } 95195699Srwatson} 96195699Srwatson 97195699Srwatson/* 98195699Srwatson * Read-only variable reporting the current CPU, which is what we use when 99195699Srwatson * db_cpu is set to -1. 100195699Srwatson */ 101195699Srwatsonint 102195699Srwatsondb_var_curcpu(struct db_variable *vp, db_expr_t *valuep, int op) 103195699Srwatson{ 104195699Srwatson 105195699Srwatson switch (op) { 106195699Srwatson case DB_VAR_GET: 107195699Srwatson *valuep = curcpu; 108195699Srwatson return (1); 109195699Srwatson 110195699Srwatson case DB_VAR_SET: 111195699Srwatson db_printf("Read-only variable.\n"); 112195699Srwatson return (0); 113195699Srwatson 114195699Srwatson default: 115195699Srwatson db_printf("db_var_curcpu: unknown operation\n"); 116195699Srwatson return (0); 117195699Srwatson } 118195699Srwatson} 119195699Srwatson 120195699Srwatson#ifdef VIMAGE 121195699Srwatson/* 122195699Srwatson * Validate the virtual network pointer used to interpret per-vnet global 123195699Srwatson * variable expansion. Right now we don't do much here, really we should 124195699Srwatson * walk the global vnet list to check it's an OK pointer. 125195699Srwatson */ 126195699Srwatsonint 127195699Srwatsondb_var_db_vnet(struct db_variable *vp, db_expr_t *valuep, int op) 128195699Srwatson{ 129195699Srwatson 130195699Srwatson switch (op) { 131195699Srwatson case DB_VAR_GET: 132195699Srwatson *valuep = (db_expr_t)db_vnet; 133195699Srwatson return (1); 134195699Srwatson 135195699Srwatson case DB_VAR_SET: 136195699Srwatson db_vnet = *(void **)valuep; 137195699Srwatson return (1); 138195699Srwatson 139195699Srwatson default: 140195699Srwatson db_printf("db_var_db_vnet: unknown operation\n"); 141195699Srwatson return (0); 142195699Srwatson } 143195699Srwatson} 144195699Srwatson 145195699Srwatson/* 146195699Srwatson * Read-only variable reporting the current vnet, which is what we use when 147195699Srwatson * db_vnet is set to NULL. 148195699Srwatson */ 149195699Srwatsonint 150195699Srwatsondb_var_curvnet(struct db_variable *vp, db_expr_t *valuep, int op) 151195699Srwatson{ 152195699Srwatson 153195699Srwatson switch (op) { 154195699Srwatson case DB_VAR_GET: 155195699Srwatson *valuep = (db_expr_t)curvnet; 156195699Srwatson return (1); 157195699Srwatson 158195699Srwatson case DB_VAR_SET: 159195699Srwatson db_printf("Read-only variable.\n"); 160195699Srwatson return (0); 161195699Srwatson 162195699Srwatson default: 163195699Srwatson db_printf("db_var_curcpu: unknown operation\n"); 164195699Srwatson return (0); 165195699Srwatson } 166195699Srwatson} 167195699Srwatson#endif 168195699Srwatson 169195699Srwatson/* 1704Srgrimes * Add symbol table, with given name, to list of symbol tables. 1714Srgrimes */ 1724Srgrimesvoid 1734Srgrimesdb_add_symbol_table(start, end, name, ref) 1744Srgrimes char *start; 1754Srgrimes char *end; 1764Srgrimes char *name; 1774Srgrimes char *ref; 1784Srgrimes{ 1794Srgrimes if (db_nsymtab >= MAXNOSYMTABS) { 1804Srgrimes printf ("No slots left for %s symbol table", name); 1814Srgrimes panic ("db_sym.c: db_add_symbol_table"); 1824Srgrimes } 1834Srgrimes 1844Srgrimes db_symtabs[db_nsymtab].start = start; 1854Srgrimes db_symtabs[db_nsymtab].end = end; 1864Srgrimes db_symtabs[db_nsymtab].name = name; 1874Srgrimes db_symtabs[db_nsymtab].private = ref; 1884Srgrimes db_nsymtab++; 1894Srgrimes} 1904Srgrimes 1914Srgrimes/* 1924Srgrimes * db_qualify("vm_map", "ux") returns "unix:vm_map". 1934Srgrimes * 1944Srgrimes * Note: return value points to static data whose content is 1954Srgrimes * overwritten by each call... but in practice this seems okay. 1964Srgrimes */ 1974Srgrimesstatic char * 1984Srgrimesdb_qualify(sym, symtabname) 19943309Sdillon c_db_sym_t sym; 2004Srgrimes register char *symtabname; 2014Srgrimes{ 20243289Sdillon const char *symname; 2034Srgrimes static char tmp[256]; 2044Srgrimes 2054Srgrimes db_symbol_values(sym, &symname, 0); 20641514Sarchie snprintf(tmp, sizeof(tmp), "%s:%s", symtabname, symname); 2074Srgrimes return tmp; 2084Srgrimes} 2094Srgrimes 2104Srgrimes 2114Srgrimesboolean_t 2124Srgrimesdb_eqname(src, dst, c) 21343926Sbde const char *src; 21443926Sbde const char *dst; 21527121Sbde int c; 2164Srgrimes{ 2174Srgrimes if (!strcmp(src, dst)) 2184Srgrimes return (TRUE); 2194Srgrimes if (src[0] == c) 2204Srgrimes return (!strcmp(src+1,dst)); 2214Srgrimes return (FALSE); 2224Srgrimes} 2234Srgrimes 2244Srgrimesboolean_t 2254Srgrimesdb_value_of_name(name, valuep) 22643289Sdillon const char *name; 2274Srgrimes db_expr_t *valuep; 2284Srgrimes{ 22943309Sdillon c_db_sym_t sym; 2304Srgrimes 2314Srgrimes sym = db_lookup(name); 23243309Sdillon if (sym == C_DB_SYM_NULL) 2334Srgrimes return (FALSE); 2344Srgrimes db_symbol_values(sym, &name, valuep); 2354Srgrimes return (TRUE); 2364Srgrimes} 2374Srgrimes 238195699Srwatsonboolean_t 239195699Srwatsondb_value_of_name_pcpu(name, valuep) 240195699Srwatson const char *name; 241195699Srwatson db_expr_t *valuep; 242195699Srwatson{ 243195699Srwatson static char tmp[256]; 244195699Srwatson db_expr_t value; 245195699Srwatson c_db_sym_t sym; 246195699Srwatson int cpu; 2474Srgrimes 248195699Srwatson if (db_cpu != -1) 249195699Srwatson cpu = db_cpu; 250195699Srwatson else 251195699Srwatson cpu = curcpu; 252195699Srwatson snprintf(tmp, sizeof(tmp), "pcpu_entry_%s", name); 253195699Srwatson sym = db_lookup(tmp); 254195699Srwatson if (sym == C_DB_SYM_NULL) 255195699Srwatson return (FALSE); 256195699Srwatson db_symbol_values(sym, &name, &value); 257195699Srwatson if (value < DPCPU_START || value >= DPCPU_STOP) 258195699Srwatson return (FALSE); 259195699Srwatson *valuep = (db_expr_t)((uintptr_t)value + dpcpu_off[cpu]); 260195699Srwatson return (TRUE); 261195699Srwatson} 262195699Srwatson 263195699Srwatsonboolean_t 264195699Srwatsondb_value_of_name_vnet(name, valuep) 265195699Srwatson const char *name; 266195699Srwatson db_expr_t *valuep; 267195699Srwatson{ 268195699Srwatson#ifdef VIMAGE 269195699Srwatson static char tmp[256]; 270195699Srwatson db_expr_t value; 271195699Srwatson c_db_sym_t sym; 272195699Srwatson struct vnet *vnet; 273195699Srwatson 274195699Srwatson if (db_vnet != NULL) 275195699Srwatson vnet = db_vnet; 276195699Srwatson else 277195699Srwatson vnet = curvnet; 278195699Srwatson snprintf(tmp, sizeof(tmp), "vnet_entry_%s", name); 279195699Srwatson sym = db_lookup(tmp); 280195699Srwatson if (sym == C_DB_SYM_NULL) 281195699Srwatson return (FALSE); 282195699Srwatson db_symbol_values(sym, &name, &value); 283195699Srwatson if (value < VNET_START || value >= VNET_STOP) 284195699Srwatson return (FALSE); 285195699Srwatson *valuep = (db_expr_t)((uintptr_t)value + vnet->vnet_data_base); 286195699Srwatson return (TRUE); 287195699Srwatson#else 288195699Srwatson return (FALSE); 289195699Srwatson#endif 290195699Srwatson} 291195699Srwatson 2924Srgrimes/* 2934Srgrimes * Lookup a symbol. 2944Srgrimes * If the symbol has a qualifier (e.g., ux:vm_map), 2954Srgrimes * then only the specified symbol table will be searched; 2964Srgrimes * otherwise, all symbol tables will be searched. 2974Srgrimes */ 29843309Sdillonstatic c_db_sym_t 2994Srgrimesdb_lookup(symstr) 30043289Sdillon const char *symstr; 3014Srgrimes{ 30243309Sdillon c_db_sym_t sp; 3034Srgrimes register int i; 3044Srgrimes int symtab_start = 0; 3054Srgrimes int symtab_end = db_nsymtab; 30643289Sdillon register const char *cp; 3074Srgrimes 3084Srgrimes /* 3094Srgrimes * Look for, remove, and remember any symbol table specifier. 3104Srgrimes */ 3114Srgrimes for (cp = symstr; *cp; cp++) { 3124Srgrimes if (*cp == ':') { 3134Srgrimes for (i = 0; i < db_nsymtab; i++) { 31443289Sdillon int n = strlen(db_symtabs[i].name); 31543289Sdillon 31643289Sdillon if ( 31743289Sdillon n == (cp - symstr) && 31843289Sdillon strncmp(symstr, db_symtabs[i].name, n) == 0 31943289Sdillon ) { 3204Srgrimes symtab_start = i; 3214Srgrimes symtab_end = i + 1; 3224Srgrimes break; 3234Srgrimes } 3244Srgrimes } 3254Srgrimes if (i == db_nsymtab) { 3264Srgrimes db_error("invalid symbol table name"); 3274Srgrimes } 3284Srgrimes symstr = cp+1; 3294Srgrimes } 3304Srgrimes } 3314Srgrimes 3324Srgrimes /* 3334Srgrimes * Look in the specified set of symbol tables. 3344Srgrimes * Return on first match. 3354Srgrimes */ 3364Srgrimes for (i = symtab_start; i < symtab_end; i++) { 3373140Sphk sp = X_db_lookup(&db_symtabs[i], symstr); 3383140Sphk if (sp) { 3394Srgrimes db_last_symtab = &db_symtabs[i]; 3404Srgrimes return sp; 3414Srgrimes } 3424Srgrimes } 3434Srgrimes return 0; 3444Srgrimes} 3454Srgrimes 3464Srgrimes/* 34727122Sbde * If TRUE, check across symbol tables for multiple occurrences 34827122Sbde * of a name. Might slow things down quite a bit. 34927122Sbde */ 35027122Sbdestatic volatile boolean_t db_qualify_ambiguous_names = FALSE; 35127122Sbde 35227122Sbde/* 3534Srgrimes * Does this symbol name appear in more than one symbol table? 3544Srgrimes * Used by db_symbol_values to decide whether to qualify a symbol. 3554Srgrimes */ 35612515Sphkstatic boolean_t 3574Srgrimesdb_symbol_is_ambiguous(sym) 35843309Sdillon c_db_sym_t sym; 3594Srgrimes{ 36043289Sdillon const char *sym_name; 3614Srgrimes register int i; 3624Srgrimes register 3634Srgrimes boolean_t found_once = FALSE; 3644Srgrimes 3654Srgrimes if (!db_qualify_ambiguous_names) 3664Srgrimes return FALSE; 3674Srgrimes 3684Srgrimes db_symbol_values(sym, &sym_name, 0); 3694Srgrimes for (i = 0; i < db_nsymtab; i++) { 3704Srgrimes if (X_db_lookup(&db_symtabs[i], sym_name)) { 3714Srgrimes if (found_once) 3724Srgrimes return TRUE; 3734Srgrimes found_once = TRUE; 3744Srgrimes } 3754Srgrimes } 3764Srgrimes return FALSE; 3774Srgrimes} 3784Srgrimes 3794Srgrimes/* 3804Srgrimes * Find the closest symbol to val, and return its name 3814Srgrimes * and the difference between val and the symbol found. 3824Srgrimes */ 38343309Sdillonc_db_sym_t 3844Srgrimesdb_search_symbol( val, strategy, offp) 3854Srgrimes register db_addr_t val; 3864Srgrimes db_strategy_t strategy; 3874Srgrimes db_expr_t *offp; 3884Srgrimes{ 3894Srgrimes register 3904Srgrimes unsigned int diff; 39136735Sdfr size_t newdiff; 3924Srgrimes register int i; 39343309Sdillon c_db_sym_t ret = C_DB_SYM_NULL, sym; 3944Srgrimes 3954Srgrimes newdiff = diff = ~0; 3964Srgrimes for (i = 0; i < db_nsymtab; i++) { 3974Srgrimes sym = X_db_search_symbol(&db_symtabs[i], val, strategy, &newdiff); 3984Srgrimes if (newdiff < diff) { 3994Srgrimes db_last_symtab = &db_symtabs[i]; 4004Srgrimes diff = newdiff; 4014Srgrimes ret = sym; 4024Srgrimes } 4034Srgrimes } 4044Srgrimes *offp = diff; 4054Srgrimes return ret; 4064Srgrimes} 4074Srgrimes 4084Srgrimes/* 4094Srgrimes * Return name and value of a symbol 4104Srgrimes */ 4114Srgrimesvoid 4124Srgrimesdb_symbol_values(sym, namep, valuep) 41343309Sdillon c_db_sym_t sym; 41443289Sdillon const char **namep; 4154Srgrimes db_expr_t *valuep; 4164Srgrimes{ 4174Srgrimes db_expr_t value; 4184Srgrimes 4194Srgrimes if (sym == DB_SYM_NULL) { 4204Srgrimes *namep = 0; 4214Srgrimes return; 4224Srgrimes } 4234Srgrimes 42437218Sdfr X_db_symbol_values(db_last_symtab, sym, namep, &value); 4254Srgrimes 4264Srgrimes if (db_symbol_is_ambiguous(sym)) 4274Srgrimes *namep = db_qualify(sym, db_last_symtab->name); 4284Srgrimes if (valuep) 4294Srgrimes *valuep = value; 4304Srgrimes} 4314Srgrimes 4324Srgrimes 4334Srgrimes/* 4344Srgrimes * Print a the closest symbol to value 4354Srgrimes * 4364Srgrimes * After matching the symbol according to the given strategy 4374Srgrimes * we print it in the name+offset format, provided the symbol's 4384Srgrimes * value is close enough (eg smaller than db_maxoff). 4394Srgrimes * We also attempt to print [filename:linenum] when applicable 4404Srgrimes * (eg for procedure names). 4414Srgrimes * 4424Srgrimes * If we could not find a reasonable name+offset representation, 4434Srgrimes * then we just print the value in hex. Small values might get 4444Srgrimes * bogus symbol associations, e.g. 3 might get some absolute 4454Srgrimes * value like _INCLUDE_VERSION or something, therefore we do 44621767Sbde * not accept symbols whose value is "small" (and use plain hex). 4474Srgrimes */ 4484Srgrimes 44937504Sbdedb_expr_t db_maxoff = 0x10000; 4504Srgrimes 4514Srgrimesvoid 4524Srgrimesdb_printsym(off, strategy) 4534Srgrimes db_expr_t off; 4544Srgrimes db_strategy_t strategy; 4554Srgrimes{ 4564Srgrimes db_expr_t d; 4574Srgrimes char *filename; 45843289Sdillon const char *name; 4594Srgrimes db_expr_t value; 4604Srgrimes int linenum; 46143309Sdillon c_db_sym_t cursym; 4624Srgrimes 4634Srgrimes cursym = db_search_symbol(off, strategy, &d); 4644Srgrimes db_symbol_values(cursym, &name, &value); 46521767Sbde if (name == 0) 46621767Sbde value = off; 46721767Sbde if (value >= DB_SMALL_VALUE_MIN && value <= DB_SMALL_VALUE_MAX) { 46837506Sbde db_printf("%+#lr", (long)off); 46921767Sbde return; 47021767Sbde } 47137504Sbde if (name == 0 || d >= (unsigned long)db_maxoff) { 47237506Sbde db_printf("%#lr", (unsigned long)off); 4734Srgrimes return; 4744Srgrimes } 475126204Sphk#ifdef DDB_NUMSYM 476126204Sphk db_printf("%#lr = %s", (unsigned long)off, name); 477126204Sphk#else 4784Srgrimes db_printf("%s", name); 479126204Sphk#endif 4804Srgrimes if (d) 48148407Speter db_printf("+%+#lr", (long)d); 4824Srgrimes if (strategy == DB_STGY_PROC) { 4834Srgrimes if (db_line_at_pc(cursym, &filename, &linenum, off)) 4844Srgrimes db_printf(" [%s:%d]", filename, linenum); 4854Srgrimes } 4864Srgrimes} 4874Srgrimes 48812515Sphkstatic boolean_t 4894Srgrimesdb_line_at_pc( sym, filename, linenum, pc) 49043309Sdillon c_db_sym_t sym; 4912142Sdg char **filename; 4922142Sdg int *linenum; 4932142Sdg db_expr_t pc; 4944Srgrimes{ 4954Srgrimes return X_db_line_at_pc( db_last_symtab, sym, filename, linenum, pc); 4964Srgrimes} 497923Sdg 498923Sdgint 499923Sdgdb_sym_numargs(sym, nargp, argnames) 50043309Sdillon c_db_sym_t sym; 501923Sdg int *nargp; 502923Sdg char **argnames; 503923Sdg{ 504923Sdg return X_db_sym_numargs(db_last_symtab, sym, nargp, argnames); 505923Sdg} 506