kern, arm: Dump dmesg on kernel panic/oops
authorAndreas Sandberg <andreas.sandberg@arm.com>
Mon, 20 Jun 2016 13:39:49 +0000 (14:39 +0100)
committerAndreas Sandberg <andreas.sandberg@arm.com>
Mon, 20 Jun 2016 13:39:49 +0000 (14:39 +0100)
Add helper functions to dump the guest kernel's dmesg buffer to a text
file in m5out. This functionality is split into two parts. First, a
dmesg dump function that can be used in other places:

void Linux::dumpDmesg(ThreadContext *, std::ostream &)

This function is used to implement two PCEvents: DmesgDumpEvent and
KernelPanic event. The only difference between the two is that the
latter produces a gem5 panic instead of a warning in addition to
dumping the kernel log.

Change-Id: I6d2af1d666ace57124089648ea906f6c787ac63c
Signed-off-by: Andreas Sandberg <andreas.sandberg@arm.com>
Reviewed-by: Nikos Nikoleris <nikos.nikoleris@arm.com>
Reviewed-by: Gabor Dozsa <gabor.dozsa@arm.com>
src/arch/arm/ArmSystem.py
src/arch/arm/linux/system.cc
src/arch/arm/linux/system.hh
src/kern/SConscript
src/kern/linux/events.cc
src/kern/linux/events.hh
src/kern/linux/helpers.cc [new file with mode: 0644]
src/kern/linux/helpers.hh [new file with mode: 0644]

index adbf2093e55ff3d5f387dca753421c443a2c9c34..ac04a56e4179daea20cc4a98a6753c586c4204b3 100644 (file)
@@ -98,6 +98,14 @@ class LinuxArmSystem(GenericArmSystem):
     type = 'LinuxArmSystem'
     cxx_header = "arch/arm/linux/system.hh"
 
+    @classmethod
+    def export_method_cxx_predecls(cls, code):
+        code('#include "arch/arm/linux/system.hh"')
+
+    @classmethod
+    def export_methods(cls, code):
+        code('''void dumpDmesg();''')
+
 class FreebsdArmSystem(GenericArmSystem):
     type = 'FreebsdArmSystem'
     cxx_header = "arch/arm/freebsd/system.hh"
index a8eed49f1325a0b43c40f54b439a986f10f541a8..311d81a37b2bed5179d26b06452a6fe545f2fbfa 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2013 ARM Limited
+ * Copyright (c) 2010-2013, 2016 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -53,6 +53,7 @@
 #include "cpu/thread_context.hh"
 #include "debug/Loader.hh"
 #include "kern/linux/events.hh"
+#include "kern/linux/helpers.hh"
 #include "mem/fs_translating_port_proxy.hh"
 #include "mem/physical.hh"
 #include "sim/stat_control.hh"
@@ -65,14 +66,21 @@ LinuxArmSystem::LinuxArmSystem(Params *p)
       enableContextSwitchStatsDump(p->enable_context_switch_stats_dump),
       taskFile(nullptr), kernelPanicEvent(nullptr), kernelOopsEvent(nullptr)
 {
+    const std::string dmesg_output = name() + ".dmesg";
     if (p->panic_on_panic) {
-        kernelPanicEvent = addKernelFuncEventOrPanic<PanicPCEvent>(
-            "panic", "Kernel panic in simulated kernel");
+        kernelPanicEvent = addKernelFuncEventOrPanic<Linux::KernelPanicEvent>(
+            "panic", "Kernel panic in simulated kernel", dmesg_output);
+    } else {
+        kernelPanicEvent = addKernelFuncEventOrPanic<Linux::DmesgDumpEvent>(
+            "panic", "Kernel panic in simulated kernel", dmesg_output);
     }
 
     if (p->panic_on_oops) {
-        kernelOopsEvent = addKernelFuncEventOrPanic<PanicPCEvent>(
-            "oops_exit", "Kernel oops in guest");
+        kernelOopsEvent = addKernelFuncEventOrPanic<Linux::KernelPanicEvent>(
+            "oops_exit", "Kernel oops in guest", dmesg_output);
+    } else {
+        kernelOopsEvent = addKernelFuncEventOrPanic<Linux::DmesgDumpEvent>(
+            "oops_exit", "Kernel oops in guest", dmesg_output);
     }
 
     // With ARM udelay() is #defined to __udelay
@@ -261,6 +269,12 @@ LinuxArmSystem::mapPid(ThreadContext *tc, uint32_t pid)
     }
 }
 
+void
+LinuxArmSystem::dumpDmesg()
+{
+    Linux::dumpDmesg(getThreadContext(0), std::cout);
+}
+
 /** This function is called whenever the the kernel function
  *  "__switch_to" is called to change running tasks.
  *
index ce1d84b6bda9f1efff2abf829ec88f2727b63060..709776ffcecdc9aaedfefb89823f003ce99e763d 100644 (file)
@@ -95,6 +95,12 @@ class LinuxArmSystem : public GenericArmSystem
      * @param tc thread context that is currentyl executing  */
     void mapPid(ThreadContext* tc, uint32_t pid);
 
