xref: /haiku/src/system/kernel/debug/debug_commands.cpp (revision a381c8a06378de22ff08adf4282b4e3f7e50d250)
1 /*
2  * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de
3  * Copyright 2002-2008, Axel Dörfler, axeld@pinc-software.de
4  * Distributed under the terms of the MIT License.
5  *
6  * Copyright 2001, Travis Geiselbrecht. All rights reserved.
7  * Distributed under the terms of the NewOS License.
8  */
9 
10 #include "debug_commands.h"
11 
12 #include <setjmp.h>
13 #include <string.h>
14 
15 #include <KernelExport.h>
16 
17 #include <debug.h>
18 #include <lock.h>
19 #include <thread.h>
20 #include <util/AutoLock.h>
21 
22 #include "debug_variables.h"
23 
24 
25 static spinlock sSpinlock = 0;
26 
27 static struct debugger_command *sCommands;
28 
29 static jmp_buf sInvokeCommandEnv;
30 static bool sInvokeCommandDirectly = false;
31 static bool sInCommand = false;
32 
33 
34 debugger_command*
35 next_debugger_command(debugger_command* command, const char* prefix, int prefixLen)
36 {
37 	if (command == NULL)
38 		command = sCommands;
39 	else
40 		command = command->next;
41 
42 	while (command != NULL && !strncmp(prefix, command->name, prefixLen) == 0)
43 		command = command->next;
44 
45 	return command;
46 }
47 
48 
49 debugger_command *
50 find_debugger_command(const char *name, bool partialMatch, bool& ambiguous)
51 {
52 	debugger_command *command;
53 
54 	ambiguous = false;
55 
56 	// search command by full name
57 
58 	for (command = sCommands; command != NULL; command = command->next) {
59 		if (strcmp(name, command->name) == 0)
60 			return command;
61 	}
62 
63 	// if it couldn't be found, search for a partial match
64 
65 	if (partialMatch) {
66 		int length = strlen(name);
67 		command = next_debugger_command(NULL, name, length);
68 		if (command != NULL) {
69 			if (next_debugger_command(command, name, length) == NULL)
70 				return command;
71 
72 			ambiguous = true;
73 		}
74 	}
75 
76 	return NULL;
77 }
78 
79 
80 /*!	Returns wether or not a debugger command is currently being invoked.
81 */
82 bool
83 in_command_invocation(void)
84 {
85 	return sInCommand;
86 }
87 
88 
89 /*!	This function is a safe gate through which debugger commands are invoked.
90 	It sets a fault handler before invoking the command, so that an invalid
91 	memory access will not result in another KDL session on top of this one
92 	(and "cont" not to work anymore). We use setjmp() + longjmp() to "unwind"
93 	the stack after catching a fault.
94  */
95 int
96 invoke_debugger_command(struct debugger_command *command, int argc, char** argv)
97 {
98 	// intercept invocations with "--help" and directly print the usage text
99 	// If we know the command's usage text, intercept "--help" invocations
100 	// and print it directly.
101 	if (argc == 2 && strcmp(argv[1], "--help") == 0 && command->usage != NULL) {
102 		kprintf("usage: %s ", command->name);
103 		kputs(command->usage);
104 		return 0;
105 	}
106 
107 	struct thread* thread = thread_get_current_thread();
108 	addr_t oldFaultHandler = thread->fault_handler;
109 
110 	// replace argv[0] with the actual command name
111 	argv[0] = (char *)command->name;
112 
113 	// Invoking the command directly might be useful when debugging debugger
114 	// commands.
115 	if (sInvokeCommandDirectly)
116 		return command->func(argc, argv);
117 
118 	sInCommand = true;
119 
120 	if (setjmp(sInvokeCommandEnv) == 0) {
121 		int result;
122 		thread->fault_handler = (addr_t)&&error;
123 		// Fake goto to trick the compiler not to optimize the code at the label
124 		// away.
125 		if (!thread)
126 			goto error;
127 
128 		result = command->func(argc, argv);
129 
130 		thread->fault_handler = oldFaultHandler;
131 		sInCommand = false;
132 		return result;
133 
134 error:
135 		longjmp(sInvokeCommandEnv, 1);
136 			// jump into the else branch
137 	} else {
138 		kprintf("\n[*** READ/WRITE FAULT ***]\n");
139 	}
140 
141 	thread->fault_handler = oldFaultHandler;
142 	sInCommand = false;
143 	return 0;
144 }
145 
146 
147 debugger_command*
148 get_debugger_commands()
149 {
150 	return sCommands;
151 }
152 
153 
154 void
155 sort_debugger_commands()
156 {
157 	// bubble sort the commands
158 	debugger_command* stopCommand = NULL;
159 	while (stopCommand != sCommands) {
160 		debugger_command** command = &sCommands;
161 		while (true) {
162 			debugger_command* nextCommand = (*command)->next;
163 			if (nextCommand == stopCommand) {
164 				stopCommand = *command;
165 				break;
166 			}
167 
168 			if (strcmp((*command)->name, nextCommand->name) > 0) {
169 				debugger_command* tmpCommand = nextCommand->next;
170 				(*command)->next = nextCommand->next;
171 				nextCommand->next = *command;
172 				*command = nextCommand;
173 			}
174 
175 			command = &(*command)->next;
176 		}
177 	}
178 }
179 
180 
181 status_t
182 add_debugger_command_etc(const char* name, debugger_command_hook func,
183 	const char* description, const char* usage, uint32 flags)
184 {
185 	struct debugger_command *cmd;
186 
187 	cmd = (struct debugger_command*)malloc(sizeof(struct debugger_command));
188 	if (cmd == NULL)
189 		return B_NO_MEMORY;
190 
191 	cmd->func = func;
192 	cmd->name = name;
193 	cmd->description = description;
194 	cmd->usage = usage;
195 	cmd->flags = flags;
196 
197 	InterruptsSpinLocker _(sSpinlock);
198 
199 	cmd->next = sCommands;
200 	sCommands = cmd;
201 
202 	return B_OK;
203 }
204 
205 
206 status_t
207 add_debugger_command_alias(const char* newName, const char* oldName,
208 	const char* description)
209 {
210 	// get the old command
211 	bool ambiguous;
212 	debugger_command* command = find_debugger_command(oldName, false,
213 		ambiguous);
214 	if (command == NULL)
215 		return B_NAME_NOT_FOUND;
216 
217 	// register new command
218 	return add_debugger_command_etc(newName, command->func,
219 		description != NULL ? description : command->description,
220 		command->usage, command->flags);
221 }
222 
223 
224 bool
225 print_debugger_command_usage(const char* commandName)
226 {
227 	// get the command
228 	bool ambiguous;
229 	debugger_command* command = find_debugger_command(commandName, true,
230 		ambiguous);
231 	if (command == NULL)
232 		return false;
233 
234 	// directly print the usage text, if we know it, otherwise invoke the
235 	// command with "--help"
236 	if (command->usage != NULL) {
237 		kprintf("usage: %s ", command->name);
238 		kputs(command->usage);
239 	} else {
240 		char* args[3] = { NULL, "--help", NULL };
241 		invoke_debugger_command(command, 2, args);
242 	}
243 
244 	return true;
245 }
246 
247 
248 //	#pragma mark - public API
249 
250 
251 int
252 add_debugger_command(char *name, int (*func)(int, char **), char *desc)
253 {
254 	return add_debugger_command_etc(name, func, desc, NULL, 0);
255 }
256 
257 
258 int
259 remove_debugger_command(char * name, int (*func)(int, char **))
260 {
261 	struct debugger_command *cmd = sCommands;
262 	struct debugger_command *prev = NULL;
263 	cpu_status state;
264 
265 	state = disable_interrupts();
266 	acquire_spinlock(&sSpinlock);
267 
268 	while (cmd) {
269 		if (!strcmp(cmd->name, name) && cmd->func == func)
270 			break;
271 
272 		prev = cmd;
273 		cmd = cmd->next;
274 	}
275 
276 	if (cmd) {
277 		if (cmd == sCommands)
278 			sCommands = cmd->next;
279 		else
280 			prev->next = cmd->next;
281 	}
282 
283 	release_spinlock(&sSpinlock);
284 	restore_interrupts(state);
285 
286 	if (cmd) {
287 		free(cmd);
288 		return B_NO_ERROR;
289 	}
290 
291 	return B_NAME_NOT_FOUND;
292 }
293 
294