/* task.c - core analysis suite
 *
 * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc.
 * Copyright (C) 2002-2018 David Anderson
 * Copyright (C) 2002-2018 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 int radix_tree_task_callback(ulong);
static void refresh_radix_tree_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_lag(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 *);
static void stack_overflow_check_init(void);
static int has_sched_policy(ulong, ulong);
static ulong task_policy(ulong);
static ulong sched_policy_bit_from_str(const char *);
static ulong make_sched_policy(const char *);

static struct sched_policy_info {
	ulong value;
	char *name;
} sched_policy_info[] = {
	{ SCHED_NORMAL,		"NORMAL" },
	{ SCHED_FIFO,		"FIFO" },
	{ SCHED_RR,		"RR" },
	{ SCHED_BATCH,		"BATCH" },
	{ SCHED_ISO,		"ISO" },
	{ SCHED_IDLE,		"IDLE" },
	{ SCHED_DEADLINE,	"DEADLINE" },
	{ ULONG_MAX,		NULL }
};

/*
 *  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);
	}

	/*
	 * Handle CONFIG_THREAD_INFO_IN_TASK changes
	 */
	MEMBER_OFFSET_INIT(task_struct_stack, "task_struct", "stack");
	MEMBER_OFFSET_INIT(task_struct_thread_info, "task_struct", "thread_info");

	if (VALID_MEMBER(task_struct_thread_info)) {
		switch (MEMBER_TYPE("task_struct", "thread_info"))
		{
		case TYPE_CODE_PTR:
			break;
		case TYPE_CODE_STRUCT:
			tt->flags |= THREAD_INFO_IN_TASK;
			break;
		default:
			error(FATAL, 
			    "unexpected type code for task_struct.thread_info: %ld\n",
				MEMBER_TYPE("task_struct", "thread_info"));
			break;
		}
	} else if (VALID_MEMBER(task_struct_stack))
		MEMBER_OFFSET_INIT(task_struct_thread_info, "task_struct", "stack");

	if (VALID_MEMBER(task_struct_thread_info)) {
		if (tt->flags & THREAD_INFO_IN_TASK) {
			MEMBER_OFFSET_INIT(thread_info_flags, "thread_info", "flags");
			/* (unnecessary) reminders */
			ASSIGN_OFFSET(thread_info_task) = INVALID_OFFSET;
			ASSIGN_OFFSET(thread_info_cpu) = INVALID_OFFSET;
			ASSIGN_OFFSET(thread_info_previous_esp) = INVALID_OFFSET;
		} else {
			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_policy, "task_struct", "policy");
	MEMBER_SIZE_INIT(task_struct_policy, "task_struct", "policy");
        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;

	MEMBER_OFFSET_INIT(pid_namespace_idr, "pid_namespace", "idr");
	MEMBER_OFFSET_INIT(idr_idr_rt, "idr", "idr_rt");

	if (symbol_exists("height_to_maxindex") ||
	    symbol_exists("height_to_maxnodes")) {
		int newver = symbol_exists("height_to_maxnodes");
		int tmp ATTRIBUTE_UNUSED;
		if (!newver) {
			if (LKCD_KERNTYPES())
				ARRAY_LENGTH_INIT_ALT(tmp, "height_to_maxindex",
					"radix_tree_preload.nodes", NULL, 0);
			else
				ARRAY_LENGTH_INIT(tmp, height_to_maxindex,
					"height_to_maxindex", NULL, 0);
		} else {
			if (LKCD_KERNTYPES())
				ARRAY_LENGTH_INIT_ALT(tmp, "height_to_maxnodes",
					"radix_tree_preload.nodes", NULL, 0);
			else
				ARRAY_LENGTH_INIT(tmp, height_to_maxnodes,
					"height_to_maxnodes", NULL, 0);
		}
		STRUCT_SIZE_INIT(radix_tree_root, "radix_tree_root");
		STRUCT_SIZE_INIT(radix_tree_node, "radix_tree_node");
		MEMBER_OFFSET_INIT(radix_tree_root_height,
			"radix_tree_root","height");
		MEMBER_OFFSET_INIT(radix_tree_root_rnode,
			"radix_tree_root","rnode");
		MEMBER_OFFSET_INIT(radix_tree_node_slots,
			"radix_tree_node","slots");
		MEMBER_OFFSET_INIT(radix_tree_node_height,
			"radix_tree_node","height");
		MEMBER_OFFSET_INIT(radix_tree_node_shift,
			"radix_tree_node","shift");
	}

	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 (VALID_MEMBER(pid_namespace_idr)) {
		STRUCT_SIZE_INIT(pid, "pid");
		tt->refresh_task_table = refresh_radix_tree_task_table;
		tt->pid_radix_tree = symbol_value("init_pid_ns") +
			OFFSET(pid_namespace_idr) + OFFSET(idr_idr_rt);
		tt->flags |= PID_RADIX_TREE;

	} else 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;
	}

	tt->pf_kthread = UNINITIALIZED;

	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);

	/*
	 *  Get the IRQ stacks info if it's configured.
	 */
        if (VALID_STRUCT(irq_ctx))
		irqstacks_init();

	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();

	stack_overflow_check_init();

	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;
	ulong ptr, hardirq_next_sp = 0;

	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")) ||
	    (hard_sp = per_cpu_symbol_search("per_cpu__hardirq_stack"))) {
		if ((kt->flags & SMP) && (kt->flags & PER_CPU_OFF)) {
			for (i = 0; i < NR_CPUS; i++) {
				if (!kt->__per_cpu_offset[i])
					continue;
				ptr = hard_sp->value + kt->__per_cpu_offset[i];

				if (!readmem(ptr, KVADDR, &ptr,
					     sizeof(void *), "hardirq ctx",
					     RETURN_ON_ERROR)) {
					error(INFO, "cannot read hardirq_ctx[%d] at %lx\n",
					      i, ptr);
					continue;
				}
				tt->hardirq_ctx[i] = ptr;
			}
		} 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;
		}

		if (MEMBER_EXISTS("irq_ctx", "tinfo"))
			tt->hardirq_tasks[i] = 
				ULONG(thread_info_buf+OFFSET(thread_info_task));
		else {
			hardirq_next_sp = ULONG(thread_info_buf);
			tt->hardirq_tasks[i] = stkptr_to_task(hardirq_next_sp);
		}
	}

	if ((soft_sp = per_cpu_symbol_search("per_cpu__softirq_ctx")) ||
	    (soft_sp = per_cpu_symbol_search("per_cpu__softirq_stack"))) {
		if ((kt->flags & SMP) && (kt->flags & PER_CPU_OFF)) {
			for (i = 0; i < NR_CPUS; i++) {
				if (!kt->__per_cpu_offset[i])
					continue;
				ptr = soft_sp->value + kt->__per_cpu_offset[i];

				if (!readmem(ptr, KVADDR, &ptr,
					     sizeof(void *), "softirq ctx",
					     RETURN_ON_ERROR)) {
					error(INFO, "cannot read softirq_ctx[%d] at %lx\n",
					      i, ptr);
					continue;
				}
				tt->softirq_ctx[i] = ptr;
			}
		} 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;
		}

		if (MEMBER_EXISTS("irq_ctx", "tinfo")) 
			tt->softirq_tasks[i] =
				ULONG(thread_info_buf+OFFSET(thread_info_task));
		else {
			tt->softirq_tasks[i] = stkptr_to_task(ULONG(thread_info_buf));
			/* Checking if softirq => hardirq nested stack */
			if ((tt->softirq_tasks[i] != NO_TASK) && hardirq_next_sp) {
				if ((tt->softirq_ctx[i] <= hardirq_next_sp) &&
				    (hardirq_next_sp < tt->softirq_ctx[i] + STACKSIZE()))
					tt->hardirq_tasks[i] = tt->softirq_tasks[i];
			}
		}
	}

        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);
}

