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