/*
 *  lcrash/arch/s390/lib/report.c
 *    dump report generator for s390 dumps
 *
 *    Copyright (C) 2000 IBM Deutschland Entwicklung GmbH,
 *                       IBM Corporation
 *    Author(s): Michael Holzheu (holzheu@de.ibm.com),
 *
 *    based on SGI version of report.c
 *
 *    Bugreports to: <Linux390@de.ibm.com>
 */

#include <lcrash.h>
#include <asm/kl_s390_util.h>

extern char map[];
extern char dump[];

/*
 * print_active_stacks
 * 
 * print stack backtraces of all currently running tasks
 */

void print_active_stacks(FILE* ofp)
{
        kaddr_t addr;
        syment_t *sp;
        void *tsp;

        if ((sp = kl_lkup_symname("init_task_union"))) {
                if (!(tsp = kl_alloc_block(TASK_STRUCT_SZ, K_TEMP))) {
                        return;
                }
		addr = sp->s_addr;
                do {
                        if (kl_get_task_struct(addr, 2, tsp)) {
                                break;
                        }
			if(task_has_cpu(tsp))
			{
                		void* lc;
                		lc = s390_get_cpu_lowcore(KL_INT(tsp,"task_struct","processor"));
				if(!lc){
					fprintf(ofp,"TASK : %#lx (%s)\n",addr,(char*)K_PTR(tsp,"task_struct","comm"));
					fprintf(ofp,"Error: Cannot locate lowcore\n");
				} else { 
                        		task_trace(addr, 0, ofp);
				}
                		s390_free_cpu_lowcore(lc);
        		}
                        addr = (kaddr_t)KL_INT(tsp,"task_struct","next_task");
                } while (addr != sp->s_addr);
                kl_free_block(tsp);
        }
}

/*
 * print_active_idle_stacks
 *
 * print stack backtraces of all active idle tasks 
 */

void print_active_idle_stacks(FILE* ofp)
{
        kaddr_t addr;
        syment_t *sp;
        void *tsp;
        int i;
	if (LINUX_2_2_X(KL_LINUX_RELEASE)) {
		sp =  kl_lkup_symname("task");
	}
	else{
		sp = kl_lkup_symname("init_tasks");
	}
        if (sp) {
                unsigned long* task_list;
                if (!(tsp = kl_alloc_block(TASK_STRUCT_SZ, K_TEMP))) { 
			return;
                }
                if(!(task_list = kl_alloc_block(sizeof(unsigned long) * s390_get_nr_of_cpus(), K_TEMP))) {
                        goto alloc_task_list_failed;
                }
                GET_BLOCK(sp->s_addr,sizeof(unsigned long) * s390_get_nr_of_cpus(), task_list);
                for(i = 1; i < s390_get_nr_of_cpus(); i++)
                {
                        addr = task_list[i];
                        if (kl_get_task_struct(addr, 2, tsp)) {
                                break;
                        }
                        if(task_has_cpu(tsp))
                        {
                                void* lc; /* lowcore */
                                lc = s390_get_cpu_lowcore(KL_INT(tsp,"task_struct","processor"));
                                if(!lc){
                                        fprintf(ofp,"TASK : %#lx (%s)\n",addr,(char*)K_PTR(tsp,"task_struct","comm"));
                                        fprintf(ofp,"Error: Cannot locate lowcore\n");
                                } else {
                                        task_trace(addr, 0, ofp);
                                }
                                s390_free_cpu_lowcore(lc);
                        }
                        addr = (kaddr_t)KL_INT(tsp,"task_struct","next_task");
                }
alloc_task_list_failed:
                kl_free_block(tsp);
        }
}

/*
 * s390_do_report() -- generate an s390 lcrash report.
 */
int
s390_do_report(int flags, FILE *ofp)
{
	time_t curtime;
	s390_dump_header_t  dump_header;
	struct timeval      h_time;
	char *              utsname;
	char*               h_dump_type;
	uint64_t            h_cpu_id;

	/* get the dump header 
	 */
	if (lseek(MIP->core_fd, 0, SEEK_SET) < 0) {
		fprintf(ofp, "Error: Cannot lseek() to get the dump header "
			"from the dump file!\n");
		return(1);
	}
	if (read(MIP->core_fd, (char *)&dump_header,
		sizeof(dump_header)) != sizeof(dump_header)) {
			fprintf(ofp, "Error: Cannot read() dump header "
				"from dump file!\n");
			return(1);
	}
	s390_tod_to_timeval(dump_header.dh_tod, &h_time);
	h_dump_type    = "S390";
	h_cpu_id       = dump_header.dh_cpu_id;

	fprintf(ofp,
		"=======================\n"
		"LCRASH CORE FILE REPORT\n"
		"=======================\n\n");

	curtime = time((time_t *)0);
	fprintf(ofp, "REPORT CREATED ON:\n    %s\n\n", ctime(&curtime));
	fprintf(ofp, "DUMP CREATED ON:\n    %s\n\n", ctime(&h_time.tv_sec));
	fprintf(ofp, "MAP:\n    %s\n\nDUMP:\n    %s\n\n", map, dump);

	fprintf(ofp,
		"================\n"
		"COREFILE SUMMARY\n"
		"================\n\n");

	fprintf(ofp, "  dump type: %s\n\n",h_dump_type);

	fprintf(ofp,
		"===================\n"
		"UTSNAME INFORMATION\n"
		"===================\n\n");

	if((utsname = get_utsname())){
		fprintf(ofp, "\n");
		fprintf(ofp, "   sysname : %s\n", (char*)
			K_PTR(utsname, NEW_UTSNAME, "sysname"));
		fprintf(ofp, "  nodename : %s\n", (char*)
			K_PTR(utsname, NEW_UTSNAME, "nodename"));
		fprintf(ofp, "   release : %s\n", (char*)
			K_PTR(utsname, NEW_UTSNAME, "release"));
		fprintf(ofp, "   version : %s\n", (char*)
			K_PTR(utsname, NEW_UTSNAME, "version"));
		fprintf(ofp, "   machine : %s\n", (char*)
			K_PTR(utsname, NEW_UTSNAME, "machine"));
		fprintf(ofp, "domainname : %s\n", (char*)
			K_PTR(utsname, NEW_UTSNAME, "domainname"));
		kl_free_block(utsname);
	} else {
		fprintf(ofp, "COULD NOT GET system_utsname!\n\n");
	}	
	fprintf(ofp, "\n");
	fprintf(ofp, "s390 cpuid : %#018lx\n\n", h_cpu_id);
	fprintf(ofp,
		"===============\n"
		"LOG BUFFER DUMP\n"
		"===============\n\n");

	/* print out the system log
	 */
	print_log_buf(ofp);
	fprintf(ofp, "\n\n");

	fprintf(ofp,
		"====================\n"
		"CURRENT SYSTEM TASKS\n"
		"====================\n\n");

	print_active_tasks(flags, ofp);

	fprintf(ofp, "\n");

	fprintf(ofp,
		"============================\n"
		"STACK TRACE OF RUNNING TASKS\n"
		"============================\n\n");

	print_active_stacks(ofp);

        fprintf(ofp, "\n");

        fprintf(ofp,
                "=================================\n"
                "STACK TRACE OF RUNNING IDLE TASKS\n"
                "=================================\n\n");

	print_active_idle_stacks(ofp);
	return 0;
}