/*
 *  Linux 4.15: pid_hash[] replaced by IDR/radix_tree
 */
static int
radix_tree_task_callback(ulong task)
{
	ulong *tlp;

	if (tt->callbacks < tt->max_tasks) {
		tlp = (ulong *)tt->task_local;
		tlp += tt->callbacks++;
		*tlp = task;
	}

	return TRUE;
}

static void
refresh_radix_tree_task_table(void)
{
	int i, cnt;
	ulong count, retries, next, curtask, curpid, upid_ns, pid_tasks_0, task;
	ulong *tlp;
	char *tp;
	struct radix_tree_pair rtp;
	struct task_context *tc;
	char *pidbuf;

	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();
	}

	count = do_radix_tree(tt->pid_radix_tree, RADIX_TREE_COUNT, NULL);
	if (CRASHDEBUG(1))
		console("do_radix_tree: count: %ld\n", count);

	retries = 0;
	pidbuf = GETBUF(SIZE(pid));

retry_radix_tree:
	if (retries && DUMPFILE())
		error(FATAL,
			"\ncannot gather a stable task list via radix tree\n");

	if ((retries == MAX_UNLIMITED_TASK_RETRIES) &&
	    !(tt->flags & TASK_INIT_DONE))
		error(FATAL,
		    "\ncannot gather a stable task list via radix tree (%d retries)\n",
			retries);

	if (count > tt->max_tasks) {
		tt->max_tasks = count + TASK_SLUSH;
		allocate_task_space(tt->max_tasks);
	}

	BZERO(tt->task_local, tt->max_tasks * sizeof(void *));
	tt->callbacks = 0;
	rtp.index = 0;
	rtp.value = (void *)&radix_tree_task_callback;
	count = do_radix_tree(tt->pid_radix_tree, RADIX_TREE_DUMP_CB, &rtp);
	if (CRASHDEBUG(1))
		console("do_radix_tree: count: %ld  tt->callbacks: %d\n", count, tt->callbacks);

	if (count > tt->max_tasks) {
		retries++;
		goto retry_radix_tree;
	}

	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 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 < tt->max_tasks; i++) {
		tlp = (ulong *)tt->task_local;
		tlp += i;
		if ((next = *tlp) == 0)
			break;

		/*
		 *  Translate radix tree contents to PIDTYPE_PID task.
		 *  - the radix tree contents are struct pid pointers
		 *  - upid is contained in pid.numbers[0]
		 *  - upid.ns should point to init->init_pid_ns
		 *  - pid->tasks[0] is first hlist_node in task->pids[3]
		 *  - get task from address of task->pids[0]
		 */
		if (!readmem(next, KVADDR, pidbuf,
		    SIZE(pid), "pid", RETURN_ON_ERROR|QUIET)) {
			error(INFO, "\ncannot read pid struct from radix tree\n");
			if (DUMPFILE())
				continue;
			hq_close();
			retries++;
			goto retry_radix_tree;
		}

		upid_ns = ULONG(pidbuf + OFFSET(pid_numbers) + OFFSET(upid_ns));
		if (upid_ns != tt->init_pid_ns)
			continue;
		pid_tasks_0 = ULONG(pidbuf + OFFSET(pid_tasks));
		if (!pid_tasks_0)
			continue;
		task = pid_tasks_0 - OFFSET(task_struct_pids);

		if (CRASHDEBUG(1))
			console("pid: %lx  ns: %lx  tasks[0]: %lx task: %lx\n",
				next, upid_ns, pid_tasks_0, task);

		if (is_idle_thread(task))
			continue;

		if (!IS_TASK_ADDR(task)) {
			error(INFO, "%s: IDR radix tree: invalid task address: %lx\n",
				DUMPFILE() ? "\n" : "", task);
			if (DUMPFILE())
				break;
			hq_close();
			retries++;
			goto retry_radix_tree;
		}

		if (!hq_enter(task)) {
			error(INFO, "%s: IDR radix tree: duplicate task: %lx\n",
				DUMPFILE() ? "\n" : "", task);
			if (DUMPFILE())
				break;
			hq_close();
			retries++;
			goto retry_radix_tree;
		}

		cnt++;
	}

	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_radix_tree;
		}

		if (task_exists(*tlp)) {
			error(WARNING,
		           "%sduplicate task address found in task list: %lx\n",
				DUMPFILE() ? "\n" : "", *tlp);
			if (DUMPFILE())
				continue;
			retries++;
			goto retry_radix_tree;
		}

		if (!(tp = fill_task_struct(*tlp))) {
			if (DUMPFILE())
				continue;
			retries++;
			goto retry_radix_tree;
		}

		if (store_context(tc, *tlp, tp)) {
			tc++;
			tt->running_tasks++;
		}
	}

	FREEBUF(pidbuf);

	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) {
		if (tt->flags & THREAD_INFO_IN_TASK) 
			tc->thread_info = task + OFFSET(task_struct_thread_info);
		else
			tc->thread_info = ULONG(tp + OFFSET(task_struct_thread_info));
		fill_thread_info(tc->thread_info);
		if (tt->flags & THREAD_INFO_IN_TASK)
                	processor_addr = (int *) (tp + OFFSET(task_struct_cpu));
		else
			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, cnt, randomized;

        rewind(pc->tmpfile);

	BZERO(lookfor1, BUFSIZE);
	BZERO(lookfor2, BUFSIZE);
	BZERO(lookfor3, BUFSIZE);
	randomized = FALSE;

        while (fgets(buf, BUFSIZE, pc->tmpfile)) {
		if (STREQ(buf, "  {\n"))
			randomized = TRUE;
		else if (randomized && STREQ(buf, "  }, \n"))
			randomized = FALSE;

		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  %s = ", 
				randomized ? "  " : "", arglist[i]);
			if (STRNEQ(buf, lookfor1)) {
				fprintf(pc->saved_fp, "%s", buf);
				if (strstr(buf, "{{\n")) 
					sprintf(lookfor2, "%s    }},", 
						randomized ? "  " : "");
				else if (strstr(buf, " = {\n")) { 
					cnt = count_leading_spaces(buf);
					sprintf(lookfor2, "%s}", space(cnt));
				} 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:y:")) != 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;

		case 'y':
			flag |= PS_POLICY;
			psinfo.policy = make_sched_policy(optarg);
			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_POLICY) && !has_sched_policy(tc->task, psi->policy))
		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, psi);
		
		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, psi);
			}
		}
	}
}

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 && psi->cpus) {
		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 && psi->cpus) {
		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 {
			if ((bt->stackbase = GET_STACKBASE(tc->task))) {
				bt->stacktop = GET_STACKTOP(tc->task);
				bt->task = tc->task;
				bt->tc = tc;
				bt->flags |= BT_KSTACKP;
				back_trace(bt);
			} else
				bt->stkptr = 0;
		}

		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 * sizeof(ulong)) == 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)
{
	ulong stackbase;

	if (tt->flags & THREAD_INFO_IN_TASK) {
		readmem(task + OFFSET(task_struct_stack), KVADDR, &stackbase,
		    sizeof(void *), "task_struct.stack", FAULT_ON_ERROR);
		return stackbase;
	} else 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 task's policy as bitmask bit.
 */
static ulong
task_policy(ulong task)
{
	ulong policy = 0;

	fill_task_struct(task);

	if (!tt->last_task_read)
		return policy;

	if (SIZE(task_struct_policy) == sizeof(unsigned int))
		policy = 1 << UINT(tt->task_struct + OFFSET(task_struct_policy));
	else
		policy = 1 << ULONG(tt->task_struct + OFFSET(task_struct_policy));

	return policy;
}

/*
 *  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:vomlgersStTpukcfFxhdaGy:")) != 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;

		case 'y':
			fd->flags |= FOREACH_y_FLAG;
			fd->policy = make_sched_policy(optarg);
			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 user-space thread group leaders
		 */
		if (STREQ(args[optind], "gleader")) {
			if (fd->flags & FOREACH_KERNEL)
				error(FATAL,
					"gleader and kernel are mutually exclusive!\n");
			fd->flags |= (FOREACH_USER|FOREACH_GLEADER);
			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_GLEADER) && tc->pid != task_tgid(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_OPT_BT))
					bt->flags |= BT_OPT_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;
				if (fd->flags & FOREACH_y_FLAG) {
					cmdflags |= PS_POLICY;
					psinfo.policy = fd->policy;
				}
				/*
				 * 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 if (machine_type("ARM64"))
		fd->flags |= FOREACH_t_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 if (tt->refresh_task_table == refresh_radix_tree_task_table)
                fprintf(fp, "refresh_radix_tree_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 & PID_RADIX_TREE)
		sprintf(&buf[strlen(buf)],
			"%sPID_RADIX_TREE", others++ ? "|" : "");
        if (tt->flags & THREAD_INFO)
                sprintf(&buf[strlen(buf)], 
			"%sTHREAD_INFO", others++ ? "|" : "");
        if (tt->flags & THREAD_INFO_IN_TASK)
                sprintf(&buf[strlen(buf)], 
			"%sTHREAD_INFO_IN_TASK", 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, "    pid_radix_tree: %lx\n", tt->pid_radix_tree);
	fprintf(fp, "         callbacks: %d\n", tt->callbacks);
	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);
	fprintf(fp, "   stack_end_magic: %lx\n", tt->stack_end_magic);
	fprintf(fp, "        pf_kthread: %lx ", tt->pf_kthread);
	switch (tt->pf_kthread) 
	{
	case UNINITIALIZED:
		fprintf(fp, "(UNINITIALIZED)\n"); 
		break;
	case 0:
		fprintf(fp, "(n/a)\n"); 
		break;
	default:
		fprintf(fp, "(PF_KTHREAD)\n"); 
		break;
	}

	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;

        if (tt->pf_kthread == UNINITIALIZED) {
		if (THIS_KERNEL_VERSION >= LINUX(2,6,27)) {
			tt->pf_kthread = PF_KTHREAD;

			if ((tc = pid_to_context(0)) &&
			    !(task_flags(tc->task) & PF_KTHREAD)) {
				error(WARNING, "pid 0: PF_KTHREAD not set?\n");
				tt->pf_kthread = 0;
			}
			if ((tc = pid_to_context(1)) && 
			    task_mm(tc->task, FALSE) &&
			    (task_flags(tc->task) & PF_KTHREAD)) {
				error(WARNING, "pid 1: PF_KTHREAD set?\n");
				tt->pf_kthread = 0;
			}
		} else
			tt->pf_kthread = 0;
	}

	if (tt->pf_kthread)
		return (task_flags(task) & tt->pf_kthread ? TRUE : FALSE);

	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;
}

/*
 * Checks if task policy corresponds to given mask.
 */
