xref: /freebsd/sys/ddb/db_sym.c (revision 0e6acb26)
1 /*-
2  * Mach Operating System
3  * Copyright (c) 1991,1990 Carnegie Mellon University
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify and distribute this software and its
7  * documentation is hereby granted, provided that both the copyright
8  * notice and this permission notice appear in all copies of the
9  * software, derivative works or modified versions, and any portions
10  * thereof, and that both notices appear in supporting documentation.
11  *
12  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
13  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
14  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
15  *
16  * Carnegie Mellon requests users of this software to return to
17  *
18  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
19  *  School of Computer Science
20  *  Carnegie Mellon University
21  *  Pittsburgh PA 15213-3890
22  *
23  * any improvements or extensions that they make and grant Carnegie the
24  * rights to redistribute these changes.
25  */
26 /*
27  * 	Author: David B. Golub, Carnegie Mellon University
28  *	Date:	7/90
29  */
30 
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33 
34 #include "opt_kstack_pages.h"
35 
36 #include <sys/param.h>
37 #include <sys/pcpu.h>
38 #include <sys/smp.h>
39 #include <sys/systm.h>
40 
41 #include <net/vnet.h>
42 
43 #include <ddb/ddb.h>
44 #include <ddb/db_sym.h>
45 #include <ddb/db_variables.h>
46 
47 #include <opt_ddb.h>
48 
49 /*
50  * Multiple symbol tables
51  */
52 #ifndef MAXNOSYMTABS
53 #define	MAXNOSYMTABS	3	/* mach, ux, emulator */
54 #endif
55 
56 static db_symtab_t	db_symtabs[MAXNOSYMTABS] = {{0,},};
57 static int db_nsymtab = 0;
58 
59 static db_symtab_t	*db_last_symtab; /* where last symbol was found */
60 
61 static c_db_sym_t	db_lookup( const char *symstr);
62 static char		*db_qualify(c_db_sym_t sym, char *symtabname);
63 static bool		db_symbol_is_ambiguous(c_db_sym_t sym);
64 static bool		db_line_at_pc(c_db_sym_t, char **, int *, db_expr_t);
65 
66 static int db_cpu = -1;
67 
68 #ifdef VIMAGE
69 static void *db_vnet = NULL;
70 #endif
71 
72 /*
73  * Validate the CPU number used to interpret per-CPU variables so we can
74  * avoid later confusion if an invalid CPU is requested.
75  */
76 int
77 db_var_db_cpu(struct db_variable *vp, db_expr_t *valuep, int op)
78 {
79 
80 	switch (op) {
81 	case DB_VAR_GET:
82 		*valuep = db_cpu;
83 		return (1);
84 
85 	case DB_VAR_SET:
86 		if (*(int *)valuep < -1 && *(int *)valuep > mp_maxid) {
87 			db_printf("Invalid value: %d", *(int*)valuep);
88 			return (0);
89 		}
90 		db_cpu = *(int *)valuep;
91 		return (1);
92 
93 	default:
94 		db_printf("db_var_db_cpu: unknown operation\n");
95 		return (0);
96 	}
97 }
98 
99 /*
100  * Read-only variable reporting the current CPU, which is what we use when
101  * db_cpu is set to -1.
102  */
103 int
104 db_var_curcpu(struct db_variable *vp, db_expr_t *valuep, int op)
105 {
106 
107 	switch (op) {
108 	case DB_VAR_GET:
109 		*valuep = curcpu;
110 		return (1);
111 
112 	case DB_VAR_SET:
113 		db_printf("Read-only variable.\n");
114 		return (0);
115 
116 	default:
117 		db_printf("db_var_curcpu: unknown operation\n");
118 		return (0);
119 	}
120 }
121 
122 #ifdef VIMAGE
123 /*
124  * Validate the virtual network pointer used to interpret per-vnet global
125  * variable expansion.  Right now we don't do much here, really we should
126  * walk the global vnet list to check it's an OK pointer.
127  */
128 int
129 db_var_db_vnet(struct db_variable *vp, db_expr_t *valuep, int op)
130 {
131 
132 	switch (op) {
133 	case DB_VAR_GET:
134 		*valuep = (db_expr_t)db_vnet;
135 		return (1);
136 
137 	case DB_VAR_SET:
138 		db_vnet = *(void **)valuep;
139 		return (1);
140 
141 	default:
142 		db_printf("db_var_db_vnet: unknown operation\n");
143 		return (0);
144 	}
145 }
146 
147 /*
148  * Read-only variable reporting the current vnet, which is what we use when
149  * db_vnet is set to NULL.
150  */
151 int
152 db_var_curvnet(struct db_variable *vp, db_expr_t *valuep, int op)
153 {
154 
155 	switch (op) {
156 	case DB_VAR_GET:
157 		*valuep = (db_expr_t)curvnet;
158 		return (1);
159 
160 	case DB_VAR_SET:
161 		db_printf("Read-only variable.\n");
162 		return (0);
163 
164 	default:
165 		db_printf("db_var_curvnet: unknown operation\n");
166 		return (0);
167 	}
168 }
169 #endif
170 
171 /*
172  * Add symbol table, with given name, to list of symbol tables.
173  */
174 void
175 db_add_symbol_table(char *start, char *end, char *name, char *ref)
176 {
177 	if (db_nsymtab >= MAXNOSYMTABS) {
178 		printf ("No slots left for %s symbol table", name);
179 		panic ("db_sym.c: db_add_symbol_table");
180 	}
181 
182 	db_symtabs[db_nsymtab].start = start;
183 	db_symtabs[db_nsymtab].end = end;
184 	db_symtabs[db_nsymtab].name = name;
185 	db_symtabs[db_nsymtab].private = ref;
186 	db_nsymtab++;
187 }
188 
189 /*
190  *  db_qualify("vm_map", "ux") returns "unix:vm_map".
191  *
192  *  Note: return value points to static data whose content is
193  *  overwritten by each call... but in practice this seems okay.
194  */
195 static char *
196 db_qualify(c_db_sym_t sym, char *symtabname)
197 {
198 	const char	*symname;
199 	static char     tmp[256];
200 
201 	db_symbol_values(sym, &symname, 0);
202 	snprintf(tmp, sizeof(tmp), "%s:%s", symtabname, symname);
203 	return tmp;
204 }
205 
206 
207 bool
208 db_eqname(const char *src, const char *dst, int c)
209 {
210 	if (!strcmp(src, dst))
211 	    return (true);
212 	if (src[0] == c)
213 	    return (!strcmp(src+1,dst));
214 	return (false);
215 }
216 
217 bool
218 db_value_of_name(const char *name, db_expr_t *valuep)
219 {
220 	c_db_sym_t	sym;
221 
222 	sym = db_lookup(name);
223 	if (sym == C_DB_SYM_NULL)
224 	    return (false);
225 	db_symbol_values(sym, &name, valuep);
226 	return (true);
227 }
228 
229 bool
230 db_value_of_name_pcpu(const char *name, db_expr_t *valuep)
231 {
232 	static char     tmp[256];
233 	db_expr_t	value;
234 	c_db_sym_t	sym;
235 	int		cpu;
236 
237 	if (db_cpu != -1)
238 		cpu = db_cpu;
239 	else
240 		cpu = curcpu;
241 	snprintf(tmp, sizeof(tmp), "pcpu_entry_%s", name);
242 	sym = db_lookup(tmp);
243 	if (sym == C_DB_SYM_NULL)
244 		return (false);
245 	db_symbol_values(sym, &name, &value);
246 	if (value < DPCPU_START || value >= DPCPU_STOP)
247 		return (false);
248 	*valuep = (db_expr_t)((uintptr_t)value + dpcpu_off[cpu]);
249 	return (true);
250 }
251 
252 bool
253 db_value_of_name_vnet(const char *name, db_expr_t *valuep)
254 {
255 #ifdef VIMAGE
256 	static char     tmp[256];
257 	db_expr_t	value;
258 	c_db_sym_t	sym;
259 	struct vnet	*vnet;
260 
261 	if (db_vnet != NULL)
262 		vnet = db_vnet;
263 	else
264 		vnet = curvnet;
265 	snprintf(tmp, sizeof(tmp), "vnet_entry_%s", name);
266 	sym = db_lookup(tmp);
267 	if (sym == C_DB_SYM_NULL)
268 		return (false);
269 	db_symbol_values(sym, &name, &value);
270 	if (value < VNET_START || value >= VNET_STOP)
271 		return (false);
272 	*valuep = (db_expr_t)((uintptr_t)value + vnet->vnet_data_base);
273 	return (true);
274 #else
275 	return (false);
276 #endif
277 }
278 
279 /*
280  * Lookup a symbol.
281  * If the symbol has a qualifier (e.g., ux:vm_map),
282  * then only the specified symbol table will be searched;
283  * otherwise, all symbol tables will be searched.
284  */
285 static c_db_sym_t
286 db_lookup(const char *symstr)
287 {
288 	c_db_sym_t sp;
289 	register int i;
290 	int symtab_start = 0;
291 	int symtab_end = db_nsymtab;
292 	register const char *cp;
293 
294 	/*
295 	 * Look for, remove, and remember any symbol table specifier.
296 	 */
297 	for (cp = symstr; *cp; cp++) {
298 		if (*cp == ':') {
299 			for (i = 0; i < db_nsymtab; i++) {
300 				int n = strlen(db_symtabs[i].name);
301 
302 				if (
303 				    n == (cp - symstr) &&
304 				    strncmp(symstr, db_symtabs[i].name, n) == 0
305 				) {
306 					symtab_start = i;
307 					symtab_end = i + 1;
308 					break;
309 				}
310 			}
311 			if (i == db_nsymtab) {
312 				db_error("invalid symbol table name");
313 			}
314 			symstr = cp+1;
315 		}
316 	}
317 
318 	/*
319 	 * Look in the specified set of symbol tables.
320 	 * Return on first match.
321 	 */
322 	for (i = symtab_start; i < symtab_end; i++) {
323 		sp = X_db_lookup(&db_symtabs[i], symstr);
324 		if (sp) {
325 			db_last_symtab = &db_symtabs[i];
326 			return sp;
327 		}
328 	}
329 	return 0;
330 }
331 
332 /*
333  * If true, check across symbol tables for multiple occurrences
334  * of a name.  Might slow things down quite a bit.
335  */
336 static volatile bool db_qualify_ambiguous_names = false;
337 
338 /*
339  * Does this symbol name appear in more than one symbol table?
340  * Used by db_symbol_values to decide whether to qualify a symbol.
341  */
342 static bool
343 db_symbol_is_ambiguous(c_db_sym_t sym)
344 {
345 	const char	*sym_name;
346 	register int	i;
347 	register bool	found_once = false;
348 
349 	if (!db_qualify_ambiguous_names)
350 		return (false);
351 
352 	db_symbol_values(sym, &sym_name, 0);
353 	for (i = 0; i < db_nsymtab; i++) {
354 		if (X_db_lookup(&db_symtabs[i], sym_name)) {
355 			if (found_once)
356 				return (true);
357 			found_once = true;
358 		}
359 	}
360 	return (false);
361 }
362 
363 /*
364  * Find the closest symbol to val, and return its name
365  * and the difference between val and the symbol found.
366  */
367 c_db_sym_t
368 db_search_symbol(db_addr_t val, db_strategy_t strategy, db_expr_t *offp)
369 {
370 	register
371 	unsigned int	diff;
372 	size_t		newdiff;
373 	register int	i;
374 	c_db_sym_t	ret = C_DB_SYM_NULL, sym;
375 
376 	newdiff = diff = val;
377 	for (i = 0; i < db_nsymtab; i++) {
378 	    sym = X_db_search_symbol(&db_symtabs[i], val, strategy, &newdiff);
379 	    if ((uintmax_t)newdiff < (uintmax_t)diff) {
380 		db_last_symtab = &db_symtabs[i];
381 		diff = newdiff;
382 		ret = sym;
383 	    }
384 	}
385 	*offp = diff;
386 	return ret;
387 }
388 
389 /*
390  * Return name and value of a symbol
391  */
392 void
393 db_symbol_values(c_db_sym_t sym, const char **namep, db_expr_t *valuep)
394 {
395 	db_expr_t	value;
396 
397 	if (sym == DB_SYM_NULL) {
398 		*namep = NULL;
399 		return;
400 	}
401 
402 	X_db_symbol_values(db_last_symtab, sym, namep, &value);
403 
404 	if (db_symbol_is_ambiguous(sym))
405 		*namep = db_qualify(sym, db_last_symtab->name);
406 	if (valuep)
407 		*valuep = value;
408 }
409 
410 
411 /*
412  * Print a the closest symbol to value
413  *
414  * After matching the symbol according to the given strategy
415  * we print it in the name+offset format, provided the symbol's
416  * value is close enough (eg smaller than db_maxoff).
417  * We also attempt to print [filename:linenum] when applicable
418  * (eg for procedure names).
419  *
420  * If we could not find a reasonable name+offset representation,
421  * then we just print the value in hex.  Small values might get
422  * bogus symbol associations, e.g. 3 might get some absolute
423  * value like _INCLUDE_VERSION or something, therefore we do
424  * not accept symbols whose value is "small" (and use plain hex).
425  */
426 
427 db_expr_t	db_maxoff = 0x10000;
428 
429 void
430 db_printsym(db_expr_t off, db_strategy_t strategy)
431 {
432 	db_expr_t	d;
433 	char 		*filename;
434 	const char	*name;
435 	int 		linenum;
436 	c_db_sym_t	cursym;
437 
438 	if (off < 0 && off >= -db_maxoff) {
439 		db_printf("%+#lr", (long)off);
440 		return;
441 	}
442 	cursym = db_search_symbol(off, strategy, &d);
443 	db_symbol_values(cursym, &name, NULL);
444 	if (name == NULL || d >= (db_addr_t)db_maxoff) {
445 		db_printf("%#lr", (unsigned long)off);
446 		return;
447 	}
448 #ifdef DDB_NUMSYM
449 	db_printf("%#lr = %s", (unsigned long)off, name);
450 #else
451 	db_printf("%s", name);
452 #endif
453 	if (d)
454 		db_printf("+%+#lr", (long)d);
455 	if (strategy == DB_STGY_PROC) {
456 		if (db_line_at_pc(cursym, &filename, &linenum, off))
457 			db_printf(" [%s:%d]", filename, linenum);
458 	}
459 }
460 
461 static bool
462 db_line_at_pc(c_db_sym_t sym, char **filename, int *linenum, db_expr_t pc)
463 {
464 	return (X_db_line_at_pc(db_last_symtab, sym, filename, linenum, pc));
465 }
466 
467 bool
468 db_sym_numargs(c_db_sym_t sym, int *nargp, char **argnames)
469 {
470 	return (X_db_sym_numargs(db_last_symtab, sym, nargp, argnames));
471 }
472