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.
11 #include "debug_commands.h"
19 #include <KernelExport.h>
22 #include <debug_heap.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
;
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
,
65 class PipeDebugOutputFilter
: public DebugOutputFilter
{
67 PipeDebugOutputFilter()
71 PipeDebugOutputFilter(debugger_command_pipe
* pipe
, int32 segment
,
72 char* buffer
, size_t bufferSize
)
77 fBufferCapacity(bufferSize
- 1),
82 virtual void PrintString(const char* string
)
87 size_t size
= strlen(string
);
88 while (const char* newLine
= strchr(string
, '\n')) {
89 size_t length
= newLine
- string
;
90 _Append(string
, length
);
93 fBuffer
[fBufferSize
] = '\0';
94 invoke_pipe_segment(fPipe
, fSegment
+ 1, fBuffer
);
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
);
111 virtual void Print(const char* format
, va_list args
)
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')) {
131 invoke_pipe_segment(fPipe
, fSegment
+ 1, line
);
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
);
145 memmove(fBuffer
, line
, left
);
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
;
159 debugger_command_pipe
* fPipe
;
162 size_t fBufferCapacity
;
167 static PipeDebugOutputFilter sPipeOutputFilters
[
168 MAX_DEBUGGER_COMMAND_PIPE_LENGTH
- 1];
172 invoke_command_trampoline(void* _parameters
)
174 invoke_command_parameters
* parameters
175 = (invoke_command_parameters
*)_parameters
;
176 parameters
->result
= parameters
->command
->func(parameters
->argc
,
182 invoke_pipe_segment(debugger_command_pipe
* pipe
, int32 index
, char* argument
)
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
];
193 segment
.argv
[segment
.argc
- 1] = argument
;
196 int32 oldIndex
= sCurrentPipeSegment
;
197 sCurrentPipeSegment
= index
;
199 int result
= invoke_debugger_command(segment
.command
, segment
.argc
,
201 segment
.invocations
++;
203 sCurrentPipeSegment
= oldIndex
;
205 // reset debug output
206 set_debug_output_filter(oldFilter
);
208 if (result
== B_KDEBUG_ERROR
) {
211 // Abort the previous pipe segment execution. The complete pipe is
212 // aborted iteratively this way.
214 abort_debugger_command();
222 next_debugger_command(debugger_command
* command
, const char* prefix
,
228 command
= command
->next
;
230 while (command
!= NULL
&& !strncmp(prefix
, command
->name
, prefixLen
) == 0)
231 command
= command
->next
;
238 find_debugger_command(const char *name
, bool partialMatch
, bool& ambiguous
)
240 debugger_command
*command
;
244 // search command by full name
246 for (command
= sCommands
; command
!= NULL
; command
= command
->next
) {
247 if (strcmp(name
, command
->name
) == 0)
251 // if it couldn't be found, search for a partial match
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
)
268 /*! Returns whether or not a debugger command is currently being invoked.
271 in_command_invocation(void)
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
);
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
304 if (gInvokeCommandDirectly
)
305 return command
->func(argc
, argv
);
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
, ¶meters
)) {
318 sInvokeCommandLevel
--;
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
329 info
->fault_address
, info
->pc
);
331 kprintf_unfiltered("\n[*** READ/WRITE FAULT (?), "
332 "pc: %#lx ***]\n", info
->pc
);
336 case INVOKE_COMMAND_ERROR
:
337 // command aborted (no page fault)
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
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
;
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
);
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;
391 if (result
!= B_KDEBUG_RESTART_PIPE
)
395 sCurrentPipe
= oldPipe
;
401 debugger_command_pipe
*
402 get_current_debugger_command_pipe()
408 debugger_command_pipe_segment
*
409 get_current_debugger_command_pipe_segment()
411 return sCurrentPipe
!= NULL
412 ? &sCurrentPipe
->segments
[sCurrentPipeSegment
] : NULL
;
417 get_debugger_commands()
424 sort_debugger_commands()
426 // bubble sort the commands
427 debugger_command
* stopCommand
= NULL
;
428 while (stopCommand
!= sCommands
) {
429 debugger_command
** command
= &sCommands
;
431 debugger_command
* nextCommand
= (*command
)->next
;
432 if (nextCommand
== stopCommand
) {
433 stopCommand
= *command
;
437 if (strcmp((*command
)->name
, nextCommand
->name
) > 0) {
438 (*command
)->next
= nextCommand
->next
;
439 nextCommand
->next
= *command
;
440 *command
= nextCommand
;
443 command
= &(*command
)->next
;
450 add_debugger_command_etc(const char* name
, debugger_command_hook func
,
451 const char* description
, const char* usage
, uint32 flags
)
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
));
464 cmd
->description
= description
;
468 InterruptsSpinLocker
_(sSpinlock
);
470 cmd
->next
= sCommands
;
478 add_debugger_command_alias(const char* newName
, const char* oldName
,
479 const char* description
)
481 // get the old command
483 debugger_command
* command
= find_debugger_command(oldName
, false,
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
);
496 print_debugger_command_usage(const char* commandName
)
500 debugger_command
* command
= find_debugger_command(commandName
, true,
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
);
511 const char* args
[3] = { NULL
, "--help", NULL
};
512 invoke_debugger_command(command
, 2, (char**)args
);
520 has_debugger_command(const char* commandName
)
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 **),
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
;
544 state
= disable_interrupts();
545 acquire_spinlock(&sSpinlock
);
548 if (!strcmp(cmd
->name
, name
) && cmd
->func
== func
)
556 if (cmd
== sCommands
)
557 sCommands
= cmd
->next
;
559 prev
->next
= cmd
->next
;
562 release_spinlock(&sSpinlock
);
563 restore_interrupts(state
);
570 return B_NAME_NOT_FOUND
;