btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / system / kernel / debug / debug_commands.cpp
blob404d94f1b6df494be2aaf9666b93defce8a598a8
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.
6 * Copyright 2001, Travis Geiselbrecht. All rights reserved.
7 * Distributed under the terms of the NewOS License.
8 */
11 #include "debug_commands.h"
13 #include <setjmp.h>
14 #include <stdio.h>
15 #include <string.h>
17 #include <algorithm>
19 #include <KernelExport.h>
21 #include <debug.h>
22 #include <debug_heap.h>
23 #include <lock.h>
24 #include <thread.h>
25 #include <util/AutoLock.h>
27 #include "debug_output_filter.h"
28 #include "debug_variables.h"
31 #define INVOKE_COMMAND_FAULT 1
32 #define INVOKE_COMMAND_ERROR 2
35 struct invoke_command_parameters {
36 debugger_command* command;
37 int argc;
38 char** argv;
39 int result;
43 static const int32 kMaxInvokeCommandDepth = 5;
44 static const int32 kOutputBufferSize = 1024;
47 bool gInvokeCommandDirectly = false;
49 static spinlock sSpinlock = B_SPINLOCK_INITIALIZER;
51 static struct debugger_command *sCommands;
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;
61 static int invoke_pipe_segment(debugger_command_pipe* pipe, int32 index,
62 char* argument);
65 class PipeDebugOutputFilter : public DebugOutputFilter {
66 public:
67 PipeDebugOutputFilter()
71 PipeDebugOutputFilter(debugger_command_pipe* pipe, int32 segment,
72 char* buffer, size_t bufferSize)
74 fPipe(pipe),
75 fSegment(segment),
76 fBuffer(buffer),
77 fBufferCapacity(bufferSize - 1),
78 fBufferSize(0)
82 virtual void PrintString(const char* string)
84 if (fPipe->broken)
85 return;
87 size_t size = strlen(string);
88 while (const char* newLine = strchr(string, '\n')) {
89 size_t length = newLine - string;
90 _Append(string, length);
92 // invoke command
93 fBuffer[fBufferSize] = '\0';
94 invoke_pipe_segment(fPipe, fSegment + 1, fBuffer);
96 fBufferSize = 0;
98 string = newLine + 1;
99 size -= length + 1;
102 _Append(string, size);
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;
111 virtual void Print(const char* format, va_list args)
113 if (fPipe->broken)
114 return;
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);
124 // execute every complete line
125 fBuffer[fBufferSize] = '\0';
126 char* line = fBuffer;
128 while (char* newLine = strchr(line, '\n')) {
129 // invoke command
130 *newLine = '\0';
131 invoke_pipe_segment(fPipe, fSegment + 1, line);
133 line = newLine + 1;
136 size_t left = fBuffer + fBufferSize - line;
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;
144 if (left > 0)
145 memmove(fBuffer, line, left);
147 fBufferSize = left;
150 private:
151 void _Append(const char* string, size_t length)
153 size_t toAppend = min_c(length, fBufferCapacity - fBufferSize);
154 memcpy(fBuffer + fBufferSize, string, toAppend);
155 fBufferSize += length;
158 private:
159 debugger_command_pipe* fPipe;
160 int32 fSegment;
161 char* fBuffer;
162 size_t fBufferCapacity;
163 size_t fBufferSize;
167 static PipeDebugOutputFilter sPipeOutputFilters[
168 MAX_DEBUGGER_COMMAND_PIPE_LENGTH - 1];
171 static void
172 invoke_command_trampoline(void* _parameters)
174 invoke_command_parameters* parameters
175 = (invoke_command_parameters*)_parameters;
176 parameters->result = parameters->command->func(parameters->argc,
177 parameters->argv);
181 static int
182 invoke_pipe_segment(debugger_command_pipe* pipe, int32 index, char* argument)
184 // set debug output
185 DebugOutputFilter* oldFilter = set_debug_output_filter(
186 index == pipe->segment_count - 1
187 ? &gDefaultDebugOutputFilter
188 : (DebugOutputFilter*)&sPipeOutputFilters[index]);
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;
195 // invoke command
196 int32 oldIndex = sCurrentPipeSegment;
197 sCurrentPipeSegment = index;
199 int result = invoke_debugger_command(segment.command, segment.argc,
200 segment.argv);
201 segment.invocations++;
203 sCurrentPipeSegment = oldIndex;
205 // reset debug output
206 set_debug_output_filter(oldFilter);
208 if (result == B_KDEBUG_ERROR) {
209 pipe->broken = true;
211 // Abort the previous pipe segment execution. The complete pipe is
212 // aborted iteratively this way.
213 if (index > 0)
214 abort_debugger_command();
217 return result;
221 debugger_command*
222 next_debugger_command(debugger_command* command, const char* prefix,
223 int prefixLen)
225 if (command == NULL)
226 command = sCommands;
227 else
228 command = command->next;
230 while (command != NULL && !strncmp(prefix, command->name, prefixLen) == 0)
231 command = command->next;
233 return command;
237 debugger_command *
238 find_debugger_command(const char *name, bool partialMatch, bool& ambiguous)
240 debugger_command *command;
242 ambiguous = false;
244 // search command by full name
246 for (command = sCommands; command != NULL; command = command->next) {
247 if (strcmp(name, command->name) == 0)
248 return command;
251 // if it couldn't be found, search for a partial match
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;
260 ambiguous = true;
264 return NULL;
268 /*! Returns whether or not a debugger command is currently being invoked.
270 bool
271 in_command_invocation(void)
273 return sInCommand;
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.
284 invoke_debugger_command(struct debugger_command *command, int argc, char** argv)
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;
296 // replace argv[0] with the actual command name
297 argv[0] = (char *)command->name;
299 DebugAllocPoolScope allocPoolScope;
300 // Will automatically clean up all allocations the command leaves over.
302 // Invoking the command directly might be useful when debugging debugger
303 // commands.
304 if (gInvokeCommandDirectly)
305 return command->func(argc, argv);
307 sInCommand = true;
309 invoke_command_parameters parameters;
310 parameters.command = command;
311 parameters.argc = argc;
312 parameters.argv = argv;
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;
322 case INVOKE_COMMAND_FAULT:
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);
334 break;
336 case INVOKE_COMMAND_ERROR:
337 // command aborted (no page fault)
338 break;
341 sInCommand = false;
342 return B_KDEBUG_ERROR;
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.
350 void
351 abort_debugger_command()
353 if (!gInvokeCommandDirectly && sInvokeCommandLevel > 0) {
354 longjmp(sInvokeCommandEnv[--sInvokeCommandLevel],
355 INVOKE_COMMAND_ERROR);
361 invoke_debugger_command_pipe(debugger_command_pipe* pipe)
363 debugger_command_pipe* oldPipe = sCurrentPipe;
364 sCurrentPipe = pipe;
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);
374 int result;
375 while (true) {
376 result = invoke_pipe_segment(pipe, 0, NULL);
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;
391 if (result != B_KDEBUG_RESTART_PIPE)
392 break;
395 sCurrentPipe = oldPipe;
397 return result;
401 debugger_command_pipe*
402 get_current_debugger_command_pipe()
404 return sCurrentPipe;
408 debugger_command_pipe_segment*
409 get_current_debugger_command_pipe_segment()
411 return sCurrentPipe != NULL
412 ? &sCurrentPipe->segments[sCurrentPipeSegment] : NULL;
416 debugger_command*
417 get_debugger_commands()
419 return sCommands;
423 void
424 sort_debugger_commands()
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;
437 if (strcmp((*command)->name, nextCommand->name) > 0) {
438 (*command)->next = nextCommand->next;
439 nextCommand->next = *command;
440 *command = nextCommand;
443 command = &(*command)->next;
449 status_t
450 add_debugger_command_etc(const char* name, debugger_command_hook func,
451 const char* description, const char* usage, uint32 flags)
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;
458 cmd = (debugger_command*)malloc(sizeof(debugger_command));
459 if (cmd == NULL)
460 return B_NO_MEMORY;
462 cmd->func = func;
463 cmd->name = name;
464 cmd->description = description;
465 cmd->usage = usage;
466 cmd->flags = flags;
468 InterruptsSpinLocker _(sSpinlock);
470 cmd->next = sCommands;
471 sCommands = cmd;
473 return B_OK;
477 status_t
478 add_debugger_command_alias(const char* newName, const char* oldName,
479 const char* description)
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;
488 // register new command
489 return add_debugger_command_etc(newName, command->func,
490 description != NULL ? description : command->description,
491 command->usage, command->flags);
495 bool
496 print_debugger_command_usage(const char* commandName)
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;
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);
515 return true;
519 bool
520 has_debugger_command(const char* commandName)
522 bool ambiguous;
523 return find_debugger_command(commandName, false, ambiguous) != NULL;
527 // #pragma mark - public API
530 add_debugger_command(const char *name, int (*func)(int, char **),
531 const char *desc)
533 return add_debugger_command_etc(name, func, desc, NULL, 0);
538 remove_debugger_command(const char * name, int (*func)(int, char **))
540 struct debugger_command *cmd = sCommands;
541 struct debugger_command *prev = NULL;
542 cpu_status state;
544 state = disable_interrupts();
545 acquire_spinlock(&sSpinlock);
547 while (cmd) {
548 if (!strcmp(cmd->name, name) && cmd->func == func)
549 break;
551 prev = cmd;
552 cmd = cmd->next;
555 if (cmd) {
556 if (cmd == sCommands)
557 sCommands = cmd->next;
558 else
559 prev->next = cmd->next;
562 release_spinlock(&sSpinlock);
563 restore_interrupts(state);
565 if (cmd) {
566 free(cmd);
567 return B_NO_ERROR;
570 return B_NAME_NOT_FOUND;