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