Skip to content

Commit

Permalink
Patch for lldb bug 26322 “core load hangs”
Browse files Browse the repository at this point in the history
Summary:
This patch changes the way ProcessElfCore.cpp handles signal information.
The patch changes ProcessElfCore.cpp to use the signal from si_signo in SIGINFO notes in preference to the value of cursig in PRSTATUS notes. The value from SIGINFO seems to be more thread specific. The value from PRSTATUS is usually the same for all threads even if only one thread received a signal.
If it cannot find any SIGINFO blocks it reverts to the old behaviour and uses the value from cursig in PRSTATUS. If after that no thread appears to have been stopped it forces the status of the first thread to be SIGSTOP to prevent lldb hanging waiting for any thread from the core file to change state.

The order is:
- If one or more threads have a non-zero si_signo in SIGINFO that will be used.
- If no threads had a SIGINFO block with a non-zero si_signo set all threads signals to the value in cursig in their PRSTATUS notes.
- If no thread has a signal set to a non-zero value set the signal for only the first thread to SIGSTOP.


This resolves two issues. The first was identified in bug 26322, the second became apparent while investigating this problem and looking at the signal values reported for each thread via “thread list”.

Firstly lldb is able to load core dumps generated by gcore where each thread has a SIGINFO note containing a signal number but cursig in the PRSTATUS block for each thread is 0.

Secondly if a SIGINFO note was found the “thread list” command will no longer show the same signal number for all threads. At the moment if a process crashes, for example with SIGILL, all threads will show “stop reason = signal SIGILL”. With this patch only the thread that executed the illegal instruction shows that stop reason. The other threads show “stop reason = signal 0”.

Reviewers: jingham, clayborg

Subscribers: sas, labath, lldb-commits

Differential Revision: https://reviews.llvm.org/D26676

llvm-svn: 287858
  • Loading branch information
hhellyer committed Nov 24, 2016
1 parent 1c7f07a commit 1a2ac9b
Show file tree
Hide file tree
Showing 15 changed files with 477 additions and 2 deletions.
@@ -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)
@@ -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 <atomic>
#include <thread>
#include <csignal>

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;
}
@@ -0,0 +1,5 @@
LEVEL = ../../../../make

CXX_SOURCES := main.cpp
ENABLE_THREADS := YES
include $(LEVEL)/Makefile.rules
@@ -0,0 +1,56 @@
#! /bin/sh

linux_check_ptrace_scope()
{
if grep -q '1' </proc/sys/kernel/yama/ptrace_scope; then
cat <<EOF
Your system prevents the use of PTRACE to attach to non-child processes. The core file
cannot be generated. Please reset /proc/sys/kernel/yama/ptrace_scope to 0 (requires root
privileges) to enable core generation via gcore.
EOF
exit 1
fi
}

set -e -x

OS=$(uname -s)
if [ "$OS" = Linux ]; then
linux_check_ptrace_scope
fi

rm -f a.out
make -f main.mk

cat <<EOF
Executable file is in a.out.
Core file will be saved as core.<pid>.
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
@@ -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)
@@ -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 <atomic>
#include <thread>
#include <csignal>

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;
}
@@ -0,0 +1,5 @@
LEVEL = ../../../../make

CXX_SOURCES := main.cpp
ENABLE_THREADS := YES
include $(LEVEL)/Makefile.rules
@@ -0,0 +1,64 @@
#! /bin/sh

linux_check_core_pattern()
{
if grep -q '^|' </proc/sys/kernel/core_pattern; then
cat <<EOF
Your system uses a crash report tool ($(cat /proc/sys/kernel/core_pattern)). Core files
will not be generated. Please reset /proc/sys/kernel/core_pattern (requires root
privileges) to enable core generation.
EOF
exit 1
fi
}

OS=$(uname -s)
case "$OS" in
FreeBSD)
core_pattern=$(sysctl -n kern.corefile)
;;
Linux)
core_pattern=$(cat /proc/sys/kernel/core_pattern)
;;
*)
echo "OS $OS not supported" >&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 <<EOF
Unable to increase the core file limit. Core file may be truncated!
To fix this, increase HARD core file limit (ulimit -H -c 1000). This may require root
privileges.
EOF
fi

rm -f a.out
make -f main.mk

cat <<EOF
Executable file is in a.out.
Core file will be saved according to pattern $core_pattern.
EOF

# Save stack size and core_dump_filter
stack_size=`ulimit -s`
ulimit -Ss 32 # Decrease stack size to 32k => 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

0 comments on commit 1a2ac9b

Please sign in to comment.