static int
has_sched_policy(ulong task, ulong policy)
{
	return !!(task_policy(task) & policy);
}

/*
 * Converts sched policy name into mask bit.
 */
static ulong
sched_policy_bit_from_str(const char *policy_str)
{
	struct sched_policy_info *info = NULL;
	ulong policy = 0;
	int found = 0;
	char *upper = NULL;
	/*
	 * Once kernel gets more than 10 scheduling policies,
	 * sizes of these arrays should be adjusted
	 */
	char digit[2] = { 0, 0 };
	char hex[4] = { 0, 0, 0, 0 };

	upper = GETBUF(strlen(policy_str) + 1);
	upper_case(policy_str, upper);

	for (info = sched_policy_info; info->name; info++) {
		snprintf(digit, sizeof digit, "%lu", info->value);
		/*
		 * Not using %#lX format here since "0X" prefix
		 * is not prepended if 0 value is given
		 */
		snprintf(hex, sizeof hex, "0X%lX", info->value);
		if (strncmp(upper, info->name, strlen(info->name)) == 0 ||
			strncmp(upper, digit, sizeof digit) == 0 ||
			strncmp(upper, hex, sizeof hex) == 0) {
			policy = 1 << info->value;
			found = 1;
			break;
		}
	}

	FREEBUF(upper);

	if (!found)
		error(FATAL,
			"%s: invalid scheduling policy\n", policy_str);

	return policy;
}

