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