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, ¶meters)) {
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