xref: /haiku/src/system/kernel/debug/debug_commands.cpp (revision 746cac055adc6ac3308c7bc2d29040fb95689cc9)
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 <stdio.h>
14 #include <string.h>
15 
16 #include <KernelExport.h>
17 
18 #include <debug.h>
19 #include <lock.h>
20 #include <thread.h>
21 #include <util/AutoLock.h>
22 
23 #include "debug_output_filter.h"
24 #include "debug_variables.h"
25 
26 
27 #define INVOKE_COMMAND_FAULT	1
28 #define INVOKE_COMMAND_ERROR	2
29 
30 static const int32 kMaxInvokeCommandDepth = 5;
31 static const int32 kOutputBufferSize = 1024;
32 
33 static spinlock sSpinlock = B_SPINLOCK_INITIALIZER;
34 
35 static struct debugger_command *sCommands;
36 
37 static jmp_buf sInvokeCommandEnv[kMaxInvokeCommandDepth];
38 static int32 sInvokeCommandLevel = 0;
39 static bool sInvokeCommandDirectly = false;
40 static bool sInCommand = false;
41 static char sOutputBuffers[MAX_DEBUGGER_COMMAND_PIPE_LENGTH][kOutputBufferSize];
42 static debugger_command_pipe* sCurrentPipe;
43 static int32 sCurrentPipeSegment;
44 
45 
46 static int invoke_pipe_segment(debugger_command_pipe* pipe, int32 index,
47 	char* argument);
48 
49 
50 class PipeDebugOutputFilter : public DebugOutputFilter {
51 public:
52 	PipeDebugOutputFilter()
53 	{
54 	}
55 
56 	PipeDebugOutputFilter(debugger_command_pipe* pipe, int32 segment,
57 			char* buffer, size_t bufferSize)
58 		:
59 		fPipe(pipe),
60 		fSegment(segment),
61 		fBuffer(buffer),
62 		fBufferCapacity(bufferSize - 1),
63 		fBufferSize(0)
64 	{
65 	}
66 
67 	virtual void PrintString(const char* string)
68 	{
69 		if (fPipe->broken)
70 			return;
71 
72 		size_t size = strlen(string);
73 		while (const char* newLine = strchr(string, '\n')) {
74 			size_t length = newLine - string;
75 			_Append(string, length);
76 
77 			// invoke command
78 			fBuffer[fBufferSize] = '\0';
79 			invoke_pipe_segment(fPipe, fSegment + 1, fBuffer);
80 
81 			fBufferSize = 0;
82 
83 			string = newLine + 1;
84 			size -= length + 1;
85 		}
86 
87 		_Append(string, size);
88 
89 		if (fBufferSize == fBufferCapacity) {
90 			// buffer is full, but contains no newline -- execute anyway
91 			invoke_pipe_segment(fPipe, fSegment + 1, fBuffer);
92 			fBufferSize = 0;
93 		}
94 	}
95 
96 	virtual void Print(const char* format, va_list args)
97 	{
98 		if (fPipe->broken)
99 			return;
100 
101 		// print directly into the buffer
102 		fBufferSize += vsnprintf(fBuffer + fBufferSize,
103 			fBufferCapacity - fBufferSize, format, args);
104 
105 		// execute every complete line
106 		fBuffer[fBufferSize] = '\0';
107 		char* line = fBuffer;
108 
109 		while (char* newLine = strchr(line, '\n')) {
110 			// invoke command
111 			*newLine = '\0';
112 			invoke_pipe_segment(fPipe, fSegment + 1, line);
113 
114 			line = newLine + 1;
115 		}
116 
117 		size_t left = fBuffer + fBufferSize - line;
118 
119 		if (left == fBufferCapacity) {
120 			// buffer is full, but contains no newline -- execute anyway
121 			invoke_pipe_segment(fPipe, fSegment + 1, fBuffer);
122 			left = 0;
123 		}
124 
125 		if (left > 0)
126 			memmove(fBuffer, line, left);
127 
128 		fBufferSize = left;
129 	}
130 
131 private:
132 	void _Append(const char* string, size_t length)
133 	{
134 		size_t toAppend = min_c(length, fBufferCapacity - fBufferSize);
135 		memcpy(fBuffer + fBufferSize, string, toAppend);
136 		fBufferSize += length;
137 	}
138 
139 private:
140 	debugger_command_pipe*	fPipe;
141 	int32					fSegment;
142 	char*					fBuffer;
143 	size_t					fBufferCapacity;
144 	size_t					fBufferSize;
145 };
146 
147 
148 static PipeDebugOutputFilter sPipeOutputFilters[
149 	MAX_DEBUGGER_COMMAND_PIPE_LENGTH - 1];
150 
151 
152 static int
153 invoke_pipe_segment(debugger_command_pipe* pipe, int32 index, char* argument)
154 {
155 	// set debug output
156 	DebugOutputFilter* oldFilter = set_debug_output_filter(
157 		index == pipe->segment_count - 1
158 			? &gDefaultDebugOutputFilter
159 			: (DebugOutputFilter*)&sPipeOutputFilters[index]);
160 
161 	// set last command argument
162 	debugger_command_pipe_segment& segment = pipe->segments[index];
163 	if (index > 0)
164 		segment.argv[segment.argc - 1] = argument;
165 
166 	// invoke command
167 	int32 oldIndex = sCurrentPipeSegment;
168 	sCurrentPipeSegment = index;
169 
170 	int result = invoke_debugger_command(segment.command, segment.argc,
171 		segment.argv);
172 	segment.invocations++;
173 
174 	sCurrentPipeSegment = oldIndex;
175 
176 	// reset debug output
177 	set_debug_output_filter(oldFilter);
178 
179 	if (result == B_KDEBUG_ERROR) {
180 		pipe->broken = true;
181 
182 		// Abort the previous pipe segment execution. The complete pipe is
183 		// aborted iteratively this way.
184 		if (index > 0)
185 			abort_debugger_command();
186 	}
187 
188 	return result;
189 }
190 
191 
192 debugger_command*
193 next_debugger_command(debugger_command* command, const char* prefix,
194 	int prefixLen)
195 {
196 	if (command == NULL)
197 		command = sCommands;
198 	else
199 		command = command->next;
200 
201 	while (command != NULL && !strncmp(prefix, command->name, prefixLen) == 0)
202 		command = command->next;
203 
204 	return command;
205 }
206 
207 
208 debugger_command *
209 find_debugger_command(const char *name, bool partialMatch, bool& ambiguous)
210 {
211 	debugger_command *command;
212 
213 	ambiguous = false;
214 
215 	// search command by full name
216 
217 	for (command = sCommands; command != NULL; command = command->next) {
218 		if (strcmp(name, command->name) == 0)
219 			return command;
220 	}
221 
222 	// if it couldn't be found, search for a partial match
223 
224 	if (partialMatch) {
225 		int length = strlen(name);
226 		command = next_debugger_command(NULL, name, length);
227 		if (command != NULL) {
228 			if (next_debugger_command(command, name, length) == NULL)
229 				return command;
230 
231 			ambiguous = true;
232 		}
233 	}
234 
235 	return NULL;
236 }
237 
238 
239 /*!	Returns wether or not a debugger command is currently being invoked.
240 */
241 bool
242 in_command_invocation(void)
243 {
244 	return sInCommand;
245 }
246 
247 
248 /*!	This function is a safe gate through which debugger commands are invoked.
249 	It sets a fault handler before invoking the command, so that an invalid
250 	memory access will not result in another KDL session on top of this one
251 	(and "cont" not to work anymore). We use setjmp() + longjmp() to "unwind"
252 	the stack after catching a fault.
253  */
254 int
255 invoke_debugger_command(struct debugger_command *command, int argc, char** argv)
256 {
257 	// intercept invocations with "--help" and directly print the usage text
258 	// If we know the command's usage text, intercept "--help" invocations
259 	// and print it directly.
260 	if (argc == 2 && argv[1] != NULL && strcmp(argv[1], "--help") == 0
261 			&& command->usage != NULL) {
262 		kprintf_unfiltered("usage: %s ", command->name);
263 		kputs_unfiltered(command->usage);
264 		return 0;
265 	}
266 
267 	struct thread* thread = thread_get_current_thread();
268 	addr_t oldFaultHandler = thread->fault_handler;
269 
270 	// replace argv[0] with the actual command name
271 	argv[0] = (char *)command->name;
272 
273 	// Invoking the command directly might be useful when debugging debugger
274 	// commands.
275 	if (sInvokeCommandDirectly)
276 		return command->func(argc, argv);
277 
278 	sInCommand = true;
279 
280 	switch (setjmp(sInvokeCommandEnv[sInvokeCommandLevel++])) {
281 		case 0:
282 			int result;
283 			thread->fault_handler = (addr_t)&&error;
284 			// Fake goto to trick the compiler not to optimize the code at the
285 			// label away.
286 			if (!thread)
287 				goto error;
288 
289 			result = command->func(argc, argv);
290 
291 			thread->fault_handler = oldFaultHandler;
292 			sInvokeCommandLevel--;
293 			sInCommand = false;
294 			return result;
295 
296 error:
297 			// jump to INVOKE_COMMAND_FAULT case, cleaning up the stack
298 			longjmp(sInvokeCommandEnv[--sInvokeCommandLevel],
299 				INVOKE_COMMAND_FAULT);
300 
301 		case INVOKE_COMMAND_FAULT:
302 		{
303 			debug_page_fault_info* info = debug_get_page_fault_info();
304 			if ((info->flags & DEBUG_PAGE_FAULT_NO_INFO) == 0) {
305 				kprintf_unfiltered("\n[*** %s FAULT at %#lx, pc: %#lx ***]\n",
306 					(info->flags & DEBUG_PAGE_FAULT_NO_INFO) != 0
307 						? "WRITE" : "READ",
308 					info->fault_address, info->pc);
309 			} else {
310 				kprintf_unfiltered("\n[*** READ/WRITE FAULT (?), "
311 					"pc: %#lx ***]\n", info->pc);
312 			}
313 			break;
314 		}
315 		case INVOKE_COMMAND_ERROR:
316 			// command aborted (no page fault)
317 			break;
318 	}
319 
320 	thread->fault_handler = oldFaultHandler;
321 	sInCommand = false;
322 	return B_KDEBUG_ERROR;
323 }
324 
325 
326 /*!	Aborts the currently executed debugger command (in fact the complete pipe),
327 	unless direct command invocation has been set. If successful, the function
328 	won't return.
329 */
330 void
331 abort_debugger_command()
332 {
333 	if (!sInvokeCommandDirectly && sInvokeCommandLevel > 0) {
334 		longjmp(sInvokeCommandEnv[--sInvokeCommandLevel],
335 			INVOKE_COMMAND_ERROR);
336 	}
337 }
338 
339 
340 int
341 invoke_debugger_command_pipe(debugger_command_pipe* pipe)
342 {
343 	debugger_command_pipe* oldPipe = sCurrentPipe;
344 	sCurrentPipe = pipe;
345 
346 	// prepare outputs
347 	// TODO: If a pipe is invoked in a pipe, outputs will clash.
348 	int32 segments = pipe->segment_count;
349 	for (int32 i = 0; i < segments - 1; i++) {
350 		new(&sPipeOutputFilters[i]) PipeDebugOutputFilter(pipe, i,
351 			sOutputBuffers[i], kOutputBufferSize);
352 	}
353 
354 	int result = invoke_pipe_segment(pipe, 0, NULL);
355 
356 	// perform final rerun for all commands that want it
357 	for (int32 i = 1; result != B_KDEBUG_ERROR && i < segments; i++) {
358 		debugger_command_pipe_segment& segment = pipe->segments[i];
359 		if ((segment.command->flags & B_KDEBUG_PIPE_FINAL_RERUN) != 0)
360 			result = invoke_pipe_segment(pipe, i, NULL);
361 	}
362 
363 	sCurrentPipe = oldPipe;
364 
365 	return result;
366 }
367 
368 
369 debugger_command_pipe*
370 get_current_debugger_command_pipe()
371 {
372 	return sCurrentPipe;
373 }
374 
375 
376 debugger_command_pipe_segment*
377 get_current_debugger_command_pipe_segment()
378 {
379 	return sCurrentPipe != NULL
380 		? &sCurrentPipe->segments[sCurrentPipeSegment] : NULL;
381 }
382 
383 
384 debugger_command*
385 get_debugger_commands()
386 {
387 	return sCommands;
388 }
389 
390 
391 void
392 sort_debugger_commands()
393 {
394 	// bubble sort the commands
395 	debugger_command* stopCommand = NULL;
396 	while (stopCommand != sCommands) {
397 		debugger_command** command = &sCommands;
398 		while (true) {
399 			debugger_command* nextCommand = (*command)->next;
400 			if (nextCommand == stopCommand) {
401 				stopCommand = *command;
402 				break;
403 			}
404 
405 			if (strcmp((*command)->name, nextCommand->name) > 0) {
406 				(*command)->next = nextCommand->next;
407 				nextCommand->next = *command;
408 				*command = nextCommand;
409 			}
410 
411 			command = &(*command)->next;
412 		}
413 	}
414 }
415 
416 
417 status_t
418 add_debugger_command_etc(const char* name, debugger_command_hook func,
419 	const char* description, const char* usage, uint32 flags)
420 {
421 	struct debugger_command *cmd;
422 
423 	cmd = (struct debugger_command*)malloc(sizeof(struct debugger_command));
424 	if (cmd == NULL)
425 		return B_NO_MEMORY;
426 
427 	cmd->func = func;
428 	cmd->name = name;
429 	cmd->description = description;
430 	cmd->usage = usage;
431 	cmd->flags = flags;
432 
433 	InterruptsSpinLocker _(sSpinlock);
434 
435 	cmd->next = sCommands;
436 	sCommands = cmd;
437 
438 	return B_OK;
439 }
440 
441 
442 status_t
443 add_debugger_command_alias(const char* newName, const char* oldName,
444 	const char* description)
445 {
446 	// get the old command
447 	bool ambiguous;
448 	debugger_command* command = find_debugger_command(oldName, false,
449 		ambiguous);
450 	if (command == NULL)
451 		return B_NAME_NOT_FOUND;
452 
453 	// register new command
454 	return add_debugger_command_etc(newName, command->func,
455 		description != NULL ? description : command->description,
456 		command->usage, command->flags);
457 }
458 
459 
460 bool
461 print_debugger_command_usage(const char* commandName)
462 {
463 	// get the command
464 	bool ambiguous;
465 	debugger_command* command = find_debugger_command(commandName, true,
466 		ambiguous);
467 	if (command == NULL)
468 		return false;
469 
470 	// directly print the usage text, if we know it, otherwise invoke the
471 	// command with "--help"
472 	if (command->usage != NULL) {
473 		kprintf_unfiltered("usage: %s ", command->name);
474 		kputs_unfiltered(command->usage);
475 	} else {
476 		char* args[3] = { NULL, "--help", NULL };
477 		invoke_debugger_command(command, 2, args);
478 	}
479 
480 	return true;
481 }
482 
483 
484 //	#pragma mark - public API
485 
486 
487 int
488 add_debugger_command(char *name, int (*func)(int, char **), char *desc)
489 {
490 	return add_debugger_command_etc(name, func, desc, NULL, 0);
491 }
492 
493 
494 int
495 remove_debugger_command(char * name, int (*func)(int, char **))
496 {
497 	struct debugger_command *cmd = sCommands;
498 	struct debugger_command *prev = NULL;
499 	cpu_status state;
500 
501 	state = disable_interrupts();
502 	acquire_spinlock(&sSpinlock);
503 
504 	while (cmd) {
505 		if (!strcmp(cmd->name, name) && cmd->func == func)
506 			break;
507 
508 		prev = cmd;
509 		cmd = cmd->next;
510 	}
511 
512 	if (cmd) {
513 		if (cmd == sCommands)
514 			sCommands = cmd->next;
515 		else
516 			prev->next = cmd->next;
517 	}
518 
519 	release_spinlock(&sSpinlock);
520 	restore_interrupts(state);
521 
522 	if (cmd) {
523 		free(cmd);
524 		return B_NO_ERROR;
525 	}
526 
527 	return B_NAME_NOT_FOUND;
528 }
529 
530