/* * Mach Operating System * Copyright (c) 1991,1990 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. */ /* * Author: David B. Golub, Carnegie Mellon University * Date: 7/90 */ #if MACH_KDB /* * Miscellaneous printing. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern unsigned long db_maxoff; /* ARGSUSED */ void db_show_regs( db_expr_t addr, boolean_t have_addr, db_expr_t count, char *modif) { struct db_variable *regp; db_expr_t value; db_addr_t offset; char * name; int i; struct db_var_aux_param aux_param; task_t task = TASK_NULL; aux_param.modif = modif; aux_param.thread = THREAD_NULL; if (db_option(modif, 't')) { if (have_addr) { if (!db_check_thread_address_valid((thread_t)addr)) return; aux_param.thread = (thread_t)addr; } else aux_param.thread = db_default_thread; if (aux_param.thread != THREAD_NULL) task = aux_param.thread->task; } for (regp = db_regs; regp < db_eregs; regp++) { if (regp->max_level > 1) { db_printf("bad multi-suffixed register %s\n", regp->name); continue; } aux_param.level = regp->max_level; for (i = regp->low; i <= regp->high; i++) { aux_param.suffix[0] = i; db_read_write_variable(regp, &value, DB_VAR_GET, &aux_param); if (regp->max_level > 0) db_printf("%s%d%*s", regp->name, i, 12-strlen(regp->name)-((i<10)?1:2), ""); else db_printf("%-12s", regp->name); db_printf("%#*N", 2+2*sizeof(vm_offset_t), value); db_find_xtrn_task_sym_and_offset((db_addr_t)value, &name, &offset, task); if (name != 0 && offset <= db_maxoff && offset != value) { db_printf("\t%s", name); if (offset != 0) db_printf("+%#r", offset); } db_printf("\n"); } } } #define OPTION_LONG 0x001 /* long print option */ #define OPTION_USER 0x002 /* print ps-like stuff */ #define OPTION_SCHED 0x004 /* print scheduling info */ #define OPTION_INDENT 0x100 /* print with indent */ #define OPTION_THREAD_TITLE 0x200 /* print thread title */ #define OPTION_TASK_TITLE 0x400 /* print thread title */ #ifndef DB_TASK_NAME #define DB_TASK_NAME(task) /* no task name */ #define DB_TASK_NAME_TITLE "" /* no task name */ #endif /* DB_TASK_NAME */ #ifndef db_thread_fp_used #define db_thread_fp_used(thread) FALSE #endif char * db_thread_stat(thread, status) const thread_t thread; char *status; { char *p = status; *p++ = (thread->state & TH_RUN) ? 'R' : '.'; *p++ = (thread->state & TH_WAIT) ? 'W' : '.'; *p++ = (thread->state & TH_SUSP) ? 'S' : '.'; *p++ = (thread->state & TH_SWAPPED) ? 'O' : '.'; *p++ = (thread->state & TH_UNINT) ? 'N' : '.'; /* show if the FPU has been used */ *p++ = db_thread_fp_used(thread) ? 'F' : '.'; *p++ = 0; return(status); } void db_print_thread( thread_t thread, int thread_id, int flag) { if (flag & OPTION_USER) { char status[8]; char *indent = ""; if (flag & OPTION_INDENT) indent = " "; if (flag & OPTION_LONG) { if (flag & OPTION_THREAD_TITLE) { db_printf("%s ID: THREAD STAT STACK PCB", indent); db_printf(" SUS PRI CONTINUE,WAIT_FUNC\n"); } db_printf("%s%3d%c %0*X %s %0*X %0*X %3d %3d ", indent, thread_id, (thread == current_thread())? '#': ':', 2*sizeof(vm_offset_t), thread, db_thread_stat(thread, status), 2*sizeof(vm_offset_t), thread->kernel_stack, 2*sizeof(vm_offset_t), thread->pcb, thread->suspend_count, thread->sched_pri); if ((thread->state & TH_SWAPPED) && thread->swap_func) { db_task_printsym((db_addr_t)thread->swap_func, DB_STGY_ANY, kernel_task); db_printf(", "); } if (thread->state & TH_WAIT) db_task_printsym((db_addr_t)thread->wait_event, DB_STGY_ANY, kernel_task); db_printf("\n"); } else if (flag & OPTION_SCHED) { if (flag & OPTION_THREAD_TITLE) { db_printf("%s " "STAT PRIORITY POLICY USAGE LAST\n", indent); db_printf("%s ID: " "RWSONF SET MAX COMP DEPR P DATA CPU SCHED UPDATED\n", indent); db_printf(" \n"); } db_printf("%s%3d%c %s %4d %4d %4d %4d %c %4d %10d %10d %10d\n", indent, thread_id, (thread == current_thread())? '#': ':', db_thread_stat(thread, status), thread->priority, thread->max_priority, thread->sched_pri, thread->depress_priority, #if MACH_FIXPRI thread->policy == POLICY_TIMESHARE ? 'T' : 'F', thread->sched_data, #else /* MACH_FIXPRI */ 'T', 0, #endif /* MACH_FIXPRI */ thread->cpu_usage, thread->sched_usage, thread->sched_stamp); } else { if (thread_id % 3 == 0) { if (flag & OPTION_INDENT) db_printf("\n "); } else db_printf(" "); db_printf("%3d%c(%0*X,%s)", thread_id, (thread == current_thread())? '#': ':', 2*sizeof(vm_offset_t), thread, db_thread_stat(thread, status)); } } else { if (flag & OPTION_INDENT) db_printf(" %3d (%0*X) ", thread_id, 2*sizeof(vm_offset_t), thread); else db_printf("(%0*X) ", 2*sizeof(vm_offset_t), thread); char status[8]; db_printf("%s", db_thread_stat(thread, status)); if (thread->state & TH_SWAPPED) { if (thread->swap_func) { db_printf("("); db_task_printsym((db_addr_t)thread->swap_func, DB_STGY_ANY, kernel_task); db_printf(")"); } else { db_printf("(swapped)"); } } if (thread->state & TH_WAIT) { db_printf(" "); db_task_printsym((db_addr_t)thread->wait_event, DB_STGY_ANY, kernel_task); } db_printf("\n"); } } void db_print_task( task_t task, int task_id, int flag) { thread_t thread; int thread_id; if (flag & OPTION_USER) { if (flag & OPTION_TASK_TITLE) { db_printf(" ID: TASK MAP THD SUS PR %s", DB_TASK_NAME_TITLE); if ((flag & (OPTION_LONG|OPTION_SCHED)) == 0) db_printf(" THREADS"); db_printf("\n"); } db_printf("%3d: %0*X %0*X %3d %3d %2d ", task_id, 2*sizeof(vm_offset_t), task, 2*sizeof(vm_offset_t), task->map, task->thread_count, task->suspend_count, task->priority); DB_TASK_NAME(task); if (flag & (OPTION_LONG|OPTION_SCHED)) { if (flag & OPTION_TASK_TITLE) flag |= OPTION_THREAD_TITLE; db_printf("\n"); } else if (task->thread_count <= 1) flag &= ~OPTION_INDENT; thread_id = 0; queue_iterate(&task->thread_list, thread, thread_t, thread_list) { db_print_thread(thread, thread_id, flag); flag &= ~OPTION_THREAD_TITLE; thread_id++; } if ((flag & (OPTION_LONG|OPTION_SCHED)) == 0) db_printf("\n"); } else { if (flag & OPTION_TASK_TITLE) db_printf(" TASK THREADS\n"); if (task->name[0]) db_printf("%3d %s (%0*X): ", task_id, task->name, 2*sizeof(vm_offset_t), task); else db_printf("%3d (%0*X): ", task_id, 2*sizeof(vm_offset_t), task); if (task->thread_count == 0) { db_printf("no threads\n"); } else { if (task->thread_count > 1) { db_printf("%d threads: \n", task->thread_count); flag |= OPTION_INDENT; } else flag &= ~OPTION_INDENT; thread_id = 0; queue_iterate(&task->thread_list, thread, thread_t, thread_list) db_print_thread(thread, thread_id++, flag); } } } void db_show_all_tasks(db_expr_t addr, boolean_t have_addr, db_expr_t count, const char *modif) { task_t task; int task_id = 0; processor_set_t pset; db_printf(" ID %-*s NAME [THREADS]\n", 2*sizeof(vm_offset_t), "TASK"); queue_iterate(&all_psets, pset, processor_set_t, all_psets) queue_iterate(&pset->tasks, task, task_t, pset_tasks) { db_printf("%3d %0*X %s [%d]\n", task_id, 2*sizeof(vm_offset_t), task, task->name, task->thread_count); task_id++; } } /*ARGSUSED*/ void db_show_all_threads(addr, have_addr, count, modif) db_expr_t addr; boolean_t have_addr; db_expr_t count; const char * modif; { task_t task; int task_id; int flag; processor_set_t pset; flag = OPTION_TASK_TITLE|OPTION_INDENT; if (db_option(modif, 'u')) flag |= OPTION_USER; if (db_option(modif, 'l')) flag |= OPTION_LONG; if (db_option(modif, 's')) flag |= OPTION_SCHED; task_id = 0; queue_iterate(&all_psets, pset, processor_set_t, all_psets) { queue_iterate(&pset->tasks, task, task_t, pset_tasks) { db_print_task(task, task_id, flag); flag &= ~OPTION_TASK_TITLE; task_id++; } } } db_addr_t db_task_from_space( ipc_space_t space, int *task_id) { task_t task; int tid = 0; processor_set_t pset; queue_iterate(&all_psets, pset, processor_set_t, all_psets) { queue_iterate(&pset->tasks, task, task_t, pset_tasks) { if (task->itk_space == space) { *task_id = tid; return (db_addr_t)task; } tid++; } } *task_id = 0; return (0); } /*ARGSUSED*/ void db_show_one_thread(addr, have_addr, count, modif) db_expr_t addr; boolean_t have_addr; db_expr_t count; const char * modif; { int flag; int thread_id; thread_t thread; flag = OPTION_THREAD_TITLE; if (db_option(modif, 'u')) flag |= OPTION_USER; if (db_option(modif, 'l')) flag |= OPTION_LONG; if (db_option(modif, 's')) flag |= OPTION_SCHED; if (!have_addr) { thread = current_thread(); if (thread == THREAD_NULL) { db_error("No thread\n"); /*NOTREACHED*/ } } else thread = (thread_t) addr; if ((thread_id = db_lookup_thread(thread)) < 0) { db_printf("bad thread address %#X\n", addr); db_error(0); /*NOTREACHED*/ } if (flag & OPTION_USER) { db_printf("TASK%d(%0*X):\n", db_lookup_task(thread->task), 2*sizeof(vm_offset_t), thread->task); db_print_thread(thread, thread_id, flag); } else { db_printf("task %d(%0*X): thread %d", db_lookup_task(thread->task), 2*sizeof(vm_offset_t), thread->task, thread_id); db_print_thread(thread, thread_id, flag); } } /*ARGSUSED*/ void db_show_one_task(addr, have_addr, count, modif) db_expr_t addr; boolean_t have_addr; db_expr_t count; const char * modif; { int flag; int task_id; task_t task; flag = OPTION_TASK_TITLE; if (db_option(modif, 'u')) flag |= OPTION_USER; if (db_option(modif, 'l')) flag |= OPTION_LONG; if (!have_addr) { task = db_current_task(); if (task == TASK_NULL) { db_error("No task\n"); /*NOTREACHED*/ } } else task = (task_t) addr; if ((task_id = db_lookup_task(task)) < 0) { db_printf("bad task address %#X\n", addr); db_error(0); /*NOTREACHED*/ } db_print_task(task, task_id, flag); } int db_port_iterate(thread, func) const thread_t thread; void (*func)(); { ipc_entry_t entry; int n = 0; struct rdxtree_iter iter; rdxtree_for_each(&thread->task->itk_space->is_map, &iter, entry) { if (entry->ie_bits & MACH_PORT_TYPE_PORT_RIGHTS) (*func)(entry->ie_name, (ipc_port_t) entry->ie_object, entry->ie_bits, n++); } return(n); } ipc_port_t db_lookup_port( thread_t thread, int id) { ipc_entry_t entry; if (thread == THREAD_NULL) return(0); if (id < 0) return(0); entry = ipc_entry_lookup(thread->task->itk_space, (mach_port_t) id); if (entry && entry->ie_bits & MACH_PORT_TYPE_PORT_RIGHTS) return((ipc_port_t)entry->ie_object); return(0); } static void db_print_port_id(id, port, bits, n) int id; const ipc_port_t port; unsigned bits; int n; { if (n != 0 && n % 3 == 0) db_printf("\n"); db_printf("\tport%d(%s,%x)", id, (bits & MACH_PORT_TYPE_RECEIVE)? "r": (bits & MACH_PORT_TYPE_SEND)? "s": "S", port); } static void db_print_port_id_long( int id, const ipc_port_t port, unsigned bits, int n) { if (n != 0) db_printf("\n"); db_printf("\tport%d(%s, port=0x%x", id, (bits & MACH_PORT_TYPE_RECEIVE)? "r": (bits & MACH_PORT_TYPE_SEND)? "s": "S", port); db_printf(", receiver_name=0x%x)", port->ip_receiver_name); } /* ARGSUSED */ void db_show_port_id(addr, have_addr, count, modif) db_expr_t addr; boolean_t have_addr; db_expr_t count; const char * modif; { thread_t thread; if (!have_addr) { thread = current_thread(); if (thread == THREAD_NULL) { db_error("No thread\n"); /*NOTREACHED*/ } } else thread = (thread_t) addr; if (db_lookup_thread(thread) < 0) { db_printf("Bad thread address %#X\n", addr); db_error(0); /*NOTREACHED*/ } if (db_option(modif, 'l')) { if (db_port_iterate(thread, db_print_port_id_long)) db_printf("\n"); return; } if (db_port_iterate(thread, db_print_port_id)) db_printf("\n"); } #endif /* MACH_KDB */