2006-01-04 19:34:13 +00:00
|
|
|
--- crash/extensions/Makefile.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/extensions/Makefile 2005-11-08 11:38:21.000000000 -0500
|
|
|
|
@@ -0,0 +1,41 @@
|
|
|
|
+#
|
|
|
|
+# Makefile for building crash shared object extensions
|
|
|
|
+#
|
|
|
|
+# Copyright (C) 2005 David Anderson
|
|
|
|
+# Copyright (C) 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.
|
|
|
|
+#
|
|
|
|
+# To build the extension shared objects in this directory, run
|
|
|
|
+# "make extensions" from the top-level directory.
|
|
|
|
+#
|
|
|
|
+# To add a new extension object:
|
|
|
|
+#
|
|
|
|
+# - add the new source file to the EXTENSION_SOURCE_FILES list
|
|
|
|
+# in the top-level Makefile
|
|
|
|
+# - add the object file name to the EXTENSION_OBJECT_FILES list
|
|
|
|
+# in the top-level Makefile
|
|
|
|
+# - create a compile stanza below, typically using "echo.so" as
|
|
|
|
+# a base template.
|
|
|
|
+#
|
|
|
|
+
|
|
|
|
+all: link_defs $(OBJECTS)
|
|
|
|
+
|
|
|
|
+link_defs:
|
|
|
|
+ @if [ ! -f defs.h ]; then \
|
|
|
|
+ ln -s ../defs.h; fi
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+echo.so: ../defs.h echo.c
|
|
|
|
+ gcc -nostartfiles -shared -rdynamic -o echo.so echo.c -fPIC -D$(TARGET)
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+dminfo.so: ../defs.h dminfo.c
|
|
|
|
+ gcc -nostartfiles -shared -rdynamic -o dminfo.so dminfo.c -fPIC -D$(TARGET)
|
|
|
|
+
|
|
|
|
--- crash/extensions/echo.c.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/extensions/echo.c 2005-11-10 16:12:50.000000000 -0500
|
|
|
|
@@ -0,0 +1,105 @@
|
|
|
|
+/* echo.c - simple example of a crash extension
|
|
|
|
+ *
|
|
|
|
+ * Copyright (C) 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.
|
|
|
|
+ */
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+#include "defs.h" /* From the crash source top-level directory */
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+void cmd_echo(); /* Declare the commands and their help data. */
|
|
|
|
+char *help_echo[];
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+static struct command_table_entry command_table[] = {
|
|
|
|
+ "echo", cmd_echo, help_echo, 0, /* One or more commands, */
|
|
|
|
+ NULL, /* terminated by NULL, */
|
|
|
|
+};
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+_init() /* Register the command set. */
|
|
|
|
+{
|
|
|
|
+ register_extension(command_table);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * The _fini() function is called if the shared object is unloaded.
|
|
|
|
+ * If desired, perform any cleanups here.
|
|
|
|
+ */
|
|
|
|
+_fini() { }
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+/*
|
|
|
|
+ * Arguments are passed to the command functions in the global args[argcnt]
|
|
|
|
+ * array. See getopt(3) for info on dash arguments. Check out defs.h and
|
|
|
|
+ * other crash commands for usage of the myriad of utility routines available
|
|
|
|
+ * to accomplish what your task.
|
|
|
|
+ */
|
|
|
|
+void
|
|
|
|
+cmd_echo()
|
|
|
|
+{
|
|
|
|
+ int c;
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ while ((c = getopt(argcnt, args, "")) != EOF) {
|
|
|
|
+ switch(c)
|
|
|
|
+ {
|
|
|
|
+ default:
|
|
|
|
+ argerrs++;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ if (argerrs)
|
|
|
|
+ cmd_usage(pc->curcmd, SYNOPSIS);
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ while (args[optind])
|
|
|
|
+ fprintf(fp, "%s ", args[optind++]);
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ fprintf(fp, "\n");
|
|
|
|
+}
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+/*
|
|
|
|
+ * The optional help data is simply an array of strings in a defined format.
|
|
|
|
+ * For example, the "help echo" command will use the help_echo[] string
|
|
|
|
+ * array below to create a help page that looks like this:
|
|
|
|
+ *
|
|
|
|
+ * NAME
|
|
|
|
+ * echo - echoes back its arguments
|
|
|
|
+ *
|
|
|
|
+ * SYNOPSIS
|
|
|
|
+ * echo arg ...
|
|
|
|
+ *
|
|
|
|
+ * DESCRIPTION
|
|
|
|
+ * This command simply echoes back its arguments.
|
|
|
|
+ *
|
|
|
|
+ * EXAMPLE
|
|
|
|
+ * Echo back all command arguments:
|
|
|
|
+ *
|
|
|
|
+ * crash> echo hello, world
|
|
|
|
+ * hello, world
|
|
|
|
+ *
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+char *help_echo[] = {
|
|
|
|
+ "echo", /* command name */
|
|
|
|
+ "echoes back its arguments", /* short description */
|
|
|
|
+ "arg ...", /* argument synopsis, or " " if none */
|
|
|
|
+
|
|
|
|
+ " This command simply echoes back its arguments.",
|
|
|
|
+ "\nEXAMPLE",
|
|
|
|
+ " Echo back all command arguments:\n",
|
|
|
|
+ " crash> echo hello, world",
|
|
|
|
+ " hello, world",
|
|
|
|
+ NULL
|
2005-03-03 16:53:39 +00:00
|
|
|
+};
|
|
|
|
+
|
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
--- crash/extensions/dminfo.c.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/extensions/dminfo.c 2005-11-10 16:12:50.000000000 -0500
|
|
|
|
@@ -0,0 +1,1531 @@
|
|
|
|
+/* dminfo.c - crash extension module for device-mapper analysis
|
|
|
|
+ *
|
|
|
|
+ * Copyright (C) 2005 NEC Corporation
|
|
|
|
+ * Copyright (C) 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.
|
|
|
|
+ */
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+#include "defs.h" /* From the crash source top-level directory */
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+/*
|
|
|
|
+ * Indices of size-offset array (Used by GET_xxx macros)
|
|
|
|
+ *
|
|
|
|
+ * DM_<struct name>_<member name>
|
|
|
|
+ */
|
|
|
|
+enum {
|
|
|
|
+ DM_hash_cell_name_list = 0,
|
|
|
|
+ DM_hash_cell_name,
|
|
|
|
+ DM_hash_cell_md,
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ DM_mapped_device_disk,
|
|
|
|
+ DM_mapped_device_map,
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ DM_gendisk_major,
|
|
|
|
+ DM_gendisk_first_minor,
|
|
|
|
+ DM_gendisk_disk_name,
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ DM_dm_table_num_targets,
|
|
|
|
+ DM_dm_table_targets,
|
|
|
|
+ DM_dm_table_devices,
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ DM_dm_target_type,
|
|
|
|
+ DM_dm_target_begin,
|
|
|
|
+ DM_dm_target_len,
|
|
|
|
+ DM_dm_target_private,
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ DM_dm_dev_count,
|
|
|
|
+ DM_dm_dev_bdev,
|
|
|
|
+ DM_dm_dev_name,
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ DM_dm_io_md,
|
|
|
|
+ DM_dm_io_bio,
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ DM_target_type_name,
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ DM_target_io_io,
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ DM_block_device_bd_disk,
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ DM_bio_bi_private,
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ DM_bio_list_head,
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ DM_linear_c_dev,
|
|
|
|
+ DM_linear_c_start,
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ DM_multipath_hw_handler,
|
|
|
|
+ DM_multipath_nr_priority_groups,
|
|
|
|
+ DM_multipath_priority_groups,
|
|
|
|
+ DM_multipath_nr_valid_paths,
|
|
|
|
+ DM_multipath_current_pg,
|
|
|
|
+ DM_multipath_queue_if_no_path,
|
|
|
|
+ DM_multipath_queue_size,
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ DM_hw_handler_type,
|
|
|
|
+ DM_hw_handler_type_name,
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ DM_priority_group_ps,
|
|
|
|
+ DM_priority_group_pg_num,
|
|
|
|
+ DM_priority_group_bypassed,
|
|
|
|
+ DM_priority_group_nr_pgpaths,
|
|
|
|
+ DM_priority_group_pgpaths,
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ DM_path_selector_type,
|
|
|
|
+ DM_path_selector_type_name,
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ DM_pgpath_fail_count,
|
|
|
|
+ DM_pgpath_path,
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ DM_path_dev,
|
|
|
|
+ DM_path_is_active,
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ DM_mirror_set_rh,
|
|
|
|
+ DM_mirror_set_reads,
|
|
|
|
+ DM_mirror_set_writes,
|
|
|
|
+ DM_mirror_set_in_sync,
|
|
|
|
+ DM_mirror_set_nr_mirrors,
|
|
|
|
+ DM_mirror_set_mirror,
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ DM_region_hash_log,
|
|
|
|
+ DM_region_hash_quiesced_regions,
|
|
|
|
+ DM_region_hash_recovered_regions,
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ DM_dirty_log_type,
|
|
|
|
+ DM_dirty_log_type_name,
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ DM_mirror_error_count,
|
|
|
|
+ DM_mirror_dev,
|
|
|
|
+ DM_mirror_offset,
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ DM_crypt_config_dev,
|
|
|
|
+ DM_crypt_config_iv_mode,
|
|
|
|
+ DM_crypt_config_tfm,
|
|
|
|
+ DM_crypt_config_key_size,
|
|
|
|
+ DM_crypt_config_key,
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ DM_crypto_tfm_crt_u,
|
|
|
|
+ DM_crypto_tfm___crt_alg,
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ DM_crypto_alg_cra_name,
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ DM_cipher_tfm_cit_mode,
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ DM_stripe_c_stripes,
|
|
|
|
+ DM_stripe_c_chunk_mask,
|
|
|
|
+ DM_stripe_c_stripe,
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ DM_stripe_dev,
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ DM_dm_snapshot_origin,
|
|
|
|
+ DM_dm_snapshot_cow,
|
|
|
|
+ DM_dm_snapshot_chunk_size,
|
|
|
|
+ DM_dm_snapshot_valid,
|
|
|
|
+ DM_dm_snapshot_type,
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ NR_DMINFO_MEMBER_TABLE_ENTRY
|
|
|
|
+};
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+/* Size-offset array for structure's member */
|
|
|
|
+static struct dminfo_member_entry {
|
|
|
|
+ unsigned long offset;
|
|
|
|
+ unsigned long size;
|
|
|
|
+} mbr_ary[NR_DMINFO_MEMBER_TABLE_ENTRY];
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+/*
|
|
|
|
+ * Macros to retrieve data of given structure's member
|
|
|
|
+ *
|
|
|
|
+ * Macros except for the MSG assume 'struct s' is at 'addr'
|
|
|
|
+ */
|
|
|
|
+#define MSG(msg, s, m) msg ": " s "." m
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+/* Initialize the size-offset array */
|
|
|
|
+#define INIT_MBR_TABLE(s, m) \
|
|
|
|
+ do { \
|
|
|
|
+ if (!mbr_ary[DM_##s##_##m].size) { \
|
|
|
|
+ mbr_ary[DM_##s##_##m].offset = MEMBER_OFFSET("struct " #s, #m); \
|
|
|
|
+ mbr_ary[DM_##s##_##m].size = MEMBER_SIZE("struct " #s, #m); \
|
|
|
|
+ } \
|
|
|
|
+ } while (0)
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+/*
|
|
|
|
+ * Store the data of member m in ret.
|
|
|
|
+ * Initialize the size-offset array for the member m if needed.
|
|
|
|
+ */
|
|
|
|
+#define GET_VALUE(addr, s, m, ret) \
|
|
|
|
+ do { \
|
|
|
|
+ INIT_MBR_TABLE(s, m); \
|
|
|
|
+ if (sizeof(ret) < mbr_ary[DM_##s##_##m].size) \
|
|
|
|
+ fprintf(fp, "%s\n", \
|
|
|
|
+ MSG("ERROR: GET_VALUE size_check", #s, #m)); \
|
|
|
|
+ readmem(addr + mbr_ary[DM_##s##_##m].offset, KVADDR, &ret, \
|
|
|
|
+ mbr_ary[DM_##s##_##m].size, MSG("GET_VALUE", #s, #m), \
|
|
|
|
+ FAULT_ON_ERROR);\
|
|
|
|
+ } while (0)
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+/*
|
|
|
|
+ * Store the address of member m in ret.
|
|
|
|
+ * Initialize the size-offset array for the member m if needed.
|
|
|
|
+ */
|
|
|
|
+#define GET_ADDR(addr, s, m, ret) \
|
|
|
|
+ do { \
|
|
|
|
+ INIT_MBR_TABLE(s, m); \
|
|
|
|
+ ret = addr + mbr_ary[DM_##s##_##m].offset; \
|
|
|
|
+ } while (0)
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+/*
|
|
|
|
+ * Store the string data of member m in ret.
|
|
|
|
+ * Initialize the size-offset array for the member m if needed.
|
|
|
|
+ */
|
|
|
|
+#define GET_STR(addr, s, m, ret, len) \
|
|
|
|
+ do { \
|
|
|
|
+ INIT_MBR_TABLE(s, m); \
|
|
|
|
+ if (!read_string(addr + mbr_ary[DM_##s##_##m].offset, ret, len - 1)) \
|
|
|
|
+ fprintf(fp, "%s\n", MSG("ERROR: GET_STR", #s, #m)); \
|
|
|
|
+ } while (0)
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+/*
|
|
|
|
+ * Store the string data pointed by member m in ret.
|
|
|
|
+ * Initialize the size-offset array for the member m if needed.
|
|
|
|
+ */
|
|
|
|
+#define GET_PTR_STR(addr, s, m, ret, len) \
|
|
|
|
+ do { \
|
|
|
|
+ unsigned long tmp; \
|
|
|
|
+ INIT_MBR_TABLE(s, m); \
|
|
|
|
+ readmem(addr + mbr_ary[DM_##s##_##m].offset, KVADDR, &tmp, \
|
|
|
|
+ mbr_ary[DM_##s##_##m].size, MSG("GET_PTR_STR", #s, #m),\
|
|
|
|
+ FAULT_ON_ERROR);\
|
|
|
|
+ if (!read_string(tmp, ret, len - 1)) \
|
|
|
|
+ fprintf(fp, "%s\n", MSG("ERROR: GET_PTR_STR", #s, #m));\
|
|
|
|
+ } while (0)
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+/*
|
|
|
|
+ * Utility function/macro to walk the list
|
2005-02-10 18:39:42 +00:00
|
|
|
+ */
|
2006-01-04 19:34:13 +00:00
|
|
|
+static unsigned long
|
|
|
|
+get_next_from_list_head(unsigned long addr)
|
2005-02-10 18:39:42 +00:00
|
|
|
+{
|
2006-01-04 19:34:13 +00:00
|
|
|
+ unsigned long ret;
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ readmem(addr + OFFSET(list_head_next), KVADDR, &ret, sizeof(void *),
|
|
|
|
+ MSG("get_next_from_list_head", "list_head", "next"),
|
|
|
|
+ FAULT_ON_ERROR);
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+#define list_for_each(next, head, last) \
|
|
|
|
+ for (next = get_next_from_list_head(head), last = 0UL; \
|
|
|
|
+ next && next != head && next != last; \
|
|
|
|
+ last = next, next = get_next_from_list_head(next))
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+/*
|
|
|
|
+ * device-mapper target analyzer
|
|
|
|
+ *
|
|
|
|
+ * device-mapper has various target driver: linear, mirror, multipath, etc.
|
|
|
|
+ * Information specific to target is stored in its own way.
|
|
|
|
+ * Target-specific analyzer is provided for each target driver for this reason.
|
|
|
|
+ */
|
|
|
|
+static struct dminfo_target_analyzer {
|
|
|
|
+ struct dminfo_target_analyzer *next;
|
|
|
|
+ char *target_name;
|
|
|
|
+ int (*ready) (void); /* returns true if analyzer is available */
|
|
|
|
+ void (*show_table) (unsigned long); /* display table info */
|
|
|
|
+ void (*show_status) (unsigned long); /* display status info */
|
|
|
|
+ void (*show_queue) (unsigned long); /* display queued I/O info */
|
|
|
|
+} analyzers_head;
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+static void
|
|
|
|
+dminfo_register_target_analyzer(struct dminfo_target_analyzer *ta)
|
|
|
|
+{
|
|
|
|
+ ta->next = analyzers_head.next;
|
|
|
|
+ analyzers_head.next = ta;
|
|
|
|
+}
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+static struct
|
|
|
|
+dminfo_target_analyzer *find_target_analyzer(char *target_type)
|
|
|
|
+{
|
|
|
|
+ struct dminfo_target_analyzer *ta;
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ for (ta = analyzers_head.next; ta; ta = ta->next)
|
|
|
|
+ if (!strcmp(ta->target_name, target_type))
|
|
|
|
+ return ta;
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ return NULL;
|
2005-02-10 18:39:42 +00:00
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
2006-01-04 19:34:13 +00:00
|
|
|
+ * zero target
|
2005-02-10 18:39:42 +00:00
|
|
|
+ */
|
|
|
|
+static int
|
2006-01-04 19:34:13 +00:00
|
|
|
+zero_ready(void)
|
2005-02-10 18:39:42 +00:00
|
|
|
+{
|
2006-01-04 19:34:13 +00:00
|
|
|
+ return 1;
|
|
|
|
+}
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+static void
|
|
|
|
+zero_show_table(unsigned long target)
|
|
|
|
+{
|
|
|
|
+ unsigned long long start, len;
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ /* Get target information */
|
|
|
|
+ GET_VALUE(target, dm_target, begin, start);
|
|
|
|
+ GET_VALUE(target, dm_target, len, len);
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ fprintf(fp, " begin:%llu len:%llu", start, len);
|
2005-02-10 18:39:42 +00:00
|
|
|
+}
|
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+static void
|
|
|
|
+zero_show_status(unsigned long target)
|
|
|
|
+{
|
|
|
|
+ /* zero target has no status */
|
|
|
|
+ fprintf(fp, " No status info");
|
|
|
|
+}
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+static void
|
|
|
|
+zero_show_queue(unsigned long target)
|
|
|
|
+{
|
|
|
|
+ /* zero target has no queue */
|
|
|
|
+ fprintf(fp, " No queue info");
|
|
|
|
+}
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+static struct dminfo_target_analyzer zero_analyzer = {
|
|
|
|
+ .target_name = "zero",
|
|
|
|
+ .ready = zero_ready,
|
|
|
|
+ .show_table = zero_show_table,
|
|
|
|
+ .show_status = zero_show_status,
|
|
|
|
+ .show_queue = zero_show_queue
|
|
|
|
+};
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+/*
|
|
|
|
+ * error target
|
2005-02-10 18:39:42 +00:00
|
|
|
+ */
|
|
|
|
+static int
|
2006-01-04 19:34:13 +00:00
|
|
|
+error_ready(void)
|
2004-09-09 03:57:09 +00:00
|
|
|
+{
|
2006-01-04 19:34:13 +00:00
|
|
|
+ return 1;
|
|
|
|
+}
|
2004-09-09 03:57:09 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+static void
|
|
|
|
+error_show_table(unsigned long target)
|
|
|
|
+{
|
|
|
|
+ unsigned long long start, len;
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ /* Get target information */
|
|
|
|
+ GET_VALUE(target, dm_target, begin, start);
|
|
|
|
+ GET_VALUE(target, dm_target, len, len);
|
2004-09-09 03:57:09 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ fprintf(fp, " begin:%llu len:%llu", start, len);
|
|
|
|
+}
|
2004-09-09 03:57:09 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+static void
|
|
|
|
+error_show_status(unsigned long target)
|
|
|
|
+{
|
|
|
|
+ /* error target has no status */
|
|
|
|
+ fprintf(fp, " No status info");
|
|
|
|
+}
|
2004-09-09 03:57:09 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+static void
|
|
|
|
+error_show_queue(unsigned long target)
|
|
|
|
+{
|
|
|
|
+ /* error target has no queue */
|
|
|
|
+ fprintf(fp, " No queue info");
|
|
|
|
+}
|
2004-09-09 03:57:09 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+static struct dminfo_target_analyzer error_analyzer = {
|
|
|
|
+ .target_name = "error",
|
|
|
|
+ .ready = error_ready,
|
|
|
|
+ .show_table = error_show_table,
|
|
|
|
+ .show_status = error_show_status,
|
|
|
|
+ .show_queue = error_show_queue
|
|
|
|
+};
|
2004-09-09 03:57:09 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+/*
|
|
|
|
+ * linear target
|
|
|
|
+ */
|
|
|
|
+static int
|
|
|
|
+linear_ready(void)
|
|
|
|
+{
|
|
|
|
+ static int debuginfo = 0;
|
2004-09-09 03:57:09 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ if (debuginfo)
|
|
|
|
+ return 1;
|
2004-09-09 03:57:09 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ if (STRUCT_EXISTS("struct linear_c")) {
|
|
|
|
+ debuginfo = 1;
|
|
|
|
+ return 1;
|
|
|
|
+ } else
|
|
|
|
+ fprintf(fp, "No such struct info: linear_c");
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ return 0;
|
2004-09-09 03:57:09 +00:00
|
|
|
+}
|
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+static void
|
|
|
|
+linear_show_table(unsigned long target)
|
|
|
|
+{
|
|
|
|
+ unsigned long lc, dm_dev;
|
|
|
|
+ unsigned long long start, len, offset;
|
|
|
|
+ char devt[BUFSIZE];
|
|
|
|
+
|
|
|
|
+ /* Get target information */
|
|
|
|
+ GET_VALUE(target, dm_target, begin, start);
|
|
|
|
+ GET_VALUE(target, dm_target, len, len);
|
|
|
|
+ GET_VALUE(target, dm_target, private, lc);
|
|
|
|
+ GET_VALUE(lc, linear_c, dev, dm_dev);
|
|
|
|
+ GET_STR(dm_dev, dm_dev, name, devt, BUFSIZE);
|
|
|
|
+ GET_VALUE(lc, linear_c, start, offset);
|
|
|
|
+
|
|
|
|
+ fprintf(fp, " begin:%llu len:%llu dev:%s offset:%llu",
|
|
|
|
+ start, len, devt, offset);
|
|
|
|
+}
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+static void
|
|
|
|
+linear_show_status(unsigned long target)
|
|
|
|
+{
|
|
|
|
+ /* linear target has no status */
|
|
|
|
+ fprintf(fp, " No status info");
|
|
|
|
+}
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+static void
|
|
|
|
+linear_show_queue(unsigned long target)
|
|
|
|
+{
|
|
|
|
+ /* linear target has no I/O queue */
|
|
|
|
+ fprintf(fp, " No queue info");
|
|
|
|
+}
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+static struct dminfo_target_analyzer linear_analyzer = {
|
|
|
|
+ .target_name = "linear",
|
|
|
|
+ .ready = linear_ready,
|
|
|
|
+ .show_table = linear_show_table,
|
|
|
|
+ .show_status = linear_show_status,
|
|
|
|
+ .show_queue = linear_show_queue
|
|
|
|
+};
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
|
|
|
+/*
|
2006-01-04 19:34:13 +00:00
|
|
|
+ * mirror target
|
2005-02-10 18:39:42 +00:00
|
|
|
+ */
|
|
|
|
+static int
|
2006-01-04 19:34:13 +00:00
|
|
|
+mirror_ready(void)
|
2005-02-10 18:39:42 +00:00
|
|
|
+{
|
2006-01-04 19:34:13 +00:00
|
|
|
+ static int debuginfo = 0;
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ if (debuginfo)
|
|
|
|
+ return 1;
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ if (STRUCT_EXISTS("struct mirror_set")) {
|
|
|
|
+ debuginfo = 1;
|
|
|
|
+ return 1;
|
|
|
|
+ } else
|
|
|
|
+ fprintf(fp, "No such struct info: mirror_set");
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ return 0;
|
|
|
|
+}
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+static void
|
|
|
|
+mirror_show_table(unsigned long target)
|
|
|
|
+{
|
|
|
|
+ unsigned int i, nr_mir;
|
|
|
|
+ unsigned long ms, rh, log, log_type, mir_size, mir_head, mir, dm_dev;
|
|
|
|
+ unsigned long long offset;
|
|
|
|
+ char buf[BUFSIZE];
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ /* Get the address of struct mirror_set */
|
|
|
|
+ GET_VALUE(target, dm_target, private, ms);
|
|
|
|
+
|
|
|
|
+ /* Get the log-type name of the mirror_set */
|
|
|
|
+ GET_ADDR(ms, mirror_set, rh, rh);
|
|
|
|
+ GET_VALUE(rh, region_hash, log, log);
|
|
|
|
+ GET_VALUE(log, dirty_log, type, log_type);
|
|
|
|
+ GET_PTR_STR(log_type, dirty_log_type, name, buf, BUFSIZE);
|
|
|
|
+ fprintf(fp, " log:%s", buf);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Display information for each mirror disks.
|
|
|
|
+ *
|
|
|
|
+ * mir_head = mirror_set.mirror.
|
|
|
|
+ * This is the head of struct mirror array.
|
|
|
|
+ */
|
|
|
|
+ fprintf(fp, " dev:");
|
|
|
|
+ mir_size = STRUCT_SIZE("struct mirror");
|
|
|
|
+ GET_ADDR(ms, mirror_set, mirror, mir_head);
|
|
|
|
+ GET_VALUE(ms, mirror_set, nr_mirrors, nr_mir);
|
|
|
|
+ for (i = 0; i < nr_mir; i++) {
|
|
|
|
+ mir = mir_head + mir_size * i; /* Get next mirror */
|
|
|
|
+
|
|
|
|
+ /* Get the devt of the mirror disk */
|
|
|
|
+ GET_VALUE(mir, mirror, dev, dm_dev);
|
|
|
|
+ GET_STR(dm_dev, dm_dev, name, buf, BUFSIZE);
|
|
|
|
+
|
|
|
|
+ /* Get the offset of the mirror disk */
|
|
|
|
+ GET_VALUE(mir, mirror, offset, offset);
|
|
|
|
+
|
|
|
|
+ fprintf(fp, "%s(%llu)%s", buf, offset,
|
|
|
|
+ i == nr_mir - 1 ? "" : ",");
|
|
|
|
+ }
|
|
|
|
+ if (i != nr_mir)
|
|
|
|
+ fprintf(fp, " ERROR: dev are less than nr_mir:%d", nr_mir);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+mirror_show_status(unsigned long target)
|
|
|
|
+{
|
|
|
|
+ unsigned int i, nr_mir, synced, nr_error;
|
|
|
|
+ unsigned long ms, mir_size, mir_head, mir, dm_dev;
|
|
|
|
+ char buf[BUFSIZE];
|
|
|
|
+
|
|
|
|
+ /* Get the address of struct mirror_set */
|
|
|
|
+ GET_VALUE(target, dm_target, private, ms);
|
|
|
|
+
|
|
|
|
+ /* Get the status info of the mirror_set */
|
|
|
|
+ GET_VALUE(ms, mirror_set, in_sync, synced);
|
|
|
|
+ fprintf(fp, " in_sync:%d", synced);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Display information for each mirror disks.
|
|
|
|
+ *
|
|
|
|
+ * mir_head = mirror_set.mirror.
|
|
|
|
+ * This is the head of struct mirror array.
|
|
|
|
+ */
|
|
|
|
+ fprintf(fp, " dev:");
|
|
|
|
+ mir_size = STRUCT_SIZE("struct mirror");
|
|
|
|
+ GET_ADDR(ms, mirror_set, mirror, mir_head);
|
|
|
|
+ GET_VALUE(ms, mirror_set, nr_mirrors, nr_mir);
|
|
|
|
+ for (i = 0; i < nr_mir; i++) {
|
|
|
|
+ mir = mir_head + mir_size * i; /* Get next mirror */
|
|
|
|
+
|
|
|
|
+ /* Get the devt of the mirror disk */
|
|
|
|
+ GET_VALUE(mir, mirror, dev, dm_dev);
|
|
|
|
+ GET_STR(dm_dev, dm_dev, name, buf, BUFSIZE);
|
|
|
|
+
|
|
|
|
+ /* Get the offset of the mirror disk */
|
|
|
|
+ GET_VALUE(mir, mirror, error_count, nr_error);
|
|
|
|
+
|
|
|
|
+ fprintf(fp, "%s(%c,%d)%s", buf, nr_error ? 'D' : 'A', nr_error,
|
|
|
|
+ i == nr_mir - 1 ? "" : ",");
|
|
|
|
+ }
|
|
|
|
+ if (i != nr_mir)
|
|
|
|
+ fprintf(fp, " ERROR: dev are less than nr_mir:%d", nr_mir);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+mirror_show_queue(unsigned long target)
|
|
|
|
+{
|
|
|
|
+ unsigned long ms, rlist, wlist, rhead, whead;
|
|
|
|
+ unsigned long rh, quis_head, rcov_head, quis_next, rcov_next;
|
|
|
|
+
|
|
|
|
+ /* Get the address of struct mirror_set */
|
|
|
|
+ GET_VALUE(target, dm_target, private, ms);
|
|
|
|
+
|
|
|
|
+ /* Get the address of queued I/O lists in struct mirror_set */
|
|
|
|
+ GET_ADDR(ms, mirror_set, reads, rlist);
|
|
|
|
+ GET_ADDR(ms, mirror_set, writes, wlist);
|
|
|
|
+
|
|
|
|
+ /* Get the head of queued I/O lists */
|
|
|
|
+ GET_VALUE(rlist, bio_list, head, rhead);
|
|
|
|
+ GET_VALUE(wlist, bio_list, head, whead);
|
|
|
|
+ fprintf(fp, " %s", rhead ? "reads" : "(reads)");
|
|
|
|
+ fprintf(fp, " %s", whead ? "writes" : "(writes)");
|
|
|
|
+
|
|
|
|
+ /* Get the address of the struct region_hash */
|
|
|
|
+ GET_ADDR(ms, mirror_set, rh, rh);
|
|
|
|
+
|
|
|
|
+ /* Get the address of recover region lists in struct region_hash */
|
|
|
|
+ GET_ADDR(rh, region_hash, quiesced_regions, quis_head);
|
|
|
|
+ GET_ADDR(rh, region_hash, recovered_regions, rcov_head);
|
|
|
|
+
|
|
|
|
+ /* Get the head of recover region lists */
|
|
|
|
+ quis_next = get_next_from_list_head(quis_head);
|
|
|
|
+ rcov_next = get_next_from_list_head(rcov_head);
|
|
|
|
+
|
|
|
|
+ fprintf(fp, " %s", quis_next != quis_head ? "quiesced" : "(quiesced)");
|
|
|
|
+ fprintf(fp, " %s", rcov_next != rcov_head ? "recovered" : "(recovered)");
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static struct dminfo_target_analyzer mirror_analyzer = {
|
|
|
|
+ .target_name = "mirror",
|
|
|
|
+ .ready = mirror_ready,
|
|
|
|
+ .show_table = mirror_show_table,
|
|
|
|
+ .show_status = mirror_show_status,
|
|
|
|
+ .show_queue = mirror_show_queue
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * multipath target
|
|
|
|
+ */
|
|
|
|
+static int
|
|
|
|
+multipath_ready(void)
|
|
|
|
+{
|
|
|
|
+ static int debuginfo = 0;
|
|
|
|
+
|
|
|
|
+ if (debuginfo)
|
|
|
|
+ return 1;
|
|
|
|
+
|
|
|
|
+ if (STRUCT_EXISTS("struct multipath")) {
|
|
|
|
+ debuginfo = 1;
|
|
|
|
+ return 1;
|
|
|
|
+ } else
|
|
|
|
+ fprintf(fp, "No such struct info: multipath");
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+multipath_show_table(unsigned long target)
|
|
|
|
+{
|
|
|
|
+ int i, j;
|
|
|
|
+ unsigned int queue_if_no_path, nr_pgs, pg_id, nr_paths;
|
|
|
|
+ unsigned long mp, hwh, hwh_type, ps, ps_type, path, dm_dev;
|
|
|
|
+ unsigned long pg_head, pg_next, pg_last;
|
|
|
|
+ unsigned long path_head, path_next, path_last;
|
|
|
|
+ char name[BUFSIZE];
|
|
|
|
+
|
|
|
|
+ /* Get the address of struct multipath */
|
|
|
|
+ GET_VALUE(target, dm_target, private, mp);
|
|
|
|
+
|
|
|
|
+ /* Get features information */
|
|
|
|
+ GET_VALUE(mp, multipath, queue_if_no_path, queue_if_no_path);
|
|
|
|
+
|
|
|
|
+ /* Get the hardware-handler information */
|
|
|
|
+ GET_ADDR(mp, multipath, hw_handler, hwh);
|
|
|
|
+ GET_VALUE(hwh, hw_handler, type, hwh_type);
|
|
|
|
+ if (hwh_type)
|
|
|
|
+ GET_PTR_STR(hwh_type, hw_handler_type, name, name, BUFSIZE);
|
|
|
|
+ else
|
|
|
|
+ strcpy(name, "none");
|
|
|
|
+
|
|
|
|
+ /* Get the number of priority groups */
|
|
|
|
+ GET_VALUE(mp, multipath, nr_priority_groups, nr_pgs);
|
|
|
|
+
|
|
|
|
+ fprintf(fp, " queue_if_no_path:%d hwh:%s nr_pgs:%d\n",
|
|
|
|
+ queue_if_no_path, name, nr_pgs);
|
|
|
|
+
|
|
|
|
+ /* Display information for each priority group */
|
|
|
|
+ fprintf(fp, " %-2s %-13s %-8s %s",
|
|
|
|
+ "PG", "PATH_SELECTOR", "NR_PATHS", "PATHS");
|
|
|
|
+ GET_ADDR(mp, multipath, priority_groups, pg_head);
|
|
|
|
+ i = 0;
|
|
|
|
+ list_for_each (pg_next, pg_head, pg_last) {
|
|
|
|
+ /* pg_next == struct priority_group */
|
|
|
|
+
|
|
|
|
+ /* Get the index of the priority group */
|
|
|
|
+ GET_VALUE(pg_next, priority_group, pg_num, pg_id);
|
|
|
|
+
|
|
|
|
+ /* Get the name of path selector */
|
|
|
|
+ GET_ADDR(pg_next, priority_group, ps, ps);
|
|
|
|
+ GET_VALUE(ps, path_selector, type, ps_type);
|
|
|
|
+ GET_PTR_STR(ps_type, path_selector_type, name, name, BUFSIZE);
|
|
|
|
+
|
|
|
|
+ /* Get the number of paths in the priority group */
|
|
|
|
+ GET_VALUE(pg_next, priority_group, nr_pgpaths, nr_paths);
|
|
|
|
+
|
|
|
|
+ fprintf(fp, "\n %-2d %-13s %-8d ", pg_id, name, nr_paths);
|
|
|
|
+
|
|
|
|
+ /* Display information for each path */
|
|
|
|
+ GET_ADDR(pg_next, priority_group, pgpaths, path_head);
|
|
|
|
+ j = 0;
|
|
|
|
+ list_for_each (path_next, path_head, path_last) {
|
|
|
|
+ /* path_next == struct pgpath */
|
|
|
|
+
|
|
|
|
+ /* Get the devt of the pgpath */
|
|
|
|
+ GET_ADDR(path_next, pgpath, path, path);
|
|
|
|
+ GET_VALUE(path, path, dev, dm_dev);
|
|
|
|
+ GET_STR(dm_dev, dm_dev, name, name, BUFSIZE);
|
|
|
|
+
|
|
|
|
+ fprintf(fp, " %s", name);
|
|
|
|
+ j++;
|
|
|
|
+ }
|
|
|
|
+ if (j != nr_paths)
|
|
|
|
+ fprintf(fp, " ERROR: paths are less than nr_paths:%d",
|
|
|
|
+ nr_paths);
|
|
|
|
+ i++;
|
|
|
|
+ }
|
|
|
|
+ if (i != nr_pgs)
|
|
|
|
+ fprintf(fp, " ERROR: pgs are less than nr_pgs:%d", nr_pgs);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+multipath_show_status(unsigned long target)
|
|
|
|
+{
|
|
|
|
+ int i, j;
|
|
|
|
+ unsigned int queue_if_no_path, nr_pgs, pg_id, nr_paths;
|
|
|
|
+ unsigned int bypassed_pg, path_active, nr_fails;
|
|
|
|
+ unsigned long mp, hwh, hwh_type, cur_pg, path, dm_dev;
|
|
|
|
+ unsigned long pg_head, pg_next, pg_last;
|
|
|
|
+ unsigned long path_head, path_next, path_last;
|
|
|
|
+ char buf[BUFSIZE], path_status;
|
|
|
|
+
|
|
|
|
+ /* Get the address of struct multipath */
|
|
|
|
+ GET_VALUE(target, dm_target, private, mp);
|
|
|
|
+
|
|
|
|
+ /* Get features information */
|
|
|
|
+ GET_VALUE(mp, multipath, queue_if_no_path, queue_if_no_path);
|
|
|
|
+
|
|
|
|
+ /* Get the hardware-handler information */
|
|
|
|
+ GET_ADDR(mp, multipath, hw_handler, hwh);
|
|
|
|
+ GET_VALUE(hwh, hw_handler, type, hwh_type);
|
|
|
|
+ if (hwh_type)
|
|
|
|
+ GET_PTR_STR(hwh_type, hw_handler_type, name, buf, BUFSIZE);
|
|
|
|
+ else
|
|
|
|
+ strcpy(buf, "none");
|
|
|
|
+
|
|
|
|
+ /* Get the number of priority groups */
|
|
|
|
+ GET_VALUE(mp, multipath, nr_priority_groups, nr_pgs);
|
|
|
|
+
|
|
|
|
+ fprintf(fp, " queue_if_no_path:%d hwh:%s nr_pgs:%d\n",
|
|
|
|
+ queue_if_no_path, buf, nr_pgs);
|
|
|
|
+
|
|
|
|
+ /* Display information for each priority group */
|
|
|
|
+ fprintf(fp, " %-2s %-9s %-8s %s",
|
|
|
|
+ "PG", "PG_STATUS", "NR_PATHS", "PATHS");
|
|
|
|
+ GET_ADDR(mp, multipath, priority_groups, pg_head);
|
|
|
|
+ i = 0;
|
|
|
|
+ list_for_each (pg_next, pg_head, pg_last) {
|
|
|
|
+ /* pg_next == struct priority_group */
|
|
|
|
+
|
|
|
|
+ /* Get the index of the priority group */
|
|
|
|
+ GET_VALUE(pg_next, priority_group, pg_num, pg_id);
|
|
|
|
+
|
|
|
|
+ /* Get the status of the priority group */
|
|
|
|
+ GET_VALUE(pg_next, priority_group, bypassed, bypassed_pg);
|
|
|
|
+ if (bypassed_pg)
|
|
|
|
+ strcpy(buf, "disabled");
|
|
|
|
+ else {
|
|
|
|
+ GET_VALUE(mp, multipath, current_pg, cur_pg);
|
|
|
|
+ if (pg_next == cur_pg)
|
|
|
|
+ strcpy(buf, "active");
|
|
|
|
+ else
|
|
|
|
+ strcpy(buf, "enabled");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Get the number of paths in the priority group */
|
|
|
|
+ GET_VALUE(pg_next, priority_group, nr_pgpaths, nr_paths);
|
|
|
|
+
|
|
|
|
+ fprintf(fp, "\n %-2d %-9s %-8d ", pg_id, buf, nr_paths);
|
|
|
|
+
|
|
|
|
+ /* Display information for each path */
|
|
|
|
+ GET_ADDR(pg_next, priority_group, pgpaths, path_head);
|
|
|
|
+ j = 0;
|
|
|
|
+ list_for_each (path_next, path_head, path_last) {
|
|
|
|
+ /* path_next == struct pgpath */
|
|
|
|
+
|
|
|
|
+ /* Get the devt of the pgpath */
|
|
|
|
+ GET_ADDR(path_next, pgpath, path, path);
|
|
|
|
+ GET_VALUE(path, path, dev, dm_dev);
|
|
|
|
+ GET_STR(dm_dev, dm_dev, name, buf, BUFSIZE);
|
|
|
|
+
|
|
|
|
+ /* Get the status of the path */
|
|
|
|
+ GET_VALUE(path, path, is_active, path_active);
|
|
|
|
+ GET_VALUE(path_next, pgpath, fail_count, nr_fails);
|
|
|
|
+ path_status = path_active ? 'A' : 'F';
|
|
|
|
+
|
|
|
|
+ fprintf(fp, " %s(%c,%u)", buf, path_status, nr_fails);
|
|
|
|
+ j++;
|
|
|
|
+ }
|
|
|
|
+ if (j != nr_paths)
|
|
|
|
+ fprintf(fp, " ERROR: paths are less than nr_paths:%d",
|
|
|
|
+ nr_paths);
|
|
|
|
+ i++;
|
|
|
|
+ }
|
|
|
|
+ if (i != nr_pgs)
|
|
|
|
+ fprintf(fp, " ERROR: pgs are less than nr_pgs:%d", nr_pgs);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+multipath_show_queue(unsigned long target)
|
|
|
|
+{
|
|
|
|
+ unsigned int queue_size;
|
|
|
|
+ unsigned long mp;
|
|
|
|
+
|
|
|
|
+ /* Get the address of struct multipath */
|
|
|
|
+ GET_VALUE(target, dm_target, private, mp);
|
|
|
|
+
|
|
|
|
+ /* Get the size of queued I/Os in this 'target' */
|
|
|
|
+ GET_VALUE(mp, multipath, queue_size, queue_size);
|
|
|
|
+
|
|
|
|
+ fprintf(fp, " queue_size:%d", queue_size);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static struct dminfo_target_analyzer multipath_analyzer = {
|
|
|
|
+ .target_name = "multipath",
|
|
|
|
+ .ready = multipath_ready,
|
|
|
|
+ .show_table = multipath_show_table,
|
|
|
|
+ .show_status = multipath_show_status,
|
|
|
|
+ .show_queue = multipath_show_queue
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * crypt target
|
|
|
|
+ */
|
|
|
|
+static int
|
|
|
|
+crypt_ready(void)
|
|
|
|
+{
|
|
|
|
+ static int debuginfo = 0;
|
|
|
|
+
|
|
|
|
+ if (debuginfo)
|
|
|
|
+ return 1;
|
|
|
|
+
|
|
|
|
+ if (STRUCT_EXISTS("struct crypt_config")) {
|
|
|
|
+ debuginfo = 1;
|
|
|
|
+ return 1;
|
|
|
|
+ } else
|
|
|
|
+ fprintf(fp, "No such struct info: crypt_config");
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+#define DMINFO_CRYPTO_TFM_MODE_ECB 0x00000001
|
|
|
|
+#define DMINFO_CRYPTO_TFM_MODE_CBC 0x00000002
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+crypt_show_table(unsigned long target)
|
|
|
|
+{
|
|
|
|
+ int i, cit_mode, key_size;
|
|
|
|
+ unsigned long cc, tfm, crt_alg, cipher, iv_mode, dm_dev;
|
|
|
|
+ char buf[BUFSIZE], *chainmode;
|
|
|
|
+
|
|
|
|
+ /* Get the address of struct crypt_config */
|
|
|
|
+ GET_VALUE(target, dm_target, private, cc);
|
|
|
|
+
|
|
|
|
+ /* Get the cipher name of the crypt_tfm */
|
|
|
|
+ GET_VALUE(cc, crypt_config, tfm, tfm);
|
|
|
|
+ GET_VALUE(tfm, crypto_tfm, __crt_alg, crt_alg);
|
|
|
|
+ GET_STR(crt_alg, crypto_alg, cra_name, buf, BUFSIZE);
|
|
|
|
+ fprintf(fp, " type:%s", buf);
|
|
|
|
+
|
|
|
|
+ /* Get the cit_mode of the crypt_tfm */
|
|
|
|
+ GET_ADDR(tfm, crypto_tfm, crt_u, cipher);
|
|
|
|
+ GET_VALUE(cipher, cipher_tfm, cit_mode, cit_mode);
|
|
|
|
+
|
|
|
|
+ if (MEMBER_EXISTS("struct crypt_config", "iv_mode")) {
|
|
|
|
+ if (cit_mode == DMINFO_CRYPTO_TFM_MODE_CBC)
|
|
|
|
+ chainmode = "cbc";
|
|
|
|
+ else if (cit_mode == DMINFO_CRYPTO_TFM_MODE_ECB)
|
|
|
|
+ chainmode = "ecb";
|
|
|
|
+ else
|
|
|
|
+ chainmode = "unknown";
|
|
|
|
+
|
|
|
|
+ /* Get the iv_mode of the crypt_config */
|
|
|
|
+ GET_VALUE(cc, crypt_config, iv_mode, iv_mode);
|
|
|
|
+ if (iv_mode) {
|
|
|
|
+ GET_PTR_STR(cc, crypt_config, iv_mode, buf, BUFSIZE);
|
|
|
|
+ fprintf(fp, "-%s-%s", chainmode, buf);
|
|
|
|
+ } else
|
|
|
|
+ fprintf(fp, "-%s", chainmode);
|
|
|
|
+
|
|
|
|
+ } else {
|
|
|
|
+ /* Compatibility mode for old dm-crypt cipher strings */
|
|
|
|
+ if (cit_mode == DMINFO_CRYPTO_TFM_MODE_CBC)
|
|
|
|
+ chainmode = "plain";
|
|
|
|
+ else if (cit_mode == DMINFO_CRYPTO_TFM_MODE_ECB)
|
|
|
|
+ chainmode = "ecb";
|
|
|
|
+ else
|
|
|
|
+ chainmode = "unknown";
|
|
|
|
+
|
|
|
|
+ fprintf(fp, "-%s", chainmode);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Get the devt of the crypt_config */
|
|
|
|
+ GET_VALUE(cc, crypt_config, dev, dm_dev);
|
|
|
|
+ GET_STR(dm_dev, dm_dev, name, buf, BUFSIZE);
|
|
|
|
+ fprintf(fp, " dev:%s", buf);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Get the key of the crypt_config.
|
|
|
|
+ */
|
|
|
|
+ GET_VALUE(cc, crypt_config, key_size, key_size);
|
|
|
|
+ GET_STR(cc, crypt_config, key, buf, MIN(key_size + 1, BUFSIZE));
|
|
|
|
+ fprintf(fp, " key:");
|
|
|
|
+ for (i = 0; i < key_size; i++)
|
|
|
|
+ fprintf(fp, "%02x", (unsigned char)buf[i]);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+crypt_show_status(unsigned long target)
|
|
|
|
+{
|
|
|
|
+ /* crypt target has no status */
|
|
|
|
+ fprintf(fp, " No status info");
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+crypt_show_queue(unsigned long target)
|
|
|
|
+{
|
|
|
|
+ /* crypt target has no queue */
|
|
|
|
+ fprintf(fp, " No queue info");
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static struct dminfo_target_analyzer crypt_analyzer = {
|
|
|
|
+ .target_name = "crypt",
|
|
|
|
+ .ready = crypt_ready,
|
|
|
|
+ .show_table = crypt_show_table,
|
|
|
|
+ .show_status = crypt_show_status,
|
|
|
|
+ .show_queue = crypt_show_queue
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * stripe target
|
|
|
|
+ */
|
|
|
|
+static int
|
|
|
|
+stripe_ready(void)
|
|
|
|
+{
|
|
|
|
+ static int debuginfo = 0;
|
|
|
|
+
|
|
|
|
+ if (debuginfo)
|
|
|
|
+ return 1;
|
|
|
|
+
|
|
|
|
+ if (STRUCT_EXISTS("struct stripe_c")) {
|
|
|
|
+ debuginfo = 1;
|
|
|
|
+ return 1;
|
|
|
|
+ } else
|
|
|
|
+ fprintf(fp, "No such struct info: stripe_c");
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+stripe_show_table(unsigned long target)
|
|
|
|
+{
|
|
|
|
+ unsigned int i, n_stripe;
|
|
|
|
+ unsigned long sc, stripe_size, s, head, dm_dev;
|
|
|
|
+ unsigned long long mask;
|
|
|
|
+ char buf[BUFSIZE];
|
|
|
|
+
|
|
|
|
+ /* Get the address of struct stripe_c */
|
|
|
|
+ GET_VALUE(target, dm_target, private, sc);
|
|
|
|
+
|
|
|
|
+ /* Get the chunk_size of the stripe_c */
|
|
|
|
+ GET_VALUE(sc, stripe_c, chunk_mask, mask);
|
|
|
|
+ fprintf(fp, " chunk_size:%llu", mask + 1);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Display the information of each stripe disks.
|
|
|
|
+ *
|
|
|
|
+ * head = stripe_c.stripe.
|
|
|
|
+ * This is the head of struct stripe array.
|
|
|
|
+ */
|
|
|
|
+ stripe_size = STRUCT_SIZE("struct stripe");
|
|
|
|
+ GET_ADDR(sc, stripe_c, stripe, head);
|
|
|
|
+ GET_VALUE(sc, stripe_c, stripes, n_stripe);
|
|
|
|
+ fprintf(fp, " dev:");
|
|
|
|
+ for (i = 0; i < n_stripe; i++) {
|
|
|
|
+ s = head + stripe_size * i; /* Get next stripe */
|
|
|
|
+
|
|
|
|
+ /* Get the devt of the stripe disk */
|
|
|
|
+ GET_VALUE(s, stripe, dev, dm_dev);
|
|
|
|
+ GET_STR(dm_dev, dm_dev, name, buf, BUFSIZE);
|
|
|
|
+
|
|
|
|
+ fprintf(fp, "%s%s", buf, i == n_stripe - 1 ? "" : ",");
|
|
|
|
+ }
|
|
|
|
+ if (i != n_stripe)
|
|
|
|
+ fprintf(fp, " ERROR: dev are less than n_stripe:%d", n_stripe);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+stripe_show_status(unsigned long target)
|
|
|
|
+{
|
|
|
|
+ /* stripe target has no status */
|
|
|
|
+ fprintf(fp, " No status info");
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+stripe_show_queue(unsigned long target)
|
|
|
|
+{
|
|
|
|
+ /* stripe target has no queue */
|
|
|
|
+ fprintf(fp, " No queue info");
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static struct dminfo_target_analyzer stripe_analyzer = {
|
|
|
|
+ .target_name = "striped",
|
|
|
|
+ .ready = stripe_ready,
|
|
|
|
+ .show_table = stripe_show_table,
|
|
|
|
+ .show_status = stripe_show_status,
|
|
|
|
+ .show_queue = stripe_show_queue
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * snapshot target
|
|
|
|
+ */
|
|
|
|
+static int
|
|
|
|
+snapshot_ready(void)
|
|
|
|
+{
|
|
|
|
+ static int debuginfo = 0;
|
|
|
|
+
|
|
|
|
+ if (debuginfo)
|
|
|
|
+ return 1;
|
|
|
|
+
|
|
|
|
+ if (STRUCT_EXISTS("struct dm_snapshot")) {
|
|
|
|
+ debuginfo = 1;
|
|
|
|
+ return 1;
|
|
|
|
+ } else
|
|
|
|
+ fprintf(fp, "No such struct info: dm_snapshot");
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+snapshot_show_table(unsigned long target)
|
|
|
|
+{
|
|
|
|
+ unsigned long snap, orig_dev, cow_dev;
|
|
|
|
+ unsigned long long chunk_size;
|
|
|
|
+ char orig_name[BUFSIZE], cow_name[BUFSIZE], type;
|
|
|
|
+
|
|
|
|
+ /* Get the address of struct dm_snapshot */
|
|
|
|
+ GET_VALUE(target, dm_target, private, snap);
|
|
|
|
+
|
|
|
|
+ /* Get snapshot parameters of the dm_snapshot */
|
|
|
|
+ GET_VALUE(snap, dm_snapshot, origin, orig_dev);
|
|
|
|
+ GET_STR(orig_dev, dm_dev, name, orig_name, BUFSIZE);
|
|
|
|
+ GET_VALUE(snap, dm_snapshot, cow, cow_dev);
|
|
|
|
+ GET_STR(cow_dev, dm_dev, name, cow_name, BUFSIZE);
|
|
|
|
+ GET_VALUE(snap, dm_snapshot, type, type);
|
|
|
|
+ GET_VALUE(snap, dm_snapshot, chunk_size, chunk_size);
|
|
|
|
+
|
|
|
|
+ fprintf(fp, " orig:%s cow:%s type:%c chunk_size:%llu",
|
|
|
|
+ orig_name, cow_name, type, chunk_size);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+snapshot_show_status(unsigned long target)
|
|
|
|
+{
|
|
|
|
+ int valid;
|
|
|
|
+ unsigned long snap;
|
|
|
|
+
|
|
|
|
+ /* Get the address of struct dm_snapshot */
|
|
|
|
+ GET_VALUE(target, dm_target, private, snap);
|
|
|
|
+
|
|
|
|
+ /* Get snapshot parameters of the dm_snapshot */
|
|
|
|
+ GET_VALUE(snap, dm_snapshot, valid, valid);
|
|
|
|
+
|
|
|
|
+ fprintf(fp, " vaild:%d", valid);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+snapshot_show_queue(unsigned long target)
|
|
|
|
+{
|
|
|
|
+ fprintf(fp, " No queue info");
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static struct dminfo_target_analyzer snapshot_analyzer = {
|
|
|
|
+ .target_name = "snapshot",
|
|
|
|
+ .ready = snapshot_ready,
|
|
|
|
+ .show_table = snapshot_show_table,
|
|
|
|
+ .show_status = snapshot_show_status,
|
|
|
|
+ .show_queue = snapshot_show_queue
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * snapshot-origin target
|
|
|
|
+ */
|
|
|
|
+static int
|
|
|
|
+origin_ready(void)
|
|
|
|
+{
|
|
|
|
+ return 1;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+origin_show_table(unsigned long target)
|
|
|
|
+{
|
|
|
|
+ unsigned long dm_dev;
|
|
|
|
+ char buf[BUFSIZE];
|
|
|
|
+
|
|
|
|
+ /* Get the name of the struct dm_dev */
|
|
|
|
+ GET_VALUE(target, dm_target, private, dm_dev);
|
|
|
|
+ GET_STR(dm_dev, dm_dev, name, buf, BUFSIZE);
|
|
|
|
+
|
|
|
|
+ fprintf(fp, " orig_dev:%s", buf);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+origin_show_status(unsigned long target)
|
|
|
|
+{
|
|
|
|
+ /* snapshot-origin target has no status */
|
|
|
|
+ fprintf(fp, " No status info");
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+origin_show_queue(unsigned long target)
|
|
|
|
+{
|
|
|
|
+ /* snapshot-origin target has no queue */
|
|
|
|
+ fprintf(fp, " No queue info");
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static struct dminfo_target_analyzer snapshot_origin_analyzer = {
|
|
|
|
+ .target_name = "snapshot-origin",
|
|
|
|
+ .ready = origin_ready,
|
|
|
|
+ .show_table = origin_show_table,
|
|
|
|
+ .show_status = origin_show_status,
|
|
|
|
+ .show_queue = origin_show_queue
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Core part of dminfo
|
|
|
|
+ */
|
|
|
|
+#define DMINFO_LIST 0
|
|
|
|
+#define DMINFO_DEPS 1
|
|
|
|
+#define DMINFO_TABLE 2
|
|
|
|
+#define DMINFO_STATUS 3
|
|
|
|
+#define DMINFO_QUEUE 4
|
|
|
|
+
|
|
|
|
+static int
|
|
|
|
+dm_core_ready(void)
|
|
|
|
+{
|
|
|
|
+ static int debuginfo = 0;
|
|
|
|
+
|
|
|
|
+ if (debuginfo)
|
|
|
|
+ return 1;
|
|
|
|
+
|
|
|
|
+ if (STRUCT_EXISTS("struct hash_cell")) {
|
|
|
|
+ debuginfo = 1;
|
|
|
|
+ return 1;
|
|
|
|
+ } else
|
|
|
|
+ fprintf(fp, "No such struct info: hash_cell\n");
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* Display dependency information of the 'table' */
|
|
|
|
+static void
|
|
|
|
+dminfo_show_deps(unsigned long table)
|
|
|
|
+{
|
|
|
|
+ int major, minor, count;
|
|
|
|
+ unsigned long head, next, last, dev, bdev;
|
|
|
|
+ char buf[BUFSIZE];
|
|
|
|
+
|
|
|
|
+ /* head = dm_table.devices */
|
|
|
|
+ GET_ADDR(table, dm_table, devices, head);
|
|
|
|
+
|
|
|
|
+ fprintf(fp, " %-3s %-3s %-16s %-5s %s\n",
|
|
|
|
+ "MAJ", "MIN", "GENDISK", "COUNT", "DEVNAME");
|
|
|
|
+
|
|
|
|
+ list_for_each (next, head, last) {
|
|
|
|
+ /* Get dependency information. (next == struct *dm_dev) */
|
|
|
|
+ GET_VALUE(next, dm_dev, count, count);
|
|
|
|
+ GET_VALUE(next, dm_dev, bdev, bdev);
|
|
|
|
+ GET_VALUE(bdev, block_device, bd_disk, dev);
|
|
|
|
+ GET_VALUE(dev, gendisk, major, major);
|
|
|
|
+ GET_VALUE(dev, gendisk, first_minor, minor);
|
|
|
|
+ GET_STR(dev, gendisk, disk_name, buf, BUFSIZE);
|
|
|
|
+
|
|
|
|
+ fprintf(fp, " %-3d %-3d %-16lx %-5d %s\n",
|
|
|
|
+ major, minor, dev, count, buf);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Display target specific information in the 'table', if the target
|
|
|
|
+ * analyzer is registered and available.
|
|
|
|
+ */
|
|
|
|
+static void
|
|
|
|
+dminfo_show_details(unsigned long table, unsigned int num_targets, int info_type)
|
|
|
|
+{
|
|
|
|
+ unsigned int i;
|
|
|
|
+ unsigned long head, target_size, target, target_type;
|
|
|
|
+ struct dminfo_target_analyzer *ta;
|
|
|
|
+ char buf[BUFSIZE];
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * head = dm_table.targets.
|
|
|
|
+ * This is the head of struct dm_target array.
|
|
|
|
+ */
|
|
|
|
+ GET_VALUE(table, dm_table, targets, head);
|
|
|
|
+ target_size = STRUCT_SIZE("struct dm_target");
|
|
|
|
+
|
|
|
|
+ fprintf(fp, " %-16s %-11s %s\n",
|
|
|
|
+ "TARGET", "TARGET_TYPE", "PRIVATE_DATA");
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < num_targets; i++, fprintf(fp, "\n")) {
|
|
|
|
+ target = head + target_size * i; /* Get next target */
|
|
|
|
+
|
|
|
|
+ /* Get target information */
|
|
|
|
+ GET_VALUE(target, dm_target, type, target_type);
|
|
|
|
+ GET_PTR_STR(target_type, target_type, name, buf, BUFSIZE);
|
|
|
|
+
|
|
|
|
+ fprintf(fp, " %-16lx %-11s", target, buf);
|
|
|
|
+
|
|
|
|
+ if (!(ta = find_target_analyzer(buf)) || !ta->ready
|
|
|
|
+ || !ta->ready())
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ switch (info_type) {
|
|
|
|
+ case DMINFO_TABLE:
|
|
|
|
+ if (ta->show_table)
|
|
|
|
+ ta->show_table(target);
|
|
|
|
+ break;
|
|
|
|
+ case DMINFO_STATUS:
|
|
|
|
+ if (ta->show_status)
|
|
|
|
+ ta->show_status(target);
|
|
|
|
+ break;
|
|
|
|
+ case DMINFO_QUEUE:
|
|
|
|
+ if (ta->show_queue)
|
|
|
|
+ ta->show_queue(target);
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (i != num_targets)
|
|
|
|
+ fprintf(fp, " ERROR: targets are less than num_targets:%d",
|
|
|
|
+ num_targets);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Display lists (and detail information if specified) of existing
|
|
|
|
+ * dm devices.
|
|
|
|
+ */
|
|
|
|
+static void
|
|
|
|
+dminfo_show_list(int additional_info)
|
|
|
|
+{
|
|
|
|
+ int i, major, minor, array_len;
|
|
|
|
+ unsigned int num_targets;
|
|
|
|
+ unsigned long _name_buckets, head, next, last, md, dev, table;
|
|
|
|
+ char buf[BUFSIZE];
|
|
|
|
+
|
|
|
|
+ _name_buckets = symbol_value("_name_buckets");
|
|
|
|
+ array_len = get_array_length("_name_buckets", NULL, 0);
|
|
|
|
+
|
|
|
|
+ if (additional_info == DMINFO_LIST)
|
|
|
|
+ fprintf(fp, "%-3s %-3s %-16s %-16s %-7s %s\n",
|
|
|
|
+ "MAJ", "MIN", "MAP_DEV", "DM_TABLE",
|
|
|
|
+ "TARGETS", "MAPNAME");
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < array_len; i++) {
|
|
|
|
+ /* head = _name_buckets[i] */
|
|
|
|
+ head = _name_buckets + (i * SIZE(list_head));
|
|
|
|
+
|
|
|
|
+ list_for_each (next, head, last) { /* next == hash_cell */
|
|
|
|
+ /* Get device and table information */
|
|
|
|
+ GET_PTR_STR(next, hash_cell, name, buf, BUFSIZE);
|
|
|
|
+ GET_VALUE(next, hash_cell, md, md);
|
|
|
|
+ GET_VALUE(md, mapped_device, disk, dev);
|
|
|
|
+ GET_VALUE(dev, gendisk, major, major);
|
|
|
|
+ GET_VALUE(dev, gendisk, first_minor, minor);
|
|
|
|
+ GET_VALUE(md, mapped_device, map, table);
|
|
|
|
+ GET_VALUE(table, dm_table, num_targets, num_targets);
|
|
|
|
+
|
|
|
|
+ if (additional_info != DMINFO_LIST)
|
|
|
|
+ fprintf(fp, "%-3s %-3s %-16s %-16s %-7s %s\n",
|
|
|
|
+ "MAJ", "MIN", "MAP_DEV", "DM_TABLE",
|
|
|
|
+ "TARGETS", "MAPNAME");
|
|
|
|
+
|
|
|
|
+ fprintf(fp, "%-3d %-3d %-16lx %-16lx %-7d %s\n",
|
|
|
|
+ major, minor, md, table, num_targets, buf);
|
|
|
|
+
|
|
|
|
+ switch(additional_info) {
|
|
|
|
+ case DMINFO_DEPS:
|
|
|
|
+ dminfo_show_deps(table);
|
|
|
|
+ break;
|
|
|
|
+ case DMINFO_TABLE:
|
|
|
|
+ case DMINFO_STATUS:
|
|
|
|
+ case DMINFO_QUEUE:
|
|
|
|
+ dminfo_show_details(table, num_targets,
|
|
|
|
+ additional_info);
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (additional_info != DMINFO_LIST)
|
|
|
|
+ fprintf(fp, "\n");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Display the original bio information for the 'bio'.
|
|
|
|
+ * If the 'bio' is for dm devices, the original bio information is pointed
|
|
|
|
+ * by bio.bi_private as struct target_io.
|
|
|
|
+ */
|
|
|
|
+static void
|
|
|
|
+dminfo_show_bio(unsigned long bio)
|
|
|
|
+{
|
|
|
|
+ int major, minor;
|
|
|
|
+ unsigned long target_io, dm_io, dm_bio, md, dev;
|
|
|
|
+ char buf[BUFSIZE];
|
|
|
|
+
|
|
|
|
+ /* Get original bio and device information */
|
|
|
|
+ GET_VALUE(bio, bio, bi_private, target_io);
|
|
|
|
+ GET_VALUE(target_io, target_io, io, dm_io);
|
|
|
|
+ GET_VALUE(dm_io, dm_io, bio, dm_bio);
|
|
|
|
+ GET_VALUE(dm_io, dm_io, md, md);
|
|
|
|
+ GET_VALUE(md, mapped_device, disk, dev);
|
|
|
|
+ GET_VALUE(dev, gendisk, major, major);
|
|
|
|
+ GET_VALUE(dev, gendisk, first_minor, minor);
|
|
|
|
+ GET_STR(dev, gendisk, disk_name, buf, BUFSIZE);
|
|
|
|
+
|
|
|
|
+ fprintf(fp, "%-16s %-3s %-3s %-16s %s\n",
|
|
|
|
+ "DM_BIO_ADDRESS", "MAJ", "MIN", "MAP_DEV", "DEVNAME");
|
|
|
|
+ fprintf(fp, "%-16lx %-3d %-3d %-16lx %s\n",
|
|
|
|
+ dm_bio, major, minor, md, buf);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+cmd_dminfo(void)
|
|
|
|
+{
|
|
|
|
+ int c, additional_info = DMINFO_LIST;
|
|
|
|
+ unsigned long bio;
|
|
|
|
+
|
|
|
|
+ if (!dm_core_ready())
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ /* Parse command line option */
|
|
|
|
+ while ((c = getopt(argcnt, args, "b:dlqst")) != EOF) {
|
|
|
|
+ switch(c)
|
|
|
|
+ {
|
|
|
|
+ case 'b':
|
|
|
|
+ bio = stol(optarg, FAULT_ON_ERROR, NULL);
|
|
|
|
+ dminfo_show_bio(bio);
|
|
|
|
+ return;
|
|
|
|
+ case 'd':
|
|
|
|
+ additional_info = DMINFO_DEPS;
|
|
|
|
+ break;
|
|
|
|
+ case 'l':
|
|
|
|
+ additional_info = DMINFO_LIST;
|
|
|
|
+ break;
|
|
|
|
+ case 'q':
|
|
|
|
+ additional_info = DMINFO_QUEUE;
|
|
|
|
+ break;
|
|
|
|
+ case 's':
|
|
|
|
+ additional_info = DMINFO_STATUS;
|
|
|
|
+ break;
|
|
|
|
+ case 't':
|
|
|
|
+ additional_info = DMINFO_TABLE;
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ argerrs++;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (argerrs)
|
|
|
|
+ cmd_usage(pc->curcmd, SYNOPSIS);
|
|
|
|
+
|
|
|
|
+ dminfo_show_list(additional_info);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * dminfo help
|
|
|
|
+ */
|
|
|
|
+static char *help_dminfo[] = {
|
|
|
|
+ "dminfo", /* command name */
|
|
|
|
+ "device mapper (dm) information", /* short description */
|
|
|
|
+ "[-b bio | -d | -l | -q | -s | -t]", /* argument synopsis */
|
|
|
|
+ " This command displays information about device-mapper mapped ",
|
|
|
|
+ " devices (dm devices).",
|
|
|
|
+ " If no argument is entered, displays lists of existing dm devices.",
|
|
|
|
+ " It's same as -l option.",
|
|
|
|
+ "",
|
|
|
|
+ " -b bio displays the information of the dm device which the bio",
|
|
|
|
+ " is submitted in. If the bio isn't for dm devices,",
|
|
|
|
+ " results will be error.",
|
|
|
|
+ " -d displays dependency information for existing dm devices.",
|
|
|
|
+ " -l displays lists of existing dm devices.",
|
|
|
|
+ " -q displays queued I/O information for each target of",
|
|
|
|
+ " existing dm devices.",
|
|
|
|
+ " -s displays status information for each target of existing",
|
|
|
|
+ " dm devices.",
|
|
|
|
+ " -t displays table information for each target of existing",
|
|
|
|
+ " dm devices.",
|
|
|
|
+ "",
|
|
|
|
+ "EXAMPLE",
|
|
|
|
+ " Display lists of dm devices. \"MAP_DEV\" is the address of the",
|
|
|
|
+ " struct mapped_device. \"DM_TABLE\" is the address of the struct",
|
|
|
|
+ " dm_table. \"TARGETS\" is the number of targets which are in",
|
|
|
|
+ " the struct dm_table.",
|
|
|
|
+ "",
|
|
|
|
+ " %s> dminfo",
|
|
|
|
+ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME",
|
|
|
|
+ " 253 8 c4866c80 c4866280 1 vg0-snap0",
|
|
|
|
+ " 253 6 f6a04a80 f6a04580 1 vg0-lv0-real",
|
|
|
|
+ " 253 0 c4840380 c4841880 1 mp0",
|
|
|
|
+ " 253 5 f7c50c80 c488e480 1 via_cbeheddbdd",
|
|
|
|
+ " 253 7 c4866a80 c4866380 1 vg0-snap0-cow",
|
|
|
|
+ " 253 4 d441e280 c919ed80 1 dummy1",
|
|
|
|
+ " 253 3 f5dc4280 cba81d80 1 dummy0",
|
|
|
|
+ " 253 2 f7c53180 c4866180 1 vg0-lv0",
|
|
|
|
+ " 253 1 f746d280 f746cd80 1 mp0p1",
|
|
|
|
+ "",
|
|
|
|
+ " Display the dm device information which the bio is submitted in.",
|
|
|
|
+ " The bio (ceacee80) is a clone of the bio (ceacee00) which is",
|
|
|
|
+ " submitted in the dm-3 (dummy0). And the bio (ceacee00) is a clone",
|
|
|
|
+ " of the bio (ceaced80) which is submitted in the dm-4 (dummy1), too.",
|
|
|
|
+ " The bio (ceaced80) is the original bio.",
|
|
|
|
+ "",
|
|
|
|
+ " %s> dminfo -b ceacee80",
|
|
|
|
+ " DM_BIO_ADDRESS MAJ MIN MAP_DEV DEVNAME",
|
|
|
|
+ " ceacee00 253 3 f5dc4280 dm-3",
|
|
|
|
+ " crash> dminfo -b ceacee00",
|
|
|
|
+ " DM_BIO_ADDRESS MAJ MIN MAP_DEV DEVNAME",
|
|
|
|
+ " ceaced80 253 4 d441e280 dm-4",
|
|
|
|
+ " crash> dminfo -b ceaced80",
|
|
|
|
+ " dminfo: invalid kernel virtual address: 64 type: \"GET_VALUE: dm_io.bio\"",
|
|
|
|
+ "",
|
|
|
|
+ " Display dependency information for each target.",
|
|
|
|
+ " The vg0-snap0 depends on thd dm-6 (vg0-lv0-real) and the dm-7",
|
|
|
|
+ " (vg0-snap0-cow)",
|
|
|
|
+ "",
|
|
|
|
+ " %s> dminfo -d",
|
|
|
|
+ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME",
|
|
|
|
+ " 253 8 c4866c80 c4866280 1 vg0-snap0",
|
|
|
|
+ " MAJ MIN GENDISK COUNT DEVNAME",
|
|
|
|
+ " 253 7 c4866980 1 dm-7",
|
|
|
|
+ " 253 6 f6a04280 1 dm-6",
|
|
|
|
+ "",
|
|
|
|
+ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME",
|
|
|
|
+ " 253 6 f6a04a80 f6a04580 1 vg0-lv0-real",
|
|
|
|
+ " MAJ MIN GENDISK COUNT DEVNAME",
|
|
|
|
+ " 8 0 f7f24c80 1 sda",
|
|
|
|
+ "",
|
|
|
|
+ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME",
|
|
|
|
+ " 253 7 c4866a80 c4866380 1 vg0-snap0-cow",
|
|
|
|
+ " MAJ MIN GENDISK COUNT DEVNAME",
|
|
|
|
+ " 8 0 f7f24c80 1 sda",
|
|
|
|
+ "",
|
|
|
|
+ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME",
|
|
|
|
+ " 253 2 f7c53180 c4866180 1 vg0-lv0",
|
|
|
|
+ " MAJ MIN GENDISK COUNT DEVNAME",
|
|
|
|
+ " 253 6 f6a04280 1 dm-6",
|
|
|
|
+ "",
|
|
|
|
+ " Display queued I/O information for each target.",
|
|
|
|
+ " The information is displayed under the \"PRIVATE_DATA\" column.",
|
|
|
|
+ "",
|
|
|
|
+ " %s> dminfo -q",
|
|
|
|
+ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME",
|
|
|
|
+ " 253 5 f7c50c80 c488e480 1 via_cbeheddbdd",
|
|
|
|
+ " TARGET TARGET_TYPE PRIVATE_DATA",
|
|
|
|
+ " f8961080 mirror (reads) (writes) (quiesced) (recovered)",
|
|
|
|
+ "",
|
|
|
|
+ " --------------------------------------------------------------",
|
|
|
|
+ " \"reads/writes\" are members of the struct mirror_set, and",
|
|
|
|
+ " \"quiesced/recovered\" are members of the struct region_hash.",
|
|
|
|
+ " If the list is empty, the member is bracketed by \"()\".",
|
|
|
|
+ " --------------------------------------------------------------",
|
|
|
|
+ "",
|
|
|
|
+ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME",
|
|
|
|
+ " 253 0 c4840380 c4841880 1 mp0",
|
|
|
|
+ " TARGET TARGET_TYPE PRIVATE_DATA",
|
|
|
|
+ " f8802080 multipath queue_size:0",
|
|
|
|
+ "",
|
|
|
|
+ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME",
|
|
|
|
+ " 253 1 f746d280 f746cd80 1 mp0p1",
|
|
|
|
+ " TARGET TARGET_TYPE PRIVATE_DATA",
|
|
|
|
+ " f8821080 linear No queue info",
|
|
|
|
+ "",
|
|
|
|
+ " Display status information for each target.",
|
|
|
|
+ " The information is displayed under the \"PRIVATE_DATA\" column.",
|
|
|
|
+ "",
|
|
|
|
+ " %s> dminfo -s",
|
|
|
|
+ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME",
|
|
|
|
+ " 253 0 c4840380 c4841880 1 mp0",
|
|
|
|
+ " TARGET TARGET_TYPE PRIVATE_DATA",
|
|
|
|
+ " f8802080 multipath queue_if_no_path:0 hwh:none nr_pgs:1",
|
|
|
|
+ " PG PG_STATUS NR_PATHS PATHS",
|
|
|
|
+ " 1 active 2 8:16(A,0) 8:32(A,0)",
|
|
|
|
+ "",
|
|
|
|
+ " --------------------------------------------------------------",
|
|
|
|
+ " Format of \"PATHS\": <major>:<minor>(<status>,<fail_count>)",
|
|
|
|
+ " Status: A:active, F:faulty",
|
|
|
|
+ " Fail_count: the value of the struct pgpath.fail_count",
|
|
|
|
+ " --------------------------------------------------------------",
|
|
|
|
+ "",
|
|
|
|
+ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME",
|
|
|
|
+ " 253 5 f7c50c80 c488e480 1 via_cbeheddbdd",
|
|
|
|
+ " TARGET TARGET_TYPE PRIVATE_DATA",
|
|
|
|
+ " f8961080 mirror in_sync:1 dev:8:16(A,0),8:32(A,0)",
|
|
|
|
+ "",
|
|
|
|
+ " --------------------------------------------------------------",
|
|
|
|
+ " Format of \"dev\": <major>:<minor>(<status>,<error_count>)",
|
|
|
|
+ " Status: A:active, D:degraded",
|
|
|
|
+ " Error_count: the value of the struct mirror.error_count",
|
|
|
|
+ " --------------------------------------------------------------",
|
|
|
|
+ "",
|
|
|
|
+ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME",
|
|
|
|
+ " 253 1 f746d280 f746cd80 1 mp0p1",
|
|
|
|
+ " TARGET TARGET_TYPE PRIVATE_DATA",
|
|
|
|
+ " f8821080 linear No status info",
|
|
|
|
+ "",
|
|
|
|
+ " Display table information for each target.",
|
|
|
|
+ " The information is displayed under the \"PRIVATE_DATA\" column.",
|
|
|
|
+ "",
|
|
|
|
+ " %s> dminfo -t",
|
|
|
|
+ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME",
|
|
|
|
+ " 253 8 c4866c80 c4866280 1 vg0-snap0",
|
|
|
|
+ " TARGET TARGET_TYPE PRIVATE_DATA",
|
|
|
|
+ " f89b4080 snapshot orig:253:6 cow:253:7 type:P chunk_size:16",
|
|
|
|
+ "",
|
|
|
|
+ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME",
|
|
|
|
+ " 253 6 f6a04a80 f6a04580 1 vg0-lv0-real",
|
|
|
|
+ " TARGET TARGET_TYPE PRIVATE_DATA",
|
|
|
|
+ " f890f080 linear begin:0 len:204800 dev:8:5 offset:384",
|
|
|
|
+ "",
|
|
|
|
+ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME",
|
|
|
|
+ " 253 0 c4840380 c4841880 1 mp0",
|
|
|
|
+ " TARGET TARGET_TYPE PRIVATE_DATA",
|
|
|
|
+ " f8802080 multipath queue_if_no_path:0 hwh:none nr_pgs:1",
|
|
|
|
+ " PG PATH_SELECTOR NR_PATHS PATHS",
|
|
|
|
+ " 1 round-robin 2 8:16 8:32",
|
|
|
|
+ "",
|
|
|
|
+ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME",
|
|
|
|
+ " 253 5 f7c50c80 c488e480 1 via_cbeheddbdd",
|
|
|
|
+ " TARGET TARGET_TYPE PRIVATE_DATA",
|
|
|
|
+ " f8961080 mirror log:core dev:8:16(0),8:32(0)",
|
|
|
|
+ "",
|
|
|
|
+ " --------------------------------------------------------------",
|
|
|
|
+ " Format of \"dev\": <major>:<minor>(<offset>)",
|
|
|
|
+ " Offset: the value of the struct mirror.offset",
|
|
|
|
+ " --------------------------------------------------------------",
|
|
|
|
+ "",
|
|
|
|
+ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME",
|
|
|
|
+ " 253 7 c4866a80 c4866380 1 vg0-snap0-cow",
|
|
|
|
+ " TARGET TARGET_TYPE PRIVATE_DATA",
|
|
|
|
+ " f899d080 linear begin:0 len:8192 dev:8:5 offset:205184",
|
|
|
|
+ "",
|
|
|
|
+ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME",
|
|
|
|
+ " 253 2 f7c53180 c4866180 1 vg0-lv0",
|
|
|
|
+ " TARGET TARGET_TYPE PRIVATE_DATA",
|
|
|
|
+ " f8bbc080 snapshot-origin orig_dev:253:6",
|
|
|
|
+ "",
|
|
|
|
+ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME",
|
|
|
|
+ " 253 1 f746d280 f746cd80 1 mp0p1",
|
|
|
|
+ " TARGET TARGET_TYPE PRIVATE_DATA",
|
|
|
|
+ " f8821080 linear begin:0 len:2040192 dev:253:0 offset:63",
|
|
|
|
+ NULL
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Registering command extension
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+static struct command_table_entry command_table[] = {
|
|
|
|
+ {"dminfo", cmd_dminfo, help_dminfo, 0},
|
|
|
|
+ {NULL, NULL, NULL, 0},
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+int _init()
|
|
|
|
+{
|
|
|
|
+ register_extension(command_table);
|
|
|
|
+
|
|
|
|
+ dminfo_register_target_analyzer(&zero_analyzer);
|
|
|
|
+ dminfo_register_target_analyzer(&error_analyzer);
|
|
|
|
+ dminfo_register_target_analyzer(&linear_analyzer);
|
|
|
|
+ dminfo_register_target_analyzer(&mirror_analyzer);
|
|
|
|
+ dminfo_register_target_analyzer(&multipath_analyzer);
|
|
|
|
+ dminfo_register_target_analyzer(&crypt_analyzer);
|
|
|
|
+ dminfo_register_target_analyzer(&stripe_analyzer);
|
|
|
|
+ dminfo_register_target_analyzer(&snapshot_analyzer);
|
|
|
|
+ dminfo_register_target_analyzer(&snapshot_origin_analyzer);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+int _fini()
|
|
|
|
+{
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
--- crash/gdb-6.1/gdb/ppc-linux-tdep.c.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/gdb-6.1/gdb/ppc-linux-tdep.c 2005-11-04 17:37:54.000000000 -0500
|
|
|
|
@@ -0,0 +1,1116 @@
|
|
|
|
+/* Target-dependent code for GDB, the GNU debugger.
|
|
|
|
+
|
|
|
|
+ Copyright 1986, 1987, 1989, 1991, 1992, 1993, 1994, 1995, 1996,
|
|
|
|
+ 1997, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
|
|
|
|
+ Copyright (c) 2004, 2005 Red Hat, Inc. All rights reserved.
|
|
|
|
+
|
|
|
|
+ This file is part of GDB.
|
|
|
|
+
|
|
|
|
+ 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.
|
|
|
|
+
|
|
|
|
+ You should have received a copy of the GNU General Public License
|
|
|
|
+ along with this program; if not, write to the Free Software
|
|
|
|
+ Foundation, Inc., 59 Temple Place - Suite 330,
|
|
|
|
+ Boston, MA 02111-1307, USA. */
|
|
|
|
+
|
|
|
|
+#include "defs.h"
|
|
|
|
+#include "frame.h"
|
|
|
|
+#include "inferior.h"
|
|
|
|
+#include "symtab.h"
|
|
|
|
+#include "target.h"
|
|
|
|
+#include "gdbcore.h"
|
|
|
|
+#include "gdbcmd.h"
|
|
|
|
+#include "symfile.h"
|
|
|
|
+#include "objfiles.h"
|
|
|
|
+#include "regcache.h"
|
|
|
|
+#include "value.h"
|
|
|
|
+#include "osabi.h"
|
|
|
|
+
|
|
|
|
+#include "solib-svr4.h"
|
|
|
|
+#include "ppc-tdep.h"
|
|
|
|
+
|
|
|
|
+/* The following instructions are used in the signal trampoline code
|
|
|
|
+ on GNU/Linux PPC. The kernel used to use magic syscalls 0x6666 and
|
|
|
|
+ 0x7777 but now uses the sigreturn syscalls. We check for both. */
|
|
|
|
+#define INSTR_LI_R0_0x6666 0x38006666
|
|
|
|
+#define INSTR_LI_R0_0x7777 0x38007777
|
|
|
|
+#define INSTR_LI_R0_NR_sigreturn 0x38000077
|
|
|
|
+#define INSTR_LI_R0_NR_rt_sigreturn 0x380000AC
|
|
|
|
+
|
|
|
|
+#define INSTR_SC 0x44000002
|
|
|
|
+
|
|
|
|
+/* Since the *-tdep.c files are platform independent (i.e, they may be
|
|
|
|
+ used to build cross platform debuggers), we can't include system
|
|
|
|
+ headers. Therefore, details concerning the sigcontext structure
|
|
|
|
+ must be painstakingly rerecorded. What's worse, if these details
|
|
|
|
+ ever change in the header files, they'll have to be changed here
|
|
|
|
+ as well. */
|
|
|
|
+
|
|
|
|
+/* __SIGNAL_FRAMESIZE from <asm/ptrace.h> */
|
|
|
|
+#define PPC_LINUX_SIGNAL_FRAMESIZE 64
|
|
|
|
+
|
|
|
|
+/* From <asm/sigcontext.h>, offsetof(struct sigcontext_struct, regs) == 0x1c */
|
|
|
|
+#define PPC_LINUX_REGS_PTR_OFFSET (PPC_LINUX_SIGNAL_FRAMESIZE + 0x1c)
|
|
|
|
+
|
|
|
|
+/* From <asm/sigcontext.h>,
|
|
|
|
+ offsetof(struct sigcontext_struct, handler) == 0x14 */
|
|
|
|
+#define PPC_LINUX_HANDLER_PTR_OFFSET (PPC_LINUX_SIGNAL_FRAMESIZE + 0x14)
|
|
|
|
+
|
|
|
|
+/* From <asm/ptrace.h>, values for PT_NIP, PT_R1, and PT_LNK */
|
|
|
|
+#define PPC_LINUX_PT_R0 0
|
|
|
|
+#define PPC_LINUX_PT_R1 1
|
|
|
|
+#define PPC_LINUX_PT_R2 2
|
|
|
|
+#define PPC_LINUX_PT_R3 3
|
|
|
|
+#define PPC_LINUX_PT_R4 4
|
|
|
|
+#define PPC_LINUX_PT_R5 5
|
|
|
|
+#define PPC_LINUX_PT_R6 6
|
|
|
|
+#define PPC_LINUX_PT_R7 7
|
|
|
|
+#define PPC_LINUX_PT_R8 8
|
|
|
|
+#define PPC_LINUX_PT_R9 9
|
|
|
|
+#define PPC_LINUX_PT_R10 10
|
|
|
|
+#define PPC_LINUX_PT_R11 11
|
|
|
|
+#define PPC_LINUX_PT_R12 12
|
|
|
|
+#define PPC_LINUX_PT_R13 13
|
|
|
|
+#define PPC_LINUX_PT_R14 14
|
|
|
|
+#define PPC_LINUX_PT_R15 15
|
|
|
|
+#define PPC_LINUX_PT_R16 16
|
|
|
|
+#define PPC_LINUX_PT_R17 17
|
|
|
|
+#define PPC_LINUX_PT_R18 18
|
|
|
|
+#define PPC_LINUX_PT_R19 19
|
|
|
|
+#define PPC_LINUX_PT_R20 20
|
|
|
|
+#define PPC_LINUX_PT_R21 21
|
|
|
|
+#define PPC_LINUX_PT_R22 22
|
|
|
|
+#define PPC_LINUX_PT_R23 23
|
|
|
|
+#define PPC_LINUX_PT_R24 24
|
|
|
|
+#define PPC_LINUX_PT_R25 25
|
|
|
|
+#define PPC_LINUX_PT_R26 26
|
|
|
|
+#define PPC_LINUX_PT_R27 27
|
|
|
|
+#define PPC_LINUX_PT_R28 28
|
|
|
|
+#define PPC_LINUX_PT_R29 29
|
|
|
|
+#define PPC_LINUX_PT_R30 30
|
|
|
|
+#define PPC_LINUX_PT_R31 31
|
|
|
|
+#define PPC_LINUX_PT_NIP 32
|
|
|
|
+#define PPC_LINUX_PT_MSR 33
|
|
|
|
+#define PPC_LINUX_PT_CTR 35
|
|
|
|
+#define PPC_LINUX_PT_LNK 36
|
|
|
|
+#define PPC_LINUX_PT_XER 37
|
|
|
|
+#define PPC_LINUX_PT_CCR 38
|
|
|
|
+#define PPC_LINUX_PT_MQ 39
|
|
|
|
+#define PPC_LINUX_PT_FPR0 48 /* each FP reg occupies 2 slots in this space */
|
|
|
|
+#define PPC_LINUX_PT_FPR31 (PPC_LINUX_PT_FPR0 + 2*31)
|
|
|
|
+#define PPC_LINUX_PT_FPSCR (PPC_LINUX_PT_FPR0 + 2*32 + 1)
|
|
|
|
+
|
|
|
|
+static int ppc_linux_at_sigtramp_return_path (CORE_ADDR pc);
|
|
|
|
+
|
|
|
|
+/* Determine if pc is in a signal trampoline...
|
|
|
|
+
|
|
|
|
+ Ha! That's not what this does at all. wait_for_inferior in
|
|
|
|
+ infrun.c calls PC_IN_SIGTRAMP in order to detect entry into a
|
|
|
|
+ signal trampoline just after delivery of a signal. But on
|
|
|
|
+ GNU/Linux, signal trampolines are used for the return path only.
|
|
|
|
+ The kernel sets things up so that the signal handler is called
|
|
|
|
+ directly.
|
|
|
|
+
|
|
|
|
+ If we use in_sigtramp2() in place of in_sigtramp() (see below)
|
|
|
|
+ we'll (often) end up with stop_pc in the trampoline and prev_pc in
|
|
|
|
+ the (now exited) handler. The code there will cause a temporary
|
|
|
|
+ breakpoint to be set on prev_pc which is not very likely to get hit
|
|
|
|
+ again.
|
|
|
|
+
|
|
|
|
+ If this is confusing, think of it this way... the code in
|
|
|
|
+ wait_for_inferior() needs to be able to detect entry into a signal
|
|
|
|
+ trampoline just after a signal is delivered, not after the handler
|
|
|
|
+ has been run.
|
|
|
|
+
|
|
|
|
+ So, we define in_sigtramp() below to return 1 if the following is
|
|
|
|
+ true:
|
|
|
|
+
|
|
|
|
+ 1) The previous frame is a real signal trampoline.
|
|
|
|
+
|
|
|
|
+ - and -
|
|
|
|
+
|
|
|
|
+ 2) pc is at the first or second instruction of the corresponding
|
|
|
|
+ handler.
|
|
|
|
+
|
|
|
|
+ Why the second instruction? It seems that wait_for_inferior()
|
|
|
|
+ never sees the first instruction when single stepping. When a
|
|
|
|
+ signal is delivered while stepping, the next instruction that
|
|
|
|
+ would've been stepped over isn't, instead a signal is delivered and
|
|
|
|
+ the first instruction of the handler is stepped over instead. That
|
|
|
|
+ puts us on the second instruction. (I added the test for the
|
|
|
|
+ first instruction long after the fact, just in case the observed
|
|
|
|
+ behavior is ever fixed.)
|
|
|
|
+
|
|
|
|
+ PC_IN_SIGTRAMP is called from blockframe.c as well in order to set
|
|
|
|
+ the frame's type (if a SIGTRAMP_FRAME). Because of our strange
|
|
|
|
+ definition of in_sigtramp below, we can't rely on the frame's type
|
|
|
|
+ getting set correctly from within blockframe.c. This is why we
|
|
|
|
+ take pains to set it in init_extra_frame_info().
|
|
|
|
+
|
|
|
|
+ NOTE: cagney/2002-11-10: I suspect the real problem here is that
|
|
|
|
+ the get_prev_frame() only initializes the frame's type after the
|
|
|
|
+ call to INIT_FRAME_INFO. get_prev_frame() should be fixed, this
|
|
|
|
+ code shouldn't be working its way around a bug :-(. */
|
|
|
|
+
|
|
|
|
+int
|
|
|
|
+ppc_linux_in_sigtramp (CORE_ADDR pc, char *func_name)
|
|
|
|
+{
|
|
|
|
+ CORE_ADDR lr;
|
|
|
|
+ CORE_ADDR sp;
|
|
|
|
+ CORE_ADDR tramp_sp;
|
|
|
|
+ char buf[4];
|
|
|
|
+ CORE_ADDR handler;
|
|
|
|
+
|
|
|
|
+ lr = read_register (gdbarch_tdep (current_gdbarch)->ppc_lr_regnum);
|
|
|
|
+ if (!ppc_linux_at_sigtramp_return_path (lr))
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ sp = read_register (SP_REGNUM);
|
|
|
|
+
|
|
|
|
+ if (target_read_memory (sp, buf, sizeof (buf)) != 0)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ tramp_sp = extract_unsigned_integer (buf, 4);
|
|
|
|
+
|
|
|
|
+ if (target_read_memory (tramp_sp + PPC_LINUX_HANDLER_PTR_OFFSET, buf,
|
|
|
|
+ sizeof (buf)) != 0)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ handler = extract_unsigned_integer (buf, 4);
|
|
|
|
+
|
|
|
|
+ return (pc == handler || pc == handler + 4);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int
|
|
|
|
+insn_is_sigreturn (unsigned long pcinsn)
|
|
|
|
+{
|
|
|
|
+ switch(pcinsn)
|
|
|
|
+ {
|
|
|
|
+ case INSTR_LI_R0_0x6666:
|
|
|
|
+ case INSTR_LI_R0_0x7777:
|
|
|
|
+ case INSTR_LI_R0_NR_sigreturn:
|
|
|
|
+ case INSTR_LI_R0_NR_rt_sigreturn:
|
|
|
|
+ return 1;
|
|
|
|
+ default:
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * The signal handler trampoline is on the stack and consists of exactly
|
|
|
|
+ * two instructions. The easiest and most accurate way of determining
|
|
|
|
+ * whether the pc is in one of these trampolines is by inspecting the
|
|
|
|
+ * instructions. It'd be faster though if we could find a way to do this
|
|
|
|
+ * via some simple address comparisons.
|
|
|
|
+ */
|
|
|
|
+static int
|
|
|
|
+ppc_linux_at_sigtramp_return_path (CORE_ADDR pc)
|
|
|
|
+{
|
|
|
|
+ char buf[12];
|
|
|
|
+ unsigned long pcinsn;
|
|
|
|
+ if (target_read_memory (pc - 4, buf, sizeof (buf)) != 0)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ /* extract the instruction at the pc */
|
|
|
|
+ pcinsn = extract_unsigned_integer (buf + 4, 4);
|
|
|
|
+
|
|
|
|
+ return (
|
|
|
|
+ (insn_is_sigreturn (pcinsn)
|
|
|
|
+ && extract_unsigned_integer (buf + 8, 4) == INSTR_SC)
|
|
|
|
+ ||
|
|
|
|
+ (pcinsn == INSTR_SC
|
|
|
|
+ && insn_is_sigreturn (extract_unsigned_integer (buf, 4))));
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static CORE_ADDR
|
|
|
|
+ppc_linux_skip_trampoline_code (CORE_ADDR pc)
|
|
|
|
+{
|
|
|
|
+ char buf[4];
|
|
|
|
+ struct obj_section *sect;
|
|
|
|
+ struct objfile *objfile;
|
|
|
|
+ unsigned long insn;
|
|
|
|
+ CORE_ADDR plt_start = 0;
|
|
|
|
+ CORE_ADDR symtab = 0;
|
|
|
|
+ CORE_ADDR strtab = 0;
|
|
|
|
+ int num_slots = -1;
|
|
|
|
+ int reloc_index = -1;
|
|
|
|
+ CORE_ADDR plt_table;
|
|
|
|
+ CORE_ADDR reloc;
|
|
|
|
+ CORE_ADDR sym;
|
|
|
|
+ long symidx;
|
|
|
|
+ char symname[1024];
|
|
|
|
+ struct minimal_symbol *msymbol;
|
|
|
|
+
|
|
|
|
+ /* Find the section pc is in; return if not in .plt */
|
|
|
|
+ sect = find_pc_section (pc);
|
|
|
|
+ if (!sect || strcmp (sect->the_bfd_section->name, ".plt") != 0)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ objfile = sect->objfile;
|
|
|
|
+
|
|
|
|
+ /* Pick up the instruction at pc. It had better be of the
|
|
|
|
+ form
|
|
|
|
+ li r11, IDX
|
|
|
|
+
|
|
|
|
+ where IDX is an index into the plt_table. */
|
|
|
|
+
|
|
|
|
+ if (target_read_memory (pc, buf, 4) != 0)
|
|
|
|
+ return 0;
|
|
|
|
+ insn = extract_unsigned_integer (buf, 4);
|
|
|
|
+
|
|
|
|
+ if ((insn & 0xffff0000) != 0x39600000 /* li r11, VAL */ )
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ reloc_index = (insn << 16) >> 16;
|
|
|
|
+
|
|
|
|
+ /* Find the objfile that pc is in and obtain the information
|
|
|
|
+ necessary for finding the symbol name. */
|
|
|
|
+ for (sect = objfile->sections; sect < objfile->sections_end; ++sect)
|
|
|
|
+ {
|
|
|
|
+ const char *secname = sect->the_bfd_section->name;
|
|
|
|
+ if (strcmp (secname, ".plt") == 0)
|
|
|
|
+ plt_start = sect->addr;
|
|
|
|
+ else if (strcmp (secname, ".rela.plt") == 0)
|
|
|
|
+ num_slots = ((int) sect->endaddr - (int) sect->addr) / 12;
|
|
|
|
+ else if (strcmp (secname, ".dynsym") == 0)
|
|
|
|
+ symtab = sect->addr;
|
|
|
|
+ else if (strcmp (secname, ".dynstr") == 0)
|
|
|
|
+ strtab = sect->addr;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Make sure we have all the information we need. */
|
|
|
|
+ if (plt_start == 0 || num_slots == -1 || symtab == 0 || strtab == 0)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ /* Compute the value of the plt table */
|
|
|
|
+ plt_table = plt_start + 72 + 8 * num_slots;
|
|
|
|
+
|
|
|
|
+ /* Get address of the relocation entry (Elf32_Rela) */
|
|
|
|
+ if (target_read_memory (plt_table + reloc_index, buf, 4) != 0)
|
|
|
|
+ return 0;
|
|
|
|
+ reloc = extract_unsigned_integer (buf, 4);
|
|
|
|
+
|
|
|
|
+ sect = find_pc_section (reloc);
|
|
|
|
+ if (!sect)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ if (strcmp (sect->the_bfd_section->name, ".text") == 0)
|
|
|
|
+ return reloc;
|
|
|
|
+
|
|
|
|
+ /* Now get the r_info field which is the relocation type and symbol
|
|
|
|
+ index. */
|
|
|
|
+ if (target_read_memory (reloc + 4, buf, 4) != 0)
|
|
|
|
+ return 0;
|
|
|
|
+ symidx = extract_unsigned_integer (buf, 4);
|
|
|
|
+
|
|
|
|
+ /* Shift out the relocation type leaving just the symbol index */
|
|
|
|
+ /* symidx = ELF32_R_SYM(symidx); */
|
|
|
|
+ symidx = symidx >> 8;
|
|
|
|
+
|
|
|
|
+ /* compute the address of the symbol */
|
|
|
|
+ sym = symtab + symidx * 4;
|
|
|
|
+
|
|
|
|
+ /* Fetch the string table index */
|
|
|
|
+ if (target_read_memory (sym, buf, 4) != 0)
|
|
|
|
+ return 0;
|
|
|
|
+ symidx = extract_unsigned_integer (buf, 4);
|
|
|
|
+
|
|
|
|
+ /* Fetch the string; we don't know how long it is. Is it possible
|
|
|
|
+ that the following will fail because we're trying to fetch too
|
|
|
|
+ much? */
|
|
|
|
+ if (target_read_memory (strtab + symidx, symname, sizeof (symname)) != 0)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ /* This might not work right if we have multiple symbols with the
|
|
|
|
+ same name; the only way to really get it right is to perform
|
|
|
|
+ the same sort of lookup as the dynamic linker. */
|
|
|
|
+ msymbol = lookup_minimal_symbol_text (symname, NULL);
|
|
|
|
+ if (!msymbol)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ return SYMBOL_VALUE_ADDRESS (msymbol);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* The rs6000 version of FRAME_SAVED_PC will almost work for us. The
|
|
|
|
+ signal handler details are different, so we'll handle those here
|
|
|
|
+ and call the rs6000 version to do the rest. */
|
|
|
|
+CORE_ADDR
|
|
|
|
+ppc_linux_frame_saved_pc (struct frame_info *fi)
|
|
|
|
+{
|
|
|
|
+ if ((get_frame_type (fi) == SIGTRAMP_FRAME))
|
|
|
|
+ {
|
|
|
|
+ CORE_ADDR regs_addr =
|
|
|
|
+ read_memory_integer (get_frame_base (fi)
|
|
|
|
+ + PPC_LINUX_REGS_PTR_OFFSET, 4);
|
|
|
|
+ /* return the NIP in the regs array */
|
|
|
|
+ return read_memory_integer (regs_addr + 4 * PPC_LINUX_PT_NIP, 4);
|
|
|
|
+ }
|
|
|
|
+ else if (get_next_frame (fi)
|
|
|
|
+ && (get_frame_type (get_next_frame (fi)) == SIGTRAMP_FRAME))
|
|
|
|
+ {
|
|
|
|
+ CORE_ADDR regs_addr =
|
|
|
|
+ read_memory_integer (get_frame_base (get_next_frame (fi))
|
|
|
|
+ + PPC_LINUX_REGS_PTR_OFFSET, 4);
|
|
|
|
+ /* return LNK in the regs array */
|
|
|
|
+ return read_memory_integer (regs_addr + 4 * PPC_LINUX_PT_LNK, 4);
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ return rs6000_frame_saved_pc (fi);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void
|
|
|
|
+ppc_linux_init_extra_frame_info (int fromleaf, struct frame_info *fi)
|
|
|
|
+{
|
|
|
|
+ rs6000_init_extra_frame_info (fromleaf, fi);
|
|
|
|
+
|
|
|
|
+ if (get_next_frame (fi) != 0)
|
|
|
|
+ {
|
|
|
|
+ /* We're called from get_prev_frame_info; check to see if
|
|
|
|
+ this is a signal frame by looking to see if the pc points
|
|
|
|
+ at trampoline code */
|
|
|
|
+ if (ppc_linux_at_sigtramp_return_path (get_frame_pc (fi)))
|
|
|
|
+ deprecated_set_frame_type (fi, SIGTRAMP_FRAME);
|
|
|
|
+ else
|
|
|
|
+ /* FIXME: cagney/2002-11-10: Is this double bogus? What
|
|
|
|
+ happens if the frame has previously been marked as a dummy? */
|
|
|
|
+ deprecated_set_frame_type (fi, NORMAL_FRAME);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+int
|
|
|
|
+ppc_linux_frameless_function_invocation (struct frame_info *fi)
|
|
|
|
+{
|
|
|
|
+ /* We'll find the wrong thing if we let
|
|
|
|
+ rs6000_frameless_function_invocation () search for a signal trampoline */
|
|
|
|
+ if (ppc_linux_at_sigtramp_return_path (get_frame_pc (fi)))
|
|
|
|
+ return 0;
|
|
|
|
+ else
|
|
|
|
+ return rs6000_frameless_function_invocation (fi);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void
|
|
|
|
+ppc_linux_frame_init_saved_regs (struct frame_info *fi)
|
|
|
|
+{
|
|
|
|
+ if ((get_frame_type (fi) == SIGTRAMP_FRAME))
|
|
|
|
+ {
|
|
|
|
+ CORE_ADDR regs_addr;
|
|
|
|
+ int i;
|
|
|
|
+ if (deprecated_get_frame_saved_regs (fi))
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ frame_saved_regs_zalloc (fi);
|
|
|
|
+
|
|
|
|
+ regs_addr =
|
|
|
|
+ read_memory_integer (get_frame_base (fi)
|
|
|
|
+ + PPC_LINUX_REGS_PTR_OFFSET, 4);
|
|
|
|
+ deprecated_get_frame_saved_regs (fi)[PC_REGNUM] = regs_addr + 4 * PPC_LINUX_PT_NIP;
|
|
|
|
+ deprecated_get_frame_saved_regs (fi)[gdbarch_tdep (current_gdbarch)->ppc_ps_regnum] =
|
|
|
|
+ regs_addr + 4 * PPC_LINUX_PT_MSR;
|
|
|
|
+ deprecated_get_frame_saved_regs (fi)[gdbarch_tdep (current_gdbarch)->ppc_cr_regnum] =
|
|
|
|
+ regs_addr + 4 * PPC_LINUX_PT_CCR;
|
|
|
|
+ deprecated_get_frame_saved_regs (fi)[gdbarch_tdep (current_gdbarch)->ppc_lr_regnum] =
|
|
|
|
+ regs_addr + 4 * PPC_LINUX_PT_LNK;
|
|
|
|
+ deprecated_get_frame_saved_regs (fi)[gdbarch_tdep (current_gdbarch)->ppc_ctr_regnum] =
|
|
|
|
+ regs_addr + 4 * PPC_LINUX_PT_CTR;
|
|
|
|
+ deprecated_get_frame_saved_regs (fi)[gdbarch_tdep (current_gdbarch)->ppc_xer_regnum] =
|
|
|
|
+ regs_addr + 4 * PPC_LINUX_PT_XER;
|
|
|
|
+ deprecated_get_frame_saved_regs (fi)[gdbarch_tdep (current_gdbarch)->ppc_mq_regnum] =
|
|
|
|
+ regs_addr + 4 * PPC_LINUX_PT_MQ;
|
|
|
|
+ for (i = 0; i < 32; i++)
|
|
|
|
+ deprecated_get_frame_saved_regs (fi)[gdbarch_tdep (current_gdbarch)->ppc_gp0_regnum + i] =
|
|
|
|
+ regs_addr + 4 * PPC_LINUX_PT_R0 + 4 * i;
|
|
|
|
+ for (i = 0; i < 32; i++)
|
|
|
|
+ deprecated_get_frame_saved_regs (fi)[FP0_REGNUM + i] = regs_addr + 4 * PPC_LINUX_PT_FPR0 + 8 * i;
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ rs6000_frame_init_saved_regs (fi);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+CORE_ADDR
|
|
|
|
+ppc_linux_frame_chain (struct frame_info *thisframe)
|
|
|
|
+{
|
|
|
|
+ /* Kernel properly constructs the frame chain for the handler */
|
|
|
|
+ if ((get_frame_type (thisframe) == SIGTRAMP_FRAME))
|
|
|
|
+ return read_memory_integer (get_frame_base (thisframe), 4);
|
|
|
|
+ else
|
|
|
|
+ return rs6000_frame_chain (thisframe);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* ppc_linux_memory_remove_breakpoints attempts to remove a breakpoint
|
|
|
|
+ in much the same fashion as memory_remove_breakpoint in mem-break.c,
|
|
|
|
+ but is careful not to write back the previous contents if the code
|
|
|
|
+ in question has changed in between inserting the breakpoint and
|
|
|
|
+ removing it.
|
|
|
|
+
|
|
|
|
+ Here is the problem that we're trying to solve...
|
|
|
|
+
|
|
|
|
+ Once upon a time, before introducing this function to remove
|
|
|
|
+ breakpoints from the inferior, setting a breakpoint on a shared
|
|
|
|
+ library function prior to running the program would not work
|
|
|
|
+ properly. In order to understand the problem, it is first
|
|
|
|
+ necessary to understand a little bit about dynamic linking on
|
|
|
|
+ this platform.
|
|
|
|
+
|
|
|
|
+ A call to a shared library function is accomplished via a bl
|
|
|
|
+ (branch-and-link) instruction whose branch target is an entry
|
|
|
|
+ in the procedure linkage table (PLT). The PLT in the object
|
|
|
|
+ file is uninitialized. To gdb, prior to running the program, the
|
|
|
|
+ entries in the PLT are all zeros.
|
|
|
|
+
|
|
|
|
+ Once the program starts running, the shared libraries are loaded
|
|
|
|
+ and the procedure linkage table is initialized, but the entries in
|
|
|
|
+ the table are not (necessarily) resolved. Once a function is
|
|
|
|
+ actually called, the code in the PLT is hit and the function is
|
|
|
|
+ resolved. In order to better illustrate this, an example is in
|
|
|
|
+ order; the following example is from the gdb testsuite.
|
|
|
|
+
|
|
|
|
+ We start the program shmain.
|
|
|
|
+
|
|
|
|
+ [kev@arroyo testsuite]$ ../gdb gdb.base/shmain
|
|
|
|
+ [...]
|
|
|
|
+
|
|
|
|
+ We place two breakpoints, one on shr1 and the other on main.
|
|
|
|
+
|
|
|
|
+ (gdb) b shr1
|
|
|
|
+ Breakpoint 1 at 0x100409d4
|
|
|
|
+ (gdb) b main
|
|
|
|
+ Breakpoint 2 at 0x100006a0: file gdb.base/shmain.c, line 44.
|
|
|
|
+
|
|
|
|
+ Examine the instruction (and the immediatly following instruction)
|
|
|
|
+ upon which the breakpoint was placed. Note that the PLT entry
|
|
|
|
+ for shr1 contains zeros.
|
|
|
|
+
|
|
|
|
+ (gdb) x/2i 0x100409d4
|
|
|
|
+ 0x100409d4 <shr1>: .long 0x0
|
|
|
|
+ 0x100409d8 <shr1+4>: .long 0x0
|
|
|
|
+
|
|
|
|
+ Now run 'til main.
|
|
|
|
+
|
|
|
|
+ (gdb) r
|
|
|
|
+ Starting program: gdb.base/shmain
|
|
|
|
+ Breakpoint 1 at 0xffaf790: file gdb.base/shr1.c, line 19.
|
|
|
|
+
|
|
|
|
+ Breakpoint 2, main ()
|
|
|
|
+ at gdb.base/shmain.c:44
|
|
|
|
+ 44 g = 1;
|
|
|
|
+
|
|
|
|
+ Examine the PLT again. Note that the loading of the shared
|
|
|
|
+ library has initialized the PLT to code which loads a constant
|
|
|
|
+ (which I think is an index into the GOT) into r11 and then
|
|
|
|
+ branchs a short distance to the code which actually does the
|
|
|
|
+ resolving.
|
|
|
|
+
|
|
|
|
+ (gdb) x/2i 0x100409d4
|
|
|
|
+ 0x100409d4 <shr1>: li r11,4
|
|
|
|
+ 0x100409d8 <shr1+4>: b 0x10040984 <sg+4>
|
|
|
|
+ (gdb) c
|
|
|
|
+ Continuing.
|
|
|
|
+
|
|
|
|
+ Breakpoint 1, shr1 (x=1)
|
|
|
|
+ at gdb.base/shr1.c:19
|
|
|
|
+ 19 l = 1;
|
|
|
|
+
|
|
|
|
+ Now we've hit the breakpoint at shr1. (The breakpoint was
|
|
|
|
+ reset from the PLT entry to the actual shr1 function after the
|
|
|
|
+ shared library was loaded.) Note that the PLT entry has been
|
|
|
|
+ resolved to contain a branch that takes us directly to shr1.
|
|
|
|
+ (The real one, not the PLT entry.)
|
|
|
|
+
|
|
|
|
+ (gdb) x/2i 0x100409d4
|
|
|
|
+ 0x100409d4 <shr1>: b 0xffaf76c <shr1>
|
|
|
|
+ 0x100409d8 <shr1+4>: b 0x10040984 <sg+4>
|
|
|
|
+
|
|
|
|
+ The thing to note here is that the PLT entry for shr1 has been
|
|
|
|
+ changed twice.
|
|
|
|
+
|
|
|
|
+ Now the problem should be obvious. GDB places a breakpoint (a
|
|
|
|
+ trap instruction) on the zero value of the PLT entry for shr1.
|
|
|
|
+ Later on, after the shared library had been loaded and the PLT
|
|
|
|
+ initialized, GDB gets a signal indicating this fact and attempts
|
|
|
|
+ (as it always does when it stops) to remove all the breakpoints.
|
|
|
|
+
|
|
|
|
+ The breakpoint removal was causing the former contents (a zero
|
|
|
|
+ word) to be written back to the now initialized PLT entry thus
|
|
|
|
+ destroying a portion of the initialization that had occurred only a
|
|
|
|
+ short time ago. When execution continued, the zero word would be
|
|
|
|
+ executed as an instruction an an illegal instruction trap was
|
|
|
|
+ generated instead. (0 is not a legal instruction.)
|
|
|
|
+
|
|
|
|
+ The fix for this problem was fairly straightforward. The function
|
|
|
|
+ memory_remove_breakpoint from mem-break.c was copied to this file,
|
|
|
|
+ modified slightly, and renamed to ppc_linux_memory_remove_breakpoint.
|
|
|
|
+ In tm-linux.h, MEMORY_REMOVE_BREAKPOINT is defined to call this new
|
|
|
|
+ function.
|
|
|
|
+
|
|
|
|
+ The differences between ppc_linux_memory_remove_breakpoint () and
|
|
|
|
+ memory_remove_breakpoint () are minor. All that the former does
|
|
|
|
+ that the latter does not is check to make sure that the breakpoint
|
|
|
|
+ location actually contains a breakpoint (trap instruction) prior
|
|
|
|
+ to attempting to write back the old contents. If it does contain
|
|
|
|
+ a trap instruction, we allow the old contents to be written back.
|
|
|
|
+ Otherwise, we silently do nothing.
|
|
|
|
+
|
|
|
|
+ The big question is whether memory_remove_breakpoint () should be
|
|
|
|
+ changed to have the same functionality. The downside is that more
|
|
|
|
+ traffic is generated for remote targets since we'll have an extra
|
|
|
|
+ fetch of a memory word each time a breakpoint is removed.
|
|
|
|
+
|
|
|
|
+ For the time being, we'll leave this self-modifying-code-friendly
|
|
|
|
+ version in ppc-linux-tdep.c, but it ought to be migrated somewhere
|
|
|
|
+ else in the event that some other platform has similar needs with
|
|
|
|
+ regard to removing breakpoints in some potentially self modifying
|
|
|
|
+ code. */
|
|
|
|
+int
|
|
|
|
+ppc_linux_memory_remove_breakpoint (CORE_ADDR addr, char *contents_cache)
|
|
|
|
+{
|
|
|
|
+ const unsigned char *bp;
|
|
|
|
+ int val;
|
|
|
|
+ int bplen;
|
|
|
|
+ char old_contents[BREAKPOINT_MAX];
|
|
|
|
+
|
|
|
|
+ /* Determine appropriate breakpoint contents and size for this address. */
|
|
|
|
+ bp = BREAKPOINT_FROM_PC (&addr, &bplen);
|
|
|
|
+ if (bp == NULL)
|
|
|
|
+ error ("Software breakpoints not implemented for this target.");
|
|
|
|
+
|
|
|
|
+ val = target_read_memory (addr, old_contents, bplen);
|
|
|
|
+
|
|
|
|
+ /* If our breakpoint is no longer at the address, this means that the
|
|
|
|
+ program modified the code on us, so it is wrong to put back the
|
|
|
|
+ old value */
|
|
|
|
+ if (val == 0 && memcmp (bp, old_contents, bplen) == 0)
|
|
|
|
+ val = target_write_memory (addr, contents_cache, bplen);
|
|
|
|
+
|
|
|
|
+ return val;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* For historic reasons, PPC 32 GNU/Linux follows PowerOpen rather
|
|
|
|
+ than the 32 bit SYSV R4 ABI structure return convention - all
|
|
|
|
+ structures, no matter their size, are put in memory. Vectors,
|
|
|
|
+ which were added later, do get returned in a register though. */
|
|
|
|
+
|
|
|
|
+static enum return_value_convention
|
|
|
|
+ppc_linux_return_value (struct gdbarch *gdbarch, struct type *valtype,
|
|
|
|
+ struct regcache *regcache, void *readbuf,
|
|
|
|
+ const void *writebuf)
|
|
|
|
+{
|
|
|
|
+ if ((TYPE_CODE (valtype) == TYPE_CODE_STRUCT
|
|
|
|
+ || TYPE_CODE (valtype) == TYPE_CODE_UNION)
|
|
|
|
+ && !((TYPE_LENGTH (valtype) == 16 || TYPE_LENGTH (valtype) == 8)
|
|
|
|
+ && TYPE_VECTOR (valtype)))
|
|
|
|
+ return RETURN_VALUE_STRUCT_CONVENTION;
|
|
|
|
+ else
|
|
|
|
+ return ppc_sysv_abi_return_value (gdbarch, valtype, regcache, readbuf,
|
|
|
|
+ writebuf);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* Fetch (and possibly build) an appropriate link_map_offsets
|
|
|
|
+ structure for GNU/Linux PPC targets using the struct offsets
|
|
|
|
+ defined in link.h (but without actual reference to that file).
|
|
|
|
+
|
|
|
|
+ This makes it possible to access GNU/Linux PPC shared libraries
|
|
|
|
+ from a GDB that was not built on an GNU/Linux PPC host (for cross
|
|
|
|
+ debugging). */
|
|
|
|
+
|
|
|
|
+struct link_map_offsets *
|
|
|
|
+ppc_linux_svr4_fetch_link_map_offsets (void)
|
|
|
|
+{
|
|
|
|
+ static struct link_map_offsets lmo;
|
|
|
|
+ static struct link_map_offsets *lmp = NULL;
|
|
|
|
+
|
|
|
|
+ if (lmp == NULL)
|
|
|
|
+ {
|
|
|
|
+ lmp = &lmo;
|
|
|
|
+
|
|
|
|
+ lmo.r_debug_size = 8; /* The actual size is 20 bytes, but
|
|
|
|
+ this is all we need. */
|
|
|
|
+ lmo.r_map_offset = 4;
|
|
|
|
+ lmo.r_map_size = 4;
|
|
|
|
+
|
|
|
|
+ lmo.link_map_size = 20; /* The actual size is 560 bytes, but
|
|
|
|
+ this is all we need. */
|
|
|
|
+ lmo.l_addr_offset = 0;
|
|
|
|
+ lmo.l_addr_size = 4;
|
|
|
|
+
|
|
|
|
+ lmo.l_name_offset = 4;
|
|
|
|
+ lmo.l_name_size = 4;
|
|
|
|
+
|
|
|
|
+ lmo.l_next_offset = 12;
|
|
|
|
+ lmo.l_next_size = 4;
|
|
|
|
+
|
|
|
|
+ lmo.l_prev_offset = 16;
|
|
|
|
+ lmo.l_prev_size = 4;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return lmp;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/* Macros for matching instructions. Note that, since all the
|
|
|
|
+ operands are masked off before they're or-ed into the instruction,
|
|
|
|
+ you can use -1 to make masks. */
|
|
|
|
+
|
|
|
|
+#define insn_d(opcd, rts, ra, d) \
|
|
|
|
+ ((((opcd) & 0x3f) << 26) \
|
|
|
|
+ | (((rts) & 0x1f) << 21) \
|
|
|
|
+ | (((ra) & 0x1f) << 16) \
|
|
|
|
+ | ((d) & 0xffff))
|
|
|
|
+
|
|
|
|
+#define insn_ds(opcd, rts, ra, d, xo) \
|
|
|
|
+ ((((opcd) & 0x3f) << 26) \
|
|
|
|
+ | (((rts) & 0x1f) << 21) \
|
|
|
|
+ | (((ra) & 0x1f) << 16) \
|
|
|
|
+ | ((d) & 0xfffc) \
|
|
|
|
+ | ((xo) & 0x3))
|
|
|
|
+
|
|
|
|
+#define insn_xfx(opcd, rts, spr, xo) \
|
|
|
|
+ ((((opcd) & 0x3f) << 26) \
|
|
|
|
+ | (((rts) & 0x1f) << 21) \
|
|
|
|
+ | (((spr) & 0x1f) << 16) \
|
|
|
|
+ | (((spr) & 0x3e0) << 6) \
|
|
|
|
+ | (((xo) & 0x3ff) << 1))
|
|
|
|
+
|
|
|
|
+/* Read a PPC instruction from memory. PPC instructions are always
|
|
|
|
+ big-endian, no matter what endianness the program is running in, so
|
|
|
|
+ we can't use read_memory_integer or one of its friends here. */
|
|
|
|
+static unsigned int
|
|
|
|
+read_insn (CORE_ADDR pc)
|
|
|
|
+{
|
|
|
|
+ unsigned char buf[4];
|
|
|
|
+
|
|
|
|
+ read_memory (pc, buf, 4);
|
|
|
|
+ return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/* An instruction to match. */
|
|
|
|
+struct insn_pattern
|
|
|
|
+{
|
|
|
|
+ unsigned int mask; /* mask the insn with this... */
|
|
|
|
+ unsigned int data; /* ...and see if it matches this. */
|
|
|
|
+ int optional; /* If non-zero, this insn may be absent. */
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/* Return non-zero if the instructions at PC match the series
|
|
|
|
+ described in PATTERN, or zero otherwise. PATTERN is an array of
|
|
|
|
+ 'struct insn_pattern' objects, terminated by an entry whose mask is
|
|
|
|
+ zero.
|
|
|
|
+
|
|
|
|
+ When the match is successful, fill INSN[i] with what PATTERN[i]
|
|
|
|
+ matched. If PATTERN[i] is optional, and the instruction wasn't
|
|
|
|
+ present, set INSN[i] to 0 (which is not a valid PPC instruction).
|
|
|
|
+ INSN should have as many elements as PATTERN. Note that, if
|
|
|
|
+ PATTERN contains optional instructions which aren't present in
|
|
|
|
+ memory, then INSN will have holes, so INSN[i] isn't necessarily the
|
|
|
|
+ i'th instruction in memory. */
|
|
|
|
+static int
|
|
|
|
+insns_match_pattern (CORE_ADDR pc,
|
|
|
|
+ struct insn_pattern *pattern,
|
|
|
|
+ unsigned int *insn)
|
|
|
|
+{
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ for (i = 0; pattern[i].mask; i++)
|
|
|
|
+ {
|
|
|
|
+ insn[i] = read_insn (pc);
|
|
|
|
+ if ((insn[i] & pattern[i].mask) == pattern[i].data)
|
|
|
|
+ pc += 4;
|
|
|
|
+ else if (pattern[i].optional)
|
|
|
|
+ insn[i] = 0;
|
|
|
|
+ else
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 1;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/* Return the 'd' field of the d-form instruction INSN, properly
|
|
|
|
+ sign-extended. */
|
|
|
|
+static CORE_ADDR
|
|
|
|
+insn_d_field (unsigned int insn)
|
|
|
|
+{
|
|
|
|
+ return ((((CORE_ADDR) insn & 0xffff) ^ 0x8000) - 0x8000);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/* Return the 'ds' field of the ds-form instruction INSN, with the two
|
|
|
|
+ zero bits concatenated at the right, and properly
|
|
|
|
+ sign-extended. */
|
|
|
|
+static CORE_ADDR
|
|
|
|
+insn_ds_field (unsigned int insn)
|
|
|
|
+{
|
|
|
|
+ return ((((CORE_ADDR) insn & 0xfffc) ^ 0x8000) - 0x8000);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/* If DESC is the address of a 64-bit PowerPC GNU/Linux function
|
|
|
|
+ descriptor, return the descriptor's entry point. */
|
|
|
|
+static CORE_ADDR
|
|
|
|
+ppc64_desc_entry_point (CORE_ADDR desc)
|
|
|
|
+{
|
|
|
|
+ /* The first word of the descriptor is the entry point. */
|
|
|
|
+ return (CORE_ADDR) read_memory_unsigned_integer (desc, 8);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/* Pattern for the standard linkage function. These are built by
|
|
|
|
+ build_plt_stub in elf64-ppc.c, whose GLINK argument is always
|
|
|
|
+ zero. */
|
|
|
|
+static struct insn_pattern ppc64_standard_linkage[] =
|
|
|
|
+ {
|
|
|
|
+ /* addis r12, r2, <any> */
|
|
|
|
+ { insn_d (-1, -1, -1, 0), insn_d (15, 12, 2, 0), 0 },
|
|
|
|
+
|
|
|
|
+ /* std r2, 40(r1) */
|
|
|
|
+ { -1, insn_ds (62, 2, 1, 40, 0), 0 },
|
|
|
|
+
|
|
|
|
+ /* ld r11, <any>(r12) */
|
|
|
|
+ { insn_ds (-1, -1, -1, 0, -1), insn_ds (58, 11, 12, 0, 0), 0 },
|
|
|
|
+
|
|
|
|
+ /* addis r12, r12, 1 <optional> */
|
|
|
|
+ { insn_d (-1, -1, -1, -1), insn_d (15, 12, 2, 1), 1 },
|
|
|
|
+
|
|
|
|
+ /* ld r2, <any>(r12) */
|
|
|
|
+ { insn_ds (-1, -1, -1, 0, -1), insn_ds (58, 2, 12, 0, 0), 0 },
|
|
|
|
+
|
|
|
|
+ /* addis r12, r12, 1 <optional> */
|
|
|
|
+ { insn_d (-1, -1, -1, -1), insn_d (15, 12, 2, 1), 1 },
|
|
|
|
+
|
|
|
|
+ /* mtctr r11 */
|
|
|
|
+ { insn_xfx (-1, -1, -1, -1), insn_xfx (31, 11, 9, 467),
|
|
|
|
+ 0 },
|
|
|
|
+
|
|
|
|
+ /* ld r11, <any>(r12) */
|
|
|
|
+ { insn_ds (-1, -1, -1, 0, -1), insn_ds (58, 11, 12, 0, 0), 0 },
|
|
|
|
+
|
|
|
|
+ /* bctr */
|
|
|
|
+ { -1, 0x4e800420, 0 },
|
|
|
|
+
|
|
|
|
+ { 0, 0, 0 }
|
|
|
|
+ };
|
|
|
|
+#define PPC64_STANDARD_LINKAGE_LEN \
|
|
|
|
+ (sizeof (ppc64_standard_linkage) / sizeof (ppc64_standard_linkage[0]))
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/* Recognize a 64-bit PowerPC GNU/Linux linkage function --- what GDB
|
|
|
|
+ calls a "solib trampoline". */
|
|
|
|
+static int
|
|
|
|
+ppc64_in_solib_call_trampoline (CORE_ADDR pc, char *name)
|
|
|
|
+{
|
|
|
|
+ /* Detecting solib call trampolines on PPC64 GNU/Linux is a pain.
|
|
|
|
+
|
|
|
|
+ It's not specifically solib call trampolines that are the issue.
|
|
|
|
+ Any call from one function to another function that uses a
|
|
|
|
+ different TOC requires a trampoline, to save the caller's TOC
|
|
|
|
+ pointer and then load the callee's TOC. An executable or shared
|
|
|
|
+ library may have more than one TOC, so even intra-object calls
|
|
|
|
+ may require a trampoline. Since executable and shared libraries
|
|
|
|
+ will all have their own distinct TOCs, every inter-object call is
|
|
|
|
+ also an inter-TOC call, and requires a trampoline --- so "solib
|
|
|
|
+ call trampolines" are just a special case.
|
|
|
|
+
|
|
|
|
+ The 64-bit PowerPC GNU/Linux ABI calls these call trampolines
|
|
|
|
+ "linkage functions". Since they need to be near the functions
|
|
|
|
+ that call them, they all appear in .text, not in any special
|
|
|
|
+ section. The .plt section just contains an array of function
|
|
|
|
+ descriptors, from which the linkage functions load the callee's
|
|
|
|
+ entry point, TOC value, and environment pointer. So
|
|
|
|
+ in_plt_section is useless. The linkage functions don't have any
|
|
|
|
+ special linker symbols to name them, either.
|
|
|
|
+
|
|
|
|
+ The only way I can see to recognize them is to actually look at
|
|
|
|
+ their code. They're generated by ppc_build_one_stub and some
|
|
|
|
+ other functions in bfd/elf64-ppc.c, so that should show us all
|
|
|
|
+ the instruction sequences we need to recognize. */
|
|
|
|
+ unsigned int insn[PPC64_STANDARD_LINKAGE_LEN];
|
|
|
|
+
|
|
|
|
+ return insns_match_pattern (pc, ppc64_standard_linkage, insn);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/* When the dynamic linker is doing lazy symbol resolution, the first
|
|
|
|
+ call to a function in another object will go like this:
|
|
|
|
+
|
|
|
|
+ - The user's function calls the linkage function:
|
|
|
|
+
|
|
|
|
+ 100007c4: 4b ff fc d5 bl 10000498
|
|
|
|
+ 100007c8: e8 41 00 28 ld r2,40(r1)
|
|
|
|
+
|
|
|
|
+ - The linkage function loads the entry point (and other stuff) from
|
|
|
|
+ the function descriptor in the PLT, and jumps to it:
|
|
|
|
+
|
|
|
|
+ 10000498: 3d 82 00 00 addis r12,r2,0
|
|
|
|
+ 1000049c: f8 41 00 28 std r2,40(r1)
|
|
|
|
+ 100004a0: e9 6c 80 98 ld r11,-32616(r12)
|
|
|
|
+ 100004a4: e8 4c 80 a0 ld r2,-32608(r12)
|
|
|
|
+ 100004a8: 7d 69 03 a6 mtctr r11
|
|
|
|
+ 100004ac: e9 6c 80 a8 ld r11,-32600(r12)
|
|
|
|
+ 100004b0: 4e 80 04 20 bctr
|
|
|
|
+
|
|
|
|
+ - But since this is the first time that PLT entry has been used, it
|
|
|
|
+ sends control to its glink entry. That loads the number of the
|
|
|
|
+ PLT entry and jumps to the common glink0 code:
|
|
|
|
+
|
|
|
|
+ 10000c98: 38 00 00 00 li r0,0
|
|
|
|
+ 10000c9c: 4b ff ff dc b 10000c78
|
|
|
|
+
|
|
|
|
+ - The common glink0 code then transfers control to the dynamic
|
|
|
|
+ linker's fixup code:
|
|
|
|
+
|
|
|
|
+ 10000c78: e8 41 00 28 ld r2,40(r1)
|
|
|
|
+ 10000c7c: 3d 82 00 00 addis r12,r2,0
|
|
|
|
+ 10000c80: e9 6c 80 80 ld r11,-32640(r12)
|
|
|
|
+ 10000c84: e8 4c 80 88 ld r2,-32632(r12)
|
|
|
|
+ 10000c88: 7d 69 03 a6 mtctr r11
|
|
|
|
+ 10000c8c: e9 6c 80 90 ld r11,-32624(r12)
|
|
|
|
+ 10000c90: 4e 80 04 20 bctr
|
|
|
|
+
|
|
|
|
+ Eventually, this code will figure out how to skip all of this,
|
|
|
|
+ including the dynamic linker. At the moment, we just get through
|
|
|
|
+ the linkage function. */
|
|
|
|
+
|
|
|
|
+/* If the current thread is about to execute a series of instructions
|
|
|
|
+ at PC matching the ppc64_standard_linkage pattern, and INSN is the result
|
|
|
|
+ from that pattern match, return the code address to which the
|
|
|
|
+ standard linkage function will send them. (This doesn't deal with
|
|
|
|
+ dynamic linker lazy symbol resolution stubs.) */
|
|
|
|
+static CORE_ADDR
|
|
|
|
+ppc64_standard_linkage_target (CORE_ADDR pc, unsigned int *insn)
|
|
|
|
+{
|
|
|
|
+ struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
|
|
|
|
+
|
|
|
|
+ /* The address of the function descriptor this linkage function
|
|
|
|
+ references. */
|
|
|
|
+ CORE_ADDR desc
|
|
|
|
+ = ((CORE_ADDR) read_register (tdep->ppc_gp0_regnum + 2)
|
|
|
|
+ + (insn_d_field (insn[0]) << 16)
|
|
|
|
+ + insn_ds_field (insn[2]));
|
|
|
|
+
|
|
|
|
+ /* The first word of the descriptor is the entry point. Return that. */
|
|
|
|
+ return ppc64_desc_entry_point (desc);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/* Given that we've begun executing a call trampoline at PC, return
|
|
|
|
+ the entry point of the function the trampoline will go to. */
|
|
|
|
+static CORE_ADDR
|
|
|
|
+ppc64_skip_trampoline_code (CORE_ADDR pc)
|
|
|
|
+{
|
|
|
|
+ unsigned int ppc64_standard_linkage_insn[PPC64_STANDARD_LINKAGE_LEN];
|
|
|
|
+
|
|
|
|
+ if (insns_match_pattern (pc, ppc64_standard_linkage,
|
|
|
|
+ ppc64_standard_linkage_insn))
|
|
|
|
+ return ppc64_standard_linkage_target (pc, ppc64_standard_linkage_insn);
|
|
|
|
+ else
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/* Support for CONVERT_FROM_FUNC_PTR_ADDR (ARCH, ADDR, TARG) on PPC64
|
|
|
|
+ GNU/Linux.
|
|
|
|
+
|
|
|
|
+ Usually a function pointer's representation is simply the address
|
|
|
|
+ of the function. On GNU/Linux on the 64-bit PowerPC however, a
|
|
|
|
+ function pointer is represented by a pointer to a TOC entry. This
|
|
|
|
+ TOC entry contains three words, the first word is the address of
|
|
|
|
+ the function, the second word is the TOC pointer (r2), and the
|
|
|
|
+ third word is the static chain value. Throughout GDB it is
|
|
|
|
+ currently assumed that a function pointer contains the address of
|
|
|
|
+ the function, which is not easy to fix. In addition, the
|
|
|
|
+ conversion of a function address to a function pointer would
|
|
|
|
+ require allocation of a TOC entry in the inferior's memory space,
|
|
|
|
+ with all its drawbacks. To be able to call C++ virtual methods in
|
|
|
|
+ the inferior (which are called via function pointers),
|
|
|
|
+ find_function_addr uses this function to get the function address
|
|
|
|
+ from a function pointer. */
|
|
|
|
+
|
|
|
|
+/* If ADDR points at what is clearly a function descriptor, transform
|
|
|
|
+ it into the address of the corresponding function. Be
|
|
|
|
+ conservative, otherwize GDB will do the transformation on any
|
|
|
|
+ random addresses such as occures when there is no symbol table. */
|
|
|
|
+
|
|
|
|
+static CORE_ADDR
|
|
|
|
+ppc64_linux_convert_from_func_ptr_addr (struct gdbarch *gdbarch,
|
|
|
|
+ CORE_ADDR addr,
|
|
|
|
+ struct target_ops *targ)
|
|
|
|
+{
|
|
|
|
+ struct section_table *s = target_section_by_addr (targ, addr);
|
|
|
|
+
|
|
|
|
+ /* Check if ADDR points to a function descriptor. */
|
|
|
|
+ if (s && strcmp (s->the_bfd_section->name, ".opd") == 0)
|
|
|
|
+ return get_target_memory_unsigned (targ, addr, 8);
|
|
|
|
+
|
|
|
|
+ return addr;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+#ifdef CRASH_MERGE
|
|
|
|
+enum {
|
|
|
|
+ PPC_ELF_NGREG = 48,
|
|
|
|
+ PPC_ELF_NFPREG = 33,
|
|
|
|
+ PPC_ELF_NVRREG = 33
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+enum {
|
|
|
|
+ ELF_GREGSET_SIZE = (PPC_ELF_NGREG * 4),
|
|
|
|
+ ELF_FPREGSET_SIZE = (PPC_ELF_NFPREG * 8)
|
|
|
|
+};
|
|
|
|
+#else
|
|
|
|
+enum {
|
|
|
|
+ ELF_NGREG = 48,
|
|
|
|
+ ELF_NFPREG = 33,
|
|
|
|
+ ELF_NVRREG = 33
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+enum {
|
|
|
|
+ ELF_GREGSET_SIZE = (ELF_NGREG * 4),
|
|
|
|
+ ELF_FPREGSET_SIZE = (ELF_NFPREG * 8)
|
|
|
|
+};
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+void
|
|
|
|
+ppc_linux_supply_gregset (char *buf)
|
|
|
|
+{
|
|
|
|
+ int regi;
|
|
|
|
+ struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
|
|
|
|
+
|
|
|
|
+ for (regi = 0; regi < 32; regi++)
|
|
|
|
+ supply_register (regi, buf + 4 * regi);
|
|
|
|
+
|
|
|
|
+ supply_register (PC_REGNUM, buf + 4 * PPC_LINUX_PT_NIP);
|
|
|
|
+ supply_register (tdep->ppc_lr_regnum, buf + 4 * PPC_LINUX_PT_LNK);
|
|
|
|
+ supply_register (tdep->ppc_cr_regnum, buf + 4 * PPC_LINUX_PT_CCR);
|
|
|
|
+ supply_register (tdep->ppc_xer_regnum, buf + 4 * PPC_LINUX_PT_XER);
|
|
|
|
+ supply_register (tdep->ppc_ctr_regnum, buf + 4 * PPC_LINUX_PT_CTR);
|
|
|
|
+ if (tdep->ppc_mq_regnum != -1)
|
|
|
|
+ supply_register (tdep->ppc_mq_regnum, buf + 4 * PPC_LINUX_PT_MQ);
|
|
|
|
+ supply_register (tdep->ppc_ps_regnum, buf + 4 * PPC_LINUX_PT_MSR);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void
|
|
|
|
+ppc_linux_supply_fpregset (char *buf)
|
|
|
|
+{
|
|
|
|
+ int regi;
|
|
|
|
+ struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
|
|
|
|
+
|
|
|
|
+ for (regi = 0; regi < 32; regi++)
|
|
|
|
+ supply_register (FP0_REGNUM + regi, buf + 8 * regi);
|
|
|
|
+
|
|
|
|
+ /* The FPSCR is stored in the low order word of the last doubleword in the
|
|
|
|
+ fpregset. */
|
|
|
|
+ supply_register (tdep->ppc_fpscr_regnum, buf + 8 * 32 + 4);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ Use a local version of this function to get the correct types for regsets.
|
|
|
|
+*/
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+fetch_core_registers (char *core_reg_sect,
|
|
|
|
+ unsigned core_reg_size,
|
|
|
|
+ int which,
|
|
|
|
+ CORE_ADDR reg_addr)
|
|
|
|
+{
|
|
|
|
+ if (which == 0)
|
|
|
|
+ {
|
|
|
|
+ if (core_reg_size == ELF_GREGSET_SIZE)
|
|
|
|
+ ppc_linux_supply_gregset (core_reg_sect);
|
|
|
|
+ else
|
|
|
|
+ warning ("wrong size gregset struct in core file");
|
|
|
|
+ }
|
|
|
|
+ else if (which == 2)
|
|
|
|
+ {
|
|
|
|
+ if (core_reg_size == ELF_FPREGSET_SIZE)
|
|
|
|
+ ppc_linux_supply_fpregset (core_reg_sect);
|
|
|
|
+ else
|
|
|
|
+ warning ("wrong size fpregset struct in core file");
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* Register that we are able to handle ELF file formats using standard
|
|
|
|
+ procfs "regset" structures. */
|
|
|
|
+
|
|
|
|
+static struct core_fns ppc_linux_regset_core_fns =
|
|
|
|
+{
|
|
|
|
+ bfd_target_elf_flavour, /* core_flavour */
|
|
|
|
+ default_check_format, /* check_format */
|
|
|
|
+ default_core_sniffer, /* core_sniffer */
|
|
|
|
+ fetch_core_registers, /* core_read_registers */
|
|
|
|
+ NULL /* next */
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+ppc_linux_init_abi (struct gdbarch_info info,
|
|
|
|
+ struct gdbarch *gdbarch)
|
|
|
|
+{
|
|
|
|
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
|
|
|
|
+
|
|
|
|
+ if (tdep->wordsize == 4)
|
|
|
|
+ {
|
|
|
|
+ /* Until November 2001, gcc did not comply with the 32 bit SysV
|
|
|
|
+ R4 ABI requirement that structures less than or equal to 8
|
|
|
|
+ bytes should be returned in registers. Instead GCC was using
|
|
|
|
+ the the AIX/PowerOpen ABI - everything returned in memory
|
|
|
|
+ (well ignoring vectors that is). When this was corrected, it
|
|
|
|
+ wasn't fixed for GNU/Linux native platform. Use the
|
|
|
|
+ PowerOpen struct convention. */
|
|
|
|
+ set_gdbarch_return_value (gdbarch, ppc_linux_return_value);
|
|
|
|
+
|
|
|
|
+ /* Note: kevinb/2002-04-12: See note in rs6000_gdbarch_init regarding
|
|
|
|
+ *_push_arguments(). The same remarks hold for the methods below. */
|
|
|
|
+ set_gdbarch_deprecated_frameless_function_invocation (gdbarch, ppc_linux_frameless_function_invocation);
|
|
|
|
+ set_gdbarch_deprecated_frame_chain (gdbarch, ppc_linux_frame_chain);
|
|
|
|
+ set_gdbarch_deprecated_frame_saved_pc (gdbarch, ppc_linux_frame_saved_pc);
|
|
|
|
+
|
|
|
|
+ set_gdbarch_deprecated_frame_init_saved_regs (gdbarch,
|
|
|
|
+ ppc_linux_frame_init_saved_regs);
|
|
|
|
+ set_gdbarch_deprecated_init_extra_frame_info (gdbarch,
|
|
|
|
+ ppc_linux_init_extra_frame_info);
|
|
|
|
+
|
|
|
|
+ set_gdbarch_memory_remove_breakpoint (gdbarch,
|
|
|
|
+ ppc_linux_memory_remove_breakpoint);
|
|
|
|
+ /* Shared library handling. */
|
|
|
|
+ set_gdbarch_in_solib_call_trampoline (gdbarch, in_plt_section);
|
|
|
|
+ set_gdbarch_skip_trampoline_code (gdbarch,
|
|
|
|
+ ppc_linux_skip_trampoline_code);
|
|
|
|
+ set_solib_svr4_fetch_link_map_offsets
|
|
|
|
+ (gdbarch, ppc_linux_svr4_fetch_link_map_offsets);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (tdep->wordsize == 8)
|
|
|
|
+ {
|
|
|
|
+ /* Handle PPC64 GNU/Linux function pointers (which are really
|
|
|
|
+ function descriptors). */
|
|
|
|
+ set_gdbarch_convert_from_func_ptr_addr
|
|
|
|
+ (gdbarch, ppc64_linux_convert_from_func_ptr_addr);
|
|
|
|
+
|
|
|
|
+ set_gdbarch_in_solib_call_trampoline
|
|
|
|
+ (gdbarch, ppc64_in_solib_call_trampoline);
|
|
|
|
+ set_gdbarch_skip_trampoline_code (gdbarch, ppc64_skip_trampoline_code);
|
|
|
|
+
|
|
|
|
+ /* PPC64 malloc's entry-point is called ".malloc". */
|
|
|
|
+ set_gdbarch_name_of_malloc (gdbarch, ".malloc");
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void
|
|
|
|
+_initialize_ppc_linux_tdep (void)
|
|
|
|
+{
|
|
|
|
+ /* Register for all sub-familes of the POWER/PowerPC: 32-bit and
|
|
|
|
+ 64-bit PowerPC, and the older rs6k. */
|
|
|
|
+ gdbarch_register_osabi (bfd_arch_powerpc, bfd_mach_ppc, GDB_OSABI_LINUX,
|
|
|
|
+ ppc_linux_init_abi);
|
|
|
|
+ gdbarch_register_osabi (bfd_arch_powerpc, bfd_mach_ppc64, GDB_OSABI_LINUX,
|
|
|
|
+ ppc_linux_init_abi);
|
|
|
|
+ gdbarch_register_osabi (bfd_arch_rs6000, bfd_mach_rs6k, GDB_OSABI_LINUX,
|
|
|
|
+ ppc_linux_init_abi);
|
|
|
|
+ add_core_fns (&ppc_linux_regset_core_fns);
|
|
|
|
+}
|
|
|
|
--- crash/main.c.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/main.c 2005-11-18 10:41:07.000000000 -0500
|
|
|
|
@@ -55,7 +55,7 @@
|
|
|
|
*/
|
|
|
|
opterr = 0;
|
|
|
|
optind = 0;
|
|
|
|
- while((c = getopt_long(argc, argv, "LgH:h:e:i:sSvc:d:tf",
|
|
|
|
+ while((c = getopt_long(argc, argv, "LgH:h:e:i:sSvc:d:tfp:",
|
|
|
|
long_options, &option_index)) != -1) {
|
|
|
|
switch (c)
|
|
|
|
{
|
|
|
|
@@ -193,6 +193,10 @@
|
|
|
|
set_vas_debug(pc->debug);
|
|
|
|
break;
|
|
|
|
|
|
|
|
+ case 'p':
|
|
|
|
+ force_page_size(optarg);
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
default:
|
|
|
|
if (STREQ(argv[optind-1], "-h"))
|
|
|
|
program_usage(LONG_FORM);
|
|
|
|
@@ -264,6 +268,17 @@
|
|
|
|
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_diskdump(argv[optind])) {
|
|
|
|
if (pc->flags & MEMORY_SOURCES) {
|
|
|
|
error(INFO,
|
|
|
|
@@ -591,6 +606,8 @@
|
|
|
|
int i;
|
|
|
|
char *p1;
|
|
|
|
char buf[BUFSIZE];
|
|
|
|
+ char homerc[BUFSIZE];
|
|
|
|
+ char localrc[BUFSIZE];
|
|
|
|
FILE *afp;
|
|
|
|
char *program;
|
|
|
|
|
|
|
|
@@ -685,11 +702,11 @@
|
|
|
|
pc->home = "(unknown)";
|
|
|
|
} else
|
|
|
|
strcpy(pc->home, p1);
|
|
|
|
- sprintf(buf, "%s/.%src", pc->home, pc->program_name);
|
|
|
|
- if (!(pc->flags & NOCRASHRC) && file_exists(buf, NULL)) {
|
|
|
|
- if ((afp = fopen(buf, "r")) == NULL)
|
|
|
|
+ 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",
|
|
|
|
- buf, strerror(errno));
|
|
|
|
+ homerc, strerror(errno));
|
|
|
|
else {
|
|
|
|
while (fgets(buf, BUFSIZE, afp))
|
|
|
|
resolve_rc_cmd(buf, ALIAS_RCHOME);
|
|
|
|
@@ -698,11 +715,12 @@
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- sprintf(buf, ".%src", pc->program_name);
|
|
|
|
- if (!(pc->flags & NOCRASHRC) && file_exists(buf, NULL)) {
|
|
|
|
- if ((afp = fopen(buf, "r")) == NULL)
|
|
|
|
+ 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",
|
|
|
|
- buf, strerror(errno));
|
|
|
|
+ localrc, strerror(errno));
|
|
|
|
else {
|
|
|
|
while (fgets(buf, BUFSIZE, afp))
|
|
|
|
resolve_rc_cmd(buf, ALIAS_RCLOCAL);
|
|
|
|
@@ -840,13 +858,19 @@
|
|
|
|
if (pc->flags & REM_S390D)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sREM_S390D", others++ ? "|" : "");
|
|
|
|
- if (pc->flags & NETDUMP)
|
|
|
|
+ if (pc->flags & NETDUMP)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sNETDUMP", others++ ? "|" : "");
|
|
|
|
+ if (pc->flags & KDUMP)
|
|
|
|
+ sprintf(&buf[strlen(buf)],
|
|
|
|
+ "%sKDUMP", 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)
|
|
|
|
+ if (pc->flags & DISKDUMP)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sDISKDUMP", others++ ? "|" : "");
|
|
|
|
if (pc->flags & SYSMAP)
|
|
|
|
@@ -855,21 +879,24 @@
|
|
|
|
if (pc->flags & SYSMAP_ARG)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sSYSMAP_ARG", others++ ? "|" : "");
|
|
|
|
- if (pc->flags & DATADEBUG)
|
|
|
|
+ if (pc->flags & DATADEBUG)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sDATADEBUG", others++ ? "|" : "");
|
|
|
|
- if (pc->flags & FINDKERNEL)
|
|
|
|
+ if (pc->flags & FINDKERNEL)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sFINDKERNEL", others++ ? "|" : "");
|
|
|
|
- if (pc->flags & VERSION_QUERY)
|
|
|
|
+ if (pc->flags & VERSION_QUERY)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sVERSION_QUERY", others++ ? "|" : "");
|
|
|
|
- if (pc->flags & READNOW)
|
|
|
|
+ if (pc->flags & READNOW)
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
"%sREADNOW", others++ ? "|" : "");
|
|
|
|
- if (pc->flags & NOCRASHRC)
|
|
|
|
+ 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)
|
|
|
|
strcat(buf, ")");
|
|
|
|
@@ -1051,6 +1078,8 @@
|
|
|
|
fprintf(fp, " readmem: read_daemon()\n");
|
|
|
|
else if (pc->readmem == read_netdump)
|
|
|
|
fprintf(fp, " readmem: read_netdump()\n");
|
|
|
|
+ else if (pc->readmem == read_kdump)
|
|
|
|
+ fprintf(fp, " readmem: read_kdump()\n");
|
|
|
|
else if (pc->readmem == read_memory_device)
|
|
|
|
fprintf(fp, " readmem: read_memory_device()\n");
|
|
|
|
else
|
|
|
|
@@ -1065,6 +1094,8 @@
|
|
|
|
fprintf(fp, " writemem: write_daemon()\n");
|
|
|
|
else if (pc->writemem == write_netdump)
|
|
|
|
fprintf(fp, " writemem: write_netdump()\n");
|
|
|
|
+ else if (pc->writemem == write_kdump)
|
|
|
|
+ fprintf(fp, " writemem: write_kdump()\n");
|
|
|
|
else if (pc->writemem == write_memory_device)
|
|
|
|
fprintf(fp, " writemem: write_memory_device()\n");
|
|
|
|
else
|
|
|
|
--- crash/tools.c.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/tools.c 2005-12-14 15:55:50.000000000 -0500
|
|
|
|
@@ -2004,6 +2004,8 @@
|
|
|
|
pc->flags &= ~(DUMPFILE_TYPES);
|
|
|
|
if (is_netdump(args[optind], NETDUMP_LOCAL))
|
|
|
|
pc->flags |= NETDUMP;
|
|
|
|
+ else if (is_kdump(args[optind], KDUMP_LOCAL))
|
|
|
|
+ pc->flags |= KDUMP;
|
|
|
|
else if (is_diskdump(args[optind]))
|
|
|
|
pc->flags |= DISKDUMP;
|
|
|
|
else if (is_lkcd_compressed_dump(args[optind]))
|
|
|
|
@@ -4300,12 +4302,12 @@
|
|
|
|
void
|
|
|
|
command_not_supported()
|
|
|
|
{
|
|
|
|
- error(FATAL, "command not supported on this architecture\n");
|
|
|
|
+ error(FATAL, "command not supported on this architecture or kernel\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
option_not_supported(int c)
|
|
|
|
{
|
|
|
|
- error(FATAL, "-%c option not supported on this architecture\n",
|
|
|
|
+ error(FATAL, "-%c option not supported on this architecture or kernel\n",
|
|
|
|
(char)c);
|
|
|
|
}
|
|
|
|
--- crash/memory.c.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/memory.c 2005-11-15 15:51:07.000000000 -0500
|
|
|
|
@@ -45,6 +45,7 @@
|
|
|
|
ulong *addrlist;
|
|
|
|
int *kmem_bufctl;
|
|
|
|
ulong *cpudata[NR_CPUS];
|
|
|
|
+ ulong *shared_array_cache;
|
|
|
|
ulong found;
|
|
|
|
ulong retval;
|
|
|
|
char *ignore;
|
|
|
|
@@ -57,6 +58,10 @@
|
|
|
|
ulong get_slabs;
|
|
|
|
char *slab_buf;
|
|
|
|
char *cache_buf;
|
|
|
|
+ struct vmlist {
|
|
|
|
+ ulong addr;
|
|
|
|
+ ulong size;
|
|
|
|
+ } *vmlist;
|
|
|
|
};
|
|
|
|
|
|
|
|
static char *memtype_string(int, int);
|
|
|
|
@@ -98,6 +103,7 @@
|
|
|
|
static void gather_cpudata_list_v1(struct meminfo *);
|
|
|
|
static void gather_cpudata_list_v2(struct meminfo *);
|
|
|
|
static int check_cpudata_list(struct meminfo *, ulong);
|
|
|
|
+static int check_shared_list(struct meminfo *, ulong);
|
|
|
|
static void gather_slab_cached_count(struct meminfo *);
|
|
|
|
static void dump_slab_objects(struct meminfo *);
|
|
|
|
static void dump_slab_objects_percpu(struct meminfo *);
|
|
|
|
@@ -110,6 +116,7 @@
|
|
|
|
static void search(ulong, ulong, ulong, int, ulong *, int);
|
|
|
|
static int next_upage(struct task_context *, ulong, ulong *);
|
|
|
|
static int next_kpage(ulong, ulong *);
|
|
|
|
+static ulong next_vmlist_vaddr(struct meminfo *, ulong);
|
|
|
|
static int vm_area_page_dump(ulong, ulong, ulong, ulong, void *,
|
|
|
|
struct reference *);
|
|
|
|
static int dump_swap_info(ulong, ulong *, ulong *);
|
|
|
|
@@ -182,6 +189,13 @@
|
|
|
|
MEMBER_OFFSET_INIT(mm_struct_mmap, "mm_struct", "mmap");
|
|
|
|
MEMBER_OFFSET_INIT(mm_struct_pgd, "mm_struct", "pgd");
|
|
|
|
MEMBER_OFFSET_INIT(mm_struct_rss, "mm_struct", "rss");
|
|
|
|
+ if (!VALID_MEMBER(mm_struct_rss))
|
|
|
|
+ MEMBER_OFFSET_INIT(mm_struct_rss, "mm_struct", "_rss");
|
|
|
|
+ if (!VALID_MEMBER(mm_struct_rss))
|
|
|
|
+ MEMBER_OFFSET_INIT(mm_struct_rss, "mm_struct", "_file_rss");
|
|
|
|
+ MEMBER_OFFSET_INIT(mm_struct_anon_rss, "mm_struct", "anon_rss");
|
|
|
|
+ if (!VALID_MEMBER(mm_struct_anon_rss))
|
|
|
|
+ MEMBER_OFFSET_INIT(mm_struct_anon_rss, "mm_struct", "_anon_rss");
|
|
|
|
MEMBER_OFFSET_INIT(mm_struct_total_vm, "mm_struct", "total_vm");
|
|
|
|
MEMBER_OFFSET_INIT(mm_struct_start_code, "mm_struct", "start_code");
|
|
|
|
MEMBER_OFFSET_INIT(vm_area_struct_vm_mm, "vm_area_struct", "vm_mm");
|
|
|
|
@@ -270,6 +284,7 @@
|
|
|
|
STRUCT_SIZE_INIT(kmem_slab_s, "kmem_slab_s");
|
|
|
|
STRUCT_SIZE_INIT(slab_s, "slab_s");
|
|
|
|
STRUCT_SIZE_INIT(slab, "slab");
|
|
|
|
+ STRUCT_SIZE_INIT(kmem_cache_s, "kmem_cache_s");
|
|
|
|
STRUCT_SIZE_INIT(pgd_t, "pgd_t");
|
|
|
|
|
|
|
|
if (!VALID_STRUCT(kmem_slab_s) && VALID_STRUCT(slab_s)) {
|
|
|
|
@@ -310,17 +325,38 @@
|
|
|
|
!VALID_STRUCT(slab_s) && VALID_STRUCT(slab)) {
|
|
|
|
vt->flags |= PERCPU_KMALLOC_V2;
|
|
|
|
|
|
|
|
- MEMBER_OFFSET_INIT(kmem_cache_s_num, "kmem_cache_s", "num");
|
|
|
|
- MEMBER_OFFSET_INIT(kmem_cache_s_next, "kmem_cache_s", "next");
|
|
|
|
- MEMBER_OFFSET_INIT(kmem_cache_s_name, "kmem_cache_s", "name");
|
|
|
|
- MEMBER_OFFSET_INIT(kmem_cache_s_colour_off, "kmem_cache_s",
|
|
|
|
- "colour_off");
|
|
|
|
- MEMBER_OFFSET_INIT(kmem_cache_s_objsize, "kmem_cache_s",
|
|
|
|
- "objsize");
|
|
|
|
- MEMBER_OFFSET_INIT(kmem_cache_s_flags, "kmem_cache_s", "flags");
|
|
|
|
- MEMBER_OFFSET_INIT(kmem_cache_s_gfporder,
|
|
|
|
- "kmem_cache_s", "gfporder");
|
|
|
|
-
|
|
|
|
+ if (VALID_STRUCT(kmem_cache_s)) {
|
|
|
|
+ MEMBER_OFFSET_INIT(kmem_cache_s_num, "kmem_cache_s", "num");
|
|
|
|
+ MEMBER_OFFSET_INIT(kmem_cache_s_next, "kmem_cache_s", "next");
|
|
|
|
+ MEMBER_OFFSET_INIT(kmem_cache_s_name, "kmem_cache_s", "name");
|
|
|
|
+ MEMBER_OFFSET_INIT(kmem_cache_s_colour_off, "kmem_cache_s",
|
|
|
|
+ "colour_off");
|
|
|
|
+ MEMBER_OFFSET_INIT(kmem_cache_s_objsize, "kmem_cache_s",
|
|
|
|
+ "objsize");
|
|
|
|
+ MEMBER_OFFSET_INIT(kmem_cache_s_flags, "kmem_cache_s", "flags");
|
|
|
|
+ MEMBER_OFFSET_INIT(kmem_cache_s_gfporder,
|
|
|
|
+ "kmem_cache_s", "gfporder");
|
|
|
|
+
|
|
|
|
+ MEMBER_OFFSET_INIT(kmem_cache_s_lists, "kmem_cache_s", "lists");
|
|
|
|
+ MEMBER_OFFSET_INIT(kmem_cache_s_array, "kmem_cache_s", "array");
|
|
|
|
+ ARRAY_LENGTH_INIT(len, NULL, "kmem_cache_s.array", NULL, 0);
|
|
|
|
+ } else {
|
|
|
|
+ STRUCT_SIZE_INIT(kmem_cache_s, "kmem_cache");
|
|
|
|
+ MEMBER_OFFSET_INIT(kmem_cache_s_num, "kmem_cache", "num");
|
|
|
|
+ MEMBER_OFFSET_INIT(kmem_cache_s_next, "kmem_cache", "next");
|
|
|
|
+ MEMBER_OFFSET_INIT(kmem_cache_s_name, "kmem_cache", "name");
|
|
|
|
+ MEMBER_OFFSET_INIT(kmem_cache_s_colour_off, "kmem_cache",
|
|
|
|
+ "colour_off");
|
|
|
|
+ MEMBER_OFFSET_INIT(kmem_cache_s_objsize, "kmem_cache",
|
|
|
|
+ "objsize");
|
|
|
|
+ MEMBER_OFFSET_INIT(kmem_cache_s_flags, "kmem_cache", "flags");
|
|
|
|
+ MEMBER_OFFSET_INIT(kmem_cache_s_gfporder,
|
|
|
|
+ "kmem_cache", "gfporder");
|
|
|
|
+
|
|
|
|
+ MEMBER_OFFSET_INIT(kmem_cache_s_lists, "kmem_cache", "lists");
|
|
|
|
+ MEMBER_OFFSET_INIT(kmem_cache_s_array, "kmem_cache", "array");
|
|
|
|
+ ARRAY_LENGTH_INIT(len, NULL, "kmem_cache.array", NULL, 0);
|
|
|
|
+ }
|
|
|
|
MEMBER_OFFSET_INIT(slab_list, "slab", "list");
|
|
|
|
MEMBER_OFFSET_INIT(slab_s_mem, "slab", "s_mem");
|
|
|
|
MEMBER_OFFSET_INIT(slab_inuse, "slab", "inuse");
|
|
|
|
@@ -330,10 +366,6 @@
|
|
|
|
MEMBER_OFFSET_INIT(array_cache_limit, "array_cache", "limit");
|
|
|
|
STRUCT_SIZE_INIT(array_cache, "array_cache");
|
|
|
|
|
|
|
|
- MEMBER_OFFSET_INIT(kmem_cache_s_lists, "kmem_cache_s", "lists");
|
|
|
|
- MEMBER_OFFSET_INIT(kmem_cache_s_array, "kmem_cache_s", "array");
|
|
|
|
- ARRAY_LENGTH_INIT(len, NULL, "kmem_cache_s.array", NULL, 0);
|
|
|
|
-
|
|
|
|
MEMBER_OFFSET_INIT(kmem_list3_slabs_partial,
|
|
|
|
"kmem_list3", "slabs_partial");
|
|
|
|
MEMBER_OFFSET_INIT(kmem_list3_slabs_full,
|
|
|
|
@@ -478,7 +510,6 @@
|
|
|
|
STRUCT_SIZE_INIT(free_area_struct, "free_area_struct");
|
|
|
|
STRUCT_SIZE_INIT(zone, "zone");
|
|
|
|
STRUCT_SIZE_INIT(zone_struct, "zone_struct");
|
|
|
|
- STRUCT_SIZE_INIT(kmem_cache_s, "kmem_cache_s");
|
|
|
|
STRUCT_SIZE_INIT(kmem_bufctl_t, "kmem_bufctl_t");
|
|
|
|
STRUCT_SIZE_INIT(swap_info_struct, "swap_info_struct");
|
|
|
|
STRUCT_SIZE_INIT(mm_struct, "mm_struct");
|
|
|
|
@@ -539,6 +570,8 @@
|
|
|
|
if (INVALID_MEMBER(zone_struct_size))
|
|
|
|
MEMBER_OFFSET_INIT(zone_struct_memsize,
|
|
|
|
"zone_struct", "memsize");
|
|
|
|
+ MEMBER_OFFSET_INIT(zone_struct_zone_start_pfn,
|
|
|
|
+ "zone_struct", "zone_start_pfn");
|
|
|
|
MEMBER_OFFSET_INIT(zone_struct_zone_start_paddr,
|
|
|
|
"zone_struct", "zone_start_paddr");
|
|
|
|
MEMBER_OFFSET_INIT(zone_struct_zone_start_mapnr,
|
|
|
|
@@ -970,7 +1003,8 @@
|
|
|
|
case DISPLAY_64:
|
|
|
|
if ((flag & (HEXADECIMAL|SYMBOLIC|DISPLAY_DEFAULT)) ==
|
|
|
|
(HEXADECIMAL|SYMBOLIC|DISPLAY_DEFAULT)) {
|
|
|
|
- if (in_ksymbol_range(mem.u64)) {
|
|
|
|
+ if (in_ksymbol_range(mem.u64) &&
|
|
|
|
+ strlen(value_to_symstr(mem.u64, buf, 0))) {
|
|
|
|
fprintf(fp, "%-16s ",
|
|
|
|
value_to_symstr(mem.u64, buf, 0));
|
|
|
|
linelen += strlen(buf)+1;
|
|
|
|
@@ -993,7 +1027,8 @@
|
|
|
|
case DISPLAY_32:
|
|
|
|
if ((flag & (HEXADECIMAL|SYMBOLIC|DISPLAY_DEFAULT)) ==
|
|
|
|
(HEXADECIMAL|SYMBOLIC|DISPLAY_DEFAULT)) {
|
|
|
|
- if (in_ksymbol_range(mem.u32)) {
|
|
|
|
+ if (in_ksymbol_range(mem.u32) &&
|
|
|
|
+ strlen(value_to_symstr(mem.u32, buf, 0))) {
|
|
|
|
fprintf(fp, INT_PRLEN == 16 ?
|
|
|
|
"%-16s " : "%-8s ",
|
|
|
|
value_to_symstr(mem.u32,
|
|
|
|
@@ -2980,6 +3015,8 @@
|
|
|
|
return;
|
|
|
|
|
|
|
|
tm->rss = ULONG(tt->mm_struct + OFFSET(mm_struct_rss));
|
|
|
|
+ if (VALID_MEMBER(mm_struct_anon_rss))
|
|
|
|
+ tm->rss += ULONG(tt->mm_struct + OFFSET(mm_struct_anon_rss));
|
|
|
|
tm->total_vm = ULONG(tt->mm_struct + OFFSET(mm_struct_total_vm));
|
|
|
|
tm->pgd_addr = ULONG(tt->mm_struct + OFFSET(mm_struct_pgd));
|
|
|
|
|
|
|
|
@@ -3036,6 +3073,9 @@
|
|
|
|
#define GET_INACTIVE_DIRTY (ADDRESS_SPECIFIED << 13) /* obsolete */
|
|
|
|
#define SLAB_GET_COUNTS (ADDRESS_SPECIFIED << 14)
|
|
|
|
#define SLAB_WALKTHROUGH (ADDRESS_SPECIFIED << 15)
|
|
|
|
+#define GET_VMLIST_COUNT (ADDRESS_SPECIFIED << 16)
|
|
|
|
+#define GET_VMLIST (ADDRESS_SPECIFIED << 17)
|
|
|
|
+#define SLAB_DATA_NOSAVE (ADDRESS_SPECIFIED << 18)
|
|
|
|
|
|
|
|
#define GET_ALL \
|
|
|
|
(GET_SHARED_PAGES|GET_TOTALRAM_PAGES|GET_BUFFERS_PAGES|GET_SLAB_PAGES)
|
|
|
|
@@ -5575,15 +5615,17 @@
|
|
|
|
char buf1[BUFSIZE];
|
|
|
|
char buf2[BUFSIZE];
|
|
|
|
ulong vmlist;
|
|
|
|
- ulong addr, size, next, pcheck;
|
|
|
|
+ ulong addr, size, next, pcheck, count;
|
|
|
|
physaddr_t paddr;
|
|
|
|
|
|
|
|
get_symbol_data("vmlist", sizeof(void *), &vmlist);
|
|
|
|
next = vmlist;
|
|
|
|
+ count = 0;
|
|
|
|
|
|
|
|
while (next) {
|
|
|
|
if ((next == vmlist) &&
|
|
|
|
- !(vi->flags & (GET_HIGHEST|GET_PHYS_TO_VMALLOC))) {
|
|
|
|
+ !(vi->flags & (GET_HIGHEST|GET_PHYS_TO_VMALLOC|
|
|
|
|
+ GET_VMLIST_COUNT|GET_VMLIST))) {
|
|
|
|
fprintf(fp, "%s ",
|
|
|
|
mkstring(buf, MAX(strlen("VM_STRUCT"), VADDR_PRLEN),
|
|
|
|
CENTER|LJUST, "VM_STRUCT"));
|
|
|
|
@@ -5599,6 +5641,20 @@
|
|
|
|
&size, sizeof(ulong),
|
|
|
|
"vmlist size", FAULT_ON_ERROR);
|
|
|
|
|
|
|
|
+ if (vi->flags & (GET_VMLIST_COUNT|GET_VMLIST)) {
|
|
|
|
+ /*
|
|
|
|
+ * Preceding GET_VMLIST_COUNT set vi->retval.
|
|
|
|
+ */
|
|
|
|
+ if (vi->flags & GET_VMLIST) {
|
|
|
|
+ if (count < vi->retval) {
|
|
|
|
+ vi->vmlist[count].addr = addr;
|
|
|
|
+ vi->vmlist[count].size = size;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ count++;
|
|
|
|
+ goto next_entry;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
if (!(vi->flags & ADDRESS_SPECIFIED) ||
|
|
|
|
((vi->memtype == KVADDR) &&
|
|
|
|
((vi->spec_addr >= addr) && (vi->spec_addr < (addr+size)))))
|
|
|
|
@@ -5639,7 +5695,7 @@
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
-
|
|
|
|
+next_entry:
|
|
|
|
readmem(next+OFFSET(vm_struct_next),
|
|
|
|
KVADDR, &next, sizeof(void *),
|
|
|
|
"vmlist next", FAULT_ON_ERROR);
|
|
|
|
@@ -5647,6 +5703,9 @@
|
|
|
|
|
|
|
|
if (vi->flags & GET_HIGHEST)
|
|
|
|
vi->retval = addr+size;
|
|
|
|
+
|
|
|
|
+ if (vi->flags & GET_VMLIST_COUNT)
|
|
|
|
+ vi->retval = count;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
@@ -6177,6 +6236,7 @@
|
|
|
|
|
|
|
|
if (!readmem(cache, KVADDR, cache_buf, SIZE(kmem_cache_s),
|
|
|
|
"kmem_cache_s buffer", RETURN_ON_ERROR)) {
|
|
|
|
+ FREEBUF(cache_buf);
|
|
|
|
vt->flags |= KMEM_CACHE_UNAVAIL;
|
|
|
|
error(INFO,
|
|
|
|
"unable to initialize kmem slab cache subsystem\n\n");
|
|
|
|
@@ -6190,6 +6250,13 @@
|
|
|
|
|
|
|
|
if ((tmp = max_cpudata_limit(cache, &tmp2)) > max_limit)
|
|
|
|
max_limit = tmp;
|
|
|
|
+ /*
|
|
|
|
+ * Recognize and bail out on any max_cpudata_limit() failures.
|
|
|
|
+ */
|
|
|
|
+ if (vt->flags & KMEM_CACHE_UNAVAIL) {
|
|
|
|
+ FREEBUF(cache_buf);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
|
|
|
|
if (tmp2 > max_cpus)
|
|
|
|
max_cpus = tmp2;
|
|
|
|
@@ -6250,25 +6317,28 @@
|
|
|
|
ulong cpudata[NR_CPUS];
|
|
|
|
int limit;
|
|
|
|
ulong max_limit;
|
|
|
|
+ ulong shared;
|
|
|
|
|
|
|
|
if (vt->flags & PERCPU_KMALLOC_V2)
|
|
|
|
goto kmem_cache_s_array;
|
|
|
|
|
|
|
|
if (INVALID_MEMBER(kmem_cache_s_cpudata)) {
|
|
|
|
- *cpus = 0;
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
+ *cpus = 0;
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- readmem(cache+OFFSET(kmem_cache_s_cpudata),
|
|
|
|
- KVADDR, &cpudata[0],
|
|
|
|
- sizeof(ulong) * ARRAY_LENGTH(kmem_cache_s_cpudata),
|
|
|
|
- "cpudata array", FAULT_ON_ERROR);
|
|
|
|
+ if (!readmem(cache+OFFSET(kmem_cache_s_cpudata),
|
|
|
|
+ KVADDR, &cpudata[0],
|
|
|
|
+ sizeof(ulong) * ARRAY_LENGTH(kmem_cache_s_cpudata),
|
|
|
|
+ "cpudata array", RETURN_ON_ERROR))
|
|
|
|
+ goto bail_out;
|
|
|
|
|
|
|
|
for (i = max_limit = 0; (i < ARRAY_LENGTH(kmem_cache_s_cpudata)) &&
|
|
|
|
cpudata[i]; i++) {
|
|
|
|
- readmem(cpudata[i]+OFFSET(cpucache_s_limit),
|
|
|
|
- KVADDR, &limit, sizeof(int),
|
|
|
|
- "cpucache limit", FAULT_ON_ERROR);
|
|
|
|
+ if (!readmem(cpudata[i]+OFFSET(cpucache_s_limit),
|
|
|
|
+ KVADDR, &limit, sizeof(int),
|
|
|
|
+ "cpucache limit", RETURN_ON_ERROR))
|
|
|
|
+ goto bail_out;
|
|
|
|
if (limit > max_limit)
|
|
|
|
max_limit = limit;
|
|
|
|
}
|
|
|
|
@@ -6279,22 +6349,45 @@
|
|
|
|
|
|
|
|
kmem_cache_s_array:
|
|
|
|
|
|
|
|
- readmem(cache+OFFSET(kmem_cache_s_array),
|
|
|
|
- KVADDR, &cpudata[0],
|
|
|
|
- sizeof(ulong) * ARRAY_LENGTH(kmem_cache_s_array),
|
|
|
|
- "array cache array", FAULT_ON_ERROR);
|
|
|
|
+ if (!readmem(cache+OFFSET(kmem_cache_s_array),
|
|
|
|
+ KVADDR, &cpudata[0],
|
|
|
|
+ sizeof(ulong) * ARRAY_LENGTH(kmem_cache_s_array),
|
|
|
|
+ "array cache array", RETURN_ON_ERROR))
|
|
|
|
+ goto bail_out;
|
|
|
|
|
|
|
|
for (i = max_limit = 0; (i < ARRAY_LENGTH(kmem_cache_s_array)) &&
|
|
|
|
cpudata[i]; i++) {
|
|
|
|
- readmem(cpudata[i]+OFFSET(array_cache_limit),
|
|
|
|
- KVADDR, &limit, sizeof(int),
|
|
|
|
- "array cache limit", FAULT_ON_ERROR);
|
|
|
|
+ if (!readmem(cpudata[i]+OFFSET(array_cache_limit),
|
|
|
|
+ KVADDR, &limit, sizeof(int),
|
|
|
|
+ "array cache limit", RETURN_ON_ERROR))
|
|
|
|
+ goto bail_out;
|
|
|
|
if (limit > max_limit)
|
|
|
|
max_limit = limit;
|
|
|
|
}
|
|
|
|
|
|
|
|
+ /*
|
|
|
|
+ * If the shared list can be accessed, check its size as well.
|
|
|
|
+ */
|
|
|
|
+ if (VALID_MEMBER(kmem_list3_shared) &&
|
|
|
|
+ VALID_MEMBER(kmem_cache_s_lists) &&
|
|
|
|
+ readmem(cache+OFFSET(kmem_cache_s_lists)+OFFSET(kmem_list3_shared),
|
|
|
|
+ KVADDR, &shared, sizeof(void *), "kmem_list3 shared",
|
|
|
|
+ RETURN_ON_ERROR|QUIET) &&
|
|
|
|
+ readmem(shared+OFFSET(array_cache_limit),
|
|
|
|
+ KVADDR, &limit, sizeof(int), "shared array_cache limit",
|
|
|
|
+ RETURN_ON_ERROR|QUIET)) {
|
|
|
|
+ if (limit > max_limit)
|
|
|
|
+ max_limit = limit;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
*cpus = i;
|
|
|
|
return max_limit;
|
|
|
|
+
|
|
|
|
+bail_out:
|
|
|
|
+ vt->flags |= KMEM_CACHE_UNAVAIL;
|
|
|
|
+ error(INFO, "unable to initialize kmem slab cache subsystem\n\n");
|
|
|
|
+ *cpus = 0;
|
|
|
|
+ return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
@@ -6353,6 +6446,7 @@
|
|
|
|
#define KMEM_OBJECT_ADDR_INUSE (4)
|
|
|
|
#define KMEM_OBJECT_ADDR_CACHED (5)
|
|
|
|
#define KMEM_ON_SLAB (6)
|
|
|
|
+#define KMEM_OBJECT_ADDR_SHARED (7)
|
|
|
|
|
|
|
|
#define DUMP_KMEM_CACHE_INFO_V1() \
|
|
|
|
{ \
|
|
|
|
@@ -6408,7 +6502,7 @@
|
|
|
|
{ \
|
|
|
|
char b1[BUFSIZE], b2[BUFSIZE]; \
|
|
|
|
ulong allocated, freeobjs; \
|
|
|
|
- if (vt->flags & PERCPU_KMALLOC_V1) { \
|
|
|
|
+ if (vt->flags & (PERCPU_KMALLOC_V1|PERCPU_KMALLOC_V2)) { \
|
|
|
|
allocated = si->s_inuse - si->cpucached_slab; \
|
|
|
|
freeobjs = si->c_num - allocated - si->cpucached_slab; \
|
|
|
|
} else { \
|
|
|
|
@@ -6419,8 +6513,8 @@
|
|
|
|
mkstring(b1, VADDR_PRLEN, LJUST|LONG_HEX, MKSTR(si->slab)), \
|
|
|
|
mkstring(b2, VADDR_PRLEN, LJUST|LONG_HEX, MKSTR(si->s_mem)), \
|
|
|
|
si->c_num, allocated, \
|
|
|
|
- vt->flags & PERCPU_KMALLOC_V1 ? freeobjs + si->cpucached_slab :\
|
|
|
|
- freeobjs); \
|
|
|
|
+ vt->flags & (PERCPU_KMALLOC_V1|PERCPU_KMALLOC_V2) ? \
|
|
|
|
+ freeobjs + si->cpucached_slab : freeobjs); \
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
@@ -6857,6 +6951,8 @@
|
|
|
|
for (i = 0; i < vt->kmem_max_cpus; i++)
|
|
|
|
si->cpudata[i] = (ulong *)
|
|
|
|
GETBUF(vt->kmem_max_limit * sizeof(ulong));
|
|
|
|
+ si->shared_array_cache = (ulong *)
|
|
|
|
+ GETBUF(vt->kmem_max_limit * sizeof(ulong));
|
|
|
|
|
|
|
|
cnt = 0;
|
|
|
|
|
|
|
|
@@ -7005,7 +7101,14 @@
|
|
|
|
" %lx (cpu %d cache)\n",
|
|
|
|
(ulong)si->spec_addr, si->cpu);
|
|
|
|
break;
|
|
|
|
- }
|
|
|
|
+
|
|
|
|
+ case KMEM_OBJECT_ADDR_SHARED:
|
|
|
|
+ fprintf(fp, free_inuse_hdr);
|
|
|
|
+ fprintf(fp,
|
|
|
|
+ " %lx (shared cache)\n",
|
|
|
|
+ (ulong)si->spec_addr);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
@@ -7033,6 +7136,7 @@
|
|
|
|
FREEBUF(si->kmem_bufctl);
|
|
|
|
for (i = 0; i < vt->kmem_max_cpus; i++)
|
|
|
|
FREEBUF(si->cpudata[i]);
|
|
|
|
+ FREEBUF(si->shared_array_cache);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@@ -7750,6 +7854,11 @@
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
+ if (si->flags & SLAB_DATA_NOSAVE) {
|
|
|
|
+ si->flags &= ~SLAB_DATA_NOSAVE;
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
if (ACTIVE())
|
|
|
|
return;
|
|
|
|
|
|
|
|
@@ -8263,7 +8372,7 @@
|
|
|
|
dump_slab_objects_percpu(struct meminfo *si)
|
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
- int on_free_list, on_cpudata_list;
|
|
|
|
+ int on_free_list, on_cpudata_list, on_shared_list;
|
|
|
|
ulong cnt, expected;
|
|
|
|
ulong obj;
|
|
|
|
|
|
|
|
@@ -8285,6 +8394,7 @@
|
|
|
|
for (i = 0, obj = si->s_mem; i < si->c_num; i++, obj += si->size) {
|
|
|
|
on_free_list = FALSE;
|
|
|
|
on_cpudata_list = FALSE;
|
|
|
|
+ on_shared_list = FALSE;
|
|
|
|
|
|
|
|
for (j = 0; j < si->c_num; j++) {
|
|
|
|
if (obj == si->addrlist[j]) {
|
|
|
|
@@ -8294,13 +8404,26 @@
|
|
|
|
}
|
|
|
|
|
|
|
|
on_cpudata_list = check_cpudata_list(si, obj);
|
|
|
|
+ on_shared_list = check_shared_list(si, obj);
|
|
|
|
|
|
|
|
if (on_free_list && on_cpudata_list) {
|
|
|
|
error(INFO,
|
|
|
|
- "\"%s\" cache: object %lx on both free and cpudata lists\n",
|
|
|
|
+ "\"%s\" cache: object %lx on both free and cpu %d lists\n",
|
|
|
|
+ si->curname, si->cpu, obj);
|
|
|
|
+ si->errors++;
|
|
|
|
+ }
|
|
|
|
+ if (on_free_list && on_shared_list) {
|
|
|
|
+ error(INFO,
|
|
|
|
+ "\"%s\" cache: object %lx on both free and shared lists\n",
|
|
|
|
si->curname, obj);
|
|
|
|
si->errors++;
|
|
|
|
}
|
|
|
|
+ if (on_cpudata_list && on_shared_list) {
|
|
|
|
+ error(INFO,
|
|
|
|
+ "\"%s\" cache: object %lx on both cpu %d and shared lists\n",
|
|
|
|
+ si->curname, obj, si->cpu);
|
|
|
|
+ si->errors++;
|
|
|
|
+ }
|
|
|
|
|
|
|
|
if (on_free_list) {
|
|
|
|
if (!(si->flags & ADDRESS_SPECIFIED))
|
|
|
|
@@ -8324,6 +8447,17 @@
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
+ } else if (on_shared_list) {
|
|
|
|
+ if (!(si->flags & ADDRESS_SPECIFIED))
|
|
|
|
+ fprintf(fp, " %lx (shared cache)\n", obj);
|
|
|
|
+ cnt++;
|
|
|
|
+ if (si->flags & ADDRESS_SPECIFIED) {
|
|
|
|
+ if (INOBJECT(si->spec_addr, obj)) {
|
|
|
|
+ si->found =
|
|
|
|
+ KMEM_OBJECT_ADDR_SHARED;
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
} else {
|
|
|
|
if (!(si->flags & ADDRESS_SPECIFIED))
|
|
|
|
fprintf(fp, " [%lx]\n", obj);
|
|
|
|
@@ -8349,7 +8483,10 @@
|
|
|
|
/*
|
|
|
|
* Determine how many of the "inuse" slab objects are actually cached
|
|
|
|
* in the kmem_cache_s header. Set the per-slab count and update the
|
|
|
|
- * cumulative per-cache count.
|
|
|
|
+ * cumulative per-cache count. With the addition of the shared list
|
|
|
|
+ * check, the terms "cpucached_cache" and "cpucached_slab" are somewhat
|
|
|
|
+ * misleading. But they both are types of objects that are cached
|
|
|
|
+ * in the kmem_cache_s header, just not necessarily per-cpu.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
@@ -8357,16 +8494,35 @@
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
ulong obj;
|
|
|
|
+ int in_cpudata, in_shared;
|
|
|
|
|
|
|
|
si->cpucached_slab = 0;
|
|
|
|
|
|
|
|
for (i = 0, obj = si->s_mem; i < si->c_num; i++, obj += si->size) {
|
|
|
|
+ in_cpudata = in_shared = 0;
|
|
|
|
if (check_cpudata_list(si, obj)) {
|
|
|
|
+ in_cpudata = TRUE;
|
|
|
|
si->cpucached_slab++;
|
|
|
|
if (si->flags & SLAB_GET_COUNTS) {
|
|
|
|
si->cpucached_cache++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
+ if (check_shared_list(si, obj)) {
|
|
|
|
+ in_shared = TRUE;
|
|
|
|
+ if (!in_cpudata) {
|
|
|
|
+ si->cpucached_slab++;
|
|
|
|
+ if (si->flags & SLAB_GET_COUNTS) {
|
|
|
|
+ si->cpucached_cache++;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (in_cpudata && in_shared) {
|
|
|
|
+ si->flags |= SLAB_DATA_NOSAVE;
|
|
|
|
+ if (!(si->flags & VERBOSE))
|
|
|
|
+ error(INFO,
|
|
|
|
+ "\"%s\" cache: object %lx on both cpu %d and shared lists\n",
|
|
|
|
+ si->curname, obj, si->cpu);
|
|
|
|
+ }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@@ -8423,7 +8579,8 @@
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
- * Updated for 2.6 slab percpu data structure.
|
|
|
|
+ * Updated for 2.6 slab percpu data structure, this also gathers
|
|
|
|
+ * the shared array_cache list as well.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
gather_cpudata_list_v2(struct meminfo *si)
|
|
|
|
@@ -8431,6 +8588,7 @@
|
|
|
|
int i, j;
|
|
|
|
int avail;
|
|
|
|
ulong cpudata[NR_CPUS];
|
|
|
|
+ ulong shared;
|
|
|
|
|
|
|
|
readmem(si->cache+OFFSET(kmem_cache_s_array),
|
|
|
|
KVADDR, &cpudata[0],
|
|
|
|
@@ -8466,8 +8624,43 @@
|
|
|
|
|
|
|
|
if (CRASHDEBUG(2))
|
|
|
|
for (j = 0; j < avail; j++)
|
|
|
|
- fprintf(fp, " %lx\n", si->cpudata[i][j]);
|
|
|
|
+ fprintf(fp, " %lx (cpu %d)\n", si->cpudata[i][j], i);
|
|
|
|
}
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * If the shared list contains anything, gather them as well.
|
|
|
|
+ */
|
|
|
|
+ BZERO(si->shared_array_cache, sizeof(ulong) * vt->kmem_max_limit);
|
|
|
|
+
|
|
|
|
+ if (!VALID_MEMBER(kmem_list3_shared) ||
|
|
|
|
+ !VALID_MEMBER(kmem_cache_s_lists) ||
|
|
|
|
+ !readmem(si->cache+OFFSET(kmem_cache_s_lists)+
|
|
|
|
+ OFFSET(kmem_list3_shared), KVADDR, &shared, sizeof(void *),
|
|
|
|
+ "kmem_list3 shared", RETURN_ON_ERROR|QUIET) ||
|
|
|
|
+ !readmem(shared+OFFSET(array_cache_avail),
|
|
|
|
+ KVADDR, &avail, sizeof(int), "shared array_cache avail",
|
|
|
|
+ RETURN_ON_ERROR|QUIET) || !avail)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ if (avail > vt->kmem_max_limit) {
|
|
|
|
+ error(INFO,
|
|
|
|
+ "\"%s\" cache: shared array_cache.avail %d greater than limit %ld\n",
|
|
|
|
+ si->curname, avail, vt->kmem_max_limit);
|
|
|
|
+ si->errors++;
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (CRASHDEBUG(2))
|
|
|
|
+ fprintf(fp, "%s: shared avail: %d\n",
|
|
|
|
+ si->curname, avail);
|
|
|
|
+
|
|
|
|
+ readmem(shared+SIZE(array_cache), KVADDR, si->shared_array_cache,
|
|
|
|
+ sizeof(void *) * avail, "shared array_cache avail",
|
|
|
|
+ FAULT_ON_ERROR);
|
|
|
|
+
|
|
|
|
+ if (CRASHDEBUG(2))
|
|
|
|
+ for (j = 0; j < avail; j++)
|
|
|
|
+ fprintf(fp, " %lx (shared list)\n", si->shared_array_cache[j]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
@@ -8491,6 +8684,27 @@
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Check whether a given address is contained in the previously-gathered
|
|
|
|
+ * shared object cache.
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+static int
|
|
|
|
+check_shared_list(struct meminfo *si, ulong obj)
|
|
|
|
+{
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ if (INVALID_MEMBER(kmem_list3_shared) ||
|
|
|
|
+ !si->shared_array_cache)
|
|
|
|
+ return FALSE;
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < si->shared_array_cache[i]; i++) {
|
|
|
|
+ if (si->shared_array_cache[i] == obj)
|
|
|
|
+ return TRUE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return FALSE;
|
|
|
|
+}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Search the various memory subsystems for instances of this address.
|
|
|
|
@@ -9321,6 +9535,43 @@
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
+ * Return the next mapped kernel virtual address in the vmlist
|
|
|
|
+ * that is equal to or comes after the passed-in address.
|
|
|
|
+ */
|
|
|
|
+static ulong
|
|
|
|
+next_vmlist_vaddr(struct meminfo *mi, ulong vaddr)
|
|
|
|
+{
|
|
|
|
+ ulong i, count;
|
|
|
|
+
|
|
|
|
+ BZERO(mi, sizeof(struct meminfo));
|
|
|
|
+
|
|
|
|
+ mi->flags = GET_VMLIST_COUNT;
|
|
|
|
+ dump_vmlist(mi);
|
|
|
|
+ count = mi->retval;
|
|
|
|
+
|
|
|
|
+ if (!count)
|
|
|
|
+ return vaddr;
|
|
|
|
+
|
|
|
|
+ mi->vmlist = (struct vmlist *)GETBUF(sizeof(struct vmlist)*count);
|
|
|
|
+ mi->flags = GET_VMLIST;
|
|
|
|
+ dump_vmlist(mi);
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < count; i++) {
|
|
|
|
+ if (vaddr <= mi->vmlist[i].addr) {
|
|
|
|
+ vaddr = mi->vmlist[i].addr;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ if (vaddr < (mi->vmlist[i].addr + mi->vmlist[i].size))
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ FREEBUF(mi->vmlist);
|
|
|
|
+
|
|
|
|
+ return vaddr;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
* Return the next kernel virtual address page that comes after
|
|
|
|
* the passed-in address.
|
|
|
|
*/
|
|
|
|
@@ -9348,6 +9599,8 @@
|
|
|
|
|
|
|
|
if (IS_VMALLOC_ADDR(vaddr_orig)) {
|
|
|
|
if (IS_VMALLOC_ADDR(vaddr) && (vaddr < vmalloc_limit)) {
|
|
|
|
+ if (machine_type("X86_64"))
|
|
|
|
+ vaddr = next_vmlist_vaddr(&meminfo, vaddr);
|
|
|
|
*nextvaddr = vaddr;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
@@ -9377,6 +9630,7 @@
|
|
|
|
/*
|
|
|
|
* We're in the physical range.
|
|
|
|
*/
|
|
|
|
+ *nextvaddr = vaddr;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
@@ -9926,12 +10180,24 @@
|
|
|
|
if (!read_string(value, buf1, BUFSIZE-1))
|
|
|
|
sprintf(buf1, "(unknown) ");
|
|
|
|
if (VALID_STRUCT(zone_struct)) {
|
|
|
|
- readmem(node_zones+
|
|
|
|
- OFFSET(zone_struct_zone_start_paddr),
|
|
|
|
- KVADDR, &zone_start_paddr,
|
|
|
|
- sizeof(ulong),
|
|
|
|
- "node_zones zone_start_paddr",
|
|
|
|
- FAULT_ON_ERROR);
|
|
|
|
+ if (VALID_MEMBER(zone_struct_zone_start_paddr))
|
|
|
|
+ {
|
|
|
|
+ readmem(node_zones+OFFSET
|
|
|
|
+ (zone_struct_zone_start_paddr),
|
|
|
|
+ KVADDR, &zone_start_paddr,
|
|
|
|
+ sizeof(ulong),
|
|
|
|
+ "node_zones zone_start_paddr",
|
|
|
|
+ FAULT_ON_ERROR);
|
|
|
|
+ } else {
|
|
|
|
+ readmem(node_zones+
|
|
|
|
+ OFFSET(zone_struct_zone_start_pfn),
|
|
|
|
+ KVADDR, &zone_start_pfn,
|
|
|
|
+ sizeof(ulong),
|
|
|
|
+ "node_zones zone_start_pfn",
|
|
|
|
+ FAULT_ON_ERROR);
|
|
|
|
+ zone_start_paddr =
|
|
|
|
+ PTOB(zone_start_pfn);
|
|
|
|
+ }
|
|
|
|
readmem(node_zones+
|
|
|
|
OFFSET(zone_struct_zone_start_mapnr),
|
|
|
|
KVADDR, &zone_start_mapnr,
|
|
|
|
@@ -10072,6 +10338,9 @@
|
|
|
|
{
|
|
|
|
uint psz;
|
|
|
|
|
|
|
|
+ if (machdep->pagesize)
|
|
|
|
+ return machdep->pagesize;
|
|
|
|
+
|
|
|
|
if (REMOTE_MEMSRC())
|
|
|
|
return remote_page_size();
|
|
|
|
|
|
|
|
@@ -10081,6 +10350,10 @@
|
|
|
|
psz = diskdump_page_size();
|
|
|
|
break;
|
|
|
|
|
|
|
|
+ case KDUMP:
|
|
|
|
+ psz = kdump_page_size();
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
case NETDUMP:
|
|
|
|
psz = netdump_page_size();
|
|
|
|
break;
|
|
|
|
@@ -10115,6 +10388,48 @@
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
+ * If the page size cannot be determined by the dumpfile (like kdump),
|
|
|
|
+ * and the processor default cannot be used, allow the force-feeding
|
|
|
|
+ * of a crash command-line page size option.
|
|
|
|
+ */
|
|
|
|
+void
|
|
|
|
+force_page_size(char *s)
|
|
|
|
+{
|
|
|
|
+ int k, err;
|
|
|
|
+ ulong psize;
|
|
|
|
+
|
|
|
|
+ k = 1;
|
|
|
|
+ err = FALSE;
|
|
|
|
+
|
|
|
|
+ switch (LASTCHAR(s))
|
|
|
|
+ {
|
|
|
|
+ case 'k':
|
|
|
|
+ case 'K':
|
|
|
|
+ LASTCHAR(s) = NULLCHAR;
|
|
|
|
+ if (decimal(s, 0))
|
|
|
|
+ k = 1024;
|
|
|
|
+ else
|
|
|
|
+ err = TRUE;
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ default:
|
|
|
|
+ if (decimal(s, 0))
|
|
|
|
+ psize = dtol(s, QUIET|RETURN_ON_ERROR, &err);
|
|
|
|
+ else if (hexadecimal(s, 0))
|
|
|
|
+ psize = htol(s, QUIET|RETURN_ON_ERROR, &err);
|
|
|
|
+ else
|
|
|
|
+ err = TRUE;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (err)
|
|
|
|
+ error(INFO, "invalid page size: %s\n", s);
|
|
|
|
+ else
|
|
|
|
+ machdep->pagesize = psize * k;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
* Return the vmalloc address referenced by the first vm_struct
|
|
|
|
* on the vmlist. This can normally be used by the machine-specific
|
|
|
|
* xxx_vmalloc_start() routines.
|
|
|
|
@@ -10186,6 +10501,8 @@
|
|
|
|
retval = remote_memory_used();
|
|
|
|
else if (pc->flags & NETDUMP)
|
|
|
|
retval = netdump_memory_used();
|
|
|
|
+ else if (pc->flags & KDUMP)
|
|
|
|
+ retval = kdump_memory_used();
|
|
|
|
else if (pc->flags & DISKDUMP)
|
|
|
|
retval = diskdump_memory_used();
|
|
|
|
else if (pc->flags & LKCD)
|
|
|
|
@@ -10201,6 +10518,8 @@
|
|
|
|
retval = remote_free_memory();
|
|
|
|
else if (pc->flags & NETDUMP)
|
|
|
|
retval = netdump_free_memory();
|
|
|
|
+ else if (pc->flags & KDUMP)
|
|
|
|
+ retval = kdump_free_memory();
|
|
|
|
else if (pc->flags & DISKDUMP)
|
|
|
|
retval = diskdump_free_memory();
|
|
|
|
else if (pc->flags & LKCD)
|
|
|
|
@@ -10216,6 +10535,8 @@
|
|
|
|
retval = remote_memory_dump(0);
|
|
|
|
else if (pc->flags & NETDUMP)
|
|
|
|
retval = netdump_memory_dump(fp);
|
|
|
|
+ else if (pc->flags & KDUMP)
|
|
|
|
+ retval = kdump_memory_dump(fp);
|
|
|
|
else if (pc->flags & DISKDUMP)
|
|
|
|
retval = diskdump_memory_dump(fp);
|
|
|
|
else if (pc->flags & LKCD)
|
|
|
|
--- crash/filesys.c.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/filesys.c 2006-01-03 15:37:33.000000000 -0500
|
|
|
|
@@ -190,7 +190,11 @@
|
|
|
|
if (!netdump_init(pc->dumpfile, fp))
|
|
|
|
error(FATAL, "%s: initialization failed\n",
|
|
|
|
pc->dumpfile);
|
|
|
|
- } else if (pc->flags & NETDUMP) {
|
|
|
|
+ } else if (pc->flags & KDUMP) {
|
|
|
|
+ if (!kdump_init(pc->dumpfile, fp))
|
|
|
|
+ error(FATAL, "%s: initialization failed\n",
|
|
|
|
+ pc->dumpfile);
|
|
|
|
+ } else if (pc->flags & DISKDUMP) {
|
|
|
|
if (!diskdump_init(pc->dumpfile, fp))
|
|
|
|
error(FATAL, "%s: initialization failed\n",
|
|
|
|
pc->dumpfile);
|
|
|
|
@@ -1706,12 +1710,20 @@
|
|
|
|
MEMBER_OFFSET_INIT(fs_struct_pwd, "fs_struct", "pwd");
|
|
|
|
MEMBER_OFFSET_INIT(fs_struct_rootmnt, "fs_struct", "rootmnt");
|
|
|
|
MEMBER_OFFSET_INIT(fs_struct_pwdmnt, "fs_struct", "pwdmnt");
|
|
|
|
- MEMBER_OFFSET_INIT(files_struct_max_fds, "files_struct", "max_fds");
|
|
|
|
- MEMBER_OFFSET_INIT(files_struct_max_fdset, "files_struct", "max_fdset");
|
|
|
|
- MEMBER_OFFSET_INIT(files_struct_open_fds, "files_struct", "open_fds");
|
|
|
|
MEMBER_OFFSET_INIT(files_struct_open_fds_init,
|
|
|
|
"files_struct", "open_fds_init");
|
|
|
|
- MEMBER_OFFSET_INIT(files_struct_fd, "files_struct", "fd");
|
|
|
|
+ MEMBER_OFFSET_INIT(files_struct_fdt, "files_struct", "fdt");
|
|
|
|
+ if (VALID_MEMBER(files_struct_fdt)) {
|
|
|
|
+ MEMBER_OFFSET_INIT(fdtable_max_fds, "fdtable", "max_fds");
|
|
|
|
+ MEMBER_OFFSET_INIT(fdtable_max_fdset, "fdtable", "max_fdset");
|
|
|
|
+ MEMBER_OFFSET_INIT(fdtable_open_fds, "fdtable", "open_fds");
|
|
|
|
+ MEMBER_OFFSET_INIT(fdtable_fd, "fdtable", "fd");
|
|
|
|
+ } else {
|
|
|
|
+ MEMBER_OFFSET_INIT(files_struct_max_fds, "files_struct", "max_fds");
|
|
|
|
+ MEMBER_OFFSET_INIT(files_struct_max_fdset, "files_struct", "max_fdset");
|
|
|
|
+ MEMBER_OFFSET_INIT(files_struct_open_fds, "files_struct", "open_fds");
|
|
|
|
+ MEMBER_OFFSET_INIT(files_struct_fd, "files_struct", "fd");
|
|
|
|
+ }
|
|
|
|
MEMBER_OFFSET_INIT(file_f_dentry, "file", "f_dentry");
|
|
|
|
MEMBER_OFFSET_INIT(file_f_vfsmnt, "file", "f_vfsmnt");
|
|
|
|
MEMBER_OFFSET_INIT(file_f_count, "file", "f_count");
|
|
|
|
@@ -1762,6 +1774,8 @@
|
|
|
|
STRUCT_SIZE_INIT(umode_t, "umode_t");
|
|
|
|
STRUCT_SIZE_INIT(dentry, "dentry");
|
|
|
|
STRUCT_SIZE_INIT(files_struct, "files_struct");
|
|
|
|
+ if (VALID_MEMBER(files_struct_fdt))
|
|
|
|
+ STRUCT_SIZE_INIT(fdtable, "fdtable");
|
|
|
|
STRUCT_SIZE_INIT(file, "file");
|
|
|
|
STRUCT_SIZE_INIT(inode, "inode");
|
|
|
|
STRUCT_SIZE_INIT(vfsmount, "vfsmount");
|
|
|
|
@@ -1998,8 +2012,9 @@
|
|
|
|
open_files_dump(ulong task, int flags, struct reference *ref)
|
|
|
|
{
|
|
|
|
struct task_context *tc;
|
|
|
|
- ulong files_struct_addr;
|
|
|
|
- char *files_struct_buf;
|
|
|
|
+ ulong files_struct_addr;
|
|
|
|
+ ulong fdtable_addr = 0;
|
|
|
|
+ char *files_struct_buf, *fdtable_buf = NULL;
|
|
|
|
ulong fs_struct_addr;
|
|
|
|
char *dentry_buf, *fs_struct_buf;
|
|
|
|
ulong root_dentry, pwd_dentry;
|
|
|
|
@@ -2027,6 +2042,8 @@
|
|
|
|
BZERO(root_pathname, BUFSIZE);
|
|
|
|
BZERO(pwd_pathname, BUFSIZE);
|
|
|
|
files_struct_buf = GETBUF(SIZE(files_struct));
|
|
|
|
+ if (VALID_STRUCT(fdtable))
|
|
|
|
+ fdtable_buf = GETBUF(SIZE(fdtable));
|
|
|
|
fill_task_struct(task);
|
|
|
|
|
|
|
|
sprintf(files_header, " FD%s%s%s%s%s%s%sTYPE%sPATH\n",
|
|
|
|
@@ -2107,24 +2124,42 @@
|
|
|
|
|
|
|
|
files_struct_addr = ULONG(tt->task_struct + OFFSET(task_struct_files));
|
|
|
|
|
|
|
|
- if (files_struct_addr) {
|
|
|
|
- readmem(files_struct_addr, KVADDR, files_struct_buf,
|
|
|
|
- SIZE(files_struct), "files_struct buffer",
|
|
|
|
- FAULT_ON_ERROR);
|
|
|
|
-
|
|
|
|
- max_fdset = INT(files_struct_buf +
|
|
|
|
+ if (files_struct_addr) {
|
|
|
|
+ readmem(files_struct_addr, KVADDR, files_struct_buf,
|
|
|
|
+ SIZE(files_struct), "files_struct buffer",
|
|
|
|
+ FAULT_ON_ERROR);
|
|
|
|
+
|
|
|
|
+ if (VALID_MEMBER(files_struct_max_fdset)) {
|
|
|
|
+ max_fdset = INT(files_struct_buf +
|
|
|
|
OFFSET(files_struct_max_fdset));
|
|
|
|
|
|
|
|
- max_fds = INT(files_struct_buf +
|
|
|
|
- OFFSET(files_struct_max_fds));
|
|
|
|
- }
|
|
|
|
+ max_fds = INT(files_struct_buf +
|
|
|
|
+ OFFSET(files_struct_max_fds));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (VALID_MEMBER(files_struct_fdt)) {
|
|
|
|
+ fdtable_addr = ULONG(files_struct_buf + OFFSET(files_struct_fdt));
|
|
|
|
|
|
|
|
- if (!files_struct_addr || max_fdset == 0 || max_fds == 0) {
|
|
|
|
+ if (fdtable_addr) {
|
|
|
|
+ readmem(fdtable_addr, KVADDR, fdtable_buf,
|
|
|
|
+ SIZE(fdtable), "fdtable buffer", FAULT_ON_ERROR);
|
|
|
|
+ max_fdset = INT(fdtable_buf +
|
|
|
|
+ OFFSET(fdtable_max_fdset));
|
|
|
|
+ max_fds = INT(fdtable_buf +
|
|
|
|
+ OFFSET(fdtable_max_fds));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if ((VALID_MEMBER(files_struct_fdt) && !fdtable_addr) ||
|
|
|
|
+ !files_struct_addr || max_fdset == 0 || max_fds == 0) {
|
|
|
|
if (ref) {
|
|
|
|
if (ref->cmdflags & FILES_REF_FOUND)
|
|
|
|
fprintf(fp, "\n");
|
|
|
|
} else
|
|
|
|
fprintf(fp, "No open files\n");
|
|
|
|
+ if (fdtable_buf)
|
|
|
|
+ FREEBUF(fdtable_buf);
|
|
|
|
FREEBUF(files_struct_buf);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
@@ -2146,8 +2181,12 @@
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- open_fds_addr = ULONG(files_struct_buf +
|
|
|
|
- OFFSET(files_struct_open_fds));
|
|
|
|
+ if (VALID_MEMBER(fdtable_open_fds))
|
|
|
|
+ open_fds_addr = ULONG(fdtable_buf +
|
|
|
|
+ OFFSET(fdtable_open_fds));
|
|
|
|
+ else
|
|
|
|
+ open_fds_addr = ULONG(files_struct_buf +
|
|
|
|
+ OFFSET(files_struct_open_fds));
|
|
|
|
|
|
|
|
if (open_fds_addr) {
|
|
|
|
if (VALID_MEMBER(files_struct_open_fds_init) &&
|
|
|
|
@@ -2157,16 +2196,21 @@
|
|
|
|
OFFSET(files_struct_open_fds_init),
|
|
|
|
&open_fds, sizeof(fd_set));
|
|
|
|
else
|
|
|
|
- readmem(open_fds_addr, KVADDR, &open_fds,
|
|
|
|
- sizeof(fd_set), "files_struct open_fds",
|
|
|
|
+ readmem(open_fds_addr, KVADDR, &open_fds,
|
|
|
|
+ sizeof(fd_set), "fdtable open_fds",
|
|
|
|
FAULT_ON_ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
- fd = ULONG(files_struct_buf + OFFSET(files_struct_fd));
|
|
|
|
+ if (VALID_MEMBER(fdtable_fd))
|
|
|
|
+ fd = ULONG(fdtable_buf + OFFSET(fdtable_fd));
|
|
|
|
+ else
|
|
|
|
+ fd = ULONG(files_struct_buf + OFFSET(files_struct_fd));
|
|
|
|
|
|
|
|
if (!open_fds_addr || !fd) {
|
|
|
|
if (ref && (ref->cmdflags & FILES_REF_FOUND))
|
|
|
|
fprintf(fp, "\n");
|
|
|
|
+ if (fdtable_buf)
|
|
|
|
+ FREEBUF(fdtable_buf);
|
|
|
|
FREEBUF(files_struct_buf);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
@@ -2220,6 +2264,8 @@
|
|
|
|
if (ref && (ref->cmdflags & FILES_REF_FOUND))
|
|
|
|
fprintf(fp, "\n");
|
|
|
|
|
|
|
|
+ if (fdtable_buf)
|
|
|
|
+ FREEBUF(fdtable_buf);
|
|
|
|
FREEBUF(files_struct_buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
--- crash/help.c.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/help.c 2006-01-03 13:26:18.000000000 -0500
|
|
|
|
@@ -508,16 +508,16 @@
|
|
|
|
" active perform the command(s) on the active thread on each CPU.\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 on the tasks",
|
|
|
|
+" command select one or more of the following commands to be run on the tasks",
|
|
|
|
" selected, or on all tasks:\n",
|
|
|
|
-" bt same as the \"bt\" command (optional flags: -r -t -l -e -R -f)",
|
|
|
|
-" vm same as the \"vm\" command (optional flags: -p -v -m -R)",
|
|
|
|
-" task same as the \"task\" command (optional flag: -R)",
|
|
|
|
-" files same as the \"files\" command (optional flag: -R)",
|
|
|
|
-" net same as the \"net\" command (optional flags: -s -S -R)",
|
|
|
|
-" set same as the \"set\" command",
|
|
|
|
-" sig same as the \"sig\" command",
|
|
|
|
-" vtop same as the \"vtop\" command (optional flags: -c -u -k)\n",
|
|
|
|
+" bt run the \"bt\" command (optional flags: -r -t -l -e -R -f -o)",
|
|
|
|
+" vm run the \"vm\" command (optional flags: -p -v -m -R)",
|
|
|
|
+" task run the \"task\" command (optional flag: -R)",
|
|
|
|
+" files run the \"files\" command (optional flag: -R)",
|
|
|
|
+" net run the \"net\" command (optional flags: -s -S -R)",
|
|
|
|
+" set run the \"set\" command",
|
|
|
|
+" sig run the \"sig\" command",
|
|
|
|
+" 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.",
|
|
|
|
" ",
|
|
|
|
@@ -1155,7 +1155,7 @@
|
|
|
|
"bt",
|
|
|
|
"backtrace",
|
|
|
|
#if defined(GDB_6_0) || defined(GDB_6_1)
|
|
|
|
-"[-a|-r|-t|-l|-e|-E|-f] [-R ref] [ -I ip ] [-S sp] [pid | taskp]",
|
|
|
|
+"[-a|-r|-t|-T|-l|-e|-E|-f|-o|-O] [-R ref] [ -I ip ] [-S sp] [pid | taskp]",
|
|
|
|
#else
|
|
|
|
"[-a|-r|-t|-l|-e|-f|-g] [-R ref] [ -I ip ] [-S sp] [pid | taskp]",
|
|
|
|
#endif
|
|
|
|
@@ -1167,6 +1167,9 @@
|
|
|
|
" pages of memory containing the task_union structure.",
|
|
|
|
" -t display all text symbols found from the last known stack location",
|
|
|
|
" to the top of the stack. (helpful if the back trace fails)",
|
|
|
|
+" -T display all text symbols found from just above the task_struct or",
|
|
|
|
+" thread_info to the top of the stack. (helpful if the back trace",
|
|
|
|
+" fails or the -t option starts too high in the process stack).",
|
|
|
|
" -l show file and line number of each stack trace text location.",
|
|
|
|
" -e search the stack for possible kernel and user mode exception frames.",
|
|
|
|
" -E search the IRQ stacks (x86, x86_64 and PPC64), and the exception",
|
|
|
|
@@ -1175,6 +1178,11 @@
|
|
|
|
" -f display all stack data contained in a frame; this option can be",
|
|
|
|
" used to determine the arguments passed to each function (x86 only);",
|
|
|
|
" on IA64, the argument register contents are dumped.",
|
|
|
|
+" -o use old backtrace method, permissable only on kernels that were",
|
|
|
|
+" compiled without the -fomit-frame_pointer (x86 only).",
|
|
|
|
+" -O use old backtrace method by default, permissable only on kernels",
|
|
|
|
+" that were compiled without the -fomit-frame_pointer; subsequent",
|
|
|
|
+" usage of this option toggles the backtrace method (x86 only).",
|
|
|
|
#if !defined(GDB_6_0) && !defined(GDB_6_1)
|
|
|
|
" -g use gdb stack trace code. (alpha only)",
|
|
|
|
#endif
|
|
|
|
@@ -1439,12 +1447,13 @@
|
|
|
|
" called \"echo\", which simply echoes back all arguments passed to it.",
|
|
|
|
" Note the comments contained within it for further details. To build it,",
|
|
|
|
" cut and paste the following output into a file, and call it, for example,",
|
|
|
|
-" \"extlib.c\". Then compile like so:",
|
|
|
|
+" \"echo.c\". Then compile like so:",
|
|
|
|
" ",
|
|
|
|
-" gcc -nostartfiles -shared -rdynamic -o extlib.so extlib.c",
|
|
|
|
+" gcc -nostartfiles -shared -rdynamic -o echo.so echo.c -fPIC -D<machine_type>",
|
|
|
|
" ",
|
|
|
|
-" The extlib.so file may be dynamically linked into %s during runtime, or",
|
|
|
|
-" during initialization by putting \"extend extlib.so\" into a .%src file",
|
|
|
|
+" where <machine-type> must be one of the MACHINE_TYPE #define's in defs.h.",
|
|
|
|
+" The echo.so file may be dynamically linked into %s during runtime, or",
|
|
|
|
+" during initialization by putting \"extend echo.so\" into a .%src file",
|
|
|
|
" located in the current directory, or in the user's $HOME directory.",
|
|
|
|
" ",
|
|
|
|
"---------------------------------- cut here ----------------------------------",
|
|
|
|
@@ -4419,10 +4428,11 @@
|
|
|
|
" Display various network related data:\n",
|
|
|
|
" -a display the ARP cache.",
|
|
|
|
" -s display open network socket/sock addresses, their family and type,",
|
|
|
|
-" and their source and destination addresses and ports.",
|
|
|
|
+" and for INET and INET6 families, their source and destination",
|
|
|
|
+" addresses and ports.",
|
|
|
|
" -S displays open network socket/sock addresses followed by a dump",
|
|
|
|
" of both structures.",
|
|
|
|
-" -n addr translates an IP address expressed as a decimal or hexadecimal ",
|
|
|
|
+" -n addr translates an IPv4 address expressed as a decimal or hexadecimal",
|
|
|
|
" value into a standard numbers-and-dots notation.",
|
|
|
|
" -R ref socket or sock address, or file descriptor.",
|
|
|
|
" pid a process PID.",
|
|
|
|
@@ -4450,8 +4460,8 @@
|
|
|
|
" Display the sockets for PID 2517, using both -s and -S output formats:\n",
|
|
|
|
" %s> net -s 2517",
|
|
|
|
" PID: 2517 TASK: c1598000 CPU: 1 COMMAND: \"rlogin\"",
|
|
|
|
-" FD SOCKET SOCK FAMILY:TYPE SOURCE:PORT DESTINATION:PORT",
|
|
|
|
-" 3 c57375dc c1ff1850 INET:STREAM 10.1.8.20:1023 10.1.16.62:513",
|
|
|
|
+" FD SOCKET SOCK FAMILY:TYPE SOURCE-PORT DESTINATION-PORT",
|
|
|
|
+" 3 c57375dc c1ff1850 INET:STREAM 10.1.8.20-1023 10.1.16.62-513",
|
|
|
|
" ",
|
|
|
|
" %s> net -S 2517",
|
|
|
|
" PID: 2517 TASK: c1598000 CPU: 1 COMMAND: \"rlogin\"",
|
|
|
|
@@ -4497,52 +4507,52 @@
|
|
|
|
" From \"foreach\", find all tasks with references to socket c08ea3cc:\n",
|
|
|
|
" %s> foreach net -s -R c08ea3cc",
|
|
|
|
" PID: 2184 TASK: c7026000 CPU: 1 COMMAND: \"klines.kss\"",
|
|
|
|
-" FD SOCKET SOCK FAMILY:TYPE SOURCE:PORT DESTINATION:PORT",
|
|
|
|
-" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0:1026 0.0.0.0:0",
|
|
|
|
+" FD SOCKET SOCK FAMILY:TYPE SOURCE-PORT DESTINATION-PORT",
|
|
|
|
+" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0-1026 0.0.0.0-0",
|
|
|
|
" ",
|
|
|
|
" PID: 2200 TASK: c670a000 CPU: 1 COMMAND: \"kpanel\"",
|
|
|
|
-" FD SOCKET SOCK FAMILY:TYPE SOURCE:PORT DESTINATION:PORT",
|
|
|
|
-" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0:1026 0.0.0.0:0",
|
|
|
|
+" FD SOCKET SOCK FAMILY:TYPE SOURCE-PORT DESTINATION-PORT",
|
|
|
|
+" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0-1026 0.0.0.0-0",
|
|
|
|
" ",
|
|
|
|
" PID: 2201 TASK: c648a000 CPU: 1 COMMAND: \"kbgndwm\"",
|
|
|
|
-" FD SOCKET SOCK FAMILY:TYPE SOURCE:PORT DESTINATION:PORT",
|
|
|
|
-" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0:1026 0.0.0.0:0",
|
|
|
|
+" FD SOCKET SOCK FAMILY:TYPE SOURCE-PORT DESTINATION-PORT",
|
|
|
|
+" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0-1026 0.0.0.0-0",
|
|
|
|
" ",
|
|
|
|
" PID: 19294 TASK: c250a000 CPU: 0 COMMAND: \"prefdm\"",
|
|
|
|
-" FD SOCKET SOCK FAMILY:TYPE SOURCE:PORT DESTINATION:PORT",
|
|
|
|
-" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0:1026 0.0.0.0:0",
|
|
|
|
+" FD SOCKET SOCK FAMILY:TYPE SOURCE-PORT DESTINATION-PORT",
|
|
|
|
+" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0-1026 0.0.0.0-0",
|
|
|
|
" ",
|
|
|
|
" PID: 2194 TASK: c62dc000 CPU: 1 COMMAND: \"kaudioserver\"",
|
|
|
|
-" FD SOCKET SOCK FAMILY:TYPE SOURCE:PORT DESTINATION:PORT",
|
|
|
|
-" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0:1026 0.0.0.0:0",
|
|
|
|
+" FD SOCKET SOCK FAMILY:TYPE SOURCE-PORT DESTINATION-PORT",
|
|
|
|
+" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0-1026 0.0.0.0-0",
|
|
|
|
" ",
|
|
|
|
" PID: 2195 TASK: c6684000 CPU: 1 COMMAND: \"maudio\"",
|
|
|
|
-" FD SOCKET SOCK FAMILY:TYPE SOURCE:PORT DESTINATION:PORT",
|
|
|
|
-" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0:1026 0.0.0.0:0",
|
|
|
|
+" FD SOCKET SOCK FAMILY:TYPE SOURCE-PORT DESTINATION-PORT",
|
|
|
|
+" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0-1026 0.0.0.0-0",
|
|
|
|
" ",
|
|
|
|
" PID: 2196 TASK: c6b58000 CPU: 1 COMMAND: \"kwmsound\"",
|
|
|
|
-" FD SOCKET SOCK FAMILY:TYPE SOURCE:PORT DESTINATION:PORT",
|
|
|
|
-" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0:1026 0.0.0.0:0",
|
|
|
|
+" FD SOCKET SOCK FAMILY:TYPE SOURCE-PORT DESTINATION-PORT",
|
|
|
|
+" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0-1026 0.0.0.0-0",
|
|
|
|
" ",
|
|
|
|
" PID: 2197 TASK: c6696000 CPU: 0 COMMAND: \"kfm\"",
|
|
|
|
-" FD SOCKET SOCK FAMILY:TYPE SOURCE:PORT DESTINATION:PORT",
|
|
|
|
-" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0:1026 0.0.0.0:0",
|
|
|
|
+" FD SOCKET SOCK FAMILY:TYPE SOURCE-PORT DESTINATION-PORT",
|
|
|
|
+" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0-1026 0.0.0.0-0",
|
|
|
|
" ",
|
|
|
|
" PID: 2199 TASK: c65ec000 CPU: 0 COMMAND: \"krootwm\"",
|
|
|
|
-" FD SOCKET SOCK FAMILY:TYPE SOURCE:PORT DESTINATION:PORT",
|
|
|
|
-" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0:1026 0.0.0.0:0",
|
|
|
|
+" FD SOCKET SOCK FAMILY:TYPE SOURCE-PORT DESTINATION-PORT",
|
|
|
|
+" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0-1026 0.0.0.0-0",
|
|
|
|
" ",
|
|
|
|
" PID: 694 TASK: c1942000 CPU: 0 COMMAND: \"prefdm\"",
|
|
|
|
-" FD SOCKET SOCK FAMILY:TYPE SOURCE:PORT DESTINATION:PORT",
|
|
|
|
-" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0:1026 0.0.0.0:0",
|
|
|
|
+" FD SOCKET SOCK FAMILY:TYPE SOURCE-PORT DESTINATION-PORT",
|
|
|
|
+" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0-1026 0.0.0.0-0",
|
|
|
|
" ",
|
|
|
|
" PID: 698 TASK: c6a2c000 CPU: 1 COMMAND: \"X\"",
|
|
|
|
-" FD SOCKET SOCK FAMILY:TYPE SOURCE:PORT DESTINATION:PORT",
|
|
|
|
-" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0:1026 0.0.0.0:0",
|
|
|
|
+" FD SOCKET SOCK FAMILY:TYPE SOURCE-PORT DESTINATION-PORT",
|
|
|
|
+" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0-1026 0.0.0.0-0",
|
|
|
|
" ",
|
|
|
|
" PID: 2159 TASK: c4a5a000 CPU: 1 COMMAND: \"kwm\"",
|
|
|
|
-" FD SOCKET SOCK FAMILY:TYPE SOURCE:PORT DESTINATION:PORT",
|
|
|
|
-" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0:1026 0.0.0.0:0",
|
|
|
|
+" FD SOCKET SOCK FAMILY:TYPE SOURCE-PORT DESTINATION-PORT",
|
|
|
|
+" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0-1026 0.0.0.0-0",
|
|
|
|
" ",
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
@@ -4854,9 +4864,11 @@
|
|
|
|
static
|
|
|
|
char *version_info[] = {
|
|
|
|
|
|
|
|
-"Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc.",
|
|
|
|
-"Copyright (C) 2004, 2005 IBM Corporation",
|
|
|
|
-"Copyright (C) 1999-2005 Hewlett-Packard Co",
|
|
|
|
+"Copyright (C) 2002, 2003, 2004, 2005, 2006 Red Hat, Inc.",
|
|
|
|
+"Copyright (C) 2004, 2005, 2006 IBM Corporation",
|
|
|
|
+"Copyright (C) 1999-2006 Hewlett-Packard Co",
|
|
|
|
+"Copyright (C) 2005 Fujitsu Limited",
|
|
|
|
+"Copyright (C) 2005 NEC Corporation",
|
|
|
|
"Copyright (C) 1999, 2002 Silicon Graphics, Inc.",
|
|
|
|
"Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc.",
|
|
|
|
"This program is free software, covered by the GNU General Public License,",
|
|
|
|
--- crash/task.c.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/task.c 2005-11-10 15:42:40.000000000 -0500
|
|
|
|
@@ -2938,11 +2938,13 @@
|
|
|
|
if (is_task_active(tc->task)) {
|
|
|
|
if (machdep->flags & HWRESET)
|
|
|
|
fprintf(fp, "(HARDWARE RESET)");
|
|
|
|
- else if (machdep->flags & SYSRQ)
|
|
|
|
+ else if ((pc->flags & SYSRQ) && (tc->task == tt->panic_task))
|
|
|
|
fprintf(fp, "(SYSRQ)");
|
|
|
|
else if (machdep->flags & INIT)
|
|
|
|
fprintf(fp, "(INIT)");
|
|
|
|
- else if (kt->cpu_flags[tc->processor] & NMI)
|
|
|
|
+ else if ((tc->processor >= 0) &&
|
|
|
|
+ (tc->processor < NR_CPUS) &&
|
|
|
|
+ (kt->cpu_flags[tc->processor] & NMI))
|
|
|
|
fprintf(fp, "(NMI)");
|
|
|
|
else if (tc->task == tt->panic_task)
|
|
|
|
fprintf(fp, "(PANIC)");
|
|
|
|
@@ -3411,6 +3413,9 @@
|
|
|
|
|
|
|
|
use_task_0:
|
|
|
|
|
|
|
|
+ if (CRASHDEBUG(1))
|
|
|
|
+ error(INFO, "get_panic_context: panic task not found\n");
|
|
|
|
+
|
|
|
|
tt->flags |= PANIC_TASK_NOT_FOUND;
|
|
|
|
tc = FIRST_CONTEXT();
|
|
|
|
return(tc->task);
|
|
|
|
@@ -3448,49 +3453,68 @@
|
|
|
|
int msg_found;
|
|
|
|
|
|
|
|
BZERO(buf, BUFSIZE);
|
|
|
|
+ msg_found = FALSE;
|
|
|
|
|
|
|
|
- if (tt->panicmsg)
|
|
|
|
+ if (tt->panicmsg) {
|
|
|
|
read_string(tt->panicmsg, buf, BUFSIZE-1);
|
|
|
|
- else if (LKCD_DUMPFILE())
|
|
|
|
+ msg_found = TRUE;
|
|
|
|
+ } else if (LKCD_DUMPFILE()) {
|
|
|
|
get_lkcd_panicmsg(buf);
|
|
|
|
- else {
|
|
|
|
- msg_found = FALSE;
|
|
|
|
+ msg_found = TRUE;
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- open_tmpfile();
|
|
|
|
- dump_log(FALSE);
|
|
|
|
+ if (msg_found == TRUE)
|
|
|
|
+ return(buf);
|
|
|
|
|
|
|
|
- rewind(pc->tmpfile);
|
|
|
|
- while (!msg_found && fgets(buf, BUFSIZE, pc->tmpfile)) {
|
|
|
|
- if (strstr(buf, "Kernel panic: "))
|
|
|
|
- msg_found = TRUE;
|
|
|
|
- }
|
|
|
|
- rewind(pc->tmpfile);
|
|
|
|
- while (!msg_found && fgets(buf, BUFSIZE, pc->tmpfile)) {
|
|
|
|
- if (strstr(buf, "Oops: ") ||
|
|
|
|
- strstr(buf, "kernel BUG at"))
|
|
|
|
- msg_found = TRUE;
|
|
|
|
- }
|
|
|
|
- rewind(pc->tmpfile);
|
|
|
|
- while (!msg_found && fgets(buf, BUFSIZE, pc->tmpfile)) {
|
|
|
|
- if (strstr(buf, "SysRq : Netdump") ||
|
|
|
|
- strstr(buf, "SysRq : Crash")) {
|
|
|
|
- machdep->flags |= SYSRQ;
|
|
|
|
- msg_found = TRUE;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- rewind(pc->tmpfile);
|
|
|
|
- while (!msg_found && fgets(buf, BUFSIZE, pc->tmpfile)) {
|
|
|
|
- if (strstr(buf, "sysrq") &&
|
|
|
|
- symbol_exists("sysrq_pressed"))
|
|
|
|
- get_symbol_data("sysrq_pressed", sizeof(int),
|
|
|
|
- &msg_found);
|
|
|
|
- }
|
|
|
|
+ open_tmpfile();
|
|
|
|
+ dump_log(FALSE);
|
|
|
|
|
|
|
|
- close_tmpfile();
|
|
|
|
+ /*
|
|
|
|
+ * First check for a SYSRQ-generated crash, and set the
|
|
|
|
+ * active-task flag appropriately. The message may or
|
|
|
|
+ * may not be used as the panic message.
|
|
|
|
+ */
|
|
|
|
+ rewind(pc->tmpfile);
|
|
|
|
+ while (fgets(buf, BUFSIZE, pc->tmpfile)) {
|
|
|
|
+ if (strstr(buf, "SysRq : Crash") ||
|
|
|
|
+ strstr(buf, "SysRq : Trigger a crashdump")) {
|
|
|
|
+ pc->flags |= SYSRQ;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- if (!msg_found)
|
|
|
|
- BZERO(buf, BUFSIZE);
|
|
|
|
+ rewind(pc->tmpfile);
|
|
|
|
+ while (!msg_found && fgets(buf, BUFSIZE, pc->tmpfile)) {
|
|
|
|
+ if (strstr(buf, "Kernel panic: "))
|
|
|
|
+ msg_found = TRUE;
|
|
|
|
}
|
|
|
|
+ rewind(pc->tmpfile);
|
|
|
|
+ while (!msg_found && fgets(buf, BUFSIZE, pc->tmpfile)) {
|
|
|
|
+ if (strstr(buf, "Oops: ") ||
|
|
|
|
+ strstr(buf, "kernel BUG at"))
|
|
|
|
+ msg_found = TRUE;
|
|
|
|
+ }
|
|
|
|
+ rewind(pc->tmpfile);
|
|
|
|
+ while (!msg_found && fgets(buf, BUFSIZE, pc->tmpfile)) {
|
|
|
|
+ if (strstr(buf, "SysRq : Netdump") ||
|
|
|
|
+ strstr(buf, "SysRq : Trigger a crashdump") ||
|
|
|
|
+ strstr(buf, "SysRq : Crash")) {
|
|
|
|
+ pc->flags |= SYSRQ;
|
|
|
|
+ msg_found = TRUE;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ rewind(pc->tmpfile);
|
|
|
|
+ while (!msg_found && fgets(buf, BUFSIZE, pc->tmpfile)) {
|
|
|
|
+ if (strstr(buf, "sysrq") &&
|
|
|
|
+ symbol_exists("sysrq_pressed"))
|
|
|
|
+ get_symbol_data("sysrq_pressed", sizeof(int),
|
|
|
|
+ &msg_found);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ close_tmpfile();
|
|
|
|
+
|
|
|
|
+ if (!msg_found)
|
|
|
|
+ BZERO(buf, BUFSIZE);
|
|
|
|
|
|
|
|
return(buf);
|
|
|
|
}
|
|
|
|
@@ -3517,7 +3541,7 @@
|
|
|
|
BZERO(&foreach_data, sizeof(struct foreach_data));
|
|
|
|
fd = &foreach_data;
|
|
|
|
|
|
|
|
- while ((c = getopt(argcnt, args, "R:vomlgersStpukcf")) != EOF) {
|
|
|
|
+ while ((c = getopt(argcnt, args, "R:vomlgersStTpukcf")) != EOF) {
|
|
|
|
switch(c)
|
|
|
|
{
|
|
|
|
case 'R':
|
|
|
|
@@ -3560,6 +3584,10 @@
|
|
|
|
fd->flags |= FOREACH_r_FLAG;
|
|
|
|
break;
|
|
|
|
|
|
|
|
+ case 'T':
|
|
|
|
+ fd->flags |= FOREACH_T_FLAG;
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
case 't':
|
|
|
|
fd->flags |= FOREACH_t_FLAG;
|
|
|
|
break;
|
|
|
|
@@ -3962,7 +3990,12 @@
|
|
|
|
bt->flags |= BT_SYMBOLIC_ARGS;
|
|
|
|
if (fd->flags & FOREACH_t_FLAG)
|
|
|
|
bt->flags |= BT_TEXT_SYMBOLS;
|
|
|
|
- if (fd->flags & FOREACH_o_FLAG)
|
|
|
|
+ if (fd->flags & FOREACH_T_FLAG) {
|
|
|
|
+ bt->flags |= BT_TEXT_SYMBOLS;
|
|
|
|
+ bt->flags |= BT_TEXT_SYMBOLS_ALL;
|
|
|
|
+ }
|
|
|
|
+ if ((fd->flags & FOREACH_o_FLAG) ||
|
|
|
|
+ (kt->flags & USE_OLD_BT))
|
|
|
|
bt->flags |= BT_OLD_BACK_TRACE;
|
|
|
|
if (fd->flags & FOREACH_e_FLAG)
|
|
|
|
bt->flags |= BT_EFRAME_SEARCH;
|
|
|
|
@@ -4188,6 +4221,12 @@
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
+ if (strstr(buf, " crash_kexec at ") ||
|
|
|
|
+ strstr(buf, " .crash_kexec at ")) {
|
|
|
|
+ found = TRUE;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
if (strstr(buf, " die at ")) {
|
|
|
|
switch (dietask)
|
|
|
|
{
|
|
|
|
@@ -4211,6 +4250,10 @@
|
|
|
|
if (dietask == (NO_TASK+1))
|
|
|
|
error(WARNING, "multiple active tasks have called die\n\n");
|
|
|
|
|
|
|
|
+ if (CRASHDEBUG(1))
|
|
|
|
+ error(INFO, "panic_search: %lx (via foreach bt)\n",
|
|
|
|
+ lasttask);
|
|
|
|
+
|
|
|
|
found_panic_task:
|
|
|
|
populate_panic_threads();
|
|
|
|
|
|
|
|
@@ -4229,6 +4272,9 @@
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
+ if (CRASHDEBUG(1))
|
|
|
|
+ error(INFO, "panic_search: failed (via foreach bt)\n");
|
|
|
|
+
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
@@ -4240,25 +4286,24 @@
|
|
|
|
{
|
|
|
|
ulong task;
|
|
|
|
|
|
|
|
- if (LKCD_DUMPFILE())
|
|
|
|
- return(get_lkcd_panic_task());
|
|
|
|
-
|
|
|
|
if (NETDUMP_DUMPFILE()) {
|
|
|
|
task = pc->flags & REM_NETDUMP ?
|
|
|
|
tt->panic_task : get_netdump_panic_task();
|
|
|
|
if (task)
|
|
|
|
return task;
|
|
|
|
- if (get_active_set())
|
|
|
|
- return(get_active_set_panic_task());
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (DISKDUMP_DUMPFILE()) {
|
|
|
|
+ } else if (KDUMP_DUMPFILE()) {
|
|
|
|
+ task = get_kdump_panic_task();
|
|
|
|
+ if (task)
|
|
|
|
+ return task;
|
|
|
|
+ } else if (DISKDUMP_DUMPFILE()) {
|
|
|
|
task = get_diskdump_panic_task();
|
|
|
|
if (task)
|
|
|
|
return task;
|
|
|
|
- if (get_active_set())
|
|
|
|
- return(get_active_set_panic_task());
|
|
|
|
- }
|
|
|
|
+ } else if (LKCD_DUMPFILE())
|
|
|
|
+ return(get_lkcd_panic_task());
|
|
|
|
+
|
|
|
|
+ if (get_active_set())
|
|
|
|
+ return(get_active_set_panic_task());
|
|
|
|
|
|
|
|
return NO_TASK;
|
|
|
|
}
|
|
|
|
@@ -4298,14 +4343,17 @@
|
|
|
|
|
|
|
|
tc = FIRST_CONTEXT();
|
|
|
|
for (i = 0; i < RUNNING_TASKS(); i++, tc++) {
|
|
|
|
- if (task_has_cpu(tc->task, NULL)) {
|
|
|
|
+ if (task_has_cpu(tc->task, NULL) &&
|
|
|
|
+ (tc->processor >= 0) &&
|
|
|
|
+ (tc->processor < NR_CPUS)) {
|
|
|
|
tt->panic_threads[tc->processor] = tc->task;
|
|
|
|
found++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found && !(kt->flags & SMP) &&
|
|
|
|
- (LKCD_DUMPFILE() || NETDUMP_DUMPFILE() || DISKDUMP_DUMPFILE()))
|
|
|
|
+ (LKCD_DUMPFILE() || NETDUMP_DUMPFILE() ||
|
|
|
|
+ KDUMP_DUMPFILE() || DISKDUMP_DUMPFILE()))
|
|
|
|
tt->panic_threads[0] = get_dumpfile_panic_task();
|
|
|
|
}
|
|
|
|
|
|
|
|
@@ -4799,23 +4847,33 @@
|
|
|
|
tt->flags &= ~ACTIVE_SET;
|
|
|
|
}
|
|
|
|
|
|
|
|
-#define RESOLVE_PANIC_AND_DIE_CALLERS() \
|
|
|
|
- if ((panic_task > (NO_TASK+1)) && !die_task) \
|
|
|
|
- return panic_task; \
|
|
|
|
- \
|
|
|
|
- if (panic_task && die_task) { \
|
|
|
|
- error(WARNING, \
|
|
|
|
- "multiple active tasks have called die and/or panic\n\n"); \
|
|
|
|
- return NO_TASK; \
|
|
|
|
- } \
|
|
|
|
- \
|
|
|
|
- if (die_task > (NO_TASK+1)) \
|
|
|
|
- return die_task; \
|
|
|
|
- else if (die_task == (NO_TASK+1)) \
|
|
|
|
- error(WARNING, \
|
|
|
|
+#define RESOLVE_PANIC_AND_DIE_CALLERS() \
|
|
|
|
+ if ((panic_task > (NO_TASK+1)) && !die_task) { \
|
|
|
|
+ if (CRASHDEBUG(1)) \
|
|
|
|
+ fprintf(fp, \
|
|
|
|
+ "get_active_set_panic_task: %lx (panic)\n", \
|
|
|
|
+ panic_task); \
|
|
|
|
+ return panic_task; \
|
|
|
|
+ } \
|
|
|
|
+ \
|
|
|
|
+ if (panic_task && die_task) { \
|
|
|
|
+ error(WARNING, \
|
|
|
|
+ "multiple active tasks have called die and/or panic\n\n"); \
|
|
|
|
+ goto no_panic_task_found; \
|
|
|
|
+ } \
|
|
|
|
+ \
|
|
|
|
+ if (die_task > (NO_TASK+1)) { \
|
|
|
|
+ if (CRASHDEBUG(1)) \
|
|
|
|
+ fprintf(fp, \
|
|
|
|
+ "get_active_set_panic_task: %lx (die)\n", \
|
|
|
|
+ die_task); \
|
|
|
|
+ return die_task; \
|
|
|
|
+ } \
|
|
|
|
+ else if (die_task == (NO_TASK+1)) \
|
|
|
|
+ error(WARNING, \
|
|
|
|
"multiple active tasks have called die\n\n");
|
|
|
|
|
|
|
|
-#define SEARCH_STACK_FOR_PANIC_AND_DIE_CALLERS() \
|
|
|
|
+#define SEARCH_STACK_FOR_PANIC_DIE_AND_KEXEC_CALLERS() \
|
|
|
|
while (fgets(buf, BUFSIZE, pc->tmpfile)) { \
|
|
|
|
if (strstr(buf, " die+")) { \
|
|
|
|
switch (die_task) \
|
|
|
|
@@ -4839,6 +4897,10 @@
|
|
|
|
break; \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
+ if (strstr(buf, " crash_kexec+") || \
|
|
|
|
+ strstr(buf, " .crash_kexec+")) { \
|
|
|
|
+ crash_kexec_task = task; \
|
|
|
|
+ } \
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
@@ -4850,11 +4912,11 @@
|
|
|
|
int i, j, found;
|
|
|
|
ulong task;
|
|
|
|
char buf[BUFSIZE];
|
|
|
|
- ulong panic_task, die_task;
|
|
|
|
+ ulong panic_task, die_task, crash_kexec_task;
|
|
|
|
char *tp;
|
|
|
|
struct task_context *tc;
|
|
|
|
|
|
|
|
- panic_task = die_task = NO_TASK;
|
|
|
|
+ panic_task = die_task = crash_kexec_task = NO_TASK;
|
|
|
|
|
|
|
|
for (i = 0; i < NR_CPUS; i++) {
|
|
|
|
if (!(task = tt->active_set[i]))
|
|
|
|
@@ -4875,7 +4937,7 @@
|
|
|
|
raw_stack_dump(GET_STACKBASE(task), STACKSIZE());
|
|
|
|
rewind(pc->tmpfile);
|
|
|
|
|
|
|
|
- SEARCH_STACK_FOR_PANIC_AND_DIE_CALLERS();
|
|
|
|
+ SEARCH_STACK_FOR_PANIC_DIE_AND_KEXEC_CALLERS();
|
|
|
|
|
|
|
|
close_tmpfile();
|
|
|
|
}
|
|
|
|
@@ -4903,7 +4965,7 @@
|
|
|
|
raw_stack_dump(tt->hardirq_ctx[i], SIZE(thread_union));
|
|
|
|
rewind(pc->tmpfile);
|
|
|
|
|
|
|
|
- SEARCH_STACK_FOR_PANIC_AND_DIE_CALLERS();
|
|
|
|
+ SEARCH_STACK_FOR_PANIC_DIE_AND_KEXEC_CALLERS();
|
|
|
|
|
|
|
|
close_tmpfile();
|
|
|
|
}
|
|
|
|
@@ -4930,7 +4992,7 @@
|
|
|
|
raw_stack_dump(tt->softirq_ctx[i], SIZE(thread_union));
|
|
|
|
rewind(pc->tmpfile);
|
|
|
|
|
|
|
|
- SEARCH_STACK_FOR_PANIC_AND_DIE_CALLERS();
|
|
|
|
+ SEARCH_STACK_FOR_PANIC_DIE_AND_KEXEC_CALLERS();
|
|
|
|
|
|
|
|
close_tmpfile();
|
|
|
|
}
|
|
|
|
@@ -4938,6 +5000,20 @@
|
|
|
|
RESOLVE_PANIC_AND_DIE_CALLERS();
|
|
|
|
}
|
|
|
|
|
|
|
|
+ if (crash_kexec_task) {
|
|
|
|
+ if (CRASHDEBUG(1))
|
|
|
|
+ error(INFO,
|
|
|
|
+ "get_active_set_panic_task: %lx (crash_kexec)\n",
|
|
|
|
+ crash_kexec_task);
|
|
|
|
+ return crash_kexec_task;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+no_panic_task_found:
|
|
|
|
+
|
|
|
|
+ if (CRASHDEBUG(1))
|
|
|
|
+ error(INFO,
|
|
|
|
+ "get_active_set_panic_task: failed\n");
|
|
|
|
+
|
|
|
|
return NO_TASK;
|
|
|
|
}
|
|
|
|
|
|
|
|
--- crash/kernel.c.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/kernel.c 2005-11-18 10:54:23.000000000 -0500
|
|
|
|
@@ -20,6 +20,7 @@
|
|
|
|
|
|
|
|
static void do_module_cmd(ulong, char *, ulong, char *, char *);
|
|
|
|
static char *find_module_objfile(char *, char *, char *);
|
|
|
|
+static char *module_objfile_search(char *, char *, char *);
|
|
|
|
static char *get_uptime(char *);
|
|
|
|
static char *get_loadavg(char *);
|
|
|
|
static void get_lkcd_regs(struct bt_info *, ulong *, ulong *);
|
|
|
|
@@ -51,7 +52,7 @@
|
|
|
|
kernel_init(int when)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
- char *p1, *p2, buf[BUFSIZE];;
|
|
|
|
+ char *p1, *p2, buf[BUFSIZE];
|
|
|
|
struct syment *sp1, *sp2;
|
|
|
|
|
|
|
|
if (pc->flags & KERNEL_DEBUG_QUERY)
|
|
|
|
@@ -1140,7 +1141,10 @@
|
|
|
|
bt = &bt_info;
|
|
|
|
BZERO(bt, sizeof(struct bt_info));
|
|
|
|
|
|
|
|
- while ((c = getopt(argcnt, args, "fF:I:S:aloreEgstd:R:")) != EOF) {
|
|
|
|
+ if (kt->flags & USE_OLD_BT)
|
|
|
|
+ bt->flags |= BT_OLD_BACK_TRACE;
|
|
|
|
+
|
|
|
|
+ while ((c = getopt(argcnt, args, "fF:I:S:aloreEgstTd:R:O")) != EOF) {
|
|
|
|
switch(c)
|
|
|
|
{
|
|
|
|
case 'f':
|
|
|
|
@@ -1151,6 +1155,28 @@
|
|
|
|
bt->flags |= BT_OLD_BACK_TRACE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
+ case 'O':
|
|
|
|
+ if (!machine_type("X86"))
|
|
|
|
+ option_not_supported(c);
|
|
|
|
+ else if (kt->flags & USE_OLD_BT) {
|
|
|
|
+ /*
|
|
|
|
+ * Make this setting idempotent across the use of
|
|
|
|
+ * $HOME/.crashrc, ./.crashrc, and "-i input" files.
|
|
|
|
+ * If we've been here before during initialization,
|
|
|
|
+ * leave it alone.
|
|
|
|
+ */
|
|
|
|
+ if (pc->flags & INIT_IFILE) {
|
|
|
|
+ error(INFO, "use old bt method by default (already set)\n");
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ kt->flags &= ~USE_OLD_BT;
|
|
|
|
+ error(INFO, "use new bt method by default\n");
|
|
|
|
+ } else {
|
|
|
|
+ kt->flags |= USE_OLD_BT;
|
|
|
|
+ error(INFO, "use old bt method by default\n");
|
|
|
|
+ }
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
case 'R':
|
|
|
|
if (refptr)
|
|
|
|
error(INFO, "only one -R option allowed\n");
|
|
|
|
@@ -1241,6 +1267,8 @@
|
|
|
|
bt->flags |= BT_SYMBOLIC_ARGS;
|
|
|
|
break;
|
|
|
|
|
|
|
|
+ case 'T':
|
|
|
|
+ bt->flags |= BT_TEXT_SYMBOLS_ALL;
|
|
|
|
case 't':
|
|
|
|
bt->flags |= BT_TEXT_SYMBOLS;
|
|
|
|
break;
|
|
|
|
@@ -1350,9 +1378,10 @@
|
|
|
|
char buf[BUFSIZE];
|
|
|
|
|
|
|
|
if (bt->flags & BT_TEXT_SYMBOLS) {
|
|
|
|
- fprintf(fp, "%sSTART: %s at %lx\n",
|
|
|
|
- space(VADDR_PRLEN > 8 ? 14 : 6),
|
|
|
|
- closest_symbol(eip), eip);
|
|
|
|
+ if (!(bt->flags & BT_TEXT_SYMBOLS_ALL))
|
|
|
|
+ fprintf(fp, "%sSTART: %s at %lx\n",
|
|
|
|
+ space(VADDR_PRLEN > 8 ? 14 : 6),
|
|
|
|
+ closest_symbol(eip), eip);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bt->hp)
|
|
|
|
@@ -1461,8 +1490,8 @@
|
|
|
|
if (bt->hp) {
|
|
|
|
if (bt->hp->esp && !INSTACK(bt->hp->esp, bt))
|
|
|
|
error(INFO,
|
|
|
|
- "invalid stack address for this task: %lx\n",
|
|
|
|
- bt->hp->esp);
|
|
|
|
+ "invalid stack address for this task: %lx\n (valid range: %lx - %lx)\n",
|
|
|
|
+ bt->hp->esp, bt->stackbase, bt->stacktop);
|
|
|
|
eip = bt->hp->eip;
|
|
|
|
esp = bt->hp->esp;
|
|
|
|
|
|
|
|
@@ -1471,6 +1500,8 @@
|
|
|
|
|
|
|
|
} else if (NETDUMP_DUMPFILE())
|
|
|
|
get_netdump_regs(bt, &eip, &esp);
|
|
|
|
+ else if (KDUMP_DUMPFILE())
|
|
|
|
+ get_kdump_regs(bt, &eip, &esp);
|
|
|
|
else if (DISKDUMP_DUMPFILE())
|
|
|
|
get_diskdump_regs(bt, &eip, &esp);
|
|
|
|
else if (LKCD_DUMPFILE())
|
|
|
|
@@ -1486,6 +1517,13 @@
|
|
|
|
if (bt->flags &
|
|
|
|
(BT_TEXT_SYMBOLS|BT_TEXT_SYMBOLS_PRINT|BT_TEXT_SYMBOLS_NOPRINT)) {
|
|
|
|
|
|
|
|
+ if (bt->flags & BT_TEXT_SYMBOLS_ALL) {
|
|
|
|
+ esp = bt->stackbase +
|
|
|
|
+ ((tt->flags & THREAD_INFO) ?
|
|
|
|
+ SIZE(thread_info) : SIZE(task_struct));
|
|
|
|
+ eip = 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
if (machdep->flags & MACHDEP_BT_TEXT) {
|
|
|
|
bt->instptr = eip;
|
|
|
|
bt->stkptr = esp;
|
|
|
|
@@ -1721,6 +1759,13 @@
|
|
|
|
*esp = *(up-1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
+ /* Egenera */
|
|
|
|
+ if (STREQ(sym, "netdump_ipi")) {
|
|
|
|
+ *eip = *up;
|
|
|
|
+ *esp = bt->task +
|
|
|
|
+ ((char *)(up-1) - bt->stackbuf);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
if (STREQ(sym, "smp_stop_cpu_interrupt")) {
|
|
|
|
*eip = *up;
|
|
|
|
*esp = bt->task +
|
|
|
|
@@ -2459,7 +2504,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
static char *
|
|
|
|
-find_module_objfile(char *modref, char *filename, char *tree)
|
|
|
|
+module_objfile_search(char *modref, char *filename, char *tree)
|
|
|
|
{
|
|
|
|
char buf[BUFSIZE];
|
|
|
|
char file[BUFSIZE];
|
|
|
|
@@ -2592,6 +2637,32 @@
|
|
|
|
return retbuf;
|
|
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * First look for a module based upon its reference name.
|
|
|
|
+ * If that fails, try replacing any underscores in the
|
|
|
|
+ * reference name with a dash.
|
|
|
|
+ *
|
|
|
|
+ * Example: module name "dm_mod" comes from "dm-mod.ko" objfile
|
|
|
|
+ */
|
|
|
|
+static char *
|
|
|
|
+find_module_objfile(char *modref, char *filename, char *tree)
|
|
|
|
+{
|
|
|
|
+ char * retbuf;
|
|
|
|
+ char tmpref[BUFSIZE];
|
|
|
|
+ int c;
|
|
|
|
+
|
|
|
|
+ retbuf = module_objfile_search(modref, filename, tree);
|
|
|
|
+
|
|
|
|
+ if (!retbuf) {
|
|
|
|
+ strncpy(tmpref, modref, BUFSIZE);
|
|
|
|
+ for (c = 0; c < BUFSIZE && tmpref[c]; c++)
|
|
|
|
+ if (tmpref[c] == '_')
|
|
|
|
+ tmpref[c] = '-';
|
|
|
|
+ retbuf = module_objfile_search(tmpref, filename, tree);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return retbuf;
|
|
|
|
+}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Unlink any temporary remote module object files.
|
|
|
|
@@ -3225,6 +3296,8 @@
|
|
|
|
fprintf(fp, "%sKMOD_V2", others++ ? "|" : "");
|
|
|
|
if (kt->flags & KALLSYMS_V2)
|
|
|
|
fprintf(fp, "%sKALLSYMS_V2", others++ ? "|" : "");
|
|
|
|
+ if (kt->flags & USE_OLD_BT)
|
|
|
|
+ fprintf(fp, "%sUSE_OLD_BT", others++ ? "|" : "");
|
|
|
|
fprintf(fp, ")\n");
|
|
|
|
fprintf(fp, " stext: %lx\n", kt->stext);
|
|
|
|
fprintf(fp, " etext: %lx\n", kt->etext);
|
|
|
|
@@ -4475,9 +4548,16 @@
|
|
|
|
ld->start = vec[i];
|
|
|
|
ld->list_head_offset = offset;
|
|
|
|
ld->end = vec_kvaddr;
|
|
|
|
+ ld->flags = RETURN_ON_LIST_ERROR;
|
|
|
|
|
|
|
|
hq_open();
|
|
|
|
- timer_cnt = do_list(ld);
|
|
|
|
+ if ((timer_cnt = do_list(ld)) == -1) {
|
|
|
|
+ /* Ignore chains with errors */
|
|
|
|
+ error(INFO,
|
|
|
|
+ "ignoring faulty timer list at index %d of timer array\n",
|
|
|
|
+ i/2);
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
if (!timer_cnt)
|
|
|
|
continue;
|
|
|
|
timer_list = (ulong *)GETBUF(timer_cnt * sizeof(ulong));
|
|
|
|
--- crash/net.c.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/net.c 2005-12-16 14:04:06.000000000 -0500
|
|
|
|
@@ -75,6 +75,7 @@
|
|
|
|
static void dump_sockets(ulong, struct reference *);
|
|
|
|
static int sym_socket_dump(ulong, int, int, ulong, struct reference *);
|
|
|
|
static void dump_hw_addr(unsigned char *, int);
|
|
|
|
+static char *dump_in6_addr_port(uint16_t *, uint16_t, char *, int *);
|
|
|
|
|
|
|
|
|
|
|
|
#define MK_TYPE_T(f,s,m) \
|
|
|
|
@@ -158,13 +159,6 @@
|
|
|
|
"in_ifaddr", "ifa_address");
|
|
|
|
|
|
|
|
STRUCT_SIZE_INIT(sock, "sock");
|
|
|
|
- MEMBER_OFFSET_INIT(sock_daddr, "sock", "daddr");
|
|
|
|
- MEMBER_OFFSET_INIT(sock_rcv_saddr, "sock", "rcv_saddr");
|
|
|
|
- MEMBER_OFFSET_INIT(sock_dport, "sock", "dport");
|
|
|
|
- MEMBER_OFFSET_INIT(sock_sport, "sock", "sport");
|
|
|
|
- MEMBER_OFFSET_INIT(sock_num, "sock", "num");
|
|
|
|
- MEMBER_OFFSET_INIT(sock_family, "sock", "family");
|
|
|
|
- MEMBER_OFFSET_INIT(sock_type, "sock", "type");
|
|
|
|
|
|
|
|
MEMBER_OFFSET_INIT(sock_family, "sock", "family");
|
|
|
|
if (VALID_MEMBER(sock_family)) {
|
|
|
|
@@ -195,7 +189,23 @@
|
|
|
|
*/
|
|
|
|
STRUCT_SIZE_INIT(inet_sock, "inet_sock");
|
|
|
|
STRUCT_SIZE_INIT(socket, "socket");
|
|
|
|
- MEMBER_OFFSET_INIT(inet_sock_inet, "inet_sock", "inet");
|
|
|
|
+
|
|
|
|
+ if (STRUCT_EXISTS("inet_opt")) {
|
|
|
|
+ MEMBER_OFFSET_INIT(inet_sock_inet, "inet_sock", "inet");
|
|
|
|
+ MEMBER_OFFSET_INIT(inet_opt_daddr, "inet_opt", "daddr");
|
|
|
|
+ MEMBER_OFFSET_INIT(inet_opt_rcv_saddr, "inet_opt", "rcv_saddr");
|
|
|
|
+ MEMBER_OFFSET_INIT(inet_opt_dport, "inet_opt", "dport");
|
|
|
|
+ MEMBER_OFFSET_INIT(inet_opt_sport, "inet_opt", "sport");
|
|
|
|
+ MEMBER_OFFSET_INIT(inet_opt_num, "inet_opt", "num");
|
|
|
|
+ } else { /* inet_opt moved to inet_sock */
|
|
|
|
+ ASSIGN_OFFSET(inet_sock_inet) = 0;
|
|
|
|
+ MEMBER_OFFSET_INIT(inet_opt_daddr, "inet_sock", "daddr");
|
|
|
|
+ MEMBER_OFFSET_INIT(inet_opt_rcv_saddr, "inet_sock", "rcv_saddr");
|
|
|
|
+ MEMBER_OFFSET_INIT(inet_opt_dport, "inet_sock", "dport");
|
|
|
|
+ MEMBER_OFFSET_INIT(inet_opt_sport, "inet_sock", "sport");
|
|
|
|
+ MEMBER_OFFSET_INIT(inet_opt_num, "inet_sock", "num");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
if (VALID_STRUCT(inet_sock) &&
|
|
|
|
INVALID_MEMBER(inet_sock_inet)) {
|
|
|
|
/*
|
|
|
|
@@ -213,12 +223,32 @@
|
|
|
|
ASSIGN_OFFSET(inet_sock_inet) =
|
|
|
|
SIZE(inet_sock) - STRUCT_SIZE("inet_opt");
|
|
|
|
}
|
|
|
|
- MEMBER_OFFSET_INIT(inet_opt_daddr, "inet_opt", "daddr");
|
|
|
|
- MEMBER_OFFSET_INIT(inet_opt_rcv_saddr, "inet_opt",
|
|
|
|
- "rcv_saddr");
|
|
|
|
- MEMBER_OFFSET_INIT(inet_opt_dport, "inet_opt", "dport");
|
|
|
|
- MEMBER_OFFSET_INIT(inet_opt_sport, "inet_opt", "sport");
|
|
|
|
- MEMBER_OFFSET_INIT(inet_opt_num, "inet_opt", "num");
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * If necessary, set inet_sock size and inet_sock_inet offset,
|
|
|
|
+ * accounting for the configuration-dependent, intervening,
|
|
|
|
+ * struct ipv6_pinfo pointer located in between the sock and
|
|
|
|
+ * inet_opt members of the inet_sock.
|
|
|
|
+ */
|
|
|
|
+ if (!VALID_STRUCT(inet_sock))
|
|
|
|
+ {
|
|
|
|
+ if (symbol_exists("tcpv6_protocol") &&
|
|
|
|
+ symbol_exists("udpv6_protocol")) {
|
|
|
|
+ ASSIGN_SIZE(inet_sock) = SIZE(sock) +
|
|
|
|
+ sizeof(void *) + STRUCT_SIZE("inet_opt");
|
|
|
|
+ ASSIGN_OFFSET(inet_sock_inet) = SIZE(sock) +
|
|
|
|
+ sizeof(void *);
|
|
|
|
+ } else {
|
|
|
|
+ ASSIGN_SIZE(inet_sock) = SIZE(sock) +
|
|
|
|
+ STRUCT_SIZE("inet_opt");
|
|
|
|
+ ASSIGN_OFFSET(inet_sock_inet) = SIZE(sock);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ MEMBER_OFFSET_INIT(ipv6_pinfo_rcv_saddr, "ipv6_pinfo", "rcv_saddr");
|
|
|
|
+ MEMBER_OFFSET_INIT(ipv6_pinfo_daddr, "ipv6_pinfo", "daddr");
|
|
|
|
+ STRUCT_SIZE_INIT(in6_addr, "in6_addr");
|
|
|
|
+
|
|
|
|
net->flags |= SOCK_V2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -378,6 +408,24 @@
|
|
|
|
nhash_buckets = (i = ARRAY_LENGTH(neigh_table_hash_buckets)) ?
|
|
|
|
i : get_array_length("neigh_table.hash_buckets",
|
|
|
|
NULL, sizeof(void *));
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * NOTE: 2.6.8 -> 2.6.9 neigh_table struct changed from:
|
|
|
|
+ *
|
|
|
|
+ * struct neighbour *hash_buckets[32];
|
|
|
|
+ * to
|
|
|
|
+ * struct neighbour **hash_buckets;
|
|
|
|
+ *
|
|
|
|
+ * Even after hardwiring and testing with the correct
|
|
|
|
+ * array size, other changes cause this command to break
|
|
|
|
+ * down, so it needs to be looked at by someone who cares...
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+ if (nhash_buckets == 0) {
|
|
|
|
+ option_not_supported('a');
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
hash_bytes = nhash_buckets * sizeof(*hash_buckets);
|
|
|
|
|
|
|
|
hash_buckets = (ulong *)GETBUF(hash_bytes);
|
|
|
|
@@ -609,8 +657,14 @@
|
|
|
|
uint16_t dport, sport;
|
|
|
|
ushort num, family, type;
|
|
|
|
char *sockbuf, *inet_sockbuf;
|
|
|
|
+ ulong ipv6_pinfo, ipv6_rcv_saddr, ipv6_daddr;
|
|
|
|
+ uint16_t u6_addr16_src[8];
|
|
|
|
+ uint16_t u6_addr16_dest[8];
|
|
|
|
+ char buf2[BUFSIZE];
|
|
|
|
+ int len;
|
|
|
|
|
|
|
|
BZERO(buf, BUFSIZE);
|
|
|
|
+ BZERO(buf2, BUFSIZE);
|
|
|
|
sockbuf = inet_sockbuf = NULL;
|
|
|
|
|
|
|
|
switch (net->flags & (SOCK_V1|SOCK_V2))
|
|
|
|
@@ -646,6 +700,7 @@
|
|
|
|
OFFSET(inet_opt_num));
|
|
|
|
family = USHORT(inet_sockbuf + OFFSET(sock_common_skc_family));
|
|
|
|
type = USHORT(inet_sockbuf + OFFSET(sock_sk_type));
|
|
|
|
+ ipv6_pinfo = ULONG(inet_sockbuf + SIZE(sock));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
@@ -723,27 +778,28 @@
|
|
|
|
}
|
|
|
|
|
|
|
|
/* make sure we have room at the end... */
|
|
|
|
- sprintf(&buf[strlen(buf)], "%s", space(MINSPACE-1));
|
|
|
|
+// sprintf(&buf[strlen(buf)], "%s", space(MINSPACE-1));
|
|
|
|
+ sprintf(&buf[strlen(buf)], " ");
|
|
|
|
|
|
|
|
if (family == AF_INET) {
|
|
|
|
if (BITS32()) {
|
|
|
|
- sprintf(&buf[strlen(buf)], "%*s:%-*d%s",
|
|
|
|
+ sprintf(&buf[strlen(buf)], "%*s-%-*d%s",
|
|
|
|
BYTES_IP_ADDR,
|
|
|
|
inet_ntoa(*((struct in_addr *)&rcv_saddr)),
|
|
|
|
BYTES_PORT_NUM,
|
|
|
|
ntohs(sport),
|
|
|
|
space(1));
|
|
|
|
- sprintf(&buf[strlen(buf)], "%*s:%-*d%s",
|
|
|
|
+ sprintf(&buf[strlen(buf)], "%*s-%-*d%s",
|
|
|
|
BYTES_IP_ADDR,
|
|
|
|
inet_ntoa(*((struct in_addr *)&daddr)),
|
|
|
|
BYTES_PORT_NUM,
|
|
|
|
ntohs(dport),
|
|
|
|
space(1));
|
|
|
|
} else {
|
|
|
|
- sprintf(&buf[strlen(buf)], " %s:%d ",
|
|
|
|
+ sprintf(&buf[strlen(buf)], " %s-%d ",
|
|
|
|
inet_ntoa(*((struct in_addr *)&rcv_saddr)),
|
|
|
|
ntohs(sport));
|
|
|
|
- sprintf(&buf[strlen(buf)], "%s:%d",
|
|
|
|
+ sprintf(&buf[strlen(buf)], "%s-%d",
|
|
|
|
inet_ntoa(*((struct in_addr *)&daddr)),
|
|
|
|
ntohs(dport));
|
|
|
|
}
|
|
|
|
@@ -753,6 +809,60 @@
|
|
|
|
FREEBUF(sockbuf);
|
|
|
|
if (inet_sockbuf)
|
|
|
|
FREEBUF(inet_sockbuf);
|
|
|
|
+
|
|
|
|
+ if (family != AF_INET6)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ switch (net->flags & (SOCK_V1|SOCK_V2))
|
|
|
|
+ {
|
|
|
|
+ case SOCK_V1:
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case SOCK_V2:
|
|
|
|
+ if (INVALID_MEMBER(ipv6_pinfo_rcv_saddr) ||
|
|
|
|
+ INVALID_MEMBER(ipv6_pinfo_daddr))
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ ipv6_rcv_saddr = ipv6_pinfo + OFFSET(ipv6_pinfo_rcv_saddr);
|
|
|
|
+ ipv6_daddr = ipv6_pinfo + OFFSET(ipv6_pinfo_daddr);
|
|
|
|
+
|
|
|
|
+ if (!readmem(ipv6_rcv_saddr, KVADDR, u6_addr16_src, SIZE(in6_addr),
|
|
|
|
+ "ipv6_rcv_saddr buffer", QUIET|RETURN_ON_ERROR))
|
|
|
|
+ break;
|
|
|
|
+ if (!readmem(ipv6_daddr, KVADDR, u6_addr16_dest, SIZE(in6_addr),
|
|
|
|
+ "ipv6_daddr buffer", QUIET|RETURN_ON_ERROR))
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ sprintf(&buf[strlen(buf)], "%*s ", BITS32() ? 22 : 12,
|
|
|
|
+ dump_in6_addr_port(u6_addr16_src, sport, buf2, &len));
|
|
|
|
+ if (BITS32() && (len > 22))
|
|
|
|
+ len = 1;
|
|
|
|
+ mkstring(dump_in6_addr_port(u6_addr16_dest, dport, buf2, NULL),
|
|
|
|
+ len, CENTER, NULL);
|
|
|
|
+ sprintf(&buf[strlen(buf)], "%s", buf2);
|
|
|
|
+
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static char *
|
|
|
|
+dump_in6_addr_port(uint16_t *addr, uint16_t port, char *buf, int *len)
|
|
|
|
+{
|
|
|
|
+ sprintf(buf, "%x:%x:%x:%x:%x:%x:%x:%x-%d",
|
|
|
|
+ ntohs(addr[0]),
|
|
|
|
+ ntohs(addr[1]),
|
|
|
|
+ ntohs(addr[2]),
|
|
|
|
+ ntohs(addr[3]),
|
|
|
|
+ ntohs(addr[4]),
|
|
|
|
+ ntohs(addr[5]),
|
|
|
|
+ ntohs(addr[6]),
|
|
|
|
+ ntohs(addr[7]),
|
|
|
|
+ ntohs(port));
|
|
|
|
+
|
|
|
|
+ if (len)
|
|
|
|
+ *len = strlen(buf);
|
|
|
|
+
|
|
|
|
+ return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1096,9 +1206,9 @@
|
|
|
|
*/
|
|
|
|
|
|
|
|
static char *socket_hdr_32 =
|
|
|
|
-"FD SOCKET SOCK FAMILY:TYPE SOURCE:PORT DESTINATION:PORT";
|
|
|
|
+"FD SOCKET SOCK FAMILY:TYPE SOURCE-PORT DESTINATION-PORT";
|
|
|
|
static char *socket_hdr_64 =
|
|
|
|
-"FD SOCKET SOCK FAMILY:TYPE SOURCE:PORT DESTINATION:PORT";
|
|
|
|
+"FD SOCKET SOCK FAMILY:TYPE SOURCE-PORT DESTINATION-PORT";
|
|
|
|
|
|
|
|
static int
|
|
|
|
sym_socket_dump(ulong file,
|
|
|
|
@@ -1223,7 +1333,12 @@
|
|
|
|
dump_struct("sock", sock, 0);
|
|
|
|
break;
|
|
|
|
case SOCK_V2:
|
|
|
|
- dump_struct("inet_sock", sock, 0);
|
|
|
|
+ if (STRUCT_EXISTS("inet_sock"))
|
|
|
|
+ dump_struct("inet_sock", sock, 0);
|
|
|
|
+ else if (STRUCT_EXISTS("sock"))
|
|
|
|
+ dump_struct("sock", sock, 0);
|
|
|
|
+ else
|
|
|
|
+ fprintf(fp, "\nunable to display inet_sock structure\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
--- crash/dev.c.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/dev.c 2005-11-23 11:09:08.000000000 -0500
|
|
|
|
@@ -91,13 +91,13 @@
|
|
|
|
switch(c)
|
|
|
|
{
|
|
|
|
case 'i':
|
|
|
|
- if (machine_type("X86") || machine_type("S390X"))
|
|
|
|
+ if (machine_type("S390X"))
|
|
|
|
option_not_supported(c);
|
|
|
|
do_io();
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 'p':
|
|
|
|
- if (machine_type("X86") || machine_type("S390X"))
|
|
|
|
+ if (machine_type("S390X"))
|
|
|
|
option_not_supported(c);
|
|
|
|
do_pci();
|
|
|
|
return;
|
|
|
|
--- crash/alpha.c.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/alpha.c 2005-11-04 17:37:53.000000000 -0500
|
|
|
|
@@ -1858,8 +1858,6 @@
|
|
|
|
fprintf(fp, " flags: %lx (", machdep->flags);
|
|
|
|
if (machdep->flags & HWRESET)
|
|
|
|
fprintf(fp, "%sHWRESET", others++ ? "|" : "");
|
|
|
|
- if (machdep->flags & SYSRQ)
|
|
|
|
- fprintf(fp, "%sSYSRQ", others++ ? "|" : "");
|
|
|
|
fprintf(fp, ")\n");
|
|
|
|
fprintf(fp, " kvbase: %lx\n", machdep->kvbase);
|
|
|
|
fprintf(fp, " identity_map_base: %lx\n", machdep->identity_map_base);
|
|
|
|
--- crash/x86.c.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/x86.c 2005-11-10 13:53:05.000000000 -0500
|
|
|
|
@@ -685,6 +685,7 @@
|
|
|
|
bt->debug ||
|
|
|
|
(bt->flags & BT_FRAMESIZE_DEBUG) ||
|
|
|
|
!(bt->flags & BT_OLD_BACK_TRACE)) {
|
|
|
|
+ bt->flags &= ~BT_OLD_BACK_TRACE;
|
|
|
|
lkcd_x86_back_trace(bt, 0, fp);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
@@ -1441,6 +1442,20 @@
|
|
|
|
rv = bt->stackbase + sizeof(ulong) * (first - stack);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * 2.6 kernels using sysenter_entry instead of system_call
|
|
|
|
+ * have a funky trampoline EIP address.
|
|
|
|
+ */
|
|
|
|
+ if (((short)pt->reg_value[INT_EFRAME_CS] == 0x73) &&
|
|
|
|
+ ((short)pt->reg_value[INT_EFRAME_DS] == 0x7b) &&
|
|
|
|
+ ((short)pt->reg_value[INT_EFRAME_ES] == 0x7b) &&
|
|
|
|
+ ((short)pt->reg_value[INT_EFRAME_SS] == 0x7b) &&
|
|
|
|
+ (pt->reg_value[INT_EFRAME_EFLAGS] == 0x246) &&
|
|
|
|
+ IS_UVADDR(pt->reg_value[INT_EFRAME_ESP], bt->tc)) {
|
|
|
|
+ rv = bt->stackbase + sizeof(ulong) * (first - stack);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
}
|
|
|
|
return(rv);
|
|
|
|
}
|
|
|
|
@@ -2355,8 +2370,6 @@
|
|
|
|
fprintf(fp, "%sPAE", others++ ? "|" : "");
|
|
|
|
if (machdep->flags & OMIT_FRAME_PTR)
|
|
|
|
fprintf(fp, "%sOMIT_FRAME_PTR", others++ ? "|" : "");
|
|
|
|
- if (machdep->flags & SYSRQ)
|
|
|
|
- fprintf(fp, "%sSYSRQ", others++ ? "|" : "");
|
|
|
|
if (machdep->flags & FRAMESIZE_DEBUG)
|
|
|
|
fprintf(fp, "%sFRAMESIZE_DEBUG", others++ ? "|" : "");
|
|
|
|
fprintf(fp, ")\n");
|
|
|
|
@@ -3092,31 +3105,31 @@
|
|
|
|
* with the -fomit-frame-pointer flag.
|
|
|
|
*/
|
|
|
|
#define PUSH_BP_MOV_ESP_BP 0xe58955
|
|
|
|
+#define PUSH_BP_CLR_EAX_MOV_ESP_BP 0xe589c03155ULL
|
|
|
|
|
|
|
|
static int
|
|
|
|
x86_omit_frame_pointer(void)
|
|
|
|
{
|
|
|
|
- ulong push_bp_mov_esp_bp[3];
|
|
|
|
+ ulonglong push_bp_mov_esp_bp;
|
|
|
|
+ int i;
|
|
|
|
+ char *checkfuncs[] = {"sys_open", "sys_fork", "sys_read"};
|
|
|
|
|
|
|
|
if (pc->flags & KERNEL_DEBUG_QUERY)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
- if (!readmem(symbol_value("sys_open"), KVADDR, &push_bp_mov_esp_bp[0],
|
|
|
|
- sizeof(ulong), "x86_omit_frame_pointer", RETURN_ON_ERROR))
|
|
|
|
- return TRUE;
|
|
|
|
- if (!readmem(symbol_value("sys_fork"), KVADDR, &push_bp_mov_esp_bp[1],
|
|
|
|
- sizeof(ulong), "x86_omit_frame_pointer", RETURN_ON_ERROR))
|
|
|
|
- return TRUE;
|
|
|
|
- if (!readmem(symbol_value("sys_read"), KVADDR, &push_bp_mov_esp_bp[2],
|
|
|
|
- sizeof(ulong), "x86_omit_frame_pointer", RETURN_ON_ERROR))
|
|
|
|
- return TRUE;
|
|
|
|
-
|
|
|
|
- if (((push_bp_mov_esp_bp[0] & 0xffffff) == PUSH_BP_MOV_ESP_BP) &&
|
|
|
|
- ((push_bp_mov_esp_bp[1] & 0xffffff) == PUSH_BP_MOV_ESP_BP) &&
|
|
|
|
- ((push_bp_mov_esp_bp[2] & 0xffffff) == PUSH_BP_MOV_ESP_BP))
|
|
|
|
- return FALSE;
|
|
|
|
+ for (i = 0; i < 2; i++) {
|
|
|
|
+ if (!readmem(symbol_value(checkfuncs[i]), KVADDR,
|
|
|
|
+ &push_bp_mov_esp_bp, sizeof(ulonglong),
|
|
|
|
+ "x86_omit_frame_pointer", RETURN_ON_ERROR))
|
|
|
|
+ return TRUE;
|
|
|
|
+ if (!(((push_bp_mov_esp_bp & 0x0000ffffffULL) ==
|
|
|
|
+ PUSH_BP_MOV_ESP_BP) ||
|
|
|
|
+ ((push_bp_mov_esp_bp & 0xffffffffffULL) ==
|
|
|
|
+ PUSH_BP_CLR_EAX_MOV_ESP_BP)))
|
|
|
|
+ return TRUE;
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- return TRUE;
|
|
|
|
+ return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
--- crash/ppc.c.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/ppc.c 2005-11-04 17:37:53.000000000 -0500
|
|
|
|
@@ -154,8 +154,6 @@
|
|
|
|
fprintf(fp, " flags: %lx (", machdep->flags);
|
|
|
|
if (machdep->flags & KSYMS_START)
|
|
|
|
fprintf(fp, "%sKSYMS_START", others++ ? "|" : "");
|
|
|
|
- if (machdep->flags & SYSRQ)
|
|
|
|
- fprintf(fp, "%sSYSRQ", others++ ? "|" : "");
|
|
|
|
fprintf(fp, ")\n");
|
|
|
|
|
|
|
|
fprintf(fp, " kvbase: %lx\n", machdep->kvbase);
|
|
|
|
--- crash/ia64.c.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/ia64.c 2005-11-21 08:59:34.000000000 -0500
|
|
|
|
@@ -401,8 +401,6 @@
|
|
|
|
fprintf(fp, "%sUNW_R0", others++ ? "|" : "");
|
|
|
|
if (machdep->flags & MEM_LIMIT)
|
|
|
|
fprintf(fp, "%sMEM_LIMIT", others++ ? "|" : "");
|
|
|
|
- if (machdep->flags & SYSRQ)
|
|
|
|
- fprintf(fp, "%sSYSRQ", others++ ? "|" : "");
|
|
|
|
if (machdep->flags & DEVMEMRD)
|
|
|
|
fprintf(fp, "%sDEVMEMRD", others++ ? "|" : "");
|
|
|
|
if (machdep->flags & INIT)
|
|
|
|
@@ -2605,6 +2603,7 @@
|
|
|
|
ia64_post_init(void)
|
|
|
|
{
|
|
|
|
struct machine_specific *ms;
|
|
|
|
+ struct gnu_request req;
|
|
|
|
|
|
|
|
ms = &ia64_machine_specific;
|
|
|
|
|
|
|
|
@@ -2677,9 +2676,10 @@
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- if (symbol_exists("ia64_init_stack") && !ms->ia64_init_stack_size)
|
|
|
|
- ms->ia64_init_stack_size = get_array_length("ia64_init_stack",
|
|
|
|
- NULL, 0);
|
|
|
|
+ if (symbol_exists("ia64_init_stack") && !ms->ia64_init_stack_size) {
|
|
|
|
+ get_symbol_type("ia64_init_stack", NULL, &req);
|
|
|
|
+ ms->ia64_init_stack_size = req.length;
|
|
|
|
+ }
|
|
|
|
|
|
|
|
if (DUMPFILE() && ia64_in_init_stack(SWITCH_STACK_ADDR(CURRENT_TASK())))
|
|
|
|
machdep->flags |= INIT;
|
|
|
|
--- crash/s390.c.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/s390.c 2005-11-04 17:37:53.000000000 -0500
|
|
|
|
@@ -178,8 +178,6 @@
|
|
|
|
fprintf(fp, " flags: %lx (", machdep->flags);
|
|
|
|
if (machdep->flags & KSYMS_START)
|
|
|
|
fprintf(fp, "%sKSYMS_START", others++ ? "|" : "");
|
|
|
|
- if (machdep->flags & SYSRQ)
|
|
|
|
- fprintf(fp, "%sSYSRQ", others++ ? "|" : "");
|
|
|
|
fprintf(fp, ")\n");
|
|
|
|
|
|
|
|
fprintf(fp, " kvbase: %lx\n", machdep->kvbase);
|
|
|
|
--- crash/s390x.c.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/s390x.c 2005-11-04 17:37:53.000000000 -0500
|
|
|
|
@@ -193,8 +193,6 @@
|
|
|
|
fprintf(fp, " flags: %lx (", machdep->flags);
|
|
|
|
if (machdep->flags & KSYMS_START)
|
|
|
|
fprintf(fp, "%sKSYMS_START", others++ ? "|" : "");
|
|
|
|
- if (machdep->flags & SYSRQ)
|
|
|
|
- fprintf(fp, "%sSYSRQ", others++ ? "|" : "");
|
|
|
|
fprintf(fp, ")\n");
|
|
|
|
|
|
|
|
fprintf(fp, " kvbase: %lx\n", machdep->kvbase);
|
|
|
|
--- crash/ppc64.c.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/ppc64.c 2005-11-18 16:58:39.000000000 -0500
|
|
|
|
@@ -47,6 +47,7 @@
|
|
|
|
static char * ppc64_check_eframe(struct ppc64_pt_regs *);
|
|
|
|
static void ppc64_print_eframe(char *, struct ppc64_pt_regs *,
|
|
|
|
struct bt_info *);
|
|
|
|
+static void parse_cmdline_arg(void);
|
|
|
|
|
|
|
|
struct machine_specific ppc64_machine_specific = { { 0 }, 0, 0 };
|
|
|
|
|
|
|
|
@@ -75,12 +76,17 @@
|
|
|
|
error(FATAL, "cannot malloc pmd space.");
|
|
|
|
if ((machdep->ptbl = (char *)malloc(PAGESIZE())) == NULL)
|
|
|
|
error(FATAL, "cannot malloc ptbl space.");
|
|
|
|
+ if ((machdep->machspec->level4 = (char *)malloc(PAGESIZE())) == NULL)
|
|
|
|
+ error(FATAL, "cannot malloc level4 space.");
|
|
|
|
machdep->last_pgd_read = 0;
|
|
|
|
machdep->last_pmd_read = 0;
|
|
|
|
machdep->last_ptbl_read = 0;
|
|
|
|
+ machdep->machspec->last_level4_read = 0;
|
|
|
|
machdep->verify_paddr = generic_verify_paddr;
|
|
|
|
machdep->ptrs_per_pgd = PTRS_PER_PGD;
|
|
|
|
machdep->flags |= MACHDEP_BT_TEXT;
|
|
|
|
+ if (machdep->cmdline_arg)
|
|
|
|
+ parse_cmdline_arg();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PRE_GDB:
|
|
|
|
@@ -109,6 +115,52 @@
|
|
|
|
break;
|
|
|
|
|
|
|
|
case POST_GDB:
|
|
|
|
+ if (!(machdep->flags & (VM_ORIG|VM_4_LEVEL))) {
|
|
|
|
+ if (THIS_KERNEL_VERSION >= LINUX(2,6,14)) {
|
|
|
|
+ machdep->flags |= VM_4_LEVEL;
|
|
|
|
+ } else {
|
|
|
|
+ machdep->flags |= VM_ORIG;
|
|
|
|
+ }
|
2005-02-10 18:39:42 +00:00
|
|
|
+ }
|
2006-01-04 19:34:13 +00:00
|
|
|
+ if (machdep->flags & VM_ORIG) {
|
|
|
|
+ /* pre-2.6.14 layout */
|
|
|
|
+ free(machdep->machspec->level4);
|
|
|
|
+ machdep->machspec->level4 = NULL;
|
|
|
|
+ machdep->ptrs_per_pgd = PTRS_PER_PGD;
|
|
|
|
+ } else {
|
|
|
|
+ /* 2.6.14 layout */
|
|
|
|
+ struct machine_specific *m = machdep->machspec;
|
|
|
|
+ if (machdep->pagesize == 65536) {
|
|
|
|
+ /* 64K pagesize */
|
|
|
|
+ m->l1_index_size = PTE_INDEX_SIZE_L4_64K;
|
|
|
|
+ m->l2_index_size = PMD_INDEX_SIZE_L4_64K;
|
|
|
|
+ m->l3_index_size = PUD_INDEX_SIZE_L4_64K;
|
|
|
|
+ m->l4_index_size = PGD_INDEX_SIZE_L4_64K;
|
|
|
|
+ m->pte_shift = PTE_SHIFT_L4_64K;
|
|
|
|
+ m->l2_masked_bits = PMD_MASKED_BITS_64K;
|
|
|
|
+ } else {
|
|
|
|
+ /* 4K pagesize */
|
|
|
|
+ m->l1_index_size = PTE_INDEX_SIZE_L4_4K;
|
|
|
|
+ m->l2_index_size = PMD_INDEX_SIZE_L4_4K;
|
|
|
|
+ m->l3_index_size = PUD_INDEX_SIZE_L4_4K;
|
|
|
|
+ m->l4_index_size = PGD_INDEX_SIZE_L4_4K;
|
|
|
|
+ m->pte_shift = PTE_SHIFT_L4_4K;
|
|
|
|
+ m->l2_masked_bits = PMD_MASKED_BITS_4K;
|
|
|
|
+ }
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ /* Compute ptrs per each level */
|
|
|
|
+ m->l1_shift = machdep->pageshift;
|
|
|
|
+ m->ptrs_per_l1 = (1 << m->l1_index_size);
|
|
|
|
+ m->ptrs_per_l2 = (1 << m->l2_index_size);
|
|
|
|
+ m->ptrs_per_l3 = (1 << m->l3_index_size);
|
|
|
|
+
|
|
|
|
+ machdep->ptrs_per_pgd = m->ptrs_per_l3;
|
|
|
|
+
|
|
|
|
+ /* Compute shifts */
|
|
|
|
+ m->l2_shift = m->l1_shift + m->l1_index_size;
|
|
|
|
+ m->l3_shift = m->l2_shift + m->l2_index_size;
|
|
|
|
+ m->l4_shift = m->l3_shift + m->l3_index_size;
|
2005-02-10 18:39:42 +00:00
|
|
|
+ }
|
2006-01-04 19:34:13 +00:00
|
|
|
machdep->vmalloc_start = ppc64_vmalloc_start;
|
|
|
|
MEMBER_OFFSET_INIT(thread_struct_pg_tables,
|
|
|
|
"thread_struct", "pg_tables");
|
|
|
|
@@ -229,10 +281,12 @@
|
|
|
|
fprintf(fp, " flags: %lx (", machdep->flags);
|
|
|
|
if (machdep->flags & KSYMS_START)
|
|
|
|
fprintf(fp, "%sKSYMS_START", others++ ? "|" : "");
|
|
|
|
- if (machdep->flags & SYSRQ)
|
|
|
|
- fprintf(fp, "%sSYSRQ", others++ ? "|" : "");
|
|
|
|
if (machdep->flags & MACHDEP_BT_TEXT)
|
|
|
|
fprintf(fp, "%sMACHDEP_BT_TEXT", others++ ? "|" : "");
|
|
|
|
+ if (machdep->flags & VM_ORIG)
|
|
|
|
+ fprintf(fp, "%sVM_ORIG", others++ ? "|" : "");
|
|
|
|
+ if (machdep->flags & VM_4_LEVEL)
|
|
|
|
+ fprintf(fp, "%sVM_4_LEVEL", others++ ? "|" : "");
|
|
|
|
fprintf(fp, ")\n");
|
|
|
|
|
|
|
|
fprintf(fp, " kvbase: %lx\n", machdep->kvbase);
|
|
|
|
@@ -278,6 +332,10 @@
|
|
|
|
fprintf(fp, " ptbl: %lx\n", (ulong)machdep->ptbl);
|
|
|
|
fprintf(fp, " ptrs_per_pgd: %d\n", machdep->ptrs_per_pgd);
|
|
|
|
fprintf(fp, " machspec: %lx\n", (ulong)machdep->machspec);
|
|
|
|
+ fprintf(fp, " pgd_index_size: %ld\n", machdep->machspec->l4_index_size);
|
|
|
|
+ fprintf(fp, " pud_index_size: %ld\n", machdep->machspec->l3_index_size);
|
|
|
|
+ fprintf(fp, " pmd_index_size: %ld\n", machdep->machspec->l2_index_size);
|
|
|
|
+ fprintf(fp, " pte_index_size: %ld\n", machdep->machspec->l1_index_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
@@ -342,7 +400,7 @@
|
|
|
|
if (!(pte & _PAGE_PRESENT)) {
|
|
|
|
if (pte && verbose) {
|
|
|
|
fprintf(fp, "\n");
|
|
|
|
- ppc64_translate_pte(pte, 0, 0);
|
|
|
|
+ ppc64_translate_pte(pte, 0, PTE_SHIFT);
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
@@ -354,7 +412,90 @@
|
|
|
|
|
|
|
|
if (verbose) {
|
|
|
|
fprintf(fp, " PAGE: %lx\n\n", PAGEBASE(*paddr));
|
|
|
|
- ppc64_translate_pte(pte, 0, 0);
|
|
|
|
+ ppc64_translate_pte(pte, 0, PTE_SHIFT);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return TRUE;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Virtual to physical memory translation. This function will be called
|
|
|
|
+ * by both ppc64_kvtop and ppc64_uvtop.
|
|
|
|
+ */
|
|
|
|
+static int
|
|
|
|
+ppc64_vtop_level4(ulong vaddr, ulong *level4, physaddr_t *paddr, int verbose)
|
|
|
|
+{
|
|
|
|
+ ulong *level4_dir;
|
|
|
|
+ ulong *page_dir;
|
|
|
|
+ ulong *page_middle;
|
|
|
|
+ ulong *page_table;
|
|
|
|
+ ulong level4_pte, pgd_pte, pmd_pte;
|
|
|
|
+ ulong pte;
|
|
|
|
+
|
|
|
|
+ if (verbose)
|
|
|
|
+ fprintf(fp, "PAGE DIRECTORY: %lx\n", (ulong)level4);
|
|
|
|
+
|
|
|
|
+ level4_dir = (ulong *)((ulong *)level4 + L4_OFFSET(vaddr));
|
|
|
|
+ FILL_L4(PAGEBASE(level4), KVADDR, PAGESIZE());
|
|
|
|
+ level4_pte = ULONG(machdep->machspec->level4 + PAGEOFFSET(level4_dir));
|
|
|
|
+ if (verbose)
|
|
|
|
+ fprintf(fp, " L4: %lx => %lx\n", (ulong)level4_dir, level4_pte);
|
|
|
|
+ if (!level4_pte)
|
|
|
|
+ return FALSE;
|
|
|
|
+
|
|
|
|
+ /* Sometimes we don't have level3 pagetable entries */
|
|
|
|
+ if (machdep->machspec->l3_index_size != 0) {
|
|
|
|
+ page_dir = (ulong *)((ulong *)level4_pte + PGD_OFFSET_L4(vaddr));
|
|
|
|
+ FILL_PGD(PAGEBASE(level4_pte), KVADDR, PAGESIZE());
|
|
|
|
+ pgd_pte = ULONG(machdep->pgd + PAGEOFFSET(page_dir));
|
|
|
|
+
|
|
|
|
+ if (verbose)
|
|
|
|
+ fprintf(fp, " PGD: %lx => %lx\n", (ulong)page_dir, pgd_pte);
|
|
|
|
+ if (!pgd_pte)
|
|
|
|
+ return FALSE;
|
|
|
|
+ } else {
|
|
|
|
+ pgd_pte = level4_pte;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ page_middle = (ulong *)((ulong *)pgd_pte + PMD_OFFSET_L4(vaddr));
|
|
|
|
+ FILL_PMD(PAGEBASE(pgd_pte), KVADDR, PAGESIZE());
|
|
|
|
+ pmd_pte = ULONG(machdep->pmd + PAGEOFFSET(page_middle));
|
|
|
|
+
|
|
|
|
+ if (verbose)
|
|
|
|
+ fprintf(fp, " PMD: %lx => %lx\n", (ulong)page_middle, pmd_pte);
|
|
|
|
+
|
|
|
|
+ if (!(pmd_pte))
|
|
|
|
+ return FALSE;
|
|
|
|
+
|
|
|
|
+ page_table = (ulong *)(pmd_pte & ~(machdep->machspec->l2_masked_bits))
|
|
|
|
+ + (BTOP(vaddr) & (machdep->machspec->ptrs_per_l1 - 1));
|
|
|
|
+ if (verbose)
|
|
|
|
+ fprintf(fp, " PMD: %lx => %lx\n",(ulong)page_middle,
|
|
|
|
+ (ulong)page_table);
|
|
|
|
+
|
|
|
|
+ FILL_PTBL(PAGEBASE(pmd_pte), KVADDR, PAGESIZE());
|
|
|
|
+ pte = ULONG(machdep->ptbl + PAGEOFFSET(page_table));
|
|
|
|
+
|
|
|
|
+ if (verbose)
|
|
|
|
+ fprintf(fp, " PTE: %lx => %lx\n", (ulong)page_table, pte);
|
|
|
|
+
|
|
|
|
+ if (!(pte & _PAGE_PRESENT)) {
|
|
|
|
+ if (pte && verbose) {
|
|
|
|
+ fprintf(fp, "\n");
|
|
|
|
+ ppc64_translate_pte(pte, 0, machdep->machspec->pte_shift);
|
|
|
|
+ }
|
|
|
|
+ return FALSE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!pte)
|
|
|
|
+ return FALSE;
|
|
|
|
+
|
|
|
|
+ *paddr = PAGEBASE(PTOB(pte >> machdep->machspec->pte_shift))
|
|
|
|
+ + PAGEOFFSET(vaddr);
|
|
|
|
+
|
|
|
|
+ if (verbose) {
|
|
|
|
+ fprintf(fp, " PAGE: %lx\n\n", PAGEBASE(*paddr));
|
|
|
|
+ ppc64_translate_pte(pte, 0, machdep->machspec->pte_shift);
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
@@ -411,7 +552,10 @@
|
|
|
|
FAULT_ON_ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
- return ppc64_vtop(vaddr, pgd, paddr, verbose);
|
|
|
|
+ if (machdep->flags & VM_4_LEVEL)
|
|
|
|
+ return ppc64_vtop_level4(vaddr, pgd, paddr, verbose);
|
|
|
|
+ else
|
|
|
|
+ return ppc64_vtop(vaddr, pgd, paddr, verbose);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
@@ -436,7 +580,10 @@
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
- return ppc64_vtop(kvaddr, (ulong *)vt->kernel_pgd[0], paddr, verbose);
|
|
|
|
+ if (machdep->flags & VM_4_LEVEL)
|
|
|
|
+ return ppc64_vtop_level4(kvaddr, (ulong *)vt->kernel_pgd[0], paddr, verbose);
|
|
|
|
+ else
|
|
|
|
+ return ppc64_vtop(kvaddr, (ulong *)vt->kernel_pgd[0], paddr, verbose);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
@@ -657,7 +804,7 @@
|
|
|
|
* If a physaddr pointer is passed in, don't print anything.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
-ppc64_translate_pte(ulong pte, void *physaddr, ulonglong unused)
|
|
|
|
+ppc64_translate_pte(ulong pte, void *physaddr, ulonglong pte_shift)
|
|
|
|
{
|
|
|
|
int c, len1, len2, len3, others, page_present;
|
|
|
|
char buf[BUFSIZE];
|
|
|
|
@@ -668,7 +815,7 @@
|
|
|
|
char *arglist[MAXARGS];
|
|
|
|
ulong paddr;
|
|
|
|
|
|
|
|
- paddr = PTOB(pte >> PTE_SHIFT);
|
|
|
|
+ paddr = PTOB(pte >> pte_shift);
|
|
|
|
page_present = (pte & _PAGE_PRESENT);
|
|
|
|
|
|
|
|
if (physaddr) {
|
|
|
|
@@ -1424,6 +1571,7 @@
|
|
|
|
if (STREQ(sym, ".netconsole_netdump") ||
|
|
|
|
STREQ(sym, ".netpoll_start_netdump") ||
|
|
|
|
STREQ(sym, ".start_disk_dump") ||
|
|
|
|
+ STREQ(sym, ".crash_kexec") ||
|
|
|
|
STREQ(sym, ".disk_dump")) {
|
|
|
|
*nip = *up;
|
|
|
|
*ksp = bt->stackbase +
|
|
|
|
@@ -2000,4 +2148,78 @@
|
|
|
|
ppc64_dump_line_number(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Force the VM address-range selection via:
|
|
|
|
+ *
|
|
|
|
+ * --machdep vm=orig
|
|
|
|
+ * --machdep vm=2.6.14
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+void
|
|
|
|
+parse_cmdline_arg(void)
|
|
|
|
+{
|
|
|
|
+ int i, c, errflag;
|
|
|
|
+ char *p;
|
|
|
|
+ char buf[BUFSIZE];
|
|
|
|
+ char *arglist[MAXARGS];
|
|
|
|
+ int lines = 0;
|
|
|
|
+
|
|
|
|
+ if (!strstr(machdep->cmdline_arg, "=")) {
|
|
|
|
+ error(WARNING, "ignoring --machdep option: %s\n\n",
|
|
|
|
+ machdep->cmdline_arg);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ strcpy(buf, machdep->cmdline_arg);
|
|
|
|
+
|
|
|
|
+ for (p = buf; *p; p++) {
|
|
|
|
+ if (*p == ',')
|
|
|
|
+ *p = ' ';
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ c = parse_line(buf, arglist);
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ for (i = 0; i < c; i++) {
|
|
|
|
+ errflag = 0;
|
|
|
|
+
|
|
|
|
+ if (STRNEQ(arglist[i], "vm=")) {
|
|
|
|
+ p = arglist[i] + strlen("vm=");
|
|
|
|
+ if (strlen(p)) {
|
|
|
|
+ if (STREQ(p, "orig")) {
|
|
|
|
+ machdep->flags |= VM_ORIG;
|
|
|
|
+ continue;
|
|
|
|
+ } else if (STREQ(p, "2.6.14")) {
|
|
|
|
+ machdep->flags |= VM_4_LEVEL;
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+ }
|
2005-02-10 18:39:42 +00:00
|
|
|
+ }
|
2006-01-04 19:34:13 +00:00
|
|
|
+
|
|
|
|
+ error(WARNING, "ignoring --machdep option: %s\n", arglist[i]);
|
|
|
|
+ lines++;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ switch (machdep->flags & (VM_ORIG|VM_4_LEVEL))
|
|
|
|
+ {
|
|
|
|
+ case VM_ORIG:
|
|
|
|
+ error(NOTE, "using original PPC64 VM address ranges\n");
|
|
|
|
+ lines++;
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case VM_4_LEVEL:
|
|
|
|
+ error(NOTE, "using 4-level pagetable PPC64 VM address ranges\n");
|
|
|
|
+ lines++;
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case (VM_ORIG|VM_4_LEVEL):
|
|
|
|
+ error(WARNING, "cannot set both vm=orig and vm=2.6.14\n");
|
|
|
|
+ lines++;
|
|
|
|
+ machdep->flags &= ~(VM_ORIG|VM_4_LEVEL);
|
2005-02-10 18:39:42 +00:00
|
|
|
+ break;
|
2006-01-04 19:34:13 +00:00
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (lines)
|
|
|
|
+ fprintf(fp, "\n");
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
#endif /* PPC64 */
|
|
|
|
--- crash/x86_64.c.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/x86_64.c 2005-12-08 16:21:30.000000000 -0500
|
|
|
|
@@ -19,6 +19,7 @@
|
|
|
|
|
|
|
|
static int x86_64_kvtop(struct task_context *, ulong, physaddr_t *, int);
|
|
|
|
static int x86_64_uvtop(struct task_context *, ulong, physaddr_t *, int);
|
|
|
|
+static int x86_64_uvtop_level4(struct task_context *, ulong, physaddr_t *, int);
|
|
|
|
static ulong x86_64_vmalloc_start(void);
|
|
|
|
static int x86_64_is_task_addr(ulong);
|
|
|
|
static int x86_64_verify_symbol(const char *, ulong, char);
|
|
|
|
@@ -32,6 +33,7 @@
|
|
|
|
#define EFRAME_VERIFY (0x2)
|
|
|
|
#define EFRAME_CS (0x4)
|
|
|
|
#define EFRAME_SEARCH (0x8)
|
|
|
|
+static int x86_64_print_eframe_location(ulong, int, FILE *);
|
|
|
|
static void x86_64_back_trace_cmd(struct bt_info *);
|
|
|
|
static ulong x86_64_in_exception_stack(struct bt_info *);
|
|
|
|
static ulong x86_64_in_irqstack(struct bt_info *);
|
|
|
|
@@ -56,6 +58,7 @@
|
|
|
|
static void x86_64_display_memmap(void);
|
|
|
|
static void x86_64_dump_line_number(ulong);
|
|
|
|
static struct line_number_hook x86_64_line_number_hooks[];
|
|
|
|
+static int x86_64_is_module_addr(ulong);
|
|
|
|
static int x86_64_is_kvaddr(ulong);
|
|
|
|
static int x86_64_is_uvaddr(ulong, struct task_context *);
|
|
|
|
void x86_64_compiler_warning_stub(void);
|
|
|
|
@@ -63,7 +66,7 @@
|
|
|
|
static void x86_64_cpu_pda_init(void);
|
|
|
|
static void x86_64_ist_init(void);
|
|
|
|
static void x86_64_post_init(void);
|
|
|
|
-
|
|
|
|
+static void parse_cmdline_arg(void);
|
|
|
|
|
|
|
|
struct machine_specific x86_64_machine_specific = { 0 };
|
|
|
|
|
|
|
|
@@ -86,6 +89,8 @@
|
|
|
|
machdep->pageoffset = machdep->pagesize - 1;
|
|
|
|
machdep->pagemask = ~((ulonglong)machdep->pageoffset);
|
|
|
|
machdep->stacksize = machdep->pagesize * 2;
|
|
|
|
+ if ((machdep->machspec->upml = (char *)malloc(PAGESIZE())) == NULL)
|
|
|
|
+ error(FATAL, "cannot malloc upml space.");
|
|
|
|
if ((machdep->pgd = (char *)malloc(PAGESIZE())) == NULL)
|
|
|
|
error(FATAL, "cannot malloc pgd space.");
|
|
|
|
if ((machdep->pmd = (char *)malloc(PAGESIZE())) == NULL)
|
|
|
|
@@ -95,15 +100,54 @@
|
|
|
|
if ((machdep->machspec->pml4 =
|
|
|
|
(char *)malloc(PAGESIZE())) == NULL)
|
|
|
|
error(FATAL, "cannot malloc pml4 space.");
|
|
|
|
+ machdep->machspec->last_upml_read = 0;
|
|
|
|
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;
|
|
|
|
machdep->flags |= MACHDEP_BT_TEXT;
|
|
|
|
+ if (machdep->cmdline_arg)
|
|
|
|
+ parse_cmdline_arg();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PRE_GDB:
|
|
|
|
+ if (!(machdep->flags & (VM_ORIG|VM_2_6_11))) {
|
|
|
|
+ if (symbol_exists("boot_vmalloc_pgt"))
|
|
|
|
+ machdep->flags |= VM_ORIG;
|
|
|
|
+ else
|
|
|
|
+ machdep->flags |= VM_2_6_11;
|
|
|
|
+ }
|
|
|
|
+ switch (machdep->flags & (VM_ORIG|VM_2_6_11))
|
|
|
|
+ {
|
|
|
|
+ case VM_ORIG:
|
|
|
|
+ /* pre-2.6.11 layout */
|
|
|
|
+ machdep->machspec->userspace_top = USERSPACE_TOP_ORIG;
|
|
|
|
+ machdep->machspec->page_offset = PAGE_OFFSET_ORIG;
|
|
|
|
+ machdep->machspec->vmalloc_start_addr = VMALLOC_START_ADDR_ORIG;
|
|
|
|
+ machdep->machspec->vmalloc_end = VMALLOC_END_ORIG;
|
|
|
|
+ machdep->machspec->modules_vaddr = MODULES_VADDR_ORIG;
|
|
|
|
+ machdep->machspec->modules_end = MODULES_END_ORIG;
|
|
|
|
+
|
|
|
|
+ free(machdep->machspec->upml);
|
|
|
|
+ machdep->machspec->upml = NULL;
|
|
|
|
+
|
|
|
|
+ machdep->uvtop = x86_64_uvtop;
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case VM_2_6_11:
|
|
|
|
+ /* 2.6.11 layout */
|
|
|
|
+ machdep->machspec->userspace_top = USERSPACE_TOP_2_6_11;
|
|
|
|
+ machdep->machspec->page_offset = PAGE_OFFSET_2_6_11;
|
|
|
|
+ machdep->machspec->vmalloc_start_addr = VMALLOC_START_ADDR_2_6_11;
|
|
|
|
+ machdep->machspec->vmalloc_end = VMALLOC_END_2_6_11;
|
|
|
|
+ machdep->machspec->modules_vaddr = MODULES_VADDR_2_6_11;
|
|
|
|
+ machdep->machspec->modules_end = MODULES_END_2_6_11;
|
|
|
|
+ machdep->flags |= VM_2_6_11;
|
|
|
|
+
|
|
|
|
+ machdep->uvtop = x86_64_uvtop_level4;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
machdep->kvbase = (ulong)PAGE_OFFSET;
|
|
|
|
machdep->identity_map_base = (ulong)PAGE_OFFSET;
|
|
|
|
machdep->is_kvaddr = x86_64_is_kvaddr;
|
|
|
|
@@ -111,7 +155,6 @@
|
|
|
|
machdep->eframe_search = x86_64_eframe_search;
|
|
|
|
machdep->back_trace = x86_64_low_budget_back_trace_cmd;
|
|
|
|
machdep->processor_speed = x86_64_processor_speed;
|
|
|
|
- machdep->uvtop = x86_64_uvtop;
|
|
|
|
machdep->kvtop = x86_64_kvtop;
|
|
|
|
machdep->get_task_pgd = x86_64_get_task_pgd;
|
|
|
|
machdep->get_stack_frame = x86_64_get_stack_frame;
|
|
|
|
@@ -191,10 +234,12 @@
|
|
|
|
fprintf(fp, "%sKSYMS_START", others++ ? "|" : "");
|
|
|
|
if (machdep->flags & PT_REGS_INIT)
|
|
|
|
fprintf(fp, "%sPT_REGS_INIT", others++ ? "|" : "");
|
|
|
|
- if (machdep->flags & SYSRQ)
|
|
|
|
- fprintf(fp, "%sSYSRQ", others++ ? "|" : "");
|
|
|
|
if (machdep->flags & MACHDEP_BT_TEXT)
|
|
|
|
fprintf(fp, "%sMACHDEP_BT_TEXT", others++ ? "|" : "");
|
|
|
|
+ if (machdep->flags & VM_ORIG)
|
|
|
|
+ fprintf(fp, "%sVM_ORIG", others++ ? "|" : "");
|
|
|
|
+ if (machdep->flags & VM_2_6_11)
|
|
|
|
+ fprintf(fp, "%sVM_2_6_11", others++ ? "|" : "");
|
|
|
|
fprintf(fp, ")\n");
|
|
|
|
|
|
|
|
fprintf(fp, " kvbase: %lx\n", machdep->kvbase);
|
|
|
|
@@ -248,8 +293,22 @@
|
|
|
|
fprintf(fp, " pmd: %lx\n", (ulong)machdep->pmd);
|
|
|
|
fprintf(fp, " ptbl: %lx\n", (ulong)machdep->ptbl);
|
|
|
|
fprintf(fp, " ptrs_per_pgd: %d\n", machdep->ptrs_per_pgd);
|
|
|
|
- fprintf(fp, " machspec: %lx\n", (ulong)machdep->machspec);
|
|
|
|
+
|
|
|
|
+ fprintf(fp, " machspec: %016lx\n", (ulong)machdep->machspec);
|
|
|
|
+ fprintf(fp, " userspace_top: %016lx\n", (ulong)ms->userspace_top);
|
|
|
|
+ fprintf(fp, " page_offset: %016lx\n", (ulong)ms->page_offset);
|
|
|
|
+ fprintf(fp, " vmalloc_start_addr: %016lx\n", (ulong)ms->vmalloc_start_addr);
|
|
|
|
+ fprintf(fp, " vmalloc_end: %016lx\n", (ulong)ms->vmalloc_end);
|
|
|
|
+ fprintf(fp, " modules_vaddr: %016lx\n", (ulong)ms->modules_vaddr);
|
|
|
|
+ fprintf(fp, " modules_end: %016lx\n", (ulong)ms->modules_end);
|
|
|
|
fprintf(fp, " pml4: %lx\n", (ulong)ms->pml4);
|
|
|
|
+ if (ms->upml) {
|
|
|
|
+ fprintf(fp, " upml: %lx\n", (ulong)ms->upml);
|
|
|
|
+ fprintf(fp, " last_upml_read: %lx\n", (ulong)ms->last_upml_read);
|
|
|
|
+ } else {
|
|
|
|
+ fprintf(fp, " upml: (unused)\n");
|
|
|
|
+ fprintf(fp, " last_upml_read: (unused)\n");
|
|
|
|
+ }
|
|
|
|
fprintf(fp, " irqstack: %lx\n", (ulong)ms->irqstack);
|
|
|
|
fprintf(fp, " pto: %s",
|
|
|
|
machdep->flags & PT_REGS_INIT ? "\n" : "(uninitialized)\n");
|
|
|
|
@@ -320,6 +379,7 @@
|
|
|
|
MEMBER_OFFSET_INIT(x8664_pda_irqstackptr, "x8664_pda", "irqstackptr");
|
|
|
|
MEMBER_OFFSET_INIT(x8664_pda_level4_pgt, "x8664_pda", "level4_pgt");
|
|
|
|
MEMBER_OFFSET_INIT(x8664_pda_cpunumber, "x8664_pda", "cpunumber");
|
|
|
|
+ MEMBER_OFFSET_INIT(x8664_pda_me, "x8664_pda", "me");
|
|
|
|
|
|
|
|
cpu_pda_buf = GETBUF(SIZE(x8664_pda));
|
|
|
|
|
|
|
|
@@ -329,9 +389,13 @@
|
|
|
|
for (i = cpus = 0; i < nr_pda; i++) {
|
|
|
|
if (!CPU_PDA_READ(i, cpu_pda_buf))
|
|
|
|
break;
|
|
|
|
- level4_pgt = ULONG(cpu_pda_buf + OFFSET(x8664_pda_level4_pgt));
|
|
|
|
+ if (VALID_MEMBER(x8664_pda_level4_pgt)) {
|
|
|
|
+ level4_pgt = ULONG(cpu_pda_buf + OFFSET(x8664_pda_level4_pgt));
|
|
|
|
+ if (!VALID_LEVEL4_PGT_ADDR(level4_pgt))
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
cpunumber = INT(cpu_pda_buf + OFFSET(x8664_pda_cpunumber));
|
|
|
|
- if (!VALID_LEVEL4_PGT_ADDR(level4_pgt) || (cpunumber != cpus))
|
|
|
|
+ if (cpunumber != cpus)
|
|
|
|
break;
|
|
|
|
cpus++;
|
|
|
|
|
|
|
|
@@ -584,12 +648,19 @@
|
|
|
|
/*
|
|
|
|
* Include both vmalloc'd and module address space as VMALLOC space.
|
|
|
|
*/
|
|
|
|
-int x86_64_IS_VMALLOC_ADDR(ulong vaddr)
|
|
|
|
+int
|
|
|
|
+x86_64_IS_VMALLOC_ADDR(ulong vaddr)
|
|
|
|
{
|
|
|
|
return ((vaddr >= VMALLOC_START && vaddr <= VMALLOC_END) ||
|
|
|
|
(vaddr >= MODULES_VADDR && vaddr <= MODULES_END));
|
|
|
|
}
|
|
|
|
|
|
|
|
+static int
|
|
|
|
+x86_64_is_module_addr(ulong vaddr)
|
|
|
|
+{
|
|
|
|
+ return (vaddr >= MODULES_VADDR && vaddr <= MODULES_END);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
/*
|
|
|
|
* Refining this may cause more problems than just doing it this way.
|
|
|
|
*/
|
|
|
|
@@ -616,6 +687,114 @@
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int
|
|
|
|
+x86_64_uvtop_level4(struct task_context *tc, ulong uvaddr, physaddr_t *paddr, int verbose)
|
|
|
|
+{
|
|
|
|
+ ulong mm;
|
|
|
|
+ ulong *pml;
|
|
|
|
+ ulong pml_paddr;
|
|
|
|
+ ulong pml_pte;
|
|
|
|
+ ulong *pgd;
|
|
|
|
+ ulong pgd_paddr;
|
|
|
|
+ ulong pgd_pte;
|
|
|
|
+ ulong *pmd;
|
|
|
|
+ ulong pmd_paddr;
|
|
|
|
+ ulong pmd_pte;
|
|
|
|
+ ulong *ptep;
|
|
|
|
+ ulong pte_paddr;
|
|
|
|
+ ulong pte;
|
|
|
|
+ physaddr_t physpage;
|
|
|
|
+
|
|
|
|
+ if (!tc)
|
|
|
|
+ error(FATAL, "current context invalid\n");
|
|
|
|
+
|
|
|
|
+ *paddr = 0;
|
|
|
|
+
|
|
|
|
+ if (IS_KVADDR(uvaddr))
|
|
|
|
+ return x86_64_kvtop(tc, uvaddr, paddr, verbose);
|
|
|
|
+
|
|
|
|
+ if ((mm = task_mm(tc->task, TRUE)))
|
|
|
|
+ pml = ULONG_PTR(tt->mm_struct + OFFSET(mm_struct_pgd));
|
|
|
|
+ else
|
|
|
|
+ readmem(tc->mm_struct + OFFSET(mm_struct_pgd), KVADDR, &pml,
|
|
|
|
+ sizeof(long), "mm_struct pgd", FAULT_ON_ERROR);
|
|
|
|
+
|
|
|
|
+ pml_paddr = x86_64_VTOP((ulong)pml);
|
|
|
|
+ FILL_UPML(pml_paddr, PHYSADDR, PAGESIZE());
|
|
|
|
+ pml = ((ulong *)pml_paddr) + pml4_index(uvaddr);
|
|
|
|
+ pml_pte = ULONG(machdep->machspec->upml + PAGEOFFSET(pml));
|
|
|
|
+ if (verbose)
|
|
|
|
+ fprintf(fp, " PML: %lx => %lx\n", (ulong)pml, pml_pte);
|
|
|
|
+ if (!(pml_pte & _PAGE_PRESENT))
|
|
|
|
+ goto no_upage;
|
|
|
|
+
|
|
|
|
+ pgd_paddr = pml_pte & PHYSICAL_PAGE_MASK;
|
|
|
|
+ FILL_PGD(pgd_paddr, PHYSADDR, PAGESIZE());
|
|
|
|
+ pgd = ((ulong *)pgd_paddr) + pgd_index(uvaddr);
|
|
|
|
+ pgd_pte = ULONG(machdep->pgd + PAGEOFFSET(pgd));
|
|
|
|
+ if (verbose)
|
|
|
|
+ fprintf(fp, " PGD: %lx => %lx\n", (ulong)pgd, pgd_pte);
|
|
|
|
+ if (!(pgd_pte & _PAGE_PRESENT))
|
|
|
|
+ goto no_upage;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * pmd = pmd_offset(pgd, address);
|
|
|
|
+ */
|
|
|
|
+ pmd_paddr = pgd_pte & PHYSICAL_PAGE_MASK;
|
|
|
|
+ FILL_PMD(pmd_paddr, PHYSADDR, PAGESIZE());
|
|
|
|
+ pmd = ((ulong *)pmd_paddr) + pmd_index(uvaddr);
|
|
|
|
+ pmd_pte = ULONG(machdep->pmd + PAGEOFFSET(pmd));
|
|
|
|
+ if (verbose)
|
|
|
|
+ fprintf(fp, " PMD: %lx => %lx\n", (ulong)pmd, pmd_pte);
|
|
|
|
+ if (!(pmd_pte & _PAGE_PRESENT))
|
|
|
|
+ goto no_upage;
|
|
|
|
+ if (pmd_pte & _PAGE_PSE) {
|
|
|
|
+ if (verbose) {
|
|
|
|
+ fprintf(fp, " PAGE: %lx (2MB)\n\n",
|
|
|
|
+ PAGEBASE(pmd_pte) & PHYSICAL_PAGE_MASK);
|
|
|
|
+ x86_64_translate_pte(pmd_pte, 0, 0);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ physpage = (PAGEBASE(pmd_pte) & PHYSICAL_PAGE_MASK) +
|
|
|
|
+ (uvaddr & ~_2MB_PAGE_MASK);
|
|
|
|
+ *paddr = physpage;
|
|
|
|
+ return TRUE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * ptep = pte_offset_map(pmd, address);
|
|
|
|
+ * pte = *ptep;
|
|
|
|
+ */
|
|
|
|
+ pte_paddr = pmd_pte & PHYSICAL_PAGE_MASK;
|
|
|
|
+ FILL_PTBL(pte_paddr, PHYSADDR, PAGESIZE());
|
|
|
|
+ ptep = ((ulong *)pte_paddr) + pte_index(uvaddr);
|
|
|
|
+ pte = ULONG(machdep->ptbl + PAGEOFFSET(ptep));
|
|
|
|
+ if (verbose)
|
|
|
|
+ fprintf(fp, " PTE: %lx => %lx\n", (ulong)ptep, pte);
|
|
|
|
+ if (!(pte & (_PAGE_PRESENT))) {
|
|
|
|
+ if (pte && verbose) {
|
|
|
|
+ fprintf(fp, "\n");
|
|
|
|
+ x86_64_translate_pte(pte, 0, 0);
|
|
|
|
+ }
|
|
|
|
+ goto no_upage;
|
2005-02-10 18:39:42 +00:00
|
|
|
+ }
|
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ *paddr = (PAGEBASE(pte) & PHYSICAL_PAGE_MASK) + PAGEOFFSET(uvaddr);
|
|
|
|
+
|
|
|
|
+ if (verbose) {
|
|
|
|
+ fprintf(fp, " PAGE: %lx\n\n",
|
|
|
|
+ PAGEBASE(*paddr) & PHYSICAL_PAGE_MASK);
|
|
|
|
+ x86_64_translate_pte(pte, 0, 0);
|
2005-02-10 18:39:42 +00:00
|
|
|
+ }
|
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ return TRUE;
|
|
|
|
+
|
|
|
|
+no_upage:
|
|
|
|
+
|
|
|
|
+ return FALSE;
|
2005-02-10 18:39:42 +00:00
|
|
|
+}
|
2006-01-04 19:34:13 +00:00
|
|
|
+
|
|
|
|
+
|
|
|
|
+static int
|
|
|
|
x86_64_uvtop(struct task_context *tc, ulong uvaddr, physaddr_t *paddr, int verbose)
|
|
|
|
{
|
|
|
|
ulong mm;
|
|
|
|
@@ -1091,6 +1270,9 @@
|
|
|
|
ulong *up;
|
|
|
|
ulong words, addr;
|
|
|
|
|
|
|
|
+ if (rsp < bt->frameptr)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
words = (rsp - bt->frameptr) / sizeof(ulong) + 1;
|
|
|
|
|
|
|
|
addr = bt->frameptr;
|
|
|
|
@@ -1308,7 +1490,7 @@
|
|
|
|
(rsp < (ms->stkinfo.ebase[c][i] +
|
|
|
|
ms->stkinfo.esize))) {
|
|
|
|
estack = ms->stkinfo.ebase[c][i];
|
|
|
|
- if (c != bt->tc->processor)
|
|
|
|
+ if (CRASHDEBUG(1) && (c != bt->tc->processor))
|
|
|
|
error(INFO,
|
|
|
|
"task cpu: %d exception stack cpu: %d\n",
|
|
|
|
bt->tc->processor, c);
|
|
|
|
@@ -1341,7 +1523,7 @@
|
|
|
|
if ((rsp >= ms->stkinfo.ibase[c]) &&
|
|
|
|
(rsp < (ms->stkinfo.ibase[c] + ms->stkinfo.isize))) {
|
|
|
|
irqstack = ms->stkinfo.ibase[c];
|
|
|
|
- if (c != bt->tc->processor)
|
|
|
|
+ if (CRASHDEBUG(1) && (c != bt->tc->processor))
|
|
|
|
error(INFO,
|
|
|
|
"task cpu: %d IRQ stack cpu: %d\n",
|
|
|
|
bt->tc->processor, c);
|
|
|
|
@@ -1399,12 +1581,13 @@
|
|
|
|
ofp = fp;
|
|
|
|
|
|
|
|
if (bt->flags & BT_TEXT_SYMBOLS) {
|
|
|
|
- fprintf(ofp, "%sSTART: %s%s at %lx\n",
|
|
|
|
- space(VADDR_PRLEN > 8 ? 14 : 6),
|
|
|
|
- closest_symbol(bt->instptr),
|
|
|
|
- STREQ(closest_symbol(bt->instptr), "thread_return") ?
|
|
|
|
- " (schedule)" : "",
|
|
|
|
- bt->instptr);
|
|
|
|
+ if (!(bt->flags & BT_TEXT_SYMBOLS_ALL))
|
|
|
|
+ fprintf(ofp, "%sSTART: %s%s at %lx\n",
|
|
|
|
+ space(VADDR_PRLEN > 8 ? 14 : 6),
|
|
|
|
+ closest_symbol(bt->instptr),
|
|
|
|
+ STREQ(closest_symbol(bt->instptr), "thread_return") ?
|
|
|
|
+ " (schedule)" : "",
|
|
|
|
+ bt->instptr);
|
|
|
|
} else if (bt->flags & BT_START) {
|
|
|
|
x86_64_print_stack_entry(bt, ofp, level,
|
|
|
|
0, bt->instptr);
|
|
|
|
@@ -1647,8 +1830,12 @@
|
|
|
|
bt->stackbuf + (irq_eframe - bt->stackbase), bt, ofp);
|
|
|
|
if (cs & 3)
|
|
|
|
done = TRUE; /* IRQ from user-mode */
|
|
|
|
- else
|
|
|
|
+ else {
|
|
|
|
+ if (x86_64_print_eframe_location(rsp, level, ofp))
|
|
|
|
+ level++;
|
|
|
|
rsp += SIZE(pt_regs);
|
|
|
|
+ irq_eframe = 0;
|
|
|
|
+ }
|
|
|
|
level++;
|
|
|
|
}
|
|
|
|
|
|
|
|
@@ -1709,6 +1896,8 @@
|
|
|
|
{
|
|
|
|
case BACKTRACE_ENTRY_AND_EFRAME_DISPLAYED:
|
|
|
|
last_process_stack_eframe = rsp + 8;
|
|
|
|
+ if (x86_64_print_eframe_location(last_process_stack_eframe, level, ofp))
|
|
|
|
+ level++;
|
|
|
|
rsp += SIZE(pt_regs);
|
|
|
|
i += SIZE(pt_regs)/sizeof(ulong);
|
|
|
|
case BACKTRACE_ENTRY_DISPLAYED:
|
|
|
|
@@ -1842,6 +2031,8 @@
|
|
|
|
long rax, rbx, rcx, rdx, rsi, rdi;
|
|
|
|
long r8, r9, r10, r11, r12, r13, r14, r15;
|
|
|
|
struct machine_specific *ms;
|
|
|
|
+ struct syment *sp;
|
|
|
|
+ ulong offset;
|
|
|
|
char *pt_regs_buf;
|
|
|
|
long verified;
|
|
|
|
int err;
|
|
|
|
@@ -1950,8 +2141,17 @@
|
|
|
|
cs & 3 ? "USER" : "KERNEL",
|
|
|
|
kvaddr ? kvaddr :
|
|
|
|
(local - bt->stackbuf) + bt->stackbase);
|
|
|
|
+ } else if (!(cs & 3)) {
|
|
|
|
+ fprintf(ofp, " [exception RIP: ");
|
|
|
|
+ if ((sp = value_search(rip, &offset))) {
|
|
|
|
+ fprintf(ofp, "%s", sp->name);
|
|
|
|
+ if (offset)
|
|
|
|
+ fprintf(ofp, (output_radix == 16) ?
|
|
|
|
+ "+0x%lx" : "+%ld", offset);
|
|
|
|
+ } else
|
|
|
|
+ fprintf(ofp, "unknown or invalid address");
|
|
|
|
+ fprintf(ofp, "]\n");
|
|
|
|
}
|
|
|
|
-
|
|
|
|
fprintf(ofp, " RIP: %016lx RSP: %016lx RFLAGS: %08lx\n",
|
|
|
|
rip, rsp, rflags);
|
|
|
|
fprintf(ofp, " RAX: %016lx RBX: %016lx RCX: %016lx\n",
|
|
|
|
@@ -2006,6 +2206,39 @@
|
|
|
|
return 0;
|
|
|
|
}
|
2004-09-09 03:57:09 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
+static int
|
|
|
|
+x86_64_print_eframe_location(ulong eframe, int level, FILE *ofp)
|
|
|
|
+{
|
|
|
|
+ return FALSE;
|
|
|
|
+
|
|
|
|
+#ifdef NOTDEF
|
|
|
|
+ ulong rip;
|
|
|
|
+ char *pt_regs_buf;
|
|
|
|
+ struct machine_specific *ms;
|
|
|
|
+ struct syment *sp;
|
|
|
|
+
|
|
|
|
+ ms = machdep->machspec;
|
|
|
|
+
|
|
|
|
+ pt_regs_buf = GETBUF(SIZE(pt_regs));
|
|
|
|
+ if (!readmem(eframe, KVADDR, pt_regs_buf, SIZE(pt_regs),
|
|
|
|
+ "pt_regs", RETURN_ON_ERROR|QUIET)) {
|
|
|
|
+ FREEBUF(pt_regs_buf);
|
|
|
|
+ return FALSE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ rip = ULONG(pt_regs_buf + ms->pto.rip);
|
|
|
|
+ FREEBUF(pt_regs_buf);
|
|
|
|
+
|
|
|
|
+ if (!(sp = value_search(rip, NULL)))
|
|
|
|
+ return FALSE;
|
|
|
|
+
|
|
|
|
+ fprintf(ofp, "%s#%d [%8lx] %s at %lx\n", level < 10 ? " " : "", level+1,
|
|
|
|
+ eframe, sp->name, rip);
|
|
|
|
+
|
|
|
|
+ return TRUE;
|
|
|
|
+#endif
|
|
|
|
+}
|
|
|
|
+
|
2005-02-10 18:39:42 +00:00
|
|
|
/*
|
2006-01-04 19:34:13 +00:00
|
|
|
* Check that the verifiable registers contain reasonable data.
|
|
|
|
*/
|
|
|
|
@@ -2021,6 +2254,11 @@
|
|
|
|
if ((cs == 0x10) && (ss == 0x18)) {
|
|
|
|
if (is_kernel_text(rip) && IS_KVADDR(rsp))
|
|
|
|
return TRUE;
|
|
|
|
+
|
|
|
|
+ if (x86_64_is_module_addr(rip) &&
|
|
|
|
+ IS_KVADDR(rsp) &&
|
|
|
|
+ (rsp == (kvaddr + SIZE(pt_regs))))
|
|
|
|
+ return TRUE;
|
|
|
|
}
|
2005-02-10 18:39:42 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
if ((cs == 0x10) && kvaddr) {
|
|
|
|
@@ -2040,6 +2278,14 @@
|
|
|
|
return TRUE;
|
|
|
|
}
|
2005-03-03 16:53:39 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
+ /*
|
|
|
|
+ * 32-bit segments
|
|
|
|
+ */
|
|
|
|
+ if ((cs == 0x23) && (ss == 0x2b)) {
|
|
|
|
+ if (IS_UVADDR(rip, bt->tc) && IS_UVADDR(rsp, bt->tc))
|
|
|
|
+ return TRUE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
@@ -2065,7 +2311,7 @@
|
|
|
|
x86_64_get_dumpfile_stack_frame(struct bt_info *bt_in, ulong *rip, ulong *rsp)
|
2005-03-03 16:53:39 +00:00
|
|
|
{
|
2006-01-04 19:34:13 +00:00
|
|
|
int panic_task;
|
|
|
|
- int i, panic, stage;
|
|
|
|
+ int i, estack, panic, stage;
|
|
|
|
char *sym;
|
|
|
|
struct syment *sp;
|
|
|
|
ulong *up;
|
|
|
|
@@ -2080,6 +2326,7 @@
|
|
|
|
ms = machdep->machspec;
|
|
|
|
ur_rip = ur_rsp = 0;
|
|
|
|
stage = 0;
|
|
|
|
+ estack = -1;
|
|
|
|
|
|
|
|
panic_task = tt->panic_task == bt->task ? TRUE : FALSE;
|
|
|
|
|
|
|
|
@@ -2119,13 +2366,14 @@
|
|
|
|
STREQ(sym, "netpoll_start_netdump") ||
|
|
|
|
STREQ(sym, "start_disk_dump") ||
|
|
|
|
STREQ(sym, "disk_dump") ||
|
|
|
|
+ STREQ(sym, "crash_kexec") ||
|
|
|
|
STREQ(sym, "try_crashdump")) {
|
|
|
|
*rip = *up;
|
|
|
|
*rsp = bt->stackbase + ((char *)(up) - bt->stackbuf);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
- if ((stage == 2) &&
|
|
|
|
+ if ((estack >= 0) &&
|
|
|
|
(STREQ(sym, "nmi_watchdog_tick") ||
|
|
|
|
STREQ(sym, "default_do_nmi"))) {
|
|
|
|
sp = x86_64_function_called_by((*up)-5);
|
|
|
|
@@ -2161,7 +2409,7 @@
|
|
|
|
next_sysrq:
|
|
|
|
*rip = *up;
|
|
|
|
*rsp = bt->stackbase + ((char *)(up) - bt->stackbuf);
|
|
|
|
- machdep->flags |= SYSRQ;
|
|
|
|
+ pc->flags |= SYSRQ;
|
|
|
|
for (i++, up++; i < LONGS_PER_STACK; i++, up++) {
|
|
|
|
sym = closest_symbol(*up);
|
|
|
|
if (STREQ(sym, "sysrq_handle_crash"))
|
|
|
|
@@ -2176,6 +2424,12 @@
|
|
|
|
*rsp = bt->stackbase + ((char *)(up) - bt->stackbuf);
|
|
|
|
return;
|
|
|
|
}
|
2005-03-03 16:53:39 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ if (!panic_task && STREQ(sym, "crash_nmi_callback")) {
|
|
|
|
+ *rip = *up;
|
|
|
|
+ *rsp = bt->stackbase + ((char *)(up) - bt->stackbuf);
|
|
|
|
+ return;
|
|
|
|
+ }
|
2005-03-03 16:53:39 +00:00
|
|
|
}
|
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
if (panic)
|
|
|
|
@@ -2191,25 +2445,30 @@
|
|
|
|
bt->stackbase = ms->stkinfo.ibase[bt->tc->processor];
|
|
|
|
bt->stacktop = ms->stkinfo.ibase[bt->tc->processor] +
|
|
|
|
ms->stkinfo.isize;
|
|
|
|
+ console("x86_64_get_dumpfile_stack_frame: searching IRQ stack at %lx\n",
|
|
|
|
+ bt->stackbase);
|
|
|
|
bt->stackbuf = ms->irqstack;
|
|
|
|
alter_stackbuf(bt);
|
|
|
|
stage = 1;
|
|
|
|
goto next_stack;
|
2005-02-10 18:39:42 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
/*
|
|
|
|
- * Check the NMI exception stack.
|
|
|
|
+ * Check the exception stacks.
|
|
|
|
*/
|
|
|
|
case 1:
|
|
|
|
- bt->stackbase = ms->stkinfo.ebase[bt->tc->processor][NMI_STACK];
|
|
|
|
- bt->stacktop = ms->stkinfo.ebase[bt->tc->processor][NMI_STACK] +
|
|
|
|
- ms->stkinfo.esize;
|
|
|
|
+ if (++estack == 7)
|
|
|
|
+ break;
|
|
|
|
+ bt->stackbase = ms->stkinfo.ebase[bt->tc->processor][estack];
|
|
|
|
+ bt->stacktop = ms->stkinfo.ebase[bt->tc->processor][estack] +
|
|
|
|
+ ms->stkinfo.esize;
|
|
|
|
+ console("x86_64_get_dumpfile_stack_frame: searching %s estack at %lx\n",
|
|
|
|
+ x86_64_exception_stacks[estack], bt->stackbase);
|
|
|
|
+ if (!(bt->stackbase))
|
|
|
|
+ goto skip_stage;
|
|
|
|
bt->stackbuf = ms->irqstack;
|
|
|
|
alter_stackbuf(bt);
|
|
|
|
- stage = 2;
|
|
|
|
goto next_stack;
|
|
|
|
|
|
|
|
- case 2:
|
|
|
|
- break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
@@ -2472,10 +2731,15 @@
|
|
|
|
for (i = cpus = 0; i < nr_pda; i++) {
|
|
|
|
if (!CPU_PDA_READ(i, cpu_pda_buf))
|
|
|
|
break;
|
|
|
|
- level4_pgt = ULONG(cpu_pda_buf + OFFSET(x8664_pda_level4_pgt));
|
|
|
|
+
|
|
|
|
+ if (VALID_MEMBER(x8664_pda_level4_pgt)) {
|
|
|
|
+ level4_pgt = ULONG(cpu_pda_buf + OFFSET(x8664_pda_level4_pgt));
|
|
|
|
+ if (!VALID_LEVEL4_PGT_ADDR(level4_pgt))
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
cpunumber = INT(cpu_pda_buf + OFFSET(x8664_pda_cpunumber));
|
|
|
|
- if (!VALID_LEVEL4_PGT_ADDR(level4_pgt) || (cpunumber != cpus))
|
|
|
|
- break;
|
|
|
|
+ if (cpunumber != cpus)
|
|
|
|
+ break;
|
|
|
|
cpus++;
|
2005-02-10 18:39:42 +00:00
|
|
|
}
|
2006-01-04 19:34:13 +00:00
|
|
|
|
|
|
|
@@ -2691,4 +2955,77 @@
|
|
|
|
x86_64_dump_line_number(0);
|
2004-09-09 03:57:09 +00:00
|
|
|
}
|
2005-02-10 18:39:42 +00:00
|
|
|
|
|
|
|
+/*
|
2006-01-04 19:34:13 +00:00
|
|
|
+ * Force the VM address-range selection via:
|
2005-02-10 18:39:42 +00:00
|
|
|
+ *
|
2006-01-04 19:34:13 +00:00
|
|
|
+ * --machdep vm=orig
|
|
|
|
+ * --machdep vm=2.6.11
|
2005-02-10 18:39:42 +00:00
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+void
|
|
|
|
+parse_cmdline_arg(void)
|
|
|
|
+{
|
|
|
|
+ int i, c, errflag;
|
|
|
|
+ char *p;
|
|
|
|
+ char buf[BUFSIZE];
|
|
|
|
+ char *arglist[MAXARGS];
|
2006-01-04 19:34:13 +00:00
|
|
|
+ int lines = 0;
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
|
|
|
+ if (!strstr(machdep->cmdline_arg, "=")) {
|
2006-01-04 19:34:13 +00:00
|
|
|
+ error(WARNING, "ignoring --machdep option: %s\n\n",
|
|
|
|
+ machdep->cmdline_arg);
|
2005-02-10 18:39:42 +00:00
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ strcpy(buf, machdep->cmdline_arg);
|
|
|
|
+
|
|
|
|
+ for (p = buf; *p; p++) {
|
|
|
|
+ if (*p == ',')
|
|
|
|
+ *p = ' ';
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ c = parse_line(buf, arglist);
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < c; i++) {
|
|
|
|
+ errflag = 0;
|
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ if (STRNEQ(arglist[i], "vm=")) {
|
|
|
|
+ p = arglist[i] + strlen("vm=");
|
2005-02-10 18:39:42 +00:00
|
|
|
+ if (strlen(p)) {
|
2006-01-04 19:34:13 +00:00
|
|
|
+ if (STREQ(p, "orig")) {
|
|
|
|
+ machdep->flags |= VM_ORIG;
|
2005-02-10 18:39:42 +00:00
|
|
|
+ continue;
|
2006-01-04 19:34:13 +00:00
|
|
|
+ } else if (STREQ(p, "2.6.11")) {
|
|
|
|
+ machdep->flags |= VM_2_6_11;
|
2005-02-10 18:39:42 +00:00
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ error(WARNING, "ignoring --machdep option: %s\n", arglist[i]);
|
2006-01-04 19:34:13 +00:00
|
|
|
+ lines++;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ switch (machdep->flags & (VM_ORIG|VM_2_6_11))
|
|
|
|
+ {
|
|
|
|
+ case VM_ORIG:
|
|
|
|
+ error(NOTE, "using original x86_64 VM address ranges\n");
|
|
|
|
+ lines++;
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case VM_2_6_11:
|
|
|
|
+ error(NOTE, "using 2.6.11 x86_64 VM address ranges\n");
|
|
|
|
+ lines++;
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case (VM_ORIG|VM_2_6_11):
|
|
|
|
+ error(WARNING, "cannot set both vm=orig and vm=2.6.11\n");
|
|
|
|
+ lines++;
|
|
|
|
+ machdep->flags &= ~(VM_ORIG|VM_2_6_11);
|
|
|
|
+ break;
|
2005-02-10 18:39:42 +00:00
|
|
|
+ }
|
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ if (lines)
|
2005-02-10 18:39:42 +00:00
|
|
|
+ fprintf(fp, "\n");
|
2006-01-04 19:34:13 +00:00
|
|
|
+}
|
|
|
|
#endif /* X86_64 */
|
|
|
|
--- crash/extensions.c.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/extensions.c 2005-11-10 09:47:46.000000000 -0500
|
|
|
|
@@ -18,9 +18,6 @@
|
|
|
|
#include "defs.h"
|
|
|
|
#include <dlfcn.h>
|
|
|
|
|
|
|
|
-static void load_extension(char *);
|
|
|
|
-static void unload_extension(char *);
|
|
|
|
-
|
|
|
|
#define DUMP_EXTENSIONS (0)
|
|
|
|
#define LOAD_EXTENSION (1)
|
|
|
|
#define UNLOAD_EXTENSION (2)
|
|
|
|
@@ -171,7 +168,7 @@
|
|
|
|
/*
|
|
|
|
* Load an extension library.
|
|
|
|
*/
|
|
|
|
-static void
|
|
|
|
+void
|
|
|
|
load_extension(char *lib)
|
|
|
|
{
|
|
|
|
struct extension_table *ext;
|
|
|
|
@@ -252,7 +249,7 @@
|
|
|
|
/*
|
|
|
|
* Unload all, or as specified, extension libraries.
|
|
|
|
*/
|
|
|
|
-static void
|
|
|
|
+void
|
|
|
|
unload_extension(char *lib)
|
|
|
|
{
|
|
|
|
struct extension_table *ext;
|
|
|
|
--- crash/symbols.c.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/symbols.c 2006-01-03 13:11:16.000000000 -0500
|
|
|
|
@@ -5433,6 +5433,8 @@
|
|
|
|
lenptr = &array_table.kmem_cache_s_c_name;
|
|
|
|
else if (STREQ(s, "kmem_cache_s.array"))
|
|
|
|
lenptr = &array_table.kmem_cache_s_array;
|
|
|
|
+ else if (STREQ(s, "kmem_cache.array"))
|
|
|
|
+ lenptr = &array_table.kmem_cache_s_array;
|
|
|
|
else if (STREQ(s, "kmem_cache_s.cpudata"))
|
|
|
|
lenptr = &array_table.kmem_cache_s_cpudata;
|
|
|
|
else if (STREQ(s, "log_buf"))
|
|
|
|
@@ -5766,6 +5768,8 @@
|
|
|
|
OFFSET(mm_struct_pgd));
|
|
|
|
fprintf(fp, " mm_struct_rss: %ld\n",
|
|
|
|
OFFSET(mm_struct_rss));
|
|
|
|
+ fprintf(fp, " mm_struct_anon_rss: %ld\n",
|
|
|
|
+ OFFSET(mm_struct_anon_rss));
|
|
|
|
fprintf(fp, " mm_struct_total_vm: %ld\n",
|
|
|
|
OFFSET(mm_struct_total_vm));
|
|
|
|
fprintf(fp, " mm_struct_start_code: %ld\n",
|
|
|
|
@@ -5972,6 +5976,16 @@
|
|
|
|
fprintf(fp, " irq_cpustat_t___softirq_mask: %ld\n",
|
|
|
|
OFFSET(irq_cpustat_t___softirq_mask));
|
|
|
|
|
|
|
|
+ fprintf(fp, " files_struct_fdt: %ld\n",
|
|
|
|
+ OFFSET(files_struct_fdt));
|
|
|
|
+ fprintf(fp, " fdtable_max_fds: %ld\n",
|
|
|
|
+ OFFSET(fdtable_max_fds));
|
|
|
|
+ fprintf(fp, " fdtable_max_fdset: %ld\n",
|
|
|
|
+ OFFSET(fdtable_max_fdset));
|
|
|
|
+ fprintf(fp, " fdtable_open_fds: %ld\n",
|
|
|
|
+ OFFSET(fdtable_open_fds));
|
|
|
|
+ fprintf(fp, " fdtable_fd: %ld\n",
|
|
|
|
+ OFFSET(fdtable_fd));
|
|
|
|
fprintf(fp, " files_struct_max_fds: %ld\n",
|
|
|
|
OFFSET(files_struct_max_fds));
|
|
|
|
fprintf(fp, " files_struct_max_fdset: %ld\n",
|
|
|
|
@@ -6217,6 +6231,11 @@
|
|
|
|
fprintf(fp, " inet_opt_num: %ld\n",
|
|
|
|
OFFSET(inet_opt_num));
|
|
|
|
|
|
|
|
+ fprintf(fp, " ipv6_pinfo_rcv_saddr: %ld\n",
|
|
|
|
+ OFFSET(ipv6_pinfo_rcv_saddr));
|
|
|
|
+ fprintf(fp, " ipv6_pinfo_daddr: %ld\n",
|
|
|
|
+ OFFSET(ipv6_pinfo_daddr));
|
|
|
|
+
|
|
|
|
fprintf(fp, " timer_list_list: %ld\n",
|
|
|
|
OFFSET(timer_list_list));
|
|
|
|
fprintf(fp, " timer_list_next: %ld\n",
|
|
|
|
@@ -6291,6 +6310,8 @@
|
|
|
|
OFFSET(zone_struct_size));
|
|
|
|
fprintf(fp, " zone_struct_memsize: %ld\n",
|
|
|
|
OFFSET(zone_struct_memsize));
|
|
|
|
+ fprintf(fp, " zone_struct_zone_start_pfn: %ld\n",
|
|
|
|
+ OFFSET(zone_struct_zone_start_pfn));
|
|
|
|
fprintf(fp, " zone_struct_zone_start_paddr: %ld\n",
|
|
|
|
OFFSET(zone_struct_zone_start_paddr));
|
|
|
|
fprintf(fp, " zone_struct_zone_start_mapnr: %ld\n",
|
|
|
|
@@ -6471,6 +6492,8 @@
|
|
|
|
OFFSET(x8664_pda_irqstackptr));
|
|
|
|
fprintf(fp, " x8664_pda_level4_pgt: %ld\n",
|
|
|
|
OFFSET(x8664_pda_level4_pgt));
|
|
|
|
+ fprintf(fp, " x8664_pda_me: %ld\n",
|
|
|
|
+ OFFSET(x8664_pda_me));
|
|
|
|
|
|
|
|
fprintf(fp, " tss_struct_ist: %ld\n",
|
|
|
|
OFFSET(tss_struct_ist));
|
|
|
|
@@ -6512,6 +6535,7 @@
|
|
|
|
fprintf(fp, " fs_struct: %ld\n", SIZE(fs_struct));
|
|
|
|
fprintf(fp, " files_struct: %ld\n",
|
|
|
|
SIZE(files_struct));
|
|
|
|
+ fprintf(fp, " fdtable: %ld\n", SIZE(fdtable));
|
|
|
|
fprintf(fp, " file: %ld\n", SIZE(file));
|
|
|
|
fprintf(fp, " inode: %ld\n", SIZE(inode));
|
|
|
|
fprintf(fp, " vfsmount: %ld\n", SIZE(vfsmount));
|
|
|
|
@@ -6546,6 +6570,7 @@
|
|
|
|
fprintf(fp, " sock: %ld\n", SIZE(sock));
|
|
|
|
fprintf(fp, " inet_sock: %ld\n", SIZE(inet_sock));
|
|
|
|
fprintf(fp, " socket: %ld\n", SIZE(socket));
|
|
|
|
+ fprintf(fp, " in6_addr: %ld\n", SIZE(in6_addr));
|
|
|
|
fprintf(fp, " signal_struct: %ld\n",
|
|
|
|
SIZE(signal_struct));
|
|
|
|
fprintf(fp, " signal_queue: %ld\n",
|
|
|
|
--- crash/cmdline.c.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/cmdline.c 2005-11-18 10:49:59.000000000 -0500
|
|
|
|
@@ -71,14 +71,17 @@
|
|
|
|
* 4. from a terminal.
|
|
|
|
* 5. from a pipe, if stdin is a pipe rather than a terminal.
|
|
|
|
*/
|
|
|
|
- if (pc->flags & RCHOME_IFILE)
|
|
|
|
+ if (pc->flags & RCHOME_IFILE) {
|
|
|
|
sprintf(pc->command_line, "< %s/.%src",
|
|
|
|
pc->home, pc->program_name);
|
|
|
|
- else if (pc->flags & RCLOCAL_IFILE)
|
|
|
|
+ pc->flags |= INIT_IFILE;
|
|
|
|
+ } else if (pc->flags & RCLOCAL_IFILE) {
|
|
|
|
sprintf(pc->command_line, "< .%src", pc->program_name);
|
|
|
|
- else if (pc->flags & CMDLINE_IFILE)
|
|
|
|
+ pc->flags |= INIT_IFILE;
|
|
|
|
+ } else if (pc->flags & CMDLINE_IFILE) {
|
|
|
|
sprintf(pc->command_line, "< %s", pc->input_file);
|
|
|
|
- else if (pc->flags & TTY) {
|
|
|
|
+ pc->flags |= INIT_IFILE;
|
|
|
|
+ } else if (pc->flags & TTY) {
|
|
|
|
if (!(pc->readline = readline(pc->prompt))) {
|
|
|
|
args[0] = NULL;
|
|
|
|
fprintf(fp, "\n");
|
|
|
|
@@ -918,7 +921,7 @@
|
|
|
|
|
|
|
|
wait_for_children(ZOMBIES_ONLY);
|
|
|
|
|
|
|
|
- pc->flags &= ~(RUNTIME_IFILE|_SIGINT_);
|
|
|
|
+ pc->flags &= ~(INIT_IFILE|RUNTIME_IFILE|_SIGINT_);
|
|
|
|
pc->sigint_cnt = 0;
|
|
|
|
pc->redirect = 0;
|
|
|
|
pc->pipe_command[0] = NULLCHAR;
|
|
|
|
--- crash/lkcd_common.c.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/lkcd_common.c 2005-11-10 15:24:16.000000000 -0500
|
|
|
|
@@ -53,6 +53,8 @@
|
|
|
|
|
|
|
|
struct lkcd_environment lkcd_environment = { 0 };
|
|
|
|
struct lkcd_environment *lkcd = &lkcd_environment;
|
|
|
|
+static int uncompress_errloc;
|
|
|
|
+static int uncompress_recover(unsigned char *, ulong, unsigned char *, ulong);
|
|
|
|
|
|
|
|
ulonglong
|
|
|
|
fix_lkcd_address(ulonglong addr)
|
|
|
|
@@ -208,6 +210,7 @@
|
|
|
|
|
|
|
|
case LKCD_DUMP_V8:
|
|
|
|
case LKCD_DUMP_V9:
|
|
|
|
+ case LKCD_DUMP_V10:
|
|
|
|
lkcd->version = LKCD_DUMP_V8;
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
@@ -1164,40 +1167,103 @@
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
+/* Returns the bit offset if it's able to correct, or negative if not */
|
|
|
|
+static int
|
|
|
|
+uncompress_recover(unsigned char *dest, ulong destlen,
|
|
|
|
+ unsigned char *source, ulong sourcelen)
|
|
|
|
+{
|
|
|
|
+ int byte, bit;
|
|
|
|
+ ulong retlen = destlen;
|
|
|
|
+ int good_decomp = 0, good_rv = -1;
|
|
|
|
+
|
|
|
|
+ /* Generate all single bit errors */
|
|
|
|
+ if (sourcelen > 16384) {
|
|
|
|
+ lkcd_print("uncompress_recover: sourcelen %ld too long\n",
|
|
|
|
+ sourcelen);
|
|
|
|
+ return(-1);
|
|
|
|
+ }
|
|
|
|
+ for (byte = 0; byte < sourcelen; byte++) {
|
|
|
|
+ for (bit = 0; bit < 8; bit++) {
|
|
|
|
+ source[byte] ^= (1 << bit);
|
|
|
|
+
|
|
|
|
+ if (uncompress(dest, &retlen, source, sourcelen) == Z_OK &&
|
|
|
|
+ retlen == destlen) {
|
|
|
|
+ good_decomp++;
|
|
|
|
+ lkcd_print("good for flipping byte %d bit %d\n",
|
|
|
|
+ byte, bit);
|
|
|
|
+ good_rv = bit + byte * 8;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Put it back */
|
|
|
|
+ source[byte] ^= (1 << bit);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (good_decomp == 0) {
|
|
|
|
+ lkcd_print("Could not correct gzip errors.\n");
|
|
|
|
+ return -2;
|
|
|
|
+ } else if (good_decomp > 1) {
|
|
|
|
+ lkcd_print("Too many valid gzip decompressions: %d.\n", good_decomp);
|
|
|
|
+ return -3;
|
|
|
|
+ } else {
|
|
|
|
+ source[good_rv >> 8] ^= 1 << (good_rv % 8);
|
|
|
|
+ uncompress(dest, &retlen, source, sourcelen);
|
|
|
|
+ source[good_rv >> 8] ^= 1 << (good_rv % 8);
|
|
|
|
+ return good_rv;
|
|
|
|
+ }
|
2005-02-10 18:39:42 +00:00
|
|
|
+}
|
|
|
|
+
|
2004-09-09 03:57:09 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
/*
|
|
|
|
* Uncompress a gzip'd buffer.
|
|
|
|
+ *
|
|
|
|
+ * Returns FALSE on error. If set, then
|
|
|
|
+ * a non-negative value of uncompress_errloc indicates the location of
|
|
|
|
+ * a single-bit error, and the data may be used.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
lkcd_uncompress_gzip(unsigned char *dest, ulong destlen,
|
|
|
|
unsigned char *source, ulong sourcelen)
|
|
|
|
{
|
|
|
|
ulong retlen = destlen;
|
|
|
|
+ int rc;
|
|
|
|
|
|
|
|
switch (uncompress(dest, &retlen, source, sourcelen))
|
|
|
|
{
|
|
|
|
case Z_OK:
|
|
|
|
if (retlen == destlen)
|
|
|
|
- return TRUE;
|
|
|
|
+ rc = TRUE;
|
|
|
|
+ break;
|
|
|
|
|
|
|
|
lkcd_print("uncompress: returned length not page size: %ld\n",
|
|
|
|
retlen);
|
|
|
|
- return FALSE;
|
|
|
|
+ rc = FALSE;
|
|
|
|
+ break;
|
|
|
|
|
|
|
|
case Z_MEM_ERROR:
|
|
|
|
lkcd_print("uncompress: Z_MEM_ERROR (not enough memory)\n");
|
|
|
|
- return FALSE;
|
|
|
|
+ rc = FALSE;
|
|
|
|
+ break;
|
|
|
|
|
|
|
|
case Z_BUF_ERROR:
|
|
|
|
lkcd_print("uncompress: "
|
|
|
|
"Z_BUF_ERROR (not enough room in output buffer)\n");
|
|
|
|
- return FALSE;
|
|
|
|
+ rc = FALSE;
|
|
|
|
+ break;
|
|
|
|
|
|
|
|
case Z_DATA_ERROR:
|
|
|
|
lkcd_print("uncompress: Z_DATA_ERROR (input data corrupted)\n");
|
|
|
|
- return FALSE;
|
|
|
|
+ rc = FALSE;
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ rc = FALSE;
|
|
|
|
+ break;
|
|
|
|
}
|
|
|
|
|
|
|
|
- return FALSE;
|
|
|
|
+ if (rc == FALSE) {
|
|
|
|
+ uncompress_errloc =
|
|
|
|
+ uncompress_recover(dest, destlen, source, sourcelen);
|
|
|
|
+ }
|
|
|
|
+ return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1252,8 +1318,9 @@
|
|
|
|
dp_flags = lkcd->get_dp_flags();
|
|
|
|
dp_address = lkcd->get_dp_address();
|
|
|
|
|
|
|
|
- if (dp_flags & LKCD_DUMP_END)
|
|
|
|
+ if (dp_flags & LKCD_DUMP_END) {
|
|
|
|
return LKCD_DUMPFILE_END;
|
|
|
|
+ }
|
|
|
|
|
|
|
|
if ((lkcd->flags & LKCD_VALID) && (page > lkcd->total_pages))
|
|
|
|
lkcd->total_pages = page;
|
|
|
|
--- crash/lkcd_v7.c.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/lkcd_v7.c 2005-11-10 15:21:50.000000000 -0500
|
|
|
|
@@ -89,7 +89,11 @@
|
|
|
|
ifd = 0;
|
|
|
|
|
|
|
|
#ifdef LKCD_INDEX_FILE
|
|
|
|
- lkcd->memory_pages = (dh->dh_memory_size * (getpagesize()/lkcd->page_size)) * 2;
|
|
|
|
+ if (dh->dh_memory_end < 0x1000000000LL) {
|
|
|
|
+ lkcd->memory_pages = dh->dh_memory_end / lkcd->page_size + 1;
|
|
|
|
+ } else {
|
|
|
|
+ lkcd->memory_pages = (dh->dh_memory_size * (getpagesize()/lkcd->page_size)) * 2;
|
|
|
|
+ }
|
|
|
|
dump_index_size = (lkcd->memory_pages * sizeof(off_t));
|
|
|
|
lkcd->page_offsets = 0;
|
|
|
|
strcpy(dumpfile_index_name, dumpfile);
|
|
|
|
--- crash/lkcd_v8.c.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/lkcd_v8.c 2005-12-15 08:59:52.000000000 -0500
|
|
|
|
@@ -26,6 +26,7 @@
|
|
|
|
// static dump_header_asm_t dump_header_asm_v8 = { 0 };
|
|
|
|
static dump_page_t dump_page = { 0 };
|
|
|
|
static void mclx_cache_page_headers_v8(void);
|
|
|
|
+static off_t lkcd_offset_to_first_page = LKCD_OFFSET_TO_FIRST_PAGE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Verify and initialize the LKCD environment, storing the common data
|
|
|
|
@@ -56,10 +57,13 @@
|
|
|
|
if (read(lkcd->fd, dh, sizeof(dump_header_t)) !=
|
|
|
|
sizeof(dump_header_t))
|
|
|
|
return FALSE;
|
|
|
|
- if ((dh->dh_version & LKCD_DUMP_VERSION_NUMBER_MASK) == LKCD_DUMP_V9)
|
|
|
|
+ if ((dh->dh_version & LKCD_DUMP_VERSION_NUMBER_MASK) == LKCD_DUMP_V9){
|
|
|
|
if (read(lkcd->fd, &dh_dump_buffer_size, sizeof(dh_dump_buffer_size)) !=
|
|
|
|
sizeof(dh_dump_buffer_size))
|
|
|
|
return FALSE;
|
|
|
|
+ lkcd_offset_to_first_page = dh_dump_buffer_size;
|
|
|
|
+ } else
|
|
|
|
+ lkcd_offset_to_first_page = LKCD_OFFSET_TO_FIRST_PAGE;
|
|
|
|
|
|
|
|
lkcd->dump_page = dp;
|
|
|
|
lkcd->dump_header = dh;
|
|
|
|
@@ -146,7 +150,7 @@
|
|
|
|
lkcd->compression = dh->dh_dump_compress;
|
|
|
|
lkcd->page_header_size = sizeof(dump_page_t);
|
|
|
|
|
|
|
|
- lseek(lkcd->fd, LKCD_OFFSET_TO_FIRST_PAGE, SEEK_SET);
|
|
|
|
+ lseek(lkcd->fd, lkcd_offset_to_first_page, SEEK_SET);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read all of the pages and save the page offsets for lkcd_lseek().
|
|
|
|
@@ -483,7 +487,7 @@
|
|
|
|
/*
|
|
|
|
* Determine the granularity between offsets.
|
|
|
|
*/
|
|
|
|
- if (lseek(lkcd->fd, page_headers[0] + LKCD_OFFSET_TO_FIRST_PAGE,
|
|
|
|
+ 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) !=
|
|
|
|
@@ -491,7 +495,7 @@
|
|
|
|
return;
|
|
|
|
physaddr1 = (dp->dp_address - lkcd->kvbase) << lkcd->page_shift;
|
|
|
|
|
|
|
|
- if (lseek(lkcd->fd, page_headers[1] + LKCD_OFFSET_TO_FIRST_PAGE,
|
|
|
|
+ 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)
|
|
|
|
@@ -508,7 +512,7 @@
|
|
|
|
for (i = 0; i < (MCLX_PAGE_HEADERS-1); i++) {
|
|
|
|
if (!page_headers[i])
|
|
|
|
break;
|
|
|
|
- lkcd->curhdroffs = page_headers[i] + LKCD_OFFSET_TO_FIRST_PAGE;
|
|
|
|
+ lkcd->curhdroffs = page_headers[i] + lkcd_offset_to_first_page;
|
|
|
|
set_mb_benchmark((granularity * (i+1))/lkcd->page_size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
--- crash/lkcd_x86_trace.c.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/lkcd_x86_trace.c 2005-11-15 14:39:22.000000000 -0500
|
|
|
|
@@ -47,11 +47,13 @@
|
|
|
|
static int setup_trace_rec(kaddr_t, kaddr_t, int, trace_t *);
|
|
|
|
static int valid_ra(kaddr_t);
|
|
|
|
static int valid_ra_function(kaddr_t, char *);
|
|
|
|
+static int eframe_incr(kaddr_t, char *);
|
|
|
|
static int find_trace(kaddr_t, kaddr_t, kaddr_t, kaddr_t, trace_t *, int);
|
|
|
|
static void dump_stack_frame(trace_t *, sframe_t *, FILE *);
|
|
|
|
static void print_trace(trace_t *, int, FILE *);
|
|
|
|
struct pt_regs;
|
|
|
|
static int eframe_type(struct pt_regs *);
|
|
|
|
+char *funcname_display(char *);
|
|
|
|
static void print_eframe(FILE *, struct pt_regs *);
|
|
|
|
static void trace_banner(FILE *);
|
|
|
|
static void print_kaddr(kaddr_t, FILE *, int);
|
|
|
|
@@ -505,7 +507,7 @@
|
|
|
|
{ "receive_chars", NULL,
|
|
|
|
COMPILER_VERSION_EQUAL, GCC(2,96,0), 0, 0, 48 },
|
|
|
|
{ "default_idle", NULL,
|
|
|
|
- COMPILER_VERSION_START, GCC(3,3,2), 0, -4, 0 },
|
|
|
|
+ COMPILER_VERSION_START, GCC(2,96,0), 0, -4, 0 },
|
|
|
|
{ NULL, NULL, 0, 0, 0, 0, 0 },
|
|
|
|
};
|
|
|
|
|
|
|
|
@@ -1206,6 +1208,93 @@
|
|
|
|
} \
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Determine how much to increment the stack pointer to find the
|
|
|
|
+ * exception frame associated with a generic "error_code" or "nmi"
|
|
|
|
+ * exception.
|
|
|
|
+ *
|
|
|
|
+ * The incoming addr is that of the call to the generic error_code
|
|
|
|
+ * or nmi exception handler function. Until later 2.6 kernels, the next
|
|
|
|
+ * instruction had always been an "addl $8,%esp". However, with later
|
|
|
|
+ * 2.6 kernels, that esp adjustment is no long valid, and there will be
|
|
|
|
+ * an immediate "jmp" instruction. Returns 4 or 12, whichever is appropriate.
|
|
|
|
+ * Cache the value the first time, and allow for future changes or additions.
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#define NMI_ADJ (0)
|
|
|
|
+#define ERROR_CODE_ADJ (1)
|
|
|
|
+#define EFRAME_ADJUSTS (ERROR_CODE_ADJ+1)
|
|
|
|
+
|
|
|
|
+static int eframe_adjust[EFRAME_ADJUSTS] = { 0 };
|
|
|
|
+
|
|
|
|
+static int
|
|
|
|
+eframe_incr(kaddr_t addr, char *funcname)
|
2004-09-09 03:57:09 +00:00
|
|
|
+{
|
2006-01-04 19:34:13 +00:00
|
|
|
+ instr_rec_t irp;
|
|
|
|
+ kaddr_t next;
|
|
|
|
+ int size, adj, val;
|
|
|
|
+
|
|
|
|
+ if (STRNEQ(funcname, "nmi")) {
|
|
|
|
+ adj = NMI_ADJ;
|
|
|
|
+ val = eframe_adjust[NMI_ADJ];
|
|
|
|
+ } else if (strstr(funcname, "error_code")) {
|
|
|
|
+ adj = ERROR_CODE_ADJ;
|
|
|
|
+ val = eframe_adjust[ERROR_CODE_ADJ];
|
|
|
|
+ } else {
|
|
|
|
+ adj = -1;
|
|
|
|
+ val = 0;
|
|
|
|
+ error(INFO,
|
|
|
|
+ "unexpected exception frame marker: %lx (%s)\n",
|
|
|
|
+ addr, funcname);
|
|
|
|
+ }
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ if (val) {
|
|
|
|
+ console("eframe_incr(%lx, %s): eframe_adjust[%d]: %d\n",
|
|
|
|
+ addr, funcname, adj, val);
|
|
|
|
+ return val;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ console("eframe_incr(%lx, %s): TBD:\n", addr, funcname);
|
|
|
|
+
|
|
|
|
+ bzero(&irp, sizeof(irp));
|
|
|
|
+ irp.aflag = 1;
|
|
|
|
+ irp.dflag = 1;
|
|
|
|
+ if (!(size = get_instr_info(addr, &irp))) {
|
|
|
|
+ if (CRASHDEBUG(1))
|
|
|
|
+ error(INFO,
|
|
|
|
+ "eframe_incr(%lx, %s): get_instr_info(%lx) failed\n",
|
|
|
|
+ addr, funcname, addr);
|
|
|
|
+ return((THIS_KERNEL_VERSION > LINUX(2,6,9)) ? 4 : 12);
|
|
|
|
+ }
|
|
|
|
+ console(" addr: %lx size: %d opcode: 0x%x insn: \"%s\"\n",
|
|
|
|
+ addr, size, irp.opcode, irp.opcodep->name);
|
|
|
|
+
|
|
|
|
+ next = addr + size;
|
|
|
|
+ bzero(&irp, sizeof(irp));
|
|
|
|
+ irp.aflag = 1;
|
|
|
|
+ irp.dflag = 1;
|
|
|
|
+ if (!(size = get_instr_info(next, &irp))) {
|
|
|
|
+ if (CRASHDEBUG(1))
|
|
|
|
+ error(INFO,
|
|
|
|
+ "eframe_incr(%lx, %s): get_instr_info(%lx) failed\n",
|
|
|
|
+ addr, funcname, next);
|
|
|
|
+ return((THIS_KERNEL_VERSION > LINUX(2,6,9)) ? 4 : 12);
|
|
|
|
+ }
|
|
|
|
+ console(" next: %lx size: %d opcode: 0x%x insn: \"%s\"\n",
|
|
|
|
+ next, size, irp.opcode, irp.opcodep->name);
|
2004-09-09 03:57:09 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ if (STREQ(irp.opcodep->name, "jmp"))
|
|
|
|
+ val = 4;
|
|
|
|
+ else
|
|
|
|
+ val = 12;
|
2004-09-09 03:57:09 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ if (adj >= 0)
|
|
|
|
+ eframe_adjust[adj] = val;
|
|
|
|
+
|
|
|
|
+ return val;
|
2004-09-09 03:57:09 +00:00
|
|
|
+}
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
/*
|
|
|
|
* find_trace()
|
|
|
|
*
|
|
|
|
@@ -1503,12 +1592,13 @@
|
|
|
|
return(trace->nframes);
|
|
|
|
#ifdef REDHAT
|
|
|
|
} else if (strstr(func_name, "error_code")
|
|
|
|
+ || STREQ(func_name, "nmi_stack_correct")
|
|
|
|
|| STREQ(func_name, "nmi")) {
|
|
|
|
#else
|
|
|
|
} else if (strstr(func_name, "error_code")) {
|
|
|
|
#endif
|
|
|
|
/* an exception frame */
|
|
|
|
- sp = curframe->fp+12;
|
|
|
|
+ sp = curframe->fp + eframe_incr(pc, func_name);
|
|
|
|
|
|
|
|
bp = sp + (KERNEL_EFRAME_SZ-1)*4;
|
|
|
|
asp = (uaddr_t*)((uaddr_t)sbp + (STACK_SIZE -
|
|
|
|
@@ -1708,6 +1798,10 @@
|
|
|
|
#endif
|
|
|
|
if (frmp->flag & EX_FRAME) {
|
|
|
|
pt = (struct pt_regs *)frmp->asp;
|
|
|
|
+ if (CRASHDEBUG(1))
|
|
|
|
+ fprintf(ofp,
|
|
|
|
+ " EXCEPTION FRAME: %lx\n",
|
|
|
|
+ (unsigned long)frmp->sp);
|
|
|
|
print_eframe(ofp, pt);
|
|
|
|
}
|
|
|
|
#ifdef REDHAT
|
|
|
|
@@ -2192,11 +2286,12 @@
|
|
|
|
else
|
|
|
|
buf[0] = NULLCHAR;
|
|
|
|
|
|
|
|
- if ((sp = eframe_label(funcname, eip)))
|
|
|
|
+ if ((sp = eframe_label(funcname, eip)))
|
|
|
|
funcname = sp->name;
|
|
|
|
|
|
|
|
fprintf(ofp, "%s#%d [%8lx] %s%s at %lx\n",
|
|
|
|
- level < 10 ? " " : "", level, esp, funcname,
|
|
|
|
+ level < 10 ? " " : "", level, esp,
|
|
|
|
+ funcname_display(funcname),
|
|
|
|
strlen(buf) ? buf : "", eip);
|
|
|
|
|
|
|
|
if (bt->flags & BT_LINE_NUMBERS) {
|
|
|
|
@@ -2325,6 +2420,25 @@
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
+ * If it makes sense to display a different function/label name
|
|
|
|
+ * in a stack entry, it can be done here. Unlike eframe_label(),
|
|
|
|
+ * this routine won't cause the passed-in function name pointer
|
|
|
|
+ * to be changed -- this is strictly for display purposes only.
|
|
|
|
+ */
|
|
|
|
+char *
|
|
|
|
+funcname_display(char *funcname)
|
|
|
|
+{
|
|
|
|
+ struct syment *sp;
|
|
|
|
+
|
|
|
|
+ if (STREQ(funcname, "nmi_stack_correct") &&
|
|
|
|
+ (sp = symbol_search("nmi")))
|
|
|
|
+ return sp->name;
|
|
|
|
+
|
|
|
|
+ return funcname;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
* Cache 2k starting from the passed-in text address. This sits on top
|
|
|
|
* of the instrbuf 256-byte cache, but we don't want to extend its size
|
|
|
|
* because we can run off the end of a module segment -- if this routine
|
|
|
|
--- crash/netdump.c.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/netdump.c 2005-11-30 15:35:07.000000000 -0500
|
|
|
|
@@ -22,12 +22,12 @@
|
|
|
|
physaddr_t phys_end;
|
|
|
|
};
|
|
|
|
|
|
|
|
-struct netdump_data {
|
|
|
|
+struct vmcore_data {
|
|
|
|
ulong flags;
|
|
|
|
int ndfd;
|
|
|
|
FILE *ofp;
|
|
|
|
uint header_size;
|
|
|
|
- char *netdump_header;
|
|
|
|
+ char *elf_header;
|
|
|
|
uint num_pt_load_segments;
|
|
|
|
struct pt_load_segment *pt_load_segments;
|
|
|
|
Elf32_Ehdr *elf32;
|
|
|
|
@@ -40,11 +40,14 @@
|
|
|
|
void *nt_prpsinfo;
|
|
|
|
void *nt_taskstruct;
|
|
|
|
ulong task_struct;
|
|
|
|
+ uint page_size;
|
|
|
|
ulong switch_stack;
|
|
|
|
+ uint num_prstatus_notes;
|
|
|
|
+ void *nt_prstatus_percpu[NR_CPUS];
|
|
|
|
};
|
|
|
|
|
|
|
|
-static struct netdump_data netdump_data = { 0 };
|
|
|
|
-static struct netdump_data *nd = &netdump_data;
|
|
|
|
+static struct vmcore_data vmcore_data = { 0 };
|
|
|
|
+static struct vmcore_data *nd = &vmcore_data;
|
|
|
|
static void netdump_print(char *, ...);
|
|
|
|
static void dump_Elf32_Ehdr(Elf32_Ehdr *);
|
|
|
|
static void dump_Elf32_Phdr(Elf32_Phdr *, int);
|
|
|
|
@@ -52,19 +55,19 @@
|
|
|
|
static void dump_Elf64_Ehdr(Elf64_Ehdr *);
|
|
|
|
static void dump_Elf64_Phdr(Elf64_Phdr *, int);
|
|
|
|
static size_t dump_Elf64_Nhdr(Elf64_Off offset, int);
|
|
|
|
-static void get_netdump_regs_x86(struct bt_info *, ulong *, ulong *);
|
|
|
|
-static void get_netdump_regs_x86_64(struct bt_info *, ulong *, ulong *);
|
|
|
|
static void get_netdump_regs_ppc64(struct bt_info *, ulong *, ulong *);
|
|
|
|
|
|
|
|
#define ELFSTORE 1
|
|
|
|
#define ELFREAD 0
|
|
|
|
+
|
|
|
|
+#define MIN_PAGE_SIZE (4096)
|
|
|
|
|
|
|
|
/*
|
|
|
|
- * Determine whether a file is a netdump creation, and if TRUE,
|
|
|
|
- * initialize the netdump_data structure.
|
|
|
|
+ * Determine whether a file is a netdump/diskdump/kdump creation,
|
|
|
|
+ * and if TRUE, initialize the vmcore_data structure.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
-is_netdump(char *file, ulong source)
|
|
|
|
+is_netdump(char *file, ulong source_query)
|
2004-09-09 03:57:09 +00:00
|
|
|
{
|
2006-01-04 19:34:13 +00:00
|
|
|
int i;
|
|
|
|
int fd;
|
|
|
|
@@ -77,6 +80,8 @@
|
|
|
|
size_t size, len, tot;
|
|
|
|
Elf32_Off offset32;
|
|
|
|
Elf64_Off offset64;
|
|
|
|
+ ulong tmp_flags;
|
|
|
|
+ char *tmp_elf_header;
|
|
|
|
|
|
|
|
if ((fd = open(file, O_RDWR)) < 0) {
|
|
|
|
if ((fd = open(file, O_RDONLY)) < 0) {
|
|
|
|
@@ -99,11 +104,24 @@
|
|
|
|
goto bailout;
|
|
|
|
}
|
|
|
|
|
|
|
|
+ tmp_flags = 0;
|
|
|
|
elf32 = (Elf32_Ehdr *)&header[0];
|
|
|
|
elf64 = (Elf64_Ehdr *)&header[0];
|
|
|
|
|
|
|
|
/*
|
|
|
|
- * Verify the ELF header
|
|
|
|
+ * Verify the ELF header, and determine the dumpfile format.
|
|
|
|
+ *
|
|
|
|
+ * For now, kdump vmcores differ from netdump/diskdump like so:
|
|
|
|
+ *
|
|
|
|
+ * 1. The first kdump PT_LOAD segment is packed just after
|
|
|
|
+ * the ELF header, whereas netdump/diskdump page-align
|
|
|
|
+ * the first PT_LOAD segment.
|
|
|
|
+ * 2. Each kdump PT_LOAD segment has a p_align field of zero,
|
|
|
|
+ * whereas netdump/diskdump have their p_align fields set
|
|
|
|
+ * to the system page-size.
|
|
|
|
+ *
|
|
|
|
+ * If either kdump difference is seen, presume kdump -- this
|
|
|
|
+ * is obviously subject to change.
|
|
|
|
*/
|
|
|
|
if (STRNEQ(elf32->e_ident, ELFMAG) &&
|
|
|
|
(elf32->e_ident[EI_CLASS] == ELFCLASS32) &&
|
|
|
|
@@ -120,10 +138,16 @@
|
|
|
|
default:
|
|
|
|
goto bailout;
|
|
|
|
}
|
|
|
|
- nd->flags |= NETDUMP_ELF32;
|
|
|
|
+
|
|
|
|
load32 = (Elf32_Phdr *)
|
|
|
|
&header[sizeof(Elf32_Ehdr)+sizeof(Elf32_Phdr)];
|
|
|
|
size = (size_t)load32->p_offset;
|
|
|
|
+
|
|
|
|
+ if ((load32->p_offset & (MIN_PAGE_SIZE-1)) &&
|
|
|
|
+ (load32->p_align == 0))
|
|
|
|
+ tmp_flags |= KDUMP_ELF32;
|
|
|
|
+ else
|
|
|
|
+ tmp_flags |= NETDUMP_ELF32;
|
|
|
|
} else if (STRNEQ(elf64->e_ident, ELFMAG) &&
|
|
|
|
(elf64->e_ident[EI_CLASS] == ELFCLASS64) &&
|
|
|
|
(elf64->e_ident[EI_VERSION] == EV_CURRENT) &&
|
|
|
|
@@ -153,35 +177,68 @@
|
|
|
|
else
|
|
|
|
goto bailout;
|
|
|
|
|
|
|
|
+ case EM_386:
|
|
|
|
+ if ((elf64->e_ident[EI_DATA] == ELFDATA2LSB) &&
|
|
|
|
+ machine_type("X86"))
|
|
|
|
+ break;
|
|
|
|
+ else
|
|
|
|
+ goto bailout;
|
|
|
|
+
|
|
|
|
default:
|
|
|
|
goto bailout;
|
|
|
|
}
|
|
|
|
- nd->flags |= NETDUMP_ELF64;
|
|
|
|
+
|
|
|
|
load64 = (Elf64_Phdr *)
|
|
|
|
&header[sizeof(Elf64_Ehdr)+sizeof(Elf64_Phdr)];
|
|
|
|
size = (size_t)load64->p_offset;
|
|
|
|
+ if ((load64->p_offset & (MIN_PAGE_SIZE-1)) &&
|
|
|
|
+ (load64->p_align == 0))
|
|
|
|
+ tmp_flags |= KDUMP_ELF64;
|
|
|
|
+ else
|
|
|
|
+ tmp_flags |= NETDUMP_ELF64;
|
|
|
|
} else
|
|
|
|
goto bailout;
|
|
|
|
|
|
|
|
- if ((nd->netdump_header = (char *)malloc(size)) == NULL) {
|
|
|
|
- fprintf(stderr, "cannot malloc netdump header buffer\n");
|
|
|
|
+ switch (DUMPFILE_FORMAT(tmp_flags))
|
|
|
|
+ {
|
|
|
|
+ case NETDUMP_ELF32:
|
|
|
|
+ case NETDUMP_ELF64:
|
|
|
|
+ if (source_query & (NETDUMP_LOCAL|NETDUMP_REMOTE))
|
|
|
|
+ break;
|
|
|
|
+ else
|
|
|
|
+ goto bailout;
|
|
|
|
+
|
|
|
|
+ case KDUMP_ELF32:
|
|
|
|
+ case KDUMP_ELF64:
|
|
|
|
+ if (source_query & KDUMP_LOCAL)
|
|
|
|
+ break;
|
|
|
|
+ else
|
|
|
|
+ goto bailout;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if ((tmp_elf_header = (char *)malloc(size)) == NULL) {
|
|
|
|
+ fprintf(stderr, "cannot malloc ELF header buffer\n");
|
|
|
|
clean_exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
- if (read(fd, nd->netdump_header, size) != size) {
|
|
|
|
+ if (read(fd, tmp_elf_header, size) != size) {
|
|
|
|
sprintf(buf, "%s: read", file);
|
|
|
|
perror(buf);
|
|
|
|
+ free(tmp_elf_header);
|
|
|
|
goto bailout;
|
|
|
|
}
|
|
|
|
|
|
|
|
nd->ndfd = fd;
|
|
|
|
- nd->flags |= source;
|
|
|
|
+ nd->elf_header = tmp_elf_header;
|
|
|
|
+ nd->flags = tmp_flags;
|
|
|
|
+ nd->flags |= source_query;
|
|
|
|
|
|
|
|
- switch (nd->flags & (NETDUMP_ELF32|NETDUMP_ELF64))
|
|
|
|
+ switch (DUMPFILE_FORMAT(nd->flags))
|
|
|
|
{
|
|
|
|
case NETDUMP_ELF32:
|
|
|
|
+ case KDUMP_ELF32:
|
|
|
|
nd->header_size = load32->p_offset;
|
|
|
|
- nd->elf32 = (Elf32_Ehdr *)&nd->netdump_header[0];
|
|
|
|
+ nd->elf32 = (Elf32_Ehdr *)&nd->elf_header[0];
|
|
|
|
nd->num_pt_load_segments = nd->elf32->e_phnum - 1;
|
|
|
|
if ((nd->pt_load_segments = (struct pt_load_segment *)
|
|
|
|
malloc(sizeof(struct pt_load_segment) *
|
|
|
|
@@ -190,9 +247,11 @@
|
|
|
|
clean_exit(1);
|
|
|
|
}
|
|
|
|
nd->notes32 = (Elf32_Phdr *)
|
|
|
|
- &nd->netdump_header[sizeof(Elf32_Ehdr)];
|
|
|
|
+ &nd->elf_header[sizeof(Elf32_Ehdr)];
|
|
|
|
nd->load32 = (Elf32_Phdr *)
|
|
|
|
- &nd->netdump_header[sizeof(Elf32_Ehdr)+sizeof(Elf32_Phdr)];
|
|
|
|
+ &nd->elf_header[sizeof(Elf32_Ehdr)+sizeof(Elf32_Phdr)];
|
|
|
|
+ if (DUMPFILE_FORMAT(nd->flags) == NETDUMP_ELF32)
|
|
|
|
+ nd->page_size = (uint)nd->load32->p_align;
|
|
|
|
dump_Elf32_Ehdr(nd->elf32);
|
|
|
|
dump_Elf32_Phdr(nd->notes32, ELFREAD);
|
|
|
|
for (i = 0; i < nd->num_pt_load_segments; i++)
|
|
|
|
@@ -205,8 +264,9 @@
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NETDUMP_ELF64:
|
|
|
|
+ case KDUMP_ELF64:
|
|
|
|
nd->header_size = load64->p_offset;
|
|
|
|
- nd->elf64 = (Elf64_Ehdr *)&nd->netdump_header[0];
|
|
|
|
+ nd->elf64 = (Elf64_Ehdr *)&nd->elf_header[0];
|
|
|
|
nd->num_pt_load_segments = nd->elf64->e_phnum - 1;
|
|
|
|
if ((nd->pt_load_segments = (struct pt_load_segment *)
|
|
|
|
malloc(sizeof(struct pt_load_segment) *
|
|
|
|
@@ -215,9 +275,11 @@
|
|
|
|
clean_exit(1);
|
|
|
|
}
|
|
|
|
nd->notes64 = (Elf64_Phdr *)
|
|
|
|
- &nd->netdump_header[sizeof(Elf64_Ehdr)];
|
|
|
|
+ &nd->elf_header[sizeof(Elf64_Ehdr)];
|
|
|
|
nd->load64 = (Elf64_Phdr *)
|
|
|
|
- &nd->netdump_header[sizeof(Elf64_Ehdr)+sizeof(Elf64_Phdr)];
|
|
|
|
+ &nd->elf_header[sizeof(Elf64_Ehdr)+sizeof(Elf64_Phdr)];
|
|
|
|
+ if (DUMPFILE_FORMAT(nd->flags) == NETDUMP_ELF64)
|
|
|
|
+ nd->page_size = (uint)nd->load64->p_align;
|
|
|
|
dump_Elf64_Ehdr(nd->elf64);
|
|
|
|
dump_Elf64_Phdr(nd->notes64, ELFREAD);
|
|
|
|
for (i = 0; i < nd->num_pt_load_segments; i++)
|
|
|
|
@@ -230,6 +292,9 @@
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
+ if (CRASHDEBUG(1))
|
|
|
|
+ netdump_memory_dump(fp);
|
|
|
|
+
|
|
|
|
return nd->header_size;
|
|
|
|
|
|
|
|
bailout:
|
|
|
|
@@ -243,7 +308,7 @@
|
|
|
|
int
|
|
|
|
netdump_init(char *unused, FILE *fptr)
|
|
|
|
{
|
|
|
|
- if (!NETDUMP_VALID())
|
|
|
|
+ if (!VMCORE_VALID())
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
nd->ofp = fptr;
|
|
|
|
@@ -263,19 +328,19 @@
|
|
|
|
/*
|
|
|
|
* The Elf32_Phdr has 32-bit fields for p_paddr, p_filesz and
|
|
|
|
* p_memsz, so for now, multiple PT_LOAD segment support is
|
|
|
|
- * restricted to 64-bit machines. Until a "standard" becomes
|
|
|
|
- * available in the future that deals with physical memory
|
|
|
|
- * segments that start at greater then 4GB, or memory segments
|
|
|
|
- * sizes that are greater than 4GB (kexec?), then this feature
|
|
|
|
- * is restricted to 64-bit machines.
|
|
|
|
+ * restricted to 64-bit machines for netdump/diskdump vmcores.
|
|
|
|
+ * However, kexec/kdump has introduced the optional use of a
|
|
|
|
+ * 64-bit ELF header for 32-bit processors.
|
|
|
|
*/
|
|
|
|
- switch (nd->flags & (NETDUMP_ELF32|NETDUMP_ELF64))
|
|
|
|
+ switch (DUMPFILE_FORMAT(nd->flags))
|
|
|
|
{
|
|
|
|
case NETDUMP_ELF32:
|
|
|
|
offset = (off_t)paddr + (off_t)nd->header_size;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NETDUMP_ELF64:
|
|
|
|
+ case KDUMP_ELF32:
|
|
|
|
+ case KDUMP_ELF64:
|
|
|
|
if (nd->num_pt_load_segments == 1) {
|
|
|
|
offset = (off_t)paddr + (off_t)nd->header_size;
|
|
|
|
break;
|
|
|
|
@@ -302,24 +367,57 @@
|
2005-02-10 18:39:42 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
if (read(nd->ndfd, bufptr, cnt) != cnt)
|
|
|
|
return READ_ERROR;
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
return cnt;
|
2004-09-09 03:57:09 +00:00
|
|
|
}
|
2005-02-10 18:39:42 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
/*
|
|
|
|
- * Write to a netdump-created dumpfile.
|
|
|
|
+ * Write to a netdump-created dumpfile. Note that cmd_wr() does not
|
|
|
|
+ * allow writes to dumpfiles, so you can't get here from there.
|
|
|
|
+ * But, if it would ever be helpful, here it is...
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
write_netdump(int fd, void *bufptr, int cnt, ulong addr, physaddr_t paddr)
|
|
|
|
{
|
|
|
|
off_t offset;
|
|
|
|
+ struct pt_load_segment *pls;
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ switch (DUMPFILE_FORMAT(nd->flags))
|
|
|
|
+ {
|
|
|
|
+ case NETDUMP_ELF32:
|
|
|
|
+ offset = (off_t)paddr + (off_t)nd->header_size;
|
|
|
|
+ break;
|
2005-02-10 18:39:42 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
- offset = (off_t)paddr + (off_t)nd->header_size;
|
|
|
|
+ case NETDUMP_ELF64:
|
|
|
|
+ case KDUMP_ELF32:
|
|
|
|
+ case KDUMP_ELF64:
|
|
|
|
+ if (nd->num_pt_load_segments == 1) {
|
|
|
|
+ offset = (off_t)paddr + (off_t)nd->header_size;
|
|
|
|
+ break;
|
|
|
|
+ }
|
2005-02-10 18:39:42 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
- if (lseek(nd->ndfd, offset, SEEK_SET) != offset)
|
|
|
|
+ for (i = offset = 0; i < nd->num_pt_load_segments; i++) {
|
|
|
|
+ pls = &nd->pt_load_segments[i];
|
|
|
|
+ if ((paddr >= pls->phys_start) &&
|
|
|
|
+ (paddr < pls->phys_end)) {
|
|
|
|
+ offset = (off_t)(paddr - pls->phys_start) +
|
|
|
|
+ pls->file_offset;
|
|
|
|
+ break;
|
2005-02-10 18:39:42 +00:00
|
|
|
+ }
|
2004-09-09 03:58:15 +00:00
|
|
|
+ }
|
2006-01-04 19:34:13 +00:00
|
|
|
+
|
|
|
|
+ if (!offset)
|
|
|
|
+ return READ_ERROR;
|
|
|
|
+
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (lseek(nd->ndfd, offset, SEEK_SET) == -1)
|
|
|
|
return SEEK_ERROR;
|
2005-02-10 18:39:42 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
if (write(nd->ndfd, bufptr, cnt) != cnt)
|
|
|
|
- return WRITE_ERROR;
|
|
|
|
+ return READ_ERROR;
|
2005-02-10 18:39:42 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
return cnt;
|
2005-02-10 18:39:42 +00:00
|
|
|
}
|
2006-01-04 19:34:13 +00:00
|
|
|
@@ -330,7 +428,7 @@
|
|
|
|
FILE *
|
|
|
|
set_netdump_fp(FILE *fp)
|
|
|
|
{
|
|
|
|
- if (!NETDUMP_VALID())
|
|
|
|
+ if (!VMCORE_VALID())
|
|
|
|
return NULL;
|
2004-09-09 03:58:15 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
nd->ofp = fp;
|
|
|
|
@@ -346,7 +444,7 @@
|
|
|
|
char buf[BUFSIZE];
|
|
|
|
va_list ap;
|
2005-02-10 18:39:42 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
- if (!fmt || !strlen(fmt) || !NETDUMP_VALID())
|
|
|
|
+ if (!fmt || !strlen(fmt) || !VMCORE_VALID())
|
|
|
|
return;
|
2005-02-10 18:39:42 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
va_start(ap, fmt);
|
|
|
|
@@ -362,33 +460,21 @@
|
|
|
|
uint
|
|
|
|
netdump_page_size(void)
|
|
|
|
{
|
|
|
|
- uint pagesz;
|
|
|
|
-
|
|
|
|
- if (!NETDUMP_VALID())
|
|
|
|
+ if (!VMCORE_VALID())
|
|
|
|
return 0;
|
2005-02-10 18:39:42 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
- switch (nd->flags & (NETDUMP_ELF32|NETDUMP_ELF64))
|
|
|
|
- {
|
|
|
|
- case NETDUMP_ELF32:
|
|
|
|
- pagesz = (uint)nd->load32->p_align;
|
|
|
|
- break;
|
|
|
|
- case NETDUMP_ELF64:
|
|
|
|
- pagesz = (uint)nd->load64->p_align;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return pagesz;
|
|
|
|
+ return nd->page_size;
|
2005-02-10 18:39:42 +00:00
|
|
|
}
|
2006-01-04 19:34:13 +00:00
|
|
|
|
|
|
|
int
|
|
|
|
netdump_free_memory(void)
|
|
|
|
{
|
|
|
|
- return (NETDUMP_VALID() ? 0 : 0);
|
|
|
|
+ return (VMCORE_VALID() ? 0 : 0);
|
2005-02-10 18:39:42 +00:00
|
|
|
}
|
2004-09-09 03:58:15 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
int netdump_memory_used(void)
|
|
|
|
{
|
|
|
|
- return (NETDUMP_VALID() ? 0 : 0);
|
|
|
|
+ return (VMCORE_VALID() ? 0 : 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
@@ -414,21 +500,57 @@
|
|
|
|
#ifdef DAEMON
|
|
|
|
return nd->task_struct;
|
|
|
|
#else
|
|
|
|
- int i;
|
|
|
|
+ int i, crashing_cpu;
|
|
|
|
size_t len;
|
|
|
|
char *user_regs;
|
|
|
|
ulong ebp, esp, task;
|
|
|
|
|
|
|
|
- if (!NETDUMP_VALID() || !get_active_set())
|
|
|
|
- return NO_TASK;
|
|
|
|
+ if (!VMCORE_VALID() || !get_active_set())
|
|
|
|
+ goto panic_task_undetermined;
|
|
|
|
|
|
|
|
- if (nd->task_struct)
|
|
|
|
+ if (nd->task_struct) {
|
|
|
|
+ if (CRASHDEBUG(1))
|
|
|
|
+ error(INFO,
|
|
|
|
+ "get_netdump_panic_task: NT_TASKSTRUCT: %lx\n",
|
|
|
|
+ nd->task_struct);
|
|
|
|
return nd->task_struct;
|
|
|
|
+ }
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ switch (DUMPFILE_FORMAT(nd->flags))
|
|
|
|
+ {
|
|
|
|
+ case NETDUMP_ELF32:
|
|
|
|
+ case NETDUMP_ELF64:
|
|
|
|
+ crashing_cpu = -1;
|
|
|
|
+ break;
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ case KDUMP_ELF32:
|
|
|
|
+ case KDUMP_ELF64:
|
|
|
|
+ crashing_cpu = -1;
|
|
|
|
+ if (symbol_exists("crashing_cpu")) {
|
|
|
|
+ get_symbol_data("crashing_cpu", sizeof(int), &i);
|
|
|
|
+ if ((i >= 0) && (i < nd->num_prstatus_notes)) {
|
|
|
|
+ crashing_cpu = i;
|
|
|
|
+ if (CRASHDEBUG(1))
|
|
|
|
+ error(INFO,
|
|
|
|
+ "get_netdump_panic_task: crashing_cpu: %d\n",
|
|
|
|
+ crashing_cpu);
|
|
|
|
+ }
|
|
|
|
+ }
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ if ((nd->num_prstatus_notes > 1) && (crashing_cpu == -1))
|
|
|
|
+ goto panic_task_undetermined;
|
|
|
|
+ break;
|
|
|
|
+ }
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ if (nd->elf32 && (nd->elf32->e_machine == EM_386)) {
|
|
|
|
+ Elf32_Nhdr *note32;
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ if ((nd->num_prstatus_notes > 1) && (crashing_cpu != -1))
|
|
|
|
+ note32 = (Elf32_Nhdr *)
|
|
|
|
+ nd->nt_prstatus_percpu[crashing_cpu];
|
|
|
|
+ else
|
|
|
|
+ note32 = (Elf32_Nhdr *)nd->nt_prstatus;
|
|
|
|
|
|
|
|
- if (nd->elf32 && nd->elf32->e_machine == EM_386) {
|
|
|
|
- Elf32_Nhdr *note32 = (Elf32_Nhdr *)
|
|
|
|
- ((char *)nd->elf32 + nd->notes32->p_offset);
|
|
|
|
-
|
|
|
|
len = sizeof(Elf32_Nhdr);
|
|
|
|
len = roundup(len + note32->n_namesz, 4);
|
|
|
|
len = roundup(len + note32->n_descsz, 4);
|
|
|
|
@@ -437,14 +559,15 @@
|
|
|
|
- SIZE(user_regs_struct) - sizeof(int);
|
|
|
|
ebp = ULONG(user_regs + OFFSET(user_regs_struct_ebp));
|
|
|
|
esp = ULONG(user_regs + OFFSET(user_regs_struct_esp));
|
|
|
|
+check_ebp_esp:
|
|
|
|
if (CRASHDEBUG(1))
|
|
|
|
- fprintf(fp,
|
|
|
|
- "get_netdump_panic_task: esp: %lx ebp: %lx\n",
|
|
|
|
+ error(INFO,
|
|
|
|
+ "get_netdump_panic_task: NT_PRSTATUS esp: %lx ebp: %lx\n",
|
|
|
|
esp, ebp);
|
|
|
|
if (IS_KVADDR(esp)) {
|
|
|
|
task = stkptr_to_task(esp);
|
|
|
|
if (CRASHDEBUG(1))
|
|
|
|
- fprintf(fp,
|
|
|
|
+ error(INFO,
|
|
|
|
"get_netdump_panic_task: esp: %lx -> task: %lx\n",
|
|
|
|
esp, task);
|
|
|
|
for (i = 0; task && (i < NR_CPUS); i++) {
|
|
|
|
@@ -455,7 +578,7 @@
|
|
|
|
if (IS_KVADDR(ebp)) {
|
|
|
|
task = stkptr_to_task(ebp);
|
|
|
|
if (CRASHDEBUG(1))
|
|
|
|
- fprintf(fp,
|
|
|
|
+ error(INFO,
|
|
|
|
"get_netdump_panic_task: ebp: %lx -> task: %lx\n",
|
|
|
|
ebp, task);
|
|
|
|
for (i = 0; task && (i < NR_CPUS); i++) {
|
|
|
|
@@ -464,25 +587,37 @@
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (nd->elf64) {
|
|
|
|
- Elf64_Nhdr *note64 = (Elf64_Nhdr *)
|
|
|
|
- ((char *)nd->elf64 + nd->notes64->p_offset);
|
|
|
|
-
|
|
|
|
+ Elf64_Nhdr *note64;
|
|
|
|
+
|
|
|
|
+ if ((nd->num_prstatus_notes > 1) && (crashing_cpu != -1))
|
|
|
|
+ note64 = (Elf64_Nhdr *)
|
|
|
|
+ nd->nt_prstatus_percpu[crashing_cpu];
|
|
|
|
+ else
|
|
|
|
+ note64 = (Elf64_Nhdr *)nd->nt_prstatus;
|
|
|
|
+
|
|
|
|
len = sizeof(Elf64_Nhdr);
|
|
|
|
len = roundup(len + note64->n_namesz, 4);
|
|
|
|
user_regs = (char *)((char *)note64 + len +
|
|
|
|
MEMBER_OFFSET("elf_prstatus", "pr_reg"));
|
|
|
|
+
|
|
|
|
+ if (nd->elf64->e_machine == EM_386) {
|
|
|
|
+ ebp = ULONG(user_regs + OFFSET(user_regs_struct_ebp));
|
|
|
|
+ esp = ULONG(user_regs + OFFSET(user_regs_struct_esp));
|
|
|
|
+ goto check_ebp_esp;
|
2004-09-09 03:58:15 +00:00
|
|
|
+ }
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
if (nd->elf64->e_machine == EM_PPC64) {
|
|
|
|
/*
|
|
|
|
* Get the GPR1 register value.
|
|
|
|
*/
|
|
|
|
esp = *(ulong *)((char *)user_regs + 8);
|
|
|
|
if (CRASHDEBUG(1))
|
|
|
|
- fprintf(fp,
|
|
|
|
- "get_netdump_panic_task: esp: %lx\n", esp);
|
|
|
|
+ error(INFO,
|
|
|
|
+ "get_netdump_panic_task: NT_PRSTATUS esp: %lx\n", esp);
|
|
|
|
if (IS_KVADDR(esp)) {
|
|
|
|
task = stkptr_to_task(esp);
|
|
|
|
if (CRASHDEBUG(1))
|
|
|
|
- fprintf(fp,
|
|
|
|
+ error(INFO,
|
|
|
|
"get_netdump_panic_task: esp: %lx -> task: %lx\n",
|
|
|
|
esp, task);
|
|
|
|
for (i = 0; task && (i < NR_CPUS); i++) {
|
|
|
|
@@ -493,8 +628,10 @@
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
+panic_task_undetermined:
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
if (CRASHDEBUG(1))
|
|
|
|
- fprintf(fp, "get_netdump_panic_task: returning NO_TASK\n");
|
|
|
|
+ error(INFO, "get_netdump_panic_task: failed\n");
|
2005-02-10 18:39:42 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
return NO_TASK;
|
|
|
|
#endif
|
|
|
|
@@ -512,7 +649,7 @@
|
|
|
|
return nd->switch_stack;
|
|
|
|
return 0;
|
|
|
|
#else
|
|
|
|
- if (!NETDUMP_VALID() || !get_active_set())
|
|
|
|
+ if (!VMCORE_VALID() || !get_active_set())
|
|
|
|
return 0;
|
2004-09-09 03:58:15 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
if (nd->task_struct == task)
|
|
|
|
@@ -525,30 +662,36 @@
|
|
|
|
int
|
|
|
|
netdump_memory_dump(FILE *fp)
|
|
|
|
{
|
|
|
|
- int i, others;
|
|
|
|
+ int i, others, wrap, flen;
|
|
|
|
size_t len, tot;
|
|
|
|
FILE *fpsave;
|
|
|
|
Elf32_Off offset32;
|
|
|
|
Elf32_Off offset64;
|
|
|
|
struct pt_load_segment *pls;
|
2004-09-09 03:58:15 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
- if (!NETDUMP_VALID())
|
|
|
|
+ if (!VMCORE_VALID())
|
|
|
|
return FALSE;
|
2005-02-10 18:39:42 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
fpsave = nd->ofp;
|
|
|
|
nd->ofp = fp;
|
2004-09-09 03:58:15 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
- netdump_print("netdump_data: \n");
|
|
|
|
+ netdump_print("vmcore_data: \n");
|
|
|
|
netdump_print(" flags: %lx (", nd->flags);
|
|
|
|
others = 0;
|
|
|
|
if (nd->flags & NETDUMP_LOCAL)
|
|
|
|
netdump_print("%sNETDUMP_LOCAL", others++ ? "|" : "");
|
|
|
|
+ if (nd->flags & KDUMP_LOCAL)
|
|
|
|
+ netdump_print("%sKDUMP_LOCAL", others++ ? "|" : "");
|
|
|
|
if (nd->flags & NETDUMP_REMOTE)
|
|
|
|
netdump_print("%sNETDUMP_REMOTE", others++ ? "|" : "");
|
|
|
|
if (nd->flags & NETDUMP_ELF32)
|
|
|
|
netdump_print("%sNETDUMP_ELF32", others++ ? "|" : "");
|
|
|
|
if (nd->flags & NETDUMP_ELF64)
|
|
|
|
netdump_print("%sNETDUMP_ELF64", others++ ? "|" : "");
|
|
|
|
+ if (nd->flags & KDUMP_ELF32)
|
|
|
|
+ netdump_print("%sKDUMP_ELF32", others++ ? "|" : "");
|
|
|
|
+ if (nd->flags & KDUMP_ELF64)
|
|
|
|
+ netdump_print("%sKDUMP_ELF64", others++ ? "|" : "");
|
|
|
|
if (nd->flags & PARTIAL_DUMP)
|
|
|
|
netdump_print("%sPARTIAL_DUMP", others++ ? "|" : "");
|
|
|
|
netdump_print(")\n");
|
|
|
|
@@ -566,7 +709,7 @@
|
|
|
|
netdump_print(" phys_end: %llx\n",
|
|
|
|
pls->phys_end);
|
|
|
|
}
|
|
|
|
- netdump_print(" netdump_header: %lx\n", nd->netdump_header);
|
|
|
|
+ netdump_print(" elf_header: %lx\n", nd->elf_header);
|
|
|
|
netdump_print(" elf32: %lx\n", nd->elf32);
|
|
|
|
netdump_print(" notes32: %lx\n", nd->notes32);
|
|
|
|
netdump_print(" load32: %lx\n", nd->load32);
|
|
|
|
@@ -577,11 +720,28 @@
|
|
|
|
netdump_print(" nt_prpsinfo: %lx\n", nd->nt_prpsinfo);
|
|
|
|
netdump_print(" nt_taskstruct: %lx\n", nd->nt_taskstruct);
|
|
|
|
netdump_print(" task_struct: %lx\n", nd->task_struct);
|
|
|
|
- netdump_print(" switch_stack: %lx\n\n", nd->switch_stack);
|
|
|
|
+ netdump_print(" page_size: %d\n", nd->page_size);
|
|
|
|
+ netdump_print(" switch_stack: %lx\n", nd->switch_stack);
|
|
|
|
+ netdump_print(" num_prstatus_notes: %d\n", nd->num_prstatus_notes);
|
|
|
|
+ netdump_print(" nt_prstatus_percpu: ");
|
|
|
|
+ wrap = sizeof(void *) == SIZEOF_32BIT ? 8 : 4;
|
|
|
|
+ flen = sizeof(void *) == SIZEOF_32BIT ? 8 : 16;
|
|
|
|
+ if (nd->num_prstatus_notes == 1)
|
|
|
|
+ netdump_print("%.*lx\n", flen, nd->nt_prstatus_percpu[0]);
|
|
|
|
+ else {
|
|
|
|
+ for (i = 0; i < nd->num_prstatus_notes; i++) {
|
|
|
|
+ if ((i % wrap) == 0)
|
|
|
|
+ netdump_print("\n ");
|
|
|
|
+ netdump_print("%.*lx ", flen,
|
|
|
|
+ nd->nt_prstatus_percpu[i]);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ netdump_print("\n\n");
|
2005-02-10 18:39:42 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
- switch (nd->flags & (NETDUMP_ELF32|NETDUMP_ELF64))
|
|
|
|
+ switch (DUMPFILE_FORMAT(nd->flags))
|
|
|
|
{
|
|
|
|
case NETDUMP_ELF32:
|
|
|
|
+ case KDUMP_ELF32:
|
|
|
|
dump_Elf32_Ehdr(nd->elf32);
|
|
|
|
dump_Elf32_Phdr(nd->notes32, ELFREAD);
|
|
|
|
for (i = 0; i < nd->num_pt_load_segments; i++)
|
|
|
|
@@ -594,6 +754,7 @@
|
|
|
|
break;
|
2004-09-09 03:57:09 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
case NETDUMP_ELF64:
|
|
|
|
+ case KDUMP_ELF64:
|
|
|
|
dump_Elf64_Ehdr(nd->elf64);
|
|
|
|
dump_Elf64_Phdr(nd->notes64, ELFREAD);
|
|
|
|
for (i = 0; i < nd->num_pt_load_segments; i++)
|
|
|
|
@@ -865,6 +1026,9 @@
|
|
|
|
netdump_print(" e_machine: %d ", elf->e_machine);
|
|
|
|
switch (elf->e_machine)
|
|
|
|
{
|
|
|
|
+ case EM_386:
|
|
|
|
+ netdump_print("(EM_386)\n");
|
|
|
|
+ break;
|
|
|
|
case EM_IA_64:
|
|
|
|
netdump_print("(EM_IA_64)\n");
|
|
|
|
break;
|
|
|
|
@@ -1061,7 +1225,7 @@
|
|
|
|
*/
|
2005-02-10 18:39:42 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
static size_t
|
|
|
|
-dump_Elf32_Nhdr(Elf32_Off offset, int store_addresses)
|
|
|
|
+dump_Elf32_Nhdr(Elf32_Off offset, int store)
|
|
|
|
{
|
|
|
|
int i, lf;
|
|
|
|
Elf32_Nhdr *note;
|
|
|
|
@@ -1085,17 +1249,26 @@
|
|
|
|
{
|
|
|
|
case NT_PRSTATUS:
|
|
|
|
netdump_print("(NT_PRSTATUS)\n");
|
|
|
|
- if (store_addresses)
|
|
|
|
- nd->nt_prstatus = (void *)note;
|
|
|
|
+ if (store) {
|
|
|
|
+ if (!nd->nt_prstatus)
|
|
|
|
+ nd->nt_prstatus = (void *)note;
|
|
|
|
+ for (i = 0; i < NR_CPUS; i++) {
|
|
|
|
+ if (!nd->nt_prstatus_percpu[i]) {
|
|
|
|
+ nd->nt_prstatus_percpu[i] = (void *)note;
|
|
|
|
+ nd->num_prstatus_notes++;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
break;
|
|
|
|
case NT_PRPSINFO:
|
|
|
|
netdump_print("(NT_PRPSINFO)\n");
|
|
|
|
- if (store_addresses)
|
|
|
|
+ if (store)
|
|
|
|
nd->nt_prpsinfo = (void *)note;
|
2005-02-10 18:39:42 +00:00
|
|
|
break;
|
2006-01-04 19:34:13 +00:00
|
|
|
case NT_TASKSTRUCT:
|
|
|
|
netdump_print("(NT_TASKSTRUCT)\n");
|
|
|
|
- if (store_addresses) {
|
|
|
|
+ if (store) {
|
|
|
|
nd->nt_taskstruct = (void *)note;
|
|
|
|
nd->task_struct = *((ulong *)(ptr + note->n_namesz));
|
|
|
|
nd->switch_stack = *((ulong *)
|
|
|
|
@@ -1105,14 +1278,36 @@
|
|
|
|
case NT_DISKDUMP:
|
|
|
|
netdump_print("(NT_DISKDUMP)\n");
|
|
|
|
uptr = (ulong *)(ptr + note->n_namesz);
|
|
|
|
- if (*uptr)
|
|
|
|
+ if (*uptr && store)
|
|
|
|
nd->flags |= PARTIAL_DUMP;
|
|
|
|
break;
|
|
|
|
+#ifdef NOTDEF
|
|
|
|
+ /*
|
|
|
|
+ * Note: Based upon the original, abandoned, proposal for
|
|
|
|
+ * its contents -- keep around for potential future use.
|
|
|
|
+ */
|
|
|
|
+ case NT_KDUMPINFO:
|
|
|
|
+ netdump_print("(NT_KDUMPINFO)\n");
|
|
|
|
+ if (store) {
|
|
|
|
+ uptr = (note->n_namesz == 5) ?
|
|
|
|
+ (ulong *)(ptr + ((note->n_namesz + 3) & ~3)) :
|
|
|
|
+ (ulong *)(ptr + note->n_namesz);
|
|
|
|
+ nd->page_size = (uint)(1 << *uptr);
|
|
|
|
+ uptr++;
|
|
|
|
+ nd->task_struct = *uptr;
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+#endif
|
|
|
|
default:
|
|
|
|
netdump_print("(?)\n");
|
2004-09-09 03:57:09 +00:00
|
|
|
}
|
2005-02-10 18:39:42 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
uptr = (ulong *)(ptr + note->n_namesz);
|
|
|
|
+ /*
|
|
|
|
+ * kdumps are off-by-1, because their n_namesz is 5 for "CORE".
|
|
|
|
+ */
|
|
|
|
+ if ((nd->flags & KDUMP_ELF32) && (note->n_namesz == 5))
|
|
|
|
+ uptr = (ulong *)(ptr + ((note->n_namesz + 3) & ~3));
|
|
|
|
for (i = lf = 0; i < note->n_descsz/sizeof(ulong); i++) {
|
|
|
|
if (((i%4)==0)) {
|
|
|
|
netdump_print("%s ",
|
|
|
|
@@ -1135,7 +1330,7 @@
|
2005-03-03 16:53:39 +00:00
|
|
|
|
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
static size_t
|
|
|
|
-dump_Elf64_Nhdr(Elf64_Off offset, int store_addresses)
|
|
|
|
+dump_Elf64_Nhdr(Elf64_Off offset, int store)
|
|
|
|
{
|
|
|
|
int i, lf;
|
|
|
|
Elf64_Nhdr *note;
|
|
|
|
@@ -1160,17 +1355,26 @@
|
|
|
|
{
|
|
|
|
case NT_PRSTATUS:
|
|
|
|
netdump_print("(NT_PRSTATUS)\n");
|
|
|
|
- if (store_addresses)
|
|
|
|
- nd->nt_prstatus = (void *)note;
|
|
|
|
+ if (store) {
|
|
|
|
+ if (!nd->nt_prstatus)
|
|
|
|
+ nd->nt_prstatus = (void *)note;
|
|
|
|
+ for (i = 0; i < NR_CPUS; i++) {
|
|
|
|
+ if (!nd->nt_prstatus_percpu[i]) {
|
|
|
|
+ nd->nt_prstatus_percpu[i] = (void *)note;
|
|
|
|
+ nd->num_prstatus_notes++;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
break;
|
|
|
|
case NT_PRPSINFO:
|
|
|
|
netdump_print("(NT_PRPSINFO)\n");
|
|
|
|
- if (store_addresses)
|
|
|
|
+ if (store)
|
|
|
|
nd->nt_prpsinfo = (void *)note;
|
|
|
|
break;
|
|
|
|
case NT_TASKSTRUCT:
|
|
|
|
netdump_print("(NT_TASKSTRUCT)\n");
|
|
|
|
- if (store_addresses) {
|
|
|
|
+ if (store) {
|
|
|
|
nd->nt_taskstruct = (void *)note;
|
|
|
|
nd->task_struct = *((ulong *)(ptr + note->n_namesz));
|
|
|
|
nd->switch_stack = *((ulong *)
|
|
|
|
@@ -1180,16 +1384,49 @@
|
|
|
|
case NT_DISKDUMP:
|
|
|
|
netdump_print("(NT_DISKDUMP)\n");
|
|
|
|
iptr = (int *)(ptr + note->n_namesz);
|
|
|
|
- if (*iptr)
|
|
|
|
+ if (*iptr && store)
|
|
|
|
nd->flags |= PARTIAL_DUMP;
|
|
|
|
if (note->n_descsz < sizeof(ulonglong))
|
|
|
|
netdump_print(" %08x", *iptr);
|
|
|
|
break;
|
|
|
|
+#ifdef NOTDEF
|
|
|
|
+ /*
|
|
|
|
+ * Note: Based upon the original, abandoned, proposal for
|
|
|
|
+ * its contents -- keep around for potential future use.
|
|
|
|
+ */
|
|
|
|
+ case NT_KDUMPINFO:
|
|
|
|
+ netdump_print("(NT_KDUMPINFO)\n");
|
|
|
|
+ if (store) {
|
|
|
|
+ uint32_t *u32ptr;
|
|
|
|
+
|
|
|
|
+ if (nd->elf64->e_machine == EM_386) {
|
|
|
|
+ u32ptr = (note->n_namesz == 5) ?
|
|
|
|
+ (uint *)(ptr + ((note->n_namesz + 3) & ~3)) :
|
|
|
|
+ (uint *)(ptr + note->n_namesz);
|
|
|
|
+ nd->page_size = 1 << *u32ptr;
|
|
|
|
+ u32ptr++;
|
|
|
|
+ nd->task_struct = *u32ptr;
|
|
|
|
+ } else {
|
|
|
|
+ uptr = (note->n_namesz == 5) ?
|
|
|
|
+ (ulonglong *)(ptr + ((note->n_namesz + 3) & ~3)) :
|
|
|
|
+ (ulonglong *)(ptr + note->n_namesz);
|
|
|
|
+ nd->page_size = (uint)(1 << *uptr);
|
|
|
|
+ uptr++;
|
|
|
|
+ nd->task_struct = *uptr;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+#endif
|
|
|
|
default:
|
|
|
|
netdump_print("(?)\n");
|
|
|
|
}
|
2005-03-03 16:53:39 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
uptr = (ulonglong *)(ptr + note->n_namesz);
|
|
|
|
+ /*
|
|
|
|
+ * kdumps are off-by-1, because their n_namesz is 5 for "CORE".
|
|
|
|
+ */
|
|
|
|
+ if ((nd->flags & KDUMP_ELF64) && (note->n_namesz == 5))
|
|
|
|
+ uptr = (ulonglong *)(ptr + ((note->n_namesz + 3) & ~3));
|
|
|
|
for (i = lf = 0; i < note->n_descsz/sizeof(ulonglong); i++) {
|
|
|
|
if (((i%2)==0)) {
|
|
|
|
netdump_print("%s ",
|
|
|
|
@@ -1251,12 +1488,12 @@
|
2005-03-03 16:53:39 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
default:
|
|
|
|
error(FATAL,
|
|
|
|
- "netdump support for ELF machine type %d not available\n",
|
|
|
|
+ "support for ELF machine type %d not available\n",
|
|
|
|
e_machine);
|
|
|
|
}
|
|
|
|
}
|
2005-03-03 16:53:39 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
-static void
|
|
|
|
+void
|
|
|
|
get_netdump_regs_x86_64(struct bt_info *bt, ulong *ripp, ulong *rspp)
|
|
|
|
{
|
|
|
|
Elf64_Nhdr *note;
|
|
|
|
@@ -1267,8 +1504,13 @@
|
|
|
|
if (is_task_active(bt->task))
|
|
|
|
bt->flags |= BT_DUMPFILE_SEARCH;
|
2005-03-03 16:53:39 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
- if (VALID_STRUCT(user_regs_struct) && (bt->task == tt->panic_task)) {
|
|
|
|
- note = (Elf64_Nhdr *)nd->nt_prstatus;
|
|
|
|
+ if ((NETDUMP_DUMPFILE() || KDUMP_DUMPFILE()) &&
|
|
|
|
+ VALID_STRUCT(user_regs_struct) && (bt->task == tt->panic_task)) {
|
|
|
|
+ if (nd->num_prstatus_notes > 1)
|
|
|
|
+ note = (Elf64_Nhdr *)
|
|
|
|
+ nd->nt_prstatus_percpu[bt->tc->processor];
|
|
|
|
+ else
|
|
|
|
+ note = (Elf64_Nhdr *)nd->nt_prstatus;
|
2005-03-03 16:53:39 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
len = sizeof(Elf64_Nhdr);
|
|
|
|
len = roundup(len + note->n_namesz, 4);
|
|
|
|
@@ -1295,7 +1537,7 @@
|
|
|
|
* the raw stack for some reasonable hooks.
|
2004-09-09 03:58:15 +00:00
|
|
|
*/
|
2004-09-09 03:57:09 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
-static void
|
|
|
|
+void
|
|
|
|
get_netdump_regs_x86(struct bt_info *bt, ulong *eip, ulong *esp)
|
|
|
|
{
|
|
|
|
int i, search, panic;
|
|
|
|
@@ -1320,6 +1562,7 @@
|
|
|
|
if (STREQ(sym, "netconsole_netdump") ||
|
|
|
|
STREQ(sym, "netpoll_start_netdump") ||
|
|
|
|
STREQ(sym, "start_disk_dump") ||
|
|
|
|
+ STREQ(sym, "crash_kexec") ||
|
|
|
|
STREQ(sym, "disk_dump")) {
|
|
|
|
*eip = *up;
|
|
|
|
*esp = search ?
|
|
|
|
@@ -1354,7 +1597,7 @@
|
|
|
|
next_sysrq:
|
|
|
|
*eip = *up;
|
|
|
|
*esp = bt->stackbase + ((char *)(up+4) - bt->stackbuf);
|
|
|
|
- machdep->flags |= SYSRQ;
|
|
|
|
+ pc->flags |= SYSRQ;
|
|
|
|
for (i++, up++; i < LONGS_PER_STACK; i++, up++) {
|
|
|
|
sym = closest_symbol(*up);
|
|
|
|
if (STREQ(sym, "sysrq_handle_crash"))
|
|
|
|
@@ -1371,7 +1614,15 @@
|
|
|
|
*esp = search ?
|
|
|
|
bt->stackbase + ((char *)(up+1) - bt->stackbuf) :
|
|
|
|
*(up-1);
|
|
|
|
- machdep->flags |= SYSRQ;
|
|
|
|
+ pc->flags |= SYSRQ;
|
|
|
|
+ return;
|
|
|
|
+ }
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ if (STREQ(sym, "crash_nmi_callback")) {
|
|
|
|
+ *eip = *up;
|
|
|
|
+ *esp = search ?
|
|
|
|
+ bt->stackbase + ((char *)(up+1) - bt->stackbuf) :
|
|
|
|
+ *(up-1);
|
|
|
|
return;
|
|
|
|
}
|
2005-02-10 18:39:42 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
@@ -1418,7 +1669,7 @@
|
|
|
|
goto retry;
|
|
|
|
}
|
2005-02-10 18:39:42 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
- console("get_netdump_regs_x86: cannot find anything useful\n");
|
|
|
|
+ console("get_netdump_regs_x86: cannot find anything useful for task: %lx\n", bt->task);
|
|
|
|
|
|
|
|
machdep->get_stack_frame(bt, eip, esp);
|
2005-02-10 18:39:42 +00:00
|
|
|
}
|
2006-01-04 19:34:13 +00:00
|
|
|
@@ -1430,7 +1681,11 @@
|
|
|
|
size_t len;
|
2005-02-10 18:39:42 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
if (bt->task == tt->panic_task) {
|
|
|
|
- note = (Elf64_Nhdr *)nd->nt_prstatus;
|
|
|
|
+ if (nd->num_prstatus_notes > 1)
|
|
|
|
+ note = (Elf64_Nhdr *)
|
|
|
|
+ nd->nt_prstatus_percpu[bt->tc->processor];
|
|
|
|
+ else
|
|
|
|
+ note = (Elf64_Nhdr *)nd->nt_prstatus;
|
2004-09-09 03:58:15 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
len = sizeof(Elf64_Nhdr);
|
|
|
|
len = roundup(len + note->n_namesz, 4);
|
|
|
|
@@ -1446,3 +1701,78 @@
|
|
|
|
{
|
|
|
|
return (nd->flags & PARTIAL_DUMP ? TRUE : FALSE);
|
|
|
|
}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * kexec/kdump generated vmcore files are similar enough in
|
|
|
|
+ * nature to netdump/diskdump such that most vmcore access
|
|
|
|
+ * functionality may be borrowed from the equivalent netdump
|
|
|
|
+ * function. If not, re-work them here.
|
|
|
|
+ */
|
|
|
|
+int
|
|
|
|
+is_kdump(char *file, ulong source_query)
|
|
|
|
+{
|
|
|
|
+ return is_netdump(file, source_query);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+int
|
|
|
|
+kdump_init(char *unused, FILE *fptr)
|
|
|
|
+{
|
|
|
|
+ return netdump_init(unused, fptr);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+ulong
|
|
|
|
+get_kdump_panic_task(void)
|
|
|
|
+{
|
|
|
|
+ return get_netdump_panic_task();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+int
|
|
|
|
+read_kdump(int fd, void *bufptr, int cnt, ulong addr, physaddr_t paddr)
|
|
|
|
+{
|
|
|
|
+ return read_netdump(fd, bufptr, cnt, addr, paddr);
|
|
|
|
+}
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+int
|
|
|
|
+write_kdump(int fd, void *bufptr, int cnt, ulong addr, physaddr_t paddr)
|
|
|
|
+{
|
|
|
|
+ return write_netdump(fd, bufptr, cnt, addr, paddr);
|
|
|
|
+}
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+void
|
|
|
|
+get_kdump_regs(struct bt_info *bt, ulong *eip, ulong *esp)
|
|
|
|
+{
|
|
|
|
+ get_netdump_regs(bt, eip, esp);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+uint
|
|
|
|
+kdump_page_size(void)
|
|
|
|
+{
|
|
|
|
+ uint pagesz;
|
|
|
|
+
|
|
|
|
+ if (!VMCORE_VALID())
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ if (!(pagesz = nd->page_size))
|
|
|
|
+ pagesz = (uint)getpagesize();
|
|
|
|
+
|
|
|
|
+ return pagesz;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+int
|
|
|
|
+kdump_free_memory(void)
|
2005-02-10 18:39:42 +00:00
|
|
|
+{
|
2006-01-04 19:34:13 +00:00
|
|
|
+ return netdump_free_memory();
|
|
|
|
+}
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+int
|
|
|
|
+kdump_memory_used(void)
|
|
|
|
+{
|
|
|
|
+ return netdump_memory_used();
|
|
|
|
+}
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+int
|
|
|
|
+kdump_memory_dump(FILE *fp)
|
|
|
|
+{
|
|
|
|
+ return netdump_memory_dump(fp);
|
2005-02-10 18:39:42 +00:00
|
|
|
+}
|
2006-01-04 19:34:13 +00:00
|
|
|
--- crash/diskdump.c.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/diskdump.c 2005-11-30 15:15:00.000000000 -0500
|
|
|
|
@@ -1,16 +1,16 @@
|
|
|
|
/*
|
|
|
|
* diskdump.c
|
|
|
|
*
|
|
|
|
- * NOTE: The Red Hat diskdump module currently creates
|
|
|
|
- * vmcore dumpfiles that are identical to those made
|
|
|
|
- * by the Red Hat netdump module, and therefore the
|
|
|
|
- * dumpfile is recognized as such. But just in case
|
|
|
|
- * there's ever a divergence, this file is being kept
|
|
|
|
- * in place, along with the DISKDUMP-related #define's
|
|
|
|
- * and their usage throughout the crash sources.
|
|
|
|
+ * The diskdump module optionally creates either ELF vmcore
|
|
|
|
+ * dumpfiles, or compressed dumpfiles derived from the LKCD format.
|
|
|
|
+ * In the case of ELF vmcore files, since they are identical to
|
|
|
|
+ * netdump dumpfiles, the facilities in netdump.c are used. For
|
|
|
|
+ * compressed dumpfiles, the facilities in this file are used.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2004, 2005 David Anderson
|
|
|
|
* Copyright (C) 2004, 2005 Red Hat, Inc. All rights reserved.
|
|
|
|
+ * Copyright (C) 2005 FUJITSU LIMITED
|
|
|
|
+ * Copyright (C) 2005 NEC Corporation
|
|
|
|
*
|
|
|
|
* This software may be freely redistributed under the terms of the
|
|
|
|
* GNU General Public License.
|
|
|
|
@@ -18,23 +18,230 @@
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
- *
|
|
|
|
- * Author: David Anderson
|
|
|
|
*/
|
2004-09-09 03:58:15 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
#include "defs.h"
|
|
|
|
#include "diskdump.h"
|
2005-02-10 18:39:42 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
+#define BITMAP_SECT_LEN 4096
|
|
|
|
+
|
|
|
|
struct diskdump_data {
|
|
|
|
ulong flags; /* DISKDUMP_LOCAL, plus anything else... */
|
|
|
|
int dfd; /* dumpfile file descriptor */
|
|
|
|
FILE *ofp; /* fprintf(dd->ofp, "xxx"); */
|
|
|
|
int machine_type; /* machine type identifier */
|
|
|
|
+
|
|
|
|
+ /* header */
|
|
|
|
+ struct disk_dump_header *header;
|
|
|
|
+ struct disk_dump_sub_header *sub_header;
|
|
|
|
+
|
|
|
|
+ size_t data_offset;
|
|
|
|
+ int block_size;
|
|
|
|
+ int block_shift;
|
|
|
|
+ char *bitmap;
|
|
|
|
+ int bitmap_len;
|
|
|
|
+ char *dumpable_bitmap;
|
|
|
|
+ int byte, bit;
|
|
|
|
+ char *compressed_page; /* copy of compressed page data */
|
|
|
|
+ char *curbufptr; /* ptr to uncompressed page buffer */
|
|
|
|
+
|
|
|
|
+ /* page cache */
|
|
|
|
+ struct page_cache_hdr { /* header for each cached page */
|
|
|
|
+ uint32_t pg_flags;
|
|
|
|
+ uint64_t pg_addr;
|
|
|
|
+ char *pg_bufptr;
|
|
|
|
+ ulong pg_hit_count;
|
|
|
|
+ } page_cache_hdr[DISKDUMP_CACHED_PAGES];
|
|
|
|
+ char *page_cache_buf; /* base of cached buffer pages */
|
|
|
|
+ int evict_index; /* next page to evict */
|
|
|
|
+ ulong evictions; /* total evictions done */
|
|
|
|
+ ulong cached_reads;
|
|
|
|
+ ulong *valid_pages;
|
|
|
|
};
|
2005-02-10 18:39:42 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
static struct diskdump_data diskdump_data = { 0 };
|
|
|
|
static struct diskdump_data *dd = &diskdump_data;
|
2004-09-09 03:58:15 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
+static inline int get_bit(char *map, int byte, int bit)
|
|
|
|
+{
|
|
|
|
+ return map[byte] & (1<<bit);
|
|
|
|
+}
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+static inline int page_is_ram(unsigned int nr)
|
|
|
|
+{
|
|
|
|
+ return get_bit(dd->bitmap, nr >> 3, nr & 7);
|
2005-02-10 18:39:42 +00:00
|
|
|
+}
|
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+static inline int page_is_dumpable(unsigned int nr)
|
|
|
|
+{
|
|
|
|
+ return dd->dumpable_bitmap[nr>>3] & (1 << (nr & 7));
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static inline int dump_is_partial(const struct disk_dump_header *header)
|
|
|
|
+{
|
|
|
|
+ return header->bitmap_blocks >=
|
|
|
|
+ divideup(divideup(header->max_mapnr, 8), dd->block_size) * 2;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int open_dump_file(char *file)
|
2004-09-09 03:58:15 +00:00
|
|
|
+{
|
2006-01-04 19:34:13 +00:00
|
|
|
+ int fd;
|
2004-09-09 03:58:15 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ fd = open(file, O_RDONLY);
|
|
|
|
+ if (fd < 0) {
|
|
|
|
+ error(INFO, "diskdump: unable to open dump file %s", file);
|
2005-02-10 18:39:42 +00:00
|
|
|
+ return FALSE;
|
|
|
|
+ }
|
2006-01-04 19:34:13 +00:00
|
|
|
+ dd->dfd = fd;
|
|
|
|
+ return TRUE;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int read_dump_header(void)
|
|
|
|
+{
|
|
|
|
+ struct disk_dump_header *header = NULL;
|
|
|
|
+ struct disk_dump_sub_header *sub_header = NULL;
|
|
|
|
+ int bitmap_len;
|
|
|
|
+ const int block_size = (int)sysconf(_SC_PAGESIZE);
|
|
|
|
+ off_t offset;
|
|
|
|
+ const off_t failed = (off_t)-1;
|
|
|
|
+ ulong pfn;
|
|
|
|
+ int i, j, max_sect_len;
|
|
|
|
+
|
|
|
|
+ if (block_size < 0)
|
2005-02-10 18:39:42 +00:00
|
|
|
+ return FALSE;
|
2006-01-04 19:34:13 +00:00
|
|
|
+
|
|
|
|
+ header = malloc(block_size);
|
|
|
|
+
|
|
|
|
+ if (lseek(dd->dfd, 0, SEEK_SET) == failed) {
|
|
|
|
+ if (CRASHDEBUG(1))
|
|
|
|
+ error(INFO, "diskdump: cannot lseek dump header\n");
|
|
|
|
+ goto err;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (read(dd->dfd, header, block_size) < block_size) {
|
|
|
|
+ if (CRASHDEBUG(1))
|
|
|
|
+ error(INFO, "diskdump: cannot read dump header\n");
|
|
|
|
+ goto err;
|
2005-02-10 18:39:42 +00:00
|
|
|
+ }
|
2004-09-09 03:58:15 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ /* validate dump header */
|
|
|
|
+ if (memcmp(header->signature, DISK_DUMP_SIGNATURE,
|
|
|
|
+ sizeof(header->signature))) {
|
|
|
|
+ if (CRASHDEBUG(1))
|
|
|
|
+ error(INFO, "diskdump: dump does not have panic dump header\n");
|
|
|
|
+ goto err;
|
|
|
|
+ }
|
2004-09-09 03:58:15 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ if (header->block_size != block_size) {
|
|
|
|
+ error(INFO, "diskdump: block size in the dump header does not match"
|
|
|
|
+ " with system page size\n");
|
|
|
|
+ goto err;
|
|
|
|
+ }
|
|
|
|
+ dd->block_size = block_size;
|
|
|
|
+ dd->block_shift = ffs(block_size) - 1;
|
|
|
|
+
|
|
|
|
+ if (sizeof(*header) + sizeof(void *) * header->nr_cpus > block_size ||
|
|
|
|
+ header->nr_cpus <= 0) {
|
|
|
|
+ error(INFO, "diskdump: invalid nr_cpus value: %d\n", header->nr_cpus);
|
|
|
|
+ goto err;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* read sub header */
|
|
|
|
+ offset = (off_t)block_size;
|
|
|
|
+ if (lseek(dd->dfd, offset, SEEK_SET) == failed) {
|
|
|
|
+ error(INFO, "diskdump: cannot lseek dump sub header\n");
|
|
|
|
+ goto err;
|
|
|
|
+ }
|
|
|
|
+ sub_header = malloc(block_size);
|
|
|
|
+ if (read(dd->dfd, sub_header, block_size)
|
|
|
|
+ < block_size) {
|
|
|
|
+ error(INFO, "diskdump: cannot read dump sub header\n");
|
|
|
|
+ goto err;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ dd->sub_header = sub_header;
|
|
|
|
+
|
|
|
|
+ /* read memory bitmap */
|
|
|
|
+ bitmap_len = block_size * header->bitmap_blocks;
|
|
|
|
+ dd->bitmap_len = bitmap_len;
|
|
|
|
+
|
|
|
|
+ offset = (off_t)block_size * (1 + header->sub_hdr_size);
|
|
|
|
+ if (lseek(dd->dfd, offset, SEEK_SET) == failed) {
|
|
|
|
+ error(INFO, "diskdump: cannot lseek memory bitmap\n");
|
|
|
|
+ goto err;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ dd->bitmap = malloc(bitmap_len);
|
|
|
|
+ dd->dumpable_bitmap = calloc(bitmap_len, 1);
|
|
|
|
+ if (read(dd->dfd, dd->bitmap, bitmap_len) < bitmap_len) {
|
|
|
|
+ error(INFO, "diskdump: cannot read memory bitmap\n");
|
|
|
|
+ goto err;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (dump_is_partial(header))
|
|
|
|
+ memcpy(dd->dumpable_bitmap, dd->bitmap + bitmap_len/2,
|
|
|
|
+ bitmap_len/2);
|
|
|
|
+ else
|
|
|
|
+ memcpy(dd->dumpable_bitmap, dd->bitmap, bitmap_len);
|
|
|
|
+
|
|
|
|
+ dd->data_offset
|
|
|
|
+ = (1 + header->sub_hdr_size + header->bitmap_blocks)
|
|
|
|
+ * header->block_size;
|
|
|
|
+
|
|
|
|
+ dd->header = header;
|
|
|
|
+
|
|
|
|
+ if (machine_type("X86"))
|
|
|
|
+ dd->machine_type = EM_386;
|
|
|
|
+ else if (machine_type("X86_64"))
|
|
|
|
+ dd->machine_type = EM_X86_64;
|
|
|
|
+ else if (machine_type("IA64"))
|
|
|
|
+ dd->machine_type = EM_IA_64;
|
|
|
|
+ else if (machine_type("PPC64"))
|
|
|
|
+ dd->machine_type = EM_PPC64;
|
|
|
|
+ else {
|
|
|
|
+ error(INFO, "diskdump: unsupported machine type: %s\n", MACHINE_TYPE);
|
|
|
|
+ goto err;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ max_sect_len = divideup(header->max_mapnr, BITMAP_SECT_LEN);
|
|
|
|
+
|
|
|
|
+ dd->valid_pages = calloc(sizeof(ulong), max_sect_len + 1);
|
|
|
|
+ pfn = 0;
|
|
|
|
+ for (i = 1; i < max_sect_len + 1; i++) {
|
|
|
|
+ dd->valid_pages[i] = dd->valid_pages[i - 1];
|
|
|
|
+ for (j = 0; j < BITMAP_SECT_LEN; j++, pfn++)
|
|
|
|
+ if (page_is_dumpable(pfn))
|
|
|
|
+ dd->valid_pages[i]++;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return TRUE;
|
|
|
|
+
|
|
|
|
+err:
|
|
|
|
+ free(header);
|
|
|
|
+ if (sub_header)
|
|
|
|
+ free(sub_header);
|
|
|
|
+ if (dd->bitmap)
|
|
|
|
+ free(dd->bitmap);
|
|
|
|
+ if (dd->dumpable_bitmap)
|
|
|
|
+ free(dd->dumpable_bitmap);
|
|
|
|
+ return FALSE;
|
2005-02-10 18:39:42 +00:00
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int
|
2006-01-04 19:34:13 +00:00
|
|
|
+pfn_to_pos(ulong pfn)
|
2005-02-10 18:39:42 +00:00
|
|
|
+{
|
2006-01-04 19:34:13 +00:00
|
|
|
+ int desc_pos, j, valid;
|
|
|
|
+
|
|
|
|
+ valid = dd->valid_pages[pfn / BITMAP_SECT_LEN];
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ for (j = round(pfn, BITMAP_SECT_LEN), desc_pos = valid; j <= pfn; j++)
|
|
|
|
+ if (page_is_dumpable(j))
|
|
|
|
+ desc_pos++;
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ return desc_pos;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
/*
|
|
|
|
* Determine whether a file is a diskdump creation, and if TRUE,
|
|
|
|
* initialize the diskdump_data structure based upon the contents
|
|
|
|
@@ -43,6 +250,31 @@
|
|
|
|
int
|
|
|
|
is_diskdump(char *file)
|
|
|
|
{
|
|
|
|
+ int sz, i;
|
|
|
|
+
|
|
|
|
+ if (!open_dump_file(file) || !read_dump_header())
|
2005-02-10 18:39:42 +00:00
|
|
|
+ return FALSE;
|
2006-01-04 19:34:13 +00:00
|
|
|
+
|
|
|
|
+ sz = dd->block_size * (DISKDUMP_CACHED_PAGES);
|
|
|
|
+ if ((dd->page_cache_buf = malloc(sz)) == NULL)
|
|
|
|
+ return FALSE;
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < DISKDUMP_CACHED_PAGES; i++)
|
|
|
|
+ dd->page_cache_hdr[i].pg_bufptr =
|
|
|
|
+ &dd->page_cache_buf[i * dd->block_size];
|
|
|
|
+
|
|
|
|
+ if ((dd->compressed_page = (char *)malloc(dd->block_size)) == NULL)
|
|
|
|
+ goto err;
|
|
|
|
+
|
|
|
|
+ dd->flags |= DISKDUMP_LOCAL;
|
|
|
|
+
|
|
|
|
+ return TRUE;
|
|
|
|
+
|
|
|
|
+err:
|
|
|
|
+ if (dd->page_cache_buf)
|
|
|
|
+ free(dd->page_cache_buf);
|
|
|
|
+ if (dd->compressed_page)
|
|
|
|
+ free(dd->compressed_page);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
@@ -53,11 +285,123 @@
|
|
|
|
int
|
|
|
|
diskdump_init(char *unused, FILE *fptr)
|
|
|
|
{
|
|
|
|
- if (!DISKDUMP_VALID())
|
|
|
|
- return FALSE;
|
|
|
|
+ if (!DISKDUMP_VALID())
|
2005-02-10 18:39:42 +00:00
|
|
|
+ return FALSE;
|
2006-01-04 19:34:13 +00:00
|
|
|
|
|
|
|
- dd->ofp = fptr;
|
|
|
|
- return TRUE;
|
|
|
|
+ dd->ofp = fptr;
|
|
|
|
+ return TRUE;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Check whether paddr is already cached.
|
|
|
|
+ */
|
|
|
|
+static int
|
|
|
|
+page_is_cached(physaddr_t paddr)
|
|
|
|
+{
|
|
|
|
+ int i;
|
|
|
|
+ struct page_cache_hdr *pgc;
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < DISKDUMP_CACHED_PAGES; i++) {
|
|
|
|
+
|
|
|
|
+ pgc = &dd->page_cache_hdr[i];
|
|
|
|
+
|
|
|
|
+ if (!DISKDUMP_VALID_PAGE(pgc->pg_flags))
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ if (pgc->pg_addr == paddr) {
|
|
|
|
+ pgc->pg_hit_count++;
|
|
|
|
+ dd->curbufptr = pgc->pg_bufptr;
|
|
|
|
+ dd->cached_reads++;
|
|
|
|
+ return TRUE;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return FALSE;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Cache the page's data.
|
|
|
|
+ *
|
|
|
|
+ * If an empty page cache location is available, take it. Otherwise, evict
|
|
|
|
+ * the entry indexed by evict_index, and then bump evict index. The hit_count
|
|
|
|
+ * is only gathered for dump_diskdump_environment().
|
|
|
|
+ *
|
|
|
|
+ * If the page is compressed, uncompress it into the selected page cache entry.
|
|
|
|
+ * If the page is raw, just copy it into the selected page cache entry.
|
|
|
|
+ * If all works OK, update diskdump->curbufptr to point to the page's
|
|
|
|
+ * uncompressed data.
|
|
|
|
+ */
|
|
|
|
+static int
|
|
|
|
+cache_page(physaddr_t paddr)
|
|
|
|
+{
|
|
|
|
+ int i, ret;
|
|
|
|
+ int found;
|
|
|
|
+ ulong pfn;
|
|
|
|
+ int desc_pos;
|
|
|
|
+ off_t seek_offset;
|
|
|
|
+ page_desc_t pd;
|
|
|
|
+ const int block_size = dd->block_size;
|
|
|
|
+ const off_t failed = (off_t)-1;
|
|
|
|
+ ulong retlen;
|
|
|
|
+
|
|
|
|
+ for (i = found = 0; i < DISKDUMP_CACHED_PAGES; i++) {
|
|
|
|
+ if (DISKDUMP_VALID_PAGE(dd->page_cache_hdr[i].pg_flags))
|
|
|
|
+ continue;
|
|
|
|
+ found = TRUE;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!found) {
|
|
|
|
+ i = dd->evict_index;
|
|
|
|
+ dd->page_cache_hdr[i].pg_hit_count = 0;
|
|
|
|
+ dd->evict_index =
|
|
|
|
+ (dd->evict_index+1) % DISKDUMP_CACHED_PAGES;
|
|
|
|
+ dd->evictions++;
|
2004-09-09 03:58:15 +00:00
|
|
|
+ }
|
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ dd->page_cache_hdr[i].pg_flags = 0;
|
|
|
|
+ dd->page_cache_hdr[i].pg_addr = paddr;
|
|
|
|
+ dd->page_cache_hdr[i].pg_hit_count++;
|
|
|
|
+
|
|
|
|
+ /* find page descriptor */
|
|
|
|
+ pfn = paddr >> dd->block_shift;
|
|
|
|
+ desc_pos = pfn_to_pos(pfn);
|
|
|
|
+ seek_offset = dd->data_offset
|
|
|
|
+ + (off_t)(desc_pos - 1)*sizeof(page_desc_t);
|
|
|
|
+ lseek(dd->dfd, seek_offset, SEEK_SET);
|
|
|
|
+
|
|
|
|
+ /* read page descriptor */
|
|
|
|
+ if (read(dd->dfd, &pd, sizeof(pd)) != sizeof(pd))
|
|
|
|
+ return READ_ERROR;
|
|
|
|
+
|
|
|
|
+ /* sanity check */
|
|
|
|
+ if (pd.size > block_size)
|
|
|
|
+ return READ_ERROR;
|
|
|
|
+
|
|
|
|
+ if (lseek(dd->dfd, pd.offset, SEEK_SET) == failed)
|
|
|
|
+ return SEEK_ERROR;
|
|
|
|
+
|
|
|
|
+ /* read page data */
|
|
|
|
+ if (read(dd->dfd, dd->compressed_page, pd.size) != pd.size)
|
|
|
|
+ return READ_ERROR;
|
|
|
|
+
|
|
|
|
+ if (pd.flags & DUMP_DH_COMPRESSED) {
|
|
|
|
+ retlen = block_size;
|
|
|
|
+ ret = uncompress(dd->page_cache_hdr[i].pg_bufptr,
|
|
|
|
+ &retlen,
|
|
|
|
+ dd->compressed_page,
|
|
|
|
+ pd.size);
|
|
|
|
+ if ((ret != Z_OK) || (retlen != block_size)) {
|
|
|
|
+ error(INFO, "diskdump: uncompress failed: %d\n", ret);
|
|
|
|
+ return READ_ERROR;
|
|
|
|
+ }
|
|
|
|
+ } else
|
|
|
|
+ memcpy(dd->page_cache_hdr[i].pg_bufptr,
|
|
|
|
+ dd->compressed_page, block_size);
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+ dd->page_cache_hdr[i].pg_flags |= PAGE_VALID;
|
|
|
|
+ dd->curbufptr = dd->page_cache_hdr[i].pg_bufptr;
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
|
|
|
+ return TRUE;
|
2006-01-04 19:34:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
@@ -66,7 +410,28 @@
|
|
|
|
int
|
|
|
|
read_diskdump(int fd, void *bufptr, int cnt, ulong addr, physaddr_t paddr)
|
|
|
|
{
|
|
|
|
- return 0;
|
|
|
|
+ int ret;
|
|
|
|
+ physaddr_t curpaddr;
|
|
|
|
+ ulong pfn, page_offset;
|
|
|
|
+
|
|
|
|
+ pfn = paddr >> dd->block_shift;
|
|
|
|
+ curpaddr = paddr & ~((physaddr_t)(dd->block_size-1));
|
|
|
|
+ page_offset = paddr & ((physaddr_t)(dd->block_size-1));
|
|
|
|
+
|
|
|
|
+ if ((pfn >= dd->header->max_mapnr) || !page_is_ram(pfn))
|
|
|
|
+ return SEEK_ERROR;
|
|
|
|
+ if (!page_is_dumpable(pfn)) {
|
|
|
|
+ memset(bufptr, 0, cnt);
|
|
|
|
+ return cnt;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!page_is_cached(curpaddr))
|
|
|
|
+ if ((ret = cache_page(curpaddr)) < 0)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ memcpy(bufptr, dd->curbufptr + page_offset, cnt);
|
|
|
|
+
|
|
|
|
+ return cnt;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
@@ -81,7 +446,22 @@
|
|
|
|
ulong
|
|
|
|
get_diskdump_panic_task(void)
|
|
|
|
{
|
|
|
|
- return NO_TASK;
|
|
|
|
+ if (!DISKDUMP_VALID() || !get_active_set())
|
|
|
|
+ return NO_TASK;
|
|
|
|
+
|
|
|
|
+ return (ulong)dd->header->tasks[dd->header->current_cpu];
|
2004-09-09 03:58:15 +00:00
|
|
|
+}
|
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+extern void get_netdump_regs_x86(struct bt_info *, ulong *, ulong *);
|
|
|
|
+extern void get_netdump_regs_x86_64(struct bt_info *, ulong *, ulong *);
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+get_diskdump_regs_ppc64(struct bt_info *bt, ulong *eip, ulong *esp)
|
|
|
|
+{
|
|
|
|
+ if (bt->task == tt->panic_task)
|
|
|
|
+ bt->machdep = &dd->sub_header->elf_regs;
|
|
|
|
+
|
|
|
|
+ machdep->get_stack_frame(bt, eip, esp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
@@ -91,12 +471,32 @@
|
|
|
|
void
|
|
|
|
get_diskdump_regs(struct bt_info *bt, ulong *eip, ulong *esp)
|
|
|
|
{
|
|
|
|
- switch (dd->machine_type)
|
|
|
|
- {
|
|
|
|
- default:
|
|
|
|
- error(FATAL,
|
|
|
|
- "diskdump support for this machine type is not available\n");
|
|
|
|
- }
|
|
|
|
+ switch (dd->machine_type)
|
|
|
|
+ {
|
|
|
|
+ case EM_386:
|
|
|
|
+ return get_netdump_regs_x86(bt, eip, esp);
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case EM_IA_64:
|
|
|
|
+ /* For normal backtraces, this information will be obtained
|
|
|
|
+ * frome the switch_stack structure, which is pointed to by
|
|
|
|
+ * the thread.ksp field of the task_struct. But it's still
|
|
|
|
+ * needed by the "bt -t" option.
|
|
|
|
+ */
|
|
|
|
+ machdep->get_stack_frame(bt, eip, esp);
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case EM_PPC64:
|
|
|
|
+ return get_diskdump_regs_ppc64(bt, eip, esp);
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case EM_X86_64:
|
|
|
|
+ return get_netdump_regs_x86_64(bt, eip, esp);
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ default:
|
|
|
|
+ error(FATAL, "diskdump: unsupported machine type: %s\n", MACHINE_TYPE);
|
|
|
|
+ }
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
@@ -105,7 +505,10 @@
|
|
|
|
uint
|
|
|
|
diskdump_page_size(void)
|
2005-02-10 18:39:42 +00:00
|
|
|
{
|
2006-01-04 19:34:13 +00:00
|
|
|
- return 0;
|
|
|
|
+ if (!DISKDUMP_VALID())
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ return dd->header->block_size;
|
2005-02-10 18:39:42 +00:00
|
|
|
}
|
2006-01-04 19:34:13 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
--- crash/unwind.c.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/unwind.c 2005-11-23 09:37:52.000000000 -0500
|
|
|
|
@@ -1397,9 +1397,22 @@
|
|
|
|
req = &request;
|
|
|
|
|
|
|
|
if (get_symbol_type("unw", "tables", req) == TYPE_CODE_UNDEF) {
|
|
|
|
- error(WARNING, "cannot determine unw.tables offset\n");
|
|
|
|
- machdep->flags |= UNW_OUT_OF_SYNC;
|
|
|
|
- } else {
|
|
|
|
+ /*
|
|
|
|
+ * KLUDGE ALERT:
|
|
|
|
+ * If unw.tables cannot be ascertained by gdb, try unw.save_order,
|
|
|
|
+ * given that it is the field just after unw.tables.
|
|
|
|
+ */
|
|
|
|
+ if (get_symbol_type("unw", "save_order", req) == TYPE_CODE_UNDEF) {
|
|
|
|
+ error(WARNING, "cannot determine unw.tables offset\n");
|
|
|
|
+ machdep->flags |= UNW_OUT_OF_SYNC;
|
|
|
|
+ } else
|
|
|
|
+ req->member_offset -= BITS_PER_BYTE * sizeof(void *);
|
|
|
|
+
|
|
|
|
+ if (CRASHDEBUG(1))
|
|
|
|
+ error(WARNING, "using unw.save_order to determine unw.tables\n");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!(machdep->flags & UNW_OUT_OF_SYNC)) {
|
|
|
|
machdep->machspec->unw_tables_offset =
|
|
|
|
req->member_offset/BITS_PER_BYTE;
|
|
|
|
|
|
|
|
--- crash/defs.h.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/defs.h 2006-01-04 14:07:52.000000000 -0500
|
|
|
|
@@ -59,7 +59,7 @@
|
|
|
|
#define NR_CPUS (32)
|
2005-02-10 18:39:42 +00:00
|
|
|
#endif
|
2006-01-04 19:34:13 +00:00
|
|
|
#ifdef X86_64
|
|
|
|
-#define NR_CPUS (32)
|
|
|
|
+#define NR_CPUS (256)
|
2005-02-10 18:39:42 +00:00
|
|
|
#endif
|
2006-01-04 19:34:13 +00:00
|
|
|
#ifdef ALPHA
|
|
|
|
#define NR_CPUS (64)
|
|
|
|
@@ -155,8 +155,8 @@
|
|
|
|
#define UNLINK_MODULES (0x1000000000ULL)
|
|
|
|
#define S390D (0x2000000000ULL)
|
|
|
|
#define REM_S390D (0x4000000000ULL)
|
|
|
|
-#define PC_UNUSED_1 (0x8000000000ULL)
|
|
|
|
-#define PC_UNUSED_2 (0x10000000000ULL)
|
|
|
|
+#define SYSRQ (0x8000000000ULL)
|
|
|
|
+#define KDUMP (0x10000000000ULL)
|
|
|
|
#define NETDUMP (0x20000000000ULL)
|
|
|
|
#define REM_NETDUMP (0x40000000000ULL)
|
|
|
|
#define SYSMAP (0x80000000000ULL)
|
|
|
|
@@ -169,11 +169,12 @@
|
|
|
|
#define VERSION_QUERY (0x4000000000000ULL)
|
|
|
|
#define READNOW (0x8000000000000ULL)
|
|
|
|
#define NOCRASHRC (0x10000000000000ULL)
|
|
|
|
+#define INIT_IFILE (0x20000000000000ULL)
|
2004-09-09 03:58:15 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
#define ACTIVE() (pc->flags & LIVE_SYSTEM)
|
|
|
|
#define DUMPFILE() (!(pc->flags & LIVE_SYSTEM))
|
|
|
|
-#define MEMORY_SOURCES (NETDUMP|MCLXCD|LKCD|DEVMEM|S390D|MEMMOD|DISKDUMP)
|
|
|
|
-#define DUMPFILE_TYPES (DISKDUMP|NETDUMP|MCLXCD|LKCD|S390D)
|
|
|
|
+#define MEMORY_SOURCES (NETDUMP|KDUMP|MCLXCD|LKCD|DEVMEM|S390D|MEMMOD|DISKDUMP)
|
|
|
|
+#define DUMPFILE_TYPES (DISKDUMP|NETDUMP|KDUMP|MCLXCD|LKCD|S390D)
|
|
|
|
#define REMOTE() (pc->flags & REMOTE_DAEMON)
|
|
|
|
#define REMOTE_ACTIVE() (pc->flags & REM_LIVE_SYSTEM)
|
|
|
|
#define REMOTE_DUMPFILE() \
|
|
|
|
@@ -182,13 +183,21 @@
|
|
|
|
#define LKCD_DUMPFILE() (pc->flags & (LKCD|REM_LKCD))
|
|
|
|
#define NETDUMP_DUMPFILE() (pc->flags & (NETDUMP|REM_NETDUMP))
|
|
|
|
#define DISKDUMP_DUMPFILE() (pc->flags & DISKDUMP)
|
|
|
|
+#define KDUMP_DUMPFILE() (pc->flags & KDUMP)
|
|
|
|
+#define SYSRQ_TASK(X) ((pc->flags & SYSRQ) && is_task_active(X))
|
|
|
|
|
|
|
|
#define NETDUMP_LOCAL (0x1) /* netdump_data flags */
|
|
|
|
#define NETDUMP_REMOTE (0x2)
|
|
|
|
-#define NETDUMP_VALID() (nd->flags & (NETDUMP_LOCAL|NETDUMP_REMOTE))
|
|
|
|
+#define VMCORE_VALID() (nd->flags & (NETDUMP_LOCAL|NETDUMP_REMOTE|KDUMP_LOCAL))
|
|
|
|
#define NETDUMP_ELF32 (0x4)
|
|
|
|
#define NETDUMP_ELF64 (0x8)
|
|
|
|
#define PARTIAL_DUMP (0x10) /* netdump or diskdump */
|
|
|
|
+#define KDUMP_ELF32 (0x20)
|
|
|
|
+#define KDUMP_ELF64 (0x40)
|
|
|
|
+#define KDUMP_LOCAL (0x80)
|
|
|
|
+
|
|
|
|
+#define DUMPFILE_FORMAT(flags) ((flags) & \
|
|
|
|
+ (NETDUMP_ELF32|NETDUMP_ELF64|KDUMP_ELF32|KDUMP_ELF64))
|
|
|
|
|
|
|
|
#define DISKDUMP_LOCAL (0x1)
|
|
|
|
#define DISKDUMP_VALID() (dd->flags & DISKDUMP_LOCAL)
|
|
|
|
@@ -407,6 +416,7 @@
|
|
|
|
#define KALLSYMS_V2 (0x2000)
|
|
|
|
#define TVEC_BASES_V2 (0x4000)
|
|
|
|
#define GCC_3_3_3 (0x8000)
|
|
|
|
+#define USE_OLD_BT (0x10000)
|
|
|
|
|
|
|
|
#define GCC_VERSION_DEPRECATED (GCC_3_2|GCC_3_2_3|GCC_2_96|GCC_3_3_2|GCC_3_3_3)
|
|
|
|
|
|
|
|
@@ -660,13 +670,11 @@
|
|
|
|
* as defined in their processor-specific files below. (see KSYMS_START defs).
|
|
|
|
*/
|
|
|
|
#define HWRESET (0x80000000)
|
|
|
|
-#define SYSRQ (0x40000000)
|
|
|
|
-#define OMIT_FRAME_PTR (0x20000000)
|
|
|
|
-#define FRAMESIZE_DEBUG (0x10000000)
|
|
|
|
-#define MACHDEP_BT_TEXT (0x8000000)
|
|
|
|
-#define DEVMEMRD (0x4000000)
|
|
|
|
-#define INIT (0x2000000)
|
|
|
|
-#define SYSRQ_TASK(X) ((machdep->flags & SYSRQ) && is_task_active(X))
|
|
|
|
+#define OMIT_FRAME_PTR (0x40000000)
|
|
|
|
+#define FRAMESIZE_DEBUG (0x20000000)
|
|
|
|
+#define MACHDEP_BT_TEXT (0x10000000)
|
|
|
|
+#define DEVMEMRD (0x8000000)
|
|
|
|
+#define INIT (0x4000000)
|
2005-02-10 18:39:42 +00:00
|
|
|
|
|
|
|
extern struct machdep_table *machdep;
|
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
@@ -737,6 +745,7 @@
|
|
|
|
#define FOREACH_c_FLAG (0x40000)
|
|
|
|
#define FOREACH_f_FLAG (0x80000)
|
|
|
|
#define FOREACH_o_FLAG (0x100000)
|
|
|
|
+#define FOREACH_T_FLAG (0x200000)
|
|
|
|
|
|
|
|
struct foreach_data {
|
|
|
|
ulong flags;
|
|
|
|
@@ -875,6 +884,7 @@
|
|
|
|
long mm_struct_mmap;
|
|
|
|
long mm_struct_pgd;
|
|
|
|
long mm_struct_rss;
|
|
|
|
+ long mm_struct_anon_rss;
|
|
|
|
long mm_struct_total_vm;
|
|
|
|
long mm_struct_start_code;
|
|
|
|
long vm_area_struct_vm_mm;
|
|
|
|
@@ -970,6 +980,11 @@
|
|
|
|
long hw_interrupt_type_set_affinity;
|
|
|
|
long irq_cpustat_t___softirq_active;
|
|
|
|
long irq_cpustat_t___softirq_mask;
|
|
|
|
+ long fdtable_max_fds;
|
|
|
|
+ long fdtable_max_fdset;
|
|
|
|
+ long fdtable_open_fds;
|
|
|
|
+ long fdtable_fd;
|
|
|
|
+ long files_struct_fdt;
|
|
|
|
long files_struct_max_fds;
|
|
|
|
long files_struct_max_fdset;
|
|
|
|
long files_struct_open_fds;
|
|
|
|
@@ -1088,6 +1103,8 @@
|
|
|
|
long inet_opt_dport;
|
|
|
|
long inet_opt_sport;
|
|
|
|
long inet_opt_num;
|
|
|
|
+ long ipv6_pinfo_rcv_saddr;
|
|
|
|
+ long ipv6_pinfo_daddr;
|
|
|
|
long timer_list_list;
|
|
|
|
long timer_list_next;
|
|
|
|
long timer_list_entry;
|
|
|
|
@@ -1123,6 +1140,7 @@
|
|
|
|
long zone_struct_name;
|
|
|
|
long zone_struct_size;
|
|
|
|
long zone_struct_memsize;
|
|
|
|
+ long zone_struct_zone_start_pfn;
|
|
|
|
long zone_struct_zone_start_paddr;
|
|
|
|
long zone_struct_zone_start_mapnr;
|
|
|
|
long zone_struct_zone_mem_map;
|
|
|
|
@@ -1210,6 +1228,7 @@
|
|
|
|
long x8664_pda_irqstackptr;
|
|
|
|
long x8664_pda_level4_pgt;
|
|
|
|
long x8664_pda_cpunumber;
|
|
|
|
+ long x8664_pda_me;
|
|
|
|
long tss_struct_ist;
|
|
|
|
};
|
|
|
|
|
|
|
|
@@ -1239,6 +1258,7 @@
|
|
|
|
long umode_t;
|
|
|
|
long dentry;
|
|
|
|
long files_struct;
|
|
|
|
+ long fdtable;
|
|
|
|
long fs_struct;
|
|
|
|
long file;
|
|
|
|
long inode;
|
|
|
|
@@ -1292,6 +1312,7 @@
|
|
|
|
long address_space;
|
|
|
|
long char_device_struct;
|
|
|
|
long inet_sock;
|
|
|
|
+ long in6_addr;
|
|
|
|
long socket;
|
|
|
|
long spinlock_t;
|
|
|
|
long radix_tree_root;
|
|
|
|
@@ -1730,16 +1751,30 @@
|
|
|
|
#define _64BIT_
|
|
|
|
#define MACHINE_TYPE "X86_64"
|
|
|
|
|
|
|
|
-#define USERSPACE_TOP 0x0000008000000000
|
|
|
|
-#define __START_KERNEL_map 0xffffffff80000000
|
|
|
|
-#define PAGE_OFFSET 0x0000010000000000
|
|
|
|
+#define USERSPACE_TOP (machdep->machspec->userspace_top)
|
|
|
|
+#define PAGE_OFFSET (machdep->machspec->page_offset)
|
|
|
|
+#define VMALLOC_START (machdep->machspec->vmalloc_start_addr)
|
|
|
|
+#define VMALLOC_END (machdep->machspec->vmalloc_end)
|
|
|
|
+#define MODULES_VADDR (machdep->machspec->modules_vaddr)
|
|
|
|
+#define MODULES_END (machdep->machspec->modules_end)
|
2004-09-09 03:58:15 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
-#define VMALLOC_START 0xffffff0000000000
|
|
|
|
-#define VMALLOC_END 0xffffff7fffffffff
|
|
|
|
-#define MODULES_VADDR 0xffffffffa0000000
|
|
|
|
-#define MODULES_END 0xffffffffafffffff
|
|
|
|
+#define __START_KERNEL_map 0xffffffff80000000
|
|
|
|
#define MODULES_LEN (MODULES_END - MODULES_VADDR)
|
|
|
|
|
|
|
|
+#define USERSPACE_TOP_ORIG 0x0000008000000000
|
|
|
|
+#define PAGE_OFFSET_ORIG 0x0000010000000000
|
|
|
|
+#define VMALLOC_START_ADDR_ORIG 0xffffff0000000000
|
|
|
|
+#define VMALLOC_END_ORIG 0xffffff7fffffffff
|
|
|
|
+#define MODULES_VADDR_ORIG 0xffffffffa0000000
|
|
|
|
+#define MODULES_END_ORIG 0xffffffffafffffff
|
|
|
|
+
|
|
|
|
+#define USERSPACE_TOP_2_6_11 0x0000800000000000
|
|
|
|
+#define PAGE_OFFSET_2_6_11 0xffff810000000000
|
|
|
|
+#define VMALLOC_START_ADDR_2_6_11 0xffffc20000000000
|
|
|
|
+#define VMALLOC_END_2_6_11 0xffffe1ffffffffff
|
|
|
|
+#define MODULES_VADDR_2_6_11 0xffffffff88000000
|
|
|
|
+#define MODULES_END_2_6_11 0xfffffffffff00000
|
|
|
|
+
|
|
|
|
#define PTOV(X) ((unsigned long)(X)+(machdep->kvbase))
|
|
|
|
#define VTOP(X) x86_64_VTOP((ulong)(X))
|
|
|
|
#define IS_VMALLOC_ADDR(X) x86_64_IS_VMALLOC_ADDR((ulong)(X))
|
|
|
|
@@ -1763,6 +1798,15 @@
|
|
|
|
PAGESIZE(), "init_level4_pgt", FAULT_ON_ERROR); \
|
|
|
|
}
|
|
|
|
|
|
|
|
+#define IS_LAST_UPML_READ(pgd) ((ulong)(pml) == machdep->machspec->last_upml_read)
|
|
|
|
+
|
|
|
|
+#define FILL_UPML(PML, TYPE, SIZE) \
|
|
|
|
+ if (!IS_LAST_UPML_READ(PML)) { \
|
|
|
|
+ readmem((ulonglong)((ulong)(PML)), TYPE, machdep->machspec->upml, \
|
|
|
|
+ SIZE, "pml page", FAULT_ON_ERROR); \
|
|
|
|
+ machdep->machspec->last_upml_read = (ulong)(PML); \
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
/*
|
|
|
|
* PHYSICAL_PAGE_MASK changed (enlarged) between 2.4 and 2.6, so
|
|
|
|
* for safety, use the 2.6 values to generate it.
|
|
|
|
@@ -2067,6 +2111,32 @@
|
|
|
|
#define PGD_OFFSET(vaddr) ((vaddr >> PGDIR_SHIFT) & 0x7ff)
|
|
|
|
#define PMD_OFFSET(vaddr) ((vaddr >> PMD_SHIFT) & (PTRS_PER_PMD - 1))
|
|
|
|
|
|
|
|
+/* 4-level page table support */
|
|
|
|
+
|
|
|
|
+/* 4K pagesize */
|
|
|
|
+#define PTE_INDEX_SIZE_L4_4K 9
|
|
|
|
+#define PMD_INDEX_SIZE_L4_4K 7
|
|
|
|
+#define PUD_INDEX_SIZE_L4_4K 7
|
|
|
|
+#define PGD_INDEX_SIZE_L4_4K 9
|
|
|
|
+#define PTE_SHIFT_L4_4K 17
|
|
|
|
+#define PMD_MASKED_BITS_4K 0
|
|
|
|
+
|
|
|
|
+/* 64K pagesize */
|
|
|
|
+#define PTE_INDEX_SIZE_L4_64K 12
|
|
|
|
+#define PMD_INDEX_SIZE_L4_64K 12
|
|
|
|
+#define PUD_INDEX_SIZE_L4_64K 0
|
|
|
|
+#define PGD_INDEX_SIZE_L4_64K 4
|
|
|
|
+#define PTE_SHIFT_L4_64K 32
|
|
|
|
+#define PMD_MASKED_BITS_64K 0x1ff
|
|
|
|
+
|
|
|
|
+#define L4_OFFSET(vaddr) ((vaddr >> (machdep->machspec->l4_shift)) & 0x1ff)
|
|
|
|
+
|
|
|
|
+#define PGD_OFFSET_L4(vaddr) \
|
|
|
|
+ ((vaddr >> (machdep->machspec->l3_shift)) & (machdep->machspec->ptrs_per_l3 - 1))
|
|
|
|
+
|
|
|
|
+#define PMD_OFFSET_L4(vaddr) \
|
|
|
|
+ ((vaddr >> (machdep->machspec->l2_shift)) & (machdep->machspec->ptrs_per_l2 - 1))
|
|
|
|
+
|
|
|
|
#define _PAGE_PRESENT 0x001UL /* software: pte contains a translation */
|
|
|
|
#define _PAGE_USER 0x002UL /* matches one of the PP bits */
|
|
|
|
#define _PAGE_RW 0x004UL /* software: user write access allowed */
|
|
|
|
@@ -2807,6 +2877,7 @@
|
|
|
|
char *swap_location(ulonglong, char *);
|
|
|
|
void clear_swap_info_cache(void);
|
|
|
|
uint memory_page_size(void);
|
|
|
|
+void force_page_size(char *);
|
|
|
|
ulong first_vmalloc_address(void);
|
|
|
|
int l1_cache_size(void);
|
|
|
|
int dumpfile_memory(int);
|
|
|
|
@@ -3005,6 +3076,8 @@
|
|
|
|
*/
|
|
|
|
void register_extension(struct command_table_entry *);
|
|
|
|
void dump_extension_table(int);
|
|
|
|
+void load_extension(char *);
|
|
|
|
+void unload_extension(char *);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* kernel.c
|
|
|
|
@@ -3069,6 +3142,7 @@
|
|
|
|
#define BT_DUMPFILE_SEARCH (0x800000000ULL)
|
|
|
|
#define BT_EFRAME_SEARCH2 (0x1000000000ULL)
|
|
|
|
#define BT_START (0x2000000000ULL)
|
|
|
|
+#define BT_TEXT_SYMBOLS_ALL (0x4000000000ULL)
|
2004-09-09 03:58:15 +00:00
|
|
|
|
2005-02-10 18:39:42 +00:00
|
|
|
#define BT_REF_HEXVAL (0x1)
|
|
|
|
#define BT_REF_SYMBOL (0x2)
|
2006-01-04 19:34:13 +00:00
|
|
|
@@ -3194,7 +3268,15 @@
|
|
|
|
#define NMI_STACK 2 /* ebase[] offset to NMI exception stack */
|
2004-09-09 03:58:15 +00:00
|
|
|
|
2005-02-10 18:39:42 +00:00
|
|
|
struct machine_specific {
|
2006-01-04 19:34:13 +00:00
|
|
|
+ ulong userspace_top;
|
|
|
|
+ ulong page_offset;
|
|
|
|
+ ulong vmalloc_start_addr;
|
|
|
|
+ ulong vmalloc_end;
|
|
|
|
+ ulong modules_vaddr;
|
|
|
|
+ ulong modules_end;
|
2005-02-10 18:39:42 +00:00
|
|
|
char *pml4;
|
2006-01-04 19:34:13 +00:00
|
|
|
+ char *upml;
|
|
|
|
+ ulong last_upml_read;
|
2005-02-10 18:39:42 +00:00
|
|
|
char *irqstack;
|
2006-01-04 19:34:13 +00:00
|
|
|
struct x86_64_pt_regs_offsets pto;
|
|
|
|
struct x86_64_stkinfo stkinfo;
|
|
|
|
@@ -3202,6 +3284,8 @@
|
|
|
|
|
|
|
|
#define KSYMS_START (0x1)
|
|
|
|
#define PT_REGS_INIT (0x2)
|
|
|
|
+#define VM_ORIG (0x4)
|
|
|
|
+#define VM_2_6_11 (0x8)
|
2004-09-09 03:58:15 +00:00
|
|
|
|
2006-01-04 19:34:13 +00:00
|
|
|
#define _2MB_PAGE_MASK (~((MEGABYTES(2))-1))
|
|
|
|
#endif
|
|
|
|
@@ -3240,13 +3324,43 @@
|
|
|
|
ulong hwintrstack[NR_CPUS];
|
|
|
|
char *hwstackbuf;
|
|
|
|
uint hwstacksize;
|
|
|
|
-};
|
|
|
|
+ char *level4;
|
|
|
|
+ ulong last_level4_read;
|
|
|
|
+
|
|
|
|
+ uint l4_index_size;
|
|
|
|
+ uint l3_index_size;
|
|
|
|
+ uint l2_index_size;
|
|
|
|
+ uint l1_index_size;
|
|
|
|
+
|
|
|
|
+ uint ptrs_per_l3;
|
|
|
|
+ uint ptrs_per_l2;
|
|
|
|
+ uint ptrs_per_l1;
|
|
|
|
+
|
|
|
|
+ uint l4_shift;
|
|
|
|
+ uint l3_shift;
|
|
|
|
+ uint l2_shift;
|
|
|
|
+ uint l1_shift;
|
|
|
|
+
|
|
|
|
+ uint pte_shift;
|
|
|
|
+ uint l2_masked_bits;
|
|
|
|
+};
|
2005-02-10 18:39:42 +00:00
|
|
|
+
|
2006-01-04 19:34:13 +00:00
|
|
|
+#define IS_LAST_L4_READ(l4) ((ulong)(l4) == machdep->machspec->last_level4_read)
|
|
|
|
+
|
|
|
|
+#define FILL_L4(L4, TYPE, SIZE) \
|
|
|
|
+ if (!IS_LAST_L4_READ(L4)) { \
|
|
|
|
+ readmem((ulonglong)((ulong)(L4)), TYPE, machdep->machspec->level4, \
|
|
|
|
+ SIZE, "level4 page", FAULT_ON_ERROR); \
|
|
|
|
+ machdep->machspec->last_level4_read = (ulong)(L4); \
|
|
|
|
+ }
|
|
|
|
|
|
|
|
void ppc64_init(int);
|
|
|
|
void ppc64_dump_machdep_table(ulong);
|
|
|
|
#define display_idt_table() \
|
|
|
|
error(FATAL, "-d option is not applicable to PowerPC architecture\n")
|
|
|
|
#define KSYMS_START (0x1)
|
|
|
|
+#define VM_ORIG (0x2)
|
|
|
|
+#define VM_4_LEVEL (0x4)
|
|
|
|
#endif
|
|
|
|
|
2005-02-10 18:39:42 +00:00
|
|
|
/*
|
2006-01-04 19:34:13 +00:00
|
|
|
@@ -3396,10 +3510,23 @@
|
|
|
|
int netdump_init(char *, FILE *);
|
|
|
|
ulong get_netdump_panic_task(void);
|
|
|
|
ulong get_netdump_switch_stack(ulong);
|
|
|
|
-int netdump_memory_dump(FILE *);
|
|
|
|
FILE *set_netdump_fp(FILE *);
|
|
|
|
+int netdump_memory_dump(FILE *);
|
|
|
|
void get_netdump_regs(struct bt_info *, ulong *, ulong *);
|
|
|
|
int is_partial_netdump(void);
|
|
|
|
+void get_netdump_regs_x86(struct bt_info *, ulong *, ulong *);
|
|
|
|
+void get_netdump_regs_x86_64(struct bt_info *, ulong *, ulong *);
|
|
|
|
+
|
|
|
|
+int read_kdump(int, void *, int, ulong, physaddr_t);
|
|
|
|
+int write_kdump(int, void *, int, ulong, physaddr_t);
|
|
|
|
+int is_kdump(char *, ulong);
|
|
|
|
+int kdump_init(char *, FILE *);
|
|
|
|
+ulong get_kdump_panic_task(void);
|
|
|
|
+uint kdump_page_size(void);
|
|
|
|
+int kdump_free_memory(void);
|
|
|
|
+int kdump_memory_used(void);
|
|
|
|
+int kdump_memory_dump(FILE *);
|
|
|
|
+void get_kdump_regs(struct bt_info *, ulong *, ulong *);
|
2005-03-03 16:53:39 +00:00
|
|
|
|
|
|
|
/*
|
2006-01-04 19:34:13 +00:00
|
|
|
* diskdump.c
|
|
|
|
@@ -3560,6 +3687,7 @@
|
|
|
|
#define LKCD_DUMP_V7 (0x7) /* DUMP_VERSION_NUMBER */
|
|
|
|
#define LKCD_DUMP_V8 (0x8) /* DUMP_VERSION_NUMBER */
|
|
|
|
#define LKCD_DUMP_V9 (0x9) /* DUMP_VERSION_NUMBER */
|
|
|
|
+#define LKCD_DUMP_V10 (0xa) /* DUMP_VERSION_NUMBER */
|
|
|
|
|
|
|
|
#define LKCD_DUMP_VERSION_NUMBER_MASK (0xf)
|
|
|
|
#define LKCD_DUMP_RAW (0x1) /* DUMP_[DH_]RAW */
|
|
|
|
--- crash/netdump.h.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/netdump.h 2005-11-04 17:37:53.000000000 -0500
|
|
|
|
@@ -24,3 +24,14 @@
|
|
|
|
|
|
|
|
#define NT_TASKSTRUCT 4
|
|
|
|
#define NT_DISKDUMP 0x70000001
|
|
|
|
+
|
|
|
|
+#ifdef NOTDEF
|
|
|
|
+/*
|
|
|
|
+ * Note: Based upon the original, abandoned, proposal for
|
|
|
|
+ * its contents -- keep around for potential future use.
|
|
|
|
+ */
|
|
|
|
+#ifndef NT_KDUMPINFO
|
|
|
|
+#define NT_KDUMPINFO 7
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+#endif /* NOTDEF */
|
|
|
|
--- crash/diskdump.h.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/diskdump.h 2005-11-30 15:15:00.000000000 -0500
|
|
|
|
@@ -3,6 +3,8 @@
|
|
|
|
*
|
|
|
|
* Copyright (C) 2004, 2005 David Anderson
|
|
|
|
* Copyright (C) 2004, 2005 Red Hat, Inc. All rights reserved.
|
|
|
|
+ * Copyright (C) 2005 FUJITSU LIMITED
|
|
|
|
+ * Copyright (C) 2005 NEC Corporation
|
|
|
|
*
|
|
|
|
* This software may be freely redistributed under the terms of the
|
|
|
|
* GNU General Public License.
|
|
|
|
@@ -10,7 +12,59 @@
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
- *
|
|
|
|
- * Author: David Anderson
|
|
|
|
*/
|
|
|
|
|
|
|
|
+#include <elf.h>
|
|
|
|
+
|
|
|
|
+#define divideup(x, y) (((x) + ((y) - 1)) / (y))
|
|
|
|
+#define round(x, y) (((x) / (y)) * (y))
|
|
|
|
+
|
|
|
|
+#define DUMP_PARTITION_SIGNATURE "diskdump"
|
|
|
|
+#define SIG_LEN (sizeof(DUMP_PARTITION_SIGNATURE) - 1)
|
|
|
|
+#define DISK_DUMP_SIGNATURE "DISKDUMP"
|
|
|
|
+
|
|
|
|
+#define DUMP_HEADER_COMPLETED 0
|
|
|
|
+#define DUMP_HEADER_INCOMPLETED 1
|
|
|
|
+#define DUMP_HEADER_COMPRESSED 8
|
|
|
|
+
|
|
|
|
+struct disk_dump_header {
|
|
|
|
+ char signature[SIG_LEN]; /* = "DISKDUMP" */
|
|
|
|
+ int header_version; /* Dump header version */
|
|
|
|
+ struct new_utsname utsname; /* copy of system_utsname */
|
|
|
|
+ struct timeval timestamp; /* Time stamp */
|
|
|
|
+ unsigned int status; /* Above flags */
|
|
|
|
+ int block_size; /* Size of a block in byte */
|
|
|
|
+ int sub_hdr_size; /* Size of arch dependent
|
|
|
|
+ header in blocks */
|
|
|
|
+ unsigned int bitmap_blocks; /* Size of Memory bitmap in
|
|
|
|
+ block */
|
|
|
|
+ unsigned int max_mapnr; /* = max_mapnr */
|
|
|
|
+ unsigned int total_ram_blocks;/* Number of blocks should be
|
|
|
|
+ written */
|
|
|
|
+ unsigned int device_blocks; /* Number of total blocks in
|
|
|
|
+ * the dump device */
|
|
|
|
+ unsigned int written_blocks; /* Number of written blocks */
|
|
|
|
+ unsigned int current_cpu; /* CPU# which handles dump */
|
|
|
|
+ int nr_cpus; /* Number of CPUs */
|
|
|
|
+ struct task_struct *tasks[0];
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+struct disk_dump_sub_header {
|
|
|
|
+ long elf_regs;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/* page flags */
|
|
|
|
+#define DUMP_DH_COMPRESSED 0x1 /* page is compressed */
|
|
|
|
+
|
|
|
|
+/* descriptor of each page for vmcore */
|
|
|
|
+typedef struct page_desc {
|
|
|
|
+ off_t offset; /* the offset of the page data*/
|
|
|
|
+ unsigned int size; /* the size of this dump page */
|
|
|
|
+ unsigned int flags; /* flags */
|
|
|
|
+ unsigned long long page_flags; /* page flags */
|
|
|
|
+} page_desc_t;
|
|
|
|
+
|
|
|
|
+#define DISKDUMP_CACHED_PAGES (16)
|
|
|
|
+#define PAGE_VALID (0x1) /* flags */
|
|
|
|
+#define DISKDUMP_VALID_PAGE(flags) ((flags) & PAGE_VALID)
|
|
|
|
+
|
|
|
|
--- crash/Makefile.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/Makefile 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
@@ -39,6 +39,8 @@
|
|
|
|
GDB_FILES=
|
|
|
|
GDB_OFILES=
|
|
|
|
|
|
|
|
+GDB_PATCH_FILES=gdb-6.1.patch
|
|
|
|
+
|
|
|
|
#
|
|
|
|
# Default installation directory
|
|
|
|
#
|
|
|
|
@@ -89,6 +91,15 @@
|
|
|
|
lkcd_fix_mem.o s390_dump.o netdump.o diskdump.o \
|
|
|
|
lkcd_x86_trace.o unwind_v1.o unwind_v2.o unwind_v3.o
|
|
|
|
|
|
|
|
+# These are the current set of crash extensions sources. They are not built
|
|
|
|
+# by default unless the third command line of the "all:" stanza is uncommented.
|
|
|
|
+# Alternatively, they can be built by entering "make extensions" from this
|
|
|
|
+# directory.
|
|
|
|
+
|
|
|
|
+EXTENSIONS=extensions
|
|
|
|
+EXTENSION_SOURCE_FILES=${EXTENSIONS}/Makefile ${EXTENSIONS}/echo.c ${EXTENSIONS}/dminfo.c
|
|
|
|
+EXTENSION_OBJECT_FILES=echo.so dminfo.so
|
|
|
|
+
|
|
|
|
DAEMON_OBJECT_FILES=remote_daemon.o va_server.o va_server_v1.o \
|
|
|
|
lkcd_common.o lkcd_v1.o lkcd_v2_v3.o lkcd_v5.o lkcd_v7.o lkcd_v8.o \
|
|
|
|
s390_dump.o netdump_daemon.o
|
|
|
|
@@ -150,10 +161,11 @@
|
2005-02-10 18:39:42 +00:00
|
|
|
${GDB}/gdb/main.c ${GDB}/gdb/symtab.c ${GDB}/gdb/target.c \
|
|
|
|
${GDB}/gdb/symfile.c ${GDB}/gdb/elfread.c \
|
2006-01-04 19:34:13 +00:00
|
|
|
${GDB}/gdb/ui-file.c ${GDB}/gdb/utils.c ${GDB}/gdb/dwarf2read.c \
|
|
|
|
- ${GDB}/include/obstack.h
|
|
|
|
+ ${GDB}/include/obstack.h ${GDB}/gdb/ppc-linux-tdep.c
|
2005-02-10 18:39:42 +00:00
|
|
|
GDB_6.1_OFILES=${GDB}/gdb/main.o ${GDB}/gdb/symtab.o \
|
|
|
|
${GDB}/gdb/target.o ${GDB}/gdb/symfile.o ${GDB}/gdb/elfread.o \
|
2006-01-04 19:34:13 +00:00
|
|
|
- ${GDB}/gdb/ui-file.o ${GDB}/gdb/utils.o ${GDB}/gdb/dwarf2read.o
|
|
|
|
+ ${GDB}/gdb/ui-file.o ${GDB}/gdb/utils.o ${GDB}/gdb/dwarf2read.o \
|
|
|
|
+ ${GDB}/gdb/ppc-linux-tdep.o
|
2005-02-10 18:39:42 +00:00
|
|
|
|
|
|
|
#
|
|
|
|
# GDB_FLAGS is passed up from the gdb Makefile.
|
2006-01-04 19:34:13 +00:00
|
|
|
@@ -175,7 +187,8 @@
|
|
|
|
|
|
|
|
CFLAGS=-g -D${TARGET} ${TARGET_CFLAGS}
|
|
|
|
|
|
|
|
-TAR_FILES=${SOURCE_FILES} Makefile COPYING README .rh_rpm_package crash.8
|
|
|
|
+TAR_FILES=${SOURCE_FILES} Makefile COPYING README .rh_rpm_package crash.8 \
|
|
|
|
+ ${EXTENSION_SOURCE_FILES}
|
|
|
|
CSCOPE_FILES=${SOURCE_FILES}
|
|
|
|
|
|
|
|
READLINE_DIRECTORY=./${GDB}/readline
|
|
|
|
@@ -184,9 +197,13 @@
|
|
|
|
|
|
|
|
REDHATFLAGS=-DREDHAT
|
|
|
|
|
|
|
|
+# To build the extensions library by default, uncomment the third command
|
|
|
|
+# line below. Otherwise they can be built by entering "make extensions".
|
|
|
|
+
|
|
|
|
all: make_configure
|
|
|
|
@./configure -p "RPMPKG=${RPMPKG}" -b
|
|
|
|
@make --no-print-directory gdb_merge
|
|
|
|
+# @make --no-print-directory extensions
|
|
|
|
|
|
|
|
gdb_merge: force
|
|
|
|
@if [ ! -f ${GDB}/README ]; then \
|
|
|
|
@@ -206,6 +223,11 @@
|
|
|
|
@for FILE in ${GDB_FILES}; do\
|
|
|
|
echo $$FILE >> gdb.files; done
|
|
|
|
@tar --exclude-from gdb.files -xvzmf ${GDB}.tar.gz
|
|
|
|
+ @make --no-print-directory gdb_patch
|
|
|
|
+
|
|
|
|
+gdb_patch:
|
|
|
|
+ if [ -f ${GDB}.patch ] && [ -s ${GDB}.patch ]; then \
|
|
|
|
+ patch -p0 < ${GDB}.patch; fi
|
|
|
|
|
|
|
|
library: make_build_data ${OBJECT_FILES}
|
|
|
|
ar -rs ${PROGRAM}lib.a ${OBJECT_FILES}
|
|
|
|
@@ -393,13 +415,13 @@
|
|
|
|
|
|
|
|
gdb_files: make_configure
|
|
|
|
@./configure -q -b
|
|
|
|
- @echo ${GDB_FILES}
|
|
|
|
+ @echo ${GDB_FILES} ${GDB_PATCH_FILES}
|
|
|
|
|
|
|
|
show_files:
|
|
|
|
@if [ -f ${PROGRAM} ]; then \
|
|
|
|
./${PROGRAM} --no_crashrc -h README > README; fi
|
|
|
|
- @echo ${SOURCE_FILES} Makefile ${GDB_FILES} COPYING README \
|
|
|
|
- .rh_rpm_package crash.8
|
|
|
|
+ @echo ${SOURCE_FILES} Makefile ${GDB_FILES} ${GDB_PATCH_FILES} COPYING README \
|
|
|
|
+ .rh_rpm_package crash.8 ${EXTENSION_SOURCE_FILES}
|
|
|
|
|
|
|
|
ctags:
|
|
|
|
ctags ${SOURCE_FILES}
|
|
|
|
@@ -411,7 +433,7 @@
|
|
|
|
do_tar:
|
|
|
|
@if [ -f ${PROGRAM} ]; then \
|
|
|
|
./${PROGRAM} --no_crashrc -h README > README; fi
|
|
|
|
- tar cvzf ${PROGRAM}.tar.gz ${TAR_FILES} ${GDB_FILES}
|
|
|
|
+ tar cvzf ${PROGRAM}.tar.gz ${TAR_FILES} ${GDB_FILES} ${GDB_PATCH_FILES}
|
|
|
|
@echo; ls -l ${PROGRAM}.tar.gz
|
|
|
|
|
|
|
|
# To create a base tar file for Red Hat RPM packaging, pass the base RPM
|
|
|
|
@@ -446,8 +468,8 @@
|
|
|
|
@rm -f ${PROGRAM}-${RELEASE}.tar.gz
|
|
|
|
@rm -f ${PROGRAM}-${RELEASE}.src.rpm
|
|
|
|
@chown root ./RELDIR/${PROGRAM}-${RELEASE}
|
|
|
|
- @tar cf - ${SOURCE_FILES} Makefile ${GDB_FILES} COPYING \
|
|
|
|
- .rh_rpm_package crash.8 | (cd ./RELDIR/${PROGRAM}-${RELEASE}; tar xf -)
|
|
|
|
+ @tar cf - ${SOURCE_FILES} Makefile ${GDB_FILES} ${GDB_PATCH_FILES} COPYING \
|
|
|
|
+ .rh_rpm_package crash.8 ${EXTENSION_SOURCE_FILES} | (cd ./RELDIR/${PROGRAM}-${RELEASE}; tar xf -)
|
|
|
|
@cp ${GDB}.tar.gz ./RELDIR/${PROGRAM}-${RELEASE}
|
|
|
|
@./${PROGRAM} --no_crashrc -h README > ./RELDIR/${PROGRAM}-${RELEASE}/README
|
|
|
|
@(cd ./RELDIR; find . -exec chown root {} ";")
|
|
|
|
@@ -488,3 +510,10 @@
|
|
|
|
|
|
|
|
dis:
|
|
|
|
objdump --disassemble --line-numbers ${PROGRAM} > ${PROGRAM}.dis
|
|
|
|
+
|
|
|
|
+extensions: make_configure
|
|
|
|
+ @./configure -q -b
|
|
|
|
+ @make --no-print-directory do_extensions
|
|
|
|
+
|
|
|
|
+do_extensions:
|
|
|
|
+ @(cd extensions; make -i OBJECTS="$(EXTENSION_OBJECT_FILES)" TARGET=$(TARGET))
|
|
|
|
--- crash/gdb-6.1.patch.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/gdb-6.1.patch 2006-01-03 13:36:25.000000000 -0500
|
|
|
|
@@ -0,0 +1,11 @@
|
|
|
|
+--- gdb-6.1/bfd/coff-alpha.c.orig
|
|
|
|
++++ gdb-6.1/bfd/coff-alpha.c
|
|
|
|
+@@ -1455,7 +1455,7 @@ alpha_relocate_section (output_bfd, info
|
|
|
|
+ amt = sizeof (struct ecoff_section_tdata);
|
|
|
|
+ lita_sec_data = ((struct ecoff_section_tdata *)
|
|
|
|
+ bfd_zalloc (input_bfd, amt));
|
|
|
|
+- ecoff_section_data (input_bfd, lita_sec) = lita_sec_data;
|
|
|
|
++ lita_sec->used_by_bfd = lita_sec_data;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (lita_sec_data->gp != 0)
|
|
|
|
--- crash/README.orig 2006-01-04 14:18:28.000000000 -0500
|
|
|
|
+++ crash/README 2006-01-04 14:18:26.000000000 -0500
|
|
|
|
@@ -69,7 +69,7 @@
|
|
|
|
After the kernel is re-compiled, the uncompressed "vmlinux" kernel
|
|
|
|
that is created in the top-level kernel build directory must be saved.
|
|
|
|
|
|
|
|
- To build this utility, simply uncompress the tar file, enter the crash-4.0
|
|
|
|
+ To build this utility, simply uncompress the tar file, enter the crash-4.0-2.18
|
|
|
|
subdirectory, and type "make". The initial build will take several minutes
|
|
|
|
because the gdb module must be configured and and built. Alternatively, the
|
|
|
|
crash source RPM file may be installed and built, and the resultant crash
|
|
|
|
@@ -89,10 +89,12 @@
|
|
|
|
|
|
|
|
$ crash
|
|
|
|
|
|
|
|
- crash 4.0
|
|
|
|
- Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc.
|
|
|
|
- Copyright (C) 2004, 2005 IBM Corporation
|
|
|
|
- Copyright (C) 1999-2005 Hewlett-Packard Co
|
|
|
|
+ crash 4.0-2.18
|
|
|
|
+ Copyright (C) 2002, 2003, 2004, 2005, 2006 Red Hat, Inc.
|
|
|
|
+ Copyright (C) 2004, 2005, 2006 IBM Corporation
|
|
|
|
+ Copyright (C) 1999-2006 Hewlett-Packard Co
|
|
|
|
+ Copyright (C) 2005 Fujitsu Limited
|
|
|
|
+ Copyright (C) 2005 NEC Corporation
|
|
|
|
Copyright (C) 1999, 2002 Silicon Graphics, Inc.
|
|
|
|
Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc.
|
|
|
|
This program is free software, covered by the GNU General Public License,
|
|
|
|
@@ -111,7 +113,7 @@
|
|
|
|
KERNEL: /boot/vmlinux
|
|
|
|
DUMPFILE: /dev/mem
|
|
|
|
CPUS: 1
|
|
|
|
- DATE: Wed Jul 13 13:26:00 2005
|
|
|
|
+ DATE: Wed Jan 4 14:18:26 2006
|
|
|
|
UPTIME: 10 days, 22:55:18
|
|
|
|
LOAD AVERAGE: 0.08, 0.03, 0.01
|
|
|
|
TASKS: 42
|
|
|
|
@@ -139,7 +141,7 @@
|
|
|
|
exit log rd task
|
|
|
|
extend mach repeat timer
|
|
|
|
|
|
|
|
- crash version: 4.0 gdb version: 6.1
|
|
|
|
+ crash version: 4.0-2.18 gdb version: 6.1
|
|
|
|
For help on any command above, enter "help <command>".
|
|
|
|
For help on input options, enter "help input".
|
|
|
|
For help on output options, enter "help output".
|
|
|
|
@@ -152,10 +154,12 @@
|
|
|
|
|
|
|
|
$ crash vmlinux vmcore
|
|
|
|
|
|
|
|
- crash 4.0
|
|
|
|
- Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc.
|
|
|
|
- Copyright (C) 2004, 2005 IBM Corporation
|
|
|
|
- Copyright (C) 1999-2005 Hewlett-Packard Co
|
|
|
|
+ crash 4.0-2.18
|
|
|
|
+ Copyright (C) 2002, 2003, 2004, 2005, 2006 Red Hat, Inc.
|
|
|
|
+ Copyright (C) 2004, 2005, 2006 IBM Corporation
|
|
|
|
+ Copyright (C) 1999-2006 Hewlett-Packard Co
|
|
|
|
+ Copyright (C) 2005 Fujitsu Limited
|
|
|
|
+ Copyright (C) 2005 NEC Corporation
|
|
|
|
Copyright (C) 1999, 2002 Silicon Graphics, Inc.
|
|
|
|
Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc.
|
|
|
|
This program is free software, covered by the GNU General Public License,
|
|
|
|
@@ -196,10 +200,12 @@
|
|
|
|
|
|
|
|
$ crash vmlinux.17 lcore.cr.17
|
|
|
|
|
|
|
|
- crash 4.0
|
|
|
|
- Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc.
|
|
|
|
- Copyright (C) 2004, 2005 IBM Corporation
|
|
|
|
- Copyright (C) 1999-2005 Hewlett-Packard Co
|
|
|
|
+ crash 4.0-2.18
|
|
|
|
+ Copyright (C) 2002, 2003, 2004, 2005, 2006 Red Hat, Inc.
|
|
|
|
+ Copyright (C) 2004, 2005, 2006 IBM Corporation
|
|
|
|
+ Copyright (C) 1999-2006 Hewlett-Packard Co
|
|
|
|
+ Copyright (C) 2005 Fujitsu Limited
|
|
|
|
+ Copyright (C) 2005 NEC Corporation
|
|
|
|
Copyright (C) 1999, 2002 Silicon Graphics, Inc.
|
|
|
|
Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc.
|
|
|
|
This program is free software, covered by the GNU General Public License,
|