crash/task.c
Dave Anderson 89ed9d0a7f Introduction of support for "live" ramdump files, such as those that
are specified by the QEMU mem-path argument of a memory-backend-file
object.  This allows the running of a live crash session against a
QEMU guest from the host machine.  In this example, the /tmp/MEM file
on a QEMU host represents the guest's physical memory:

  $ qemu-kvm ...other-options... \
  -object memory-backend-file,id=MEM,size=128m,mem-path=/tmp/MEM,share=on \
  -numa node,memdev=MEM -m 128

and a live session run can be run against the guest kernel like so:

  $ crash <path-to-guest-vmlinux> live:/tmp/MEM@0

By prepending the ramdump image name with "live:", the crash session will
act as if it were running a normal live session.
(oleg@redhat.com)
2016-05-04 11:50:19 -04:00

10073 lines
262 KiB
C

/* task.c - core analysis suite
*
* Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc.
* Copyright (C) 2002-2016 David Anderson
* Copyright (C) 2002-2016 Red Hat, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "defs.h"
static ulong get_panic_context(void);
static int sort_by_pid(const void *, const void *);
static void show_ps(ulong, struct psinfo *);
static struct task_context *panic_search(void);
static void allocate_task_space(int);
static void refresh_fixed_task_table(void);
static void refresh_unlimited_task_table(void);
static void refresh_pidhash_task_table(void);
static void refresh_pid_hash_task_table(void);
static void refresh_hlist_task_table(void);
static void refresh_hlist_task_table_v2(void);
static void refresh_hlist_task_table_v3(void);
static void refresh_active_task_table(void);
static struct task_context *store_context(struct task_context *, ulong, char *);
static void refresh_context(ulong, ulong);
static ulong parent_of(ulong);
static void parent_list(ulong);
static void child_list(ulong);
static void initialize_task_state(void);
static void dump_task_states(void);
static void show_ps_data(ulong, struct task_context *, struct psinfo *);
static void show_task_times(struct task_context *, ulong);
static void show_task_args(struct task_context *);
static void show_task_rlimit(struct task_context *);
static void show_tgid_list(ulong);
static int compare_start_time(const void *, const void *);
static int start_time_timespec(void);
static ulonglong convert_start_time(ulonglong, ulonglong);
static ulong get_dumpfile_panic_task(void);
static ulong get_active_set_panic_task(void);
static void populate_panic_threads(void);
static int verify_task(struct task_context *, int);
static ulong get_idle_task(int, char *);
static ulong get_curr_task(int, char *);
static long rq_idx(int);
static long cpu_idx(int);
static void dump_runq(void);
static void dump_on_rq_timestamp(void);
static void dump_on_rq_milliseconds(void);
static void dump_runqueues(void);
static void dump_prio_array(int, ulong, char *);
static void dump_task_runq_entry(struct task_context *, int);
static void print_group_header_fair(int, ulong, void *);
static void print_parent_task_group_fair(void *, int);
static int dump_tasks_in_lower_dequeued_cfs_rq(int, ulong, int, struct task_context *);
static int dump_tasks_in_cfs_rq(ulong);
static int dump_tasks_in_task_group_cfs_rq(int, ulong, int, struct task_context *);
static void dump_on_rq_tasks(void);
static void cfs_rq_offset_init(void);
static void task_group_offset_init(void);
static void dump_CFS_runqueues(void);
static void print_group_header_rt(ulong, void *);
static void print_parent_task_group_rt(void *, int);
static int dump_tasks_in_lower_dequeued_rt_rq(int, ulong, int);
static int dump_RT_prio_array(ulong, char *);
static void dump_tasks_in_task_group_rt_rq(int, ulong, int);
static char *get_task_group_name(ulong);
static void sort_task_group_info_array(void);
static void print_task_group_info_array(void);
static void reuse_task_group_info_array(void);
static void free_task_group_info_array(void);
static void fill_task_group_info_array(int, ulong, char *, int);
static void dump_tasks_by_task_group(void);
static void task_struct_member(struct task_context *,unsigned int, struct reference *);
static void signal_reference(struct task_context *, ulong, struct reference *);
static void do_sig_thread_group(ulong);
static void dump_signal_data(struct task_context *, ulong);
#define TASK_LEVEL (0x1)
#define THREAD_GROUP_LEVEL (0x2)
#define TASK_INDENT (0x4)
static int sigrt_minmax(int *, int *);
static void signame_list(void);
static void sigqueue_list(ulong);
static ulonglong task_signal(ulong, ulong*);
static ulonglong task_blocked(ulong);
static void translate_sigset(ulonglong);
static ulonglong sigaction_mask(ulong);
static int task_has_cpu(ulong, char *);
static int is_foreach_keyword(char *, int *);
static void foreach_cleanup(void *);
static void ps_cleanup(void *);
static char *task_pointer_string(struct task_context *, ulong, char *);
static int panic_context_adjusted(struct task_context *tc);
static void show_last_run(struct task_context *, struct psinfo *);
static void show_milliseconds(struct task_context *, struct psinfo *);
static char *translate_nanoseconds(ulonglong, char *);
static int sort_by_last_run(const void *arg1, const void *arg2);
static void sort_context_array_by_last_run(void);
static void show_ps_summary(ulong);
static void irqstacks_init(void);
static void parse_task_thread(int argcnt, char *arglist[], struct task_context *);
/*
* Figure out how much space will be required to hold the task context
* data, malloc() it, and call refresh_task_table() to fill it up.
* Gather a few key offset and size values. Lastly, get, and then set,
* the initial context.
*/
void
task_init(void)
{
long len;
int dim, task_struct_size;
struct syment *nsp;
long tss_offset, thread_offset;
long eip_offset, esp_offset, ksp_offset;
struct gnu_request req;
ulong active_pid;
if (!(tt->idle_threads = (ulong *)calloc(NR_CPUS, sizeof(ulong))))
error(FATAL, "cannot malloc idle_threads array");
if (DUMPFILE() &&
!(tt->panic_threads = (ulong *)calloc(NR_CPUS, sizeof(ulong))))
error(FATAL, "cannot malloc panic_threads array");
if (kernel_symbol_exists("nr_tasks")) {
/*
* Figure out what maximum NR_TASKS would be by getting the
* address of the next symbol after "task".
*/
tt->task_start = symbol_value("task");
if ((nsp = next_symbol("task", NULL)) == NULL)
error(FATAL, "cannot determine size of task table\n");
tt->flags |= TASK_ARRAY_EXISTS;
tt->task_end = nsp->value;
tt->max_tasks = (tt->task_end-tt->task_start) / sizeof(void *);
allocate_task_space(tt->max_tasks);
tss_offset = MEMBER_OFFSET_INIT(task_struct_tss,
"task_struct", "tss");
eip_offset = MEMBER_OFFSET_INIT(thread_struct_eip,
"thread_struct", "eip");
esp_offset = MEMBER_OFFSET_INIT(thread_struct_esp,
"thread_struct", "esp");
ksp_offset = MEMBER_OFFSET_INIT(thread_struct_ksp,
"thread_struct", "ksp");
ASSIGN_OFFSET(task_struct_tss_eip) =
(eip_offset == INVALID_OFFSET) ?
INVALID_OFFSET : tss_offset + eip_offset;
ASSIGN_OFFSET(task_struct_tss_esp) =
(esp_offset == INVALID_OFFSET) ?
INVALID_OFFSET : tss_offset + esp_offset;
ASSIGN_OFFSET(task_struct_tss_ksp) =
(ksp_offset == INVALID_OFFSET) ?
INVALID_OFFSET : tss_offset + ksp_offset;
tt->flags |= TASK_REFRESH;
tt->refresh_task_table = refresh_fixed_task_table;
readmem(tt->task_start, KVADDR, &tt->idle_threads[0],
kt->cpus * sizeof(void *), "idle threads",
FAULT_ON_ERROR);
} else {
/*
* Make the task table big enough to hold what's running.
* It can be realloc'd later if it grows on a live system.
*/
get_symbol_data("nr_threads", sizeof(int), &tt->nr_threads);
tt->max_tasks = tt->nr_threads + NR_CPUS + TASK_SLUSH;
allocate_task_space(tt->max_tasks);
thread_offset = MEMBER_OFFSET_INIT(task_struct_thread,
"task_struct", "thread");
eip_offset = MEMBER_OFFSET_INIT(thread_struct_eip,
"thread_struct", "eip");
esp_offset = MEMBER_OFFSET_INIT(thread_struct_esp,
"thread_struct", "esp");
/*
* Handle x86/x86_64 merger.
*/
if (eip_offset == INVALID_OFFSET)
eip_offset = MEMBER_OFFSET_INIT(thread_struct_eip,
"thread_struct", "ip");
if (esp_offset == INVALID_OFFSET)
esp_offset = MEMBER_OFFSET_INIT(thread_struct_esp,
"thread_struct", "sp");
ksp_offset = MEMBER_OFFSET_INIT(thread_struct_ksp,
"thread_struct", "ksp");
ASSIGN_OFFSET(task_struct_thread_eip) =
(eip_offset == INVALID_OFFSET) ?
INVALID_OFFSET : thread_offset + eip_offset;
ASSIGN_OFFSET(task_struct_thread_esp) =
(esp_offset == INVALID_OFFSET) ?
INVALID_OFFSET : thread_offset + esp_offset;
ASSIGN_OFFSET(task_struct_thread_ksp) =
(ksp_offset == INVALID_OFFSET) ?
INVALID_OFFSET : thread_offset + ksp_offset;
tt->flags |= TASK_REFRESH;
tt->refresh_task_table = refresh_unlimited_task_table;
get_idle_threads(&tt->idle_threads[0], kt->cpus);
}
if (MEMBER_EXISTS("task_struct", "thread_info"))
MEMBER_OFFSET_INIT(task_struct_thread_info, "task_struct",
"thread_info");
else if (MEMBER_EXISTS("task_struct", "stack"))
MEMBER_OFFSET_INIT(task_struct_thread_info, "task_struct",
"stack");
else
ASSIGN_OFFSET(task_struct_thread_info) = INVALID_OFFSET;
if (VALID_MEMBER(task_struct_thread_info)) {
MEMBER_OFFSET_INIT(thread_info_task, "thread_info", "task");
MEMBER_OFFSET_INIT(thread_info_cpu, "thread_info", "cpu");
MEMBER_OFFSET_INIT(thread_info_flags, "thread_info", "flags");
MEMBER_OFFSET_INIT(thread_info_previous_esp, "thread_info",
"previous_esp");
STRUCT_SIZE_INIT(thread_info, "thread_info");
tt->flags |= THREAD_INFO;
}
MEMBER_OFFSET_INIT(task_struct_state, "task_struct", "state");
MEMBER_OFFSET_INIT(task_struct_exit_state, "task_struct", "exit_state");
MEMBER_OFFSET_INIT(task_struct_pid, "task_struct", "pid");
MEMBER_OFFSET_INIT(task_struct_comm, "task_struct", "comm");
MEMBER_OFFSET_INIT(task_struct_next_task, "task_struct", "next_task");
MEMBER_OFFSET_INIT(task_struct_processor, "task_struct", "processor");
MEMBER_OFFSET_INIT(task_struct_p_pptr, "task_struct", "p_pptr");
MEMBER_OFFSET_INIT(task_struct_parent, "task_struct", "parent");
if (INVALID_MEMBER(task_struct_parent))
MEMBER_OFFSET_INIT(task_struct_parent, "task_struct",
"real_parent");
MEMBER_OFFSET_INIT(task_struct_has_cpu, "task_struct", "has_cpu");
MEMBER_OFFSET_INIT(task_struct_cpus_runnable,
"task_struct", "cpus_runnable");
MEMBER_OFFSET_INIT(task_struct_cpu, "task_struct", "cpu");
MEMBER_OFFSET_INIT(task_struct_active_mm, "task_struct", "active_mm");
MEMBER_OFFSET_INIT(task_struct_next_run, "task_struct", "next_run");
MEMBER_OFFSET_INIT(task_struct_flags, "task_struct", "flags");
MEMBER_SIZE_INIT(task_struct_flags, "task_struct", "flags");
MEMBER_OFFSET_INIT(task_struct_pidhash_next,
"task_struct", "pidhash_next");
MEMBER_OFFSET_INIT(task_struct_pgrp, "task_struct", "pgrp");
MEMBER_OFFSET_INIT(task_struct_tgid, "task_struct", "tgid");
MEMBER_OFFSET_INIT(task_struct_pids, "task_struct", "pids");
MEMBER_OFFSET_INIT(task_struct_last_run, "task_struct", "last_run");
MEMBER_OFFSET_INIT(task_struct_timestamp, "task_struct", "timestamp");
MEMBER_OFFSET_INIT(task_struct_sched_info, "task_struct", "sched_info");
if (VALID_MEMBER(task_struct_sched_info))
MEMBER_OFFSET_INIT(sched_info_last_arrival,
"sched_info", "last_arrival");
if (VALID_MEMBER(task_struct_last_run) ||
VALID_MEMBER(task_struct_timestamp) ||
VALID_MEMBER(sched_info_last_arrival)) {
char buf[BUFSIZE];
strcpy(buf, "alias last ps -l");
alias_init(buf);
}
MEMBER_OFFSET_INIT(pid_link_pid, "pid_link", "pid");
MEMBER_OFFSET_INIT(pid_hash_chain, "pid", "hash_chain");
STRUCT_SIZE_INIT(pid_link, "pid_link");
STRUCT_SIZE_INIT(upid, "upid");
if (VALID_STRUCT(upid)) {
MEMBER_OFFSET_INIT(upid_nr, "upid", "nr");
MEMBER_OFFSET_INIT(upid_ns, "upid", "ns");
MEMBER_OFFSET_INIT(upid_pid_chain, "upid", "pid_chain");
MEMBER_OFFSET_INIT(pid_numbers, "pid", "numbers");
MEMBER_OFFSET_INIT(pid_tasks, "pid", "tasks");
tt->init_pid_ns = symbol_value("init_pid_ns");
}
MEMBER_OFFSET_INIT(pid_pid_chain, "pid", "pid_chain");
STRUCT_SIZE_INIT(task_struct, "task_struct");
if (kernel_symbol_exists("arch_task_struct_size") &&
readmem(symbol_value("arch_task_struct_size"), KVADDR,
&task_struct_size, sizeof(int),
"arch_task_struct_size", RETURN_ON_ERROR)) {
ASSIGN_SIZE(task_struct) = task_struct_size;
if (STRUCT_SIZE("task_struct") != SIZE(task_struct))
add_to_downsized("task_struct");
if (CRASHDEBUG(1))
fprintf(fp, "downsize task_struct: %ld to %ld\n",
STRUCT_SIZE("task_struct"),
SIZE(task_struct));
}
MEMBER_OFFSET_INIT(task_struct_sig, "task_struct", "sig");
MEMBER_OFFSET_INIT(task_struct_signal, "task_struct", "signal");
MEMBER_OFFSET_INIT(task_struct_blocked, "task_struct", "blocked");
MEMBER_OFFSET_INIT(task_struct_sigpending, "task_struct", "sigpending");
MEMBER_OFFSET_INIT(task_struct_pending, "task_struct", "pending");
MEMBER_OFFSET_INIT(task_struct_sigqueue, "task_struct", "sigqueue");
MEMBER_OFFSET_INIT(task_struct_sighand, "task_struct", "sighand");
MEMBER_OFFSET_INIT(signal_struct_count, "signal_struct", "count");
MEMBER_OFFSET_INIT(signal_struct_nr_threads, "signal_struct", "nr_threads");
MEMBER_OFFSET_INIT(signal_struct_action, "signal_struct", "action");
MEMBER_OFFSET_INIT(signal_struct_shared_pending, "signal_struct",
"shared_pending");
MEMBER_OFFSET_INIT(k_sigaction_sa, "k_sigaction", "sa");
MEMBER_OFFSET_INIT(sigaction_sa_handler, "sigaction", "sa_handler");
MEMBER_OFFSET_INIT(sigaction_sa_mask, "sigaction", "sa_mask");
MEMBER_OFFSET_INIT(sigaction_sa_flags, "sigaction", "sa_flags");
MEMBER_OFFSET_INIT(sigpending_head, "sigpending", "head");
if (INVALID_MEMBER(sigpending_head))
MEMBER_OFFSET_INIT(sigpending_list, "sigpending", "list");
MEMBER_OFFSET_INIT(sigpending_signal, "sigpending", "signal");
MEMBER_SIZE_INIT(sigpending_signal, "sigpending", "signal");
STRUCT_SIZE_INIT(sigqueue, "sigqueue");
STRUCT_SIZE_INIT(signal_queue, "signal_queue");
STRUCT_SIZE_INIT(sighand_struct, "sighand_struct");
if (VALID_STRUCT(sighand_struct))
MEMBER_OFFSET_INIT(sighand_struct_action, "sighand_struct",
"action");
MEMBER_OFFSET_INIT(siginfo_si_signo, "siginfo", "si_signo");
STRUCT_SIZE_INIT(signal_struct, "signal_struct");
STRUCT_SIZE_INIT(k_sigaction, "k_sigaction");
MEMBER_OFFSET_INIT(task_struct_start_time, "task_struct", "start_time");
MEMBER_SIZE_INIT(task_struct_start_time, "task_struct", "start_time");
MEMBER_SIZE_INIT(task_struct_utime, "task_struct", "utime");
MEMBER_SIZE_INIT(task_struct_stime, "task_struct", "stime");
MEMBER_OFFSET_INIT(task_struct_times, "task_struct", "times");
MEMBER_OFFSET_INIT(tms_tms_utime, "tms", "tms_utime");
MEMBER_OFFSET_INIT(tms_tms_stime, "tms", "tms_stime");
MEMBER_OFFSET_INIT(task_struct_utime, "task_struct", "utime");
MEMBER_OFFSET_INIT(task_struct_stime, "task_struct", "stime");
STRUCT_SIZE_INIT(cputime_t, "cputime_t");
if (symbol_exists("cfq_slice_async")) {
uint cfq_slice_async;
get_symbol_data("cfq_slice_async", sizeof(int),
&cfq_slice_async);
if (cfq_slice_async) {
machdep->hz = cfq_slice_async * 25;
if (CRASHDEBUG(2))
fprintf(fp,
"cfq_slice_async exists: setting hz to %d\n",
machdep->hz);
}
}
if (VALID_MEMBER(runqueue_arrays))
MEMBER_OFFSET_INIT(task_struct_run_list, "task_struct",
"run_list");
MEMBER_OFFSET_INIT(task_struct_rss_stat, "task_struct",
"rss_stat");
MEMBER_OFFSET_INIT(task_rss_stat_count, "task_rss_stat",
"count");
if ((tt->task_struct = (char *)malloc(SIZE(task_struct))) == NULL)
error(FATAL, "cannot malloc task_struct space.");
if ((tt->mm_struct = (char *)malloc(SIZE(mm_struct))) == NULL)
error(FATAL, "cannot malloc mm_struct space.");
if ((tt->flags & THREAD_INFO) &&
((tt->thread_info = (char *)malloc(SIZE(thread_info))) == NULL))
error(FATAL, "cannot malloc thread_info space.");
STRUCT_SIZE_INIT(task_union, "task_union");
STRUCT_SIZE_INIT(thread_union, "thread_union");
if (VALID_SIZE(task_union) && (SIZE(task_union) != STACKSIZE())) {
error(WARNING, "\nnon-standard stack size: %ld\n",
len = SIZE(task_union));
machdep->stacksize = len;
} else if (VALID_SIZE(thread_union) &&
((len = SIZE(thread_union)) != STACKSIZE()))
machdep->stacksize = len;
if (symbol_exists("pidhash") && symbol_exists("pid_hash") &&
!symbol_exists("pidhash_shift"))
error(FATAL,
"pidhash and pid_hash both exist -- cannot distinquish between them\n");
if (symbol_exists("pid_hash") && symbol_exists("pidhash_shift")) {
int pidhash_shift;
if (get_symbol_type("PIDTYPE_PID", NULL, &req) !=
TYPE_CODE_ENUM)
error(FATAL,
"cannot determine PIDTYPE_PID pid_hash dimension\n");
get_symbol_data("pidhash_shift", sizeof(int), &pidhash_shift);
tt->pidhash_len = 1 << pidhash_shift;
get_symbol_data("pid_hash", sizeof(ulong), &tt->pidhash_addr);
if (VALID_MEMBER(pid_link_pid) && VALID_MEMBER(pid_hash_chain)) {
get_symbol_data("pid_hash", sizeof(ulong), &tt->pidhash_addr);
tt->refresh_task_table = refresh_pid_hash_task_table;
} else {
tt->pidhash_addr = symbol_value("pid_hash");
if (LKCD_KERNTYPES()) {
if (VALID_STRUCT(pid_link)) {
if (VALID_STRUCT(upid) && VALID_MEMBER(pid_numbers))
tt->refresh_task_table =
refresh_hlist_task_table_v3;
else
tt->refresh_task_table =
refresh_hlist_task_table_v2;
} else
tt->refresh_task_table =
refresh_hlist_task_table;
builtin_array_length("pid_hash",
tt->pidhash_len, NULL);
} else {
if (!get_array_length("pid_hash", NULL,
sizeof(void *)) && VALID_STRUCT(pid_link)) {
if (VALID_STRUCT(upid) && VALID_MEMBER(pid_numbers))
tt->refresh_task_table =
refresh_hlist_task_table_v3;
else
tt->refresh_task_table =
refresh_hlist_task_table_v2;
}
else
tt->refresh_task_table =
refresh_hlist_task_table;
}
}
tt->flags |= PID_HASH;
} else if (symbol_exists("pid_hash")) {
if (get_symbol_type("PIDTYPE_PGID", NULL, &req) !=
TYPE_CODE_ENUM)
error(FATAL,
"cannot determine PIDTYPE_PID pid_hash dimension\n");
if (!(tt->pidhash_len = get_array_length("pid_hash",
&dim, SIZE(list_head))))
error(FATAL,
"cannot determine pid_hash array dimensions\n");
tt->pidhash_addr = symbol_value("pid_hash");
tt->refresh_task_table = refresh_pid_hash_task_table;
tt->flags |= PID_HASH;
} else if (symbol_exists("pidhash")) {
tt->pidhash_addr = symbol_value("pidhash");
tt->pidhash_len = get_array_length("pidhash", NULL, 0);
if (tt->pidhash_len == 0) {
if (!(nsp = next_symbol("pidhash", NULL)))
error(FATAL,
"cannot determine pidhash length\n");
tt->pidhash_len =
(nsp->value-tt->pidhash_addr) / sizeof(void *);
}
if (ACTIVE())
tt->refresh_task_table = refresh_pidhash_task_table;
tt->flags |= PIDHASH;
}
/*
* Get the IRQ stacks info if it's configured.
*/
if (VALID_STRUCT(irq_ctx))
irqstacks_init();
get_active_set();
if (tt->flags & ACTIVE_ONLY)
tt->refresh_task_table = refresh_active_task_table;
tt->refresh_task_table();
if (tt->flags & TASK_REFRESH_OFF)
tt->flags &= ~(TASK_REFRESH|TASK_REFRESH_OFF);
if (ACTIVE()) {
active_pid = REMOTE() ? pc->server_pid :
LOCAL_ACTIVE() ? pc->program_pid : 1;
set_context(NO_TASK, active_pid);
tt->this_task = pid_to_task(active_pid);
}
else {
if (KDUMP_DUMPFILE())
map_cpus_to_prstatus();
else if (ELF_NOTES_VALID() && DISKDUMP_DUMPFILE())
map_cpus_to_prstatus_kdump_cmprs();
please_wait("determining panic task");
set_context(get_panic_context(), NO_PID);
please_wait_done();
}
sort_context_array();
sort_tgid_array();
if (pc->flags & SILENT)
initialize_task_state();
tt->flags |= TASK_INIT_DONE;
}
/*
* Store the pointers to the hard and soft irq_ctx arrays as well as
* the task pointers contained within each of them.
*/
static void
irqstacks_init(void)
{
int i;
char *thread_info_buf;
struct syment *hard_sp, *soft_sp;
if (!(tt->hardirq_ctx = (ulong *)calloc(NR_CPUS, sizeof(ulong))))
error(FATAL, "cannot malloc hardirq_ctx space.");
if (!(tt->hardirq_tasks = (ulong *)calloc(NR_CPUS, sizeof(ulong))))
error(FATAL, "cannot malloc hardirq_tasks space.");
if (!(tt->softirq_ctx = (ulong *)calloc(NR_CPUS, sizeof(ulong))))
error(FATAL, "cannot malloc softirq_ctx space.");
if (!(tt->softirq_tasks = (ulong *)calloc(NR_CPUS, sizeof(ulong))))
error(FATAL, "cannot malloc softirq_tasks space.");
thread_info_buf = GETBUF(SIZE(irq_ctx));
if ((hard_sp = per_cpu_symbol_search("per_cpu__hardirq_ctx"))) {
if ((kt->flags & SMP) && (kt->flags & PER_CPU_OFF)) {
for (i = 0; i < NR_CPUS; i++) {
if (!kt->__per_cpu_offset[i])
continue;
tt->hardirq_ctx[i] = hard_sp->value +
kt->__per_cpu_offset[i];
}
} else
tt->hardirq_ctx[0] = hard_sp->value;
} else if (symbol_exists("hardirq_ctx")) {
i = get_array_length("hardirq_ctx", NULL, 0);
get_symbol_data("hardirq_ctx",
sizeof(long)*(i <= NR_CPUS ? i : NR_CPUS),
&tt->hardirq_ctx[0]);
} else
error(WARNING, "cannot determine hardirq_ctx addresses\n");
for (i = 0; i < NR_CPUS; i++) {
if (!(tt->hardirq_ctx[i]))
continue;
if (!readmem(tt->hardirq_ctx[i], KVADDR, thread_info_buf,
SIZE(irq_ctx), "hardirq thread_union",
RETURN_ON_ERROR)) {
error(INFO, "cannot read hardirq_ctx[%d] at %lx\n",
i, tt->hardirq_ctx[i]);
continue;
}
tt->hardirq_tasks[i] =
ULONG(thread_info_buf+OFFSET(thread_info_task));
}
if ((soft_sp = per_cpu_symbol_search("per_cpu__softirq_ctx"))) {
if ((kt->flags & SMP) && (kt->flags & PER_CPU_OFF)) {
for (i = 0; i < NR_CPUS; i++) {
if (!kt->__per_cpu_offset[i])
continue;
tt->softirq_ctx[i] = soft_sp->value +
kt->__per_cpu_offset[i];
}
} else
tt->softirq_ctx[0] = soft_sp->value;
} else if (symbol_exists("softirq_ctx")) {
i = get_array_length("softirq_ctx", NULL, 0);
get_symbol_data("softirq_ctx",
sizeof(long)*(i <= NR_CPUS ? i : NR_CPUS),
&tt->softirq_ctx[0]);
} else
error(WARNING, "cannot determine softirq_ctx addresses\n");
for (i = 0; i < NR_CPUS; i++) {
if (!(tt->softirq_ctx[i]))
continue;
if (!readmem(tt->softirq_ctx[i], KVADDR, thread_info_buf,
SIZE(irq_ctx), "softirq thread_union",
RETURN_ON_ERROR)) {
error(INFO, "cannot read softirq_ctx[%d] at %lx\n",
i, tt->hardirq_ctx[i]);
continue;
}
tt->softirq_tasks[i] =
ULONG(thread_info_buf+OFFSET(thread_info_task));
}
tt->flags |= IRQSTACKS;
FREEBUF(thread_info_buf);
}
int
in_irq_ctx(ulonglong type, int cpu, ulong addr)
{
if (!(tt->flags & IRQSTACKS))
return FALSE;
switch (type)
{
case BT_SOFTIRQ:
if (tt->softirq_ctx[cpu] &&
(addr >= tt->softirq_ctx[cpu]) &&
(addr < (tt->softirq_ctx[cpu] + STACKSIZE())))
return TRUE;
break;
case BT_HARDIRQ:
if (tt->hardirq_ctx[cpu] &&
(addr >= tt->hardirq_ctx[cpu]) &&
(addr < (tt->hardirq_ctx[cpu] + STACKSIZE())))
return TRUE;
break;
}
return FALSE;
}
/*
* Allocate or re-allocated space for the task_context array and task list.
*/
static void
allocate_task_space(int cnt)
{
if (tt->context_array == NULL) {
if (!(tt->task_local = (void *)
malloc(cnt * sizeof(void *))))
error(FATAL,
"cannot malloc kernel task array (%d tasks)", cnt);
if (!(tt->context_array = (struct task_context *)
malloc(cnt * sizeof(struct task_context))))
error(FATAL, "cannot malloc context array (%d tasks)",
cnt);
if (!(tt->tgid_array = (struct tgid_context *)
malloc(cnt * sizeof(struct tgid_context))))
error(FATAL, "cannot malloc tgid array (%d tasks)",
cnt);
} else {
if (!(tt->task_local = (void *)
realloc(tt->task_local, cnt * sizeof(void *))))
error(FATAL,
"%scannot realloc kernel task array (%d tasks)",
(pc->flags & RUNTIME) ? "" : "\n", cnt);
if (!(tt->context_array = (struct task_context *)
realloc(tt->context_array,
cnt * sizeof(struct task_context))))
error(FATAL,
"%scannot realloc context array (%d tasks)",
(pc->flags & RUNTIME) ? "" : "\n", cnt);
if (!(tt->tgid_array = (struct tgid_context *)
realloc(tt->tgid_array,
cnt * sizeof(struct tgid_context))))
error(FATAL,
"%scannot realloc tgid array (%d tasks)",
(pc->flags & RUNTIME) ? "" : "\n", cnt);
}
}
/*
* This routine runs one time on dumpfiles, and constantly on live systems.
* It walks through the kernel task array looking for active tasks, and
* populates the local task table with their essential data.
*/
static void
refresh_fixed_task_table(void)
{
int i;
ulong *tlp;
struct task_context *tc;
ulong curtask;
ulong retries;
ulong curpid;
char *tp;
#define TASK_FREE(x) ((x == 0) || (((ulong)(x) >= tt->task_start) && \
((ulong)(x) < tt->task_end)))
#define TASK_IN_USE(x) (!TASK_FREE(x))
if (DUMPFILE() && (tt->flags & TASK_INIT_DONE))
return;
if (DUMPFILE()) {
fprintf(fp, (pc->flags & SILENT) || !(pc->flags & TTY) ?
"" : "%splease wait... (gathering task table data)",
GDB_PATCHED() ? "" : "\n");
fflush(fp);
if (!symbol_exists("panic_threads"))
tt->flags |= POPULATE_PANIC;
}
if (ACTIVE() && !(tt->flags & TASK_REFRESH))
return;
curpid = NO_PID;
curtask = NO_TASK;
/*
* The current task's task_context entry may change,
* or the task may not even exist anymore.
*/
if (ACTIVE() && (tt->flags & TASK_INIT_DONE)) {
curtask = CURRENT_TASK();
curpid = CURRENT_PID();
}
retries = 0;
retry:
if (!readmem(tt->task_start, KVADDR, tt->task_local,
tt->max_tasks * sizeof(void *), "kernel task array",
RETURN_ON_ERROR))
error(FATAL, "cannot read kernel task array");
clear_task_cache();
for (i = 0, tlp = (ulong *)tt->task_local,
tt->running_tasks = 0, tc = tt->context_array;
i < tt->max_tasks; i++, tlp++) {
if (TASK_IN_USE(*tlp)) {
if (!(tp = fill_task_struct(*tlp))) {
if (DUMPFILE())
continue;
retries++;
goto retry;
}
if (store_context(tc, *tlp, tp)) {
tc++;
tt->running_tasks++;
}
}
}
if (DUMPFILE()) {
fprintf(fp, (pc->flags & SILENT) || !(pc->flags & TTY) ? "" :
"\r \r");
fflush(fp);
}
if (ACTIVE() && (tt->flags & TASK_INIT_DONE))
refresh_context(curtask, curpid);
tt->retries = MAX(tt->retries, retries);
}
/*
* Verify that a task_context's data makes sense enough to include
* in the task_context array.
*/
static int
verify_task(struct task_context *tc, int level)
{
int i;
ulong next_task;
ulong readflag;
readflag = ACTIVE() ? (RETURN_ON_ERROR|QUIET) : (RETURN_ON_ERROR);
switch (level)
{
case 1:
if (!readmem(tc->task + OFFSET(task_struct_next_task),
KVADDR, &next_task, sizeof(void *), "next_task", readflag)) {
return FALSE;
}
if (!IS_TASK_ADDR(next_task))
return FALSE;
if (tc->processor & ~NO_PROC_ID)
return FALSE;
/* fall through */
case 2:
if (!IS_TASK_ADDR(tc->ptask))
return FALSE;
if ((tc->processor < 0) || (tc->processor >= NR_CPUS)) {
for (i = 0; i < NR_CPUS; i++) {
if (tc->task == tt->active_set[i]) {
error(WARNING,
"active task %lx on cpu %d: corrupt cpu value: %u\n\n",
tc->task, i, tc->processor);
tc->processor = i;
return TRUE;
}
}
if (CRASHDEBUG(1))
error(INFO,
"verify_task: task: %lx invalid processor: %u",
tc->task, tc->processor);
return FALSE;
}
break;
}
return TRUE;
}
/*
* This routine runs one time on dumpfiles, and constantly on live systems.
* It walks through the kernel task array looking for active tasks, and
* populates the local task table with their essential data.
*/
#define MAX_UNLIMITED_TASK_RETRIES (500)
void
refresh_unlimited_task_table(void)
{
int i;
ulong *tlp;
struct task_context *tc;
ulong curtask;
ulong curpid;
struct list_data list_data, *ld;
ulong init_tasks[NR_CPUS];
ulong retries;
char *tp;
int cnt;
if (DUMPFILE() && (tt->flags & TASK_INIT_DONE))
return;
if (DUMPFILE()) {
fprintf(fp, (pc->flags & SILENT) || !(pc->flags & TTY) ?
"" : "%splease wait... (gathering task table data)",
GDB_PATCHED() ? "" : "\n");
fflush(fp);
if (!symbol_exists("panic_threads"))
tt->flags |= POPULATE_PANIC;
}
if (ACTIVE() && !(tt->flags & TASK_REFRESH))
return;
curpid = NO_PID;
curtask = NO_TASK;
tp = NULL;
/*
* The current task's task_context entry may change,
* or the task may not even exist anymore.
*/
if (ACTIVE() && (tt->flags & TASK_INIT_DONE)) {
curtask = CURRENT_TASK();
curpid = CURRENT_PID();
}
retries = 0;
retry:
if (retries && DUMPFILE()) {
if (tt->flags & PIDHASH) {
error(WARNING,
"\ncannot gather a stable task list -- trying pidhash\n");
refresh_pidhash_task_table();
return;
}
error(FATAL, "\ncannot gather a stable task list\n");
}
if ((retries == MAX_UNLIMITED_TASK_RETRIES) &&
!(tt->flags & TASK_INIT_DONE))
error(FATAL, "cannot gather a stable task list\n");
/*
* Populate the task_local array with a quick walk-through.
* If there's not enough room in the local array, realloc() it.
*/
ld = &list_data;
BZERO(ld, sizeof(struct list_data));
ld->flags |= RETURN_ON_LIST_ERROR;
ld->start = symbol_value("init_task_union");
ld->member_offset = OFFSET(task_struct_next_task);
if (!hq_open()) {
error(INFO, "cannot hash task_struct entries\n");
if (!(tt->flags & TASK_INIT_DONE))
clean_exit(1);
error(INFO, "using stale task_structs\n");
FREEBUF(tp);
return;
}
if ((cnt = do_list(ld)) < 0) {
retries++;
goto retry;
}
if ((cnt+NR_CPUS+1) > tt->max_tasks) {
tt->max_tasks = cnt + NR_CPUS + TASK_SLUSH;
allocate_task_space(tt->max_tasks);
hq_close();
if (!DUMPFILE())
retries++;
goto retry;
}
BZERO(tt->task_local, tt->max_tasks * sizeof(void *));
cnt = retrieve_list((ulong *)tt->task_local, cnt);
hq_close();
/*
* If SMP, add in the other idle tasks.
*/
if (kt->flags & SMP) {
/*
* Now get the rest of the init_task[] entries, starting
* at offset 1 since we've got the init_task already.
*/
BZERO(&init_tasks[0], sizeof(ulong) * NR_CPUS);
get_idle_threads(&init_tasks[0], kt->cpus);
tlp = (ulong *)tt->task_local;
tlp += cnt;
for (i = 1; i < kt->cpus; i++) {
if (init_tasks[i]) {
*tlp = init_tasks[i];
tlp++;
}
}
}
clear_task_cache();
for (i = 0, tlp = (ulong *)tt->task_local,
tt->running_tasks = 0, tc = tt->context_array;
i < tt->max_tasks; i++, tlp++) {
if (!(*tlp))
continue;
if (!IS_TASK_ADDR(*tlp)) {
error(INFO,
"\ninvalid task address in task list: %lx\n", *tlp);
retries++;
goto retry;
}
if (task_exists(*tlp)) {
error(INFO,
"\nduplicate task address in task list: %lx\n",
*tlp);
retries++;
goto retry;
}
if (!(tp = fill_task_struct(*tlp))) {
if (DUMPFILE())
continue;
retries++;
goto retry;
}
if (store_context(tc, *tlp, tp)) {
tc++;
tt->running_tasks++;
}
}
if (DUMPFILE()) {
fprintf(fp, (pc->flags & SILENT) || !(pc->flags & TTY) ? "" :
"\r \r");
fflush(fp);
}
if (ACTIVE() && (tt->flags & TASK_INIT_DONE))
refresh_context(curtask, curpid);
tt->retries = MAX(tt->retries, retries);
}
/*
* This routine runs one time on dumpfiles, and constantly on live systems.
* It walks through the kernel pidhash array looking for active tasks, and
* populates the local task table with their essential data.
*
* The following manner of refreshing the task table can be used for all
* kernels that have a pidhash[] array, whether or not they still
* have a fixed task[] array or an unlimited list.
*/
static void
refresh_pidhash_task_table(void)
{
int i;
char *pidhash, *tp;
ulong *pp, next, pnext;
int len, cnt;
struct task_context *tc;
ulong curtask;
ulong curpid;
ulong retries;
ulong *tlp;
if (DUMPFILE() && (tt->flags & TASK_INIT_DONE)) /* impossible */
return;
if (DUMPFILE()) { /* impossible */
fprintf(fp, (pc->flags & SILENT) || !(pc->flags & TTY) ?
"" : "\rplease wait... (gathering task table data)");
fflush(fp);
if (!symbol_exists("panic_threads"))
tt->flags |= POPULATE_PANIC;
}
if (ACTIVE() && !(tt->flags & TASK_REFRESH))
return;
curpid = NO_PID;
curtask = NO_TASK;
/*
* The current task's task_context entry may change,
* or the task may not even exist anymore.
*/
if (ACTIVE() && (tt->flags & TASK_INIT_DONE)) {
curtask = CURRENT_TASK();
curpid = CURRENT_PID();
}
len = tt->pidhash_len;
pidhash = GETBUF(len * sizeof(ulong));
retries = 0;
retry_pidhash:
if (retries && DUMPFILE())
error(FATAL,"\ncannot gather a stable task list via pidhash\n");
if ((retries == MAX_UNLIMITED_TASK_RETRIES) &&
!(tt->flags & TASK_INIT_DONE))
error(FATAL,
"\ncannot gather a stable task list via pidhash (%d retries)\n",
retries);
if (!readmem(tt->pidhash_addr, KVADDR, pidhash,
len * sizeof(ulong), "pidhash contents", RETURN_ON_ERROR))
error(FATAL, "\ncannot read pidhash array\n");
if (!hq_open()) {
error(INFO, "cannot hash task_struct entries\n");
if (!(tt->flags & TASK_INIT_DONE))
clean_exit(1);
error(INFO, "using stale task_structs\n");
FREEBUF(pidhash);
return;
}
/*
* Get the idle threads first.
*/
cnt = 0;
for (i = 0; i < kt->cpus; i++) {
if (hq_enter(tt->idle_threads[i]))
cnt++;
else
error(WARNING, "%sduplicate idle tasks?\n",
DUMPFILE() ? "\n" : "");
}
/*
* Then dump the pidhash contents.
*/
for (i = 0, pp = (ulong *)pidhash; i < len; i++, pp++) {
if (!(*pp) || !IS_KVADDR(*pp))
continue;
/*
* Mininum verification here -- make sure that a task address
* and its pidhash_next entry (if any) both appear to be
* properly aligned before accepting the task.
*/
next = *pp;
while (next) {
if (!IS_TASK_ADDR(next)) {
error(INFO,
"%sinvalid task address in pidhash: %lx\n",
DUMPFILE() ? "\n" : "", next);
if (DUMPFILE())
break;
hq_close();
retries++;
goto retry_pidhash;
}
if (!readmem(next + OFFSET(task_struct_pidhash_next),
KVADDR, &pnext, sizeof(void *),
"pidhash_next entry", QUIET|RETURN_ON_ERROR)) {
error(INFO, "%scannot read from task: %lx\n",
DUMPFILE() ? "\n" : "", next);
if (DUMPFILE())
break;
hq_close();
retries++;
goto retry_pidhash;
}
if (!hq_enter(next)) {
error(INFO,
"%sduplicate task in pidhash: %lx\n",
DUMPFILE() ? "\n" : "", next);
if (DUMPFILE())
break;
hq_close();
retries++;
goto retry_pidhash;
}
next = pnext;
cnt++;
}
}
if ((cnt+1) > tt->max_tasks) {
tt->max_tasks = cnt + NR_CPUS + TASK_SLUSH;
allocate_task_space(tt->max_tasks);
hq_close();
if (!DUMPFILE())
retries++;
goto retry_pidhash;
}
BZERO(tt->task_local, tt->max_tasks * sizeof(void *));
cnt = retrieve_list((ulong *)tt->task_local, cnt);
hq_close();
clear_task_cache();
for (i = 0, tlp = (ulong *)tt->task_local,
tt->running_tasks = 0, tc = tt->context_array;
i < tt->max_tasks; i++, tlp++) {
if (!(*tlp))
continue;
if (!IS_TASK_ADDR(*tlp)) {
error(WARNING,
"%sinvalid task address found in task list: %lx\n",
DUMPFILE() ? "\n" : "", *tlp);
if (DUMPFILE())
continue;
retries++;
goto retry_pidhash;
}
if (task_exists(*tlp)) {
error(WARNING,
"%sduplicate task address found in task list: %lx\n",
DUMPFILE() ? "\n" : "", *tlp);
if (DUMPFILE())
continue;
retries++;
goto retry_pidhash;
}
if (!(tp = fill_task_struct(*tlp))) {
if (DUMPFILE())
continue;
retries++;
goto retry_pidhash;
}
if (store_context(tc, *tlp, tp)) {
tc++;
tt->running_tasks++;
}
}
FREEBUF(pidhash);
if (DUMPFILE()) {
fprintf(fp, (pc->flags & SILENT) || !(pc->flags & TTY) ? "" :
"\r \r");
fflush(fp);
}
if (ACTIVE() && (tt->flags & TASK_INIT_DONE))
refresh_context(curtask, curpid);
tt->retries = MAX(tt->retries, retries);
}
/*
* The following manner of refreshing the task table is used for all
* kernels that have a pid_hash[][] array.
*
* This routine runs one time on dumpfiles, and constantly on live systems.
* It walks through the kernel pid_hash[PIDTYPE_PID] array looking for active
* tasks, and populates the local task table with their essential data.
*/
#define HASH_TO_TASK(X) ((ulong)(X) - (OFFSET(task_struct_pids) + \
OFFSET(pid_link_pid) + OFFSET(pid_hash_chain)))
#define TASK_TO_HASH(X) ((ulong)(X) + (OFFSET(task_struct_pids) + \
OFFSET(pid_link_pid) + OFFSET(pid_hash_chain)))
static void
refresh_pid_hash_task_table(void)
{
int i;
struct kernel_list_head *pid_hash, *pp, *kpp;
char *tp;
ulong next, pnext;
int len, cnt;
struct task_context *tc;
ulong curtask;
ulong curpid;
ulong retries;
ulong *tlp;
if (DUMPFILE() && (tt->flags & TASK_INIT_DONE)) /* impossible */
return;
if (DUMPFILE()) { /* impossible */
please_wait("gathering task table data");
if (!symbol_exists("panic_threads"))
tt->flags |= POPULATE_PANIC;
}
if (ACTIVE() && !(tt->flags & TASK_REFRESH))
return;
curpid = NO_PID;
curtask = NO_TASK;
/*
* The current task's task_context entry may change,
* or the task may not even exist anymore.
*/
if (ACTIVE() && (tt->flags & TASK_INIT_DONE)) {
curtask = CURRENT_TASK();
curpid = CURRENT_PID();
}
len = tt->pidhash_len;
pid_hash = (struct kernel_list_head *)GETBUF(len * SIZE(list_head));
retries = 0;
retry_pid_hash:
if (retries && DUMPFILE())
error(FATAL,
"\ncannot gather a stable task list via pid_hash\n");
if ((retries == MAX_UNLIMITED_TASK_RETRIES) &&
!(tt->flags & TASK_INIT_DONE))
error(FATAL,
"\ncannot gather a stable task list via pid_hash (%d retries)\n",
retries);
if (!readmem(tt->pidhash_addr, KVADDR, pid_hash,
len * SIZE(list_head), "pid_hash contents", RETURN_ON_ERROR))
error(FATAL, "\ncannot read pid_hash array\n");
if (!hq_open()) {
error(INFO, "cannot hash task_struct entries\n");
if (!(tt->flags & TASK_INIT_DONE))
clean_exit(1);
error(INFO, "using stale task_structs\n");
FREEBUF(pid_hash);
return;
}
/*
* Get the idle threads first.
*/
cnt = 0;
for (i = 0; i < kt->cpus; i++) {
if (hq_enter(tt->idle_threads[i]))
cnt++;
else
error(WARNING, "%sduplicate idle tasks?\n",
DUMPFILE() ? "\n" : "");
}
for (i = 0; i < len; i++) {
pp = &pid_hash[i];
kpp = (struct kernel_list_head *)(tt->pidhash_addr +
i * SIZE(list_head));
if (pp->next == kpp)
continue;
if (CRASHDEBUG(7))
console("%lx: pid_hash[%d]: %lx (%lx) %lx (%lx)\n", kpp, i,
pp->next, HASH_TO_TASK(pp->next),
pp->prev, HASH_TO_TASK(pp->prev));
next = (ulong)HASH_TO_TASK(pp->next);
while (next) {
if (!IS_TASK_ADDR(next)) {
error(INFO,
"%sinvalid task address in pid_hash: %lx\n",
DUMPFILE() ? "\n" : "", next);
if (DUMPFILE())
break;
hq_close();
retries++;
goto retry_pid_hash;
}
if (!readmem(TASK_TO_HASH(next),
KVADDR, &pnext, sizeof(void *),
"pid_hash entry", QUIET|RETURN_ON_ERROR)) {
error(INFO, "%scannot read from task: %lx\n",
DUMPFILE() ? "\n" : "", next);
if (DUMPFILE())
break;
hq_close();
retries++;
goto retry_pid_hash;
}
if (!is_idle_thread(next) && !hq_enter(next)) {
error(INFO,
"%sduplicate task in pid_hash: %lx\n",
DUMPFILE() ? "\n" : "", next);
if (DUMPFILE())
break;
hq_close();
retries++;
goto retry_pid_hash;
}
cnt++;
if (pnext == (ulong)kpp)
break;
next = HASH_TO_TASK(pnext);
}
}
BZERO(tt->task_local, tt->max_tasks * sizeof(void *));
cnt = retrieve_list((ulong *)tt->task_local, cnt);
hq_close();
clear_task_cache();
for (i = 0, tlp = (ulong *)tt->task_local,
tt->running_tasks = 0, tc = tt->context_array;
i < tt->max_tasks; i++, tlp++) {
if (!(*tlp))
continue;
if (!IS_TASK_ADDR(*tlp)) {
error(WARNING,
"%sinvalid task address found in task list: %lx\n",
DUMPFILE() ? "\n" : "", *tlp);
if (DUMPFILE())
continue;
retries++;
goto retry_pid_hash;
}
if (task_exists(*tlp)) {
error(WARNING,
"%sduplicate task address found in task list: %lx\n",
DUMPFILE() ? "\n" : "", *tlp);
if (DUMPFILE())
continue;
retries++;
goto retry_pid_hash;
}
if (!(tp = fill_task_struct(*tlp))) {
if (DUMPFILE())
continue;
retries++;
goto retry_pid_hash;
}
if (store_context(tc, *tlp, tp)) {
tc++;
tt->running_tasks++;
}
}
FREEBUF(pid_hash);
please_wait_done();
if (ACTIVE() && (tt->flags & TASK_INIT_DONE))
refresh_context(curtask, curpid);
tt->retries = MAX(tt->retries, retries);
}
/*
* Adapt to yet another scheme, using later 2.6 hlist_head and hlist_nodes.
*/
#define HLIST_TO_TASK(X) ((ulong)(X) - (OFFSET(task_struct_pids) + \
OFFSET(pid_pid_chain)))
static void
refresh_hlist_task_table(void)
{
int i;
ulong *pid_hash;
struct syment *sp;
ulong pidhash_array;
ulong kpp;
char *tp;
ulong next, pnext, pprev;
char *nodebuf;
int plen, len, cnt;
long value;
struct task_context *tc;
ulong curtask;
ulong curpid;
ulong retries;
ulong *tlp;
if (DUMPFILE() && (tt->flags & TASK_INIT_DONE)) /* impossible */
return;
if (DUMPFILE()) { /* impossible */
please_wait("gathering task table data");
if (!symbol_exists("panic_threads"))
tt->flags |= POPULATE_PANIC;
}
if (ACTIVE() && !(tt->flags & TASK_REFRESH))
return;
curpid = NO_PID;
curtask = NO_TASK;
/*
* The current task's task_context entry may change,
* or the task may not even exist anymore.
*/
if (ACTIVE() && (tt->flags & TASK_INIT_DONE)) {
curtask = CURRENT_TASK();
curpid = CURRENT_PID();
}
if (!(plen = get_array_length("pid_hash", NULL, sizeof(void *)))) {
/*
* Workaround for gcc omitting debuginfo data for pid_hash.
*/
if (enumerator_value("PIDTYPE_MAX", &value)) {
if ((sp = next_symbol("pid_hash", NULL)) &&
(((sp->value - tt->pidhash_addr) / sizeof(void *)) < value))
error(WARNING, "possible pid_hash array mis-handling\n");
plen = (int)value;
} else {
error(WARNING,
"cannot determine pid_hash array dimensions\n");
plen = 1;
}
}
pid_hash = (ulong *)GETBUF(plen * sizeof(void *));
if (!readmem(tt->pidhash_addr, KVADDR, pid_hash,
plen * SIZE(hlist_head), "pid_hash[] contents", RETURN_ON_ERROR))
error(FATAL, "\ncannot read pid_hash array\n");
if (CRASHDEBUG(7))
for (i = 0; i < plen; i++)
console("pid_hash[%d]: %lx\n", i, pid_hash[i]);
/*
* The zero'th (PIDTYPE_PID) entry is the hlist_head array
* that we want.
*/
if (CRASHDEBUG(1)) {
if (!enumerator_value("PIDTYPE_PID", &value))
error(WARNING,
"possible pid_hash array mis-handling: PIDTYPE_PID: (unknown)\n");
else if (value != 0)
error(WARNING,
"possible pid_hash array mis-handling: PIDTYPE_PID: %d \n",
value);
}
pidhash_array = pid_hash[0];
FREEBUF(pid_hash);
len = tt->pidhash_len;
pid_hash = (ulong *)GETBUF(len * SIZE(hlist_head));
nodebuf = GETBUF(SIZE(hlist_node));
retries = 0;
retry_pid_hash:
if (retries && DUMPFILE())
error(FATAL,
"\ncannot gather a stable task list via pid_hash\n");
if ((retries == MAX_UNLIMITED_TASK_RETRIES) &&
!(tt->flags & TASK_INIT_DONE))
error(FATAL,
"\ncannot gather a stable task list via pid_hash (%d retries)\n",
retries);
if (!readmem(pidhash_array, KVADDR, pid_hash,
len * SIZE(hlist_head), "pid_hash[0] contents", RETURN_ON_ERROR))
error(FATAL, "\ncannot read pid_hash[0] array\n");
if (!hq_open()) {
error(INFO, "cannot hash task_struct entries\n");
if (!(tt->flags & TASK_INIT_DONE))
clean_exit(1);
error(INFO, "using stale task_structs\n");
FREEBUF(pid_hash);
return;
}
/*
* Get the idle threads first.
*/
cnt = 0;
for (i = 0; i < kt->cpus; i++) {
if (hq_enter(tt->idle_threads[i]))
cnt++;
else
error(WARNING, "%sduplicate idle tasks?\n",
DUMPFILE() ? "\n" : "");
}
for (i = 0; i < len; i++) {
if (!pid_hash[i])
continue;
if (!readmem(pid_hash[i], KVADDR, nodebuf,
SIZE(hlist_node), "pid_hash node", RETURN_ON_ERROR|QUIET)) {
error(INFO, "\ncannot read pid_hash node\n");
if (DUMPFILE())
continue;
hq_close();
retries++;
goto retry_pid_hash;
}
kpp = pid_hash[i];
next = (ulong)HLIST_TO_TASK(kpp);
pnext = ULONG(nodebuf + OFFSET(hlist_node_next));
pprev = ULONG(nodebuf + OFFSET(hlist_node_pprev));
if (CRASHDEBUG(1))
console("pid_hash[%d]: %lx task: %lx (node: %lx) next: %lx pprev: %lx\n",
i, pid_hash[i], next, kpp, pnext, pprev);
while (next) {
if (!IS_TASK_ADDR(next)) {
error(INFO,
"%sinvalid task address in pid_hash: %lx\n",
DUMPFILE() ? "\n" : "", next);
if (DUMPFILE())
break;
hq_close();
retries++;
goto retry_pid_hash;
}
if (!is_idle_thread(next) && !hq_enter(next)) {
error(INFO,
"%sduplicate task in pid_hash: %lx\n",
DUMPFILE() ? "\n" : "", next);
if (DUMPFILE())
break;
hq_close();
retries++;
goto retry_pid_hash;
}
cnt++;
if (!pnext)
break;
if (!readmem((ulonglong)pnext, KVADDR, nodebuf,
SIZE(hlist_node), "task hlist_node", RETURN_ON_ERROR|QUIET)) {
error(INFO, "\ncannot read hlist_node from task\n");
if (DUMPFILE())
break;
hq_close();
retries++;
goto retry_pid_hash;
}
kpp = (ulong)pnext;
next = (ulong)HLIST_TO_TASK(kpp);
pnext = ULONG(nodebuf + OFFSET(hlist_node_next));
pprev = ULONG(nodebuf + OFFSET(hlist_node_pprev));
if (CRASHDEBUG(1))
console(" chained task: %lx (node: %lx) next: %lx pprev: %lx\n",
(ulong)HLIST_TO_TASK(kpp), kpp, pnext, pprev);
}
}
if (cnt > tt->max_tasks) {
tt->max_tasks = cnt + TASK_SLUSH;
allocate_task_space(tt->max_tasks);
hq_close();
if (!DUMPFILE())
retries++;
goto retry_pid_hash;
}
BZERO(tt->task_local, tt->max_tasks * sizeof(void *));
cnt = retrieve_list((ulong *)tt->task_local, cnt);
hq_close();
clear_task_cache();
for (i = 0, tlp = (ulong *)tt->task_local,
tt->running_tasks = 0, tc = tt->context_array;
i < tt->max_tasks; i++, tlp++) {
if (!(*tlp))
continue;
if (!IS_TASK_ADDR(*tlp)) {
error(WARNING,
"%sinvalid task address found in task list: %lx\n",
DUMPFILE() ? "\n" : "", *tlp);
if (DUMPFILE())
continue;
retries++;
goto retry_pid_hash;
}
if (task_exists(*tlp)) {
error(WARNING,
"%sduplicate task address found in task list: %lx\n",
DUMPFILE() ? "\n" : "", *tlp);
if (DUMPFILE())
continue;
retries++;
goto retry_pid_hash;
}
if (!(tp = fill_task_struct(*tlp))) {
if (DUMPFILE())
continue;
retries++;
goto retry_pid_hash;
}
if (store_context(tc, *tlp, tp)) {
tc++;
tt->running_tasks++;
}
}
FREEBUF(pid_hash);
FREEBUF(nodebuf);
please_wait_done();
if (ACTIVE() && (tt->flags & TASK_INIT_DONE))
refresh_context(curtask, curpid);
tt->retries = MAX(tt->retries, retries);
}
/*
* 2.6.17 replaced:
* static struct hlist_head *pid_hash[PIDTYPE_MAX];
* with
* static struct hlist_head *pid_hash;
*/
static void
refresh_hlist_task_table_v2(void)
{
int i;
ulong *pid_hash;
ulong pidhash_array;
ulong kpp;
char *tp;
ulong next, pnext, pprev;
char *nodebuf;
int len, cnt;
struct task_context *tc;
ulong curtask;
ulong curpid;
ulong retries;
ulong *tlp;
if (DUMPFILE() && (tt->flags & TASK_INIT_DONE)) /* impossible */
return;
if (DUMPFILE()) { /* impossible */
please_wait("gathering task table data");
if (!symbol_exists("panic_threads"))
tt->flags |= POPULATE_PANIC;
}
if (ACTIVE() && !(tt->flags & TASK_REFRESH))
return;
curpid = NO_PID;
curtask = NO_TASK;
/*
* The current task's task_context entry may change,
* or the task may not even exist anymore.
*/
if (ACTIVE() && (tt->flags & TASK_INIT_DONE)) {
curtask = CURRENT_TASK();
curpid = CURRENT_PID();
}
get_symbol_data("pid_hash", sizeof(void *), &pidhash_array);
len = tt->pidhash_len;
pid_hash = (ulong *)GETBUF(len * SIZE(hlist_head));
nodebuf = GETBUF(SIZE(pid_link));
retries = 0;
retry_pid_hash:
if (retries && DUMPFILE())
error(FATAL,
"\ncannot gather a stable task list via pid_hash\n");
if ((retries == MAX_UNLIMITED_TASK_RETRIES) &&
!(tt->flags & TASK_INIT_DONE))
error(FATAL,
"\ncannot gather a stable task list via pid_hash (%d retries)\n",
retries);
if (!readmem(pidhash_array, KVADDR, pid_hash,
len * SIZE(hlist_head), "pid_hash contents", RETURN_ON_ERROR))
error(FATAL, "\ncannot read pid_hash array\n");
if (!hq_open()) {
error(INFO, "cannot hash task_struct entries\n");
if (!(tt->flags & TASK_INIT_DONE))
clean_exit(1);
error(INFO, "using stale task_structs\n");
FREEBUF(pid_hash);
return;
}
/*
* Get the idle threads first.
*/
cnt = 0;
for (i = 0; i < kt->cpus; i++) {
if (hq_enter(tt->idle_threads[i]))
cnt++;
else
error(WARNING, "%sduplicate idle tasks?\n",
DUMPFILE() ? "\n" : "");
}
for (i = 0; i < len; i++) {
if (!pid_hash[i])
continue;
if (!readmem(pid_hash[i], KVADDR, nodebuf,
SIZE(pid_link), "pid_hash node pid_link", RETURN_ON_ERROR|QUIET)) {
error(INFO, "\ncannot read pid_hash node pid_link\n");
if (DUMPFILE())
continue;
hq_close();
retries++;
goto retry_pid_hash;
}
kpp = pid_hash[i];
next = ULONG(nodebuf + OFFSET(pid_link_pid));
if (next)
next -= OFFSET(task_struct_pids);
pnext = ULONG(nodebuf + OFFSET(hlist_node_next));
pprev = ULONG(nodebuf + OFFSET(hlist_node_pprev));
if (CRASHDEBUG(1))
console("pid_hash[%d]: %lx task: %lx (node: %lx) next: %lx pprev: %lx\n",
i, pid_hash[i], next, kpp, pnext, pprev);
while (1) {
if (next) {
if (!IS_TASK_ADDR(next)) {
error(INFO,
"%sinvalid task address in pid_hash: %lx\n",
DUMPFILE() ? "\n" : "", next);
if (DUMPFILE())
break;
hq_close();
retries++;
goto retry_pid_hash;
}
if (!is_idle_thread(next) && !hq_enter(next)) {
error(INFO,
"%sduplicate task in pid_hash: %lx\n",
DUMPFILE() ? "\n" : "", next);
if (DUMPFILE())
break;
hq_close();
retries++;
goto retry_pid_hash;
}
}
cnt++;
if (!pnext)
break;
if (!readmem((ulonglong)pnext, KVADDR, nodebuf,
SIZE(pid_link), "task hlist_node pid_link", RETURN_ON_ERROR|QUIET)) {
error(INFO, "\ncannot read hlist_node pid_link from node next\n");
if (DUMPFILE())
break;
hq_close();
retries++;
goto retry_pid_hash;
}
kpp = (ulong)pnext;
next = ULONG(nodebuf + OFFSET(pid_link_pid));
if (next)
next -= OFFSET(task_struct_pids);
pnext = ULONG(nodebuf + OFFSET(hlist_node_next));
pprev = ULONG(nodebuf + OFFSET(hlist_node_pprev));
if (CRASHDEBUG(1))
console(" chained task: %lx (node: %lx) next: %lx pprev: %lx\n",
next, kpp, pnext, pprev);
}
}
if (cnt > tt->max_tasks) {
tt->max_tasks = cnt + TASK_SLUSH;
allocate_task_space(tt->max_tasks);
hq_close();
if (!DUMPFILE())
retries++;
goto retry_pid_hash;
}
BZERO(tt->task_local, tt->max_tasks * sizeof(void *));
cnt = retrieve_list((ulong *)tt->task_local, cnt);
hq_close();
clear_task_cache();
for (i = 0, tlp = (ulong *)tt->task_local,
tt->running_tasks = 0, tc = tt->context_array;
i < tt->max_tasks; i++, tlp++) {
if (!(*tlp))
continue;
if (!IS_TASK_ADDR(*tlp)) {
error(WARNING,
"%sinvalid task address found in task list: %lx\n",
DUMPFILE() ? "\n" : "", *tlp);
if (DUMPFILE())
continue;
retries++;
goto retry_pid_hash;
}
if (task_exists(*tlp)) {
error(WARNING,
"%sduplicate task address found in task list: %lx\n",
DUMPFILE() ? "\n" : "", *tlp);
if (DUMPFILE())
continue;
retries++;
goto retry_pid_hash;
}
if (!(tp = fill_task_struct(*tlp))) {
if (DUMPFILE())
continue;
retries++;
goto retry_pid_hash;
}
if (store_context(tc, *tlp, tp)) {
tc++;
tt->running_tasks++;
}
}
FREEBUF(pid_hash);
FREEBUF(nodebuf);
please_wait_done();
if (ACTIVE() && (tt->flags & TASK_INIT_DONE))
refresh_context(curtask, curpid);
tt->retries = MAX(tt->retries, retries);
}
/*
* 2.6.24: The pid_hash[] hlist_head entries were changed to point
* to the hlist_node structure embedded in a upid structure.
*/
static void
refresh_hlist_task_table_v3(void)
{
int i;
ulong *pid_hash;
ulong pidhash_array;
ulong kpp;
char *tp;
ulong next, pnext, pprev;
ulong upid;
char *nodebuf;
int len, cnt;
struct task_context *tc;
ulong curtask;
ulong curpid;
ulong retries;
ulong *tlp;
uint upid_nr;
ulong upid_ns;
int chained;
ulong pid;
ulong pid_tasks_0;
if (DUMPFILE() && (tt->flags & TASK_INIT_DONE)) /* impossible */
return;
if (DUMPFILE()) { /* impossible */
please_wait("gathering task table data");
if (!symbol_exists("panic_threads"))
tt->flags |= POPULATE_PANIC;
}
if (ACTIVE() && !(tt->flags & TASK_REFRESH))
return;
curpid = NO_PID;
curtask = NO_TASK;
/*
* The current task's task_context entry may change,
* or the task may not even exist anymore.
*/
if (ACTIVE() && (tt->flags & TASK_INIT_DONE)) {
curtask = CURRENT_TASK();
curpid = CURRENT_PID();
}
get_symbol_data("pid_hash", sizeof(void *), &pidhash_array);
len = tt->pidhash_len;
pid_hash = (ulong *)GETBUF(len * SIZE(hlist_head));
nodebuf = GETBUF(SIZE(upid));
retries = 0;
retry_pid_hash:
if (retries && DUMPFILE())
error(FATAL,
"\ncannot gather a stable task list via pid_hash\n");
if ((retries == MAX_UNLIMITED_TASK_RETRIES) &&
!(tt->flags & TASK_INIT_DONE))
error(FATAL,
"\ncannot gather a stable task list via pid_hash (%d retries)\n",
retries);
if (!readmem(pidhash_array, KVADDR, pid_hash,
len * SIZE(hlist_head), "pid_hash contents", RETURN_ON_ERROR))
error(FATAL, "\ncannot read pid_hash array\n");
if (!hq_open()) {
error(INFO, "cannot hash task_struct entries\n");
if (!(tt->flags & TASK_INIT_DONE))
clean_exit(1);
error(INFO, "using stale task_structs\n");
FREEBUF(pid_hash);
return;
}
/*
* Get the idle threads first.
*/
cnt = 0;
for (i = 0; i < kt->cpus; i++) {
if (!tt->idle_threads[i])
continue;
if (hq_enter(tt->idle_threads[i]))
cnt++;
else
error(WARNING, "%sduplicate idle tasks?\n",
DUMPFILE() ? "\n" : "");
}
for (i = 0; i < len; i++) {
if (!pid_hash[i])
continue;
kpp = pid_hash[i];
upid = pid_hash[i] - OFFSET(upid_pid_chain);
chained = 0;
do_chained:
if (!readmem(upid, KVADDR, nodebuf, SIZE(upid),
"pid_hash upid", RETURN_ON_ERROR|QUIET)) {
error(INFO, "\npid_hash[%d]: cannot read pid_hash upid\n", i);
if (DUMPFILE())
continue;
hq_close();
retries++;
goto retry_pid_hash;
}
pnext = ULONG(nodebuf + OFFSET(upid_pid_chain) + OFFSET(hlist_node_next));
pprev = ULONG(nodebuf + OFFSET(upid_pid_chain) + OFFSET(hlist_node_pprev));
upid_nr = UINT(nodebuf + OFFSET(upid_nr));
upid_ns = ULONG(nodebuf + OFFSET(upid_ns));
/*
* Use init_pid_ns level 0 (PIDTYPE_PID).
*/
if (upid_ns != tt->init_pid_ns) {
if (!accessible(upid_ns)) {
error(INFO,
"%spid_hash[%d]: invalid upid.ns: %lx\n",
DUMPFILE() ? "\n" : "",
i, upid_ns);
continue;
}
goto chain_next;
}
pid = upid - OFFSET(pid_numbers);
if (!readmem(pid + OFFSET(pid_tasks), KVADDR, &pid_tasks_0,
sizeof(void *), "pid tasks", RETURN_ON_ERROR|QUIET)) {
error(INFO, "\npid_hash[%d]: cannot read pid.tasks[0]\n", i);
if (DUMPFILE())
continue;
hq_close();
retries++;
goto retry_pid_hash;
}
if (pid_tasks_0 == 0)
goto chain_next;
next = pid_tasks_0 - OFFSET(task_struct_pids);
if (CRASHDEBUG(1)) {
if (chained)
console(" %lx upid: %lx nr: %d pid: %lx\n"
" pnext/pprev: %.*lx/%lx task: %lx\n",
kpp, upid, upid_nr, pid, VADDR_PRLEN, pnext, pprev, next);
else
console("pid_hash[%4d]: %lx upid: %lx nr: %d pid: %lx\n"
" pnext/pprev: %.*lx/%lx task: %lx\n",
i, kpp, upid, upid_nr, pid, VADDR_PRLEN, pnext, pprev, next);
}
if (!IS_TASK_ADDR(next)) {
error(INFO, "%spid_hash[%d]: invalid task address: %lx\n",
DUMPFILE() ? "\n" : "", i, next);
if (DUMPFILE())
break;
hq_close();
retries++;
goto retry_pid_hash;
}
if (!is_idle_thread(next) && !hq_enter(next)) {
error(INFO, "%spid_hash[%d]: duplicate task: %lx\n",
DUMPFILE() ? "\n" : "", i, next);
if (DUMPFILE())
break;
hq_close();
retries++;
goto retry_pid_hash;
}
cnt++;
chain_next:
if (pnext) {
if (chained >= tt->max_tasks) {
error(INFO,
"%spid_hash[%d]: corrupt/invalid upid chain\n",
DUMPFILE() ? "\n" : "", i);
continue;
}
kpp = pnext;
upid = pnext - OFFSET(upid_pid_chain);
chained++;
goto do_chained;
}
}
if (cnt > tt->max_tasks) {
tt->max_tasks = cnt + TASK_SLUSH;
allocate_task_space(tt->max_tasks);
hq_close();
if (!DUMPFILE())
retries++;
goto retry_pid_hash;
}
BZERO(tt->task_local, tt->max_tasks * sizeof(void *));
cnt = retrieve_list((ulong *)tt->task_local, cnt);
hq_close();
clear_task_cache();
for (i = 0, tlp = (ulong *)tt->task_local,
tt->running_tasks = 0, tc = tt->context_array;
i < tt->max_tasks; i++, tlp++) {
if (!(*tlp))
continue;
if (!IS_TASK_ADDR(*tlp)) {
error(WARNING,
"%sinvalid task address found in task list: %lx\n",
DUMPFILE() ? "\n" : "", *tlp);
if (DUMPFILE())
continue;
retries++;
goto retry_pid_hash;
}
if (task_exists(*tlp)) {
error(WARNING,
"%sduplicate task address found in task list: %lx\n",
DUMPFILE() ? "\n" : "", *tlp);
if (DUMPFILE())
continue;
retries++;
goto retry_pid_hash;
}
if (!(tp = fill_task_struct(*tlp))) {
if (DUMPFILE())
continue;
retries++;
goto retry_pid_hash;
}
if (store_context(tc, *tlp, tp)) {
tc++;
tt->running_tasks++;
}
}
FREEBUF(pid_hash);
FREEBUF(nodebuf);
please_wait_done();
if (ACTIVE() && (tt->flags & TASK_INIT_DONE))
refresh_context(curtask, curpid);
tt->retries = MAX(tt->retries, retries);
}
static void
refresh_active_task_table(void)
{
int i;
char *tp;
int cnt;
struct task_context *tc;
ulong curtask;
ulong curpid;
ulong retries;
ulong *tlp;
if (DUMPFILE() && (tt->flags & TASK_INIT_DONE)) /* impossible */
return;
if (DUMPFILE()) {
please_wait("gathering task table data");
if (!symbol_exists("panic_threads"))
tt->flags |= POPULATE_PANIC;
}
if (ACTIVE() && !(tt->flags & TASK_REFRESH))
return;
curtask = NO_TASK;
curpid = NO_PID;
retries = 0;
get_active_set();
/*
* The current task's task_context entry may change,
* or the task may not even exist anymore.
*/
if (ACTIVE() && (tt->flags & TASK_INIT_DONE)) {
curtask = CURRENT_TASK();
curpid = CURRENT_PID();
}
retry_active:
if (!hq_open()) {
error(INFO, "cannot hash task_struct entries\n");
if (!(tt->flags & TASK_INIT_DONE))
clean_exit(1);
error(INFO, "using stale task_structs\n");
return;
}
/*
* Get the active tasks.
*/
cnt = 0;
for (i = 0; i < kt->cpus; i++) {
if (hq_enter(tt->active_set[i]))
cnt++;
else
error(WARNING, "%sduplicate active tasks?\n",
DUMPFILE() ? "\n" : "");
}
BZERO(tt->task_local, tt->max_tasks * sizeof(void *));
cnt = retrieve_list((ulong *)tt->task_local, cnt);
hq_close();
clear_task_cache();
for (i = 0, tlp = (ulong *)tt->task_local,
tt->running_tasks = 0, tc = tt->context_array;
i < tt->max_tasks; i++, tlp++) {
if (!(*tlp))
continue;
if (!IS_TASK_ADDR(*tlp)) {
error(WARNING,
"%sinvalid task address found in task list: %lx\n",
DUMPFILE() ? "\n" : "", *tlp);
if (DUMPFILE())
continue;
retries++;
goto retry_active;
}
if (task_exists(*tlp)) {
error(WARNING,
"%sduplicate task address found in task list: %lx\n",
DUMPFILE() ? "\n" : "", *tlp);
if (DUMPFILE())
continue;
retries++;
goto retry_active;
}
if (!(tp = fill_task_struct(*tlp))) {
if (DUMPFILE())
continue;
retries++;
goto retry_active;
}
if (store_context(tc, *tlp, tp)) {
tc++;
tt->running_tasks++;
} else if (DUMPFILE())
error(WARNING, "corrupt/invalid active task: %lx\n",
*tlp);
}
if (!tt->running_tasks) {
if (DUMPFILE())
error(FATAL, "cannot determine any active tasks!\n");
retries++;
goto retry_active;
}
please_wait_done();
if (ACTIVE() && (tt->flags & TASK_INIT_DONE))
refresh_context(curtask, curpid);
tt->retries = MAX(tt->retries, retries);
}
/*
* Fill a task_context structure with the data from a task. If a NULL
* task_context pointer is passed in, use the next available one.
*/
static struct task_context *
store_context(struct task_context *tc, ulong task, char *tp)
{
pid_t *pid_addr, *tgid_addr;
char *comm_addr;
int *processor_addr;
ulong *parent_addr;
ulong *mm_addr;
int has_cpu;
int do_verify;
struct tgid_context *tg;
processor_addr = NULL;
if (tt->refresh_task_table == refresh_fixed_task_table)
do_verify = 1;
else if (tt->refresh_task_table == refresh_pid_hash_task_table)
do_verify = 2;
else if (tt->refresh_task_table == refresh_hlist_task_table)
do_verify = 2;
else if (tt->refresh_task_table == refresh_hlist_task_table_v2)
do_verify = 2;
else if (tt->refresh_task_table == refresh_hlist_task_table_v3)
do_verify = 2;
else if (tt->refresh_task_table == refresh_active_task_table)
do_verify = 2;
else
do_verify = 0;
if (!tc)
tc = tt->context_array + tt->running_tasks;
pid_addr = (pid_t *)(tp + OFFSET(task_struct_pid));
tgid_addr = (pid_t *)(tp + OFFSET(task_struct_tgid));
comm_addr = (char *)(tp + OFFSET(task_struct_comm));
if (tt->flags & THREAD_INFO) {
tc->thread_info = ULONG(tp + OFFSET(task_struct_thread_info));
fill_thread_info(tc->thread_info);
processor_addr = (int *) (tt->thread_info +
OFFSET(thread_info_cpu));
} else if (VALID_MEMBER(task_struct_processor))
processor_addr = (int *) (tp + OFFSET(task_struct_processor));
else if (VALID_MEMBER(task_struct_cpu))
processor_addr = (int *) (tp + OFFSET(task_struct_cpu));
if (VALID_MEMBER(task_struct_p_pptr))
parent_addr = (ulong *)(tp + OFFSET(task_struct_p_pptr));
else
parent_addr = (ulong *)(tp + OFFSET(task_struct_parent));
mm_addr = (ulong *)(tp + OFFSET(task_struct_mm));
has_cpu = task_has_cpu(task, tp);
tc->pid = (ulong)(*pid_addr);
strlcpy(tc->comm, comm_addr, TASK_COMM_LEN);
if (machine_type("SPARC64"))
tc->processor = *(unsigned short *)processor_addr;
else
tc->processor = *processor_addr;
tc->ptask = *parent_addr;
tc->mm_struct = *mm_addr;
tc->task = task;
tc->tc_next = NULL;
/*
* Fill a tgid_context structure with the data from
* the incoming task.
*/
tg = tt->tgid_array + tt->running_tasks;
tg->tgid = *tgid_addr;
tg->task = task;
if (do_verify && !verify_task(tc, do_verify)) {
error(INFO, "invalid task address: %lx\n", tc->task);
BZERO(tc, sizeof(struct task_context));
return NULL;
}
if (has_cpu && (tt->flags & POPULATE_PANIC))
tt->panic_threads[tc->processor] = tc->task;
return tc;
}
/*
* The current context may have moved to a new spot in the task table
* or have exited since the last command. If it still exists, reset its
* new position. If it doesn't exist, set the context back to the initial
* crash context. If necessary, complain and show the restored context.
*/
static void
refresh_context(ulong curtask, ulong curpid)
{
ulong value, complain;
struct task_context *tc;
if (task_exists(curtask) && pid_exists(curpid)) {
set_context(curtask, NO_PID);
} else {
set_context(tt->this_task, NO_PID);
complain = TRUE;
if (STREQ(args[0], "set") && (argcnt == 2) &&
IS_A_NUMBER(args[1])) {
switch (str_to_context(args[optind], &value, &tc))
{
case STR_PID:
case STR_TASK:
complain = FALSE;
break;
case STR_INVALID:
complain = TRUE;
break;
}
}
if (complain) {
error(INFO, "current context no longer exists -- "
"restoring \"%s\" context:\n\n",
pc->program_name);
show_context(CURRENT_CONTEXT());
fprintf(fp, "\n");
}
}
}
/*
* Sort the task_context array by PID number; for PID 0, sort by processor.
*/
void
sort_context_array(void)
{
ulong curtask;
curtask = CURRENT_TASK();
qsort((void *)tt->context_array, (size_t)tt->running_tasks,
sizeof(struct task_context), sort_by_pid);
set_context(curtask, NO_PID);
}
static int
sort_by_pid(const void *arg1, const void *arg2)
{
struct task_context *t1, *t2;
t1 = (struct task_context *)arg1;
t2 = (struct task_context *)arg2;
if ((t1->pid == 0) && (t2->pid == 0))
return (t1->processor < t2->processor ? -1 :
t1->processor == t2->processor ? 0 : 1);
else
return (t1->pid < t2->pid ? -1 :
t1->pid == t2->pid ? 0 : 1);
}
static int
sort_by_last_run(const void *arg1, const void *arg2)
{
ulong task_last_run_stamp(ulong);
struct task_context *t1, *t2;
ulonglong lr1, lr2;
t1 = (struct task_context *)arg1;
t2 = (struct task_context *)arg2;
lr1 = task_last_run(t1->task);
lr2 = task_last_run(t2->task);
return (lr2 < lr1 ? -1 :
lr2 == lr1 ? 0 : 1);
}
static void
sort_context_array_by_last_run(void)
{
ulong curtask;
curtask = CURRENT_TASK();
qsort((void *)tt->context_array, (size_t)tt->running_tasks,
sizeof(struct task_context), sort_by_last_run);
set_context(curtask, NO_PID);
}
/*
* Set the tgid_context array by tgid number.
*/
void
sort_tgid_array(void)
{
if (VALID_MEMBER(mm_struct_rss) || (!VALID_MEMBER(task_struct_rss_stat)))
return;
qsort((void *)tt->tgid_array, (size_t)tt->running_tasks,
sizeof(struct tgid_context), sort_by_tgid);
tt->last_tgid = tt->tgid_array;
}
int
sort_by_tgid(const void *arg1, const void *arg2)
{
struct tgid_context *t1, *t2;
t1 = (struct tgid_context *)arg1;
t2 = (struct tgid_context *)arg2;
return (t1->tgid < t2->tgid ? -1 :
t1->tgid == t2->tgid ? 0 : 1);
}
/*
* Keep a stash of the last task_struct accessed. Chances are it will
* be hit several times before the next task is accessed.
*/
char *
fill_task_struct(ulong task)
{
if (XEN_HYPER_MODE())
return NULL;
if (!IS_LAST_TASK_READ(task)) {
if (!readmem(task, KVADDR, tt->task_struct,
SIZE(task_struct), "fill_task_struct",
ACTIVE() ? (RETURN_ON_ERROR|QUIET) : RETURN_ON_ERROR)) {
tt->last_task_read = 0;
return NULL;
}
}
tt->last_task_read = task;
return(tt->task_struct);
}
/*
* Keep a stash of the last thread_info struct accessed. Chances are it will
* be hit several times before the next task is accessed.
*/
char *
fill_thread_info(ulong thread_info)
{
if (!IS_LAST_THREAD_INFO_READ(thread_info)) {
if (!readmem(thread_info, KVADDR, tt->thread_info,
SIZE(thread_info), "fill_thread_info",
ACTIVE() ? (RETURN_ON_ERROR|QUIET) : RETURN_ON_ERROR)) {
tt->last_thread_info_read = 0;
return NULL;
}
}
tt->last_thread_info_read = thread_info;
return(tt->thread_info);
}
/*
* Used by back_trace(), copy the complete kernel stack into a local buffer
* and fill the task_struct buffer, dealing with possible future separation
* of task_struct and stack and/or cache coloring of stack top.
*/
void
fill_stackbuf(struct bt_info *bt)
{
if (!bt->stackbuf) {
bt->stackbuf = GETBUF(bt->stacktop - bt->stackbase);
if (!readmem(bt->stackbase, KVADDR, bt->stackbuf,
bt->stacktop - bt->stackbase,
"stack contents", RETURN_ON_ERROR))
error(FATAL, "read of stack at %lx failed\n",
bt->stackbase);
}
if (XEN_HYPER_MODE())
return;
if (!IS_LAST_TASK_READ(bt->task)) {
if (bt->stackbase == bt->task) {
BCOPY(bt->stackbuf, tt->task_struct, SIZE(task_struct));
tt->last_task_read = bt->task;
} else
fill_task_struct(bt->task);
}
}
/*
* Keeping the task_struct info intact, alter the contents of the already
* allocated local copy of a kernel stack, for things like IRQ stacks or
* non-standard eframe searches. The caller must change the stackbase
* and stacktop values.
*/
void
alter_stackbuf(struct bt_info *bt)
{
if (!readmem(bt->stackbase, KVADDR, bt->stackbuf,
bt->stacktop - bt->stackbase, "stack contents", RETURN_ON_ERROR))
error(FATAL, "read of stack at %lx failed\n", bt->stackbase);
}
/*
* In the same vein as fill_task_struct(), keep a stash of the mm_struct
* of a task.
*/
char *fill_mm_struct(ulong mm)
{
if (!IS_LAST_MM_READ(mm)) {
if (!readmem(mm, KVADDR, tt->mm_struct,
SIZE(mm_struct), "fill_mm_struct",
ACTIVE() ? (RETURN_ON_ERROR|QUIET) : RETURN_ON_ERROR)) {
tt->last_mm_read = 0;
return NULL;
}
}
tt->last_mm_read = mm;
return(tt->mm_struct);
}
/*
* If active, clear out references to the last task and mm_struct read.
*/
void
clear_task_cache(void)
{
if (ACTIVE())
tt->last_task_read = tt->last_mm_read = 0;
}
/*
* Shorthand command to dump the current context's task_struct, or if
* pid or task arguments are entered, the task_structs of the targets.
* References to structure members can be given to pare down the output,
* which are put in a comma-separated list.
*/
void
cmd_task(void)
{
int c, tcnt, bogus;
unsigned int radix;
ulong value;
struct reference *ref;
struct task_context *tc;
ulong *tasklist;
char *memberlist;
tasklist = (ulong *)GETBUF((MAXARGS+NR_CPUS)*sizeof(ulong));
ref = (struct reference *)GETBUF(sizeof(struct reference));
memberlist = GETBUF(BUFSIZE);
ref->str = memberlist;
radix = 0;
while ((c = getopt(argcnt, args, "xdhR:")) != EOF) {
switch(c)
{
case 'h':
case 'x':
if (radix == 10)
error(FATAL,
"-d and -x are mutually exclusive\n");
radix = 16;
break;
case 'd':
if (radix == 16)
error(FATAL,
"-d and -x are mutually exclusive\n");
radix = 10;
break;
case 'R':
if (strlen(ref->str))
strcat(ref->str, ",");
strcat(ref->str, optarg);
break;
default:
argerrs++;
break;
}
}
if (argerrs)
cmd_usage(pc->curcmd, SYNOPSIS);
tcnt = bogus = 0;
while (args[optind]) {
if (IS_A_NUMBER(args[optind])) {
switch (str_to_context(args[optind], &value, &tc))
{
case STR_PID:
for (tc = pid_to_context(value); tc;
tc = tc->tc_next)
tasklist[tcnt++] = tc->task;
break;
case STR_TASK:
tasklist[tcnt++] = value;
break;
case STR_INVALID:
bogus++;
error(INFO, "invalid task or pid value: %s\n\n",
args[optind]);
break;
}
} else if (strstr(args[optind], ",") ||
MEMBER_EXISTS("task_struct", args[optind])) {
if (strlen(ref->str))
strcat(ref->str, ",");
strcat(ref->str, args[optind]);
} else if (strstr(args[optind], ".") || strstr(args[optind], "[")) {
if (strlen(ref->str))
strcat(ref->str, ",");
strcat(ref->str, args[optind]);
} else
error(INFO,
"invalid task, pid, or task_struct member: %s\n\n",
args[optind]);
optind++;
}
if (!tcnt && !bogus)
tasklist[tcnt++] = CURRENT_TASK();
for (c = 0; c < tcnt; c++)
do_task(tasklist[c], 0, strlen(ref->str) ? ref : NULL, radix);
}
/*
* Do the work for the task command.
*/
void
do_task(ulong task, ulong flags, struct reference *ref, unsigned int radix)
{
struct task_context *tc;
tc = task_to_context(task);
if (ref) {
print_task_header(fp, tc, 0);
task_struct_member(tc, radix, ref);
} else {
if (!(flags & FOREACH_TASK))
print_task_header(fp, tc, 0);
dump_struct("task_struct", task, radix);
if (tt->flags & THREAD_INFO) {
fprintf(fp, "\n");
dump_struct("thread_info", tc->thread_info, radix);
}
}
fprintf(fp, "\n");
}
/*
* Search the task_struct for the referenced field.
*/
static void
task_struct_member(struct task_context *tc, unsigned int radix, struct reference *ref)
{
int i;
int argcnt;
char *arglist[MAXARGS];
char *refcopy;
struct datatype_member dm;
if ((count_chars(ref->str, ',')+1) > MAXARGS) {
error(INFO,
"too many -R arguments in comma-separated list!\n");
return;
}
refcopy = GETBUF(strlen(ref->str)+1);
strcpy(refcopy, ref->str);
replace_string(refcopy, ",", ' ');
argcnt = parse_line(refcopy, arglist);
open_tmpfile();
dump_struct("task_struct", tc->task, radix);
if (tt->flags & THREAD_INFO)
dump_struct("thread_info", tc->thread_info, radix);
for (i = 0; i < argcnt; i++) {
if (count_chars(arglist[i], '.') || count_chars(arglist[i], '[')) {
dm.member = arglist[i];
parse_for_member_extended(&dm, 0);
} else {
if (!MEMBER_EXISTS("task_struct", arglist[i]) &&
!MEMBER_EXISTS("thread_info", arglist[i]))
error(INFO, "%s: not a task_struct or "
"thread_info member\n", arglist[i]);
parse_task_thread(1, &arglist[i], tc);
}
}
close_tmpfile();
FREEBUF(refcopy);
}
static void
parse_task_thread(int argcnt, char *arglist[], struct task_context *tc) {
char buf[BUFSIZE];
char lookfor1[BUFSIZE];
char lookfor2[BUFSIZE];
char lookfor3[BUFSIZE];
int i;
rewind(pc->tmpfile);
BZERO(lookfor1, BUFSIZE);
BZERO(lookfor2, BUFSIZE);
BZERO(lookfor3, BUFSIZE);
while (fgets(buf, BUFSIZE, pc->tmpfile)) {
if (strlen(lookfor2)) {
fprintf(pc->saved_fp, "%s", buf);
if (STRNEQ(buf, lookfor2))
BZERO(lookfor2, BUFSIZE);
continue;
}
if (strlen(lookfor3)) {
fprintf(pc->saved_fp, "%s", buf);
if (strstr(buf, lookfor3))
BZERO(lookfor3, BUFSIZE);
continue;
}
for (i = 0; i < argcnt; i++) {
BZERO(lookfor1, BUFSIZE);
BZERO(lookfor2, BUFSIZE);
BZERO(lookfor3, BUFSIZE);
sprintf(lookfor1, " %s = ", arglist[i]);
if (STRNEQ(buf, lookfor1)) {
fprintf(pc->saved_fp, "%s", buf);
if (strstr(buf, "{{\n"))
sprintf(lookfor2, " }},");
else if (strstr(buf, "{\n"))
sprintf(lookfor2, " },");
else if (strstr(buf, "{"))
sprintf(lookfor3, "},");
break;
}
}
}
}
static char *ps_exclusive =
"-a, -t, -c, -p, -g, -l, -m, -S and -r flags are all mutually-exclusive\n";
static void
check_ps_exclusive(ulong flag, ulong thisflag)
{
if (flag & (PS_EXCLUSIVE & ~thisflag))
error(FATAL, ps_exclusive);
}
/*
* Display ps-like data for all tasks, or as specified by pid, task, or
* command-name arguments.
*/
void
cmd_ps(void)
{
int c, ac;
ulong flag;
ulong value;
static struct psinfo psinfo;
struct task_context *tc;
char *cpuspec, *p;
BZERO(&psinfo, sizeof(struct psinfo));
cpuspec = NULL;
flag = 0;
while ((c = getopt(argcnt, args, "SgstcpkuGlmarC:")) != EOF) {
switch(c)
{
case 'k':
if (flag & PS_USER)
error(FATAL,
"-u and -k are mutually exclusive\n");
flag |= PS_KERNEL;
break;
case 'u':
if (flag & PS_KERNEL)
error(FATAL,
"-u and -k are mutually exclusive\n");
flag |= PS_USER;
break;
case 'G':
if (flag & PS_GROUP)
break;
else if (hq_open())
flag |= PS_GROUP;
else
error(INFO, "cannot hash thread group tasks\n");
break;
/*
* The a, t, c, p, g, l and r flags are all mutually-exclusive.
*/
case 'g':
check_ps_exclusive(flag, PS_TGID_LIST);
flag |= PS_TGID_LIST;
break;
case 'a':
check_ps_exclusive(flag, PS_ARGV_ENVP);
flag |= PS_ARGV_ENVP;
break;
case 't':
check_ps_exclusive(flag, PS_TIMES);
flag |= PS_TIMES;
break;
case 'c':
check_ps_exclusive(flag, PS_CHILD_LIST);
flag |= PS_CHILD_LIST;
break;
case 'p':
check_ps_exclusive(flag, PS_PPID_LIST);
flag |= PS_PPID_LIST;
break;
case 'm':
if (INVALID_MEMBER(task_struct_last_run) &&
INVALID_MEMBER(task_struct_timestamp) &&
INVALID_MEMBER(sched_info_last_arrival)) {
error(INFO,
"last-run timestamps do not exist in this kernel\n");
argerrs++;
break;
}
if (INVALID_MEMBER(rq_timestamp))
option_not_supported(c);
check_ps_exclusive(flag, PS_MSECS);
flag |= PS_MSECS;
break;
case 'l':
if (INVALID_MEMBER(task_struct_last_run) &&
INVALID_MEMBER(task_struct_timestamp) &&
INVALID_MEMBER(sched_info_last_arrival)) {
error(INFO,
"last-run timestamps do not exist in this kernel\n");
argerrs++;
break;
}
check_ps_exclusive(flag, PS_LAST_RUN);
flag |= PS_LAST_RUN;
break;
case 's':
flag |= PS_KSTACKP;
break;
case 'r':
check_ps_exclusive(flag, PS_RLIMIT);
flag |= PS_RLIMIT;
break;
case 'S':
check_ps_exclusive(flag, PS_SUMMARY);
flag |= PS_SUMMARY;
break;
case 'C':
cpuspec = optarg;
psinfo.cpus = get_cpumask_buf();
make_cpumask(cpuspec, psinfo.cpus, FAULT_ON_ERROR, NULL);
break;
default:
argerrs++;
break;
}
}
if (argerrs)
cmd_usage(pc->curcmd, SYNOPSIS);
if (flag & (PS_LAST_RUN|PS_MSECS))
sort_context_array_by_last_run();
else if (psinfo.cpus) {
error(INFO, "-C option is only applicable with -l and -m\n");
goto bailout;
}
if (!args[optind]) {
show_ps(PS_SHOW_ALL|flag, &psinfo);
return;
}
if (flag & PS_SUMMARY)
error(FATAL, "-S option takes no arguments\n");
if (psinfo.cpus)
error(INFO,
"-C option is not applicable with specified tasks\n");
ac = 0;
while (args[optind]) {
if (IS_A_NUMBER(args[optind])) {
switch (str_to_context(args[optind], &value, &tc))
{
case STR_PID:
psinfo.pid[ac] = value;
psinfo.task[ac] = NO_TASK;
psinfo.type[ac] = PS_BY_PID;
flag |= PS_BY_PID;
break;
case STR_TASK:
psinfo.task[ac] = value;
psinfo.pid[ac] = NO_PID;
psinfo.type[ac] = PS_BY_TASK;
flag |= PS_BY_TASK;
break;
case STR_INVALID:
error(INFO, "invalid task or pid value: %s\n\n",
args[optind]);
break;
}
ac++;
} else if (SINGLE_QUOTED_STRING(args[optind])) {
/*
* Regular expression is exclosed within "'" character.
* The args[optind] string may not be modified, so a copy
* is duplicated.
*/
if (psinfo.regexs == MAX_PS_ARGS)
error(INFO, "too many expressions specified!\n");
else {
p = strdup(&args[optind][1]);
LASTCHAR(p) = NULLCHAR;
if (regcomp(&psinfo.regex_data[psinfo.regexs].regex,
p, REG_EXTENDED|REG_NOSUB)) {
error(INFO,
"invalid regular expression: %s\n", p);
free(p);
goto bailout;
}
psinfo.regex_data[psinfo.regexs].pattern = p;
if (psinfo.regexs++ == 0) {
pc->cmd_cleanup_arg = (void *)&psinfo;
pc->cmd_cleanup = ps_cleanup;
}
psinfo.type[ac] = PS_BY_REGEX;
flag |= PS_BY_REGEX;
ac++;
}
optind++;
continue;
} else {
psinfo.pid[ac] = NO_PID;
psinfo.task[ac] = NO_TASK;
p = args[optind][0] == '\\' ?
&args[optind][1] : args[optind];
strlcpy(psinfo.comm[ac], p, TASK_COMM_LEN);
psinfo.type[ac] = PS_BY_CMD;
flag |= PS_BY_CMD;
ac++;
}
optind++;
}
psinfo.argc = ac;
show_ps(flag, &psinfo);
bailout:
ps_cleanup((void *)&psinfo);
}
/*
* Clean up regex buffers and pattern strings.
*/
static void
ps_cleanup(void *arg)
{
int i;
struct psinfo *ps;
pc->cmd_cleanup = NULL;
pc->cmd_cleanup_arg = NULL;
ps = (struct psinfo *)arg;
for (i = 0; i < ps->regexs; i++) {
regfree(&ps->regex_data[i].regex);
free(ps->regex_data[i].pattern);
}
if (ps->cpus)
FREEBUF(ps->cpus);
}
/*
* Do the work requested by cmd_ps().
*/
static void
show_ps_data(ulong flag, struct task_context *tc, struct psinfo *psi)
{
struct task_mem_usage task_mem_usage, *tm;
char buf1[BUFSIZE];
char buf2[BUFSIZE];
char buf3[BUFSIZE];
ulong tgid;
int task_active;
if ((flag & PS_USER) && is_kernel_thread(tc->task))
return;
if ((flag & PS_KERNEL) && !is_kernel_thread(tc->task))
return;
if (flag & PS_GROUP) {
if (flag & (PS_LAST_RUN|PS_MSECS))
error(FATAL, "-G not supported with -%c option\n",
flag & PS_LAST_RUN ? 'l' : 'm');
tgid = task_tgid(tc->task);
if (tc->pid != tgid) {
if (pc->curcmd_flags & TASK_SPECIFIED) {
if (!(tc = tgid_to_context(tgid)))
return;
if (hq_entry_exists((ulong)tc))
return;
hq_enter((ulong)tc);
} else
return;
} else {
if (hq_entry_exists((ulong)tc))
return;
hq_enter((ulong)tc);
}
}
if (flag & PS_PPID_LIST) {
parent_list(tc->task);
fprintf(fp, "\n");
return;
}
if (flag & PS_CHILD_LIST) {
child_list(tc->task);
fprintf(fp, "\n");
return;
}
if (flag & (PS_LAST_RUN)) {
show_last_run(tc, psi);
return;
}
if (flag & (PS_MSECS)) {
show_milliseconds(tc, psi);
return;
}
if (flag & PS_ARGV_ENVP) {
show_task_args(tc);
return;
}
if (flag & PS_RLIMIT) {
show_task_rlimit(tc);
return;
}
if (flag & PS_TGID_LIST) {
show_tgid_list(tc->task);
return;
}
tm = &task_mem_usage;
get_task_mem_usage(tc->task, tm);
task_active = is_task_active(tc->task);
if (task_active) {
if (hide_offline_cpu(tc->processor))
fprintf(fp, "- ");
else
fprintf(fp, "> ");
} else
fprintf(fp, " ");
fprintf(fp, "%5ld %5ld %2s %s %3s",
tc->pid, task_to_pid(tc->ptask),
task_cpu(tc->processor, buf2, !VERBOSE),
task_pointer_string(tc, flag & PS_KSTACKP, buf3),
task_state_string(tc->task, buf1, !VERBOSE));
pad_line(fp, strlen(buf1) > 3 ? 1 : 2, ' ');
sprintf(buf1, "%.1f", tm->pct_physmem);
if (strlen(buf1) == 3)
mkstring(buf1, 4, CENTER|RJUST, NULL);
fprintf(fp, "%s ", buf1);
fprintf(fp, "%7ld ", (tm->total_vm * PAGESIZE())/1024);
fprintf(fp, "%6ld ", (tm->rss * PAGESIZE())/1024);
if (is_kernel_thread(tc->task))
fprintf(fp, "[%s]\n", tc->comm);
else
fprintf(fp, "%s\n", tc->comm);
}
static void
show_ps(ulong flag, struct psinfo *psi)
{
int i, ac;
struct task_context *tc;
int print;
char buf[BUFSIZE];
if (!(flag & (PS_EXCLUSIVE|PS_NO_HEADER)))
fprintf(fp,
" PID PPID CPU %s ST %%MEM VSZ RSS COMM\n",
flag & PS_KSTACKP ?
mkstring(buf, VADDR_PRLEN, CENTER|RJUST, "KSTACKP") :
mkstring(buf, VADDR_PRLEN, CENTER, "TASK"));
if (flag & PS_SHOW_ALL) {
if (flag & PS_TIMES) {
show_task_times(NULL, flag);
return;
}
if (flag & PS_SUMMARY) {
show_ps_summary(flag);
return;
}
if (psi->cpus) {
show_ps_data(flag, NULL, psi);
return;
}
tc = FIRST_CONTEXT();
for (i = 0; i < RUNNING_TASKS(); i++, tc++)
show_ps_data(flag, tc, NULL);
return;
}
pc->curcmd_flags |= TASK_SPECIFIED;
tc = FIRST_CONTEXT();
for (i = 0; i < RUNNING_TASKS(); i++, tc++) {
for (ac = 0; ac < psi->argc; ac++) {
print = FALSE;
switch(psi->type[ac])
{
case PS_BY_PID:
if (tc->pid == psi->pid[ac])
print = TRUE;
break;
case PS_BY_TASK:
if ((tc->task == psi->task[ac]))
print = TRUE;
break;
case PS_BY_CMD:
if (STREQ(tc->comm, psi->comm[ac])) {
if (flag & (PS_TGID_LIST|PS_GROUP)) {
if (tc->pid == task_tgid(tc->task))
print = TRUE;
else
print = FALSE;
} else
print = TRUE;
}
break;
case PS_BY_REGEX:
if (regexec(&psi->regex_data[ac].regex,
tc->comm, 0, NULL, 0) == 0) {
if (flag & (PS_TGID_LIST|PS_GROUP)) {
if (tc->pid == task_tgid(tc->task))
print = TRUE;
else
print = FALSE;
} else
print = TRUE;
}
break;
}
if (print) {
if (flag & PS_TIMES)
show_task_times(tc, flag);
else
show_ps_data(flag, tc, NULL);
}
}
}
}
static void
show_ps_summary(ulong flag)
{
int i, s;
struct task_context *tc;
char buf[BUFSIZE];
#define MAX_STATES 20
struct ps_state {
long cnt;
char string[3];
} ps_state[MAX_STATES];
if (flag & (PS_USER|PS_KERNEL|PS_GROUP))
error(FATAL, "-S option cannot be used with other options\n");
for (s = 0; s < MAX_STATES; s++)
ps_state[s].cnt = 0;
tc = FIRST_CONTEXT();
for (i = 0; i < RUNNING_TASKS(); i++, tc++) {
task_state_string(tc->task, buf, !VERBOSE);
for (s = 0; s < MAX_STATES; s++) {
if (ps_state[s].cnt &&
STREQ(ps_state[s].string, buf)) {
ps_state[s].cnt++;
break;
}
if (ps_state[s].cnt == 0) {
strcpy(ps_state[s].string, buf);
ps_state[s].cnt++;
break;
}
}
}
for (s = 0; s < MAX_STATES; s++) {
if (ps_state[s].cnt)
fprintf(fp,
" %s: %ld\n", ps_state[s].string, ps_state[s].cnt);
}
}
/*
* Display the task preceded by the last_run stamp and its
* current state.
*/
static void
show_last_run(struct task_context *tc, struct psinfo *psi)
{
int i, c, others;
struct task_context *tcp;
char format[15];
char buf[BUFSIZE];
tcp = FIRST_CONTEXT();
sprintf(buf, pc->output_radix == 10 ? "%lld" : "%llx",
task_last_run(tcp->task));
c = strlen(buf);
sprintf(format, "[%c%dll%c] ", '%', c,
pc->output_radix == 10 ? 'u' : 'x');
if (psi) {
for (c = others = 0; c < kt->cpus; c++) {
if (!NUM_IN_BITMAP(psi->cpus, c))
continue;
fprintf(fp, "%sCPU: %d",
others++ ? "\n" : "", c);
if (hide_offline_cpu(c)) {
fprintf(fp, " [OFFLINE]\n");
continue;
} else
fprintf(fp, "\n");
tcp = FIRST_CONTEXT();
for (i = 0; i < RUNNING_TASKS(); i++, tcp++) {
if (tcp->processor != c)
continue;
fprintf(fp, format, task_last_run(tcp->task));
fprintf(fp, "[%s] ",
task_state_string(tcp->task, buf, !VERBOSE));
print_task_header(fp, tcp, FALSE);
}
}
} else if (tc) {
fprintf(fp, format, task_last_run(tc->task));
fprintf(fp, "[%s] ", task_state_string(tc->task, buf, !VERBOSE));
print_task_header(fp, tc, FALSE);
} else {
tcp = FIRST_CONTEXT();
for (i = 0; i < RUNNING_TASKS(); i++, tcp++) {
fprintf(fp, format, task_last_run(tcp->task));
fprintf(fp, "[%s] ", task_state_string(tcp->task, buf, !VERBOSE));
print_task_header(fp, tcp, FALSE);
}
}
}
/*
* Translate a value in nanoseconds into a string showing days,
* hours, minutes, seconds and milliseconds.
*/
static char *
translate_nanoseconds(ulonglong value, char *buf)
{
ulong days, hours, mins, secs, ms;
value = value / 1000000L;
ms = value % 1000L;
value = value / 1000L;
secs = value % 60L;
value = value / 60L;
mins = value % 60L;
value = value / 60L;
hours = value % 24L;
value = value / 24L;
days = value;
sprintf(buf, "%ld %02ld:%02ld:%02ld.%03ld",
days, hours, mins, secs, ms);
return buf;
}
/*
* Display the task preceded by a per-rq translation of the
* sched_info.last_arrival and its current state.
*/
static void
show_milliseconds(struct task_context *tc, struct psinfo *psi)
{
int i, c, others, days, max_days;
struct task_context *tcp;
char format[15];
char buf[BUFSIZE];
struct syment *rq_sp;
ulong runq;
ulonglong rq_clock;
long long delta;
if (!(rq_sp = per_cpu_symbol_search("per_cpu__runqueues")))
error(FATAL, "cannot determine per-cpu runqueue address\n");
tcp = FIRST_CONTEXT();
sprintf(buf, pc->output_radix == 10 ? "%lld" : "%llx",
task_last_run(tcp->task));
c = strlen(buf);
sprintf(format, "[%c%dll%c] ", '%', c,
pc->output_radix == 10 ? 'u' : 'x');
if (psi) {
for (c = others = 0; c < kt->cpus; c++) {
if (!NUM_IN_BITMAP(psi->cpus, c))
continue;
fprintf(fp, "%sCPU: %d",
others++ ? "\n" : "", c);
if (hide_offline_cpu(c)) {
fprintf(fp, " [OFFLINE]\n");
continue;
} else
fprintf(fp, "\n");
if ((kt->flags & SMP) && (kt->flags & PER_CPU_OFF))
runq = rq_sp->value + kt->__per_cpu_offset[c];
else
runq = rq_sp->value;
readmem(runq + OFFSET(rq_timestamp), KVADDR, &rq_clock,
sizeof(ulonglong), "per-cpu rq clock",
FAULT_ON_ERROR);
translate_nanoseconds(rq_clock, buf);
max_days = first_space(buf) - buf;
tcp = FIRST_CONTEXT();
for (i = 0; i < RUNNING_TASKS(); i++, tcp++) {
if (tcp->processor != c)
continue;
delta = rq_clock - task_last_run(tcp->task);
if (delta < 0)
delta = 0;
translate_nanoseconds(delta, buf);
days = first_space(buf) - buf;
fprintf(fp, "[%s%s] ", space(max_days - days),
buf);
fprintf(fp, "[%s] ",
task_state_string(tcp->task,
buf, !VERBOSE));
print_task_header(fp, tcp, FALSE);
}
}
} else if (tc) {
if ((kt->flags & SMP) && (kt->flags & PER_CPU_OFF))
runq = rq_sp->value + kt->__per_cpu_offset[tc->processor];
else
runq = rq_sp->value;
readmem(runq + OFFSET(rq_timestamp), KVADDR, &rq_clock,
sizeof(ulonglong), "per-cpu rq clock",
FAULT_ON_ERROR);
translate_nanoseconds(rq_clock, buf);
max_days = first_space(buf) - buf;
delta = rq_clock - task_last_run(tc->task);
if (delta < 0)
delta = 0;
translate_nanoseconds(delta, buf);
days = first_space(buf) - buf;
fprintf(fp, "[%s%s] ", space(max_days - days), buf);
fprintf(fp, "[%s] ", task_state_string(tc->task, buf, !VERBOSE));
print_task_header(fp, tc, FALSE);
} else {
tcp = FIRST_CONTEXT();
for (i = 0; i < RUNNING_TASKS(); i++, tcp++) {
if ((kt->flags & SMP) && (kt->flags & PER_CPU_OFF))
runq = rq_sp->value +
kt->__per_cpu_offset[tcp->processor];
else
runq = rq_sp->value;
readmem(runq + OFFSET(rq_timestamp), KVADDR, &rq_clock,
sizeof(ulonglong), "per-cpu rq clock",
FAULT_ON_ERROR);
delta = rq_clock - task_last_run(tcp->task);
if (delta < 0)
delta = 0;
fprintf(fp, "[%s] ", translate_nanoseconds(delta, buf));
fprintf(fp, "[%s] ", task_state_string(tcp->task, buf, !VERBOSE));
print_task_header(fp, tcp, FALSE);
}
}
}
/*
* Show the argv and envp strings pointed to by mm_struct->arg_start
* and mm_struct->env_start. The user addresses need to broken up
* into physical on a page-per-page basis because we typically are
* not going to be working in the context of the target task.
*/
static void
show_task_args(struct task_context *tc)
{
ulong arg_start, arg_end, env_start, env_end;
char *buf, *bufptr, *p1;
char *as, *ae, *es, *ee;
physaddr_t paddr;
ulong uvaddr, size, cnt;
int c, d;
print_task_header(fp, tc, 0);
if (!tc || !tc->mm_struct) { /* probably a kernel thread */
error(INFO, "no user stack\n\n");
return;
}
if (!task_mm(tc->task, TRUE))
return;
if (INVALID_MEMBER(mm_struct_arg_start)) {
MEMBER_OFFSET_INIT(mm_struct_arg_start, "mm_struct", "arg_start");
MEMBER_OFFSET_INIT(mm_struct_arg_end, "mm_struct", "arg_end");
MEMBER_OFFSET_INIT(mm_struct_env_start, "mm_struct", "env_start");
MEMBER_OFFSET_INIT(mm_struct_env_end, "mm_struct", "env_end");
}
arg_start = ULONG(tt->mm_struct + OFFSET(mm_struct_arg_start));
arg_end = ULONG(tt->mm_struct + OFFSET(mm_struct_arg_end));
env_start = ULONG(tt->mm_struct + OFFSET(mm_struct_env_start));
env_end = ULONG(tt->mm_struct + OFFSET(mm_struct_env_end));
if (CRASHDEBUG(1)) {
fprintf(fp, "arg_start: %lx arg_end: %lx (%ld)\n",
arg_start, arg_end, arg_end - arg_start);
fprintf(fp, "env_start: %lx env_end: %lx (%ld)\n",
env_start, env_end, env_end - env_start);
}
buf = GETBUF(env_end - arg_start + 1);
uvaddr = arg_start;
size = env_end - arg_start;
bufptr = buf;
while (size > 0) {
if (!uvtop(tc, uvaddr, &paddr, 0)) {
error(INFO, "cannot access user stack address: %lx\n\n",
uvaddr);
goto bailout;
}
cnt = PAGESIZE() - PAGEOFFSET(uvaddr);
if (cnt > size)
cnt = size;
if (!readmem(paddr, PHYSADDR, bufptr, cnt,
"user stack contents", RETURN_ON_ERROR|QUIET)) {
error(INFO, "cannot access user stack address: %lx\n\n",
uvaddr);
goto bailout;
}
uvaddr += cnt;
bufptr += cnt;
size -= cnt;
}
as = buf;
ae = &buf[arg_end - arg_start];
es = &buf[env_start - arg_start];
ee = &buf[env_end - arg_start];
fprintf(fp, "ARG: ");
for (p1 = as, c = 0; p1 < ae; p1++) {
if (*p1 == NULLCHAR) {
if (c)
fprintf(fp, " ");
c = 0;
} else {
fprintf(fp, "%c", *p1);
c++;
}
}
fprintf(fp, "\nENV: ");
for (p1 = es, c = d = 0; p1 < ee; p1++) {
if (*p1 == NULLCHAR) {
if (c)
fprintf(fp, "\n");
c = 0;
} else {
fprintf(fp, "%s%c", !c && (p1 != es) ? " " : "", *p1);
c++, d++;
}
}
fprintf(fp, "\n%s", d ? "" : "\n");
bailout:
FREEBUF(buf);
}
char *rlim_names[] = {
/* 0 */ "CPU",
/* 1 */ "FSIZE",
/* 2 */ "DATA",
/* 3 */ "STACK",
/* 4 */ "CORE",
/* 5 */ "RSS",
/* 6 */ "NPROC",
/* 7 */ "NOFILE",
/* 8 */ "MEMLOCK",
/* 9 */ "AS",
/* 10 */ "LOCKS",
/* 11 */ "SIGPENDING",
/* 12 */ "MSGQUEUE",
/* 13 */ "NICE",
/* 14 */ "RTPRIO",
/* 15 */ "RTTIME",
NULL,
};
#ifndef RLIM_INFINITY
#define RLIM_INFINITY (~0UL)
#endif
/*
* Show the current and maximum rlimit values.
*/
static void
show_task_rlimit(struct task_context *tc)
{
int i, j, len1, len2, rlimit_index;
int in_task_struct, in_signal_struct;
char *rlimit_buffer;
ulong *p1, rlim_addr;
char buf1[BUFSIZE];
char buf2[BUFSIZE];
char buf3[BUFSIZE];
rlimit_index = 0;
if (!VALID_MEMBER(task_struct_rlim) && !VALID_MEMBER(signal_struct_rlim)) {
MEMBER_OFFSET_INIT(task_struct_rlim, "task_struct", "rlim");
MEMBER_OFFSET_INIT(signal_struct_rlim, "signal_struct", "rlim");
STRUCT_SIZE_INIT(rlimit, "rlimit");
if (!VALID_MEMBER(task_struct_rlim) &&
!VALID_MEMBER(signal_struct_rlim))
error(FATAL, "cannot determine rlimit array location\n");
} else if (!VALID_STRUCT(rlimit))
error(FATAL, "cannot determine rlimit structure definition\n");
in_task_struct = in_signal_struct = FALSE;
if (VALID_MEMBER(task_struct_rlim)) {
rlimit_index = get_array_length("task_struct.rlim", NULL, 0);
in_task_struct = TRUE;
} else if (VALID_MEMBER(signal_struct_rlim)) {
if (!VALID_MEMBER(task_struct_signal))
error(FATAL, "cannot determine rlimit array location\n");
rlimit_index = get_array_length("signal_struct.rlim", NULL, 0);
in_signal_struct = TRUE;
}
if (!rlimit_index)
error(FATAL, "cannot determine rlimit array size\n");
for (i = len1 = 0; i < rlimit_index; i++) {
if (rlim_names[i] == NULL)
continue;
if ((j = strlen(rlim_names[i])) > len1)
len1 = j;
}
len2 = strlen("(unlimited)");
rlimit_buffer = GETBUF(rlimit_index * SIZE(rlimit));
print_task_header(fp, tc, 0);
fill_task_struct(tc->task);
if (in_task_struct) {
BCOPY(tt->task_struct + OFFSET(task_struct_rlim),
rlimit_buffer, rlimit_index * SIZE(rlimit));
} else if (in_signal_struct) {
rlim_addr = ULONG(tt->task_struct + OFFSET(task_struct_signal));
if (!readmem(rlim_addr + OFFSET(signal_struct_rlim),
KVADDR, rlimit_buffer, rlimit_index * SIZE(rlimit),
"signal_struct rlimit array", RETURN_ON_ERROR)) {
FREEBUF(rlimit_buffer);
return;
}
}
fprintf(fp, " %s %s %s\n",
mkstring(buf1, len1, RJUST, "RLIMIT"),
mkstring(buf2, len2, CENTER|RJUST, "CURRENT"),
mkstring(buf3, len2, CENTER|RJUST, "MAXIMUM"));
for (p1 = (ulong *)rlimit_buffer, i = 0; i < rlimit_index; i++) {
fprintf(fp, " %s ", mkstring(buf1, len1, RJUST,
rlim_names[i] ? rlim_names[i] : "(unknown)"));
if (*p1 == (ulong)RLIM_INFINITY)
fprintf(fp, "(unlimited) ");
else
fprintf(fp, "%s ", mkstring(buf1, len2,
CENTER|LJUST|LONG_DEC, MKSTR(*p1)));
p1++;
if (*p1 == (ulong)RLIM_INFINITY)
fprintf(fp, "(unlimited)\n");
else
fprintf(fp, "%s\n", mkstring(buf1, len2,
CENTER|LJUST|LONG_DEC, MKSTR(*p1)));
p1++;
}
fprintf(fp, "\n");
FREEBUF(rlimit_buffer);
}
/*
* Put either the task_struct address or kernel stack pointer into a string.
* If the kernel stack pointer is requested, piggy-back on top of the
* back trace code to avoid having to deal with machine dependencies,
* live active tasks, and dumpfile panic tasks.
*/
static char *
task_pointer_string(struct task_context *tc, ulong do_kstackp, char *buf)
{
struct bt_info bt_info, *bt;
char buf1[BUFSIZE];
if (do_kstackp) {
bt = &bt_info;
BZERO(bt, sizeof(struct bt_info));;
if (is_task_active(tc->task)) {
bt->stkptr = 0;
} else if (VALID_MEMBER(task_struct_thread_esp)) {
readmem(tc->task + OFFSET(task_struct_thread_esp),
KVADDR, &bt->stkptr, sizeof(void *),
"thread_struct esp", FAULT_ON_ERROR);
} else if (VALID_MEMBER(task_struct_thread_ksp)) {
readmem(tc->task + OFFSET(task_struct_thread_ksp),
KVADDR, &bt->stkptr, sizeof(void *),
"thread_struct ksp", FAULT_ON_ERROR);
} else {
bt->task = tc->task;
bt->tc = tc;
bt->stackbase = GET_STACKBASE(tc->task);
bt->stacktop = GET_STACKTOP(tc->task);
bt->flags |= BT_KSTACKP;
back_trace(bt);
}
if (bt->stkptr)
sprintf(buf, "%s",
mkstring(buf1, VADDR_PRLEN,
CENTER|RJUST|LONG_HEX,
MKSTR(bt->stkptr)));
else
sprintf(buf, "%s",
mkstring(buf1, VADDR_PRLEN, CENTER|RJUST, "--"));
} else
sprintf(buf, "%s",
mkstring(buf1, VADDR_PRLEN,
CENTER|RJUST|LONG_HEX,
MKSTR(tc->task)));
return buf;
}
/*
* Dump the task list ordered by start_time.
*/
struct kernel_timeval {
unsigned int tv_sec;
unsigned int tv_usec;
};
struct task_start_time {
struct task_context *tc;
ulonglong start_time;
ulong tms_utime;
ulong tms_stime;
struct timeval old_utime;
struct timeval old_stime;
struct kernel_timeval kutime;
struct kernel_timeval kstime;
ulonglong utime;
ulonglong stime;
};
static void
show_task_times(struct task_context *tcp, ulong flags)
{
int i, tasks, use_kernel_timeval, use_utime_stime;
struct task_context *tc;
struct task_start_time *task_start_times, *tsp;
ulong jiffies, tgid;
ulonglong jiffies_64;
char buf1[BUFSIZE];
task_start_times = (struct task_start_time *)
GETBUF(RUNNING_TASKS() * sizeof(struct task_start_time));
use_kernel_timeval = STRUCT_EXISTS("kernel_timeval");
if (VALID_MEMBER(task_struct_utime) &&
(SIZE(task_struct_utime) ==
(BITS32() ? sizeof(uint32_t) : sizeof(uint64_t))))
use_utime_stime = TRUE;
else
use_utime_stime = FALSE;
get_symbol_data("jiffies", sizeof(long), &jiffies);
if (symbol_exists("jiffies_64"))
get_uptime(NULL, &jiffies_64);
tsp = task_start_times;
tc = tcp ? tcp : FIRST_CONTEXT();
for (i = tasks = 0; i < RUNNING_TASKS(); i++, tc++) {
if ((flags & PS_USER) && is_kernel_thread(tc->task))
continue;
if ((flags & PS_KERNEL) && !is_kernel_thread(tc->task))
continue;
if (flags & PS_GROUP) {
tgid = task_tgid(tc->task);
if (tc->pid != tgid) {
if (tcp) {
if (!(tc = tgid_to_context(tgid)))
return;
} else
continue;
}
if (hq_entry_exists((ulong)tc))
return;
hq_enter((ulong)tc);
}
fill_task_struct(tc->task);
if (!tt->last_task_read) {
if (tcp)
return;
continue;
}
tsp->tc = tc;
if (BITS32() && (SIZE(task_struct_start_time) == 8)) {
if (start_time_timespec())
tsp->start_time =
ULONG(tt->task_struct +
OFFSET(task_struct_start_time));
else
tsp->start_time =
ULONGLONG(tt->task_struct +
OFFSET(task_struct_start_time));
} else {
start_time_timespec();
tsp->start_time = ULONG(tt->task_struct +
OFFSET(task_struct_start_time));
}
if (VALID_MEMBER(task_struct_times)) {
tsp->tms_utime = ULONG(tt->task_struct +
OFFSET(task_struct_times) +
OFFSET(tms_tms_utime));
tsp->tms_stime = ULONG(tt->task_struct +
OFFSET(task_struct_times) +
OFFSET(tms_tms_stime));
} else if (VALID_MEMBER(task_struct_utime)) {
if (use_utime_stime) {
tsp->utime = ULONG(tt->task_struct +
OFFSET(task_struct_utime));
tsp->stime = ULONG(tt->task_struct +
OFFSET(task_struct_stime));
} else if (use_kernel_timeval) {
BCOPY(tt->task_struct +
OFFSET(task_struct_utime), &tsp->kutime,
sizeof(struct kernel_timeval));
BCOPY(tt->task_struct +
OFFSET(task_struct_stime), &tsp->kstime,
sizeof(struct kernel_timeval));
} else if (VALID_STRUCT(cputime_t)) {
/* since linux 2.6.11 */
if (SIZE(cputime_t) == 8) {
uint64_t utime_64, stime_64;
BCOPY(tt->task_struct +
OFFSET(task_struct_utime),
&utime_64, 8);
BCOPY(tt->task_struct +
OFFSET(task_struct_stime),
&stime_64, 8);
/* convert from micro-sec. to sec. */
tsp->old_utime.tv_sec = utime_64 / 1000000;
tsp->old_stime.tv_sec = stime_64 / 1000000;
} else {
uint32_t utime_32, stime_32;
BCOPY(tt->task_struct +
OFFSET(task_struct_utime),
&utime_32, 4);
BCOPY(tt->task_struct +
OFFSET(task_struct_stime),
&stime_32, 4);
tsp->old_utime.tv_sec = utime_32;
tsp->old_stime.tv_sec = stime_32;
}
} else {
BCOPY(tt->task_struct +
OFFSET(task_struct_utime),
&tsp->utime, sizeof(struct timeval));
BCOPY(tt->task_struct +
OFFSET(task_struct_stime),
&tsp->stime, sizeof(struct timeval));
}
}
tasks++;
tsp++;
if (tcp)
break;
}
qsort((void *)task_start_times, (size_t)tasks,
sizeof(struct task_start_time), compare_start_time);
for (i = 0, tsp = task_start_times; i < tasks; i++, tsp++) {
print_task_header(fp, tsp->tc, 0);
fprintf(fp, " RUN TIME: %s\n", symbol_exists("jiffies_64") ?
convert_time(convert_start_time(tsp->start_time, jiffies_64), buf1) :
convert_time(jiffies - tsp->start_time, buf1));
fprintf(fp, " START TIME: %llu\n", tsp->start_time);
if (VALID_MEMBER(task_struct_times)) {
fprintf(fp, " USER TIME: %ld\n", tsp->tms_utime);
fprintf(fp, " SYSTEM TIME: %ld\n\n", tsp->tms_stime);
} else if (VALID_MEMBER(task_struct_utime)) {
if (use_utime_stime) {
fprintf(fp, " UTIME: %lld\n",
(ulonglong)tsp->utime);
fprintf(fp, " STIME: %lld\n\n",
(ulonglong)tsp->stime);
} else if (use_kernel_timeval) {
fprintf(fp, " USER TIME: %d\n",
tsp->kutime.tv_sec);
fprintf(fp, " SYSTEM TIME: %d\n\n",
tsp->kstime.tv_sec);
} else {
fprintf(fp, " USER TIME: %ld\n",
tsp->old_utime.tv_sec);
fprintf(fp, " SYSTEM TIME: %ld\n\n",
tsp->old_stime.tv_sec);
}
}
}
FREEBUF(task_start_times);
}
static int
start_time_timespec(void)
{
char buf[BUFSIZE];
switch(tt->flags & (TIMESPEC | NO_TIMESPEC | START_TIME_NSECS))
{
case TIMESPEC:
return TRUE;
case NO_TIMESPEC:
case START_TIME_NSECS:
return FALSE;
default:
break;
}
tt->flags |= NO_TIMESPEC;
open_tmpfile();
sprintf(buf, "ptype struct task_struct");
if (!gdb_pass_through(buf, NULL, GNU_RETURN_ON_ERROR)) {
close_tmpfile();
return FALSE;
}
rewind(pc->tmpfile);
while (fgets(buf, BUFSIZE, pc->tmpfile)) {
if (strstr(buf, "start_time;")) {
if (strstr(buf, "struct timespec")) {
tt->flags &= ~NO_TIMESPEC;
tt->flags |= TIMESPEC;
}
}
}
close_tmpfile();
if ((tt->flags & NO_TIMESPEC) && (SIZE(task_struct_start_time) == 8)) {
tt->flags &= ~NO_TIMESPEC;
tt->flags |= START_TIME_NSECS;
}
return (tt->flags & TIMESPEC ? TRUE : FALSE);
}
static ulonglong
convert_start_time(ulonglong start_time, ulonglong current)
{
ulong tmp1, tmp2;
ulonglong wrapped;
switch(tt->flags & (TIMESPEC | NO_TIMESPEC | START_TIME_NSECS))
{
case START_TIME_NSECS:
start_time /= 1000000000ULL; /* FALLTHROUGH */
case TIMESPEC:
if ((start_time * (ulonglong)machdep->hz) > current)
return 0;
else
return current - (start_time * (ulonglong)machdep->hz);
case NO_TIMESPEC:
if (THIS_KERNEL_VERSION >= LINUX(2,6,0)) {
wrapped = (start_time & 0xffffffff00000000ULL);
if (wrapped) {
wrapped -= 0x100000000ULL;
start_time &= 0x00000000ffffffffULL;
start_time |= wrapped;
start_time += (ulonglong)(300*machdep->hz);
} else {
tmp1 = (ulong)(uint)(-300*machdep->hz);
tmp2 = (ulong)start_time;
start_time = (ulonglong)(tmp2 - tmp1);
}
}
break;
default:
break;
}
return start_time;
}
/*
* The comparison function must return an integer less than,
* equal to, or greater than zero if the first argument is
* considered to be respectively less than, equal to, or
* greater than the second. If two members compare as equal,
* their order in the sorted array is undefined.
*/
static int
compare_start_time(const void *v1, const void *v2)
{
struct task_start_time *t1, *t2;
t1 = (struct task_start_time *)v1;
t2 = (struct task_start_time *)v2;
return (t1->start_time < t2->start_time ? -1 :
t1->start_time == t2->start_time ? 0 : 1);
}
static ulong
parent_of(ulong task)
{
long offset;
ulong parent;
if (VALID_MEMBER(task_struct_parent))
offset = OFFSET(task_struct_parent);
else
offset = OFFSET(task_struct_p_pptr);
readmem(task+offset, KVADDR, &parent,
sizeof(void *), "task parent", FAULT_ON_ERROR);
return parent;
}
/*
* Dump the parental hierarchy of a task.
*/
static void
parent_list(ulong task)
{
int i, j, cnt;
struct task_context *tc;
char *buffer;
long reserved;
ulong *task_list, child, parent;
reserved = 100 * sizeof(ulong);
buffer = GETBUF(reserved);
task_list = (ulong *)buffer;
child = task_list[0] = task;
parent = parent_of(child);
cnt = 1;
while (child != parent) {
child = task_list[cnt++] = parent;
parent = parent_of(child);
if (cnt == reserved) {
RESIZEBUF(buffer, reserved, reserved * 2);
reserved *= 2;
task_list = (ulong *)buffer;
}
}
for (i = cnt-1, j = 0; i >= 0; i--, j++) {
INDENT(j);
tc = task_to_context(task_list[i]);
if (tc)
print_task_header(fp, tc, 0);
}
FREEBUF(task_list);
}
/*
* Dump the children of a task.
*/
static void
child_list(ulong task)
{
int i;
int cnt;
struct task_context *tc;
tc = task_to_context(task);
print_task_header(fp, tc, 0);
tc = FIRST_CONTEXT();
for (i = cnt = 0; i < RUNNING_TASKS(); i++, tc++) {
if (tc->ptask == task) {
INDENT(2);
print_task_header(fp, tc, 0);
cnt++;
}
}
if (!cnt)
fprintf(fp, " (no children)\n");
}
/*
* Dump the children of a task.
*/
static void
show_tgid_list(ulong task)
{
int i;
int cnt;
struct task_context *tc;
ulong tgid;
tc = task_to_context(task);
tgid = task_tgid(task);
if (tc->pid != tgid) {
if (pc->curcmd_flags & TASK_SPECIFIED) {
if (!(tc = tgid_to_context(tgid)))
return;
task = tc->task;
} else
return;
}
if ((tc->pid == 0) && (pc->curcmd_flags & IDLE_TASK_SHOWN))
return;
print_task_header(fp, tc, 0);
tc = FIRST_CONTEXT();
for (i = cnt = 0; i < RUNNING_TASKS(); i++, tc++) {
if (tc->task == task)
continue;
if (task_tgid(tc->task) == tgid) {
INDENT(2);
print_task_header(fp, tc, 0);
cnt++;
if (tc->pid == 0)
pc->curcmd_flags |= IDLE_TASK_SHOWN;
}
}
if (!cnt)
fprintf(fp, " (no threads)\n");
fprintf(fp, "\n");
}
/*
* Return the first task found that belongs to a pid.
*/
ulong
pid_to_task(ulong pid)
{
int i;
struct task_context *tc;
tc = FIRST_CONTEXT();
for (i = 0; i < RUNNING_TASKS(); i++, tc++)
if (tc->pid == pid)
return(tc->task);
return((ulong)NULL);
}
/*
* Return the pid of a task.
*/
ulong
task_to_pid(ulong task)
{
int i;
struct task_context *tc;
tc = FIRST_CONTEXT();
for (i = 0; i < RUNNING_TASKS(); i++, tc++)
if (tc->task == task)
return(tc->pid);
return(NO_PID);
}
/*
* Verify whether a task exists.
*/
int
task_exists(ulong task)
{
int i;
struct task_context *tc;
tc = FIRST_CONTEXT();
for (i = 0; i < RUNNING_TASKS(); i++, tc++)
if (tc->task == task)
return TRUE;
return FALSE;
}
/*
* Return the task_context structure of a task.
*/
struct task_context *
task_to_context(ulong task)
{
int i;
struct task_context *tc;
tc = FIRST_CONTEXT();
for (i = 0; i < RUNNING_TASKS(); i++, tc++)
if (tc->task == task)
return tc;
return NULL;
}
/*
* Return a tgid's parent task_context structure.
*/
struct task_context *
tgid_to_context(ulong parent_tgid)
{
int i;
struct task_context *tc;
ulong tgid;
tc = FIRST_CONTEXT();
for (i = 0; i < RUNNING_TASKS(); i++, tc++) {
tgid = task_tgid(tc->task);
if ((tgid == parent_tgid) && (tgid == tc->pid))
return tc;
}
return NULL;
}
/*
* Return the task_context structure of the first task found with a pid,
* while linking all tasks that have that pid.
*/
struct task_context *
pid_to_context(ulong pid)
{
int i;
struct task_context *tc, *firsttc, *lasttc;
tc = FIRST_CONTEXT();
firsttc = lasttc = NULL;
for (i = 0; i < RUNNING_TASKS(); i++, tc++) {
if (tc->pid == pid) {
if (!firsttc)
firsttc = tc;
if (lasttc)
lasttc->tc_next = tc;
tc->tc_next = NULL;
lasttc = tc;
}
}
return firsttc;
}
/*
* Verify whether a pid exists, and if found, linking all tasks having the pid.
*/
int
pid_exists(ulong pid)
{
int i;
struct task_context *tc, *lasttc;
int count;
tc = FIRST_CONTEXT();
count = 0;
lasttc = NULL;
for (i = 0; i < RUNNING_TASKS(); i++, tc++) {
if (tc->pid == pid) {
count++;
if (lasttc)
lasttc->tc_next = tc;
tc->tc_next = NULL;
lasttc = tc;
}
}
return(count);
}
/*
* Translate a stack pointer to a task, dealing with possible split.
* If that doesn't work, check the hardirq_stack and softirq_stack.
*/
ulong
stkptr_to_task(ulong sp)
{
int i, c;
struct task_context *tc;
struct bt_info bt_info, *bt;
bt = &bt_info;
tc = FIRST_CONTEXT();
for (i = 0; i < RUNNING_TASKS(); i++, tc++) {
bt->stackbase = GET_STACKBASE(tc->task);
bt->stacktop = GET_STACKTOP(tc->task);
if (INSTACK(sp, bt))
return tc->task;
}
if (!(tt->flags & IRQSTACKS))
return NO_TASK;
bt = &bt_info;
tc = FIRST_CONTEXT();
for (i = 0; i < RUNNING_TASKS(); i++, tc++) {
for (c = 0; c < NR_CPUS; c++) {
if (tt->hardirq_ctx[c]) {
bt->stackbase = tt->hardirq_ctx[c];
bt->stacktop = bt->stackbase +
SIZE(irq_ctx);
if (INSTACK(sp, bt) &&
(tt->hardirq_tasks[c] == tc->task))
return tc->task;
}
if (tt->softirq_ctx[c]) {
bt->stackbase = tt->softirq_ctx[c];
bt->stacktop = bt->stackbase +
SIZE(irq_ctx);
if (INSTACK(sp, bt) &&
(tt->softirq_tasks[c] == tc->task))
return tc->task;
}
}
}
return NO_TASK;
}
/*
* Translate a task pointer to its thread_info.
*/
ulong
task_to_thread_info(ulong task)
{
int i;
struct task_context *tc;
if (!(tt->flags & THREAD_INFO))
error(FATAL,
"task_to_thread_info: thread_info struct does not exist!\n");
tc = FIRST_CONTEXT();
for (i = 0; i < RUNNING_TASKS(); i++, tc++) {
if (tc->task == task)
return tc->thread_info;
}
return(error(FATAL, "task does not exist: %lx\n", task));
}
/*
* Translate a task address to its stack base, dealing with potential split.
*/
ulong
task_to_stackbase(ulong task)
{
if (tt->flags & THREAD_INFO)
return task_to_thread_info(task);
else
return (task & ~(STACKSIZE()-1));
}
/*
* Try to translate a decimal or hexadecimal string into a task or pid,
* failing if no task or pid exists, or if there is ambiguity between
* the decimal and hexadecimal translations. However, if the value could
* be a decimal PID and a hexadecimal PID of two different processes, then
* default to the decimal value.
*
* This was added in preparation for overlapping, zero-based, user and kernel
* virtual addresses on s390 and s390x, allowing for the entry of ambiguous
* decimal/hexadecimal task address values without the leading "0x".
* It should be used in lieu of "stol" when parsing for task/pid arguments.
*/
int
str_to_context(char *string, ulong *value, struct task_context **tcp)
{
ulong dvalue, hvalue;
int found, type;
char *s;
struct task_context *tc_dp, *tc_dt, *tc_hp, *tc_ht;
if (string == NULL) {
error(INFO, "received NULL string\n");
return STR_INVALID;
}
s = string;
dvalue = hvalue = BADADDR;
if (decimal(s, 0))
dvalue = dtol(s, RETURN_ON_ERROR, NULL);
if (hexadecimal(s, 0)) {
if (STRNEQ(s, "0x") || STRNEQ(s, "0X"))
s += 2;
if (strlen(s) <= MAX_HEXADDR_STRLEN)
hvalue = htol(s, RETURN_ON_ERROR, NULL);
}
found = 0;
tc_dp = tc_dt = tc_hp = tc_ht = NULL;
type = STR_INVALID;
if (dvalue != BADADDR) {
if ((tc_dp = pid_to_context(dvalue)))
found++;
if ((tc_dt = task_to_context(dvalue)))
found++;
}
if ((hvalue != BADADDR) && (dvalue != hvalue)) {
if ((tc_hp = pid_to_context(hvalue)))
found++;
if ((tc_ht = task_to_context(hvalue)))
found++;
}
switch (found)
{
case 2:
if (tc_dp && tc_hp) {
*tcp = tc_dp;
*value = dvalue;
type = STR_PID;
}
break;
case 1:
if (tc_dp) {
*tcp = tc_dp;
*value = dvalue;
type = STR_PID;
}
if (tc_dt) {
*tcp = tc_dt;
*value = dvalue;
type = STR_TASK;
}
if (tc_hp) {
*tcp = tc_hp;
*value = hvalue;
type = STR_PID;
}
if (tc_ht) {
*tcp = tc_ht;
*value = hvalue;
type = STR_TASK;
}
break;
}
return type;
}
/*
* Return the task if the vaddr is part of a task's task_struct.
*/
ulong
vaddr_in_task_struct(ulong vaddr)
{
int i;
struct task_context *tc;
tc = FIRST_CONTEXT();
for (i = 0; i < RUNNING_TASKS(); i++, tc++) {
if ((vaddr >= tc->task) &&
(vaddr < (tc->task + SIZE(task_struct))))
return tc->task;
}
return NO_TASK;
}
/*
* Verify whether any task is running a command.
*/
int
comm_exists(char *s)
{
int i, cnt;
struct task_context *tc;
char buf[TASK_COMM_LEN];
strlcpy(buf, s, TASK_COMM_LEN);
tc = FIRST_CONTEXT();
for (i = cnt = 0; i < RUNNING_TASKS(); i++, tc++)
if (STREQ(tc->comm, buf))
cnt++;
return cnt;
}
/*
* Set a new context. If only a pid is passed, the first task found with
* that pid is selected.
*/
int
set_context(ulong task, ulong pid)
{
int i;
struct task_context *tc;
int found;
tc = FIRST_CONTEXT();
for (i = 0, found = FALSE; i < RUNNING_TASKS(); i++, tc++) {
if (task && (tc->task == task)) {
found = TRUE;
break;
} else if (pid == tc->pid) {
found = TRUE;
break;
}
}
if (found) {
CURRENT_CONTEXT() = tc;
return TRUE;
} else {
if (task)
error(INFO, "cannot set context for task: %lx\n", task);
else
error(INFO, "cannot set context for pid: %d\n", pid);
return FALSE;
}
}
/*
* Check whether the panic was determined to be caused by a "sys -panic"
* command. If so, fix the task_context's pid despite what the task_struct
* says.
*/
#define CONTEXT_ADJUSTED (1)
#define CONTEXT_ERRONEOUS (2)
static int
panic_context_adjusted(struct task_context *tc)
{
pid_t pgrp, tgid;
char buf[BUFSIZE];
if (!(DUMPFILE() && (tc == task_to_context(tt->panic_task)) &&
(tc->pid == 0) && STRNEQ(tc->comm, pc->program_name) &&
strstr(get_panicmsg(buf), "Attempted to kill the idle task")))
return 0;
if (INVALID_MEMBER(task_struct_pgrp) ||
INVALID_MEMBER(task_struct_tgid))
return CONTEXT_ERRONEOUS;
fill_task_struct(tc->task);
pgrp = tt->last_task_read ?
UINT(tt->task_struct + OFFSET(task_struct_pgrp)) : 0;
tgid = tt->last_task_read ?
UINT(tt->task_struct + OFFSET(task_struct_tgid)) : 0;
if (pgrp && tgid && (pgrp == tgid) && !pid_exists((ulong)pgrp)) {
tc->pid = (ulong)pgrp;
return CONTEXT_ADJUSTED;
}
return CONTEXT_ERRONEOUS;
}
/*
* Display a task context.
*/
void
show_context(struct task_context *tc)
{
char buf[BUFSIZE];
char *p1;
int adjusted, cnt, indent;
adjusted = pc->flags & RUNTIME ? 0 : panic_context_adjusted(tc);
indent = pc->flags & RUNTIME ? 0 : 5;
INDENT(indent);
fprintf(fp, " PID: %ld\n", tc->pid);
INDENT(indent);
fprintf(fp, "COMMAND: \"%s\"\n", tc->comm);
INDENT(indent);
fprintf(fp, " TASK: %lx ", tc->task);
if ((machdep->flags & (INIT|MCA)) && (tc->pid == 0))
cnt = comm_exists(tc->comm);
else
cnt = TASKS_PER_PID(tc->pid);
if (cnt > 1)
fprintf(fp, "(1 of %d) ", cnt);
if (tt->flags & THREAD_INFO)
fprintf(fp, "[THREAD_INFO: %lx]", tc->thread_info);
fprintf(fp, "\n");
INDENT(indent);
fprintf(fp, " CPU: %s\n", task_cpu(tc->processor, buf, VERBOSE));
INDENT(indent);
fprintf(fp, " STATE: %s ",
task_state_string(tc->task, buf, VERBOSE));
if (is_task_active(tc->task)) {
if (machdep->flags & HWRESET)
fprintf(fp, "(HARDWARE RESET)");
else if ((pc->flags & SYSRQ) && (tc->task == tt->panic_task))
fprintf(fp, "(SYSRQ)");
else if (machdep->flags & INIT)
fprintf(fp, "(INIT)");
else if ((machdep->flags & MCA) && (tc->task == tt->panic_task))
fprintf(fp, "(MCA)");
else if ((tc->processor >= 0) &&
(tc->processor < NR_CPUS) &&
(kt->cpu_flags[tc->processor] & NMI))
fprintf(fp, "(NMI)");
else if ((tc->task == tt->panic_task) &&
XENDUMP_DUMPFILE() && (kt->xen_flags & XEN_SUSPEND))
fprintf(fp, "(SUSPEND)");
else if ((tc->task == tt->panic_task) && !(pc->flags2 & SNAP))
fprintf(fp, "(PANIC)");
else
fprintf(fp, "(ACTIVE)");
}
if (!(pc->flags & RUNTIME) && !ACTIVE() &&
(tt->flags & PANIC_TASK_NOT_FOUND) &&
!SYSRQ_TASK(tc->task)) {
fprintf(fp, "\n"); INDENT(indent);
if (machine_type("S390") || machine_type("S390X"))
fprintf(fp, " INFO: no panic task found");
else if (tt->panic_processor >= 0)
fprintf(fp,
"WARNING: reported panic task %lx not found",
tt->panic_threads[tt->panic_processor]);
else
fprintf(fp, "WARNING: panic task not found");
}
fprintf(fp, "\n");
if (pc->flags & RUNTIME)
return;
/*
* Dump any pre-first-prompt messages here.
*/
cnt = 0;
if (pc->flags & NAMELIST_UNLINKED) {
strcpy(buf, pc->namelist);
if ((p1 = strstr(buf, "@")))
*p1 = NULLCHAR;
fprintf(fp,
"%sNOTE: To save the remote \"%s\" locally,\n enter: \"save kernel\"\n",
cnt++ ? "" : "\n", buf);
}
if (REMOTE_DUMPFILE())
fprintf(fp,
"%sNOTE: To save the remote \"%s\" locally,\n enter: \"save dumpfile\"\n",
cnt++ ? "" : "\n",
basename(pc->server_memsrc));
/*
* If this panic was caused by a "sys -panic" command, issue the
* proper warning message.
*/
switch (adjusted)
{
case CONTEXT_ADJUSTED:
fprintf(fp,
"%sNOTE: The \"%s\" task_struct will erroneously show a p_pid of 0\n",
cnt++ ? "" : "\n", tc->comm);
break;
case CONTEXT_ERRONEOUS:
fprintf(fp,
"%sWARNING: The \"%s\" context will erroneously show a PID of 0\n",
cnt++ ? "" : "\n", tc->comm);
break;
}
if (!(pc->flags & RUNTIME) && (tt->flags & ACTIVE_ONLY))
error(WARNING,
"\nonly the active tasks on each cpu are being tracked\n");
}
/*
* Translate a task_struct state value into a long (verbose), or short string,
* or if requested, just pass back the state value.
*/
#define TASK_STATE_UNINITIALIZED (-1)
static long _RUNNING_ = TASK_STATE_UNINITIALIZED;
static long _INTERRUPTIBLE_ = TASK_STATE_UNINITIALIZED;
static long _UNINTERRUPTIBLE_ = TASK_STATE_UNINITIALIZED;
static long _STOPPED_ = TASK_STATE_UNINITIALIZED;
static long _TRACING_STOPPED_ = TASK_STATE_UNINITIALIZED;
long _ZOMBIE_ = TASK_STATE_UNINITIALIZED; /* also used by IS_ZOMBIE() */
static long _DEAD_ = TASK_STATE_UNINITIALIZED;
static long _SWAPPING_ = TASK_STATE_UNINITIALIZED;
static long _EXCLUSIVE_ = TASK_STATE_UNINITIALIZED;
static long _WAKEKILL_ = TASK_STATE_UNINITIALIZED;
static long _WAKING_ = TASK_STATE_UNINITIALIZED;
static long _NONINTERACTIVE_ = TASK_STATE_UNINITIALIZED;
static long _PARKED_ = TASK_STATE_UNINITIALIZED;
static long _NOLOAD_ = TASK_STATE_UNINITIALIZED;
#define valid_task_state(X) ((X) != TASK_STATE_UNINITIALIZED)
static void
dump_task_states(void)
{
int hi, lo;
fprintf(fp, " RUNNING: %3ld (0x%lx)\n",
_RUNNING_, _RUNNING_);
fprintf(fp, " INTERRUPTIBLE: %3ld (0x%lx)\n",
_INTERRUPTIBLE_, _INTERRUPTIBLE_);
fprintf(fp, " UNINTERRUPTIBLE: %3ld (0x%lx)\n",
_UNINTERRUPTIBLE_, _UNINTERRUPTIBLE_);
fprintf(fp, " STOPPED: %3ld (0x%lx)\n",
_STOPPED_, _STOPPED_);
if (valid_task_state(_TRACING_STOPPED_)) {
if (count_bits_long(_TRACING_STOPPED_) > 1) {
lo = lowest_bit_long(_TRACING_STOPPED_);
hi = highest_bit_long(_TRACING_STOPPED_);
fprintf(fp,
" TRACING_STOPPED: %3d and %d (0x%x and 0x%x)\n",
1<<lo, 1<<hi, 1<<lo, 1<<hi);
} else
fprintf(fp, " TRACING_STOPPED: %3ld (0x%lx)\n",
_TRACING_STOPPED_, _TRACING_STOPPED_);
}
fprintf(fp, " ZOMBIE: %3ld (0x%lx)\n",
_ZOMBIE_, _ZOMBIE_);
if (count_bits_long(_DEAD_) > 1) {
lo = lowest_bit_long(_DEAD_);
hi = highest_bit_long(_DEAD_);
fprintf(fp, " DEAD: %3d and %d (0x%x and 0x%x)\n",
1<<lo, 1<<hi, 1<<lo, 1<<hi);
} else
fprintf(fp, " DEAD: %3ld (0x%lx)\n",
_DEAD_, _DEAD_);
if (valid_task_state(_NONINTERACTIVE_))
fprintf(fp, " NONINTERACTIVE: %3ld (0x%lx)\n",
_NONINTERACTIVE_, _NONINTERACTIVE_);
if (valid_task_state(_SWAPPING_))
fprintf(fp, " SWAPPING: %3ld (0x%lx)\n",
_SWAPPING_, _SWAPPING_);
if (valid_task_state(_EXCLUSIVE_))
fprintf(fp, " EXCLUSIVE: %3ld (0x%lx)\n",
_EXCLUSIVE_, _EXCLUSIVE_);
if (valid_task_state(_WAKEKILL_) && valid_task_state(_WAKING_)) {
if (_WAKEKILL_ < _WAKING_) {
fprintf(fp, " WAKEKILL: %3ld (0x%lx)\n",
_WAKEKILL_, _WAKEKILL_);
fprintf(fp, " WAKING: %3ld (0x%lx)\n",
_WAKING_, _WAKING_);
} else {
fprintf(fp, " WAKING: %3ld (0x%lx)\n",
_WAKING_, _WAKING_);
fprintf(fp, " WAKEKILL: %3ld (0x%lx)\n",
_WAKEKILL_, _WAKEKILL_);
}
}
if (valid_task_state(_PARKED_))
fprintf(fp, " PARKED: %3ld (0x%lx)\n",
_PARKED_, _PARKED_);
if (valid_task_state(_NOLOAD_))
fprintf(fp, " NOLOAD: %3ld (0x%lx)\n",
_NOLOAD_, _NOLOAD_);
}
/*
* Initialize the task state fields based upon the kernel's task_state_array
* string table.
*/
static void
initialize_task_state(void)
{
int i, len;
ulong bitpos;
ulong str, task_state_array;
char buf[BUFSIZE];
if (!symbol_exists("task_state_array") ||
!readmem(task_state_array = symbol_value("task_state_array"),
KVADDR, &str, sizeof(void *),
"task_state_array", RETURN_ON_ERROR)) {
old_defaults:
_RUNNING_ = 0;
_INTERRUPTIBLE_ = 1;
_UNINTERRUPTIBLE_ = 2;
_ZOMBIE_ = 4;
_STOPPED_ = 8;
_SWAPPING_ = 16;
_EXCLUSIVE_ = 32;
return;
}
/*
* If the later version of stat_nam[] array exists that contains
* WAKING, WAKEKILL and PARKED, use it instead of task_state_array[].
*/
if (((len = get_array_length("stat_nam", NULL, 0)) > 0) &&
read_string(symbol_value("stat_nam"), buf, BUFSIZE-1) &&
ascii_string(buf) && (strlen(buf) > strlen("RSDTtZX"))) {
for (i = 0; i < strlen(buf); i++) {
switch (buf[i])
{
case 'R':
_RUNNING_ = i;
break;
case 'S':
_INTERRUPTIBLE_ = i;
break;
case 'D':
_UNINTERRUPTIBLE_ = (1 << (i-1));
break;
case 'T':
_STOPPED_ = (1 << (i-1));
break;
case 't':
_TRACING_STOPPED_ = (1 << (i-1));
break;
case 'X':
if (_DEAD_ == UNINITIALIZED)
_DEAD_ = (1 << (i-1));
else
_DEAD_ |= (1 << (i-1));
break;
case 'Z':
_ZOMBIE_ = (1 << (i-1));
break;
case 'x':
if (_DEAD_ == UNINITIALIZED)
_DEAD_ = (1 << (i-1));
else
_DEAD_ |= (1 << (i-1));
break;
case 'K':
_WAKEKILL_ = (1 << (i-1));
break;
case 'W':
_WAKING_ = (1 << (i-1));
break;
case 'P':
_PARKED_ = (1 << (i-1));
break;
case 'N':
_NOLOAD_ = (1 << (i-1));
break;
}
}
goto done_states;
}
if ((len = get_array_length("task_state_array", NULL, 0)) <= 0)
goto old_defaults;
bitpos = 0;
for (i = 0; i < len; i++) {
if (!read_string(str, buf, BUFSIZE-1))
break;
if (CRASHDEBUG(3))
fprintf(fp, "%s%s[%d][%s]\n", bitpos ? "" : "\n",
i < 10 ? " " : "", i, buf);
if (strstr(buf, "(running)"))
_RUNNING_ = bitpos;
else if (strstr(buf, "(sleeping)"))
_INTERRUPTIBLE_ = bitpos;
else if (strstr(buf, "(disk sleep)"))
_UNINTERRUPTIBLE_ = bitpos;
else if (strstr(buf, "(stopped)"))
_STOPPED_ = bitpos;
else if (strstr(buf, "(zombie)"))
_ZOMBIE_ = bitpos;
else if (strstr(buf, "(dead)")) {
if (_DEAD_ == TASK_STATE_UNINITIALIZED)
_DEAD_ = bitpos;
else
_DEAD_ |= bitpos;
} else if (strstr(buf, "(swapping)")) /* non-existent? */
_SWAPPING_ = bitpos;
else if (strstr(buf, "(tracing stop)")) {
if (_TRACING_STOPPED_ == TASK_STATE_UNINITIALIZED)
_TRACING_STOPPED_ = bitpos;
else
_TRACING_STOPPED_ |= bitpos;
} else if (strstr(buf, "(wakekill)"))
_WAKEKILL_ = bitpos;
else if (strstr(buf, "(waking)"))
_WAKING_ = bitpos;
else if (strstr(buf, "(parked)"))
_PARKED_ = bitpos;
if (!bitpos)
bitpos = 1;
else
bitpos = bitpos << 1;
task_state_array += sizeof(void *);
if (!readmem(task_state_array, KVADDR, &str, sizeof(void *),
"task_state_array", RETURN_ON_ERROR))
break;
}
if ((THIS_KERNEL_VERSION >= LINUX(2,6,16)) &&
(THIS_KERNEL_VERSION < LINUX(2,6,24))) {
_NONINTERACTIVE_ = 64;
}
if (THIS_KERNEL_VERSION >= LINUX(2,6,32)) {
/*
* Account for states not listed in task_state_array[]
*/
if (count_bits_long(_DEAD_) == 1) {
bitpos = 1<< lowest_bit_long(_DEAD_);
_DEAD_ |= (bitpos<<1); /* TASK_DEAD */
_WAKEKILL_ = (bitpos<<2); /* TASK_WAKEKILL */
_WAKING_ = (bitpos<<3); /* TASK_WAKING */
}
}
done_states:
if (CRASHDEBUG(3))
dump_task_states();
if (!valid_task_state(_RUNNING_) ||
!valid_task_state(_INTERRUPTIBLE_) ||
!valid_task_state(_UNINTERRUPTIBLE_) ||
!valid_task_state(_ZOMBIE_) ||
!valid_task_state(_STOPPED_)) {
if (CRASHDEBUG(3))
fprintf(fp,
"initialize_task_state: using old defaults\n");
goto old_defaults;
}
}
/*
* Print multiple state strings if appropriate.
*/
static char *
task_state_string_verbose(ulong task, char *buf)
{
long state, both;
int count;
state = task_state(task);
buf[0] = NULLCHAR;
count = 0;
if (state == _RUNNING_) {
sprintf(buf, "TASK_RUNNING");
return buf;
}
if (state & _INTERRUPTIBLE_)
sprintf(&buf[strlen(buf)], "%sTASK_INTERRUPTIBLE",
count++ ? "|" : "");
if (state & _UNINTERRUPTIBLE_)
sprintf(&buf[strlen(buf)], "%sTASK_UNINTERRUPTIBLE",
count++ ? "|" : "");
if (state & _STOPPED_)
sprintf(&buf[strlen(buf)], "%sTASK_STOPPED",
count++ ? "|" : "");
if (state & _TRACING_STOPPED_)
sprintf(&buf[strlen(buf)], "%sTASK_TRACED",
count++ ? "|" : "");
if ((both = (state & _DEAD_))) {
if (count_bits_long(both) > 1)
sprintf(&buf[strlen(buf)], "%sEXIT_DEAD|TASK_DEAD",
count++ ? "|" : "");
else
sprintf(&buf[strlen(buf)], "%sEXIT_DEAD",
count++ ? "|" : "");
}
if (state & _ZOMBIE_)
sprintf(&buf[strlen(buf)], "%sEXIT_ZOMBIE",
count++ ? "|" : "");
if (valid_task_state(_WAKING_) && (state & _WAKING_))
sprintf(&buf[strlen(buf)], "%sTASK_WAKING",
count++ ? "|" : "");
if (valid_task_state(_WAKEKILL_) && (state & _WAKEKILL_))
sprintf(&buf[strlen(buf)], "%sTASK_WAKEKILL",
count++ ? "|" : "");
if (valid_task_state(_NOLOAD_) && (state & _NOLOAD_))
sprintf(&buf[strlen(buf)], "%sTASK_NOLOAD",
count++ ? "|" : "");
if (valid_task_state(_NONINTERACTIVE_) &&
(state & _NONINTERACTIVE_))
sprintf(&buf[strlen(buf)], "%sTASK_NONINTERACTIVE",
count++ ? "|" : "");
if (state == _PARKED_) {
sprintf(buf, "TASK_PARKED");
return buf;
}
return buf;
}
char *
task_state_string(ulong task, char *buf, int verbose)
{
long state;
int exclusive;
int valid, set;
if (_RUNNING_ == TASK_STATE_UNINITIALIZED)
initialize_task_state();
if (verbose)
return task_state_string_verbose(task, buf);
if (buf)
sprintf(buf, verbose ? "(unknown)" : "??");
state = task_state(task);
set = valid = exclusive = 0;
if (valid_task_state(_EXCLUSIVE_)) {
exclusive = state & _EXCLUSIVE_;
state &= ~(_EXCLUSIVE_);
}
if (state == _RUNNING_) {
sprintf(buf, "RU");
valid++;
}
if (state & _INTERRUPTIBLE_) {
sprintf(buf, "IN");
valid++;
set++;
}
if (state & _UNINTERRUPTIBLE_) {
sprintf(buf, "UN");
valid++;
set++;
}
if (state & _ZOMBIE_) {
sprintf(buf, "ZO");
valid++;
set++;
}
if (state & _STOPPED_) {
sprintf(buf, "ST");
valid++;
set++;
}
if (valid_task_state(_TRACING_STOPPED_) &&
(state & _TRACING_STOPPED_)) {
sprintf(buf, "TR");
valid++;
set++;
}
if (state == _SWAPPING_) {
sprintf(buf, "SW");
valid++;
set++;
}
if ((state & _DEAD_) && !set) {
sprintf(buf, "DE");
valid++;
set++;
}
if (state == _PARKED_) {
sprintf(buf, "PA");
valid++;
}
if (state == _WAKING_) {
sprintf(buf, "WA");
valid++;
}
if (valid && exclusive)
strcat(buf, "EX");
return buf;
}
/*
* Return a task's state and exit_state together.
*/
ulong
task_state(ulong task)
{
ulong state, exit_state;
fill_task_struct(task);
if (!tt->last_task_read)
return 0;
state = ULONG(tt->task_struct + OFFSET(task_struct_state));
exit_state = VALID_MEMBER(task_struct_exit_state) ?
ULONG(tt->task_struct + OFFSET(task_struct_exit_state)) : 0;
return (state | exit_state);
}
/*
* Return a task's flags.
*/
ulong
task_flags(ulong task)
{
ulong flags;
fill_task_struct(task);
if (tt->last_task_read) {
if (SIZE(task_struct_flags) == sizeof(unsigned int))
flags = UINT(tt->task_struct +
OFFSET(task_struct_flags));
else
flags = ULONG(tt->task_struct +
OFFSET(task_struct_flags));
} else
flags = 0;
return flags;
}
/*
* Return a task's tgid.
*/
ulong
task_tgid(ulong task)
{
uint tgid;
fill_task_struct(task);
tgid = tt->last_task_read ?
UINT(tt->task_struct + OFFSET(task_struct_tgid)) : 0;
return (ulong)tgid;
}
ulonglong
task_last_run(ulong task)
{
ulong last_run;
ulonglong timestamp;
timestamp = 0;
fill_task_struct(task);
if (VALID_MEMBER(task_struct_last_run)) {
last_run = tt->last_task_read ? ULONG(tt->task_struct +
OFFSET(task_struct_last_run)) : 0;
timestamp = (ulonglong)last_run;
} else if (VALID_MEMBER(task_struct_timestamp))
timestamp = tt->last_task_read ? ULONGLONG(tt->task_struct +
OFFSET(task_struct_timestamp)) : 0;
else if (VALID_MEMBER(sched_info_last_arrival))
timestamp = tt->last_task_read ? ULONGLONG(tt->task_struct +
OFFSET(task_struct_sched_info) +
OFFSET(sched_info_last_arrival)) : 0;
return timestamp;
}
/*
* Return a task's mm_struct address. If "fill" is set, the mm_struct
* cache is loaded.
*/
ulong
task_mm(ulong task, int fill)
{
ulong mm_struct;
fill_task_struct(task);
if (!tt->last_task_read)
return 0;
mm_struct = ULONG(tt->task_struct + OFFSET(task_struct_mm));
if (fill && mm_struct)
fill_mm_struct(mm_struct);
return mm_struct;
}
/*
* Translate a processor number into a string, taking NO_PROC_ID into account.
*/
char *
task_cpu(int processor, char *buf, int verbose)
{
if (processor < NR_CPUS)
sprintf(buf, "%d", processor);
else
sprintf(buf, verbose ? "(unknown)" : "?");
return buf;
}
/*
* Check either the panic_threads[] array on a dump, or the has_cpu flag
* of a task_struct on a live system. Also account for deprecation of
* usage of has_cpu on non-SMP systems.
*/
int
is_task_active(ulong task)
{
int has_cpu;
if (LOCAL_ACTIVE() && (task == tt->this_task))
return TRUE;
if (DUMPFILE() && is_panic_thread(task))
return TRUE;
fill_task_struct(task);
has_cpu = tt->last_task_read ?
task_has_cpu(task, tt->task_struct) : 0;
return(has_cpu);
}
/*
* Return true if a task is the panic_task or is contained within the
* panic_threads[] array.
*/
int
is_panic_thread(ulong task)
{
int i;
if (DUMPFILE()) {
if (tt->panic_task == task)
return TRUE;
for (i = 0; i < NR_CPUS; i++)
if (tt->panic_threads[i] == task)
return TRUE;
}
return FALSE;
}
/*
* Depending upon the kernel, check the task_struct's has_cpu or cpus_runnable
* field if either exist, or the global runqueues[].curr via get_active_set()
* to determine whether a task is running on a cpu.
*/
static int
task_has_cpu(ulong task, char *local_task)
{
int i, has_cpu;
ulong cpus_runnable;
if (DUMPFILE() && (task == tt->panic_task)) /* no need to continue */
return TRUE;
if (VALID_MEMBER(task_struct_has_cpu)) {
if (local_task)
has_cpu = INT(local_task+OFFSET(task_struct_has_cpu));
else if (!readmem((ulong)(task+OFFSET(task_struct_has_cpu)),
KVADDR, &has_cpu, sizeof(int),
"task_struct has_cpu", RETURN_ON_ERROR))
has_cpu = FALSE;
} else if (VALID_MEMBER(task_struct_cpus_runnable)) {
if (local_task)
cpus_runnable = ULONG(local_task +
OFFSET(task_struct_cpus_runnable));
else if (!readmem((ulong)(task +
OFFSET(task_struct_cpus_runnable)),
KVADDR, &cpus_runnable, sizeof(ulong),
"task_struct cpus_runnable", RETURN_ON_ERROR))
cpus_runnable = ~0UL;
has_cpu = (cpus_runnable != ~0UL);
} else if (get_active_set()) {
for (i = 0, has_cpu = FALSE; i < NR_CPUS; i++) {
if (task == tt->active_set[i]) {
has_cpu = TRUE;
break;
}
}
} else
error(FATAL,
"task_struct has no has_cpu, or cpus_runnable; runqueues[] not defined?\n");
return has_cpu;
}
/*
* If a task is in the panic_threads array and has an associated panic_ksp
* array entry, return it.
*/
int
get_panic_ksp(struct bt_info *bt, ulong *ksp)
{
int i;
if (tt->flags & PANIC_KSP) {
for (i = 0; i < NR_CPUS; i++) {
if ((tt->panic_threads[i] == bt->task) &&
tt->panic_ksp[i] &&
INSTACK(tt->panic_ksp[i], bt)) {
*ksp = tt->panic_ksp[i];
return TRUE;
}
}
}
return FALSE;
}
/*
* Look for kcore's storage information for the system's panic state.
* If it's not there (somebody else's dump format?), look through all the
* stack traces for evidence of panic.
*/
static ulong
get_panic_context(void)
{
int i;
struct task_context *tc;
ulong panic_threads_addr;
ulong task;
char *tp;
for (i = 0; i < NR_CPUS; i++) {
if (!(task = tt->active_set[i]))
continue;
if (!task_exists(task)) {
error(WARNING,
"active task %lx on cpu %d not found in PID hash\n\n",
task, i);
if ((tp = fill_task_struct(task))) {
if ((tc = store_context(NULL, task, tp)))
tt->running_tasks++;
else
continue;
}
}
}
/*
* --no_panic command line option
*/
if (tt->flags & PANIC_TASK_NOT_FOUND)
goto use_task_0;
tt->panic_processor = -1;
task = NO_TASK;
tc = FIRST_CONTEXT();
if (symbol_exists("panic_threads") &&
symbol_exists("panicmsg") &&
symbol_exists("panic_processor")) {
panic_threads_addr = symbol_value("panic_threads");
get_symbol_data("panic_processor", sizeof(int),
&tt->panic_processor);
get_symbol_data("panicmsg", sizeof(char *), &tt->panicmsg);
if (!readmem(panic_threads_addr, KVADDR, tt->panic_threads,
sizeof(void *)*NR_CPUS, "panic_processor array",
RETURN_ON_ERROR))
goto use_task_0;
task = tt->panic_threads[tt->panic_processor];
if (symbol_exists("panic_ksp")) {
if (!(tt->panic_ksp = (ulong *)
calloc(NR_CPUS, sizeof(void *))))
error(FATAL,
"cannot malloc panic_ksp array.\n");
readmem(symbol_value("panic_ksp"), KVADDR,
tt->panic_ksp,
sizeof(void *)*NR_CPUS, "panic_ksp array",
RETURN_ON_ERROR);
tt->flags |= PANIC_KSP;
}
if (machdep->flags & HWRESET) {
populate_panic_threads();
task = tt->panic_threads[0];
}
}
if (task && task_exists(task))
return(tt->panic_task = task);
if (task)
error(INFO, "reported panic task %lx does not exist!\n\n",
task);
if ((tc = panic_search())) {
tt->panic_processor = tc->processor;
return(tt->panic_task = tc->task);
}
use_task_0:
if (CRASHDEBUG(1))
error(INFO, "get_panic_context: panic task not found\n");
tt->flags |= PANIC_TASK_NOT_FOUND;
tc = FIRST_CONTEXT();
return(tc->task);
}
/*
* Get the active task on a cpu -- from a dumpfile only.
*/
ulong
get_active_task(int cpu)
{
int i;
ulong task;
struct task_context *tc;
if (DUMPFILE() && (task = tt->panic_threads[cpu]))
return task;
tc = FIRST_CONTEXT();
for (i = 0; i < RUNNING_TASKS(); i++, tc++) {
if ((tc->processor == cpu) && is_task_active(tc->task))
return(tc->task);
}
return NO_TASK;
}
/*
* Read the panic string.
*/
char *
get_panicmsg(char *buf)
{
int msg_found;
BZERO(buf, BUFSIZE);
msg_found = FALSE;
if (tt->panicmsg) {
read_string(tt->panicmsg, buf, BUFSIZE-1);
msg_found = TRUE;
} else if (LKCD_DUMPFILE()) {
get_lkcd_panicmsg(buf);
msg_found = TRUE;
}
if (msg_found == TRUE)
return(buf);
open_tmpfile();
dump_log(SHOW_LOG_TEXT);
/*
* First check for a SYSRQ-generated crash, and set the
* active-task flag appropriately. The message may or
* may not be used as the panic message.
*/
rewind(pc->tmpfile);
while (fgets(buf, BUFSIZE, pc->tmpfile)) {
if (strstr(buf, "SysRq : Crash") ||
strstr(buf, "SysRq : Trigger a crash")) {
pc->flags |= SYSRQ;
break;
}
}
rewind(pc->tmpfile);
while (!msg_found && fgets(buf, BUFSIZE, pc->tmpfile)) {
if (strstr(buf, "general protection fault: ") ||
strstr(buf, "double fault: ") ||
strstr(buf, "divide error: ") ||
strstr(buf, "stack segment: ")) {
msg_found = TRUE;
break;
}
}
rewind(pc->tmpfile);
while (!msg_found && fgets(buf, BUFSIZE, pc->tmpfile)) {
if (strstr(buf, "SysRq : Netdump") ||
strstr(buf, "SysRq : Crash") ||
strstr(buf, "SysRq : Trigger a crash")) {
pc->flags |= SYSRQ;
msg_found = TRUE;
break;
}
}
rewind(pc->tmpfile);
while (!msg_found && fgets(buf, BUFSIZE, pc->tmpfile)) {
if (strstr(buf, "Oops: ") ||
strstr(buf, "Kernel BUG at") ||
strstr(buf, "kernel BUG at") ||
strstr(buf, "Unable to handle kernel paging request") ||
strstr(buf, "Unable to handle kernel NULL pointer dereference") ||
strstr(buf, "BUG: unable to handle kernel "))
msg_found = TRUE;
}
rewind(pc->tmpfile);
while (!msg_found && fgets(buf, BUFSIZE, pc->tmpfile)) {
if (strstr(buf, "sysrq") &&
symbol_exists("sysrq_pressed")) {
get_symbol_data("sysrq_pressed", sizeof(int),
&msg_found);
break;
}
}
rewind(pc->tmpfile);
while (!msg_found && fgets(buf, BUFSIZE, pc->tmpfile)) {
if (strstr(buf, "Kernel panic: ") ||
strstr(buf, "Kernel panic - ")) {
msg_found = TRUE;
break;
}
}
rewind(pc->tmpfile);
while (!msg_found && fgets(buf, BUFSIZE, pc->tmpfile)) {
if (strstr(buf, "[Hardware Error]: ")) {
msg_found = TRUE;
break;
}
}
close_tmpfile();
if (!msg_found)
BZERO(buf, BUFSIZE);
return(buf);
}
/*
* This command allows the running of a set of commands on any or all
* tasks running on a system. The target tasks may be designated by
* pid, task or command name. The available command set is designated by
* the FOREACH_xxx definitions below. If a running command name string
* conflicts with a foreach command, the command name string may be
* prefixed with a \ character.
*/
void
cmd_foreach(void)
{
int a, c, k, t, p;
ulong value;
static struct foreach_data foreach_data;
struct foreach_data *fd;
struct task_context *tc;
char *p1;
int key;
BZERO(&foreach_data, sizeof(struct foreach_data));
fd = &foreach_data;
while ((c = getopt(argcnt, args, "R:vomlgersStTpukcfFxhdaG")) != EOF) {
switch(c)
{
case 'R':
fd->reference = optarg;
break;
case 'h':
case 'x':
fd->flags |= FOREACH_x_FLAG;
break;
case 'd':
fd->flags |= FOREACH_d_FLAG;
break;
case 'v':
fd->flags |= FOREACH_v_FLAG;
break;
case 'm':
fd->flags |= FOREACH_m_FLAG;
break;
case 'l':
fd->flags |= FOREACH_l_FLAG;
break;
case 'o':
fd->flags |= FOREACH_o_FLAG;
break;
case 'g':
fd->flags |= FOREACH_g_FLAG;
break;
case 'e':
fd->flags |= FOREACH_e_FLAG;
break;
case 's':
fd->flags |= FOREACH_s_FLAG;
break;
case 'S':
fd->flags |= FOREACH_S_FLAG;
break;
case 'r':
fd->flags |= FOREACH_r_FLAG;
break;
case 'T':
fd->flags |= FOREACH_T_FLAG;
break;
case 't':
fd->flags |= FOREACH_t_FLAG;
break;
case 'p':
fd->flags |= FOREACH_p_FLAG;
break;
case 'u':
fd->flags |= FOREACH_u_FLAG;
break;
case 'k':
fd->flags |= FOREACH_k_FLAG;
break;
case 'c':
fd->flags |= FOREACH_c_FLAG;
break;
case 'f':
fd->flags |= FOREACH_f_FLAG;
break;
case 'F':
if (fd->flags & FOREACH_F_FLAG)
fd->flags |= FOREACH_F_FLAG2;
else
fd->flags |= FOREACH_F_FLAG;
break;
case 'a':
fd->flags |= FOREACH_a_FLAG;
break;
case 'G':
fd->flags |= FOREACH_G_FLAG;
break;
default:
argerrs++;
break;
}
}
if (argerrs || !args[optind])
cmd_usage(pc->curcmd, SYNOPSIS);
a = c = k = t = p = 0;
while (args[optind]) {
/*
* Once a keyword has been entered, then only accept
* command arguments.
*/
if (k) {
p1 = args[optind];
goto command_argument;
}
/*
* If it's a keyword, grab it and check no further.
*/
if (is_foreach_keyword(args[optind], &key)) {
if (k == MAX_FOREACH_KEYWORDS)
error(INFO, "too many keywords!\n");
else
fd->keyword_array[k++] = key;
optind++;
continue;
}
/*
* If it's a task pointer or pid, take it.
*/
if (IS_A_NUMBER(args[optind])) {
if (STREQ(args[optind], "DE") && pid_exists(0xde)) {
error(INFO, "ambiguous task-identifying argument: %s\n", args[optind]);
error(CONT, "for a \"state\" argument, use: \\DE\n");
error(CONT, "for a \"pid\" argument, use: 0xDE, 0xde, de or 222\n\n");
cmd_usage(pc->curcmd, SYNOPSIS);
return;
}
switch (str_to_context(args[optind], &value, &tc))
{
case STR_PID:
if (p == MAX_FOREACH_PIDS)
error(INFO,
"too many pids specified!\n");
else {
fd->pid_array[p++] = value;
fd->flags |= FOREACH_SPECIFIED;
}
optind++;
continue;
case STR_TASK:
if (t == MAX_FOREACH_TASKS)
error(INFO,
"too many tasks specified!\n");
else {
fd->task_array[t++] = value;
fd->flags |= FOREACH_SPECIFIED;
}
optind++;
continue;
case STR_INVALID:
break;
}
}
/*
* Select all kernel threads.
*/
if (STREQ(args[optind], "kernel")) {
if (fd->flags & FOREACH_USER)
error(FATAL,
"user and kernel are mutually exclusive!\n");
fd->flags |= FOREACH_KERNEL;
optind++;
continue;
}
if ((args[optind][0] == '\\') &&
STREQ(&args[optind][1], "DE"))
shift_string_left(args[optind], 1);
if (STREQ(args[optind], "RU") ||
STREQ(args[optind], "IN") ||
STREQ(args[optind], "UN") ||
STREQ(args[optind], "ST") ||
STREQ(args[optind], "TR") ||
STREQ(args[optind], "ZO") ||
STREQ(args[optind], "DE") ||
STREQ(args[optind], "PA") ||
STREQ(args[optind], "WA") ||
STREQ(args[optind], "SW")) {
if (fd->flags & FOREACH_STATE)
error(FATAL, "only one task state allowed\n");
if (STREQ(args[optind], "RU"))
fd->state = _RUNNING_;
else if (STREQ(args[optind], "IN"))
fd->state = _INTERRUPTIBLE_;
else if (STREQ(args[optind], "UN"))
fd->state = _UNINTERRUPTIBLE_;
else if (STREQ(args[optind], "ST"))
fd->state = _STOPPED_;
else if (STREQ(args[optind], "TR"))
fd->state = _TRACING_STOPPED_;
else if (STREQ(args[optind], "ZO"))
fd->state = _ZOMBIE_;
else if (STREQ(args[optind], "DE"))
fd->state = _DEAD_;
else if (STREQ(args[optind], "SW"))
fd->state = _SWAPPING_;
else if (STREQ(args[optind], "PA"))
fd->state = _PARKED_;
else if (STREQ(args[optind], "WA"))
fd->state = _WAKING_;
if (fd->state == TASK_STATE_UNINITIALIZED)
error(FATAL,
"invalid task state for this kernel: %s\n",
args[optind]);
fd->flags |= FOREACH_STATE;
optind++;
continue;
}
/*
* Select only user threads.
*/
if (STREQ(args[optind], "user")) {
if (fd->flags & FOREACH_KERNEL)
error(FATAL,
"user and kernel are mutually exclusive!\n");
fd->flags |= FOREACH_USER;
optind++;
continue;
}
/*
* Select only active tasks (dumpfile only)
*/
if (STREQ(args[optind], "active")) {
if (!DUMPFILE())
error(FATAL,
"active option not allowed on live systems\n");
fd->flags |= FOREACH_ACTIVE;
optind++;
continue;
}
/*
* Regular expression is exclosed within "'" character.
* The args[optind] string may not be modified, so a copy
* is duplicated.
*/
if (SINGLE_QUOTED_STRING(args[optind])) {
if (fd->regexs == MAX_REGEX_ARGS)
error(INFO, "too many expressions specified!\n");
else {
p1 = strdup(&args[optind][1]);
LASTCHAR(p1) = NULLCHAR;
if (regcomp(&fd->regex_info[fd->regexs].regex, p1,
REG_EXTENDED|REG_NOSUB)) {
error(INFO,
"invalid regular expression: %s\n",
p1);
free(p1);
goto bailout;
}
fd->regex_info[fd->regexs].pattern = p1;
if (fd->regexs++ == 0) {
pc->cmd_cleanup_arg = (void *)fd;
pc->cmd_cleanup = foreach_cleanup;
}
}
optind++;
continue;
}
/*
* If it's a command name, prefixed or otherwise, take it.
*/
p1 = (args[optind][0] == '\\') ?
&args[optind][1] : args[optind];
if (comm_exists(p1)) {
if (c == MAX_FOREACH_COMMS)
error(INFO, "too many commands specified!\n");
else {
fd->comm_array[c++] = p1;
fd->flags |= FOREACH_SPECIFIED;
}
optind++;
continue;
}
command_argument:
/*
* If no keyword has been entered, we don't know what this
* is -- most likely it's a bogus command specifier. We set
* FOREACH_SPECIFIED in case it was a bad specifier and no
* other task selectors exist -- which in turn would causes
* the command to be erroneously run on all tasks.
*/
if (!k) {
fd->flags |= FOREACH_SPECIFIED;
error(INFO, "unknown argument: \"%s\"\n",
args[optind]);
optind++;
continue;
}
/*
* Must be an command argument -- so store it and let
* the command deal with it...
*/
if (a == MAX_FOREACH_ARGS)
error(INFO, "too many arguments specified!\n");
else
fd->arg_array[a++] = (ulong)p1;
optind++;
}
fd->flags |= FOREACH_CMD;
fd->pids = p;
fd->keys = k;
fd->comms = c;
fd->tasks = t;
fd->args = a;
if (fd->keys)
foreach(fd);
else
error(INFO, "no keywords specified\n");
bailout:
foreach_cleanup((void *)fd);
}
/*
* Do the work for cmd_foreach().
*/
void
foreach(struct foreach_data *fd)
{
int i, j, k, a;
struct task_context *tc, *tgc;
int specified;
int doit;
int subsequent;
unsigned int radix;
ulong cmdflags;
ulong tgid;
struct reference reference, *ref;
int print_header;
struct bt_info bt_info, *bt;
char buf[TASK_COMM_LEN];
struct psinfo psinfo;
/*
* Filter out any command/option issues.
*/
if (CRASHDEBUG(1)) {
fprintf(fp, " flags: %lx\n", fd->flags);
fprintf(fp, " task_array: %s", fd->tasks ? "" : "(none)");
for (j = 0; j < fd->tasks; j++)
fprintf(fp, "[%lx] ", fd->task_array[j]);
fprintf(fp, "\n");
fprintf(fp, " pid_array: %s", fd->pids ? "" : "(none)");
for (j = 0; j < fd->pids; j++)
fprintf(fp, "[%ld] ", fd->pid_array[j]);
fprintf(fp, "\n");
fprintf(fp, " comm_array: %s", fd->comms ? "" : "(none)");
for (j = 0; j < fd->comms; j++)
fprintf(fp, "[%s] ", fd->comm_array[j]);
fprintf(fp, "\n");
fprintf(fp, " regex_info: %s", fd->regexs ? "" : "(none)\n");
for (j = 0; j < fd->regexs; j++) {
fprintf(fp, "%s[%d] pattern: [%s] ",
j ? " " : "",
j, fd->regex_info[j].pattern);
fprintf(fp, "regex: [%lx]\n",
(ulong)&fd->regex_info[j].regex);
}
fprintf(fp, "\n");
fprintf(fp, "keyword_array: %s", fd->keys ? "" : "(none)");
for (k = 0; k < fd->keys; k++)
fprintf(fp, "[%d] ", fd->keyword_array[k]);
fprintf(fp, "\n");
fprintf(fp, " arg_array: %s", fd->args ? "" : "(none)");
for (a = 0; a < fd->args; a++)
fprintf(fp, "[%lx (%s)] ",
fd->arg_array[a],
(char *)fd->arg_array[a]);
fprintf(fp, "\n");
fprintf(fp, " reference: \"%s\"\n",
fd->reference ? fd->reference : "");
}
print_header = TRUE;
bt = NULL;
for (k = 0; k < fd->keys; k++) {
switch(fd->keyword_array[k])
{
case FOREACH_NET:
switch (fd->flags & (FOREACH_s_FLAG|FOREACH_S_FLAG))
{
case (FOREACH_s_FLAG|FOREACH_S_FLAG):
error(WARNING,
"net -s and -S options are mutually exclusive!\n");
fd->flags = FOREACH_s_FLAG;
break;
case 0:
error(WARNING,
"net command requires -s or -S option\n\n");
fd->flags |= FOREACH_s_FLAG;
break;
}
if ((fd->flags & (FOREACH_x_FLAG|FOREACH_d_FLAG)) ==
(FOREACH_x_FLAG|FOREACH_d_FLAG))
error(FATAL,
"net: -x and -d options are mutually exclusive\n");
break;
case FOREACH_VTOP:
if (!fd->args)
error(FATAL,
"foreach command requires address argument\n");
if (fd->reference)
error(FATAL,
"vtop command does not support -R option\n");
if ((fd->flags & (FOREACH_u_FLAG|FOREACH_k_FLAG)) ==
(FOREACH_u_FLAG|FOREACH_k_FLAG))
error(FATAL,
"vtop: -u and -k options are mutually exclusive\n");
break;
case FOREACH_VM:
if ((fd->flags & (FOREACH_x_FLAG|FOREACH_d_FLAG)) ==
(FOREACH_x_FLAG|FOREACH_d_FLAG))
error(FATAL,
"vm: -x and -d options are mutually exclusive\n");
if (count_bits_long(fd->flags &
(FOREACH_i_FLAG|FOREACH_p_FLAG|
FOREACH_m_FLAG|FOREACH_v_FLAG)) > 1)
error(FATAL,
"vm command accepts only one of -p, -m or -v flags\n");
if (fd->reference) {
if (fd->flags & FOREACH_i_FLAG)
error(FATAL,
"vm: -i is not applicable to the -R option\n");
if (fd->flags & FOREACH_m_FLAG)
error(FATAL,
"vm: -m is not applicable to the -R option\n");
if (fd->flags & FOREACH_v_FLAG)
error(FATAL,
"vm: -v is not applicable to the -R option\n");
}
break;
case FOREACH_BT:
if ((fd->flags & (FOREACH_x_FLAG|FOREACH_d_FLAG)) ==
(FOREACH_x_FLAG|FOREACH_d_FLAG))
error(FATAL,
"bt: -x and -d options are mutually exclusive\n");
if ((fd->flags & FOREACH_l_FLAG) && NO_LINE_NUMBERS()) {
error(INFO, "line numbers are not available\n");
fd->flags &= ~FOREACH_l_FLAG;
}
#ifndef GDB_5_3
if ((fd->flags & FOREACH_g_FLAG))
error(FATAL,
"bt -g option is not supported when issued from foreach\n");
#endif
bt = &bt_info;
break;
case FOREACH_TASK:
if ((fd->flags & (FOREACH_x_FLAG|FOREACH_d_FLAG)) ==
(FOREACH_x_FLAG|FOREACH_d_FLAG))
error(FATAL,
"task: -x and -d options are mutually exclusive\n");
if (count_bits_long(fd->flags &
(FOREACH_x_FLAG|FOREACH_d_FLAG)) > 1)
error(FATAL,
"task command accepts -R member[,member],"
" and either -x or -d flags\n");
break;
case FOREACH_SET:
if (fd->reference)
error(FATAL,
"set command does not support -R option\n");
break;
case FOREACH_SIG:
if (fd->flags & (FOREACH_l_FLAG|FOREACH_s_FLAG))
error(FATAL,
"sig: -l and -s options are not applicable\n");
if (fd->flags & FOREACH_g_FLAG) {
if (!hq_open()) {
error(INFO,
"cannot hash thread group tasks\n");
fd->flags &= ~FOREACH_g_FLAG;
} else
print_header = FALSE;
}
break;
case FOREACH_PS:
if (count_bits_long(fd->flags & FOREACH_PS_EXCLUSIVE) > 1)
error(FATAL, ps_exclusive);
if ((fd->flags & (FOREACH_l_FLAG|FOREACH_m_FLAG)) &&
(fd->flags & FOREACH_G_FLAG))
error(FATAL, "-G not supported with -%c option\n",
fd->flags & FOREACH_l_FLAG ? 'l' : 'm');
BZERO(&psinfo, sizeof(struct psinfo));
if (fd->flags & FOREACH_G_FLAG) {
if (!hq_open()) {
error(INFO,
"cannot hash thread group tasks\n");
fd->flags &= ~FOREACH_G_FLAG;
}
}
if (fd->flags & (FOREACH_l_FLAG|FOREACH_m_FLAG))
sort_context_array_by_last_run();
if ((fd->flags & FOREACH_m_FLAG) &&
INVALID_MEMBER(rq_timestamp))
option_not_supported('m');
print_header = FALSE;
break;
case FOREACH_FILES:
if (fd->flags & FOREACH_p_FLAG)
error(FATAL,
"files command does not support -p option\n");
break;
case FOREACH_TEST:
break;
}
}
subsequent = FALSE;
specified = (fd->tasks || fd->pids || fd->comms || fd->regexs ||
(fd->flags & FOREACH_SPECIFIED));
ref = &reference;
tc = FIRST_CONTEXT();
for (i = 0; i < RUNNING_TASKS(); i++, tc++) {
doit = FALSE;
if ((fd->flags & FOREACH_ACTIVE) && !is_task_active(tc->task))
continue;
if ((fd->flags & FOREACH_USER) && is_kernel_thread(tc->task))
continue;
if ((fd->flags & FOREACH_KERNEL) && !is_kernel_thread(tc->task))
continue;
if (fd->flags & FOREACH_STATE) {
if (fd->state == _RUNNING_) {
if (task_state(tc->task) != _RUNNING_)
continue;
} else if (!(task_state(tc->task) & fd->state))
continue;
}
if (specified) {
for (j = 0; j < fd->tasks; j++) {
if (fd->task_array[j] == tc->task) {
doit = TRUE;
break;
}
}
for (j = 0; !doit && (j < fd->pids); j++) {
if (fd->pid_array[j] == tc->pid) {
doit = TRUE;
break;
}
}
for (j = 0; !doit && (j < fd->comms); j++) {
strlcpy(buf, fd->comm_array[j], TASK_COMM_LEN);
if (STREQ(buf, tc->comm)) {
doit = TRUE;
break;
}
}
for (j = 0; !doit && (j < fd->regexs); j++) {
if (regexec(&fd->regex_info[j].regex,
tc->comm, 0, NULL, 0) == 0) {
doit = TRUE;
break;
}
}
} else
doit = TRUE;
if (!doit)
continue;
if (output_closed() || received_SIGINT()) {
free_all_bufs();
goto foreach_bailout;
}
if (setjmp(pc->foreach_loop_env)) {
free_all_bufs();
continue;
}
pc->flags |= IN_FOREACH;
if (fd->reference) {
BZERO(ref, sizeof(struct reference));
ref->str = fd->reference;
} else if (print_header)
print_task_header(fp, tc, subsequent++);
for (k = 0; k < fd->keys; k++) {
free_all_bufs();
switch(fd->keyword_array[k])
{
case FOREACH_BT:
pc->curcmd = "bt";
BZERO(bt, sizeof(struct bt_info));;
bt->task = tc->task;
bt->tc = tc;
bt->stackbase = GET_STACKBASE(tc->task);
bt->stacktop = GET_STACKTOP(tc->task);
if (fd->flags & FOREACH_r_FLAG)
bt->flags |= BT_RAW;
if (fd->flags & FOREACH_s_FLAG)
bt->flags |= BT_SYMBOL_OFFSET;
if (fd->flags & FOREACH_t_FLAG)
bt->flags |= BT_TEXT_SYMBOLS;
if (fd->flags & FOREACH_T_FLAG) {
bt->flags |= BT_TEXT_SYMBOLS;
bt->flags |= BT_TEXT_SYMBOLS_ALL;
}
if ((fd->flags & FOREACH_o_FLAG) ||
(kt->flags & USE_OLD_BT))
bt->flags |= BT_OLD_BACK_TRACE;
if (fd->flags & FOREACH_e_FLAG)
bt->flags |= BT_EFRAME_SEARCH;
#ifdef GDB_5_3
if (fd->flags & FOREACH_g_FLAG)
bt->flags |= BT_USE_GDB;
#endif
if (fd->flags & FOREACH_l_FLAG)
bt->flags |= BT_LINE_NUMBERS;
if (fd->flags & FOREACH_f_FLAG)
bt->flags |= BT_FULL;
if (fd->flags & FOREACH_F_FLAG)
bt->flags |= (BT_FULL|BT_FULL_SYM_SLAB);
if (fd->flags & FOREACH_F_FLAG2)
bt->flags |= BT_FULL_SYM_SLAB2;
if (fd->flags & FOREACH_x_FLAG)
bt->radix = 16;
if (fd->flags & FOREACH_d_FLAG)
bt->radix = 10;
if (fd->reference)
bt->ref = ref;
back_trace(bt);
break;
case FOREACH_VM:
pc->curcmd = "vm";
cmdflags = 0;
if (fd->flags & FOREACH_x_FLAG)
cmdflags = PRINT_RADIX_16;
else if (fd->flags & FOREACH_d_FLAG)
cmdflags = PRINT_RADIX_10;
if (fd->flags & FOREACH_i_FLAG)
vm_area_dump(tc->task,
PRINT_INODES, 0, NULL);
else if (fd->flags & FOREACH_p_FLAG)
vm_area_dump(tc->task,
PHYSADDR, 0,
fd->reference ? ref : NULL);
else if (fd->flags & FOREACH_m_FLAG)
vm_area_dump(tc->task,
PRINT_MM_STRUCT|cmdflags, 0, NULL);
else if (fd->flags & FOREACH_v_FLAG)
vm_area_dump(tc->task,
PRINT_VMA_STRUCTS|cmdflags, 0, NULL);
else
vm_area_dump(tc->task, 0, 0,
fd->reference ? ref : NULL);
break;
case FOREACH_TASK:
pc->curcmd = "task";
if (fd->flags & FOREACH_x_FLAG)
radix = 16;
else if (fd->flags & FOREACH_d_FLAG)
radix = 10;
else
radix = pc->output_radix;
do_task(tc->task, FOREACH_TASK,
fd->reference ? ref : NULL,
radix);
break;
case FOREACH_SIG:
pc->curcmd = "sig";
if (fd->flags & FOREACH_g_FLAG) {
tgid = task_tgid(tc->task);
tgc = tgid_to_context(tgid);
if (hq_enter(tgc->task))
do_sig_thread_group(tgc->task);
} else
do_sig(tc->task, FOREACH_SIG,
fd->reference ? ref : NULL);
break;
case FOREACH_SET:
pc->curcmd = "set";
show_context(tc);
break;
case FOREACH_PS:
pc->curcmd = "ps";
psinfo.task[0] = tc->task;
psinfo.pid[0] = NO_PID;
psinfo.type[0] = PS_BY_TASK;
psinfo.argc = 1;
cmdflags = PS_BY_TASK;
if (subsequent++)
cmdflags |= PS_NO_HEADER;
if (fd->flags & FOREACH_G_FLAG)
cmdflags |= PS_GROUP;
if (fd->flags & FOREACH_s_FLAG)
cmdflags |= PS_KSTACKP;
/*
* mutually exclusive flags
*/
if (fd->flags & FOREACH_a_FLAG)
cmdflags |= PS_ARGV_ENVP;
else if (fd->flags & FOREACH_c_FLAG)
cmdflags |= PS_CHILD_LIST;
else if (fd->flags & FOREACH_p_FLAG)
cmdflags |= PS_PPID_LIST;
else if (fd->flags & FOREACH_t_FLAG)
cmdflags |= PS_TIMES;
else if (fd->flags & FOREACH_l_FLAG)
cmdflags |= PS_LAST_RUN;
else if (fd->flags & FOREACH_m_FLAG)
cmdflags |= PS_MSECS;
else if (fd->flags & FOREACH_r_FLAG)
cmdflags |= PS_RLIMIT;
else if (fd->flags & FOREACH_g_FLAG)
cmdflags |= PS_TGID_LIST;
show_ps(cmdflags, &psinfo);
break;
case FOREACH_FILES:
pc->curcmd = "files";
cmdflags = 0;
if (fd->flags & FOREACH_i_FLAG)
cmdflags |= PRINT_INODES;
if (fd->flags & FOREACH_c_FLAG)
cmdflags |= PRINT_NRPAGES;
open_files_dump(tc->task,
cmdflags,
fd->reference ? ref : NULL);
break;
case FOREACH_NET:
pc->curcmd = "net";
if (fd->flags & (FOREACH_s_FLAG|FOREACH_S_FLAG))
dump_sockets_workhorse(tc->task,
fd->flags,
fd->reference ? ref : NULL);
break;
case FOREACH_VTOP:
pc->curcmd = "vtop";
cmdflags = 0;
if (fd->flags & FOREACH_c_FLAG)
cmdflags |= USE_USER_PGD;
if (fd->flags & FOREACH_u_FLAG)
cmdflags |= UVADDR;
if (fd->flags & FOREACH_k_FLAG)
cmdflags |= KVADDR;
for (a = 0; a < fd->args; a++) {
do_vtop(htol((char *)fd->arg_array[a],
FAULT_ON_ERROR, NULL), tc,
cmdflags);
}
break;
case FOREACH_TEST:
pc->curcmd = "test";
foreach_test(tc->task, 0);
break;
}
pc->curcmd = "foreach";
}
}
/*
* Post-process any commands requiring it.
*/
for (k = 0; k < fd->keys; k++) {
switch(fd->keyword_array[k])
{
case FOREACH_SIG:
if (fd->flags & FOREACH_g_FLAG)
hq_close();
break;
}
}
foreach_bailout:
pc->flags &= ~IN_FOREACH;
}
/*
* Clean up regex buffers and pattern strings.
*/
static void
foreach_cleanup(void *arg)
{
int i;
struct foreach_data *fd;
pc->cmd_cleanup = NULL;
pc->cmd_cleanup_arg = NULL;
fd = (struct foreach_data *)arg;
for (i = 0; i < fd->regexs; i++) {
regfree(&fd->regex_info[i].regex);
free(fd->regex_info[i].pattern);
}
}
/*
* The currently available set of foreach commands.
*/
static int
is_foreach_keyword(char *s, int *key)
{
if (STREQ(args[optind], "bt")) {
*key = FOREACH_BT;
return TRUE;
}
if (STREQ(args[optind], "vm")) {
*key = FOREACH_VM;
return TRUE;
}
if (STREQ(args[optind], "task")) {
*key = FOREACH_TASK;
return TRUE;
}
if (STREQ(args[optind], "set")) {
*key = FOREACH_SET;
return TRUE;
}
if (STREQ(args[optind], "files")) {
*key = FOREACH_FILES;
return TRUE;
}
if (STREQ(args[optind], "net")) {
*key = FOREACH_NET;
return TRUE;
}
if (STREQ(args[optind], "vtop")) {
*key = FOREACH_VTOP;
return TRUE;
}
if (STREQ(args[optind], "sig")) {
*key = FOREACH_SIG;
return TRUE;
}
if (STREQ(args[optind], "test")) {
*key = FOREACH_TEST;
return TRUE;
}
if (STREQ(args[optind], "ps")) {
*key = FOREACH_PS;
return TRUE;
}
return FALSE;
}
/*
* Try the dumpfile-specific manner of finding the panic task first. If
* that fails, find the panic task the hard way -- do a "foreach bt" in the
* background, and look for the only one that has "panic" embedded in it.
*/
static struct task_context *
panic_search(void)
{
struct foreach_data foreach_data, *fd;
char *p1, *p2, *tp;
ulong lasttask, dietask, found;
char buf[BUFSIZE];
struct task_context *tc;
if ((lasttask = get_dumpfile_panic_task())) {
found = TRUE;
goto found_panic_task;
}
if (pc->flags2 & LIVE_DUMP)
return NULL;
BZERO(&foreach_data, sizeof(struct foreach_data));
fd = &foreach_data;
fd->keys = 1;
fd->keyword_array[0] = FOREACH_BT;
if (machine_type("S390X"))
fd->flags |= FOREACH_o_FLAG;
else
fd->flags |= (FOREACH_t_FLAG|FOREACH_o_FLAG);
dietask = lasttask = NO_TASK;
found = FALSE;
open_tmpfile();
foreach(fd);
rewind(pc->tmpfile);
while (fgets(buf, BUFSIZE, pc->tmpfile)) {
if ((p1 = strstr(buf, " TASK: "))) {
p1 += strlen(" TASK: ");
p2 = p1;
while (!whitespace(*p2))
p2++;
*p2 = NULLCHAR;
lasttask = htol(p1, RETURN_ON_ERROR, NULL);
}
if (strstr(buf, " panic at ")) {
found = TRUE;
break;
}
if (strstr(buf, " crash_kexec at ") ||
strstr(buf, " .crash_kexec at ")) {
found = TRUE;
break;
}
if (strstr(buf, " die at ")) {
switch (dietask)
{
case NO_TASK:
dietask = lasttask;
break;
default:
if (dietask != lasttask)
dietask = NO_TASK+1;
break;
}
}
}
close_tmpfile();
if (!found && (dietask > (NO_TASK+1)) && task_has_cpu(dietask, NULL)) {
lasttask = dietask;
found = TRUE;
}
if (dietask == (NO_TASK+1))
error(WARNING, "multiple active tasks have called die\n\n");
if (CRASHDEBUG(1) && found)
error(INFO, "panic_search: %lx (via foreach bt)\n",
lasttask);
found_panic_task:
populate_panic_threads();
if (found) {
if ((tc = task_to_context(lasttask)))
return tc;
/*
* If the task list was corrupted, add this one in.
*/
if ((tp = fill_task_struct(lasttask))) {
if ((tc = store_context(NULL, lasttask, tp))) {
tt->running_tasks++;
return tc;
}
}
}
if (CRASHDEBUG(1))
error(INFO, "panic_search: failed (via foreach bt)\n");
return NULL;
}
/*
* Get the panic task from the appropriate dumpfile handler.
*/
static ulong
get_dumpfile_panic_task(void)
{
ulong task;
if (NETDUMP_DUMPFILE()) {
task = pc->flags & REM_NETDUMP ?
tt->panic_task : get_netdump_panic_task();
if (task)
return task;
} else if (KDUMP_DUMPFILE()) {
task = get_kdump_panic_task();
if (task)
return task;
} else if (DISKDUMP_DUMPFILE()) {
task = get_diskdump_panic_task();
if (task)
return task;
} else if (KVMDUMP_DUMPFILE()) {
task = get_kvmdump_panic_task();
if (task)
return task;
} else if (XENDUMP_DUMPFILE()) {
task = get_xendump_panic_task();
if (task)
return task;
} else if (LKCD_DUMPFILE())
return(get_lkcd_panic_task());
if (pc->flags2 & LIVE_DUMP)
return NO_TASK;
if (get_active_set())
return(get_active_set_panic_task());
return NO_TASK;
}
/*
* If runqueues is defined in the kernel, get the panic threads from the
* active set.
*
* If it's an LKCD dump, or for some other reason the active threads cannot
* be determined, do it the hard way.
*
* NOTE: this function should be deprecated -- the work should have been
* done in the initial task table refresh.
*/
static void
populate_panic_threads(void)
{
int i;
int found;
struct task_context *tc;
if (get_active_set()) {
for (i = 0; i < NR_CPUS; i++)
tt->panic_threads[i] = tt->active_set[i];
return;
}
found = 0;
if (!(machdep->flags & HWRESET)) {
for (i = 0; i < kt->cpus; i++) {
if (tt->panic_threads[i]) {
if (++found == kt->cpus)
return;
}
}
}
tc = FIRST_CONTEXT();
for (i = 0; i < RUNNING_TASKS(); i++, tc++) {
if (task_has_cpu(tc->task, NULL) &&
(tc->processor >= 0) &&
(tc->processor < NR_CPUS)) {
tt->panic_threads[tc->processor] = tc->task;
found++;
}
}
if (!found && !(kt->flags & SMP) &&
(LKCD_DUMPFILE() || NETDUMP_DUMPFILE() ||
KDUMP_DUMPFILE() || DISKDUMP_DUMPFILE() || KVMDUMP_DUMPFILE()))
tt->panic_threads[0] = get_dumpfile_panic_task();
}
/*
* Separate the foreach command's output on a task-by-task basis by
* displaying this header string.
*/
void
print_task_header(FILE *out, struct task_context *tc, int newline)
{
char buf[BUFSIZE];
char buf1[BUFSIZE];
fprintf(out, "%sPID: %-5ld TASK: %s CPU: %-2s COMMAND: \"%s\"\n",
newline ? "\n" : "", tc->pid,
mkstring(buf1, VADDR_PRLEN, LJUST|LONG_HEX, MKSTR(tc->task)),
task_cpu(tc->processor, buf, !VERBOSE), tc->comm);
}
/*
* "help -t" output
*/
void
dump_task_table(int verbose)
{
int i, j, more, nr_cpus;
struct task_context *tc;
struct tgid_context *tg;
char buf[BUFSIZE];
int others, wrap, flen;
tc = tt->current;
others = 0;
more = FALSE;
fprintf(fp, " current: %lx [%ld]\n", (ulong)tt->current,
(ulong)(tt->current - tt->context_array));
if (tt->current) {
fprintf(fp, " .pid: %ld\n", tc->pid);
fprintf(fp, " .comm: \"%s\"\n", tc->comm);
fprintf(fp, " .task: %lx\n", tc->task);
fprintf(fp, " .thread_info: %lx\n", tc->thread_info);
fprintf(fp, " .processor: %d\n", tc->processor);
fprintf(fp, " .ptask: %lx\n", tc->ptask);
fprintf(fp, " .mm_struct: %lx\n", tc->mm_struct);
fprintf(fp, " .tc_next: %lx\n", (ulong)tc->tc_next);
}
fprintf(fp, " context_array: %lx\n", (ulong)tt->context_array);
fprintf(fp, " tgid_array: %lx\n", (ulong)tt->tgid_array);
fprintf(fp, " tgid_searches: %ld\n", tt->tgid_searches);
fprintf(fp, " tgid_cache_hits: %ld (%ld%%)\n", tt->tgid_cache_hits,
tt->tgid_searches ?
tt->tgid_cache_hits * 100 / tt->tgid_searches : 0);
fprintf(fp, " last_tgid: %lx\n", (ulong)tt->last_tgid);
fprintf(fp, "refresh_task_table: ");
if (tt->refresh_task_table == refresh_fixed_task_table)
fprintf(fp, "refresh_fixed_task_table()\n");
else if (tt->refresh_task_table == refresh_unlimited_task_table)
fprintf(fp, "refresh_unlimited_task_table()\n");
else if (tt->refresh_task_table == refresh_pidhash_task_table)
fprintf(fp, "refresh_pidhash_task_table()\n");
else if (tt->refresh_task_table == refresh_pid_hash_task_table)
fprintf(fp, "refresh_pid_hash_task_table()\n");
else if (tt->refresh_task_table == refresh_hlist_task_table)
fprintf(fp, "refresh_hlist_task_table()\n");
else if (tt->refresh_task_table == refresh_hlist_task_table_v2)
fprintf(fp, "refresh_hlist_task_table_v2()\n");
else if (tt->refresh_task_table == refresh_hlist_task_table_v3)
fprintf(fp, "refresh_hlist_task_table_v3()\n");
else if (tt->refresh_task_table == refresh_active_task_table)
fprintf(fp, "refresh_active_task_table()\n");
else
fprintf(fp, "%lx\n", (ulong)tt->refresh_task_table);
buf[0] = NULLCHAR;
fprintf(fp, " flags: %lx ", tt->flags);
sprintf(buf, "(");
if (tt->flags & TASK_INIT_DONE)
sprintf(&buf[strlen(buf)],
"%sTASK_INIT_DONE", others++ ? "|" : "");
if (tt->flags & TASK_ARRAY_EXISTS)
sprintf(&buf[strlen(buf)],
"%sTASK_ARRAY_EXISTS", others++ ? "|" : "");
if (tt->flags & PANIC_TASK_NOT_FOUND)
sprintf(&buf[strlen(buf)],
"%sPANIC_TASK_NOT_FOUND", others++ ? "|" : "");
if (tt->flags & TASK_REFRESH)
sprintf(&buf[strlen(buf)],
"%sTASK_REFRESH", others++ ? "|" : "");
if (tt->flags & TASK_REFRESH_OFF)
sprintf(&buf[strlen(buf)],
"%sTASK_REFRESH_OFF", others++ ? "|" : "");
if (tt->flags & PANIC_KSP)
sprintf(&buf[strlen(buf)],
"%sPANIC_KSP", others++ ? "|" : "");
if (tt->flags & POPULATE_PANIC)
sprintf(&buf[strlen(buf)],
"%sPOPULATE_PANIC", others++ ? "|" : "");
if (tt->flags & ACTIVE_SET)
sprintf(&buf[strlen(buf)],
"%sACTIVE_SET", others++ ? "|" : "");
if (tt->flags & PIDHASH)
sprintf(&buf[strlen(buf)],
"%sPIDHASH", others++ ? "|" : "");
if (tt->flags & PID_HASH)
sprintf(&buf[strlen(buf)],
"%sPID_HASH", others++ ? "|" : "");
if (tt->flags & THREAD_INFO)
sprintf(&buf[strlen(buf)],
"%sTHREAD_INFO", others++ ? "|" : "");
if (tt->flags & IRQSTACKS)
sprintf(&buf[strlen(buf)],
"%sIRQSTACKS", others++ ? "|" : "");
if (tt->flags & TIMESPEC)
sprintf(&buf[strlen(buf)],
"%sTIMESPEC", others++ ? "|" : "");
if (tt->flags & NO_TIMESPEC)
sprintf(&buf[strlen(buf)],
"%sNO_TIMESPEC", others++ ? "|" : "");
if (tt->flags & START_TIME_NSECS)
sprintf(&buf[strlen(buf)],
"%sSTART_TIME_NSECS", others++ ? "|" : "");
if (tt->flags & ACTIVE_ONLY)
sprintf(&buf[strlen(buf)],
"%sACTIVE_ONLY", others++ ? "|" : "");
sprintf(&buf[strlen(buf)], ")");
if (strlen(buf) > 54)
fprintf(fp, "\n%s\n", mkstring(buf, 80, CENTER|LJUST, NULL));
else
fprintf(fp, "%s\n", buf);
fprintf(fp, " task_start: %lx\n", tt->task_start);
fprintf(fp, " task_end: %lx\n", tt->task_end);
fprintf(fp, " task_local: %lx\n", (ulong)tt->task_local);
fprintf(fp, " max_tasks: %d\n", tt->max_tasks);
fprintf(fp, " nr_threads: %d\n", tt->nr_threads);
fprintf(fp, " running_tasks: %ld\n", tt->running_tasks);
fprintf(fp, " retries: %ld\n", tt->retries);
fprintf(fp, " panicmsg: \"%s\"\n",
strip_linefeeds(get_panicmsg(buf)));
fprintf(fp, " panic_processor: %d\n", tt->panic_processor);
fprintf(fp, " panic_task: %lx\n", tt->panic_task);
fprintf(fp, " this_task: %lx\n", tt->this_task);
fprintf(fp, " pidhash_len: %d\n", tt->pidhash_len);
fprintf(fp, " pidhash_addr: %lx\n", tt->pidhash_addr);
fprintf(fp, " last_task_read: %lx\n", tt->last_task_read);
fprintf(fp, " last_mm_read: %lx\n", tt->last_mm_read);
fprintf(fp, " task_struct: %lx\n", (ulong)tt->task_struct);
fprintf(fp, " mm_struct: %lx\n", (ulong)tt->mm_struct);
fprintf(fp, " init_pid_ns: %lx\n", tt->init_pid_ns);
fprintf(fp, " filepages: %ld\n", tt->filepages);
fprintf(fp, " anonpages: %ld\n", tt->anonpages);
wrap = sizeof(void *) == SIZEOF_32BIT ? 8 : 4;
flen = sizeof(void *) == SIZEOF_32BIT ? 8 : 16;
nr_cpus = kt->kernel_NR_CPUS ? kt->kernel_NR_CPUS : NR_CPUS;
fprintf(fp, " idle_threads:");
for (i = 0; i < nr_cpus; i++) {
if (!tt->idle_threads) {
fprintf(fp, " (unused)");
break;
}
if ((i % wrap) == 0) {
fprintf(fp, "\n ");
for (j = i, more = FALSE; j < nr_cpus; j++) {
if (tt->idle_threads[j]) {
more = TRUE;
break;
}
}
}
fprintf(fp, "%.*lx ", flen, tt->idle_threads[i]);
if (!more) {
fprintf(fp, "...");
break;
}
}
fprintf(fp, "\n");
fprintf(fp, " active_set:");
for (i = 0; i < nr_cpus; i++) {
if (!tt->active_set) {
fprintf(fp, " (unused)");
break;
}
if ((i % wrap) == 0) {
fprintf(fp, "\n ");
for (j = i, more = FALSE; j < nr_cpus; j++) {
if (tt->active_set[j]) {
more = TRUE;
break;
}
}
}
fprintf(fp, "%.*lx ", flen, tt->active_set[i]);
if (!more) {
fprintf(fp, "...");
break;
}
}
fprintf(fp, "\n");
fprintf(fp, " panic_threads:");
for (i = 0; i < nr_cpus; i++) {
if (!tt->panic_threads) {
fprintf(fp, " (unused)");
break;
}
if ((i % wrap) == 0) {
fprintf(fp, "\n ");
for (j = i, more = FALSE; j < nr_cpus; j++) {
if (tt->panic_threads[j]) {
more = TRUE;
break;
}
}
}
fprintf(fp, "%.*lx ", flen, tt->panic_threads[i]);
if (!more) {
fprintf(fp, "...");
break;
}
}
fprintf(fp, "\n");
fprintf(fp, " panic_ksp:");
for (i = 0; i < nr_cpus; i++) {
if (!tt->panic_ksp) {
fprintf(fp, " (unused)");
break;
}
if ((i % wrap) == 0) {
fprintf(fp, "\n ");
for (j = i, more = FALSE; j < nr_cpus; j++) {
if (tt->panic_ksp[j]) {
more = TRUE;
break;
}
}
}
fprintf(fp, "%.*lx ", flen, tt->panic_ksp[i]);
if (!more) {
fprintf(fp, "...");
break;
}
}
fprintf(fp, "\n");
fprintf(fp, " hardirq_ctx:");
for (i = 0; i < nr_cpus; i++) {
if (!tt->hardirq_ctx) {
fprintf(fp, " (unused)");
break;
}
if ((i % wrap) == 0) {
fprintf(fp, "\n ");
for (j = i, more = FALSE; j < nr_cpus; j++) {
if (tt->hardirq_ctx[j]) {
more = TRUE;
break;
}
}
}
fprintf(fp, "%.*lx ", flen, tt->hardirq_ctx[i]);
if (!more) {
fprintf(fp, "...");
break;
}
}
fprintf(fp, "\n");
fprintf(fp, " hardirq_tasks:");
for (i = 0; i < nr_cpus; i++) {
if (!tt->hardirq_tasks) {
fprintf(fp, " (unused)");
break;
}
if ((i % wrap) == 0) {
fprintf(fp, "\n ");
for (j = i, more = FALSE; j < nr_cpus; j++) {
if (tt->hardirq_tasks[j]) {
more = TRUE;
break;
}
}
}
fprintf(fp, "%.*lx ", flen, tt->hardirq_tasks[i]);
if (!more) {
fprintf(fp, "...");
break;
}
}
fprintf(fp, "\n");
fprintf(fp, " softirq_ctx:");
for (i = 0; i < nr_cpus; i++) {
if (!tt->softirq_ctx) {
fprintf(fp, " (unused)");
break;
}
if ((i % wrap) == 0) {
fprintf(fp, "\n ");
for (j = i, more = FALSE; j < nr_cpus; j++) {
if (tt->softirq_ctx[j]) {
more = TRUE;
break;
}
}
}
fprintf(fp, "%.*lx ", flen, tt->softirq_ctx[i]);
if (!more) {
fprintf(fp, "...");
break;
}
}
fprintf(fp, "\n");
fprintf(fp, " softirq_tasks:");
for (i = 0; i < nr_cpus; i++) {
if (!tt->softirq_tasks) {
fprintf(fp, " (unused)");
break;
}
if ((i % wrap) == 0) {
fprintf(fp, "\n ");
for (j = i, more = FALSE; j < nr_cpus; j++) {
if (tt->softirq_tasks[j]) {
more = TRUE;
break;
}
}
}
fprintf(fp, "%.*lx ", flen, tt->softirq_tasks[i]);
if (!more) {
fprintf(fp, "...");
break;
}
}
fprintf(fp, "\n");
dump_task_states();
if (!verbose)
return;
if (tt->flags & THREAD_INFO)
fprintf(fp,
"\nINDEX TASK/THREAD_INFO PID CPU PTASK MM_STRUCT COMM\n");
else
fprintf(fp,
"\nINDEX TASK PID CPU PTASK MM_STRUCT COMM\n");
tc = FIRST_CONTEXT();
for (i = 0; i < RUNNING_TASKS(); i++, tc++) {
if (tt->flags & THREAD_INFO)
fprintf(fp,
"[%3d] %08lx/%08lx %5ld %d %08lx %08lx %s\n",
i, tc->task, tc->thread_info, tc->pid,
tc->processor, tc->ptask, (ulong)tc->mm_struct,
tc->comm);
else
fprintf(fp, "[%3d] %08lx %5ld %d %08lx %08lx %s\n",
i, tc->task, tc->pid, tc->processor, tc->ptask,
(ulong)tc->mm_struct, tc->comm);
}
fprintf(fp, "\nINDEX TASK TGID (COMM)\n");
for (i = 0; i < RUNNING_TASKS(); i++) {
tg = &tt->tgid_array[i];
tc = task_to_context(tg->task);
fprintf(fp, "[%3d] %lx %ld (%s)\n", i, tg->task, tg->tgid, tc->comm);
}
}
/*
* Determine whether a task is a kernel thread. This would seem easier than
* it looks, but on live systems it's easy to get faked out.
*/
int
is_kernel_thread(ulong task)
{
struct task_context *tc;
ulong mm;
tc = task_to_context(task);
if ((tc->pid == 0) && !STREQ(tc->comm, pc->program_name))
return TRUE;
if (_ZOMBIE_ == TASK_STATE_UNINITIALIZED)
initialize_task_state();
if (IS_ZOMBIE(task) || IS_EXITING(task))
return FALSE;
/*
* Check for shifting sands on a live system.
*/
mm = task_mm(task, TRUE);
if (ACTIVE() && (mm != tc->mm_struct))
return FALSE;
/*
* Later version Linux kernel threads have no mm_struct at all.
* Earlier version kernel threads point to common init_mm.
*/
if (!tc->mm_struct) {
if (IS_EXITING(task))
return FALSE;
if (!task_state(task) && !task_flags(task))
return FALSE;
return TRUE;
} else if (tc->mm_struct == symbol_value("init_mm"))
return TRUE;
return FALSE;
}
/*
* Gather an arry of pointers to the per-cpu idle tasks. The tasklist
* argument must be at least the size of ulong[NR_CPUS]. There may be
* junk in everything after the first entry on a single CPU box, so the
* data gathered may be throttled by kt->cpus.
*/
void
get_idle_threads(ulong *tasklist, int nr_cpus)
{
int i, cnt;
ulong runq, runqaddr;
char *runqbuf;
struct syment *rq_sp;
BZERO(tasklist, sizeof(ulong) * NR_CPUS);
runqbuf = NULL;
cnt = 0;
if ((rq_sp = per_cpu_symbol_search("per_cpu__runqueues")) &&
VALID_MEMBER(runqueue_idle)) {
runqbuf = GETBUF(SIZE(runqueue));
for (i = 0; i < nr_cpus; i++) {
if (machine_type("SPARC64") &&
cpu_map_addr("possible") &&
!(in_cpu_map(POSSIBLE, i)))
continue;
if ((kt->flags & SMP) && (kt->flags & PER_CPU_OFF))
runq = rq_sp->value + kt->__per_cpu_offset[i];
else
runq = rq_sp->value;
readmem(runq, KVADDR, runqbuf,
SIZE(runqueue), "runqueues entry (per_cpu)",
FAULT_ON_ERROR);
tasklist[i] = ULONG(runqbuf + OFFSET(runqueue_idle));
if (IS_KVADDR(tasklist[i]))
cnt++;
}
} else if (symbol_exists("runqueues") && VALID_MEMBER(runqueue_idle)) {
runq = symbol_value("runqueues");
runqbuf = GETBUF(SIZE(runqueue));
for (i = 0; i < nr_cpus; i++, runq += SIZE(runqueue)) {
readmem(runq, KVADDR, runqbuf,
SIZE(runqueue), "runqueues entry (old)",
FAULT_ON_ERROR);
tasklist[i] = ULONG(runqbuf + OFFSET(runqueue_idle));
if (IS_KVADDR(tasklist[i]))
cnt++;
}
} else if (symbol_exists("runqueues") && VALID_MEMBER(runqueue_cpu)) {
runq = symbol_value("runqueues");
runqbuf = GETBUF(SIZE(runqueue));
for (i = 0; i < nr_cpus; i++) {
runqaddr = runq + (SIZE(runqueue) * rq_idx(i));
readmem(runqaddr, KVADDR, runqbuf,
SIZE(runqueue), "runqueues entry",
FAULT_ON_ERROR);
if ((tasklist[i] = get_idle_task(i, runqbuf)))
cnt++;
}
} else if (symbol_exists("init_tasks")) {
readmem(symbol_value("init_tasks"), KVADDR, tasklist,
sizeof(void *) * nr_cpus, "init_tasks array",
FAULT_ON_ERROR);
if (IS_KVADDR(tasklist[0]))
cnt++;
else
BZERO(tasklist, sizeof(ulong) * NR_CPUS);
} else if (OPENVZ()) {
runq = symbol_value("pcpu_info");
runqbuf = GETBUF(SIZE(pcpu_info));
for (i = 0; i < nr_cpus; i++, runq += SIZE(pcpu_info)) {
readmem(runq, KVADDR, runqbuf, SIZE(pcpu_info),
"pcpu info", FAULT_ON_ERROR);
tasklist[i] = ULONG(runqbuf + OFFSET(pcpu_info_idle));
if (IS_KVADDR(tasklist[i]))
cnt++;
}
}
if (runqbuf)
FREEBUF(runqbuf);
if (!cnt) {
error(INFO,
"cannot determine idle task addresses from init_tasks[] or runqueues[]\n");
tasklist[0] = symbol_value("init_task_union");
}
}
/*
* Emulate the kernel rq_idx() macro.
*/
static long
rq_idx(int cpu)
{
if (kt->runq_siblings == 1)
return cpu;
else if (!(kt->__rq_idx))
return 0;
else
return kt->__rq_idx[cpu];
}
/*
* Emulate the kernel cpu_idx() macro.
*/
static long
cpu_idx(int cpu)
{
if (kt->runq_siblings == 1)
return 0;
else if (!(kt->__cpu_idx))
return 0;
else
return kt->__cpu_idx[cpu];
}
/*
* Dig out the idle task data from a runqueue structure.
*/
static ulong
get_idle_task(int cpu, char *runqbuf)
{
ulong idle_task;
idle_task = ULONG(runqbuf + OFFSET(runqueue_cpu) +
(SIZE(cpu_s) * cpu_idx(cpu)) + OFFSET(cpu_s_idle));
if (IS_KVADDR(idle_task))
return idle_task;
else {
if (cpu < kt->cpus)
error(INFO,
"cannot determine idle task for cpu %d\n", cpu);
return NO_TASK;
}
}
/*
* Dig out the current task data from a runqueue structure.
*/
static ulong
get_curr_task(int cpu, char *runqbuf)
{
ulong curr_task;
curr_task = ULONG(runqbuf + OFFSET(runqueue_cpu) +
(SIZE(cpu_s) * cpu_idx(cpu)) + OFFSET(cpu_s_curr));
if (IS_KVADDR(curr_task))
return curr_task;
else
return NO_TASK;
}
/*
* On kernels with runqueue[] array, store the active set of tasks.
*/
int
get_active_set(void)
{
int i, cnt;
ulong runq, runqaddr;
char *runqbuf;
struct syment *rq_sp;
if (tt->flags & ACTIVE_SET)
return TRUE;
runq = 0;
rq_sp = per_cpu_symbol_search("per_cpu__runqueues");
if (!rq_sp) {
if (symbol_exists("runqueues"))
runq = symbol_value("runqueues");
else if (OPENVZ())
runq = symbol_value("pcpu_info");
else
return FALSE;
} else
runq = rq_sp->value;
if (!tt->active_set &&
!(tt->active_set = (ulong *)calloc(NR_CPUS, sizeof(ulong))))
error(FATAL, "cannot malloc active_set array");
runqbuf = GETBUF(SIZE(runqueue));
cnt = 0;
if (OPENVZ()) {
ulong vcpu_struct;
char *pcpu_info_buf, *vcpu_struct_buf;
pcpu_info_buf = GETBUF(SIZE(pcpu_info));
vcpu_struct_buf = GETBUF(SIZE(vcpu_struct));
for (i = 0; i < kt->cpus; i++, runq += SIZE(pcpu_info)) {
readmem(runq, KVADDR, pcpu_info_buf,
SIZE(pcpu_info), "pcpu_info", FAULT_ON_ERROR);
vcpu_struct= ULONG(pcpu_info_buf +
OFFSET(pcpu_info_vcpu));
readmem(vcpu_struct, KVADDR, vcpu_struct_buf,
SIZE(vcpu_struct), "pcpu_info->vcpu",
FAULT_ON_ERROR);
tt->active_set[i] = ULONG(vcpu_struct_buf +
OFFSET(vcpu_struct_rq) + OFFSET(runqueue_curr));
if (IS_KVADDR(tt->active_set[i]))
cnt++;
}
FREEBUF(pcpu_info_buf);
FREEBUF(vcpu_struct_buf);
} else if (VALID_MEMBER(runqueue_curr) && rq_sp) {
for (i = 0; i < kt->cpus; i++) {
if ((kt->flags & SMP) && (kt->flags & PER_CPU_OFF))
runq = rq_sp->value + kt->__per_cpu_offset[i];
else
runq = rq_sp->value;
readmem(runq, KVADDR, runqbuf, SIZE(runqueue),
"active runqueues entry (per_cpu)",
FAULT_ON_ERROR);
tt->active_set[i] = ULONG(runqbuf +
OFFSET(runqueue_curr));
if (IS_KVADDR(tt->active_set[i]))
cnt++;
}
} else if (VALID_MEMBER(runqueue_curr)) {
for (i = 0; i < MAX(kt->cpus, kt->kernel_NR_CPUS); i++,
runq += SIZE(runqueue)) {
readmem(runq, KVADDR, runqbuf,
SIZE(runqueue), "(old) runqueues curr",
FAULT_ON_ERROR);
tt->active_set[i] = ULONG(runqbuf +
OFFSET(runqueue_curr));
if (IS_KVADDR(tt->active_set[i]))
cnt++;
}
} else if (VALID_MEMBER(runqueue_cpu)) {
for (i = 0; i < kt->cpus; i++) {
runqaddr = runq + (SIZE(runqueue) * rq_idx(i));
readmem(runqaddr, KVADDR, runqbuf,
SIZE(runqueue), "runqueues curr",
FAULT_ON_ERROR);
if ((tt->active_set[i] = get_curr_task(i, runqbuf)))
cnt++;
}
}
if (cnt) {
tt->flags |= ACTIVE_SET;
return TRUE;
} else {
error(INFO, "get_active_set: no tasks found?\n");
return FALSE;
}
}
/*
* Clear the ACTIVE_SET flag on a live system, forcing a re-read of the
* runqueues[] array the next time get_active_set() is called above.
*/
void
clear_active_set(void)
{
if (ACTIVE() && (tt->flags & TASK_REFRESH))
tt->flags &= ~ACTIVE_SET;
}
#define RESOLVE_PANIC_AND_DIE_CALLERS() \
if (xen_panic_task) { \
if (CRASHDEBUG(1)) \
error(INFO, \
"get_active_set_panic_task: %lx (xen_panic_event)\n", \
xen_panic_task); \
return xen_panic_task; \
} \
if (crash_kexec_task) { \
if (CRASHDEBUG(1)) \
error(INFO, \
"get_active_set_panic_task: %lx (crash_kexec)\n", \
crash_kexec_task); \
return crash_kexec_task; \
} \
if (crash_fadump_task) { \
if (CRASHDEBUG(1)) \
error(INFO, \
"get_active_set_panic_task: %lx (crash_fadump)\n", \
crash_fadump_task); \
return crash_fadump_task; \
} \
if ((panic_task > (NO_TASK+1)) && !die_task) { \
if (CRASHDEBUG(1)) \
fprintf(fp, \
"get_active_set_panic_task: %lx (panic)\n", \
panic_task); \
return panic_task; \
} \
\
if (panic_task && die_task) { \
if ((panic_task > (NO_TASK+1)) && \
(panic_task == die_task)) { \
if (CRASHDEBUG(1)) \
fprintf(fp, \
"get_active_set_panic_task: %lx (panic)\n", \
panic_task); \
return panic_task; \
} \
error(WARNING, \
"multiple active tasks have called die and/or panic\n\n"); \
goto no_panic_task_found; \
} \
\
if (die_task > (NO_TASK+1)) { \
if (CRASHDEBUG(1)) \
fprintf(fp, \
"get_active_set_panic_task: %lx (die)\n", \
die_task); \
return die_task; \
} \
else if (die_task == (NO_TASK+1)) \
error(WARNING, \
"multiple active tasks have called die\n\n");
#define SEARCH_STACK_FOR_PANIC_DIE_AND_KEXEC_CALLERS() \
while (fgets(buf, BUFSIZE, pc->tmpfile)) { \
if (strstr(buf, " die+")) { \
switch (die_task) \
{ \
case NO_TASK: \
die_task = task; \
break; \
default: \
if (die_task != task) \
die_task = NO_TASK+1; \
break; \
} \
} \
if (strstr(buf, " panic+")) { \
switch (panic_task) \
{ \
case NO_TASK: \
panic_task = task; \
if (XENDUMP_DUMPFILE()) \
xendump_panic_hook(buf); \
break; \
default: \
if (panic_task != task) \
panic_task = NO_TASK+1; \
break; \
} \
} \
if (strstr(buf, " crash_kexec+") || \
strstr(buf, " .crash_kexec+")) { \
crash_kexec_task = task; \
} \
if (strstr(buf, " .crash_fadump+")) \
crash_fadump_task = task; \
if (strstr(buf, " machine_kexec+") || \
strstr(buf, " .machine_kexec+")) { \
crash_kexec_task = task; \
} \
if (strstr(buf, " xen_panic_event+") || \
strstr(buf, " .xen_panic_event+")){ \
xen_panic_task = task; \
xendump_panic_hook(buf); \
} \
if (machine_type("IA64") && XENDUMP_DUMPFILE() && !xen_panic_task && \
strstr(buf, " sysrq_handle_crashdump+")) \
xen_sysrq_task = task; \
}
/*
* Search the active set tasks for instances of die or panic calls.
*/
static ulong
get_active_set_panic_task()
{
int i, j, found;
ulong task;
char buf[BUFSIZE];
ulong panic_task, die_task, crash_kexec_task, crash_fadump_task;
ulong xen_panic_task;
ulong xen_sysrq_task;
panic_task = die_task = crash_kexec_task = xen_panic_task = NO_TASK;
xen_sysrq_task = NO_TASK;
crash_fadump_task = NO_TASK;
for (i = 0; i < NR_CPUS; i++) {
if (!(task = tt->active_set[i]) || !task_exists(task))
continue;
open_tmpfile();
raw_stack_dump(GET_STACKBASE(task), STACKSIZE());
rewind(pc->tmpfile);
SEARCH_STACK_FOR_PANIC_DIE_AND_KEXEC_CALLERS();
close_tmpfile();
}
RESOLVE_PANIC_AND_DIE_CALLERS();
if (tt->flags & IRQSTACKS) {
panic_task = die_task = NO_TASK;
for (i = 0; i < NR_CPUS; i++) {
if (!(task = tt->hardirq_tasks[i]))
continue;
for (j = found = 0; j < NR_CPUS; j++) {
if (task == tt->active_set[j]) {
found++;
break;
}
}
if (!found)
continue;
open_tmpfile();
raw_stack_dump(tt->hardirq_ctx[i], SIZE(thread_union));
rewind(pc->tmpfile);
SEARCH_STACK_FOR_PANIC_DIE_AND_KEXEC_CALLERS();
close_tmpfile();
}
RESOLVE_PANIC_AND_DIE_CALLERS();
panic_task = die_task = NO_TASK;
for (i = 0; i < NR_CPUS; i++) {
if (!(task = tt->softirq_tasks[i]))
continue;
for (j = found = 0; j < NR_CPUS; j++) {
if (task == tt->active_set[j]) {
found++;
break;
}
}
if (!found)
continue;
open_tmpfile();
raw_stack_dump(tt->softirq_ctx[i], SIZE(thread_union));
rewind(pc->tmpfile);
SEARCH_STACK_FOR_PANIC_DIE_AND_KEXEC_CALLERS();
close_tmpfile();
}
RESOLVE_PANIC_AND_DIE_CALLERS();
}
if (crash_kexec_task) {
if (CRASHDEBUG(1))
error(INFO,
"get_active_set_panic_task: %lx (crash_kexec)\n",
crash_kexec_task);
return crash_kexec_task;
}
if (crash_fadump_task) {
if (CRASHDEBUG(1))
error(INFO,
"get_active_set_panic_task: %lx (crash_fadump)\n",
crash_fadump_task);
return crash_fadump_task;
}
if (xen_sysrq_task) {
if (CRASHDEBUG(1))
error(INFO,
"get_active_set_panic_task: %lx (sysrq_handle_crashdump)\n",
xen_sysrq_task);
return xen_sysrq_task;
}
no_panic_task_found:
if (CRASHDEBUG(1))
error(INFO,
"get_active_set_panic_task: failed\n");
return NO_TASK;
}
/*
* Determine whether a task is one of the idle threads.
*/
int
is_idle_thread(ulong task)
{
int i;
for (i = 0; i < NR_CPUS; i++)
if (task == tt->idle_threads[i])
return TRUE;
return FALSE;
}
/*
* Dump the current run queue task list. This command should be expanded
* to deal with timer queues, bottom halves, etc...
*/
void
cmd_runq(void)
{
int c;
char arg_buf[BUFSIZE];
ulong *cpus = NULL;
int sched_debug = 0;
int dump_timestamp_flag = 0;
int dump_task_group_flag = 0;
int dump_milliseconds_flag = 0;
while ((c = getopt(argcnt, args, "dtgmc:")) != EOF) {
switch(c)
{
case 'd':
sched_debug = 1;
break;
case 't':
dump_timestamp_flag = 1;
break;
case 'm':
dump_milliseconds_flag = 1;
break;
case 'g':
if ((INVALID_MEMBER(task_group_cfs_rq) &&
INVALID_MEMBER(task_group_rt_rq)) ||
INVALID_MEMBER(task_group_parent))
option_not_supported(c);
dump_task_group_flag = 1;
break;
case 'c':
if (pc->curcmd_flags & CPUMASK) {
error(INFO, "only one -c option allowed\n");
argerrs++;
} else {
pc->curcmd_flags |= CPUMASK;
BZERO(arg_buf, BUFSIZE);
strncpy(arg_buf, optarg, strlen(optarg));
cpus = get_cpumask_buf();
make_cpumask(arg_buf, cpus, FAULT_ON_ERROR, NULL);
pc->curcmd_private = (ulong)cpus;
}
break;
default:
argerrs++;
break;
}
}
if (argerrs)
cmd_usage(pc->curcmd, SYNOPSIS);
if (dump_timestamp_flag)
dump_on_rq_timestamp();
else if (dump_milliseconds_flag)
dump_on_rq_milliseconds();
else if (sched_debug)
dump_on_rq_tasks();
else if (dump_task_group_flag)
dump_tasks_by_task_group();
else
dump_runq();
if (cpus)
FREEBUF(cpus);
}
/*
* Displays the runqueue and active task timestamps of each cpu.
*/
static void
dump_on_rq_timestamp(void)
{
ulong runq;
char buf[BUFSIZE];
char format[15];
struct syment *rq_sp;
struct task_context *tc;
int cpu, len, indent;
ulonglong timestamp;
ulong *cpus;
indent = runq = 0;
cpus = pc->curcmd_flags & CPUMASK ?
(ulong *)(ulong)pc->curcmd_private : NULL;
if (!(rq_sp = per_cpu_symbol_search("per_cpu__runqueues")))
error(FATAL, "per-cpu runqueues do not exist\n");
if (INVALID_MEMBER(rq_timestamp))
option_not_supported('t');
for (cpu = 0; cpu < kt->cpus; cpu++) {
if (cpus && !NUM_IN_BITMAP(cpus, cpu))
continue;
if ((kt->flags & SMP) && (kt->flags &PER_CPU_OFF))
runq = rq_sp->value + kt->__per_cpu_offset[cpu];
else
runq = rq_sp->value;
readmem(runq + OFFSET(rq_timestamp), KVADDR, &timestamp,
sizeof(ulonglong), "per-cpu rq timestamp",
FAULT_ON_ERROR);
sprintf(buf, pc->output_radix == 10 ? "%llu" : "%llx",
timestamp);
fprintf(fp, "%sCPU %d: ", cpu < 10 ? " " : "", cpu);
if (hide_offline_cpu(cpu)) {
fprintf(fp, "[OFFLINE]\n");
continue;
} else
fprintf(fp, "%s\n", buf);
len = strlen(buf);
if ((tc = task_to_context(tt->active_set[cpu]))){
if (cpu < 10)
indent = 7;
else if (cpu < 100)
indent = 8;
else if (cpu < 1000)
indent = 9;
if (cpu < 10)
indent++;
timestamp = task_last_run(tc->task);
sprintf(format, "%c0%dll%c", '%', len,
pc->output_radix == 10 ? 'u' : 'x');
sprintf(buf, format, timestamp);
fprintf(fp, "%s%s PID: %-5ld TASK: %lx COMMAND: \"%s\"\n",
space(indent), buf, tc->pid, tc->task, tc->comm);
} else
fprintf(fp, "\n");
}
}
/*
* Displays the runqueue and active task timestamps of each cpu.
*/
static void
dump_on_rq_milliseconds(void)
{
ulong runq;
char buf[BUFSIZE];
struct syment *rq_sp;
struct task_context *tc;
int cpu, max_indent, indent, max_days, days;
long long delta;
ulonglong task_timestamp, rq_timestamp;
ulong *cpus;
if (!(rq_sp = per_cpu_symbol_search("per_cpu__runqueues")))
error(FATAL, "per-cpu runqueues do not exist\n");
if (INVALID_MEMBER(rq_timestamp))
option_not_supported('m');
if (kt->cpus < 10)
max_indent = 1;
else if (kt->cpus < 100)
max_indent = 2;
else if (kt->cpus < 1000)
max_indent = 3;
else
max_indent = 4;
max_days = days = 0;
cpus = pc->curcmd_flags & CPUMASK ?
(ulong *)(ulong)pc->curcmd_private : NULL;
for (cpu = 0; cpu < kt->cpus; cpu++) {
if (cpus && !NUM_IN_BITMAP(cpus, cpu))
continue;
if ((kt->flags & SMP) && (kt->flags &PER_CPU_OFF))
runq = rq_sp->value + kt->__per_cpu_offset[cpu];
else
runq = rq_sp->value;
readmem(runq + OFFSET(rq_timestamp), KVADDR, &rq_timestamp,
sizeof(ulonglong), "per-cpu rq timestamp",
FAULT_ON_ERROR);
if (!max_days) {
translate_nanoseconds(rq_timestamp, buf);
max_days = first_space(buf) - buf;
}
if (cpu < 10)
indent = max_indent;
else if (cpu < 100)
indent = max_indent - 1;
else if (cpu < 1000)
indent = max_indent - 2;
else
indent = max_indent - 4;
if (hide_offline_cpu(cpu)) {
fprintf(fp, "%sCPU %d: [OFFLINE]\n", space(indent), cpu);
continue;
}
if ((tc = task_to_context(tt->active_set[cpu])))
task_timestamp = task_last_run(tc->task);
else {
fprintf(fp, "%sCPU %d: [unknown]\n", space(indent), cpu);
continue;
}
delta = rq_timestamp - task_timestamp;
if (delta < 0)
delta = 0;
translate_nanoseconds(delta, buf);
days = first_space(buf) - buf;
fprintf(fp,
"%sCPU %d: [%s%s] PID: %-5ld TASK: %lx COMMAND: \"%s\"\n",
space(indent), cpu, space(max_days - days), buf, tc->pid,
tc->task, tc->comm);
}
}
/*
* Dump the task run queue on behalf cmd_runq().
*/
static void
dump_runq(void)
{
int i;
ulong next, runqueue_head;
long offs;
int qlen, cnt;
ulong *tlist;
struct task_context *tc;
if (VALID_MEMBER(rq_cfs)) {
dump_CFS_runqueues();
return;
}
if (VALID_MEMBER(runqueue_arrays)) {
dump_runqueues();
return;
}
offs = runqueue_head = 0;
qlen = 1000;
start_again:
tlist = (ulong *)GETBUF(qlen * sizeof(void *));
if (symbol_exists("runqueue_head")) {
next = runqueue_head = symbol_value("runqueue_head");
offs = 0;
} else if (VALID_MEMBER(task_struct_next_run)) {
offs = OFFSET(task_struct_next_run);
next = runqueue_head = symbol_value("init_task_union");
} else
error(FATAL,
"cannot determine run queue structures\n");
cnt = 0;
do {
if (cnt == qlen) {
FREEBUF(tlist);
qlen += 1000;
goto start_again;
}
tlist[cnt++] = next;
readmem(next+offs, KVADDR, &next, sizeof(void *),
"run queue entry", FAULT_ON_ERROR);
if (next == runqueue_head)
break;
} while (next);
for (i = 0; i < cnt; i++) {
if (tlist[i] == runqueue_head)
continue;
if (!(tc = task_to_context(VIRTPAGEBASE(tlist[i])))) {
fprintf(fp,
"PID: ? TASK: %lx CPU: ? COMMAND: ?\n",
tlist[i]);
continue;
}
if (!is_idle_thread(tc->task))
print_task_header(fp, tc, 0);
}
}
#define RUNQ_ACTIVE (1)
#define RUNQ_EXPIRED (2)
static void
dump_runqueues(void)
{
int cpu, displayed;
ulong runq, offset;
char *runqbuf;
ulong active, expired, arrays;
struct task_context *tc;
struct syment *rq_sp;
ulong *cpus;
runq = 0;
rq_sp = per_cpu_symbol_search("per_cpu__runqueues");
if (!rq_sp) {
if (symbol_exists("runqueues"))
runq = symbol_value("runqueues");
else
error(FATAL, "cannot determine run queue structures\n");
}
get_active_set();
runqbuf = GETBUF(SIZE(runqueue));
cpus = pc->curcmd_flags & CPUMASK ?
(ulong *)(ulong)pc->curcmd_private : NULL;
for (cpu = displayed = 0; cpu < kt->cpus; cpu++, runq += SIZE(runqueue)) {
if (cpus && !NUM_IN_BITMAP(cpus, cpu))
continue;
if (rq_sp) {
if ((kt->flags & SMP) && (kt->flags & PER_CPU_OFF))
runq = rq_sp->value + kt->__per_cpu_offset[cpu];
else
runq = rq_sp->value;
}
fprintf(fp, "%sCPU %d ", displayed++ ? "\n" : "", cpu);
if (hide_offline_cpu(cpu)) {
fprintf(fp, "[OFFLINE]\n");
continue;
} else
fprintf(fp, "RUNQUEUE: %lx\n", runq);
fprintf(fp, " CURRENT: ");
if ((tc = task_to_context(tt->active_set[cpu])))
fprintf(fp, "PID: %-5ld TASK: %lx COMMAND: \"%s\"\n",
tc->pid, tc->task, tc->comm);
else
fprintf(fp, "%lx\n", tt->active_set[cpu]);
readmem(runq, KVADDR, runqbuf, SIZE(runqueue),
"runqueues array entry", FAULT_ON_ERROR);
active = ULONG(runqbuf + OFFSET(runqueue_active));
expired = ULONG(runqbuf + OFFSET(runqueue_expired));
arrays = runq + OFFSET(runqueue_arrays);
console("active: %lx\n", active);
console("expired: %lx\n", expired);
console("arrays: %lx\n", arrays);
offset = active == arrays ? OFFSET(runqueue_arrays) :
OFFSET(runqueue_arrays) + SIZE(prio_array);
offset = active - runq;
dump_prio_array(RUNQ_ACTIVE, active, &runqbuf[offset]);
offset = expired == arrays ? OFFSET(runqueue_arrays) :
OFFSET(runqueue_arrays) + SIZE(prio_array);
offset = expired - runq;
dump_prio_array(RUNQ_EXPIRED, expired, &runqbuf[offset]);
}
}
static void
dump_prio_array(int which, ulong k_prio_array, char *u_prio_array)
{
int i, c, cnt, tot, nr_active;
int qheads ATTRIBUTE_UNUSED;
ulong offset, kvaddr, uvaddr;
ulong list_head[2];
struct list_data list_data, *ld;
struct task_context *tc;
ulong *tlist;
qheads = (i = ARRAY_LENGTH(prio_array_queue)) ?
i : get_array_length("prio_array.queue", NULL, SIZE(list_head));
console("dump_prio_array[%d]: %lx %lx\n",
which, k_prio_array, (ulong)u_prio_array);
nr_active = INT(u_prio_array + OFFSET(prio_array_nr_active));
console("nr_active: %d\n", nr_active);
fprintf(fp, " %s PRIO_ARRAY: %lx\n",
which == RUNQ_ACTIVE ? "ACTIVE" : "EXPIRED", k_prio_array);
if (CRASHDEBUG(1))
fprintf(fp, "nr_active: %d\n", nr_active);
ld = &list_data;
for (i = tot = 0; i < 140; i++) {
offset = OFFSET(prio_array_queue) + (i * SIZE(list_head));
kvaddr = k_prio_array + offset;
uvaddr = (ulong)u_prio_array + offset;
BCOPY((char *)uvaddr, (char *)&list_head[0], sizeof(ulong)*2);
if (CRASHDEBUG(1))
fprintf(fp, "prio_array[%d] @ %lx => %lx/%lx %s\n",
i, kvaddr, list_head[0], list_head[1],
(list_head[0] == list_head[1]) &&
(list_head[0] == kvaddr) ? "(empty)" : "");
if ((list_head[0] == kvaddr) && (list_head[1] == kvaddr))
continue;
console("[%d] %lx => %lx-%lx ", i, kvaddr, list_head[0],
list_head[1]);
fprintf(fp, " [%3d] ", i);
BZERO(ld, sizeof(struct list_data));
ld->start = list_head[0];
ld->list_head_offset = OFFSET(task_struct_run_list);
ld->end = kvaddr;
hq_open();
cnt = do_list(ld);
hq_close();
console("%d entries\n", cnt);
tlist = (ulong *)GETBUF((cnt) * sizeof(ulong));
cnt = retrieve_list(tlist, cnt);
for (c = 0; c < cnt; c++) {
if (!(tc = task_to_context(tlist[c])))
continue;
if (c)
INDENT(11);
fprintf(fp, "PID: %-5ld TASK: %lx COMMAND: \"%s\"\n",
tc->pid, tc->task, tc->comm);
}
tot += cnt;
FREEBUF(tlist);
}
if (!tot) {
INDENT(5);
fprintf(fp, "[no tasks queued]\n");
}
}
#define MAX_GROUP_NUM 200
struct task_group_info {
int use;
int depth;
char *name;
ulong task_group;
struct task_group_info *parent;
};
static struct task_group_info **tgi_array;
static int tgi_p = 0;
static int tgi_p_max = 0;
static void
sort_task_group_info_array(void)
{
int i, j;
struct task_group_info *tmp;
for (i = 0; i < tgi_p - 1; i++) {
for (j = 0; j < tgi_p - i - 1; j++) {
if (tgi_array[j]->depth > tgi_array[j+1]->depth) {
tmp = tgi_array[j+1];
tgi_array[j+1] = tgi_array[j];
tgi_array[j] = tmp;
}
}
}
}
static void
print_task_group_info_array(void)
{
int i;
for (i = 0; i < tgi_p; i++) {
fprintf(fp, "%d : use=%d, depth=%d, group=%lx, ", i,
tgi_array[i]->use, tgi_array[i]->depth,
tgi_array[i]->task_group);
fprintf(fp, "name=%s, ",
tgi_array[i]->name ? tgi_array[i]->name : "NULL");
if (tgi_array[i]->parent)
fprintf(fp, "parent=%lx",
tgi_array[i]->parent->task_group);
fprintf(fp, "\n");
}
}
static void
free_task_group_info_array(void)
{
int i;
for (i = 0; i < tgi_p; i++) {
if (tgi_array[i]->name)
FREEBUF(tgi_array[i]->name);
FREEBUF(tgi_array[i]);
}
tgi_p = 0;
FREEBUF(tgi_array);
}
static void
reuse_task_group_info_array(void)
{
int i;
for (i = 0; i < tgi_p; i++) {
if (tgi_array[i]->depth == 0)
tgi_array[i]->use = 0;
else
tgi_array[i]->use = 1;
}
}
static void
dump_task_runq_entry(struct task_context *tc, int current)
{
int prio;
readmem(tc->task + OFFSET(task_struct_prio), KVADDR,
&prio, sizeof(int), "task prio", FAULT_ON_ERROR);
fprintf(fp, "[%3d] ", prio);
fprintf(fp, "PID: %-5ld TASK: %lx COMMAND: \"%s\"",
tc->pid, tc->task, tc->comm);
if (current)
fprintf(fp, " [CURRENT]\n");
else
fprintf(fp, "\n");
}
static void
print_group_header_fair(int depth, ulong cfs_rq, void *t)
{
int throttled;
struct task_group_info *tgi = (struct task_group_info *)t;
INDENT(2 + 3 * depth);
fprintf(fp, "TASK_GROUP: %lx CFS_RQ: %lx ",
tgi->task_group, cfs_rq);
if (tgi->name)
fprintf(fp, " <%s>", tgi->name);
if (VALID_MEMBER(cfs_rq_throttled)) {
readmem(cfs_rq + OFFSET(cfs_rq_throttled), KVADDR,
&throttled, sizeof(int), "cfs_rq throttled",
FAULT_ON_ERROR);
if (throttled)
fprintf(fp, " (THROTTLED)");
}
fprintf(fp, "\n");
}
static void
print_parent_task_group_fair(void *t, int cpu)
{
struct task_group_info *tgi;
ulong cfs_rq_c, cfs_rq_p;
tgi = ((struct task_group_info *)t)->parent;
if (tgi && tgi->use)
print_parent_task_group_fair(tgi, cpu);
else
return;
readmem(tgi->task_group + OFFSET(task_group_cfs_rq),
KVADDR, &cfs_rq_c, sizeof(ulong),
"task_group cfs_rq", FAULT_ON_ERROR);
readmem(cfs_rq_c + cpu * sizeof(ulong), KVADDR, &cfs_rq_p,
sizeof(ulong), "task_group cfs_rq", FAULT_ON_ERROR);
print_group_header_fair(tgi->depth, cfs_rq_p, tgi);
tgi->use = 0;
}
static int
dump_tasks_in_lower_dequeued_cfs_rq(int depth, ulong cfs_rq, int cpu,
struct task_context *ctc)
{
int i, total, nr_running;
ulong group, cfs_rq_c, cfs_rq_p;
total = 0;
for (i = 0; i < tgi_p; i++) {
if (tgi_array[i]->use == 0 || tgi_array[i]->depth - depth != 1)
continue;
readmem(cfs_rq + OFFSET(cfs_rq_tg), KVADDR, &group,
sizeof(ulong), "cfs_rq tg", FAULT_ON_ERROR);
if (group != tgi_array[i]->parent->task_group)
continue;
readmem(tgi_array[i]->task_group + OFFSET(task_group_cfs_rq),
KVADDR, &cfs_rq_c, sizeof(ulong), "task_group cfs_rq",
FAULT_ON_ERROR);
readmem(cfs_rq_c + cpu * sizeof(ulong), KVADDR, &cfs_rq_p,
sizeof(ulong), "task_group cfs_rq", FAULT_ON_ERROR);
if (cfs_rq == cfs_rq_p)
continue;
readmem(cfs_rq_p + OFFSET(cfs_rq_nr_running), KVADDR,
&nr_running, sizeof(int), "cfs_rq nr_running",
FAULT_ON_ERROR);
if (nr_running == 0) {
total += dump_tasks_in_lower_dequeued_cfs_rq(depth + 1,
cfs_rq_p, cpu, ctc);
continue;
}
print_parent_task_group_fair(tgi_array[i], cpu);
total++;
total += dump_tasks_in_task_group_cfs_rq(depth + 1, cfs_rq_p, cpu, ctc);
}
return total;
}
static int
dump_tasks_in_cfs_rq(ulong cfs_rq)
{
struct task_context *tc;
struct rb_root *root;
struct rb_node *node;
ulong my_q, leftmost, curr, curr_my_q;
int total;
total = 0;
if (VALID_MEMBER(sched_entity_my_q)) {
readmem(cfs_rq + OFFSET(cfs_rq_curr), KVADDR, &curr,
sizeof(ulong), "curr", FAULT_ON_ERROR);
if (curr) {
readmem(curr + OFFSET(sched_entity_my_q), KVADDR,
&curr_my_q, sizeof(ulong), "curr->my_q",
FAULT_ON_ERROR);
if (curr_my_q)
total += dump_tasks_in_cfs_rq(curr_my_q);
}
}
readmem(cfs_rq + OFFSET(cfs_rq_rb_leftmost), KVADDR, &leftmost,
sizeof(ulong), "rb_leftmost", FAULT_ON_ERROR);
root = (struct rb_root *)(cfs_rq + OFFSET(cfs_rq_tasks_timeline));
for (node = rb_first(root); leftmost && node; node = rb_next(node)) {
if (VALID_MEMBER(sched_entity_my_q)) {
readmem((ulong)node - OFFSET(sched_entity_run_node)
+ OFFSET(sched_entity_my_q), KVADDR, &my_q,
sizeof(ulong), "my_q", FAULT_ON_ERROR);
if (my_q) {
total += dump_tasks_in_cfs_rq(my_q);
continue;
}
}
tc = task_to_context((ulong)node - OFFSET(task_struct_se) -
OFFSET(sched_entity_run_node));
if (!tc)
continue;
if (hq_enter((ulong)tc)) {
INDENT(5);
dump_task_runq_entry(tc, 0);
} else {
error(WARNING, "duplicate CFS runqueue node: task %lx\n",
tc->task);
return total;
}
total++;
}
return total;
}
static int
dump_tasks_in_task_group_cfs_rq(int depth, ulong cfs_rq, int cpu,
struct task_context *ctc)
{
struct task_context *tc;
struct rb_root *root;
struct rb_node *node;
ulong my_q, leftmost, curr, curr_my_q, tg;
int total, i;
total = 0;
curr_my_q = curr = 0;
if (depth) {
readmem(cfs_rq + OFFSET(cfs_rq_tg), KVADDR,
&tg, sizeof(ulong), "cfs_rq tg",
FAULT_ON_ERROR);
for (i = 0; i < tgi_p; i++) {
if (tgi_array[i]->task_group == tg) {
print_group_header_fair(depth,
cfs_rq, tgi_array[i]);
tgi_array[i]->use = 0;
break;
}
}
}
if (VALID_MEMBER(sched_entity_my_q)) {
readmem(cfs_rq + OFFSET(cfs_rq_curr), KVADDR, &curr,
sizeof(ulong), "curr", FAULT_ON_ERROR);
if (curr) {
readmem(curr + OFFSET(sched_entity_my_q), KVADDR,
&curr_my_q, sizeof(ulong), "curr->my_q",
FAULT_ON_ERROR);
if (curr_my_q) {
total++;
total += dump_tasks_in_task_group_cfs_rq(depth + 1,
curr_my_q, cpu, ctc);
}
}
}
/*
* check if "curr" is the task that is current running task
*/
if (!curr_my_q && ctc && (curr - OFFSET(task_struct_se)) == ctc->task) {
/* curr is not in the rb tree, so let's print it here */
total++;
INDENT(5 + 3 * depth);
dump_task_runq_entry(ctc, 1);
}
readmem(cfs_rq + OFFSET(cfs_rq_rb_leftmost), KVADDR, &leftmost,
sizeof(ulong), "rb_leftmost", FAULT_ON_ERROR);
root = (struct rb_root *)(cfs_rq + OFFSET(cfs_rq_tasks_timeline));
for (node = rb_first(root); leftmost && node; node = rb_next(node)) {
if (VALID_MEMBER(sched_entity_my_q)) {
readmem((ulong)node - OFFSET(sched_entity_run_node)
+ OFFSET(sched_entity_my_q), KVADDR, &my_q,
sizeof(ulong), "my_q", FAULT_ON_ERROR);
if (my_q) {
total++;
total += dump_tasks_in_task_group_cfs_rq(depth + 1,
my_q, cpu, ctc);
continue;
}
}
tc = task_to_context((ulong)node - OFFSET(task_struct_se) -
OFFSET(sched_entity_run_node));
if (!tc)
continue;
if (hq_enter((ulong)tc)) {
INDENT(5 + 3 * depth);
dump_task_runq_entry(tc, 0);
} else {
error(WARNING, "duplicate CFS runqueue node: task %lx\n",
tc->task);
return total;
}
total++;
}
total += dump_tasks_in_lower_dequeued_cfs_rq(depth, cfs_rq, cpu, ctc);
if (!total) {
INDENT(5 + 3 * depth);
fprintf(fp, "[no tasks queued]\n");
}
return total;
}
static void
dump_on_rq_tasks(void)
{
char buf[BUFSIZE];
struct task_context *tc;
int i, cpu, on_rq, tot;
ulong *cpus;
if (!VALID_MEMBER(task_struct_on_rq)) {
MEMBER_OFFSET_INIT(task_struct_se, "task_struct", "se");
STRUCT_SIZE_INIT(sched_entity, "sched_entity");
MEMBER_OFFSET_INIT(sched_entity_on_rq, "sched_entity", "on_rq");
MEMBER_OFFSET_INIT(task_struct_on_rq, "task_struct", "on_rq");
MEMBER_OFFSET_INIT(task_struct_prio, "task_struct", "prio");
if (INVALID_MEMBER(task_struct_on_rq)) {
if (INVALID_MEMBER(task_struct_se) ||
INVALID_SIZE(sched_entity))
option_not_supported('d');
}
}
cpus = pc->curcmd_flags & CPUMASK ?
(ulong *)(ulong)pc->curcmd_private : NULL;
for (cpu = 0; cpu < kt->cpus; cpu++) {
if (cpus && !NUM_IN_BITMAP(cpus, cpu))
continue;
fprintf(fp, "%sCPU %d", cpu ? "\n" : "", cpu);
if (hide_offline_cpu(cpu)) {
fprintf(fp, " [OFFLINE]\n");
continue;
} else
fprintf(fp, "\n");
tc = FIRST_CONTEXT();
tot = 0;
for (i = 0; i < RUNNING_TASKS(); i++, tc++) {
if (VALID_MEMBER(task_struct_on_rq)) {
readmem(tc->task + OFFSET(task_struct_on_rq),
KVADDR, &on_rq, sizeof(int),
"task on_rq", FAULT_ON_ERROR);
} else {
readmem(tc->task + OFFSET(task_struct_se), KVADDR,
buf, SIZE(sched_entity), "task se",
FAULT_ON_ERROR);
on_rq = INT(buf + OFFSET(sched_entity_on_rq));
}
if (!on_rq || tc->processor != cpu)
continue;
INDENT(5);
dump_task_runq_entry(tc, 0);
tot++;
}
if (!tot) {
INDENT(5);
fprintf(fp, "[no tasks queued]\n");
}
}
}
static void
cfs_rq_offset_init(void)
{
if (!VALID_STRUCT(cfs_rq)) {
STRUCT_SIZE_INIT(cfs_rq, "cfs_rq");
STRUCT_SIZE_INIT(rt_rq, "rt_rq");
MEMBER_OFFSET_INIT(rq_rt, "rq", "rt");
MEMBER_OFFSET_INIT(rq_nr_running, "rq", "nr_running");
MEMBER_OFFSET_INIT(task_struct_se, "task_struct", "se");
STRUCT_SIZE_INIT(sched_entity, "sched_entity");
MEMBER_OFFSET_INIT(sched_entity_run_node, "sched_entity",
"run_node");
MEMBER_OFFSET_INIT(sched_entity_cfs_rq, "sched_entity",
"cfs_rq");
MEMBER_OFFSET_INIT(sched_entity_my_q, "sched_entity",
"my_q");
MEMBER_OFFSET_INIT(sched_rt_entity_my_q, "sched_rt_entity",
"my_q");
MEMBER_OFFSET_INIT(sched_entity_on_rq, "sched_entity", "on_rq");
MEMBER_OFFSET_INIT(cfs_rq_rb_leftmost, "cfs_rq", "rb_leftmost");
MEMBER_OFFSET_INIT(cfs_rq_nr_running, "cfs_rq", "nr_running");
MEMBER_OFFSET_INIT(cfs_rq_tasks_timeline, "cfs_rq",
"tasks_timeline");
MEMBER_OFFSET_INIT(cfs_rq_curr, "cfs_rq", "curr");
MEMBER_OFFSET_INIT(rt_rq_active, "rt_rq", "active");
MEMBER_OFFSET_INIT(task_struct_run_list, "task_struct",
"run_list");
MEMBER_OFFSET_INIT(task_struct_on_rq, "task_struct", "on_rq");
MEMBER_OFFSET_INIT(task_struct_prio, "task_struct",
"prio");
MEMBER_OFFSET_INIT(task_struct_rt, "task_struct", "rt");
MEMBER_OFFSET_INIT(sched_rt_entity_run_list, "sched_rt_entity",
"run_list");
MEMBER_OFFSET_INIT(rt_prio_array_queue, "rt_prio_array", "queue");
}
}
static void
task_group_offset_init(void)
{
if (!VALID_STRUCT(task_group)) {
STRUCT_SIZE_INIT(task_group, "task_group");
MEMBER_OFFSET_INIT(rt_rq_rt_nr_running, "rt_rq", "rt_nr_running");
MEMBER_OFFSET_INIT(cfs_rq_tg, "cfs_rq", "tg");
MEMBER_OFFSET_INIT(rt_rq_tg, "rt_rq", "tg");
MEMBER_OFFSET_INIT(rt_rq_highest_prio, "rt_rq", "highest_prio");
MEMBER_OFFSET_INIT(task_group_css, "task_group", "css");
MEMBER_OFFSET_INIT(cgroup_subsys_state_cgroup,
"cgroup_subsys_state", "cgroup");
MEMBER_OFFSET_INIT(cgroup_dentry, "cgroup", "dentry");
MEMBER_OFFSET_INIT(cgroup_kn, "cgroup", "kn");
MEMBER_OFFSET_INIT(kernfs_node_name, "kernfs_node", "name");
MEMBER_OFFSET_INIT(kernfs_node_parent, "kernfs_node", "parent");
MEMBER_OFFSET_INIT(task_group_siblings, "task_group", "siblings");
MEMBER_OFFSET_INIT(task_group_children, "task_group", "children");
MEMBER_OFFSET_INIT(task_group_cfs_bandwidth,
"task_group", "cfs_bandwidth");
MEMBER_OFFSET_INIT(cfs_rq_throttled, "cfs_rq",
"throttled");
MEMBER_OFFSET_INIT(task_group_rt_bandwidth,
"task_group", "rt_bandwidth");
MEMBER_OFFSET_INIT(rt_rq_rt_throttled, "rt_rq",
"rt_throttled");
}
}
static void
dump_CFS_runqueues(void)
{
int cpu, tot, displayed;
ulong runq, cfs_rq, prio_array;
char *runqbuf, *cfs_rq_buf;
ulong tasks_timeline ATTRIBUTE_UNUSED;
struct task_context *tc;
struct rb_root *root;
struct syment *rq_sp, *init_sp;
ulong *cpus;
cfs_rq_offset_init();
if (!(rq_sp = per_cpu_symbol_search("per_cpu__runqueues")))
error(FATAL, "per-cpu runqueues do not exist\n");
runqbuf = GETBUF(SIZE(runqueue));
if ((init_sp = per_cpu_symbol_search("per_cpu__init_cfs_rq")))
cfs_rq_buf = GETBUF(SIZE(cfs_rq));
else
cfs_rq_buf = NULL;
get_active_set();
cpus = pc->curcmd_flags & CPUMASK ?
(ulong *)(ulong)pc->curcmd_private : NULL;
for (cpu = displayed = 0; cpu < kt->cpus; cpu++) {
if (cpus && !NUM_IN_BITMAP(cpus, cpu))
continue;
if ((kt->flags & SMP) && (kt->flags & PER_CPU_OFF))
runq = rq_sp->value + kt->__per_cpu_offset[cpu];
else
runq = rq_sp->value;
fprintf(fp, "%sCPU %d ", displayed++ ? "\n" : "", cpu);
if (hide_offline_cpu(cpu)) {
fprintf(fp, "[OFFLINE]\n");
continue;
} else
fprintf(fp, "RUNQUEUE: %lx\n", runq);
fprintf(fp, " CURRENT: ");
if ((tc = task_to_context(tt->active_set[cpu])))
fprintf(fp, "PID: %-5ld TASK: %lx COMMAND: \"%s\"\n",
tc->pid, tc->task, tc->comm);
else
fprintf(fp, "%lx\n", tt->active_set[cpu]);
readmem(runq, KVADDR, runqbuf, SIZE(runqueue),
"per-cpu rq", FAULT_ON_ERROR);
if (cfs_rq_buf) {
/*
* Use default task group's cfs_rq on each cpu.
*/
if ((kt->flags & SMP) && (kt->flags & PER_CPU_OFF))
cfs_rq = init_sp->value + kt->__per_cpu_offset[cpu];
else
cfs_rq = init_sp->value;
readmem(cfs_rq, KVADDR, cfs_rq_buf, SIZE(cfs_rq),
"per-cpu cfs_rq", FAULT_ON_ERROR);
root = (struct rb_root *)(cfs_rq +
OFFSET(cfs_rq_tasks_timeline));
} else {
cfs_rq = runq + OFFSET(rq_cfs);
root = (struct rb_root *)(runq + OFFSET(rq_cfs) +
OFFSET(cfs_rq_tasks_timeline));
}
prio_array = runq + OFFSET(rq_rt) + OFFSET(rt_rq_active);
fprintf(fp, " RT PRIO_ARRAY: %lx\n", prio_array);
tot = dump_RT_prio_array(prio_array,
&runqbuf[OFFSET(rq_rt) + OFFSET(rt_rq_active)]);
if (!tot) {
INDENT(5);
fprintf(fp, "[no tasks queued]\n");
}
fprintf(fp, " CFS RB_ROOT: %lx\n", (ulong)root);
hq_open();
tot = dump_tasks_in_cfs_rq(cfs_rq);
hq_close();
if (!tot) {
INDENT(5);
fprintf(fp, "[no tasks queued]\n");
}
}
FREEBUF(runqbuf);
if (cfs_rq_buf)
FREEBUF(cfs_rq_buf);
}
static void
print_group_header_rt(ulong rt_rq, void *t)
{
int throttled;
struct task_group_info *tgi = (struct task_group_info *)t;
fprintf(fp, "TASK_GROUP: %lx RT_RQ: %lx", tgi->task_group, rt_rq);
if (tgi->name)
fprintf(fp, " <%s>", tgi->name);
if (VALID_MEMBER(task_group_rt_bandwidth)) {
readmem(rt_rq + OFFSET(rt_rq_rt_throttled), KVADDR,
&throttled, sizeof(int), "rt_rq rt_throttled",
FAULT_ON_ERROR);
if (throttled)
fprintf(fp, " (THROTTLED)");
}
fprintf(fp, "\n");
}
static void
print_parent_task_group_rt(void *t, int cpu)
{
int prio;
struct task_group_info *tgi;
ulong rt_rq_c, rt_rq_p;
tgi = ((struct task_group_info *)t)->parent;
if (tgi && tgi->use)
print_parent_task_group_fair(tgi, cpu);
else
return;
readmem(tgi->task_group + OFFSET(task_group_rt_rq),
KVADDR, &rt_rq_c, sizeof(ulong),
"task_group rt_rq", FAULT_ON_ERROR);
readmem(rt_rq_c + cpu * sizeof(ulong), KVADDR, &rt_rq_p,
sizeof(ulong), "task_group rt_rq", FAULT_ON_ERROR);
readmem(rt_rq_p + OFFSET(rt_rq_highest_prio), KVADDR, &prio,
sizeof(int), "rt_rq highest prio", FAULT_ON_ERROR);
INDENT(-1 + 6 * tgi->depth);
fprintf(fp, "[%3d] ", prio);
print_group_header_rt(rt_rq_p, tgi);
tgi->use = 0;
}
static int
dump_tasks_in_lower_dequeued_rt_rq(int depth, ulong rt_rq, int cpu)
{
int i, prio, tot, delta, nr_running;
ulong rt_rq_c, rt_rq_p, group;
tot = 0;
for (i = 0; i < tgi_p; i++) {
delta = tgi_array[i]->depth - depth;
if (delta > 1)
break;
if (tgi_array[i]->use == 0 || delta < 1)
continue;
readmem(rt_rq + OFFSET(rt_rq_tg), KVADDR, &group,
sizeof(ulong), "rt_rq tg", FAULT_ON_ERROR);
if (group != tgi_array[i]->parent->task_group)
continue;
readmem(tgi_array[i]->task_group + OFFSET(task_group_rt_rq),
KVADDR, &rt_rq_c, sizeof(ulong), "task_group rt_rq",
FAULT_ON_ERROR);
readmem(rt_rq_c + cpu * sizeof(ulong), KVADDR, &rt_rq_p,
sizeof(ulong), "task_group rt_rq", FAULT_ON_ERROR);
if (rt_rq == rt_rq_p)
continue;
readmem(rt_rq_p + OFFSET(rt_rq_rt_nr_running), KVADDR,
&nr_running, sizeof(int), "rt_rq rt_nr_running",
FAULT_ON_ERROR);
if (nr_running == 0) {
tot += dump_tasks_in_lower_dequeued_rt_rq(depth + 1,
rt_rq_p, cpu);
continue;
}
print_parent_task_group_rt(tgi_array[i], cpu);
readmem(rt_rq_p + OFFSET(rt_rq_highest_prio), KVADDR,
&prio, sizeof(int), "rt_rq highest_prio",
FAULT_ON_ERROR);
INDENT(5 + 6 * depth);
fprintf(fp, "[%3d] ", prio);
tot++;
dump_tasks_in_task_group_rt_rq(depth + 1, rt_rq_p, cpu);
}
return tot;
}
static int
dump_RT_prio_array(ulong k_prio_array, char *u_prio_array)
{
int i, c, tot, cnt, qheads;
ulong offset, kvaddr, uvaddr;
ulong list_head[2];
struct list_data list_data, *ld;
struct task_context *tc;
ulong my_q, task_addr;
char *rt_rq_buf;
qheads = (i = ARRAY_LENGTH(rt_prio_array_queue)) ?
i : get_array_length("rt_prio_array.queue", NULL, SIZE(list_head));
ld = &list_data;
for (i = tot = 0; i < qheads; i++) {
offset = OFFSET(rt_prio_array_queue) + (i * SIZE(list_head));
kvaddr = k_prio_array + offset;
uvaddr = (ulong)u_prio_array + offset;
BCOPY((char *)uvaddr, (char *)&list_head[0], sizeof(ulong)*2);
if (CRASHDEBUG(1))
fprintf(fp, "rt_prio_array[%d] @ %lx => %lx/%lx\n",
i, kvaddr, list_head[0], list_head[1]);
if ((list_head[0] == kvaddr) && (list_head[1] == kvaddr))
continue;
BZERO(ld, sizeof(struct list_data));
ld->start = list_head[0];
ld->flags |= LIST_ALLOCATE;
if (VALID_MEMBER(task_struct_rt) &&
VALID_MEMBER(sched_rt_entity_run_list))
ld->list_head_offset = OFFSET(sched_rt_entity_run_list);
else
ld->list_head_offset = OFFSET(task_struct_run_list);
ld->end = kvaddr;
cnt = do_list(ld);
for (c = 0; c < cnt; c++) {
task_addr = ld->list_ptr[c];
if (VALID_MEMBER(sched_rt_entity_my_q)) {
readmem(ld->list_ptr[c] + OFFSET(sched_rt_entity_my_q),
KVADDR, &my_q, sizeof(ulong), "my_q",
FAULT_ON_ERROR);
if (my_q) {
rt_rq_buf = GETBUF(SIZE(rt_rq));
readmem(my_q, KVADDR, rt_rq_buf,
SIZE(rt_rq), "rt_rq",
FAULT_ON_ERROR);
tot += dump_RT_prio_array(
my_q + OFFSET(rt_rq_active),
&rt_rq_buf[OFFSET(rt_rq_active)]);
FREEBUF(rt_rq_buf);
continue;
}
}
if (VALID_MEMBER(task_struct_rt))
task_addr -= OFFSET(task_struct_rt);
else
task_addr -= OFFSET(task_struct_run_list);
if (!(tc = task_to_context(task_addr)))
continue;
INDENT(5);
fprintf(fp, "[%3d] ", i);
fprintf(fp, "PID: %-5ld TASK: %lx COMMAND: \"%s\"\n",
tc->pid, tc->task, tc->comm);
tot++;
}
FREEBUF(ld->list_ptr);
}
return tot;
}
static void
dump_tasks_in_task_group_rt_rq(int depth, ulong rt_rq, int cpu)
{
int i, c, tot, cnt, qheads;
ulong offset, kvaddr, uvaddr;
ulong list_head[2];
struct list_data list_data, *ld;
struct task_context *tc;
ulong my_q, task_addr, tg, k_prio_array;
char *rt_rq_buf, *u_prio_array;
k_prio_array = rt_rq + OFFSET(rt_rq_active);
rt_rq_buf = GETBUF(SIZE(rt_rq));
readmem(rt_rq, KVADDR, rt_rq_buf, SIZE(rt_rq), "rt_rq", FAULT_ON_ERROR);
u_prio_array = &rt_rq_buf[OFFSET(rt_rq_active)];
if (depth) {
readmem(rt_rq + OFFSET(rt_rq_tg), KVADDR,
&tg, sizeof(ulong), "rt_rq tg",
FAULT_ON_ERROR);
for (i = 0; i < tgi_p; i++) {
if (tgi_array[i]->task_group == tg) {
print_group_header_rt(rt_rq, tgi_array[i]);
tgi_array[i]->use = 0;
break;
}
}
}
qheads = (i = ARRAY_LENGTH(rt_prio_array_queue)) ?
i : get_array_length("rt_prio_array.queue", NULL, SIZE(list_head));
ld = &list_data;
for (i = tot = 0; i < qheads; i++) {
offset = OFFSET(rt_prio_array_queue) + (i * SIZE(list_head));
kvaddr = k_prio_array + offset;
uvaddr = (ulong)u_prio_array + offset;
BCOPY((char *)uvaddr, (char *)&list_head[0], sizeof(ulong)*2);
if (CRASHDEBUG(1))
fprintf(fp, "rt_prio_array[%d] @ %lx => %lx/%lx\n",
i, kvaddr, list_head[0], list_head[1]);
if ((list_head[0] == kvaddr) && (list_head[1] == kvaddr))
continue;
BZERO(ld, sizeof(struct list_data));
ld->start = list_head[0];
ld->flags |= LIST_ALLOCATE;
if (VALID_MEMBER(task_struct_rt) &&
VALID_MEMBER(sched_rt_entity_run_list))
ld->list_head_offset = OFFSET(sched_rt_entity_run_list);
else
ld->list_head_offset = OFFSET(task_struct_run_list);
ld->end = kvaddr;
cnt = do_list(ld);
for (c = 0; c < cnt; c++) {
task_addr = ld->list_ptr[c];
if (INVALID_MEMBER(sched_rt_entity_my_q))
goto is_task;
readmem(ld->list_ptr[c] + OFFSET(sched_rt_entity_my_q),
KVADDR, &my_q, sizeof(ulong), "my_q",
FAULT_ON_ERROR);
if (!my_q) {
task_addr -= OFFSET(task_struct_rt);
goto is_task;
}
INDENT(5 + 6 * depth);
fprintf(fp, "[%3d] ", i);
tot++;
dump_tasks_in_task_group_rt_rq(depth + 1, my_q, cpu);
continue;
is_task:
if (!(tc = task_to_context(task_addr)))
continue;
INDENT(5 + 6 * depth);
fprintf(fp, "[%3d] ", i);
fprintf(fp, "PID: %-5ld TASK: %lx COMMAND: \"%s\"\n",
tc->pid, tc->task, tc->comm);
tot++;
}
FREEBUF(ld->list_ptr);
}
tot += dump_tasks_in_lower_dequeued_rt_rq(depth, rt_rq, cpu);
if (!tot) {
INDENT(5 + 6 * depth);
fprintf(fp, "[no tasks queued]\n");
}
FREEBUF(rt_rq_buf);
}
static char *
get_task_group_name(ulong group)
{
ulong cgroup, dentry, kernfs_node, parent, name;
char *dentry_buf, *tmp;
char buf[BUFSIZE];
int len;
tmp = NULL;
readmem(group + OFFSET(task_group_css) + OFFSET(cgroup_subsys_state_cgroup),
KVADDR, &cgroup, sizeof(ulong),
"task_group css cgroup", FAULT_ON_ERROR);
if (cgroup == 0)
return NULL;
if (VALID_MEMBER(cgroup_dentry)) {
readmem(cgroup + OFFSET(cgroup_dentry), KVADDR, &dentry, sizeof(ulong),
"cgroup dentry", FAULT_ON_ERROR);
if (dentry == 0)
return NULL;
dentry_buf = GETBUF(SIZE(dentry));
readmem(dentry, KVADDR, dentry_buf, SIZE(dentry),
"dentry", FAULT_ON_ERROR);
len = UINT(dentry_buf + OFFSET(dentry_d_name) + OFFSET(qstr_len));
tmp = GETBUF(len + 1);
name = ULONG(dentry_buf + OFFSET(dentry_d_name) + OFFSET(qstr_name));
readmem(name, KVADDR, tmp, len, "qstr name", FAULT_ON_ERROR);
FREEBUF(dentry_buf);
return tmp;
}
/*
* Emulate kernfs_name() and kernfs_name_locked()
*/
if (INVALID_MEMBER(cgroup_kn) || INVALID_MEMBER(kernfs_node_name) ||
INVALID_MEMBER(kernfs_node_parent))
return NULL;
readmem(cgroup + OFFSET(cgroup_kn), KVADDR, &kernfs_node, sizeof(ulong),
"cgroup kn", FAULT_ON_ERROR);
if (kernfs_node == 0)
return NULL;
readmem(kernfs_node + OFFSET(kernfs_node_parent), KVADDR, &parent,
sizeof(ulong), "kernfs_node parent", FAULT_ON_ERROR);
if (!parent) {
tmp = GETBUF(2);
strcpy(tmp, "/");
return tmp;
}
readmem(kernfs_node + OFFSET(kernfs_node_name), KVADDR, &name,
sizeof(ulong), "kernfs_node name", FAULT_ON_ERROR);
if (!name || !read_string(name, buf, BUFSIZE-1))
return NULL;
tmp = GETBUF(strlen(buf)+1);
strcpy(tmp, buf);
return tmp;
}
static void
fill_task_group_info_array(int depth, ulong group, char *group_buf, int i)
{
int d;
ulong kvaddr, uvaddr, offset;
ulong list_head[2], next;
struct task_group_info **tgi_array_new;
d = tgi_p;
tgi_array[tgi_p] = (struct task_group_info *)
GETBUF(sizeof(struct task_group_info));
if (depth)
tgi_array[tgi_p]->use = 1;
else
tgi_array[tgi_p]->use = 0;
tgi_array[tgi_p]->depth = depth;
tgi_array[tgi_p]->name = get_task_group_name(group);
tgi_array[tgi_p]->task_group = group;
if (i >= 0)
tgi_array[tgi_p]->parent = tgi_array[i];
else
tgi_array[tgi_p]->parent = NULL;
tgi_p++;
if (tgi_p == tgi_p_max) {
tgi_p_max += MAX_GROUP_NUM;
tgi_array_new = (struct task_group_info **)
GETBUF(sizeof(void *) * tgi_p_max);
BCOPY(tgi_array, tgi_array_new, sizeof(void *) * tgi_p);
FREEBUF(tgi_array);
tgi_array = tgi_array_new;
}
offset = OFFSET(task_group_children);
kvaddr = group + offset;
uvaddr = (ulong)(group_buf + offset);
BCOPY((char *)uvaddr, (char *)&list_head[0], sizeof(ulong)*2);
if ((list_head[0] == kvaddr) && (list_head[1] == kvaddr))
return;
next = list_head[0];
while (next != kvaddr) {
group = next - OFFSET(task_group_siblings);
readmem(group, KVADDR, group_buf, SIZE(task_group),
"task_group", FAULT_ON_ERROR);
next = ULONG(group_buf + OFFSET(task_group_siblings) +
OFFSET(list_head_next));
fill_task_group_info_array(depth + 1, group, group_buf, d);
}
}
static void
dump_tasks_by_task_group(void)
{
int cpu, displayed;
ulong root_task_group, cfs_rq = 0, cfs_rq_p;
ulong rt_rq = 0, rt_rq_p;
char *buf;
struct task_context *tc;
char *task_group_name;
ulong *cpus;
cfs_rq_offset_init();
task_group_offset_init();
root_task_group = 0;
task_group_name = NULL;
if (symbol_exists("init_task_group")) {
root_task_group = symbol_value("init_task_group");
task_group_name = "INIT";
} else if (symbol_exists("root_task_group")) {
root_task_group = symbol_value("root_task_group");
task_group_name = "ROOT";
} else
error(FATAL, "cannot determine root task_group\n");
tgi_p_max = MAX_GROUP_NUM;
tgi_array = (struct task_group_info **)GETBUF(sizeof(void *)
* tgi_p_max);
buf = GETBUF(SIZE(task_group));
readmem(root_task_group, KVADDR, buf, SIZE(task_group),
"task_group", FAULT_ON_ERROR);
if (VALID_MEMBER(task_group_rt_rq))
rt_rq = ULONG(buf + OFFSET(task_group_rt_rq));
if (VALID_MEMBER(task_group_cfs_rq))
cfs_rq = ULONG(buf + OFFSET(task_group_cfs_rq));
fill_task_group_info_array(0, root_task_group, buf, -1);
sort_task_group_info_array();
if (CRASHDEBUG(1))
print_task_group_info_array();
get_active_set();
cpus = pc->curcmd_flags & CPUMASK ?
(ulong *)(ulong)pc->curcmd_private : NULL;
for (cpu = displayed = 0; cpu < kt->cpus; cpu++) {
if (cpus && !NUM_IN_BITMAP(cpus, cpu))
continue;
if (rt_rq)
readmem(rt_rq + cpu * sizeof(ulong), KVADDR,
&rt_rq_p, sizeof(ulong), "task_group rt_rq",
FAULT_ON_ERROR);
if (cfs_rq)
readmem(cfs_rq + cpu * sizeof(ulong), KVADDR,
&cfs_rq_p, sizeof(ulong), "task_group cfs_rq",
FAULT_ON_ERROR);
fprintf(fp, "%sCPU %d", displayed++ ? "\n" : "", cpu);
if (hide_offline_cpu(cpu)) {
fprintf(fp, " [OFFLINE]\n");
continue;
} else
fprintf(fp, "\n");
fprintf(fp, " CURRENT: ");
if ((tc = task_to_context(tt->active_set[cpu])))
fprintf(fp, "PID: %-5ld TASK: %lx COMMAND: \"%s\"\n",
tc->pid, tc->task, tc->comm);
else
fprintf(fp, "%lx\n", tt->active_set[cpu]);
if (rt_rq) {
fprintf(fp, " %s_TASK_GROUP: %lx RT_RQ: %lx\n",
task_group_name, root_task_group, rt_rq_p);
reuse_task_group_info_array();
dump_tasks_in_task_group_rt_rq(0, rt_rq_p, cpu);
}
if (cfs_rq) {
fprintf(fp, " %s_TASK_GROUP: %lx CFS_RQ: %lx\n",
task_group_name, root_task_group, cfs_rq_p);
reuse_task_group_info_array();
dump_tasks_in_task_group_cfs_rq(0, cfs_rq_p, cpu, tc);
}
}
FREEBUF(buf);
free_task_group_info_array();
}
#undef _NSIG
#define _NSIG 64
#define _NSIG_BPW machdep->bits
#define _NSIG_WORDS (_NSIG / _NSIG_BPW)
#undef SIGRTMIN
#define SIGRTMIN 32
static struct signame {
char *name;
char *altname;
} signame[_NSIG] = {
/* 0 */ {NULL, NULL},
/* 1 */ {"SIGHUP", NULL},
/* 2 */ {"SIGINT", NULL},
/* 3 */ {"SIGQUIT", NULL},
/* 4 */ {"SIGILL", NULL},
/* 5 */ {"SIGTRAP", NULL},
/* 6 */ {"SIGABRT", "SIGIOT"},
/* 7 */ {"SIGBUS", NULL},
/* 8 */ {"SIGFPE", NULL},
/* 9 */ {"SIGKILL", NULL},
/* 10 */ {"SIGUSR1", NULL},
/* 11 */ {"SIGSEGV", NULL},
/* 12 */ {"SIGUSR2", NULL},
/* 13 */ {"SIGPIPE", NULL},
/* 14 */ {"SIGALRM", NULL},
/* 15 */ {"SIGTERM", NULL},
/* 16 */ {"SIGSTKFLT", NULL},
/* 17 */ {"SIGCHLD", "SIGCLD"},
/* 18 */ {"SIGCONT", NULL},
/* 19 */ {"SIGSTOP", NULL},
/* 20 */ {"SIGTSTP", NULL},
/* 21 */ {"SIGTTIN", NULL},
/* 22 */ {"SIGTTOU", NULL},
/* 23 */ {"SIGURG", NULL},
/* 24 */ {"SIGXCPU", NULL},
/* 25 */ {"SIGXFSZ", NULL},
/* 26 */ {"SIGVTALRM", NULL},
/* 27 */ {"SIGPROF", NULL},
/* 28 */ {"SIGWINCH", NULL},
/* 29 */ {"SIGIO", "SIGPOLL"},
/* 30 */ {"SIGPWR", NULL},
/* 31 */ {"SIGSYS", "SIGUNUSED"},
{NULL, NULL}, /* Real time signals start here. */
};
static int
sigrt_minmax(int *min, int *max)
{
int sigrtmax, j;
sigrtmax = THIS_KERNEL_VERSION < LINUX(2,5,0) ?
_NSIG - 1 : _NSIG;
if (min && max) {
j = sigrtmax-SIGRTMIN-1;
*max = j / 2;
*min = j - *max;
}
return sigrtmax;
}
static void
signame_list(void)
{
int i, sigrtmax, j, min, max;
sigrtmax = sigrt_minmax(&min, &max);
j = 1;
for (i = 1; i <= sigrtmax; i++) {
if ((i == SIGRTMIN) || (i == sigrtmax)) {
fprintf(fp, "[%d] %s", i,
(i== SIGRTMIN) ? "SIGRTMIN" : "SIGRTMAX");
} else if (i > SIGRTMIN) {
if (j <= min){
fprintf(fp, "[%d] %s%d", i , "SIGRTMIN+", j);
j++;
} else if (max >= 1) {
fprintf(fp, "[%d] %s%d", i , "SIGRTMAX-",max);
max--;
}
} else {
if (!signame[i].name)
continue;
fprintf(fp, "%s[%d] %s", i < 10 ? " " : "",
i, signame[i].name);
if (signame[i].altname)
fprintf(fp, "/%s", signame[i].altname);
}
fprintf(fp, "\n");
}
}
/*
* Translate the bits in a signal set into their name strings.
*/
static void
translate_sigset(ulonglong sigset)
{
int sigrtmax, min, max, i, j, c, len;
char buf[BUFSIZE];
if (!sigset) {
fprintf(fp, "(none)\n");
return;
}
len = 0;
sigrtmax= sigrt_minmax(&min, &max);
j = 1;
for (i = 1, c = 0; i <= sigrtmax; i++) {
if (sigset & (ulonglong)1) {
if (i == SIGRTMIN || i == sigrtmax)
sprintf(buf, "%s%s", c++ ? " " : "",
(i==SIGRTMIN) ? "SIGRTMIN" : "SIGRTMAX");
else if (i > SIGRTMIN) {
if (j <= min)
sprintf(buf, "%s%s%d",
c++ ? " " : "", "SIGRTMIN+", j);
else if (max >= 1)
sprintf(buf, "%s%s%d",
c++ ? " " : "", "SIGRTMAX-", max);
} else
sprintf(buf, "%s%s", c++ ? " " : "",
signame[i].name);
if ((len + strlen(buf)) > 80) {
shift_string_left(buf, 1);
fprintf(fp, "\n");
len = 0;
}
len += strlen(buf);
fprintf(fp, "%s", buf);
}
sigset >>= 1;
if (i > SIGRTMIN) {
if (j <= min)
j++;
else if (max >= 1)
max--;
}
}
fprintf(fp, "\n");
}
/*
* Machine dependent interface to modify signame struct contents.
*/
void modify_signame(int sig, char *name, char *altname)
{
signame[sig].name = name;
signame[sig].altname = altname;
}
/*
* Display all signal-handling data for a task.
*
* Reference handling framework is here, but not used as of yet.
*/
void
cmd_sig(void)
{
int c, tcnt, bogus;
ulong value;
ulonglong sigset;
struct reference *ref;
struct task_context *tc;
ulong *tasklist;
char *siglist;
int thread_group = FALSE;
tasklist = (ulong *)GETBUF((MAXARGS+NR_CPUS)*sizeof(ulong));
ref = (struct reference *)GETBUF(sizeof(struct reference));
siglist = GETBUF(BUFSIZE);
ref->str = siglist;
while ((c = getopt(argcnt, args, "lR:s:g")) != EOF) {
switch(c)
{
case 's':
sigset = htoll(optarg, FAULT_ON_ERROR, NULL);
translate_sigset(sigset);
return;
case 'R':
if (strlen(ref->str))
strcat(ref->str, ",");
strcat(ref->str, optarg);
break;
case 'l':
signame_list();
return;
case 'g':
pc->curcmd_flags |= TASK_SPECIFIED;
thread_group = TRUE;
break;
default:
argerrs++;
break;
}
}
if (argerrs)
cmd_usage(pc->curcmd, SYNOPSIS);
tcnt = bogus = 0;
while (args[optind]) {
if (IS_A_NUMBER(args[optind])) {
switch (str_to_context(args[optind], &value, &tc))
{
case STR_PID:
for (tc = pid_to_context(value); tc;
tc = tc->tc_next)
tasklist[tcnt++] = tc->task;
break;
case STR_TASK:
tasklist[tcnt++] = value;
break;
case STR_INVALID:
bogus++;
error(INFO, "invalid task or pid value: %s\n\n",
args[optind]);
break;
}
} else if (strstr(args[optind], ",") ||
MEMBER_EXISTS("task_struct", args[optind])) {
if (strlen(ref->str))
strcat(ref->str, ",");
strcat(ref->str, args[optind]);
} else
error(INFO, "invalid task or pid value: %s\n\n",
args[optind]);
optind++;
}
if (!tcnt && !bogus)
tasklist[tcnt++] = CURRENT_TASK();
for (c = 0; c < tcnt; c++) {
if (thread_group)
do_sig_thread_group(tasklist[c]);
else {
do_sig(tasklist[c], 0, strlen(ref->str) ? ref : NULL);
fprintf(fp, "\n");
}
}
}
/*
* Do the work for the "sig -g" command option, coming from sig or foreach.
*/
static void
do_sig_thread_group(ulong task)
{
int i;
int cnt;
struct task_context *tc;
ulong tgid;
tc = task_to_context(task);
tgid = task_tgid(task);
if (tc->pid != tgid) {
if (pc->curcmd_flags & TASK_SPECIFIED) {
if (!(tc = tgid_to_context(tgid)))
return;
task = tc->task;
} else
return;
}
if ((tc->pid == 0) && (pc->curcmd_flags & IDLE_TASK_SHOWN))
return;
print_task_header(fp, tc, 0);
dump_signal_data(tc, THREAD_GROUP_LEVEL);
fprintf(fp, "\n ");
print_task_header(fp, tc, 0);
dump_signal_data(tc, TASK_LEVEL|TASK_INDENT);
tc = FIRST_CONTEXT();
for (i = cnt = 0; i < RUNNING_TASKS(); i++, tc++) {
if (tc->task == task)
continue;
if (task_tgid(tc->task) == tgid) {
fprintf(fp, "\n ");
print_task_header(fp, tc, 0);
dump_signal_data(tc, TASK_LEVEL|TASK_INDENT);
cnt++;
if (tc->pid == 0)
pc->curcmd_flags |= IDLE_TASK_SHOWN;
}
}
fprintf(fp, "\n");
}
/*
* Do the work for the sig command, coming from sig or foreach.
*/
void
do_sig(ulong task, ulong flags, struct reference *ref)
{
struct task_context *tc;
tc = task_to_context(task);
if (ref)
signal_reference(tc, flags, ref);
else {
if (!(flags & FOREACH_SIG))
print_task_header(fp, tc, 0);
dump_signal_data(tc, TASK_LEVEL|THREAD_GROUP_LEVEL);
}
}
/*
* Implementation for -R reference for the sig command.
*/
static void
signal_reference(struct task_context *tc, ulong flags, struct reference *ref)
{
if (flags & FOREACH_SIG)
error(FATAL, "sig: -R not supported yet\n");
else
error(FATAL, "-R not supported yet\n");
}
/*
* Dump all signal-handling data for a task.
*/
static void
dump_signal_data(struct task_context *tc, ulong flags)
{
int i, sigrtmax, others, use_sighand;
int translate, sigpending;
uint ti_flags;
ulonglong sigset, blocked, mask;
ulong signal_struct, kaddr, handler, sa_flags, sigqueue;
ulong sighand_struct;
long size;
char *signal_buf, *uaddr;
ulong shared_pending, signal;
char buf1[BUFSIZE];
char buf2[BUFSIZE];
char buf3[BUFSIZE];
char buf4[BUFSIZE];
sigpending = sigqueue = 0;
sighand_struct = signal_struct = 0;
if (VALID_STRUCT(sigqueue) && !VALID_MEMBER(sigqueue_next)) {
MEMBER_OFFSET_INIT(sigqueue_next, "sigqueue", "next");
MEMBER_OFFSET_INIT(sigqueue_list, "sigqueue", "list");
MEMBER_OFFSET_INIT(sigqueue_info, "sigqueue", "info");
} else if (!VALID_MEMBER(signal_queue_next)) {
MEMBER_OFFSET_INIT(signal_queue_next, "signal_queue", "next");
MEMBER_OFFSET_INIT(signal_queue_info, "signal_queue", "info");
}
sigset = task_signal(tc->task, 0);
if (!tt->last_task_read)
return;
if (VALID_MEMBER(task_struct_sig))
signal_struct = ULONG(tt->task_struct +
OFFSET(task_struct_sig));
else if (VALID_MEMBER(task_struct_signal))
signal_struct = ULONG(tt->task_struct +
OFFSET(task_struct_signal));
size = MAX(SIZE(signal_struct), VALID_SIZE(signal_queue) ?
SIZE(signal_queue) : SIZE(sigqueue));
if (VALID_SIZE(sighand_struct))
size = MAX(size, SIZE(sighand_struct));
signal_buf = GETBUF(size);
if (signal_struct)
readmem(signal_struct, KVADDR, signal_buf,
SIZE(signal_struct), "signal_struct buffer",
FAULT_ON_ERROR);
/*
* Signal dispositions (thread group level).
*/
if (flags & THREAD_GROUP_LEVEL) {
if (flags & TASK_INDENT)
INDENT(2);
fprintf(fp, "SIGNAL_STRUCT: %lx ", signal_struct);
if (!signal_struct) {
fprintf(fp, "\n");
return;
}
if (VALID_MEMBER(signal_struct_count))
fprintf(fp, "COUNT: %d\n",
INT(signal_buf + OFFSET(signal_struct_count)));
else if (VALID_MEMBER(signal_struct_nr_threads))
fprintf(fp, "NR_THREADS: %d\n",
INT(signal_buf + OFFSET(signal_struct_nr_threads)));
else
fprintf(fp, "\n");
if (flags & TASK_INDENT)
INDENT(2);
fprintf(fp, " SIG %s %s %s %s\n",
mkstring(buf1, VADDR_PRLEN == 8 ? 9 : VADDR_PRLEN,
CENTER, "SIGACTION"),
mkstring(buf2, UVADDR_PRLEN, RJUST, "HANDLER"),
mkstring(buf3, 16, CENTER, "MASK"),
mkstring(buf4, VADDR_PRLEN, LJUST, "FLAGS"));
if (VALID_MEMBER(task_struct_sighand)) {
sighand_struct = ULONG(tt->task_struct +
OFFSET(task_struct_sighand));
readmem(sighand_struct, KVADDR, signal_buf,
SIZE(sighand_struct), "sighand_struct buffer",
FAULT_ON_ERROR);
use_sighand = TRUE;
} else
use_sighand = FALSE;
sigrtmax = sigrt_minmax(NULL, NULL);
for (i = 1; i <= sigrtmax; i++) {
if (flags & TASK_INDENT)
INDENT(2);
fprintf(fp, "%s[%d] ", i < 10 ? " " : "", i);
if (use_sighand) {
kaddr = sighand_struct +
OFFSET(sighand_struct_action) +
((i-1) * SIZE(k_sigaction));
uaddr = signal_buf +
OFFSET(sighand_struct_action) +
((i-1) * SIZE(k_sigaction));
} else {
kaddr = signal_struct +
OFFSET(signal_struct_action) +
((i-1) * SIZE(k_sigaction));
uaddr = signal_buf +
OFFSET(signal_struct_action) +
((i-1) * SIZE(k_sigaction));
}
handler = ULONG(uaddr + OFFSET(sigaction_sa_handler));
switch ((long)handler)
{
case -1:
mkstring(buf1, UVADDR_PRLEN, RJUST, "SIG_ERR");
break;
case 0:
mkstring(buf1, UVADDR_PRLEN, RJUST, "SIG_DFL");
break;
case 1:
mkstring(buf1, UVADDR_PRLEN, RJUST, "SIG_IGN");
break;
default:
mkstring(buf1, UVADDR_PRLEN, RJUST|LONG_HEX,
MKSTR(handler));
break;
}
mask = sigaction_mask((ulong)uaddr);
sa_flags = ULONG(uaddr + OFFSET(sigaction_sa_flags));
fprintf(fp, "%s%s %s %016llx %lx ",
space(MINSPACE-1),
mkstring(buf2,
UVADDR_PRLEN,LJUST|LONG_HEX,MKSTR(kaddr)),
buf1,
mask,
sa_flags);
if (sa_flags) {
others = 0; translate = 1;
if (sa_flags & SA_NOCLDSTOP)
fprintf(fp, "%s%sSA_NOCLDSTOP",
translate-- > 0 ? "(" : "",
others++ ? "|" : "");
#ifdef SA_RESTORER
if (sa_flags & SA_RESTORER)
fprintf(fp, "%s%sSA_RESTORER",
translate-- > 0 ? "(" : "",
others++ ? "|" : "");
#endif
#ifdef SA_NOCLDWAIT
if (sa_flags & SA_NOCLDWAIT)
fprintf(fp, "%s%sSA_NOCLDWAIT",
translate-- > 0 ? "(" : "",
others++ ? "|" : "");
#endif
if (sa_flags & SA_SIGINFO)
fprintf(fp, "%s%sSA_SIGINFO",
translate-- > 0 ? "(" : "",
others++ ? "|" : "");
if (sa_flags & SA_ONSTACK)
fprintf(fp, "%s%sSA_ONSTACK",
translate-- > 0 ? "(" : "",
others++ ? "|" : "");
if (sa_flags & SA_RESTART)
fprintf(fp, "%s%sSA_RESTART",
translate-- > 0 ? "(" : "",
others++ ? "|" : "");
if (sa_flags & SA_NODEFER)
fprintf(fp, "%s%sSA_NODEFER",
translate-- > 0 ? "(" : "",
others++ ? "|" : "");
if (sa_flags & SA_RESETHAND)
fprintf(fp, "%s%sSA_RESETHAND",
translate-- > 0 ? "(" : "",
others++ ? "|" : "");
if (translate < 1)
fprintf(fp, ")");
}
fprintf(fp, "\n");
}
}
if (flags & TASK_LEVEL) {
/*
* Pending signals (task level).
*/
if (VALID_MEMBER(task_struct_sigpending))
sigpending = INT(tt->task_struct +
OFFSET(task_struct_sigpending));
else if (VALID_MEMBER(thread_info_flags)) {
fill_thread_info(tc->thread_info);
ti_flags = UINT(tt->thread_info + OFFSET(thread_info_flags));
sigpending = ti_flags & (1<<TIF_SIGPENDING);
}
if (flags & TASK_INDENT)
INDENT(2);
fprintf(fp, "SIGPENDING: %s\n", sigpending ? "yes" : "no");
/*
* Blocked signals (task level).
*/
blocked = task_blocked(tc->task);
if (flags & TASK_INDENT)
INDENT(2);
fprintf(fp, " BLOCKED: %016llx\n", blocked);
/*
* Pending queue (task level).
*/
if (flags & TASK_INDENT)
INDENT(2);
if (VALID_MEMBER(signal_struct_shared_pending)) {
fprintf(fp, "PRIVATE_PENDING\n");
if (flags & TASK_INDENT)
INDENT(2);
}
fprintf(fp, " SIGNAL: %016llx\n", sigset);
if (VALID_MEMBER(task_struct_sigqueue))
sigqueue = ULONG(tt->task_struct +
OFFSET(task_struct_sigqueue));
else if (VALID_MEMBER(task_struct_pending))
sigqueue = ULONG(tt->task_struct +
OFFSET(task_struct_pending) +
OFFSET_OPTION(sigpending_head,
sigpending_list));
if (VALID_MEMBER(sigqueue_list) && empty_list(sigqueue))
sigqueue = 0;
if (flags & TASK_INDENT)
INDENT(2);
if (sigqueue) {
fprintf(fp, " SIGQUEUE: SIG %s\n",
mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, "SIGINFO"));
sigqueue_list(sigqueue);
} else
fprintf(fp, " SIGQUEUE: (empty)\n");
}
/*
* Pending queue (thread group level).
*/
if ((flags & THREAD_GROUP_LEVEL) &&
VALID_MEMBER(signal_struct_shared_pending)) {
fprintf(fp, "SHARED_PENDING\n");
shared_pending = signal_struct + OFFSET(signal_struct_shared_pending);
signal = shared_pending + OFFSET(sigpending_signal);
readmem(signal, KVADDR, signal_buf,SIZE(sigpending_signal),
"signal", FAULT_ON_ERROR);
sigset = task_signal(0, (ulong*)signal_buf);
if (flags & TASK_INDENT)
INDENT(2);
fprintf(fp, " SIGNAL: %016llx\n", sigset);
sigqueue = (shared_pending +
OFFSET_OPTION(sigpending_head, sigpending_list) +
OFFSET(list_head_next));
readmem(sigqueue,KVADDR, signal_buf,
SIZE(sigqueue), "sigqueue", FAULT_ON_ERROR);
sigqueue = ULONG(signal_buf);
if (VALID_MEMBER(sigqueue_list) && empty_list(sigqueue))
sigqueue = 0;
if (flags & TASK_INDENT)
INDENT(2);
if (sigqueue) {
fprintf(fp, " SIGQUEUE: SIG %s\n",
mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, "SIGINFO"));
sigqueue_list(sigqueue);
} else
fprintf(fp, " SIGQUEUE: (empty)\n");
}
FREEBUF(signal_buf);
}
/*
* Dump a pending signal queue (private/shared).
*/
static void sigqueue_list(ulong sigqueue) {
ulong sigqueue_save, next;
int sig;
char *signal_buf;
long size;
size = VALID_SIZE(signal_queue) ? SIZE(signal_queue) : SIZE(sigqueue);
signal_buf = GETBUF(size);
sigqueue_save = sigqueue;
while (sigqueue) {
readmem(sigqueue, KVADDR, signal_buf,
SIZE_OPTION(signal_queue, sigqueue),
"signal_queue/sigqueue", FAULT_ON_ERROR);
if (VALID_MEMBER(signal_queue_next) &&
VALID_MEMBER(signal_queue_info)) {
next = ULONG(signal_buf + OFFSET(signal_queue_next));
sig = INT(signal_buf + OFFSET(signal_queue_info) +
OFFSET(siginfo_si_signo));
} else {
next = ULONG(signal_buf +
OFFSET_OPTION(sigqueue_next, sigqueue_list));
sig = INT(signal_buf + OFFSET(sigqueue_info) +
OFFSET(siginfo_si_signo));
}
if (sigqueue_save == next)
break;
fprintf(fp, " %3d %lx\n",
sig, sigqueue +
OFFSET_OPTION(signal_queue_info, sigqueue_info));
sigqueue = next;
}
FREEBUF(signal_buf);
}
/*
* Return the current set of signals sent to a task, in the form of
* a long long data type form that can be easily masked regardless
* of its size.
*/
static ulonglong
task_signal(ulong task, ulong *signal)
{
ulong *sigset_ptr;
ulonglong sigset = 0;
if (task) {
fill_task_struct(task);
if (!tt->last_task_read)
return 0;
if (VALID_MEMBER(sigpending_signal)) {
sigset_ptr = (ulong *)(tt->task_struct +
OFFSET(task_struct_pending) +
OFFSET(sigpending_signal));
} else if (VALID_MEMBER(task_struct_signal)) {
sigset_ptr = (ulong *)(tt->task_struct +
OFFSET(task_struct_signal));
} else
return 0;
} else if (signal) {
sigset_ptr = signal;
} else
return 0;
switch (_NSIG_WORDS)
{
case 1:
sigset = (ulonglong)sigset_ptr[0];
break;
case 2:
sigset = (ulonglong)(sigset_ptr[1]) << 32;
sigset |= (ulonglong)(sigset_ptr[0]);
break;
}
return sigset;
}
/*
* Return the current set of signals that a task has blocked, in the form
* of a long long data type form that can be easily masked regardless
* of its size.
*/
static ulonglong
task_blocked(ulong task)
{
ulonglong sigset;
ulong *sigset_ptr;
fill_task_struct(task);
if (!tt->last_task_read)
return 0;
sigset_ptr = (ulong *)(tt->task_struct + OFFSET(task_struct_blocked));
sigset = (ulonglong)(sigset_ptr[1]) << 32;
sigset |= (ulonglong)(sigset_ptr[0]);
return sigset;
}
static ulonglong
sigaction_mask(ulong sigaction)
{
ulonglong sigset;
ulong *sigset_ptr;
sigset = 0;
sigset_ptr = (ulong *)(sigaction + OFFSET(sigaction_sa_mask));
switch (_NSIG_WORDS)
{
case 1:
sigset = (ulonglong)sigset_ptr[0];
break;
case 2:
sigset = (ulonglong)(sigset_ptr[1]) << 32;
sigset |= (ulonglong)(sigset_ptr[0]);
break;
}
return sigset;
}
/*
* Deal with potential separation of task_struct and kernel stack.
*/
ulong
generic_get_stackbase(ulong task)
{
return task_to_stackbase(task);
}
ulong
generic_get_stacktop(ulong task)
{
return task_to_stackbase(task) + STACKSIZE();
}