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 wether 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 struct debugger_command *cmd; 454 455 cmd = (struct debugger_command*)malloc(sizeof(struct debugger_command)); 456 if (cmd == NULL) 457 return B_NO_MEMORY; 458 459 cmd->func = func; 460 cmd->name = name; 461 cmd->description = description; 462 cmd->usage = usage; 463 cmd->flags = flags; 464 465 InterruptsSpinLocker _(sSpinlock); 466 467 cmd->next = sCommands; 468 sCommands = cmd; 469 470 return B_OK; 471 } 472 473 474 status_t 475 add_debugger_command_alias(const char* newName, const char* oldName, 476 const char* description) 477 { 478 // get the old command 479 bool ambiguous; 480 debugger_command* command = find_debugger_command(oldName, false, 481 ambiguous); 482 if (command == NULL) 483 return B_NAME_NOT_FOUND; 484 485 // register new command 486 return add_debugger_command_etc(newName, command->func, 487 description != NULL ? description : command->description, 488 command->usage, command->flags); 489 } 490 491 492 bool 493 print_debugger_command_usage(const char* commandName) 494 { 495 // get the command 496 bool ambiguous; 497 debugger_command* command = find_debugger_command(commandName, true, 498 ambiguous); 499 if (command == NULL) 500 return false; 501 502 // directly print the usage text, if we know it, otherwise invoke the 503 // command with "--help" 504 if (command->usage != NULL) { 505 kprintf_unfiltered("usage: %s ", command->name); 506 kputs_unfiltered(command->usage); 507 } else { 508 const char* args[3] = { NULL, "--help", NULL }; 509 invoke_debugger_command(command, 2, (char**)args); 510 } 511 512 return true; 513 } 514 515 516 bool 517 has_debugger_command(const char* commandName) 518 { 519 bool ambiguous; 520 return find_debugger_command(commandName, false, ambiguous) != NULL; 521 } 522 523 524 // #pragma mark - public API 525 526 int 527 add_debugger_command(const char *name, int (*func)(int, char **), 528 const char *desc) 529 { 530 return add_debugger_command_etc(name, func, desc, NULL, 0); 531 } 532 533 534 int 535 remove_debugger_command(const char * name, int (*func)(int, char **)) 536 { 537 struct debugger_command *cmd = sCommands; 538 struct debugger_command *prev = NULL; 539 cpu_status state; 540 541 state = disable_interrupts(); 542 acquire_spinlock(&sSpinlock); 543 544 while (cmd) { 545 if (!strcmp(cmd->name, name) && cmd->func == func) 546 break; 547 548 prev = cmd; 549 cmd = cmd->next; 550 } 551 552 if (cmd) { 553 if (cmd == sCommands) 554 sCommands = cmd->next; 555 else 556 prev->next = cmd->next; 557 } 558 559 release_spinlock(&sSpinlock); 560 restore_interrupts(state); 561 562 if (cmd) { 563 free(cmd); 564 return B_NO_ERROR; 565 } 566 567 return B_NAME_NOT_FOUND; 568 } 569 570