mgorny updated this revision to Diff 189545.
mgorny added a comment.
Herald added a reviewer: serge-sans-paille.
Herald added a subscriber: jdoerfert.

Added two tests, more pending. Sadly, I wasn't able to reuse even the test 
cases since `-nostdlib` doesn't really work on NetBSD (resulting executables 
are not recognized as valid executables).


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D32149/new/

https://reviews.llvm.org/D32149

Files:
  
lldb/packages/Python/lldbsuite/test/functionalities/postmortem/netbsd-core/1lwp_SIGSEGV.amd64
  
lldb/packages/Python/lldbsuite/test/functionalities/postmortem/netbsd-core/1lwp_SIGSEGV.amd64.core
  
lldb/packages/Python/lldbsuite/test/functionalities/postmortem/netbsd-core/1lwp_SIGSEGV.c
  
lldb/packages/Python/lldbsuite/test/functionalities/postmortem/netbsd-core/2lwp_t2_SIGSEGV.amd64
  
lldb/packages/Python/lldbsuite/test/functionalities/postmortem/netbsd-core/2lwp_t2_SIGSEGV.amd64.core
  
lldb/packages/Python/lldbsuite/test/functionalities/postmortem/netbsd-core/2lwp_t2_SIGSEGV.c
  
lldb/packages/Python/lldbsuite/test/functionalities/postmortem/netbsd-core/GNUmakefile
  
lldb/packages/Python/lldbsuite/test/functionalities/postmortem/netbsd-core/TestNetBSDCore.py
  lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
  lldb/source/Plugins/Process/elf-core/RegisterUtilities.h

Index: lldb/source/Plugins/Process/elf-core/RegisterUtilities.h
===================================================================
--- lldb/source/Plugins/Process/elf-core/RegisterUtilities.h
+++ lldb/source/Plugins/Process/elf-core/RegisterUtilities.h
@@ -27,9 +27,42 @@
 }
 
 namespace NETBSD {
-enum { NT_PROCINFO = 1, NT_AUXV, NT_AMD64_REGS = 33, NT_AMD64_FPREGS = 35 };
+enum { NT_PROCINFO = 1, NT_AUXV = 2 };
+
+/* Size in bytes */
+enum { NT_PROCINFO_SIZE = 160 };
+
+/* Size in bytes */
+enum {
+  NT_PROCINFO_CPI_VERSION_SIZE = 4,
+  NT_PROCINFO_CPI_CPISIZE_SIZE = 4,
+  NT_PROCINFO_CPI_SIGNO_SIZE = 4,
+  NT_PROCINFO_CPI_SIGCODE_SIZE = 4,
+  NT_PROCINFO_CPI_SIGPEND_SIZE = 16,
+  NT_PROCINFO_CPI_SIGMASK_SIZE = 16,
+  NT_PROCINFO_CPI_SIGIGNORE_SIZE = 16,
+  NT_PROCINFO_CPI_SIGCATCH_SIZE = 16,
+  NT_PROCINFO_CPI_PID_SIZE = 4,
+  NT_PROCINFO_CPI_PPID_SIZE = 4,
+  NT_PROCINFO_CPI_PGRP_SIZE = 4,
+  NT_PROCINFO_CPI_SID_SIZE = 4,
+  NT_PROCINFO_CPI_RUID_SIZE = 4,
+  NT_PROCINFO_CPI_EUID_SIZE = 4,
+  NT_PROCINFO_CPI_SVUID_SIZE = 4,
+  NT_PROCINFO_CPI_RGID_SIZE = 4,
+  NT_PROCINFO_CPI_EGID_SIZE = 4,
+  NT_PROCINFO_CPI_SVGID_SIZE = 4,
+  NT_PROCINFO_CPI_NLWPS_SIZE = 4,
+  NT_PROCINFO_CPI_NAME_SIZE = 32,
+  NT_PROCINFO_CPI_SIGLWP_SIZE = 4,
+};
+
+namespace AMD64 {
+enum { NT_REGS = 33, NT_FPREGS = 35 };
 }
 
+} // namespace NETBSD
+
 namespace OPENBSD {
 enum {
   NT_PROCINFO = 10,
@@ -91,7 +124,7 @@
     // The result from FXSAVE is in NT_PRXFPREG for i386 core files
     {llvm::Triple::Linux, llvm::Triple::x86, LINUX::NT_PRXFPREG},
     {llvm::Triple::Linux, llvm::Triple::UnknownArch, LINUX::NT_FPREGSET},
-    {llvm::Triple::NetBSD, llvm::Triple::x86_64, NETBSD::NT_AMD64_FPREGS},
+    {llvm::Triple::NetBSD, llvm::Triple::x86_64, NETBSD::AMD64::NT_FPREGS},
     {llvm::Triple::OpenBSD, llvm::Triple::UnknownArch, OPENBSD::NT_FPREGS},
 };
 
Index: lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
===================================================================
--- lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
+++ lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
@@ -446,16 +446,48 @@
   thread_data.gpregset = DataExtractor(data, offset, len);
 }
 