/*
 * Converts sched policy string set into bitmask.
 */
static ulong
make_sched_policy(const char *policy_str)
{
	ulong policy = 0;
	char *iter = NULL;
	char *orig = NULL;
	char *cur = NULL;

	iter = STRDUPBUF(policy_str);
	orig = iter;

	while ((cur = strsep(&iter, ",")))
		policy |= sched_policy_bit_from_str(cur);

	FREEBUF(orig);

	return policy;
}

/*
 *  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_lag_flag = 0;
	int dump_task_group_flag = 0;
	int dump_milliseconds_flag = 0;

        while ((c = getopt(argcnt, args, "dtTgmc:")) != EOF) {
                switch(c)
                {
		case 'd':
			sched_debug = 1;
			break;
		case 't':
			dump_timestamp_flag = 1;
			break;
		case 'T':
			dump_lag_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_lag_flag)
		dump_on_rq_lag();
	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"); 

	}
}

/*
 * Runqueue timestamp struct for dump_on_rq_lag().
 */
struct runq_ts_info {
	int cpu;
	ulonglong ts;
};

/*
 * Comparison function for dump_on_rq_lag().
 * Sorts runqueue timestamps in a descending order.
 */
static int
compare_runq_ts(const void *p1, const void *p2)
{
	const struct runq_ts_info *ts1 = p1;
	const struct runq_ts_info *ts2 = p2;

	if (ts1->ts > ts2->ts)
		return -1;

	if (ts1->ts < ts2->ts)
		return 1;

	return 0;
}

