diff --git a/lldb/source/Commands/CommandObjectTarget.cpp b/lldb/source/Commands/CommandObjectTarget.cpp --- a/lldb/source/Commands/CommandObjectTarget.cpp +++ b/lldb/source/Commands/CommandObjectTarget.cpp @@ -3962,8 +3962,12 @@ "name."), m_current_frame_option( LLDB_OPT_SET_2, false, "frame", 'F', - "Locate the debug symbols for the currently selected frame.", - false, true) + "Locate the debug symbols for the currently selected frame.", false, + true), + m_current_stack_option(LLDB_OPT_SET_2, false, "stack", 'S', + "Locate the debug symbols for every frame in " + "the current call stack.", + false, true) { m_option_group.Append(&m_uuid_option_group, LLDB_OPT_SET_ALL, @@ -3971,6 +3975,8 @@ m_option_group.Append(&m_file_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append(&m_current_frame_option, LLDB_OPT_SET_2, LLDB_OPT_SET_2); + m_option_group.Append(&m_current_stack_option, LLDB_OPT_SET_2, + LLDB_OPT_SET_2); m_option_group.Finalize(); } @@ -4247,6 +4253,63 @@ return true; } + bool AddSymbolsForStack(CommandReturnObject &result, bool &flush) { + assert(m_current_stack_option.GetOptionValue().OptionWasSet()); + + Process *process = m_exe_ctx.GetProcessPtr(); + if (!process) { + result.AppendError( + "a process must exist in order to use the --stack option"); + return false; + } + + const StateType process_state = process->GetState(); + if (!StateIsStoppedState(process_state, true)) { + result.AppendErrorWithFormat("process is not stopped: %s", + StateAsCString(process_state)); + return false; + } + + Thread *thread = m_exe_ctx.GetThreadPtr(); + if (!thread) { + result.AppendError("invalid current thread"); + return false; + } + + bool symbols_found = false; + uint32_t frame_count = thread->GetStackFrameCount(); + for (uint32_t i = 0; i < frame_count; ++i) { + lldb::StackFrameSP frame_sp = thread->GetStackFrameAtIndex(i); + + ModuleSP frame_module_sp( + frame_sp->GetSymbolContext(eSymbolContextModule).module_sp); + if (!frame_module_sp) + continue; + + ModuleSpec module_spec; + module_spec.GetUUID() = frame_module_sp->GetUUID(); + + if (FileSystem::Instance().Exists( + frame_module_sp->GetPlatformFileSpec())) { + module_spec.GetArchitecture() = frame_module_sp->GetArchitecture(); + module_spec.GetFileSpec() = frame_module_sp->GetPlatformFileSpec(); + } + + bool current_frame_flush = false; + if (DownloadObjectAndSymbolFile(module_spec, result, current_frame_flush)) + symbols_found = true; + flush |= current_frame_flush; + } + + if (!symbols_found) { + result.AppendError( + "unable to find debug symbols in the current call stack"); + return false; + } + + return true; + } + bool DoExecute(Args &args, CommandReturnObject &result) override { Target *target = m_exe_ctx.GetTargetPtr(); result.SetStatus(eReturnStatusFailed); @@ -4257,6 +4320,8 @@ const bool file_option_set = m_file_option.GetOptionValue().OptionWasSet(); const bool frame_option_set = m_current_frame_option.GetOptionValue().OptionWasSet(); + const bool stack_option_set = + m_current_stack_option.GetOptionValue().OptionWasSet(); const size_t argc = args.GetArgumentCount(); if (argc == 0) { @@ -4266,6 +4331,8 @@ AddSymbolsForFile(result, flush); else if (frame_option_set) AddSymbolsForFrame(result, flush); + else if (stack_option_set) + AddSymbolsForStack(result, flush); else result.AppendError("one or more symbol file paths must be specified, " "or options must be specified"); @@ -4335,6 +4402,7 @@ OptionGroupUUID m_uuid_option_group; OptionGroupFile m_file_option; OptionGroupBoolean m_current_frame_option; + OptionGroupBoolean m_current_stack_option; }; #pragma mark CommandObjectTargetSymbols diff --git a/lldb/test/API/macosx/add-dsym/TestAddDsymDownload.py b/lldb/test/API/macosx/add-dsym/TestAddDsymDownload.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/macosx/add-dsym/TestAddDsymDownload.py @@ -0,0 +1,98 @@ +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +@skipUnlessDarwin +class AddDsymDownload(TestBase): + + mydir = TestBase.compute_mydir(__file__) + dwarfdump_uuid_regex = re.compile('UUID: ([-0-9a-fA-F]+) \(([^\(]+)\) .*') + + def get_uuid(self): + dwarfdump_cmd_output = subprocess.check_output( + ('/usr/bin/dwarfdump --uuid "%s"' % self.exe), + shell=True).decode("utf-8") + for line in dwarfdump_cmd_output.splitlines(): + match = self.dwarfdump_uuid_regex.search(line) + if match: + return match.group(1) + return None + + def create_dsym_for_uuid(self): + shell_cmds = [ + '#! /bin/sh', '# the last argument is the uuid', + 'while [ $# -gt 1 ]', 'do', ' shift', 'done', 'ret=0', + 'echo ""', + 'echo ""', + 'echo ""', '', + 'if [ "$1" != "%s" ]' % (self.uuid), 'then', + ' echo "DBGErrornot found"', + ' echo ""', ' exit 1', 'fi', + ' uuid=%s' % self.uuid, + ' bin=%s' % self.exe, + ' dsym=%s' % self.dsym, 'echo "$uuid"', '', + 'echo "DBGDSYMPath$dsym"', + 'echo "DBGSymbolRichExecutable$bin"', + 'echo ""', 'exit $ret' + ] + + with open(self.dsym_for_uuid, "w") as writer: + for l in shell_cmds: + writer.write(l + '\n') + + os.chmod(self.dsym_for_uuid, 0o755) + + def setUp(self): + TestBase.setUp(self) + self.source = 'main.c' + self.exe = self.getBuildArtifact("a.out") + self.dsym = os.path.join( + self.getBuildDir(), + "hide.app/Contents/a.out.dSYM/Contents/Resources/DWARF/", + os.path.basename(self.exe)) + self.dsym_for_uuid = self.getBuildArtifact("dsym-for-uuid.sh") + + self.buildDefault(dictionary={'MAKE_DSYM': 'YES'}) + self.assertTrue(os.path.exists(self.exe)) + self.assertTrue(os.path.exists(self.dsym)) + + self.uuid = self.get_uuid() + self.assertNotEqual(self.uuid, None, "Could not get uuid for a.out") + + self.create_dsym_for_uuid() + + os.environ['LLDB_APPLE_DSYMFORUUID_EXECUTABLE'] = self.dsym_for_uuid + self.addTearDownHook( + lambda: os.environ.pop('LLDB_APPLE_DSYMFORUUID_EXECUTABLE', None)) + + def do_test(self, command): + self.target = self.dbg.CreateTarget(self.exe) + self.assertTrue(self.target, VALID_TARGET) + + main_bp = self.target.BreakpointCreateByName("main", "a.out") + self.assertTrue(main_bp, VALID_BREAKPOINT) + + self.process = self.target.LaunchSimple( + None, None, self.get_process_working_directory()) + self.assertTrue(self.process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + self.assertEquals(self.process.GetState(), lldb.eStateStopped, + STOPPED_DUE_TO_BREAKPOINT) + + self.runCmd(command) + self.expect("frame select", substrs=['a.out`main at main.c']) + + @no_debug_info_test + def test_frame(self): + self.do_test("add-dsym --frame") + + @no_debug_info_test + def test_uuid(self): + self.do_test("add-dsym --uuid {}".format(self.uuid)) + + @no_debug_info_test + def test_stack(self): + self.do_test("add-dsym --stack")