crash-8.0.0/0000755000175000017500000000000014176532105013047 5ustar troyhebetroyhebecrash-8.0.0/lkcd_vmdump_v2_v3.h0000644000175000017500000001662414147340161016552 0ustar troyhebetroyhebe/* lkcd_vmdump_v2_v3.h - core analysis suite * * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. * Copyright (C) 2002, 2003, 2004, 2005, 2006 David Anderson * Copyright (C) 2002, 2003, 2004, 2005, 2006 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. */ /* * Kernel header file for Linux crash dumps. * * Created by: Matt Robinson (yakker@sgi.com) * * Copyright 1999 Silicon Graphics, Inc. All rights reserved. * */ /* This header file includes all structure definitions for crash dumps. */ #ifndef _VMDUMP_H #define _VMDUMP_H /* necessary header files */ #ifndef MCLX #include /* for utsname structure */ #include /* for architecture-specific header */ #endif #if defined(ARM) || defined(X86) || defined(PPC) || defined(S390) || \ defined(S390X) || defined(ARM64) || defined(MIPS) || \ defined(MIPS64) || defined(SPARC64) /* * Kernel header file for Linux crash dumps. * * Created by: Matt Robinson (yakker@sgi.com) * * Copyright 1999 Silicon Graphics, Inc. All rights reserved. * */ /* This header file holds the architecture specific crash dump header */ #ifndef _ASM_VMDUMP_H #define _ASM_VMDUMP_H /* necessary header files */ typedef unsigned int u32; #include /* for pt_regs */ /* definitions */ #define DUMP_ASM_MAGIC_NUMBER 0xdeaddeadULL /* magic number */ #define DUMP_ASM_VERSION_NUMBER 0x1 /* version number */ /* * Structure: dump_header_asm_t * Function: This is the header for architecture-specific stuff. It * follows right after the dump header. */ typedef struct _dump_header_asm_s { /* the dump magic number -- unique to verify dump is valid */ uint64_t dha_magic_number; /* the version number of this dump */ uint32_t dha_version; /* the size of this header (in case we can't read it) */ uint32_t dha_header_size; /* the esp for i386 systems */ uint32_t dha_esp; /* the eip for i386 systems */ uint32_t dha_eip; /* the dump registers */ #ifndef S390 #ifndef S390X #ifndef ARM64 struct pt_regs dha_regs; #endif #endif #endif } dump_header_asm_t; #endif /* _ASM_VMDUMP_H */ #endif /* ARM || X86 || PPC */ #if defined(ALPHA) || defined(IA64) || defined(X86_64) || defined(PPC64) /* * Plug in the real ../arch/alpha/vmdump.h when available. For now the * data here are just placeholders... */ #ifndef IA64 typedef unsigned int u32; #include /* for pt_regs */ #endif /* definitions */ #define DUMP_ASM_MAGIC_NUMBER 0xdeaddeadULL /* magic number */ #define DUMP_ASM_VERSION_NUMBER 0x1 /* version number */ typedef struct _dump_header_asm_s { /* the dump magic number -- unique to verify dump is valid */ uint64_t dha_magic_number; /* the version number of this dump */ uint32_t dha_version; /* the size of this header (in case we can't read it) */ uint32_t dha_header_size; /* the esp for i386 systems */ uint32_t dha_esp; /* the eip for i386 systems */ uint32_t dha_eip; /* the dump registers */ #ifndef IA64 struct pt_regs dha_regs; #endif } dump_header_asm_t; #endif /* ALPHA or IA64 (?) */ /* necessary header definitions in all cases */ #define DUMP_KIOBUF_NUMBER 0xdeadbeef /* special number for kiobuf maps */ #ifdef CONFIG_VMDUMP /* size of a dump header page */ #define DUMP_PAGE_SZ 64 * 1024 /* size of dump page buffer */ /* standard header definitions */ #define DUMP_MAGIC_NUMBER 0xa8190173618f23edULL /* dump magic number */ #define DUMP_VERSION_NUMBER 0x2 /* dump version number */ #define DUMP_PANIC_LEN 0x100 /* dump panic string length */ /* dump flags -- add as necessary */ #define DUMP_RAW 0x1 /* raw page (no compression) */ #define DUMP_COMPRESSED 0x2 /* page is compressed */ #define DUMP_END 0x4 /* end marker on a full dump */ /* dump types - type specific stuff added later for page typing */ #define DUMP_NONE 0 /* no dumping at all -- just bail */ #define DUMP_HEADER 1 /* kernel dump header only */ #define DUMP_KERN 2 /* dump header and kernel pages */ #define DUMP_USED 3 /* dump header, kernel/user pages */ #define DUMP_ALL 4 /* dump header, all memory pages */ /* * Structure: dump_header_t * Function: This is the header dumped at the top of every valid crash * dump. * easy reassembly of each crash dump page. The address bits * are split to make things easier for 64-bit/32-bit system * conversions. */ typedef struct _dump_header_s { /* the dump magic number -- unique to verify dump is valid */ uint64_t dh_magic_number; /* the version number of this dump */ uint32_t dh_version; /* the size of this header (in case we can't read it) */ uint32_t dh_header_size; /* the level of this dump (just a header?) */ uint32_t dh_dump_level; /* the size of a Linux memory page (4K, 8K, 16K, etc.) */ uint32_t dh_page_size; /* the size of all physical memory */ uint64_t dh_memory_size; /* the start of physical memory */ uint64_t dh_memory_start; /* the end of physical memory */ uint64_t dh_memory_end; /* the number of pages in this dump specifically */ uint32_t dh_num_pages; /* the panic string, if available */ char dh_panic_string[DUMP_PANIC_LEN]; /* the time of the system crash */ struct timeval dh_time; /* the utsname (uname) information */ struct new_utsname dh_utsname; /* the address of the current task */ struct task_struct *dh_current_task; } dump_header_t; /* * Structure: dump_page_t * Function: To act as the header associated to each physical page of * memory saved in the system crash dump. This allows for * easy reassembly of each crash dump page. The address bits * are split to make things easier for 64-bit/32-bit system * conversions. */ typedef struct _dump_page_s { /* the address of this dump page */ uint64_t dp_address; /* the size of this dump page */ uint32_t dp_size; /* flags (currently DUMP_COMPRESSED, DUMP_RAW or DUMP_END) */ uint32_t dp_flags; } dump_page_t; #endif /* CONFIG_VMDUMP */ #ifdef __KERNEL__ extern void dump_init(uint64_t, uint64_t); extern void dump_open(char *); extern void dump_execute(char *, struct pt_regs *); #endif #endif /* _VMDUMP_H */ crash-8.0.0/lkcd_v5.c0000644000175000017500000003103214147340161014536 0ustar troyhebetroyhebe/* lkcd_v5.c - core analysis suite * * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. * Copyright (C) 2002, 2003, 2004, 2005 David Anderson * Copyright (C) 2002, 2003, 2004, 2005 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. */ #define LKCD_COMMON #include "defs.h" #include "lkcd_dump_v5.h" static dump_header_t dump_header_v5 = { 0 }; static dump_page_t dump_page = { 0 }; static void mclx_cache_page_headers_v5(void); /* * Verify and initialize the LKCD environment, storing the common data * in the global lkcd_environment structure. */ int lkcd_dump_init_v5(FILE *fp, int fd) { int i; int eof; uint32_t pgcnt; dump_header_t *dh; dump_page_t *dp; lkcd->fd = fd; lkcd->fp = fp; lseek(lkcd->fd, 0, SEEK_SET); dh = &dump_header_v5; dp = &dump_page; if (read(lkcd->fd, dh, sizeof(dump_header_t)) != sizeof(dump_header_t)) return FALSE; lkcd->dump_page = dp; lkcd->dump_header = dh; if (lkcd->debug) dump_lkcd_environment(LKCD_DUMP_HEADER_ONLY); /* * Allocate and clear the benchmark offsets, one per megabyte. */ lkcd->page_size = dh->dh_page_size; lkcd->page_shift = ffs(lkcd->page_size) - 1; lkcd->bits = sizeof(long) * 8; lkcd->benchmark_pages = (dh->dh_num_pages/LKCD_PAGES_PER_MEGABYTE())+1; lkcd->total_pages = dh->dh_num_pages; lkcd->zone_shift = ffs(ZONE_SIZE) - 1; lkcd->zone_mask = ~(ZONE_SIZE - 1); lkcd->num_zones = 0; lkcd->max_zones = 0; lkcd->zoned_offsets = 0; lkcd->get_dp_flags = get_dp_flags_v5; lkcd->get_dp_address = get_dp_address_v5; lkcd->compression = dh->dh_dump_compress; lkcd->page_header_size = sizeof(dump_page_t); lkcd->get_dp_size = get_dp_size_v5; lseek(lkcd->fd, LKCD_OFFSET_TO_FIRST_PAGE, SEEK_SET); for (pgcnt = 0, eof = FALSE; !eof; pgcnt++) { switch (lkcd_load_dump_page_header(dp, pgcnt)) { case LKCD_DUMPFILE_OK: case LKCD_DUMPFILE_END: break; case LKCD_DUMPFILE_EOF: lkcd_print("reached EOF\n"); eof = TRUE; continue; } if (dp->dp_flags & ~(DUMP_DH_COMPRESSED|DUMP_DH_RAW|DUMP_DH_END|LKCD_DUMP_MCLX_V0)) { lkcd_print("unknown page flag in dump: %lx\n", dp->dp_flags); } if (dp->dp_flags & (LKCD_DUMP_MCLX_V0|LKCD_DUMP_MCLX_V1)) lkcd->flags |= LKCD_MCLX; if (dp->dp_size > 4096) { lkcd_print("dp_size > 4096: %d\n", dp->dp_size); dump_lkcd_environment(LKCD_DUMP_PAGE_ONLY); } if (dp->dp_flags & DUMP_DH_END) { lkcd_print("found DUMP_DH_END\n"); break; } lseek(lkcd->fd, dp->dp_size, SEEK_CUR); if (!LKCD_DEBUG(2)) break; } /* * Allocate space for LKCD_CACHED_PAGES data pages plus one to * contain a copy of the compressed data of the current page. */ if ((lkcd->page_cache_buf = (char *)malloc (dh->dh_page_size * (LKCD_CACHED_PAGES))) == NULL) return FALSE; /* * Clear the page data areas. */ lkcd_free_memory(); for (i = 0; i < LKCD_CACHED_PAGES; i++) { lkcd->page_cache_hdr[i].pg_bufptr = &lkcd->page_cache_buf[i * dh->dh_page_size]; } if ((lkcd->compressed_page = (char *)malloc(dh->dh_page_size)) == NULL) return FALSE; if ((lkcd->page_hash = (struct page_hash_entry *)calloc (LKCD_PAGE_HASH, sizeof(struct page_hash_entry))) == NULL) return FALSE; lkcd->total_pages = eof || (pgcnt > dh->dh_num_pages) ? pgcnt : dh->dh_num_pages; lkcd->panic_task = (ulong)dh->dh_current_task; lkcd->panic_string = (char *)&dh->dh_panic_string[0]; if (dh->dh_version & LKCD_DUMP_MCLX_V1) mclx_cache_page_headers_v5(); if (!fp) lkcd->flags |= LKCD_REMOTE; lkcd->flags |= LKCD_VALID; return TRUE; } /* * Return the current page's dp_size. */ uint32_t get_dp_size_v5(void) { dump_page_t *dp; dp = (dump_page_t *)lkcd->dump_page; return(dp->dp_size); } /* * Return the current page's dp_flags. */ uint32_t get_dp_flags_v5(void) { dump_page_t *dp; dp = (dump_page_t *)lkcd->dump_page; return(dp->dp_flags); } /* * Return the current page's dp_address. */ uint64_t get_dp_address_v5(void) { dump_page_t *dp; dp = (dump_page_t *)lkcd->dump_page; return(dp->dp_address); } /* * help -S output, or as specified by arg. */ void dump_lkcd_environment_v5(ulong arg) { int others; dump_header_t *dh; dump_page_t *dp; dh = (dump_header_t *)lkcd->dump_header; dp = (dump_page_t *)lkcd->dump_page; if (arg == LKCD_DUMP_HEADER_ONLY) goto dump_header_only; if (arg == LKCD_DUMP_PAGE_ONLY) goto dump_page_only; dump_header_only: lkcd_print(" dump_header:\n"); lkcd_print(" dh_magic_number: "); lkcd_print(BITS32() ? "%llx " : "%lx ", dh->dh_magic_number); if (dh->dh_magic_number == DUMP_MAGIC_NUMBER) lkcd_print("(DUMP_MAGIC_NUMBER)\n"); else if (dh->dh_magic_number == DUMP_MAGIC_LIVE) lkcd_print("(DUMP_MAGIC_LIVE)\n"); else lkcd_print("(?)\n"); others = 0; lkcd_print(" dh_version: "); lkcd_print(BITS32() ? "%lx (" : "%x (", dh->dh_version); switch (dh->dh_version & LKCD_DUMP_VERSION_NUMBER_MASK) { case LKCD_DUMP_V1: lkcd_print("%sLKCD_DUMP_V1", others++ ? "|" : ""); break; case LKCD_DUMP_V2: lkcd_print("%sLKCD_DUMP_V2", others++ ? "|" : ""); break; case LKCD_DUMP_V3: lkcd_print("%sLKCD_DUMP_V3", others++ ? "|" : ""); break; case LKCD_DUMP_V5: lkcd_print("%sLKCD_DUMP_V5", others++ ? "|" : ""); break; } if (dh->dh_version & LKCD_DUMP_MCLX_V0) lkcd_print("%sLKCD_DUMP_MCLX_V0", others++ ? "|" : ""); if (dh->dh_version & LKCD_DUMP_MCLX_V1) lkcd_print("%sLKCD_DUMP_MCLX_V1", others++ ? "|" : ""); lkcd_print(")\n"); lkcd_print(" dh_header_size: "); lkcd_print(BITS32() ? "%ld\n" : "%d\n", dh->dh_header_size); lkcd_print(" dh_dump_level: "); lkcd_print(BITS32() ? "%lx (" : "%x (", dh->dh_dump_level); others = 0; if (dh->dh_dump_level & DUMP_LEVEL_HEADER) lkcd_print("%sDUMP_LEVEL_HEADER", others++ ? "|" : ""); if (dh->dh_dump_level & DUMP_LEVEL_KERN) lkcd_print("%sDUMP_LEVEL_KERN", others++ ? "|" : ""); if (dh->dh_dump_level & DUMP_LEVEL_USED) lkcd_print("%sDUMP_LEVEL_USED", others++ ? "|" : ""); if (dh->dh_dump_level & DUMP_LEVEL_ALL) lkcd_print("%sDUMP_LEVEL_ALL", others++ ? "|" : ""); lkcd_print(")\n"); lkcd_print(" dh_page_size: "); lkcd_print(BITS32() ? "%ld\n" : "%d\n", dh->dh_page_size); lkcd_print(" dh_memory_size: "); lkcd_print(BITS32() ? "%lld\n" : "%ld\n", dh->dh_memory_size); lkcd_print(" dh_memory_start: "); lkcd_print(BITS32() ? "%llx\n" : "%lx\n", dh->dh_memory_start); lkcd_print(" dh_memory_end: "); lkcd_print(BITS32() ? "%llx\n" : "%lx\n", dh->dh_memory_end); lkcd_print(" dh_num_pages: "); lkcd_print(BITS32() ? "%ld\n" : "%d\n", dh->dh_num_pages); lkcd_print(" dh_panic_string: %s%s", dh->dh_panic_string, dh && dh->dh_panic_string && strstr(dh->dh_panic_string, "\n") ? "" : "\n"); lkcd_print(" dh_time: %s\n", strip_linefeeds(ctime(&(dh->dh_time.tv_sec)))); lkcd_print("dh_utsname_sysname: %s\n", dh->dh_utsname_sysname); lkcd_print("dh_utsname_nodename: %s\n", dh->dh_utsname_nodename); lkcd_print("dh_utsname_release: %s\n", dh->dh_utsname_release); lkcd_print("dh_utsname_version: %s\n", dh->dh_utsname_version); lkcd_print("dh_utsname_machine: %s\n", dh->dh_utsname_machine); lkcd_print("dh_utsname_domainname: %s\n", dh->dh_utsname_domainname); lkcd_print(" dh_current_task: %lx\n", dh->dh_current_task); lkcd_print(" dh_dump_compress: "); lkcd_print(BITS32() ? "%lx (" : "%x (", dh->dh_dump_compress); others = 0; if (dh->dh_dump_compress == DUMP_COMPRESS_NONE) lkcd_print("%sDUMP_COMPRESS_NONE", others++ ? "|" : ""); if (dh->dh_dump_compress & DUMP_COMPRESS_RLE) lkcd_print("%sDUMP_COMPRESS_RLE", others++ ? "|" : ""); if (dh->dh_dump_compress & DUMP_COMPRESS_GZIP) lkcd_print("%sDUMP_COMPRESS_GZIP", others++ ? "|" : ""); lkcd_print(")\n"); lkcd_print(" dh_dump_flags: "); others = 0; lkcd_print(BITS32() ? "%lx (" : "%x (", dh->dh_dump_flags); if (dh->dh_dump_flags & DUMP_FLAGS_NONDISRUPT) lkcd_print("%sDUMP_FLAGS_NONDISRUPT", others++ ? "|" : ""); lkcd_print(")\n"); lkcd_print(" dh_dump_device: "); lkcd_print(BITS32() ? "%lx\n" : "%x\n", dh->dh_dump_device); if (arg == LKCD_DUMP_HEADER_ONLY) return; dump_page_only: lkcd_print(" dump_page:\n"); lkcd_print(" dp_address: "); lkcd_print(BITS32() ? "%llx\n" : "%lx\n", dp->dp_address); lkcd_print(" dp_size: "); lkcd_print(BITS32() ? "%ld\n" : "%d\n", dp->dp_size); lkcd_print(" dp_flags: "); lkcd_print(BITS32() ? "%lx (" : "%x (", dp->dp_flags); others = 0; if (dp->dp_flags & DUMP_DH_COMPRESSED) lkcd_print("DUMP_DH_COMPRESSED", others++); if (dp->dp_flags & DUMP_DH_RAW) lkcd_print("%sDUMP_DH_RAW", others++ ? "|" : ""); if (dp->dp_flags & DUMP_DH_END) lkcd_print("%sDUMP_DH_END", others++ ? "|" : ""); if (dp->dp_flags & LKCD_DUMP_MCLX_V0) lkcd_print("%sLKCD_DUMP_MCLX_V0", others++ ? "|" : ""); lkcd_print(")\n"); } void dump_dump_page_v5(char *s, void *dpp) { dump_page_t *dp; uint32_t flags; int others; console(s); dp = (dump_page_t *)dpp; others = 0; console(BITS32() ? "dp_address: %llx " : "dp_address: %lx ", dp->dp_address); console("dp_size: %ld ", dp->dp_size); console("dp_flags: %lx (", flags = dp->dp_flags); if (flags & DUMP_DH_COMPRESSED) console("DUMP_DH_COMPRESSED", others++); if (flags & DUMP_DH_RAW) console("%sDUMP_DH_RAW", others++ ? "|" : ""); if (flags & DUMP_DH_END) console("%sDUMP_DH_END", others++ ? "|" : ""); if (flags & LKCD_DUMP_MCLX_V0) console("%sLKCD_DUMP_MCLX_V0", others++ ? "|" : ""); console(")\n"); } /* * Read the MCLX-enhanced page header cache. Verify the first one, which * is a pointer to the page header for address 1MB, and take the rest at * blind faith. Note that the page headers do not include the 64K dump * header offset, which must be added to the values found. */ static void mclx_cache_page_headers_v5(void) { int i; uint64_t physaddr1, physaddr2, page_headers[MCLX_PAGE_HEADERS]; dump_page_t dump_page, *dp; ulong granularity; if (LKCD_DEBUG(2)) /* dump headers have all been read */ return; if (lkcd->total_pages > MEGABYTES(1))/* greater than 4G not supported */ return; if (lseek(lkcd->fd, sizeof(dump_header_t), SEEK_SET) == -1) return; if (read(lkcd->fd, page_headers, MCLX_V1_PAGE_HEADER_CACHE) != MCLX_V1_PAGE_HEADER_CACHE) return; dp = &dump_page; /* * Determine the granularity between offsets. */ if (lseek(lkcd->fd, page_headers[0] + LKCD_OFFSET_TO_FIRST_PAGE, SEEK_SET) == -1) return; if (read(lkcd->fd, dp, lkcd->page_header_size) != lkcd->page_header_size) return; physaddr1 = (dp->dp_address - lkcd->kvbase) << lkcd->page_shift; if (lseek(lkcd->fd, page_headers[1] + LKCD_OFFSET_TO_FIRST_PAGE, SEEK_SET) == -1) return; if (read(lkcd->fd, dp, lkcd->page_header_size) != lkcd->page_header_size) return; physaddr2 = (dp->dp_address - lkcd->kvbase) << lkcd->page_shift; if ((physaddr1 % MEGABYTES(1)) || (physaddr2 % MEGABYTES(1)) || (physaddr2 < physaddr1)) return; granularity = physaddr2 - physaddr1; for (i = 0; i < (MCLX_PAGE_HEADERS-1); i++) { if (!page_headers[i]) break; lkcd->curhdroffs = page_headers[i] + LKCD_OFFSET_TO_FIRST_PAGE; set_mb_benchmark((granularity * (i+1))/lkcd->page_size); } } crash-8.0.0/gdb_interface.c0000644000175000017500000007064714147340161016002 0ustar troyhebetroyhebe/* gdb_interface.c - core analysis suite * * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. * Copyright (C) 2002-2015,2018-2019 David Anderson * Copyright (C) 2002-2015,2018-2019 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" #ifndef GDB_10_2 static void exit_after_gdb_info(void); #endif static int is_restricted_command(char *, ulong); static void strip_redirection(char *); int get_frame_offset(ulong); int *gdb_output_format; unsigned int *gdb_print_max; unsigned char *gdb_prettyprint_structs; unsigned char *gdb_prettyprint_arrays; unsigned int *gdb_repeat_count_threshold; unsigned char *gdb_stop_print_at_null; unsigned int *gdb_output_radix; static void gdb_error_debug(void); static ulong gdb_user_print_option_address(char *); /* * Called from main() this routine sets up the call-back hook such that * gdb's main() routine -- renamed gdb_main() -- will call back to * our main_loop() after gdb initializes. */ void gdb_main_loop(int argc, char **argv) { argc = 1; if (pc->flags & SILENT) { if (pc->flags & READNOW) argv[argc++] = "--readnow"; argv[argc++] = "--quiet"; argv[argc++] = pc->namelist_debug ? pc->namelist_debug : (pc->debuginfo_file && (st->flags & CRC_MATCHES) ? pc->debuginfo_file : pc->namelist); } else { if (pc->flags & READNOW) argv[argc++] = "--readnow"; argv[argc++] = pc->namelist_debug ? pc->namelist_debug : (pc->debuginfo_file && (st->flags & CRC_MATCHES) ? pc->debuginfo_file : pc->namelist); } if (CRASHDEBUG(1)) { int i; fprintf(fp, "gdb "); for (i = 1; i < argc; i++) fprintf(fp, "%s ", argv[i]); fprintf(fp, "\n"); } optind = 0; #ifndef GDB_10_2 #if defined(GDB_5_3) || defined(GDB_6_0) || defined(GDB_6_1) command_loop_hook = main_loop; #else deprecated_command_loop_hook = main_loop; #endif #endif gdb_main_entry(argc, argv); } /* * Update any hooks that gdb has set. */ void update_gdb_hooks(void) { #if defined(GDB_6_0) || defined(GDB_6_1) command_loop_hook = pc->flags & VERSION_QUERY ? exit_after_gdb_info : main_loop; target_new_objfile_hook = NULL; #endif #if defined(GDB_7_0) || defined(GDB_7_3_1) || defined(GDB_7_6) deprecated_command_loop_hook = pc->flags & VERSION_QUERY ? exit_after_gdb_info : main_loop; #endif } void gdb_readnow_warning(void) { if ((THIS_GCC_VERSION >= GCC(3,4,0)) && (THIS_GCC_VERSION < GCC(4,0,0)) && !(pc->flags & READNOW)) { fprintf(stderr, "WARNING: Because this kernel was compiled with gcc version %d.%d.%d, certain\n" " commands or command options may fail unless crash is invoked with\n" " the \"--readnow\" command line option.\n\n", kt->gcc_version[0], kt->gcc_version[1], kt->gcc_version[2]); } } /* * Used only by the -v command line option, get gdb to initialize itself * with no arguments, print its version and GPL paragraph, and then call * back to exit_after_gdb_info(). */ void display_gdb_banner(void) { optind = 0; #ifndef GDB_10_2 #if defined(GDB_5_3) || defined(GDB_6_0) || defined(GDB_6_1) command_loop_hook = exit_after_gdb_info; #else deprecated_command_loop_hook = exit_after_gdb_info; #endif #endif args[0] = "gdb"; args[1] = "-version"; gdb_main_entry(2, args); } #ifndef GDB_10_2 static void exit_after_gdb_info(void) { fprintf(fp, "\n"); clean_exit(0); } #endif /* * Stash a copy of the gdb version locally. This can be called before * gdb gets initialized, so bypass gdb_interface(). */ void get_gdb_version(void) { struct gnu_request request; if (!pc->gdb_version) { request.command = GNU_VERSION; gdb_command_funnel(&request); /* bypass gdb_interface() */ pc->gdb_version = request.buf; } } void gdb_session_init(void) { struct gnu_request *req; int debug_data_pulled_in; if (!have_partial_symbols() && !have_full_symbols()) no_debugging_data(FATAL); /* * Restore the SIGINT and SIGPIPE handlers, which got temporarily * re-assigned by gdb. The SIGINT call also initializes GDB's * SIGINT sigaction. */ SIGACTION(SIGINT, restart, &pc->sigaction, &pc->gdb_sigaction); SIGACTION(SIGPIPE, SIG_IGN, &pc->sigaction, NULL); if (!(pc->flags & DROP_CORE)) SIGACTION(SIGSEGV, restart, &pc->sigaction, NULL); /* * Set up pointers to gdb variables. */ #if defined(GDB_5_3) || defined(GDB_6_0) || defined(GDB_6_1) gdb_output_format = &output_format; gdb_print_max = &print_max; gdb_prettyprint_structs = &prettyprint_structs; gdb_prettyprint_arrays = &prettyprint_arrays; gdb_repeat_count_threshold = &repeat_count_threshold; gdb_stop_print_at_null = &stop_print_at_null; gdb_output_radix = &output_radix; #else gdb_output_format = (int *) gdb_user_print_option_address("output_format"); gdb_print_max = (unsigned int *) gdb_user_print_option_address("print_max"); gdb_prettyprint_structs = (unsigned char *) gdb_user_print_option_address("prettyprint_structs"); gdb_prettyprint_arrays = (unsigned char *) gdb_user_print_option_address("prettyprint_arrays"); gdb_repeat_count_threshold = (unsigned int *) gdb_user_print_option_address("repeat_count_threshold"); gdb_stop_print_at_null = (unsigned char *) gdb_user_print_option_address("stop_print_at_null"); gdb_output_radix = (unsigned int *) gdb_user_print_option_address("output_radix"); #endif /* * If the output radix is set via the --hex or --dec command line * option, then pc->output_radix will be non-zero; otherwise use * the gdb default. */ if (pc->output_radix) { *gdb_output_radix = pc->output_radix; *gdb_output_format = (*gdb_output_radix == 10) ? 0 : 'x'; } switch (*gdb_output_radix) { case 10: case 16: pc->output_radix = *gdb_output_radix; break; default: pc->output_radix = *gdb_output_radix = 10; *gdb_output_format = 0; } *gdb_prettyprint_structs = 1; *gdb_repeat_count_threshold = 0x7fffffff; *gdb_print_max = 256; #ifdef GDB_5_3 gdb_disassemble_from_exec = 0; #endif pc->flags |= GDB_INIT; /* set here so gdb_interface will work */ req = (struct gnu_request *)GETBUF(sizeof(struct gnu_request)); req->buf = GETBUF(BUFSIZE); /* * Make sure the namelist has symbolic data. Later versions of * gcc may require that debug data be pulled in by printing a * static kernel data structure. */ debug_data_pulled_in = FALSE; retry: BZERO(req->buf, BUFSIZE); req->command = GNU_GET_DATATYPE; req->name = XEN_HYPER_MODE() ? "page_info" : "task_struct"; req->flags = GNU_RETURN_ON_ERROR; gdb_interface(req); if (req->flags & GNU_COMMAND_FAILED) { if (XEN_HYPER_MODE()) no_debugging_data(WARNING); /* just bail out */ if (!debug_data_pulled_in) { if (CRASHDEBUG(1)) error(INFO, "gdb_session_init: pulling in debug data by accessing init_mm.mmap %s\n", symbol_exists("sysfs_mount") ? "and syfs_mount" : ""); debug_data_pulled_in = TRUE; req->command = GNU_PASS_THROUGH; req->flags = GNU_RETURN_ON_ERROR|GNU_NO_READMEM; req->name = NULL; if (symbol_exists("sysfs_mount")) sprintf(req->buf, "print sysfs_mount, init_mm.mmap"); else sprintf(req->buf, "print init_mm.mmap"); gdb_interface(req); if (!(req->flags & GNU_COMMAND_FAILED)) goto retry; } no_debugging_data(WARNING); } if (pc->flags & KERNEL_DEBUG_QUERY) { fprintf(fp, "\n%s: %s: contains debugging data\n\n", pc->program_name, pc->namelist); if (REMOTE()) remote_exit(); clean_exit(0); } /* * Set up any pre-ordained gdb settings here that can't be * accessed directly. */ req->command = GNU_PASS_THROUGH; req->name = NULL, req->flags = 0; sprintf(req->buf, "set height 0"); gdb_interface(req); req->command = GNU_PASS_THROUGH; req->name = NULL, req->flags = 0; sprintf(req->buf, "set width 0"); gdb_interface(req); #ifdef GDB_10_2 req->command = GNU_PASS_THROUGH; req->name = NULL, req->flags = 0; sprintf(req->buf, "set max-value-size unlimited"); gdb_interface(req); req->command = GNU_PASS_THROUGH; req->name = NULL, req->flags = 0; sprintf(req->buf, "set max-completions unlimited"); gdb_interface(req); #endif #if 0 /* * Patch gdb's symbol values with the correct values from either * the System.map or non-debug vmlinux, whichever is in effect. */ if ((pc->flags & SYSMAP) || (kt->flags & (RELOC_SET|RELOC_FORCE)) || (pc->namelist_debug && !pc->debuginfo_file)) { req->command = GNU_PATCH_SYMBOL_VALUES; req->flags = GNU_RETURN_ON_ERROR; gdb_interface(req); if (req->flags & GNU_COMMAND_FAILED) error(FATAL, "patching of gdb symbol values failed\n"); } else if (!(pc->flags & SILENT)) #else if (!(pc->flags & SILENT)) #endif fprintf(fp, "\n"); FREEBUF(req->buf); FREEBUF(req); } /* * Quickest way to gdb -- just pass a command string to pass through. */ int gdb_pass_through(char *cmd, FILE *fptr, ulong flags) { struct gnu_request *req; int retval; if (CRASHDEBUG(1)) console("gdb_pass_through: [%s]\n", cmd); req = (struct gnu_request *)GETBUF(sizeof(struct gnu_request)); req->buf = cmd; if (fptr) req->fp = fptr; req->command = GNU_PASS_THROUGH; req->flags = flags; gdb_interface(req); if ((req->flags & (GNU_RETURN_ON_ERROR|GNU_COMMAND_FAILED)) == (GNU_RETURN_ON_ERROR|GNU_COMMAND_FAILED)) retval = FALSE; else retval = TRUE; FREEBUF(req); return retval; } /* * General purpose routine for passing commands to gdb. All gdb commands * come through here, where they are passed to gdb_command_funnel(). */ void gdb_interface(struct gnu_request *req) { if (!(pc->flags & GDB_INIT)) error(FATAL, "gdb_interface: gdb not initialized?\n"); if (output_closed()) restart(0); if (!req->fp) { req->fp = ((pc->flags & RUNTIME) || (pc->flags2 & ALLOW_FP)) ? fp : CRASHDEBUG(1) ? fp : pc->nullfp; } pc->cur_req = req; pc->cur_gdb_cmd = req->command; if (CRASHDEBUG(2)) dump_gnu_request(req, IN_GDB); if (!(pc->flags & DROP_CORE)) SIGACTION(SIGSEGV, restart, &pc->sigaction, NULL); else SIGACTION(SIGSEGV, SIG_DFL, &pc->sigaction, NULL); if (interruptible()) { SIGACTION(SIGINT, pc->gdb_sigaction.sa_handler, &pc->gdb_sigaction, NULL); } else { SIGACTION(SIGINT, SIG_IGN, &pc->sigaction, NULL); SIGACTION(SIGPIPE, SIG_IGN, &pc->sigaction, NULL); } pc->flags |= IN_GDB; gdb_command_funnel(req); pc->flags &= ~IN_GDB; SIGACTION(SIGINT, restart, &pc->sigaction, NULL); SIGACTION(SIGSEGV, SIG_DFL, &pc->sigaction, NULL); if (req->flags & GNU_COMMAND_FAILED) gdb_error_debug(); if (CRASHDEBUG(2)) dump_gnu_request(req, !IN_GDB); pc->last_gdb_cmd = pc->cur_gdb_cmd; pc->cur_gdb_cmd = 0; pc->cur_req = NULL; } /* * help -g output */ void dump_gdb_data(void) { fprintf(fp, " prettyprint_arrays: %d\n", *gdb_prettyprint_arrays); fprintf(fp, " prettyprint_structs: %d\n", *gdb_prettyprint_structs); fprintf(fp, "repeat_count_threshold: %x\n", *gdb_repeat_count_threshold); fprintf(fp, " stop_print_at_null: %d\n", *gdb_stop_print_at_null); fprintf(fp, " print_max: %d\n", *gdb_print_max); fprintf(fp, " output_radix: %d\n", *gdb_output_radix); fprintf(fp, " output_format: "); switch (*gdb_output_format) { case 'x': fprintf(fp, "hex\n"); break; case 'o': fprintf(fp, "octal\n"); break; case 0: fprintf(fp, "decimal\n"); break; } } void dump_gnu_request(struct gnu_request *req, int in_gdb) { int others; char buf[BUFSIZE]; if (pc->flags & KERNEL_DEBUG_QUERY) return; console("%scommand: %d (%s)\n", in_gdb ? "GDB IN: " : "GDB OUT: ", req->command, gdb_command_string(req->command, buf, TRUE)); console("buf: %lx ", req->buf); if (req->buf && ascii_string(req->buf)) console(" \"%s\"", req->buf); console("\n"); console("fp: %lx ", req->fp); if (req->fp == pc->nullfp) console("(pc->nullfp) "); if (req->fp == pc->stdpipe) console("(pc->stdpipe) "); if (req->fp == pc->pipe) console("(pc->pipe) "); if (req->fp == pc->ofile) console("(pc->ofile) "); if (req->fp == pc->ifile) console("(pc->ifile) "); if (req->fp == pc->ifile_pipe) console("(pc->ifile_pipe) "); if (req->fp == pc->ifile_ofile) console("(pc->ifile_ofile) "); if (req->fp == pc->tmpfile) console("(pc->tmpfile) "); if (req->fp == pc->saved_fp) console("(pc->saved_fp) "); if (req->fp == pc->tmp_fp) console("(pc->tmp_fp) "); console("flags: %lx (", req->flags); others = 0; if (req->flags & GNU_PRINT_LINE_NUMBERS) console("%sGNU_PRINT_LINE_NUMBERS", others++ ? "|" : ""); if (req->flags & GNU_FUNCTION_ONLY) console("%sGNU_FUNCTION_ONLY", others++ ? "|" : ""); if (req->flags & GNU_PRINT_ENUMERATORS) console("%sGNU_PRINT_ENUMERATORS", others++ ? "|" : ""); if (req->flags & GNU_RETURN_ON_ERROR) console("%sGNU_RETURN_ON_ERROR", others++ ? "|" : ""); if (req->flags & GNU_FROM_TTY_OFF) console("%sGNU_FROM_TTY_OFF", others++ ? "|" : ""); if (req->flags & GNU_NO_READMEM) console("%sGNU_NO_READMEM", others++ ? "|" : ""); if (req->flags & GNU_VAR_LENGTH_TYPECODE) console("%sGNU_VAR_LENGTH_TYPECODE", others++ ? "|" : ""); console(")\n"); console("addr: %lx ", req->addr); console("addr2: %lx ", req->addr2); console("count: %ld\n", req->count); if ((ulong)req->name > (ulong)PATCH_KERNEL_SYMBOLS_STOP) console("name: \"%s\" ", req->name); else console("name: %lx ", (ulong)req->name); console("length: %ld ", req->length); console("typecode: %d\n", req->typecode); #if defined(GDB_5_3) || defined(GDB_6_0) || defined(GDB_6_1) || defined(GDB_7_0) console("typename: %s\n", req->typename); #else console("type_name: %s\n", req->type_name); #endif console("target_typename: %s\n", req->target_typename); console("target_length: %ld ", req->target_length); console("target_typecode: %d ", req->target_typecode); console("is_typedef: %d ", req->is_typedef); console("member: \"%s\" ", req->member); console("member_offset: %ld\n", req->member_offset); console("member_length: %ld\n", req->member_length); console("member_typecode: %d\n", req->member_typecode); console("member_main_type_name: %s\n", req->member_main_type_name); console("member_main_type_tag_name: %s\n", req->member_main_type_tag_name); console("member_target_type_name: %s\n", req->member_target_type_name); console("member_target_type_tag_name: %s\n", req->member_target_type_tag_name); console("value: %lx ", req->value); console("tagname: \"%s\" ", req->tagname); console("pc: %lx ", req->pc); if (is_kernel_text(req->pc)) console("(%s)", value_to_symstr(req->pc, buf, 0)); console("\n"); console("sp: %lx ", req->sp); console("ra: %lx ", req->ra); console("frame: %ld ", req->frame); console("prevsp: %lx\n", req->prevsp); console("prevpc: %lx ", req->prevpc); console("lastsp: %lx ", req->lastsp); console("task: %lx ", req->task); console("debug: %lx\n", req->debug); console("\n"); } char * gdb_command_string(int cmd, char *buf, int live) { switch (cmd) { case GNU_PASS_THROUGH: sprintf(buf, "GNU_PASS_THROUGH"); break; case GNU_DATATYPE_INIT: sprintf(buf, "GNU_DATATYPE_INIT"); break; case GNU_DISASSEMBLE: sprintf(buf, "GNU_DISASSEMBLE"); break; case GNU_GET_LINE_NUMBER: sprintf(buf, "GNU_GET_LINE_NUMBER"); break; case GNU_GET_DATATYPE: if (live) sprintf(buf, "GNU_GET_DATATYPE[%s]", pc->cur_req->name ? pc->cur_req->name : "?"); else sprintf(buf, "GNU_GET_DATATYPE"); break; case GNU_STACK_TRACE: sprintf(buf, "GNU_STACK_TRACE"); break; case GNU_ALPHA_FRAME_OFFSET: sprintf(buf, "GNU_ALPHA_FRAME_OFFSET"); break; case GNU_COMMAND_EXISTS: sprintf(buf, "GNU_COMMAND_EXISTS"); break; case GNU_FUNCTION_NUMARGS: sprintf(buf, "GNU_FUNCTION_NUMARGS"); break; case GNU_RESOLVE_TEXT_ADDR: sprintf(buf, "GNU_RESOLVE_TEXT_ADDR"); break; case GNU_DEBUG_COMMAND: sprintf(buf, "GNU_DEBUG_COMMAND"); break; case GNU_ADD_SYMBOL_FILE: sprintf(buf, "GNU_ADD_SYMBOL_FILE"); break; case GNU_DELETE_SYMBOL_FILE: sprintf(buf, "GNU_DELETE_SYMBOL_FILE"); break; case GNU_VERSION: sprintf(buf, "GNU_VERSION"); break; case GNU_GET_SYMBOL_TYPE: sprintf(buf, "GNU_GET_SYMBOL_TYPE"); break; case GNU_PATCH_SYMBOL_VALUES: sprintf(buf, "GNU_PATCH_SYMBOL_VALUES"); break; case GNU_USER_PRINT_OPTION: sprintf(buf, "GNU_USER_PRINT_OPTION"); break; case GNU_SET_CRASH_BLOCK: sprintf(buf, "GNU_SET_CRASH_BLOCK"); break; case GNU_GET_FUNCTION_RANGE: sprintf(buf, "GNU_GET_FUNCTION_RANGE"); break; case 0: buf[0] = NULLCHAR; break; default: sprintf(buf, "(?)\n"); break; } return buf; } /* * Restore known gdb state. */ void restore_gdb_sanity(void) { if (!(pc->flags & GDB_INIT)) return; if (pc->output_radix) { *gdb_output_radix = pc->output_radix; *gdb_output_format = (*gdb_output_radix == 10) ? 0 : 'x'; } *gdb_prettyprint_structs = 1; /* these may piss somebody off... */ *gdb_repeat_count_threshold = 0x7fffffff; if (st->flags & ADD_SYMBOL_FILE) { error(INFO, "%s\n gdb add-symbol-file command failed\n", st->current->mod_namelist); delete_load_module(st->current->mod_base); st->flags &= ~ADD_SYMBOL_FILE; } if (pc->cur_gdb_cmd) { pc->last_gdb_cmd = pc->cur_gdb_cmd; pc->cur_gdb_cmd = 0; } } /* * Check whether string in args[0] is a valid gdb command. */ int is_gdb_command(int merge_orig_args, ulong flags) { int retval; struct gnu_request *req; if (!args[0]) return FALSE; if (STREQ(args[0], "Q")) { args[0] = "q"; return TRUE; } if (is_restricted_command(args[0], flags)) return FALSE; req = (struct gnu_request *)GETBUF(sizeof(struct gnu_request)); req->buf = GETBUF(strlen(args[0])+1); req->command = GNU_COMMAND_EXISTS; req->name = args[0]; req->flags = GNU_RETURN_ON_ERROR; req->fp = pc->nullfp; gdb_interface(req); if (req->flags & GNU_COMMAND_FAILED) retval = FALSE; else retval = req->value; FREEBUF(req->buf); FREEBUF(req); if (retval && merge_orig_args) { int i; for (i = argcnt; i; i--) args[i] = args[i-1]; args[0] = "gdb"; argcnt++; } return retval; } /* * Check whether a command is on the gdb-prohibited list. */ static char *prohibited_list[] = { "run", "r", "break", "b", "tbreak", "hbreak", "thbreak", "rbreak", "watch", "rwatch", "awatch", "attach", "continue", "c", "fg", "detach", "finish", "handle", "interrupt", "jump", "kill", "next", "nexti", "signal", "step", "s", "stepi", "target", "until", "delete", "clear", "disable", "enable", "condition", "ignore", "frame", "catch", "tcatch", "return", "file", "exec-file", "core-file", "symbol-file", "load", "si", "ni", "shell", "sy", NULL /* must be last */ }; static char *restricted_list[] = { "define", "document", "while", "if", NULL /* must be last */ }; #define RESTRICTED_GDB_COMMAND \ "restricted gdb command: %s\n%s\"%s\" may only be used in a .gdbinit file or in a command file.\n%sThe .gdbinit file is read automatically during %s initialization.\n%sOther user-defined command files may be read interactively during\n%s%s runtime by using the gdb \"source\" command.\n" static int is_restricted_command(char *cmd, ulong flags) { int i; char *newline; for (i = 0; prohibited_list[i]; i++) { if (STREQ(prohibited_list[i], cmd)) { if (flags == RETURN_ON_ERROR) return TRUE; pc->curcmd = pc->program_name; error(FATAL, "prohibited gdb command: %s\n", cmd); } } for (i = 0; restricted_list[i]; i++) { if (STREQ(restricted_list[i], cmd)) { if (flags == RETURN_ON_ERROR) return TRUE; newline = space(strlen(pc->program_name)+2); pc->curcmd = pc->program_name; error(FATAL, RESTRICTED_GDB_COMMAND, cmd, newline, cmd, newline, pc->program_name, newline, newline, pc->program_name); } } return FALSE; } /* * Remove pipe/redirection stuff from the end of the command line. */ static void strip_redirection(char *buf) { char *p1, *p2; p1 = strstr_rightmost(buf, args[argcnt-1]); p2 = p1 + strlen(args[argcnt-1]); console("strip_redirection: [%s]\n", p2); if ((p1 = strpbrk(p2, "|!>"))) *p1 = NULLCHAR; strip_ending_whitespace(buf); } /* * Command for passing strings directly to gdb. */ void cmd_gdb(void) { char buf[BUFSIZE]; char **argv; argv = STREQ(args[0], "gdb") ? &args[1] : &args[0]; if (*argv == NULL) cmd_usage(pc->curcmd, SYNOPSIS); if (STREQ(*argv, "set") && argv[1]) { /* * Intercept set commands in case something has to be done * here or elsewhere. */ if (STREQ(argv[1], "gdb")) { cmd_set(); return; } if (STREQ(argv[1], "output-radix") && argv[2]) pc->output_radix = stol(argv[2], FAULT_ON_ERROR, NULL); } /* * If the command is not restricted, pass it on. */ if (!is_restricted_command(*argv, FAULT_ON_ERROR)) { if (STREQ(pc->command_line, "gdb")) { strcpy(buf, first_space(pc->orig_line)); strip_beginning_whitespace(buf); } else strcpy(buf, pc->orig_line); if (pc->redirect & (REDIRECT_TO_FILE|REDIRECT_TO_PIPE)) strip_redirection(buf); if (!gdb_pass_through(buf, NULL, GNU_RETURN_ON_ERROR)) error(INFO, "gdb request failed: %s\n", buf); } } /* * The gdb target_xfer_memory() has a hook installed to re-route * all memory accesses back here; reads of 1 or 4 bytes come primarily * from text disassembly requests, and are diverted to the text cache. */ int gdb_readmem_callback(ulong addr, void *buf, int len, int write) { char locbuf[SIZEOF_32BIT], *p1; int memtype; ulong readflags; if (write) return FALSE; if (!(pc->cur_req)) { return(readmem(addr, KVADDR, buf, len, "gdb_readmem_callback", RETURN_ON_ERROR)); } if (pc->cur_req->flags & GNU_NO_READMEM) return TRUE; readflags = pc->curcmd_flags & PARTIAL_READ_OK ? RETURN_ON_ERROR|RETURN_PARTIAL : RETURN_ON_ERROR; if (STREQ(pc->curcmd, "bpf") && pc->curcmd_private && (addr > (ulong)pc->curcmd_private)) readflags |= QUIET; if (pc->curcmd_flags & MEMTYPE_UVADDR) memtype = UVADDR; else if (pc->curcmd_flags & MEMTYPE_FILEADDR) memtype = FILEADDR; else if (!IS_KVADDR(addr)) { if (STREQ(pc->curcmd, "gdb") && STRNEQ(pc->cur_req->buf, "x/")) { memtype = UVADDR; } else { if (CRASHDEBUG(1)) console("gdb_readmem_callback: %lx %d FAILED\n", addr, len); return FALSE; } } else memtype = KVADDR; if (CRASHDEBUG(1)) console("gdb_readmem_callback[%d]: %lx %d\n", memtype, addr, len); if (memtype == FILEADDR) return(readmem(pc->curcmd_private, memtype, buf, len, "gdb_readmem_callback", readflags)); switch (len) { case SIZEOF_8BIT: if (STREQ(pc->curcmd, "bt")) { if (readmem(addr, memtype, buf, SIZEOF_8BIT, "gdb_readmem_callback", readflags)) return TRUE; } p1 = (char *)buf; if (!readmem(addr, memtype, locbuf, SIZEOF_32BIT, "gdb_readmem_callback", readflags)) return FALSE; *p1 = locbuf[0]; return TRUE; case SIZEOF_32BIT: if (STREQ(pc->curcmd, "bt")) { if (readmem(addr, memtype, buf, SIZEOF_32BIT, "gdb_readmem_callback", readflags)) return TRUE; } if (!readmem(addr, memtype, buf, SIZEOF_32BIT, "gdb_readmem callback", readflags)) return FALSE; return TRUE; } return(readmem(addr, memtype, buf, len, "gdb_readmem_callback", readflags)); } /* * Machine-specific line-number pc section range verifier. */ int gdb_line_number_callback(ulong pc, ulong low, ulong high) { if (machdep->verify_line_number) return machdep->verify_line_number(pc, low, high); return TRUE; } /* * Prevent gdb from trying to translate and print pointers * that are not kernel virtual addresses. */ int gdb_print_callback(ulong addr) { if (!addr) return FALSE; else return IS_KVADDR(addr); } /* * Used by gdb_interface() to catch gdb-related errors, if desired. */ static void gdb_error_debug(void) { char buf1[BUFSIZE]; char buf2[BUFSIZE]; int buffers; if (CRASHDEBUG(2)) { sprintf(buf2, "\n"); if (CRASHDEBUG(5) && (buffers = get_embedded())) sprintf(buf2, "(%d buffer%s in use)\n", buffers, buffers > 1 ? "s" : ""); fprintf(stderr, "%s: returned via gdb_error_hook %s", gdb_command_string(pc->cur_gdb_cmd, buf1, TRUE), buf2); console("%s: returned via gdb_error_hook %s", gdb_command_string(pc->cur_gdb_cmd, buf1, TRUE), buf2); } } /* * gdb callback to access debug mode. */ int gdb_CRASHDEBUG(ulong dval) { if (CRASHDEBUG(dval)) return TRUE; return (pc->cur_req && (pc->cur_req->debug >= dval)); } static ulong gdb_user_print_option_address(char *name) { struct gnu_request request; request.command = GNU_USER_PRINT_OPTION; request.name = name; gdb_command_funnel(&request); return request.addr; } /* * Try to set a crash scope block based upon the vaddr. */ int gdb_set_crash_scope(ulong vaddr, char *arg) { struct gnu_request request, *req = &request; char name[BUFSIZE]; struct load_module *lm; if (vaddr) { if (!is_kernel_text(vaddr)) { error(INFO, "invalid text address: %s\n", arg); return FALSE; } if (module_symbol(vaddr, NULL, &lm, name, 0)) { if (!(lm->mod_flags & MOD_LOAD_SYMS)) { error(INFO, "attempting to find/load \"%s\" module debuginfo\n", lm->mod_name); if (!load_module_symbols_helper(lm->mod_name)) { error(INFO, "cannot find/load \"%s\" module debuginfo\n", lm->mod_name); return FALSE; } } } } req->command = GNU_SET_CRASH_BLOCK; req->addr = vaddr; req->flags = 0; req->addr2 = 0; req->fp = pc->nullfp; gdb_command_funnel(req); if (CRASHDEBUG(1)) fprintf(fp, "gdb_set_crash_scope: %s addr: %lx block: %lx\n", req->flags & GNU_COMMAND_FAILED ? "FAILED" : "OK", req->addr, req->addr2); if (req->flags & GNU_COMMAND_FAILED) { error(INFO, "gdb cannot find text block for address: %s\n", arg); return FALSE; } return TRUE; } #ifndef ALPHA /* * Stub routine needed for resolution by non-alpha, modified gdb code. */ int get_frame_offset(ulong pc) { return (error(FATAL, "get_frame_offset: invalid request for non-alpha systems!\n")); } #endif /* !ALPHA */ unsigned long crash_get_kaslr_offset(void); unsigned long crash_get_kaslr_offset(void) { return kt->relocate * -1; } /* Callbacks for crash_target */ int crash_get_nr_cpus(void); int crash_get_cpu_reg (int cpu, int regno, const char *regname, int regsize, void *val); int crash_get_nr_cpus(void) { if (SADUMP_DUMPFILE()) return sadump_get_nr_cpus(); else if (DISKDUMP_DUMPFILE()) return diskdump_get_nr_cpus(); else if (KDUMP_DUMPFILE()) return kdump_get_nr_cpus(); else if (VMSS_DUMPFILE()) return vmware_vmss_get_nr_cpus(); /* Just CPU #0 */ return 1; } int crash_get_cpu_reg (int cpu, int regno, const char *regname, int regsize, void *value) { if (!machdep->get_cpu_reg) return FALSE; return machdep->get_cpu_reg(cpu, regno, regname, regsize, value); } crash-8.0.0/mips64.c0000644000175000017500000011210014147340161014325 0ustar troyhebetroyhebe/* mips64.c - core analysis suite * * Copyright (C) 2021 Loongson Technology Co., Ltd. * * 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. */ #ifdef MIPS64 #include #include "defs.h" static void mips64_init_page_flags(void); static int mips64_translate_pte(ulong pte, void *physaddr, ulonglong pte64); static int mips64_pgd_vtop(ulong *pgd, ulong vaddr, physaddr_t *paddr, int verbose); static int mips64_uvtop(struct task_context *tc, ulong vaddr, physaddr_t *paddr, int verbose); static int mips64_kvtop(struct task_context *tc, ulong kvaddr, physaddr_t *paddr, int verbose); static void mips64_cmd_mach(void); static void mips64_display_machine_stats(void); static void mips64_back_trace_cmd(struct bt_info *bt); static void mips64_analyze_function(ulong start, ulong offset, struct mips64_unwind_frame *current, struct mips64_unwind_frame *previous); static void mips64_dump_backtrace_entry(struct bt_info *bt, struct syment *sym, struct mips64_unwind_frame *current, struct mips64_unwind_frame *previous, int level); static void mips64_dump_exception_stack(struct bt_info *bt, char *pt_regs); static int mips64_is_exception_entry(struct syment *sym); static void mips64_display_full_frame(struct bt_info *bt, struct mips64_unwind_frame *current, struct mips64_unwind_frame *previous); static void mips64_stackframe_init(void); static void mips64_get_stack_frame(struct bt_info *bt, ulong *pcp, ulong *spp); static int mips64_get_dumpfile_stack_frame(struct bt_info *bt, ulong *nip, ulong *ksp); static int mips64_get_frame(struct bt_info *bt, ulong *pcp, ulong *spp); static int mips64_init_active_task_regs(void); static int mips64_get_crash_notes(void); static int mips64_get_elf_notes(void); /* * 3 Levels paging PAGE_SIZE=16KB * PGD | PMD | PTE | OFFSET | * 11 | 11 | 11 | 14 | */ /* From arch/mips/include/asm/pgtable{,-64}.h */ typedef struct { ulong pgd; } pgd_t; typedef struct { ulong pmd; } pmd_t; typedef struct { ulong pte; } pte_t; #define PMD_ORDER 0 #define PTE_ORDER 0 #define PMD_SHIFT (PAGESHIFT() + (PAGESHIFT() + PTE_ORDER - 3)) #define PMD_SIZE (1UL << PMD_SHIFT) #define PMD_MASK (~(PMD_SIZE - 1)) #define PGDIR_SHIFT (PMD_SHIFT + (PAGESHIFT() + PMD_ORDER - 3)) #define PGDIR_SIZE (1UL << PGDIR_SHIFT) #define PGDIR_MASK (~(PGDIR_SIZE - 1)) #define PTRS_PER_PTE (1UL << (PAGESHIFT() - 3)) #define PTRS_PER_PMD PTRS_PER_PTE #define PTRS_PER_PGD PTRS_PER_PTE #define USER_PTRS_PER_PGD (0x80000000UL/PGDIR_SIZE) #define pte_index(addr) (((addr) >> PAGESHIFT()) & (PTRS_PER_PTE - 1)) #define pmd_index(addr) (((addr) >> PMD_SHIFT) & (PTRS_PER_PMD - 1)) #define pgd_index(addr) (((addr) >> PGDIR_SHIFT) & (PTRS_PER_PGD - 1)) #define MIPS64_CPU_RIXI (1UL << 23) /* CPU has TLB Read/eXec Inhibit */ /* From arch/mips/include/uapi/asm/reg.h */ #define MIPS64_EF_R0 0 #define MIPS64_EF_R29 29 #define MIPS64_EF_R31 31 #define MIPS64_EF_LO 32 #define MIPS64_EF_HI 33 #define MIPS64_EF_CP0_EPC 34 #define MIPS64_EF_CP0_BADVADDR 35 #define MIPS64_EF_CP0_STATUS 36 #define MIPS64_EF_CP0_CAUSE 37 static struct machine_specific mips64_machine_specific = { 0 }; /* * Holds registers during the crash. */ static struct mips64_register *panic_task_regs; /* * 31 15 14 12 11 10 9 8 7 6 5 4 3 2 1 0 * +-------------------+--------+--+--+--+--+--+--+--+--+--+--+--+--+ * | VPN | C | D| V| G|RI|XI|SP|PN| H| M| A| W| P| * +-------------------+--------+--+--+--+--+--+--+--+--+--+--+--+--+ */ static void mips64_init_page_flags(void) { ulong shift = 0; _PAGE_PRESENT = 1UL << shift++; _PAGE_WRITE = 1UL << shift++; _PAGE_ACCESSED = 1UL << shift++; _PAGE_MODIFIED = 1UL << shift++; _PAGE_HUGE = 1UL << shift++; _PAGE_PROTNONE = 1UL << shift++; if (THIS_KERNEL_VERSION >= LINUX(4,5,0)) _PAGE_SPECIAL = 1UL << shift++; _PAGE_NO_EXEC = 1UL << shift++; _PAGE_NO_READ = _PAGE_READ = 1UL << shift++; _PAGE_GLOBAL = 1UL << shift++; _PAGE_VALID = 1UL << shift++; _PAGE_DIRTY = 1UL << shift++; _PFN_SHIFT = PAGESHIFT() - 12 + shift + 3; } /* * Translate a PTE, returning TRUE if the page is present. * If a physaddr pointer is passed in, don't print anything. */ static int mips64_translate_pte(ulong pte, void *physaddr, ulonglong pte64) { char ptebuf[BUFSIZE]; char physbuf[BUFSIZE]; char buf[BUFSIZE]; int page_present; int len1, len2, others; ulong paddr; paddr = PTOB(pte >> _PFN_SHIFT); page_present = !!(pte & _PAGE_PRESENT); if (physaddr) { *(ulong *)physaddr = paddr; return page_present; } sprintf(ptebuf, "%lx", pte); len1 = MAX(strlen(ptebuf), strlen("PTE")); fprintf(fp, "%s ", mkstring(buf, len1, CENTER | LJUST, "PTE")); if (!page_present) return page_present; sprintf(physbuf, "%lx", paddr); len2 = MAX(strlen(physbuf), strlen("PHYSICAL")); fprintf(fp, "%s ", mkstring(buf, len2, CENTER | LJUST, "PHYSICAL")); fprintf(fp, "FLAGS\n"); fprintf(fp, "%s %s ", mkstring(ptebuf, len1, CENTER | RJUST, NULL), mkstring(physbuf, len2, CENTER | RJUST, NULL)); fprintf(fp, "("); others = 0; #define CHECK_PAGE_FLAG(flag) \ if ((_PAGE_##flag) && (pte & _PAGE_##flag)) \ fprintf(fp, "%s" #flag, others++ ? "|" : "") if (pte) { CHECK_PAGE_FLAG(PRESENT); CHECK_PAGE_FLAG(WRITE); CHECK_PAGE_FLAG(ACCESSED); CHECK_PAGE_FLAG(MODIFIED); CHECK_PAGE_FLAG(HUGE); CHECK_PAGE_FLAG(PROTNONE); CHECK_PAGE_FLAG(SPECIAL); CHECK_PAGE_FLAG(NO_EXEC); CHECK_PAGE_FLAG(NO_READ); CHECK_PAGE_FLAG(READ); CHECK_PAGE_FLAG(GLOBAL); CHECK_PAGE_FLAG(VALID); CHECK_PAGE_FLAG(DIRTY); } else { fprintf(fp, "no mapping"); } fprintf(fp, ")\n"); return page_present; } /* * Virtual to physical memory translation. This function will be called * by both mips64_kvtop and mips64_uvtop. */ static int mips64_pgd_vtop(ulong *pgd, ulong vaddr, physaddr_t *paddr, int verbose) { ulong *pgd_ptr, pgd_val; ulong *pmd_ptr, pmd_val; ulong *pte_ptr, pte_val; if (verbose) { const char *segment; if (vaddr < 0x4000000000000000lu) segment = "xuseg"; else if (vaddr < 0x8000000000000000lu) segment = "xsseg"; else if (vaddr < 0xc000000000000000lu) segment = "xkphys"; else if (vaddr < 0xffffffff80000000lu) segment = "xkseg"; else if (vaddr < 0xffffffffa0000000lu) segment = "kseg0"; else if (vaddr < 0xffffffffc0000000lu) segment = "kseg1"; else if (vaddr < 0xffffffffe0000000lu) segment = "sseg"; else segment = "kseg3"; fprintf(fp, "SEGMENT: %s\n", segment); } if (IS_CKPHYS(vaddr) || IS_XKPHYS(vaddr)) { *paddr = VTOP(vaddr); return TRUE; } if (verbose) fprintf(fp, "PAGE DIRECTORY: %016lx\n", (ulong)pgd); pgd_ptr = pgd + pgd_index(vaddr); FILL_PGD(PAGEBASE(pgd), KVADDR, PAGESIZE()); pgd_val = ULONG(machdep->pgd + PAGEOFFSET(pgd_ptr)); if (verbose) fprintf(fp, " PGD: %16lx => %16lx\n", (ulong)pgd_ptr, pgd_val); if (!pgd_val) goto no_page; pmd_ptr = (ulong *)(VTOP(pgd_val) + sizeof(pmd_t) * pmd_index(vaddr)); FILL_PMD(PAGEBASE(pmd_ptr), PHYSADDR, PAGESIZE()); pmd_val = ULONG(machdep->pmd + PAGEOFFSET(pmd_ptr)); if (verbose) fprintf(fp, " PMD: %016lx => %016lx\n", (ulong)pmd_ptr, pmd_val); if (!pmd_val) goto no_page; pte_ptr = (ulong *)(VTOP(pmd_val) + sizeof(pte_t) * pte_index(vaddr)); FILL_PTBL(PAGEBASE(pte_ptr), PHYSADDR, PAGESIZE()); pte_val = ULONG(machdep->ptbl + PAGEOFFSET(pte_ptr)); if (verbose) fprintf(fp, " PTE: %016lx => %016lx\n", (ulong)pte_ptr, pte_val); if (!pte_val) goto no_page; if (!(pte_val & _PAGE_PRESENT)) { if (verbose) { fprintf(fp, "\n"); mips64_translate_pte((ulong)pte_val, 0, pte_val); } return FALSE; } *paddr = PTOB(pte_val >> _PFN_SHIFT) + PAGEOFFSET(vaddr); if (verbose) { fprintf(fp, " PAGE: %016lx\n\n", PAGEBASE(*paddr)); mips64_translate_pte(pte_val, 0, 0); } return TRUE; no_page: fprintf(fp, "invalid\n"); return FALSE; } /* Translates a user virtual address to its physical address. cmd_vtop() sets * the verbose flag so that the pte translation gets displayed; all other * callers quietly accept the translation. */ static int mips64_uvtop(struct task_context *tc, ulong vaddr, physaddr_t *paddr, int verbose) { ulong mm, active_mm; ulong *pgd; if (!tc) error(FATAL, "current context invalid\n"); *paddr = 0; if (is_kernel_thread(tc->task) && IS_KVADDR(vaddr)) { readmem(tc->task + OFFSET(task_struct_active_mm), KVADDR, &active_mm, sizeof(void *), "task active_mm contents", FAULT_ON_ERROR); if (!active_mm) error(FATAL, "no active_mm for this kernel thread\n"); readmem(active_mm + OFFSET(mm_struct_pgd), KVADDR, &pgd, sizeof(long), "mm_struct pgd", FAULT_ON_ERROR); } else { if ((mm = task_mm(tc->task, TRUE))) pgd = ULONG_PTR(tt->mm_struct + OFFSET(mm_struct_pgd)); else readmem(tc->mm_struct + OFFSET(mm_struct_pgd), KVADDR, &pgd, sizeof(long), "mm_struct pgd", FAULT_ON_ERROR); } return mips64_pgd_vtop(pgd, vaddr, paddr, verbose);; } /* Translates a user virtual address to its physical address. cmd_vtop() sets * the verbose flag so that the pte translation gets displayed; all other * callers quietly accept the translation. */ static int mips64_kvtop(struct task_context *tc, ulong kvaddr, physaddr_t *paddr, int verbose) { if (!IS_KVADDR(kvaddr)) return FALSE; if (!verbose) { if (IS_CKPHYS(kvaddr) || IS_XKPHYS(kvaddr)) { *paddr = VTOP(kvaddr); return TRUE; } } return mips64_pgd_vtop((ulong *)vt->kernel_pgd[0], kvaddr, paddr, verbose); } /* * Machine dependent command. */ static void mips64_cmd_mach(void) { int c; while ((c = getopt(argcnt, args, "cmo")) != EOF) { switch (c) { case 'c': case 'm': case 'o': option_not_supported(c); break; default: argerrs++; break; } } if (argerrs) cmd_usage(pc->curcmd, SYNOPSIS); mips64_display_machine_stats(); } /* * "mach" command output. */ static void mips64_display_machine_stats(void) { struct new_utsname *uts; char buf[BUFSIZE]; ulong mhz; uts = &kt->utsname; fprintf(fp, " MACHINE TYPE: %s\n", uts->machine); fprintf(fp, " MEMORY SIZE: %s\n", get_memory_size(buf)); fprintf(fp, " CPUS: %d\n", get_cpus_to_display()); fprintf(fp, " PROCESSOR SPEED: "); if ((mhz = machdep->processor_speed())) fprintf(fp, "%ld Mhz\n", mhz); else fprintf(fp, "(unknown)\n"); fprintf(fp, " HZ: %d\n", machdep->hz); fprintf(fp, " PAGE SIZE: %d\n", PAGESIZE()); fprintf(fp, " KERNEL STACK SIZE: %ld\n", STACKSIZE()); } /* * Unroll a kernel stack. */ static void mips64_back_trace_cmd(struct bt_info *bt) { struct mips64_unwind_frame current, previous; struct mips64_register *regs; struct mips64_pt_regs_main *mains; struct mips64_pt_regs_cp0 *cp0; char pt_regs[SIZE(pt_regs)]; int level = 0; int invalid_ok = 1; if (bt->flags & BT_REGS_NOT_FOUND) return; previous.sp = previous.pc = previous.ra = 0; current.pc = bt->instptr; current.sp = bt->stkptr; current.ra = 0; if (!INSTACK(current.sp, bt)) return; if (bt->machdep) { regs = bt->machdep; previous.pc = current.ra = regs->regs[MIPS64_EF_R31]; } while (current.sp <= bt->stacktop - 32 - SIZE(pt_regs)) { struct syment *symbol = NULL; ulong offset; if (CRASHDEBUG(8)) fprintf(fp, "level %d pc %#lx ra %#lx sp %lx\n", level, current.pc, current.ra, current.sp); if (!IS_KVADDR(current.pc) && !invalid_ok) return; symbol = value_search(current.pc, &offset); if (!symbol && !invalid_ok) { error(FATAL, "PC is unknown symbol (%lx)", current.pc); return; } invalid_ok = 0; /* * If we get an address which points to the start of a * function, then it could one of the following: * * - we are dealing with a noreturn function. The last call * from a noreturn function has an ra which points to the * start of the function after it. This is common in the * oops callchain because of die() which is annotated as * noreturn. * * - we have taken an exception at the start of this function. * In this case we already have the RA in current.ra. * * - we are in one of these routines which appear with zero * offset in manually-constructed stack frames: * * * ret_from_exception * * ret_from_irq * * ret_from_fork * * ret_from_kernel_thread */ if (symbol && !STRNEQ(symbol->name, "ret_from") && !offset && !current.ra && current.sp < bt->stacktop - 32 - SIZE(pt_regs)) { if (CRASHDEBUG(8)) fprintf(fp, "zero offset at %s, try previous symbol\n", symbol->name); symbol = value_search(current.pc - 4, &offset); if (!symbol) { error(FATAL, "PC is unknown symbol (%lx)", current.pc); return; } } if (symbol && mips64_is_exception_entry(symbol)) { mains = (struct mips64_pt_regs_main *) \ (pt_regs + OFFSET(pt_regs_regs)); cp0 = (struct mips64_pt_regs_cp0 *) \ (pt_regs + OFFSET(pt_regs_cp0_badvaddr)); GET_STACK_DATA(current.sp, pt_regs, sizeof(pt_regs)); previous.ra = mains->regs[31]; previous.sp = mains->regs[29]; current.ra = cp0->cp0_epc; if (CRASHDEBUG(8)) fprintf(fp, "exception pc %#lx ra %#lx sp %lx\n", previous.pc, previous.ra, previous.sp); /* The PC causing the exception may have been invalid */ invalid_ok = 1; } else if (symbol) { mips64_analyze_function(symbol->value, offset, ¤t, &previous); } else { /* * The current PC is invalid. Assume that the code * jumped through a invalid pointer and that the SP has * not been adjusted. */ previous.sp = current.sp; } mips64_dump_backtrace_entry(bt, symbol, ¤t, &previous, level++); current.pc = current.ra; current.sp = previous.sp; current.ra = previous.ra; if (CRASHDEBUG(8)) fprintf(fp, "next %d pc %#lx ra %#lx sp %lx\n", level, current.pc, current.ra, current.sp); previous.sp = previous.pc = previous.ra = 0; } } static void mips64_analyze_function(ulong start, ulong offset, struct mips64_unwind_frame *current, struct mips64_unwind_frame *previous) { ulong i, reg; ulong rapos = 0; ulong spadjust = 0; uint32_t *funcbuf, *ip; if (CRASHDEBUG(8)) fprintf(fp, "%s: start %#lx offset %#lx\n", __func__, start, offset); if (!offset) { previous->sp = current->sp; return; } ip = funcbuf = (uint32_t *)GETBUF(offset); if (!readmem(start, KVADDR, funcbuf, offset, "mips64_analyze_function", RETURN_ON_ERROR)) { FREEBUF(funcbuf); error(WARNING, "Cannot read function at %16lx\n", start); return; } for (i = 0; i < offset; i += 4) { ulong insn = *ip & 0xffffffff; ulong high = (insn >> 16) & 0xffff; ulong low = insn & 0xffff; if (CRASHDEBUG(8)) fprintf(fp, "insn @ %#lx = %#lx\n", start + i, insn); if (high == 0x27bd || high == 0x67bd) { /* ADDIU/DADDIU sp, sp, imm */ if (!(low & 0x8000)) break; spadjust += 0x10000 - low; if (CRASHDEBUG(8)) fprintf(fp, "spadjust = %lu\n", spadjust); } else if (high == 0xafbf) { /* SW RA, imm(SP) */ rapos = current->sp + low; if (CRASHDEBUG(8)) fprintf(fp, "rapos %lx\n", rapos); break; } else if (high == 0xffbf) { /* SD RA, imm(SP) */ rapos = current->sp + low; if (CRASHDEBUG(8)) fprintf(fp, "rapos %lx\n", rapos); break; } else if ((insn & 0xffe08020) == 0xeba00020) { /* GSSQ reg, reg, offset(SP) */ reg = (insn >> 16) & 0x1f; if (reg == 31) { low = ((((insn >> 6) & 0x1ff) ^ 0x100) - 0x100) << 4; rapos = current->sp + low; if (CRASHDEBUG(8)) fprintf(fp, "rapos %lx\n", rapos); break; } reg = insn & 0x1f; if (reg == 31) { low = (((((insn >> 6) & 0x1ff) ^ 0x100) - 0x100) << 4) + 8; rapos = current->sp + low; if (CRASHDEBUG(8)) fprintf(fp, "rapos %lx\n", rapos); break; } } ip++; } FREEBUF(funcbuf); previous->sp = current->sp + spadjust; if (rapos && !readmem(rapos, KVADDR, ¤t->ra, sizeof(current->ra), "RA from stack", RETURN_ON_ERROR)) { error(FATAL, "Cannot read RA from stack %lx", rapos); return; } } static void mips64_dump_backtrace_entry(struct bt_info *bt, struct syment *sym, struct mips64_unwind_frame *current, struct mips64_unwind_frame *previous, int level) { const char *name = sym ? sym->name : "(invalid)"; struct load_module *lm; char *name_plus_offset = NULL; struct syment *symp; ulong symbol_offset; char buf[BUFSIZE]; char pt_regs[SIZE(pt_regs)]; if (bt->flags & BT_SYMBOL_OFFSET) { symp = value_search(current->pc, &symbol_offset); if (symp && symbol_offset) name_plus_offset = value_to_symstr(current->pc, buf, bt->radix); } fprintf(fp, "%s#%d [%016lx] %s at %016lx", level < 10 ? " " : "", level, current->sp, name_plus_offset ? name_plus_offset : name, current->pc); if (module_symbol(current->pc, NULL, &lm, NULL, 0)) fprintf(fp, " [%s]", lm->mod_name); fprintf(fp, "\n"); /* * 'bt -l', get a line number associated with a current pc address. */ if (bt->flags & BT_LINE_NUMBERS) { get_line_number(current->pc, buf, FALSE); if (strlen(buf)) fprintf(fp, " %s\n", buf); } if (sym && mips64_is_exception_entry(sym)) { GET_STACK_DATA(current->sp, &pt_regs, SIZE(pt_regs)); mips64_dump_exception_stack(bt, pt_regs); } /* bt -f */ if (bt->flags & BT_FULL) { fprintf(fp, " " "[PC: %016lx RA: %016lx SP: %016lx SIZE: %ld]\n", current->pc, current->ra, current->sp, previous->sp - current->sp); mips64_display_full_frame(bt, current, previous); } } static void mips64_dump_exception_stack(struct bt_info *bt, char *pt_regs) { struct mips64_pt_regs_main *mains; struct mips64_pt_regs_cp0 *cp0; int i; char buf[BUFSIZE]; mains = (struct mips64_pt_regs_main *) (pt_regs + OFFSET(pt_regs_regs)); cp0 = (struct mips64_pt_regs_cp0 *) \ (pt_regs + OFFSET(pt_regs_cp0_badvaddr)); for (i = 0; i < 32; i += 4) { fprintf(fp, " $%2d : %016lx %016lx %016lx %016lx\n", i, mains->regs[i], mains->regs[i+1], mains->regs[i+2], mains->regs[i+3]); } fprintf(fp, " Hi : %016lx\n", mains->hi); fprintf(fp, " Lo : %016lx\n", mains->lo); value_to_symstr(cp0->cp0_epc, buf, 16); fprintf(fp, " epc : %016lx %s\n", cp0->cp0_epc, buf); value_to_symstr(mains->regs[31], buf, 16); fprintf(fp, " ra : %016lx %s\n", mains->regs[31], buf); fprintf(fp, " Status: %016lx\n", mains->cp0_status); fprintf(fp, " Cause : %016lx\n", cp0->cp0_cause); fprintf(fp, " BadVA : %016lx\n", cp0->cp0_badvaddr); } static int mips64_is_exception_entry(struct syment *sym) { return STREQ(sym->name, "ret_from_exception") || STREQ(sym->name, "ret_from_irq") || STREQ(sym->name, "work_resched") || STREQ(sym->name, "handle_sys") || STREQ(sym->name, "handle_sysn32") || STREQ(sym->name, "handle_sys64"); } /* * 'bt -f' commend output * Display all stack data contained in a frame */ static void mips64_display_full_frame(struct bt_info *bt, struct mips64_unwind_frame *current, struct mips64_unwind_frame *previous) { int i, u_idx; ulong *up; ulong words, addr; char buf[BUFSIZE]; if (previous->sp < current->sp) return; if (!(INSTACK(previous->sp, bt) && INSTACK(current->sp, bt))) return; words = (previous->sp - current->sp) / sizeof(ulong) + 1; addr = current->sp; u_idx = (current->sp - bt->stackbase) / sizeof(ulong); for (i = 0; i < words; i++, u_idx++) { if (!(i & 1)) fprintf(fp, "%s %lx: ", i ? "\n" : "", addr); up = (ulong *)(&bt->stackbuf[u_idx*sizeof(ulong)]); fprintf(fp, "%s ", format_stack_entry(bt, buf, *up, 0)); addr += sizeof(ulong); } fprintf(fp, "\n"); } static void mips64_stackframe_init(void) { long task_struct_thread = MEMBER_OFFSET("task_struct", "thread"); long thread_reg29 = MEMBER_OFFSET("thread_struct", "reg29"); long thread_reg31 = MEMBER_OFFSET("thread_struct", "reg31"); if ((task_struct_thread == INVALID_OFFSET) || (thread_reg29 == INVALID_OFFSET) || (thread_reg31 == INVALID_OFFSET)) { error(FATAL, "cannot determine thread_struct offsets\n"); return; } ASSIGN_OFFSET(task_struct_thread_reg29) = task_struct_thread + thread_reg29; ASSIGN_OFFSET(task_struct_thread_reg31) = task_struct_thread + thread_reg31; STRUCT_SIZE_INIT(pt_regs, "pt_regs"); MEMBER_OFFSET_INIT(pt_regs_regs, "pt_regs", "regs"); MEMBER_OFFSET_INIT(pt_regs_cp0_badvaddr, "pt_regs", "cp0_badvaddr"); } /* * Get a stack frame combination of pc and ra from the most relevant spot. */ static void mips64_get_stack_frame(struct bt_info *bt, ulong *pcp, ulong *spp) { ulong ksp, nip; int ret = 0; nip = ksp = 0; bt->machdep = NULL; if (DUMPFILE() && is_task_active(bt->task)) ret = mips64_get_dumpfile_stack_frame(bt, &nip, &ksp); else ret = mips64_get_frame(bt, &nip, &ksp); if (!ret) error(WARNING, "cannot determine starting stack frame for task %lx\n", bt->task); if (pcp) *pcp = nip; if (spp) *spp = ksp; } /* * Get the starting point for the active cpu in a diskdump. */ static int mips64_get_dumpfile_stack_frame(struct bt_info *bt, ulong *nip, ulong *ksp) { const struct machine_specific *ms = machdep->machspec; struct mips64_register *regs; ulong epc, r29; if (!ms->crash_task_regs) { bt->flags |= BT_REGS_NOT_FOUND; return FALSE; } /* * We got registers for panic task from crash_notes. Just return them. */ regs = &ms->crash_task_regs[bt->tc->processor]; epc = regs->regs[MIPS64_EF_CP0_EPC]; r29 = regs->regs[MIPS64_EF_R29]; if (!epc && !r29) { bt->flags |= BT_REGS_NOT_FOUND; return FALSE; } if (nip) *nip = epc; if (ksp) *ksp = r29; bt->machdep = regs; return TRUE; } /* * Do the work for mips64_get_stack_frame() for non-active tasks. * Get SP and PC values for idle tasks. */ static int mips64_get_frame(struct bt_info *bt, ulong *pcp, ulong *spp) { if (!bt->tc || !(tt->flags & THREAD_INFO)) return FALSE; if (!readmem(bt->task + OFFSET(task_struct_thread_reg31), KVADDR, pcp, sizeof(*pcp), "thread_struct.regs31", RETURN_ON_ERROR)) { return FALSE; } if (!readmem(bt->task + OFFSET(task_struct_thread_reg29), KVADDR, spp, sizeof(*spp), "thread_struct.regs29", RETURN_ON_ERROR)) { return FALSE; } return TRUE; } static int mips64_init_active_task_regs(void) { int retval; retval = mips64_get_crash_notes(); if (retval == TRUE) return retval; return mips64_get_elf_notes(); } /* * Retrieve task registers for the time of the crash. */ static int mips64_get_crash_notes(void) { struct machine_specific *ms = machdep->machspec; ulong crash_notes; Elf64_Nhdr *note; ulong offset; char *buf, *p; ulong *notes_ptrs; ulong i; /* * crash_notes contains per cpu memory for storing cpu states * in case of system crash. */ if (!symbol_exists("crash_notes")) return FALSE; crash_notes = symbol_value("crash_notes"); notes_ptrs = (ulong *)GETBUF(kt->cpus*sizeof(notes_ptrs[0])); /* * Read crash_notes for the first CPU. crash_notes are in standard ELF * note format. */ if (!readmem(crash_notes, KVADDR, ¬es_ptrs[kt->cpus-1], sizeof(notes_ptrs[kt->cpus-1]), "crash_notes", RETURN_ON_ERROR)) { error(WARNING, "cannot read crash_notes\n"); FREEBUF(notes_ptrs); return FALSE; } if (symbol_exists("__per_cpu_offset")) { /* * Add __per_cpu_offset for each cpu to form the pointer to the notes */ for (i = 0; i < kt->cpus; i++) notes_ptrs[i] = notes_ptrs[kt->cpus-1] + kt->__per_cpu_offset[i]; } buf = GETBUF(SIZE(note_buf)); if (!(panic_task_regs = calloc((size_t)kt->cpus, sizeof(*panic_task_regs)))) error(FATAL, "cannot calloc panic_task_regs space\n"); for (i = 0; i < kt->cpus; i++) { if (!readmem(notes_ptrs[i], KVADDR, buf, SIZE(note_buf), "note_buf_t", RETURN_ON_ERROR)) { error(WARNING, "cannot find NT_PRSTATUS note for cpu: %d\n", i); goto fail; } /* * Do some sanity checks for this note before reading registers from it. */ note = (Elf64_Nhdr *)buf; p = buf + sizeof(Elf64_Nhdr); /* * dumpfiles created with qemu won't have crash_notes, but there will * be elf notes; dumpfiles created by kdump do not create notes for * offline cpus. */ if (note->n_namesz == 0 && (DISKDUMP_DUMPFILE() || KDUMP_DUMPFILE())) { if (DISKDUMP_DUMPFILE()) note = diskdump_get_prstatus_percpu(i); else if (KDUMP_DUMPFILE()) note = netdump_get_prstatus_percpu(i); if (note) { /* * SIZE(note_buf) accounts for a "final note", which is a * trailing empty elf note header. */ long notesz = SIZE(note_buf) - sizeof(Elf64_Nhdr); if (sizeof(Elf64_Nhdr) + roundup(note->n_namesz, 4) + note->n_descsz == notesz) BCOPY((char *)note, buf, notesz); } else { error(WARNING, "cannot find NT_PRSTATUS note for cpu: %d\n", i); continue; } } /* * Check the sanity of NT_PRSTATUS note only for each online cpu. */ if (note->n_type != NT_PRSTATUS) { error(WARNING, "invalid NT_PRSTATUS note (n_type != NT_PRSTATUS)\n"); goto fail; } if (!STRNEQ(p, "CORE")) { error(WARNING, "invalid NT_PRSTATUS note (name != \"CORE\"\n"); goto fail; } /* * Find correct location of note data. This contains elf_prstatus * structure which has registers etc. for the crashed task. */ offset = sizeof(Elf64_Nhdr); offset = roundup(offset + note->n_namesz, 4); p = buf + offset; /* start of elf_prstatus */ BCOPY(p + OFFSET(elf_prstatus_pr_reg), &panic_task_regs[i], sizeof(panic_task_regs[i])); } /* * And finally we have the registers for the crashed task. This is * used later on when dumping backtrace. */ ms->crash_task_regs = panic_task_regs; FREEBUF(buf); FREEBUF(notes_ptrs); return TRUE; fail: FREEBUF(buf); FREEBUF(notes_ptrs); free(panic_task_regs); return FALSE; } static int mips64_get_elf_notes(void) { struct machine_specific *ms = machdep->machspec; int i; if (!DISKDUMP_DUMPFILE() && !KDUMP_DUMPFILE()) return FALSE; panic_task_regs = calloc(kt->cpus, sizeof(*panic_task_regs)); if (!panic_task_regs) error(FATAL, "cannot calloc panic_task_regs space\n"); for (i = 0; i < kt->cpus; i++) { Elf64_Nhdr *note = NULL; size_t len; if (DISKDUMP_DUMPFILE()) note = diskdump_get_prstatus_percpu(i); else if (KDUMP_DUMPFILE()) note = netdump_get_prstatus_percpu(i); if (!note) { error(WARNING, "cannot find NT_PRSTATUS note for cpu: %d\n", i); continue; } len = sizeof(Elf64_Nhdr); len = roundup(len + note->n_namesz, 4); BCOPY((char *)note + len + OFFSET(elf_prstatus_pr_reg), &panic_task_regs[i], sizeof(panic_task_regs[i])); } ms->crash_task_regs = panic_task_regs; return TRUE; } /* * Accept or reject a symbol from the kernel namelist. */ static int mips64_verify_symbol(const char *name, ulong value, char type) { if (CRASHDEBUG(8) && name && strlen(name)) fprintf(fp, "%08lx %s\n", value, name); if (STREQ(name, "_text") || STREQ(name, "_stext")) machdep->flags |= KSYMS_START; return (name && strlen(name) && (machdep->flags & KSYMS_START) && !STRNEQ(name, "__func__.") && !STRNEQ(name, "__crc_")); } /* * Override smp_num_cpus if possible and necessary. */ static int mips64_get_smp_cpus(void) { return (get_cpus_online() > 0) ? get_cpus_online() : kt->cpus; } static ulong mips64_get_page_size(void) { return memory_page_size(); } /* * Determine where vmalloc'd memory starts. */ static ulong mips64_vmalloc_start(void) { return first_vmalloc_address(); } /* * Calculate and return the speed of the processor. */ static ulong mips64_processor_speed(void) { unsigned long cpu_hz1 = 0, cpu_hz2 = 0; if (machdep->mhz) return (machdep->mhz); if (symbol_exists("mips_cpu_frequency")) { get_symbol_data("mips_cpu_frequency", sizeof(int), &cpu_hz1); if (cpu_hz1) return(machdep->mhz = cpu_hz1/1000000); } if (symbol_exists("cpu_clock_freq")) { get_symbol_data("cpu_clock_freq", sizeof(int), &cpu_hz2); if (cpu_hz2) return(machdep->mhz = cpu_hz2/1000000); } return 0; } /* * Checks whether given task is valid task address. */ static int mips64_is_task_addr(ulong task) { if (tt->flags & THREAD_INFO) return IS_KVADDR(task); return (IS_KVADDR(task) && ALIGNED_STACK_OFFSET(task) == 0); } /* * "help -m/M" command output */ void mips64_dump_machdep_table(ulong arg) { int others = 0; fprintf(fp, " flags: %lx (", machdep->flags); if (machdep->flags & KSYMS_START) fprintf(fp, "%sKSYMS_START", others++ ? "|" : ""); fprintf(fp, ")\n"); fprintf(fp, " kvbase: %lx\n", machdep->kvbase); fprintf(fp, " identity_map_base: %lx\n", machdep->identity_map_base); fprintf(fp, " pagesize: %d\n", machdep->pagesize); fprintf(fp, " pageshift: %d\n", machdep->pageshift); fprintf(fp, " pagemask: %llx\n", machdep->pagemask); fprintf(fp, " pageoffset: %lx\n", machdep->pageoffset); fprintf(fp, " pgdir_shift: %d\n", PGDIR_SHIFT); fprintf(fp, " ptrs_per_pgd: %lu\n", PTRS_PER_PGD); fprintf(fp, " ptrs_per_pte: %ld\n", PTRS_PER_PTE); fprintf(fp, " stacksize: %ld\n", machdep->stacksize); fprintf(fp, " hz: %d\n", machdep->hz); fprintf(fp, " memsize: %ld (0x%lx)\n", machdep->memsize, machdep->memsize); fprintf(fp, " bits: %d\n", machdep->bits); fprintf(fp, " back_trace: mips64_back_trace_cmd()\n"); fprintf(fp, " processor_speed: mips64_processor_speed()\n"); fprintf(fp, " uvtop: mips64_uvtop()\n"); fprintf(fp, " kvtop: mips64_kvtop()\n"); fprintf(fp, " get_stack_frame: mips64_get_stack_frame()\n"); fprintf(fp, " get_stackbase: generic_get_stackbase()\n"); fprintf(fp, " get_stacktop: generic_get_stacktop()\n"); fprintf(fp, " translate_pte: mips64_translate_pte()\n"); fprintf(fp, " memory_size: generic_memory_size()\n"); fprintf(fp, " vmalloc_start: mips64_vmalloc_start()\n"); fprintf(fp, " is_task_addr: mips64_is_task_addr()\n"); fprintf(fp, " verify_symbol: mips64_verify_symbol()\n"); fprintf(fp, " dis_filter: generic_dis_filter()\n"); fprintf(fp, " dump_irq: generic_dump_irq()\n"); fprintf(fp, " show_interrupts: generic_show_interrupts()\n"); fprintf(fp, " get_irq_affinity: generic_get_irq_affinity()\n"); fprintf(fp, " cmd_mach: mips64_cmd_mach()\n"); fprintf(fp, " get_smp_cpus: mips64_get_smp_cpus()\n"); fprintf(fp, " is_kvaddr: generic_is_kvaddr()\n"); fprintf(fp, " is_uvaddr: generic_is_uvaddr()\n"); fprintf(fp, " verify_paddr: generic_verify_paddr()\n"); fprintf(fp, " init_kernel_pgd: NULL\n"); fprintf(fp, " value_to_symbol: generic_machdep_value_to_symbol()\n"); fprintf(fp, " line_number_hooks: NULL\n"); fprintf(fp, " last_pgd_read: %lx\n", machdep->last_pgd_read); fprintf(fp, " last_pmd_read: %lx\n", machdep->last_pmd_read); fprintf(fp, " last_ptbl_read: %lx\n", machdep->last_ptbl_read); fprintf(fp, " pgd: %lx\n", (ulong)machdep->pgd); fprintf(fp, " pmd: %lx\n", (ulong)machdep->pmd); fprintf(fp, " ptbl: %lx\n", (ulong)machdep->ptbl); fprintf(fp, " section_size_bits: %ld\n", machdep->section_size_bits); fprintf(fp, " max_physmem_bits: %ld\n", machdep->max_physmem_bits); fprintf(fp, " sections_per_root: %ld\n", machdep->sections_per_root); fprintf(fp, " machspec: %lx\n", (ulong)machdep->machspec); } static void pt_level_alloc(char **lvl, char *name) { size_t sz = PAGESIZE(); void *pointer = malloc(sz); if (!pointer) error(FATAL, name); *lvl = pointer; } /* * Do all necessary machine-specific setup here. This is called several * times during initialization. */ void mips64_init(int when) { switch (when) { case SETUP_ENV: machdep->process_elf_notes = process_elf64_notes; break; case PRE_SYMTAB: machdep->verify_symbol = mips64_verify_symbol; machdep->machspec = &mips64_machine_specific; if (pc->flags & KERNEL_DEBUG_QUERY) return; machdep->last_pgd_read = 0; machdep->last_pmd_read = 0; machdep->last_ptbl_read = 0; machdep->verify_paddr = generic_verify_paddr; machdep->ptrs_per_pgd = PTRS_PER_PGD; break; case PRE_GDB: machdep->pagesize = mips64_get_page_size(); machdep->pageshift = ffs(machdep->pagesize) - 1; machdep->pageoffset = machdep->pagesize - 1; machdep->pagemask = ~((ulonglong)machdep->pageoffset); if (machdep->pagesize >= 16384) machdep->stacksize = machdep->pagesize; else machdep->stacksize = machdep->pagesize * 2; pt_level_alloc(&machdep->pgd, "cannot malloc pgd space."); pt_level_alloc(&machdep->pmd, "cannot malloc pmd space."); pt_level_alloc(&machdep->ptbl, "cannot malloc ptbl space."); machdep->kvbase = 0x8000000000000000lu; machdep->identity_map_base = machdep->kvbase; machdep->is_kvaddr = generic_is_kvaddr; machdep->is_uvaddr = generic_is_uvaddr; machdep->uvtop = mips64_uvtop; machdep->kvtop = mips64_kvtop; machdep->cmd_mach = mips64_cmd_mach; machdep->back_trace = mips64_back_trace_cmd; machdep->get_stack_frame = mips64_get_stack_frame; machdep->vmalloc_start = mips64_vmalloc_start; machdep->processor_speed = mips64_processor_speed; machdep->get_stackbase = generic_get_stackbase; machdep->get_stacktop = generic_get_stacktop; machdep->translate_pte = mips64_translate_pte; machdep->memory_size = generic_memory_size; machdep->is_task_addr = mips64_is_task_addr; machdep->get_smp_cpus = mips64_get_smp_cpus; machdep->dis_filter = generic_dis_filter; machdep->dump_irq = generic_dump_irq; machdep->show_interrupts = generic_show_interrupts; machdep->get_irq_affinity = generic_get_irq_affinity; machdep->value_to_symbol = generic_machdep_value_to_symbol; machdep->init_kernel_pgd = NULL; break; case POST_GDB: mips64_init_page_flags(); machdep->section_size_bits = _SECTION_SIZE_BITS; machdep->max_physmem_bits = _MAX_PHYSMEM_BITS; mips64_stackframe_init(); if (!machdep->hz) machdep->hz = 250; if (symbol_exists("irq_desc")) ARRAY_LENGTH_INIT(machdep->nr_irqs, irq_desc, "irq_desc", NULL, 0); else if (kernel_symbol_exists("nr_irqs")) get_symbol_data("nr_irqs", sizeof(unsigned int), &machdep->nr_irqs); MEMBER_OFFSET_INIT(elf_prstatus_pr_reg, "elf_prstatus", "pr_reg"); STRUCT_SIZE_INIT(note_buf, "note_buf_t"); break; case POST_VM: /* * crash_notes contains machine specific information about the * crash. In particular, it contains CPU registers at the time * of the crash. We need this information to extract correct * backtraces from the panic task. */ if (!ACTIVE() && !mips64_init_active_task_regs()) error(WARNING, "cannot retrieve registers for active task%s\n\n", kt->cpus > 1 ? "s" : ""); break; } } /* * 'help -r' command output */ void mips64_display_regs_from_elf_notes(int cpu, FILE *ofp) { const struct machine_specific *ms = machdep->machspec; struct mips64_register *regs; if (!ms->crash_task_regs) { error(INFO, "registers not collected for cpu %d\n", cpu); return; } regs = &ms->crash_task_regs[cpu]; if (!regs->regs[MIPS64_EF_R29] && !regs->regs[MIPS64_EF_CP0_EPC]) { error(INFO, "registers not collected for cpu %d\n", cpu); return; } fprintf(ofp, " R0: %016lx R1: %016lx R2: %016lx\n" " R3: %016lx R4: %016lx R5: %016lx\n" " R6: %016lx R7: %016lx R8: %016lx\n" " R9: %016lx R10: %016lx R11: %016lx\n" " R12: %016lx R13: %016lx R14: %016lx\n" " R15: %016lx R16: %016lx R17: %016lx\n" " R18: %016lx R19: %016lx R20: %016lx\n" " R21: %016lx R22: %016lx R23: %016lx\n" " R24: %016lx R25: %016lx R26: %016lx\n" " R27: %016lx R28: %016lx R29: %016lx\n" " R30: %016lx R31: %016lx\n" " LO: %016lx HI: %016lx\n" " EPC: %016lx BADVADDR: %016lx\n" " STATUS: %016lx CAUSE: %016lx\n", regs->regs[MIPS64_EF_R0], regs->regs[MIPS64_EF_R0 + 1], regs->regs[MIPS64_EF_R0 + 2], regs->regs[MIPS64_EF_R0 + 3], regs->regs[MIPS64_EF_R0 + 4], regs->regs[MIPS64_EF_R0 + 5], regs->regs[MIPS64_EF_R0 + 6], regs->regs[MIPS64_EF_R0 + 7], regs->regs[MIPS64_EF_R0 + 8], regs->regs[MIPS64_EF_R0 + 9], regs->regs[MIPS64_EF_R0 + 10], regs->regs[MIPS64_EF_R0 + 11], regs->regs[MIPS64_EF_R0 + 12], regs->regs[MIPS64_EF_R0 + 13], regs->regs[MIPS64_EF_R0 + 14], regs->regs[MIPS64_EF_R0 + 15], regs->regs[MIPS64_EF_R0 + 16], regs->regs[MIPS64_EF_R0 + 17], regs->regs[MIPS64_EF_R0 + 18], regs->regs[MIPS64_EF_R0 + 19], regs->regs[MIPS64_EF_R0 + 20], regs->regs[MIPS64_EF_R0 + 21], regs->regs[MIPS64_EF_R0 + 22], regs->regs[MIPS64_EF_R0 + 23], regs->regs[MIPS64_EF_R0 + 24], regs->regs[MIPS64_EF_R0 + 25], regs->regs[MIPS64_EF_R0 + 26], regs->regs[MIPS64_EF_R0 + 27], regs->regs[MIPS64_EF_R0 + 28], regs->regs[MIPS64_EF_R0 + 29], regs->regs[MIPS64_EF_R0 + 30], regs->regs[MIPS64_EF_R0 + 31], regs->regs[MIPS64_EF_LO], regs->regs[MIPS64_EF_HI], regs->regs[MIPS64_EF_CP0_EPC], regs->regs[MIPS64_EF_CP0_BADVADDR], regs->regs[MIPS64_EF_CP0_STATUS], regs->regs[MIPS64_EF_CP0_CAUSE]); } #else /* !MIPS64 */ #include "defs.h" void mips64_display_regs_from_elf_notes(int cpu, FILE *ofp) { return; } #endif /* !MIPS64 */ crash-8.0.0/help.c0000644000175000017500000150400114147340161014141 0ustar troyhebetroyhebe/* help.c - core analysis suite * * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. * Copyright (C) 2002-2020 David Anderson * Copyright (C) 2002-2020 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 void reshuffle_cmdlist(void); static int sort_command_name(const void *, const void *); static void display_commands(void); static void display_copying_info(void); static void display_warranty_info(void); static void display_output_info(void); static void display_input_info(void); static void display_README(void); static char *gnu_public_license[]; static char *gnu_public_license_v3[]; static char *version_info[]; static char *output_info[]; static char *input_info[]; static char *README[]; static void dump_registers(void); #define GPLv2 2 #define GPLv3 3 #if defined(GDB_5_3) || defined(GDB_6_0) || defined(GDB_6_1) static int GPL_version = GPLv2; #else static int GPL_version = GPLv3; #endif static char *program_usage_info[] = { "", "USAGE:", "", " crash [OPTION]... NAMELIST MEMORY-IMAGE[@ADDRESS] (dumpfile form)", " crash [OPTION]... [NAMELIST] (live system form)", "", "OPTIONS:", "", " NAMELIST", " This is a pathname to an uncompressed kernel image (a vmlinux", " file), or a Xen hypervisor image (a xen-syms file) which has", " been compiled with the \"-g\" option. If using the dumpfile form,", " a vmlinux file may be compressed in either gzip or bzip2 formats.", "", " MEMORY-IMAGE", " A kernel core dump file created by the netdump, diskdump, LKCD", " kdump, xendump or kvmdump facilities.", "", " If a MEMORY-IMAGE argument is not entered, the session will be", " invoked on the live system, which typically requires root privileges", " because of the device file used to access system RAM. By default, ", " /dev/crash will be used if it exists. If it does not exist, then ", " /dev/mem will be used; but if the kernel has been configured with ", " CONFIG_STRICT_DEVMEM, then /proc/kcore will be used. It is permissible", " to explicitly enter /dev/crash, /dev/mem or /proc/kcore.", "", " An @ADDRESS value must be appended to the MEMORY-IMAGE if the dumpfile", " is a raw RAM dumpfile that has no header information describing the file", " contents. Multiple MEMORY-IMAGE@ADDRESS ordered pairs may be entered,", " with each dumpfile containing a contiguous block of RAM, where the ADDRESS", " value is the physical start address of the block expressed in hexadecimal.", " The physical address value(s) will be used to create a temporary ELF header", " in /var/tmp, which will only exist during the crash session. If a raw RAM", " dumpfile represents a live memory source, such as that specified by the QEMU", " mem-path argument of a memory-backend-file object, then \"live:\" must be", " prepended to the MEMORY-IMAGE name.", "", " mapfile", " If the NAMELIST file is not the same kernel that is running", " (live system form), or the kernel that was running when the system", " crashed (dumpfile form), then the System.map file of the original ", " kernel should be entered on the command line.", "", " -h [option]", " --help [option]", " Without an option argument, display a crash usage help message.", " If the option argument is a crash command name, the help page", " for that command is displayed. If it is the string \"input\", a", " page describing the various crash command line input options is", " displayed. If it is the string \"output\", a page describing command", " line output options is displayed. If it is the string \"all\", then", " all of the possible help messages are displayed. After the help", " message is displayed, crash exits.", "", " -s ", " Silently proceed directly to the \"crash>\" prompt without displaying", " any version, GPL, or crash initialization data during startup, and by", " default, runtime command output is not passed to any scrolling command.", "", " -i file", " Execute the command(s) contained in \"file\" prior to displaying ", " the \"crash>\" prompt for interactive user input.", "", " -d num ", " Set the internal debug level. The higher the number, the more", " debugging data will be printed when crash initializes and runs.", "", " -S ", " Use /boot/System.map as the mapfile.", "", " -e vi | emacs", " Set the readline(3) command line editing mode to \"vi\" or \"emacs\". ", " The default editing mode is \"vi\".", "", " -f ", " Force the usage of a compressed vmlinux file if its original", " name does not start with \"vmlinux\".", "", " -k ", " Indicate that the NAMELIST file is an LKCD \"Kerntypes\" debuginfo file.", "", " -g [namelist]", " Determine if a vmlinux or xen-syms namelist file contains debugging data.", "", " -t ", " Display the system-crash timestamp and exit.", "", " -L ", " Attempt to lock all of its virtual address space into memory by", " calling mlockall(MCL_CURRENT|MCL_FUTURE) during initialization.", " If the system call fails, an error message will be displayed,", " but the session continues.", "", " -c tty-device", " Open the tty-device as the console used for debug messages.", "", " -p page-size", " If a processor's page size cannot be determined by the dumpfile, ", " and the processor default cannot be used, use page-size.", "", " -o filename", " Only used with the MEMORY-IMAGE@ADDRESS format for raw RAM dumpfiles,", " specifies a filename of a new ELF vmcore that will be created and used", " as the dumpfile. It will be saved to allow future use as a standalone", " vmcore, replacing the original raw RAM dumpfile.", "", " -m option=value", " --machdep option=value", " Pass an option and value pair to machine-dependent code. These", " architecture-specific option/pairs should only be required in", " very rare circumstances:", "", " X86_64:", " phys_base=", " irq_eframe_link=", " irq_stack_gap=", " max_physmem_bits=", " kernel_image_size=", " vm=orig (pre-2.6.11 virtual memory address ranges)", " vm=2.6.11 (2.6.11 and later virtual memory address ranges)", " vm=xen (Xen kernel virtual memory address ranges)", " vm=xen-rhel4 (RHEL4 Xen kernel virtual address ranges)", " vm=5level (5-level page tables)", " page_offset=", " PPC64:", " vm=orig", " vm=2.6.14 (4-level page tables)", " IA64:", " phys_start=", " init_stack_size=", " vm=4l (4-level page tables)", " ARM:", " phys_base=", " ARM64:", " phys_offset=", " kimage_voffset=", " max_physmem_bits=", " vabits_actual=", " X86:", " page_offset=", "", " -x ", " Automatically load extension modules from a particular directory.", " The directory is determined by the following order of precedence:", "", " (1) the directory specified in the CRASH_EXTENSIONS shell ", " environment variable", " (2) /usr/lib64/crash/extensions (64-bit architectures)", " (3) /usr/lib/crash/extensions (32-bit architectures)", " (4) the ./extensions subdirectory of the current directory", "", " --active", " Track only the active task on each cpu.", "", " --buildinfo", " Display the crash binary's build date, the user ID of the builder,", " the hostname of the machine where the build was done, the target", " architecture, the version number, and the compiler version.", "", " --memory_module modname", " Use the modname as an alternative kernel module to the crash.ko", " module that creates the /dev/crash device.", "", " --memory_device device", " Use device as an alternative device to the /dev/crash, /dev/mem", " or /proc/kcore devices.", "", " --log dumpfile", " Dump the contents of the kernel log buffer. A kernel namelist", " argument is not necessary, but the dumpfile must contain the", " VMCOREINFO data taken from the original /proc/vmcore ELF header.", "", " --no_kallsyms", " Do not use kallsyms-generated symbol information contained within", " kernel module object files.", "", " --no_modules", " Do not access or display any kernel module related information.", "", " --no_ikconfig", " Do not attempt to read configuration data that was built into", " kernels configured with CONFIG_IKCONFIG.", "", " --no_data_debug", " Do not verify the validity of all structure member offsets and", " structure sizes that it uses.", "", " --no_kmem_cache", " Do not initialize the kernel's slab cache infrastructure, and", " commands that use kmem_cache-related data will not work.", "", " --no_elf_notes", " Do not use the registers from the ELF NT_PRSTATUS notes saved", " in a compressed kdump header for backtraces.", "", " --kmem_cache_delay", " Delay the initialization of the kernel's slab cache infrastructure", " until it is required by a run-time command.", "", " --readnow", " Pass this flag to the embedded gdb module, which will override", " the two-stage strategy that it uses for reading symbol tables", " from the NAMELIST. If module symbol tables are loaded during", " runtime with the \"mod\" command, the same override will occur.", "", " --smp ", " Specify that the system being analyzed is an SMP kernel.", "", " -v", " --version", " Display the version of the crash utility, the version of the", " embedded gdb module, GPL information, and copyright notices.", "", " --cpus number", " Specify the number of cpus in the SMP system being analyzed.", "", " --osrelease dumpfile", " Display the OSRELEASE vmcoreinfo string from a kdump dumpfile", " header.", "", " --hyper", " Force the session to be that of a Xen hypervisor.", "", " --p2m_mfn pfn", " When a Xen Hypervisor or its dom0 kernel crashes, the dumpfile", " is typically analyzed with either the Xen hypervisor or the dom0", " kernel. It is also possible to analyze any of the guest domU", " kernels if the pfn_to_mfn_list_list pfn value of the guest kernel", " is passed on the command line along with its NAMELIST and the ", " dumpfile.", "", " --xen_phys_start physical-address", " Supply the base physical address of the Xen hypervisor's text", " and static data for older xendump dumpfiles that did not pass", " that information in the dumpfile header.", "", " --zero_excluded", " If the makedumpfile(8) facility has filtered a compressed kdump", " dumpfile to exclude various types of non-essential pages, or has", " marked a compressed or ELF kdump dumpfile as incomplete due to", " an ENOSPC or other error during its creation, any attempt to", " read missing pages will fail. With this flag, reads from any", " of those pages will return zero-filled memory.", "", " --no_panic", " Do not attempt to find the task that was running when the kernel", " crashed. Set the initial context to that of the \"swapper\" task", " on cpu 0.", "", " --more ", " Use /bin/more as the command output scroller, overriding the", " default of /usr/bin/less and any settings in either ./.crashrc", " or $HOME/.crashrc.", "", " --less ", " Use /usr/bin/less as the command output scroller, overriding any", " settings in either ./.crashrc or $HOME/.crashrc.", "", " --CRASHPAGER", " Use the output paging command defined in the CRASHPAGER shell", " environment variable, overriding any settings in either ./.crashrc ", " or $HOME/.crashrc.", "", " --no_scroll", " Do not pass run-time command output to any scrolling command.", "", " --no_strip", " Do not strip cloned kernel text symbol names.", "", " --no_crashrc", " Do not execute the commands in either $HOME/.crashrc or ./.crashrc.", "", " --mod directory", " When loading the debuginfo data of kernel modules with the \"mod -S\"", " command, search for their object files in directory instead of in ", " the standard location.", "", " --src directory", " Search for the kernel source code in directory instead of in the", " standard location that is compiled into the debuginfo data.", "", " --reloc size", " When analyzing live x86 kernels configured with a CONFIG_PHYSICAL_START ", " value that is larger than its CONFIG_PHYSICAL_ALIGN value, then it will", " be necessary to enter a relocation size equal to the difference between", " the two values.", "", " --hash count", " Set the number of internal hash queue heads used for list gathering", " and verification. The default count is 32768.", "", " --kaslr offset | auto", " If x86, x86_64 or s390x kernel was configured with CONFIG_RANDOMIZE_BASE,", " the offset value is equal to the difference between the symbol values ", " compiled into the vmlinux file and their relocated KASLR value. If", " set to auto, the KASLR offset value will be automatically calculated.", "", " --minimal", " Bring up a session that is restricted to the log, dis, rd, sym,", " eval, set and exit commands. This option may provide a way to", " extract some minimal/quick information from a corrupted or truncated", " dumpfile, or in situations where one of the several kernel subsystem ", " initialization routines would abort the crash session.", "", " --kvmhost [32|64]", " When examining an x86 KVM guest dumpfile, this option specifies", " that the KVM host that created the dumpfile was an x86 (32-bit)", " or an x86_64 (64-bit) machine, overriding the automatically", " determined value.", "", " --kvmio ", " override the automatically-calculated KVM guest I/O hole size.", "", " --offline [show|hide]", " Show or hide command output that is associated with offline cpus,", " overriding any settings in either ./.crashrc or $HOME/.crashrc.", "", "FILES:", "", " .crashrc", " Initialization commands. The file can be located in the user's", " HOME directory and/or the current directory. Commands found in", " the .crashrc file in the HOME directory are executed before", " those in the current directory's .crashrc file.", "", "ENVIRONMENT VARIABLES:", "", " EDITOR ", " Command input is read using readline(3). If EDITOR is set to", " emacs or vi then suitable keybindings are used. If EDITOR is", " not set, then vi is used. This can be overridden by \"set vi\" or", " \"set emacs\" commands located in a .crashrc file, or by entering", " \"-e emacs\" on the crash command line.", "", " CRASHPAGER", " If CRASHPAGER is set, its value is used as the name of the program", " to which command output will be sent. If not, then command output", " output is sent to \"/usr/bin/less -E -X\" by default.", "", " CRASH_MODULE_PATH", " Specifies an alternative directory tree to search for kernel", " module object files.", "", " CRASH_EXTENSIONS", " Specifies a directory containing extension modules that will be", " loaded automatically if the -x command line option is used.", "", NULL }; void program_usage(int form) { if (form == SHORT_FORM) { fprintf(fp, "\nUsage:\n\n"); fprintf(fp, "%s\n%s\n", program_usage_info[3], program_usage_info[4]); fprintf(fp, "\nEnter \"%s -h\" for details.\n", pc->program_name); clean_exit(1); } else { FILE *scroll; char *scroll_command; char **p; if ((scroll_command = setup_scroll_command()) && (scroll = popen(scroll_command, "w"))) fp = scroll; else scroll = NULL; for (p = program_usage_info; *p; p++) { fprintf(fp, *p, pc->program_name); fprintf(fp, "\n"); } fflush(fp); if (scroll) pclose(scroll); clean_exit(0); } } /* * Get an updated count of commands for subsequent help menu display, * reshuffling the deck if this is the first time or if something's changed. */ void help_init(void) { struct command_table_entry *cp; struct extension_table *ext; for (pc->ncmds = 0, cp = pc->cmd_table; cp->name; cp++) { if (!(cp->flags & HIDDEN_COMMAND)) pc->ncmds++; } for (ext = extension_table; ext; ext = ext->next) { for (cp = ext->command_table; cp->name; cp++) { if (!(cp->flags & (CLEANUP|HIDDEN_COMMAND))) pc->ncmds++; } } if (!pc->cmdlist) { pc->cmdlistsz = pc->ncmds; if ((pc->cmdlist = (char **) malloc(sizeof(char *) * pc->cmdlistsz)) == NULL) error(FATAL, "cannot malloc command list space\n"); } else if (pc->ncmds > pc->cmdlistsz) { pc->cmdlistsz = pc->ncmds; if ((pc->cmdlist = (char **)realloc(pc->cmdlist, sizeof(char *) * pc->cmdlistsz)) == NULL) error(FATAL, "cannot realloc command list space\n"); } reshuffle_cmdlist(); } /* * If the command list is modified during runtime, re-shuffle the list * for proper help menu display. */ static void reshuffle_cmdlist(void) { int i, cnt; struct command_table_entry *cp; struct extension_table *ext; for (i = 0; i < pc->cmdlistsz; i++) pc->cmdlist[i] = NULL; for (cnt = 0, cp = pc->cmd_table; cp->name; cp++) { if (!(cp->flags & HIDDEN_COMMAND)) pc->cmdlist[cnt++] = cp->name; } for (ext = extension_table; ext; ext = ext->next) { for (cp = ext->command_table; cp->name; cp++) { if (!(cp->flags & (CLEANUP|HIDDEN_COMMAND))) pc->cmdlist[cnt++] = cp->name; } } if (cnt > pc->cmdlistsz) error(FATAL, "help table malfunction!\n"); qsort((void *)pc->cmdlist, (size_t)cnt, sizeof(char *), sort_command_name); } /* * The help list is in alphabetical order, with exception of the "q" command, * which has historically always been the last command in the list. */ static int sort_command_name(const void *name1, const void *name2) { char **s1, **s2; s1 = (char **)name1; s2 = (char **)name2; if (STREQ(*s1, "q")) return 1; return strcmp(*s1, *s2); } /* * Get help for a command, to dump an internal table, or the GNU public * license copying/warranty information. */ void cmd_help(void) { int c; int oflag; oflag = 0; while ((c = getopt(argcnt, args, "efNDdmM:ngcaBbHhkKsvVoptTzLOr")) != EOF) { switch(c) { case 'e': dump_extension_table(VERBOSE); return; case 'f': dump_filesys_table(VERBOSE); return; case 'n': case 'D': dumpfile_memory(DUMPFILE_MEM_DUMP); return; case 'd': dump_dev_table(); return; case 'M': dump_machdep_table(stol(optarg, FAULT_ON_ERROR, NULL)); return; case 'm': dump_machdep_table(0); return; case 'g': dump_gdb_data(); return; case 'N': dump_net_table(); return; case 'a': dump_alias_data(); return; case 'b': dump_shared_bufs(); return; case 'B': dump_build_data(); return; case 'c': dump_numargs_cache(); return; case 'H': dump_hash_table(VERBOSE); return; case 'h': dump_hash_table(!VERBOSE); return; case 'k': dump_kernel_table(!VERBOSE); return; case 'K': dump_kernel_table(VERBOSE); return; case 's': dump_symbol_table(); return; case 'V': dump_vm_table(VERBOSE); return; case 'v': dump_vm_table(!VERBOSE); return; case 'O': dump_offset_table(NULL, TRUE); return; case 'o': oflag = TRUE; break; case 'T': dump_task_table(VERBOSE); return; case 't': dump_task_table(!VERBOSE); return; case 'p': dump_program_context(); return; case 'z': fprintf(fp, "help options:\n"); fprintf(fp, " -a - alias data\n"); fprintf(fp, " -b - shared buffer data\n"); fprintf(fp, " -B - build data\n"); fprintf(fp, " -c - numargs cache\n"); fprintf(fp, " -d - device table\n"); fprintf(fp, " -D - dumpfile contents/statistics\n"); fprintf(fp, " -e - extension table data\n"); fprintf(fp, " -f - filesys table\n"); fprintf(fp, " -g - gdb data\n"); fprintf(fp, " -h - hash_table data\n"); fprintf(fp, " -H - hash_table data (verbose)\n"); fprintf(fp, " -k - kernel_table\n"); fprintf(fp, " -K - kernel_table (verbose)\n"); fprintf(fp, " -L - LKCD page cache environment\n"); fprintf(fp, " -M machine specific\n"); fprintf(fp, " -m - machdep_table\n"); fprintf(fp, " -N - net_table\n"); fprintf(fp, " -n - dumpfile contents/statistics\n"); fprintf(fp, " -o - offset_table and size_table\n"); fprintf(fp, " -p - program_context\n"); fprintf(fp, " -r - dump registers from dumpfile header\n"); fprintf(fp, " -s - symbol table data\n"); fprintf(fp, " -t - task_table\n"); fprintf(fp, " -T - task_table plus context_array\n"); fprintf(fp, " -v - vm_table\n"); fprintf(fp, " -V - vm_table (verbose)\n"); fprintf(fp, " -z - help options\n"); return; case 'L': dumpfile_memory(DUMPFILE_ENVIRONMENT); return; case 'r': dump_registers(); return; default: argerrs++; break; } } if (argerrs) cmd_usage(pc->curcmd, COMPLETE_HELP); if (!args[optind]) { if (oflag) dump_offset_table(NULL, FALSE); else display_help_screen(""); return; } do { if (oflag) dump_offset_table(args[optind], FALSE); else cmd_usage(args[optind], COMPLETE_HELP|MUST_HELP); optind++; } while (args[optind]); } static void dump_registers(void) { if (pc->flags2 & QEMU_MEM_DUMP_ELF) { dump_registers_for_qemu_mem_dump(); return; } else if (DISKDUMP_DUMPFILE()) { dump_registers_for_compressed_kdump(); return; } else if (NETDUMP_DUMPFILE() || KDUMP_DUMPFILE()) { dump_registers_for_elf_dumpfiles(); return; } else if (VMSS_DUMPFILE()) { dump_registers_for_vmss_dump(); return; } error(FATAL, "-r option not supported on %s\n", ACTIVE() ? "a live system" : "this dumpfile type"); } /* * Format and display the help menu. */ void display_help_screen(char *indent) { int i, j, rows; char **namep; help_init(); fprintf(fp, "\n%s", indent); rows = (pc->ncmds + (HELP_COLUMNS-1)) / HELP_COLUMNS; for (i = 0; i < rows; i++) { namep = &pc->cmdlist[i]; for (j = 0; j < HELP_COLUMNS; j++) { fprintf(fp,"%-15s", *namep); namep += rows; if ((namep - pc->cmdlist) >= pc->ncmds) break; } fprintf(fp,"\n%s", indent); } fprintf(fp, "\n%s%s version: %-6s gdb version: %s\n", indent, pc->program_name, pc->program_version, pc->gdb_version); fprintf(fp, "%sFor help on any command above, enter \"help \".\n", indent); fprintf(fp, "%sFor help on input options, enter \"help input\".\n", indent); fprintf(fp, "%sFor help on output options, enter \"help output\".\n", indent); #ifdef NO_LONGER_TRUE fprintf(fp, "%sFor the most recent version: " "http://www.missioncriticallinux.com/download\n\n", indent); #else fprintf(fp, "\n"); #endif } /* * Used for generating HTML pages, dump the commands in the order * they would be seen on the help menu, i.e., from left-to-right, row-by-row. * Line ends are signaled with a "BREAK" string. */ static void display_commands(void) { int i, j, rows; char **namep; help_init(); rows = (pc->ncmds + (HELP_COLUMNS-1)) / HELP_COLUMNS; for (i = 0; i < rows; i++) { namep = &pc->cmdlist[i]; for (j = 0; j < HELP_COLUMNS; j++) { fprintf(fp,"%s\n", *namep); namep += rows; if ((namep - pc->cmdlist) >= pc->ncmds) { fprintf(fp, "BREAK\n"); break; } } } } /* * Help data for a command must be formatted using the following template: "command-name", "command description line", "argument-usage line", "description...", "description...", "description...", NULL, * The first line is concatenated with the second line, and will follow the * help command's "NAME" header. * The first and third lines will also be concatenated, and will follow the * help command's "SYNOPSIS" header. If the command has no arguments, enter * a string consisting of a space, i.e., " ". * The fourth and subsequent lines will follow the help command's "DESCRIPTION" * header. * * The program name can be referenced by using the %%s format. The final * entry in each command's help data string list must be a NULL. */ char *help_foreach[] = { "foreach", "display command data for multiple tasks in the system", "[[pid | taskp | name | state | [kernel | user | gleader]] ...]\n" " command [flag] [argument]", " This command allows for an examination of various kernel data associated", " with any, or all, tasks in the system, without having to set the context", " to each targeted task.\n", " pid perform the command(s) on this PID.", " taskp perform the command(s) on task referenced by this hexadecimal", " task_struct pointer.", " name perform the command(s) on all tasks with this name. If the", " task name can be confused with a foreach command name, then", " precede the name string with a \"\\\". If the name string is", " enclosed within \"'\" characters, then the encompassed string", " must be a POSIX extended regular expression that will be used", " to match task names.", " user perform the command(s) on all user (non-kernel) threads.", " gleader perform the command(s) on all user (non-kernel) thread group leaders.", " kernel perform the command(s) on all kernel threads.", " active perform the command(s) on the active thread on each CPU.", " state perform the command(s) on all tasks in the specified state, which", " may be one of: RU, IN, UN, ST, ZO, TR, SW, DE, WA, PA, ID or NE.\n", " If none of the task-identifying arguments above are entered, the command", " will be performed on all tasks.\n", " command select one or more of the following commands to be run on the tasks", " selected, or on all tasks:\n", " bt run the \"bt\" command (optional flags: -r -t -l -e -R -f -F", " -o -s -x -d)", " vm run the \"vm\" command (optional flags: -p -v -m -R -d -x)", " task run the \"task\" command (optional flags: -R -d -x)", " files run the \"files\" command (optional flag: -c -R)", " net run the \"net\" command (optional flags: -s -S -R -d -x)", " set run the \"set\" command", " ps run the \"ps\" command (optional flags: -G -s -p -c -t -l -a", " -g -r -y)", " sig run the \"sig\" command (optional flag: -g)", " vtop run the \"vtop\" command (optional flags: -c -u -k)\n", " flag Pass this optional flag to the command selected.", " argument Pass this argument to the command selected.", " ", " A header containing the PID, task address, cpu and command name will be", " pre-pended before the command output for each selected task. Consult the", " help page of each of the command types above for details.", "\nEXAMPLES", " Display the stack traces for all tasks:\n", " %s> foreach bt", " PID: 4752 TASK: c7680000 CPU: 1 COMMAND: \"xterm\"", " #0 [c7681edc] schedule at c01135f6", " (void)", " #1 [c7681f34] schedule_timeout at c01131ff", " (24)", " #2 [c7681f64] do_select at c0132838", " (5, c7681fa4, c7681fa0)", " #3 [c7681fbc] sys_select at c0132dad", " (5, 8070300, 8070380, 0, 0)", " #4 [bffffb0c] system_call at c0109944", " EAX: 0000008e EBX: 00000005 ECX: 08070300 EDX: 08070380 ", " DS: 002b ESI: 00000000 ES: 002b EDI: 00000000 ", " SS: 002b ESP: bffffadc EBP: bffffb0c ", " CS: 0023 EIP: 402259ee ERR: 0000008e EFLAGS: 00000246 ", " ", " PID: 557 TASK: c5600000 CPU: 0 COMMAND: \"nfsd\"", " #0 [c5601f38] schedule at c01135f6", " (void)", " #1 [c5601f90] schedule_timeout at c01131ff", " (c5600000)", " #2 [c5601fb8] svc_recv at c805363a", " (c0096f40, c5602800, 7fffffff, 100, c65c9f1c)", " #3 [c5601fec] (nfsd module) at c806e303", " (c5602800, c5602800, c0096f40, 6c6e0002, 50)", " #4 [c65c9f24] kernel_thread at c010834f", " (0, 0, ext2_file_inode_operations)", " ", " PID: 824 TASK: c7c84000 CPU: 0 COMMAND: \"mingetty\"", " ...\n", " Display the task_struct structure for each \"bash\" command:\n", " %s> foreach bash task", " ...\n", " Display the open files for all tasks:\n", " %s> foreach files", " ...\n", " Display the state of tasks whose name contains a match to \"event.*\":\n", " %s> foreach 'event.*' task -R state", " PID: 99 TASK: ffff8804750d5500 CPU: 0 COMMAND: \"events/0\"", " state = 1,", " ", " PID: 100 TASK: ffff8804750d4ac0 CPU: 1 COMMAND: \"events/1\"", " state = 1,", " ", " PID: 101 TASK: ffff8804750d4080 CPU: 2 COMMAND: \"events/2\"", " state = 1,", " ...\n", " Display the stack traces for all blocked (TASK_UNINTERRUPTIBLE) tasks:\n", " %s> foreach UN bt", " PID: 428 TASK: ffff880036b6c560 CPU: 1 COMMAND: \"jbd2/dm-1-8\"", " #0 [ffff880035779a70] __schedule at ffffffff815df272", " #1 [ffff880035779b08] schedule at ffffffff815dfacf", " #2 [ffff880035779b18] io_schedule at ffffffff815dfb7f", " #3 [ffff880035779b38] sleep_on_page at ffffffff81119a4e", " #4 [ffff880035779b48] __wait_on_bit at ffffffff815e039f", " #5 [ffff880035779b98] wait_on_page_bit at ffffffff81119bb8", " #6 [ffff880035779be8] filemap_fdatawait_range at ffffffff81119ccc", " #7 [ffff880035779cd8] filemap_fdatawait at ffffffff81119d8b", " #8 [ffff880035779ce8] jbd2_journal_commit_transaction at ffffffff8123a99c", " #9 [ffff880035779e58] kjournald2 at ffffffff8123ee7b", " #10 [ffff880035779ee8] kthread at ffffffff8108fb9c", " #11 [ffff880035779f48] kernel_thread_helper at ffffffff815ebaf4", " ...\n", NULL }; char *help_ascii[] = { "ascii", "translate a hexadecimal string to ASCII", "value ...", " Translates 32-bit or 64-bit hexadecimal values to ASCII. If no argument", " is entered, an ASCII chart is displayed.", "\nEXAMPLES", " Translate the hexadecimal value of 0x62696c2f7273752f to ASCII:", "\n %s> ascii 62696c2f7273752f", " 62696c2f7273752f: /usr/lib", "\n Display an ASCII chart:", "\n %s> ascii", " ", " 0 1 2 3 4 5 6 7", " +-------------------------------", " 0 | NUL DLE SP 0 @ P ' p", " 1 | SOH DC1 ! 1 A Q a q", " 2 | STX DC2 \" 2 B R b r", " 3 | ETX DC3 # 3 C S c s", " 4 | EOT DC4 $ 4 D T d t", " 5 | ENQ NAK \% 5 E U e u", " 6 | ACK SYN & 6 F V f v", " 7 | BEL ETB ` 7 G W g w", " 8 | BS CAN ( 8 H X h x", " 9 | HT EM ) 9 I Y i y", " A | LF SUB * : J Z j z", " B | VT ESC + ; K [ k {", " C | FF FS , < L \\ l |", " D | CR GS _ = M ] m }", " E | SO RS . > N ^ n ~", " F | SI US / ? O - o DEL", NULL }; char *help_quit[] = { "quit", "exit this session", " ", " Bail out of the current %s session.", "\nNOTE", " This command is equivalent to the \"exit\" command.", NULL }; char *help_exit[] = { "exit", "exit this session", " ", " Bail out of the current %s session.", "\nNOTE", " This command is equivalent to the \"q\" command.", NULL }; char *help_help[] = { "help", "get help", "[command | all] [-