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