+  public: // Exported Python methods
+    /**
+     * Dump the kernel's dmesg buffer to stdout
+     */
+    void dumpDmesg();
+
   private:
     /** Event to halt the simulator if the kernel calls panic()  */
     PCEvent *kernelPanicEvent;
index 7bcf542130b2c473e5bb478f4d0e5fefb6823272..305cf63818fc1aadbded00dd52035ca4f73d3ecc 100644 (file)
@@ -36,6 +36,7 @@ if env['TARGET_ISA'] == 'null':
 Source('kernel_stats.cc')
 Source('linux/events.cc')
 Source('linux/linux.cc')
+Source('linux/helpers.cc')
 Source('linux/printk.cc')
 Source('freebsd/events.cc')
 Source('operatingsystem.cc')
index 42f058a72aec186586dbbfe9427720157e2da358..f4e694436ac8980ed4836b7f1db1c5d6731d918b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 ARM Limited
+ * Copyright (c) 2011, 2016 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
  *          Ali Saidi
  */
 
+#include "kern/linux/events.hh"
+
 #include <sstream>
 
 #include "arch/utility.hh"
+#include "base/output.hh"
 #include "base/trace.hh"
+#include "cpu/base.hh"
 #include "cpu/thread_context.hh"
 #include "debug/DebugPrintf.hh"
-#include "kern/linux/events.hh"
+#include "kern/linux/helpers.hh"
 #include "kern/linux/printk.hh"
 #include "kern/system_events.hh"
 #include "sim/arguments.hh"
@@ -94,5 +98,30 @@ UDelayEvent::process(ThreadContext *tc)
     }
 }
 
+void
+DmesgDumpEvent::process(ThreadContext *tc)
+{
+    StringWrap name(tc->getCpuPtr()->name() + ".dmesg_dump_event");
+
+    inform("Dumping kernel dmesg buffer to %s...\n", fname);
+    OutputStream *os = simout.create(fname);
+    dumpDmesg(tc, *os->stream());
+    simout.close(os);
+
+    warn(descr());
+}
+
+void
+KernelPanicEvent::process(ThreadContext *tc)
+{
+    StringWrap name(tc->getCpuPtr()->name() + ".dmesg_dump_event");
+
+    inform("Dumping kernel dmesg buffer to %s...\n", fname);
+    OutputStream *os = simout.create(fname);
+    dumpDmesg(tc, *os->stream());
+    simout.close(os);
+
+    panic(descr());
+}
 
 } // namespace linux
