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
35struct invoke_command_parameters {
36	debugger_command*	command;
37	int					argc;
38	char**				argv;
39	int					result;
40};
41
42
43static const int32 kMaxInvokeCommandDepth = 5;
44static const int32 kOutputBufferSize = 1024;
45
46
47bool gInvokeCommandDirectly = false;
48
49static spinlock sSpinlock = B_SPINLOCK_INITIALIZER;
50
51static struct debugger_command *sCommands;
52
53static jmp_buf sInvokeCommandEnv[kMaxInvokeCommandDepth];
54static int32 sInvokeCommandLevel = 0;
55static bool sInCommand = false;
56static char sOutputBuffers[MAX_DEBUGGER_COMMAND_PIPE_LENGTH][kOutputBufferSize];
57static debugger_command_pipe* sCurrentPipe;
58static int32 sCurrentPipeSegment;
59
60
61static int invoke_pipe_segment(debugger_command_pipe* pipe, int32 index,
62	char* argument);
63
64
65class PipeDebugOutputFilter : public DebugOutputFilter {
66public:
67	PipeDebugOutputFilter()
68	{
69	}
70
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
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
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
150private:
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
158private:
159	debugger_command_pipe*	fPipe;
160	int32					fSegment;
161	char*					fBuffer;
162	size_t					fBufferCapacity;
163	size_t					fBufferSize;
164};
165
166
167static PipeDebugOutputFilter sPipeOutputFilters[
168	MAX_DEBUGGER_COMMAND_PIPE_LENGTH - 1];
169
170
171static void
172invoke_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
181static int
182invoke_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
221debugger_command*
222next_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
237debugger_command *
238find_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*/
270bool
271in_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 */
283int
284invoke_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*/
350void
351abort_debugger_command()
352{
353	if (!gInvokeCommandDirectly && sInvokeCommandLevel > 0) {
354		longjmp(sInvokeCommandEnv[--sInvokeCommandLevel],
355			INVOKE_COMMAND_ERROR);
356	}
357}
358
359
360int
361invoke_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
401debugger_command_pipe*
402get_current_debugger_command_pipe()
403{
404	return sCurrentPipe;
405}
406
407
408debugger_command_pipe_segment*
409get_current_debugger_command_pipe_segment()
410{
411	return sCurrentPipe != NULL
412		? &sCurrentPipe->segments[sCurrentPipeSegment] : NULL;
413}
414
415
416debugger_command*
417get_debugger_commands()
418{
419	return sCommands;
420}
421
422
423void
424sort_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
449status_t
450add_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
477status_t
478add_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
495bool
496print_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
519bool
520has_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
529int
530add_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
537int
538remove_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