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: 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 150 private: 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 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 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* 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 * 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 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 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 351 abort_debugger_command() 352 { 353 if (!gInvokeCommandDirectly && sInvokeCommandLevel > 0) { 354 longjmp(sInvokeCommandEnv[--sInvokeCommandLevel], 355 INVOKE_COMMAND_ERROR); 356 } 357 } 358 359 360 int 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* 402 get_current_debugger_command_pipe() 403 { 404 return sCurrentPipe; 405 } 406 407 408 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* 417 get_debugger_commands() 418 { 419 return sCommands; 420 } 421 422 423 void 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 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 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 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 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 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 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