| File: | obj-scan-build/../i386/i386/db_trace.c |
| Location: | line 166, column 10 |
| Description: | Access to field 'pcb' results in a dereference of a null pointer (loaded from variable 'thread') |
| 1 | /* | |||
| 2 | * Mach Operating System | |||
| 3 | * Copyright (c) 1993,1992,1991,1990 Carnegie Mellon University | |||
| 4 | * All Rights Reserved. | |||
| 5 | * | |||
| 6 | * Permission to use, copy, modify and distribute this software and its | |||
| 7 | * documentation is hereby granted, provided that both the copyright | |||
| 8 | * notice and this permission notice appear in all copies of the | |||
| 9 | * software, derivative works or modified versions, and any portions | |||
| 10 | * thereof, and that both notices appear in supporting documentation. | |||
| 11 | * | |||
| 12 | * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" | |||
| 13 | * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR | |||
| 14 | * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. | |||
| 15 | * | |||
| 16 | * Carnegie Mellon requests users of this software to return to | |||
| 17 | * | |||
| 18 | * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU | |||
| 19 | * School of Computer Science | |||
| 20 | * Carnegie Mellon University | |||
| 21 | * Pittsburgh PA 15213-3890 | |||
| 22 | * | |||
| 23 | * any improvements or extensions that they make and grant Carnegie Mellon | |||
| 24 | * the rights to redistribute these changes. | |||
| 25 | */ | |||
| 26 | ||||
| 27 | #if MACH_KDB1 | |||
| 28 | ||||
| 29 | #include <string.h> | |||
| 30 | ||||
| 31 | #include <mach/boolean.h> | |||
| 32 | #include <vm/vm_map.h> | |||
| 33 | #include <kern/thread.h> | |||
| 34 | #include <kern/task.h> | |||
| 35 | ||||
| 36 | #include <machine/db_machdep.h> | |||
| 37 | #include <machine/machspl.h> | |||
| 38 | #include <machine/db_interface.h> | |||
| 39 | #include <machine/db_trace.h> | |||
| 40 | ||||
| 41 | #include <ddb/db_access.h> | |||
| 42 | #include <ddb/db_command.h> | |||
| 43 | #include <ddb/db_output.h> | |||
| 44 | #include <ddb/db_sym.h> | |||
| 45 | #include <ddb/db_variables.h> | |||
| 46 | #include <ddb/db_task_thread.h> | |||
| 47 | ||||
| 48 | #include "trap.h" | |||
| 49 | ||||
| 50 | /* | |||
| 51 | * Machine register set. | |||
| 52 | */ | |||
| 53 | struct db_variable db_regs[] = { | |||
| 54 | { "cs", (long *)&ddb_regs.cs, db_i386_reg_value }, | |||
| 55 | { "ds", (long *)&ddb_regs.ds, db_i386_reg_value }, | |||
| 56 | { "es", (long *)&ddb_regs.es, db_i386_reg_value }, | |||
| 57 | { "fs", (long *)&ddb_regs.fs, db_i386_reg_value }, | |||
| 58 | { "gs", (long *)&ddb_regs.gs, db_i386_reg_value }, | |||
| 59 | { "ss", (long *)&ddb_regs.ss, db_i386_reg_value }, | |||
| 60 | { "eax",(long *)&ddb_regs.eax, db_i386_reg_value }, | |||
| 61 | { "ecx",(long *)&ddb_regs.ecx, db_i386_reg_value }, | |||
| 62 | { "edx",(long *)&ddb_regs.edx, db_i386_reg_value }, | |||
| 63 | { "ebx",(long *)&ddb_regs.ebx, db_i386_reg_value }, | |||
| 64 | { "esp",(long *)&ddb_regs.uesp,db_i386_reg_value }, | |||
| 65 | { "ebp",(long *)&ddb_regs.ebp, db_i386_reg_value }, | |||
| 66 | { "esi",(long *)&ddb_regs.esi, db_i386_reg_value }, | |||
| 67 | { "edi",(long *)&ddb_regs.edi, db_i386_reg_value }, | |||
| 68 | { "eip",(long *)&ddb_regs.eip, db_i386_reg_value }, | |||
| 69 | { "efl",(long *)&ddb_regs.efl, db_i386_reg_value }, | |||
| 70 | }; | |||
| 71 | struct db_variable *db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]); | |||
| 72 | ||||
| 73 | /* | |||
| 74 | * Stack trace. | |||
| 75 | */ | |||
| 76 | #define INKERNEL(va)(((vm_offset_t)(va)) >= 0xC0000000UL) (((vm_offset_t)(va)) >= VM_MIN_KERNEL_ADDRESS0xC0000000UL) | |||
| 77 | ||||
| 78 | struct i386_frame { | |||
| 79 | struct i386_frame *f_frame; | |||
| 80 | long f_retaddr; | |||
| 81 | long f_arg0; | |||
| 82 | }; | |||
| 83 | ||||
| 84 | #define TRAP1 1 | |||
| 85 | #define INTERRUPT2 2 | |||
| 86 | #define SYSCALL3 3 | |||
| 87 | ||||
| 88 | db_addr_t db_user_trap_symbol_value = 0; | |||
| 89 | db_addr_t db_kernel_trap_symbol_value = 0; | |||
| 90 | db_addr_t db_interrupt_symbol_value = 0; | |||
| 91 | db_addr_t db_return_to_iret_symbol_value = 0; | |||
| 92 | db_addr_t db_syscall_symbol_value = 0; | |||
| 93 | boolean_t db_trace_symbols_found = FALSE((boolean_t) 0); | |||
| 94 | ||||
| 95 | struct i386_kregs { | |||
| 96 | char *name; | |||
| 97 | long offset; | |||
| 98 | } i386_kregs[] = { | |||
| 99 | { "ebx", (long)(&((struct i386_kernel_state *)0)->k_ebx) }, | |||
| 100 | { "esp", (long)(&((struct i386_kernel_state *)0)->k_esp) }, | |||
| 101 | { "ebp", (long)(&((struct i386_kernel_state *)0)->k_ebp) }, | |||
| 102 | { "edi", (long)(&((struct i386_kernel_state *)0)->k_edi) }, | |||
| 103 | { "esi", (long)(&((struct i386_kernel_state *)0)->k_esi) }, | |||
| 104 | { "eip", (long)(&((struct i386_kernel_state *)0)->k_eip) }, | |||
| 105 | { 0 }, | |||
| 106 | }; | |||
| 107 | ||||
| 108 | long * | |||
| 109 | db_lookup_i386_kreg( | |||
| 110 | const char *name, | |||
| 111 | const long *kregp) | |||
| 112 | { | |||
| 113 | struct i386_kregs *kp; | |||
| 114 | ||||
| 115 | for (kp = i386_kregs; kp->name; kp++) { | |||
| 116 | if (strcmp(name, kp->name) == 0) | |||
| 117 | return (long *)((long)kregp + kp->offset); | |||
| 118 | } | |||
| 119 | return 0; | |||
| 120 | } | |||
| 121 | ||||
| 122 | void | |||
| 123 | db_i386_reg_value( | |||
| 124 | struct db_variable *vp, | |||
| 125 | db_expr_t *valuep, | |||
| 126 | int flag, | |||
| 127 | db_var_aux_param_t ap) | |||
| 128 | { | |||
| 129 | long *dp = 0; | |||
| 130 | db_expr_t null_reg = 0; | |||
| 131 | thread_t thread = ap->thread; | |||
| ||||
| 132 | extern unsigned int_stack_high; | |||
| 133 | ||||
| 134 | if (db_option(ap->modif, 'u')) { | |||
| 135 | if (thread == THREAD_NULL((thread_t) 0)) { | |||
| 136 | if ((thread = current_thread()(active_threads[(0)])) == THREAD_NULL((thread_t) 0)) | |||
| 137 | db_error("no user registers\n"); | |||
| 138 | } | |||
| 139 | if (thread == current_thread()(active_threads[(0)])) { | |||
| 140 | if (ddb_regs.cs & 0x3) | |||
| 141 | dp = vp->valuep; | |||
| 142 | else if (ddb_regs.ebp < int_stack_high) | |||
| 143 | db_error("cannot get/set user registers in nested interrupt\n"); | |||
| 144 | } | |||
| 145 | } else { | |||
| 146 | if (thread == THREAD_NULL((thread_t) 0) || thread == current_thread()(active_threads[(0)])) { | |||
| 147 | dp = vp->valuep; | |||
| 148 | } else if ((thread->state & TH_SWAPPED0x0100) == 0 && | |||
| 149 | thread->kernel_stack) { | |||
| 150 | dp = db_lookup_i386_kreg(vp->name, | |||
| 151 | (long *)(STACK_IKS(thread->kernel_stack)((struct i386_kernel_state *)((thread->kernel_stack) + (1* 4096)) - 1))); | |||
| 152 | if (dp == 0) | |||
| 153 | dp = &null_reg; | |||
| 154 | } else if ((thread->state & TH_SWAPPED0x0100) && | |||
| 155 | thread->swap_func != thread_exception_return) { | |||
| 156 | /*.....this breaks t/t $taskN.0...*/ | |||
| 157 | /* only EIP is valid */ | |||
| 158 | if (vp->valuep == (long *) &ddb_regs.eip) { | |||
| 159 | dp = (long *)(&thread->swap_func); | |||
| 160 | } else { | |||
| 161 | dp = &null_reg; | |||
| 162 | } | |||
| 163 | } | |||
| 164 | } | |||
| 165 | if (dp == 0) { | |||
| 166 | if (thread->pcb == 0) | |||
| ||||
| 167 | db_error("no pcb\n"); | |||
| 168 | dp = (long *)((long)(&thread->pcb->iss) + | |||
| 169 | ((long)vp->valuep - (long)&ddb_regs)); | |||
| 170 | } | |||
| 171 | if (flag == DB_VAR_SET1) | |||
| 172 | *dp = *valuep; | |||
| 173 | else | |||
| 174 | *valuep = *dp; | |||
| 175 | } | |||
| 176 | ||||
| 177 | void | |||
| 178 | db_find_trace_symbols(void) | |||
| 179 | { | |||
| 180 | db_expr_t value; | |||
| 181 | #ifdef __ELF__1 | |||
| 182 | #define P | |||
| 183 | #else | |||
| 184 | #define P "_" | |||
| 185 | #endif | |||
| 186 | if (db_value_of_name(P"user_trap", &value)) | |||
| 187 | db_user_trap_symbol_value = (db_addr_t) value; | |||
| 188 | if (db_value_of_name(P"kernel_trap", &value)) | |||
| 189 | db_kernel_trap_symbol_value = (db_addr_t) value; | |||
| 190 | if (db_value_of_name(P"interrupt", &value)) | |||
| 191 | db_interrupt_symbol_value = (db_addr_t) value; | |||
| 192 | if (db_value_of_name(P"return_to_iret", &value)) | |||
| 193 | db_return_to_iret_symbol_value = (db_addr_t) value; | |||
| 194 | if (db_value_of_name(P"syscall", &value)) | |||
| 195 | db_syscall_symbol_value = (db_addr_t) value; | |||
| 196 | #undef P | |||
| 197 | db_trace_symbols_found = TRUE((boolean_t) 1); | |||
| 198 | } | |||
| 199 | ||||
| 200 | /* | |||
| 201 | * Figure out how many arguments were passed into the frame at "fp". | |||
| 202 | */ | |||
| 203 | const int db_numargs_default = 5; | |||
| 204 | ||||
| 205 | int | |||
| 206 | db_numargs( | |||
| 207 | struct i386_frame *fp, | |||
| 208 | task_t task) | |||
| 209 | { | |||
| 210 | long *argp; | |||
| 211 | long inst; | |||
| 212 | long args; | |||
| 213 | extern char etext[]; | |||
| 214 | ||||
| 215 | argp = (long *)db_get_task_value((long)&fp->f_retaddr, sizeof(long), FALSE((boolean_t) 0), task); | |||
| 216 | if (argp < (long *)VM_MIN_KERNEL_ADDRESS0xC0000000UL || argp > (long *)etext) | |||
| 217 | args = db_numargs_default; | |||
| 218 | else if (!DB_CHECK_ACCESS((long)argp, sizeof(long), task)db_check_access((long)argp,sizeof(long),task)) | |||
| 219 | args = db_numargs_default; | |||
| 220 | else { | |||
| 221 | inst = db_get_task_value((long)argp, sizeof(long), FALSE((boolean_t) 0), task); | |||
| 222 | if ((inst & 0xff) == 0x59) /* popl %ecx */ | |||
| 223 | args = 1; | |||
| 224 | else if ((inst & 0xffff) == 0xc483) /* addl %n, %esp */ | |||
| 225 | args = ((inst >> 16) & 0xff) / 4; | |||
| 226 | else | |||
| 227 | args = db_numargs_default; | |||
| 228 | } | |||
| 229 | return args; | |||
| 230 | } | |||
| 231 | ||||
| 232 | struct interrupt_frame { | |||
| 233 | struct i386_frame *if_frame; /* point to next frame */ | |||
| 234 | long if_retaddr; /* return address to _interrupt */ | |||
| 235 | long if_unit; /* unit number */ | |||
| 236 | spl_t if_spl; /* saved spl */ | |||
| 237 | long if_iretaddr; /* _return_to_{iret,iret_i} */ | |||
| 238 | long if_edx; /* old sp(iret) or saved edx(iret_i) */ | |||
| 239 | long if_ecx; /* saved ecx(iret_i) */ | |||
| 240 | long if_eax; /* saved eax(iret_i) */ | |||
| 241 | long if_eip; /* saved eip(iret_i) */ | |||
| 242 | long if_cs; /* saved cs(iret_i) */ | |||
| 243 | long if_efl; /* saved efl(iret_i) */ | |||
| 244 | }; | |||
| 245 | ||||
| 246 | /* | |||
| 247 | * Figure out the next frame up in the call stack. | |||
| 248 | * For trap(), we print the address of the faulting instruction and | |||
| 249 | * proceed with the calling frame. We return the ip that faulted. | |||
| 250 | * If the trap was caused by jumping through a bogus pointer, then | |||
| 251 | * the next line in the backtrace will list some random function as | |||
| 252 | * being called. It should get the argument list correct, though. | |||
| 253 | * It might be possible to dig out from the next frame up the name | |||
| 254 | * of the function that faulted, but that could get hairy. | |||
| 255 | */ | |||
| 256 | void | |||
| 257 | db_nextframe( | |||
| 258 | struct i386_frame **lfp, /* in/out */ | |||
| 259 | struct i386_frame **fp, /* in/out */ | |||
| 260 | db_addr_t *ip, /* out */ | |||
| 261 | long frame_type, /* in */ | |||
| 262 | const thread_t thread) /* in */ | |||
| 263 | { | |||
| 264 | struct i386_saved_state *saved_regs; | |||
| 265 | struct interrupt_frame *ifp; | |||
| 266 | task_t task = (thread != THREAD_NULL((thread_t) 0))? thread->task: TASK_NULL((task_t) 0); | |||
| 267 | ||||
| 268 | switch(frame_type) { | |||
| 269 | case TRAP1: | |||
| 270 | /* | |||
| 271 | * We know that trap() has 1 argument and we know that | |||
| 272 | * it is an (struct i386_saved_state *). | |||
| 273 | */ | |||
| 274 | saved_regs = (struct i386_saved_state *) | |||
| 275 | db_get_task_value((long)&((*fp)->f_arg0),sizeof(long),FALSE((boolean_t) 0),task); | |||
| 276 | db_printf(">>>>> %s (%d) at ", | |||
| 277 | trap_name(saved_regs->trapno), saved_regs->trapno); | |||
| 278 | db_task_printsym(saved_regs->eip, DB_STGY_PROC2, task); | |||
| 279 | db_printf(" <<<<<\n"); | |||
| 280 | *fp = (struct i386_frame *)saved_regs->ebp; | |||
| 281 | *ip = (db_addr_t)saved_regs->eip; | |||
| 282 | break; | |||
| 283 | case INTERRUPT2: | |||
| 284 | if (*lfp == 0) { | |||
| 285 | db_printf(">>>>> interrupt <<<<<\n"); | |||
| 286 | goto miss_frame; | |||
| 287 | } | |||
| 288 | db_printf(">>>>> interrupt at "); | |||
| 289 | ifp = (struct interrupt_frame *)(*lfp); | |||
| 290 | *fp = ifp->if_frame; | |||
| 291 | if (ifp->if_iretaddr == db_return_to_iret_symbol_value) | |||
| 292 | *ip = ((struct i386_interrupt_state *) ifp->if_edx)->eip; | |||
| 293 | else | |||
| 294 | *ip = (db_addr_t) ifp->if_eip; | |||
| 295 | db_task_printsym(*ip, DB_STGY_PROC2, task); | |||
| 296 | db_printf(" <<<<<\n"); | |||
| 297 | break; | |||
| 298 | case SYSCALL3: | |||
| 299 | if (thread != THREAD_NULL((thread_t) 0) && thread->pcb) { | |||
| 300 | *ip = (db_addr_t) thread->pcb->iss.eip; | |||
| 301 | *fp = (struct i386_frame *) thread->pcb->iss.ebp; | |||
| 302 | break; | |||
| 303 | } | |||
| 304 | /* falling down for unknown case */ | |||
| 305 | default: | |||
| 306 | miss_frame: | |||
| 307 | *ip = (db_addr_t) | |||
| 308 | db_get_task_value((long)&(*fp)->f_retaddr, sizeof(long), FALSE((boolean_t) 0), task); | |||
| 309 | *lfp = *fp; | |||
| 310 | *fp = (struct i386_frame *) | |||
| 311 | db_get_task_value((long)&(*fp)->f_frame, sizeof(long), FALSE((boolean_t) 0), task); | |||
| 312 | break; | |||
| 313 | } | |||
| 314 | } | |||
| 315 | ||||
| 316 | #define F_USER_TRACE1 1 | |||
| 317 | #define F_TRACE_THREAD2 2 | |||
| 318 | ||||
| 319 | void | |||
| 320 | db_stack_trace_cmd( | |||
| 321 | db_expr_t addr, | |||
| 322 | boolean_t have_addr, | |||
| 323 | db_expr_t count, | |||
| 324 | const char *modif) | |||
| 325 | { | |||
| 326 | boolean_t trace_thread = FALSE((boolean_t) 0); | |||
| 327 | struct i386_frame *frame; | |||
| 328 | db_addr_t callpc; | |||
| 329 | int flags = 0; | |||
| 330 | thread_t th; | |||
| 331 | ||||
| 332 | { | |||
| 333 | const char *cp = modif; | |||
| 334 | char c; | |||
| 335 | ||||
| 336 | while ((c = *cp++) != 0) { | |||
| 337 | if (c == 't') | |||
| 338 | trace_thread = TRUE((boolean_t) 1); | |||
| 339 | if (c == 'u') | |||
| 340 | flags |= F_USER_TRACE1; | |||
| 341 | } | |||
| 342 | } | |||
| 343 | ||||
| 344 | if (!have_addr && !trace_thread) { | |||
| 345 | frame = (struct i386_frame *)ddb_regs.ebp; | |||
| 346 | callpc = (db_addr_t)ddb_regs.eip; | |||
| 347 | th = current_thread()(active_threads[(0)]); | |||
| 348 | } else if (trace_thread) { | |||
| 349 | if (have_addr) { | |||
| 350 | th = (thread_t) addr; | |||
| 351 | if (!db_check_thread_address_valid(th)) | |||
| 352 | return; | |||
| 353 | } else { | |||
| 354 | th = db_default_thread; | |||
| 355 | if (th == THREAD_NULL((thread_t) 0)) | |||
| 356 | th = current_thread()(active_threads[(0)]); | |||
| 357 | if (th == THREAD_NULL((thread_t) 0)) { | |||
| 358 | db_printf("no active thread\n"); | |||
| 359 | return; | |||
| 360 | } | |||
| 361 | } | |||
| 362 | if (th == current_thread()(active_threads[(0)])) { | |||
| 363 | frame = (struct i386_frame *)ddb_regs.ebp; | |||
| 364 | callpc = (db_addr_t)ddb_regs.eip; | |||
| 365 | } else { | |||
| 366 | if (th->pcb == 0) { | |||
| 367 | db_printf("thread has no pcb\n"); | |||
| 368 | return; | |||
| 369 | } | |||
| 370 | if ((th->state & TH_SWAPPED0x0100) || th->kernel_stack == 0) { | |||
| 371 | struct i386_saved_state *iss = &th->pcb->iss; | |||
| 372 | ||||
| 373 | db_printf("Continuation "); | |||
| 374 | db_task_printsym((db_addr_t)th->swap_func, | |||
| 375 | DB_STGY_PROC2, | |||
| 376 | th->task); | |||
| 377 | db_printf("\n"); | |||
| 378 | ||||
| 379 | frame = (struct i386_frame *) (iss->ebp); | |||
| 380 | callpc = (db_addr_t) (iss->eip); | |||
| 381 | } else { | |||
| 382 | struct i386_kernel_state *iks; | |||
| 383 | iks = STACK_IKS(th->kernel_stack)((struct i386_kernel_state *)((th->kernel_stack) + (1*4096 )) - 1); | |||
| 384 | frame = (struct i386_frame *) (iks->k_ebp); | |||
| 385 | callpc = (db_addr_t) (iks->k_eip); | |||
| 386 | } | |||
| 387 | } | |||
| 388 | } else { | |||
| 389 | frame = (struct i386_frame *)addr; | |||
| 390 | th = (db_default_thread)? db_default_thread: current_thread()(active_threads[(0)]); | |||
| 391 | callpc = (db_addr_t)db_get_task_value((long)&frame->f_retaddr, sizeof(long), | |||
| 392 | FALSE((boolean_t) 0), | |||
| 393 | (th == THREAD_NULL((thread_t) 0)) ? TASK_NULL((task_t) 0) : th->task); | |||
| 394 | } | |||
| 395 | ||||
| 396 | db_i386_stack_trace( th, frame, callpc, count, flags ); | |||
| 397 | } | |||
| 398 | ||||
| 399 | ||||
| 400 | void | |||
| 401 | db_i386_stack_trace( | |||
| 402 | const thread_t th, | |||
| 403 | struct i386_frame *frame, | |||
| 404 | db_addr_t callpc, | |||
| 405 | db_expr_t count, | |||
| 406 | int flags) | |||
| 407 | { | |||
| 408 | task_t task; | |||
| 409 | boolean_t kernel_only; | |||
| 410 | long *argp; | |||
| 411 | long user_frame = 0; | |||
| 412 | struct i386_frame *lastframe; | |||
| 413 | int frame_type; | |||
| 414 | char *filename; | |||
| 415 | int linenum; | |||
| 416 | extern unsigned long db_maxoff; | |||
| 417 | ||||
| 418 | if (count == -1) | |||
| 419 | count = 65535; | |||
| 420 | ||||
| 421 | kernel_only = (flags & F_USER_TRACE1) == 0; | |||
| 422 | ||||
| 423 | task = (th == THREAD_NULL((thread_t) 0)) ? TASK_NULL((task_t) 0) : th->task; | |||
| 424 | ||||
| 425 | if (!db_trace_symbols_found) | |||
| 426 | db_find_trace_symbols(); | |||
| 427 | ||||
| 428 | if (!INKERNEL(callpc)(((vm_offset_t)(callpc)) >= 0xC0000000UL) && !INKERNEL(frame)(((vm_offset_t)(frame)) >= 0xC0000000UL)) { | |||
| 429 | db_printf(">>>>> user space <<<<<\n"); | |||
| 430 | user_frame++; | |||
| 431 | } | |||
| 432 | ||||
| 433 | lastframe = 0; | |||
| 434 | while (count-- && frame != 0) { | |||
| 435 | int narg; | |||
| 436 | char * name; | |||
| 437 | db_expr_t offset; | |||
| 438 | ||||
| 439 | if (INKERNEL(callpc)(((vm_offset_t)(callpc)) >= 0xC0000000UL) && user_frame == 0) { | |||
| 440 | db_addr_t call_func = 0; | |||
| 441 | ||||
| 442 | db_sym_t sym_tmp; | |||
| 443 | db_symbol_values(0, | |||
| 444 | sym_tmp = db_search_task_symbol(callpc, | |||
| 445 | DB_STGY_XTRN1, | |||
| 446 | (db_addr_t *)&offset, | |||
| 447 | TASK_NULL((task_t) 0)), | |||
| 448 | &name, (db_expr_t *)&call_func); | |||
| 449 | db_free_symbol(sym_tmp); | |||
| 450 | if ((db_user_trap_symbol_value && call_func == db_user_trap_symbol_value) || | |||
| 451 | (db_kernel_trap_symbol_value && call_func == db_kernel_trap_symbol_value)) { | |||
| 452 | frame_type = TRAP1; | |||
| 453 | narg = 1; | |||
| 454 | } else if (db_interrupt_symbol_value && call_func == db_interrupt_symbol_value) { | |||
| 455 | frame_type = INTERRUPT2; | |||
| 456 | goto next_frame; | |||
| 457 | } else if (db_syscall_symbol_value && call_func == db_syscall_symbol_value) { | |||
| 458 | frame_type = SYSCALL3; | |||
| 459 | goto next_frame; | |||
| 460 | } else { | |||
| 461 | frame_type = 0; | |||
| 462 | narg = db_numargs(frame, task); | |||
| 463 | } | |||
| 464 | } else if (INKERNEL(callpc)(((vm_offset_t)(callpc)) >= 0xC0000000UL) ^ INKERNEL(frame)(((vm_offset_t)(frame)) >= 0xC0000000UL)) { | |||
| 465 | frame_type = 0; | |||
| 466 | narg = -1; | |||
| 467 | } else { | |||
| 468 | frame_type = 0; | |||
| 469 | narg = db_numargs(frame, task); | |||
| 470 | } | |||
| 471 | ||||
| 472 | db_find_task_sym_and_offset(callpc, &name,do { db_sym_t s; db_symbol_values(0, s = db_search_task_symbol (callpc,0 ,(db_addr_t *)&offset,task), &name, 0); db_free_symbol (s); } while(0); | |||
| 473 | (db_addr_t *)&offset, task)do { db_sym_t s; db_symbol_values(0, s = db_search_task_symbol (callpc,0 ,(db_addr_t *)&offset,task), &name, 0); db_free_symbol (s); } while(0);; | |||
| 474 | if (name == 0 || offset > db_maxoff) { | |||
| 475 | db_printf("0x%x(", callpc); | |||
| 476 | offset = 0; | |||
| 477 | } else | |||
| 478 | db_printf("%s(", name); | |||
| 479 | ||||
| 480 | argp = &frame->f_arg0; | |||
| 481 | while (narg > 0) { | |||
| 482 | db_printf("%x", db_get_task_value((long)argp,sizeof(long),FALSE((boolean_t) 0),task)); | |||
| 483 | argp++; | |||
| 484 | if (--narg != 0) | |||
| 485 | db_printf(","); | |||
| 486 | } | |||
| 487 | if (narg < 0) | |||
| 488 | db_printf("..."); | |||
| 489 | db_printf(")"); | |||
| 490 | if (offset) { | |||
| 491 | db_printf("+%x", offset); | |||
| 492 | } | |||
| 493 | if (db_line_at_pc(0, &filename, &linenum, callpc)) { | |||
| 494 | db_printf(" [%s", filename); | |||
| 495 | if (linenum > 0) | |||
| 496 | db_printf(":%d", linenum); | |||
| 497 | db_printf("]"); | |||
| 498 | } | |||
| 499 | db_printf("\n"); | |||
| 500 | ||||
| 501 | next_frame: | |||
| 502 | db_nextframe(&lastframe, &frame, &callpc, frame_type, th); | |||
| 503 | ||||
| 504 | if (frame == 0) { | |||
| 505 | /* end of chain */ | |||
| 506 | break; | |||
| 507 | } | |||
| 508 | if (!INKERNEL(lastframe)(((vm_offset_t)(lastframe)) >= 0xC0000000UL) || | |||
| 509 | (!INKERNEL(callpc)(((vm_offset_t)(callpc)) >= 0xC0000000UL) && !INKERNEL(frame)(((vm_offset_t)(frame)) >= 0xC0000000UL))) | |||
| 510 | user_frame++; | |||
| 511 | if (user_frame == 1) { | |||
| 512 | db_printf(">>>>> user space <<<<<\n"); | |||
| 513 | if (kernel_only) | |||
| 514 | break; | |||
| 515 | } | |||
| 516 | if (frame <= lastframe) { | |||
| 517 | if (INKERNEL(lastframe)(((vm_offset_t)(lastframe)) >= 0xC0000000UL) && !INKERNEL(frame)(((vm_offset_t)(frame)) >= 0xC0000000UL)) | |||
| 518 | continue; | |||
| 519 | db_printf("Bad frame pointer: 0x%x\n", frame); | |||
| 520 | break; | |||
| 521 | } | |||
| 522 | } | |||
| 523 | } | |||
| 524 | ||||
| 525 | #define CTHREADS_SUPPORT1 1 | |||
| 526 | ||||
| 527 | #if CTHREADS_SUPPORT1 | |||
| 528 | ||||
| 529 | thread_t | |||
| 530 | db_find_kthread( | |||
| 531 | vm_offset_t ustack_base, | |||
| 532 | vm_size_t ustack_top, | |||
| 533 | task_t task) | |||
| 534 | { | |||
| 535 | thread_t thread; | |||
| 536 | if (task == TASK_NULL((task_t) 0)) | |||
| 537 | task = db_current_task()(((active_threads[(0)]))? (active_threads[(0)])->task: ((task_t ) 0)); | |||
| 538 | ||||
| 539 | queue_iterate(&task->thread_list, thread, thread_t, thread_list)for ((thread) = (thread_t) ((&task->thread_list)->next ); !(((&task->thread_list)) == ((queue_entry_t)(thread ))); (thread) = (thread_t) ((&(thread)->thread_list)-> next)) { | |||
| 540 | vm_offset_t usp = thread->pcb->iss.uesp/*ebp works*/; | |||
| 541 | if (usp >= ustack_base && usp < ustack_top) | |||
| 542 | return thread; | |||
| 543 | } | |||
| 544 | return THREAD_NULL((thread_t) 0); | |||
| 545 | } | |||
| 546 | ||||
| 547 | static void db_cproc_state( | |||
| 548 | int state, | |||
| 549 | char s[4]) | |||
| 550 | { | |||
| 551 | if (state == 0) { | |||
| 552 | *s++ = 'R'; | |||
| 553 | } else { | |||
| 554 | if (state & 1) *s++ = 'S'; | |||
| 555 | if (state & 2) *s++ = 'B'; | |||
| 556 | if (state & 4) *s++ = 'C'; | |||
| 557 | } | |||
| 558 | *s = 0; | |||
| 559 | } | |||
| 560 | ||||
| 561 | /* offsets in a cproc structure */ | |||
| 562 | /* TODO: longs? */ | |||
| 563 | const int db_cproc_next_offset = 0 * 4; | |||
| 564 | const int db_cproc_incarnation_offset = 1 * 4; | |||
| 565 | const int db_cproc_list_offset = 2 * 4; | |||
| 566 | const int db_cproc_wait_offset = 3 * 4; | |||
| 567 | const int db_cproc_context_offset = 5 * 4; | |||
| 568 | const int db_cproc_state_offset = 7 * 4; | |||
| 569 | const int db_cproc_stack_base_offset = 10 * 4 + sizeof(mach_msg_header_t); | |||
| 570 | const int db_cproc_stack_size_offset = 11 * 4 + sizeof(mach_msg_header_t); | |||
| 571 | ||||
| 572 | /* offsets in a cproc_switch context structure */ | |||
| 573 | const int db_cprocsw_framep_offset = 3 * 4; | |||
| 574 | const int db_cprocsw_pc_offset = 4 * 4; | |||
| 575 | ||||
| 576 | #include <machine/setjmp.h> | |||
| 577 | ||||
| 578 | extern jmp_buf_t *db_recover; | |||
| 579 | ||||
| 580 | void db_trace_cproc( | |||
| 581 | vm_offset_t cproc, | |||
| 582 | thread_t thread) | |||
| 583 | { | |||
| 584 | jmp_buf_t db_jmpbuf; | |||
| 585 | jmp_buf_t *prev = db_recover; | |||
| 586 | task_t task; | |||
| 587 | db_addr_t pc, fp; | |||
| 588 | ||||
| 589 | task = (thread == THREAD_NULL((thread_t) 0))? TASK_NULL((task_t) 0): thread->task; | |||
| 590 | ||||
| 591 | if (!_setjmp(db_recover = &db_jmpbuf)) { | |||
| 592 | char pstate[4]; | |||
| 593 | unsigned int s, w, n, c, cth; | |||
| 594 | ||||
| 595 | s = db_get_task_value(cproc + db_cproc_state_offset, 4, FALSE((boolean_t) 0), task); | |||
| 596 | w = db_get_task_value(cproc + db_cproc_wait_offset, 4, FALSE((boolean_t) 0), task); | |||
| 597 | n = db_get_task_value(cproc + db_cproc_next_offset, 4, FALSE((boolean_t) 0), task); | |||
| 598 | c = db_get_task_value(cproc + db_cproc_context_offset, 4, FALSE((boolean_t) 0), task); | |||
| 599 | cth = db_get_task_value(cproc + db_cproc_incarnation_offset, 4, FALSE((boolean_t) 0), task); | |||
| 600 | ||||
| 601 | db_cproc_state(s, pstate); | |||
| 602 | ||||
| 603 | db_printf("CThread %x (cproc %x) %s", cth, cproc, pstate); | |||
| 604 | if (w) db_printf(" awaits %x", w); | |||
| 605 | if (n) db_printf(" next %x", n); | |||
| 606 | db_printf("\n"); | |||
| 607 | ||||
| 608 | if ((s != 0) && (c != 0)) { | |||
| 609 | pc = db_get_task_value(c + db_cprocsw_pc_offset, 4, FALSE((boolean_t) 0), task); | |||
| 610 | fp = c + db_cprocsw_framep_offset; | |||
| 611 | } else { | |||
| 612 | db_addr_t sb; | |||
| 613 | vm_size_t ss; | |||
| 614 | ||||
| 615 | sb = db_get_task_value(cproc + db_cproc_stack_base_offset, sizeof(db_expr_t), FALSE((boolean_t) 0), task); | |||
| 616 | ss = db_get_task_value(cproc + db_cproc_stack_size_offset, sizeof(db_expr_t), FALSE((boolean_t) 0), task); | |||
| 617 | db_printf(" Stack base: %x\n", sb); | |||
| 618 | /* | |||
| 619 | * Lessee now.. | |||
| 620 | */ | |||
| 621 | thread = db_find_kthread(sb, sb+ss, task); | |||
| 622 | if (thread != THREAD_NULL((thread_t) 0)) { | |||
| 623 | pc = thread->pcb->iss.eip; | |||
| 624 | fp = thread->pcb->iss.ebp; | |||
| 625 | } else | |||
| 626 | fp = -1; | |||
| 627 | } | |||
| 628 | ||||
| 629 | if (fp != -1) | |||
| 630 | db_i386_stack_trace(thread, (struct i386_frame*)fp, pc, | |||
| 631 | -1, F_USER_TRACE1); | |||
| 632 | } | |||
| 633 | ||||
| 634 | db_recover = prev; | |||
| 635 | } | |||
| 636 | ||||
| 637 | void db_all_cprocs( | |||
| 638 | const task_t task, | |||
| 639 | db_expr_t cproc_list) | |||
| 640 | { | |||
| 641 | jmp_buf_t db_jmpbuf; | |||
| 642 | jmp_buf_t *prev = db_recover; | |||
| 643 | thread_t thread; | |||
| 644 | db_expr_t cproc, next; | |||
| 645 | ||||
| 646 | ||||
| 647 | if (task != TASK_NULL((task_t) 0)) { | |||
| 648 | thread = (thread_t) queue_first(&task->thread_list)((&task->thread_list)->next); | |||
| 649 | } else | |||
| 650 | thread = current_thread()(active_threads[(0)]); | |||
| 651 | ||||
| 652 | if (cproc_list != 0) | |||
| 653 | next = cproc_list; | |||
| 654 | else | |||
| 655 | if (!db_value_of_name("unix::cproc_list", &next)) { | |||
| 656 | db_printf("No cprocs.\n"); | |||
| 657 | return; | |||
| 658 | } | |||
| 659 | ||||
| 660 | ||||
| 661 | while (next) { | |||
| 662 | if (_setjmp(db_recover = &db_jmpbuf)) | |||
| 663 | break; | |||
| 664 | ||||
| 665 | cproc = db_get_task_value(next, 4, FALSE((boolean_t) 0), TASK_NULL((task_t) 0)); | |||
| 666 | if (cproc == 0) break; | |||
| 667 | next = cproc + db_cproc_list_offset; | |||
| 668 | ||||
| 669 | db_trace_cproc(cproc, thread); | |||
| 670 | } | |||
| 671 | ||||
| 672 | db_recover = prev; | |||
| 673 | } | |||
| 674 | ||||
| 675 | #endif /* CTHREADS_SUPPORT */ | |||
| 676 | ||||
| 677 | #endif /* MACH_KDB */ |