diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h index 77b046f4a283..367970e8739d 100644 --- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h +++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h @@ -77,6 +77,15 @@ public: BreakpointOptions *bp_options, CommandReturnObject &result); + /// Set a one-liner as the callback for the breakpoint command. + virtual void + SetBreakpointCommandCallback (CommandInterpreter &interpreter, + BreakpointOptions *bp_options, + const char *oneliner) + { + return; + } + const char * GetScriptInterpreterPtyName (); diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreterPython.h b/lldb/include/lldb/Interpreter/ScriptInterpreterPython.h index 8be353b0a28a..e24d0efefea4 100644 --- a/lldb/include/lldb/Interpreter/ScriptInterpreterPython.h +++ b/lldb/include/lldb/Interpreter/ScriptInterpreterPython.h @@ -62,6 +62,12 @@ public: BreakpointOptions *bp_options, CommandReturnObject &result); + /// Set a one-liner as the callback for the breakpoint command. + void + SetBreakpointCommandCallback (CommandInterpreter &interpreter, + BreakpointOptions *bp_options, + const char *oneliner); + StringList ReadCommandInputFromUser (FILE *in_file); diff --git a/lldb/source/Commands/CommandObjectBreakpointCommand.cpp b/lldb/source/Commands/CommandObjectBreakpointCommand.cpp index 60c7d8fef5ed..396933a03810 100644 --- a/lldb/source/Commands/CommandObjectBreakpointCommand.cpp +++ b/lldb/source/Commands/CommandObjectBreakpointCommand.cpp @@ -258,9 +258,15 @@ CommandObjectBreakpointCommandAdd::Execute { if (m_options.m_use_script_language) { - interpreter.GetScriptInterpreter()->CollectDataForBreakpointCommandCallback (interpreter, - bp_loc_sp->GetLocationOptions(), - result); + // Special handling for one-liner. + if (command.GetArgumentCount() == 2) + interpreter.GetScriptInterpreter()->SetBreakpointCommandCallback (interpreter, + bp_loc_sp->GetLocationOptions(), + command.GetArgumentAtIndex(1)); + else + interpreter.GetScriptInterpreter()->CollectDataForBreakpointCommandCallback (interpreter, + bp_loc_sp->GetLocationOptions(), + result); } else { @@ -274,9 +280,15 @@ CommandObjectBreakpointCommandAdd::Execute { if (m_options.m_use_script_language) { - interpreter.GetScriptInterpreter()->CollectDataForBreakpointCommandCallback (interpreter, - bp->GetOptions(), - result); + // Special handling for one-liner. + if (command.GetArgumentCount() == 2) + interpreter.GetScriptInterpreter()->SetBreakpointCommandCallback (interpreter, + bp->GetOptions(), + command.GetArgumentAtIndex(1)); + else + interpreter.GetScriptInterpreter()->CollectDataForBreakpointCommandCallback (interpreter, + bp->GetOptions(), + result); } else { diff --git a/lldb/source/Interpreter/ScriptInterpreterPython.cpp b/lldb/source/Interpreter/ScriptInterpreterPython.cpp index f39a0ad8b539..ea8a11c2471e 100644 --- a/lldb/source/Interpreter/ScriptInterpreterPython.cpp +++ b/lldb/source/Interpreter/ScriptInterpreterPython.cpp @@ -653,6 +653,26 @@ ScriptInterpreterPython::CollectDataForBreakpointCommandCallback (CommandInterpr } } +// Set a Python one-liner as the callback for the breakpoint command. +void +ScriptInterpreterPython::SetBreakpointCommandCallback (CommandInterpreter &interpreter, + BreakpointOptions *bp_options, + const char *oneliner) +{ + std::auto_ptr data_ap(new BreakpointOptions::CommandData()); + + // It's necessary to set both user_source and script_source to the oneliner. + // The former is used to generate callback description (as in breakpoint command list) + // while the latter is used for Python to interpret during the actual callback. + data_ap->user_source.AppendString (oneliner); + data_ap->script_source.AppendString (oneliner); + + BatonSP baton_sp (new BreakpointOptions::CommandBaton (data_ap.release())); + bp_options->SetCallback (ScriptInterpreterPython::BreakpointCallbackFunction, baton_sp); + + return; +} + bool ScriptInterpreterPython::ExportFunctionDefinitionToInterpreter (StringList &function_def) { diff --git a/lldb/test/conditional_break/TestConditionalBreak.py b/lldb/test/conditional_break/TestConditionalBreak.py index 20ccca6f1028..d00c3a452000 100644 --- a/lldb/test/conditional_break/TestConditionalBreak.py +++ b/lldb/test/conditional_break/TestConditionalBreak.py @@ -13,16 +13,27 @@ class ConditionalBreakTestCase(TestBase): mydir = "conditional_break" @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") - def test_with_dsym(self): + def test_with_dsym_python(self): """Exercise some thread and frame APIs to break if c() is called by a().""" self.buildDsym() self.do_conditional_break() - def test_with_dwarf(self): + def test_with_dwarf_python(self): """Exercise some thread and frame APIs to break if c() is called by a().""" self.buildDwarf() self.do_conditional_break() + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + def test_with_dsym_command(self): + """Simulate a user using lldb commands to break on c() if called from a().""" + self.buildDsym() + self.simulate_conditional_break_by_user() + + def test_with_dwarf_command(self): + """Simulate a user using lldb commands to break on c() if called from a().""" + self.buildDwarf() + self.simulate_conditional_break_by_user() + def do_conditional_break(self): """Exercise some thread and frame APIs to break if c() is called by a().""" exe = os.path.join(os.getcwd(), "a.out") @@ -70,6 +81,33 @@ class ConditionalBreakTestCase(TestBase): #process.Continue() self.runCmd("process continue") + def simulate_conditional_break_by_user(self): + """Simulate a user using lldb commands to break on c() if called from a().""" + + # Sourcing .lldb in the current working directory, which sets the main + # executable, sets the breakpoint on c(), and adds the callback for the + # breakpoint such that lldb only stops when the caller of c() is a(). + # the "my" package that defines the date() function. + self.runCmd("command source .lldb") + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['state is Stopped', 'stop reason = breakpoint']) + + # The frame info for frame #0 points to a.out`c and its immediate caller + # (frame #1) points to a.out`a. + + self.expect("frame info", "We should stop at c()", + substrs = ["a.out`c"]) + + # Select our parent frame as the current frame. + self.runCmd("frame select 1") + self.expect("frame info", "The immediate caller should be a()", + substrs = ["a.out`a"]) + + if __name__ == '__main__': import atexit