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