From a05677126d06cf1abc019db231f5f918cf08e3f7 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Sat, 10 Jan 2015 04:01:03 +0000 Subject: [PATCH] Hoist the RegisterNumber class out of RegisterContextLLDB and make it more generally available. Add checks to UnwindAssembly_x86::AugmentUnwindPlanFromCallSite() so that it won't try to augment an UnwindPlan that already describes the function epilogue. Add a test case for backtracing out of _sigtramp on Darwin systems. This could probably be adapted to test the same thing on linux/bsd but the function names of sigtramp and kill are probably platform specific and I'm not sure what they should be. llvm-svn: 225578 --- lldb/lldb.xcodeproj/project.pbxproj | 4 + .../Plugins/Process/Utility/RegisterContextLLDB.h | 160 +-------------------- .../UnwindAssembly/x86/UnwindAssembly-x86.cpp | 71 ++++++++- lldb/source/Utility/RegisterNumber.cpp | 151 +++++++++++++++++++ lldb/test/functionalities/unwind/sigtramp/Makefile | 5 + .../unwind/sigtramp/TestSigtrampUnwind.py | 94 ++++++++++++ lldb/test/functionalities/unwind/sigtramp/main.c | 27 ++++ 7 files changed, 350 insertions(+), 162 deletions(-) create mode 100644 lldb/source/Utility/RegisterNumber.cpp create mode 100644 lldb/test/functionalities/unwind/sigtramp/Makefile create mode 100644 lldb/test/functionalities/unwind/sigtramp/TestSigtrampUnwind.py create mode 100644 lldb/test/functionalities/unwind/sigtramp/main.c diff --git a/lldb/lldb.xcodeproj/project.pbxproj b/lldb/lldb.xcodeproj/project.pbxproj index 3f851ba..e637d50 100644 --- a/lldb/lldb.xcodeproj/project.pbxproj +++ b/lldb/lldb.xcodeproj/project.pbxproj @@ -745,6 +745,7 @@ AF1729D7182C907200E0AB97 /* HistoryUnwind.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF1729D5182C907200E0AB97 /* HistoryUnwind.cpp */; }; AF1F7B07189C904B0087DB9C /* AppleGetPendingItemsHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF1F7B05189C904B0087DB9C /* AppleGetPendingItemsHandler.cpp */; }; AF1F7B08189C904B0087DB9C /* AppleGetPendingItemsHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = AF1F7B06189C904B0087DB9C /* AppleGetPendingItemsHandler.h */; }; + AF1FA88A1A60A69500272AFC /* RegisterNumber.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF1FA8891A60A69500272AFC /* RegisterNumber.cpp */; }; AF23B4DB19009C66003E2A58 /* FreeBSDSignals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF23B4D919009C66003E2A58 /* FreeBSDSignals.cpp */; }; AF254E31170CCC33007AE5C9 /* PlatformDarwinKernel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF254E2F170CCC33007AE5C9 /* PlatformDarwinKernel.cpp */; }; AF254E32170CCC33007AE5C9 /* PlatformDarwinKernel.h in Headers */ = {isa = PBXBuildFile; fileRef = AF254E30170CCC33007AE5C9 /* PlatformDarwinKernel.h */; }; @@ -2200,6 +2201,7 @@ AF1729D5182C907200E0AB97 /* HistoryUnwind.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = HistoryUnwind.cpp; path = Utility/HistoryUnwind.cpp; sourceTree = ""; }; AF1F7B05189C904B0087DB9C /* AppleGetPendingItemsHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AppleGetPendingItemsHandler.cpp; sourceTree = ""; }; AF1F7B06189C904B0087DB9C /* AppleGetPendingItemsHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppleGetPendingItemsHandler.h; sourceTree = ""; }; + AF1FA8891A60A69500272AFC /* RegisterNumber.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterNumber.cpp; path = source/Utility/RegisterNumber.cpp; sourceTree = ""; }; AF23B4D919009C66003E2A58 /* FreeBSDSignals.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FreeBSDSignals.cpp; path = Utility/FreeBSDSignals.cpp; sourceTree = ""; }; AF23B4DA19009C66003E2A58 /* FreeBSDSignals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FreeBSDSignals.h; path = Utility/FreeBSDSignals.h; sourceTree = ""; }; AF254E2F170CCC33007AE5C9 /* PlatformDarwinKernel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PlatformDarwinKernel.cpp; sourceTree = ""; }; @@ -3069,6 +3071,7 @@ 94EBAC8313D9EE26009BA64E /* PythonPointer.h */, 94BA8B6E176F8CA0005A91B5 /* Range.h */, 94BA8B6C176F8C9B005A91B5 /* Range.cpp */, + AF1FA8891A60A69500272AFC /* RegisterNumber.cpp */, B2462249141AE62200F3D409 /* Utils.h */, ); name = Utility; @@ -5568,6 +5571,7 @@ 94F48F251A01C687005C0EC6 /* StringPrinter.cpp in Sources */, 94094C6B163B6F840083A547 /* ValueObjectCast.cpp in Sources */, AF9107EF168570D200DBCD3C /* RegisterContextDarwin_arm64.cpp in Sources */, + AF1FA88A1A60A69500272AFC /* RegisterNumber.cpp in Sources */, 94CB255B16B069770059775D /* CXXFormatterFunctions.cpp in Sources */, 94CB255C16B069770059775D /* DataVisualization.cpp in Sources */, 94CD705016F8DF1C00CF1E42 /* LibCxxList.cpp in Sources */, diff --git a/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.h b/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.h index 636e952..5f94a97 100644 --- a/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.h +++ b/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.h @@ -16,6 +16,7 @@ #include "lldb/Target/RegisterContext.h" #include "lldb/Symbol/UnwindPlan.h" #include "lldb/Symbol/SymbolContext.h" +#include "lldb/Utility/RegisterNumber.h" #include "UnwindLLDB.h" namespace lldb_private { @@ -86,165 +87,6 @@ public: private: - //-------------------------------------------------------------------- - /// A convenience class for RegisterContextLLDB which can convert between - /// different register kinds. - /// - /// This ends up being a common operation in RegisterContextLLDB as we try - /// to bridge between the different register numbering systems -- having a - /// simple object to enclose all of that conversion, and cache results so - /// we don't re-fetch, simplifies the code significantly. - //-------------------------------------------------------------------- - class RegisterNumber { - public: - RegisterNumber (lldb_private::Thread &thread, lldb::RegisterKind kind, uint32_t num) : - m_reg_ctx_sp (thread.GetRegisterContext()), - m_regnum (num), - m_kind (kind), - m_kind_regnum_map (), - m_name ("") - { - if (m_reg_ctx_sp.get()) - { - const lldb_private::RegisterInfo *reginfo = m_reg_ctx_sp->GetRegisterInfoAtIndex (GetAsKind (lldb::eRegisterKindLLDB)); - if (reginfo && reginfo->name) - { - m_name = reginfo->name; - } - } - } - - // This constructor plus the init() method below allow for the placeholder - // creation of an invalid object initially, possibly to be filled in. It - // would be more consistent to have three Set* methods to set the three - // data that the object needs. - RegisterNumber () : - m_reg_ctx_sp(), - m_regnum (LLDB_INVALID_REGNUM), - m_kind (lldb::kNumRegisterKinds), - m_kind_regnum_map (), - m_name (nullptr) - { - } - - void - init (lldb_private::Thread &thread, lldb::RegisterKind kind, uint32_t num) - { - m_reg_ctx_sp = thread.GetRegisterContext(); - m_regnum = num; - m_kind = kind; - if (m_reg_ctx_sp.get()) - { - const lldb_private::RegisterInfo *reginfo = m_reg_ctx_sp->GetRegisterInfoAtIndex (GetAsKind (lldb::eRegisterKindLLDB)); - if (reginfo && reginfo->name) - { - m_name = reginfo->name; - } - } - } - - const RegisterNumber & - operator = (const RegisterNumber &rhs) - { - m_reg_ctx_sp = rhs.m_reg_ctx_sp; - m_regnum = rhs.m_regnum; - m_kind = rhs.m_kind; - for (auto it : rhs.m_kind_regnum_map) - m_kind_regnum_map[it.first] = it.second; - m_name = rhs.m_name; - return *this; - } - - bool - operator == (RegisterNumber &rhs) - { - if (IsValid() != rhs.IsValid()) - return false; - - if (m_kind == rhs.m_kind) - { - if (m_regnum == rhs.m_regnum) - return true; - else - return false; - } - - uint32_t rhs_regnum = rhs.GetAsKind (m_kind); - if (rhs_regnum != LLDB_INVALID_REGNUM) - { - if (m_regnum == rhs_regnum) - return true; - else - return false; - } - uint32_t lhs_regnum = GetAsKind (rhs.m_kind); - { - if (lhs_regnum == rhs.m_regnum) - return true; - else - return false; - } - return false; - } - - bool - IsValid () const - { - return m_regnum != LLDB_INVALID_REGNUM; - } - - uint32_t - GetAsKind (lldb::RegisterKind kind) - { - if (m_regnum == LLDB_INVALID_REGNUM) - return LLDB_INVALID_REGNUM; - - if (kind == m_kind) - return m_regnum; - - Collection::iterator iter = m_kind_regnum_map.find (kind); - if (iter != m_kind_regnum_map.end()) - { - return iter->second; - } - uint32_t output_regnum = LLDB_INVALID_REGNUM; - if (m_reg_ctx_sp - && m_reg_ctx_sp->ConvertBetweenRegisterKinds (m_kind, m_regnum, kind, output_regnum) - && output_regnum != LLDB_INVALID_REGNUM) - { - m_kind_regnum_map[kind] = output_regnum; - } - return output_regnum; - } - - uint32_t - GetRegisterNumber () const - { - return m_regnum; - } - - lldb::RegisterKind - GetRegisterKind () const - { - return m_kind; - } - - const char * - GetName () - { - return m_name; - } - - private: - typedef std::map Collection; - - lldb::RegisterContextSP m_reg_ctx_sp; - uint32_t m_regnum; - lldb::RegisterKind m_kind; - Collection m_kind_regnum_map; - const char *m_name; - }; - enum FrameType { eNormalFrame, diff --git a/lldb/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp b/lldb/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp index 730784b..af70858 100644 --- a/lldb/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp +++ b/lldb/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp @@ -23,6 +23,7 @@ #include "lldb/Target/Thread.h" #include "lldb/Target/Target.h" #include "lldb/Target/UnwindAssembly.h" +#include "lldb/Utility/RegisterNumber.h" using namespace lldb; using namespace lldb_private; @@ -955,7 +956,9 @@ AssemblyParse_x86::augment_unwind_plan_from_call_site (AddressRange& func, Unwin // If we already have one row for this instruction, we can continue. while (row_id < unwind_plan.GetRowCount() && unwind_plan.GetRowAtIndex (row_id)->GetOffset() <= offset) + { row_id++; + } UnwindPlan::RowSP original_row = unwind_plan.GetRowAtIndex (row_id - 1); if (original_row->GetOffset() == offset) { @@ -1262,9 +1265,71 @@ UnwindAssembly_x86::GetNonCallSiteUnwindPlanFromAssembly (AddressRange& func, Th bool UnwindAssembly_x86::AugmentUnwindPlanFromCallSite (AddressRange& func, Thread& thread, UnwindPlan& unwind_plan) { - ExecutionContext exe_ctx (thread.shared_from_this()); - AssemblyParse_x86 asm_parse(exe_ctx, m_cpu, m_arch, func); - return asm_parse.augment_unwind_plan_from_call_site (func, unwind_plan); + bool do_augment_unwindplan = true; + + UnwindPlan::RowSP first_row = unwind_plan.GetRowForFunctionOffset (0); + UnwindPlan::RowSP last_row = unwind_plan.GetRowForFunctionOffset (-1); + + // If the UnwindPlan correctly describes the prologue and the epilogue, then + // we shouldn't do any augmentation (and risk messing up correct unwind instructions) + + // See if the first row (which should be the register state on function entry) + // and the last row (which should be the register state on function exit) match. + + if (first_row != last_row && first_row->GetOffset() != last_row->GetOffset()) + { + RegisterNumber sp_regnum (thread, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); + + // The first & last row have the same CFA register + // and the same CFA offset value + // and the CFA register is esp/rsp (the stack pointer). + + // We're checking that both of them have an unwind rule like "CFA=esp+4" or CFA+rsp+8". + + if (first_row->GetCFAType() == last_row->GetCFAType() + && first_row->GetCFAType() == UnwindPlan::Row::CFAType::CFAIsRegisterPlusOffset + && first_row->GetCFARegister() == last_row->GetCFARegister() + && first_row->GetCFAOffset() == last_row->GetCFAOffset() + && RegisterNumber (thread, unwind_plan.GetRegisterKind(), first_row->GetCFARegister()) == sp_regnum) + { + RegisterNumber pc_regnum (thread, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + + // Get the register locations for eip/rip from the first & last rows. + // Are they both CFA plus an offset? Is it the same offset? + + UnwindPlan::Row::RegisterLocation first_row_pc_loc; + UnwindPlan::Row::RegisterLocation last_row_pc_loc; + if (first_row->GetRegisterInfo (pc_regnum.GetAsKind (unwind_plan.GetRegisterKind()), first_row_pc_loc) + && last_row->GetRegisterInfo (pc_regnum.GetAsKind (unwind_plan.GetRegisterKind()), last_row_pc_loc)) + { + if (first_row_pc_loc.IsAtCFAPlusOffset() + && last_row_pc_loc.IsAtCFAPlusOffset() + && first_row_pc_loc.GetOffset() == last_row_pc_loc.GetOffset()) + { + + // One last sanity check: Is the unwind rule for getting the caller pc value + // "deref the CFA-4" or "deref the CFA-8"? + + // If so, we have an UnwindPlan that already describes the epilogue and we don't need + // to modify it at all. + + if (first_row_pc_loc.GetOffset() == -4 || first_row_pc_loc.GetOffset() == -8) + { + do_augment_unwindplan = false; + } + } + } + } + } + + if (do_augment_unwindplan) + { + ExecutionContext exe_ctx (thread.shared_from_this()); + AssemblyParse_x86 asm_parse(exe_ctx, m_cpu, m_arch, func); + return asm_parse.augment_unwind_plan_from_call_site (func, unwind_plan); + } + + return false; } bool diff --git a/lldb/source/Utility/RegisterNumber.cpp b/lldb/source/Utility/RegisterNumber.cpp new file mode 100644 index 0000000..ba9afed --- /dev/null +++ b/lldb/source/Utility/RegisterNumber.cpp @@ -0,0 +1,151 @@ +//===--------------------- RegisterNumber.cpp -------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/RegisterNumber.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/RegisterContext.h" + +using namespace lldb_private; + + +RegisterNumber::RegisterNumber (lldb_private::Thread &thread, lldb::RegisterKind kind, uint32_t num) : + m_reg_ctx_sp (thread.GetRegisterContext()), + m_regnum (num), + m_kind (kind), + m_kind_regnum_map (), + m_name ("") +{ + if (m_reg_ctx_sp.get()) + { + const lldb_private::RegisterInfo *reginfo = m_reg_ctx_sp->GetRegisterInfoAtIndex (GetAsKind (lldb::eRegisterKindLLDB)); + if (reginfo && reginfo->name) + { + m_name = reginfo->name; + } + } +} + +RegisterNumber::RegisterNumber () : + m_reg_ctx_sp(), + m_regnum (LLDB_INVALID_REGNUM), + m_kind (lldb::kNumRegisterKinds), + m_kind_regnum_map (), + m_name (nullptr) +{ +} + +void +RegisterNumber::init (lldb_private::Thread &thread, lldb::RegisterKind kind, uint32_t num) +{ + m_reg_ctx_sp = thread.GetRegisterContext(); + m_regnum = num; + m_kind = kind; + if (m_reg_ctx_sp.get()) + { + const lldb_private::RegisterInfo *reginfo = m_reg_ctx_sp->GetRegisterInfoAtIndex (GetAsKind (lldb::eRegisterKindLLDB)); + if (reginfo && reginfo->name) + { + m_name = reginfo->name; + } + } +} + +const RegisterNumber & +RegisterNumber::operator = (const RegisterNumber &rhs) +{ + m_reg_ctx_sp = rhs.m_reg_ctx_sp; + m_regnum = rhs.m_regnum; + m_kind = rhs.m_kind; + for (auto it : rhs.m_kind_regnum_map) + m_kind_regnum_map[it.first] = it.second; + m_name = rhs.m_name; + return *this; +} + +bool +RegisterNumber::operator == (RegisterNumber &rhs) +{ + if (IsValid() != rhs.IsValid()) + return false; + + if (m_kind == rhs.m_kind) + { + if (m_regnum == rhs.m_regnum) + return true; + else + return false; + } + + uint32_t rhs_regnum = rhs.GetAsKind (m_kind); + if (rhs_regnum != LLDB_INVALID_REGNUM) + { + if (m_regnum == rhs_regnum) + return true; + else + return false; + } + uint32_t lhs_regnum = GetAsKind (rhs.m_kind); + { + if (lhs_regnum == rhs.m_regnum) + return true; + else + return false; + } + return false; +} + +bool +RegisterNumber::IsValid () const +{ + return m_reg_ctx_sp.get() + && m_kind != lldb::kNumRegisterKinds + && m_regnum != LLDB_INVALID_REGNUM; +} + +uint32_t +RegisterNumber::GetAsKind (lldb::RegisterKind kind) +{ + if (m_regnum == LLDB_INVALID_REGNUM) + return LLDB_INVALID_REGNUM; + + if (kind == m_kind) + return m_regnum; + + Collection::iterator iter = m_kind_regnum_map.find (kind); + if (iter != m_kind_regnum_map.end()) + { + return iter->second; + } + uint32_t output_regnum = LLDB_INVALID_REGNUM; + if (m_reg_ctx_sp + && m_reg_ctx_sp->ConvertBetweenRegisterKinds (m_kind, m_regnum, kind, output_regnum) + && output_regnum != LLDB_INVALID_REGNUM) + { + m_kind_regnum_map[kind] = output_regnum; + } + return output_regnum; +} + +uint32_t +RegisterNumber::GetRegisterNumber () const +{ + return m_regnum; +} + +lldb::RegisterKind +RegisterNumber::GetRegisterKind () const +{ + return m_kind; +} + +const char * +RegisterNumber::GetName () +{ + return m_name; +} diff --git a/lldb/test/functionalities/unwind/sigtramp/Makefile b/lldb/test/functionalities/unwind/sigtramp/Makefile new file mode 100644 index 0000000..b09a579 --- /dev/null +++ b/lldb/test/functionalities/unwind/sigtramp/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/lldb/test/functionalities/unwind/sigtramp/TestSigtrampUnwind.py b/lldb/test/functionalities/unwind/sigtramp/TestSigtrampUnwind.py new file mode 100644 index 0000000..09eeddf --- /dev/null +++ b/lldb/test/functionalities/unwind/sigtramp/TestSigtrampUnwind.py @@ -0,0 +1,94 @@ +""" +Test that we can backtrace correctly with 'sigtramp' functions on the stack +""" + +import os, time +import unittest2 +import lldb +from lldbtest import * +import lldbutil + +class SigtrampUnwind(TestBase): + mydir = TestBase.compute_mydir(__file__) + + # On different platforms the "_sigtramp" and "__kill" frames are likely to be different. + # This test could probably be adapted to run on linux/*bsd easily enough. + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + @dsym_test + def test_with_dsym (self): + """Test that we can backtrace correctly with _sigtramp on the stack""" + self.buildDsym() + self.setTearDownCleanup() + self.sigtramp_unwind_tests() + + @dwarf_test + def test_with_dwarf (self): + """Test that we can backtrace correctly with _sigtramp on the stack""" + self.buildDwarf() + self.setTearDownCleanup() + self.sigtramp_unwind_tests() + + def sigtramp_unwind_tests (self): + exe = os.path.join(os.getcwd(), "a.out") + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + + lldbutil.run_break_set_by_file_and_line (self, "main.c", line_number('main.c', '// Set breakpoint here'), num_expected_locations=1) + + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + if not process: + self.fail("SBTarget.Launch() failed") + + if process.GetState() != lldb.eStateStopped: + self.fail("Process should be in the 'stopped' state, " + "instead the actual state is: '%s'" % + lldbutil.state_type_to_str(process.GetState())) + + self.expect("pro handle -n false -p true -s false SIGUSR1", "Have lldb pass SIGUSR1 signals", + substrs = ["SIGUSR1", "true", "false", "false"]) + + lldbutil.run_break_set_by_symbol (self, "handler", num_expected_locations=1, module_name="a.out") + + self.runCmd("continue") + + thread = process.GetThreadAtIndex(0) + + found_handler = False + found_sigtramp = False + found_kill = False + found_main = False + + for f in thread.frames: + if f.GetFunctionName() == "handler": + found_handler = True + if f.GetFunctionName() == "_sigtramp": + found_sigtramp = True + if f.GetFunctionName() == "__kill": + found_kill = True + if f.GetFunctionName() == "main": + found_main = True + + if self.TraceOn(): + print "Backtrace once we're stopped:" + for f in thread.frames: + print " %d %s" % (f.GetFrameID(), f.GetFunctionName()) + + if found_handler == False: + self.fail("Unable to find handler() in backtrace.") + + if found_sigtramp == False: + self.fail("Unable to find _sigtramp() in backtrace.") + + if found_kill == False: + self.fail("Unable to find kill() in backtrace.") + + if found_main == False: + self.fail("Unable to find main() in backtrace.") + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() diff --git a/lldb/test/functionalities/unwind/sigtramp/main.c b/lldb/test/functionalities/unwind/sigtramp/main.c new file mode 100644 index 0000000..aaa03e7 --- /dev/null +++ b/lldb/test/functionalities/unwind/sigtramp/main.c @@ -0,0 +1,27 @@ +#include +#include +#include +#include + +void handler (int in) +{ + puts ("in handler routine"); + while (1) + ; +} + +void +foo () +{ + puts ("in foo ()"); + kill (getpid(), SIGUSR1); +} +int main () +{ + puts ("in main"); // Set breakpoint here + signal (SIGUSR1, handler); + puts ("signal handler set up"); + foo(); + puts ("exiting"); + return 0; +} -- 2.7.4