From f86248d9ba1909392211968400ea97bd06da04c6 Mon Sep 17 00:00:00 2001 From: Richard Mitton Date: Thu, 12 Sep 2013 02:20:34 +0000 Subject: [PATCH] Added a 'jump' command, similar to GDBs. This allows the PC to be directly changed to a different line. It's similar to the example python script in examples/python/jump.py, except implemented as a builtin. Also this version will track the current function correctly even if the target line resolves to multiple addresses. (e.g. debugging a templated function) llvm-svn: 190572 --- lldb/include/lldb/API/SBThread.h | 3 + lldb/include/lldb/Core/Error.h | 2 +- lldb/include/lldb/Core/Module.h | 26 +++ lldb/include/lldb/Core/ModuleList.h | 30 ++- lldb/include/lldb/Target/RegisterContext.h | 2 + lldb/include/lldb/Target/Thread.h | 5 +- lldb/scripts/Python/interface/SBThread.i | 7 +- lldb/source/API/SBThread.cpp | 25 +++ lldb/source/Commands/CommandObjectThread.cpp | 203 ++++++++++++++++++ lldb/source/Core/Error.cpp | 8 +- lldb/source/Core/Module.cpp | 24 ++- lldb/source/Core/ModuleList.cpp | 14 +- .../source/Interpreter/CommandInterpreter.cpp | 27 +++ lldb/source/Target/RegisterContext.cpp | 13 ++ lldb/source/Target/Thread.cpp | 73 +++++++ .../test/functionalities/thread/jump/Makefile | 4 + .../thread/jump/TestThreadJump.py | 75 +++++++ .../test/functionalities/thread/jump/main.cpp | 34 +++ .../functionalities/thread/jump/other.cpp | 13 ++ 19 files changed, 579 insertions(+), 9 deletions(-) create mode 100644 lldb/test/functionalities/thread/jump/Makefile create mode 100644 lldb/test/functionalities/thread/jump/TestThreadJump.py create mode 100644 lldb/test/functionalities/thread/jump/main.cpp create mode 100644 lldb/test/functionalities/thread/jump/other.cpp diff --git a/lldb/include/lldb/API/SBThread.h b/lldb/include/lldb/API/SBThread.h index 9645f925035e..9da75ac11025 100644 --- a/lldb/include/lldb/API/SBThread.h +++ b/lldb/include/lldb/API/SBThread.h @@ -117,6 +117,9 @@ public: lldb::SBFileSpec &file_spec, uint32_t line); + SBError + JumpToLine (lldb::SBFileSpec &file_spec, uint32_t line); + void RunToAddress (lldb::addr_t addr); diff --git a/lldb/include/lldb/Core/Error.h b/lldb/include/lldb/Core/Error.h index ab5f5f9bd0da..19fbfd30207d 100644 --- a/lldb/include/lldb/Core/Error.h +++ b/lldb/include/lldb/Core/Error.h @@ -68,7 +68,7 @@ public: Error (ValueType err, lldb::ErrorType type = lldb::eErrorTypeGeneric); explicit - Error (const char* err_str); + Error (const char *format, ...) __attribute__ ((format (printf, 2, 3))); Error (const Error &rhs); //------------------------------------------------------------------ diff --git a/lldb/include/lldb/Core/Module.h b/lldb/include/lldb/Core/Module.h index 9c135529f453..3f01eaf6c93c 100644 --- a/lldb/include/lldb/Core/Module.h +++ b/lldb/include/lldb/Core/Module.h @@ -346,6 +346,32 @@ public: bool append, SymbolContextList& sc_list); + //------------------------------------------------------------------ + /// Find addresses by file/line + /// + /// @param[in] target_sp + /// The target the addresses are desired for. + /// + /// @param[in] file + /// Source file to locate. + /// + /// @param[in] line + /// Source line to locate. + /// + /// @param[in] function + /// Optional filter function. Addresses within this function will be + /// added to the 'local' list. All others will be added to the 'extern' list. + /// + /// @param[out] output_local + /// All matching addresses within 'function' + /// + /// @param[out] output_extern + /// All matching addresses not within 'function' + void FindAddressesForLine (const lldb::TargetSP target_sp, + const FileSpec &file, uint32_t line, + Function *function, + std::vector
&output_local, std::vector
&output_extern); + //------------------------------------------------------------------ /// Find global and static variables by name. /// diff --git a/lldb/include/lldb/Core/ModuleList.h b/lldb/include/lldb/Core/ModuleList.h index 1198e4196481..f03f79fb00ce 100644 --- a/lldb/include/lldb/Core/ModuleList.h +++ b/lldb/include/lldb/Core/ModuleList.h @@ -439,7 +439,35 @@ public: bool FindSourceFile (const FileSpec &orig_spec, FileSpec &new_spec) const; - + + + //------------------------------------------------------------------ + /// Find addresses by file/line + /// + /// @param[in] target_sp + /// The target the addresses are desired for. + /// + /// @param[in] file + /// Source file to locate. + /// + /// @param[in] line + /// Source line to locate. + /// + /// @param[in] function + /// Optional filter function. Addresses within this function will be + /// added to the 'local' list. All others will be added to the 'extern' list. + /// + /// @param[out] output_local + /// All matching addresses within 'function' + /// + /// @param[out] output_extern + /// All matching addresses not within 'function' + void FindAddressesForLine (const lldb::TargetSP target_sp, + const FileSpec &file, uint32_t line, + Function *function, + std::vector
&output_local, std::vector
&output_extern); + + bool Remove (const lldb::ModuleSP &module_sp); diff --git a/lldb/include/lldb/Target/RegisterContext.h b/lldb/include/lldb/Target/RegisterContext.h index dd0e73fc7eb3..bc24cf92e290 100644 --- a/lldb/include/lldb/Target/RegisterContext.h +++ b/lldb/include/lldb/Target/RegisterContext.h @@ -129,6 +129,8 @@ public: bool SetPC (uint64_t pc); + bool SetPC (Address addr); + uint64_t GetSP (uint64_t fail_value = LLDB_INVALID_ADDRESS); diff --git a/lldb/include/lldb/Target/Thread.h b/lldb/include/lldb/Target/Thread.h index e4e532e4b331..88fcdd4b8149 100644 --- a/lldb/include/lldb/Target/Thread.h +++ b/lldb/include/lldb/Target/Thread.h @@ -377,7 +377,10 @@ public: Error ReturnFromFrame (lldb::StackFrameSP frame_sp, lldb::ValueObjectSP return_value_sp, bool broadcast = false); - + + Error + JumpToLine (const FileSpec &file, uint32_t line, bool can_leave_function, std::string *warnings = NULL); + virtual lldb::StackFrameSP GetFrameWithStackID (const StackID &stack_id) { diff --git a/lldb/scripts/Python/interface/SBThread.i b/lldb/scripts/Python/interface/SBThread.i index 91193df214a9..783b8ccb2615 100644 --- a/lldb/scripts/Python/interface/SBThread.i +++ b/lldb/scripts/Python/interface/SBThread.i @@ -136,10 +136,13 @@ public: StepInstruction(bool step_over); SBError - StepOverUntil (lldb::SBFrame &frame, - lldb::SBFileSpec &file_spec, + StepOverUntil (lldb::SBFrame &frame, + lldb::SBFileSpec &file_spec, uint32_t line); + SBError + JumpToLine (lldb::SBFileSpec &file_spec, uint32_t line); + void RunToAddress (lldb::addr_t addr); diff --git a/lldb/source/API/SBThread.cpp b/lldb/source/API/SBThread.cpp index 2752620c9baf..ceaaa34b2bbe 100644 --- a/lldb/source/API/SBThread.cpp +++ b/lldb/source/API/SBThread.cpp @@ -909,6 +909,31 @@ SBThread::StepOverUntil (lldb::SBFrame &sb_frame, return sb_error; } +SBError +SBThread::JumpToLine (lldb::SBFileSpec &file_spec, uint32_t line) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + SBError sb_error; + + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + if (log) + log->Printf ("SBThread(%p)::JumpToLine (file+line = %s:%u)", exe_ctx.GetThreadPtr(), file_spec->GetPath().c_str(), line); + + if (!exe_ctx.HasThreadScope()) + { + sb_error.SetErrorString("this SBThread object is invalid"); + return sb_error; + } + + Thread *thread = exe_ctx.GetThreadPtr(); + + Error err = thread->JumpToLine (file_spec.get(), line, true); + sb_error.SetError (err); + return sb_error; +} + SBError SBThread::ReturnFromFrame (SBFrame &frame, SBValue &return_value) { diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp index d1b52febbdfd..265d1dd3c257 100644 --- a/lldb/source/Commands/CommandObjectThread.cpp +++ b/lldb/source/Commands/CommandObjectThread.cpp @@ -1462,6 +1462,208 @@ CommandObjectThreadReturn::CommandOptions::g_option_table[] = { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } }; +//------------------------------------------------------------------------- +// CommandObjectThreadJump +//------------------------------------------------------------------------- + +class CommandObjectThreadJump : public CommandObjectParsed +{ +public: + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + OptionParsingStarting (); + } + + void + OptionParsingStarting () + { + m_filenames.Clear(); + m_line_num = 0; + m_line_offset = 0; + m_load_addr = LLDB_INVALID_ADDRESS; + m_force = false; + } + + virtual + ~CommandOptions () + { + } + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + bool success; + const int short_option = m_getopt_table[option_idx].val; + Error error; + + switch (short_option) + { + case 'f': + m_filenames.AppendIfUnique (FileSpec(option_arg, false)); + if (m_filenames.GetSize() > 1) + return Error("only one source file expected."); + break; + case 'l': + m_line_num = Args::StringToUInt32 (option_arg, 0, 0, &success); + if (!success || m_line_num == 0) + return Error("invalid line number: '%s'.", option_arg); + break; + case 'b': + m_line_offset = Args::StringToSInt32 (option_arg, 0, 0, &success); + if (!success) + return Error("invalid line offset: '%s'.", option_arg); + break; + case 'a': + { + ExecutionContext exe_ctx (m_interpreter.GetExecutionContext()); + m_load_addr = Args::StringToAddress(&exe_ctx, option_arg, LLDB_INVALID_ADDRESS, &error); + } + break; + case 'r': + m_force = true; + break; + + default: + return Error("invalid short option character '%c'", short_option); + + } + return error; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + FileSpecList m_filenames; + uint32_t m_line_num; + int32_t m_line_offset; + lldb::addr_t m_load_addr; + bool m_force; + + static OptionDefinition g_option_table[]; + }; + + virtual + Options * + GetOptions () + { + return &m_options; + } + + CommandObjectThreadJump (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "thread jump", + "Sets the program counter to a new address.", + "thread jump", + eFlagRequiresFrame | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused ), + m_options (interpreter) + { + } + + ~CommandObjectThreadJump() + { + } + +protected: + + bool DoExecute (Args& args, CommandReturnObject &result) + { + RegisterContext *reg_ctx = m_exe_ctx.GetRegisterContext(); + StackFrame *frame = m_exe_ctx.GetFramePtr(); + Thread *thread = m_exe_ctx.GetThreadPtr(); + Target *target = m_exe_ctx.GetTargetPtr(); + const SymbolContext &sym_ctx = frame->GetSymbolContext (eSymbolContextLineEntry); + + if (m_options.m_load_addr != LLDB_INVALID_ADDRESS) + { + // Use this address directly. + Address dest = Address(m_options.m_load_addr); + + lldb::addr_t callAddr = dest.GetCallableLoadAddress (target); + if (callAddr == LLDB_INVALID_ADDRESS) + { + result.AppendErrorWithFormat ("Invalid destination address."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (!reg_ctx->SetPC (callAddr)) + { + result.AppendErrorWithFormat ("Error changing PC value for thread %d.", thread->GetIndexID()); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + else + { + // Pick either the absolute line, or work out a relative one. + int32_t line = (int32_t)m_options.m_line_num; + if (line == 0) + line = sym_ctx.line_entry.line + m_options.m_line_offset; + + // Try the current file, but override if asked. + FileSpec file = sym_ctx.line_entry.file; + if (m_options.m_filenames.GetSize() == 1) + file = m_options.m_filenames.GetFileSpecAtIndex(0); + + if (!file) + { + result.AppendErrorWithFormat ("No source file available for the current location."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + std::string warnings; + Error err = thread->JumpToLine (file, line, m_options.m_force, &warnings); + + if (err.Fail()) + { + result.SetError (err); + return false; + } + + if (!warnings.empty()) + result.AppendWarning (warnings.c_str()); + } + + result.SetStatus (eReturnStatusSuccessFinishResult); + return true; + } + + CommandOptions m_options; +}; +OptionDefinition +CommandObjectThreadJump::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "file", 'f', OptionParser::eRequiredArgument, NULL, CommandCompletions::eSourceFileCompletion, eArgTypeFilename, + "Specifies the source file to jump to."}, + + { LLDB_OPT_SET_1, true, "line", 'l', OptionParser::eRequiredArgument, NULL, 0, eArgTypeLineNum, + "Specifies the line number to jump to."}, + + { LLDB_OPT_SET_2, true, "by", 'b', OptionParser::eRequiredArgument, NULL, 0, eArgTypeOffset, + "Jumps by a relative line offset from the current line."}, + + { LLDB_OPT_SET_3, true, "address", 'a', OptionParser::eRequiredArgument, NULL, 0, eArgTypeAddressOrExpression, + "Jumps to a specific address."}, + + { LLDB_OPT_SET_1| + LLDB_OPT_SET_2| + LLDB_OPT_SET_3, false, "force",'r', OptionParser::eNoArgument, NULL, 0, eArgTypeNone,"Allows the PC to leave the current function."}, + + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + //------------------------------------------------------------------------- // CommandObjectMultiwordThread //------------------------------------------------------------------------- @@ -1476,6 +1678,7 @@ CommandObjectMultiwordThread::CommandObjectMultiwordThread (CommandInterpreter & LoadSubCommand ("continue", CommandObjectSP (new CommandObjectThreadContinue (interpreter))); LoadSubCommand ("list", CommandObjectSP (new CommandObjectThreadList (interpreter))); LoadSubCommand ("return", CommandObjectSP (new CommandObjectThreadReturn (interpreter))); + LoadSubCommand ("jump", CommandObjectSP (new CommandObjectThreadJump (interpreter))); LoadSubCommand ("select", CommandObjectSP (new CommandObjectThreadSelect (interpreter))); LoadSubCommand ("until", CommandObjectSP (new CommandObjectThreadUntil (interpreter))); LoadSubCommand ("step-in", CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ( diff --git a/lldb/source/Core/Error.cpp b/lldb/source/Core/Error.cpp index bca30be6813c..7aabe5b386d4 100644 --- a/lldb/source/Core/Error.cpp +++ b/lldb/source/Core/Error.cpp @@ -52,12 +52,16 @@ Error::Error (const Error &rhs) : { } -Error::Error (const char* err_str): +Error::Error (const char* format, ...): m_code (0), m_type (eErrorTypeInvalid), m_string () { - SetErrorString(err_str); + va_list args; + va_start (args, format); + SetErrorToGenericError (); + SetErrorStringWithVarArg (format, args); + va_end (args); } //---------------------------------------------------------------------- diff --git a/lldb/source/Core/Module.cpp b/lldb/source/Core/Module.cpp index 4252ed4cb6c6..859ec866ac03 100644 --- a/lldb/source/Core/Module.cpp +++ b/lldb/source/Core/Module.cpp @@ -9,6 +9,7 @@ #include "lldb/lldb-python.h" +#include "lldb/Core/AddressResolverFileLine.h" #include "lldb/Core/Error.h" #include "lldb/Core/Module.h" #include "lldb/Core/DataBuffer.h" @@ -752,6 +753,27 @@ Module::FindFunctions (const RegularExpression& regex, return sc_list.GetSize() - start_size; } +void +Module::FindAddressesForLine (const lldb::TargetSP target_sp, + const FileSpec &file, uint32_t line, + Function *function, + std::vector
&output_local, std::vector
&output_extern) +{ + SearchFilterByModule filter(target_sp, m_file); + AddressResolverFileLine resolver(file, line, true); + resolver.ResolveAddress (filter); + + for (size_t n=0;n &output_local, std::vector
&output_extern) +{ + Mutex::Locker locker(m_modules_mutex); + collection::const_iterator pos, end = m_modules.end(); + for (pos = m_modules.begin(); pos != end; ++pos) + { + (*pos)->FindAddressesForLine(target_sp, file, line, function, output_local, output_extern); + } +} ModuleSP ModuleList::FindFirstModule (const ModuleSpec &module_spec) const diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp index 16243f9c6264..67ca12f4c234 100644 --- a/lldb/source/Interpreter/CommandInterpreter.cpp +++ b/lldb/source/Interpreter/CommandInterpreter.cpp @@ -228,6 +228,13 @@ CommandInterpreter::Initialize () AddAlias ("t", cmd_obj_sp); } + cmd_obj_sp = GetCommandSPExact ("_regexp-jump",false); + if (cmd_obj_sp) + { + AddAlias ("j", cmd_obj_sp); + AddAlias ("jump", cmd_obj_sp); + } + cmd_obj_sp = GetCommandSPExact ("_regexp-list", false); if (cmd_obj_sp) { @@ -618,6 +625,26 @@ CommandInterpreter::LoadCommandDictionary () } } + std::unique_ptr + jump_regex_cmd_ap(new CommandObjectRegexCommand (*this, + "_regexp-jump", + "Sets the program counter to a new address.", + "_regexp-jump []\n" + "_regexp-jump [<+-lineoffset>]\n" + "_regexp-jump [:]\n" + "_regexp-jump [*]\n", 2)); + if (jump_regex_cmd_ap.get()) + { + if (jump_regex_cmd_ap->AddRegexCommand("^\\*(.*)$", "thread jump --addr %1") && + jump_regex_cmd_ap->AddRegexCommand("^([0-9]+)$", "thread jump --line %1") && + jump_regex_cmd_ap->AddRegexCommand("^([^:]+):([0-9]+)$", "thread jump --file %1 --line %2") && + jump_regex_cmd_ap->AddRegexCommand("^([+\\-][0-9]+)$", "thread jump --by %1")) + { + CommandObjectSP jump_regex_cmd_sp(jump_regex_cmd_ap.release()); + m_command_dict[jump_regex_cmd_sp->GetCommandName ()] = jump_regex_cmd_sp; + } + } + } int diff --git a/lldb/source/Target/RegisterContext.cpp b/lldb/source/Target/RegisterContext.cpp index b422ef356f4f..7c86ed7a83d4 100644 --- a/lldb/source/Target/RegisterContext.cpp +++ b/lldb/source/Target/RegisterContext.cpp @@ -113,6 +113,19 @@ RegisterContext::SetPC(uint64_t pc) return success; } +bool +RegisterContext::SetPC(Address addr) +{ + TargetSP target_sp = m_thread.CalculateTarget(); + Target *target = target_sp.get(); + + lldb::addr_t callAddr = addr.GetCallableLoadAddress (target); + if (callAddr == LLDB_INVALID_ADDRESS) + return false; + + return SetPC (callAddr); +} + uint64_t RegisterContext::GetSP(uint64_t fail_value) { diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp index b65434bf9482..dde5755efd68 100644 --- a/lldb/source/Target/Thread.cpp +++ b/lldb/source/Target/Thread.cpp @@ -1745,6 +1745,79 @@ Thread::ReturnFromFrame (lldb::StackFrameSP frame_sp, lldb::ValueObjectSP return return return_error; } +static void DumpAddressList (Stream &s, const std::vector
&list, ExecutionContextScope *exe_scope) +{ + for (size_t n=0;nGetSymbolContext(eSymbolContextFunction); + + // Find candidate locations. + std::vector
candidates, within_function, outside_function; + target->GetImages().FindAddressesForLine (target_sp, file, line, sc.function, within_function, outside_function); + + // If possible, we try and stay within the current function. + // Within a function, we accept multiple locations (optimized code may do this, + // there's no solution here so we do the best we can). + // However if we're trying to leave the function, we don't know how to pick the + // right location, so if there's more than one then we bail. + if (!within_function.empty()) + candidates = within_function; + else if (outside_function.size() == 1 && can_leave_function) + candidates = outside_function; + + // Check if we got anything. + if (candidates.empty()) + { + if (outside_function.empty()) + { + return Error("Cannot locate an address for %s:%i.", + file.GetFilename().AsCString(), line); + } + else if (outside_function.size() == 1) + { + return Error("%s:%i is outside the current function.", + file.GetFilename().AsCString(), line); + } + else + { + StreamString sstr; + DumpAddressList(sstr, outside_function, target); + return Error("%s:%i has multiple candidate locations:\n%s", + file.GetFilename().AsCString(), line, sstr.GetString().c_str()); + } + } + + // Accept the first location, warn about any others. + Address dest = candidates[0]; + if (warnings && candidates.size() > 1) + { + StreamString sstr; + sstr.Printf("%s:%i appears multiple times in this function, selecting the first location:\n", + file.GetFilename().AsCString(), line); + DumpAddressList(sstr, candidates, target); + *warnings = sstr.GetString(); + } + + if (!reg_ctx->SetPC (dest)) + return Error("Cannot change PC to target address."); + + return Error(); +} + void Thread::DumpUsingSettingsFormat (Stream &strm, uint32_t frame_idx) { diff --git a/lldb/test/functionalities/thread/jump/Makefile b/lldb/test/functionalities/thread/jump/Makefile new file mode 100644 index 000000000000..9d032c106deb --- /dev/null +++ b/lldb/test/functionalities/thread/jump/Makefile @@ -0,0 +1,4 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp other.cpp +include $(LEVEL)/Makefile.rules diff --git a/lldb/test/functionalities/thread/jump/TestThreadJump.py b/lldb/test/functionalities/thread/jump/TestThreadJump.py new file mode 100644 index 000000000000..47e48fd05686 --- /dev/null +++ b/lldb/test/functionalities/thread/jump/TestThreadJump.py @@ -0,0 +1,75 @@ +""" +Test jumping to different places. +""" + +import os, time +import unittest2 +import lldb +from lldbtest import * +import lldbutil + +class ThreadJumpTestCase(TestBase): + + mydir = os.path.join("functionalities", "thread", "jump") + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + @dsym_test + def test_with_dsym(self): + """Test thread jump handling.""" + self.buildDsym(dictionary=self.getBuildFlags()) + self.thread_jump_test() + + @dwarf_test + def test_with_dwarf(self): + """Test thread jump handling.""" + self.buildDwarf(dictionary=self.getBuildFlags()) + self.thread_jump_test() + + def do_min_test(self, start, jump, var, value): + self.runCmd("j %i" % start) # jump to the start marker + self.runCmd("thread step-in") # step into the min fn + self.runCmd("j %i" % jump) # jump to the branch we're interested in + self.runCmd("thread step-out") # return out + self.runCmd("thread step-over") # assign to the global + self.expect("expr %s" % var, substrs = [value]) # check it + + def thread_jump_test(self): + """Test thread exit handling.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Find the line numbers for our breakpoints. + self.mark1 = line_number('main.cpp', '// 1st marker') + self.mark2 = line_number('main.cpp', '// 2nd marker') + self.mark3 = line_number('main.cpp', '// 3rd marker') + self.mark4 = line_number('main.cpp', '// 4th marker') + self.mark5 = line_number('other.cpp', '// other marker') + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.mark3, num_expected_locations=1) + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint 1. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT + " 1", + substrs = ['stopped', + '* thread #1', + 'stop reason = breakpoint 1']) + + self.do_min_test(self.mark3, self.mark1, "i", "4"); # Try the int path, force it to return 'a' + self.do_min_test(self.mark3, self.mark2, "i", "5"); # Try the int path, force it to return 'b' + self.do_min_test(self.mark4, self.mark1, "j", "7"); # Try the double path, force it to return 'a' + self.do_min_test(self.mark4, self.mark2, "j", "8"); # Try the double path, force it to return 'b' + + # Try jumping to another function in a different file. + self.runCmd("thread jump --file other.cpp --line %i --force" % self.mark5) + self.expect("process status", + substrs = ["at other.cpp:%i" % self.mark5]) + + # Try jumping to another function (without forcing) + self.expect("j main.cpp:%i" % self.mark1, COMMAND_FAILED_AS_EXPECTED, error = True, + substrs = ["error"]) + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() diff --git a/lldb/test/functionalities/thread/jump/main.cpp b/lldb/test/functionalities/thread/jump/main.cpp new file mode 100644 index 000000000000..b60fd4570bb6 --- /dev/null +++ b/lldb/test/functionalities/thread/jump/main.cpp @@ -0,0 +1,34 @@ +//===-- 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 program counter jumps. + +int otherfn(); + +template +T min(T a, T b) +{ + if (a < b) + { + return a; // 1st marker + } else { + return b; // 2nd marker + } +} + +int main () +{ + int i; + double j; + + i = min(4, 5); // 3rd marker + j = min(7.0, 8.0); // 4th marker + + return 0; +} diff --git a/lldb/test/functionalities/thread/jump/other.cpp b/lldb/test/functionalities/thread/jump/other.cpp new file mode 100644 index 000000000000..8108a52c163d --- /dev/null +++ b/lldb/test/functionalities/thread/jump/other.cpp @@ -0,0 +1,13 @@ +//===-- other.cpp -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +int otherfn() +{ + return 4; // other marker +}