-static void ParseNetBSDProcInfo(ThreadData &thread_data,
-                                const DataExtractor &data) {
+static llvm::Error ParseNetBSDProcInfo(const DataExtractor &data,
+                                       uint32_t &cpi_nlwps,
+                                       uint32_t &cpi_signo,
+                                       uint32_t &cpi_siglwp,
+                                       uint32_t &cpi_pid) {
   lldb::offset_t offset = 0;
 
-  int version = data.GetU32(&offset);
+  uint32_t version = data.GetU32(&offset);
   if (version != 1)
-    return;
+    return llvm::make_error<llvm::StringError>(
+        "Error parsing NetBSD core(5) notes: Unsupported procinfo version",
+        llvm::inconvertibleErrorCode());
 
-  offset += 4;
-  thread_data.signo = data.GetU32(&offset);
+  uint32_t cpisize = data.GetU32(&offset);
+  if (cpisize != NETBSD::NT_PROCINFO_SIZE)
+    return llvm::make_error<llvm::StringError>(
+        "Error parsing NetBSD core(5) notes: Unsupported procinfo size",
+        llvm::inconvertibleErrorCode());
+
+  cpi_signo = data.GetU32(&offset); /* killing signal */
+
+  offset += NETBSD::NT_PROCINFO_CPI_SIGCODE_SIZE;
+  offset += NETBSD::NT_PROCINFO_CPI_SIGPEND_SIZE;
+  offset += NETBSD::NT_PROCINFO_CPI_SIGMASK_SIZE;
+  offset += NETBSD::NT_PROCINFO_CPI_SIGIGNORE_SIZE;
+  offset += NETBSD::NT_PROCINFO_CPI_SIGCATCH_SIZE;
+  cpi_pid = data.GetU32(&offset);
+  offset += NETBSD::NT_PROCINFO_CPI_PPID_SIZE;
+  offset += NETBSD::NT_PROCINFO_CPI_PGRP_SIZE;
+  offset += NETBSD::NT_PROCINFO_CPI_SID_SIZE;
+  offset += NETBSD::NT_PROCINFO_CPI_RUID_SIZE;
+  offset += NETBSD::NT_PROCINFO_CPI_EUID_SIZE;
+  offset += NETBSD::NT_PROCINFO_CPI_SVUID_SIZE;
+  offset += NETBSD::NT_PROCINFO_CPI_RGID_SIZE;
+  offset += NETBSD::NT_PROCINFO_CPI_EGID_SIZE;
+  offset += NETBSD::NT_PROCINFO_CPI_SVGID_SIZE;
+  cpi_nlwps = data.GetU32(&offset); /* number of LWPs */
+
+  offset += NETBSD::NT_PROCINFO_CPI_NAME_SIZE;
+  cpi_siglwp = data.GetU32(&offset); /* LWP target of killing signal */
+
+  return llvm::Error::success();
 }
 
 static void ParseOpenBSDProcInfo(ThreadData &thread_data,
@@ -541,37 +573,133 @@
   return llvm::Error::success();
 }
 
+/// NetBSD specific Thread context from PT_NOTE segment
+///
+/// NetBSD ELF core files use notes to provide information about
+/// the process's state.  The note name is "NetBSD-CORE" for
+/// information that is global to the process, and "NetBSD-CORE@nn",
+/// where "nn" is the lwpid of the LWP that the information belongs
+/// to (such as register state).
+///
+/// NetBSD uses the following note identifiers:
+///
+///      ELF_NOTE_NETBSD_CORE_PROCINFO (value 1)
+///             Note is a "netbsd_elfcore_procinfo" structure.
+///      ELF_NOTE_NETBSD_CORE_AUXV     (value 2; since NetBSD 8.0)
+///             Note is an array of AuxInfo structures.
+///
+/// NetBSD also uses ptrace(2) request numbers (the ones that exist in
+/// machine-dependent space) to identify register info notes.  The
+/// info in such notes is in the same format that ptrace(2) would
+/// export that information.
+///
+/// For more information see /usr/include/sys/exec_elf.h
+///
 llvm::Error ProcessElfCore::parseNetBSDNotes(llvm::ArrayRef<CoreNote> notes) {
   ThreadData thread_data;
-  for (const auto &note : notes) {
-    // NetBSD per-thread information is stored in notes named "NetBSD-CORE@nnn"
-    // so match on the initial part of the string.
-    if (!llvm::StringRef(note.info.n_name).startswith("NetBSD-CORE"))
-      continue;
+  bool had_nt_regs = false;
 
-    switch (note.info.n_type) {
-    case NETBSD::NT_PROCINFO:
-      ParseNetBSDProcInfo(thread_data, note.data);
-      break;
-    case NETBSD::NT_AUXV:
-      m_auxv = note.data;
-      break;
+  // To be extracted from struct netbsd_elfcore_procinfo
+  // Used to sanity check of the LWPs of the process
+  uint32_t nlwps = 0;
+  uint32_t signo;  // killing signal
+  uint32_t siglwp; // LWP target of killing signal
+  uint32_t pr_pid;
 
-    case NETBSD::NT_AMD64_REGS:
-      if (GetArchitecture().GetMachine() == llvm::Triple::x86_64)
-        thread_data.gpregset = note.data;
-      break;
-    default:
-      thread_data.notes.push_back(note);
-      break;
+  for (const auto &note : notes) {
+    llvm::StringRef name = note.info.n_name;
+
+    if (name == "NetBSD-CORE") {
+      if (note.info.n_type == NETBSD::NT_PROCINFO) {
+        llvm::Error error = ParseNetBSDProcInfo(note.data, nlwps, signo,
+                                                siglwp, pr_pid);
+        if (error)
+          return error;
+        SetID(pr_pid);
+      } else if (note.info.n_type == NETBSD::NT_AUXV) {
+        m_auxv = note.data;
+      }
+    } else if (name.consume_front("NetBSD-CORE@")) {
+      lldb::tid_t tid;
+      if (name.getAsInteger(10, tid))
+        return llvm::make_error<llvm::StringError>(
+            "Error parsing NetBSD core(5) notes: Cannot convert LWP ID "
+            "to integer",
+            llvm::inconvertibleErrorCode());
+
+      switch (GetArchitecture().GetMachine()) {
+      case llvm::Triple::x86_64: {
+        // Assume order PT_GETREGS, PT_GETFPREGS
+        if (note.info.n_type == NETBSD::AMD64::NT_REGS) {
+          // If this is the next thread, push the previous one first.
+          if (had_nt_regs) {
+            m_thread_data.push_back(thread_data);
+            thread_data = ThreadData();
+            had_nt_regs = false;
+          }
+
+          thread_data.gpregset = note.data;
+          thread_data.tid = tid;
+          if (thread_data.gpregset.GetByteSize() == 0)
+            return llvm::make_error<llvm::StringError>(
+                "Could not find general purpose registers note in core file.",
+                llvm::inconvertibleErrorCode());
+          had_nt_regs = true;
+        } else if (note.info.n_type == NETBSD::AMD64::NT_FPREGS) {
+          if (!had_nt_regs || tid != thread_data.tid)
+            return llvm::make_error<llvm::StringError>(
+                "Error parsing NetBSD core(5) notes: Unexpected order "
+                "of NOTEs PT_GETFPREG before PT_GETREG",
+                llvm::inconvertibleErrorCode());
+          thread_data.notes.push_back(note);
+        }
+      } break;
+      default:
+        break;
+      }
     }
   }
-  if (thread_data.gpregset.GetByteSize() == 0) {
+
+  // Push the last thread.
+  if (had_nt_regs)
+    m_thread_data.push_back(thread_data);
+
+  if (m_thread_data.empty())
     return llvm::make_error<llvm::StringError>(
-        "Could not find general purpose registers note in core file.",
+        "Error parsing NetBSD core(5) notes: No threads information "
+        "specified in notes",
+        llvm::inconvertibleErrorCode());
+
+  if (m_thread_data.size() != nlwps)
+    return llvm::make_error<llvm::StringError>(
+        "Error parsing NetBSD core(5) notes: Mismatch between the number "
+        "of LWPs in netbsd_elfcore_procinfo and the number of LWPs specified "
+        "by MD notes",
         llvm::inconvertibleErrorCode());
+
+  // Signal targeted at the whole process.
+  if (siglwp == 0) {
+    for (auto &data : m_thread_data)
+      data.signo = signo;
   }
-  m_thread_data.push_back(thread_data);
+  // Signal destined for a particular LWP.
+  else {
+    bool passed = false;
+
+    for (auto &data : m_thread_data) {
+      if (data.tid == siglwp) {
+        data.signo = signo;
+        passed = true;
+        break;
+      }
+    }
+
+    if (!passed)
+      return llvm::make_error<llvm::StringError>(
+          "Error parsing NetBSD core(5) notes: Signal passed to unknown LWP",
+          llvm::inconvertibleErrorCode());
+  }
+
   return llvm::Error::success();
 }
 
Index: lldb/packages/Python/lldbsuite/test/functionalities/postmortem/netbsd-core/TestNetBSDCore.py
===================================================================
--- /dev/null
+++ lldb/packages/Python/lldbsuite/test/functionalities/postmortem/netbsd-core/TestNetBSDCore.py
@@ -0,0 +1,185 @@
+"""
+Test NetBSD core file debugging.
+"""
+
+from __future__ import division, print_function
+
+import shutil
+import struct
+import os
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class NetBSDCoreCommonTestCase(TestBase):
+    NO_DEBUG_INFO_TESTCASE = True
+
+    mydir = TestBase.compute_mydir(__file__)
+
+    def setUp(self):
+        super(NetBSDCoreCommonTestCase, self).setUp()
+        self._initial_platform = lldb.DBG.GetSelectedPlatform()
+
+    def tearDown(self):
+        lldb.DBG.SetSelectedPlatform(self._initial_platform)
+        super(NetBSDCoreCommonTestCase, self).tearDown()
+
+    def check_memory_regions(self, process, region_count):
+        region_list = process.GetMemoryRegions()
+        self.assertEqual(region_list.GetSize(), region_count)
+
+        region = lldb.SBMemoryRegionInfo()
+
+        # Check we have the right number of regions.
+        self.assertEqual(region_list.GetSize(), region_count)
+
+        # Check that getting a region beyond the last in the list fails.
+        self.assertFalse(
+            region_list.GetMemoryRegionAtIndex(
+                region_count, region))
+
+        # Check each region is valid.
+        for i in range(region_list.GetSize()):
+            # Check we can actually get this region.
+            self.assertTrue(region_list.GetMemoryRegionAtIndex(i, region))
+
+            # Every region in the list should be mapped.
+            self.assertTrue(region.IsMapped())
+
+            # Test the address at the start of a region returns it's enclosing
+            # region.
+            begin_address = region.GetRegionBase()
+            region_at_begin = lldb.SBMemoryRegionInfo()
+            error = process.GetMemoryRegionInfo(begin_address, region_at_begin)
+            self.assertEqual(region, region_at_begin)
+
+            # Test an address in the middle of a region returns it's enclosing
+            # region.
+            middle_address = (region.GetRegionBase() +
+                              region.GetRegionEnd()) // 2
+            region_at_middle = lldb.SBMemoryRegionInfo()
+            error = process.GetMemoryRegionInfo(
+                middle_address, region_at_middle)
+            self.assertEqual(region, region_at_middle)
+
+            # Test the address at the end of a region returns it's enclosing
+            # region.
+            end_address = region.GetRegionEnd() - 1
+            region_at_end = lldb.SBMemoryRegionInfo()
+            error = process.GetMemoryRegionInfo(end_address, region_at_end)
+            self.assertEqual(region, region_at_end)
+
+            # Check that quering the end address does not return this region but
+            # the next one.
+            next_region = lldb.SBMemoryRegionInfo()
+            error = process.GetMemoryRegionInfo(
+                region.GetRegionEnd(), next_region)
+            self.assertNotEqual(region, next_region)
+            self.assertEqual(
+                region.GetRegionEnd(),
+                next_region.GetRegionBase())
+
+        # Check that query beyond the last region returns an unmapped region
+        # that ends at LLDB_INVALID_ADDRESS
+        last_region = lldb.SBMemoryRegionInfo()
+        region_list.GetMemoryRegionAtIndex(region_count - 1, last_region)
+        end_region = lldb.SBMemoryRegionInfo()
+        error = process.GetMemoryRegionInfo(
+            last_region.GetRegionEnd(), end_region)
+        self.assertFalse(end_region.IsMapped())
+        self.assertEqual(
+            last_region.GetRegionEnd(),
+            end_region.GetRegionBase())
+        self.assertEqual(end_region.GetRegionEnd(), lldb.LLDB_INVALID_ADDRESS)
+
+    def check_state(self, process):
+        with open(os.devnull) as devnul:
+            # sanitize test output
+            self.dbg.SetOutputFileHandle(devnul, False)
+            self.dbg.SetErrorFileHandle(devnul, False)
+
+            self.assertTrue(process.is_stopped)
+
+            # Process.Continue
+            error = process.Continue()
+            self.assertFalse(error.Success())
+            self.assertTrue(process.is_stopped)
+
+            # Thread.StepOut
+            thread = process.GetSelectedThread()
+            thread.StepOut()
+            self.assertTrue(process.is_stopped)
+
+            # command line
+            self.dbg.HandleCommand('s')
+            self.assertTrue(process.is_stopped)
+            self.dbg.HandleCommand('c')
+            self.assertTrue(process.is_stopped)
+
+            # restore file handles
+            self.dbg.SetOutputFileHandle(None, False)
+            self.dbg.SetErrorFileHandle(None, False)
+
+    def check_backtrace(self, thread, filename, backtrace):
+        self.assertGreaterEqual(thread.GetNumFrames(), len(backtrace))
+        src = filename.rpartition('.')[0] + '.c'
+        for i in range(len(backtrace)):
+            frame = thread.GetFrameAtIndex(i)
+            self.assertTrue(frame)
+            self.assertEqual(frame.GetFunctionName(), backtrace[i])
+            self.assertEqual(frame.GetLineEntry().GetLine(),
+                             line_number(src, "Frame " + backtrace[i]))
+            self.assertEqual(
+                frame.FindVariable("F").GetValueAsUnsigned(), ord(
+                    backtrace[i][0]))
+
+    def do_test(self, filename, pid, region_count):
+        target = self.dbg.CreateTarget(filename)
+        process = target.LoadCore(filename + ".core")
+
+        self.assertTrue(process, PROCESS_IS_VALID)
+        self.assertEqual(process.GetNumThreads(), self.THREAD_COUNT)
+        self.assertEqual(process.GetProcessID(), pid)
+
+        self.check_state(process)
+
+        self.check_stack(process, pid, filename)
+
+        self.check_memory_regions(process, region_count)
+
+        self.dbg.DeleteTarget(target)
+
+
+class NetBSD1LWPCoreTestCase(NetBSDCoreCommonTestCase):
+    THREAD_COUNT = 1
+
+    def check_stack(self, process, pid, filename):
+        thread = process.GetSelectedThread()
+        self.assertTrue(thread)
+        self.assertEqual(thread.GetThreadID(), 1)
+        backtrace = ["bar", "foo", "main"]
+        self.check_backtrace(thread, filename, backtrace)
+
+    @skipIfLLVMTargetMissing("X86")
+    def test_amd64(self):
+        """Test single-threaded amd64 core dump."""
+        self.do_test("1lwp_SIGSEGV.amd64", pid=693, region_count=21)
+
+
+class NetBSD2LWPT2CoreTestCase(NetBSDCoreCommonTestCase):
+    THREAD_COUNT = 2
+
+    def check_stack(self, process, pid, filename):
+        thread = process.GetSelectedThread()
+        self.assertTrue(thread)
+        self.assertEqual(thread.GetThreadID(), 2)
+        backtrace = ["bar", "foo", "lwp_main"]
+        self.check_backtrace(thread, filename, backtrace)
+
+    @skipIfLLVMTargetMissing("X86")
+    def test_2lwp_t2_amd64(self):
+        """Test double-threaded amd64 core dump where thread 2 is signalled."""
+        self.do_test("2lwp_t2_SIGSEGV.amd64", pid=622, region_count=24)
Index: lldb/packages/Python/lldbsuite/test/functionalities/postmortem/netbsd-core/GNUmakefile
===================================================================
--- /dev/null
+++ lldb/packages/Python/lldbsuite/test/functionalities/postmortem/netbsd-core/GNUmakefile
@@ -0,0 +1,15 @@
+ARCH = $(shell uname -m)
+PROGRAMS = 1lwp_SIGSEGV 2lwp_t2_SIGSEGV
+EXECS = $(patsubst %,%.$(ARCH),$(PROGRAMS))
+CORES = $(patsubst %,%.core,$(EXECS))
+
+all: $(CORES) $(EXECS)
+clean:
+	rm -f $(CORES) $(EXECS)
+
+%.core: %
+	sysctl -w proc.$$$$.corename=$@; ulimit -s 16; ! ./$<
+%.$(ARCH): %.c
+	$(CC) -o $@ -g $<
+
+.PHONY: all clean
Index: lldb/packages/Python/lldbsuite/test/functionalities/postmortem/netbsd-core/2lwp_t2_SIGSEGV.c
===================================================================
--- /dev/null
+++ lldb/packages/Python/lldbsuite/test/functionalities/postmortem/netbsd-core/2lwp_t2_SIGSEGV.c
@@ -0,0 +1,30 @@
+#include <lwp.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+static void bar(char *boom) {
+  char F = 'b';
+  *boom = 47; // Frame bar
+}
+
+static void foo(char *boom, void (*boomer)(char *)) {
+  char F = 'f';
+  boomer(boom); // Frame foo
+}
+
+void lwp_main(void *unused) {
+  char F = 'l';
+  foo(0, bar); // Frame lwp_main
+}
+
+int main(int argc, char **argv) {
+  ucontext_t uc;
+  lwpid_t lid;
+  static const size_t ssize = 16 * 1024;
+  void *stack;
+
+  stack = malloc(ssize);
+  _lwp_makecontext(&uc, lwp_main, NULL, NULL, stack, ssize);
+  _lwp_create(&uc, 0, &lid);
+  _lwp_wait(lid, NULL);
+}
Index: lldb/packages/Python/lldbsuite/test/functionalities/postmortem/netbsd-core/1lwp_SIGSEGV.c
===================================================================
--- /dev/null
+++ lldb/packages/Python/lldbsuite/test/functionalities/postmortem/netbsd-core/1lwp_SIGSEGV.c
@@ -0,0 +1,14 @@
+static void bar(char *boom) {
+  char F = 'b';
+  *boom = 47; // Frame bar
+}
+
+static void foo(char *boom, void (*boomer)(char *)) {
+  char F = 'f';
+  boomer(boom); // Frame foo
+}
+
+void main(void) {
+  char F = 'm';
+  foo(0, bar); // Frame main
+}
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to