/*
 * Calculates integer log10
 */
static ulong
__log10ul(ulong x)
{
	ulong ret = 1;

	while (x > 9) {
		ret++;
		x /= 10;
	}

	return ret;
}

/*
 * Displays relative CPU lag.
 */
static void
dump_on_rq_lag(void)
{
	struct syment *rq_sp;
	int cpu;
	ulong runq;
	ulonglong timestamp;
	struct runq_ts_info runq_ts[kt->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('T');

	for (cpu = 0; cpu < kt->cpus; cpu++) {
		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);

		runq_ts[cpu].cpu = cpu;
		runq_ts[cpu].ts = timestamp;
	}

	qsort(runq_ts, (size_t)kt->cpus, sizeof(struct runq_ts_info), compare_runq_ts);

	for (cpu = 0; cpu < kt->cpus; cpu++) {
		fprintf(fp, "%sCPU %d: %.2lf secs\n",
			space(2 + __log10ul(kt->cpus) - __log10ul(runq_ts[cpu].cpu)),
			runq_ts[cpu].cpu,
			((double)runq_ts[0].ts - (double)runq_ts[cpu].ts) / 1000000000.0);
	}
}

/*
 *  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_tasks_timeline, "cfs_rq", 
			"tasks_timeline");
		MEMBER_OFFSET_INIT(cfs_rq_rb_leftmost, "cfs_rq", "rb_leftmost");
		if (INVALID_MEMBER(cfs_rq_rb_leftmost) && 
		    VALID_MEMBER(cfs_rq_tasks_timeline) &&
		    MEMBER_EXISTS("rb_root_cached", "rb_leftmost"))
			ASSIGN_OFFSET(cfs_rq_rb_leftmost) = OFFSET(cfs_rq_tasks_timeline) + 
				MEMBER_OFFSET("rb_root_cached", "rb_leftmost");
		MEMBER_OFFSET_INIT(cfs_rq_nr_running, "cfs_rq", "nr_running");
		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();
}

#define STACK_END_MAGIC 0x57AC6E9D

static void
stack_overflow_check_init(void)
{
	int pid;
	struct task_context *tc;
	ulong location, magic;

	if (!(tt->flags & THREAD_INFO))
		return;

	for (pid = 1; pid < 10; pid++) {
 		if (!(tc = pid_to_context(pid)))
			continue;

		if (tt->flags & THREAD_INFO_IN_TASK)
			location = task_to_stackbase(tc->task);
		else
			location = tc->thread_info + SIZE(thread_info);

		if (!readmem(location, KVADDR, &magic, sizeof(long), 
		    "stack magic", RETURN_ON_ERROR|QUIET))
			continue;

		if (magic == STACK_END_MAGIC) {
			tt->stack_end_magic = STACK_END_MAGIC;
			break;
		}
	}
}

/*
 *  Check thread_info.task and thread_info.cpu members, 
 *  and the STACK_END_MAGIC location.
 */
