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 <string.h> 14 15 #include <KernelExport.h> 16 17 #include <debug.h> 18 #include <lock.h> 19 #include <thread.h> 20 #include <util/AutoLock.h> 21 22 #include "debug_variables.h" 23 24 25 static spinlock sSpinlock = 0; 26 27 static struct debugger_command *sCommands; 28 29 static jmp_buf sInvokeCommandEnv; 30 static bool sInvokeCommandDirectly = false; 31 static bool sInCommand = false; 32 33 34 debugger_command* 35 next_debugger_command(debugger_command* command, const char* prefix, int prefixLen) 36 { 37 if (command == NULL) 38 command = sCommands; 39 else 40 command = command->next; 41 42 while (command != NULL && !strncmp(prefix, command->name, prefixLen) == 0) 43 command = command->next; 44 45 return command; 46 } 47 48 49 debugger_command * 50 find_debugger_command(const char *name, bool partialMatch, bool& ambiguous) 51 { 52 debugger_command *command; 53 54 ambiguous = false; 55 56 // search command by full name 57 58 for (command = sCommands; command != NULL; command = command->next) { 59 if (strcmp(name, command->name) == 0) 60 return command; 61 } 62 63 // if it couldn't be found, search for a partial match 64 65 if (partialMatch) { 66 int length = strlen(name); 67 command = next_debugger_command(NULL, name, length); 68 if (command != NULL) { 69 if (next_debugger_command(command, name, length) == NULL) 70 return command; 71 72 ambiguous = true; 73 } 74 } 75 76 return NULL; 77 } 78 79 80 /*! Returns wether or not a debugger command is currently being invoked. 81 */ 82 bool 83 in_command_invocation(void) 84 { 85 return sInCommand; 86 } 87 88 89 /*! This function is a safe gate through which debugger commands are invoked. 90 It sets a fault handler before invoking the command, so that an invalid 91 memory access will not result in another KDL session on top of this one 92 (and "cont" not to work anymore). We use setjmp() + longjmp() to "unwind" 93 the stack after catching a fault. 94 */ 95 int 96 invoke_debugger_command(struct debugger_command *command, int argc, char** argv) 97 { 98 // intercept invocations with "--help" and directly print the usage text 99 // If we know the command's usage text, intercept "--help" invocations 100 // and print it directly. 101 if (argc == 2 && strcmp(argv[1], "--help") == 0 && command->usage != NULL) { 102 kprintf("usage: %s ", command->name); 103 kputs(command->usage); 104 return 0; 105 } 106 107 struct thread* thread = thread_get_current_thread(); 108 addr_t oldFaultHandler = thread->fault_handler; 109 110 // replace argv[0] with the actual command name 111 argv[0] = (char *)command->name; 112 113 // Invoking the command directly might be useful when debugging debugger 114 // commands. 115 if (sInvokeCommandDirectly) 116 return command->func(argc, argv); 117 118 sInCommand = true; 119 120 if (setjmp(sInvokeCommandEnv) == 0) { 121 int result; 122 thread->fault_handler = (addr_t)&&error; 123 // Fake goto to trick the compiler not to optimize the code at the label 124 // away. 125 if (!thread) 126 goto error; 127 128 result = command->func(argc, argv); 129 130 thread->fault_handler = oldFaultHandler; 131 sInCommand = false; 132 return result; 133 134 error: 135 longjmp(sInvokeCommandEnv, 1); 136 // jump into the else branch 137 } else { 138 kprintf("\n[*** READ/WRITE FAULT ***]\n"); 139 } 140 141 thread->fault_handler = oldFaultHandler; 142 sInCommand = false; 143 return 0; 144 } 145 146 147 debugger_command* 148 get_debugger_commands() 149 { 150 return sCommands; 151 } 152 153 154 void 155 sort_debugger_commands() 156 { 157 // bubble sort the commands 158 debugger_command* stopCommand = NULL; 159 while (stopCommand != sCommands) { 160 debugger_command** command = &sCommands; 161 while (true) { 162 debugger_command* nextCommand = (*command)->next; 163 if (nextCommand == stopCommand) { 164 stopCommand = *command; 165 break; 166 } 167 168 if (strcmp((*command)->name, nextCommand->name) > 0) { 169 debugger_command* tmpCommand = nextCommand->next; 170 (*command)->next = nextCommand->next; 171 nextCommand->next = *command; 172 *command = nextCommand; 173 } 174 175 command = &(*command)->next; 176 } 177 } 178 } 179 180 181 status_t 182 add_debugger_command_etc(const char* name, debugger_command_hook func, 183 const char* description, const char* usage, uint32 flags) 184 { 185 struct debugger_command *cmd; 186 187 cmd = (struct debugger_command*)malloc(sizeof(struct debugger_command)); 188 if (cmd == NULL) 189 return B_NO_MEMORY; 190 191 cmd->func = func; 192 cmd->name = name; 193 cmd->description = description; 194 cmd->usage = usage; 195 cmd->flags = flags; 196 197 InterruptsSpinLocker _(sSpinlock); 198 199 cmd->next = sCommands; 200 sCommands = cmd; 201 202 return B_OK; 203 } 204 205 206 status_t 207 add_debugger_command_alias(const char* newName, const char* oldName, 208 const char* description) 209 { 210 // get the old command 211 bool ambiguous; 212 debugger_command* command = find_debugger_command(oldName, false, 213 ambiguous); 214 if (command == NULL) 215 return B_NAME_NOT_FOUND; 216 217 // register new command 218 return add_debugger_command_etc(newName, command->func, 219 description != NULL ? description : command->description, 220 command->usage, command->flags); 221 } 222 223 224 bool 225 print_debugger_command_usage(const char* commandName) 226 { 227 // get the command 228 bool ambiguous; 229 debugger_command* command = find_debugger_command(commandName, true, 230 ambiguous); 231 if (command == NULL) 232 return false; 233 234 // directly print the usage text, if we know it, otherwise invoke the 235 // command with "--help" 236 if (command->usage != NULL) { 237 kprintf("usage: %s ", command->name); 238 kputs(command->usage); 239 } else { 240 char* args[3] = { NULL, "--help", NULL }; 241 invoke_debugger_command(command, 2, args); 242 } 243 244 return true; 245 } 246 247 248 // #pragma mark - public API 249 250 251 int 252 add_debugger_command(char *name, int (*func)(int, char **), char *desc) 253 { 254 return add_debugger_command_etc(name, func, desc, NULL, 0); 255 } 256 257 258 int 259 remove_debugger_command(char * name, int (*func)(int, char **)) 260 { 261 struct debugger_command *cmd = sCommands; 262 struct debugger_command *prev = NULL; 263 cpu_status state; 264 265 state = disable_interrupts(); 266 acquire_spinlock(&sSpinlock); 267 268 while (cmd) { 269 if (!strcmp(cmd->name, name) && cmd->func == func) 270 break; 271 272 prev = cmd; 273 cmd = cmd->next; 274 } 275 276 if (cmd) { 277 if (cmd == sCommands) 278 sCommands = cmd->next; 279 else 280 prev->next = cmd->next; 281 } 282 283 release_spinlock(&sSpinlock); 284 restore_interrupts(state); 285 286 if (cmd) { 287 free(cmd); 288 return B_NO_ERROR; 289 } 290 291 return B_NAME_NOT_FOUND; 292 } 293 294