Index: packages/Python/lldbsuite/test/functionalities/postmortem/elf-core/gcore/TestGCore.py =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/functionalities/postmortem/elf-core/gcore/TestGCore.py @@ -0,0 +1,52 @@ +""" +Test signal reporting when debugging with linux core files. +""" + +from __future__ import print_function + +import shutil +import struct + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class GCoreTestCase(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + mydir = TestBase.compute_mydir(__file__) + _initial_platform = lldb.DBG.GetSelectedPlatform() + + _i386_pid = 5586 + _x86_64_pid = 5669 + + @skipIf(oslist=['windows']) + @skipIf(triple='^mips') + def test_i386(self): + """Test that lldb can read the process information from an i386 linux core file.""" + self.do_test("linux-i386", self._i386_pid) + + @skipIf(oslist=['windows']) + @skipIf(triple='^mips') + def test_x86_64(self): + """Test that lldb can read the process information from an x86_64 linux core file.""" + self.do_test("linux-x86_64", self._x86_64_pid) + + def do_test(self, filename, pid): + target = self.dbg.CreateTarget("") + process = target.LoadCore(filename + ".core") + self.assertTrue(process, PROCESS_IS_VALID) + self.assertEqual(process.GetNumThreads(), 3) + self.assertEqual(process.GetProcessID(), pid) + + for thread in process: + reason = thread.GetStopReason() + self.assertEqual(reason, lldb.eStopReasonSignal) + signal = thread.GetStopReasonDataAtIndex(1) + # Check we got signal 19 (SIGSTOP) + self.assertEqual(signal, 19) + + self.dbg.DeleteTarget(target) + lldb.DBG.SetSelectedPlatform(self._initial_platform) Index: packages/Python/lldbsuite/test/functionalities/postmortem/elf-core/gcore/main.cpp =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/functionalities/postmortem/elf-core/gcore/main.cpp @@ -0,0 +1,63 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This test verifies the correct handling of child thread exits. + +#include +#include +#include + +pseudo_barrier_t g_barrier1; +pseudo_barrier_t g_barrier2; + +void * +thread1 () +{ + // Synchronize with the main thread. + pseudo_barrier_wait(g_barrier1); + + // Synchronize with the main thread and thread2. + pseudo_barrier_wait(g_barrier2); + + // Return + return NULL; +} + +void * +thread2 () +{ + + // Synchronize with thread1 and the main thread. + pseudo_barrier_wait(g_barrier2); // Should not reach here. + + // Return + return NULL; +} + +int main () +{ + + pseudo_barrier_init(g_barrier1, 2); + pseudo_barrier_init(g_barrier2, 3); + + // Create a thread. + std::thread thread_1(thread1); + + // Wait for thread1 to start. + pseudo_barrier_wait(g_barrier1); + + // Wait for thread1 to start. + std::thread thread_2(thread2); + + // Thread 2 is waiting for another thread to reach the barrier. + // This should have for ever. (So we can run gcore against this process.) + thread_2.join(); + + return 0; +} Index: packages/Python/lldbsuite/test/functionalities/postmortem/elf-core/gcore/main.mk =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/functionalities/postmortem/elf-core/gcore/main.mk @@ -0,0 +1,5 @@ +LEVEL = ../../../../make + +CXX_SOURCES := main.cpp +ENABLE_THREADS := YES +include $(LEVEL)/Makefile.rules Index: packages/Python/lldbsuite/test/functionalities/postmortem/elf-core/gcore/make-core.sh =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/functionalities/postmortem/elf-core/gcore/make-core.sh @@ -0,0 +1,56 @@ +#! /bin/sh + +linux_check_ptrace_scope() +{ + if grep -q '1' . +EOF + +stack_size=`ulimit -s` + +# Decrease stack size to 16k => smaller core files. +# gcore won't run with the smaller stack +ulimit -Ss 16 + +core_dump_filter=`cat /proc/self/coredump_filter` +echo 0 > /proc/self/coredump_filter + +./a.out & + +pid=$! + +echo $core_dump_filter > /proc/self/coredump_filter + +# Reset stack size as so there's enough space to run gcore. +ulimit -s $stack_size + +echo "Sleeping for 5 seconds to wait for $pid" + +sleep 5 +echo "Taking core from process $pid" + +gcore -o core $pid + +echo "Killing process $pid" +kill -9 $pid Index: packages/Python/lldbsuite/test/functionalities/postmortem/elf-core/thread_crash/TestLinuxCoreThreads.py =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/functionalities/postmortem/elf-core/thread_crash/TestLinuxCoreThreads.py @@ -0,0 +1,61 @@ +""" +Test signal reporting when debugging with linux core files. +""" + +from __future__ import print_function + +import shutil +import struct + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class LinuxCoreThreadsTestCase(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + mydir = TestBase.compute_mydir(__file__) + _initial_platform = lldb.DBG.GetSelectedPlatform() + + _i386_pid = 5193 + _x86_64_pid = 5222 + + # Thread id for the failing thread. + _i386_tid = 5195 + _x86_64_tid = 5250 + + @skipIf(oslist=['windows']) + @skipIf(triple='^mips') + def test_i386(self): + """Test that lldb can read the process information from an i386 linux core file.""" + self.do_test("linux-i386", self._i386_pid, self._i386_tid) + + @skipIf(oslist=['windows']) + @skipIf(triple='^mips') + def test_x86_64(self): + """Test that lldb can read the process information from an x86_64 linux core file.""" + self.do_test("linux-x86_64", self._x86_64_pid, self._x86_64_tid) + + def do_test(self, filename, pid, tid): + target = self.dbg.CreateTarget("") + process = target.LoadCore(filename + ".core") + self.assertTrue(process, PROCESS_IS_VALID) + self.assertEqual(process.GetNumThreads(), 3) + self.assertEqual(process.GetProcessID(), pid) + + for thread in process: + reason = thread.GetStopReason() + if( thread.GetThreadID() == tid ): + self.assertEqual(reason, lldb.eStopReasonSignal) + signal = thread.GetStopReasonDataAtIndex(1) + # Check we got signal 4 (SIGILL) + self.assertEqual(signal, 4) + else: + signal = thread.GetStopReasonDataAtIndex(1) + # Check we got no signal on the other threads + self.assertEqual(signal, 0) + + self.dbg.DeleteTarget(target) + lldb.DBG.SetSelectedPlatform(self._initial_platform) Index: packages/Python/lldbsuite/test/functionalities/postmortem/elf-core/thread_crash/main.cpp =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/functionalities/postmortem/elf-core/thread_crash/main.cpp @@ -0,0 +1,63 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This test verifies the correct handling of child thread exits. + +#include +#include +#include + +pseudo_barrier_t g_barrier1; +pseudo_barrier_t g_barrier2; + +void * +thread1 () +{ + // Synchronize with the main thread. + pseudo_barrier_wait(g_barrier1); + + // Synchronize with the main thread and thread2. + pseudo_barrier_wait(g_barrier2); + + // Return + return NULL; // Should not reach here. (thread2 should raise SIGILL) +} + +void * +thread2 () +{ + raise(SIGILL); // Raise SIGILL + + // Synchronize with thread1 and the main thread. + pseudo_barrier_wait(g_barrier2); // Should not reach here. + + // Return + return NULL; +} + +int main () +{ + pseudo_barrier_init(g_barrier1, 2); + pseudo_barrier_init(g_barrier2, 3); + + // Create a thread. + std::thread thread_1(thread1); + + // Wait for thread1 to start. + pseudo_barrier_wait(g_barrier1); + + // Create another thread. + std::thread thread_2(thread2); + + // Wait for thread2 to start. + // Second thread should crash but first thread and main thread may reach here. + pseudo_barrier_wait(g_barrier2); + + return 0; +} Index: packages/Python/lldbsuite/test/functionalities/postmortem/elf-core/thread_crash/main.mk =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/functionalities/postmortem/elf-core/thread_crash/main.mk @@ -0,0 +1,5 @@ +LEVEL = ../../../../make + +CXX_SOURCES := main.cpp +ENABLE_THREADS := YES +include $(LEVEL)/Makefile.rules Index: packages/Python/lldbsuite/test/functionalities/postmortem/elf-core/thread_crash/make-core.sh =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/functionalities/postmortem/elf-core/thread_crash/make-core.sh @@ -0,0 +1,64 @@ +#! /bin/sh + +linux_check_core_pattern() +{ + if grep -q '^|' &2 + exit 1 + ;; +esac + +set -e -x + +if [ "$OS" = Linux ]; then + linux_check_core_pattern +fi + +ulimit -c 1000 +real_limit=$(ulimit -c) +if [ $real_limit -lt 100 ]; then + cat < smaller core files. + +core_dump_filter=`cat /proc/self/coredump_filter` +echo 0 > /proc/self/coredump_filter + +exec ./a.out + +# Reset stack size and core_dump_filter +echo core_dump_filter > /proc/self/coredump_filter +ulimit -s $stack_size Index: source/Plugins/Process/elf-core/ProcessElfCore.cpp =================================================================== --- source/Plugins/Process/elf-core/ProcessElfCore.cpp +++ source/Plugins/Process/elf-core/ProcessElfCore.cpp @@ -214,6 +214,29 @@ SetUnixSignals(UnixSignals::Create(GetArchitecture())); + // Ensure we found at least one thread that was stopped on a signal. + bool siginfo_signal_found = false; + bool prstatus_signal_found = false; + // Check we found a signal in a SIGINFO note. + for (const auto &thread_data: m_thread_data) { + if (thread_data.signo != 0) + siginfo_signal_found = true; + if (thread_data.prstatus_sig != 0) + prstatus_signal_found = true; + } + if (!siginfo_signal_found) { + // If we don't have signal from SIGINFO use the signal from each threads + // PRSTATUS note. + if (prstatus_signal_found) { + for (auto &thread_data: m_thread_data) + thread_data.signo = thread_data.prstatus_sig; + } else if (m_thread_data.size() > 0) { + // If all else fails force the first thread to be SIGSTOP + m_thread_data.begin()->signo = + GetUnixSignals()->GetSignalNumberFromName("SIGSTOP"); + } + } + // Core files are useless without the main executable. See if we can locate // the main // executable using data we found in the core file notes. @@ -402,6 +425,7 @@ NT_AUXV, NT_FILE = 0x46494c45, NT_PRXFPREG = 0x46e62b7f, + NT_SIGINFO = 0x53494749, }; namespace FREEBSD { @@ -485,6 +509,7 @@ ArchSpec arch = GetArchitecture(); ELFLinuxPrPsInfo prpsinfo; ELFLinuxPrStatus prstatus; + ELFLinuxSigInfo siginfo; size_t header_size; size_t len; Error error; @@ -546,7 +571,7 @@ error = prstatus.Parse(note_data, arch); if (error.Fail()) return error; - thread_data->signo = prstatus.pr_cursig; + thread_data->prstatus_sig = prstatus.pr_cursig; thread_data->tid = prstatus.pr_pid; header_size = ELFLinuxPrStatus::GetSize(arch); len = note_data.GetByteSize() - header_size; @@ -588,6 +613,12 @@ m_nt_file_entries[i].path.SetCString(path); } } break; + case NT_SIGINFO: { + error = siginfo.Parse(note_data, arch); + if (error.Fail()) + return error; + thread_data->signo = siginfo.si_signo; + } break; default: break; } Index: source/Plugins/Process/elf-core/ThreadElfCore.h =================================================================== --- source/Plugins/Process/elf-core/ThreadElfCore.h +++ source/Plugins/Process/elf-core/ThreadElfCore.h @@ -82,6 +82,38 @@ static_assert(sizeof(ELFLinuxPrStatus) == 112, "sizeof ELFLinuxPrStatus is not correct!"); +struct ELFLinuxSigInfo { + int32_t si_signo; + int32_t si_code; + int32_t si_errno; + + ELFLinuxSigInfo(); + + lldb_private::Error Parse(lldb_private::DataExtractor &data, + const lldb_private::ArchSpec &arch); + + // Return the bytesize of the structure + // 64 bit - just sizeof + // 32 bit - hardcoded because we are reusing the struct, but some of the + // members are smaller - + // so the layout is not the same + static size_t GetSize(const lldb_private::ArchSpec &arch) { + switch (arch.GetCore()) { + case lldb_private::ArchSpec::eCore_x86_64_x86_64: + return sizeof(ELFLinuxSigInfo); + case lldb_private::ArchSpec::eCore_s390x_generic: + case lldb_private::ArchSpec::eCore_x86_32_i386: + case lldb_private::ArchSpec::eCore_x86_32_i486: + return 12; + default: + return 0; + } + } +}; + +static_assert(sizeof(ELFLinuxSigInfo) == 12, + "sizeof ELFLinuxSigInfo is not correct!"); + // PRPSINFO structure's size differs based on architecture. // This is the layout in the x86-64 arch case. // In the i386 case we parse it manually and fill it again @@ -133,7 +165,8 @@ lldb_private::DataExtractor fpregset; lldb_private::DataExtractor vregset; lldb::tid_t tid; - int signo; + int signo = 0; + int prstatus_sig = 0; std::string name; }; Index: source/Plugins/Process/elf-core/ThreadElfCore.cpp =================================================================== --- source/Plugins/Process/elf-core/ThreadElfCore.cpp +++ source/Plugins/Process/elf-core/ThreadElfCore.cpp @@ -320,3 +320,45 @@ return error; } + +//---------------------------------------------------------------- +// Parse SIGINFO from NOTE entry +//---------------------------------------------------------------- +ELFLinuxSigInfo::ELFLinuxSigInfo() { + memset(this, 0, sizeof(ELFLinuxSigInfo)); +} + +Error ELFLinuxSigInfo::Parse(DataExtractor &data, const ArchSpec &arch) { + Error error; + ByteOrder byteorder = data.GetByteOrder(); + if (GetSize(arch) > data.GetByteSize()) { + error.SetErrorStringWithFormat( + "NT_SIGINFO size should be %zu, but the remaining bytes are: %" PRIu64, + GetSize(arch), data.GetByteSize()); + return error; + } + + switch (arch.GetCore()) { + case ArchSpec::eCore_x86_64_x86_64: + data.ExtractBytes(0, sizeof(ELFLinuxPrStatus), byteorder, this); + break; + case ArchSpec::eCore_s390x_generic: + case ArchSpec::eCore_x86_32_i386: + case ArchSpec::eCore_x86_32_i486: { + // Parsing from a 32 bit ELF core file, and populating/reusing the structure + // properly, because the struct is for the 64 bit version + offset_t offset = 0; + si_signo = data.GetU32(&offset); + si_code = data.GetU32(&offset); + si_errno = data.GetU32(&offset); + + break; + } + default: + error.SetErrorStringWithFormat("ELFLinuxSigInfo::%s Unknown architecture", + __FUNCTION__); + break; + } + + return error; +}