void 
check_stack_overflow(void)
{
	int i, overflow, cpu_size, cpu, total;
	char buf[BUFSIZE];
	ulong magic, task, stackbase;
	struct task_context *tc;

	if (!tt->stack_end_magic && 
	    INVALID_MEMBER(thread_info_task) && 
	    INVALID_MEMBER(thread_info_cpu))
		option_not_supported('v');

	cpu_size = VALID_MEMBER(thread_info_cpu) ? 
		MEMBER_SIZE("thread_info", "cpu") : 0;

	tc = FIRST_CONTEXT();
	for (i = total = 0; i < RUNNING_TASKS(); i++, tc++) {
		overflow = 0;

		if (tt->flags & THREAD_INFO_IN_TASK) {
			if (!readmem(task_to_stackbase(tc->task), KVADDR, &stackbase, 
			    sizeof(ulong), "stack overflow check", RETURN_ON_ERROR))
				continue;
			goto check_stack_end_magic;
		} else {
			if (!readmem(tc->thread_info, KVADDR, buf, 
			    SIZE(thread_info) + sizeof(ulong), 
			    "stack overflow check", RETURN_ON_ERROR))
				continue;
		}

		if (VALID_MEMBER(thread_info_task)) {
			task = ULONG(buf + OFFSET(thread_info_task));
			if (task != tc->task) {
				print_task_header(fp, tc, 0);
				fprintf(fp, 
				    "  possible stack overflow: thread_info.task: %lx != %lx\n",
					task, tc->task);
				overflow++; total++;
			}
		}

		if (VALID_MEMBER(thread_info_cpu)) {
			switch (cpu_size)
			{
			case 1:
				cpu = UCHAR(buf + OFFSET(thread_info_cpu));
				break;
			case 2:
				cpu = USHORT(buf + OFFSET(thread_info_cpu));
				break;
			case 4:
				cpu = UINT(buf + OFFSET(thread_info_cpu));
				break;
			default:
				cpu = 0;
				break;
			}
			if (cpu >= kt->cpus) {
				if (!overflow)
					print_task_header(fp, tc, 0);
				fprintf(fp, 
				    "  possible stack overflow: thread_info.cpu: %d >= %d\n",
					cpu, kt->cpus);
				overflow++; total++;
			}
		}

check_stack_end_magic:
		if (!tt->stack_end_magic)
			continue;

		if (tt->flags & THREAD_INFO_IN_TASK) 
			magic = stackbase;
		else
			magic = ULONG(buf + SIZE(thread_info));

		if (tc->pid == 0) {
			if (kernel_symbol_exists("init_task")) {
				if (tc->task == symbol_value("init_task"))
					continue;
			} else 
				continue;
		}

		if (magic != STACK_END_MAGIC) {
			if (!overflow)
				print_task_header(fp, tc, 0);
			fprintf(fp, 
			    "  possible stack overflow: %lx: %lx != STACK_END_MAGIC\n",
				tc->thread_info + SIZE(thread_info), magic);
			overflow++, total++;
		}

		if (overflow)
			fprintf(fp, "\n");
	}

	if (!total)
		fprintf(fp, "No stack overflows detected\n");
}
