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