index 3f5f2526f422c8f7272419713cd831da8274d16e..b8aad9d79e78574b6fa45ae17de620bfb6c3df04 100644 (file)
@@ -1,4 +1,16 @@
 /*
+ * Copyright (c) 2016 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
  * Copyright (c) 2004-2006 The Regents of The University of Michigan
  * All rights reserved.
  *
@@ -44,6 +56,46 @@ class DebugPrintkEvent : public SkipFuncEvent
     virtual void process(ThreadContext *xc);
 };
 
+/**
+ * Dump the guest kernel's dmesg buffer to a file in gem5's output
+ * directory and print a warning.
+ *
+ * @warn This event uses Linux::dumpDmesg() and comes with the same
+ * limitations. Most importantly, the kernel's address mappings must
+ * be available to the translating proxy.
+ */
+class DmesgDumpEvent : public PCEvent
+{
+  protected:
+    std::string fname;
+
+  public:
+    DmesgDumpEvent(PCEventQueue *q, const std::string &desc, Addr addr,
+                   const std::string &_fname)
+        : PCEvent(q, desc, addr), fname(_fname) {}
+    virtual void process(ThreadContext *xc);
+};
+
+/**
+ * Dump the guest kernel's dmesg buffer to a file in gem5's output
+ * directory and panic.
+ *
+ * @warn This event uses Linux::dumpDmesg() and comes with the same
+ * limitations. Most importantly, the kernel's address mappings must
+ * be available to the translating proxy.
+ */
+class KernelPanicEvent : public PCEvent
+{
+  protected:
+    std::string fname;
+
+  public:
+    KernelPanicEvent(PCEventQueue *q, const std::string &desc, Addr addr,
+               const std::string &_fname)
+        : PCEvent(q, desc, addr), fname(_fname) {}
+    virtual void process(ThreadContext *xc);
+};
+
 /** A class to skip udelay() and related calls in the kernel.
  * This class has two additional parameters that take the argument to udelay and
  * manipulated it to come up with ns and eventually ticks to quiesce for.
diff --git a/src/kern/linux/helpers.cc b/src/kern/linux/helpers.cc
new file mode 100644 (file)
index 0000000..54fc421
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2016 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Andreas Sandberg
+ */
+
+#include "kern/linux/helpers.hh"
+
+#include "arch/isa_traits.hh"
+#include "config/the_isa.hh"
+#include "cpu/thread_context.hh"
+#include "mem/fs_translating_port_proxy.hh"
+#include "sim/system.hh"
+
+struct DmesgEntry {
+    uint64_t ts_nsec;
+    uint16_t len;
+    uint16_t text_len;
+    uint16_t dict_len;
+    uint8_t facility;
+    uint8_t flags;
+} M5_ATTR_PACKED;
+
+static int
+dumpDmesgEntry(const uint8_t *base, const uint8_t *end, std::ostream &os)
+{
+    const size_t max_length = end - base;
+    DmesgEntry de;
+
+    if (max_length < sizeof(de)) {
+        warn("Malformed dmesg entry\n");
+        return -1;
+    }
+
+    memcpy(&de, base, sizeof(de));
+    de.ts_nsec = TheISA::gtoh(de.ts_nsec);
+    de.len = TheISA::gtoh(de.len);
+    de.text_len = TheISA::gtoh(de.text_len);
+
+    if (de.len < sizeof(de) ||
+        max_length < de.len ||
+        max_length < sizeof(DmesgEntry) + de.text_len) {
+
+        warn("Malformed dmesg entry:\n");
+        warn("\tMax length: %i\n", max_length);
+        warn("\tde.len: %i\n", de.len);
+        warn("\tde.text_len: %i\n", de.text_len);
+        return -1;
+    }
+
+    ccprintf(os, "[%.6f] ", de.ts_nsec * 10e-9);
+    os.write((char *)base + sizeof(de), de.text_len);
+    os << std::endl;
+
+    return de.len;
+}
+
+void
+Linux::dumpDmesg(ThreadContext *tc, std::ostream &os)
+{
+    System *system = tc->getSystemPtr();
+    const SymbolTable *symtab = system->kernelSymtab;
+    FSTranslatingPortProxy proxy(tc);
+
+    Addr addr_lb = 0, addr_lb_len = 0, addr_first = 0, addr_next = 0;
+    const bool found_symbols =
+        symtab->findAddress("__log_buf", addr_lb) &&
+        symtab->findAddress("log_buf_len", addr_lb_len) &&
+        symtab->findAddress("log_first_idx", addr_first) &&
+        symtab->findAddress("log_next_idx", addr_next);
+
+    if (!found_symbols) {
+        warn("Failed to find kernel dmesg symbols.\n");
+        return;
+    }
+
+    uint32_t log_buf_len = proxy.readGtoH<uint32_t>(addr_lb_len);
+    uint32_t log_first_idx = proxy.readGtoH<uint32_t>(addr_first);
+    uint32_t log_next_idx = proxy.readGtoH<uint32_t>(addr_next);
+
+    if (log_first_idx >= log_buf_len || log_next_idx >= log_buf_len) {
+        warn("dmesg pointers/length corrupted\n");
+        return;
+    }
+
+    // Normalize and read the dmesg ring buffer
+    std::vector<uint8_t> log_buf(log_buf_len);
+    int length;
+    if (log_first_idx < log_next_idx) {
+        length = log_next_idx - log_first_idx;
+        if (length < 0 || length > log_buf.size()) {
+            warn("Unexpected dmesg buffer length\n");
+            return;
+        }
+        proxy.readBlob(addr_lb + log_first_idx, log_buf.data(), length);
+    } else {
+        const int length_2 = log_buf_len - log_first_idx;
+        if (length_2 < 0 || length_2 + log_next_idx > log_buf.size()) {
+            warn("Unexpected dmesg buffer length\n");
+            return;
+        }
+        length = log_buf_len;
+        proxy.readBlob(addr_lb + log_first_idx, log_buf.data(), length_2);
+        proxy.readBlob(addr_lb, log_buf.data() + length_2, log_next_idx);
+    }
+
+    // Print dmesg buffer content
+    const uint8_t *cur = log_buf.data(), *end = log_buf.data() + length;
+    while (cur < end) {
+        int ret = dumpDmesgEntry(cur, end, os);
+        if (ret < 0)
+            return;
+        cur += ret;
+    }
+}
diff --git a/src/kern/linux/helpers.hh b/src/kern/linux/helpers.hh
new file mode 100644 (file)
index 0000000..cebda7d
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2016 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Andreas Sandberg
+ */
+
+#ifndef __KERN_LINUX_HELPERS_HH__
+#define __KERN_LINUX_HELPERS_HH__
+
+#include <ostream>
+
+class ThreadContext;
+
+namespace Linux {
+
+/**
+ * Dump Linux's dmesg log buffer to the an output stream.
+ *
+ * @warn This assumes that the kernel address mappings are available
+ * to the translating proxy.
+ */
+void dumpDmesg(ThreadContext *tc, std::ostream &os);
+
+} // namespace Linux
+
+#endif // __KERN_LINUX_HELPERS_HH__