2014-01-28 21:46:11 +00:00
|
|
|
/* main.c - core analysis suite
|
|
|
|
*
|
|
|
|
* Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc.
|
2016-05-04 15:50:19 +00:00
|
|
|
* Copyright (C) 2002-2016 David Anderson
|
|
|
|
* Copyright (C) 2002-2016 Red Hat, Inc. All rights reserved.
|
2014-01-28 21:46:11 +00:00
|
|
|
*
|
|
|
|
* 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"
|
|
|
|
#include "xen_hyper_defs.h"
|
|
|
|
#include <curses.h>
|
|
|
|
#include <getopt.h>
|
|
|
|
#include <sys/prctl.h>
|
|
|
|
|
|
|
|
static void setup_environment(int, char **);
|
|
|
|
static int is_external_command(void);
|
|
|
|
static int is_builtin_command(void);
|
|
|
|
static int is_input_file(void);
|
|
|
|
static void check_xen_hyper(void);
|
|
|
|
static void show_untrusted_files(void);
|
|
|
|
static void get_osrelease(char *);
|
|
|
|
static void get_log(char *);
|
2015-01-30 15:25:34 +00:00
|
|
|
static char *no_vmcoreinfo(const char *);
|
2014-01-28 21:46:11 +00:00
|
|
|
|
|
|
|
static struct option long_options[] = {
|
|
|
|
{"memory_module", required_argument, 0, 0},
|
|
|
|
{"memory_device", required_argument, 0, 0},
|
|
|
|
{"no_kallsyms", 0, 0, 0},
|
|
|
|
{"no_modules", 0, 0, 0},
|
|
|
|
{"help", optional_argument, 0, 'h'},
|
|
|
|
{"no_data_debug", 0, 0, 0},
|
|
|
|
{"no_crashrc", 0, 0, 0},
|
|
|
|
{"no_kmem_cache", 0, 0, 0},
|
|
|
|
{"kmem_cache_delay", 0, 0, 0},
|
|
|
|
{"readnow", 0, 0, 0},
|
|
|
|
{"smp", 0, 0, 0},
|
|
|
|
{"machdep", required_argument, 0, 0},
|
|
|
|
{"version", 0, 0, 0},
|
|
|
|
{"buildinfo", 0, 0, 0},
|
|
|
|
{"cpus", required_argument, 0, 0},
|
|
|
|
{"no_ikconfig", 0, 0, 0},
|
|
|
|
{"hyper", 0, 0, 0},
|
|
|
|
{"p2m_mfn", required_argument, 0, 0},
|
|
|
|
{"xen_phys_start", required_argument, 0, 0},
|
|
|
|
{"zero_excluded", 0, 0, 0},
|
|
|
|
{"no_panic", 0, 0, 0},
|
|
|
|
{"more", 0, 0, 0},
|
|
|
|
{"less", 0, 0, 0},
|
|
|
|
{"CRASHPAGER", 0, 0, 0},
|
|
|
|
{"no_scroll", 0, 0, 0},
|
|
|
|
{"reloc", required_argument, 0, 0},
|
2014-02-21 15:32:04 +00:00
|
|
|
{"kaslr", required_argument, 0, 0},
|
2014-01-28 21:46:11 +00:00
|
|
|
{"active", 0, 0, 0},
|
|
|
|
{"minimal", 0, 0, 0},
|
|
|
|
{"mod", required_argument, 0, 0},
|
|
|
|
{"kvmhost", required_argument, 0, 0},
|
|
|
|
{"kvmio", required_argument, 0, 0},
|
|
|
|
{"no_elf_notes", 0, 0, 0},
|
|
|
|
{"osrelease", required_argument, 0, 0},
|
|
|
|
{"log", required_argument, 0, 0},
|
|
|
|
{"hex", 0, 0, 0},
|
|
|
|
{"dec", 0, 0, 0},
|
|
|
|
{"no_strip", 0, 0, 0},
|
2014-03-13 15:56:30 +00:00
|
|
|
{"hash", required_argument, 0, 0},
|
Implement a new "offline" internal crash variable that can be set to
either "show" (the default) or "hide". When set to "hide", certain
command output associated with offline cpus will be hidden from view,
and the output will indicate that the cpu is "[OFFLINE]". The new
variable can be set during invocation on the crash command line via
the option "--offline [show|hide]". During runtime, or in a .crashrc
or other crash input file, the variable can be set by entering
"set offline [show|hide]". The commands or options that are affected
when the variable is set to "hide" are as follows:
o On X86_64 machines, the "bt -E" option will not search exception
stacks associated with offline cpus.
o On X86_64 machines, the "mach" command will append "[OFFLINE]"
to the addresses of IRQ and exception stacks associated with
offline cpus.
o On X86_64 machines, the "mach -c" command will not display the
cpuinfo_x86 data structure associated with offline cpus.
o The "help -r" option has been fixed so as to not attempt to
display register sets of offline cpus from ELF kdump vmcores,
compressed kdump vmcores, and ELF kdump clones created by
"virsh dump --memory-only".
o The "bt -c" option will not accept an offline cpu number.
o The "set -c" option will not accept an offline cpu number.
o The "irq -s" option will not display statistics associated with
offline cpus.
o The "timer" command will not display hrtimer data associated
with offline cpus.
o The "timer -r" option will not display hrtimer data associated
with offline cpus.
o The "ptov" command will append "[OFFLINE]" when translating a
per-cpu address offset to a virtal address of an offline cpu.
o The "kmem -o" option will append "[OFFLINE]" to the base per-cpu
virtual address of an offline cpu.
o The "kmem -S" option in CONFIG_SLUB kernels will not display
per-cpu data associated with offline cpus.
o When a per-cpu address reference is passed to the "struct"
command, the data structure will not be displayed for offline
cpus.
o When a per-cpu symbol and cpu reference is passed to the "p"
command, the data will not be displayed for offline cpus.
o When the "ps -[l|m]" option is passed the optional "-C [cpus]"
option, the tasks queued on offline cpus are not shown.
o The "runq" command and the "runq [-t/-m/-g/-d]" options will not
display runqueue data for offline cpus.
o The "ps" command will replace the ">" active task indicator to
a "-" for offline cpus.
The initial system information banner and the "sys" command will
display the total number of cpus as before, but will append the count
of offline cpus. Lastly, a fix has been made for the initialization
time determination of the maximum number of per-cpu objects queued
in a CONFIG_SLAB kmem_cache so as to continue checking all cpus
higher than the first offline cpu. These changes in behavior are not
dependent upon the setting of the crash "offline" variable.
(qiaonuohan@cn.fujitsu.com)
2014-10-06 19:32:37 +00:00
|
|
|
{"offline", required_argument, 0, 0},
|
2015-09-01 20:23:24 +00:00
|
|
|
{"src", required_argument, 0, 0},
|
2014-01-28 21:46:11 +00:00
|
|
|
{0, 0, 0, 0}
|
|
|
|
};
|
|
|
|
|
|
|
|
int
|
|
|
|
main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
int i, c, option_index;
|
|
|
|
char *tmpname;
|
|
|
|
|
|
|
|
setup_environment(argc, argv);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get and verify command line options.
|
|
|
|
*/
|
|
|
|
opterr = 0;
|
|
|
|
optind = 0;
|
2014-07-31 18:58:26 +00:00
|
|
|
while((c = getopt_long(argc, argv, "Lkgh::e:i:sSvc:d:tfp:m:xo:",
|
2014-01-28 21:46:11 +00:00
|
|
|
long_options, &option_index)) != -1) {
|
|
|
|
switch (c)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
if (STREQ(long_options[option_index].name,
|
|
|
|
"memory_module"))
|
|
|
|
pc->memory_module = optarg;
|
|
|
|
|
|
|
|
else if (STREQ(long_options[option_index].name,
|
|
|
|
"memory_device"))
|
|
|
|
pc->memory_device = optarg;
|
|
|
|
|
|
|
|
else if (STREQ(long_options[option_index].name,
|
|
|
|
"no_kallsyms"))
|
|
|
|
kt->flags |= NO_KALLSYMS;
|
|
|
|
|
|
|
|
else if (STREQ(long_options[option_index].name,
|
|
|
|
"no_modules"))
|
|
|
|
kt->flags |= NO_MODULE_ACCESS;
|
|
|
|
|
|
|
|
else if (STREQ(long_options[option_index].name,
|
|
|
|
"no_ikconfig"))
|
|
|
|
kt->flags |= NO_IKCONFIG;
|
|
|
|
|
|
|
|
else if (STREQ(long_options[option_index].name,
|
|
|
|
"no_data_debug"))
|
|
|
|
pc->flags &= ~DATADEBUG;
|
|
|
|
|
|
|
|
else if (STREQ(long_options[option_index].name,
|
|
|
|
"no_kmem_cache"))
|
|
|
|
vt->flags |= KMEM_CACHE_UNAVAIL;
|
|
|
|
|
|
|
|
else if (STREQ(long_options[option_index].name,
|
|
|
|
"kmem_cache_delay"))
|
|
|
|
vt->flags |= KMEM_CACHE_DELAY;
|
|
|
|
|
|
|
|
else if (STREQ(long_options[option_index].name,
|
|
|
|
"readnow"))
|
|
|
|
pc->flags |= READNOW;
|
|
|
|
|
|
|
|
else if (STREQ(long_options[option_index].name,
|
|
|
|
"smp"))
|
|
|
|
kt->flags |= SMP;
|
|
|
|
|
|
|
|
else if (STREQ(long_options[option_index].name,
|
|
|
|
"machdep")) {
|
|
|
|
for (i = 0; i < MAX_MACHDEP_ARGS; i++) {
|
|
|
|
if (machdep->cmdline_args[i])
|
|
|
|
continue;
|
|
|
|
machdep->cmdline_args[i] = optarg;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (i == MAX_MACHDEP_ARGS)
|
|
|
|
error(INFO, "option ignored: %s\n",
|
|
|
|
optarg);
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (STREQ(long_options[option_index].name,
|
|
|
|
"version")) {
|
|
|
|
pc->flags |= VERSION_QUERY;
|
|
|
|
display_version();
|
|
|
|
display_gdb_banner();
|
|
|
|
clean_exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (STREQ(long_options[option_index].name,
|
|
|
|
"buildinfo")) {
|
|
|
|
dump_build_data();
|
|
|
|
clean_exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (STREQ(long_options[option_index].name, "cpus"))
|
|
|
|
kt->cpus_override = optarg;
|
|
|
|
|
|
|
|
else if (STREQ(long_options[option_index].name, "hyper"))
|
|
|
|
pc->flags |= XEN_HYPER;
|
|
|
|
|
|
|
|
else if (STREQ(long_options[option_index].name, "p2m_mfn"))
|
|
|
|
xen_kdump_p2m_mfn(optarg);
|
|
|
|
|
|
|
|
else if (STREQ(long_options[option_index].name, "xen_phys_start"))
|
|
|
|
set_xen_phys_start(optarg);
|
|
|
|
|
|
|
|
else if (STREQ(long_options[option_index].name, "zero_excluded"))
|
|
|
|
*diskdump_flags |= ZERO_EXCLUDED;
|
|
|
|
|
|
|
|
else if (STREQ(long_options[option_index].name, "no_elf_notes")) {
|
|
|
|
if (machine_type("X86") || machine_type("X86_64"))
|
|
|
|
*diskdump_flags |= NO_ELF_NOTES;
|
|
|
|
else
|
|
|
|
error(INFO,
|
|
|
|
"--no_elf_notes is only applicable to "
|
|
|
|
"the X86 and X86_64 architectures.\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (STREQ(long_options[option_index].name, "no_panic"))
|
|
|
|
tt->flags |= PANIC_TASK_NOT_FOUND;
|
|
|
|
|
|
|
|
else if (STREQ(long_options[option_index].name, "no_strip"))
|
|
|
|
st->flags |= NO_STRIP;
|
|
|
|
|
|
|
|
else if (STREQ(long_options[option_index].name, "more")) {
|
|
|
|
if ((pc->scroll_command != SCROLL_NONE) &&
|
|
|
|
file_exists("/bin/more", NULL))
|
|
|
|
pc->scroll_command = SCROLL_MORE;
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (STREQ(long_options[option_index].name, "less")) {
|
|
|
|
if ((pc->scroll_command != SCROLL_NONE) &&
|
|
|
|
file_exists("/usr/bin/less", NULL))
|
|
|
|
pc->scroll_command = SCROLL_LESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (STREQ(long_options[option_index].name, "CRASHPAGER")) {
|
|
|
|
if ((pc->scroll_command != SCROLL_NONE) &&
|
|
|
|
CRASHPAGER_valid())
|
|
|
|
pc->scroll_command = SCROLL_CRASHPAGER;
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (STREQ(long_options[option_index].name, "no_scroll"))
|
|
|
|
pc->flags &= ~SCROLL;
|
|
|
|
|
|
|
|
else if (STREQ(long_options[option_index].name, "no_crashrc"))
|
|
|
|
pc->flags |= NOCRASHRC;
|
|
|
|
|
|
|
|
else if (STREQ(long_options[option_index].name, "active"))
|
|
|
|
tt->flags |= ACTIVE_ONLY;
|
|
|
|
|
|
|
|
else if (STREQ(long_options[option_index].name, "mod"))
|
|
|
|
kt->module_tree = optarg;
|
|
|
|
|
2014-03-13 15:56:30 +00:00
|
|
|
else if (STREQ(long_options[option_index].name, "hash")) {
|
|
|
|
if (!calculate(optarg, &pc->nr_hash_queues, NULL, 0)) {
|
|
|
|
error(INFO, "invalid --hash argument: %s\n",
|
|
|
|
optarg);
|
|
|
|
}
|
|
|
|
} else if (STREQ(long_options[option_index].name, "kaslr")) {
|
2014-02-25 20:28:47 +00:00
|
|
|
if (!machine_type("X86_64"))
|
|
|
|
error(INFO, "--kaslr only valid "
|
|
|
|
"with X86_64 machine type.\n");
|
|
|
|
else if (STREQ(optarg, "auto"))
|
|
|
|
kt->flags2 |= (RELOC_AUTO|KASLR);
|
|
|
|
else {
|
|
|
|
if (!calculate(optarg, &kt->relocate,
|
|
|
|
NULL, 0)) {
|
|
|
|
error(INFO,
|
|
|
|
"invalid --kaslr argument: %s\n",
|
|
|
|
optarg);
|
|
|
|
program_usage(SHORT_FORM);
|
|
|
|
}
|
|
|
|
kt->relocate *= -1;
|
|
|
|
kt->flags |= RELOC_SET;
|
|
|
|
kt->flags2 |= KASLR;
|
2014-02-21 15:32:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} else if (STREQ(long_options[option_index].name, "reloc")) {
|
2014-01-28 21:46:11 +00:00
|
|
|
if (!calculate(optarg, &kt->relocate, NULL, 0)) {
|
|
|
|
error(INFO, "invalid --reloc argument: %s\n",
|
|
|
|
optarg);
|
|
|
|
program_usage(SHORT_FORM);
|
2014-02-21 15:32:04 +00:00
|
|
|
}
|
2014-01-28 21:46:11 +00:00
|
|
|
kt->flags |= RELOC_SET;
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (STREQ(long_options[option_index].name, "minimal"))
|
|
|
|
pc->flags |= MINIMAL_MODE;
|
|
|
|
|
|
|
|
else if (STREQ(long_options[option_index].name, "kvmhost"))
|
|
|
|
set_kvmhost_type(optarg);
|
|
|
|
|
|
|
|
else if (STREQ(long_options[option_index].name, "kvmio"))
|
|
|
|
set_kvm_iohole(optarg);
|
|
|
|
|
|
|
|
else if (STREQ(long_options[option_index].name, "osrelease")) {
|
|
|
|
pc->flags2 |= GET_OSRELEASE;
|
|
|
|
get_osrelease(optarg);
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (STREQ(long_options[option_index].name, "log")) {
|
|
|
|
pc->flags2 |= GET_LOG;
|
|
|
|
get_log(optarg);
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (STREQ(long_options[option_index].name, "hex")) {
|
|
|
|
pc->flags2 |= RADIX_OVERRIDE;
|
|
|
|
pc->output_radix = 16;
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (STREQ(long_options[option_index].name, "dec")) {
|
|
|
|
pc->flags2 |= RADIX_OVERRIDE;
|
|
|
|
pc->output_radix = 10;
|
|
|
|
}
|
Implement a new "offline" internal crash variable that can be set to
either "show" (the default) or "hide". When set to "hide", certain
command output associated with offline cpus will be hidden from view,
and the output will indicate that the cpu is "[OFFLINE]". The new
variable can be set during invocation on the crash command line via
the option "--offline [show|hide]". During runtime, or in a .crashrc
or other crash input file, the variable can be set by entering
"set offline [show|hide]". The commands or options that are affected
when the variable is set to "hide" are as follows:
o On X86_64 machines, the "bt -E" option will not search exception
stacks associated with offline cpus.
o On X86_64 machines, the "mach" command will append "[OFFLINE]"
to the addresses of IRQ and exception stacks associated with
offline cpus.
o On X86_64 machines, the "mach -c" command will not display the
cpuinfo_x86 data structure associated with offline cpus.
o The "help -r" option has been fixed so as to not attempt to
display register sets of offline cpus from ELF kdump vmcores,
compressed kdump vmcores, and ELF kdump clones created by
"virsh dump --memory-only".
o The "bt -c" option will not accept an offline cpu number.
o The "set -c" option will not accept an offline cpu number.
o The "irq -s" option will not display statistics associated with
offline cpus.
o The "timer" command will not display hrtimer data associated
with offline cpus.
o The "timer -r" option will not display hrtimer data associated
with offline cpus.
o The "ptov" command will append "[OFFLINE]" when translating a
per-cpu address offset to a virtal address of an offline cpu.
o The "kmem -o" option will append "[OFFLINE]" to the base per-cpu
virtual address of an offline cpu.
o The "kmem -S" option in CONFIG_SLUB kernels will not display
per-cpu data associated with offline cpus.
o When a per-cpu address reference is passed to the "struct"
command, the data structure will not be displayed for offline
cpus.
o When a per-cpu symbol and cpu reference is passed to the "p"
command, the data will not be displayed for offline cpus.
o When the "ps -[l|m]" option is passed the optional "-C [cpus]"
option, the tasks queued on offline cpus are not shown.
o The "runq" command and the "runq [-t/-m/-g/-d]" options will not
display runqueue data for offline cpus.
o The "ps" command will replace the ">" active task indicator to
a "-" for offline cpus.
The initial system information banner and the "sys" command will
display the total number of cpus as before, but will append the count
of offline cpus. Lastly, a fix has been made for the initialization
time determination of the maximum number of per-cpu objects queued
in a CONFIG_SLAB kmem_cache so as to continue checking all cpus
higher than the first offline cpu. These changes in behavior are not
dependent upon the setting of the crash "offline" variable.
(qiaonuohan@cn.fujitsu.com)
2014-10-06 19:32:37 +00:00
|
|
|
|
|
|
|
else if (STREQ(long_options[option_index].name, "offline")) {
|
|
|
|
if (STREQ(optarg, "show"))
|
|
|
|
pc->flags2 &= ~OFFLINE_HIDE;
|
|
|
|
else if (STREQ(optarg, "hide"))
|
|
|
|
pc->flags2 |= OFFLINE_HIDE;
|
|
|
|
else {
|
|
|
|
error(INFO, "invalid --offline argument: %s\n", optarg);
|
|
|
|
program_usage(SHORT_FORM);
|
|
|
|
}
|
|
|
|
}
|
2014-01-28 21:46:11 +00:00
|
|
|
|
2015-09-01 20:23:24 +00:00
|
|
|
else if (STREQ(long_options[option_index].name, "src"))
|
|
|
|
kt->source_tree = optarg;
|
|
|
|
|
2014-01-28 21:46:11 +00:00
|
|
|
else {
|
|
|
|
error(INFO, "internal error: option %s unhandled\n",
|
|
|
|
long_options[option_index].name);
|
|
|
|
program_usage(SHORT_FORM);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'f':
|
|
|
|
st->flags |= FORCE_DEBUGINFO;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'g':
|
|
|
|
pc->flags |= KERNEL_DEBUG_QUERY;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'h':
|
|
|
|
/* note: long_getopt's handling of optional arguments is weak.
|
|
|
|
* To it, an optional argument must be part of the same argument
|
|
|
|
* as the flag itself (eg. --help=commands or -hcommands).
|
|
|
|
* We want to accept "--help commands" or "-h commands".
|
|
|
|
* So we must do that part ourselves.
|
|
|
|
*/
|
|
|
|
if (optarg != NULL)
|
|
|
|
cmd_usage(optarg, COMPLETE_HELP|PIPE_TO_SCROLL|MUST_HELP);
|
|
|
|
else if (argv[optind] != NULL && argv[optind][0] != '-')
|
|
|
|
cmd_usage(argv[optind++], COMPLETE_HELP|PIPE_TO_SCROLL|MUST_HELP);
|
|
|
|
else
|
|
|
|
program_usage(LONG_FORM);
|
|
|
|
clean_exit(0);
|
|
|
|
|
|
|
|
case 'k':
|
|
|
|
pc->flags |= KERNTYPES;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'e':
|
|
|
|
if (STREQ(optarg, "vi"))
|
|
|
|
pc->editing_mode = "vi";
|
|
|
|
else if (STREQ(optarg, "emacs"))
|
|
|
|
pc->editing_mode = "emacs";
|
|
|
|
else
|
|
|
|
fprintf(fp, "invalid edit mode: %s\n", optarg);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 't':
|
2015-04-28 19:14:12 +00:00
|
|
|
kt->flags2 |= GET_TIMESTAMP;
|
2014-01-28 21:46:11 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'i':
|
|
|
|
pc->input_file = optarg;
|
|
|
|
pc->flags |= CMDLINE_IFILE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'v':
|
|
|
|
pc->flags |= VERSION_QUERY;
|
|
|
|
display_version();
|
|
|
|
display_gdb_banner();
|
|
|
|
clean_exit(0);
|
|
|
|
|
|
|
|
case 's':
|
|
|
|
pc->flags |= SILENT;
|
|
|
|
pc->flags &= ~SCROLL;
|
|
|
|
// pc->scroll_command = SCROLL_NONE; (why?)
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'L':
|
|
|
|
if (mlockall(MCL_CURRENT|MCL_FUTURE) == -1)
|
|
|
|
perror("mlockall");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'S':
|
|
|
|
if (is_system_map("/boot/System.map")) {
|
|
|
|
pc->system_map = "/boot/System.map";
|
|
|
|
pc->flags |= (SYSMAP|SYSMAP_ARG);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'c':
|
|
|
|
create_console_device(optarg);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'd':
|
|
|
|
pc->debug = atol(optarg);
|
|
|
|
set_lkcd_debug(pc->debug);
|
|
|
|
set_vas_debug(pc->debug);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'p':
|
|
|
|
force_page_size(optarg);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'm':
|
|
|
|
for (i = 0; i < MAX_MACHDEP_ARGS; i++) {
|
|
|
|
if (machdep->cmdline_args[i])
|
|
|
|
continue;
|
|
|
|
machdep->cmdline_args[i] = optarg;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (i == MAX_MACHDEP_ARGS)
|
|
|
|
error(INFO, "option ignored: %s\n",
|
|
|
|
optarg);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'x':
|
|
|
|
pc->flags |= PRELOAD_EXTENSIONS;
|
|
|
|
break;
|
|
|
|
|
2014-07-31 18:58:26 +00:00
|
|
|
case 'o':
|
|
|
|
ramdump_elf_output_file(optarg);
|
|
|
|
break;
|
|
|
|
|
2014-01-28 21:46:11 +00:00
|
|
|
default:
|
|
|
|
error(INFO, "invalid option: %s\n",
|
|
|
|
argv[optind-1]);
|
|
|
|
program_usage(SHORT_FORM);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
opterr = 1;
|
|
|
|
|
|
|
|
display_version();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Take the kernel and dumpfile arguments in either order.
|
|
|
|
*/
|
|
|
|
while (argv[optind]) {
|
|
|
|
|
2014-07-31 18:58:26 +00:00
|
|
|
if (is_ramdump(argv[optind])) {
|
|
|
|
if (pc->flags & MEMORY_SOURCES) {
|
|
|
|
error(INFO,
|
|
|
|
"too many dumpfile arguments\n");
|
|
|
|
program_usage(SHORT_FORM);
|
|
|
|
}
|
2016-05-04 15:50:19 +00:00
|
|
|
|
|
|
|
if (ACTIVE()) {
|
|
|
|
pc->flags |= LIVE_RAMDUMP;
|
|
|
|
pc->readmem = read_ramdump;
|
|
|
|
pc->writemem = NULL;
|
|
|
|
optind++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2014-07-31 18:58:26 +00:00
|
|
|
pc->dumpfile = ramdump_to_elf();
|
|
|
|
if (is_kdump(pc->dumpfile, KDUMP_LOCAL)) {
|
|
|
|
pc->flags |= KDUMP;
|
|
|
|
if (is_ramdump_image())
|
|
|
|
pc->readmem = read_ramdump;
|
|
|
|
else
|
|
|
|
pc->readmem = read_kdump;
|
|
|
|
pc->writemem = NULL;
|
|
|
|
} else {
|
|
|
|
error(INFO, "malformed ELF file: %s\n",
|
|
|
|
pc->dumpfile);
|
|
|
|
program_usage(SHORT_FORM);
|
|
|
|
}
|
|
|
|
optind++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2014-01-28 21:46:11 +00:00
|
|
|
if (is_remote_daemon(argv[optind])) {
|
|
|
|
if (pc->flags & DUMPFILE_TYPES) {
|
|
|
|
error(INFO,
|
|
|
|
"too many dumpfile/memory arguments\n");
|
|
|
|
program_usage(SHORT_FORM);
|
|
|
|
}
|
|
|
|
pc->flags2 |= REMOTE_DAEMON;
|
|
|
|
optind++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (STREQ(argv[optind], "/dev/crash")) {
|
|
|
|
pc->memory_device = argv[optind];
|
|
|
|
optind++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-08-18 20:37:16 +00:00
|
|
|
if (!file_exists(argv[optind], NULL)) {
|
|
|
|
error(INFO, "%s: %s\n", argv[optind], strerror(ENOENT));
|
|
|
|
program_usage(SHORT_FORM);
|
|
|
|
} else if (is_directory(argv[optind])) {
|
|
|
|
error(INFO, "%s: not a supported file format\n",
|
|
|
|
argv[optind]);
|
|
|
|
program_usage(SHORT_FORM);
|
|
|
|
} else if (!is_readable(argv[optind]))
|
2014-01-28 21:46:11 +00:00
|
|
|
program_usage(SHORT_FORM);
|
|
|
|
|
|
|
|
if (is_kernel(argv[optind])) {
|
|
|
|
if (pc->namelist || pc->server_namelist) {
|
|
|
|
if (!select_namelist(argv[optind])) {
|
|
|
|
error(INFO,
|
|
|
|
"too many namelist arguments\n");
|
|
|
|
program_usage(SHORT_FORM);
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
pc->namelist = argv[optind];
|
|
|
|
|
|
|
|
} else if (is_compressed_kernel(argv[optind], &tmpname)) {
|
|
|
|
if (pc->namelist) {
|
|
|
|
if (!select_namelist(tmpname)) {
|
|
|
|
error(INFO,
|
|
|
|
"too many namelist arguments\n");
|
|
|
|
program_usage(SHORT_FORM);
|
|
|
|
}
|
|
|
|
if (pc->namelist_debug == tmpname) {
|
|
|
|
pc->namelist_debug_orig = argv[optind];
|
|
|
|
} else {
|
|
|
|
pc->namelist_debug_orig = pc->namelist_orig;
|
|
|
|
pc->namelist_orig = argv[optind];
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
pc->namelist = tmpname;
|
|
|
|
pc->namelist_orig = argv[optind];
|
|
|
|
}
|
|
|
|
pc->cleanup = NULL;
|
|
|
|
|
|
|
|
} else if (!(pc->flags & KERNEL_DEBUG_QUERY)) {
|
|
|
|
|
|
|
|
if (is_flattened_format(argv[optind]))
|
|
|
|
pc->flags2 |= FLAT;
|
|
|
|
|
|
|
|
if (STREQ(argv[optind], "/dev/mem")) {
|
|
|
|
if (pc->flags & MEMORY_SOURCES) {
|
|
|
|
error(INFO,
|
|
|
|
"too many dumpfile arguments\n");
|
|
|
|
program_usage(SHORT_FORM);
|
|
|
|
}
|
|
|
|
pc->flags |= DEVMEM;
|
|
|
|
pc->dumpfile = NULL;
|
|
|
|
pc->readmem = read_dev_mem;
|
|
|
|
pc->writemem = write_dev_mem;
|
|
|
|
pc->live_memsrc = argv[optind];
|
|
|
|
|
|
|
|
} else if (is_proc_kcore(argv[optind], KCORE_LOCAL)) {
|
|
|
|
if (pc->flags & MEMORY_SOURCES) {
|
|
|
|
error(INFO,
|
|
|
|
"too many dumpfile arguments\n");
|
|
|
|
program_usage(SHORT_FORM);
|
|
|
|
}
|
|
|
|
pc->flags |= PROC_KCORE;
|
|
|
|
pc->dumpfile = NULL;
|
|
|
|
pc->readmem = read_proc_kcore;
|
|
|
|
pc->writemem = write_proc_kcore;
|
|
|
|
pc->live_memsrc = argv[optind];
|
|
|
|
|
|
|
|
} else if (is_netdump(argv[optind], NETDUMP_LOCAL)) {
|
|
|
|
if (pc->flags & MEMORY_SOURCES) {
|
|
|
|
error(INFO,
|
|
|
|
"too many dumpfile arguments\n");
|
|
|
|
program_usage(SHORT_FORM);
|
|
|
|
}
|
|
|
|
pc->flags |= NETDUMP;
|
|
|
|
pc->dumpfile = argv[optind];
|
|
|
|
|
|
|
|
if (is_sadump_xen()) {
|
|
|
|
pc->readmem = read_kdump;
|
|
|
|
pc->writemem = write_kdump;
|
|
|
|
} else {
|
|
|
|
pc->readmem = read_netdump;
|
|
|
|
pc->writemem = write_netdump;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (is_kdump(argv[optind], KDUMP_LOCAL)) {
|
|
|
|
if (pc->flags & MEMORY_SOURCES) {
|
|
|
|
error(INFO,
|
|
|
|
"too many dumpfile arguments\n");
|
|
|
|
program_usage(SHORT_FORM);
|
|
|
|
}
|
|
|
|
pc->flags |= KDUMP;
|
|
|
|
pc->dumpfile = argv[optind];
|
|
|
|
pc->readmem = read_kdump;
|
|
|
|
pc->writemem = write_kdump;
|
|
|
|
|
|
|
|
} else if (is_kvmdump(argv[optind])) {
|
|
|
|
if (pc->flags & MEMORY_SOURCES) {
|
|
|
|
error(INFO,
|
|
|
|
"too many dumpfile arguments\n");
|
|
|
|
program_usage(SHORT_FORM);
|
|
|
|
}
|
|
|
|
pc->flags |= KVMDUMP;
|
|
|
|
pc->dumpfile = argv[optind];
|
|
|
|
pc->readmem = read_kvmdump;
|
|
|
|
pc->writemem = write_kvmdump;
|
|
|
|
|
|
|
|
} else if (is_kvmdump_mapfile(argv[optind])) {
|
|
|
|
if (pc->kvmdump_mapfile) {
|
|
|
|
error(INFO,
|
|
|
|
"too many KVM map file arguments\n");
|
|
|
|
program_usage(SHORT_FORM);
|
|
|
|
}
|
|
|
|
pc->kvmdump_mapfile = argv[optind];
|
|
|
|
|
|
|
|
} else if (is_xendump(argv[optind])) {
|
|
|
|
if (pc->flags & MEMORY_SOURCES) {
|
|
|
|
error(INFO,
|
|
|
|
"too many dumpfile arguments\n");
|
|
|
|
program_usage(SHORT_FORM);
|
|
|
|
}
|
|
|
|
pc->flags |= XENDUMP;
|
|
|
|
pc->dumpfile = argv[optind];
|
|
|
|
pc->readmem = read_xendump;
|
|
|
|
pc->writemem = write_xendump;
|
|
|
|
|
|
|
|
} else if (is_system_map(argv[optind])) {
|
|
|
|
pc->system_map = argv[optind];
|
|
|
|
pc->flags |= (SYSMAP|SYSMAP_ARG);
|
|
|
|
|
|
|
|
} else if (is_diskdump(argv[optind])) {
|
|
|
|
if ((pc->flags & MEMORY_SOURCES) &&
|
|
|
|
(!dumpfile_is_split())) {
|
|
|
|
error(INFO,
|
|
|
|
"too many dumpfile arguments\n");
|
|
|
|
program_usage(SHORT_FORM);
|
|
|
|
}
|
|
|
|
pc->flags |= DISKDUMP;
|
|
|
|
pc->dumpfile = argv[optind];
|
|
|
|
pc->readmem = read_diskdump;
|
|
|
|
pc->writemem = write_diskdump;
|
|
|
|
|
|
|
|
} else if (is_lkcd_compressed_dump(argv[optind])) {
|
|
|
|
if (pc->flags & MEMORY_SOURCES) {
|
|
|
|
error(INFO,
|
|
|
|
"too many dumpfile arguments\n");
|
|
|
|
program_usage(SHORT_FORM);
|
|
|
|
}
|
|
|
|
pc->flags |= LKCD;
|
|
|
|
pc->dumpfile = argv[optind];
|
|
|
|
pc->readmem = read_lkcd_dumpfile;
|
|
|
|
pc->writemem = write_lkcd_dumpfile;
|
|
|
|
|
|
|
|
} else if (is_mclx_compressed_dump(argv[optind])) {
|
|
|
|
if (pc->flags & MEMORY_SOURCES) {
|
|
|
|
error(INFO,
|
|
|
|
"too many dumpfile arguments\n");
|
|
|
|
program_usage(SHORT_FORM);
|
|
|
|
}
|
|
|
|
pc->flags |= MCLXCD;
|
|
|
|
pc->dumpfile = argv[optind];
|
|
|
|
pc->readmem = read_mclx_dumpfile;
|
|
|
|
pc->writemem = write_mclx_dumpfile;
|
|
|
|
|
|
|
|
} else if (is_s390_dump(argv[optind])) {
|
|
|
|
if (pc->flags & MEMORY_SOURCES) {
|
|
|
|
error(INFO,
|
|
|
|
"too many dumpfile arguments\n");
|
|
|
|
program_usage(SHORT_FORM);
|
|
|
|
}
|
|
|
|
pc->flags |= S390D;
|
|
|
|
pc->dumpfile = argv[optind];
|
|
|
|
pc->readmem = read_s390_dumpfile;
|
|
|
|
pc->writemem = write_s390_dumpfile;
|
|
|
|
|
|
|
|
} else if (is_sadump(argv[optind])) {
|
|
|
|
if ((pc->flags & MEMORY_SOURCES) &&
|
|
|
|
!sadump_is_diskset()) {
|
|
|
|
error(INFO,
|
|
|
|
"too many dumpfile arguments\n");
|
|
|
|
program_usage(SHORT_FORM);
|
|
|
|
}
|
|
|
|
pc->flags |= SADUMP;
|
|
|
|
pc->dumpfile = argv[optind];
|
|
|
|
pc->readmem = read_sadump;
|
|
|
|
pc->writemem = write_sadump;
|
|
|
|
|
2015-02-05 19:40:44 +00:00
|
|
|
} else if (is_vmware_vmss(argv[optind])) {
|
|
|
|
if (pc->flags & MEMORY_SOURCES) {
|
|
|
|
error(INFO,
|
|
|
|
"too many dumpfile arguments\n");
|
|
|
|
program_usage(SHORT_FORM);
|
|
|
|
}
|
|
|
|
pc->flags |= VMWARE_VMSS;
|
|
|
|
pc->dumpfile = argv[optind];
|
|
|
|
pc->readmem = read_vmware_vmss;
|
|
|
|
pc->writemem = write_vmware_vmss;
|
|
|
|
|
2014-01-28 21:46:11 +00:00
|
|
|
} else {
|
|
|
|
error(INFO,
|
|
|
|
"%s: not a supported file format\n",
|
|
|
|
argv[optind]);
|
|
|
|
program_usage(SHORT_FORM);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
optind++;
|
|
|
|
}
|
|
|
|
|
|
|
|
check_xen_hyper();
|
|
|
|
|
|
|
|
if (setjmp(pc->main_loop_env))
|
|
|
|
clean_exit(1);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize various subsystems.
|
|
|
|
*/
|
|
|
|
fd_init();
|
|
|
|
buf_init();
|
|
|
|
cmdline_init();
|
|
|
|
mem_init();
|
|
|
|
hq_init();
|
|
|
|
machdep_init(PRE_SYMTAB);
|
|
|
|
symtab_init();
|
|
|
|
paravirt_init();
|
|
|
|
machdep_init(PRE_GDB);
|
|
|
|
datatype_init();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* gdb_main_loop() modifies "command_loop_hook" to point to the
|
|
|
|
* main_loop() function below, and then calls gdb's main() function.
|
|
|
|
* After gdb initializes itself, it calls back to main_loop().
|
|
|
|
*/
|
|
|
|
gdb_main_loop(argc, argv);
|
|
|
|
|
|
|
|
clean_exit(0);
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This routine is called from above, but also will be re-entered
|
|
|
|
* as part of gdb's SIGINT handling. Since GDB_INIT and RUNTIME
|
|
|
|
* will be set on re-entrancy, the initialization routines won't
|
|
|
|
* be called. This can be avoided by always making gdb ignore SIGINT.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
main_loop(void)
|
|
|
|
{
|
|
|
|
if (pc->flags2 & ERASEINFO_DATA)
|
|
|
|
error(WARNING, "\n%s:\n "
|
|
|
|
"Kernel data has been erased from this dumpfile. This may "
|
|
|
|
"cause\n the crash session to fail entirely, may "
|
|
|
|
"cause commands to fail,\n or may result in "
|
Added recognition of the new DUMP_DH_COMPRESSED_INCOMPLETE flag in
the header of compressed kdumps, and the new DUMP_ELF_INCOMPLETE flag
in the header of ELF kdumps. If the makedumpfile(8) facility fails
to complete the creation of compressed or ELF kdump vmcore files
due to ENOSPC or other error, it will mark the vmcore as incomplete.
If either flag is set, the crash utility will issue a warning that
the dumpfile is known to be incomplete during initialization, just
prior to the system banner display. When reads are attempted on
missing data, a read error will be returned. As an alternative,
zero-filled data will be returned if the "--zero_excluded" command
line flag is used, or the "zero_excluded" runtime variable is set
to "on". In either case, the read errors or zero-filled memory
may cause the crash session to fail entirely, cause commands to
fail, or may result in other unpredictable runtime behavior.
(anderson@redhat.com, zhouwj-fnst@cn.fujitsu.com)
2014-10-30 14:42:38 +00:00
|
|
|
"unpredictable\n runtime behavior.\n",
|
|
|
|
pc->dumpfile);
|
|
|
|
|
2015-08-04 15:50:02 +00:00
|
|
|
if (pc->flags2 & INCOMPLETE_DUMP) {
|
Added recognition of the new DUMP_DH_COMPRESSED_INCOMPLETE flag in
the header of compressed kdumps, and the new DUMP_ELF_INCOMPLETE flag
in the header of ELF kdumps. If the makedumpfile(8) facility fails
to complete the creation of compressed or ELF kdump vmcore files
due to ENOSPC or other error, it will mark the vmcore as incomplete.
If either flag is set, the crash utility will issue a warning that
the dumpfile is known to be incomplete during initialization, just
prior to the system banner display. When reads are attempted on
missing data, a read error will be returned. As an alternative,
zero-filled data will be returned if the "--zero_excluded" command
line flag is used, or the "zero_excluded" runtime variable is set
to "on". In either case, the read errors or zero-filled memory
may cause the crash session to fail entirely, cause commands to
fail, or may result in other unpredictable runtime behavior.
(anderson@redhat.com, zhouwj-fnst@cn.fujitsu.com)
2014-10-30 14:42:38 +00:00
|
|
|
error(WARNING, "\n%s:\n "
|
|
|
|
"This dumpfile is incomplete. This may cause the crash session"
|
|
|
|
"\n to fail entirely, may cause commands to fail, or may"
|
|
|
|
" result in\n unpredictable runtime behavior.\n",
|
2014-01-28 21:46:11 +00:00
|
|
|
pc->dumpfile);
|
2015-08-04 15:50:02 +00:00
|
|
|
if (!(*diskdump_flags & ZERO_EXCLUDED))
|
|
|
|
fprintf(fp,
|
|
|
|
" NOTE: This dumpfile may be analyzed with the --zero_excluded command\n"
|
|
|
|
" line option, in which case any read requests from missing pages\n"
|
|
|
|
" will return zero-filled memory.\n");
|
|
|
|
}
|
2014-01-28 21:46:11 +00:00
|
|
|
|
2015-10-29 18:43:45 +00:00
|
|
|
if (pc->flags2 & EXCLUDED_VMEMMAP) {
|
|
|
|
error(WARNING, "\n%s:\n "
|
|
|
|
"This dumpfile is incomplete because the page structures associated\n"
|
|
|
|
" with excluded pages may also be excluded. This may cause the crash\n"
|
|
|
|
" session to fail entirely, may cause commands to fail (most notably\n"
|
|
|
|
" the \"kmem\" command), or may result in unpredictable runtime behavior.\n",
|
|
|
|
pc->dumpfile);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2014-01-28 21:46:11 +00:00
|
|
|
if (!(pc->flags & GDB_INIT)) {
|
|
|
|
gdb_session_init();
|
|
|
|
show_untrusted_files();
|
|
|
|
kdump_backup_region_init();
|
|
|
|
if (XEN_HYPER_MODE()) {
|
|
|
|
#ifdef XEN_HYPERVISOR_ARCH
|
|
|
|
machdep_init(POST_GDB);
|
|
|
|
xen_hyper_init();
|
|
|
|
machdep_init(POST_INIT);
|
|
|
|
#else
|
|
|
|
error(FATAL, XEN_HYPERVISOR_NOT_SUPPORTED);
|
|
|
|
#endif
|
|
|
|
} else if (!(pc->flags & MINIMAL_MODE)) {
|
|
|
|
read_in_kernel_config(IKCFG_INIT);
|
|
|
|
kernel_init();
|
|
|
|
machdep_init(POST_GDB);
|
|
|
|
vm_init();
|
|
|
|
machdep_init(POST_VM);
|
|
|
|
module_init();
|
|
|
|
help_init();
|
|
|
|
task_init();
|
|
|
|
vfs_init();
|
|
|
|
net_init();
|
|
|
|
dev_init();
|
|
|
|
machdep_init(POST_INIT);
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
SIGACTION(SIGINT, restart, &pc->sigaction, NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Display system statistics and current context.
|
|
|
|
*/
|
|
|
|
if (!(pc->flags & SILENT) && !(pc->flags & RUNTIME)) {
|
|
|
|
if (XEN_HYPER_MODE()) {
|
|
|
|
#ifdef XEN_HYPERVISOR_ARCH
|
|
|
|
xen_hyper_display_sys_stats();
|
|
|
|
xen_hyper_show_vcpu_context(XEN_HYPER_VCPU_LAST_CONTEXT());
|
|
|
|
fprintf(fp, "\n");
|
|
|
|
#else
|
|
|
|
error(FATAL, XEN_HYPERVISOR_NOT_SUPPORTED);
|
|
|
|
#endif
|
|
|
|
} else if (!(pc->flags & MINIMAL_MODE)) {
|
|
|
|
display_sys_stats();
|
|
|
|
show_context(CURRENT_CONTEXT());
|
|
|
|
fprintf(fp, "\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pc->flags & MINIMAL_MODE)
|
|
|
|
error(NOTE,
|
|
|
|
"minimal mode commands: log, dis, rd, sym, eval, set, extend and exit\n\n");
|
|
|
|
|
|
|
|
pc->flags |= RUNTIME;
|
|
|
|
|
|
|
|
if (pc->flags & PRELOAD_EXTENSIONS)
|
|
|
|
preload_extensions();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return here if a non-recoverable error occurs
|
|
|
|
* during command execution.
|
|
|
|
*/
|
|
|
|
if (setjmp(pc->main_loop_env)) {
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* process_command_line() reads, parses and stores input command lines
|
|
|
|
* in the global args[] array. exec_command() figures out what to
|
|
|
|
* do with the parsed line.
|
|
|
|
*/
|
|
|
|
while (TRUE) {
|
|
|
|
process_command_line();
|
|
|
|
exec_command();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Most of the time args[0] simply contains the name string of a command
|
|
|
|
* found in the global command_table[]. Special consideration is done for
|
|
|
|
* dealing with input files, "known" external commands, and built-in commands.
|
|
|
|
* If none of the above apply, the args[0] string is checked against the
|
|
|
|
* known list of structure, union and typedef names, and if found, passed
|
|
|
|
* on to cmd_struct(), cmd_union() or cmd_whatis().
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
exec_command(void)
|
|
|
|
{
|
|
|
|
struct command_table_entry *ct;
|
|
|
|
struct args_input_file args_ifile;
|
|
|
|
|
|
|
|
if (args[0] && (args[0][0] == '\\') && args[0][1]) {
|
|
|
|
shift_string_left(args[0], 1);
|
|
|
|
shift_string_left(pc->orig_line, 1);
|
|
|
|
pc->curcmd_flags |= NO_MODIFY;
|
|
|
|
}
|
|
|
|
|
|
|
|
reattempt:
|
|
|
|
if (!args[0])
|
|
|
|
return;
|
|
|
|
|
|
|
|
optind = argerrs = 0;
|
|
|
|
|
|
|
|
if ((ct = get_command_table_entry(args[0]))) {
|
|
|
|
if (ct->flags & REFRESH_TASK_TABLE) {
|
|
|
|
if (XEN_HYPER_MODE()) {
|
|
|
|
#ifdef XEN_HYPERVISOR_ARCH
|
|
|
|
xen_hyper_refresh_domain_context_space();
|
|
|
|
xen_hyper_refresh_vcpu_context_space();
|
|
|
|
#else
|
|
|
|
error(FATAL, XEN_HYPERVISOR_NOT_SUPPORTED);
|
|
|
|
#endif
|
|
|
|
} else if (!(pc->flags & MINIMAL_MODE)) {
|
|
|
|
tt->refresh_task_table();
|
|
|
|
sort_context_array();
|
2014-09-11 15:31:14 +00:00
|
|
|
sort_tgid_array();
|
2014-01-28 21:46:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!STREQ(pc->curcmd, pc->program_name))
|
|
|
|
pc->lastcmd = pc->curcmd;
|
|
|
|
pc->curcmd = ct->name;
|
|
|
|
pc->cmdgencur++;
|
|
|
|
|
|
|
|
if (is_args_input_file(ct, &args_ifile))
|
|
|
|
exec_args_input_file(ct, &args_ifile);
|
|
|
|
else
|
|
|
|
(*ct->func)();
|
|
|
|
|
|
|
|
pc->lastcmd = pc->curcmd;
|
|
|
|
pc->curcmd = pc->program_name;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_input_file())
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (is_external_command())
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (is_builtin_command())
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (is_datatype_command())
|
|
|
|
goto reattempt;
|
|
|
|
|
|
|
|
if (STRNEQ(args[0], "#") || STRNEQ(args[0], "//"))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!(pc->flags & MINIMAL_MODE) &&
|
|
|
|
is_gdb_command(TRUE, FAULT_ON_ERROR))
|
|
|
|
goto reattempt;
|
|
|
|
|
|
|
|
if (REMOTE() && remote_execute())
|
|
|
|
return;
|
|
|
|
|
|
|
|
pc->curcmd = pc->program_name;
|
|
|
|
|
|
|
|
if (pc->flags & MINIMAL_MODE)
|
|
|
|
error(INFO,
|
|
|
|
"%s: command not available in minimal mode\n"
|
|
|
|
"NOTE: minimal mode commands: log, dis, rd, sym, eval, set, extend and exit\n",
|
|
|
|
args[0]);
|
|
|
|
else
|
|
|
|
error(INFO, "command not found: %s\n", args[0]);
|
|
|
|
|
|
|
|
if (pc->curcmd_flags & REPEAT)
|
|
|
|
pc->curcmd_flags &= ~REPEAT;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the command_table structure associated with a command name.
|
|
|
|
*/
|
|
|
|
struct command_table_entry *
|
|
|
|
get_command_table_entry(char *name)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct command_table_entry *cp;
|
|
|
|
struct extension_table *ext;
|
|
|
|
|
|
|
|
if (pc->flags2 & GDB_CMD_MODE) {
|
|
|
|
if (STREQ(name, "crash")) {
|
|
|
|
if (argcnt == 1)
|
|
|
|
error(FATAL,
|
|
|
|
"a crash command must follow "
|
|
|
|
"the \"crash\" directive\n");
|
|
|
|
for (i = 1; i <= argcnt; i++)
|
|
|
|
args[i-1] = args[i];
|
|
|
|
argcnt--;
|
|
|
|
name = args[0];
|
|
|
|
} else
|
|
|
|
name = "gdb";
|
|
|
|
}
|
|
|
|
|
|
|
|
for (cp = pc->cmd_table; cp->name; cp++) {
|
|
|
|
if (STREQ(cp->name, name)) {
|
|
|
|
if (!(pc->flags & MINIMAL_MODE) || (cp->flags & MINIMAL))
|
|
|
|
return cp;
|
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (ext = extension_table; ext; ext = ext->next) {
|
|
|
|
for (cp = ext->command_table; cp->name; cp++) {
|
|
|
|
if (STREQ(cp->name, name)) {
|
|
|
|
if (!(pc->flags & MINIMAL_MODE) || (cp->flags & MINIMAL))
|
|
|
|
return cp;
|
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
is_input_file(void)
|
|
|
|
{
|
|
|
|
if (STREQ(args[0], "<")) {
|
|
|
|
exec_input_file();
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
is_builtin_command(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct remote_file remote_file, *rfp;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* cmd_test() is used strictly for debugging -- but not advertised
|
|
|
|
* in the help menu.
|
|
|
|
*/
|
|
|
|
if (STREQ(args[0], "test")) {
|
|
|
|
pc->curcmd = "test";
|
|
|
|
cmd_test();
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (STREQ(args[0], "save")) {
|
|
|
|
pc->curcmd = "save";
|
|
|
|
rfp = &remote_file;
|
|
|
|
BZERO(rfp, sizeof(struct remote_file));
|
|
|
|
rfp->flags |= REMOTE_VERBOSE;
|
|
|
|
for (i = 1; i < argcnt; i++) {
|
|
|
|
rfp->filename = args[i];
|
|
|
|
get_remote_file(rfp);
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Pure laziness -- to avoid having to type the exclamation point at the
|
|
|
|
* beginning of the line.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
is_external_command(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
char *cmd;
|
|
|
|
char command[BUFSIZE];
|
|
|
|
|
|
|
|
cmd = args[0];
|
|
|
|
|
|
|
|
if (STREQ(cmd, "vi") ||
|
|
|
|
STREQ(cmd, "pwd") ||
|
|
|
|
STREQ(cmd, "grep") ||
|
|
|
|
STREQ(cmd, "cat") ||
|
|
|
|
STREQ(cmd, "more") ||
|
|
|
|
STREQ(cmd, "less") ||
|
|
|
|
STREQ(cmd, "echo") ||
|
|
|
|
STREQ(cmd, "ls")) {
|
|
|
|
sprintf(command, "%s", cmd);
|
|
|
|
for (i = 1; i < argcnt; i++) {
|
|
|
|
strcat(command, " ");
|
|
|
|
if (strstr(args[i], " ")) {
|
|
|
|
strcat(command, "\"");
|
|
|
|
strcat(command, args[i]);
|
|
|
|
strcat(command, "\"");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
strcat(command, args[i]);
|
|
|
|
}
|
|
|
|
if (system(command) == -1)
|
|
|
|
perror(command);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
cmd_quit(void)
|
|
|
|
{
|
|
|
|
if (REMOTE())
|
|
|
|
remote_exit();
|
|
|
|
|
|
|
|
clean_exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
cmd_mach(void)
|
|
|
|
{
|
|
|
|
machdep->cmd_mach();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
setup_environment(int argc, char **argv)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
char *p1;
|
|
|
|
char buf[BUFSIZE];
|
|
|
|
char homerc[BUFSIZE];
|
|
|
|
char localrc[BUFSIZE];
|
|
|
|
FILE *afp;
|
|
|
|
char *program;
|
|
|
|
|
|
|
|
program = argv[0];
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Program output typically goes via "fprintf(fp, ...)", but the
|
|
|
|
* contents of fp are modified on the fly to handle redirection
|
|
|
|
* to pipes or output files.
|
|
|
|
*/
|
|
|
|
fp = stdout;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Start populating the program_context structure. It's used so
|
|
|
|
* frequently that "pc" has been declared globally to point to the
|
|
|
|
* "program_context" structure.
|
|
|
|
*/
|
|
|
|
pc->program_name = (char *)basename(program);
|
|
|
|
pc->program_path = program;
|
|
|
|
pc->program_version = build_version;
|
|
|
|
pc->program_pid = (ulong)getpid();
|
|
|
|
pc->curcmd = pc->program_name;
|
|
|
|
pc->flags = (HASH|SCROLL);
|
|
|
|
pc->flags |= DATADEBUG; /* default until unnecessary */
|
|
|
|
pc->confd = -2;
|
|
|
|
pc->machine_type = MACHINE_TYPE;
|
|
|
|
pc->readmem = read_dev_mem; /* defaults until argv[] is parsed */
|
|
|
|
pc->writemem = write_dev_mem;
|
2015-01-30 15:25:34 +00:00
|
|
|
pc->read_vmcoreinfo = no_vmcoreinfo;
|
2014-01-28 21:46:11 +00:00
|
|
|
pc->memory_module = NULL;
|
|
|
|
pc->memory_device = MEMORY_DRIVER_DEVICE;
|
|
|
|
machdep->bits = sizeof(long) * 8;
|
|
|
|
machdep->verify_paddr = generic_verify_paddr;
|
|
|
|
machdep->get_kvaddr_ranges = generic_get_kvaddr_ranges;
|
|
|
|
pc->redhat_debug_loc = DEFAULT_REDHAT_DEBUG_LOCATION;
|
|
|
|
pc->cmdgencur = 0;
|
|
|
|
pc->cmd_table = linux_command_table;
|
|
|
|
kt->BUG_bytes = -1;
|
|
|
|
kt->flags |= PRE_KERNEL_INIT;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set up to perform a clean_exit() upon parent death.
|
|
|
|
*/
|
|
|
|
SIGACTION(SIGUSR2, restart, &pc->sigaction, NULL);
|
|
|
|
prctl(PR_SET_PDEATHSIG, SIGUSR2);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get gdb version before initializing it since this might be one
|
|
|
|
* of the short-hand commands that need it without running gdb.
|
|
|
|
*/
|
|
|
|
get_gdb_version();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set up the default scrolling behavior for terminal output.
|
|
|
|
*/
|
|
|
|
if (isatty(fileno(stdout))) {
|
|
|
|
if (CRASHPAGER_valid()) {
|
|
|
|
pc->flags |= SCROLL;
|
|
|
|
pc->scroll_command = SCROLL_CRASHPAGER;
|
|
|
|
} else if (file_exists("/usr/bin/less", NULL)) {
|
|
|
|
pc->flags |= SCROLL;
|
|
|
|
pc->scroll_command = SCROLL_LESS;
|
|
|
|
} else if (file_exists("/bin/more", NULL)) {
|
|
|
|
pc->flags |= SCROLL;
|
|
|
|
pc->scroll_command = SCROLL_MORE;
|
|
|
|
} else {
|
|
|
|
pc->scroll_command = SCROLL_NONE;
|
|
|
|
pc->flags &= ~SCROLL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Setup the readline command line editing mode based upon the
|
|
|
|
* following order:
|
|
|
|
*
|
|
|
|
* (1) EDITOR environment variable
|
|
|
|
* (2) overridden by any .crashrc entry: "set vi" or "set emacs"
|
|
|
|
* (3) RL_VI_MODE if not set anywhere else
|
|
|
|
*/
|
|
|
|
|
|
|
|
pc->flags |= READLINE;
|
|
|
|
pc->editing_mode = "no_mode";
|
|
|
|
|
|
|
|
if ((p1 = getenv("EDITOR"))) {
|
|
|
|
if (strstr(p1, "vi"))
|
|
|
|
pc->editing_mode = "vi";
|
|
|
|
if (strstr(p1, "emacs"))
|
|
|
|
pc->editing_mode = "emacs";
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Resolve $HOME .rc file first, then the one in the local directory.
|
|
|
|
* Note that only "set" and "alias" commands are done at this time.
|
|
|
|
*/
|
|
|
|
for (i = 1; i < argc; i++)
|
|
|
|
if (STREQ(argv[i], "--no_crashrc"))
|
|
|
|
pc->flags |= NOCRASHRC;
|
|
|
|
|
|
|
|
alias_init(NULL);
|
|
|
|
|
|
|
|
if ((p1 = getenv("HOME"))) {
|
|
|
|
if ((pc->home = (char *)malloc(strlen(p1)+1)) == NULL) {
|
|
|
|
error(INFO, "home directory malloc: %s\n",
|
|
|
|
strerror(errno));
|
|
|
|
pc->home = "(unknown)";
|
|
|
|
} else
|
|
|
|
strcpy(pc->home, p1);
|
|
|
|
sprintf(homerc, "%s/.%src", pc->home, pc->program_name);
|
|
|
|
if (!(pc->flags & NOCRASHRC) && file_exists(homerc, NULL)) {
|
|
|
|
if ((afp = fopen(homerc, "r")) == NULL)
|
|
|
|
error(INFO, "cannot open %s: %s\n",
|
|
|
|
homerc, strerror(errno));
|
|
|
|
else if (untrusted_file(afp, homerc))
|
|
|
|
fclose(afp);
|
|
|
|
else {
|
|
|
|
while (fgets(buf, BUFSIZE, afp))
|
|
|
|
resolve_rc_cmd(buf, ALIAS_RCHOME);
|
|
|
|
fclose(afp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sprintf(localrc, ".%src", pc->program_name);
|
|
|
|
if (!same_file(homerc, localrc) &&
|
|
|
|
!(pc->flags & NOCRASHRC) && file_exists(localrc, NULL)) {
|
|
|
|
if ((afp = fopen(localrc, "r")) == NULL)
|
|
|
|
error(INFO, "cannot open %s: %s\n",
|
|
|
|
localrc, strerror(errno));
|
|
|
|
else if (untrusted_file(afp, localrc))
|
|
|
|
fclose(afp);
|
|
|
|
else {
|
|
|
|
while (fgets(buf, BUFSIZE, afp))
|
|
|
|
resolve_rc_cmd(buf, ALIAS_RCLOCAL);
|
|
|
|
fclose(afp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (STREQ(pc->editing_mode, "no_mode"))
|
|
|
|
pc->editing_mode = "vi";
|
|
|
|
|
|
|
|
machdep_init(SETUP_ENV);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* "help -p" output
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
dump_program_context(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int others = 0;
|
|
|
|
char *p1;
|
|
|
|
char buf[BUFSIZE];
|
|
|
|
char buf2[BUFSIZE];
|
|
|
|
|
|
|
|
fprintf(fp, " program_name: %s\n", pc->program_name);
|
|
|
|
fprintf(fp, " program_path: %s\n", pc->program_path);
|
|
|
|
fprintf(fp, " program_version: %s\n", pc->program_version);
|
|
|
|
fprintf(fp, " gdb_version: %s\n", pc->gdb_version);
|
|
|
|
fprintf(fp, " program_pid: %ld\n", pc->program_pid);
|
|
|
|
fprintf(fp, " prompt: \"%s\"\n", pc->prompt);
|
|
|
|
fprintf(fp, " flags: %llx ", pc->flags);
|
|
|
|
|
|
|
|
if (pc->flags)
|
|
|
|
sprintf(buf, "(");
|
|
|
|
if (pc->flags & RUNTIME)
|
|
|
|
sprintf(&buf[strlen(buf)], "%sRUNTIME", others++ ? "|" : "");
|
|
|
|
if (pc->flags & LIVE_SYSTEM)
|
|
|
|
sprintf(&buf[strlen(buf)], "%sLIVE_SYSTEM",
|
|
|
|
others++ ? "|" : "");
|
|
|
|
if (pc->flags & TTY)
|
|
|
|
sprintf(&buf[strlen(buf)], "%sTTY", others++ ? "|" : "");
|
|
|
|
if (pc->flags & IN_FOREACH)
|
|
|
|
sprintf(&buf[strlen(buf)], "%sIN_FOREACH", others++ ? "|" : "");
|
|
|
|
if (pc->flags & MFD_RDWR)
|
|
|
|
sprintf(&buf[strlen(buf)], "%sMFD_RDWR", others++ ? "|" : "");
|
|
|
|
if (pc->flags & KVMDUMP)
|
|
|
|
sprintf(&buf[strlen(buf)], "%sKVMDUMP", others++ ? "|" : "");
|
|
|
|
if (pc->flags & SILENT)
|
|
|
|
sprintf(&buf[strlen(buf)], "%sSILENT", others++ ? "|" : "");
|
|
|
|
if (pc->flags & HASH)
|
|
|
|
sprintf(&buf[strlen(buf)], "%sHASH", others++ ? "|" : "");
|
|
|
|
if (pc->flags & SCROLL)
|
|
|
|
sprintf(&buf[strlen(buf)], "%sSCROLL", others++ ? "|" : "");
|
|
|
|
if (pc->flags & NO_CONSOLE)
|
|
|
|
sprintf(&buf[strlen(buf)], "%sNO_CONSOLE", others++ ? "|" : "");
|
|
|
|
if (pc->flags & MCLXCD)
|
|
|
|
sprintf(&buf[strlen(buf)], "%sMCLXCD", others++ ? "|" : "");
|
|
|
|
if (pc->flags & RUNTIME_IFILE)
|
|
|
|
sprintf(&buf[strlen(buf)], "%sRUNTIME_IFILE",
|
|
|
|
others++ ? "|" : "");
|
|
|
|
if (pc->flags & CMDLINE_IFILE)
|
|
|
|
sprintf(&buf[strlen(buf)], "%sCMDLINE_IFILE",
|
|
|
|
others++ ? "|" : "");
|
|
|
|
if (pc->flags & DROP_CORE)
|
|
|
|
sprintf(&buf[strlen(buf)], "%sDROP_CORE", others++ ? "|" : "");
|
|
|
|
if (pc->flags & LKCD)
|
|
|
|
sprintf(&buf[strlen(buf)], "%sLKCD", others++ ? "|" : "");
|
|
|
|
if (pc->flags & GDB_INIT)
|
|
|
|
sprintf(&buf[strlen(buf)], "%sGDB_INIT", others++ ? "|" : "");
|
|
|
|
if (pc->flags & IN_GDB)
|
|
|
|
sprintf(&buf[strlen(buf)], "%sIN_GDB", others++ ? "|" : "");
|
|
|
|
if (pc->flags & RCHOME_IFILE)
|
|
|
|
sprintf(&buf[strlen(buf)], "%sRCHOME_IFILE",
|
|
|
|
others++ ? "|" : "");
|
|
|
|
if (pc->flags & RCLOCAL_IFILE)
|
|
|
|
sprintf(&buf[strlen(buf)], "%sRCLOCAL_IFILE",
|
|
|
|
others++ ? "|" : "");
|
|
|
|
if (pc->flags & READLINE)
|
|
|
|
sprintf(&buf[strlen(buf)], "%sREADLINE", others++ ? "|" : "");
|
|
|
|
if (pc->flags & _SIGINT_)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%s_SIGINT_", others++ ? "|" : "");
|
|
|
|
if (pc->flags & IN_RESTART)
|
|
|
|
sprintf(&buf[strlen(buf)], "%sIN_RESTART", others++ ? "|" : "");
|
|
|
|
if (pc->flags & KERNEL_DEBUG_QUERY)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sKERNEL_DEBUG_QUERY", others++ ? "|" : "");
|
|
|
|
if (pc->flags & DEVMEM)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sDEVMEM", others++ ? "|" : "");
|
|
|
|
if (pc->flags & MEMMOD)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sMEMMOD", others++ ? "|" : "");
|
|
|
|
if (pc->flags & MODPRELOAD)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sMODPRELOAD", others++ ? "|" : "");
|
|
|
|
if (pc->flags & REM_LIVE_SYSTEM)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sREM_LIVE_SYSTEM", others++ ? "|" : "");
|
2016-05-04 15:50:19 +00:00
|
|
|
if (pc->flags2 & MEMSRC_LOCAL)
|
2014-01-28 21:46:11 +00:00
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sMEMSRC_LOCAL", others++ ? "|" : "");
|
|
|
|
if (pc->flags & NAMELIST_LOCAL)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sNAMELIST_LOCAL", others++ ? "|" : "");
|
|
|
|
if (pc->flags & DUMPFILE_SAVED)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sDUMPFILE_SAVED", others++ ? "|" : "");
|
|
|
|
if (pc->flags & NAMELIST_SAVED)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sNAMELIST_SAVED", others++ ? "|" : "");
|
|
|
|
if (pc->flags & UNLINK_NAMELIST)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sUNLINK_NAMELIST", others++ ? "|" : "");
|
|
|
|
if (pc->flags & NAMELIST_UNLINKED)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sNAMELIST_UNLINKED", others++ ? "|" : "");
|
|
|
|
if (pc->flags & REM_MCLXCD)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sREM_MCLXCD", others++ ? "|" : "");
|
|
|
|
if (pc->flags & REM_LKCD)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sREM_LKCD", others++ ? "|" : "");
|
|
|
|
if (pc->flags & NAMELIST_NO_GZIP)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sNAMELIST_NO_GZIP", others++ ? "|" : "");
|
|
|
|
if (pc->flags & UNLINK_MODULES)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sUNLINK_MODULES", others++ ? "|" : "");
|
|
|
|
if (pc->flags & S390D)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sS390D", others++ ? "|" : "");
|
|
|
|
if (pc->flags & REM_S390D)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sREM_S390D", others++ ? "|" : "");
|
|
|
|
if (pc->flags & NETDUMP)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sNETDUMP", others++ ? "|" : "");
|
|
|
|
if (pc->flags & XENDUMP)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sXENDUMP", others++ ? "|" : "");
|
|
|
|
if (pc->flags & KDUMP)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sKDUMP", others++ ? "|" : "");
|
|
|
|
if (pc->flags & SADUMP)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sSADUMP", others++ ? "|" : "");
|
|
|
|
if (pc->flags & SYSRQ)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sSYSRQ", others++ ? "|" : "");
|
|
|
|
if (pc->flags & REM_NETDUMP)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sREM_NETDUMP", others++ ? "|" : "");
|
|
|
|
if (pc->flags & DISKDUMP)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sDISKDUMP", others++ ? "|" : "");
|
|
|
|
if (pc->flags & SYSMAP)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sSYSMAP", others++ ? "|" : "");
|
|
|
|
if (pc->flags & SYSMAP_ARG)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sSYSMAP_ARG", others++ ? "|" : "");
|
|
|
|
if (pc->flags & DATADEBUG)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sDATADEBUG", others++ ? "|" : "");
|
|
|
|
if (pc->flags & FINDKERNEL)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sFINDKERNEL", others++ ? "|" : "");
|
|
|
|
if (pc->flags & VERSION_QUERY)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sVERSION_QUERY", others++ ? "|" : "");
|
|
|
|
if (pc->flags & READNOW)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sREADNOW", others++ ? "|" : "");
|
|
|
|
if (pc->flags & NOCRASHRC)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sNOCRASHRC", others++ ? "|" : "");
|
|
|
|
if (pc->flags & INIT_IFILE)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sINIT_IFILE", others++ ? "|" : "");
|
|
|
|
if (pc->flags & XEN_HYPER)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sXEN_HYPER", others++ ? "|" : "");
|
|
|
|
if (pc->flags & XEN_CORE)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sXEN_CORE", others++ ? "|" : "");
|
|
|
|
if (pc->flags & PLEASE_WAIT)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sPLEASE_WAIT", others++ ? "|" : "");
|
|
|
|
if (pc->flags & IFILE_ERROR)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sIFILE_ERROR", others++ ? "|" : "");
|
|
|
|
if (pc->flags & MINIMAL_MODE)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sMINIMAL_MODE", others++ ? "|" : "");
|
|
|
|
if (pc->flags & CRASHBUILTIN)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sCRASHBUILTIN", others++ ? "|" : "");
|
|
|
|
if (pc->flags & PRELOAD_EXTENSIONS)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sPRELOAD_EXTENSIONS", others++ ? "|" : "");
|
|
|
|
if (pc->flags & PROC_KCORE)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sPROC_KCORE", others++ ? "|" : "");
|
|
|
|
|
|
|
|
if (pc->flags)
|
|
|
|
strcat(buf, ")");
|
|
|
|
|
|
|
|
if (strlen(buf)) {
|
|
|
|
if (strlen(buf) > 46) {
|
|
|
|
sprintf(buf2, "\n%s\n",
|
|
|
|
mkstring(buf, 80, CENTER|LJUST, NULL));
|
|
|
|
if (strlen(buf2) <= 82)
|
|
|
|
fprintf(fp, "%s", buf2);
|
|
|
|
else {
|
|
|
|
for (i = strlen(buf2)-1; i; i--) {
|
|
|
|
if ((buf2[i] == '|') && (i < 80))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
strcpy(buf, buf2);
|
|
|
|
buf[i+1] = NULLCHAR;
|
|
|
|
fprintf(fp, "%s\n %s", buf, &buf2[i+1]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
fprintf(fp, "%s\n", buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
others = 0;
|
|
|
|
fprintf(fp, " flags2: %llx (", pc->flags2);
|
|
|
|
if (pc->flags2 & FLAT)
|
|
|
|
fprintf(fp, "%sFLAT", others++ ? "|" : "");
|
|
|
|
if (pc->flags2 & ELF_NOTES)
|
|
|
|
fprintf(fp, "%sELF_NOTES", others++ ? "|" : "");
|
|
|
|
if (pc->flags2 & GET_OSRELEASE)
|
|
|
|
fprintf(fp, "%sGET_OSRELEASE", others++ ? "|" : "");
|
|
|
|
if (pc->flags2 & REMOTE_DAEMON)
|
|
|
|
fprintf(fp, "%sREMOTE_DAEMON", others++ ? "|" : "");
|
|
|
|
if (pc->flags2 & LIVE_DUMP)
|
|
|
|
fprintf(fp, "%sLIVE_DUMP", others++ ? "|" : "");
|
|
|
|
if (pc->flags2 & RADIX_OVERRIDE)
|
|
|
|
fprintf(fp, "%sRADIX_OVERRIDE", others++ ? "|" : "");
|
2014-11-13 19:40:54 +00:00
|
|
|
if (pc->flags2 & QEMU_MEM_DUMP_ELF)
|
|
|
|
fprintf(fp, "%sQEMU_MEM_DUMP_ELF", others++ ? "|" : "");
|
|
|
|
if (pc->flags2 & QEMU_MEM_DUMP_COMPRESSED)
|
|
|
|
fprintf(fp, "%sQEMU_MEM_DUMP_COMPRESSED", others++ ? "|" : "");
|
2014-01-28 21:46:11 +00:00
|
|
|
if (pc->flags2 & GET_LOG)
|
|
|
|
fprintf(fp, "%sGET_LOG", others++ ? "|" : "");
|
|
|
|
if (pc->flags2 & VMCOREINFO)
|
|
|
|
fprintf(fp, "%sVMCOREINFO", others++ ? "|" : "");
|
|
|
|
if (pc->flags2 & ALLOW_FP)
|
|
|
|
fprintf(fp, "%sALLOW_FP", others++ ? "|" : "");
|
2014-07-31 18:58:26 +00:00
|
|
|
if (pc->flags2 & RAMDUMP)
|
|
|
|
fprintf(fp, "%sRAMDUMP", others++ ? "|" : "");
|
Implement a new "offline" internal crash variable that can be set to
either "show" (the default) or "hide". When set to "hide", certain
command output associated with offline cpus will be hidden from view,
and the output will indicate that the cpu is "[OFFLINE]". The new
variable can be set during invocation on the crash command line via
the option "--offline [show|hide]". During runtime, or in a .crashrc
or other crash input file, the variable can be set by entering
"set offline [show|hide]". The commands or options that are affected
when the variable is set to "hide" are as follows:
o On X86_64 machines, the "bt -E" option will not search exception
stacks associated with offline cpus.
o On X86_64 machines, the "mach" command will append "[OFFLINE]"
to the addresses of IRQ and exception stacks associated with
offline cpus.
o On X86_64 machines, the "mach -c" command will not display the
cpuinfo_x86 data structure associated with offline cpus.
o The "help -r" option has been fixed so as to not attempt to
display register sets of offline cpus from ELF kdump vmcores,
compressed kdump vmcores, and ELF kdump clones created by
"virsh dump --memory-only".
o The "bt -c" option will not accept an offline cpu number.
o The "set -c" option will not accept an offline cpu number.
o The "irq -s" option will not display statistics associated with
offline cpus.
o The "timer" command will not display hrtimer data associated
with offline cpus.
o The "timer -r" option will not display hrtimer data associated
with offline cpus.
o The "ptov" command will append "[OFFLINE]" when translating a
per-cpu address offset to a virtal address of an offline cpu.
o The "kmem -o" option will append "[OFFLINE]" to the base per-cpu
virtual address of an offline cpu.
o The "kmem -S" option in CONFIG_SLUB kernels will not display
per-cpu data associated with offline cpus.
o When a per-cpu address reference is passed to the "struct"
command, the data structure will not be displayed for offline
cpus.
o When a per-cpu symbol and cpu reference is passed to the "p"
command, the data will not be displayed for offline cpus.
o When the "ps -[l|m]" option is passed the optional "-C [cpus]"
option, the tasks queued on offline cpus are not shown.
o The "runq" command and the "runq [-t/-m/-g/-d]" options will not
display runqueue data for offline cpus.
o The "ps" command will replace the ">" active task indicator to
a "-" for offline cpus.
The initial system information banner and the "sys" command will
display the total number of cpus as before, but will append the count
of offline cpus. Lastly, a fix has been made for the initialization
time determination of the maximum number of per-cpu objects queued
in a CONFIG_SLAB kmem_cache so as to continue checking all cpus
higher than the first offline cpu. These changes in behavior are not
dependent upon the setting of the crash "offline" variable.
(qiaonuohan@cn.fujitsu.com)
2014-10-06 19:32:37 +00:00
|
|
|
if (pc->flags2 & OFFLINE_HIDE)
|
|
|
|
fprintf(fp, "%sOFFLINE_HIDE", others++ ? "|" : "");
|
Added recognition of the new DUMP_DH_COMPRESSED_INCOMPLETE flag in
the header of compressed kdumps, and the new DUMP_ELF_INCOMPLETE flag
in the header of ELF kdumps. If the makedumpfile(8) facility fails
to complete the creation of compressed or ELF kdump vmcore files
due to ENOSPC or other error, it will mark the vmcore as incomplete.
If either flag is set, the crash utility will issue a warning that
the dumpfile is known to be incomplete during initialization, just
prior to the system banner display. When reads are attempted on
missing data, a read error will be returned. As an alternative,
zero-filled data will be returned if the "--zero_excluded" command
line flag is used, or the "zero_excluded" runtime variable is set
to "on". In either case, the read errors or zero-filled memory
may cause the crash session to fail entirely, cause commands to
fail, or may result in other unpredictable runtime behavior.
(anderson@redhat.com, zhouwj-fnst@cn.fujitsu.com)
2014-10-30 14:42:38 +00:00
|
|
|
if (pc->flags2 & INCOMPLETE_DUMP)
|
|
|
|
fprintf(fp, "%sINCOMPLETE_DUMP", others++ ? "|" : "");
|
2015-08-11 14:27:04 +00:00
|
|
|
if (pc->flags2 & SNAP)
|
|
|
|
fprintf(fp, "%sSNAP", others++ ? "|" : "");
|
2015-10-29 18:43:45 +00:00
|
|
|
if (pc->flags2 & EXCLUDED_VMEMMAP)
|
|
|
|
fprintf(fp, "%sEXCLUDED_VMEMMAP", others++ ? "|" : "");
|
2014-01-28 21:46:11 +00:00
|
|
|
fprintf(fp, ")\n");
|
|
|
|
|
|
|
|
fprintf(fp, " namelist: %s\n", pc->namelist);
|
|
|
|
fprintf(fp, " dumpfile: %s\n", pc->dumpfile);
|
|
|
|
fprintf(fp, " live_memsrc: %s\n", pc->live_memsrc);
|
|
|
|
fprintf(fp, " system_map: %s\n", pc->system_map);
|
|
|
|
fprintf(fp, " namelist_debug: %s\n", pc->namelist_debug);
|
|
|
|
fprintf(fp, " debuginfo_file: %s\n", pc->debuginfo_file);
|
|
|
|
fprintf(fp, " namelist_orig: %s\n", pc->namelist_orig);
|
|
|
|
fprintf(fp, "namelist_dbg_orig: %s\n", pc->namelist_debug_orig);
|
|
|
|
fprintf(fp, " kvmdump_mapfile: %s\n", pc->kvmdump_mapfile);
|
|
|
|
fprintf(fp, " memory_module: %s\n", pc->memory_module);
|
|
|
|
fprintf(fp, " memory_device: %s\n", pc->memory_device);
|
|
|
|
fprintf(fp, " machine_type: %s\n", pc->machine_type);
|
|
|
|
fprintf(fp, " editing_mode: %s\n", pc->editing_mode);
|
|
|
|
fprintf(fp, " nfd: %d\n", pc->nfd);
|
|
|
|
fprintf(fp, " mfd: %d\n", pc->mfd);
|
|
|
|
fprintf(fp, " kfd: %d\n", pc->kfd);
|
|
|
|
fprintf(fp, " dfd: %d\n", pc->dfd);
|
|
|
|
fprintf(fp, " confd: %d\n", pc->confd);
|
|
|
|
fprintf(fp, " home: %s\n", pc->home);
|
|
|
|
fprintf(fp, " command_line: ");
|
|
|
|
if (STRNEQ(pc->command_line, args[0]))
|
|
|
|
fprintf(fp, "%s\n", concat_args(buf, 0, FALSE));
|
|
|
|
else
|
|
|
|
fprintf(fp, "%s\n", pc->command_line);
|
|
|
|
fprintf(fp, " orig_line: %s\n", pc->orig_line);
|
|
|
|
fprintf(fp, " eoc_index: %d\n", pc->eoc_index);
|
|
|
|
fprintf(fp, " readline: %lx\n", (ulong)pc->readline);
|
|
|
|
fprintf(fp, " my_tty: %s\n", pc->my_tty);
|
|
|
|
fprintf(fp, " debug: %ld\n", pc->debug);
|
|
|
|
fprintf(fp, " debug_save: %ld\n", pc->debug_save);
|
|
|
|
fprintf(fp, " console: %s\n", pc->console);
|
|
|
|
fprintf(fp, " redhat_debug_loc: %s\n", pc->redhat_debug_loc);
|
|
|
|
fprintf(fp, " pipefd[2]: %d,%d\n", pc->pipefd[0], pc->pipefd[1]);
|
|
|
|
fprintf(fp, " nullfp: %lx\n", (ulong)pc->nullfp);
|
|
|
|
fprintf(fp, " stdpipe: %lx\n", (ulong)pc->stdpipe);
|
|
|
|
fprintf(fp, " pipe: %lx\n", (ulong)pc->pipe);
|
|
|
|
fprintf(fp, " ifile: %lx\n", (ulong)pc->ifile);
|
|
|
|
fprintf(fp, " ofile: %lx\n", (ulong)pc->ofile);
|
|
|
|
fprintf(fp, " ifile_pipe: %lx\n", (ulong)pc->ifile_pipe);
|
|
|
|
fprintf(fp, " ifile_ofile: %lx\n", (ulong)pc->ifile_ofile);
|
|
|
|
fprintf(fp, " args_ifile: %lx\n", (ulong)pc->args_ifile);
|
|
|
|
fprintf(fp, " input_file: %s\n", pc->input_file);
|
|
|
|
fprintf(fp, "ifile_in_progress: %lx (", pc->ifile_in_progress);
|
|
|
|
others = 0;
|
|
|
|
if (pc->ifile_in_progress & RCHOME_IFILE)
|
|
|
|
fprintf(fp, "%sRCHOME_IFILE", others++ ? "|" : "");
|
|
|
|
if (pc->ifile_in_progress & RCLOCAL_IFILE)
|
|
|
|
fprintf(fp, "%sRCLOCAL_IFILE", others++ ? "|" : "");
|
|
|
|
if (pc->ifile_in_progress & CMDLINE_IFILE)
|
|
|
|
fprintf(fp, "%sCMDLINE_IFILE", others++ ? "|" : "");
|
|
|
|
if (pc->ifile_in_progress & RUNTIME_IFILE)
|
|
|
|
fprintf(fp, "%sRUNTIME_IFILE", others++ ? "|" : "");
|
|
|
|
fprintf(fp, ")\n");
|
|
|
|
fprintf(fp, " ifile_offset: %lld\n", (ulonglong)pc->ifile_offset);
|
|
|
|
fprintf(fp, "runtime_ifile_cmd: %s\n", pc->runtime_ifile_cmd ?
|
|
|
|
pc->runtime_ifile_cmd : "(unused)");
|
|
|
|
fprintf(fp, " scroll_command: ");
|
|
|
|
switch (pc->scroll_command)
|
|
|
|
{
|
|
|
|
case SCROLL_NONE:
|
|
|
|
fprintf(fp, "SCROLL_NONE\n");
|
|
|
|
break;
|
|
|
|
case SCROLL_LESS:
|
|
|
|
fprintf(fp, "SCROLL_LESS\n");
|
|
|
|
break;
|
|
|
|
case SCROLL_MORE:
|
|
|
|
fprintf(fp, "SCROLL_MORE\n");
|
|
|
|
break;
|
|
|
|
case SCROLL_CRASHPAGER:
|
|
|
|
fprintf(fp, "SCROLL_CRASHPAGER (%s)\n", getenv("CRASHPAGER"));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf[0] = NULLCHAR;
|
|
|
|
fprintf(fp, " redirect: %lx ", pc->redirect);
|
|
|
|
if (pc->redirect)
|
|
|
|
sprintf(buf, "(");
|
|
|
|
others = 0;
|
|
|
|
if (pc->redirect & FROM_COMMAND_LINE)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sFROM_COMMAND_LINE", others++ ? "|" : "");
|
|
|
|
if (pc->redirect & FROM_INPUT_FILE)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sFROM_INPUT_FILE", others++ ? "|" : "");
|
|
|
|
if (pc->redirect & REDIRECT_NOT_DONE)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sREDIRECT_NOT_DONE", others++ ? "|" : "");
|
|
|
|
if (pc->redirect & REDIRECT_TO_PIPE)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sREDIRECT_TO_PIPE", others++ ? "|" : "");
|
|
|
|
if (pc->redirect & REDIRECT_TO_STDPIPE)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sREDIRECT_TO_STDPIPE", others++ ? "|" : "");
|
|
|
|
if (pc->redirect & REDIRECT_TO_FILE)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sREDIRECT_TO_FILE", others++ ? "|" : "");
|
|
|
|
if (pc->redirect & REDIRECT_FAILURE)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sREDIRECT_FAILURE", others++ ? "|" : "");
|
|
|
|
if (pc->redirect & REDIRECT_SHELL_ESCAPE)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sREDIRECT_SHELL_ESCAPE", others++ ? "|" : "");
|
|
|
|
if (pc->redirect & REDIRECT_SHELL_COMMAND)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sREDIRECT_SHELL_COMMAND", others++ ? "|" : "");
|
|
|
|
if (pc->redirect & REDIRECT_PID_KNOWN)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sREDIRECT_PID_KNOWN", others++ ? "|" : "");
|
|
|
|
if (pc->redirect & REDIRECT_MULTI_PIPE)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sREDIRECT_MULTI_PIPE", others++ ? "|" : "");
|
|
|
|
if (pc->redirect)
|
|
|
|
strcat(buf, ")");
|
|
|
|
|
|
|
|
if (strlen(buf)) {
|
|
|
|
if (strlen(buf) > 54)
|
|
|
|
fprintf(fp, "\n%s\n",
|
|
|
|
mkstring(buf, 80, CENTER|LJUST, NULL));
|
|
|
|
else
|
|
|
|
fprintf(fp, "%s\n", buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pc->redirect)
|
|
|
|
fprintf(fp, "\n");
|
|
|
|
|
|
|
|
fprintf(fp, " stdpipe_pid: %d\n", pc->stdpipe_pid);
|
|
|
|
fprintf(fp, " pipe_pid: %d\n", pc->pipe_pid);
|
|
|
|
fprintf(fp, " pipe_shell_pid: %d\n", pc->pipe_shell_pid);
|
|
|
|
fprintf(fp, " pipe_command: %s\n", pc->pipe_command);
|
|
|
|
if (pc->symfile && pc->symfile2) {
|
|
|
|
fprintf(fp, " symfile: %lx (%ld)\n",
|
|
|
|
(ulong)pc->symfile, (ulong)ftell(pc->symfile));
|
|
|
|
fprintf(fp, " symfile2: %lx (%ld)\n",
|
|
|
|
(ulong)pc->symfile2, (ulong)ftell(pc->symfile2));
|
|
|
|
} else {
|
|
|
|
fprintf(fp, " symfile: %lx \n", (ulong)pc->symfile);
|
|
|
|
fprintf(fp, " symfile2: %lx \n", (ulong)pc->symfile2);
|
|
|
|
}
|
|
|
|
fprintf(fp, " tmpfile: %lx\n", (ulong)pc->tmpfile);
|
|
|
|
fprintf(fp, " saved_fp: %lx\n", (ulong)pc->saved_fp);
|
|
|
|
fprintf(fp, " tmp_fp: %lx\n", (ulong)pc->tmp_fp);
|
|
|
|
fprintf(fp, " tmpfile2: %lx\n", (ulong)pc->tmpfile2);
|
|
|
|
|
|
|
|
fprintf(fp, " cmd_table: %s\n", XEN_HYPER_MODE() ?
|
|
|
|
"xen_hyper_command_table" : "linux_command_table");
|
|
|
|
fprintf(fp, " curcmd: %s\n", pc->curcmd);
|
|
|
|
fprintf(fp, " lastcmd: %s\n", pc->lastcmd);
|
|
|
|
fprintf(fp, " cur_gdb_cmd: %d %s\n", pc->cur_gdb_cmd,
|
|
|
|
gdb_command_string(pc->cur_gdb_cmd, buf, FALSE));
|
|
|
|
fprintf(fp, " last_gdb_cmd: %d %s\n", pc->last_gdb_cmd,
|
|
|
|
gdb_command_string(pc->last_gdb_cmd, buf, FALSE));
|
|
|
|
fprintf(fp, " cur_req: %lx\n", (ulong)pc->cur_req);
|
|
|
|
fprintf(fp, " cmdgencur: %ld\n", pc->cmdgencur);
|
|
|
|
fprintf(fp, " curcmd_flags: %lx (", pc->curcmd_flags);
|
|
|
|
others = 0;
|
|
|
|
if (pc->curcmd_flags & XEN_MACHINE_ADDR)
|
|
|
|
fprintf(fp, "%sXEN_MACHINE_ADDR", others ? "|" : "");
|
|
|
|
if (pc->curcmd_flags & REPEAT)
|
|
|
|
fprintf(fp, "%sREPEAT", others ? "|" : "");
|
|
|
|
if (pc->curcmd_flags & IDLE_TASK_SHOWN)
|
|
|
|
fprintf(fp, "%sIDLE_TASK_SHOWN", others ? "|" : "");
|
|
|
|
if (pc->curcmd_flags & TASK_SPECIFIED)
|
|
|
|
fprintf(fp, "%sTASK_SPECIFIED", others ? "|" : "");
|
|
|
|
if (pc->curcmd_flags & MEMTYPE_UVADDR)
|
|
|
|
fprintf(fp, "%sMEMTYPE_UVADDR", others ? "|" : "");
|
|
|
|
if (pc->curcmd_flags & MEMTYPE_FILEADDR)
|
|
|
|
fprintf(fp, "%sMEMTYPE_FILEADDR", others ? "|" : "");
|
|
|
|
if (pc->curcmd_flags & HEADER_PRINTED)
|
|
|
|
fprintf(fp, "%sHEADER_PRINTED", others ? "|" : "");
|
|
|
|
if (pc->curcmd_flags & BAD_INSTRUCTION)
|
|
|
|
fprintf(fp, "%sBAD_INSTRUCTION", others ? "|" : "");
|
|
|
|
if (pc->curcmd_flags & UD2A_INSTRUCTION)
|
|
|
|
fprintf(fp, "%sUD2A_INSTRUCTION", others ? "|" : "");
|
|
|
|
if (pc->curcmd_flags & IRQ_IN_USE)
|
|
|
|
fprintf(fp, "%sIRQ_IN_USE", others ? "|" : "");
|
|
|
|
if (pc->curcmd_flags & IGNORE_ERRORS)
|
|
|
|
fprintf(fp, "%sIGNORE_ERRORS", others ? "|" : "");
|
|
|
|
if (pc->curcmd_flags & FROM_RCFILE)
|
|
|
|
fprintf(fp, "%sFROM_RCFILE", others ? "|" : "");
|
|
|
|
if (pc->curcmd_flags & MEMTYPE_KVADDR)
|
|
|
|
fprintf(fp, "%sMEMTYPE_KVADDR", others ? "|" : "");
|
|
|
|
if (pc->curcmd_flags & NO_MODIFY)
|
|
|
|
fprintf(fp, "%sNO_MODIFY", others ? "|" : "");
|
|
|
|
if (pc->curcmd_flags & MOD_SECTIONS)
|
|
|
|
fprintf(fp, "%sMOD_SECTIONS", others ? "|" : "");
|
|
|
|
if (pc->curcmd_flags & MOD_READNOW)
|
|
|
|
fprintf(fp, "%sMOD_READNOW", others ? "|" : "");
|
Added a new "vm -M <mm_struct>" option. When a task is exiting,
the mm_struct address pointer in its task_struct is NULL'd out, and
as a result, the "vm" command looks like this:
crash> vm
PID: 4563 TASK: ffff88049863f500 CPU: 8 COMMAND: "postgres"
MM PGD RSS TOTAL_VM
0 0 0k 0k
However, the mm_struct address can be retrieved from the task's
kernel stack and entered manually with this option, which allows the
"vm" command to attempt to dump the virtual memory data of the task.
It may, or may not, work, depending upon how far the virtual memory
deconstruction has proceeded. This option only verifies that the
address entered is from the "mm_struct" slab cache, and that
its mm_struct.mm_count is non-zero.
(qiaonuohan@cn.fujitsu.com, anderson@redhat.com)
2014-12-11 22:01:50 +00:00
|
|
|
if (pc->curcmd_flags & MM_STRUCT_FORCE)
|
|
|
|
fprintf(fp, "%sMM_STRUCT_FORCE", others ? "|" : "");
|
2015-04-22 18:40:11 +00:00
|
|
|
if (pc->curcmd_flags & CPUMASK)
|
|
|
|
fprintf(fp, "%sCPUMASK", others ? "|" : "");
|
2015-11-30 20:55:26 +00:00
|
|
|
if (pc->curcmd_flags & PARTIAL_READ_OK)
|
|
|
|
fprintf(fp, "%sPARTIAL_READ_OK", others ? "|" : "");
|
2014-01-28 21:46:11 +00:00
|
|
|
fprintf(fp, ")\n");
|
|
|
|
fprintf(fp, " curcmd_private: %llx\n", pc->curcmd_private);
|
|
|
|
fprintf(fp, " cmd_cleanup: %lx\n", (ulong)pc->cmd_cleanup);
|
|
|
|
fprintf(fp, " cmd_cleanup_arg: %lx\n", (ulong)pc->cmd_cleanup_arg);
|
|
|
|
fprintf(fp, " sigint_cnt: %d\n", pc->sigint_cnt);
|
|
|
|
fprintf(fp, " sigaction: %lx\n", (ulong)&pc->sigaction);
|
|
|
|
fprintf(fp, " gdb_sigaction: %lx\n", (ulong)&pc->gdb_sigaction);
|
|
|
|
fprintf(fp, " main_loop_env: %lx\n", (ulong)&pc->main_loop_env);
|
|
|
|
fprintf(fp, " foreach_loop_env: %lx\n", (ulong)&pc->foreach_loop_env);
|
|
|
|
fprintf(fp, "gdb_interface_env: %lx\n", (ulong)&pc->gdb_interface_env);
|
|
|
|
fprintf(fp, " termios_orig: %lx\n", (ulong)&pc->termios_orig);
|
|
|
|
fprintf(fp, " termios_raw: %lx\n", (ulong)&pc->termios_raw);
|
|
|
|
fprintf(fp, " ncmds: %d\n", pc->ncmds);
|
|
|
|
fprintf(fp, " cmdlist: %lx\n", (ulong)pc->cmdlist);
|
|
|
|
fprintf(fp, " cmdlistsz: %d\n", pc->cmdlistsz);
|
|
|
|
fprintf(fp, " output_radix: %d (%s)\n", pc->output_radix,
|
|
|
|
pc->output_radix == 16 ?
|
|
|
|
"hex" : ((pc->output_radix == 10) ? "decimal" : "???"));
|
|
|
|
|
|
|
|
fprintf(fp, " server: %s\n", pc->server);
|
|
|
|
fprintf(fp, " server_pid: %ld\n", pc->server_pid);
|
|
|
|
fprintf(fp, " port: %d\n", pc->port);
|
|
|
|
fprintf(fp, " sockfd: %d\n", pc->sockfd);
|
|
|
|
fprintf(fp, " server_memsrc: %s\n", pc->server_memsrc);
|
|
|
|
fprintf(fp, " server_namelist: %s\n", pc->server_namelist);
|
|
|
|
fprintf(fp, " rmfd: %d\n", pc->rmfd);
|
|
|
|
fprintf(fp, " rkfd: %d\n", pc->rkfd);
|
|
|
|
fprintf(fp, " rcvbufsize: %ld\n", pc->rcvbufsize);
|
|
|
|
|
|
|
|
fprintf(fp, " readmem: ");
|
|
|
|
if ((p1 = readmem_function_name()))
|
|
|
|
fprintf(fp, "%s()\n", p1);
|
|
|
|
else
|
|
|
|
fprintf(fp, "%lx\n", (ulong)pc->readmem);
|
|
|
|
|
|
|
|
fprintf(fp, " writemem: ");
|
|
|
|
if ((p1 = writemem_function_name()))
|
|
|
|
fprintf(fp, "%s()\n", p1);
|
|
|
|
else
|
|
|
|
fprintf(fp, "%lx\n", (ulong)pc->writemem);
|
|
|
|
|
|
|
|
fprintf(fp, " dumpfile memory: %d\n",
|
|
|
|
dumpfile_memory(DUMPFILE_MEM_USED));
|
|
|
|
fprintf(fp, " curext: %lx\n", (ulong)pc->curext);
|
|
|
|
fprintf(fp, " sbrk: %lx\n", (ulong)pc->sbrk);
|
|
|
|
fprintf(fp, " cleanup: %s\n", pc->cleanup);
|
|
|
|
fprintf(fp, " scope: %lx %s\n", pc->scope,
|
|
|
|
pc->scope ? "" : "(not set)");
|
2014-03-13 15:56:30 +00:00
|
|
|
fprintf(fp, " nr_hash_queues: %ld\n", pc->nr_hash_queues);
|
2014-04-17 20:14:32 +00:00
|
|
|
fprintf(fp, " read_vmcoreinfo: %lx\n", (ulong)pc->read_vmcoreinfo);
|
2014-01-28 21:46:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
char *
|
|
|
|
readmem_function_name(void)
|
|
|
|
{
|
|
|
|
if (pc->readmem == read_dev_mem)
|
|
|
|
return("read_dev_mem");
|
|
|
|
else if (pc->readmem == read_mclx_dumpfile)
|
|
|
|
return("read_mclx_dumpfile");
|
|
|
|
else if (pc->readmem == read_lkcd_dumpfile)
|
|
|
|
return("read_lkcd_dumpfile");
|
|
|
|
else if (pc->readmem == read_daemon)
|
|
|
|
return("read_daemon");
|
|
|
|
else if (pc->readmem == read_netdump)
|
|
|
|
return("read_netdump");
|
|
|
|
else if (pc->readmem == read_xendump)
|
|
|
|
return("read_xendump");
|
|
|
|
else if (pc->readmem == read_kdump)
|
|
|
|
return("read_kdump");
|
|
|
|
else if (pc->readmem == read_memory_device)
|
|
|
|
return("read_memory_device");
|
|
|
|
else if (pc->readmem == read_xendump_hyper)
|
|
|
|
return("read_xendump_hyper");
|
|
|
|
else if (pc->readmem == read_diskdump)
|
|
|
|
return("read_diskdump");
|
|
|
|
else if (pc->readmem == read_proc_kcore)
|
|
|
|
return("read_proc_kcore");
|
|
|
|
else if (pc->readmem == read_sadump)
|
|
|
|
return("read_sadump");
|
|
|
|
else if (pc->readmem == read_s390_dumpfile)
|
|
|
|
return("read_s390_dumpfile");
|
2014-07-31 18:58:26 +00:00
|
|
|
else if (pc->readmem == read_ramdump)
|
|
|
|
return("read_ramdump");
|
2015-02-05 19:40:44 +00:00
|
|
|
else if (pc->readmem == read_vmware_vmss)
|
|
|
|
return("read_vmware_vmss");
|
2014-01-28 21:46:11 +00:00
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *
|
|
|
|
writemem_function_name(void)
|
|
|
|
{
|
|
|
|
if (pc->writemem == write_dev_mem)
|
|
|
|
return("write_dev_mem");
|
|
|
|
else if (pc->writemem == write_mclx_dumpfile)
|
|
|
|
return("write_mclx_dumpfile");
|
|
|
|
else if (pc->writemem == write_lkcd_dumpfile)
|
|
|
|
return("write_lkcd_dumpfile");
|
|
|
|
else if (pc->writemem == write_daemon)
|
|
|
|
return("write_daemon");
|
|
|
|
else if (pc->writemem == write_netdump)
|
|
|
|
return("write_netdump");
|
|
|
|
else if (pc->writemem == write_xendump)
|
|
|
|
return("write_xendump");
|
|
|
|
else if (pc->writemem == write_kdump)
|
|
|
|
return("write_kdump");
|
|
|
|
else if (pc->writemem == write_memory_device)
|
|
|
|
return("write_memory_device");
|
|
|
|
// else if (pc->writemem == write_xendump_hyper)
|
|
|
|
// return("write_xendump_hyper");
|
|
|
|
else if (pc->writemem == write_diskdump)
|
|
|
|
return("write_diskdump");
|
|
|
|
else if (pc->writemem == write_proc_kcore)
|
|
|
|
return("write_proc_kcore");
|
|
|
|
else if (pc->writemem == write_sadump)
|
|
|
|
return("write_sadump");
|
|
|
|
else if (pc->writemem == write_s390_dumpfile)
|
|
|
|
return("write_s390_dumpfile");
|
2015-02-05 19:40:44 +00:00
|
|
|
else if (pc->writemem == write_vmware_vmss)
|
|
|
|
return("write_vmware_vmss");
|
2014-01-28 21:46:11 +00:00
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* "help -B" output
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
dump_build_data(void)
|
|
|
|
{
|
|
|
|
fprintf(fp, " build_command: %s\n", build_command);
|
|
|
|
fprintf(fp, " build_data: %s\n", build_data);
|
|
|
|
fprintf(fp, " build_target: %s\n", build_target);
|
|
|
|
fprintf(fp, " build_version: %s\n", build_version);
|
|
|
|
fprintf(fp, "compiler version: %s\n", compiler_version);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Perform any cleanup activity here.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
clean_exit(int status)
|
|
|
|
{
|
|
|
|
if (pc->flags & MEMMOD)
|
|
|
|
cleanup_memory_driver();
|
|
|
|
|
|
|
|
if ((pc->namelist_orig) && file_exists(pc->namelist, NULL))
|
|
|
|
unlink(pc->namelist);
|
|
|
|
if ((pc->namelist_debug_orig) && file_exists(pc->namelist_debug, NULL))
|
|
|
|
unlink(pc->namelist_debug);
|
|
|
|
if (pc->cleanup && file_exists(pc->cleanup, NULL))
|
|
|
|
unlink(pc->cleanup);
|
|
|
|
|
2014-07-31 18:58:26 +00:00
|
|
|
ramdump_cleanup();
|
2014-01-28 21:46:11 +00:00
|
|
|
exit(status);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check whether this session is for xen hypervisor analysis.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
check_xen_hyper(void)
|
|
|
|
{
|
|
|
|
if (!pc->namelist)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!XEN_HYPER_MODE()) {
|
|
|
|
if (STRNEQ(basename(pc->namelist), "xen-syms"))
|
|
|
|
pc->flags |= XEN_HYPER;
|
|
|
|
else
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef XEN_HYPERVISOR_ARCH
|
|
|
|
pc->cmd_table = xen_hyper_command_table;
|
|
|
|
if (pc->flags & XENDUMP)
|
|
|
|
pc->readmem = read_xendump_hyper;
|
|
|
|
#else
|
|
|
|
error(FATAL, XEN_HYPERVISOR_NOT_SUPPORTED);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reject untrusted .crashrc, $HOME/.crashrc,
|
|
|
|
* .gdbinit, and $HOME/.gdbinit files.
|
|
|
|
*/
|
|
|
|
static char *untrusted_file_list[4] = { 0 };
|
|
|
|
|
|
|
|
int
|
|
|
|
untrusted_file(FILE *filep, char *filename)
|
|
|
|
{
|
|
|
|
struct stat sbuf;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (filep && (fstat(fileno(filep), &sbuf) == 0) &&
|
|
|
|
(sbuf.st_uid == getuid()) && !(sbuf.st_mode & S_IWOTH))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
|
|
if (!untrusted_file_list[i]) {
|
|
|
|
untrusted_file_list[i] = strdup(filename);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
show_untrusted_files(void)
|
|
|
|
{
|
|
|
|
int i, cnt;
|
|
|
|
|
|
|
|
for (i = cnt = 0; i < 4; i++) {
|
|
|
|
if (untrusted_file_list[i]) {
|
|
|
|
error(WARNING, "not using untrusted file: \"%s\"\n",
|
|
|
|
untrusted_file_list[i]);
|
|
|
|
free(untrusted_file_list[i]);
|
|
|
|
cnt++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (cnt)
|
|
|
|
fprintf(fp, "\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If GET_OSRELEASE is still set, the OS release has been
|
|
|
|
* found and displayed.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
get_osrelease(char *dumpfile)
|
|
|
|
{
|
|
|
|
int retval = 1;
|
|
|
|
|
2015-06-16 20:40:19 +00:00
|
|
|
if (is_flattened_format(dumpfile)) {
|
|
|
|
if (pc->flags2 & GET_OSRELEASE)
|
|
|
|
retval = 0;
|
|
|
|
} else if (is_diskdump(dumpfile)) {
|
2014-01-28 21:46:11 +00:00
|
|
|
if (pc->flags2 & GET_OSRELEASE)
|
|
|
|
retval = 0;
|
|
|
|
} else if (is_kdump(dumpfile, KDUMP_LOCAL)) {
|
|
|
|
if (pc->flags2 & GET_OSRELEASE)
|
|
|
|
retval = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (retval)
|
|
|
|
fprintf(fp, "unknown\n");
|
|
|
|
|
|
|
|
clean_exit(retval);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
get_log(char *dumpfile)
|
|
|
|
{
|
|
|
|
|
|
|
|
int retval = 1;
|
|
|
|
|
|
|
|
if (is_flattened_format(dumpfile))
|
|
|
|
pc->flags2 |= FLAT;
|
|
|
|
|
|
|
|
if (is_diskdump(dumpfile)) {
|
|
|
|
if (pc->flags2 & GET_LOG)
|
|
|
|
retval = 0;
|
|
|
|
} else if (is_kdump(dumpfile, KDUMP_LOCAL)) {
|
|
|
|
if (pc->flags2 & GET_LOG)
|
|
|
|
retval = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (retval)
|
|
|
|
fprintf(fp, "%s: no VMCOREINFO data\n", dumpfile);
|
|
|
|
|
|
|
|
clean_exit(retval);
|
|
|
|
}
|
2015-01-30 15:25:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
static char *
|
|
|
|
no_vmcoreinfo(const char *unused)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|