1 | /* $NetBSD: db_command.c,v 1.147 2016/04/13 00:47:02 ozaki-r Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 1996, 1997, 1998, 1999, 2002, 2009 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Adam Hamsik, and by Andrew Doran. |
9 | * |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions |
12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ |
31 | |
32 | /* |
33 | * Mach Operating System |
34 | * Copyright (c) 1991,1990 Carnegie Mellon University |
35 | * All Rights Reserved. |
36 | * |
37 | * Permission to use, copy, modify and distribute this software and its |
38 | * documentation is hereby granted, provided that both the copyright |
39 | * notice and this permission notice appear in all copies of the |
40 | * software, derivative works or modified versions, and any portions |
41 | * thereof, and that both notices appear in supporting documentation. |
42 | * |
43 | * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" |
44 | * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR |
45 | * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. |
46 | * |
47 | * Carnegie Mellon requests users of this software to return to |
48 | * |
49 | * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU |
50 | * School of Computer Science |
51 | * Carnegie Mellon University |
52 | * Pittsburgh PA 15213-3890 |
53 | * |
54 | * any improvements or extensions that they make and grant Carnegie the |
55 | * rights to redistribute these changes. |
56 | */ |
57 | |
58 | /* |
59 | * Command dispatcher. |
60 | */ |
61 | |
62 | #include <sys/cdefs.h> |
63 | __KERNEL_RCSID(0, "$NetBSD: db_command.c,v 1.147 2016/04/13 00:47:02 ozaki-r Exp $" ); |
64 | |
65 | #ifdef _KERNEL_OPT |
66 | #include "opt_aio.h" |
67 | #include "opt_ddb.h" |
68 | #include "opt_kgdb.h" |
69 | #include "opt_mqueue.h" |
70 | #include "opt_inet.h" |
71 | #include "opt_kernhist.h" |
72 | #include "opt_ddbparam.h" |
73 | #include "opt_multiprocessor.h" |
74 | #endif |
75 | |
76 | #include <sys/param.h> |
77 | #include <sys/systm.h> |
78 | #include <sys/reboot.h> |
79 | #include <sys/device.h> |
80 | #include <sys/lwp.h> |
81 | #include <sys/mbuf.h> |
82 | #include <sys/namei.h> |
83 | #include <sys/pool.h> |
84 | #include <sys/proc.h> |
85 | #include <sys/vnode.h> |
86 | #include <sys/vmem.h> |
87 | #include <sys/lockdebug.h> |
88 | #include <sys/cpu.h> |
89 | #include <sys/buf.h> |
90 | #include <sys/module.h> |
91 | #include <sys/kernhist.h> |
92 | |
93 | /*include queue macros*/ |
94 | #include <sys/queue.h> |
95 | |
96 | #include <ddb/ddb.h> |
97 | |
98 | #include <uvm/uvm_extern.h> |
99 | #include <uvm/uvm_ddb.h> |
100 | |
101 | #include <net/route.h> |
102 | |
103 | /* |
104 | * Results of command search. |
105 | */ |
106 | #define CMD_EXACT 0 |
107 | #define CMD_PREFIX 1 |
108 | #define CMD_NONE 2 |
109 | #define CMD_AMBIGUOUS 3 |
110 | |
111 | /* |
112 | * Exported global variables |
113 | */ |
114 | bool db_cmd_loop_done; |
115 | label_t *db_recover; |
116 | db_addr_t db_dot; |
117 | db_addr_t db_last_addr; |
118 | db_addr_t db_prev; |
119 | db_addr_t db_next; |
120 | |
121 | |
122 | /* |
123 | * New DDB api for adding and removing commands uses three lists, because |
124 | * we use two types of commands |
125 | * a) standard commands without subcommands -> reboot |
126 | * b) show commands which are subcommands of show command -> show aio_jobs |
127 | * c) if defined machine specific commands |
128 | * |
129 | * ddb_add_cmd, ddb_rem_cmd use type (DDB_SHOW_CMD||DDB_BASE_CMD)argument to |
130 | * add them to representativ lists. |
131 | */ |
132 | |
133 | static const struct db_command db_command_table[]; |
134 | static const struct db_command db_show_cmds[]; |
135 | |
136 | #ifdef DB_MACHINE_COMMANDS |
137 | /* arch/<arch>/<arch>/db_interface.c */ |
138 | extern const struct db_command db_machine_command_table[]; |
139 | #endif |
140 | |
141 | /* the global queue of all command tables */ |
142 | TAILQ_HEAD(db_cmd_tbl_en_head, db_cmd_tbl_en); |
143 | |
144 | /* TAILQ entry used to register command tables */ |
145 | struct db_cmd_tbl_en { |
146 | const struct db_command *db_cmd; /* cmd table */ |
147 | TAILQ_ENTRY(db_cmd_tbl_en) db_cmd_next; |
148 | }; |
149 | |
150 | /* head of base commands list */ |
151 | static struct db_cmd_tbl_en_head db_base_cmd_list = |
152 | TAILQ_HEAD_INITIALIZER(db_base_cmd_list); |
153 | static struct db_cmd_tbl_en db_base_cmd_builtins = |
154 | { .db_cmd = db_command_table }; |
155 | |
156 | /* head of show commands list */ |
157 | static struct db_cmd_tbl_en_head db_show_cmd_list = |
158 | TAILQ_HEAD_INITIALIZER(db_show_cmd_list); |
159 | static struct db_cmd_tbl_en db_show_cmd_builtins = |
160 | { .db_cmd = db_show_cmds }; |
161 | |
162 | /* head of machine commands list */ |
163 | static struct db_cmd_tbl_en_head db_mach_cmd_list = |
164 | TAILQ_HEAD_INITIALIZER(db_mach_cmd_list); |
165 | #ifdef DB_MACHINE_COMMANDS |
166 | static struct db_cmd_tbl_en db_mach_cmd_builtins = |
167 | { .db_cmd = db_machine_command_table }; |
168 | #endif |
169 | |
170 | /* |
171 | * if 'ed' style: 'dot' is set at start of last item printed, |
172 | * and '+' points to next line. |
173 | * Otherwise: 'dot' points to next item, '..' points to last. |
174 | */ |
175 | static bool db_ed_style = true; |
176 | |
177 | static void db_init_commands(void); |
178 | static int db_register_tbl_entry(uint8_t type, |
179 | struct db_cmd_tbl_en *list_ent); |
180 | static void db_cmd_list(const struct db_cmd_tbl_en_head *); |
181 | static int db_cmd_search(const char *, struct db_cmd_tbl_en_head *, |
182 | const struct db_command **); |
183 | static int db_cmd_search_table(const char *, const struct db_command *, |
184 | const struct db_command **); |
185 | static void db_cmd_search_failed(char *, int); |
186 | static const struct db_command *db_read_command(void); |
187 | static void db_command(const struct db_command **); |
188 | static void db_buf_print_cmd(db_expr_t, bool, db_expr_t, const char *); |
189 | static void db_event_print_cmd(db_expr_t, bool, db_expr_t, const char *); |
190 | static void db_fncall(db_expr_t, bool, db_expr_t, const char *); |
191 | static void db_help_print_cmd(db_expr_t, bool, db_expr_t, const char *); |
192 | static void db_lock_print_cmd(db_expr_t, bool, db_expr_t, const char *); |
193 | static void db_mount_print_cmd(db_expr_t, bool, db_expr_t, const char *); |
194 | static void db_mbuf_print_cmd(db_expr_t, bool, db_expr_t, const char *); |
195 | static void db_map_print_cmd(db_expr_t, bool, db_expr_t, const char *); |
196 | static void db_namecache_print_cmd(db_expr_t, bool, db_expr_t, |
197 | const char *); |
198 | static void db_object_print_cmd(db_expr_t, bool, db_expr_t, const char *); |
199 | static void db_page_print_cmd(db_expr_t, bool, db_expr_t, const char *); |
200 | static void db_show_all_pages(db_expr_t, bool, db_expr_t, const char *); |
201 | static void db_pool_print_cmd(db_expr_t, bool, db_expr_t, const char *); |
202 | static void db_reboot_cmd(db_expr_t, bool, db_expr_t, const char *); |
203 | static void db_sifting_cmd(db_expr_t, bool, db_expr_t, const char *); |
204 | static void db_stack_trace_cmd(db_expr_t, bool, db_expr_t, const char *); |
205 | static void db_sync_cmd(db_expr_t, bool, db_expr_t, const char *); |
206 | static void db_whatis_cmd(db_expr_t, bool, db_expr_t, const char *); |
207 | static void db_uvmexp_print_cmd(db_expr_t, bool, db_expr_t, const char *); |
208 | #ifdef KERNHIST |
209 | static void db_kernhist_print_cmd(db_expr_t, bool, db_expr_t, const char *); |
210 | #endif |
211 | static void db_vnode_print_cmd(db_expr_t, bool, db_expr_t, const char *); |
212 | static void db_vmem_print_cmd(db_expr_t, bool, db_expr_t, const char *); |
213 | |
214 | static const struct db_command db_show_cmds[] = { |
215 | /*added from all sub cmds*/ |
216 | { DDB_ADD_CMD("callout" , db_show_callout, |
217 | 0 ,"List all used callout functions." ,NULL,NULL) }, |
218 | { DDB_ADD_CMD("pages" , db_show_all_pages, |
219 | 0 ,"List all used memory pages." ,NULL,NULL) }, |
220 | { DDB_ADD_CMD("proc" , db_show_proc, |
221 | 0 ,"Print process information." ,NULL,NULL) }, |
222 | { DDB_ADD_CMD("procs" , db_show_all_procs, |
223 | 0 ,"List all processes." ,NULL,NULL) }, |
224 | { DDB_ADD_CMD("pools" , db_show_all_pools, |
225 | 0 ,"Show all pools" ,NULL,NULL) }, |
226 | #ifdef AIO |
227 | /*added from all sub cmds*/ |
228 | { DDB_ADD_CMD("aio_jobs" , db_show_aio_jobs, 0, |
229 | "Show aio jobs" ,NULL,NULL) }, |
230 | #endif |
231 | { DDB_ADD_CMD("all" , NULL, |
232 | CS_COMPAT, NULL,NULL,NULL) }, |
233 | #if defined(INET) |
234 | { DDB_ADD_CMD("routes" , db_show_routes, 0,NULL,NULL,NULL) }, |
235 | #endif |
236 | #ifdef _KERNEL |
237 | { DDB_ADD_CMD("breaks" , db_listbreak_cmd, 0, |
238 | "Display all breaks." ,NULL,NULL) }, |
239 | #endif |
240 | { DDB_ADD_CMD("buf" , db_buf_print_cmd, 0, |
241 | "Print the struct buf at address." , "[/f] address" ,NULL) }, |
242 | { DDB_ADD_CMD("event" , db_event_print_cmd, 0, |
243 | "Print all the non-zero evcnt(9) event counters." , "[/fitm]" ,NULL) }, |
244 | { DDB_ADD_CMD("files" , db_show_files_cmd, 0, |
245 | "Print the files open by process at address" , |
246 | "[/f] address" , NULL) }, |
247 | { DDB_ADD_CMD("lock" , db_lock_print_cmd, 0,NULL,NULL,NULL) }, |
248 | { DDB_ADD_CMD("map" , db_map_print_cmd, 0, |
249 | "Print the vm_map at address." , "[/f] address" ,NULL) }, |
250 | { DDB_ADD_CMD("module" , db_show_module_cmd, 0, |
251 | "Print kernel modules" , NULL, NULL) }, |
252 | { DDB_ADD_CMD("mount" , db_mount_print_cmd, 0, |
253 | "Print the mount structure at address." , "[/f] address" ,NULL) }, |
254 | #ifdef MQUEUE |
255 | { DDB_ADD_CMD("mqueue" , db_show_mqueue_cmd, 0, |
256 | "Print the message queues" , NULL, NULL) }, |
257 | #endif |
258 | { DDB_ADD_CMD("mbuf" , db_mbuf_print_cmd, 0,NULL,NULL, |
259 | "-c prints all mbuf chains" ) }, |
260 | { DDB_ADD_CMD("ncache" , db_namecache_print_cmd, 0, |
261 | "Dump the namecache list." , "address" ,NULL) }, |
262 | { DDB_ADD_CMD("object" , db_object_print_cmd, 0, |
263 | "Print the vm_object at address." , "[/f] address" ,NULL) }, |
264 | { DDB_ADD_CMD("page" , db_page_print_cmd, 0, |
265 | "Print the vm_page at address." , "[/f] address" ,NULL) }, |
266 | { DDB_ADD_CMD("panic" , db_show_panic, 0, |
267 | "Print the current panic string" ,NULL,NULL) }, |
268 | { DDB_ADD_CMD("pool" , db_pool_print_cmd, 0, |
269 | "Print the pool at address." , "[/clp] address" ,NULL) }, |
270 | { DDB_ADD_CMD("registers" , db_show_regs, 0, |
271 | "Display the register set." , "[/u]" ,NULL) }, |
272 | { DDB_ADD_CMD("sched_qs" , db_show_sched_qs, 0, |
273 | "Print the state of the scheduler's run queues." , |
274 | NULL,NULL) }, |
275 | { DDB_ADD_CMD("uvmexp" , db_uvmexp_print_cmd, 0, |
276 | "Print a selection of UVM counters and statistics." , |
277 | NULL,NULL) }, |
278 | #ifdef KERNHIST |
279 | { DDB_ADD_CMD("kernhist" , db_kernhist_print_cmd, 0, |
280 | "Print the UVM history logs." , |
281 | NULL,NULL) }, |
282 | #endif |
283 | { DDB_ADD_CMD("vnode" , db_vnode_print_cmd, 0, |
284 | "Print the vnode at address." , "[/f] address" ,NULL) }, |
285 | { DDB_ADD_CMD("vmem" , db_vmem_print_cmd, 0, |
286 | "Print the vmem usage." , "[/a] address" , NULL) }, |
287 | { DDB_ADD_CMD("vmems" , db_show_all_vmems, 0, |
288 | "Show all vmems." , NULL, NULL) }, |
289 | #ifdef _KERNEL |
290 | { DDB_ADD_CMD("watches" , db_listwatch_cmd, 0, |
291 | "Display all watchpoints." , NULL,NULL) }, |
292 | #endif |
293 | { DDB_ADD_CMD(NULL, NULL, 0,NULL,NULL,NULL) } |
294 | }; |
295 | |
296 | static const struct db_command db_command_table[] = { |
297 | { DDB_ADD_CMD("b" , db_breakpoint_cmd, 0, |
298 | "Set a breakpoint at address" , "[/u] address[,count]." ,NULL) }, |
299 | { DDB_ADD_CMD("break" , db_breakpoint_cmd, 0, |
300 | "Set a breakpoint at address" , "[/u] address[,count]." ,NULL) }, |
301 | { DDB_ADD_CMD("bt" , db_stack_trace_cmd, 0, |
302 | "Show backtrace." , "See help trace." ,NULL) }, |
303 | { DDB_ADD_CMD("c" , db_continue_cmd, 0, |
304 | "Continue execution." , "[/c]" ,NULL) }, |
305 | { DDB_ADD_CMD("call" , db_fncall, CS_OWN, |
306 | "Call the function" , "address[(expression[,...])]" ,NULL) }, |
307 | { DDB_ADD_CMD("callout" , db_show_callout, 0, NULL, |
308 | NULL,NULL ) }, |
309 | { DDB_ADD_CMD("continue" , db_continue_cmd, 0, |
310 | "Continue execution." , "[/c]" ,NULL) }, |
311 | { DDB_ADD_CMD("d" , db_delete_cmd, 0, |
312 | "Delete a breakpoint." , "address | #number" ,NULL) }, |
313 | { DDB_ADD_CMD("delete" , db_delete_cmd, 0, |
314 | "Delete a breakpoint." , "address | #number" ,NULL) }, |
315 | { DDB_ADD_CMD("dmesg" , db_dmesg, 0, |
316 | "Show kernel message buffer." , "[count]" ,NULL) }, |
317 | { DDB_ADD_CMD("dwatch" , db_deletewatch_cmd, 0, |
318 | "Delete the watchpoint." , "address" ,NULL) }, |
319 | { DDB_ADD_CMD("examine" , db_examine_cmd, CS_SET_DOT, |
320 | "Display the address locations." , |
321 | "[/modifier] address[,count]" ,NULL) }, |
322 | { DDB_ADD_CMD("exit" , db_continue_cmd, 0, |
323 | "Continue execution." , "[/c]" ,NULL) }, |
324 | { DDB_ADD_CMD("help" , db_help_print_cmd, CS_OWN|CS_NOREPEAT, |
325 | "Display help about commands" , |
326 | "Use other commands as arguments." ,NULL) }, |
327 | { DDB_ADD_CMD("kill" , db_kill_proc, CS_OWN, |
328 | "Send a signal to the process" ,"pid[,signal_number]" , |
329 | " pid:\t\t\tthe process id (may need 0t prefix for decimal)\n" |
330 | " signal_number:\tthe signal to send" ) }, |
331 | #ifdef KGDB |
332 | { DDB_ADD_CMD("kgdb" , db_kgdb_cmd, 0, NULL,NULL,NULL) }, |
333 | #endif |
334 | { DDB_ADD_CMD("machine" ,NULL,CS_MACH, |
335 | "Architecture specific functions." ,NULL,NULL) }, |
336 | { DDB_ADD_CMD("match" , db_trace_until_matching_cmd,0, |
337 | "Stop at the matching return instruction." ,"See help next" ,NULL) }, |
338 | { DDB_ADD_CMD("next" , db_trace_until_matching_cmd,0, |
339 | "Stop at the matching return instruction." ,"[/p]" ,NULL) }, |
340 | { DDB_ADD_CMD("p" , db_print_cmd, 0, |
341 | "Print address according to the format." , |
342 | "[/axzodurc] address [address ...]" ,NULL) }, |
343 | { DDB_ADD_CMD("print" , db_print_cmd, 0, |
344 | "Print address according to the format." , |
345 | "[/axzodurc] address [address ...]" ,NULL) }, |
346 | { DDB_ADD_CMD("ps" , db_show_all_procs, 0, |
347 | "Print all processes." ,"See show all procs" ,NULL) }, |
348 | { DDB_ADD_CMD("quit" , db_continue_cmd, 0, |
349 | "Continue execution." , "[/c]" ,NULL) }, |
350 | { DDB_ADD_CMD("reboot" , db_reboot_cmd, CS_OWN, |
351 | "Reboot" ,"0x1 RB_ASKNAME, 0x2 RB_SINGLE, 0x4 RB_NOSYNC, 0x8 RB_HALT," |
352 | "0x40 RB_KDB, 0x100 RB_DUMP, 0x808 RB_POWERDOWN" ,NULL) }, |
353 | { DDB_ADD_CMD("s" , db_single_step_cmd, 0, |
354 | "Single-step count times." ,"[/p] [,count]" ,NULL) }, |
355 | { DDB_ADD_CMD("search" , db_search_cmd, CS_OWN|CS_SET_DOT, |
356 | "Search memory from address for value." , |
357 | "[/bhl] address value [mask] [,count]" ,NULL) }, |
358 | { DDB_ADD_CMD("set" , db_set_cmd, CS_OWN, |
359 | "Set the named variable" ,"$variable [=] expression" ,NULL) }, |
360 | { DDB_ADD_CMD("show" , NULL, CS_SHOW, |
361 | "Show kernel stats." , NULL,NULL) }, |
362 | { DDB_ADD_CMD("sifting" , db_sifting_cmd, CS_OWN, |
363 | "Search the symbol tables " ,"[/F] string" ,NULL) }, |
364 | { DDB_ADD_CMD("step" , db_single_step_cmd, 0, |
365 | "Single-step count times." ,"[/p] [,count]" ,NULL) }, |
366 | { DDB_ADD_CMD("sync" , db_sync_cmd, CS_OWN, |
367 | "Force a crash dump, and then reboot." ,NULL,NULL) }, |
368 | { DDB_ADD_CMD("trace" , db_stack_trace_cmd, 0, |
369 | "Stack trace from frame-address." , |
370 | "[/u[l]] [frame-address][,count]" ,NULL) }, |
371 | { DDB_ADD_CMD("until" , db_trace_until_call_cmd,0, |
372 | "Stop at the next call or return instruction." ,"[/p]" ,NULL) }, |
373 | { DDB_ADD_CMD("w" , db_write_cmd, CS_MORE|CS_SET_DOT, |
374 | "Write the expressions at succeeding locations." , |
375 | "[/bhl] address expression [expression ...]" ,NULL) }, |
376 | { DDB_ADD_CMD("watch" , db_watchpoint_cmd, CS_MORE, |
377 | "Set a watchpoint for a region. " ,"address[,size]" ,NULL) }, |
378 | { DDB_ADD_CMD("whatis" , db_whatis_cmd, 0, |
379 | "Describe what an address is" , "address" , NULL) }, |
380 | { DDB_ADD_CMD("write" , db_write_cmd, CS_MORE|CS_SET_DOT, |
381 | "Write the expressions at succeeding locations." , |
382 | "[/bhl] address expression [expression ...]" ,NULL) }, |
383 | { DDB_ADD_CMD("x" , db_examine_cmd, CS_SET_DOT, |
384 | "Display the address locations." , |
385 | "[/modifier] address[,count]" ,NULL) }, |
386 | { DDB_ADD_CMD(NULL, NULL, 0, NULL, NULL, NULL) } |
387 | }; |
388 | |
389 | static const struct db_command *db_last_command = NULL; |
390 | #if defined(DDB_COMMANDONENTER) |
391 | char db_cmd_on_enter[DB_LINE_MAXLEN + 1] = ___STRING(DDB_COMMANDONENTER); |
392 | #else /* defined(DDB_COMMANDONENTER) */ |
393 | char db_cmd_on_enter[DB_LINE_MAXLEN + 1] = "" ; |
394 | #endif /* defined(DDB_COMMANDONENTER) */ |
395 | #define DB_LINE_SEP ';' |
396 | |
397 | /* |
398 | * Execute commandlist after ddb start |
399 | * This function goes through the command list created from commands and ';' |
400 | */ |
401 | static void |
402 | db_execute_commandlist(const char *cmdlist) |
403 | { |
404 | const char *cmd = cmdlist; |
405 | const struct db_command *dummy = NULL; |
406 | |
407 | while (*cmd != '\0') { |
408 | const char *ep = cmd; |
409 | |
410 | while (*ep != '\0' && *ep != DB_LINE_SEP) { |
411 | ep++; |
412 | } |
413 | db_set_line(cmd, ep); |
414 | db_command(&dummy); |
415 | cmd = ep; |
416 | if (*cmd == DB_LINE_SEP) { |
417 | cmd++; |
418 | } |
419 | } |
420 | } |
421 | |
422 | /* Initialize ddb command tables */ |
423 | void |
424 | db_init_commands(void) |
425 | { |
426 | static bool done = false; |
427 | |
428 | if (done) return; |
429 | done = true; |
430 | |
431 | /* register command tables */ |
432 | (void)db_register_tbl_entry(DDB_BASE_CMD, &db_base_cmd_builtins); |
433 | #ifdef DB_MACHINE_COMMANDS |
434 | (void)db_register_tbl_entry(DDB_MACH_CMD, &db_mach_cmd_builtins); |
435 | #endif |
436 | (void)db_register_tbl_entry(DDB_SHOW_CMD, &db_show_cmd_builtins); |
437 | } |
438 | |
439 | |
440 | /* |
441 | * Add command table to the specified list |
442 | * Arg: |
443 | * int type specifies type of command table DDB_SHOW_CMD|DDB_BASE_CMD|DDB_MAC_CMD |
444 | * *cmd_tbl poiter to static allocated db_command table |
445 | * |
446 | * Command table must be NULL terminated array of struct db_command |
447 | */ |
448 | int |
449 | db_register_tbl(uint8_t type, const struct db_command *cmd_tbl) |
450 | { |
451 | struct db_cmd_tbl_en *list_ent; |
452 | |
453 | /* empty list - ignore */ |
454 | if (cmd_tbl->name == 0) |
455 | return 0; |
456 | |
457 | /* force builtin commands to be registered first */ |
458 | db_init_commands(); |
459 | |
460 | /* now create a list entry for this table */ |
461 | list_ent = db_zalloc(sizeof(*list_ent)); |
462 | if (list_ent == NULL) |
463 | return ENOMEM; |
464 | list_ent->db_cmd=cmd_tbl; |
465 | |
466 | /* and register it */ |
467 | return db_register_tbl_entry(type, list_ent); |
468 | } |
469 | |
470 | static int |
471 | db_register_tbl_entry(uint8_t type, struct db_cmd_tbl_en *list_ent) |
472 | { |
473 | struct db_cmd_tbl_en_head *list; |
474 | |
475 | switch(type) { |
476 | case DDB_BASE_CMD: |
477 | list = &db_base_cmd_list; |
478 | break; |
479 | case DDB_SHOW_CMD: |
480 | list = &db_show_cmd_list; |
481 | break; |
482 | case DDB_MACH_CMD: |
483 | list = &db_mach_cmd_list; |
484 | break; |
485 | default: |
486 | return ENOENT; |
487 | } |
488 | |
489 | TAILQ_INSERT_TAIL(list, list_ent, db_cmd_next); |
490 | |
491 | return 0; |
492 | } |
493 | |
494 | /* |
495 | * Remove command table specified with db_cmd address == cmd_tbl |
496 | */ |
497 | int |
498 | db_unregister_tbl(uint8_t type,const struct db_command *cmd_tbl) |
499 | { |
500 | struct db_cmd_tbl_en *list_ent; |
501 | struct db_cmd_tbl_en_head *list; |
502 | |
503 | /* find list on which the entry should live */ |
504 | switch (type) { |
505 | case DDB_BASE_CMD: |
506 | list=&db_base_cmd_list; |
507 | break; |
508 | case DDB_SHOW_CMD: |
509 | list=&db_show_cmd_list; |
510 | break; |
511 | case DDB_MACH_CMD: |
512 | list=&db_mach_cmd_list; |
513 | break; |
514 | default: |
515 | return EINVAL; |
516 | } |
517 | |
518 | TAILQ_FOREACH (list_ent, list, db_cmd_next) { |
519 | if (list_ent->db_cmd == cmd_tbl){ |
520 | TAILQ_REMOVE(list, |
521 | list_ent, db_cmd_next); |
522 | db_free(list_ent, sizeof(*list_ent)); |
523 | return 0; |
524 | } |
525 | } |
526 | return ENOENT; |
527 | } |
528 | |
529 | /* This function is called from machine trap code. */ |
530 | void |
531 | db_command_loop(void) |
532 | { |
533 | label_t db_jmpbuf; |
534 | label_t *savejmp; |
535 | |
536 | /* |
537 | * Initialize 'prev' and 'next' to dot. |
538 | */ |
539 | db_prev = db_dot; |
540 | db_next = db_dot; |
541 | |
542 | db_cmd_loop_done = false; |
543 | |
544 | /* Init default command tables add machine, base, |
545 | show command tables to the list */ |
546 | db_init_commands(); |
547 | |
548 | /* save context for return from ddb */ |
549 | savejmp = db_recover; |
550 | db_recover = &db_jmpbuf; |
551 | (void) setjmp(&db_jmpbuf); |
552 | |
553 | /* |
554 | * Execute default ddb start commands only if this is the |
555 | * first entry into DDB, in case the start commands fault |
556 | * and we recurse into here. |
557 | */ |
558 | if (!savejmp) |
559 | db_execute_commandlist(db_cmd_on_enter); |
560 | |
561 | (void) setjmp(&db_jmpbuf); |
562 | while (!db_cmd_loop_done) { |
563 | if (db_print_position() != 0) |
564 | db_printf("\n" ); |
565 | db_output_line = 0; |
566 | (void) db_read_line(); |
567 | db_command(&db_last_command); |
568 | } |
569 | |
570 | db_recover = savejmp; |
571 | } |
572 | |
573 | /* |
574 | * Search command table for command prefix |
575 | */ |
576 | static int |
577 | db_cmd_search_table(const char *name, |
578 | const struct db_command *table, |
579 | const struct db_command **cmdp) |
580 | { |
581 | |
582 | const struct db_command *cmd; |
583 | int result; |
584 | |
585 | result = CMD_NONE; |
586 | *cmdp = NULL; |
587 | |
588 | for (cmd = table; cmd->name != 0; cmd++) { |
589 | const char *lp; |
590 | const char *rp; |
591 | |
592 | lp = name; |
593 | rp = cmd->name; |
594 | while (*lp != '\0' && *lp == *rp) { |
595 | rp++; |
596 | lp++; |
597 | } |
598 | |
599 | if (*lp != '\0') /* mismatch or extra chars in name */ |
600 | continue; |
601 | |
602 | if (*rp == '\0') { /* exact match */ |
603 | *cmdp = cmd; |
604 | return (CMD_EXACT); |
605 | } |
606 | |
607 | /* prefix match: end of name, not end of command */ |
608 | if (result == CMD_NONE) { |
609 | result = CMD_PREFIX; |
610 | *cmdp = cmd; |
611 | } |
612 | else if (result == CMD_PREFIX) { |
613 | result = CMD_AMBIGUOUS; |
614 | *cmdp = NULL; |
615 | } |
616 | } |
617 | |
618 | return (result); |
619 | } |
620 | |
621 | |
622 | /* |
623 | * Search list of command tables for command |
624 | */ |
625 | static int |
626 | db_cmd_search(const char *name, |
627 | struct db_cmd_tbl_en_head *list_head, |
628 | const struct db_command **cmdp) |
629 | { |
630 | struct db_cmd_tbl_en *list_ent; |
631 | const struct db_command *found_command; |
632 | bool accept_prefix_match; |
633 | int result; |
634 | |
635 | result = CMD_NONE; |
636 | found_command = NULL; |
637 | accept_prefix_match = true; |
638 | |
639 | TAILQ_FOREACH(list_ent, list_head, db_cmd_next) { |
640 | const struct db_command *cmd; |
641 | int found; |
642 | |
643 | found = db_cmd_search_table(name, list_ent->db_cmd, &cmd); |
644 | if (found == CMD_EXACT) { |
645 | result = CMD_EXACT; |
646 | found_command = cmd; |
647 | break; |
648 | } |
649 | |
650 | if (found == CMD_PREFIX) { |
651 | if (accept_prefix_match) { |
652 | /* |
653 | * Continue search, but note current result |
654 | * in case we won't find anything else. |
655 | */ |
656 | accept_prefix_match = false; |
657 | result = CMD_PREFIX; |
658 | found_command = cmd; |
659 | } else { |
660 | /* |
661 | * Watch out for globally ambiguous |
662 | * prefix match that is not locally |
663 | * ambiguous - with one match in one |
664 | * table and another match(es) in |
665 | * another table. |
666 | */ |
667 | result = CMD_AMBIGUOUS; |
668 | found_command = NULL; |
669 | } |
670 | } |
671 | else if (found == CMD_AMBIGUOUS) { |
672 | accept_prefix_match = false; |
673 | result = CMD_AMBIGUOUS; |
674 | found_command = NULL; |
675 | } |
676 | } |
677 | |
678 | *cmdp = found_command; |
679 | return result; |
680 | } |
681 | |
682 | static void |
683 | db_cmd_search_failed(char *name, int search_result) |
684 | { |
685 | if (search_result == CMD_NONE) |
686 | db_printf("No such command: %s\n" , name); |
687 | else |
688 | db_printf("Ambiguous command: %s\n" , name); |
689 | } |
690 | |
691 | |
692 | /* |
693 | * List commands to the console. |
694 | */ |
695 | static void |
696 | db_cmd_list(const struct db_cmd_tbl_en_head *list) |
697 | { |
698 | |
699 | struct db_cmd_tbl_en *list_ent; |
700 | const struct db_command *table; |
701 | size_t i, j, w, columns, lines, numcmds, width=0; |
702 | const char *p; |
703 | |
704 | TAILQ_FOREACH(list_ent,list,db_cmd_next) { |
705 | table = list_ent->db_cmd; |
706 | for (i = 0; table[i].name != NULL; i++) { |
707 | w = strlen(table[i].name); |
708 | if (w > width) |
709 | width = w; |
710 | } |
711 | } |
712 | |
713 | width = DB_NEXT_TAB(width); |
714 | |
715 | columns = db_max_width / width; |
716 | if (columns == 0) |
717 | columns = 1; |
718 | |
719 | TAILQ_FOREACH(list_ent,list,db_cmd_next) { |
720 | table = list_ent->db_cmd; |
721 | |
722 | for (numcmds = 0; table[numcmds].name != NULL; numcmds++) |
723 | ; |
724 | lines = (numcmds + columns - 1) / columns; |
725 | |
726 | for (i = 0; i < lines; i++) { |
727 | for (j = 0; j < columns; j++) { |
728 | p = table[j * lines + i].name; |
729 | if (p) |
730 | db_printf("%s" , p); |
731 | if (j * lines + i + lines >= numcmds) { |
732 | db_putchar('\n'); |
733 | break; |
734 | } |
735 | if (p) { |
736 | w = strlen(p); |
737 | while (w < width) { |
738 | w = DB_NEXT_TAB(w); |
739 | db_putchar('\t'); |
740 | } |
741 | } |
742 | } |
743 | } |
744 | } |
745 | return; |
746 | } |
747 | |
748 | /* |
749 | * Read complete command with all subcommands, starting with current |
750 | * db_tok_string. If subcommand is missing, print the list of all |
751 | * subcommands. If command/subcommand is not found, print an error |
752 | * message. Returns pointer to "leaf" command or NULL. |
753 | */ |
754 | static const struct db_command * |
755 | db_read_command(void) |
756 | { |
757 | const struct db_command *command; |
758 | struct db_cmd_tbl_en_head *list; |
759 | int found; |
760 | int t; |
761 | |
762 | list = &db_base_cmd_list; |
763 | do { |
764 | found = db_cmd_search(db_tok_string, list, &command); |
765 | if (command == NULL) { |
766 | db_cmd_search_failed(db_tok_string, found); |
767 | db_flush_lex(); |
768 | return NULL; |
769 | } |
770 | |
771 | if (command->flag == CS_SHOW) |
772 | list = &db_show_cmd_list; |
773 | else if (command->flag == CS_MACH) |
774 | list = &db_mach_cmd_list; |
775 | else if (command->flag == CS_COMPAT) |
776 | /* same list */; |
777 | else |
778 | break; /* expect no more subcommands */ |
779 | |
780 | t = db_read_token(); /* read subcommand */ |
781 | if (t != tIDENT) { |
782 | /* if none given - just print all of them */ |
783 | db_cmd_list(list); |
784 | db_flush_lex(); |
785 | return NULL; |
786 | } |
787 | } while (list != NULL); |
788 | |
789 | return command; |
790 | } |
791 | |
792 | /* |
793 | * Parse command line and execute apropriate function. |
794 | */ |
795 | static void |
796 | db_command(const struct db_command **last_cmdp) |
797 | { |
798 | const struct db_command *command; |
799 | static db_expr_t last_count = 0; |
800 | db_expr_t addr, count; |
801 | char modif[TOK_STRING_SIZE]; |
802 | |
803 | int t; |
804 | bool have_addr = false; |
805 | |
806 | command = NULL; |
807 | |
808 | t = db_read_token(); |
809 | if ((t == tEOL) || (t == tCOMMA)) { |
810 | /* |
811 | * An empty line repeats last command, at 'next'. |
812 | * Only a count repeats the last command with the new count. |
813 | */ |
814 | command = *last_cmdp; |
815 | |
816 | if (!command) |
817 | return; |
818 | |
819 | addr = (db_expr_t)db_next; |
820 | if (t == tCOMMA) { |
821 | if (!db_expression(&count)) { |
822 | db_printf("Count missing\n" ); |
823 | db_flush_lex(); |
824 | return; |
825 | } |
826 | } else |
827 | count = last_count; |
828 | have_addr = false; |
829 | modif[0] = '\0'; |
830 | db_skip_to_eol(); |
831 | |
832 | } else if (t == tEXCL) { |
833 | db_fncall(0, 0, 0, NULL); |
834 | return; |
835 | |
836 | } else if (t != tIDENT) { |
837 | db_printf("?\n" ); |
838 | db_flush_lex(); |
839 | return; |
840 | |
841 | } else { |
842 | |
843 | command = db_read_command(); |
844 | if (command == NULL) |
845 | return; |
846 | |
847 | if ((command->flag & CS_OWN) == 0) { |
848 | |
849 | /* |
850 | * Standard syntax: |
851 | * command [/modifier] [addr] [,count] |
852 | */ |
853 | t = db_read_token(); /* get modifier */ |
854 | if (t == tSLASH) { |
855 | t = db_read_token(); |
856 | if (t != tIDENT) { |
857 | db_printf("Bad modifier\n" ); |
858 | db_flush_lex(); |
859 | return; |
860 | } |
861 | /* save modifier */ |
862 | strlcpy(modif, db_tok_string, sizeof(modif)); |
863 | |
864 | } else { |
865 | db_unread_token(t); |
866 | modif[0] = '\0'; |
867 | } |
868 | |
869 | if (db_expression(&addr)) { /*get address*/ |
870 | db_dot = (db_addr_t) addr; |
871 | db_last_addr = db_dot; |
872 | have_addr = true; |
873 | } else { |
874 | addr = (db_expr_t) db_dot; |
875 | have_addr = false; |
876 | } |
877 | |
878 | t = db_read_token(); |
879 | if (t == tCOMMA) { /*Get count*/ |
880 | if (!db_expression(&count)) { |
881 | db_printf("Count missing\n" ); |
882 | db_flush_lex(); |
883 | return; |
884 | } |
885 | } else { |
886 | db_unread_token(t); |
887 | count = -1; |
888 | } |
889 | if ((command->flag & CS_MORE) == 0) { |
890 | db_skip_to_eol(); |
891 | } |
892 | } |
893 | } |
894 | |
895 | if (command != NULL && command->flag & CS_NOREPEAT) { |
896 | *last_cmdp = NULL; |
897 | last_count = 0; |
898 | } else { |
899 | *last_cmdp = command; |
900 | last_count = count; |
901 | } |
902 | |
903 | |
904 | if (command != NULL) { |
905 | /* |
906 | * Execute the command. |
907 | */ |
908 | if (command->fcn != NULL) |
909 | (*command->fcn)(addr, have_addr, count, modif); |
910 | |
911 | if (command->flag & CS_SET_DOT) { |
912 | /* |
913 | * If command changes dot, set dot to |
914 | * previous address displayed (if 'ed' style). |
915 | */ |
916 | if (db_ed_style) |
917 | db_dot = db_prev; |
918 | else |
919 | db_dot = db_next; |
920 | } else { |
921 | /* |
922 | * If command does not change dot, |
923 | * set 'next' location to be the same. |
924 | */ |
925 | db_next = db_dot; |
926 | } |
927 | } |
928 | } |
929 | |
930 | /* |
931 | * Print help for commands |
932 | */ |
933 | static void |
934 | db_help_print_cmd(db_expr_t addr, bool have_addr, db_expr_t count, |
935 | const char *modif) |
936 | { |
937 | const struct db_command *command; |
938 | int t; |
939 | |
940 | t = db_read_token(); |
941 | |
942 | /* is there another command after the "help"? */ |
943 | if (t != tIDENT) { |
944 | /* print base commands */ |
945 | db_cmd_list(&db_base_cmd_list); |
946 | return; |
947 | } |
948 | |
949 | command = db_read_command(); |
950 | if (command == NULL) |
951 | return; |
952 | |
953 | #ifdef DDB_VERBOSE_HELP |
954 | db_printf("Command: %s\n" , command->name); |
955 | if (command->cmd_descr != NULL) |
956 | db_printf(" Description: %s\n" , command->cmd_descr); |
957 | if (command->cmd_arg != NULL) |
958 | db_printf(" Arguments: %s\n" , command->cmd_arg); |
959 | if (command->cmd_arg_help != NULL) |
960 | db_printf(" Arguments description:\n%s\n" , |
961 | command->cmd_arg_help); |
962 | if ((command->cmd_arg == NULL) && (command->cmd_descr == NULL)) |
963 | db_printf(" No help message.\n" ); |
964 | #endif |
965 | |
966 | db_skip_to_eol(); |
967 | } |
968 | |
969 | /*ARGSUSED*/ |
970 | static void |
971 | db_map_print_cmd(db_expr_t addr, bool have_addr, db_expr_t count, |
972 | const char *modif) |
973 | { |
974 | #ifdef _KERNEL |
975 | bool full = false; |
976 | |
977 | if (modif[0] == 'f') |
978 | full = true; |
979 | |
980 | if (have_addr == false) |
981 | addr = (db_expr_t)(uintptr_t)db_read_ptr("kernel_map" ); |
982 | |
983 | uvm_map_printit((struct vm_map *)(uintptr_t) addr, full, db_printf); |
984 | #endif /* XXX CRASH(8) */ |
985 | } |
986 | |
987 | /*ARGSUSED*/ |
988 | static void |
989 | db_object_print_cmd(db_expr_t addr, bool have_addr, |
990 | db_expr_t count, const char *modif) |
991 | { |
992 | #ifdef _KERNEL /* XXX CRASH(8) */ |
993 | bool full = false; |
994 | |
995 | if (modif[0] == 'f') |
996 | full = true; |
997 | |
998 | uvm_object_printit((struct uvm_object *)(uintptr_t) addr, full, |
999 | db_printf); |
1000 | #endif |
1001 | } |
1002 | |
1003 | /*ARGSUSED*/ |
1004 | static void |
1005 | db_page_print_cmd(db_expr_t addr, bool have_addr, |
1006 | db_expr_t count, const char *modif) |
1007 | { |
1008 | #ifdef _KERNEL /* XXX CRASH(8) */ |
1009 | bool full = false; |
1010 | |
1011 | if (modif[0] == 'f') |
1012 | full = true; |
1013 | |
1014 | uvm_page_printit((struct vm_page *)(uintptr_t) addr, full, db_printf); |
1015 | #endif |
1016 | } |
1017 | |
1018 | /*ARGSUSED*/ |
1019 | static void |
1020 | db_show_all_pages(db_expr_t addr, bool have_addr, |
1021 | db_expr_t count, const char *modif) |
1022 | { |
1023 | |
1024 | #ifdef _KERNEL /* XXX CRASH(8) */ |
1025 | uvm_page_printall(db_printf); |
1026 | #endif |
1027 | } |
1028 | |
1029 | /*ARGSUSED*/ |
1030 | static void |
1031 | db_buf_print_cmd(db_expr_t addr, bool have_addr, |
1032 | db_expr_t count, const char *modif) |
1033 | { |
1034 | #ifdef _KERNEL /* XXX CRASH(8) */ |
1035 | bool full = false; |
1036 | |
1037 | if (modif[0] == 'f') |
1038 | full = true; |
1039 | |
1040 | vfs_buf_print((struct buf *)(uintptr_t) addr, full, db_printf); |
1041 | #endif |
1042 | } |
1043 | |
1044 | /*ARGSUSED*/ |
1045 | static void |
1046 | db_event_print_cmd(db_expr_t addr, bool have_addr, |
1047 | db_expr_t count, const char *modif) |
1048 | { |
1049 | bool showzero = false; |
1050 | bool showall = true; |
1051 | bool showintr = false; |
1052 | bool showtrap = false; |
1053 | bool showmisc = false; |
1054 | struct evcnt ev, *evp; |
1055 | char buf[80]; |
1056 | int i; |
1057 | |
1058 | i = 0; |
1059 | while (modif[i]) { |
1060 | switch (modif[i]) { |
1061 | case 'f': |
1062 | showzero = true; |
1063 | break; |
1064 | case 'i': |
1065 | showintr = true; |
1066 | showall = false; |
1067 | break; |
1068 | case 't': |
1069 | showtrap = true; |
1070 | showall = false; |
1071 | break; |
1072 | case 'm': |
1073 | showmisc = true; |
1074 | showall = false; |
1075 | break; |
1076 | } |
1077 | i++; |
1078 | } |
1079 | |
1080 | if (showall) |
1081 | showmisc = showintr = showtrap = true; |
1082 | |
1083 | evp = (struct evcnt *)db_read_ptr("allevents" ); |
1084 | while (evp != NULL) { |
1085 | db_read_bytes((db_addr_t)evp, sizeof(ev), (char *)&ev); |
1086 | evp = ev.ev_list.tqe_next; |
1087 | if (ev.ev_count == 0 && !showzero) |
1088 | continue; |
1089 | if (ev.ev_type == EVCNT_TYPE_INTR && !showintr) |
1090 | continue; |
1091 | if (ev.ev_type == EVCNT_TYPE_TRAP && !showtrap) |
1092 | continue; |
1093 | if (ev.ev_type == EVCNT_TYPE_MISC && !showmisc) |
1094 | continue; |
1095 | db_read_bytes((db_addr_t)ev.ev_group, ev.ev_grouplen + 1, buf); |
1096 | db_printf("evcnt type %d: %s " , ev.ev_type, buf); |
1097 | db_read_bytes((db_addr_t)ev.ev_name, ev.ev_namelen + 1, buf); |
1098 | db_printf("%s = %lld\n" , buf, (long long)ev.ev_count); |
1099 | } |
1100 | } |
1101 | |
1102 | /*ARGSUSED*/ |
1103 | static void |
1104 | db_vnode_print_cmd(db_expr_t addr, bool have_addr, |
1105 | db_expr_t count, const char *modif) |
1106 | { |
1107 | #ifdef _KERNEL /* XXX CRASH(8) */ |
1108 | bool full = false; |
1109 | |
1110 | if (modif[0] == 'f') |
1111 | full = true; |
1112 | |
1113 | vfs_vnode_print((struct vnode *)(uintptr_t) addr, full, db_printf); |
1114 | #endif |
1115 | } |
1116 | |
1117 | /*ARGSUSED*/ |
1118 | static void |
1119 | db_vmem_print_cmd(db_expr_t addr, bool have_addr, |
1120 | db_expr_t count, const char *modif) |
1121 | { |
1122 | |
1123 | #ifdef _KERNEL /* XXX CRASH(8) */ |
1124 | vmem_print((uintptr_t) addr, modif, db_printf); |
1125 | #endif |
1126 | } |
1127 | |
1128 | static void |
1129 | db_mount_print_cmd(db_expr_t addr, bool have_addr, |
1130 | db_expr_t count, const char *modif) |
1131 | { |
1132 | #ifdef _KERNEL /* XXX CRASH(8) */ |
1133 | bool full = false; |
1134 | |
1135 | if (modif[0] == 'f') |
1136 | full = true; |
1137 | |
1138 | vfs_mount_print((struct mount *)(uintptr_t) addr, full, db_printf); |
1139 | #endif |
1140 | } |
1141 | |
1142 | /*ARGSUSED*/ |
1143 | static void |
1144 | db_mbuf_print_cmd(db_expr_t addr, bool have_addr, |
1145 | db_expr_t count, const char *modif) |
1146 | { |
1147 | |
1148 | #ifdef _KERNEL /* XXX CRASH(8) */ |
1149 | m_print((const struct mbuf *)(uintptr_t) addr, modif, db_printf); |
1150 | #endif |
1151 | } |
1152 | |
1153 | /*ARGSUSED*/ |
1154 | static void |
1155 | db_pool_print_cmd(db_expr_t addr, bool have_addr, |
1156 | db_expr_t count, const char *modif) |
1157 | { |
1158 | |
1159 | #ifdef _KERNEL /* XXX CRASH(8) */ |
1160 | pool_printit((struct pool *)(uintptr_t) addr, modif, db_printf); |
1161 | #endif |
1162 | } |
1163 | |
1164 | /*ARGSUSED*/ |
1165 | static void |
1166 | db_namecache_print_cmd(db_expr_t addr, bool have_addr, |
1167 | db_expr_t count, const char *modif) |
1168 | { |
1169 | |
1170 | #ifdef _KERNEL /* XXX CRASH(8) */ |
1171 | namecache_print((struct vnode *)(uintptr_t) addr, db_printf); |
1172 | #endif |
1173 | } |
1174 | |
1175 | /*ARGSUSED*/ |
1176 | static void |
1177 | db_uvmexp_print_cmd(db_expr_t addr, bool have_addr, |
1178 | db_expr_t count, const char *modif) |
1179 | { |
1180 | |
1181 | #ifdef _KERNEL /* XXX CRASH(8) */ |
1182 | uvmexp_print(db_printf); |
1183 | #endif |
1184 | } |
1185 | |
1186 | #ifdef KERNHIST |
1187 | /*ARGSUSED*/ |
1188 | static void |
1189 | db_kernhist_print_cmd(db_expr_t addr, bool have_addr, |
1190 | db_expr_t count, const char *modif) |
1191 | { |
1192 | |
1193 | kernhist_print((void *)(uintptr_t)addr, db_printf); |
1194 | } |
1195 | #endif |
1196 | |
1197 | /*ARGSUSED*/ |
1198 | static void |
1199 | db_lock_print_cmd(db_expr_t addr, bool have_addr, |
1200 | db_expr_t count, const char *modif) |
1201 | { |
1202 | |
1203 | #ifdef _KERNEL /* XXX CRASH(8) */ |
1204 | lockdebug_lock_print((void *)(uintptr_t)addr, db_printf); |
1205 | #endif |
1206 | } |
1207 | |
1208 | /* |
1209 | * Call random function: |
1210 | * !expr(arg,arg,arg) |
1211 | */ |
1212 | /*ARGSUSED*/ |
1213 | static void |
1214 | db_fncall(db_expr_t addr, bool have_addr, |
1215 | db_expr_t count, const char *modif) |
1216 | { |
1217 | #ifdef _KERNEL |
1218 | db_expr_t fn_addr; |
1219 | #define MAXARGS 11 |
1220 | db_expr_t args[MAXARGS]; |
1221 | int nargs = 0; |
1222 | db_expr_t retval; |
1223 | db_expr_t (*func)(db_expr_t, ...); |
1224 | int t; |
1225 | |
1226 | if (!db_expression(&fn_addr)) { |
1227 | db_printf("Bad function\n" ); |
1228 | db_flush_lex(); |
1229 | return; |
1230 | } |
1231 | func = (db_expr_t (*)(db_expr_t, ...))(uintptr_t) fn_addr; |
1232 | |
1233 | t = db_read_token(); |
1234 | if (t == tLPAREN) { |
1235 | if (db_expression(&args[0])) { |
1236 | nargs++; |
1237 | while ((t = db_read_token()) == tCOMMA) { |
1238 | if (nargs == MAXARGS) { |
1239 | db_printf("Too many arguments\n" ); |
1240 | db_flush_lex(); |
1241 | return; |
1242 | } |
1243 | if (!db_expression(&args[nargs])) { |
1244 | db_printf("Argument missing\n" ); |
1245 | db_flush_lex(); |
1246 | return; |
1247 | } |
1248 | nargs++; |
1249 | } |
1250 | db_unread_token(t); |
1251 | } |
1252 | if (db_read_token() != tRPAREN) { |
1253 | db_printf("?\n" ); |
1254 | db_flush_lex(); |
1255 | return; |
1256 | } |
1257 | } |
1258 | db_skip_to_eol(); |
1259 | |
1260 | while (nargs < MAXARGS) { |
1261 | args[nargs++] = 0; |
1262 | } |
1263 | |
1264 | retval = (*func)(args[0], args[1], args[2], args[3], args[4], |
1265 | args[5], args[6], args[7], args[8], args[9]); |
1266 | db_printf("%s\n" , db_num_to_str(retval)); |
1267 | #else /* _KERNEL */ |
1268 | db_printf("This command can only be used in-kernel.\n" ); |
1269 | #endif /* _KERNEL */ |
1270 | } |
1271 | |
1272 | static void |
1273 | db_reboot_cmd(db_expr_t addr, bool have_addr, |
1274 | db_expr_t count, const char *modif) |
1275 | { |
1276 | #ifdef _KERNEL |
1277 | db_expr_t bootflags; |
1278 | |
1279 | /* Flags, default to RB_AUTOBOOT */ |
1280 | if (!db_expression(&bootflags)) |
1281 | bootflags = (db_expr_t)RB_AUTOBOOT; |
1282 | if (db_read_token() != tEOL) { |
1283 | db_error("?\n" ); |
1284 | /*NOTREACHED*/ |
1285 | } |
1286 | /* |
1287 | * We are leaving DDB, never to return upward. |
1288 | * Clear db_recover so that we can debug faults in functions |
1289 | * called from cpu_reboot. |
1290 | */ |
1291 | db_recover = 0; |
1292 | panicstr = "reboot forced via kernel debugger" ; |
1293 | cpu_reboot((int)bootflags, NULL); |
1294 | #else /* _KERNEL */ |
1295 | db_printf("This command can only be used in-kernel.\n" ); |
1296 | #endif /* _KERNEL */ |
1297 | } |
1298 | |
1299 | static void |
1300 | db_sifting_cmd(db_expr_t addr, bool have_addr, |
1301 | db_expr_t count, const char *modif) |
1302 | { |
1303 | int mode, t; |
1304 | |
1305 | t = db_read_token(); |
1306 | if (t == tSLASH) { |
1307 | t = db_read_token(); |
1308 | if (t != tIDENT) { |
1309 | bad_modifier: |
1310 | db_printf("Bad modifier\n" ); |
1311 | db_flush_lex(); |
1312 | return; |
1313 | } |
1314 | if (!strcmp(db_tok_string, "F" )) |
1315 | mode = 'F'; |
1316 | else |
1317 | goto bad_modifier; |
1318 | t = db_read_token(); |
1319 | } else |
1320 | mode = 0; |
1321 | |
1322 | if (t == tIDENT) |
1323 | db_sifting(db_tok_string, mode); |
1324 | else { |
1325 | db_printf("Bad argument (non-string)\n" ); |
1326 | db_flush_lex(); |
1327 | } |
1328 | } |
1329 | |
1330 | static void |
1331 | db_stack_trace_cmd(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif) |
1332 | { |
1333 | register const char *cp = modif; |
1334 | register char c; |
1335 | void (*pr)(const char *, ...); |
1336 | |
1337 | pr = db_printf; |
1338 | while ((c = *cp++) != 0) |
1339 | if (c == 'l') |
1340 | pr = (void (*)(const char *, ...))printf; |
1341 | |
1342 | if (count == -1) |
1343 | count = 65535; |
1344 | |
1345 | db_stack_trace_print(addr, have_addr, count, modif, pr); |
1346 | } |
1347 | |
1348 | static void |
1349 | db_sync_cmd(db_expr_t addr, bool have_addr, |
1350 | db_expr_t count, const char *modif) |
1351 | { |
1352 | #ifdef _KERNEL |
1353 | /* |
1354 | * We are leaving DDB, never to return upward. |
1355 | * Clear db_recover so that we can debug faults in functions |
1356 | * called from cpu_reboot. |
1357 | */ |
1358 | db_recover = 0; |
1359 | panicstr = "dump forced via kernel debugger" ; |
1360 | cpu_reboot(RB_DUMP, NULL); |
1361 | #else /* _KERNEL */ |
1362 | db_printf("This command can only be used in-kernel.\n" ); |
1363 | #endif /* _KERNEL */ |
1364 | } |
1365 | |
1366 | /* |
1367 | * Describe what an address is |
1368 | */ |
1369 | void |
1370 | db_whatis_cmd(db_expr_t address, bool have_addr, |
1371 | db_expr_t count, const char *modif) |
1372 | { |
1373 | const uintptr_t addr = (uintptr_t)address; |
1374 | |
1375 | db_lwp_whatis(addr, db_printf); |
1376 | #ifdef _KERNEL /* XXX CRASH(8) */ |
1377 | pool_whatis(addr, db_printf); |
1378 | vmem_whatis(addr, db_printf); |
1379 | uvm_whatis(addr, db_printf); |
1380 | module_whatis(addr, db_printf); |
1381 | #endif |
1382 | } |
1383 | |