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