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