diff options
Diffstat (limited to 'lldb/source')
127 files changed, 5806 insertions, 3211 deletions
diff --git a/lldb/source/API/SBCommandInterpreter.cpp b/lldb/source/API/SBCommandInterpreter.cpp index 4ea79d336e08..34323bc5a2c3 100644 --- a/lldb/source/API/SBCommandInterpreter.cpp +++ b/lldb/source/API/SBCommandInterpreter.cpp @@ -208,7 +208,7 @@ void SBCommandInterpreter::HandleCommandsFromFile( LLDB_INSTRUMENT_VA(this, file, override_context, options, result); if (!IsValid()) { - result->AppendError("SBCommandInterpreter is not valid."); + result->AppendError("SBCommandInterpreter is not valid"); return; } diff --git a/lldb/source/API/SBFrame.cpp b/lldb/source/API/SBFrame.cpp index b12cfceacd75..b6724bb0c411 100644 --- a/lldb/source/API/SBFrame.cpp +++ b/lldb/source/API/SBFrame.cpp @@ -1118,6 +1118,22 @@ bool SBFrame::IsArtificial() const { return false; } +bool SBFrame::IsSynthetic() const { + LLDB_INSTRUMENT_VA(this); + + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return false; + } + + if (StackFrame *frame = exe_ctx->GetFramePtr()) + return frame->IsSynthetic(); + + return false; +} + bool SBFrame::IsHidden() const { LLDB_INSTRUMENT_VA(this); diff --git a/lldb/source/API/SBFunction.cpp b/lldb/source/API/SBFunction.cpp index 19861f6af364..65b02d6b309c 100644 --- a/lldb/source/API/SBFunction.cpp +++ b/lldb/source/API/SBFunction.cpp @@ -79,6 +79,15 @@ const char *SBFunction::GetMangledName() const { return nullptr; } +const char *SBFunction::GetBaseName() const { + LLDB_INSTRUMENT_VA(this); + + if (!m_opaque_ptr) + return nullptr; + + return m_opaque_ptr->GetMangled().GetBaseName().AsCString(); +} + bool SBFunction::operator==(const SBFunction &rhs) const { LLDB_INSTRUMENT_VA(this, rhs); diff --git a/lldb/source/API/SBHostOS.cpp b/lldb/source/API/SBHostOS.cpp index a77a703bba37..cd9b8571df3e 100644 --- a/lldb/source/API/SBHostOS.cpp +++ b/lldb/source/API/SBHostOS.cpp @@ -86,15 +86,7 @@ SBFileSpec SBHostOS::GetLLDBPath(lldb::PathType path_type) { SBFileSpec SBHostOS::GetUserHomeDirectory() { LLDB_INSTRUMENT(); - - FileSpec homedir; - FileSystem::Instance().GetHomeDirectory(homedir); - FileSystem::Instance().Resolve(homedir); - - SBFileSpec sb_fspec; - sb_fspec.SetFileSpec(homedir); - - return sb_fspec; + return HostInfo::GetUserHomeDir(); } lldb::thread_t SBHostOS::ThreadCreate(const char *name, diff --git a/lldb/source/API/SBSymbol.cpp b/lldb/source/API/SBSymbol.cpp index 3b59119494f3..3030c8329212 100644 --- a/lldb/source/API/SBSymbol.cpp +++ b/lldb/source/API/SBSymbol.cpp @@ -79,6 +79,15 @@ const char *SBSymbol::GetMangledName() const { return name; } +const char *SBSymbol::GetBaseName() const { + LLDB_INSTRUMENT_VA(this); + + if (!m_opaque_ptr) + return nullptr; + + return m_opaque_ptr->GetMangled().GetBaseName().AsCString(); +} + bool SBSymbol::operator==(const SBSymbol &rhs) const { LLDB_INSTRUMENT_VA(this, rhs); diff --git a/lldb/source/API/SBThread.cpp b/lldb/source/API/SBThread.cpp index ec68b2a4b6f3..4e4aa48bc9a2 100644 --- a/lldb/source/API/SBThread.cpp +++ b/lldb/source/API/SBThread.cpp @@ -157,52 +157,8 @@ size_t SBThread::GetStopReasonDataCount() { if (exe_ctx) { if (exe_ctx->HasThreadScope()) { StopInfoSP stop_info_sp = exe_ctx->GetThreadPtr()->GetStopInfo(); - if (stop_info_sp) { - StopReason reason = stop_info_sp->GetStopReason(); - switch (reason) { - case eStopReasonInvalid: - case eStopReasonNone: - case eStopReasonTrace: - case eStopReasonExec: - case eStopReasonPlanComplete: - case eStopReasonThreadExiting: - case eStopReasonInstrumentation: - case eStopReasonProcessorTrace: - case eStopReasonVForkDone: - case eStopReasonHistoryBoundary: - // There is no data for these stop reasons. - return 0; - - case eStopReasonBreakpoint: { - break_id_t site_id = stop_info_sp->GetValue(); - lldb::BreakpointSiteSP bp_site_sp( - exe_ctx->GetProcessPtr()->GetBreakpointSiteList().FindByID( - site_id)); - if (bp_site_sp) - return bp_site_sp->GetNumberOfConstituents() * 2; - else - return 0; // Breakpoint must have cleared itself... - } break; - - case eStopReasonWatchpoint: - return 1; - - case eStopReasonSignal: - return 1; - - case eStopReasonInterrupt: - return 1; - - case eStopReasonException: - return 1; - - case eStopReasonFork: - return 1; - - case eStopReasonVFork: - return 1; - } - } + if (stop_info_sp) + return stop_info_sp->GetStopReasonDataCount(); } } else { LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); @@ -220,63 +176,8 @@ uint64_t SBThread::GetStopReasonDataAtIndex(uint32_t idx) { if (exe_ctx->HasThreadScope()) { Thread *thread = exe_ctx->GetThreadPtr(); StopInfoSP stop_info_sp = thread->GetStopInfo(); - if (stop_info_sp) { - StopReason reason = stop_info_sp->GetStopReason(); - switch (reason) { - case eStopReasonInvalid: - case eStopReasonNone: - case eStopReasonTrace: - case eStopReasonExec: - case eStopReasonPlanComplete: - case eStopReasonThreadExiting: - case eStopReasonInstrumentation: - case eStopReasonProcessorTrace: - case eStopReasonVForkDone: - case eStopReasonHistoryBoundary: - // There is no data for these stop reasons. - return 0; - - case eStopReasonBreakpoint: { - break_id_t site_id = stop_info_sp->GetValue(); - lldb::BreakpointSiteSP bp_site_sp( - exe_ctx->GetProcessPtr()->GetBreakpointSiteList().FindByID( - site_id)); - if (bp_site_sp) { - uint32_t bp_index = idx / 2; - BreakpointLocationSP bp_loc_sp( - bp_site_sp->GetConstituentAtIndex(bp_index)); - if (bp_loc_sp) { - if (idx & 1) { - // Odd idx, return the breakpoint location ID - return bp_loc_sp->GetID(); - } else { - // Even idx, return the breakpoint ID - return bp_loc_sp->GetBreakpoint().GetID(); - } - } - } - return LLDB_INVALID_BREAK_ID; - } break; - - case eStopReasonWatchpoint: - return stop_info_sp->GetValue(); - - case eStopReasonSignal: - return stop_info_sp->GetValue(); - - case eStopReasonInterrupt: - return stop_info_sp->GetValue(); - - case eStopReasonException: - return stop_info_sp->GetValue(); - - case eStopReasonFork: - return stop_info_sp->GetValue(); - - case eStopReasonVFork: - return stop_info_sp->GetValue(); - } - } + if (stop_info_sp) + return stop_info_sp->GetStopReasonDataAtIndex(idx); } } else { LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); diff --git a/lldb/source/Commands/CommandObjectBreakpoint.cpp b/lldb/source/Commands/CommandObjectBreakpoint.cpp index 38ec375c0307..de0a7e709341 100644 --- a/lldb/source/Commands/CommandObjectBreakpoint.cpp +++ b/lldb/source/Commands/CommandObjectBreakpoint.cpp @@ -609,12 +609,12 @@ protected: const size_t num_files = m_options.m_filenames.GetSize(); if (num_files == 0) { if (!GetDefaultFile(target, file, result)) { - result.AppendError("No file supplied and no default file available."); + result.AppendError("no file supplied and no default file available"); return; } } else if (num_files > 1) { - result.AppendError("Only one file at a time is allowed for file and " - "line breakpoints."); + result.AppendError("only one file at a time is allowed for file and " + "line breakpoints"); return; } else file = m_options.m_filenames.GetFileSpecAtIndex(0); @@ -784,7 +784,7 @@ protected: } result.SetStatus(eReturnStatusSuccessFinishResult); } else if (!bp_sp) { - result.AppendError("Breakpoint creation failed: No breakpoint created."); + result.AppendError("breakpoint creation failed: no breakpoint created"); } } @@ -940,7 +940,7 @@ protected: size_t num_breakpoints = breakpoints.GetSize(); if (num_breakpoints == 0) { - result.AppendError("No breakpoints exist to be enabled."); + result.AppendError("no breakpoints exist to be enabled"); return; } @@ -1048,7 +1048,7 @@ protected: size_t num_breakpoints = breakpoints.GetSize(); if (num_breakpoints == 0) { - result.AppendError("No breakpoints exist to be disabled."); + result.AppendError("no breakpoints exist to be disabled"); return; } @@ -1224,7 +1224,7 @@ protected: } result.SetStatus(eReturnStatusSuccessFinishNoResult); } else { - result.AppendError("Invalid breakpoint ID."); + result.AppendError("invalid breakpoint ID"); } } } @@ -1318,7 +1318,7 @@ protected: // Early return if there's no breakpoint at all. if (num_breakpoints == 0) { - result.AppendError("Breakpoint clear: No breakpoint cleared."); + result.AppendError("breakpoint clear: no breakpoint cleared"); return; } @@ -1364,7 +1364,7 @@ protected: output_stream.EOL(); result.SetStatus(eReturnStatusSuccessFinishNoResult); } else { - result.AppendError("Breakpoint clear: No breakpoint cleared."); + result.AppendError("breakpoint clear: no breakpoint cleared"); } } @@ -1459,7 +1459,7 @@ protected: size_t num_breakpoints = breakpoints.GetSize(); if (num_breakpoints == 0) { - result.AppendError("No breakpoints exist to be deleted."); + result.AppendError("no breakpoints exist to be deleted"); return; } @@ -1504,7 +1504,7 @@ protected: } } if (valid_bp_ids.GetSize() == 0) { - result.AppendError("No disabled breakpoints."); + result.AppendError("no disabled breakpoints"); return; } } else { @@ -1712,7 +1712,7 @@ protected: const size_t argc = command.GetArgumentCount(); if (argc == 0) { - result.AppendError("No names provided."); + result.AppendError("no names provided"); return; } @@ -1799,7 +1799,7 @@ public: protected: void DoExecute(Args &command, CommandReturnObject &result) override { if (!m_name_options.m_name.OptionWasSet()) { - result.AppendError("No name option provided."); + result.AppendError("no name option provided"); return; } @@ -1813,7 +1813,7 @@ protected: size_t num_breakpoints = breakpoints.GetSize(); if (num_breakpoints == 0) { - result.AppendError("No breakpoints, cannot add names."); + result.AppendError("no breakpoints, cannot add names"); return; } @@ -1825,7 +1825,7 @@ protected: if (result.Succeeded()) { if (valid_bp_ids.GetSize() == 0) { - result.AppendError("No breakpoints specified, cannot add names."); + result.AppendError("no breakpoints specified, cannot add names"); return; } size_t num_valid_ids = valid_bp_ids.GetSize(); @@ -1873,7 +1873,7 @@ public: protected: void DoExecute(Args &command, CommandReturnObject &result) override { if (!m_name_options.m_name.OptionWasSet()) { - result.AppendError("No name option provided."); + result.AppendError("no name option provided"); return; } @@ -1887,7 +1887,7 @@ protected: size_t num_breakpoints = breakpoints.GetSize(); if (num_breakpoints == 0) { - result.AppendError("No breakpoints, cannot delete names."); + result.AppendError("no breakpoints, cannot delete names"); return; } @@ -1899,7 +1899,7 @@ protected: if (result.Succeeded()) { if (valid_bp_ids.GetSize() == 0) { - result.AppendError("No breakpoints specified, cannot delete names."); + result.AppendError("no breakpoints specified, cannot delete names"); return; } ConstString bp_name(m_name_options.m_name.GetCurrentValue()); diff --git a/lldb/source/Commands/CommandObjectCommands.cpp b/lldb/source/Commands/CommandObjectCommands.cpp index 3049eb8c20db..a3293f0f7966 100644 --- a/lldb/source/Commands/CommandObjectCommands.cpp +++ b/lldb/source/Commands/CommandObjectCommands.cpp @@ -418,7 +418,7 @@ protected: if ((pos != std::string::npos) && (pos > 0)) raw_command_string = raw_command_string.substr(pos); } else { - result.AppendError("Error parsing command string. No alias created."); + result.AppendError("error parsing command string. No alias created"); return; } @@ -2888,7 +2888,7 @@ protected: size_t num_args = command.GetArgumentCount(); if (num_args == 0) { - result.AppendError("No command was specified."); + result.AppendError("no command was specified"); return; } diff --git a/lldb/source/Commands/CommandObjectDisassemble.cpp b/lldb/source/Commands/CommandObjectDisassemble.cpp index 70e687e19ac6..c0553d2c6c8b 100644 --- a/lldb/source/Commands/CommandObjectDisassemble.cpp +++ b/lldb/source/Commands/CommandObjectDisassemble.cpp @@ -154,6 +154,10 @@ Status CommandObjectDisassemble::CommandOptions::SetOptionValue( } } break; + case 'v': + enable_variable_annotations = true; + break; + case '\x01': force = true; break; @@ -180,6 +184,7 @@ void CommandObjectDisassemble::CommandOptions::OptionParsingStarting( end_addr = LLDB_INVALID_ADDRESS; symbol_containing_addr = LLDB_INVALID_ADDRESS; raw = false; + enable_variable_annotations = false; plugin_name.clear(); Target *target = @@ -503,8 +508,9 @@ void CommandObjectDisassemble::DoExecute(Args &command, "\"disassemble\" arguments are specified as options.\n"); const int terminal_width = GetCommandInterpreter().GetDebugger().GetTerminalWidth(); + const bool use_color = GetCommandInterpreter().GetDebugger().GetUseColor(); GetOptions()->GenerateOptionUsage(result.GetErrorStream(), *this, - terminal_width); + terminal_width, use_color); return; } @@ -528,6 +534,9 @@ void CommandObjectDisassemble::DoExecute(Args &command, if (m_options.raw) options |= Disassembler::eOptionRawOuput; + if (m_options.enable_variable_annotations) + options |= Disassembler::eOptionVariableAnnotations; + llvm::Expected<std::vector<AddressRange>> ranges = GetRangesForSelectedMode(result); if (!ranges) { diff --git a/lldb/source/Commands/CommandObjectDisassemble.h b/lldb/source/Commands/CommandObjectDisassemble.h index 4fbcd72d1c04..eed44adce5ff 100644 --- a/lldb/source/Commands/CommandObjectDisassemble.h +++ b/lldb/source/Commands/CommandObjectDisassemble.h @@ -78,6 +78,7 @@ public: // in SetOptionValue if anything the selects a location is set. lldb::addr_t symbol_containing_addr = 0; bool force = false; + bool enable_variable_annotations = false; }; CommandObjectDisassemble(CommandInterpreter &interpreter); diff --git a/lldb/source/Commands/CommandObjectFrame.cpp b/lldb/source/Commands/CommandObjectFrame.cpp index 56926999d167..88a02dce35b9 100644 --- a/lldb/source/Commands/CommandObjectFrame.cpp +++ b/lldb/source/Commands/CommandObjectFrame.cpp @@ -140,7 +140,7 @@ protected: } else { StopInfoSP stop_info_sp = thread->GetStopInfo(); if (!stop_info_sp) { - result.AppendError("No arguments provided, and no stop info."); + result.AppendError("no arguments provided, and no stop info"); return; } @@ -148,7 +148,7 @@ protected: } if (!valobj_sp) { - result.AppendError("No diagnosis available."); + result.AppendError("no diagnosis available"); return; } @@ -310,7 +310,7 @@ protected: if (frame_idx == 0) { // If you are already at the bottom of the stack, then just warn // and don't reset the frame. - result.AppendError("Already at the bottom of the stack."); + result.AppendError("already at the bottom of the stack"); return; } else frame_idx = 0; @@ -335,7 +335,7 @@ protected: if (frame_idx == num_frames - 1) { // If we are already at the top of the stack, just warn and don't // reset the frame. - result.AppendError("Already at the top of the stack."); + result.AppendError("already at the top of the stack"); return; } else frame_idx = num_frames - 1; @@ -349,7 +349,8 @@ protected: command[0].c_str()); m_options.GenerateOptionUsage( result.GetErrorStream(), *this, - GetCommandInterpreter().GetDebugger().GetTerminalWidth()); + GetCommandInterpreter().GetDebugger().GetTerminalWidth(), + GetCommandInterpreter().GetDebugger().GetUseColor()); return; } diff --git a/lldb/source/Commands/CommandObjectLog.cpp b/lldb/source/Commands/CommandObjectLog.cpp index 17efae189b05..0c01da0b5683 100644 --- a/lldb/source/Commands/CommandObjectLog.cpp +++ b/lldb/source/Commands/CommandObjectLog.cpp @@ -547,7 +547,7 @@ protected: Timer::SetQuiet(!increment); result.SetStatus(eReturnStatusSuccessFinishNoResult); } else - result.AppendError("Could not convert increment value to boolean."); + result.AppendError("could not convert increment value to boolean"); } if (!result.Succeeded()) { diff --git a/lldb/source/Commands/CommandObjectMemory.cpp b/lldb/source/Commands/CommandObjectMemory.cpp index af1ff3e84510..5786e757ef7e 100644 --- a/lldb/source/Commands/CommandObjectMemory.cpp +++ b/lldb/source/Commands/CommandObjectMemory.cpp @@ -365,6 +365,8 @@ protected: return; } + ExecutionContextScope *exe_scope = m_exe_ctx.GetBestExecutionContextScope(); + CompilerType compiler_type; Status error; @@ -520,7 +522,7 @@ protected: --pointer_count; } - auto size_or_err = compiler_type.GetByteSize(nullptr); + auto size_or_err = compiler_type.GetByteSize(exe_scope); if (!size_or_err) { result.AppendErrorWithFormat( "unable to get the byte size of the type '%s'\n%s", @@ -640,7 +642,7 @@ protected: if (!m_format_options.GetFormatValue().OptionWasSet()) m_format_options.GetFormatValue().SetCurrentValue(eFormatDefault); - auto size_or_err = compiler_type.GetByteSize(nullptr); + auto size_or_err = compiler_type.GetByteSize(exe_scope); if (!size_or_err) { result.AppendError(llvm::toString(size_or_err.takeError())); return; @@ -800,7 +802,6 @@ protected: output_stream_p = &result.GetOutputStream(); } - ExecutionContextScope *exe_scope = m_exe_ctx.GetBestExecutionContextScope(); if (compiler_type.GetOpaqueQualType()) { for (uint32_t i = 0; i < item_count; ++i) { addr_t item_addr = addr + (i * item_byte_size); diff --git a/lldb/source/Commands/CommandObjectMultiword.cpp b/lldb/source/Commands/CommandObjectMultiword.cpp index c99b75ff2914..a369557cca84 100644 --- a/lldb/source/Commands/CommandObjectMultiword.cpp +++ b/lldb/source/Commands/CommandObjectMultiword.cpp @@ -159,7 +159,7 @@ void CommandObjectMultiword::Execute(const char *args_string, auto sub_command = args[0].ref(); if (sub_command.empty()) { - result.AppendError("Need to specify a non-empty subcommand."); + result.AppendError("need to specify a non-empty subcommand"); return; } diff --git a/lldb/source/Commands/CommandObjectProcess.cpp b/lldb/source/Commands/CommandObjectProcess.cpp index 84c576e721e7..7d326404a550 100644 --- a/lldb/source/Commands/CommandObjectProcess.cpp +++ b/lldb/source/Commands/CommandObjectProcess.cpp @@ -259,7 +259,7 @@ protected: if (!exe_module_sp) exe_module_sp = target->GetExecutableModule(); if (!exe_module_sp) { - result.AppendWarning("Could not get executable module after launch."); + result.AppendWarning("could not get executable module after launch"); } else { const char *archname = diff --git a/lldb/source/Commands/CommandObjectProtocolServer.cpp b/lldb/source/Commands/CommandObjectProtocolServer.cpp index f11e27f01c8a..c5ab9e9f05be 100644 --- a/lldb/source/Commands/CommandObjectProtocolServer.cpp +++ b/lldb/source/Commands/CommandObjectProtocolServer.cpp @@ -15,6 +15,7 @@ #include "lldb/Utility/UriParser.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/FormatAdapters.h" +#include <string> using namespace llvm; using namespace lldb; @@ -28,7 +29,7 @@ public: CommandObjectProtocolServerStart(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "protocol-server start", "start protocol server", - "protocol-server start <protocol> <connection>") { + "protocol-server start <protocol> [<connection>]") { AddSimpleArgumentList(lldb::eArgTypeProtocol, eArgRepeatPlain); AddSimpleArgumentList(lldb::eArgTypeConnectURL, eArgRepeatPlain); } @@ -51,15 +52,13 @@ protected: return; } - if (args.GetArgumentCount() < 2) { - result.AppendError("no connection specified"); - return; - } - llvm::StringRef connection_uri = args.GetArgumentAtIndex(1); + std::string connection_uri = "listen://[localhost]:0"; + if (args.GetArgumentCount() >= 2) + connection_uri = args.GetArgumentAtIndex(1); const char *connection_error = - "unsupported connection specifier, expected 'accept:///path' or " - "'listen://[host]:port', got '{0}'."; + "unsupported connection specifier, expected 'accept:///path' " + "or 'listen://[host]:port', got '{0}'."; auto uri = lldb_private::URI::Parse(connection_uri); if (!uri) { result.AppendErrorWithFormatv(connection_error, connection_uri); diff --git a/lldb/source/Commands/CommandObjectSource.cpp b/lldb/source/Commands/CommandObjectSource.cpp index 7e7d3f065b62..0b4599b16ef0 100644 --- a/lldb/source/Commands/CommandObjectSource.cpp +++ b/lldb/source/Commands/CommandObjectSource.cpp @@ -513,7 +513,7 @@ protected: "No selected frame to use to find the default source."); return false; } else if (!cur_frame->HasDebugInformation()) { - result.AppendError("No debug info for the selected frame."); + result.AppendError("no debug info for the selected frame"); return false; } else { const SymbolContext &sc = @@ -553,11 +553,11 @@ protected: } } if (!m_module_list.GetSize()) { - result.AppendError("No modules match the input."); + result.AppendError("no modules match the input"); return; } } else if (target.GetImages().GetSize() == 0) { - result.AppendError("The target has no associated executable images."); + result.AppendError("the target has no associated executable images"); return; } diff --git a/lldb/source/Commands/CommandObjectTarget.cpp b/lldb/source/Commands/CommandObjectTarget.cpp index dbebbbd38093..004542e3e6ae 100644 --- a/lldb/source/Commands/CommandObjectTarget.cpp +++ b/lldb/source/Commands/CommandObjectTarget.cpp @@ -2420,7 +2420,7 @@ protected: result.GetErrorStream().SetAddressByteSize(addr_byte_size); if (command.GetArgumentCount() == 0) { - result.AppendError("file option must be specified."); + result.AppendError("file option must be specified"); return; } else { // Dump specified images (by basename or fullpath) @@ -3565,13 +3565,13 @@ protected: ThreadList threads(process->GetThreadList()); if (threads.GetSize() == 0) { - result.AppendError("The process must be paused to use this command."); + result.AppendError("the process must be paused to use this command"); return; } ThreadSP thread(threads.GetThreadAtIndex(0)); if (!thread) { - result.AppendError("The process must be paused to use this command."); + result.AppendError("the process must be paused to use this command"); return; } @@ -4075,7 +4075,8 @@ public: default: m_options.GenerateOptionUsage( result.GetErrorStream(), *this, - GetCommandInterpreter().GetDebugger().GetTerminalWidth()); + GetCommandInterpreter().GetDebugger().GetTerminalWidth(), + GetCommandInterpreter().GetDebugger().GetUseColor()); syntax_error = true; break; } diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp index 57c23d533fb9..bbec714642ec 100644 --- a/lldb/source/Commands/CommandObjectThread.cpp +++ b/lldb/source/Commands/CommandObjectThread.cpp @@ -1570,7 +1570,7 @@ protected: uint32_t frame_idx = frame_sp->GetFrameIndex(); if (frame_sp->IsInlined()) { - result.AppendError("Don't know how to return from inlined frames."); + result.AppendError("don't know how to return from inlined frames"); return; } diff --git a/lldb/source/Commands/CommandObjectWatchpoint.cpp b/lldb/source/Commands/CommandObjectWatchpoint.cpp index e79c3b8939fa..12effed12a3c 100644 --- a/lldb/source/Commands/CommandObjectWatchpoint.cpp +++ b/lldb/source/Commands/CommandObjectWatchpoint.cpp @@ -44,7 +44,7 @@ static bool CheckTargetForWatchpointOperations(Target &target, bool process_is_valid = target.GetProcessSP() && target.GetProcessSP()->IsAlive(); if (!process_is_valid) { - result.AppendError("There's no process or it is not alive."); + result.AppendError("there's no process or it is not alive"); return false; } // Target passes our checks, return true. @@ -243,7 +243,7 @@ protected: std::vector<uint32_t> wp_ids; if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs( target, command, wp_ids)) { - result.AppendError("Invalid watchpoints specification."); + result.AppendError("invalid watchpoints specification"); return; } @@ -298,7 +298,7 @@ protected: size_t num_watchpoints = watchpoints.GetSize(); if (num_watchpoints == 0) { - result.AppendError("No watchpoints exist to be enabled."); + result.AppendError("no watchpoints exist to be enabled"); return; } @@ -314,7 +314,7 @@ protected: std::vector<uint32_t> wp_ids; if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs( target, command, wp_ids)) { - result.AppendError("Invalid watchpoints specification."); + result.AppendError("invalid watchpoints specification"); return; } @@ -366,7 +366,7 @@ protected: size_t num_watchpoints = watchpoints.GetSize(); if (num_watchpoints == 0) { - result.AppendError("No watchpoints exist to be disabled."); + result.AppendError("no watchpoints exist to be disabled"); return; } @@ -385,7 +385,7 @@ protected: std::vector<uint32_t> wp_ids; if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs( target, command, wp_ids)) { - result.AppendError("Invalid watchpoints specification."); + result.AppendError("invalid watchpoints specification"); return; } @@ -476,7 +476,7 @@ protected: size_t num_watchpoints = watchpoints.GetSize(); if (num_watchpoints == 0) { - result.AppendError("No watchpoints exist to be deleted."); + result.AppendError("no watchpoints exist to be deleted"); return; } @@ -500,7 +500,7 @@ protected: std::vector<uint32_t> wp_ids; if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command, wp_ids)) { - result.AppendError("Invalid watchpoints specification."); + result.AppendError("invalid watchpoints specification"); return; } @@ -596,7 +596,7 @@ protected: size_t num_watchpoints = watchpoints.GetSize(); if (num_watchpoints == 0) { - result.AppendError("No watchpoints exist to be ignored."); + result.AppendError("no watchpoints exist to be ignored"); return; } @@ -611,7 +611,7 @@ protected: std::vector<uint32_t> wp_ids; if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs( target, command, wp_ids)) { - result.AppendError("Invalid watchpoints specification."); + result.AppendError("invalid watchpoints specification"); return; } @@ -715,7 +715,7 @@ protected: size_t num_watchpoints = watchpoints.GetSize(); if (num_watchpoints == 0) { - result.AppendError("No watchpoints exist to be modified."); + result.AppendError("no watchpoints exist to be modified"); return; } @@ -728,7 +728,7 @@ protected: std::vector<uint32_t> wp_ids; if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs( target, command, wp_ids)) { - result.AppendError("Invalid watchpoints specification."); + result.AppendError("invalid watchpoints specification"); return; } diff --git a/lldb/source/Commands/CommandObjectWatchpointCommand.cpp b/lldb/source/Commands/CommandObjectWatchpointCommand.cpp index 32cb80b421fd..062bf75eb8ae 100644 --- a/lldb/source/Commands/CommandObjectWatchpointCommand.cpp +++ b/lldb/source/Commands/CommandObjectWatchpointCommand.cpp @@ -376,7 +376,7 @@ protected: std::vector<uint32_t> valid_wp_ids; if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command, valid_wp_ids)) { - result.AppendError("Invalid watchpoints specification."); + result.AppendError("invalid watchpoints specification"); return; } @@ -470,7 +470,7 @@ protected: std::vector<uint32_t> valid_wp_ids; if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command, valid_wp_ids)) { - result.AppendError("Invalid watchpoints specification."); + result.AppendError("invalid watchpoints specification"); return; } @@ -525,7 +525,7 @@ protected: std::vector<uint32_t> valid_wp_ids; if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command, valid_wp_ids)) { - result.AppendError("Invalid watchpoints specification."); + result.AppendError("invalid watchpoints specification"); return; } diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td index 17d72cdcad12..595b3d08abec 100644 --- a/lldb/source/Commands/Options.td +++ b/lldb/source/Commands/Options.td @@ -1,232 +1,354 @@ include "OptionsBase.td" let Command = "target modules dump symtab" in { - def tm_sort : Option<"sort", "s">, Group<1>, - Desc<"Supply a sort order when dumping the symbol table.">, - EnumArg<"SortOrder">; - def tm_smn : Option<"show-mangled-names", "m">, Group<1>, - Desc<"Do not demangle symbol names before showing them.">; + def tm_sort : Option<"sort", "s">, + Group<1>, + Desc<"${S}upply a sort order when dumping the symbol table.">, + EnumArg<"SortOrder">; + def tm_smn : Option<"show-mangled-names", "m">, + Group<1>, + Desc<"Do not de${m}angle symbol names before showing them.">; } let Command = "target modules dump separate debug info" in { - def tm_json : Option<"json", "j">, Group<1>, - Desc<"Output the details in JSON format.">; - def tm_errors_only : Option<"errors-only", "e">, Group<1>, - Desc<"Filter to show only debug info files with errors.">; - def tm_force_load_all_debug_info : Option<"force-load-all-debug-info", "f">, - Group<1>, - Desc<"Load all debug info files.">; + def tm_json : Option<"json", "j">, + Group<1>, + Desc<"Output the details in ${J}SON format.">; + def tm_errors_only + : Option<"errors-only", "e">, + Group<1>, + Desc<"Filter to show only debug info files with ${e}rrors.">; + def tm_force_load_all_debug_info + : Option<"force-load-all-debug-info", "f">, + Group<1>, + Desc<"${F}orce load all debug info files.">; } let Command = "help" in { def help_hide_aliases : Option<"hide-aliases", "a">, - Desc<"Hide aliases in the command list.">; + Desc<"Hide ${a}liases in the command list.">; def help_hide_user : Option<"hide-user-commands", "u">, - Desc<"Hide user-defined commands from the list.">; + Desc<"Hide ${u}ser-defined commands from the list.">; def help_show_hidden : Option<"show-hidden-commands", "h">, - Desc<"Include commands prefixed with an underscore.">; + Desc<"Include commands prefixed with an underscore.">; } let Command = "settings set" in { - def setset_global : Option<"global", "g">, - Desc<"Apply the new value to the global default value.">; - def setset_force : Option<"force", "f">, - Desc<"Force an empty value to be accepted as the default.">; - def setset_exists : Option<"exists", "e">, - Desc<"Set the setting if it exists, but do not cause the command to raise " - "an error if it does not exist.">; + def setset_global + : Option<"global", "g">, + Desc<"Apply the new value to the ${g}lobal default value.">; + def setset_force + : Option<"force", "f">, + Desc<"${F}orce an empty value to be accepted as the default.">; + def setset_exists + : Option<"exists", "e">, + Desc<"Set the setting if it ${e}xists, but do not cause the command to " + "raise an error if it does not exist.">; } let Command = "settings write" in { - def setwrite_file : Option<"file", "f">, Required, Arg<"Filename">, - Completion<"DiskFile">, - Desc<"The file into which to write the settings.">; + def setwrite_file : Option<"file", "f">, + Required, + Arg<"Filename">, + Completion<"DiskFile">, + Desc<"The ${f}ile into which to write the settings.">; def setwrite_append : Option<"append", "a">, - Desc<"Append to saved settings file if it exists.">; + Desc<"${A}ppend to saved settings file if it exists.">; } let Command = "settings read" in { - def setread_file : Option<"file", "f">, Required, Arg<"Filename">, - Completion<"DiskFile">, - Desc<"The file from which to read the settings.">; + def setread_file : Option<"file", "f">, + Required, + Arg<"Filename">, + Completion<"DiskFile">, + Desc<"The ${f}ile from which to read the settings.">; } let Command = "settings clear" in { - def setclear_all : Option<"all", "a">, - Desc<"Clear all settings.">; + def setclear_all : Option<"all", "a">, Desc<"Clear ${a}ll settings.">; } let Command = "settings show" in { def setshow_defaults : Option<"defaults", "d">, - Desc<"Include default values if defined.">; + Desc<"Include ${d}efault values if defined.">; } let Command = "breakpoint list" in { // FIXME: We need to add an "internal" command, and then add this sort of // thing to it. But I need to see it for now, and don't want to wait. def blist_internal : Option<"internal", "i">, - Desc<"Show debugger internal breakpoints">; - def blist_brief : Option<"brief", "b">, Group<1>, - Desc<"Give a brief description of the breakpoint (no location info).">; - def blist_full : Option<"full", "f">, Group<2>, - Desc<"Give a full description of the breakpoint and its locations.">; - def blist_verbose : Option<"verbose", "v">, Group<3>, - Desc<"Explain everything we know about the breakpoint (for debugging " - "debugger bugs).">; - def blist_dummy_bp : Option<"dummy-breakpoints", "D">, - Desc<"List Dummy breakpoints - i.e. breakpoints set before a file is " - "provided, which prime new targets.">; + Desc<"Show debugger ${i}nternal breakpoints">; + def blist_brief : Option<"brief", "b">, + Group<1>, + Desc<"Give a ${b}rief description of the breakpoint (no " + "location info).">; + def blist_full + : Option<"full", "f">, + Group<2>, + Desc<"Give a ${f}ull description of the breakpoint and its locations.">; + def blist_verbose : Option<"verbose", "v">, + Group<3>, + Desc<"Explain everything we know about the breakpoint " + "(for debugging debugger bugs).">; + def blist_dummy_bp + : Option<"dummy-breakpoints", "D">, + Desc<"List ${D}ummy breakpoints - i.e. breakpoints set before a file " + "is provided, which prime new targets.">; } let Command = "breakpoint modify" in { - def breakpoint_modify_ignore_count : Option<"ignore-count", "i">, Group<1>, - Arg<"Count">, - Desc<"Set the number of times this breakpoint is skipped before stopping.">; - def breakpoint_modify_one_shot : Option<"one-shot", "o">, Group<1>, - Arg<"Boolean">, - Desc<"The breakpoint is deleted the first time it stop causes a stop.">; - def breakpoint_modify_thread_index : Option<"thread-index", "x">, Group<1>, - Arg<"ThreadIndex">, Desc<"The breakpoint stops only for the thread whose " - "index matches this argument.">; - def breakpoint_modify_thread_id : Option<"thread-id", "t">, Group<1>, - Arg<"ThreadID">, Desc<"The breakpoint stops only for the thread whose TID " - "matches this argument. The token 'current' resolves to the current thread's ID.">; - def breakpoint_modify_thread_name : Option<"thread-name", "T">, Group<1>, - Arg<"ThreadName">, Desc<"The breakpoint stops only for the thread whose " - "thread name matches this argument.">; - def breakpoint_modify_queue_name : Option<"queue-name", "q">, Group<1>, - Arg<"QueueName">, Desc<"The breakpoint stops only for threads in the queue " - "whose name is given by this argument.">; - def breakpoint_modify_condition : Option<"condition", "c">, Group<1>, - Arg<"Expression">, Desc<"The breakpoint stops only if this condition " - "expression evaluates to true.">; + def breakpoint_modify_ignore_count + : Option<"ignore-count", "i">, + Group<1>, + Arg<"Count">, + Desc<"Set the number of times this breakpoint is skipped before " + "stopping.">; + def breakpoint_modify_one_shot + : Option<"one-shot", "o">, + Group<1>, + Arg<"Boolean">, + Desc<"The breakpoint is deleted the first time it causes a stop.">; + def breakpoint_modify_thread_index + : Option<"thread-index", "x">, + Group<1>, + Arg<"ThreadIndex">, + Desc<"The breakpoint stops only for the thread whose inde${x} matches " + "this argument.">; + def breakpoint_modify_thread_id + : Option<"thread-id", "t">, + Group<1>, + Arg<"ThreadID">, + Desc<"The breakpoint stops only for the ${t}hread whose TID matches " + "this argument. The token 'current' resolves to the current " + "thread's ID.">; + def breakpoint_modify_thread_name + : Option<"thread-name", "T">, + Group<1>, + Arg<"ThreadName">, + Desc<"The breakpoint stops only for the ${t}hread whose thread name " + "matches this argument.">; + def breakpoint_modify_queue_name + : Option<"queue-name", "q">, + Group<1>, + Arg<"QueueName">, + Desc<"The breakpoint stops only for threads in the ${q}ueue whose name " + "is given by this argument.">; + def breakpoint_modify_condition + : Option<"condition", "c">, + Group<1>, + Arg<"Expression">, + Desc<"The breakpoint stops only if this ${c}ondition expression " + "evaluates to true.">; def breakpoint_modify_condition_language : Option<"condition-language", "Y">, Group<1>, Arg<"Language">, Desc<"Specifies the Language to use when executing the breakpoint's " "condition expression.">; - def breakpoint_modify_auto_continue : Option<"auto-continue", "G">, Group<1>, - Arg<"Boolean">, - Desc<"The breakpoint will auto-continue after running its commands.">; - def breakpoint_modify_enable : Option<"enable", "e">, Group<2>, - Desc<"Enable the breakpoint.">; - def breakpoint_modify_disable : Option<"disable", "d">, Group<3>, - Desc<"Disable the breakpoint.">; - def breakpoint_modify_command : Option<"command", "C">, Group<4>, - Arg<"Command">, - Desc<"A command to run when the breakpoint is hit, can be provided more " - "than once, the commands will be run in left-to-right order.">; + def breakpoint_modify_auto_continue + : Option<"auto-continue", "G">, + Group<1>, + Arg<"Boolean">, + Desc<"The breakpoint will auto-continue after running its commands.">; + def breakpoint_modify_enable : Option<"enable", "e">, + Group<2>, + Desc<"${E}nable the breakpoint.">; + def breakpoint_modify_disable : Option<"disable", "d">, + Group<3>, + Desc<"${D}isable the breakpoint.">; + def breakpoint_modify_command + : Option<"command", "C">, + Group<4>, + Arg<"Command">, + Desc< + "A ${c}ommand to run when the breakpoint is hit, can be provided " + "more than once, the commands will be run in left-to-right order.">; } let Command = "breakpoint dummy" in { - def breakpoint_dummy_options_dummy_breakpoints : - Option<"dummy-breakpoints", "D">, Group<1>, - Desc<"Act on Dummy breakpoints - i.e. breakpoints set before a file is " - "provided, which prime new targets.">; + def breakpoint_dummy_options_dummy_breakpoints + : Option<"dummy-breakpoints", "D">, + Group<1>, + Desc<"Act on ${D}ummy breakpoints - i.e. breakpoints set before a file " + "is provided, which prime new targets.">; } let Command = "breakpoint set" in { - def breakpoint_set_shlib : Option<"shlib", "s">, Arg<"ShlibName">, - Completion<"Module">, Groups<[1,2,3,4,5,6,7,8,9,11,12]>, // *not* in group 10 - Desc<"Set the breakpoint only in this shared library. Can repeat this " - "option multiple times to specify multiple shared libraries.">; - def breakpoint_set_hardware : Option<"hardware", "H">, - Desc<"Require the breakpoint to use hardware breakpoints.">; - def breakpoint_set_file : Option<"file", "f">, Arg<"Filename">, - Completion<"SourceFile">, Groups<[1,3,4,5,6,7,8,9,11]>, - Desc<"Specifies the source file in which to set this breakpoint. Note, by " - "default lldb only looks for files that are #included if they use the " - "standard include file extensions. To set breakpoints on .c/.cpp/.m/.mm " - "files that are #included, set target.inline-breakpoint-strategy to " - "\"always\".">; - def breakpoint_set_line : Option<"line", "l">, Group<1>, Arg<"LineNum">, - Required, - Desc<"Specifies the line number on which to set this breakpoint.">; - def breakpoint_set_column : Option<"column", "u">, Group<1>, Arg<"ColumnNum">, - Desc<"Specifies the column number on which to set this breakpoint.">; - def breakpoint_set_address : Option<"address", "a">, Group<2>, - Arg<"AddressOrExpression">, Required, - Desc<"Set the breakpoint at the specified address. If the address maps " - "uniquely to a particular binary, then the address will be converted to " - "a \"file\"address, so that the breakpoint will track that binary+offset " - "no matter where the binary eventually loads. Alternately, if you also " - "specify the module - with the -s option - then the address will be " - "treated as a file address in that module, and resolved accordingly. " - "Again, this will allow lldb to track that offset on subsequent reloads. " - "The module need not have been loaded at the time you specify this " - "breakpoint, and will get resolved when the module is loaded.">; - def breakpoint_set_name : Option<"name", "n">, Group<3>, Arg<"FunctionName">, - Completion<"Symbol">, Required, - Desc<"Set the breakpoint by function name. Can be repeated multiple times " - "to make one breakpoint for multiple names.">; - def breakpoint_set_source_regexp_function : - Option<"source-regexp-function", "X">, Group<9>, Arg<"FunctionName">, - Completion<"Symbol">, - Desc<"When used with '-p' limits the source regex to source contained in " - "the named functions. Can be repeated multiple times.">; - def breakpoint_set_fullname : Option<"fullname", "F">, Group<4>, - Arg<"FullName">, Required, Completion<"Symbol">, - Desc<"Set the breakpoint by fully qualified function names. For C++ this " - "means namespaces and all arguments, and for Objective-C this means a full " - "function prototype with class and selector. Can be repeated multiple times" - " to make one breakpoint for multiple names.">; - def breakpoint_set_selector : Option<"selector", "S">, Group<5>, - Arg<"Selector">, Required, - Desc<"Set the breakpoint by Objective-C selector name. Can be repeated " - "multiple times to make one breakpoint for multiple Selectors.">; - def breakpoint_set_method : Option<"method", "M">, Group<6>, Arg<"Method">, - Required, Desc<"Set the breakpoint by C++ method names. Can be repeated " - "multiple times to make one breakpoint for multiple methods.">; - def breakpoint_set_func_regex : Option<"func-regex", "r">, Group<7>, - Arg<"RegularExpression">, Required, Desc<"Set the breakpoint by function " - "name, evaluating a regular-expression to find the function name(s).">; - def breakpoint_set_basename : Option<"basename", "b">, Group<8>, - Arg<"FunctionName">, Required, Completion<"Symbol">, - Desc<"Set the breakpoint by function basename (C++ namespaces and arguments" - " will be ignored). Can be repeated multiple times to make one breakpoint " - "for multiple symbols.">; - def breakpoint_set_source_pattern_regexp : - Option<"source-pattern-regexp", "p">, Group<9>, Arg<"RegularExpression">, - Required, Desc<"Set the breakpoint by specifying a regular expression which" - " is matched against the source text in a source file or files specified " - "with the -f can be specified more than once. If no source files " - "are specified, uses the current \"default source file\". If you want to " - "match against all source files, pass the \"--all-files\" option.">; - def breakpoint_set_all_files : Option<"all-files", "A">, Group<9>, - Desc<"All files are searched for source pattern matches.">; - def breakpoint_set_language_exception : Option<"language-exception", "E">, - Group<10>, Arg<"Language">, Required, - Desc<"Set the breakpoint on exceptions thrown by the specified language " - "(without options, on throw but not catch.)">; - def breakpoint_set_on_throw : Option<"on-throw", "w">, Group<10>, - Arg<"Boolean">, Desc<"Set the breakpoint on exception throW.">; - def breakpoint_set_on_catch : Option<"on-catch", "h">, Group<10>, - Arg<"Boolean">, Desc<"Set the breakpoint on exception catcH.">; - def breakpoint_set_language : Option<"language", "L">, GroupRange<3, 8>, - Arg<"Language">, - Desc<"Specifies the Language to use when interpreting the breakpoint's " - "expression (note: currently only implemented for setting breakpoints on " - "identifiers). If not set the target.language setting is used.">; - def breakpoint_set_skip_prologue : Option<"skip-prologue", "K">, - Arg<"Boolean">, Groups<[1,3,4,5,6,7,8,12]>, - Desc<"Skip the prologue if the breakpoint is at the beginning of a " - "function. If not set the target.skip-prologue setting is used.">; - def breakpoint_set_breakpoint_name : Option<"breakpoint-name", "N">, - Arg<"BreakpointName">, - Desc<"Adds this to the list of names for this breakpoint.">; - def breakpoint_set_address_slide : Option<"address-slide", "R">, - Arg<"Address">, Groups<[1,3,4,5,6,7,8,12]>, - Desc<"Add the specified offset to whatever address(es) the breakpoint " - "resolves to. At present this applies the offset directly as given, and " - "doesn't try to align it to instruction boundaries.">; - def breakpoint_set_move_to_nearest_code : Option<"move-to-nearest-code", "m">, - Groups<[1,9,12]>, Arg<"Boolean">, - Desc<"Move breakpoints to nearest code. If not set the " - "target.move-to-nearest-code setting is used.">; - def breakpoint_set_file_colon_line : Option<"joint-specifier", "y">, Group<12>, Arg<"FileLineColumn">, - Required, Completion<"SourceFile">, - Desc<"A specifier in the form filename:line[:column] for setting file & line breakpoints.">; + def breakpoint_set_shlib + : Option<"shlib", "s">, + Arg<"ShlibName">, + Completion<"Module">, + Groups<[1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12]>, // *not* in group 10 + Desc< + "${S}et the breakpoint only in this shared library. Can repeat " + "this option multiple times to specify multiple shared libraries.">; + def breakpoint_set_hardware + : Option<"hardware", "H">, + Desc<"Require the breakpoint to use ${h}ardware breakpoints.">; + def breakpoint_set_file + : Option<"file", "f">, + Arg<"Filename">, + Completion<"SourceFile">, + Groups<[1, 3, 4, 5, 6, 7, 8, 9, 11]>, + Desc<"Specifies the source ${f}ile in which to set this breakpoint. " + "Note, by default lldb only looks for files that are #included if " + "they use the standard include file extensions. To set " + "breakpoints on .c/.cpp/.m/.mm files that are #included, set " + "target.inline-breakpoint-strategy to \"always\".">; + def breakpoint_set_line + : Option<"line", "l">, + Group<1>, + Arg<"LineNum">, + Required, + Desc<"Specifies the ${l}ine number on which to set this breakpoint.">; + def breakpoint_set_column + : Option<"column", "u">, + Group<1>, + Arg<"ColumnNum">, + Desc<"Specifies the col${u}mn number on which to set this breakpoint.">; + def breakpoint_set_address + : Option<"address", "a">, + Group<2>, + Arg<"AddressOrExpression">, + Required, + Desc<"Set the breakpoint at the specified ${a}ddress. If the address " + "maps uniquely to a particular binary, then the address will be " + "converted to a \"file\"address, so that the breakpoint will " + "track that binary+offset no matter where the binary eventually " + "loads. Alternately, if you also specify the module - with the " + "-s option - then the address will be treated as a file address " + "in that module, and resolved accordingly. Again, this will " + "allow lldb to track that offset on subsequent reloads. The " + "module need not have been loaded at the time you specify this " + "breakpoint, and will get resolved when the module is loaded.">; + def breakpoint_set_name + : Option<"name", "n">, + Group<3>, + Arg<"FunctionName">, + Completion<"Symbol">, + Required, + Desc<"Set the breakpoint by function ${n}ame. Can be repeated " + "multiple times to make one breakpoint for multiple names.">; + def breakpoint_set_source_regexp_function + : Option<"source-regexp-function", "X">, + Group<9>, + Arg<"FunctionName">, + Completion<"Symbol">, + Desc<"When used with '-p' limits the source regex to source contained " + "in the named functions. Can be repeated multiple times.">; + def breakpoint_set_fullname + : Option<"fullname", "F">, + Group<4>, + Arg<"FullName">, + Required, + Completion<"Symbol">, + Desc<"Set the breakpoint by ${f}ully qualified function names. For C++ " + "this means namespaces and all arguments, and for Objective-C " + "this means a full function prototype with class and selector. " + "Can be repeated multiple times to make one breakpoint for " + "multiple names.">; + def breakpoint_set_selector + : Option<"selector", "S">, + Group<5>, + Arg<"Selector">, + Required, + Desc<"Set the breakpoint by Objective-C ${s}elector name. Can be " + "repeated multiple times to make one breakpoint for multiple " + "Selectors.">; + def breakpoint_set_method + : Option<"method", "M">, + Group<6>, + Arg<"Method">, + Required, + Desc<"Set the breakpoint by C++ ${m}ethod names. Can be repeated " + "multiple times to make one breakpoint for multiple methods.">; + def breakpoint_set_func_regex + : Option<"func-regex", "r">, + Group<7>, + Arg<"RegularExpression">, + Required, + Desc<"Set the breakpoint by function name, evaluating a " + "${r}egular-expression to find the function name(s).">; + def breakpoint_set_basename + : Option<"basename", "b">, + Group<8>, + Arg<"FunctionName">, + Required, + Completion<"Symbol">, + Desc<"Set the breakpoint by function ${b}asename (C++ namespaces and " + "arguments will be ignored). Can be repeated multiple times to " + "make one breakpoint for multiple symbols.">; + def breakpoint_set_source_pattern_regexp + : Option<"source-pattern-regexp", "p">, + Group<9>, + Arg<"RegularExpression">, + Required, + Desc<"Set the breakpoint by specifying a regular expression which is " + "matched against the source text in a source file or files " + "specified with the -f can be specified more than once. If no " + "source files are specified, uses the current \"default source " + "file\". If you want to match against all source files, ${p}ass " + "the \"--all-files\" option.">; + def breakpoint_set_all_files + : Option<"all-files", "A">, + Group<9>, + Desc<"${A}ll files are searched for source pattern matches.">; + def breakpoint_set_language_exception + : Option<"language-exception", "E">, + Group<10>, + Arg<"Language">, + Required, + Desc<"Set the breakpoint on ${e}xceptions thrown by the specified " + "language (without options, on throw but not catch.)">; + def breakpoint_set_on_throw + : Option<"on-throw", "w">, + Group<10>, + Arg<"Boolean">, + Desc<"Set the breakpoint on exception thro${w}.">; + def breakpoint_set_on_catch + : Option<"on-catch", "h">, + Group<10>, + Arg<"Boolean">, + Desc<"Set the breakpoint on exception catc${h}.">; + def breakpoint_set_language + : Option<"language", "L">, + GroupRange<3, 8>, + Arg<"Language">, + Desc<"Specifies the ${L}anguage to use when interpreting the " + "breakpoint's expression (note: currently only implemented for " + "setting breakpoints on identifiers). If not set the " + "target.language setting is used.">; + def breakpoint_set_skip_prologue + : Option<"skip-prologue", "K">, + Arg<"Boolean">, + Groups<[1, 3, 4, 5, 6, 7, 8, 12]>, + Desc<"S${k}ip the prologue if the breakpoint is at the beginning of a " + "function. If not set the target.skip-prologue setting is used.">; + def breakpoint_set_breakpoint_name + : Option<"breakpoint-name", "N">, + Arg<"BreakpointName">, + Desc<"Adds this to the list of ${n}ames for this breakpoint.">; + def breakpoint_set_address_slide + : Option<"address-slide", "R">, + Arg<"Address">, + Groups<[1, 3, 4, 5, 6, 7, 8, 12]>, + Desc<"Add the specified offset to whatever address(es) the breakpoint " + "${r}esolves to. At present this applies the offset directly as " + "given, and doesn't try to align it to instruction boundaries.">; + def breakpoint_set_move_to_nearest_code + : Option<"move-to-nearest-code", "m">, + Groups<[1, 9, 12]>, + Arg<"Boolean">, + Desc<"${M}ove breakpoints to nearest code. If not set the " + "target.move-to-nearest-code setting is used.">; + def breakpoint_set_file_colon_line + : Option<"joint-specifier", "y">, + Group<12>, + Arg<"FileLineColumn">, + Required, + Completion<"SourceFile">, + Desc<"A specifier in the form filename:line[:column] for setting file " + "& line breakpoints.">; /* Don't add this option till it actually does something useful... def breakpoint_set_exception_typename : Option<"exception-typename", "O">, Arg<"TypeName">, Desc<"The breakpoint will only stop if an " @@ -236,465 +358,743 @@ let Command = "breakpoint set" in { } let Command = "breakpoint clear" in { - def breakpoint_clear_file : Option<"file", "f">, Group<1>, Arg<"Filename">, - Completion<"SourceFile">, - Desc<"Specify the breakpoint by source location in this particular file.">; - def breakpoint_clear_line : Option<"line", "l">, Group<1>, Arg<"LineNum">, - Required, - Desc<"Specify the breakpoint by source location at this particular line.">; + def breakpoint_clear_file : Option<"file", "f">, + Group<1>, + Arg<"Filename">, + Completion<"SourceFile">, + Desc<"Specify the breakpoint by source location " + "in this particular ${f}ile.">; + def breakpoint_clear_line : Option<"line", "l">, + Group<1>, + Arg<"LineNum">, + Required, + Desc<"Specify the breakpoint by source location " + "at this particular ${l}ine.">; } let Command = "breakpoint delete" in { - def breakpoint_delete_force : Option<"force", "f">, Group<1>, - Desc<"Delete all breakpoints without querying for confirmation.">; - def breakpoint_delete_dummy_breakpoints : Option<"dummy-breakpoints", "D">, - Group<1>, Desc<"Delete Dummy breakpoints - i.e. breakpoints set before a " - "file is provided, which prime new targets.">; - def breakpoint_delete_disabled : Option<"disabled", "d">, Group<1>, - Desc<"Delete all breakpoints which are currently disabled. When using the disabled option " - "any breakpoints listed on the command line are EXCLUDED from deletion.">; + def breakpoint_delete_force + : Option<"force", "f">, + Group<1>, + Desc<"Delete all breakpoints without querying for confirmation.">; + def breakpoint_delete_dummy_breakpoints + : Option<"dummy-breakpoints", "D">, + Group<1>, + Desc<"Delete Dummy breakpoints - i.e. breakpoints set before a file is " + "provided, which prime new targets.">; + def breakpoint_delete_disabled + : Option<"disabled", "d">, + Group<1>, + Desc<"${D}elete all breakpoints which are currently disabled. When " + "using the disabled option any breakpoints listed on the command " + "line are EXCLUDED from deletion.">; } let Command = "breakpoint name" in { - def breakpoint_name_name : Option<"name", "N">, Group<1>, - Arg<"BreakpointName">, Desc<"Specifies a breakpoint name to use.">; - def breakpoint_name_breakpoint_id : Option<"breakpoint-id", "B">, Group<2>, - Arg<"BreakpointID">, Desc<"Specify a breakpoint ID to use.">; - def breakpoint_name_dummy_breakpoints : Option<"dummy-breakpoints", "D">, - Group<3>, Desc<"Operate on Dummy breakpoints - i.e. breakpoints set before " - "a file is provided, which prime new targets.">; - def breakpoint_name_help_string : Option<"help-string", "H">, Group<4>, - Arg<"None">, Desc<"A help string describing the purpose of this name.">; + def breakpoint_name_name : Option<"name", "N">, + Group<1>, + Arg<"BreakpointName">, + Desc<"Specifies a breakpoint name to use.">; + def breakpoint_name_breakpoint_id : Option<"breakpoint-id", "B">, + Group<2>, + Arg<"BreakpointID">, + Desc<"Specify a breakpoint ID to use.">; + def breakpoint_name_dummy_breakpoints + : Option<"dummy-breakpoints", "D">, + Group<3>, + Desc<"Operate on Dummy breakpoints - i.e. breakpoints set before a " + "file is provided, which prime new targets.">; + def breakpoint_name_help_string + : Option<"help-string", "H">, + Group<4>, + Arg<"None">, + Desc<"A help string describing the purpose of this name.">; } let Command = "breakpoint access" in { - def breakpoint_access_allow_list : Option<"allow-list", "L">, Group<1>, - Arg<"Boolean">, Desc<"Determines whether the breakpoint will show up in " - "break list if not referred to explicitly.">; - def breakpoint_access_allow_disable : Option<"allow-disable", "A">, Group<2>, - Arg<"Boolean">, Desc<"Determines whether the breakpoint can be disabled by " - "name or when all breakpoints are disabled.">; - def breakpoint_access_allow_delete : Option<"allow-delete", "D">, Group<3>, - Arg<"Boolean">, Desc<"Determines whether the breakpoint can be deleted by " - "name or when all breakpoints are deleted.">; + def breakpoint_access_allow_list + : Option<"allow-list", "L">, + Group<1>, + Arg<"Boolean">, + Desc<"Determines whether the breakpoint will show up in break list if " + "not referred to explicitly.">; + def breakpoint_access_allow_disable + : Option<"allow-disable", "A">, + Group<2>, + Arg<"Boolean">, + Desc<"Determines whether the breakpoint can be disabled by name or " + "when all breakpoints are disabled.">; + def breakpoint_access_allow_delete + : Option<"allow-delete", "D">, + Group<3>, + Arg<"Boolean">, + Desc<"Determines whether the breakpoint can be deleted by name or when " + "all breakpoints are deleted.">; } let Command = "breakpoint read" in { - def breakpoint_read_file : Option<"file", "f">, Arg<"Filename">, Required, - Completion<"DiskFile">, - Desc<"The file from which to read the breakpoints.">; - def breakpoint_read_breakpoint_name : Option<"breakpoint-name", "N">, - Arg<"BreakpointName">, Desc<"Only read in breakpoints with this name.">; + def breakpoint_read_file + : Option<"file", "f">, + Arg<"Filename">, + Required, + Completion<"DiskFile">, + Desc<"The ${f}ile from which to read the breakpoints.">; + def breakpoint_read_breakpoint_name + : Option<"breakpoint-name", "N">, + Arg<"BreakpointName">, + Desc<"Only read in breakpoints with this name.">; } let Command = "breakpoint write" in { - def breakpoint_write_file : Option<"file", "f">, Arg<"Filename">, Required, - Completion<"DiskFile">, - Desc<"The file into which to write the breakpoints.">; - def breakpoint_write_append : Option<"append", "a">, - Desc<"Append to saved breakpoints file if it exists.">; + def breakpoint_write_file + : Option<"file", "f">, + Arg<"Filename">, + Required, + Completion<"DiskFile">, + Desc<"The ${f}ile into which to write the breakpoints.">; + def breakpoint_write_append + : Option<"append", "a">, + Desc<"${A}ppend to saved breakpoints file if it exists.">; } let Command = "breakpoint command add" in { - def breakpoint_add_one_liner : Option<"one-liner", "o">, Group<1>, - Arg<"OneLiner">, Desc<"Specify a one-line breakpoint command inline. Be " - "sure to surround it with quotes.">; - def breakpoint_add_stop_on_error : Option<"stop-on-error", "e">, - Arg<"Boolean">, Desc<"Specify whether breakpoint command execution should " - "terminate on error.">; - def breakpoint_add_script_type : Option<"script-type", "s">, - EnumArg<"ScriptLang">, - Desc<"Specify the language for the commands - if none is specified, the " - "lldb command interpreter will be used.">; - def breakpoint_add_dummy_breakpoints : Option<"dummy-breakpoints", "D">, - Desc<"Sets Dummy breakpoints - i.e. breakpoints set before a file is " - "provided, which prime new targets.">; + def breakpoint_add_one_liner + : Option<"one-liner", "o">, + Group<1>, + Arg<"OneLiner">, + Desc<"Specify a ${o}ne-line breakpoint command inline. Be sure to " + "surround it with quotes.">; + def breakpoint_add_stop_on_error + : Option<"stop-on-error", "e">, + Arg<"Boolean">, + Desc<"Specify whether breakpoint command execution should terminate on " + "error.">; + def breakpoint_add_script_type + : Option<"script-type", "s">, + EnumArg<"ScriptLang">, + Desc<"Specify the language for the commands - if none is specified, " + "the lldb command interpreter will be used.">; + def breakpoint_add_dummy_breakpoints + : Option<"dummy-breakpoints", "D">, + Desc<"Sets Dummy breakpoints - i.e. breakpoints set before a file is " + "provided, which prime new targets.">; } let Command = "breakpoint command delete" in { - def breakpoint_command_delete_dummy_breakpoints : - Option<"dummy-breakpoints", "D">, Group<1>, - Desc<"Delete commands from Dummy breakpoints - i.e. breakpoints set before " - "a file is provided, which prime new targets.">; + def breakpoint_command_delete_dummy_breakpoints + : Option<"dummy-breakpoints", "D">, + Group<1>, + Desc<"Delete commands from Dummy breakpoints - i.e. breakpoints set " + "before a file is provided, which prime new targets.">; } let Command = "disassemble" in { - def disassemble_options_bytes : Option<"bytes", "b">, - Desc<"Show opcode bytes when disassembling.">; - def disassemble_options_kind : Option<"kind", "k">, - Desc<"Show instruction control flow kind. Refer to the enum " - "`InstructionControlFlowKind` for a list of control flow kind. " - "As an important note, far jumps, far calls and far returns often indicate " - "calls to and from kernel.">; - def disassemble_options_context : Option<"context", "C">, Arg<"NumLines">, - Desc<"Number of context lines of source to show.">; - def disassemble_options_mixed : Option<"mixed", "m">, - Desc<"Enable mixed source and assembly display.">; - def disassemble_options_raw : Option<"raw", "r">, - Desc<"Print raw disassembly with no symbol information.">; - def disassemble_options_plugin : Option<"plugin", "P">, Arg<"Plugin">, - Desc<"Name of the disassembler plugin you want to use.">; - def disassemble_options_flavor : Option<"flavor", "F">, - Arg<"DisassemblyFlavor">, Desc<"Name of the disassembly flavor you want to " - "use. Currently the only valid options are default, and for Intel " - "architectures, att and intel.">; - def disassemble_options_cpu : Option<"cpu", "X">, Arg<"CPUName">, - Desc<"Override the CPU for disassembling.">; - def disassemble_options_features : Option<"features", "Y">, Arg<"CPUFeatures">, - Desc<"Specify additional CPU features for disassembling.">; - def disassemble_options_arch : Option<"arch", "A">, Arg<"Architecture">, - Desc<"Specify the architecture to use from cross disassembly.">; - def disassemble_options_start_address : Option<"start-address", "s">, - Groups<[1,2]>, Arg<"AddressOrExpression">, Required, - Desc<"Address at which to start disassembling.">; - def disassemble_options_end_address : Option<"end-address", "e">, Group<1>, - Arg<"AddressOrExpression">, Desc<"Address at which to end disassembling.">; - def disassemble_options_count : Option<"count", "c">, Groups<[2,3,4,5,7]>, - Arg<"NumLines">, Desc<"Number of instructions to display.">; - def disassemble_options_name : Option<"name", "n">, Group<3>, - Arg<"FunctionName">, Completion<"Symbol">, - Desc<"Disassemble entire contents of the given function name.">; - def disassemble_options_frame : Option<"frame", "f">, Group<4>, - Desc<"Disassemble from the start of the current frame's function.">; - def disassemble_options_pc : Option<"pc", "p">, Group<5>, - Desc<"Disassemble around the current pc.">; - def disassemble_options_line : Option<"line", "l">, Group<6>, - Desc<"Disassemble the current frame's current source line instructions if " - "there is debug line table information, else disassemble around the pc.">; - def disassemble_options_address : Option<"address", "a">, Group<7>, - Arg<"AddressOrExpression">, - Desc<"Disassemble function containing this address.">; - def disassemble_options_force : Option<"force", "\\x01">, Groups<[2,3,4,5,7]>, - Desc<"Force disassembly of large functions.">; + def disassemble_options_bytes + : Option<"bytes", "b">, + Desc<"Show opcode ${b}ytes when disassembling.">; + def disassemble_options_kind + : Option<"kind", "k">, + Desc<"Show instruction control flow ${k}ind. Refer to the enum " + "`InstructionControlFlowKind` for a list of control flow kind. As " + "an important note, far jumps, far calls and far returns often " + "indicate calls to and from kernel.">; + def disassemble_options_context + : Option<"context", "C">, + Arg<"NumLines">, + Desc<"Number of ${c}ontext lines of source to show.">; + def disassemble_options_mixed + : Option<"mixed", "m">, + Desc<"Enable ${m}ixed source and assembly display.">; + def disassemble_options_raw + : Option<"raw", "r">, + Desc<"Print ${r}aw disassembly with no symbol information.">; + def disassemble_options_plugin + : Option<"plugin", "P">, + Arg<"Plugin">, + Desc<"Name of the disassembler ${p}lugin you want to use.">; + def disassemble_options_flavor + : Option<"flavor", "F">, + Arg<"DisassemblyFlavor">, + Desc<"Name of the disassembly ${f}lavor you want to use. Currently the " + "only valid options are default, and for Intel architectures, att " + "and intel.">; + def disassemble_options_cpu : Option<"cpu", "X">, + Arg<"CPUName">, + Desc<"Override the CPU for disassembling.">; + def disassemble_options_features + : Option<"features", "Y">, + Arg<"CPUFeatures">, + Desc<"Specify additional CPU features for disassembling.">; + def disassemble_options_arch + : Option<"arch", "A">, + Arg<"Architecture">, + Desc<"Specify the ${a}rchitecture to use for cross disassembly.">; + def disassemble_options_start_address + : Option<"start-address", "s">, + Groups<[1, 2]>, + Arg<"AddressOrExpression">, + Required, + Desc<"Address at which to ${s}tart disassembling.">; + def disassemble_options_end_address + : Option<"end-address", "e">, + Group<1>, + Arg<"AddressOrExpression">, + Desc<"Address at which to ${e}nd disassembling.">; + def disassemble_options_count : Option<"count", "c">, + Groups<[2, 3, 4, 5, 7]>, + Arg<"NumLines">, + Desc<"Number of instructions to display.">; + def disassemble_options_name + : Option<"name", "n">, + Group<3>, + Arg<"FunctionName">, + Completion<"Symbol">, + Desc<"Disassemble entire contents of the given function ${n}ame.">; + def disassemble_options_frame + : Option<"frame", "f">, + Group<4>, + Desc<"Disassemble from the start of the current frame's function.">; + def disassemble_options_pc : Option<"pc", "p">, + Group<5>, + Desc<"Disassemble around the current ${p}c.">; + def disassemble_options_line + : Option<"line", "l">, + Group<6>, + Desc<"Disassemble the current frame's current source ${l}ine " + "instructions if there is debug line table information, else " + "disassemble around the pc.">; + def disassemble_options_address + : Option<"address", "a">, + Group<7>, + Arg<"AddressOrExpression">, + Desc<"Disassemble function containing this ${a}ddress.">; + def disassemble_options_force : Option<"force", "\\x01">, + Groups<[2, 3, 4, 5, 7]>, + Desc<"Force disassembly of large functions.">; + def disassemble_options_variable + : Option<"variable", "v">, + Desc<"Enable ${v}ariable disassembly annotations for this invocation.">; } let Command = "diagnostics dump" in { - def diagnostics_dump_directory : Option<"directory", "d">, Group<1>, - Arg<"Path">, Desc<"Dump the diagnostics to the given directory.">; + def diagnostics_dump_directory + : Option<"directory", "d">, + Group<1>, + Arg<"Path">, + Desc<"Dump the diagnostics to the given directory.">; } let Command = "expression" in { - def expression_options_all_threads : Option<"all-threads", "a">, - Groups<[1,2]>, Arg<"Boolean">, Desc<"Should we run all threads if the " - "execution doesn't complete on one thread.">; - def expression_options_ignore_breakpoints : Option<"ignore-breakpoints", "i">, - Groups<[1,2]>, Arg<"Boolean">, - Desc<"Ignore breakpoint hits while running expressions">; - def expression_options_timeout : Option<"timeout", "t">, Groups<[1,2]>, - Arg<"UnsignedInteger">, - Desc<"Timeout value (in microseconds) for running the expression.">; - def expression_options_unwind_on_error : Option<"unwind-on-error", "u">, - Groups<[1,2]>, Arg<"Boolean">, - Desc<"Clean up program state if the expression causes a crash, or raises a " - "signal. Note, unlike gdb hitting a breakpoint is controlled by another " - "option (-i).">; - def expression_options_debug : Option<"debug", "g">, Groups<[1,2]>, - Desc<"When specified, debug the JIT code by setting a breakpoint on the " - "first instruction and forcing breakpoints to not be ignored (-i0) and no " - "unwinding to happen on error (-u0).">; - def expression_options_language : Option<"language", "l">, Groups<[1,2,3]>, - Arg<"Language">, Desc<"Specifies the Language to use when parsing the " - "expression. If not set the target.language setting is used.">; - def expression_options_apply_fixits : Option<"apply-fixits", "X">, - Groups<[1,2]>, Arg<"Boolean">, Desc<"If true, simple fix-it hints will be " - "automatically applied to the expression.">; - def expression_options_description_verbosity : - Option<"description-verbosity", "v">, Group<1>, - OptionalEnumArg<"DescriptionVerbosity">, - Desc<"How verbose should the output of this expression be, if the object " - "description is asked for.">; - def expression_options_top_level : Option<"top-level", "p">, Groups<[1,2]>, - Desc<"Interpret the expression as a complete translation unit, without " - "injecting it into the local context. Allows declaration of persistent, " - "top-level entities without a $ prefix.">; - def expression_options_allow_jit : Option<"allow-jit", "j">, Groups<[1,2]>, - Arg<"Boolean">, - Desc<"Controls whether the expression can fall back to being JITted if it's " - "not supported by the interpreter (defaults to true).">; - def persistent_result : Option<"persistent-result", "\\x01">, Groups<[1,2]>, - Arg<"Boolean">, - Desc<"Persist expression result in a variable for subsequent use. " - "Expression results will be labeled with $-prefixed variables, e.g. $0, " - "$1, etc.">; + def expression_options_all_threads + : Option<"all-threads", "a">, + Groups<[1, 2]>, + Arg<"Boolean">, + Desc< + "Should we run ${a}ll threads if the execution doesn't complete on " + "one thread.">; + def expression_options_ignore_breakpoints + : Option<"ignore-breakpoints", "i">, + Groups<[1, 2]>, + Arg<"Boolean">, + Desc<"${I}gnore breakpoint hits while running expressions">; + def expression_options_timeout + : Option<"timeout", "t">, + Groups<[1, 2]>, + Arg<"UnsignedInteger">, + Desc<"${T}imeout value (in microseconds) for running the expression.">; + def expression_options_unwind_on_error + : Option<"unwind-on-error", "u">, + Groups<[1, 2]>, + Arg<"Boolean">, + Desc<"Clean up program state if the expression causes a crash, or " + "raises a signal. Note, unlike gdb hitting a breakpoint is " + "controlled by another option (-i).">; + def expression_options_debug + : Option<"debug", "g">, + Groups<[1, 2]>, + Desc<"When specified, debug the JIT code by setting a breakpoint on " + "the first instruction and forcing breakpoints to not be ignored " + "(-i0) and no unwinding to happen on error (-u0).">; + def expression_options_language + : Option<"language", "l">, + Groups<[1, 2, 3]>, + Arg<"Language">, + Desc<"Specifies the Language to use when parsing the expression. If " + "not set the target.language setting is used.">; + def expression_options_apply_fixits + : Option<"apply-fixits", "X">, + Groups<[1, 2]>, + Arg<"Boolean">, + Desc<"If true, simple fix-it hints will be automatically applied to " + "the expression.">; + def expression_options_description_verbosity + : Option<"description-verbosity", "v">, + Group<1>, + OptionalEnumArg<"DescriptionVerbosity">, + Desc<"How verbose should the output of this expression be, if the " + "object description is asked for.">; + def expression_options_top_level + : Option<"top-level", "p">, + Groups<[1, 2]>, + Desc<"Interpret the expression as a complete translation unit, without " + "injecting it into the local context. Allows declaration of " + "persistent, top-level entities without a $ prefix.">; + def expression_options_allow_jit + : Option<"allow-jit", "j">, + Groups<[1, 2]>, + Arg<"Boolean">, + Desc<"Controls whether the expression can fall back to being JITted if " + "it's not supported by the interpreter (defaults to true).">; + def persistent_result + : Option<"persistent-result", "\\x01">, + Groups<[1, 2]>, + Arg<"Boolean">, + Desc<"Persist expression result in a variable for subsequent use. " + "Expression results will be labeled with $-prefixed variables, " + "e.g. $0, $1, etc.">; } let Command = "frame diag" in { - def frame_diag_register : Option<"register", "r">, Group<1>, - Arg<"RegisterName">, Desc<"A register to diagnose.">; - def frame_diag_address : Option<"address", "a">, Group<1>, Arg<"Address">, - Desc<"An address to diagnose.">; - def frame_diag_offset : Option<"offset", "o">, Group<1>, Arg<"Offset">, - Desc<"An optional offset. Requires --register.">; + def frame_diag_register : Option<"register", "r">, + Group<1>, + Arg<"RegisterName">, + Desc<"A register to diagnose.">; + def frame_diag_address : Option<"address", "a">, + Group<1>, + Arg<"Address">, + Desc<"An address to diagnose.">; + def frame_diag_offset : Option<"offset", "o">, + Group<1>, + Arg<"Offset">, + Desc<"An optional offset. Requires --register.">; } let Command = "frame select" in { - def frame_select_relative : Option<"relative", "r">, Group<1>, Arg<"Offset">, - Desc<"A relative frame index offset from the current frame index.">; + def frame_select_relative + : Option<"relative", "r">, + Group<1>, + Arg<"Offset">, + Desc<"A relative frame index offset from the current frame index.">; } let Command = "frame recognizer add" in { - def frame_recognizer_shlib : Option<"shlib", "s">, Arg<"ShlibName">, - Completion<"Module">, - Desc<"Name of the module or shared library that this recognizer applies " - "to.">; - def frame_recognizer_function : Option<"function", "n">, Arg<"Name">, - Completion<"Symbol">, - Desc<"Name of the function that this recognizer applies to. " - "Can be specified more than once except if -x|--regex is provided.">; - def frame_recognizer_python_class : Option<"python-class", "l">, Group<2>, - Arg<"PythonClass">, - Desc<"Give the name of a Python class to use for this frame recognizer.">; - def frame_recognizer_regex : Option<"regex", "x">, - Desc<"Function name and module name are actually regular expressions.">; - def frame_recognizer_first_instruction_only : Option<"first-instruction-only", "f">, Arg<"Boolean">, - Desc<"If true, only apply this recognizer to frames whose PC currently points to the " - "first instruction of the specified function. If false, the recognizer " - "will always be applied, regardless of the current position within the specified function. The " - "implementer should keep in mind that some features, e.g. accessing function argument " - "values via $arg<N>, are not guaranteed to work reliably in this case, so extra care must " - "be taken to make the recognizer operate correctly. Defaults to true.">; + def frame_recognizer_shlib : Option<"shlib", "s">, + Arg<"ShlibName">, + Completion<"Module">, + Desc<"Name of the module or shared library that " + "this recognizer applies to.">; + def frame_recognizer_function + : Option<"function", "n">, + Arg<"Name">, + Completion<"Symbol">, + Desc<"Name of the function that this recognizer applies to. Can be " + "specified more than once except if -x|--regex is provided.">; + def frame_recognizer_python_class : Option<"python-class", "l">, + Group<2>, + Arg<"PythonClass">, + Desc<"Give the name of a Python class to " + "use for this frame recognizer.">; + def frame_recognizer_regex + : Option<"regex", "x">, + Desc<"Function name and module name are actually regular expressions.">; + def frame_recognizer_first_instruction_only + : Option<"first-instruction-only", "f">, + Arg<"Boolean">, + Desc<"If true, only apply this recognizer to frames whose PC currently " + "points to the first instruction of the specified function. If " + "false, the recognizer will always be applied, regardless of the " + "current position within the specified function. The implementer " + "should keep in mind that some features, e.g., accessing function " + "argument values via $arg<N>, are not guaranteed to work reliably " + "in this case, so extra care must be taken to make the recognizer " + "operate correctly. Defaults to true.">; } let Command = "history" in { - def history_count : Option<"count", "c">, Group<1>, Arg<"UnsignedInteger">, - Desc<"How many history commands to print.">; - def history_start_index : Option<"start-index", "s">, Group<1>, - Arg<"UnsignedInteger">, Desc<"Index at which to start printing history " - "commands (or end to mean tail mode).">; - def history_end_index : Option<"end-index", "e">, Group<1>, - Arg<"UnsignedInteger">, - Desc<"Index at which to stop printing history commands.">; - def history_clear : Option<"clear", "C">, Group<2>, - Desc<"Clears the current command history.">; + def history_count : Option<"count", "c">, + Group<1>, + Arg<"UnsignedInteger">, + Desc<"How many history commands to print.">; + def history_start_index : Option<"start-index", "s">, + Group<1>, + Arg<"UnsignedInteger">, + Desc<"Index at which to start printing history " + "commands (or end to mean tail mode).">; + def history_end_index + : Option<"end-index", "e">, + Group<1>, + Arg<"UnsignedInteger">, + Desc<"Index at which to stop printing history commands.">; + def history_clear : Option<"clear", "C">, + Group<2>, + Desc<"Clears the current command history.">; } let Command = "log enable" in { - def log_file : Option<"file", "f">, Group<1>, Arg<"Filename">, - Desc<"Set the destination file to log to.">; - def log_handler : Option<"log-handler", "h">, Group<1>, - EnumArg<"LogHandler">, Desc<"Specify a log handler which determines where log messages are written.">; - def log_buffer_size : Option<"buffer", "b">, Group<1>, Arg<"UnsignedInteger">, - Desc<"Set the log to be buffered, using the specified buffer size, if supported by the log handler.">; - def log_verbose : Option<"verbose", "v">, Group<1>, - Desc<"Enable verbose logging.">; - def log_sequence : Option<"sequence", "s">, Group<1>, - Desc<"Prepend all log lines with an increasing integer sequence id.">; - def log_timestamp : Option<"timestamp", "T">, Group<1>, - Desc<"Prepend all log lines with a timestamp.">; - def log_pid_tid : Option<"pid-tid", "p">, Group<1>, - Desc<"Prepend all log lines with the process and thread ID that generates " - "the log line.">; - def log_thread_name : Option<"thread-name", "n">, Group<1>, - Desc<"Prepend all log lines with the thread name for the thread that " - "generates the log line.">; - - def log_stack : Option<"stack", "S">, Group<1>, - Desc<"Append a stack backtrace to each log line.">; - def log_append : Option<"append", "a">, Group<1>, - Desc<"Append to the log file instead of overwriting.">; - def log_file_function : Option<"file-function", "F">, Group<1>, - Desc<"Prepend the names of files and function that generate the logs.">; + def log_file : Option<"file", "f">, + Group<1>, + Arg<"Filename">, + Desc<"Set the destination file to log to.">; + def log_handler : Option<"log-handler", "h">, + Group<1>, + EnumArg<"LogHandler">, + Desc<"Specify a log handler which determines where log " + "messages are written.">; + def log_buffer_size : Option<"buffer", "b">, + Group<1>, + Arg<"UnsignedInteger">, + Desc<"Set the log to be buffered, using the specified " + "buffer size, if supported by the log handler.">; + def log_verbose : Option<"verbose", "v">, + Group<1>, + Desc<"Enable verbose logging.">; + def log_sequence + : Option<"sequence", "s">, + Group<1>, + Desc<"Prepend all log lines with an increasing integer sequence id.">; + def log_timestamp : Option<"timestamp", "T">, + Group<1>, + Desc<"Prepend all log lines with a timestamp.">; + def log_pid_tid : Option<"pid-tid", "p">, + Group<1>, + Desc<"Prepend all log lines with the process and thread ID " + "that generates the log line.">; + def log_thread_name : Option<"thread-name", "n">, + Group<1>, + Desc<"Prepend all log lines with the thread name for " + "the thread that generates the log line.">; + + def log_stack : Option<"stack", "S">, + Group<1>, + Desc<"Append a stack backtrace to each log line.">; + def log_append : Option<"append", "a">, + Group<1>, + Desc<"Append to the log file instead of overwriting.">; + def log_file_function + : Option<"file-function", "F">, + Group<1>, + Desc<"Prepend the names of files and function that generate the logs.">; } let Command = "log dump" in { - def log_dump_file : Option<"file", "f">, Group<1>, Arg<"Filename">, - Desc<"Set the destination file to dump to.">; + def log_dump_file : Option<"file", "f">, + Group<1>, + Arg<"Filename">, + Desc<"Set the destination file to dump to.">; } let Command = "memory read" in { - def memory_read_num_per_line : Option<"num-per-line", "l">, Group<1>, - Arg<"NumberPerLine">, Desc<"The number of items per line to display.">; - def memory_read_binary : Option<"binary", "b">, Group<2>, - Desc<"If true, memory will be saved as binary. If false, the memory is " - "saved save as an ASCII dump that uses the format, size, count and number " - "per line settings.">; - def memory_read_type : Option<"type", "t">, Groups<[3,4]>, Arg<"Name">, - Required, Desc<"The name of a type to view memory as.">; - def memory_read_language : Option<"language", "x">, Group<4>, Arg<"Language">, - Desc<"The language of the type to view memory as.">; - def memory_read_offset : Option<"offset", "E">, Group<3>, Arg<"Count">, - Desc<"How many elements of the specified type to skip before starting to " - "display data.">; - def memory_read_force : Option<"force", "r">, Groups<[1,2,3]>, - Desc<"Necessary if reading over target.max-memory-read-size bytes.">; + def memory_read_num_per_line + : Option<"num-per-line", "l">, + Group<1>, + Arg<"NumberPerLine">, + Desc<"The number of items per line to display.">; + def memory_read_binary + : Option<"binary", "b">, + Group<2>, + Desc<"If true, memory will be saved as binary. If false, the memory is " + "saved save as an ASCII dump that uses the format, size, count " + "and number per line settings.">; + def memory_read_type : Option<"type", "t">, + Groups<[3, 4]>, + Arg<"Name">, + Required, + Desc<"The name of a type to view memory as.">; + def memory_read_language + : Option<"language", "x">, + Group<4>, + Arg<"Language">, + Desc<"The language of the type to view memory as.">; + def memory_read_offset : Option<"offset", "E">, + Group<3>, + Arg<"Count">, + Desc<"How many elements of the specified type to " + "skip before starting to display data.">; + def memory_read_force + : Option<"force", "r">, + Groups<[1, 2, 3]>, + Desc<"Necessary if reading over target.max-memory-read-size bytes.">; } let Command = "memory find" in { - def memory_find_expression : Option<"expression", "e">, Group<1>, - Arg<"Expression">, Required, - Desc<"Evaluate an expression to obtain a byte pattern.">; - def memory_find_string : Option<"string", "s">, Group<2>, Arg<"Name">, - Required, Desc<"Use text to find a byte pattern.">; - def memory_find_count : Option<"count", "c">, Arg<"Count">, - Desc<"How many times to perform the search.">; - def memory_find_dump_offset : Option<"dump-offset", "o">, Arg<"Offset">, - Desc<"When dumping memory for a match, an offset from the match location to" - " start dumping from.">; + def memory_find_expression + : Option<"expression", "e">, + Group<1>, + Arg<"Expression">, + Required, + Desc<"Evaluate an expression to obtain a byte pattern.">; + def memory_find_string : Option<"string", "s">, + Group<2>, + Arg<"Name">, + Required, + Desc<"Use text to find a byte pattern.">; + def memory_find_count : Option<"count", "c">, + Arg<"Count">, + Desc<"How many times to perform the search.">; + def memory_find_dump_offset + : Option<"dump-offset", "o">, + Arg<"Offset">, + Desc<"When dumping memory for a match, an offset from the match " + "location to start dumping from.">; } let Command = "memory write" in { - def memory_write_infile : Option<"infile", "i">, Group<1>, Arg<"Filename">, - Required, Desc<"Write memory using the contents of a file.">; - def memory_write_offset : Option<"offset", "o">, Group<1>, Arg<"Offset">, - Desc<"Start writing bytes from an offset within the input file.">; + def memory_write_infile : Option<"infile", "i">, + Group<1>, + Arg<"Filename">, + Required, + Desc<"Write memory using the contents of a file.">; + def memory_write_offset + : Option<"offset", "o">, + Group<1>, + Arg<"Offset">, + Desc<"Start writing bytes from an offset within the input file.">; } let Command = "memory region" in { - def memory_region_all : Option<"all", "a">, Group<2>, Required, - Desc<"Show all memory regions. This is equivalent to starting from address " - "0 and repeating the command. Unmapped areas are included.">; + def memory_region_all : Option<"all", "a">, + Group<2>, + Required, + Desc<"Show all memory regions. This is equivalent to " + "starting from address 0 and repeating the " + "command. Unmapped areas are included.">; } let Command = "memory tag write" in { - def memory_write_end_addr : Option<"end-addr", "e">, Group<1>, - Arg<"AddressOrExpression">, Desc< - "Set tags for start address to end-addr, repeating tags as needed" - " to cover the range. (instead of calculating the range from the" - " number of tags given)">; + def memory_write_end_addr + : Option<"end-addr", "e">, + Group<1>, + Arg<"AddressOrExpression">, + Desc<"Set tags for start address to end-addr, repeating tags as needed " + "to cover the range. (instead of calculating the range from the " + "number of tags given)">; } let Command = "register read" in { - def register_read_alternate : Option<"alternate", "A">, - Desc<"Display register names using the alternate register name if there " - "is one.">; - def register_read_set : Option<"set", "s">, Group<1>, Arg<"Index">, - Desc<"Specify which register sets to dump by index.">; - def register_read_all : Option<"all", "a">, Group<2>, - Desc<"Show all register sets.">; + def register_read_alternate + : Option<"alternate", "A">, + Desc<"Display register names using the alternate register name if " + "there is one.">; + def register_read_set : Option<"set", "s">, + Group<1>, + Arg<"Index">, + Desc<"Specify which register sets to dump by index.">; + def register_read_all : Option<"all", "a">, + Group<2>, + Desc<"Show all register sets.">; } let Command = "source" in { - def source_stop_on_error : Option<"stop-on-error", "e">, Arg<"Boolean">, - Desc<"If true, stop executing commands on error.">; - def source_stop_on_continue : Option<"stop-on-continue", "c">, Arg<"Boolean">, - Desc<"If true, stop executing commands on continue.">; - def source_silent_run : Option<"silent-run", "s">, Arg<"Boolean">, - Desc<"If true don't echo commands while executing.">; - def cmd_relative_to_command_file : Option<"relative-to-command-file", "C">, - Desc<"Resolve non-absolute paths relative to the location of the " - "current command file. This argument can only be used when the command is " - "being sourced from a file.">; + def source_stop_on_error : Option<"stop-on-error", "e">, + Arg<"Boolean">, + Desc<"If true, stop executing commands on error.">; + def source_stop_on_continue + : Option<"stop-on-continue", "c">, + Arg<"Boolean">, + Desc<"If true, stop executing commands on continue.">; + def source_silent_run : Option<"silent-run", "s">, + Arg<"Boolean">, + Desc<"If true don't echo commands while executing.">; + def cmd_relative_to_command_file + : Option<"relative-to-command-file", "C">, + Desc<"Resolve non-absolute paths relative to the location of the " + "current command file. This argument can only be used when the " + "command is being sourced from a file.">; } let Command = "alias" in { - def alias_help : Option<"help", "h">, Arg<"HelpText">, - Desc<"Help text for this command">; - def alias_long_help : Option<"long-help", "H">, Arg<"HelpText">, - Desc<"Long help text for this command">; + def alias_help : Option<"help", "h">, + Arg<"HelpText">, + Desc<"Help text for this command">; + def alias_long_help : Option<"long-help", "H">, + Arg<"HelpText">, + Desc<"Long help text for this command">; } let Command = "regex" in { - def regex_help : Option<"help", "h">, Group<1>, Arg<"None">, - Desc<"The help text to display for this command.">; - def regex_syntax : Option<"syntax", "s">, Group<1>, Arg<"None">, - Desc<"A syntax string showing the typical usage syntax.">; + def regex_help : Option<"help", "h">, + Group<1>, + Arg<"None">, + Desc<"The help text to display for this command.">; + def regex_syntax : Option<"syntax", "s">, + Group<1>, + Arg<"None">, + Desc<"A syntax string showing the typical usage syntax.">; } let Command = "permissions" in { - def permissions_permissions_value : Option<"permissions-value", "v">, - Arg<"PermissionsNumber">, - Desc<"Give out the numeric value for permissions (e.g. 757)">; - def permissions_permissions_string : Option<"permissions-string", "s">, - Arg<"PermissionsString">, - Desc<"Give out the string value for permissions (e.g. rwxr-xr--).">; + def permissions_permissions_value + : Option<"permissions-value", "v">, + Arg<"PermissionsNumber">, + Desc<"Give out the numeric value for permissions (e.g. 757)">; + def permissions_permissions_string + : Option<"permissions-string", "s">, + Arg<"PermissionsString">, + Desc<"Give out the string value for permissions (e.g. rwxr-xr--).">; def permissions_user_read : Option<"user-read", "r">, - Desc<"Allow user to read.">; + Desc<"Allow user to read.">; def permissions_user_write : Option<"user-write", "w">, - Desc<"Allow user to write.">; + Desc<"Allow user to write.">; def permissions_user_exec : Option<"user-exec", "x">, - Desc<"Allow user to execute.">; + Desc<"Allow user to execute.">; def permissions_group_read : Option<"group-read", "R">, - Desc<"Allow group to read.">; + Desc<"Allow group to read.">; def permissions_group_write : Option<"group-write", "W">, - Desc<"Allow group to write.">; + Desc<"Allow group to write.">; def permissions_group_exec : Option<"group-exec", "X">, - Desc<"Allow group to execute.">; + Desc<"Allow group to execute.">; def permissions_world_read : Option<"world-read", "d">, - Desc<"Allow world to read.">; + Desc<"Allow world to read.">; def permissions_world_write : Option<"world-write", "t">, - Desc<"Allow world to write.">; + Desc<"Allow world to write.">; def permissions_world_exec : Option<"world-exec", "e">, - Desc<"Allow world to execute.">; + Desc<"Allow world to execute.">; } let Command = "platform fread" in { - def platform_fread_offset : Option<"offset", "o">, Group<1>, Arg<"Index">, - Desc<"Offset into the file at which to start reading.">; - def platform_fread_count : Option<"count", "c">, Group<1>, Arg<"Count">, - Desc<"Number of bytes to read from the file.">; + def platform_fread_offset + : Option<"offset", "o">, + Group<1>, + Arg<"Index">, + Desc<"Offset into the file at which to start reading.">; + def platform_fread_count : Option<"count", "c">, + Group<1>, + Arg<"Count">, + Desc<"Number of bytes to read from the file.">; } let Command = "platform fwrite" in { - def platform_fwrite_offset : Option<"offset", "o">, Group<1>, Arg<"Index">, - Desc<"Offset into the file at which to start reading.">; - def platform_fwrite_data : Option<"data", "d">, Group<1>, Arg<"Value">, - Desc<"Text to write to the file.">; + def platform_fwrite_offset + : Option<"offset", "o">, + Group<1>, + Arg<"Index">, + Desc<"Offset into the file at which to start reading.">; + def platform_fwrite_data : Option<"data", "d">, + Group<1>, + Arg<"Value">, + Desc<"Text to write to the file.">; } let Command = "platform process list" in { - def platform_process_list_pid : Option<"pid", "p">, Group<1>, Arg<"Pid">, - Desc<"List the process info for a specific process ID.">; - def platform_process_list_name : Option<"name", "n">, Group<2>, - Arg<"ProcessName">, Required, - Desc<"Find processes with executable basenames that match a string.">; - def platform_process_list_ends_with : Option<"ends-with", "e">, Group<3>, - Arg<"ProcessName">, Required, - Desc<"Find processes with executable basenames that end with a string.">; - def platform_process_list_starts_with : Option<"starts-with", "s">, Group<4>, - Arg<"ProcessName">, Required, - Desc<"Find processes with executable basenames that start with a string.">; - def platform_process_list_contains : Option<"contains", "c">, Group<5>, - Arg<"ProcessName">, Required, - Desc<"Find processes with executable basenames that contain a string.">; - def platform_process_list_regex : Option<"regex", "r">, Group<6>, - Arg<"RegularExpression">, Required, - Desc<"Find processes with executable basenames that match a regular " - "expression.">; - def platform_process_list_parent : Option<"parent", "P">, GroupRange<2, 6>, - Arg<"Pid">, Desc<"Find processes that have a matching parent process ID.">; - def platform_process_list_uid : Option<"uid", "u">, GroupRange<2, 6>, - Arg<"UnsignedInteger">, Validator<"&posix_validator">, - Desc<"Find processes that have a matching user ID.">; - def platform_process_list_euid : Option<"euid", "U">, GroupRange<2, 6>, - Arg<"UnsignedInteger">, Validator<"&posix_validator">, - Desc<"Find processes that have a matching effective user ID.">; - def platform_process_list_gid : Option<"gid", "g">, GroupRange<2, 6>, - Arg<"UnsignedInteger">, Validator<"&posix_validator">, - Desc<"Find processes that have a matching group ID.">; - def platform_process_list_egid : Option<"egid", "G">, GroupRange<2, 6>, - Arg<"UnsignedInteger">, Validator<"&posix_validator">, - Desc<"Find processes that have a matching effective group ID.">; - def platform_process_list_arch : Option<"arch", "a">, GroupRange<2, 6>, - Arg<"Architecture">, - Desc<"Find processes that have a matching architecture.">; - def platform_process_list_show_args : Option<"show-args", "A">, - GroupRange<1, 6>, - Desc<"Show process arguments instead of the process executable basename.">; - def platform_process_list_all_users: Option<"all-users", "x">, - GroupRange<1,6>, - Desc<"Show processes matching all user IDs.">; - def platform_process_list_verbose : Option<"verbose", "v">, GroupRange<1, 6>, - Desc<"Enable verbose output.">; + def platform_process_list_pid + : Option<"pid", "p">, + Group<1>, + Arg<"Pid">, + Desc<"List the process info for a specific process ID.">; + def platform_process_list_name + : Option<"name", "n">, + Group<2>, + Arg<"ProcessName">, + Required, + Desc<"Find processes with executable basenames that match a string.">; + def platform_process_list_ends_with + : Option<"ends-with", "e">, + Group<3>, + Arg<"ProcessName">, + Required, + Desc< + "Find processes with executable basenames that end with a string.">; + def platform_process_list_starts_with + : Option<"starts-with", "s">, + Group<4>, + Arg<"ProcessName">, + Required, + Desc<"Find processes with executable basenames that start with a " + "string.">; + def platform_process_list_contains + : Option<"contains", "c">, + Group<5>, + Arg<"ProcessName">, + Required, + Desc<"Find processes with executable basenames that contain a string.">; + def platform_process_list_regex + : Option<"regex", "r">, + Group<6>, + Arg<"RegularExpression">, + Required, + Desc<"Find processes with executable basenames that match a regular " + "expression.">; + def platform_process_list_parent + : Option<"parent", "P">, + GroupRange<2, 6>, + Arg<"Pid">, + Desc<"Find processes that have a matching parent process ID.">; + def platform_process_list_uid + : Option<"uid", "u">, + GroupRange<2, 6>, + Arg<"UnsignedInteger">, + Validator<"&posix_validator">, + Desc<"Find processes that have a matching user ID.">; + def platform_process_list_euid + : Option<"euid", "U">, + GroupRange<2, 6>, + Arg<"UnsignedInteger">, + Validator<"&posix_validator">, + Desc<"Find processes that have a matching effective user ID.">; + def platform_process_list_gid + : Option<"gid", "g">, + GroupRange<2, 6>, + Arg<"UnsignedInteger">, + Validator<"&posix_validator">, + Desc<"Find processes that have a matching group ID.">; + def platform_process_list_egid + : Option<"egid", "G">, + GroupRange<2, 6>, + Arg<"UnsignedInteger">, + Validator<"&posix_validator">, + Desc<"Find processes that have a matching effective group ID.">; + def platform_process_list_arch + : Option<"arch", "a">, + GroupRange<2, 6>, + Arg<"Architecture">, + Desc<"Find processes that have a matching architecture.">; + def platform_process_list_show_args + : Option<"show-args", "A">, + GroupRange<1, 6>, + Desc<"Show process arguments instead of the process executable " + "basename.">; + def platform_process_list_all_users + : Option<"all-users", "x">, + GroupRange<1, 6>, + Desc<"Show processes matching all user IDs.">; + def platform_process_list_verbose : Option<"verbose", "v">, + GroupRange<1, 6>, + Desc<"Enable verbose output.">; } let Command = "platform process attach" in { - def platform_process_attach_plugin : Option<"plugin", "P">, Arg<"Plugin">, - Desc<"Name of the process plugin you want to use.">; - def platform_process_attach_pid : Option<"pid", "p">, Group<1>, Arg<"Pid">, - Desc<"The process ID of an existing process to attach to.">; - def platform_process_attach_name : Option<"name", "n">, Group<2>, - Arg<"ProcessName">, Desc<"The name of the process to attach to.">; - def platform_process_attach_waitfor : Option<"waitfor", "w">, Group<2>, - Desc<"Wait for the process with <process-name> to launch.">; + def platform_process_attach_plugin + : Option<"plugin", "P">, + Arg<"Plugin">, + Desc<"Name of the process plugin you want to use.">; + def platform_process_attach_pid + : Option<"pid", "p">, + Group<1>, + Arg<"Pid">, + Desc<"The process ID of an existing process to attach to.">; + def platform_process_attach_name + : Option<"name", "n">, + Group<2>, + Arg<"ProcessName">, + Desc<"The name of the process to attach to.">; + def platform_process_attach_waitfor + : Option<"waitfor", "w">, + Group<2>, + Desc<"Wait for the process with <process-name> to launch.">; } let Command = "platform shell" in { - def platform_shell_host : Option<"host", "h">, - Desc<"Run the commands on the host shell when enabled.">; - def platform_shell_timeout : Option<"timeout", "t">, Arg<"Value">, - Desc<"Seconds to wait for the remote host to finish running the command.">; - def platform_shell_interpreter : Option<"shell", "s">, Arg<"Path">, - Desc<"Shell interpreter path. This is the binary used to run the command.">; + def platform_shell_host + : Option<"host", "h">, + Desc<"Run the commands on the host shell when enabled.">; + def platform_shell_timeout : Option<"timeout", "t">, + Arg<"Value">, + Desc<"Seconds to wait for the remote host to " + "finish running the command.">; + def platform_shell_interpreter : Option<"shell", "s">, + Arg<"Path">, + Desc<"Shell interpreter path. This is the " + "binary used to run the command.">; } let Command = "plugin list" in { @@ -703,808 +1103,1316 @@ let Command = "plugin list" in { } let Command = "process launch" in { - def process_launch_stop_at_entry : Option<"stop-at-entry", "s">, - Desc<"Stop at the entry point of the program when launching a process.">; - def process_launch_stop_at_user_entry : Option<"stop-at-user-entry", "m">, - Desc<"Stop at the user entry point when launching a process. For C based " - "languages this will be the 'main' function, but this might differ for " - "other languages.">; - def process_launch_disable_aslr : Option<"disable-aslr", "A">, Arg<"Boolean">, - Desc<"Set whether to disable address space layout randomization when launching a process.">; - def process_launch_plugin : Option<"plugin", "P">, Arg<"Plugin">, - Desc<"Name of the process plugin you want to use.">; - def process_launch_working_dir : Option<"working-dir", "w">, Arg<"DirectoryName">, - Desc<"Set the current working directory to <path> when running the inferior. This option " - "applies only to the current `process launch` invocation. If " - "`target.launch-working-dir` is set and this option is given, the value of this " - "option will be used instead of the setting.">; - def process_launch_arch : Option<"arch", "a">, Arg<"Architecture">, - Desc<"Set the architecture for the process to launch when ambiguous.">; - def process_launch_environment : Option<"environment", "E">, - Arg<"None">, Desc<"Specify an environment variable name/value string " - "(--environment NAME=VALUE). Can be specified multiple times for subsequent " - "environment entries.">; - def process_launch_shell : Option<"shell", "c">, GroupRange<1,3>, - OptionalArg<"Filename">, Desc<"Run the process in a shell (not supported on all platforms).">; - def process_launch_stdin : Option<"stdin", "i">, Group<1>, - Arg<"Filename">, Desc<"Redirect stdin for the process to <filename>.">; - def process_launch_stdout : Option<"stdout", "o">, Group<1>, - Arg<"Filename">, Desc<"Redirect stdout for the process to <filename>.">; - def process_launch_stderr : Option<"stderr", "e">, Group<1>, - Arg<"Filename">, Desc<"Redirect stderr for the process to <filename>.">; - def process_launch_tty : Option<"tty", "t">, Group<2>, - Desc<"Start the process in a terminal (not supported on all platforms).">; - def process_launch_no_stdio : Option<"no-stdio", "n">, Group<3>, - Desc<"Do not set up for terminal I/O to go to running process.">; - def process_launch_shell_expand_args : Option<"shell-expand-args", "X">, Group<4>, - Arg<"Boolean">, Desc<"Set whether to shell expand arguments to the process when launching.">; + def process_launch_stop_at_entry + : Option<"stop-at-entry", "s">, + Desc< + "Stop at the entry point of the program when launching a process.">; + def process_launch_stop_at_user_entry + : Option<"stop-at-user-entry", "m">, + Desc<"Stop at the user entry point when launching a process. For C " + "based languages this will be the 'main' function, but this might " + "differ for other languages.">; + def process_launch_disable_aslr + : Option<"disable-aslr", "A">, + Arg<"Boolean">, + Desc<"Set whether to disable address space layout randomization when " + "launching a process.">; + def process_launch_plugin + : Option<"plugin", "P">, + Arg<"Plugin">, + Desc<"Name of the process plugin you want to use.">; + def process_launch_working_dir + : Option<"working-dir", "w">, + Arg<"DirectoryName">, + Desc<"Set the current working directory to <path> when running the " + "inferior. This option applies only to the current `process " + "launch` invocation. If `target.launch-working-dir` is set and " + "this option is given, the value of this option will be used " + "instead of the setting.">; + def process_launch_arch + : Option<"arch", "a">, + Arg<"Architecture">, + Desc<"Set the architecture for the process to launch when ambiguous.">; + def process_launch_environment + : Option<"environment", "E">, + Arg<"None">, + Desc<"Specify an environment variable name/value string (--environment " + "NAME=VALUE). Can be specified multiple times for subsequent " + "environment entries.">; + def process_launch_shell + : Option<"shell", "c">, + GroupRange<1, 3>, + OptionalArg<"Filename">, + Desc<"Run the process in a shell (not supported on all platforms).">; + def process_launch_stdin + : Option<"stdin", "i">, + Group<1>, + Arg<"Filename">, + Desc<"Redirect stdin for the process to <filename>.">; + def process_launch_stdout + : Option<"stdout", "o">, + Group<1>, + Arg<"Filename">, + Desc<"Redirect stdout for the process to <filename>.">; + def process_launch_stderr + : Option<"stderr", "e">, + Group<1>, + Arg<"Filename">, + Desc<"Redirect stderr for the process to <filename>.">; + def process_launch_tty : Option<"tty", "t">, + Group<2>, + Desc<"Start the process in a terminal (not " + "supported on all platforms).">; + def process_launch_no_stdio + : Option<"no-stdio", "n">, + Group<3>, + Desc<"Do not set up for terminal I/O to go to running process.">; + def process_launch_shell_expand_args + : Option<"shell-expand-args", "X">, + Group<4>, + Arg<"Boolean">, + Desc<"Set whether to shell expand arguments to the process when " + "launching.">; } let Command = "process attach" in { - def process_attach_continue : Option<"continue", "c">, - Desc<"Immediately continue the process once attached.">; - def process_attach_plugin : Option<"plugin", "P">, Arg<"Plugin">, - Desc<"Name of the process plugin you want to use.">; - def process_attach_pid : Option<"pid", "p">, Group<1>, Arg<"Pid">, - Desc<"The process ID of an existing process to attach to.">; - def process_attach_name : Option<"name", "n">, Group<2>, Arg<"ProcessName">, - Desc<"The name of the process to attach to.">; - def process_attach_include_existing : Option<"include-existing", "i">, - Group<2>, Desc<"Include existing processes when doing attach -w.">; - def process_attach_waitfor : Option<"waitfor", "w">, Group<2>, - Desc<"Wait for the process with <process-name> to launch.">; + def process_attach_continue + : Option<"continue", "c">, + Desc<"Immediately ${c}ontinue the process once attached.">; + def process_attach_plugin + : Option<"plugin", "P">, + Arg<"Plugin">, + Desc<"Name of the process ${p}lugin you want to use.">; + def process_attach_pid + : Option<"pid", "p">, + Group<1>, + Arg<"Pid">, + Desc<"The ${p}rocess ID of an existing process to attach to.">; + def process_attach_name : Option<"name", "n">, + Group<2>, + Arg<"ProcessName">, + Desc<"The ${n}ame of the process to attach to.">; + def process_attach_include_existing + : Option<"include-existing", "i">, + Group<2>, + Desc<"${I}nclude existing processes when doing attach -w.">; + def process_attach_waitfor + : Option<"waitfor", "w">, + Group<2>, + Desc<"${W}ait for the process with <process-name> to launch.">; } let Command = "process continue" in { - def process_continue_ignore_count : Option<"ignore-count", "i">, Groups<[1,2]>, - Arg<"UnsignedInteger">, Desc<"Ignore <N> crossings of the breakpoint (if it" - " exists) for the currently selected thread.">; - def process_continue_run_to_bkpt : Option<"continue-to-bkpt", "b">, Groups<[3,4]>, - Arg<"BreakpointIDRange">, Desc<"Specify a breakpoint to continue to, temporarily " - "ignoring other breakpoints. Can be specified more than once. " - "The continue action will be done synchronously if this option is specified.">; - def thread_continue_forward : Option<"forward", "F">, Groups<[1,3]>, - Desc<"Set the direction to forward before continuing.">; - def thread_continue_reverse : Option<"reverse", "R">, Groups<[2,4]>, - Desc<"Set the direction to reverse before continuing.">; + def process_continue_ignore_count + : Option<"ignore-count", "i">, + Groups<[1, 2]>, + Arg<"UnsignedInteger">, + Desc<"Ignore <N> crossings of the breakpoint (if it exists) for the " + "currently selected thread.">; + def process_continue_run_to_bkpt + : Option<"continue-to-bkpt", "b">, + Groups<[3, 4]>, + Arg<"BreakpointIDRange">, + Desc<"Specify a breakpoint to continue to, temporarily ignoring other " + "breakpoints. Can be specified more than once. The continue " + "action will be done synchronously if this option is specified.">; + def thread_continue_forward + : Option<"forward", "F">, + Groups<[1, 3]>, + Desc<"Set the direction to forward before continuing.">; + def thread_continue_reverse + : Option<"reverse", "R">, + Groups<[2, 4]>, + Desc<"Set the direction to reverse before continuing.">; } let Command = "process detach" in { - def process_detach_keep_stopped : Option<"keep-stopped", "s">, Group<1>, - Arg<"Boolean">, Desc<"Whether or not the process should be kept stopped on" - " detach (if possible).">; + def process_detach_keep_stopped + : Option<"keep-stopped", "s">, + Group<1>, + Arg<"Boolean">, + Desc<"Whether or not the process should be kept stopped on" + " detach (if possible).">; } let Command = "process connect" in { - def process_connect_plugin : Option<"plugin", "p">, Arg<"Plugin">, - Desc<"Name of the process plugin you want to use.">; + def process_connect_plugin + : Option<"plugin", "p">, + Arg<"Plugin">, + Desc<"Name of the process plugin you want to use.">; } let Command = "process load" in { - def process_load_install : Option<"install", "i">, OptionalArg<"Path">, - Desc<"Install the shared library to the target. If specified without an " - "argument then the library will installed in the current working " - "directory.">; + def process_load_install + : Option<"install", "i">, + OptionalArg<"Path">, + Desc< + "Install the shared library to the target. If specified without an " + "argument then the library will installed in the current working " + "directory.">; } let Command = "process handle" in { - def process_handle_clear : Option<"clear", "c">, Group<2>, - Desc<"Removes the signals listed from the Target signal handlers">; - def process_handle_stop : Option<"stop", "s">, Group<1>, Arg<"Boolean">, - Desc<"Whether or not the process should be stopped if the signal is " - "received.">; - def process_handle_notify : Option<"notify", "n">, Group<1>, Arg<"Boolean">, - Desc<"Whether or not the debugger should notify the user if the signal is " - "received.">; - def process_handle_pass : Option<"pass", "p">, Group<1>, Arg<"Boolean">, - Desc<"Whether or not the signal should be passed to the process.">; - def process_handle_only_target : Option<"target", "t">, Group<1>, - Desc<"Show only the signals with behaviors modified in this target">; - def process_handle_dummy : Option<"dummy", "d">, Group<2>, - Desc<"Also clear the values in the dummy target so they won't be inherited by new targets.">; + def process_handle_clear + : Option<"clear", "c">, + Group<2>, + Desc<"Removes the signals listed from the Target signal handlers">; + def process_handle_stop + : Option<"stop", "s">, + Group<1>, + Arg<"Boolean">, + Desc<"Whether or not the process should be stopped if the signal is " + "received.">; + def process_handle_notify : Option<"notify", "n">, + Group<1>, + Arg<"Boolean">, + Desc<"Whether or not the debugger should notify " + "the user if the signal is " + "received.">; + def process_handle_pass + : Option<"pass", "p">, + Group<1>, + Arg<"Boolean">, + Desc<"Whether or not the signal should be passed to the process.">; + def process_handle_only_target + : Option<"target", "t">, + Group<1>, + Desc<"Show only the signals with behaviors modified in this target">; + def process_handle_dummy : Option<"dummy", "d">, + Group<2>, + Desc<"Also clear the values in the dummy target " + "so they won't be inherited by new targets.">; } let Command = "process status" in { - def process_status_verbose : Option<"verbose", "v">, Group<1>, - Desc<"Show verbose process status including extended crash information.">; - def process_status_dump : Option<"dump-modification-id", "d">, Group<1>, - Desc<"Dump the state of the ProcessModID of the stopped process.">; + def process_status_verbose : Option<"verbose", "v">, + Group<1>, + Desc<"Show verbose process status including " + "extended crash information.">; + def process_status_dump + : Option<"dump-modification-id", "d">, + Group<1>, + Desc<"Dump the state of the ProcessModID of the stopped process.">; } let Command = "process save_core" in { - def process_save_core_style : Option<"style", "s">, Group<1>, - EnumArg<"SaveCoreStyle">, Desc<"Request a specific style " - "of corefile to be saved.">; - def process_save_core_plugin_name : Option<"plugin-name", "p">, - OptionalArg<"Plugin">, Desc<"Specify a plugin name to create the core file. " - "This allows core files to be saved in different formats.">; + def process_save_core_style : Option<"style", "s">, + Group<1>, + EnumArg<"SaveCoreStyle">, + Desc<"Request a specific style " + "of corefile to be saved.">; + def process_save_core_plugin_name + : Option<"plugin-name", "p">, + OptionalArg<"Plugin">, + Desc<"Specify a plugin name to create the core file. " + "This allows core files to be saved in different formats.">; } let Command = "script import" in { - def script_import_allow_reload : Option<"allow-reload", "r">, Group<1>, - Desc<"Allow the script to be loaded even if it was already loaded before. " - "This argument exists for backwards compatibility, but reloading is always " - "allowed, whether you specify it or not.">; - def relative_to_command_file : Option<"relative-to-command-file", "c">, - Group<1>, Desc<"Resolve non-absolute paths relative to the location of the " - "current command file. This argument can only be used when the command is " - "being sourced from a file.">; - def silent : Option<"silent", "s">, Group<1>, - Desc<"If true don't print any script output while importing.">; + def script_import_allow_reload + : Option<"allow-reload", "r">, + Group<1>, + Desc<"Allow the script to be loaded even if it was already loaded " + "before. " + "This argument exists for backwards compatibility, but reloading " + "is always " + "allowed, whether you specify it or not.">; + def relative_to_command_file + : Option<"relative-to-command-file", "c">, + Group<1>, + Desc<"Resolve non-absolute paths relative to the location of the " + "current command file. This argument can only be used when the " + "command is " + "being sourced from a file.">; + def silent : Option<"silent", "s">, + Group<1>, + Desc<"If true don't print any script output while importing.">; } let Command = "script add" in { - def script_add_function : Option<"function", "f">, Group<1>, - Arg<"PythonFunction">, - Desc<"Name of the Python function to bind to this command name.">; - def script_add_class : Option<"class", "c">, Groups<[2,3]>, - Arg<"PythonClass">, - Desc<"Name of the Python class to bind to this command name.">; - def script_add_help : Option<"help", "h">, Group<1>, Arg<"HelpText">, - Desc<"The help text to display for this command.">; - def script_add_overwrite : Option<"overwrite", "o">, - Desc<"Overwrite an existing command at this node.">; - def script_add_synchronicity : Option<"synchronicity", "s">, - EnumArg<"ScriptedCommandSynchronicity">, - Desc<"Set the synchronicity of this command's executions with regard to " - "LLDB event system.">; - def script_add_completion_type : Option<"completion-type", "C">, - Groups<[1,2]>, EnumArg<"CompletionType">, - Desc<"Specify which completion type the command should use - if none is " - "specified, the command won't use auto-completion.">; - def script_add_parsed_command : Option<"parsed", "p">, Group<3>, - Desc<"Make a parsed command. The command class will provide the command " - "definition by implementing get_options and get_arguments.">; - + def script_add_function + : Option<"function", "f">, + Group<1>, + Arg<"PythonFunction">, + Desc<"Name of the Python function to bind to this command name.">; + def script_add_class + : Option<"class", "c">, + Groups<[2, 3]>, + Arg<"PythonClass">, + Desc<"Name of the Python class to bind to this command name.">; + def script_add_help : Option<"help", "h">, + Group<1>, + Arg<"HelpText">, + Desc<"The help text to display for this command.">; + def script_add_overwrite + : Option<"overwrite", "o">, + Desc<"Overwrite an existing command at this node.">; + def script_add_synchronicity + : Option<"synchronicity", "s">, + EnumArg<"ScriptedCommandSynchronicity">, + Desc< + "Set the synchronicity of this command's executions with regard to " + "LLDB event system.">; + def script_add_completion_type + : Option<"completion-type", "C">, + Groups<[1, 2]>, + EnumArg<"CompletionType">, + Desc< + "Specify which completion type the command should use - if none is " + "specified, the command won't use auto-completion.">; + def script_add_parsed_command + : Option<"parsed", "p">, + Group<3>, + Desc< + "Make a parsed command. The command class will provide the command " + "definition by implementing get_options and get_arguments.">; } let Command = "container add" in { - def container_add_help : Option<"help", "h">, Arg<"HelpText">, - Desc<"Help text for this command">; - def container_add_long_help : Option<"long-help", "H">, Arg<"HelpText">, - Desc<"Long help text for this command">; - def container_add_overwrite : Option<"overwrite", "o">, Group<1>, - Desc<"Overwrite an existing command at this node.">; + def container_add_help : Option<"help", "h">, + Arg<"HelpText">, + Desc<"Help text for this command">; + def container_add_long_help : Option<"long-help", "H">, + Arg<"HelpText">, + Desc<"Long help text for this command">; + def container_add_overwrite + : Option<"overwrite", "o">, + Group<1>, + Desc<"Overwrite an existing command at this node.">; } let Command = "scripting run" in { def script_language : Option<"language", "l">, - EnumArg<"ScriptLang">, Desc<"Specify the scripting " - " language. If none is specific the default scripting language is used.">; + EnumArg<"ScriptLang">, + Desc<"Specify the scripting " + " language. If none is specific the default " + "scripting language is used.">; } let Command = "scripting extension list" in { - def scripting_extension_list_language : Option<"language", "l">, - EnumArg<"ScriptLang">, Desc<"Specify the scripting " - " language. If none is specified the default scripting language is used.">; + def scripting_extension_list_language + : Option<"language", "l">, + EnumArg<"ScriptLang">, + Desc<"Specify the scripting " + " language. If none is specified the default scripting language " + "is used.">; } let Command = "source info" in { - def source_info_count : Option<"count", "c">, Arg<"Count">, - Desc<"The number of line entries to display.">; - def source_info_shlib : Option<"shlib", "s">, Groups<[1,2]>, Arg<"ShlibName">, - Completion<"Module">, Desc<"Look up the source in the given module or " - "shared library (can be specified more than once).">; - def source_info_file : Option<"file", "f">, Group<1>, Arg<"Filename">, - Completion<"SourceFile">, Desc<"The file from which to display source.">; - def source_info_line : Option<"line", "l">, Group<1>, Arg<"LineNum">, - Desc<"The line number at which to start the displaying lines.">; - def source_info_end_line : Option<"end-line", "e">, Group<1>, Arg<"LineNum">, - Desc<"The line number at which to stop displaying lines.">; - def source_info_name : Option<"name", "n">, Group<2>, Arg<"Symbol">, - Completion<"Symbol">, - Desc<"The name of a function whose source to display.">; - def source_info_address : Option<"address", "a">, Group<3>, - Arg<"AddressOrExpression">, Desc<"Lookup the address and display the source" - " information for the corresponding file and line.">; + def source_info_count : Option<"count", "c">, + Arg<"Count">, + Desc<"The number of line entries to display.">; + def source_info_shlib + : Option<"shlib", "s">, + Groups<[1, 2]>, + Arg<"ShlibName">, + Completion<"Module">, + Desc<"Look up the source in the given module or " + "shared library (can be specified more than once).">; + def source_info_file : Option<"file", "f">, + Group<1>, + Arg<"Filename">, + Completion<"SourceFile">, + Desc<"The file from which to display source.">; + def source_info_line + : Option<"line", "l">, + Group<1>, + Arg<"LineNum">, + Desc<"The line number at which to start the displaying lines.">; + def source_info_end_line + : Option<"end-line", "e">, + Group<1>, + Arg<"LineNum">, + Desc<"The line number at which to stop displaying lines.">; + def source_info_name + : Option<"name", "n">, + Group<2>, + Arg<"Symbol">, + Completion<"Symbol">, + Desc<"The name of a function whose source to display.">; + def source_info_address + : Option<"address", "a">, + Group<3>, + Arg<"AddressOrExpression">, + Desc<"Lookup the address and display the source" + " information for the corresponding file and line.">; } let Command = "source list" in { - def source_list_count : Option<"count", "c">, Arg<"Count">, - Desc<"The number of source lines to display.">; - def source_list_shlib : Option<"shlib", "s">, Groups<[1,2,5]>, Arg<"ShlibName">, - Completion<"Module">, - Desc<"Look up the source file in the given shared library.">; - def source_list_show_breakpoints : Option<"show-breakpoints", "b">, - Desc<"Show the line table locations from the debug information that " - "indicate valid places to set source level breakpoints.">; - def source_list_file : Option<"file", "f">, Group<1>, Arg<"Filename">, - Completion<"SourceFile">, Desc<"The file from which to display source.">; - def source_list_line : Option<"line", "l">, Group<1>, Arg<"LineNum">, - Desc<"The line number at which to start the display source.">; - def source_list_name : Option<"name", "n">, Group<2>, Arg<"Symbol">, - Completion<"Symbol">, - Desc<"The name of a function whose source to display.">; - def source_list_address : Option<"address", "a">, Group<3>, - Arg<"AddressOrExpression">, Desc<"Lookup the address and display the source" - " information for the corresponding file and line.">; - def source_list_reverse : Option<"reverse", "r">, Group<4>, Desc<"Reverse the" - " listing to look backwards from the last displayed block of source.">; - def source_list_file_colon_line : Option<"joint-specifier", "y">, Group<5>, - Arg<"FileLineColumn">, Completion<"SourceFile">, - Desc<"A specifier in the form filename:line[:column] from which to display" - " source.">; + def source_list_count : Option<"count", "c">, + Arg<"Count">, + Desc<"The number of source lines to display.">; + def source_list_shlib + : Option<"shlib", "s">, + Groups<[1, 2, 5]>, + Arg<"ShlibName">, + Completion<"Module">, + Desc<"Look up the source file in the given shared library.">; + def source_list_show_breakpoints + : Option<"show-breakpoints", "b">, + Desc<"Show the line table locations from the debug information that " + "indicate valid places to set source level breakpoints.">; + def source_list_file : Option<"file", "f">, + Group<1>, + Arg<"Filename">, + Completion<"SourceFile">, + Desc<"The file from which to display source.">; + def source_list_line + : Option<"line", "l">, + Group<1>, + Arg<"LineNum">, + Desc<"The line number at which to start displaying source.">; + def source_list_name + : Option<"name", "n">, + Group<2>, + Arg<"Symbol">, + Completion<"Symbol">, + Desc<"The name of a function whose source to display.">; + def source_list_address + : Option<"address", "a">, + Group<3>, + Arg<"AddressOrExpression">, + Desc<"Lookup the address and display the source information for the " + "corresponding file and line.">; + def source_list_reverse : Option<"reverse", "r">, + Group<4>, + Desc<"Reverse the listing to look backwards from " + "the last displayed block of source.">; + def source_list_file_colon_line + : Option<"joint-specifier", "y">, + Group<5>, + Arg<"FileLineColumn">, + Completion<"SourceFile">, + Desc<"A specifier in the form filename:line[:column] from which to " + "display source.">; } let Command = "target dependents" in { - def dependents_no_dependents : Option<"no-dependents", "d">, Group<1>, - OptionalEnumArg<"Value">, - Desc<"Whether or not to load dependents when creating a target. If the " - "option is not specified, the value is implicitly 'default'. If the " - "option is specified but without a value, the value is implicitly " - "'true'.">; + def dependents_no_dependents + : Option<"no-dependents", "d">, + Group<1>, + OptionalEnumArg<"Value">, + Desc<"Whether or not to load dependents when creating a target. If the " + "option is not specified, the value is implicitly 'default'. If " + "the " + "option is specified but without a value, the value is implicitly " + "'true'.">; } let Command = "target modules dump" in { def target_modules_dump_verbose : Option<"verbose", "v">, - Desc<"Enable verbose dump.">; + Desc<"Enable verbose dump.">; } let Command = "target modules list" in { - def target_modules_list_address : Option<"address", "a">, Group<1>, - Arg<"AddressOrExpression">, Desc<"Display the image at this address.">; - def target_modules_list_arch : Option<"arch", "A">, Group<1>, - OptionalArg<"Width">, Desc<"Display the architecture when listing images.">; - def target_modules_list_triple : Option<"triple", "t">, Group<1>, - OptionalArg<"Width">, Desc<"Display the triple when listing images.">; - def target_modules_list_header : Option<"header", "h">, Group<1>, - Desc<"Display the image base address as a load address if debugging, a file" - " address otherwise.">; - def target_modules_list_offset : Option<"offset", "o">, Group<1>, - Desc<"Display the image load address offset from the base file address " - "(the slide amount).">; - def target_modules_list_uuid : Option<"uuid", "u">, Group<1>, - Desc<"Display the UUID when listing images.">; - def target_modules_list_fullpath : Option<"fullpath", "f">, Group<1>, - OptionalArg<"Width">, - Desc<"Display the fullpath to the image object file.">; - def target_modules_list_directory : Option<"directory", "d">, Group<1>, - OptionalArg<"Width">, Desc<"Display the directory with optional width for " - "the image object file.">; - def target_modules_list_basename : Option<"basename", "b">, Group<1>, - OptionalArg<"Width">, Desc<"Display the basename with optional width for " - "the image object file.">; - def target_modules_list_symfile : Option<"symfile", "s">, Group<1>, - OptionalArg<"Width">, Desc<"Display the fullpath to the image symbol file " - "with optional width.">; - def target_modules_list_symfile_unique : Option<"symfile-unique", "S">, - Group<1>, OptionalArg<"Width">, Desc<"Display the symbol file with optional" - " width only if it is different from the executable object file.">; - def target_modules_list_mod_time : Option<"mod-time", "m">, Group<1>, - OptionalArg<"Width">, Desc<"Display the modification time with optional " - "width of the module.">; - def target_modules_list_ref_count : Option<"ref-count", "r">, Group<1>, - OptionalArg<"Width">, Desc<"Display whether the module is still in the " - "the shared module cache (Y/N), and its shared pointer use_count.">; - def target_modules_list_pointer : Option<"pointer", "p">, Group<1>, - OptionalArg<"None">, Desc<"Display the module pointer.">; - def target_modules_list_global : Option<"global", "g">, Group<1>, - Desc<"Display the modules from the global module list, not just the " - "current target.">; + def target_modules_list_address : Option<"address", "a">, + Group<1>, + Arg<"AddressOrExpression">, + Desc<"Display the image at this address.">; + def target_modules_list_arch + : Option<"arch", "A">, + Group<1>, + OptionalArg<"Width">, + Desc<"Display the architecture when listing images.">; + def target_modules_list_triple + : Option<"triple", "t">, + Group<1>, + OptionalArg<"Width">, + Desc<"Display the triple when listing images.">; + def target_modules_list_header : Option<"header", "h">, + Group<1>, + Desc<"Display the image base address as a " + "load address if debugging, a file" + " address otherwise.">; + def target_modules_list_offset + : Option<"offset", "o">, + Group<1>, + Desc<"Display the image load address offset from the base file address " + "(the slide amount).">; + def target_modules_list_uuid : Option<"uuid", "u">, + Group<1>, + Desc<"Display the UUID when listing images.">; + def target_modules_list_fullpath + : Option<"fullpath", "f">, + Group<1>, + OptionalArg<"Width">, + Desc<"Display the fullpath to the image object file.">; + def target_modules_list_directory + : Option<"directory", "d">, + Group<1>, + OptionalArg<"Width">, + Desc<"Display the directory with optional width for " + "the image object file.">; + def target_modules_list_basename + : Option<"basename", "b">, + Group<1>, + OptionalArg<"Width">, + Desc<"Display the basename with optional width for " + "the image object file.">; + def target_modules_list_symfile + : Option<"symfile", "s">, + Group<1>, + OptionalArg<"Width">, + Desc<"Display the fullpath to the image symbol file " + "with optional width.">; + def target_modules_list_symfile_unique + : Option<"symfile-unique", "S">, + Group<1>, + OptionalArg<"Width">, + Desc<"Display the symbol file with optional" + " width only if it is different from the executable object file.">; + def target_modules_list_mod_time + : Option<"mod-time", "m">, + Group<1>, + OptionalArg<"Width">, + Desc<"Display the modification time with optional " + "width of the module.">; + def target_modules_list_ref_count + : Option<"ref-count", "r">, + Group<1>, + OptionalArg<"Width">, + Desc< + "Display whether the module is still in the " + "the shared module cache (Y/N), and its shared pointer use_count.">; + def target_modules_list_pointer : Option<"pointer", "p">, + Group<1>, + OptionalArg<"None">, + Desc<"Display the module pointer.">; + def target_modules_list_global + : Option<"global", "g">, + Group<1>, + Desc<"Display the modules from the global module list, not just the " + "current target.">; } let Command = "target modules show unwind" in { - def target_modules_show_unwind_name : Option<"name", "n">, Group<1>, - Arg<"FunctionName">, - Desc<"Show unwind instructions for a function or symbol name.">; - def target_modules_show_unwind_address : Option<"address", "a">, Group<2>, - Arg<"AddressOrExpression">, Desc<"Show unwind instructions for a function " - "or symbol containing an address">; - def target_modules_show_unwind_cached : Option<"cached", "c">, - Arg<"Boolean">, Desc<"Show cached unwind information">; + def target_modules_show_unwind_name + : Option<"name", "n">, + Group<1>, + Arg<"FunctionName">, + Desc<"Show unwind instructions for a function or symbol name.">; + def target_modules_show_unwind_address + : Option<"address", "a">, + Group<2>, + Arg<"AddressOrExpression">, + Desc<"Show unwind instructions for a function " + "or symbol containing an address">; + def target_modules_show_unwind_cached + : Option<"cached", "c">, + Arg<"Boolean">, + Desc<"Show cached unwind information">; } let Command = "target modules lookup" in { - def target_modules_lookup_address : Option<"address", "a">, Group<1>, - Arg<"AddressOrExpression">, Required, Desc<"Lookup an address in one or " - "more target modules.">; - def target_modules_lookup_offset : Option<"offset", "o">, Group<1>, - Arg<"Offset">, Desc<"When looking up an address subtract <offset> from any " - "addresses before doing the lookup.">; + def target_modules_lookup_address : Option<"address", "a">, + Group<1>, + Arg<"AddressOrExpression">, + Required, + Desc<"Lookup an address in one or " + "more target modules.">; + def target_modules_lookup_offset + : Option<"offset", "o">, + Group<1>, + Arg<"Offset">, + Desc<"When looking up an address subtract <offset> from any " + "addresses before doing the lookup.">; // FIXME: re-enable regex for types when the LookupTypeInModule actually uses // the regex option by adding to group 6. - def target_modules_lookup_regex : Option<"regex", "r">, Groups<[2,4,5]>, - Desc<"The <name> argument for name lookups are regular expressions.">; - def target_modules_lookup_symbol : Option<"symbol", "s">, Group<2>, - Arg<"Symbol">, Required, Desc<"Lookup a symbol by name in the symbol tables" - " in one or more target modules.">; - def target_modules_lookup_file : Option<"file", "f">, Group<3>, - Arg<"Filename">, Required, Desc<"Lookup a file by fullpath or basename in " - "one or more target modules.">; - def target_modules_lookup_line : Option<"line", "l">, Group<3>, - Arg<"LineNum">, Desc<"Lookup a line number in a file (must be used in " - "conjunction with --file).">; - def target_modules_lookup_no_inlines : Option<"no-inlines", "i">, - GroupRange<3,5>, - Desc<"Ignore inline entries (must be used in conjunction with --file or " - "--function).">; - def target_modules_lookup_function : Option<"function", "F">, Group<4>, - Arg<"FunctionName">, Required, Desc<"Lookup a function by name in the debug" - " symbols in one or more target modules.">; - def target_modules_lookup_name : Option<"name", "n">, Group<5>, - Arg<"FunctionOrSymbol">, Required, Desc<"Lookup a function or symbol by " - "name in one or more target modules.">; - def target_modules_lookup_type : Option<"type", "t">, Group<6>, Arg<"Name">, - Required, Desc<"Lookup a type by name in the debug symbols in one or more " - "target modules.">; - def target_modules_lookup_variables_ranges : Option<"show-variable-ranges", - "\\x01">, GroupRange<1, 6>, Desc<"Dump valid ranges of variables (must be " - "used in conjunction with --verbose">; - def target_modules_lookup_verbose : Option<"verbose", "v">, - Desc<"Enable verbose lookup information.">; - def target_modules_lookup_all : Option<"all", "A">, Desc<"Print all matches, " - "not just the best match, if a best match is available.">; + def target_modules_lookup_regex + : Option<"regex", "r">, + Groups<[2, 4, 5]>, + Desc<"The <name> argument for name lookups are regular expressions.">; + def target_modules_lookup_symbol + : Option<"symbol", "s">, + Group<2>, + Arg<"Symbol">, + Required, + Desc<"Lookup a symbol by name in the symbol tables" + " in one or more target modules.">; + def target_modules_lookup_file + : Option<"file", "f">, + Group<3>, + Arg<"Filename">, + Required, + Desc<"Lookup a file by fullpath or basename in " + "one or more target modules.">; + def target_modules_lookup_line + : Option<"line", "l">, + Group<3>, + Arg<"LineNum">, + Desc<"Lookup a line number in a file (must be used in " + "conjunction with --file).">; + def target_modules_lookup_no_inlines + : Option<"no-inlines", "i">, + GroupRange<3, 5>, + Desc< + "Ignore inline entries (must be used in conjunction with --file or " + "--function).">; + def target_modules_lookup_function + : Option<"function", "F">, + Group<4>, + Arg<"FunctionName">, + Required, + Desc<"Lookup a function by name in the debug" + " symbols in one or more target modules.">; + def target_modules_lookup_name : Option<"name", "n">, + Group<5>, + Arg<"FunctionOrSymbol">, + Required, + Desc<"Lookup a function or symbol by " + "name in one or more target modules.">; + def target_modules_lookup_type + : Option<"type", "t">, + Group<6>, + Arg<"Name">, + Required, + Desc<"Lookup a type by name in the debug symbols in one or more " + "target modules.">; + def target_modules_lookup_variables_ranges + : Option<"show-variable-ranges", "\\x01">, + GroupRange<1, 6>, + Desc<"Dump valid ranges of variables (must be used in conjunction with " + "--verbose">; + def target_modules_lookup_verbose + : Option<"verbose", "v">, + Desc<"Enable verbose lookup information.">; + def target_modules_lookup_all : Option<"all", "A">, + Desc<"Print all matches, not just the best " + "match, if a best match is available.">; } let Command = "target stop hook add" in { - def target_stop_hook_add_one_liner : Option<"one-liner", "o">, GroupRange<1,3>, - Arg<"OneLiner">, Desc<"Add a command for the stop hook. Can be specified " - "more than once, and commands will be run in the order they appear.">; - def target_stop_hook_add_shlib : Option<"shlib", "s">, Arg<"ShlibName">, - Completion<"Module">, - Desc<"Set the module within which the stop-hook is to be run.">; - def target_stop_hook_add_thread_index : Option<"thread-index", "x">, - Arg<"ThreadIndex">, Desc<"The stop hook is run only for the thread whose " - "index matches this argument.">; - def target_stop_hook_add_thread_id : Option<"thread-id", "t">, - Arg<"ThreadID">, Desc<"The stop hook is run only for the thread whose TID " - "matches this argument.">; - def target_stop_hook_add_thread_name : Option<"thread-name", "T">, - Arg<"ThreadName">, Desc<"The stop hook is run only for the thread whose " - "thread name matches this argument.">; - def target_stop_hook_add_queue_name : Option<"queue-name", "q">, - Arg<"QueueName">, Desc<"The stop hook is run only for threads in the queue " - "whose name is given by this argument.">; - def target_stop_hook_add_file : Option<"file", "f">, Groups<[1,4]>, - Arg<"Filename">, Desc<"Specify the source file within which the stop-hook " - "is to be run.">, Completion<"SourceFile">; - def target_stop_hook_add_start_line : Option<"start-line", "l">, Groups<[1,4]>, - Arg<"LineNum">, Desc<"Set the start of the line range for which the " - "stop-hook is to be run.">; - def target_stop_hook_add_end_line : Option<"end-line", "e">, Groups<[1,4]>, - Arg<"LineNum">, Desc<"Set the end of the line range for which the stop-hook" - " is to be run.">; - def target_stop_hook_add_classname : Option<"classname", "c">, Groups<[2,5]>, - Arg<"ClassName">, - Desc<"Specify the class within which the stop-hook is to be run.">; - def target_stop_hook_add_name : Option<"name", "n">, Groups<[3,6]>, - Arg<"FunctionName">, Desc<"Set the function name within which the stop hook" - " will be run.">, Completion<"Symbol">; - def target_stop_hook_add_auto_continue : Option<"auto-continue", "G">, - Arg<"Boolean">, Desc<"The stop-hook will auto-continue after running its" - " commands.">; - def target_stop_hook_add_at_initial_stop : Option<"at-initial-stop", "I">, - Arg<"Boolean">, Desc<"Whether the stop-hook will trigger when lldb " - "initially gains control of the process. For a process launch, this " - "initial stop may happen very early on - before the loader has run. You " - "can use this option if you do not want some stop-hooks to run then. " - "Defaults to true.">; + def target_stop_hook_add_one_liner + : Option<"one-liner", "o">, + GroupRange<1, 3>, + Arg<"OneLiner">, + Desc<"Add a command for the stop hook. Can be specified more than " + "once, and commands will be run in the order they appear.">; + def target_stop_hook_add_shlib + : Option<"shlib", "s">, + Arg<"ShlibName">, + Completion<"Module">, + Desc<"Set the module within which the stop-hook is to be run.">; + def target_stop_hook_add_thread_index + : Option<"thread-index", "x">, + Arg<"ThreadIndex">, + Desc<"The stop hook is run only for the thread whose index matches " + "this argument.">; + def target_stop_hook_add_thread_id + : Option<"thread-id", "t">, + Arg<"ThreadID">, + Desc<"The stop hook is run only for the thread whose TID matches this " + "argument.">; + def target_stop_hook_add_thread_name + : Option<"thread-name", "T">, + Arg<"ThreadName">, + Desc<"The stop hook is run only for the thread whose thread name " + "matches this argument.">; + def target_stop_hook_add_queue_name + : Option<"queue-name", "q">, + Arg<"QueueName">, + Desc<"The stop hook is run only for threads in the ${q}ueue whose name " + "is given by this argument.">; + def target_stop_hook_add_file : Option<"file", "f">, + Groups<[1, 4]>, + Arg<"Filename">, + Desc<"Specify the source ${f}ile within " + "which the stop-hook is to be run.">, + Completion<"SourceFile">; + def target_stop_hook_add_start_line + : Option<"start-line", "l">, + Groups<[1, 4]>, + Arg<"LineNum">, + Desc<"Set the start of the ${l}ine range for which the " + "stop-hook is to be run.">; + def target_stop_hook_add_end_line + : Option<"end-line", "e">, + Groups<[1, 4]>, + Arg<"LineNum">, + Desc<"Set the ${e}nd of the line range for which the stop-hook is to " + "be run.">; + def target_stop_hook_add_classname + : Option<"classname", "c">, + Groups<[2, 5]>, + Arg<"ClassName">, + Desc<"Specify the ${c}lass within which the stop-hook is to be run.">; + def target_stop_hook_add_name + : Option<"name", "n">, + Groups<[3, 6]>, + Arg<"FunctionName">, + Desc< + "Set the ${f}unction name within which the stop hook will be run.">, + Completion<"Symbol">; + def target_stop_hook_add_auto_continue + : Option<"auto-continue", "G">, + Arg<"Boolean">, + Desc<"The stop-hook will auto-continue after running its commands.">; + def target_stop_hook_add_at_initial_stop + : Option<"at-initial-stop", "I">, + Arg<"Boolean">, + Desc<"Whether the stop-hook will trigger when lldb ${i}nitially gains " + "control of the process. For a process launch, this initial stop " + "may happen very early on - before the loader has run. You can " + "use this option if you do not want some stop-hooks to run then. " + "Defaults to true.">; } let Command = "thread backtrace" in { - def thread_backtrace_count : Option<"count", "c">, Group<1>, Arg<"Count">, - Desc<"How many frames to display (0 for all)">; - def thread_backtrace_start : Option<"start", "s">, Group<1>, - Arg<"FrameIndex">, Desc<"Frame in which to start the backtrace">; - def thread_backtrace_extended : Option<"extended", "e">, Group<1>, - Arg<"Boolean">, Desc<"Show the extended backtrace, if available">; - def thread_backtrace_unfiltered : Option<"unfiltered", "u">, Group<1>, - Desc<"Do not filter out frames according to installed frame recognizers">; + def thread_backtrace_count : Option<"count", "c">, + Group<1>, + Arg<"Count">, + Desc<"How many frames to display (0 for all)">; + def thread_backtrace_start : Option<"start", "s">, + Group<1>, + Arg<"FrameIndex">, + Desc<"Frame in which to ${s}tart the backtrace">; + def thread_backtrace_extended + : Option<"extended", "e">, + Group<1>, + Arg<"Boolean">, + Desc<"Show the ${e}xtended backtrace, if available">; + def thread_backtrace_unfiltered : Option<"unfiltered", "u">, + Group<1>, + Desc<"Do not filter out frames according " + "to installed frame recognizers">; } let Command = "thread step scope" in { - def thread_step_scope_step_in_avoids_no_debug : - Option<"step-in-avoids-no-debug", "a">, Group<1>, Arg<"Boolean">, - Desc<"A boolean value that sets whether stepping into functions will step " - "over functions with no debug information.">; - def thread_step_scope_step_out_avoids_no_debug : - Option<"step-out-avoids-no-debug", "A">, Group<1>, Arg<"Boolean">, - Desc<"A boolean value, if true stepping out of functions will continue to" - " step out till it hits a function with debug information.">; - def thread_step_scope_count : Option<"count", "c">, Group<1>, Arg<"Count">, - Desc<"How many times to perform the stepping operation - currently only " - "supported for step-inst and next-inst.">; - def thread_step_scope_end_linenumber : Option<"end-linenumber", "e">, - Group<1>, Arg<"LineNum">, Desc<"The line at which to stop stepping - " - "defaults to the next line and only supported for step-in and step-over." - " You can also pass the string 'block' to step to the end of the current" - " block. This is particularly use in conjunction with --step-target to" - " step through a complex calling sequence.">; - def thread_step_scope_run_mode : Option<"run-mode", "m">, Group<1>, - EnumArg<"RunMode">, Desc<"Determine how to run other " - "threads while stepping the current thread.">; - def thread_step_scope_step_over_regexp : Option<"step-over-regexp", "r">, - Group<1>, Arg<"RegularExpression">, Desc<"A regular expression that defines " - "function names to not to stop at when stepping in.">; - def thread_step_scope_step_in_target : Option<"step-in-target", "t">, - Group<1>, Arg<"FunctionName">, Desc<"The name of the directly called " - "function step in should stop at when stepping into.">; + def thread_step_scope_step_in_avoids_no_debug + : Option<"step-in-avoids-no-debug", "a">, + Group<1>, + Arg<"Boolean">, + Desc<"A boolean value that sets whether stepping into functions will " + "step over functions with no debug information.">; + def thread_step_scope_step_out_avoids_no_debug + : Option<"step-out-avoids-no-debug", "A">, + Group<1>, + Arg<"Boolean">, + Desc<"A boolean value, if true stepping out of functions will continue " + "to step out till it hits a function with debug information.">; + def thread_step_scope_count + : Option<"count", "c">, + Group<1>, + Arg<"Count">, + Desc<"How many times to perform the stepping operation - currently " + "only supported for step-inst and next-inst.">; + def thread_step_scope_end_linenumber + : Option<"end-linenumber", "e">, + Group<1>, + Arg<"LineNum">, + Desc<"The line at which to stop stepping - defaults to the next line " + "and only supported for step-in and step-over. You can also pass " + "the string 'block' to step to the end of the current block. This " + "is particularly useful in conjunction with --step-target to step " + "through a complex calling sequence.">; + def thread_step_scope_run_mode : Option<"run-mode", "m">, + Group<1>, + EnumArg<"RunMode">, + Desc<"Determine how to run other threads " + "while stepping the current thread.">; + def thread_step_scope_step_over_regexp + : Option<"step-over-regexp", "r">, + Group<1>, + Arg<"RegularExpression">, + Desc<"A ${r}egular expression that defines function names to not to " + "stop at when stepping in.">; + def thread_step_scope_step_in_target + : Option<"step-in-target", "t">, + Group<1>, + Arg<"FunctionName">, + Desc<"The name of the directly called function step in should stop at " + "when stepping into.">; } let Command = "thread until" in { - def thread_until_frame : Option<"frame", "f">, Group<1>, Arg<"FrameIndex">, - Desc<"Frame index for until operation - defaults to 0">; - def thread_until_thread : Option<"thread", "t">, Group<1>, Arg<"ThreadIndex">, - Desc<"Thread index for the thread for until operation">; - def thread_until_run_mode : Option<"run-mode", "m">, Group<1>, - EnumArg<"RunMode">, Desc<"Determine how to run other " - "threads while stepping this one">; - def thread_until_address : Option<"address", "a">, Group<1>, - Arg<"AddressOrExpression">, Desc<"Run until we reach the specified address, " - "or leave the function - can be specified multiple times.">; + def thread_until_frame + : Option<"frame", "f">, + Group<1>, + Arg<"FrameIndex">, + Desc<"Frame index for until operation - defaults to 0">; + def thread_until_thread + : Option<"thread", "t">, + Group<1>, + Arg<"ThreadIndex">, + Desc<"Thread index for the thread for until operation">; + def thread_until_run_mode : Option<"run-mode", "m">, + Group<1>, + EnumArg<"RunMode">, + Desc<"Determine how to run other " + "threads while stepping this one">; + def thread_until_address + : Option<"address", "a">, + Group<1>, + Arg<"AddressOrExpression">, + Desc<"Run until we reach the specified address, " + "or leave the function - can be specified multiple times.">; } let Command = "thread info" in { - def thread_info_json : Option<"json", "j">, Desc<"Display the thread info in" - " JSON format.">; - def thread_info_stop_info : Option<"stop-info", "s">, Desc<"Display the " - "extended stop info in JSON format.">; + def thread_info_json : Option<"json", "j">, + Desc<"Display the thread info in" + " JSON format.">; + def thread_info_stop_info : Option<"stop-info", "s">, + Desc<"Display the " + "extended stop info in JSON format.">; def thread_info_backing_thread : Option<"backing-thread", "b">, - Desc<"If this is an OS plugin thread, query the backing thread instead; has" - " no effect otherwise.">; + Desc<"If this is an OS plugin thread, query " + "the backing thread instead; has" + " no effect otherwise.">; } let Command = "thread return" in { - def thread_return_from_expression : Option<"from-expression", "x">, - Desc<"Return from the innermost expression evaluation.">; + def thread_return_from_expression + : Option<"from-expression", "x">, + Desc<"Return from the innermost expression evaluation.">; } let Command = "thread jump" in { - def thread_jump_file : Option<"file", "f">, Group<1>, Arg<"Filename">, - Completion<"SourceFile">, Desc<"Specifies the source file to jump to.">; - def thread_jump_line : Option<"line", "l">, Group<1>, Arg<"LineNum">, - Required, Desc<"Specifies the line number to jump to.">; - def thread_jump_by : Option<"by", "b">, Group<2>, Arg<"Offset">, Required, - Desc<"Jumps by a relative line offset from the current line," - "can be a positive or negative offset">; - def thread_jump_address : Option<"address", "a">, Group<3>, - Arg<"AddressOrExpression">, Required, Desc<"Jumps to a specific address.">; - def thread_jump_force : Option<"force", "r">, Groups<[1,2,3]>, - Desc<"Allows the PC to leave the current function.">; + def thread_jump_file : Option<"file", "f">, + Group<1>, + Arg<"Filename">, + Completion<"SourceFile">, + Desc<"Specifies the source file to jump to.">; + def thread_jump_line : Option<"line", "l">, + Group<1>, + Arg<"LineNum">, + Required, + Desc<"Specifies the line number to jump to.">; + def thread_jump_by + : Option<"by", "b">, + Group<2>, + Arg<"Offset">, + Required, + Desc<"Jumps by a relative line offset from the current line, " + "can be a positive or negative offset.">; + def thread_jump_address : Option<"address", "a">, + Group<3>, + Arg<"AddressOrExpression">, + Required, + Desc<"Jumps to a specific address.">; + def thread_jump_force : Option<"force", "r">, + Groups<[1, 2, 3]>, + Desc<"Allows the PC to leave the current function.">; } let Command = "thread plan list" in { - def thread_plan_list_verbose : Option<"verbose", "v">, Group<1>, - Desc<"Display more information about the thread plans">; - def thread_plan_list_internal : Option<"internal", "i">, Group<1>, - Desc<"Display internal as well as user thread plans">; - def thread_plan_list_thread_id : Option<"thread-id", "t">, Group<1>, - Arg<"ThreadID">, Desc<"List the thread plans for this TID, can be " - "specified more than once.">; - def thread_plan_list_unreported : Option<"unreported", "u">, Group<1>, - Desc<"Display thread plans for unreported threads">; + def thread_plan_list_verbose + : Option<"verbose", "v">, + Group<1>, + Desc<"Display more information about the thread plans">; + def thread_plan_list_internal + : Option<"internal", "i">, + Group<1>, + Desc<"Display internal as well as user thread plans">; + def thread_plan_list_thread_id + : Option<"thread-id", "t">, + Group<1>, + Arg<"ThreadID">, + Desc<"List the thread plans for this TID, can be " + "specified more than once.">; + def thread_plan_list_unreported + : Option<"unreported", "u">, + Group<1>, + Desc<"Display thread plans for unreported threads">; } let Command = "thread select" in { - def thread_select_thread_id : Option<"thread-id", "t">, Group<2>, - Arg<"ThreadID">, Completion<"ThreadID">, - Desc<"Provide a thread ID instead of a thread index.">; + def thread_select_thread_id + : Option<"thread-id", "t">, + Group<2>, + Arg<"ThreadID">, + Completion<"ThreadID">, + Desc<"Provide a thread ID instead of a thread index.">; } let Command = "thread trace dump function calls" in { - def thread_trace_dump_function_calls_file : Option<"file", "F">, Group<1>, - Arg<"Filename">, - Desc<"Dump the function calls to a file instead of the standard output.">; - def thread_trace_dump_function_calls_json: Option<"json", "j">, - Group<1>, - Desc<"Dump in simple JSON format.">; - def thread_trace_dump_function_calls_pretty_json: Option<"pretty-json", "J">, - Group<1>, - Desc<"Dump in JSON format but pretty printing the output for easier " - "readability.">; + def thread_trace_dump_function_calls_file + : Option<"file", "F">, + Group<1>, + Arg<"Filename">, + Desc<"Dump the function calls to a file instead of the standard " + "output.">; + def thread_trace_dump_function_calls_json + : Option<"json", "j">, + Group<1>, + Desc<"Dump in simple JSON format.">; + def thread_trace_dump_function_calls_pretty_json + : Option<"pretty-json", "J">, + Group<1>, + Desc<"Dump in JSON format but pretty printing the output for easier " + "readability.">; } let Command = "thread trace dump instructions" in { - def thread_trace_dump_instructions_forwards: Option<"forwards", "f">, - Group<1>, - Desc<"If specified, the trace is traversed forwards chronologically " - "starting at the oldest instruction. Otherwise, it starts at the most " - "recent one and the traversal is backwards.">; - def thread_trace_dump_instructions_count : Option<"count", "c">, Group<1>, - Arg<"Count">, - Desc<"The number of instructions to display starting at the most recent " - "instruction, or the oldest if --forwards is provided.">; - def thread_trace_dump_instructions_all : Option<"all", "a">, Group<1>, - Desc<"From the starting point of the trace, dump all instructions " - "available.">; - def thread_trace_dump_instructions_id: Option<"id", "i">, Group<1>, - Arg<"Index">, - Desc<"Custom starting instruction id from where to start traversing. This " - "id can be provided in decimal or hexadecimal representation.">; - def thread_trace_dump_instructions_skip: Option<"skip", "s">, Group<1>, - Arg<"Index">, - Desc<"How many trace items (instructions, errors and events) to skip from " - "the starting position of the trace before starting the traversal.">; - def thread_trace_dump_instructions_raw : Option<"raw", "r">, Group<1>, - Desc<"Dump only instruction address without disassembly nor symbol " - "information.">; - def thread_trace_dump_instructions_file : Option<"file", "F">, Group<1>, - Arg<"Filename">, - Desc<"Dump the instruction to a file instead of the standard output.">; - def thread_trace_dump_instructions_json: Option<"json", "j">, - Group<1>, - Desc<"Dump in simple JSON format.">; - def thread_trace_dump_instructions_pretty_print: Option<"pretty-json", "J">, - Group<1>, - Desc<"Dump in JSON format but pretty printing the output for easier " - "readability.">; - def thread_trace_dump_instructions_show_kind : Option<"kind", "k">, Group<1>, - Desc<"Show instruction control flow kind. Refer to the enum " - "`InstructionControlFlowKind` for a list of control flow kind. " - "As an important note, far jumps, far calls and far returns often indicate " - "calls to and from kernel.">; - def thread_trace_dump_instructions_show_timestamps: Option<"time", "t">, - Group<1>, - Desc<"For each trace item, print the corresponding wall clock timestamp " - "if available.">; - def thread_trace_dump_instructions_show_events : Option<"events", "e">, - Group<1>, - Desc<"Dump the events that happened during the execution of the target.">; - def thread_trace_dump_instruction_only_events : Option<"only-events", "E">, - Group<1>, - Desc<"Dump only the events that happened during the execution of the " - "target. No instructions are dumped.">; - def thread_trace_dump_instructions_continue: Option<"continue", "C">, - Group<1>, - Desc<"Continue dumping instructions right where the previous invocation of " - "this command was left, or from the beginning if this is the first " - "invocation. The --skip argument is discarded and the other arguments are " - "preserved from the previous invocation when possible.">; + def thread_trace_dump_instructions_forwards + : Option<"forwards", "f">, + Group<1>, + Desc<"If specified, the trace is traversed forwards chronologically " + "starting at the oldest instruction. Otherwise, it starts at the " + "most " + "recent one and the traversal is backwards.">; + def thread_trace_dump_instructions_count + : Option<"count", "c">, + Group<1>, + Arg<"Count">, + Desc< + "The number of instructions to display starting at the most recent " + "instruction, or the oldest if --forwards is provided.">; + def thread_trace_dump_instructions_all + : Option<"all", "a">, + Group<1>, + Desc<"From the starting point of the trace, dump all instructions " + "available.">; + def thread_trace_dump_instructions_id + : Option<"id", "i">, + Group<1>, + Arg<"Index">, + Desc<"Custom starting instruction id from where to start traversing. " + "This " + "id can be provided in decimal or hexadecimal representation.">; + def thread_trace_dump_instructions_skip + : Option<"skip", "s">, + Group<1>, + Arg<"Index">, + Desc<"How many trace items (instructions, errors and events) to skip " + "from " + "the starting position of the trace before starting the " + "traversal.">; + def thread_trace_dump_instructions_raw + : Option<"raw", "r">, + Group<1>, + Desc<"Dump only instruction address without disassembly nor symbol " + "information.">; + def thread_trace_dump_instructions_file + : Option<"file", "F">, + Group<1>, + Arg<"Filename">, + Desc<"Dump the instruction to a file instead of the standard output.">; + def thread_trace_dump_instructions_json : Option<"json", "j">, + Group<1>, + Desc<"Dump in simple JSON format.">; + def thread_trace_dump_instructions_pretty_print + : Option<"pretty-json", "J">, + Group<1>, + Desc<"Dump in JSON format but pretty printing the output for easier " + "readability.">; + def thread_trace_dump_instructions_show_kind + : Option<"kind", "k">, + Group<1>, + Desc<"Show instruction control flow kind. Refer to the enum " + "`InstructionControlFlowKind` for a list of control flow kind. " + "As an important note, far jumps, far calls and far returns often " + "indicate " + "calls to and from kernel.">; + def thread_trace_dump_instructions_show_timestamps + : Option<"time", "t">, + Group<1>, + Desc< + "For each trace item, print the corresponding wall clock timestamp " + "if available.">; + def thread_trace_dump_instructions_show_events + : Option<"events", "e">, + Group<1>, + Desc<"Dump the events that happened during the execution of the " + "target.">; + def thread_trace_dump_instruction_only_events + : Option<"only-events", "E">, + Group<1>, + Desc<"Dump only the events that happened during the execution of the " + "target. No instructions are dumped.">; + def thread_trace_dump_instructions_continue + : Option<"continue", "C">, + Group<1>, + Desc< + "Continue dumping instructions right where the previous invocation " + "of " + "this command was left, or from the beginning if this is the first " + "invocation. The --skip argument is discarded and the other " + "arguments are " + "preserved from the previous invocation when possible.">; } let Command = "thread trace dump info" in { - def thread_trace_dump_info_verbose : Option<"verbose", "v">, Group<1>, - Desc<"show verbose thread trace dump info">; - def thread_trace_dump_info_json: Option<"json", "j">, Group<1>, - Desc<"Dump in JSON format.">; + def thread_trace_dump_info_verbose + : Option<"verbose", "v">, + Group<1>, + Desc<"show verbose thread trace dump info">; + def thread_trace_dump_info_json : Option<"json", "j">, + Group<1>, + Desc<"Dump in JSON format.">; } let Command = "type summary add" in { - def type_summary_add_category : Option<"category", "w">, Arg<"Name">, - Desc<"Add this to the given category instead of the default one.">; - def type_summary_add_cascade : Option<"cascade", "C">, Arg<"Boolean">, - Desc<"If true, cascade through typedef chains.">; - def type_summary_add_no_value : Option<"no-value", "v">, - Desc<"Don't show the value, just show the summary, for this type.">; - def type_summary_add_skip_pointers : Option<"skip-pointers", "p">, - Desc<"Don't use this format for pointers-to-type objects.">; - def type_summary_add_pointer_match_depth : Option<"pointer-match-depth", "d">, - Arg<"UnsignedInteger">, - Desc<"Specify the maximum pointer depth that this format can be apply to " - "(default to 1). It's only effective when --skip-pointers is not set.">; - def type_summary_add_skip_references : Option<"skip-references", "r">, - Desc<"Don't use this format for references-to-type objects.">; - def type_summary_add_regex : Option<"regex", "x">, - Desc<"Type names are actually regular expressions.">; - def type_summary_add_recognizer_function : - Option<"recognizer-function", "\\x01">, - Desc<"The names in the argument list are actually the names of python " - "functions that decide whether to use this summary for any given type. " - "Cannot be specified at the same time as --regex (-x).">; - def type_summary_add_inline_children : Option<"inline-children", "c">, - Group<1>, Required, - Desc<"If true, inline all child values into summary string.">; - def type_summary_add_omit_names : Option<"omit-names", "O">, Group<1>, - Desc<"If true, omit value names in the summary display.">; - def type_summary_add_summary_string : Option<"summary-string", "s">, Group<2>, - Arg<"SummaryString">, Required, - Desc<"Summary string used to display text and object contents.">; - def type_summary_add_python_script : Option<"python-script", "o">, Group<3>, - Arg<"PythonScript">, - Desc<"Give a one-liner Python script as part of the command.">; - def type_summary_add_python_function : Option<"python-function", "F">, - Group<3>, Arg<"PythonFunction">, - Desc<"Give the name of a Python function to use for this type.">; - def type_summary_add_input_python : Option<"input-python", "P">, Group<3>, - Desc<"Input Python code to use for this type manually.">; - def type_summary_add_expand : Option<"expand", "e">, Groups<[2,3]>, - Desc<"Expand aggregate data types to show children on separate lines.">; - def type_summary_add_hide_empty : Option<"hide-empty", "h">, Groups<[2,3]>, - Desc<"Do not expand aggregate data types with no children.">; - def type_summary_add_name : Option<"name", "n">, Groups<[2,3]>, Arg<"Name">, - Desc<"A name for this summary string.">; + def type_summary_add_category + : Option<"category", "w">, + Arg<"Name">, + Desc<"Add this to the given category instead of the default one.">; + def type_summary_add_cascade + : Option<"cascade", "C">, + Arg<"Boolean">, + Desc<"If true, cascade through typedef chains.">; + def type_summary_add_no_value + : Option<"no-value", "v">, + Desc<"Don't show the value, just show the summary, for this type.">; + def type_summary_add_skip_pointers + : Option<"skip-pointers", "p">, + Desc<"Don't use this format for pointers-to-type objects.">; + def type_summary_add_pointer_match_depth + : Option<"pointer-match-depth", "d">, + Arg<"UnsignedInteger">, + Desc<"Specify the maximum pointer depth that this format can be apply " + "to " + "(default to 1). It's only effective when --skip-pointers is not " + "set.">; + def type_summary_add_skip_references + : Option<"skip-references", "r">, + Desc<"Don't use this format for references-to-type objects.">; + def type_summary_add_regex + : Option<"regex", "x">, + Desc<"Type names are actually regular expressions.">; + def type_summary_add_recognizer_function + : Option<"recognizer-function", "\\x01">, + Desc<"The names in the argument list are actually the names of python " + "functions that decide whether to use this summary for any given " + "type. " + "Cannot be specified at the same time as --regex (-x).">; + def type_summary_add_inline_children + : Option<"inline-children", "c">, + Group<1>, + Required, + Desc<"If true, inline all child values into summary string.">; + def type_summary_add_omit_names + : Option<"omit-names", "O">, + Group<1>, + Desc<"If true, omit value names in the summary display.">; + def type_summary_add_summary_string + : Option<"summary-string", "s">, + Group<2>, + Arg<"SummaryString">, + Required, + Desc<"Summary string used to display text and object contents.">; + def type_summary_add_python_script + : Option<"python-script", "o">, + Group<3>, + Arg<"PythonScript">, + Desc<"Give a one-liner Python script as part of the command.">; + def type_summary_add_python_function + : Option<"python-function", "F">, + Group<3>, + Arg<"PythonFunction">, + Desc<"Give the name of a Python function to use for this type.">; + def type_summary_add_input_python + : Option<"input-python", "P">, + Group<3>, + Desc<"Input Python code to use for this type manually.">; + def type_summary_add_expand + : Option<"expand", "e">, + Groups<[2, 3]>, + Desc<"Expand aggregate data types to show children on separate lines.">; + def type_summary_add_hide_empty + : Option<"hide-empty", "h">, + Groups<[2, 3]>, + Desc<"Do not expand aggregate data types with no children.">; + def type_summary_add_name : Option<"name", "n">, + Groups<[2, 3]>, + Arg<"Name">, + Desc<"A name for this summary string.">; } let Command = "type synth add" in { - def type_synth_add_cascade : Option<"cascade", "C">, Arg<"Boolean">, - Desc<"If true, cascade through typedef chains.">; - def type_synth_add_skip_pointers : Option<"skip-pointers", "p">, - Desc<"Don't use this format for pointers-to-type objects.">; - def type_synth_add_skip_references : Option<"skip-references", "r">, - Desc<"Don't use this format for references-to-type objects.">; - def type_synth_add_category : Option<"category", "w">, Arg<"Name">, - Desc<"Add this to the given category instead of the default one.">; - def type_synth_add_python_class : Option<"python-class", "l">, Group<2>, - Arg<"PythonClass">, - Desc<"Use this Python class to produce synthetic children.">; - def type_synth_add_input_python : Option<"input-python", "P">, Group<3>, - Desc<"Type Python code to generate a class that provides synthetic " - "children.">; - def type_synth_add_regex : Option<"regex", "x">, - Desc<"Type names are actually regular expressions.">; - def type_synth_add_recognizer_function : - Option<"recognizer-function", "\\x01">, - Desc<"The names in the argument list are actually the names of python " - "functions that decide whether to use this summary for any given type. " - "Cannot be specified at the same time as --regex (-x).">; + def type_synth_add_cascade : Option<"cascade", "C">, + Arg<"Boolean">, + Desc<"If true, cascade through typedef chains.">; + def type_synth_add_skip_pointers + : Option<"skip-pointers", "p">, + Desc<"Don't use this format for pointers-to-type objects.">; + def type_synth_add_skip_references + : Option<"skip-references", "r">, + Desc<"Don't use this format for references-to-type objects.">; + def type_synth_add_category + : Option<"category", "w">, + Arg<"Name">, + Desc<"Add this to the given category instead of the default one.">; + def type_synth_add_python_class + : Option<"python-class", "l">, + Group<2>, + Arg<"PythonClass">, + Desc<"Use this Python class to produce synthetic children.">; + def type_synth_add_input_python + : Option<"input-python", "P">, + Group<3>, + Desc<"Type Python code to generate a class that provides synthetic " + "children.">; + def type_synth_add_regex + : Option<"regex", "x">, + Desc<"Type names are actually regular expressions.">; + def type_synth_add_recognizer_function + : Option<"recognizer-function", "\\x01">, + Desc<"The names in the argument list are actually the names of python " + "functions that decide whether to use this summary for any given " + "type. " + "Cannot be specified at the same time as --regex (-x).">; } let Command = "type format add" in { - def type_format_add_category : Option<"category", "w">, Arg<"Name">, - Desc<"Add this to the given category instead of the default one.">; - def type_format_add_cascade : Option<"cascade", "C">, Arg<"Boolean">, - Desc<"If true, cascade through typedef chains.">; - def type_format_add_skip_pointers : Option<"skip-pointers", "p">, - Desc<"Don't use this format for pointers-to-type objects.">; - def type_format_add_skip_references : Option<"skip-references", "r">, - Desc<"Don't use this format for references-to-type objects.">; - def type_format_add_regex : Option<"regex", "x">, - Desc<"Type names are actually regular expressions.">; - def type_format_add_type : Option<"type", "t">, Group<2>, Arg<"Name">, - Desc<"Format variables as if they were of this type.">; + def type_format_add_category + : Option<"category", "w">, + Arg<"Name">, + Desc<"Add this to the given category instead of the default one.">; + def type_format_add_cascade + : Option<"cascade", "C">, + Arg<"Boolean">, + Desc<"If true, cascade through typedef chains.">; + def type_format_add_skip_pointers + : Option<"skip-pointers", "p">, + Desc<"Don't use this format for pointers-to-type objects.">; + def type_format_add_skip_references + : Option<"skip-references", "r">, + Desc<"Don't use this format for references-to-type objects.">; + def type_format_add_regex + : Option<"regex", "x">, + Desc<"Type names are actually regular expressions.">; + def type_format_add_type + : Option<"type", "t">, + Group<2>, + Arg<"Name">, + Desc<"Format variables as if they were of this type.">; } let Command = "type formatter delete" in { - def type_formatter_delete_all : Option<"all", "a">, Group<1>, - Desc<"Delete from every category.">; - def type_formatter_delete_category : Option<"category", "w">, Group<2>, - Arg<"Name">, Desc<"Delete from given category.">; - def type_formatter_delete_language : Option<"language", "l">, Group<3>, - Arg<"Language">, Desc<"Delete from given language's category.">; + def type_formatter_delete_all : Option<"all", "a">, + Group<1>, + Desc<"Delete from every category.">; + def type_formatter_delete_category : Option<"category", "w">, + Group<2>, + Arg<"Name">, + Desc<"Delete from given category.">; + def type_formatter_delete_language + : Option<"language", "l">, + Group<3>, + Arg<"Language">, + Desc<"Delete from given language's category.">; } let Command = "type formatter clear" in { def type_formatter_clear_all : Option<"all", "a">, - Desc<"Clear every category.">; + Desc<"Clear every category.">; } let Command = "type formatter list" in { - def type_formatter_list_category_regex : Option<"category-regex", "w">, - Group<1>, Arg<"Name">, Desc<"Only show categories matching this filter.">; - def type_formatter_list_language : Option<"language", "l">, Group<2>, - Arg<"Language">, Desc<"Only show the category for a specific language.">; + def type_formatter_list_category_regex + : Option<"category-regex", "w">, + Group<1>, + Arg<"Name">, + Desc<"Only show categories matching this filter.">; + def type_formatter_list_language + : Option<"language", "l">, + Group<2>, + Arg<"Language">, + Desc<"Only show the category for a specific language.">; } let Command = "type category define" in { - def type_category_define_enabled : Option<"enabled", "e">, - Desc<"If specified, this category will be created enabled.">; - def type_category_define_language : Option<"language", "l">, Arg<"Language">, - Desc<"Specify the language that this category is supported for.">; + def type_category_define_enabled + : Option<"enabled", "e">, + Desc<"If specified, this category will be created enabled.">; + def type_category_define_language + : Option<"language", "l">, + Arg<"Language">, + Desc<"Specify the language that this category is supported for.">; } let Command = "type category enable" in { - def type_category_enable_language : Option<"language", "l">, Arg<"Language">, - Desc<"Enable the category for this language.">; + def type_category_enable_language + : Option<"language", "l">, + Arg<"Language">, + Desc<"Enable the category for this language.">; } let Command = "type category disable" in { - def type_category_disable_language : Option<"language", "l">, Arg<"Language">, - Desc<"Enable the category for this language.">; + def type_category_disable_language + : Option<"language", "l">, + Arg<"Language">, + Desc<"Disable the category for this language.">; } let Command = "type filter add" in { - def type_filter_add_cascade : Option<"cascade", "C">, Arg<"Boolean">, - Desc<"If true, cascade through typedef chains.">; - def type_filter_add_skip_pointers : Option<"skip-pointers", "p">, - Desc<"Don't use this format for pointers-to-type objects.">; - def type_filter_add_skip_references : Option<"skip-references", "r">, - Desc<"Don't use this format for references-to-type objects.">; - def type_filter_add_category : Option<"category", "w">, Arg<"Name">, - Desc<"Add this to the given category instead of the default one.">; - def type_filter_add_child : Option<"child", "c">, Arg<"ExpressionPath">, - Desc<"Include this expression path in the synthetic view.">; - def type_filter_add_regex : Option<"regex", "x">, - Desc<"Type names are actually regular expressions.">; + def type_filter_add_cascade + : Option<"cascade", "C">, + Arg<"Boolean">, + Desc<"If true, cascade through typedef chains.">; + def type_filter_add_skip_pointers + : Option<"skip-pointers", "p">, + Desc<"Don't use this format for pointers-to-type objects.">; + def type_filter_add_skip_references + : Option<"skip-references", "r">, + Desc<"Don't use this format for references-to-type objects.">; + def type_filter_add_category + : Option<"category", "w">, + Arg<"Name">, + Desc<"Add this to the given category instead of the default one.">; + def type_filter_add_child + : Option<"child", "c">, + Arg<"ExpressionPath">, + Desc<"Include this expression path in the synthetic view.">; + def type_filter_add_regex + : Option<"regex", "x">, + Desc<"Type names are actually regular expressions.">; } let Command = "type lookup" in { def type_lookup_show_help : Option<"show-help", "h">, - Desc<"Display available help for types">; - def type_lookup_language : Option<"language", "l">, Arg<"Language">, - Desc<"Which language's types should the search scope be">; + Desc<"Display available help for types">; + def type_lookup_language + : Option<"language", "l">, + Arg<"Language">, + Desc<"Which language's types should the search scope be">; } let Command = "watchpoint list" in { - def watchpoint_list_brief : Option<"brief", "b">, Group<1>, Desc<"Give a " - "brief description of the watchpoint (no location info).">; - def watchpoint_list_full : Option<"full", "f">, Group<2>, Desc<"Give a full " - "description of the watchpoint and its locations.">; - def watchpoint_list_verbose : Option<"verbose", "v">, Group<3>, Desc<"Explain " - "everything we know about the watchpoint (for debugging debugger bugs).">; + def watchpoint_list_brief + : Option<"brief", "b">, + Group<1>, + Desc<"Give a " + "brief description of the watchpoint (no location info).">; + def watchpoint_list_full + : Option<"full", "f">, + Group<2>, + Desc<"Give a full " + "description of the watchpoint and its locations.">; + def watchpoint_list_verbose : Option<"verbose", "v">, + Group<3>, + Desc<"Explain " + "everything we know about the watchpoint " + "(for debugging debugger bugs).">; } let Command = "watchpoint ignore" in { - def watchpoint_ignore_ignore_count : Option<"ignore-count", "i">, - Arg<"Count">, Required, Desc<"Set the number of times this watchpoint is" - " skipped before stopping.">; + def watchpoint_ignore_ignore_count + : Option<"ignore-count", "i">, + Arg<"Count">, + Required, + Desc<"Set the number of times this watchpoint is" + " skipped before stopping.">; } let Command = "watchpoint modify" in { - def watchpoint_modify_condition : Option<"condition", "c">, Arg<"Expression">, - Desc<"The watchpoint stops only if this condition expression evaluates " - "to true.">; + def watchpoint_modify_condition + : Option<"condition", "c">, + Arg<"Expression">, + Desc<"The watchpoint stops only if this condition expression evaluates " + "to true.">; } let Command = "watchpoint command add" in { - def watchpoint_command_add_one_liner : Option<"one-liner", "o">, Group<1>, - Arg<"OneLiner">, Desc<"Specify a one-line watchpoint command inline. Be " - "sure to surround it with quotes.">; - def watchpoint_command_add_stop_on_error : Option<"stop-on-error", "e">, - Arg<"Boolean">, Desc<"Specify whether watchpoint command execution should " - "terminate on error.">; - def watchpoint_command_add_script_type : Option<"script-type", "s">, - EnumArg<"ScriptLang">, Desc<"Specify the language for the" - " commands - if none is specified, the lldb command interpreter will be " - "used.">; - def watchpoint_command_add_python_function : Option<"python-function", "F">, - Group<2>, Arg<"PythonFunction">, Desc<"Give the name of a Python function " - "to run as command for this watchpoint. Be sure to give a module name if " - "appropriate.">; + def watchpoint_command_add_one_liner + : Option<"one-liner", "o">, + Group<1>, + Arg<"OneLiner">, + Desc<"Specify a one-line watchpoint command inline. Be " + "sure to surround it with quotes.">; + def watchpoint_command_add_stop_on_error + : Option<"stop-on-error", "e">, + Arg<"Boolean">, + Desc<"Specify whether watchpoint command execution should " + "terminate on error.">; + def watchpoint_command_add_script_type + : Option<"script-type", "s">, + EnumArg<"ScriptLang">, + Desc<"Specify the language for the" + " commands - if none is specified, the lldb command interpreter " + "will be " + "used.">; + def watchpoint_command_add_python_function + : Option<"python-function", "F">, + Group<2>, + Arg<"PythonFunction">, + Desc<"Give the name of a Python function " + "to run as command for this watchpoint. Be sure to give a module " + "name if " + "appropriate.">; } let Command = "watchpoint delete" in { - def watchpoint_delete_force : Option<"force", "f">, Group<1>, - Desc<"Delete all watchpoints without querying for confirmation.">; + def watchpoint_delete_force + : Option<"force", "f">, + Group<1>, + Desc<"Delete all watchpoints without querying for confirmation.">; } let Command = "trace load" in { - def trace_load_verbose : Option<"verbose", "v">, Group<1>, - Desc<"Show verbose trace load logging for debugging the plug-in " - "implementation.">; + def trace_load_verbose + : Option<"verbose", "v">, + Group<1>, + Desc<"Show verbose trace load logging for debugging the plug-in " + "implementation.">; } let Command = "trace save" in { - def trace_save_compact: Option<"compact", "c">, - Group<1>, - Desc<"Try not to save to disk information irrelevant to the traced " - "processes. Each trace plug-in implements this in a different " - "fashion.">; + def trace_save_compact + : Option<"compact", "c">, + Group<1>, + Desc<"Try not to save to disk information irrelevant to the traced " + "processes. Each trace plug-in implements this in a different " + "fashion.">; } let Command = "trace dump" in { - def trace_dump_verbose : Option<"verbose", "v">, Group<1>, - Desc<"Show verbose trace information.">; + def trace_dump_verbose : Option<"verbose", "v">, + Group<1>, + Desc<"Show verbose trace information.">; } let Command = "trace schema" in { - def trace_schema_verbose : Option<"verbose", "v">, Group<1>, - Desc<"Show verbose trace schema logging for debugging the plug-in.">; + def trace_schema_verbose + : Option<"verbose", "v">, + Group<1>, + Desc<"Show verbose trace schema logging for debugging the plug-in.">; } let Command = "statistics dump" in { - def statistics_dump_all: Option<"all-targets", "a">, Group<1>, - Desc<"Include statistics for all targets.">; - def statistics_dump_summary: Option<"summary", "s">, Group<1>, - Desc<"Dump only high-level summary statistics. " - "Exclude targets, modules, breakpoints etc... details.">; - def statistics_dump_force: Option<"load-all-debug-info", "f">, Group<1>, - Desc<"Dump the total possible debug info statistics. " - "Force loading all the debug information if not yet loaded, and collect " - "statistics with those.">; - def statistics_dump_targets: Option<"targets", "r">, Group<1>, - Arg<"Boolean">, - Desc<"Dump statistics for the targets, including breakpoints, expression " - "evaluations, frame variables, etc. " - "Defaults to true in both default mode and summary mode. " - "In default mode, if both '--targets' and '--modules' are 'true', a list " - "of module identifiers will be added to the 'targets' section.">; - def statistics_dump_modules: Option<"modules", "m">, Group<1>, - Arg<"Boolean">, - Desc<"Dump statistics for the modules, including time and size of various " - "aspects of the module and debug information, type system, path, etc. " - "Defaults to true, unless the '--summary' mode is enabled, in which case " - "this is turned off unless specified. " - "In default mode, if both '--targets' and '--modules' are 'true', a list " - "of module identifiers will be added to the 'targets' section.">; + def statistics_dump_all : Option<"all-targets", "a">, + Group<1>, + Desc<"Include statistics for all targets.">; + def statistics_dump_summary + : Option<"summary", "s">, + Group<1>, + Desc<"Dump only high-level summary statistics. Exclude targets, " + "modules, breakpoints etc... details.">; + def statistics_dump_force + : Option<"load-all-debug-info", "f">, + Group<1>, + Desc<"Dump the total possible debug info statistics. Force loading all " + "the debug information if not yet loaded, and collect statistics " + "with those.">; + def statistics_dump_targets + : Option<"targets", "r">, + Group<1>, + Arg<"Boolean">, + Desc<"Dump statistics for the targets, including breakpoints, " + "expression evaluations, frame variables, etc. Defaults to true " + "in both default mode and summary mode. In default mode, if both " + "'--targets' and '--modules' are 'true', a list of module " + "identifiers will be added to the 'targets' section.">; + def statistics_dump_modules + : Option<"modules", "m">, + Group<1>, + Arg<"Boolean">, + Desc<"Dump statistics for the modules, including time and size of " + "various aspects of the module and debug information, type " + "system, path, etc. Defaults to true, unless the '--summary' mode " + "is enabled, in which case this is turned off unless specified. " + "In default mode, if both '--targets' and '--modules' are 'true', " + "a list of module identifiers will be added to the 'targets' " + "section.">; def statistics_dump_transcript : Option<"transcript", "t">, Group<1>, Arg<"Boolean">, Desc<"If the setting interpreter.save-transcript is enabled and this " "option is 'true', include a JSON array with all commands the " - "user and/or " - "scripts executed during a debug session. " - "Defaults to false. ">; + "user and/or scripts executed during a debug session. Defaults to " + "false. ">; def statistics_dump_plugins : Option<"plugins", "p">, Group<1>, diff --git a/lldb/source/Core/CoreProperties.td b/lldb/source/Core/CoreProperties.td index 53dd333f045c..fda34a8ad263 100644 --- a/lldb/source/Core/CoreProperties.td +++ b/lldb/source/Core/CoreProperties.td @@ -59,7 +59,7 @@ let Definition = "debugger" in { Desc<"The default disassembly format string to use when disassembling instruction sequences.">; def FrameFormat: Property<"frame-format", "FormatEntity">, Global, - DefaultStringValue<"frame #${frame.index}: ${ansi.fg.cyan}${frame.pc}${ansi.normal}{ ${module.file.basename}{`${function.name-with-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}{${function.is-optimized} [opt]}{${function.is-inlined} [inlined]}{${frame.is-artificial} [artificial]}\\\\n">, + DefaultStringValue<"frame #${frame.index}: ${ansi.fg.cyan}${frame.pc}${ansi.normal}{ ${module.file.basename}{`${function.name-with-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}${frame.kind}{${function.is-optimized} [opt]}{${function.is-inlined} [inlined]}{${frame.is-artificial} [artificial]}\\\\n">, Desc<"The default frame format string to use when displaying stack frame information for threads.">; def NotiftVoid: Property<"notify-void", "Boolean">, Global, @@ -233,7 +233,7 @@ let Definition = "debugger" in { Desc<"If true, LLDB will automatically escape non-printable and escape characters when formatting strings.">; def FrameFormatUnique: Property<"frame-format-unique", "FormatEntity">, Global, - DefaultStringValue<"frame #${frame.index}: ${ansi.fg.cyan}${frame.pc}${ansi.normal}{ ${module.file.basename}{`${function.name-without-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}{${function.is-optimized} [opt]}{${function.is-inlined} [inlined]}{${frame.is-artificial} [artificial]}\\\\n">, + DefaultStringValue<"frame #${frame.index}: ${ansi.fg.cyan}${frame.pc}${ansi.normal}{ ${module.file.basename}{`${function.name-without-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}${frame.kind}{${function.is-optimized} [opt]}{${function.is-inlined} [inlined]}{${frame.is-artificial} [artificial]}\\\\n">, Desc<"The default frame format string to use when displaying stack frame information for threads from thread backtrace unique.">; def ShowAutosuggestion: Property<"show-autosuggestion", "Boolean">, Global, diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp index e0a7d6934570..f2ed1f739534 100644 --- a/lldb/source/Core/Disassembler.cpp +++ b/lldb/source/Core/Disassembler.cpp @@ -26,7 +26,11 @@ #include "lldb/Symbol/Function.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/ABI.h" #include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Target.h" @@ -41,6 +45,8 @@ #include "lldb/lldb-private-enumerations.h" #include "lldb/lldb-private-interfaces.h" #include "lldb/lldb-private-types.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Support/Compiler.h" #include "llvm/TargetParser/Triple.h" @@ -280,6 +286,127 @@ bool Disassembler::ElideMixedSourceAndDisassemblyLine( return false; } +// For each instruction, this block attempts to resolve in-scope variables +// and determine if the current PC falls within their +// DWARF location entry. If so, it prints a simplified annotation using the +// variable name and its resolved location (e.g., "var = reg; " ). +// +// Annotations are only included if the variable has a valid DWARF location +// entry, and the location string is non-empty after filtering. Decoding +// errors and DWARF opcodes are intentionally omitted to keep the output +// concise and user-friendly. +// +// The goal is to give users helpful live variable hints alongside the +// disassembled instruction stream, similar to how debug information +// enhances source-level debugging. +std::vector<std::string> +VariableAnnotator::annotate(Instruction &inst, Target &target, + const lldb::ModuleSP &module_sp) { + std::vector<std::string> events; + + // If we lost module context, everything becomes <undef>. + if (!module_sp) { + for (const auto &KV : Live_) + events.emplace_back(llvm::formatv("{0} = <undef>", KV.second.name).str()); + Live_.clear(); + return events; + } + + // Resolve function/block at this *file* address. + SymbolContext sc; + const Address &iaddr = inst.GetAddress(); + const auto mask = eSymbolContextFunction | eSymbolContextBlock; + if (!module_sp->ResolveSymbolContextForAddress(iaddr, mask, sc) || + !sc.function) { + // No function context: everything dies here. + for (const auto &KV : Live_) + events.emplace_back(llvm::formatv("{0} = <undef>", KV.second.name).str()); + Live_.clear(); + return events; + } + + // Collect in-scope variables for this instruction into Current. + VariableList var_list; + // Innermost block containing iaddr. + if (Block *B = sc.block) { + auto filter = [](Variable *v) -> bool { return v && !v->IsArtificial(); }; + B->AppendVariables(/*can_create*/ true, + /*get_parent_variables*/ true, + /*stop_if_block_is_inlined_function*/ false, + /*filter*/ filter, + /*variable_list*/ &var_list); + } + + const lldb::addr_t pc_file = iaddr.GetFileAddress(); + const lldb::addr_t func_file = sc.function->GetAddress().GetFileAddress(); + + // ABI from Target (pretty reg names if plugin exists). Safe to be null. + lldb::ABISP abi_sp = ABI::FindPlugin(nullptr, target.GetArchitecture()); + ABI *abi = abi_sp.get(); + + llvm::DIDumpOptions opts; + opts.ShowAddresses = false; + // Prefer "register-only" output when we have an ABI. + opts.PrintRegisterOnly = static_cast<bool>(abi_sp); + + llvm::DenseMap<lldb::user_id_t, VarState> Current; + + for (size_t i = 0, e = var_list.GetSize(); i != e; ++i) { + lldb::VariableSP v = var_list.GetVariableAtIndex(i); + if (!v || v->IsArtificial()) + continue; + + const char *nm = v->GetName().AsCString(); + llvm::StringRef name = nm ? nm : "<anon>"; + + DWARFExpressionList &exprs = v->LocationExpressionList(); + if (!exprs.IsValid()) + continue; + + auto entry_or_err = exprs.GetExpressionEntryAtAddress(func_file, pc_file); + if (!entry_or_err) + continue; + + auto entry = *entry_or_err; + + StreamString loc_ss; + entry.expr->DumpLocation(&loc_ss, eDescriptionLevelBrief, abi, opts); + + llvm::StringRef loc = llvm::StringRef(loc_ss.GetString()).trim(); + if (loc.empty()) + continue; + + Current.try_emplace(v->GetID(), + VarState{std::string(name), std::string(loc)}); + } + + // Diff Live_ → Current. + + // 1) Starts/changes: iterate Current and compare with Live_. + for (const auto &KV : Current) { + auto it = Live_.find(KV.first); + if (it == Live_.end()) { + // Newly live. + events.emplace_back( + llvm::formatv("{0} = {1}", KV.second.name, KV.second.last_loc).str()); + } else if (it->second.last_loc != KV.second.last_loc) { + // Location changed. + events.emplace_back( + llvm::formatv("{0} = {1}", KV.second.name, KV.second.last_loc).str()); + } + } + + // 2) Ends: anything that was live but is not in Current becomes <undef>. + for (const auto &KV : Live_) { + if (!Current.count(KV.first)) + events.emplace_back(llvm::formatv("{0} = <undef>", KV.second.name).str()); + } + + // Commit new state. + Live_ = std::move(Current); + return events; +} + void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch, const ExecutionContext &exe_ctx, bool mixed_source_and_assembly, @@ -376,6 +503,7 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch, } } + VariableAnnotator annot; previous_symbol = nullptr; SourceLine previous_line; for (size_t i = 0; i < num_instructions_found; ++i) { @@ -540,10 +668,26 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch, const bool show_bytes = (options & eOptionShowBytes) != 0; const bool show_control_flow_kind = (options & eOptionShowControlFlowKind) != 0; - inst->Dump(&strm, max_opcode_byte_size, true, show_bytes, + + StreamString inst_line; + + inst->Dump(&inst_line, max_opcode_byte_size, true, show_bytes, show_control_flow_kind, &exe_ctx, &sc, &prev_sc, nullptr, address_text_size); + + if ((options & eOptionVariableAnnotations) && target_sp) { + auto annotations = annot.annotate(*inst, *target_sp, module_sp); + if (!annotations.empty()) { + const size_t annotation_column = 100; + inst_line.FillLastLineToColumn(annotation_column, ' '); + inst_line.PutCString("; "); + inst_line.PutCString(llvm::join(annotations, ", ")); + } + } + + strm.PutCString(inst_line.GetString()); strm.EOL(); + } else { break; } @@ -724,9 +868,7 @@ bool Instruction::DumpEmulation(const ArchSpec &arch) { return false; } -bool Instruction::CanSetBreakpoint () { - return !HasDelaySlot(); -} +bool Instruction::CanSetBreakpoint() { return !HasDelaySlot(); } bool Instruction::HasDelaySlot() { // Default is false. @@ -1073,10 +1215,8 @@ void InstructionList::Append(lldb::InstructionSP &inst_sp) { m_instructions.push_back(inst_sp); } -uint32_t -InstructionList::GetIndexOfNextBranchInstruction(uint32_t start, - bool ignore_calls, - bool *found_calls) const { +uint32_t InstructionList::GetIndexOfNextBranchInstruction( + uint32_t start, bool ignore_calls, bool *found_calls) const { size_t num_instructions = m_instructions.size(); uint32_t next_branch = UINT32_MAX; diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp index 5d3c8b421d5d..491f5c6320d9 100644 --- a/lldb/source/Core/FormatEntity.cpp +++ b/lldb/source/Core/FormatEntity.cpp @@ -108,6 +108,7 @@ constexpr Definition g_frame_child_entries[] = { Entry::DefinitionWithChildren("reg", EntryType::FrameRegisterByName, g_string_entry), Definition("is-artificial", EntryType::FrameIsArtificial), + Definition("kind", EntryType::FrameKind), }; constexpr Definition g_function_child_entries[] = { @@ -380,6 +381,7 @@ const char *FormatEntity::Entry::TypeToCString(Type t) { ENUM_TO_CSTR(FrameRegisterFlags); ENUM_TO_CSTR(FrameRegisterByName); ENUM_TO_CSTR(FrameIsArtificial); + ENUM_TO_CSTR(FrameKind); ENUM_TO_CSTR(ScriptFrame); ENUM_TO_CSTR(FunctionID); ENUM_TO_CSTR(FunctionDidChange); @@ -1679,7 +1681,7 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, StackFrame *frame = exe_ctx->GetFramePtr(); if (frame) { const Address &pc_addr = frame->GetFrameCodeAddress(); - if (pc_addr.IsValid()) { + if (pc_addr.IsValid() || frame->IsSynthetic()) { if (DumpAddressAndContent(s, sc, exe_ctx, pc_addr, false)) return true; } @@ -1747,6 +1749,18 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, return false; } + case Entry::Type::FrameKind: { + if (exe_ctx) + if (StackFrame *frame = exe_ctx->GetFramePtr()) { + if (frame->IsSynthetic()) + s.PutCString(" [synthetic]"); + else if (frame->IsHistorical()) + s.PutCString(" [history]"); + return true; + } + return false; + } + case Entry::Type::ScriptFrame: if (exe_ctx) { StackFrame *frame = exe_ctx->GetFramePtr(); diff --git a/lldb/source/Core/Mangled.cpp b/lldb/source/Core/Mangled.cpp index ce4db4e0daa8..91b9c0007617 100644 --- a/lldb/source/Core/Mangled.cpp +++ b/lldb/source/Core/Mangled.cpp @@ -556,3 +556,18 @@ void Mangled::Encode(DataEncoder &file, ConstStringTable &strtab) const { break; } } + +ConstString Mangled::GetBaseName() const { + const auto &demangled_info = GetDemangledInfo(); + if (!demangled_info.has_value()) + return {}; + + ConstString demangled_name = GetDemangledName(); + if (!demangled_name) + return {}; + + const char *name_str = demangled_name.AsCString(); + const auto &range = demangled_info->BasenameRange; + return ConstString( + llvm::StringRef(name_str + range.first, range.second - range.first)); +} diff --git a/lldb/source/Core/ModuleList.cpp b/lldb/source/Core/ModuleList.cpp index 060732f421f8..bc63a41c90d1 100644 --- a/lldb/source/Core/ModuleList.cpp +++ b/lldb/source/Core/ModuleList.cpp @@ -844,8 +844,6 @@ public: } bool RemoveIfOrphaned(const Module *module_ptr) { - if (!module_ptr) - return false; std::lock_guard<std::recursive_mutex> guard(GetMutex()); RemoveFromMap(*module_ptr, /*if_orphaned=*/true); return m_list.RemoveIfOrphaned(module_ptr); @@ -982,7 +980,7 @@ private: }; struct SharedModuleListInfo { - SharedModuleList module_list; + ModuleList module_list; ModuleListProperties module_list_properties; }; } @@ -1000,7 +998,7 @@ static SharedModuleListInfo &GetSharedModuleListInfo() return *g_shared_module_list_info; } -static SharedModuleList &GetSharedModuleList() { +static ModuleList &GetSharedModuleList() { return GetSharedModuleListInfo().module_list; } @@ -1010,8 +1008,8 @@ ModuleListProperties &ModuleList::GetGlobalModuleListProperties() { bool ModuleList::ModuleIsInCache(const Module *module_ptr) { if (module_ptr) { - SharedModuleList &shared_module_list = GetSharedModuleList(); - return shared_module_list.FindModule(*module_ptr).get() != nullptr; + ModuleList &shared_module_list = GetSharedModuleList(); + return shared_module_list.FindModule(module_ptr).get() != nullptr; } return false; } @@ -1034,8 +1032,9 @@ ModuleList::GetSharedModule(const ModuleSpec &module_spec, ModuleSP &module_sp, const FileSpecList *module_search_paths_ptr, llvm::SmallVectorImpl<lldb::ModuleSP> *old_modules, bool *did_create_ptr, bool always_create) { - SharedModuleList &shared_module_list = GetSharedModuleList(); - std::lock_guard<std::recursive_mutex> guard(shared_module_list.GetMutex()); + ModuleList &shared_module_list = GetSharedModuleList(); + std::lock_guard<std::recursive_mutex> guard( + shared_module_list.m_modules_mutex); char path[PATH_MAX]; Status error; diff --git a/lldb/source/Core/ProtocolServer.cpp b/lldb/source/Core/ProtocolServer.cpp index 41636cdacdec..38668f39795a 100644 --- a/lldb/source/Core/ProtocolServer.cpp +++ b/lldb/source/Core/ProtocolServer.cpp @@ -8,24 +8,29 @@ #include "lldb/Core/ProtocolServer.h" #include "lldb/Core/PluginManager.h" +#include "llvm/Support/Error.h" using namespace lldb_private; using namespace lldb; -ProtocolServer *ProtocolServer::GetOrCreate(llvm::StringRef name) { - static std::mutex g_mutex; +static std::pair<llvm::StringMap<ProtocolServerUP> &, std::mutex &> Servers() { static llvm::StringMap<ProtocolServerUP> g_protocol_server_instances; + static std::mutex g_mutex; + return {g_protocol_server_instances, g_mutex}; +} + +ProtocolServer *ProtocolServer::GetOrCreate(llvm::StringRef name) { + auto [protocol_server_instances, mutex] = Servers(); - std::lock_guard<std::mutex> guard(g_mutex); + std::lock_guard<std::mutex> guard(mutex); - auto it = g_protocol_server_instances.find(name); - if (it != g_protocol_server_instances.end()) + auto it = protocol_server_instances.find(name); + if (it != protocol_server_instances.end()) return it->second.get(); if (ProtocolServerCreateInstance create_callback = PluginManager::GetProtocolCreateCallbackForPluginName(name)) { - auto pair = - g_protocol_server_instances.try_emplace(name, create_callback()); + auto pair = protocol_server_instances.try_emplace(name, create_callback()); return pair.first->second.get(); } @@ -45,3 +50,18 @@ std::vector<llvm::StringRef> ProtocolServer::GetSupportedProtocols() { return supported_protocols; } + +llvm::Error ProtocolServer::Terminate() { + llvm::Error error = llvm::Error::success(); + + auto [protocol_server_instances, mutex] = Servers(); + std::lock_guard<std::mutex> guard(mutex); + for (auto &instance : protocol_server_instances) { + if (llvm::Error instance_error = instance.second->Stop()) + error = llvm::joinErrors(std::move(error), std::move(instance_error)); + } + + protocol_server_instances.clear(); + + return error; +} diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp index ed4e4e4e341a..332cf2c86024 100644 --- a/lldb/source/Expression/DWARFExpression.cpp +++ b/lldb/source/Expression/DWARFExpression.cpp @@ -67,7 +67,8 @@ void DWARFExpression::UpdateValue(uint64_t const_value, } void DWARFExpression::DumpLocation(Stream *s, lldb::DescriptionLevel level, - ABI *abi) const { + ABI *abi, + llvm::DIDumpOptions options) const { auto *MCRegInfo = abi ? &abi->GetMCRegisterInfo() : nullptr; auto GetRegName = [&MCRegInfo](uint64_t DwarfRegNum, bool IsEH) -> llvm::StringRef { @@ -79,10 +80,9 @@ void DWARFExpression::DumpLocation(Stream *s, lldb::DescriptionLevel level, return llvm::StringRef(RegName); return {}; }; - llvm::DIDumpOptions DumpOpts; - DumpOpts.GetNameForDWARFReg = GetRegName; + options.GetNameForDWARFReg = GetRegName; llvm::DWARFExpression E(m_data.GetAsLLVM(), m_data.GetAddressByteSize()); - llvm::printDwarfExpression(&E, s->AsRawOstream(), DumpOpts, nullptr); + llvm::printDwarfExpression(&E, s->AsRawOstream(), options, nullptr); } RegisterKind DWARFExpression::GetRegisterKind() const { return m_reg_kind; } @@ -2078,7 +2078,7 @@ llvm::Expected<Value> DWARFExpression::Evaluate( case DW_OP_implicit_pointer: { dwarf4_location_description_kind = Implicit; - return llvm::createStringError("Could not evaluate %s.", + return llvm::createStringError("could not evaluate %s", DW_OP_value_to_name(op)); } diff --git a/lldb/source/Expression/DWARFExpressionList.cpp b/lldb/source/Expression/DWARFExpressionList.cpp index ef7333518f00..91c174013603 100644 --- a/lldb/source/Expression/DWARFExpressionList.cpp +++ b/lldb/source/Expression/DWARFExpressionList.cpp @@ -254,7 +254,7 @@ llvm::Expected<Value> DWARFExpressionList::Evaluate( } if (!pc.IsValid()) { - return llvm::createStringError("Invalid PC in frame."); + return llvm::createStringError("invalid PC in frame"); } addr_t pc_load_addr = pc.GetLoadAddress(exe_ctx->GetTargetPtr()); const DWARFExpression *entry = diff --git a/lldb/source/Expression/Expression.cpp b/lldb/source/Expression/Expression.cpp index 796851ff15ca..16ecb1d7deef 100644 --- a/lldb/source/Expression/Expression.cpp +++ b/lldb/source/Expression/Expression.cpp @@ -34,10 +34,10 @@ Expression::Expression(ExecutionContextScope &exe_scope) llvm::Expected<FunctionCallLabel> lldb_private::FunctionCallLabel::fromString(llvm::StringRef label) { - llvm::SmallVector<llvm::StringRef, 4> components; - label.split(components, ":", /*MaxSplit=*/3); + llvm::SmallVector<llvm::StringRef, 5> components; + label.split(components, ":", /*MaxSplit=*/4); - if (components.size() != 4) + if (components.size() != 5) return llvm::createStringError("malformed function call label."); if (components[0] != FunctionCallLabelPrefix) @@ -45,8 +45,10 @@ lldb_private::FunctionCallLabel::fromString(llvm::StringRef label) { "expected function call label prefix '{0}' but found '{1}' instead.", FunctionCallLabelPrefix, components[0])); - llvm::StringRef module_label = components[1]; - llvm::StringRef die_label = components[2]; + llvm::StringRef discriminator = components[1]; + llvm::StringRef module_label = components[2]; + llvm::StringRef die_label = components[3]; + llvm::StringRef lookup_name = components[4]; lldb::user_id_t module_id = 0; if (!llvm::to_integer(module_label, module_id)) @@ -58,20 +60,23 @@ lldb_private::FunctionCallLabel::fromString(llvm::StringRef label) { return llvm::createStringError( llvm::formatv("failed to parse symbol ID from '{0}'.", die_label)); - return FunctionCallLabel{/*.module_id=*/module_id, + return FunctionCallLabel{/*.discriminator=*/discriminator, + /*.module_id=*/module_id, /*.symbol_id=*/die_id, - /*.lookup_name=*/components[3]}; + /*.lookup_name=*/lookup_name}; } std::string lldb_private::FunctionCallLabel::toString() const { - return llvm::formatv("{0}:{1:x}:{2:x}:{3}", FunctionCallLabelPrefix, - module_id, symbol_id, lookup_name) + return llvm::formatv("{0}:{1}:{2:x}:{3:x}:{4}", FunctionCallLabelPrefix, + discriminator, module_id, symbol_id, lookup_name) .str(); } void llvm::format_provider<FunctionCallLabel>::format( const FunctionCallLabel &label, raw_ostream &OS, StringRef Style) { - OS << llvm::formatv("FunctionCallLabel{ module_id: {0:x}, symbol_id: {1:x}, " - "lookup_name: {2} }", - label.module_id, label.symbol_id, label.lookup_name); + OS << llvm::formatv("FunctionCallLabel{ discriminator: {0}, module_id: " + "{1:x}, symbol_id: {2:x}, " + "lookup_name: {3} }", + label.discriminator, label.module_id, label.symbol_id, + label.lookup_name); } diff --git a/lldb/source/Expression/IRExecutionUnit.cpp b/lldb/source/Expression/IRExecutionUnit.cpp index d557084acb74..25d4a87b89ef 100644 --- a/lldb/source/Expression/IRExecutionUnit.cpp +++ b/lldb/source/Expression/IRExecutionUnit.cpp @@ -543,62 +543,7 @@ lldb::SectionType IRExecutionUnit::GetSectionTypeFromSectionName( else if (name.starts_with("__debug_") || name.starts_with(".debug_")) { const uint32_t name_idx = name[0] == '_' ? 8 : 7; llvm::StringRef dwarf_name(name.substr(name_idx)); - switch (dwarf_name[0]) { - case 'a': - if (dwarf_name == "abbrev") - sect_type = lldb::eSectionTypeDWARFDebugAbbrev; - else if (dwarf_name == "aranges") - sect_type = lldb::eSectionTypeDWARFDebugAranges; - else if (dwarf_name == "addr") - sect_type = lldb::eSectionTypeDWARFDebugAddr; - break; - - case 'f': - if (dwarf_name == "frame") - sect_type = lldb::eSectionTypeDWARFDebugFrame; - break; - - case 'i': - if (dwarf_name == "info") - sect_type = lldb::eSectionTypeDWARFDebugInfo; - break; - - case 'l': - if (dwarf_name == "line") - sect_type = lldb::eSectionTypeDWARFDebugLine; - else if (dwarf_name == "loc") - sect_type = lldb::eSectionTypeDWARFDebugLoc; - else if (dwarf_name == "loclists") - sect_type = lldb::eSectionTypeDWARFDebugLocLists; - break; - - case 'm': - if (dwarf_name == "macinfo") - sect_type = lldb::eSectionTypeDWARFDebugMacInfo; - break; - - case 'p': - if (dwarf_name == "pubnames") - sect_type = lldb::eSectionTypeDWARFDebugPubNames; - else if (dwarf_name == "pubtypes") - sect_type = lldb::eSectionTypeDWARFDebugPubTypes; - break; - - case 's': - if (dwarf_name == "str") - sect_type = lldb::eSectionTypeDWARFDebugStr; - else if (dwarf_name == "str_offsets") - sect_type = lldb::eSectionTypeDWARFDebugStrOffsets; - break; - - case 'r': - if (dwarf_name == "ranges") - sect_type = lldb::eSectionTypeDWARFDebugRanges; - break; - - default: - break; - } + sect_type = ObjectFile::GetDWARFSectionTypeFromName(dwarf_name); } else if (name.starts_with("__apple_") || name.starts_with(".apple_")) sect_type = lldb::eSectionTypeInvalid; else if (name == "__objc_imageinfo") @@ -778,7 +723,7 @@ private: /// Returns address of the function referred to by the special function call /// label \c label. static llvm::Expected<lldb::addr_t> -ResolveFunctionCallLabel(const FunctionCallLabel &label, +ResolveFunctionCallLabel(FunctionCallLabel &label, const lldb_private::SymbolContext &sc, bool &symbol_was_missing_weak) { symbol_was_missing_weak = false; diff --git a/lldb/source/Expression/IRMemoryMap.cpp b/lldb/source/Expression/IRMemoryMap.cpp index 150699352a2e..f978217fa8f2 100644 --- a/lldb/source/Expression/IRMemoryMap.cpp +++ b/lldb/source/Expression/IRMemoryMap.cpp @@ -637,10 +637,19 @@ void IRMemoryMap::WriteScalarToMemory(lldb::addr_t process_address, } void IRMemoryMap::WritePointerToMemory(lldb::addr_t process_address, - lldb::addr_t address, Status &error) { + lldb::addr_t pointer, Status &error) { error.Clear(); - Scalar scalar(address); + /// Only ask the Process to fix `pointer` if the address belongs to the + /// process. An address belongs to the process if the Allocation policy is not + /// eAllocationPolicyHostOnly. + auto it = FindAllocation(pointer, 1); + if (it == m_allocations.end() || + it->second.m_policy != AllocationPolicy::eAllocationPolicyHostOnly) + if (auto process_sp = GetProcessWP().lock()) + pointer = process_sp->FixAnyAddress(pointer); + + Scalar scalar(pointer); WriteScalarToMemory(process_address, scalar, GetAddressByteSize(), error); } diff --git a/lldb/source/Host/common/Editline.cpp b/lldb/source/Host/common/Editline.cpp index 5ed30fbb231d..1b1922e71076 100644 --- a/lldb/source/Host/common/Editline.cpp +++ b/lldb/source/Host/common/Editline.cpp @@ -10,10 +10,8 @@ #include <iomanip> #include <optional> -#include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/Editline.h" -#include "lldb/Host/FileSystem.h" -#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" #include "lldb/Host/StreamFile.h" #include "lldb/Utility/AnsiTerminal.h" #include "lldb/Utility/CompletionRequest.h" @@ -100,11 +98,6 @@ bool IsOnlySpaces(const EditLineStringType &content) { return true; } -static size_t ColumnWidth(llvm::StringRef str) { - std::string stripped = ansi::StripAnsiTerminalCodes(str); - return llvm::sys::locale::columnWidth(stripped); -} - static int GetOperation(HistoryOperation op) { // The naming used by editline for the history operations is counter // intuitive to how it's used in LLDB's editline implementation. @@ -219,20 +212,19 @@ private: const char *GetHistoryFilePath() { // Compute the history path lazily. if (m_path.empty() && m_history && !m_prefix.empty()) { - llvm::SmallString<128> lldb_history_file; - FileSystem::Instance().GetHomeDirectory(lldb_history_file); - llvm::sys::path::append(lldb_history_file, ".lldb"); + FileSpec lldb_dir = HostInfo::GetUserLLDBDir(); // LLDB stores its history in ~/.lldb/. If for some reason this directory // isn't writable or cannot be created, history won't be available. - if (!llvm::sys::fs::create_directory(lldb_history_file)) { + if (!llvm::sys::fs::create_directory(lldb_dir.GetPath())) { #if LLDB_EDITLINE_USE_WCHAR std::string filename = m_prefix + "-widehistory"; #else std::string filename = m_prefix + "-history"; #endif - llvm::sys::path::append(lldb_history_file, filename); - m_path = std::string(lldb_history_file.str()); + FileSpec lldb_history_file = + lldb_dir.CopyByAppendingPathComponent(filename); + m_path = lldb_history_file.GetPath(); } } @@ -332,8 +324,8 @@ std::string Editline::PromptForIndex(int line_index) { if (m_set_continuation_prompt.length() > 0) { continuation_prompt = m_set_continuation_prompt; // Ensure that both prompts are the same length through space padding - const size_t prompt_width = ColumnWidth(prompt); - const size_t cont_prompt_width = ColumnWidth(continuation_prompt); + const size_t prompt_width = ansi::ColumnWidth(prompt); + const size_t cont_prompt_width = ansi::ColumnWidth(continuation_prompt); const size_t padded_prompt_width = std::max(prompt_width, cont_prompt_width); if (prompt_width < padded_prompt_width) @@ -358,7 +350,9 @@ void Editline::SetCurrentLine(int line_index) { m_current_prompt = PromptForIndex(line_index); } -size_t Editline::GetPromptWidth() { return ColumnWidth(PromptForIndex(0)); } +size_t Editline::GetPromptWidth() { + return ansi::ColumnWidth(PromptForIndex(0)); +} bool Editline::IsEmacs() { const char *editor; @@ -448,7 +442,7 @@ void Editline::DisplayInput(int firstIndex) { int Editline::CountRowsForLine(const EditLineStringType &content) { std::string prompt = PromptForIndex(0); // Prompt width is constant during an edit session - int line_length = (int)(content.length() + ColumnWidth(prompt)); + int line_length = (int)(content.length() + ansi::ColumnWidth(prompt)); return (line_length / m_terminal_width) + 1; } diff --git a/lldb/source/Host/common/File.cpp b/lldb/source/Host/common/File.cpp index 23b6dc9fe850..8fd1ca069dc0 100644 --- a/lldb/source/Host/common/File.cpp +++ b/lldb/source/Host/common/File.cpp @@ -36,6 +36,7 @@ #include "llvm/Support/Errno.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Process.h" +#include "llvm/Support/raw_ostream.h" using namespace lldb; using namespace lldb_private; @@ -247,6 +248,32 @@ uint32_t File::GetPermissions(Status &error) const { return file_stats.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); } +NativeFile::NativeFile() = default; + +NativeFile::NativeFile(FILE *fh, bool transfer_ownership) + : m_stream(fh), m_own_stream(transfer_ownership) { +#ifdef _WIN32 + // In order to properly display non ASCII characters in Windows, we need to + // use Windows APIs to print to the console. This is only required if the + // stream outputs to a console. + int fd = _fileno(fh); + is_windows_console = + ::GetFileType((HANDLE)::_get_osfhandle(fd)) == FILE_TYPE_CHAR; +#endif +} + +NativeFile::NativeFile(int fd, OpenOptions options, bool transfer_ownership) + : m_descriptor(fd), m_own_descriptor(transfer_ownership), + m_options(options) { +#ifdef _WIN32 + // In order to properly display non ASCII characters in Windows, we need to + // use Windows APIs to print to the console. This is only required if the + // file outputs to a console. + is_windows_console = + ::GetFileType((HANDLE)::_get_osfhandle(fd)) == FILE_TYPE_CHAR; +#endif +} + bool NativeFile::IsValid() const { std::scoped_lock<std::mutex, std::mutex> lock(m_descriptor_mutex, m_stream_mutex); return DescriptorIsValidUnlocked() || StreamIsValidUnlocked(); @@ -629,6 +656,13 @@ Status NativeFile::Write(const void *buf, size_t &num_bytes) { } if (ValueGuard stream_guard = StreamIsValid()) { +#ifdef _WIN32 + if (is_windows_console) { + llvm::raw_fd_ostream(_fileno(m_stream), false) + .write((char *)buf, num_bytes); + return error; + } +#endif bytes_written = ::fwrite(buf, 1, num_bytes, m_stream); if (bytes_written == 0) { diff --git a/lldb/source/Host/common/HostInfoBase.cpp b/lldb/source/Host/common/HostInfoBase.cpp index 89dfe4a9e9ba..a02ac77df66a 100644 --- a/lldb/source/Host/common/HostInfoBase.cpp +++ b/lldb/source/Host/common/HostInfoBase.cpp @@ -61,6 +61,10 @@ struct HostInfoBaseFields { FileSpec m_lldb_clang_resource_dir; llvm::once_flag m_lldb_system_plugin_dir_once; FileSpec m_lldb_system_plugin_dir; + llvm::once_flag m_lldb_user_home_dir_once; + FileSpec m_lldb_user_home_dir; + llvm::once_flag m_lldb_user_lldb_dir_once; + FileSpec m_lldb_user_lldb_dir; llvm::once_flag m_lldb_user_plugin_dir_once; FileSpec m_lldb_user_plugin_dir; llvm::once_flag m_lldb_process_tmp_dir_once; @@ -161,6 +165,26 @@ FileSpec HostInfoBase::GetSystemPluginDir() { return g_fields->m_lldb_system_plugin_dir; } +FileSpec HostInfoBase::GetUserHomeDir() { + llvm::call_once(g_fields->m_lldb_user_home_dir_once, []() { + if (!HostInfo::ComputeUserHomeDirectory(g_fields->m_lldb_user_home_dir)) + g_fields->m_lldb_user_home_dir = FileSpec(); + LLDB_LOG(GetLog(LLDBLog::Host), "user home dir -> `{0}`", + g_fields->m_lldb_user_home_dir); + }); + return g_fields->m_lldb_user_home_dir; +} + +FileSpec HostInfoBase::GetUserLLDBDir() { + llvm::call_once(g_fields->m_lldb_user_lldb_dir_once, []() { + if (!HostInfo::ComputeUserLLDBHomeDirectory(g_fields->m_lldb_user_lldb_dir)) + g_fields->m_lldb_user_lldb_dir = FileSpec(); + LLDB_LOG(GetLog(LLDBLog::Host), "user lldb home dir -> `{0}`", + g_fields->m_lldb_user_lldb_dir); + }); + return g_fields->m_lldb_user_lldb_dir; +} + FileSpec HostInfoBase::GetUserPluginDir() { llvm::call_once(g_fields->m_lldb_user_plugin_dir_once, []() { if (!HostInfo::ComputeUserPluginsDirectory( @@ -316,6 +340,20 @@ bool HostInfoBase::ComputeSystemPluginsDirectory(FileSpec &file_spec) { return false; } +bool HostInfoBase::ComputeUserHomeDirectory(FileSpec &file_spec) { + FileSpec temp_file("~"); + FileSystem::Instance().Resolve(temp_file); + file_spec.SetDirectory(temp_file.GetPathAsConstString()); + return true; +} + +bool HostInfoBase::ComputeUserLLDBHomeDirectory(FileSpec &file_spec) { + FileSpec home_dir_spec = GetUserHomeDir(); + home_dir_spec.AppendPathComponent(".lldb"); + file_spec.SetDirectory(home_dir_spec.GetPathAsConstString()); + return true; +} + bool HostInfoBase::ComputeUserPluginsDirectory(FileSpec &file_spec) { // TODO(zturner): Figure out how to compute the user plugins directory for // all platforms. diff --git a/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm b/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm index 61f94190c956..79e1322a870e 100644 --- a/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm +++ b/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm @@ -39,7 +39,7 @@ #include <Foundation/Foundation.h> #include <mach-o/dyld.h> #if defined(MAC_OS_X_VERSION_MIN_REQUIRED) && \ - MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_12_0 + MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_12_0 #if __has_include(<mach-o/dyld_introspection.h>) #include <mach-o/dyld_introspection.h> #define SDK_HAS_NEW_DYLD_INTROSPECTION_SPIS @@ -78,8 +78,8 @@ std::optional<std::string> HostInfoMacOSX::GetOSBuildString() { static void ParseOSVersion(llvm::VersionTuple &version, NSString *Key) { @autoreleasepool { NSDictionary *version_info = - [NSDictionary dictionaryWithContentsOfFile: - @"/System/Library/CoreServices/SystemVersion.plist"]; + [NSDictionary dictionaryWithContentsOfFile: + @"/System/Library/CoreServices/SystemVersion.plist"]; NSString *version_value = [version_info objectForKey: Key]; const char *version_str = [version_value UTF8String]; version.tryParse(version_str); @@ -225,9 +225,9 @@ bool HostInfoMacOSX::ComputeSystemPluginsDirectory(FileSpec &file_spec) { } bool HostInfoMacOSX::ComputeUserPluginsDirectory(FileSpec &file_spec) { - FileSpec temp_file("~/Library/Application Support/LLDB/PlugIns"); - FileSystem::Instance().Resolve(temp_file); - file_spec.SetDirectory(temp_file.GetPathAsConstString()); + FileSpec home_dir_spec = GetUserHomeDir(); + home_dir_spec.AppendPathComponent("Library/Application Support/LLDB/PlugIns"); + file_spec.SetDirectory(home_dir_spec.GetPathAsConstString()); return true; } diff --git a/lldb/source/Host/windows/Host.cpp b/lldb/source/Host/windows/Host.cpp index 4277b8edb38e..e8973a3fb937 100644 --- a/lldb/source/Host/windows/Host.cpp +++ b/lldb/source/Host/windows/Host.cpp @@ -22,10 +22,8 @@ #include "lldb/Utility/StreamString.h" #include "lldb/Utility/StructuredData.h" -#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/ConvertUTF.h" -#include "llvm/Support/ManagedStatic.h" // Windows includes #include <tlhelp32.h> @@ -308,52 +306,28 @@ Environment Host::GetEnvironment() { return env; } -/// Manages the lifecycle of a Windows Event's Source. -/// The destructor will call DeregisterEventSource. -/// This class is meant to be used with \ref llvm::ManagedStatic. -class WindowsEventLog { -public: - WindowsEventLog() : handle(RegisterEventSource(nullptr, L"lldb")) {} - - ~WindowsEventLog() { - if (handle) - DeregisterEventSource(handle); - } - - HANDLE GetHandle() const { return handle; } - -private: - HANDLE handle; -}; - -static llvm::ManagedStatic<WindowsEventLog> event_log; - void Host::SystemLog(Severity severity, llvm::StringRef message) { if (message.empty()) return; - HANDLE h = event_log->GetHandle(); - if (!h) - return; - - llvm::SmallVector<wchar_t, 1> argsUTF16; - if (UTF8ToUTF16(message.str(), argsUTF16)) - return; + std::string log_msg; + llvm::raw_string_ostream stream(log_msg); - WORD event_type; switch (severity) { case lldb::eSeverityWarning: - event_type = EVENTLOG_WARNING_TYPE; + stream << "[Warning] "; break; case lldb::eSeverityError: - event_type = EVENTLOG_ERROR_TYPE; + stream << "[Error] "; break; case lldb::eSeverityInfo: default: - event_type = EVENTLOG_INFORMATION_TYPE; + stream << "[Info] "; + break; } - LPCWSTR messages[1] = {argsUTF16.data()}; - ReportEventW(h, event_type, 0, 0, nullptr, std::size(messages), 0, messages, - nullptr); + stream << message; + stream.flush(); + + OutputDebugStringA(log_msg.c_str()); } diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp index 650b754fd8ac..d909c5650c95 100644 --- a/lldb/source/Interpreter/CommandInterpreter.cpp +++ b/lldb/source/Interpreter/CommandInterpreter.cpp @@ -52,6 +52,7 @@ #include "lldb/Core/Telemetry.h" #include "lldb/Host/StreamFile.h" #include "lldb/Utility/ErrorMessages.h" +#include "lldb/Utility/FileSpec.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/State.h" @@ -1801,13 +1802,13 @@ CommandObject *CommandInterpreter::BuildAliasResult( // Make sure we aren't going outside the bounds of the cmd string: if (strpos < start_fudge) { - result.AppendError("Unmatched quote at command beginning."); + result.AppendError("unmatched quote at command beginning"); return nullptr; } llvm::StringRef arg_text = entry.ref(); if (strpos - start_fudge + arg_text.size() + len_fudge > raw_input_string.size()) { - result.AppendError("Unmatched quote at command end."); + result.AppendError("unmatched quote at command end"); return nullptr; } raw_input_string = raw_input_string.erase( @@ -2090,7 +2091,7 @@ bool CommandInterpreter::HandleCommand(const char *command_line, command_string = command_line; original_command_string = command_line; if (m_repeat_command.empty()) { - result.AppendError("No auto repeat."); + result.AppendError("no auto repeat"); return false; } @@ -2502,22 +2503,18 @@ int CommandInterpreter::GetOptionArgumentPosition(const char *in_string) { return position; } -static void GetHomeInitFile(llvm::SmallVectorImpl<char> &init_file, - llvm::StringRef suffix = {}) { +static void GetHomeInitFile(FileSpec &init_file, llvm::StringRef suffix = {}) { std::string init_file_name = ".lldbinit"; if (!suffix.empty()) { init_file_name.append("-"); init_file_name.append(suffix.str()); } - FileSystem::Instance().GetHomeDirectory(init_file); - llvm::sys::path::append(init_file, init_file_name); - - FileSystem::Instance().Resolve(init_file); + init_file = + HostInfo::GetUserHomeDir().CopyByAppendingPathComponent(init_file_name); } -static void GetHomeREPLInitFile(llvm::SmallVectorImpl<char> &init_file, - LanguageType language) { +static void GetHomeREPLInitFile(FileSpec &init_file, LanguageType language) { if (language == eLanguageTypeUnknown) { LanguageSet repl_languages = Language::GetLanguagesSupportingREPLs(); if (auto main_repl_language = repl_languages.GetSingularLanguage()) @@ -2531,9 +2528,9 @@ static void GetHomeREPLInitFile(llvm::SmallVectorImpl<char> &init_file, llvm::Twine(Language::GetNameForLanguageType(language)) + llvm::Twine("-repl")) .str(); - FileSystem::Instance().GetHomeDirectory(init_file); - llvm::sys::path::append(init_file, init_file_name); - FileSystem::Instance().Resolve(init_file); + + init_file = + HostInfo::GetUserHomeDir().CopyByAppendingPathComponent(init_file_name); } static void GetCwdInitFile(llvm::SmallVectorImpl<char> &init_file) { @@ -2588,10 +2585,10 @@ void CommandInterpreter::SourceInitFileCwd(CommandReturnObject &result) { SourceInitFile(FileSpec(init_file.str()), result); break; case eLoadCWDlldbinitWarn: { - llvm::SmallString<128> home_init_file; + FileSpec home_init_file; GetHomeInitFile(home_init_file); if (llvm::sys::path::parent_path(init_file) == - llvm::sys::path::parent_path(home_init_file)) { + llvm::sys::path::parent_path(home_init_file.GetPath())) { result.SetStatus(eReturnStatusSuccessFinishNoResult); } else { result.AppendError(InitFileWarning); @@ -2611,24 +2608,24 @@ void CommandInterpreter::SourceInitFileHome(CommandReturnObject &result, return; } - llvm::SmallString<128> init_file; + FileSpec init_file; if (is_repl) GetHomeREPLInitFile(init_file, GetDebugger().GetREPLLanguage()); - if (init_file.empty()) + if (init_file.GetPath().empty()) GetHomeInitFile(init_file); if (!m_skip_app_init_files) { llvm::StringRef program_name = HostInfo::GetProgramFileSpec().GetFilename().GetStringRef(); - llvm::SmallString<128> program_init_file; + FileSpec program_init_file; GetHomeInitFile(program_init_file, program_name); if (FileSystem::Instance().Exists(program_init_file)) init_file = program_init_file; } - SourceInitFile(FileSpec(init_file.str()), result); + SourceInitFile(init_file, result); } void CommandInterpreter::SourceInitFileGlobal(CommandReturnObject &result) { diff --git a/lldb/source/Interpreter/CommandObject.cpp b/lldb/source/Interpreter/CommandObject.cpp index 129646ebddb9..22eeceb2ef7e 100644 --- a/lldb/source/Interpreter/CommandObject.cpp +++ b/lldb/source/Interpreter/CommandObject.cpp @@ -217,7 +217,7 @@ bool CommandObject::CheckRequirements(CommandReturnObject &result) { if (process == nullptr) { // A process that is not running is considered paused. if (GetFlags().Test(eCommandProcessMustBeLaunched)) { - result.AppendError("Process must exist."); + result.AppendError("process must exist"); return false; } } else { @@ -236,7 +236,7 @@ bool CommandObject::CheckRequirements(CommandReturnObject &result) { case eStateExited: case eStateUnloaded: if (GetFlags().Test(eCommandProcessMustBeLaunched)) { - result.AppendError("Process must be launched."); + result.AppendError("process must be launched"); return false; } break; @@ -255,7 +255,7 @@ bool CommandObject::CheckRequirements(CommandReturnObject &result) { if (GetFlags().Test(eCommandProcessMustBeTraced)) { Target *target = m_exe_ctx.GetTargetPtr(); if (target && !target->GetTrace()) { - result.AppendError("Process is not being traced."); + result.AppendError("process is not being traced"); return false; } } @@ -359,7 +359,8 @@ bool CommandObject::HelpTextContainsWord(llvm::StringRef search_word, StreamString usage_help; GetOptions()->GenerateOptionUsage( usage_help, *this, - GetCommandInterpreter().GetDebugger().GetTerminalWidth()); + GetCommandInterpreter().GetDebugger().GetTerminalWidth(), + GetCommandInterpreter().GetDebugger().GetUseColor()); if (!usage_help.Empty()) { llvm::StringRef usage_text = usage_help.GetString(); if (usage_text.contains_insensitive(search_word)) @@ -672,7 +673,8 @@ void CommandObject::GenerateHelpText(Stream &output_strm) { if (options != nullptr) { options->GenerateOptionUsage( output_strm, *this, - GetCommandInterpreter().GetDebugger().GetTerminalWidth()); + GetCommandInterpreter().GetDebugger().GetTerminalWidth(), + GetCommandInterpreter().GetDebugger().GetUseColor()); } llvm::StringRef long_help = GetHelpLong(); if (!long_help.empty()) { diff --git a/lldb/source/Interpreter/Options.cpp b/lldb/source/Interpreter/Options.cpp index 4cf68db46615..cae617813d2f 100644 --- a/lldb/source/Interpreter/Options.cpp +++ b/lldb/source/Interpreter/Options.cpp @@ -19,6 +19,7 @@ #include "lldb/Interpreter/CommandObject.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Target/Target.h" +#include "lldb/Utility/AnsiTerminal.h" #include "lldb/Utility/DiagnosticsRendering.h" #include "lldb/Utility/StreamString.h" #include "llvm/ADT/STLExtras.h" @@ -261,7 +262,8 @@ Option *Options::GetLongOptions() { void Options::OutputFormattedUsageText(Stream &strm, const OptionDefinition &option_def, - uint32_t output_max_columns) { + uint32_t output_max_columns, + bool use_color) { std::string actual_text; if (option_def.validator) { const char *condition = option_def.validator->ShortConditionString(); @@ -271,14 +273,16 @@ void Options::OutputFormattedUsageText(Stream &strm, actual_text.append("] "); } } - actual_text.append(option_def.usage_text); + actual_text.append( + ansi::FormatAnsiTerminalCodes(option_def.usage_text, use_color)); + const size_t visible_length = ansi::ColumnWidth(actual_text); // Will it all fit on one line? - if (static_cast<uint32_t>(actual_text.length() + strm.GetIndentLevel()) < + if (static_cast<uint32_t>(visible_length + strm.GetIndentLevel()) < output_max_columns) { // Output it as a single line. - strm.Indent(actual_text); + strm.Indent(ansi::FormatAnsiTerminalCodes(actual_text, use_color)); strm.EOL(); } else { // We need to break it up into multiple lines. @@ -286,7 +290,7 @@ void Options::OutputFormattedUsageText(Stream &strm, int text_width = output_max_columns - strm.GetIndentLevel() - 1; int start = 0; int end = start; - int final_end = actual_text.length(); + int final_end = visible_length; int sub_len; while (end < final_end) { @@ -312,7 +316,8 @@ void Options::OutputFormattedUsageText(Stream &strm, strm.Indent(); assert(start < final_end); assert(start + sub_len <= final_end); - strm.Write(actual_text.c_str() + start, sub_len); + strm.PutCString(ansi::FormatAnsiTerminalCodes( + llvm::StringRef(actual_text.c_str() + start, sub_len), use_color)); start = end + 1; } strm.EOL(); @@ -385,7 +390,7 @@ static bool PrintOption(const OptionDefinition &opt_def, } void Options::GenerateOptionUsage(Stream &strm, CommandObject &cmd, - uint32_t screen_width) { + uint32_t screen_width, bool use_color) { auto opt_defs = GetDefinitions(); const uint32_t save_indent_level = strm.GetIndentLevel(); llvm::StringRef name = cmd.GetCommandName(); @@ -527,7 +532,7 @@ void Options::GenerateOptionUsage(Stream &strm, CommandObject &cmd, strm.IndentMore(5); if (opt_def.usage_text) - OutputFormattedUsageText(strm, opt_def, screen_width); + OutputFormattedUsageText(strm, opt_def, screen_width, use_color); if (!opt_def.enum_values.empty()) { strm.Indent(); strm.Printf("Values: "); @@ -628,6 +633,7 @@ bool Options::HandleOptionCompletion(CompletionRequest &request, auto opt_defs = GetDefinitions(); llvm::StringRef cur_opt_str = request.GetCursorArgumentPrefix(); + const bool use_color = interpreter.GetDebugger().GetUseColor(); for (size_t i = 0; i < opt_element_vector.size(); i++) { size_t opt_pos = static_cast<size_t>(opt_element_vector[i].opt_pos); @@ -647,7 +653,8 @@ bool Options::HandleOptionCompletion(CompletionRequest &request, if (!def.short_option) continue; opt_str[1] = def.short_option; - request.AddCompletion(opt_str, def.usage_text); + request.AddCompletion(opt_str, ansi::FormatAnsiTerminalCodes( + def.usage_text, use_color)); } return true; @@ -659,7 +666,8 @@ bool Options::HandleOptionCompletion(CompletionRequest &request, full_name.erase(full_name.begin() + 2, full_name.end()); full_name.append(def.long_option); - request.AddCompletion(full_name, def.usage_text); + request.AddCompletion(full_name, ansi::FormatAnsiTerminalCodes( + def.usage_text, use_color)); } return true; } else if (opt_defs_index != OptionArgElement::eUnrecognizedArg) { @@ -670,7 +678,9 @@ bool Options::HandleOptionCompletion(CompletionRequest &request, const OptionDefinition &opt = opt_defs[opt_defs_index]; llvm::StringRef long_option = opt.long_option; if (cur_opt_str.starts_with("--") && cur_opt_str != long_option) { - request.AddCompletion("--" + long_option.str(), opt.usage_text); + request.AddCompletion( + "--" + long_option.str(), + ansi::FormatAnsiTerminalCodes(opt.usage_text, use_color)); return true; } else request.AddCompletion(request.GetCursorArgumentPrefix()); @@ -686,7 +696,9 @@ bool Options::HandleOptionCompletion(CompletionRequest &request, for (auto &def : opt_defs) { llvm::StringRef long_option(def.long_option); if (long_option.starts_with(cur_opt_str)) - request.AddCompletion("--" + long_option.str(), def.usage_text); + request.AddCompletion( + "--" + long_option.str(), + ansi::FormatAnsiTerminalCodes(def.usage_text, use_color)); } } return true; @@ -1282,7 +1294,7 @@ llvm::Expected<Args> Options::Parse(const Args &args, Status error; Option *long_options = GetLongOptions(); if (long_options == nullptr) { - return llvm::createStringError("Invalid long options."); + return llvm::createStringError("invalid long options"); } std::string short_options = BuildShortOptions(long_options); diff --git a/lldb/source/Plugins/ABI/ARM/ABISysV_arm.cpp b/lldb/source/Plugins/ABI/ARM/ABISysV_arm.cpp index 2bcb2c0de97a..bb0c4ba3f1b5 100644 --- a/lldb/source/Plugins/ABI/ARM/ABISysV_arm.cpp +++ b/lldb/source/Plugins/ABI/ARM/ABISysV_arm.cpp @@ -1921,6 +1921,13 @@ UnwindPlanSP ABISysV_arm::CreateFunctionEntryUnwindPlan() { UnwindPlanSP ABISysV_arm::CreateDefaultUnwindPlan() { // TODO: Handle thumb + // If we had a Target argument, could at least check + // target.GetArchitecture().GetTriple().isArmMClass() + // which is always thumb. + // To handle thumb properly, we'd need to fetch the current + // CPSR state at unwind time to tell if the processor is + // in thumb mode in this stack frame. There's no way to + // express something like that in an UnwindPlan today. uint32_t fp_reg_num = dwarf_r11; uint32_t pc_reg_num = dwarf_pc; diff --git a/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.cpp b/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.cpp index 81c72122cb7e..721c4bcfe634 100644 --- a/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.cpp +++ b/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.cpp @@ -9,10 +9,18 @@ #include "Plugins/Architecture/Arm/ArchitectureArm.h" #include "Plugins/Process/Utility/ARMDefines.h" #include "Plugins/Process/Utility/InstructionUtils.h" +#include "Utility/ARM_DWARF_Registers.h" #include "lldb/Core/PluginManager.h" +#include "lldb/Symbol/UnwindPlan.h" +#include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" +#include "lldb/Target/RegisterNumber.h" #include "lldb/Target/Thread.h" +#include "lldb/Target/UnwindLLDB.h" #include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegisterValue.h" using namespace lldb_private; using namespace lldb; @@ -150,3 +158,181 @@ addr_t ArchitectureArm::GetOpcodeLoadAddress(addr_t opcode_addr, } return opcode_addr & ~(1ull); } + +// The ARM M-Profile Armv7-M Architecture Reference Manual, +// subsection "B1.5 Armv7-M exception model", see the parts +// describing "Exception entry behavior" and "Exception +// return behavior". +// When an exception happens on this processor, certain registers are +// saved below the stack pointer, the stack pointer is decremented, +// a special value is put in the link register to indicate the +// exception has been taken, and an exception handler function +// is invoked. +// +// Detect that special value in $lr, and if present, add +// unwind rules for the registers that were saved above this +// stack frame's CFA. Overwrite any register locations that +// the current_unwindplan has for these registers; they are +// not correct when we're invoked this way. +UnwindPlanSP ArchitectureArm::GetArchitectureUnwindPlan( + Thread &thread, RegisterContextUnwind *regctx, + std::shared_ptr<const UnwindPlan> current_unwindplan) { + + ProcessSP process_sp = thread.GetProcess(); + if (!process_sp) + return {}; + + const ArchSpec arch = process_sp->GetTarget().GetArchitecture(); + if (!arch.GetTriple().isArmMClass() || arch.GetAddressByteSize() != 4) + return {}; + + // Get the caller's LR value from regctx (the LR value + // at function entry to this function). + RegisterNumber ra_regnum(thread, eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_RA); + uint32_t ra_regnum_lldb = ra_regnum.GetAsKind(eRegisterKindLLDB); + + if (ra_regnum_lldb == LLDB_INVALID_REGNUM) + return {}; + + UnwindLLDB::ConcreteRegisterLocation regloc = {}; + bool got_concrete_location = false; + if (regctx->SavedLocationForRegister(ra_regnum_lldb, regloc) == + UnwindLLDB::RegisterSearchResult::eRegisterFound) { + got_concrete_location = true; + } else { + RegisterNumber pc_regnum(thread, eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_PC); + uint32_t pc_regnum_lldb = pc_regnum.GetAsKind(eRegisterKindLLDB); + if (regctx->SavedLocationForRegister(pc_regnum_lldb, regloc) == + UnwindLLDB::RegisterSearchResult::eRegisterFound) + got_concrete_location = true; + } + + if (!got_concrete_location) + return {}; + + addr_t callers_return_address = LLDB_INVALID_ADDRESS; + const RegisterInfo *reg_info = regctx->GetRegisterInfoAtIndex(ra_regnum_lldb); + if (reg_info) { + RegisterValue reg_value; + if (regctx->ReadRegisterValueFromRegisterLocation(regloc, reg_info, + reg_value)) { + callers_return_address = reg_value.GetAsUInt32(); + } + } + + if (callers_return_address == LLDB_INVALID_ADDRESS) + return {}; + + // ARMv7-M ARM says that the LR will be set to + // one of these values when an exception has taken + // place: + // if HaveFPExt() then + // if CurrentMode==Mode_Handler then + // LR = Ones(27):NOT(CONTROL.FPCA):'0001'; + // else + // LR = Ones(27):NOT(CONTROL.FPCA):'1':CONTROL.SPSEL:'01'; + // else + // if CurrentMode==Mode_Handler then + // LR = Ones(28):'0001'; + // else + // LR = Ones(29):CONTROL.SPSEL:'01'; + + // Top 27 bits are set for an exception return. + const uint32_t exception_return = -1U & ~0b11111U; + // Bit4 is 1 if only GPRs were saved. + const uint32_t gprs_only = 0b10000; + // Bit<1:0> are '01'. + const uint32_t lowbits = 0b01; + + if ((callers_return_address & exception_return) != exception_return) + return {}; + if ((callers_return_address & lowbits) != lowbits) + return {}; + + const bool fp_regs_saved = !(callers_return_address & gprs_only); + + const RegisterKind plan_regkind = current_unwindplan->GetRegisterKind(); + UnwindPlanSP new_plan = std::make_shared<UnwindPlan>(plan_regkind); + new_plan->SetSourceName("Arm Cortex-M exception return UnwindPlan"); + new_plan->SetSourcedFromCompiler(eLazyBoolNo); + new_plan->SetUnwindPlanValidAtAllInstructions(eLazyBoolYes); + new_plan->SetUnwindPlanForSignalTrap(eLazyBoolYes); + + int stored_regs_size = fp_regs_saved ? 0x68 : 0x20; + + uint32_t gpr_regs[] = {dwarf_r0, dwarf_r1, dwarf_r2, dwarf_r3, + dwarf_r12, dwarf_lr, dwarf_pc, dwarf_cpsr}; + const int gpr_reg_count = std::size(gpr_regs); + uint32_t fpr_regs[] = {dwarf_s0, dwarf_s1, dwarf_s2, dwarf_s3, + dwarf_s4, dwarf_s5, dwarf_s6, dwarf_s7, + dwarf_s8, dwarf_s9, dwarf_s10, dwarf_s11, + dwarf_s12, dwarf_s13, dwarf_s14, dwarf_s15}; + const int fpr_reg_count = std::size(fpr_regs); + + RegisterContextSP reg_ctx_sp = thread.GetRegisterContext(); + std::vector<uint32_t> saved_regs; + for (int i = 0; i < gpr_reg_count; i++) { + uint32_t regno = gpr_regs[i]; + reg_ctx_sp->ConvertBetweenRegisterKinds(eRegisterKindDWARF, gpr_regs[i], + plan_regkind, regno); + saved_regs.push_back(regno); + } + if (fp_regs_saved) { + for (int i = 0; i < fpr_reg_count; i++) { + uint32_t regno = fpr_regs[i]; + reg_ctx_sp->ConvertBetweenRegisterKinds(eRegisterKindDWARF, fpr_regs[i], + plan_regkind, regno); + saved_regs.push_back(regno); + } + } + + addr_t cfa; + if (!regctx->GetCFA(cfa)) + return {}; + + // The CPSR value saved to stack is actually (from Armv7-M ARM) + // "XPSR<31:10>:frameptralign:XPSR<8:0>" + // Bit 9 indicates that the stack pointer was aligned (to + // an 8-byte alignment) when the exception happened, and we must + // account for that when restoring the original stack pointer value. + Status error; + uint32_t callers_xPSR = + process_sp->ReadUnsignedIntegerFromMemory(cfa + 0x1c, 4, 0, error); + const bool align_stack = callers_xPSR & (1U << 9); + uint32_t callers_sp = cfa + stored_regs_size; + if (align_stack) + callers_sp |= 4; + + Log *log = GetLog(LLDBLog::Unwind); + LLDB_LOGF(log, + "ArchitectureArm::GetArchitectureUnwindPlan found caller return " + "addr of 0x%" PRIx64 ", for frame with CFA 0x%" PRIx64 + ", fp_regs_saved %d, stored_regs_size 0x%x, align stack %d", + callers_return_address, cfa, fp_regs_saved, stored_regs_size, + align_stack); + + uint32_t sp_regnum = dwarf_sp; + reg_ctx_sp->ConvertBetweenRegisterKinds(eRegisterKindDWARF, dwarf_sp, + plan_regkind, sp_regnum); + + const int row_count = current_unwindplan->GetRowCount(); + for (int i = 0; i < row_count; i++) { + UnwindPlan::Row row = *current_unwindplan->GetRowAtIndex(i); + uint32_t offset = 0; + const size_t saved_reg_count = saved_regs.size(); + for (size_t j = 0; j < saved_reg_count; j++) { + // The locations could be set with + // SetRegisterLocationToIsConstant(regno, cfa+offset) + // expressing it in terms of CFA addr+offset - this UnwindPlan + // is only used once, with this specific CFA. I'm not sure + // which will be clearer for someone reading the unwind log. + row.SetRegisterLocationToAtCFAPlusOffset(saved_regs[j], offset, true); + offset += 4; + } + row.SetRegisterLocationToIsCFAPlusOffset(sp_regnum, callers_sp - cfa, true); + new_plan->AppendRow(row); + } + return new_plan; +} diff --git a/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.h b/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.h index f579d6b62505..52277dc5dbae 100644 --- a/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.h +++ b/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.h @@ -10,6 +10,7 @@ #define LLDB_SOURCE_PLUGINS_ARCHITECTURE_ARM_ARCHITECTUREARM_H #include "lldb/Core/Architecture.h" +#include "lldb/Target/Thread.h" namespace lldb_private { @@ -29,6 +30,10 @@ public: lldb::addr_t GetOpcodeLoadAddress(lldb::addr_t load_addr, AddressClass addr_class) const override; + lldb::UnwindPlanSP GetArchitectureUnwindPlan( + lldb_private::Thread &thread, lldb_private::RegisterContextUnwind *regctx, + std::shared_ptr<const UnwindPlan> current_unwindplan) override; + private: static std::unique_ptr<Architecture> Create(const ArchSpec &arch); ArchitectureArm() = default; diff --git a/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp b/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp index d92e16366f0a..66d0a50985be 100644 --- a/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp +++ b/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp @@ -559,7 +559,7 @@ public: lldb::InstructionControlFlowKind GetControlFlowKind(const lldb_private::ExecutionContext *exe_ctx) override { DisassemblerScope disasm(*this, exe_ctx); - if (disasm){ + if (disasm) { if (disasm->GetArchitecture().GetMachine() == llvm::Triple::x86) return x86::GetControlFlowKind(/*is_64b=*/false, m_opcode); else if (disasm->GetArchitecture().GetMachine() == llvm::Triple::x86_64) @@ -1249,12 +1249,15 @@ private: }; std::unique_ptr<DisassemblerLLVMC::MCDisasmInstance> -DisassemblerLLVMC::MCDisasmInstance::Create(const char *triple, const char *cpu, +DisassemblerLLVMC::MCDisasmInstance::Create(const char *triple_name, + const char *cpu, const char *features_str, unsigned flavor, DisassemblerLLVMC &owner) { using Instance = std::unique_ptr<DisassemblerLLVMC::MCDisasmInstance>; + llvm::Triple triple(triple_name); + std::string Status; const llvm::Target *curr_target = llvm::TargetRegistry::lookupTarget(triple, Status); @@ -1605,9 +1608,8 @@ DisassemblerLLVMC::DisassemblerLLVMC(const ArchSpec &arch, // thumb instruction disassembler. if (llvm_arch == llvm::Triple::arm) { std::string thumb_triple(thumb_arch.GetTriple().getTriple()); - m_alternate_disasm_up = - MCDisasmInstance::Create(thumb_triple.c_str(), "", features_str.c_str(), - flavor, *this); + m_alternate_disasm_up = MCDisasmInstance::Create( + thumb_triple.c_str(), "", features_str.c_str(), flavor, *this); if (!m_alternate_disasm_up) m_disasm_up.reset(); diff --git a/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h b/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h index 42ea5aacecb4..8b1c3c3f467f 100644 --- a/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h +++ b/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h @@ -45,7 +45,8 @@ protected: lldb::addr_t GetLoadAddress(lldb::ModuleSP executable); private: - std::map<lldb::ModuleSP, lldb::addr_t> m_loaded_modules; + std::map<lldb::ModuleWP, lldb::addr_t, std::owner_less<lldb::ModuleWP>> + m_loaded_modules; }; } // namespace lldb_private diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp index 86ff010e760f..097a4661e0c9 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp @@ -536,23 +536,19 @@ static void SetupLangOpts(CompilerInstance &compiler, lldb::StackFrameSP frame_sp = exe_scope.CalculateStackFrame(); lldb::ProcessSP process_sp = exe_scope.CalculateProcess(); - // Defaults to lldb::eLanguageTypeUnknown. - lldb::LanguageType frame_lang = expr.Language().AsLanguageType(); - - // Make sure the user hasn't provided a preferred execution language with - // `expression --language X -- ...` - if (frame_sp && frame_lang == lldb::eLanguageTypeUnknown) - frame_lang = frame_sp->GetLanguage().AsLanguageType(); + lldb::LanguageType language = expr.Language().AsLanguageType(); - if (process_sp && frame_lang != lldb::eLanguageTypeUnknown) { - LLDB_LOGF(log, "Frame has language of type %s", - lldb_private::Language::GetNameForLanguageType(frame_lang)); - } + if (process_sp) + LLDB_LOG( + log, + "Frame has language of type {0}\nPicked {1} for expression evaluation.", + lldb_private::Language::GetNameForLanguageType( + frame_sp ? frame_sp->GetLanguage().AsLanguageType() + : lldb::eLanguageTypeUnknown), + lldb_private::Language::GetNameForLanguageType(language)); - lldb::LanguageType language = expr.Language().AsLanguageType(); LangOptions &lang_opts = compiler.getLangOpts(); - // FIXME: should this switch on frame_lang? switch (language) { case lldb::eLanguageTypeC: case lldb::eLanguageTypeC89: diff --git a/lldb/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.cpp b/lldb/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.cpp index 926555380334..8bcb4e5535d2 100644 --- a/lldb/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.cpp +++ b/lldb/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.cpp @@ -147,17 +147,15 @@ EmulateInstructionMIPS::EmulateInstructionMIPS( if (arch_flags & ArchSpec::eMIPSAse_dspr2) features += "+dspr2,"; - m_reg_info.reset(target->createMCRegInfo(triple.getTriple())); + m_reg_info.reset(target->createMCRegInfo(triple)); assert(m_reg_info.get()); m_insn_info.reset(target->createMCInstrInfo()); assert(m_insn_info.get()); llvm::MCTargetOptions MCOptions; - m_asm_info.reset( - target->createMCAsmInfo(*m_reg_info, triple.getTriple(), MCOptions)); - m_subtype_info.reset( - target->createMCSubtargetInfo(triple.getTriple(), cpu, features)); + m_asm_info.reset(target->createMCAsmInfo(*m_reg_info, triple, MCOptions)); + m_subtype_info.reset(target->createMCSubtargetInfo(triple, cpu, features)); assert(m_asm_info.get() && m_subtype_info.get()); m_context = std::make_unique<llvm::MCContext>( @@ -174,7 +172,7 @@ EmulateInstructionMIPS::EmulateInstructionMIPS( features += "+micromips,"; m_alt_subtype_info.reset( - target->createMCSubtargetInfo(triple.getTriple(), cpu, features)); + target->createMCSubtargetInfo(triple, cpu, features)); assert(m_alt_subtype_info.get()); m_alt_disasm.reset( @@ -1005,11 +1003,10 @@ bool EmulateInstructionMIPS::SetInstruction(const Opcode &insn_opcode, uint64_t next_inst_addr = (m_addr & (~1ull)) + current_inst_size; Address next_addr(next_inst_addr); - const size_t bytes_read = - target->ReadMemory(next_addr, /* Address of next instruction */ - buf, sizeof(uint32_t), error, - false, /* force_live_memory */ - &load_addr); + const size_t bytes_read = target->ReadMemory( + next_addr, /* Address of next instruction */ + buf, sizeof(uint32_t), error, false, /* force_live_memory */ + &load_addr); if (bytes_read == 0) return true; diff --git a/lldb/source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.cpp b/lldb/source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.cpp index 780cc68fb75e..ad55d0ba97ad 100644 --- a/lldb/source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.cpp +++ b/lldb/source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.cpp @@ -151,17 +151,15 @@ EmulateInstructionMIPS64::EmulateInstructionMIPS64( if (arch_flags & ArchSpec::eMIPSAse_micromips) features += "+micromips,"; - m_reg_info.reset(target->createMCRegInfo(triple.getTriple())); + m_reg_info.reset(target->createMCRegInfo(triple)); assert(m_reg_info.get()); m_insn_info.reset(target->createMCInstrInfo()); assert(m_insn_info.get()); llvm::MCTargetOptions MCOptions; - m_asm_info.reset( - target->createMCAsmInfo(*m_reg_info, triple.getTriple(), MCOptions)); - m_subtype_info.reset( - target->createMCSubtargetInfo(triple.getTriple(), cpu, features)); + m_asm_info.reset(target->createMCAsmInfo(*m_reg_info, triple, MCOptions)); + m_subtype_info.reset(target->createMCSubtargetInfo(triple, cpu, features)); assert(m_asm_info.get() && m_subtype_info.get()); m_context = std::make_unique<llvm::MCContext>( diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index b4207439f528..277de8f44482 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -604,126 +604,6 @@ bool CPlusPlusLanguage::ExtractContextAndIdentifier( return false; } -namespace { -class NodeAllocator { - llvm::BumpPtrAllocator Alloc; - -public: - void reset() { Alloc.Reset(); } - - template <typename T, typename... Args> T *makeNode(Args &&...args) { - return new (Alloc.Allocate(sizeof(T), alignof(T))) - T(std::forward<Args>(args)...); - } - - void *allocateNodeArray(size_t sz) { - return Alloc.Allocate(sizeof(llvm::itanium_demangle::Node *) * sz, - alignof(llvm::itanium_demangle::Node *)); - } -}; - -template <typename Derived> -class ManglingSubstitutor - : public llvm::itanium_demangle::AbstractManglingParser<Derived, - NodeAllocator> { - using Base = - llvm::itanium_demangle::AbstractManglingParser<Derived, NodeAllocator>; - -public: - ManglingSubstitutor() : Base(nullptr, nullptr) {} - - template <typename... Ts> - ConstString substitute(llvm::StringRef Mangled, Ts &&...Vals) { - this->getDerived().reset(Mangled, std::forward<Ts>(Vals)...); - return substituteImpl(Mangled); - } - -protected: - void reset(llvm::StringRef Mangled) { - Base::reset(Mangled.begin(), Mangled.end()); - Written = Mangled.begin(); - Result.clear(); - Substituted = false; - } - - ConstString substituteImpl(llvm::StringRef Mangled) { - Log *log = GetLog(LLDBLog::Language); - if (this->parse() == nullptr) { - LLDB_LOG(log, "Failed to substitute mangling in {0}", Mangled); - return ConstString(); - } - if (!Substituted) - return ConstString(); - - // Append any trailing unmodified input. - appendUnchangedInput(); - LLDB_LOG(log, "Substituted mangling {0} -> {1}", Mangled, Result); - return ConstString(Result); - } - - void trySubstitute(llvm::StringRef From, llvm::StringRef To) { - if (!llvm::StringRef(currentParserPos(), this->numLeft()).starts_with(From)) - return; - - // We found a match. Append unmodified input up to this point. - appendUnchangedInput(); - - // And then perform the replacement. - Result += To; - Written += From.size(); - Substituted = true; - } - -private: - /// Input character until which we have constructed the respective output - /// already. - const char *Written = ""; - - llvm::SmallString<128> Result; - - /// Whether we have performed any substitutions. - bool Substituted = false; - - const char *currentParserPos() const { return this->First; } - - void appendUnchangedInput() { - Result += - llvm::StringRef(Written, std::distance(Written, currentParserPos())); - Written = currentParserPos(); - } -}; - -/// Given a mangled function `Mangled`, replace all the primitive function type -/// arguments of `Search` with type `Replace`. -class TypeSubstitutor : public ManglingSubstitutor<TypeSubstitutor> { - llvm::StringRef Search; - llvm::StringRef Replace; - -public: - void reset(llvm::StringRef Mangled, llvm::StringRef Search, - llvm::StringRef Replace) { - ManglingSubstitutor::reset(Mangled); - this->Search = Search; - this->Replace = Replace; - } - - llvm::itanium_demangle::Node *parseType() { - trySubstitute(Search, Replace); - return ManglingSubstitutor::parseType(); - } -}; - -class CtorDtorSubstitutor : public ManglingSubstitutor<CtorDtorSubstitutor> { -public: - llvm::itanium_demangle::Node * - parseCtorDtorName(llvm::itanium_demangle::Node *&SoFar, NameState *State) { - trySubstitute("C1", "C2"); - trySubstitute("D1", "D2"); - return ManglingSubstitutor::parseCtorDtorName(SoFar, State); - } -}; -} // namespace - std::vector<ConstString> CPlusPlusLanguage::GenerateAlternateFunctionManglings( const ConstString mangled_name) const { std::vector<ConstString> alternates; @@ -751,29 +631,49 @@ std::vector<ConstString> CPlusPlusLanguage::GenerateAlternateFunctionManglings( alternates.push_back(ConstString(fixed_scratch)); } - TypeSubstitutor TS; + auto *log = GetLog(LLDBLog::Language); + // `char` is implementation defined as either `signed` or `unsigned`. As a // result a char parameter has 3 possible manglings: 'c'-char, 'a'-signed // char, 'h'-unsigned char. If we're looking for symbols with a signed char // parameter, try finding matches which have the general case 'c'. - if (ConstString char_fixup = - TS.substitute(mangled_name.GetStringRef(), "a", "c")) - alternates.push_back(char_fixup); + if (auto char_fixup_or_err = + SubstituteType_ItaniumMangle(mangled_name.GetStringRef(), "a", "c")) { + // LLDB_LOG(log, "Substituted mangling {0} -> {1}", Mangled, Result); + if (*char_fixup_or_err) + alternates.push_back(*char_fixup_or_err); + } else + LLDB_LOG_ERROR(log, char_fixup_or_err.takeError(), + "Failed to substitute 'char' type mangling: {0}"); // long long parameter mangling 'x', may actually just be a long 'l' argument - if (ConstString long_fixup = - TS.substitute(mangled_name.GetStringRef(), "x", "l")) - alternates.push_back(long_fixup); + if (auto long_fixup_or_err = + SubstituteType_ItaniumMangle(mangled_name.GetStringRef(), "x", "l")) { + if (*long_fixup_or_err) + alternates.push_back(*long_fixup_or_err); + } else + LLDB_LOG_ERROR(log, long_fixup_or_err.takeError(), + "Failed to substitute 'long long' type mangling: {0}"); // unsigned long long parameter mangling 'y', may actually just be unsigned // long 'm' argument - if (ConstString ulong_fixup = - TS.substitute(mangled_name.GetStringRef(), "y", "m")) - alternates.push_back(ulong_fixup); - - if (ConstString ctor_fixup = - CtorDtorSubstitutor().substitute(mangled_name.GetStringRef())) - alternates.push_back(ctor_fixup); + if (auto ulong_fixup_or_err = + SubstituteType_ItaniumMangle(mangled_name.GetStringRef(), "y", "m")) { + if (*ulong_fixup_or_err) + alternates.push_back(*ulong_fixup_or_err); + } else + LLDB_LOG_ERROR( + log, ulong_fixup_or_err.takeError(), + "Failed to substitute 'unsigned long long' type mangling: {0}"); + + if (auto ctor_fixup_or_err = SubstituteStructorAliases_ItaniumMangle( + mangled_name.GetStringRef())) { + if (*ctor_fixup_or_err) { + alternates.push_back(*ctor_fixup_or_err); + } + } else + LLDB_LOG_ERROR(log, ctor_fixup_or_err.takeError(), + "Failed to substitute structor alias manglings: {0}"); return alternates; } @@ -849,31 +749,27 @@ static void LoadLibCxxFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { lldb_private::formatters::LibcxxStringSummaryProviderASCII, "std::string summary provider", "^std::__[[:alnum:]]+::basic_string<char, " - "std::__[[:alnum:]]+::char_traits<char>, " - "std::__[[:alnum:]]+::allocator<char> >$", + "std::__[[:alnum:]]+::char_traits<char>,.*>$", stl_summary_flags, true); AddCXXSummary(cpp_category_sp, lldb_private::formatters::LibcxxStringSummaryProviderASCII, "std::string summary provider", "^std::__[[:alnum:]]+::basic_string<unsigned char, " - "std::__[[:alnum:]]+::char_traits<unsigned char>, " - "std::__[[:alnum:]]+::allocator<unsigned char> >$", + "std::__[[:alnum:]]+::char_traits<unsigned char>,.*>$", stl_summary_flags, true); AddCXXSummary(cpp_category_sp, lldb_private::formatters::LibcxxStringSummaryProviderUTF16, "std::u16string summary provider", "^std::__[[:alnum:]]+::basic_string<char16_t, " - "std::__[[:alnum:]]+::char_traits<char16_t>, " - "std::__[[:alnum:]]+::allocator<char16_t> >$", + "std::__[[:alnum:]]+::char_traits<char16_t>,.*>$", stl_summary_flags, true); AddCXXSummary(cpp_category_sp, lldb_private::formatters::LibcxxStringSummaryProviderUTF32, "std::u32string summary provider", "^std::__[[:alnum:]]+::basic_string<char32_t, " - "std::__[[:alnum:]]+::char_traits<char32_t>, " - "std::__[[:alnum:]]+::allocator<char32_t> >$", + "std::__[[:alnum:]]+::char_traits<char32_t>,.*>$", stl_summary_flags, true); AddCXXSummary(cpp_category_sp, @@ -884,8 +780,7 @@ static void LoadLibCxxFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { lldb_private::formatters::LibcxxWStringSummaryProvider, "std::wstring summary provider", "^std::__[[:alnum:]]+::basic_string<wchar_t, " - "std::__[[:alnum:]]+::char_traits<wchar_t>, " - "std::__[[:alnum:]]+::allocator<wchar_t> >$", + "std::__[[:alnum:]]+::char_traits<wchar_t>,.*>$", stl_summary_flags, true); AddCXXSummary(cpp_category_sp, @@ -1401,24 +1296,16 @@ static void RegisterStdStringSummaryProvider( category_sp->AddTypeSummary(makeSpecifier(string_ty), summary_sp); - // std::basic_string<char> category_sp->AddTypeSummary( makeSpecifier(llvm::formatv("std::basic_string<{}>", char_ty).str()), summary_sp); - // std::basic_string<char,std::char_traits<char>,std::allocator<char> > - category_sp->AddTypeSummary( - makeSpecifier(llvm::formatv("std::basic_string<{0},std::char_traits<{0}>," - "std::allocator<{0}> >", - char_ty) - .str()), - summary_sp); - // std::basic_string<char, std::char_traits<char>, std::allocator<char> > + category_sp->AddTypeSummary( - makeSpecifier( - llvm::formatv("std::basic_string<{0}, std::char_traits<{0}>, " - "std::allocator<{0}> >", + std::make_shared<lldb_private::TypeNameSpecifierImpl>( + llvm::formatv("^std::basic_string<{0}, ?std::char_traits<{0}>,.*>$", char_ty) - .str()), + .str(), + eFormatterMatchRegex), summary_sp); } @@ -1463,20 +1350,17 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { cpp_category_sp->AddTypeSummary("std::__cxx11::string", eFormatterMatchExact, string_summary_sp); cpp_category_sp->AddTypeSummary( - "std::__cxx11::basic_string<char, std::char_traits<char>, " - "std::allocator<char> >", - eFormatterMatchExact, string_summary_sp); - cpp_category_sp->AddTypeSummary("std::__cxx11::basic_string<unsigned char, " - "std::char_traits<unsigned char>, " - "std::allocator<unsigned char> >", - eFormatterMatchExact, string_summary_sp); + "^std::__cxx11::basic_string<char, std::char_traits<char>,.*>$", + eFormatterMatchRegex, string_summary_sp); + cpp_category_sp->AddTypeSummary("^std::__cxx11::basic_string<unsigned char, " + "std::char_traits<unsigned char>,.*>$", + eFormatterMatchRegex, string_summary_sp); cpp_category_sp->AddTypeSummary("std::__cxx11::wstring", eFormatterMatchExact, string_summary_sp); cpp_category_sp->AddTypeSummary( - "std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, " - "std::allocator<wchar_t> >", - eFormatterMatchExact, string_summary_sp); + "^std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>,.*>$", + eFormatterMatchRegex, string_summary_sp); SyntheticChildren::Flags stl_synth_flags; stl_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences( @@ -2442,6 +2326,160 @@ bool CPlusPlusLanguage::HandleFrameFormatVariable( } } +namespace { +class NodeAllocator { + llvm::BumpPtrAllocator Alloc; + +public: + void reset() { Alloc.Reset(); } + + template <typename T, typename... Args> T *makeNode(Args &&...args) { + return new (Alloc.Allocate(sizeof(T), alignof(T))) + T(std::forward<Args>(args)...); + } + + void *allocateNodeArray(size_t sz) { + return Alloc.Allocate(sizeof(llvm::itanium_demangle::Node *) * sz, + alignof(llvm::itanium_demangle::Node *)); + } +}; + +template <typename Derived> +class ManglingSubstitutor + : public llvm::itanium_demangle::AbstractManglingParser<Derived, + NodeAllocator> { + using Base = + llvm::itanium_demangle::AbstractManglingParser<Derived, NodeAllocator>; + +public: + ManglingSubstitutor() : Base(nullptr, nullptr) {} + + template <typename... Ts> + llvm::Expected<ConstString> substitute(llvm::StringRef Mangled, + Ts &&...Vals) { + this->getDerived().reset(Mangled, std::forward<Ts>(Vals)...); + return substituteImpl(Mangled); + } + +protected: + void reset(llvm::StringRef Mangled) { + Base::reset(Mangled.begin(), Mangled.end()); + Written = Mangled.begin(); + Result.clear(); + Substituted = false; + } + + llvm::Expected<ConstString> substituteImpl(llvm::StringRef Mangled) { + if (this->parse() == nullptr) + return llvm::createStringError( + llvm::formatv("Failed to substitute mangling in '{0}'", Mangled)); + + if (!Substituted) + return ConstString(); + + // Append any trailing unmodified input. + appendUnchangedInput(); + return ConstString(Result); + } + + void trySubstitute(llvm::StringRef From, llvm::StringRef To) { + if (!llvm::StringRef(currentParserPos(), this->numLeft()).starts_with(From)) + return; + + // We found a match. Append unmodified input up to this point. + appendUnchangedInput(); + + // And then perform the replacement. + Result += To; + Written += From.size(); + Substituted = true; + } + +private: + /// Input character until which we have constructed the respective output + /// already. + const char *Written = ""; + + llvm::SmallString<128> Result; + + /// Whether we have performed any substitutions. + bool Substituted = false; + + const char *currentParserPos() const { return this->First; } + + void appendUnchangedInput() { + Result += + llvm::StringRef(Written, std::distance(Written, currentParserPos())); + Written = currentParserPos(); + } +}; + +/// Given a mangled function `Mangled`, replace all the primitive function type +/// arguments of `Search` with type `Replace`. +class TypeSubstitutor : public ManglingSubstitutor<TypeSubstitutor> { + llvm::StringRef Search; + llvm::StringRef Replace; + +public: + void reset(llvm::StringRef Mangled, llvm::StringRef Search, + llvm::StringRef Replace) { + ManglingSubstitutor::reset(Mangled); + this->Search = Search; + this->Replace = Replace; + } + + llvm::itanium_demangle::Node *parseType() { + trySubstitute(Search, Replace); + return ManglingSubstitutor::parseType(); + } +}; + +class CtorDtorSubstitutor : public ManglingSubstitutor<CtorDtorSubstitutor> { + llvm::StringRef Search; + llvm::StringRef Replace; + +public: + void reset(llvm::StringRef Mangled, llvm::StringRef Search, + llvm::StringRef Replace) { + ManglingSubstitutor::reset(Mangled); + this->Search = Search; + this->Replace = Replace; + } + + void reset(llvm::StringRef Mangled) { ManglingSubstitutor::reset(Mangled); } + + llvm::itanium_demangle::Node * + parseCtorDtorName(llvm::itanium_demangle::Node *&SoFar, NameState *State) { + if (!Search.empty() && !Replace.empty()) { + trySubstitute(Search, Replace); + } else { + trySubstitute("D1", "D2"); + trySubstitute("C1", "C2"); + } + return ManglingSubstitutor::parseCtorDtorName(SoFar, State); + } +}; +} // namespace + +llvm::Expected<ConstString> +CPlusPlusLanguage::SubstituteType_ItaniumMangle(llvm::StringRef mangled_name, + llvm::StringRef subst_from, + llvm::StringRef subst_to) { + return TypeSubstitutor().substitute(mangled_name, subst_from, subst_to); +} + +llvm::Expected<ConstString> CPlusPlusLanguage::SubstituteStructor_ItaniumMangle( + llvm::StringRef mangled_name, llvm::StringRef subst_from, + llvm::StringRef subst_to) { + return CtorDtorSubstitutor().substitute(mangled_name, subst_from, subst_to); +} + +llvm::Expected<ConstString> +CPlusPlusLanguage::SubstituteStructorAliases_ItaniumMangle( + llvm::StringRef mangled_name) { + return CtorDtorSubstitutor().substitute(mangled_name); +} + #define LLDB_PROPERTIES_language_cplusplus #include "LanguageCPlusPlusProperties.inc" diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h index 4a30299dd265..9a528ca7b03f 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h @@ -164,6 +164,76 @@ public: ConstString FindBestAlternateFunctionMangledName( const Mangled mangled, const SymbolContext &sym_ctx) const override; + /// Substitutes Itanium type encoding substrings given by \c subst_from + /// in \c mangled_name with \c subst_to. + /// + /// This function will only replace Itanium type encodings (i.e., <type> + /// productions in the Itanium ABI mangling grammar). However, no verifiction + /// is done on whether \c subst_from or \c subst_to is a valid type encoding. + /// + /// \param[in] mangled_name Mangled name to perform the substitutions in. + /// This function only supports Itanium ABI mangling. + /// + /// \param[in] subst_from The substring to substitute. + /// + /// \param[in] subst_to The substring to insert. + /// + /// \returns The mangled string with substitutions. If no substitutions + /// have been made, returns an empty \c ConstString (even if the string + /// already contained the substitutions). If an error occurred, this function + /// returns the error. + /// + static llvm::Expected<ConstString> + SubstituteType_ItaniumMangle(llvm::StringRef mangled_name, + llvm::StringRef subst_from, + llvm::StringRef subst_to); + + /// Substitutes Itanium structor encoding substrings given by \c subst_from + /// in \c mangled_name with \c subst_to. + /// + /// This function will only replace Itanium structor encodings (i.e., + /// <ctor-dtor-name> productions in the Itanium ABI mangling grammar). + /// However, no verifiction is done on whether \c subst_from or \c subst_to is + /// a valid structor encoding. + /// + /// \param[in] mangled_name Mangled name to perform the substitutions in. + /// This function only supports Itanium ABI mangling. + /// + /// \param[in] subst_from The substring to substitute. + /// + /// \param[in] subst_to The substring to insert. + /// + /// \returns The mangled string with substitutions. If no substitutions + /// have been made, returns an empty \c ConstString (even if the string + /// already contained the substitutions). If an error occurred, this function + /// returns the error. + /// + static llvm::Expected<ConstString> + SubstituteStructor_ItaniumMangle(llvm::StringRef mangled_name, + llvm::StringRef subst_from, + llvm::StringRef subst_to); + + /// Tries replacing Itanium structor encoding substrings in \c mangled_name + /// with potential aliases.j + /// + /// This function will only replace Itanium structor encodings (i.e., + /// <ctor-dtor-name> productions in the Itanium ABI mangling grammar). + /// + /// E.g., on some platforms, the C1/D1 variants are aliased to the C2/D2 + /// variants. This function will try to replace occurrences of C1/D1 with + /// C2/D2. + /// + /// \param[in] mangled_name Mangled name to perform the substitutions in. + /// This function only supports Itanium ABI mangling. + /// + /// \returns The mangled string with substitutions. If no substitutions + /// have been made, returns an empty \c ConstString (even if the string + /// already contained the substitutions). If an error occurred, this function + /// returns the error. + /// + static llvm::Expected<ConstString> + SubstituteStructorAliases_ItaniumMangle(llvm::StringRef mangled_name); + llvm::StringRef GetInstanceVariableName() override { return "this"; } FormatEntity::Entry GetFunctionNameFormat() const override; diff --git a/lldb/source/Plugins/Language/CPlusPlus/GenericList.cpp b/lldb/source/Plugins/Language/CPlusPlus/GenericList.cpp index ea1edbfd3ac9..5289027fbd8a 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/GenericList.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/GenericList.cpp @@ -339,11 +339,18 @@ lldb::ChildCacheState LibCxxForwardListFrontEnd::Update() { if (err.Fail() || !backend_addr) return lldb::ChildCacheState::eRefetch; - ValueObjectSP impl_sp(m_backend.GetChildMemberWithName("__before_begin_")); + auto list_base_sp = m_backend.GetChildAtIndex(0); + if (!list_base_sp) + return lldb::ChildCacheState::eRefetch; + + // Anonymous strucutre index is in base class at index 0. + auto [impl_sp, is_compressed_pair] = + GetValueOrOldCompressedPair(*list_base_sp, /*anon_struct_idx=*/0, + "__before_begin_", "__before_begin_"); if (!impl_sp) return ChildCacheState::eRefetch; - if (isOldCompressedPairLayout(*impl_sp)) + if (is_compressed_pair) impl_sp = GetFirstValueOfLibCXXCompressedPair(*impl_sp); if (!impl_sp) @@ -366,17 +373,10 @@ llvm::Expected<uint32_t> LibCxxListFrontEnd::CalculateNumChildren() { if (!m_head || !m_tail || m_node_address == 0) return 0; - ValueObjectSP size_node_sp(m_backend.GetChildMemberWithName("__size_")); - if (!size_node_sp) { - size_node_sp = m_backend.GetChildMemberWithName( - "__size_alloc_"); // pre-compressed_pair rework - - if (!isOldCompressedPairLayout(*size_node_sp)) - return llvm::createStringError("Unexpected std::list layout: expected " - "old __compressed_pair layout."); - + auto [size_node_sp, is_compressed_pair] = GetValueOrOldCompressedPair( + m_backend, /*anon_struct_idx=*/1, "__size_", "__size_alloc_"); + if (is_compressed_pair) size_node_sp = GetFirstValueOfLibCXXCompressedPair(*size_node_sp); - } if (size_node_sp) m_count = size_node_sp->GetValueAsUnsigned(UINT32_MAX); diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp index a7874047942c..6053d042b29b 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp @@ -49,11 +49,6 @@ static void consumeInlineNamespace(llvm::StringRef &name) { } } -bool lldb_private::formatters::isOldCompressedPairLayout( - ValueObject &pair_obj) { - return isStdTemplate(pair_obj.GetTypeName(), "__compressed_pair"); -} - bool lldb_private::formatters::isStdTemplate(ConstString type_name, llvm::StringRef type) { llvm::StringRef name = type_name.GetStringRef(); @@ -105,6 +100,44 @@ lldb_private::formatters::GetSecondValueOfLibCXXCompressedPair( return value; } +std::pair<lldb::ValueObjectSP, bool> +lldb_private::formatters::GetValueOrOldCompressedPair( + ValueObject &obj, size_t anon_struct_idx, llvm::StringRef child_name, + llvm::StringRef compressed_pair_name) { + auto is_old_compressed_pair = [](ValueObject &pair_obj) -> bool { + return isStdTemplate(pair_obj.GetTypeName(), "__compressed_pair"); + }; + + // Try searching the child member in an anonymous structure first. + if (auto unwrapped = obj.GetChildAtIndex(anon_struct_idx)) { + ValueObjectSP node_sp(obj.GetChildMemberWithName(child_name)); + if (node_sp) + return {node_sp, is_old_compressed_pair(*node_sp)}; + } + + // Older versions of libc++ don't wrap the children in anonymous structures. + // Try that instead. + ValueObjectSP node_sp(obj.GetChildMemberWithName(child_name)); + if (node_sp) + return {node_sp, is_old_compressed_pair(*node_sp)}; + + // Try the even older __compressed_pair layout. + + assert(!compressed_pair_name.empty()); + + node_sp = obj.GetChildMemberWithName(compressed_pair_name); + + // Unrecognized layout (possibly older than LLDB supports). + if (!node_sp) + return {nullptr, false}; + + // Expected old compressed_pair layout, but got something else. + if (!is_old_compressed_pair(*node_sp)) + return {nullptr, false}; + + return {node_sp, true}; +} + bool lldb_private::formatters::LibcxxFunctionSummaryProvider( ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { @@ -205,11 +238,12 @@ bool lldb_private::formatters::LibcxxUniquePointerSummaryProvider( if (!valobj_sp) return false; - ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName("__ptr_")); + auto [ptr_sp, is_compressed_pair] = GetValueOrOldCompressedPair( + *valobj_sp, /*anon_struct_idx=*/0, "__ptr_", "__ptr_"); if (!ptr_sp) return false; - if (isOldCompressedPairLayout(*ptr_sp)) + if (is_compressed_pair) ptr_sp = GetFirstValueOfLibCXXCompressedPair(*ptr_sp); if (!ptr_sp) @@ -379,13 +413,14 @@ lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::Update() { if (!valobj_sp) return lldb::ChildCacheState::eRefetch; - ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName("__ptr_")); + auto [ptr_sp, is_compressed_pair] = GetValueOrOldCompressedPair( + *valobj_sp, /*anon_struct_idx=*/0, "__ptr_", "__ptr_"); if (!ptr_sp) return lldb::ChildCacheState::eRefetch; // Retrieve the actual pointer and the deleter, and clone them to give them // user-friendly names. - if (isOldCompressedPairLayout(*ptr_sp)) { + if (is_compressed_pair) { if (ValueObjectSP value_pointer_sp = GetFirstValueOfLibCXXCompressedPair(*ptr_sp)) m_value_ptr_sp = value_pointer_sp->Clone(ConstString("pointer")); @@ -424,17 +459,15 @@ enum class StringLayout { CSD, DSC }; } static ValueObjectSP ExtractLibCxxStringData(ValueObject &valobj) { - if (auto rep_sp = valobj.GetChildMemberWithName("__rep_")) - return rep_sp; - - ValueObjectSP valobj_r_sp = valobj.GetChildMemberWithName("__r_"); - if (!valobj_r_sp || !valobj_r_sp->GetError().Success()) + auto [valobj_r_sp, is_compressed_pair] = GetValueOrOldCompressedPair( + valobj, /*anon_struct_idx=*/0, "__rep_", "__r_"); + if (!valobj_r_sp) return nullptr; - if (!isOldCompressedPairLayout(*valobj_r_sp)) - return nullptr; + if (is_compressed_pair) + return GetFirstValueOfLibCXXCompressedPair(*valobj_r_sp); - return GetFirstValueOfLibCXXCompressedPair(*valobj_r_sp); + return valobj_r_sp; } /// Determine the size in bytes of \p valobj (a libc++ std::string object) and diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h index d88a6ecb1fa8..819f8a985f9b 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h @@ -25,7 +25,22 @@ GetChildMemberWithName(ValueObject &obj, lldb::ValueObjectSP GetFirstValueOfLibCXXCompressedPair(ValueObject &pair); lldb::ValueObjectSP GetSecondValueOfLibCXXCompressedPair(ValueObject &pair); -bool isOldCompressedPairLayout(ValueObject &pair_obj); + +/// Returns the ValueObjectSP of the child of \c obj. If \c obj has no +/// child named \c child_name, returns the __compressed_pair child instead +/// with \c compressed_pair_name, if one exists. +/// +/// Latest libc++ wrap the compressed children in an anonymous structure. +/// The \c anon_struct_idx indicates the location of this struct. +/// +/// The returned boolean is \c true if the returned child was has an old-style +/// libc++ __compressed_pair layout. +/// +/// If no child was found returns a nullptr. +std::pair<lldb::ValueObjectSP, bool> +GetValueOrOldCompressedPair(ValueObject &obj, size_t anon_struct_idx, + llvm::StringRef child_name, + llvm::StringRef compressed_pair_name); bool isStdTemplate(ConstString type_name, llvm::StringRef type); bool LibcxxStringSummaryProviderASCII( diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp index 41441dfbc718..85766966f155 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp @@ -200,7 +200,8 @@ public: llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override; private: - llvm::Expected<uint32_t> CalculateNumChildrenForOldCompressedPairLayout(); + llvm::Expected<uint32_t> + CalculateNumChildrenForOldCompressedPairLayout(ValueObject &pair); /// Returns the ValueObject for the __tree_node type that /// holds the key/value pair of the node at index \ref idx. @@ -254,16 +255,8 @@ lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd:: llvm::Expected<uint32_t> lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd:: - CalculateNumChildrenForOldCompressedPairLayout() { - ValueObjectSP node_sp(m_tree->GetChildMemberWithName("__pair3_")); - if (!node_sp) - return 0; - - if (!isOldCompressedPairLayout(*node_sp)) - return llvm::createStringError("Unexpected std::map layout: expected " - "old __compressed_pair layout."); - - node_sp = GetFirstValueOfLibCXXCompressedPair(*node_sp); + CalculateNumChildrenForOldCompressedPairLayout(ValueObject &pair) { + auto node_sp = GetFirstValueOfLibCXXCompressedPair(pair); if (!node_sp) return 0; @@ -281,12 +274,16 @@ llvm::Expected<uint32_t> lldb_private::formatters:: if (m_tree == nullptr) return 0; - if (auto node_sp = m_tree->GetChildMemberWithName("__size_")) { - m_count = node_sp->GetValueAsUnsigned(0); - return m_count; - } + auto [size_sp, is_compressed_pair] = GetValueOrOldCompressedPair( + *m_tree, /*anon_struct_idx=*/2, "__size_", "__pair3_"); + if (!size_sp) + return llvm::createStringError("Unexpected std::map layout"); - return CalculateNumChildrenForOldCompressedPairLayout(); + if (is_compressed_pair) + return CalculateNumChildrenForOldCompressedPairLayout(*size_sp); + + m_count = size_sp->GetValueAsUnsigned(0); + return m_count; } ValueObjectSP diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp index 501fd0945b82..f88a5319068a 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp @@ -130,22 +130,17 @@ CompilerType lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: CompilerType lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: GetNodeType() { - auto node_sp = m_backend.GetChildAtNamePath({"__table_", "__first_node_"}); - - if (!node_sp) { - auto p1_sp = m_backend.GetChildAtNamePath({"__table_", "__p1_"}); - if (!p1_sp) - return {}; + auto table_sp = m_backend.GetChildMemberWithName("__table_"); + if (!table_sp) + return {}; - if (!isOldCompressedPairLayout(*p1_sp)) - return {}; + auto [node_sp, is_compressed_pair] = GetValueOrOldCompressedPair( + *table_sp, /*anon_struct_idx=*/1, "__first_node_", "__p1_"); + if (is_compressed_pair) + node_sp = GetFirstValueOfLibCXXCompressedPair(*node_sp); - node_sp = GetFirstValueOfLibCXXCompressedPair(*p1_sp); - if (!node_sp) - return {}; - } - - assert(node_sp); + if (!node_sp) + return {}; return node_sp->GetCompilerType().GetTypeTemplateArgument(0).GetPointeeType(); } @@ -223,19 +218,15 @@ lldb::ValueObjectSP lldb_private::formatters:: llvm::Expected<size_t> lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: CalculateNumChildrenImpl(ValueObject &table) { - if (auto size_sp = table.GetChildMemberWithName("__size_")) + auto [size_sp, is_compressed_pair] = GetValueOrOldCompressedPair( + table, /*anon_struct_idx=*/2, "__size_", "__p2_"); + if (!is_compressed_pair && size_sp) return size_sp->GetValueAsUnsigned(0); - ValueObjectSP p2_sp = table.GetChildMemberWithName("__p2_"); - if (!p2_sp) - return llvm::createStringError( - "Unexpected std::unordered_map layout: __p2_ member not found."); + if (!is_compressed_pair) + return llvm::createStringError("Unsupported std::unordered_map layout."); - if (!isOldCompressedPairLayout(*p2_sp)) - return llvm::createStringError("Unexpected std::unordered_map layout: old " - "__compressed_pair layout not found."); - - ValueObjectSP num_elements_sp = GetFirstValueOfLibCXXCompressedPair(*p2_sp); + ValueObjectSP num_elements_sp = GetFirstValueOfLibCXXCompressedPair(*size_sp); if (!num_elements_sp) return llvm::createStringError( @@ -246,19 +237,13 @@ lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: } static ValueObjectSP GetTreePointer(ValueObject &table) { - ValueObjectSP tree_sp = table.GetChildMemberWithName("__first_node_"); - if (!tree_sp) { - ValueObjectSP p1_sp = table.GetChildMemberWithName("__p1_"); - if (!p1_sp) - return nullptr; - - if (!isOldCompressedPairLayout(*p1_sp)) - return nullptr; - - tree_sp = GetFirstValueOfLibCXXCompressedPair(*p1_sp); - if (!tree_sp) - return nullptr; - } + auto [tree_sp, is_compressed_pair] = GetValueOrOldCompressedPair( + table, /*anon_struct_idx=*/1, "__first_node_", "__p1_"); + if (is_compressed_pair) + tree_sp = GetFirstValueOfLibCXXCompressedPair(*tree_sp); + + if (!tree_sp) + return nullptr; return tree_sp->GetChildMemberWithName("__next_"); } diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp index 4bcdf01c221a..202cebf9bf85 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp @@ -84,7 +84,7 @@ llvm::Expected<uint32_t> lldb_private::formatters:: LibcxxStdVectorSyntheticFrontEnd::CalculateNumChildren() { if (!m_start || !m_finish) return llvm::createStringError( - "Failed to determine start/end of vector data."); + "failed to determine start/end of vector data"); uint64_t start_val = m_start->GetValueAsUnsigned(0); uint64_t finish_val = m_finish->GetValueAsUnsigned(0); @@ -94,18 +94,18 @@ llvm::Expected<uint32_t> lldb_private::formatters:: return 0; if (start_val == 0) - return llvm::createStringError("Invalid value for start of vector."); + return llvm::createStringError("invalid value for start of vector"); if (finish_val == 0) - return llvm::createStringError("Invalid value for end of vector."); + return llvm::createStringError("invalid value for end of vector"); if (start_val > finish_val) return llvm::createStringError( - "Start of vector data begins after end pointer."); + "start of vector data begins after end pointer"); size_t num_children = (finish_val - start_val); if (num_children % m_element_size) - return llvm::createStringError("Size not multiple of element size."); + return llvm::createStringError("size not multiple of element size"); return num_children / m_element_size; } @@ -126,17 +126,15 @@ lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::GetChildAtIndex( } static ValueObjectSP GetDataPointer(ValueObject &root) { - if (auto cap_sp = root.GetChildMemberWithName("__cap_")) - return cap_sp; - - ValueObjectSP cap_sp = root.GetChildMemberWithName("__end_cap_"); + auto [cap_sp, is_compressed_pair] = GetValueOrOldCompressedPair( + root, /*anon_struct_idx=*/2, "__cap_", "__end_cap_"); if (!cap_sp) return nullptr; - if (!isOldCompressedPairLayout(*cap_sp)) - return nullptr; + if (is_compressed_pair) + return GetFirstValueOfLibCXXCompressedPair(*cap_sp); - return GetFirstValueOfLibCXXCompressedPair(*cap_sp); + return cap_sp; } lldb::ChildCacheState diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlVector.cpp b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlVector.cpp index cfc98d27f56d..99e73ca46fa2 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlVector.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlVector.cpp @@ -70,7 +70,7 @@ llvm::Expected<uint32_t> lldb_private::formatters:: MsvcStlVectorSyntheticFrontEnd::CalculateNumChildren() { if (!m_start || !m_finish) return llvm::createStringError( - "Failed to determine start/end of vector data."); + "failed to determine start/end of vector data"); uint64_t start_val = m_start->GetValueAsUnsigned(0); uint64_t finish_val = m_finish->GetValueAsUnsigned(0); @@ -80,18 +80,18 @@ llvm::Expected<uint32_t> lldb_private::formatters:: return 0; if (start_val == 0) - return llvm::createStringError("Invalid value for start of vector."); + return llvm::createStringError("invalid value for start of vector"); if (finish_val == 0) - return llvm::createStringError("Invalid value for end of vector."); + return llvm::createStringError("invalid value for end of vector"); if (start_val > finish_val) return llvm::createStringError( - "Start of vector data begins after end pointer."); + "start of vector data begins after end pointer"); size_t num_children = (finish_val - start_val); if (num_children % m_element_size) - return llvm::createStringError("Size not multiple of element size."); + return llvm::createStringError("size not multiple of element size"); return num_children / m_element_size; } diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp index f69358de6a28..931baf5927a0 100644 --- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp +++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp @@ -2037,6 +2037,19 @@ static char FindArmAarch64MappingSymbol(const char *symbol_name) { return '\0'; } +static char FindRISCVMappingSymbol(const char *symbol_name) { + if (!symbol_name) + return '\0'; + + if (strcmp(symbol_name, "$d") == 0) { + return 'd'; + } + if (strcmp(symbol_name, "$x") == 0) { + return 'x'; + } + return '\0'; +} + #define STO_MIPS_ISA (3 << 6) #define STO_MICROMIPS (2 << 6) #define IS_MICROMIPS(ST_OTHER) (((ST_OTHER)&STO_MIPS_ISA) == STO_MICROMIPS) @@ -2102,6 +2115,12 @@ ObjectFileELF::ParseSymbols(Symtab *symtab, user_id_t start_id, if (!symbol_name) symbol_name = ""; + // Skip local symbols starting with ".L" because these are compiler + // generated local labels used for internal purposes (e.g. debugging, + // optimization) and are not relevant for symbol resolution or external + // linkage. + if (llvm::StringRef(symbol_name).starts_with(".L")) + continue; // No need to add non-section symbols that have no names if (symbol.getType() != STT_SECTION && (symbol_name == nullptr || symbol_name[0] == '\0')) @@ -2190,7 +2209,6 @@ ObjectFileELF::ParseSymbols(Symtab *symtab, user_id_t start_id, int64_t symbol_value_offset = 0; uint32_t additional_flags = 0; - if (arch.IsValid()) { if (arch.GetMachine() == llvm::Triple::arm) { if (symbol.getBinding() == STB_LOCAL) { @@ -2235,6 +2253,27 @@ ObjectFileELF::ParseSymbols(Symtab *symtab, user_id_t start_id, if (mapping_symbol) continue; } + } else if (arch.GetTriple().isRISCV()) { + if (symbol.getBinding() == STB_LOCAL) { + char mapping_symbol = FindRISCVMappingSymbol(symbol_name); + if (symbol_type == eSymbolTypeCode) { + // Only handle $d and $x mapping symbols. + // Other mapping symbols are ignored as they don't affect address + // classification. + switch (mapping_symbol) { + case 'x': + // $x - marks a RISCV instruction sequence + address_class_map[symbol.st_value] = AddressClass::eCode; + break; + case 'd': + // $d - marks a RISCV data item sequence + address_class_map[symbol.st_value] = AddressClass::eData; + break; + } + } + if (mapping_symbol) + continue; + } } if (arch.GetMachine() == llvm::Triple::arm) { diff --git a/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.cpp b/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.cpp index cb8ba05d461d..69885aa53ca3 100644 --- a/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.cpp +++ b/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.cpp @@ -12,6 +12,7 @@ #include "lldb/Core/PluginManager.h" #include "lldb/Core/Section.h" #include "lldb/Symbol/Symbol.h" +#include "lldb/Target/Target.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" #include "llvm/ADT/DenseSet.h" @@ -233,6 +234,40 @@ void ObjectFileJSON::CreateSections(SectionList &unified_section_list) { } } +bool ObjectFileJSON::SetLoadAddress(Target &target, lldb::addr_t value, + bool value_is_offset) { + Log *log(GetLog(LLDBLog::DynamicLoader)); + if (!m_sections_up) + return true; + + addr_t slide = value; + if (!value_is_offset) { + addr_t lowest_addr = LLDB_INVALID_ADDRESS; + for (const SectionSP §ion_sp : *m_sections_up) { + addr_t section_load_addr = section_sp->GetFileAddress(); + lowest_addr = std::min(lowest_addr, section_load_addr); + } + if (lowest_addr == LLDB_INVALID_ADDRESS) + return false; + slide = value - lowest_addr; + } + + // Apply slide to each section's file address. + for (const SectionSP §ion_sp : *m_sections_up) { + addr_t section_load_addr = section_sp->GetFileAddress(); + if (section_load_addr != LLDB_INVALID_ADDRESS) { + LLDB_LOGF( + log, + "ObjectFileJSON::SetLoadAddress section %s to load addr 0x%" PRIx64, + section_sp->GetName().AsCString(), section_load_addr + slide); + target.SetSectionLoadAddress(section_sp, section_load_addr + slide, + /*warn_multiple=*/true); + } + } + + return true; +} + bool ObjectFileJSON::MagicBytesMatch(DataBufferSP data_sp, lldb::addr_t data_offset, lldb::addr_t data_length) { diff --git a/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.h b/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.h index b72565f46886..029c8ff18893 100644 --- a/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.h +++ b/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.h @@ -86,6 +86,9 @@ public: Strata CalculateStrata() override { return eStrataUser; } + bool SetLoadAddress(Target &target, lldb::addr_t value, + bool value_is_offset) override; + static bool MagicBytesMatch(lldb::DataBufferSP data_sp, lldb::addr_t offset, lldb::addr_t length); diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp index d7cb60e3f0c3..924e34053d41 100644 --- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp +++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp @@ -2785,7 +2785,6 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) { const char *symbol_name_non_abi_mangled = NULL; SectionSP symbol_section; - uint32_t symbol_byte_size = 0; bool add_nlist = true; bool is_debug = ((nlist.n_type & N_STAB) != 0); bool demangled_is_synthesized = false; @@ -3437,61 +3436,6 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) { if (symbol_section) { const addr_t section_file_addr = symbol_section->GetFileAddress(); - if (symbol_byte_size == 0 && - function_starts_count > 0) { - addr_t symbol_lookup_file_addr = nlist.n_value; - // Do an exact address match for non-ARM addresses, - // else get the closest since the symbol might be a - // thumb symbol which has an address with bit zero - // set - FunctionStarts::Entry *func_start_entry = - function_starts.FindEntry(symbol_lookup_file_addr, - !is_arm); - if (is_arm && func_start_entry) { - // Verify that the function start address is the - // symbol address (ARM) or the symbol address + 1 - // (thumb) - if (func_start_entry->addr != - symbol_lookup_file_addr && - func_start_entry->addr != - (symbol_lookup_file_addr + 1)) { - // Not the right entry, NULL it out... - func_start_entry = NULL; - } - } - if (func_start_entry) { - func_start_entry->data = true; - - addr_t symbol_file_addr = func_start_entry->addr; - uint32_t symbol_flags = 0; - if (is_arm) { - if (symbol_file_addr & 1) - symbol_flags = MACHO_NLIST_ARM_SYMBOL_IS_THUMB; - symbol_file_addr &= THUMB_ADDRESS_BIT_MASK; - } - - const FunctionStarts::Entry *next_func_start_entry = - function_starts.FindNextEntry(func_start_entry); - const addr_t section_end_file_addr = - section_file_addr + - symbol_section->GetByteSize(); - if (next_func_start_entry) { - addr_t next_symbol_file_addr = - next_func_start_entry->addr; - // Be sure the clear the Thumb address bit when - // we calculate the size from the current and - // next address - if (is_arm) - next_symbol_file_addr &= THUMB_ADDRESS_BIT_MASK; - symbol_byte_size = std::min<lldb::addr_t>( - next_symbol_file_addr - symbol_file_addr, - section_end_file_addr - symbol_file_addr); - } else { - symbol_byte_size = - section_end_file_addr - symbol_file_addr; - } - } - } symbol_value -= section_file_addr; } @@ -3620,9 +3564,6 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) { } sym[sym_idx].SetFlags(nlist.n_type << 16 | nlist.n_desc); - if (symbol_byte_size > 0) - sym[sym_idx].SetByteSize(symbol_byte_size); - if (demangled_is_synthesized) sym[sym_idx].SetDemangledNameIsSynthesized(true); ++sym_idx; @@ -3711,7 +3652,6 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) { SymbolType type = eSymbolTypeInvalid; SectionSP symbol_section; - lldb::addr_t symbol_byte_size = 0; bool add_nlist = true; bool is_gsym = false; bool demangled_is_synthesized = false; @@ -4297,47 +4237,6 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) { if (symbol_section) { const addr_t section_file_addr = symbol_section->GetFileAddress(); - if (symbol_byte_size == 0 && function_starts_count > 0) { - addr_t symbol_lookup_file_addr = nlist.n_value; - // Do an exact address match for non-ARM addresses, else get the - // closest since the symbol might be a thumb symbol which has an - // address with bit zero set. - FunctionStarts::Entry *func_start_entry = - function_starts.FindEntry(symbol_lookup_file_addr, !is_arm); - if (is_arm && func_start_entry) { - // Verify that the function start address is the symbol address - // (ARM) or the symbol address + 1 (thumb). - if (func_start_entry->addr != symbol_lookup_file_addr && - func_start_entry->addr != (symbol_lookup_file_addr + 1)) { - // Not the right entry, NULL it out... - func_start_entry = nullptr; - } - } - if (func_start_entry) { - func_start_entry->data = true; - - addr_t symbol_file_addr = func_start_entry->addr; - if (is_arm) - symbol_file_addr &= THUMB_ADDRESS_BIT_MASK; - - const FunctionStarts::Entry *next_func_start_entry = - function_starts.FindNextEntry(func_start_entry); - const addr_t section_end_file_addr = - section_file_addr + symbol_section->GetByteSize(); - if (next_func_start_entry) { - addr_t next_symbol_file_addr = next_func_start_entry->addr; - // Be sure the clear the Thumb address bit when we calculate the - // size from the current and next address - if (is_arm) - next_symbol_file_addr &= THUMB_ADDRESS_BIT_MASK; - symbol_byte_size = std::min<lldb::addr_t>( - next_symbol_file_addr - symbol_file_addr, - section_end_file_addr - symbol_file_addr); - } else { - symbol_byte_size = section_end_file_addr - symbol_file_addr; - } - } - } symbol_value -= section_file_addr; } @@ -4444,9 +4343,6 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) { if (nlist.n_desc & N_WEAK_REF) sym[sym_idx].SetIsWeak(true); - if (symbol_byte_size > 0) - sym[sym_idx].SetByteSize(symbol_byte_size); - if (demangled_is_synthesized) sym[sym_idx].SetDemangledNameIsSynthesized(true); @@ -4565,23 +4461,7 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) { Address symbol_addr; if (module_sp->ResolveFileAddress(symbol_file_addr, symbol_addr)) { SectionSP symbol_section(symbol_addr.GetSection()); - uint32_t symbol_byte_size = 0; if (symbol_section) { - const addr_t section_file_addr = symbol_section->GetFileAddress(); - const FunctionStarts::Entry *next_func_start_entry = - function_starts.FindNextEntry(func_start_entry); - const addr_t section_end_file_addr = - section_file_addr + symbol_section->GetByteSize(); - if (next_func_start_entry) { - addr_t next_symbol_file_addr = next_func_start_entry->addr; - if (is_arm) - next_symbol_file_addr &= THUMB_ADDRESS_BIT_MASK; - symbol_byte_size = std::min<lldb::addr_t>( - next_symbol_file_addr - symbol_file_addr, - section_end_file_addr - symbol_file_addr); - } else { - symbol_byte_size = section_end_file_addr - symbol_file_addr; - } sym[sym_idx].SetID(synthetic_sym_id++); // Don't set the name for any synthetic symbols, the Symbol // object will generate one if needed when the name is accessed @@ -4593,8 +4473,6 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) { add_symbol_addr(symbol_addr.GetFileAddress()); if (symbol_flags) sym[sym_idx].SetFlags(symbol_flags); - if (symbol_byte_size) - sym[sym_idx].SetByteSize(symbol_byte_size); ++sym_idx; } } diff --git a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp index 777b20e9bb0f..492b44186720 100644 --- a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp +++ b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp @@ -71,6 +71,47 @@ GetWasmString(llvm::DataExtractor &data, llvm::DataExtractor::Cursor &c) { return std::string(toStringRef(llvm::ArrayRef(str_storage))); } +/// An "init expr" refers to a constant expression used to determine the initial +/// value of certain elements within a module during instantiation. These +/// expressions are restricted to operations that can be evaluated at module +/// instantiation time. Currently we only support simple constant opcodes. +static lldb::offset_t GetWasmOffsetFromInitExpr(DataExtractor &data, + lldb::offset_t &offset) { + lldb::offset_t init_expr_offset = LLDB_INVALID_OFFSET; + + uint8_t opcode = data.GetU8(&offset); + switch (opcode) { + case llvm::wasm::WASM_OPCODE_I32_CONST: + case llvm::wasm::WASM_OPCODE_I64_CONST: + init_expr_offset = data.GetSLEB128(&offset); + break; + case llvm::wasm::WASM_OPCODE_GLOBAL_GET: + init_expr_offset = data.GetULEB128(&offset); + break; + case llvm::wasm::WASM_OPCODE_F32_CONST: + case llvm::wasm::WASM_OPCODE_F64_CONST: + // Not a meaningful offset. + data.GetFloat(&offset); + break; + case llvm::wasm::WASM_OPCODE_REF_NULL: + // Not a meaningful offset. + data.GetULEB128(&offset); + break; + } + + // Make sure the opcodes we read aren't part of an extended init expr. + opcode = data.GetU8(&offset); + if (opcode == llvm::wasm::WASM_OPCODE_END) + return init_expr_offset; + + // Extended init expressions are not supported, but we still have to parse + // them to skip over them and read the next segment. + do { + opcode = data.GetU8(&offset); + } while (opcode != llvm::wasm::WASM_OPCODE_END); + return LLDB_INVALID_OFFSET; +} + /// Checks whether the data buffer starts with a valid Wasm module header. static bool ValidateModuleHeader(const DataBufferSP &data_sp) { if (!data_sp || data_sp->GetByteSize() < kWasmHeaderSize) @@ -261,17 +302,20 @@ bool ObjectFileWasm::ParseHeader() { return true; } -static llvm::Expected<std::vector<AddressRange>> -ParseFunctions(SectionSP code_section_sp) { - DataExtractor data; - code_section_sp->GetSectionData(data); +struct WasmFunction { + lldb::offset_t section_offset = LLDB_INVALID_OFFSET; + uint32_t size = 0; +}; + +static llvm::Expected<std::vector<WasmFunction>> +ParseFunctions(DataExtractor &data) { lldb::offset_t offset = 0; llvm::Expected<uint32_t> function_count = GetULEB32(data, offset); if (!function_count) return function_count.takeError(); - std::vector<AddressRange> functions; + std::vector<WasmFunction> functions; functions.reserve(*function_count); for (uint32_t i = 0; i < *function_count; ++i) { @@ -281,7 +325,7 @@ ParseFunctions(SectionSP code_section_sp) { // llvm-objdump considers the ULEB with the function size to be part of the // function. We can't do that here because that would break symbolic // breakpoints, as that address is never executed. - functions.emplace_back(code_section_sp, offset, *function_size); + functions.push_back({offset, *function_size}); std::optional<lldb::offset_t> next_offset = llvm::checkedAddUnsigned<lldb::offset_t>(offset, *function_size); @@ -294,17 +338,22 @@ ParseFunctions(SectionSP code_section_sp) { } struct WasmSegment { - WasmSegment(SectionSP section_sp, lldb::offset_t offset, uint32_t size) - : address_range(section_sp, offset, size) {}; + enum SegmentType { + Active, + Passive, + }; + std::string name; - AddressRange address_range; -}; + SegmentType type = Passive; + lldb::offset_t section_offset = LLDB_INVALID_OFFSET; + uint32_t size = 0; + uint32_t memory_index = 0; + lldb::offset_t init_expr_offset = 0; -static llvm::Expected<std::vector<WasmSegment>> -ParseData(SectionSP data_section_sp) { - DataExtractor data; - data_section_sp->GetSectionData(data); + lldb::offset_t GetFileOffset() const { return section_offset & 0xffffffff; } +}; +static llvm::Expected<std::vector<WasmSegment>> ParseData(DataExtractor &data) { lldb::offset_t offset = 0; llvm::Expected<uint32_t> segment_count = GetULEB32(data, offset); @@ -319,27 +368,34 @@ ParseData(SectionSP data_section_sp) { if (!flags) return flags.takeError(); + WasmSegment segment; + // Data segments have a mode that identifies them as either passive or // active. An active data segment copies its contents into a memory during // instantiation, as specified by a memory index and a constant expression // defining an offset into that memory. + segment.type = (*flags & llvm::wasm::WASM_DATA_SEGMENT_IS_PASSIVE) + ? WasmSegment::Passive + : WasmSegment::Active; + if (*flags & llvm::wasm::WASM_DATA_SEGMENT_HAS_MEMINDEX) { + assert(segment.type == WasmSegment::Active); llvm::Expected<uint32_t> memidx = GetULEB32(data, offset); if (!memidx) return memidx.takeError(); + segment.memory_index = *memidx; } - if ((*flags & llvm::wasm::WASM_DATA_SEGMENT_IS_PASSIVE) == 0) { - // Skip over the constant expression. - for (uint8_t b = 0; b != llvm::wasm::WASM_OPCODE_END;) - b = data.GetU8(&offset); - } + if (segment.type == WasmSegment::Active) + segment.init_expr_offset = GetWasmOffsetFromInitExpr(data, offset); llvm::Expected<uint32_t> segment_size = GetULEB32(data, offset); if (!segment_size) return segment_size.takeError(); - segments.emplace_back(data_section_sp, offset, *segment_size); + segment.section_offset = offset; + segment.size = *segment_size; + segments.push_back(segment); std::optional<lldb::offset_t> next_offset = llvm::checkedAddUnsigned<lldb::offset_t>(offset, *segment_size); @@ -352,13 +408,11 @@ ParseData(SectionSP data_section_sp) { } static llvm::Expected<std::vector<Symbol>> -ParseNames(SectionSP name_section_sp, - const std::vector<AddressRange> &function_ranges, +ParseNames(SectionSP code_section_sp, DataExtractor &name_data, + const std::vector<WasmFunction> &functions, std::vector<WasmSegment> &segments) { - DataExtractor name_section_data; - name_section_sp->GetSectionData(name_section_data); - llvm::DataExtractor data = name_section_data.GetAsLLVM(); + llvm::DataExtractor data = name_data.GetAsLLVM(); llvm::DataExtractor::Cursor c(0); std::vector<Symbol> symbols; while (c && c.tell() < data.size()) { @@ -380,12 +434,13 @@ ParseNames(SectionSP name_section_sp, llvm::Expected<std::string> name = GetWasmString(data, c); if (!name) return name.takeError(); - if (*idx >= function_ranges.size()) + if (*idx >= functions.size()) continue; symbols.emplace_back( - symbols.size(), Mangled(*name), lldb::eSymbolTypeCode, + symbols.size(), *name, lldb::eSymbolTypeCode, /*external=*/false, /*is_debug=*/false, /*is_trampoline=*/false, - /*is_artificial=*/false, function_ranges[*idx], + /*is_artificial=*/false, code_section_sp, + functions[i].section_offset, functions[i].size, /*size_is_valid=*/true, /*contains_linker_annotations=*/false, /*flags=*/0); } @@ -405,12 +460,6 @@ ParseNames(SectionSP name_section_sp, continue; // Update the segment name. segments[i].name = *name; - symbols.emplace_back( - symbols.size(), Mangled(*name), lldb::eSymbolTypeData, - /*external=*/false, /*is_debug=*/false, /*is_trampoline=*/false, - /*is_artificial=*/false, segments[i].address_range, - /*size_is_valid=*/true, /*contains_linker_annotations=*/false, - /*flags=*/0); } } break; @@ -432,80 +481,11 @@ ParseNames(SectionSP name_section_sp, } void ObjectFileWasm::ParseSymtab(Symtab &symtab) { - assert(m_sections_up && "sections must be parsed"); - Log *log = GetLog(LLDBLog::Object); - - // The name section contains names and indexes. First parse the data from the - // relevant sections so we can access it by its index. - std::vector<AddressRange> functions; - std::vector<WasmSegment> segments; - - // Parse the code section. - if (SectionSP code_section_sp = - m_sections_up->FindSectionByType(lldb::eSectionTypeCode, false)) { - llvm::Expected<std::vector<AddressRange>> maybe_functions = - ParseFunctions(code_section_sp); - if (!maybe_functions) { - LLDB_LOG_ERROR(log, maybe_functions.takeError(), - "Failed to parse Wasm code section: {0}"); - return; - } - functions = *maybe_functions; - } - - // Parse the data section. - SectionSP data_section_sp = - m_sections_up->FindSectionByType(lldb::eSectionTypeData, false); - if (data_section_sp) { - llvm::Expected<std::vector<WasmSegment>> maybe_segments = - ParseData(data_section_sp); - if (!maybe_segments) { - LLDB_LOG_ERROR(log, maybe_segments.takeError(), - "Failed to parse Wasm data section: {0}"); - return; - } - segments = *maybe_segments; - } - - // Parse the name section. - SectionSP name_section_sp = - m_sections_up->FindSectionByType(lldb::eSectionTypeWasmName, false); - if (!name_section_sp) { - LLDB_LOG(log, "Failed to parse Wasm symbol table: no names section"); - return; - } - - llvm::Expected<std::vector<Symbol>> symbols = - ParseNames(name_section_sp, functions, segments); - if (!symbols) { - LLDB_LOG_ERROR(log, symbols.takeError(), "Failed to parse Wasm names: {0}"); - return; - } - - for (const Symbol &symbol : *symbols) + for (const Symbol &symbol : m_symbols) symtab.AddSymbol(symbol); - lldb::user_id_t segment_id = 0; - for (const WasmSegment &segment : segments) { - const lldb::addr_t segment_addr = - segment.address_range.GetBaseAddress().GetFileAddress(); - const size_t segment_size = segment.address_range.GetByteSize(); - SectionSP segment_sp = std::make_shared<Section>( - /*parent_section_sp=*/data_section_sp, GetModule(), - /*obj_file=*/this, - ++segment_id << 8, // 1-based segment index, shifted by 8 bits to avoid - // collision with section IDs. - ConstString(segment.name), eSectionTypeData, - /*file_vm_addr=*/segment_addr, - /*vm_size=*/segment_size, - /*file_offset=*/segment_addr, - /*file_size=*/segment_size, - /*log2align=*/0, /*flags=*/0); - m_sections_up->AddSection(segment_sp); - GetModule()->GetSectionList()->AddSection(segment_sp); - } - symtab.Finalize(); + m_symbols.clear(); } static SectionType GetSectionTypeFromName(llvm::StringRef Name) { @@ -516,7 +496,27 @@ static SectionType GetSectionTypeFromName(llvm::StringRef Name) { return eSectionTypeOther; } +std::optional<ObjectFileWasm::section_info> +ObjectFileWasm::GetSectionInfo(uint32_t section_id) { + for (const section_info §_info : m_sect_infos) { + if (sect_info.id == section_id) + return sect_info; + } + return std::nullopt; +} + +std::optional<ObjectFileWasm::section_info> +ObjectFileWasm::GetSectionInfo(llvm::StringRef section_name) { + for (const section_info §_info : m_sect_infos) { + if (sect_info.name == section_name) + return sect_info; + } + return std::nullopt; +} + void ObjectFileWasm::CreateSections(SectionList &unified_section_list) { + Log *log = GetLog(LLDBLog::Object); + if (m_sections_up) return; @@ -530,7 +530,7 @@ void ObjectFileWasm::CreateSections(SectionList &unified_section_list) { SectionType section_type = eSectionTypeOther; ConstString section_name; offset_t file_offset = sect_info.offset & 0xffffffff; - addr_t vm_addr = file_offset; + addr_t vm_addr = sect_info.offset; size_t vm_size = sect_info.size; if (llvm::wasm::WASM_SEC_CODE == sect_info.id) { @@ -542,9 +542,6 @@ void ObjectFileWasm::CreateSections(SectionList &unified_section_list) { // For this reason Section::GetFileAddress() must return zero for the // Code section. vm_addr = 0; - } else if (llvm::wasm::WASM_SEC_DATA == sect_info.id) { - section_type = eSectionTypeData; - section_name = ConstString("data"); } else { section_type = GetSectionTypeFromName(sect_info.name.GetStringRef()); if (section_type == eSectionTypeOther) @@ -556,23 +553,107 @@ void ObjectFileWasm::CreateSections(SectionList &unified_section_list) { } } - SectionSP section_sp( - new Section(GetModule(), // Module to which this section belongs. - this, // ObjectFile to which this section belongs and - // should read section data from. - section_type, // Section ID. - section_name, // Section name. - section_type, // Section type. - vm_addr, // VM address. - vm_size, // VM size in bytes of this section. - file_offset, // Offset of this section in the file. - sect_info.size, // Size of the section as found in the file. - 0, // Alignment of the section - 0, // Flags for this section. - 1)); // Number of host bytes per target byte + SectionSP section_sp = std::make_shared<Section>( + GetModule(), // Module to which this section belongs. + this, // ObjectFile to which this section belongs and + // should read section data from. + section_type, // Section ID. + section_name, // Section name. + section_type, // Section type. + vm_addr, // VM address. + vm_size, // VM size in bytes of this section. + file_offset, // Offset of this section in the file. + sect_info.size, // Size of the section as found in the file. + 0, // Alignment of the section + 0, // Flags for this section. + 1); // Number of host bytes per target byte m_sections_up->AddSection(section_sp); unified_section_list.AddSection(section_sp); } + + // The name section contains names and indexes. First parse the data from the + // relevant sections so we can access it by its index. + std::vector<WasmFunction> functions; + std::vector<WasmSegment> segments; + + // Parse the code section. + if (std::optional<section_info> info = + GetSectionInfo(llvm::wasm::WASM_SEC_CODE)) { + DataExtractor code_data = ReadImageData(info->offset, info->size); + llvm::Expected<std::vector<WasmFunction>> maybe_functions = + ParseFunctions(code_data); + if (!maybe_functions) { + LLDB_LOG_ERROR(log, maybe_functions.takeError(), + "Failed to parse Wasm code section: {0}"); + } else { + functions = *maybe_functions; + } + } + + // Parse the data section. + std::optional<section_info> data_info = + GetSectionInfo(llvm::wasm::WASM_SEC_DATA); + if (data_info) { + DataExtractor data_data = ReadImageData(data_info->offset, data_info->size); + llvm::Expected<std::vector<WasmSegment>> maybe_segments = + ParseData(data_data); + if (!maybe_segments) { + LLDB_LOG_ERROR(log, maybe_segments.takeError(), + "Failed to parse Wasm data section: {0}"); + } else { + segments = *maybe_segments; + } + } + + if (std::optional<section_info> info = GetSectionInfo("name")) { + DataExtractor names_data = ReadImageData(info->offset, info->size); + llvm::Expected<std::vector<Symbol>> symbols = ParseNames( + m_sections_up->FindSectionByType(lldb::eSectionTypeCode, false), + names_data, functions, segments); + if (!symbols) { + LLDB_LOG_ERROR(log, symbols.takeError(), + "Failed to parse Wasm names: {0}"); + } else { + m_symbols = *symbols; + } + } + + lldb::user_id_t segment_id = 0; + for (const WasmSegment &segment : segments) { + if (segment.type == WasmSegment::Active) { + // FIXME: Support segments with a memory index. + if (segment.memory_index != 0) { + LLDB_LOG(log, "Skipping segment {0}: non-zero memory index is " + "currently unsupported"); + continue; + } + + if (segment.init_expr_offset == LLDB_INVALID_OFFSET) { + LLDB_LOG(log, "Skipping segment {0}: unsupported init expression"); + continue; + } + } + + const lldb::addr_t file_vm_addr = + segment.type == WasmSegment::Active + ? segment.init_expr_offset + : data_info->offset + segment.section_offset; + const lldb::offset_t file_offset = + data_info->GetFileOffset() + segment.GetFileOffset(); + SectionSP segment_sp = std::make_shared<Section>( + GetModule(), + /*obj_file=*/this, + ++segment_id << 8, // 1-based segment index, shifted by 8 bits to avoid + // collision with section IDs. + ConstString(segment.name), eSectionTypeData, + /*file_vm_addr=*/file_vm_addr, + /*vm_size=*/segment.size, + /*file_offset=*/file_offset, + /*file_size=*/segment.size, + /*log2align=*/0, /*flags=*/0); + m_sections_up->AddSection(segment_sp); + GetModule()->GetSectionList()->AddSection(segment_sp); + } } bool ObjectFileWasm::SetLoadAddress(Target &target, lldb::addr_t load_address, @@ -697,7 +778,7 @@ void ObjectFileWasm::Dump(Stream *s) { } void ObjectFileWasm::DumpSectionHeader(llvm::raw_ostream &ostream, - const section_info_t &sh) { + const section_info &sh) { ostream << llvm::left_justify(sh.name.GetStringRef(), 16) << " " << llvm::format_hex(sh.offset, 10) << " " << llvm::format_hex(sh.size, 10) << " " << llvm::format_hex(sh.id, 6) diff --git a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.h b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.h index 531b5f0437a4..86ecbf26803c 100644 --- a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.h +++ b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.h @@ -128,20 +128,25 @@ private: /// Read a range of bytes from the Wasm module. DataExtractor ReadImageData(lldb::offset_t offset, uint32_t size); - typedef struct section_info { + struct section_info { lldb::offset_t offset; uint32_t size; uint32_t id; ConstString name; - } section_info_t; + lldb::offset_t GetFileOffset() const { return offset & 0xffffffff; } + }; + + std::optional<section_info> GetSectionInfo(uint32_t section_id); + std::optional<section_info> GetSectionInfo(llvm::StringRef section_name); /// Wasm section header dump routines. /// \{ - void DumpSectionHeader(llvm::raw_ostream &ostream, const section_info_t &sh); + void DumpSectionHeader(llvm::raw_ostream &ostream, const section_info &sh); void DumpSectionHeaders(llvm::raw_ostream &ostream); /// \} - std::vector<section_info_t> m_sect_infos; + std::vector<section_info> m_sect_infos; + std::vector<Symbol> m_symbols; ArchSpec m_arch; UUID m_uuid; }; diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp index fdafacf410d6..c1bc6a3f036b 100644 --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp @@ -76,7 +76,7 @@ NativeRegisterContextLinux_arm::NativeRegisterContextLinux_arm( ::memset(&m_fpr, 0, sizeof(m_fpr)); ::memset(&m_gpr_arm, 0, sizeof(m_gpr_arm)); ::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs)); - ::memset(&m_hbr_regs, 0, sizeof(m_hbr_regs)); + ::memset(&m_hbp_regs, 0, sizeof(m_hbp_regs)); // 16 is just a maximum value, query hardware for actual watchpoint count m_max_hwp_supported = 16; @@ -283,522 +283,43 @@ bool NativeRegisterContextLinux_arm::IsFPR(unsigned reg) const { return false; } -uint32_t NativeRegisterContextLinux_arm::NumSupportedHardwareBreakpoints() { - Log *log = GetLog(POSIXLog::Breakpoints); - - LLDB_LOGF(log, "NativeRegisterContextLinux_arm::%s()", __FUNCTION__); - - Status error; - - // Read hardware breakpoint and watchpoint information. - error = ReadHardwareDebugInfo(); - - if (error.Fail()) - return 0; - - LLDB_LOG(log, "{0}", m_max_hbp_supported); - return m_max_hbp_supported; -} - -uint32_t -NativeRegisterContextLinux_arm::SetHardwareBreakpoint(lldb::addr_t addr, - size_t size) { - Log *log = GetLog(POSIXLog::Breakpoints); - LLDB_LOG(log, "addr: {0:x}, size: {1:x}", addr, size); - - // Read hardware breakpoint and watchpoint information. - Status error = ReadHardwareDebugInfo(); - - if (error.Fail()) - return LLDB_INVALID_INDEX32; - - uint32_t control_value = 0, bp_index = 0; - - // Setup address and control values. - // Use size to get a hint of arm vs thumb modes. - switch (size) { - case 2: - control_value = (0x3 << 5) | 7; - addr &= ~1; - break; - case 4: - control_value = (0xfu << 5) | 7; - addr &= ~3; - break; - default: - return LLDB_INVALID_INDEX32; - } - - // Iterate over stored breakpoints and find a free bp_index - bp_index = LLDB_INVALID_INDEX32; - for (uint32_t i = 0; i < m_max_hbp_supported; i++) { - if ((m_hbr_regs[i].control & 1) == 0) { - bp_index = i; // Mark last free slot - } else if (m_hbr_regs[i].address == addr) { - return LLDB_INVALID_INDEX32; // We do not support duplicate breakpoints. - } - } - - if (bp_index == LLDB_INVALID_INDEX32) - return LLDB_INVALID_INDEX32; - - // Update breakpoint in local cache - m_hbr_regs[bp_index].real_addr = addr; - m_hbr_regs[bp_index].address = addr; - m_hbr_regs[bp_index].control = control_value; - - // PTRACE call to set corresponding hardware breakpoint register. - error = WriteHardwareDebugRegs(NativeRegisterContextDBReg::eDREGTypeBREAK, - bp_index); - - if (error.Fail()) { - m_hbr_regs[bp_index].address = 0; - m_hbr_regs[bp_index].control &= ~1; - - return LLDB_INVALID_INDEX32; - } - - return bp_index; -} - -bool NativeRegisterContextLinux_arm::ClearHardwareBreakpoint(uint32_t hw_idx) { - Log *log = GetLog(POSIXLog::Breakpoints); - LLDB_LOG(log, "hw_idx: {0}", hw_idx); - - // Read hardware breakpoint and watchpoint information. - Status error = ReadHardwareDebugInfo(); - - if (error.Fail()) - return false; - - if (hw_idx >= m_max_hbp_supported) - return false; - - // Create a backup we can revert to in case of failure. - lldb::addr_t tempAddr = m_hbr_regs[hw_idx].address; - uint32_t tempControl = m_hbr_regs[hw_idx].control; - - m_hbr_regs[hw_idx].control &= ~1; - m_hbr_regs[hw_idx].address = 0; - - // PTRACE call to clear corresponding hardware breakpoint register. - error = WriteHardwareDebugRegs(NativeRegisterContextDBReg::eDREGTypeBREAK, - hw_idx); - - if (error.Fail()) { - m_hbr_regs[hw_idx].control = tempControl; - m_hbr_regs[hw_idx].address = tempAddr; - - return false; - } - - return true; -} - -Status NativeRegisterContextLinux_arm::GetHardwareBreakHitIndex( - uint32_t &bp_index, lldb::addr_t trap_addr) { - Log *log = GetLog(POSIXLog::Breakpoints); - - LLDB_LOGF(log, "NativeRegisterContextLinux_arm::%s()", __FUNCTION__); - - lldb::addr_t break_addr; - - for (bp_index = 0; bp_index < m_max_hbp_supported; ++bp_index) { - break_addr = m_hbr_regs[bp_index].address; - - if ((m_hbr_regs[bp_index].control & 0x1) && (trap_addr == break_addr)) { - m_hbr_regs[bp_index].hit_addr = trap_addr; - return Status(); - } - } - - bp_index = LLDB_INVALID_INDEX32; - return Status(); -} - -Status NativeRegisterContextLinux_arm::ClearAllHardwareBreakpoints() { - Log *log = GetLog(POSIXLog::Breakpoints); - - LLDB_LOGF(log, "NativeRegisterContextLinux_arm::%s()", __FUNCTION__); - - Status error; - - // Read hardware breakpoint and watchpoint information. - error = ReadHardwareDebugInfo(); - - if (error.Fail()) - return error; - - lldb::addr_t tempAddr = 0; - uint32_t tempControl = 0; - - for (uint32_t i = 0; i < m_max_hbp_supported; i++) { - if (m_hbr_regs[i].control & 0x01) { - // Create a backup we can revert to in case of failure. - tempAddr = m_hbr_regs[i].address; - tempControl = m_hbr_regs[i].control; - - // Clear breakpoints in local cache - m_hbr_regs[i].control &= ~1; - m_hbr_regs[i].address = 0; - - // Ptrace call to update hardware debug registers - error = - WriteHardwareDebugRegs(NativeRegisterContextDBReg::eDREGTypeBREAK, i); - - if (error.Fail()) { - m_hbr_regs[i].control = tempControl; - m_hbr_regs[i].address = tempAddr; - - return error; - } - } - } - - return Status(); -} - -uint32_t NativeRegisterContextLinux_arm::NumSupportedHardwareWatchpoints() { - Log *log = GetLog(POSIXLog::Watchpoints); - - // Read hardware breakpoint and watchpoint information. - Status error = ReadHardwareDebugInfo(); - - if (error.Fail()) - return 0; - - LLDB_LOG(log, "{0}", m_max_hwp_supported); - return m_max_hwp_supported; -} - -uint32_t NativeRegisterContextLinux_arm::SetHardwareWatchpoint( - lldb::addr_t addr, size_t size, uint32_t watch_flags) { - Log *log = GetLog(POSIXLog::Watchpoints); - LLDB_LOG(log, "addr: {0:x}, size: {1:x} watch_flags: {2:x}", addr, size, - watch_flags); - - // Read hardware breakpoint and watchpoint information. - Status error = ReadHardwareDebugInfo(); - - if (error.Fail()) - return LLDB_INVALID_INDEX32; - - uint32_t control_value = 0, wp_index = 0, addr_word_offset = 0, byte_mask = 0; - lldb::addr_t real_addr = addr; - - // Check if we are setting watchpoint other than read/write/access Also - // update watchpoint flag to match Arm write-read bit configuration. - switch (watch_flags) { - case 1: - watch_flags = 2; - break; - case 2: - watch_flags = 1; - break; - case 3: - break; - default: - return LLDB_INVALID_INDEX32; - } - - // Can't watch zero bytes - // Can't watch more than 4 bytes per WVR/WCR pair - - if (size == 0 || size > 4) - return LLDB_INVALID_INDEX32; - - // Check 4-byte alignment for hardware watchpoint target address. Below is a - // hack to recalculate address and size in order to make sure we can watch - // non 4-byte aligned addresses as well. - if (addr & 0x03) { - uint8_t watch_mask = (addr & 0x03) + size; - - if (watch_mask > 0x04) - return LLDB_INVALID_INDEX32; - else if (watch_mask <= 0x02) - size = 2; - else - size = 4; - - addr = addr & (~0x03); - } - - // We can only watch up to four bytes that follow a 4 byte aligned address - // per watchpoint register pair, so make sure we can properly encode this. - addr_word_offset = addr % 4; - byte_mask = ((1u << size) - 1u) << addr_word_offset; - - // Check if we need multiple watchpoint register - if (byte_mask > 0xfu) - return LLDB_INVALID_INDEX32; - - // Setup control value - // Make the byte_mask into a valid Byte Address Select mask - control_value = byte_mask << 5; - - // Turn on appropriate watchpoint flags read or write - control_value |= (watch_flags << 3); - - // Enable this watchpoint and make it stop in privileged or user mode; - control_value |= 7; - - // Make sure bits 1:0 are clear in our address - addr &= ~((lldb::addr_t)3); - - // Iterate over stored watchpoints and find a free wp_index - wp_index = LLDB_INVALID_INDEX32; - for (uint32_t i = 0; i < m_max_hwp_supported; i++) { - if ((m_hwp_regs[i].control & 1) == 0) { - wp_index = i; // Mark last free slot - } else if (m_hwp_regs[i].address == addr) { - return LLDB_INVALID_INDEX32; // We do not support duplicate watchpoints. - } - } - - if (wp_index == LLDB_INVALID_INDEX32) - return LLDB_INVALID_INDEX32; - - // Update watchpoint in local cache - m_hwp_regs[wp_index].real_addr = real_addr; - m_hwp_regs[wp_index].address = addr; - m_hwp_regs[wp_index].control = control_value; - - // PTRACE call to set corresponding watchpoint register. - error = WriteHardwareDebugRegs(NativeRegisterContextDBReg::eDREGTypeWATCH, - wp_index); - - if (error.Fail()) { - m_hwp_regs[wp_index].address = 0; - m_hwp_regs[wp_index].control &= ~1; - - return LLDB_INVALID_INDEX32; - } - - return wp_index; -} - -bool NativeRegisterContextLinux_arm::ClearHardwareWatchpoint( - uint32_t wp_index) { - Log *log = GetLog(POSIXLog::Watchpoints); - LLDB_LOG(log, "wp_index: {0}", wp_index); - - // Read hardware breakpoint and watchpoint information. - Status error = ReadHardwareDebugInfo(); - - if (error.Fail()) - return false; - - if (wp_index >= m_max_hwp_supported) - return false; - - // Create a backup we can revert to in case of failure. - lldb::addr_t tempAddr = m_hwp_regs[wp_index].address; - uint32_t tempControl = m_hwp_regs[wp_index].control; - - // Update watchpoint in local cache - m_hwp_regs[wp_index].control &= ~1; - m_hwp_regs[wp_index].address = 0; - - // Ptrace call to update hardware debug registers - error = WriteHardwareDebugRegs(NativeRegisterContextDBReg::eDREGTypeWATCH, - wp_index); - - if (error.Fail()) { - m_hwp_regs[wp_index].control = tempControl; - m_hwp_regs[wp_index].address = tempAddr; - - return false; - } - - return true; -} - -Status NativeRegisterContextLinux_arm::ClearAllHardwareWatchpoints() { - // Read hardware breakpoint and watchpoint information. - Status error = ReadHardwareDebugInfo(); - - if (error.Fail()) - return error; - - lldb::addr_t tempAddr = 0; - uint32_t tempControl = 0; - - for (uint32_t i = 0; i < m_max_hwp_supported; i++) { - if (m_hwp_regs[i].control & 0x01) { - // Create a backup we can revert to in case of failure. - tempAddr = m_hwp_regs[i].address; - tempControl = m_hwp_regs[i].control; - - // Clear watchpoints in local cache - m_hwp_regs[i].control &= ~1; - m_hwp_regs[i].address = 0; - - // Ptrace call to update hardware debug registers - error = - WriteHardwareDebugRegs(NativeRegisterContextDBReg::eDREGTypeWATCH, i); - - if (error.Fail()) { - m_hwp_regs[i].control = tempControl; - m_hwp_regs[i].address = tempAddr; - - return error; - } - } - } - - return Status(); -} - -uint32_t NativeRegisterContextLinux_arm::GetWatchpointSize(uint32_t wp_index) { - Log *log = GetLog(POSIXLog::Watchpoints); - LLDB_LOG(log, "wp_index: {0}", wp_index); - - switch ((m_hwp_regs[wp_index].control >> 5) & 0x0f) { - case 0x01: - return 1; - case 0x03: - return 2; - case 0x07: - return 3; - case 0x0f: - return 4; - default: - return 0; - } -} -bool NativeRegisterContextLinux_arm::WatchpointIsEnabled(uint32_t wp_index) { - Log *log = GetLog(POSIXLog::Watchpoints); - LLDB_LOG(log, "wp_index: {0}", wp_index); - - if ((m_hwp_regs[wp_index].control & 0x1) == 0x1) - return true; - else - return false; -} - -Status -NativeRegisterContextLinux_arm::GetWatchpointHitIndex(uint32_t &wp_index, - lldb::addr_t trap_addr) { - Log *log = GetLog(POSIXLog::Watchpoints); - LLDB_LOG(log, "wp_index: {0}, trap_addr: {1:x}", wp_index, trap_addr); - - uint32_t watch_size; - lldb::addr_t watch_addr; - - for (wp_index = 0; wp_index < m_max_hwp_supported; ++wp_index) { - watch_size = GetWatchpointSize(wp_index); - watch_addr = m_hwp_regs[wp_index].address; - - if (WatchpointIsEnabled(wp_index) && trap_addr >= watch_addr && - trap_addr < watch_addr + watch_size) { - m_hwp_regs[wp_index].hit_addr = trap_addr; - return Status(); - } - } - - wp_index = LLDB_INVALID_INDEX32; - return Status(); -} - -lldb::addr_t -NativeRegisterContextLinux_arm::GetWatchpointAddress(uint32_t wp_index) { - Log *log = GetLog(POSIXLog::Watchpoints); - LLDB_LOG(log, "wp_index: {0}", wp_index); - - if (wp_index >= m_max_hwp_supported) - return LLDB_INVALID_ADDRESS; - - if (WatchpointIsEnabled(wp_index)) - return m_hwp_regs[wp_index].real_addr; - else - return LLDB_INVALID_ADDRESS; -} - -lldb::addr_t -NativeRegisterContextLinux_arm::GetWatchpointHitAddress(uint32_t wp_index) { - Log *log = GetLog(POSIXLog::Watchpoints); - LLDB_LOG(log, "wp_index: {0}", wp_index); - - if (wp_index >= m_max_hwp_supported) - return LLDB_INVALID_ADDRESS; - - if (WatchpointIsEnabled(wp_index)) - return m_hwp_regs[wp_index].hit_addr; - else - return LLDB_INVALID_ADDRESS; -} - -Status NativeRegisterContextLinux_arm::ReadHardwareDebugInfo() { - Status error; - - if (!m_refresh_hwdebug_info) { - return Status(); - } +llvm::Error NativeRegisterContextLinux_arm::ReadHardwareDebugInfo() { + if (!m_refresh_hwdebug_info) + return llvm::Error::success(); #ifdef __arm__ unsigned int cap_val; - - error = NativeProcessLinux::PtraceWrapper(PTRACE_GETHBPREGS, m_thread.GetID(), - nullptr, &cap_val, - sizeof(unsigned int)); + Status error = NativeProcessLinux::PtraceWrapper( + PTRACE_GETHBPREGS, m_thread.GetID(), nullptr, &cap_val, + sizeof(unsigned int)); if (error.Fail()) - return error; + return error.ToError(); m_max_hwp_supported = (cap_val >> 8) & 0xff; m_max_hbp_supported = cap_val & 0xff; m_refresh_hwdebug_info = false; - return error; + return error.ToError(); #else // __aarch64__ return arm64::ReadHardwareDebugInfo(m_thread.GetID(), m_max_hwp_supported, - m_max_hbp_supported); + m_max_hbp_supported) + .ToError(); #endif // ifdef __arm__ } -Status NativeRegisterContextLinux_arm::WriteHardwareDebugRegs( - NativeRegisterContextDBReg::DREGType hwbType, int hwb_index) { - Status error; - +llvm::Error +NativeRegisterContextLinux_arm::WriteHardwareDebugRegs(DREGType hwbType) { #ifdef __arm__ - lldb::addr_t *addr_buf; - uint32_t *ctrl_buf; - - if (hwbType == NativeRegisterContextDBReg::eDREGTypeWATCH) { - addr_buf = &m_hwp_regs[hwb_index].address; - ctrl_buf = &m_hwp_regs[hwb_index].control; - - error = NativeProcessLinux::PtraceWrapper( - PTRACE_SETHBPREGS, m_thread.GetID(), - (PTRACE_TYPE_ARG3)(intptr_t) - ((hwb_index << 1) + 1), addr_buf, - sizeof(unsigned int)); - - if (error.Fail()) - return error; - - error = NativeProcessLinux::PtraceWrapper( - PTRACE_SETHBPREGS, m_thread.GetID(), - (PTRACE_TYPE_ARG3)(intptr_t) - ((hwb_index << 1) + 2), ctrl_buf, - sizeof(unsigned int)); - } else { - addr_buf = &m_hbr_regs[hwb_index].address; - ctrl_buf = &m_hbr_regs[hwb_index].control; - - error = NativeProcessLinux::PtraceWrapper( - PTRACE_SETHBPREGS, m_thread.GetID(), - (PTRACE_TYPE_ARG3)(intptr_t)((hwb_index << 1) + 1), addr_buf, - sizeof(unsigned int)); + uint32_t max_index = m_max_hbp_supported; + if (hwbType == eDREGTypeWATCH) + max_index = m_max_hwp_supported; - if (error.Fail()) + for (uint32_t idx = 0; idx < max_index; ++idx) + if (auto error = WriteHardwareDebugReg(hwbType, idx)) return error; - error = NativeProcessLinux::PtraceWrapper( - PTRACE_SETHBPREGS, m_thread.GetID(), - (PTRACE_TYPE_ARG3)(intptr_t)((hwb_index << 1) + 2), ctrl_buf, - sizeof(unsigned int)); - } - - return error; + return llvm::Error::success(); #else // __aarch64__ uint32_t max_supported = (hwbType == NativeRegisterContextDBReg::eDREGTypeWATCH) @@ -806,12 +327,48 @@ Status NativeRegisterContextLinux_arm::WriteHardwareDebugRegs( : m_max_hbp_supported; auto ®s = (hwbType == NativeRegisterContextDBReg::eDREGTypeWATCH) ? m_hwp_regs - : m_hbr_regs; + : m_hbp_regs; return arm64::WriteHardwareDebugRegs(hwbType, m_thread.GetID(), max_supported, - regs); + regs) + .ToError(); #endif // ifdef __arm__ } +#ifdef __arm__ +llvm::Error +NativeRegisterContextLinux_arm::WriteHardwareDebugReg(DREGType hwbType, + int hwb_index) { + Status error; + lldb::addr_t *addr_buf; + uint32_t *ctrl_buf; + int addr_idx = (hwb_index << 1) + 1; + int ctrl_idx = addr_idx + 1; + + if (hwbType == NativeRegisterContextDBReg::eDREGTypeWATCH) { + addr_idx *= -1; + addr_buf = &m_hwp_regs[hwb_index].address; + ctrl_idx *= -1; + ctrl_buf = &m_hwp_regs[hwb_index].control; + } else { + addr_buf = &m_hbp_regs[hwb_index].address; + ctrl_buf = &m_hbp_regs[hwb_index].control; + } + + error = NativeProcessLinux::PtraceWrapper( + PTRACE_SETHBPREGS, m_thread.GetID(), (PTRACE_TYPE_ARG3)(intptr_t)addr_idx, + addr_buf, sizeof(unsigned int)); + + if (error.Fail()) + return error.ToError(); + + error = NativeProcessLinux::PtraceWrapper( + PTRACE_SETHBPREGS, m_thread.GetID(), (PTRACE_TYPE_ARG3)(intptr_t)ctrl_idx, + ctrl_buf, sizeof(unsigned int)); + + return error.ToError(); +} +#endif // ifdef __arm__ + uint32_t NativeRegisterContextLinux_arm::CalculateFprOffset( const RegisterInfo *reg_info) const { return reg_info->byte_offset - GetGPRSize(); diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h index 3a31d68d7a3c..cf36859b16ad 100644 --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h @@ -12,7 +12,7 @@ #define lldb_NativeRegisterContextLinux_arm_h #include "Plugins/Process/Linux/NativeRegisterContextLinux.h" -#include "Plugins/Process/Utility/NativeRegisterContextDBReg.h" +#include "Plugins/Process/Utility/NativeRegisterContextDBReg_arm.h" #include "Plugins/Process/Utility/RegisterInfoPOSIX_arm.h" #include "Plugins/Process/Utility/lldb-arm-register-enums.h" @@ -21,7 +21,8 @@ namespace process_linux { class NativeProcessLinux; -class NativeRegisterContextLinux_arm : public NativeRegisterContextLinux { +class NativeRegisterContextLinux_arm : public NativeRegisterContextLinux, + public NativeRegisterContextDBReg_arm { public: NativeRegisterContextLinux_arm(const ArchSpec &target_arch, NativeThreadProtocol &native_thread); @@ -42,39 +43,6 @@ public: Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; - // Hardware breakpoints/watchpoint management functions - - uint32_t NumSupportedHardwareBreakpoints() override; - - uint32_t SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override; - - bool ClearHardwareBreakpoint(uint32_t hw_idx) override; - - Status ClearAllHardwareBreakpoints() override; - - Status GetHardwareBreakHitIndex(uint32_t &bp_index, - lldb::addr_t trap_addr) override; - - uint32_t NumSupportedHardwareWatchpoints() override; - - uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, - uint32_t watch_flags) override; - - bool ClearHardwareWatchpoint(uint32_t hw_index) override; - - Status ClearAllHardwareWatchpoints() override; - - Status GetWatchpointHitIndex(uint32_t &wp_index, - lldb::addr_t trap_addr) override; - - lldb::addr_t GetWatchpointHitAddress(uint32_t wp_index) override; - - lldb::addr_t GetWatchpointAddress(uint32_t wp_index) override; - - uint32_t GetWatchpointSize(uint32_t wp_index); - - bool WatchpointIsEnabled(uint32_t wp_index); - protected: Status DoReadRegisterValue(uint32_t offset, const char *reg_name, uint32_t size, RegisterValue &value) override; @@ -100,23 +68,18 @@ private: uint32_t m_gpr_arm[k_num_gpr_registers_arm]; RegisterInfoPOSIX_arm::FPU m_fpr; - std::array<NativeRegisterContextDBReg::DREG, 16> - m_hbr_regs; // Arm native linux hardware breakpoints - std::array<NativeRegisterContextDBReg::DREG, 16> - m_hwp_regs; // Arm native linux hardware watchpoints - - uint32_t m_max_hwp_supported; - uint32_t m_max_hbp_supported; bool m_refresh_hwdebug_info; bool IsGPR(unsigned reg) const; bool IsFPR(unsigned reg) const; - Status ReadHardwareDebugInfo(); + llvm::Error ReadHardwareDebugInfo() override; - Status WriteHardwareDebugRegs(NativeRegisterContextDBReg::DREGType hwbType, - int hwb_index); + llvm::Error WriteHardwareDebugRegs(DREGType hwbType) override; +#ifdef __arm__ + llvm::Error WriteHardwareDebugReg(DREGType hwbType, int hwb_index); +#endif uint32_t CalculateFprOffset(const RegisterInfo *reg_info) const; diff --git a/lldb/source/Plugins/Process/Utility/CMakeLists.txt b/lldb/source/Plugins/Process/Utility/CMakeLists.txt index 5d99c22dafe1..b1e326ec064e 100644 --- a/lldb/source/Plugins/Process/Utility/CMakeLists.txt +++ b/lldb/source/Plugins/Process/Utility/CMakeLists.txt @@ -13,6 +13,7 @@ add_lldb_library(lldbPluginProcessUtility MemoryTagManagerAArch64MTE.cpp NativeProcessSoftwareSingleStep.cpp NativeRegisterContextDBReg.cpp + NativeRegisterContextDBReg_arm.cpp NativeRegisterContextDBReg_arm64.cpp NativeRegisterContextDBReg_loongarch.cpp NativeRegisterContextDBReg_x86.cpp diff --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.cpp b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.cpp index 19601b7f35d4..f35027e93514 100644 --- a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.cpp +++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.cpp @@ -44,15 +44,16 @@ uint32_t NativeRegisterContextDBReg::SetHardwareBreakpoint(lldb::addr_t addr, return LLDB_INVALID_INDEX32; } - uint32_t control_value = 0, bp_index = 0; - if (!ValidateBreakpoint(size, addr)) return LLDB_INVALID_INDEX32; - control_value = MakeBreakControlValue(size); + uint32_t control_value = MakeBreakControlValue(size); + auto details = AdjustBreakpoint({size, addr}); + size = details.size; + addr = details.addr; // Iterate over stored breakpoints and find a free bp_index - bp_index = LLDB_INVALID_INDEX32; + uint32_t bp_index = LLDB_INVALID_INDEX32; for (uint32_t i = 0; i < m_max_hbp_supported; i++) { if (!BreakpointIsEnabled(i)) bp_index = i; // Mark last free slot @@ -222,7 +223,7 @@ uint32_t NativeRegisterContextDBReg::SetHardwareWatchpoint( addr = adjusted->addr; // Check if we are setting watchpoint other than read/write/access Also - // update watchpoint flag to match AArch64/LoongArch write-read bit + // update watchpoint flag to match ARM/AArch64/LoongArch write-read bit // configuration. switch (watch_flags) { case lldb::eWatchpointKindWrite: diff --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.h b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.h index 9b6ecd382c3f..2dd11dcf74de 100644 --- a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.h +++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.h @@ -12,6 +12,7 @@ #include "Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h" #include <array> +#include <optional> // Common utilities for hardware breakpoints and hardware watchpoints on AArch64 // and LoongArch. @@ -76,19 +77,25 @@ protected: // On AArch64 and Loongarch the hardware breakpoint length size is 4, and the // target address must 4-byte alignment. - bool ValidateBreakpoint(size_t size, lldb::addr_t addr) { + virtual bool ValidateBreakpoint(size_t size, lldb::addr_t addr) { return (size == 4) && !(addr & 0x3); } + struct WatchpointDetails { size_t size; lldb::addr_t addr; }; virtual std::optional<WatchpointDetails> AdjustWatchpoint(const WatchpointDetails &details) = 0; + + using BreakpointDetails = WatchpointDetails; + virtual BreakpointDetails AdjustBreakpoint(const BreakpointDetails &details) { + return details; + } + virtual uint32_t MakeBreakControlValue(size_t size) = 0; virtual uint32_t MakeWatchControlValue(size_t size, uint32_t watch_flags) = 0; virtual uint32_t GetWatchpointSize(uint32_t wp_index) = 0; - virtual llvm::Error ReadHardwareDebugInfo() = 0; virtual llvm::Error WriteHardwareDebugRegs(DREGType hwbType) = 0; virtual lldb::addr_t FixWatchpointHitAddress(lldb::addr_t hit_addr) { diff --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm.cpp b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm.cpp new file mode 100644 index 000000000000..803fed54d6b0 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm.cpp @@ -0,0 +1,117 @@ +//===-- NativeRegisterContextDBReg_arm.cpp --------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "NativeRegisterContextDBReg_arm.h" + +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegisterValue.h" + +using namespace lldb_private; + +uint32_t NativeRegisterContextDBReg_arm::GetWatchpointSize(uint32_t wp_index) { + Log *log = GetLog(LLDBLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + switch ((m_hwp_regs[wp_index].control >> 5) & 0x0f) { + case 0x01: + return 1; + case 0x03: + return 2; + case 0x07: + return 3; + case 0x0f: + return 4; + default: + return 0; + } +} + +std::optional<NativeRegisterContextDBReg::WatchpointDetails> +NativeRegisterContextDBReg_arm::AdjustWatchpoint( + const WatchpointDetails &details) { + auto [size, addr] = details; + + if (size == 0 || size > 4) + return {}; + + // Check 4-byte alignment for hardware watchpoint target address. Below is a + // hack to recalculate address and size in order to make sure we can watch + // non 4-byte aligned addresses as well. + if (addr & 0x03) { + uint8_t watch_mask = (addr & 0x03) + size; + if (watch_mask > 0x04) + return {}; + else if (watch_mask <= 0x02) + size = 2; + else + size = 4; + + addr = addr & (~0x03); + } + + return WatchpointDetails{size, addr}; +} + +NativeRegisterContextDBReg::BreakpointDetails +NativeRegisterContextDBReg_arm::AdjustBreakpoint( + const BreakpointDetails &details) { + BreakpointDetails bd = details; + // Use size to get a hint of arm vs thumb modes. + // LLDB usually aligns this client side, but other clients may not. + switch (bd.size) { + case 2: + bd.addr &= ~1; + break; + case 4: + bd.addr &= ~3; + break; + default: + // We assume that ValidateBreakpoint would have caught this earlier. + llvm_unreachable("Invalid breakpoint size!"); + } + + return bd; +} + +uint32_t NativeRegisterContextDBReg_arm::MakeBreakControlValue(size_t size) { + switch (size) { + case 2: + return (0x3 << 5) | 7; + case 4: + return (0xfu << 5) | 7; + default: + // ValidateBreakpoint would have rejected this earlier. + llvm_unreachable("Invalid breakpoint size."); + } +} + +uint32_t +NativeRegisterContextDBReg_arm::MakeWatchControlValue(size_t size, + uint32_t watch_flags) { + // We can only watch up to four bytes that follow a 4 byte aligned address + // per watchpoint register pair, so make sure we can properly encode this. + // We assume that the address was 4 byte aligned by AdjustWatchpoint. + uint32_t byte_mask = (1u << size) - 1u; + + // Check if we need multiple watchpoint register + if (byte_mask > 0xfu) + return LLDB_INVALID_INDEX32; + + // Setup control value + // Make the byte_mask into a valid Byte Address Select mask + uint32_t control_value = byte_mask << 5; + + // Turn on appropriate watchpoint flags read or write + control_value |= (watch_flags << 3); + + // Enable this watchpoint and make it stop in privileged or user mode; + control_value |= 7; + + return control_value; +} diff --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm.h b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm.h new file mode 100644 index 000000000000..253ae96c90c9 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm.h @@ -0,0 +1,42 @@ +//===-- NativeRegisterContextDBReg_arm.h ------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_NativeRegisterContextDBReg_arm_h +#define lldb_NativeRegisterContextDBReg_arm_h + +#include "Plugins/Process/Utility/NativeRegisterContextDBReg.h" + +namespace lldb_private { + +class NativeRegisterContextDBReg_arm : public NativeRegisterContextDBReg { +public: + NativeRegisterContextDBReg_arm() + : NativeRegisterContextDBReg(/*enable_bit=*/0x1U) {} + +private: + uint32_t GetWatchpointSize(uint32_t wp_index) override; + + std::optional<WatchpointDetails> + AdjustWatchpoint(const WatchpointDetails &details) override; + + BreakpointDetails AdjustBreakpoint(const BreakpointDetails &details) override; + + uint32_t MakeBreakControlValue(size_t size) override; + + uint32_t MakeWatchControlValue(size_t size, uint32_t watch_flags) override; + + bool ValidateBreakpoint(size_t size, + [[maybe_unused]] lldb::addr_t addr) override { + // Break on 4 or 2 byte instructions. + return size == 4 || size == 2; + } +}; + +} // namespace lldb_private + +#endif // #ifndef lldb_NativeRegisterContextDBReg_arm_h diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp index fbf128553fd5..3b8d6a84c964 100644 --- a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp +++ b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp @@ -79,7 +79,7 @@ static lldb_private::RegisterInfo g_register_infos_mte[] = { DEFINE_EXTENSION_REG(mte_ctrl)}; static lldb_private::RegisterInfo g_register_infos_tls[] = { - DEFINE_EXTENSION_REG(tpidr), + DEFINE_EXTENSION_REG_GENERIC(tpidr, LLDB_REGNUM_GENERIC_TP), // Only present when SME is present DEFINE_EXTENSION_REG(tpidr2)}; diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfos_arm64.h b/lldb/source/Plugins/Process/Utility/RegisterInfos_arm64.h index c9c4d7ceae55..829fa076d221 100644 --- a/lldb/source/Plugins/Process/Utility/RegisterInfos_arm64.h +++ b/lldb/source/Plugins/Process/Utility/RegisterInfos_arm64.h @@ -456,6 +456,7 @@ static uint32_t g_d29_invalidates[] = {fpu_v29, fpu_s29, LLDB_INVALID_REGNUM}; static uint32_t g_d30_invalidates[] = {fpu_v30, fpu_s30, LLDB_INVALID_REGNUM}; static uint32_t g_d31_invalidates[] = {fpu_v31, fpu_s31, LLDB_INVALID_REGNUM}; +// clang-format off // Generates register kinds array with DWARF, EH frame and generic kind #define MISC_KIND(reg, type, generic_kind) \ { \ @@ -470,6 +471,11 @@ static uint32_t g_d31_invalidates[] = {fpu_v31, fpu_s31, LLDB_INVALID_REGNUM}; LLDB_INVALID_REGNUM, lldb_kind \ } +#define GENERIC_KIND(genenric_kind) \ + { \ + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, genenric_kind, \ + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM \ + } // Generates register kinds array for registers with only lldb kind #define KIND_ALL_INVALID \ { \ @@ -484,8 +490,6 @@ static uint32_t g_d31_invalidates[] = {fpu_v31, fpu_s31, LLDB_INVALID_REGNUM}; #define MISC_FPU_KIND(lldb_kind) LLDB_KIND(lldb_kind) #define MISC_EXC_KIND(lldb_kind) LLDB_KIND(lldb_kind) -// clang-format off - // Defines a 64-bit general purpose register #define DEFINE_GPR64(reg, generic_kind) \ { \ @@ -540,6 +544,12 @@ static uint32_t g_d31_invalidates[] = {fpu_v31, fpu_s31, LLDB_INVALID_REGNUM}; #reg, nullptr, 8, 0, lldb::eEncodingUint, lldb::eFormatHex, \ KIND_ALL_INVALID, nullptr, nullptr, nullptr, \ } + +#define DEFINE_EXTENSION_REG_GENERIC(reg, generic_kind) \ + { \ + #reg, nullptr, 8, 0, lldb::eEncodingUint, lldb::eFormatHex, \ + GENERIC_KIND(generic_kind), nullptr, nullptr, nullptr, \ + } static lldb_private::RegisterInfo g_register_infos_arm64_le[] = { // DEFINE_GPR64(name, GENERIC KIND) diff --git a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp index 88eeddf17878..8f5f1242116f 100644 --- a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp +++ b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp @@ -99,7 +99,7 @@ bool ProcessElfCore::CanDebug(lldb::TargetSP target_sp, ProcessElfCore::ProcessElfCore(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp, const FileSpec &core_file) - : PostMortemProcess(target_sp, listener_sp, core_file) {} + : PostMortemProcess(target_sp, listener_sp, core_file), m_uuids() {} // Destructor ProcessElfCore::~ProcessElfCore() { @@ -257,12 +257,12 @@ Status ProcessElfCore::DoLoadCore() { // the main executable using data we found in the core file notes. lldb::ModuleSP exe_module_sp = GetTarget().GetExecutableModule(); if (!exe_module_sp) { - // The first entry in the NT_FILE might be our executable if (!m_nt_file_entries.empty()) { + llvm::StringRef executable_path = GetMainExecutablePath(); ModuleSpec exe_module_spec; exe_module_spec.GetArchitecture() = arch; - exe_module_spec.GetUUID() = m_nt_file_entries[0].uuid; - exe_module_spec.GetFileSpec().SetFile(m_nt_file_entries[0].path, + exe_module_spec.GetUUID() = FindModuleUUID(executable_path); + exe_module_spec.GetFileSpec().SetFile(executable_path, FileSpec::Style::native); if (exe_module_spec.GetFileSpec()) { exe_module_sp = @@ -277,21 +277,38 @@ Status ProcessElfCore::DoLoadCore() { void ProcessElfCore::UpdateBuildIdForNTFileEntries() { Log *log = GetLog(LLDBLog::Process); + m_uuids.clear(); for (NT_FILE_Entry &entry : m_nt_file_entries) { - entry.uuid = FindBuidIdInCoreMemory(entry.start); - if (log && entry.uuid.IsValid()) - LLDB_LOGF(log, "%s found UUID @ %16.16" PRIx64 ": %s \"%s\"", - __FUNCTION__, entry.start, entry.uuid.GetAsString().c_str(), - entry.path.c_str()); + UUID uuid = FindBuidIdInCoreMemory(entry.start); + if (uuid.IsValid()) { + // Assert that either the path is not in the map or the UUID matches + assert(m_uuids.count(entry.path) == 0 || m_uuids[entry.path] == uuid); + m_uuids[entry.path] = uuid; + if (log) + LLDB_LOGF(log, "%s found UUID @ %16.16" PRIx64 ": %s \"%s\"", + __FUNCTION__, entry.start, uuid.GetAsString().c_str(), + entry.path.c_str()); + } } } +llvm::StringRef ProcessElfCore::GetMainExecutablePath() { + if (m_nt_file_entries.empty()) + return ""; + + // The first entry in the NT_FILE might be our executable + llvm::StringRef executable_path = m_nt_file_entries[0].path; + // Prefer the NT_FILE entry matching m_executable_name as main executable. + for (const NT_FILE_Entry &file_entry : m_nt_file_entries) + if (llvm::StringRef(file_entry.path).ends_with("/" + m_executable_name)) { + executable_path = file_entry.path; + break; + } + return executable_path; +} + UUID ProcessElfCore::FindModuleUUID(const llvm::StringRef path) { - // Returns the gnu uuid from matched NT_FILE entry - for (NT_FILE_Entry &entry : m_nt_file_entries) - if (path == entry.path && entry.uuid.IsValid()) - return entry.uuid; - return UUID(); + return m_uuids[std::string(path)]; } lldb_private::DynamicLoader *ProcessElfCore::GetDynamicLoader() { @@ -935,6 +952,7 @@ llvm::Error ProcessElfCore::parseLinuxNotes(llvm::ArrayRef<CoreNote> notes) { return status.ToError(); thread_data.name.assign (prpsinfo.pr_fname, strnlen (prpsinfo.pr_fname, sizeof (prpsinfo.pr_fname))); SetID(prpsinfo.pr_pid); + m_executable_name = prpsinfo.pr_fname; break; } case ELF::NT_SIGINFO: { diff --git a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h index a91c04a277f6..576c6858477a 100644 --- a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h +++ b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h @@ -17,6 +17,7 @@ #define LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_PROCESSELFCORE_H #include <list> +#include <unordered_map> #include <vector> #include "lldb/Target/PostMortemProcess.h" @@ -115,10 +116,6 @@ private: lldb::addr_t end; lldb::addr_t file_ofs; std::string path; - // Add a UUID member for convenient access. The UUID value is not in the - // NT_FILE entries, we will find it in core memory and store it here for - // easy access. - lldb_private::UUID uuid; }; // For ProcessElfCore only @@ -152,6 +149,12 @@ private: // NT_FILE entries found from the NOTE segment std::vector<NT_FILE_Entry> m_nt_file_entries; + // Map from file path to UUID for quick lookup + std::unordered_map<std::string, lldb_private::UUID> m_uuids; + + // Executable name found from the ELF PRPSINFO + std::string m_executable_name; + // Parse thread(s) data structures(prstatus, prpsinfo) from given NOTE segment llvm::Error ParseThreadContextsFromNoteSegment( const elf::ELFProgramHeader &segment_header, @@ -165,6 +168,9 @@ private: lldb_private::UUID FindModuleUUID(const llvm::StringRef path) override; + // Returns the main executable path + llvm::StringRef GetMainExecutablePath(); + // Returns the value of certain type of note of a given start address lldb_private::UUID FindBuidIdInCoreMemory(lldb::addr_t address); diff --git a/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.cpp b/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.cpp index 3a62081827c6..c9e90ebf8a4d 100644 --- a/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.cpp +++ b/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.cpp @@ -23,6 +23,10 @@ RegisterContextCorePOSIX_arm::RegisterContextCorePOSIX_arm( gpregset.GetByteSize()); m_gpr.SetData(m_gpr_buffer); m_gpr.SetByteOrder(gpregset.GetByteOrder()); + + const llvm::Triple &target_triple = + m_register_info_up->GetTargetArchitecture().GetTriple(); + m_fpr = getRegset(notes, target_triple, ARM_VFP_Desc); } RegisterContextCorePOSIX_arm::~RegisterContextCorePOSIX_arm() = default; @@ -43,14 +47,25 @@ bool RegisterContextCorePOSIX_arm::WriteFPR() { bool RegisterContextCorePOSIX_arm::ReadRegister(const RegisterInfo *reg_info, RegisterValue &value) { - lldb::offset_t offset = reg_info->byte_offset; - if (offset + reg_info->byte_size <= GetGPRSize()) { - uint64_t v = m_gpr.GetMaxU64(&offset, reg_info->byte_size); - if (offset == reg_info->byte_offset + reg_info->byte_size) { - value = v; - return true; + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg == LLDB_INVALID_REGNUM) + return false; + + if (IsGPR(reg)) { + lldb::offset_t offset = reg_info->byte_offset; + if (m_gpr.ValidOffsetForDataOfSize(offset, reg_info->byte_size)) { + value = m_gpr.GetMaxU64(&offset, reg_info->byte_size); + return offset == reg_info->byte_offset + reg_info->byte_size; } + } else if (IsFPR(reg)) { + assert(reg_info->byte_offset >= GetGPRSize()); + lldb::offset_t offset = reg_info->byte_offset - GetGPRSize(); + if (m_fpr.ValidOffsetForDataOfSize(offset, reg_info->byte_size)) + return value + .SetValueFromData(*reg_info, m_fpr, offset, /*partial_data_ok=*/false) + .Success(); } + return false; } diff --git a/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.h b/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.h index 8d773a046bca..e466ee242181 100644 --- a/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.h +++ b/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.h @@ -48,6 +48,7 @@ protected: private: lldb::DataBufferSP m_gpr_buffer; lldb_private::DataExtractor m_gpr; + lldb_private::DataExtractor m_fpr; }; #endif // LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_REGISTERCONTEXTPOSIXCORE_ARM_H diff --git a/lldb/source/Plugins/Process/elf-core/RegisterUtilities.h b/lldb/source/Plugins/Process/elf-core/RegisterUtilities.h index 59382a12cde0..a5b0d788a190 100644 --- a/lldb/source/Plugins/Process/elf-core/RegisterUtilities.h +++ b/lldb/source/Plugins/Process/elf-core/RegisterUtilities.h @@ -152,6 +152,11 @@ constexpr RegsetDesc AARCH64_GCS_Desc[] = { {llvm::Triple::Linux, llvm::Triple::aarch64, llvm::ELF::NT_ARM_GCS}, }; +constexpr RegsetDesc ARM_VFP_Desc[] = { + {llvm::Triple::FreeBSD, llvm::Triple::arm, llvm::ELF::NT_ARM_VFP}, + {llvm::Triple::Linux, llvm::Triple::arm, llvm::ELF::NT_ARM_VFP}, +}; + constexpr RegsetDesc PPC_VMX_Desc[] = { {llvm::Triple::FreeBSD, llvm::Triple::UnknownArch, llvm::ELF::NT_PPC_VMX}, {llvm::Triple::Linux, llvm::Triple::UnknownArch, llvm::ELF::NT_PPC_VMX}, diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp index 85e141d41747..91f3a6ce383b 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -5773,7 +5773,7 @@ public: CommandObjectProcessGDBRemotePacketMonitor(CommandInterpreter &interpreter) : CommandObjectRaw(interpreter, "process plugin packet monitor", "Send a qRcmd packet through the GDB remote protocol " - "and print the response." + "and print the response. " "The argument passed to this command will be hex " "encoded into a valid 'qRcmd' packet, sent and the " "response will be printed.") {} diff --git a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp index 6037c8d2514b..a780b3f59ade 100644 --- a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp +++ b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp @@ -799,6 +799,23 @@ Status ProcessMachCore::DoGetMemoryRegionInfo(addr_t load_addr, region_info.SetMapped(MemoryRegionInfo::eNo); } return Status(); + } else { + // The corefile has no LC_SEGMENT at this virtual address, + // but see if there is a binary whose Section has been + // loaded at that address in the current Target. + Address addr; + if (GetTarget().ResolveLoadAddress(load_addr, addr)) { + SectionSP section_sp(addr.GetSection()); + if (section_sp) { + region_info.GetRange().SetRangeBase( + section_sp->GetLoadBaseAddress(&GetTarget())); + region_info.GetRange().SetByteSize(section_sp->GetByteSize()); + if (region_info.GetRange().Contains(load_addr)) { + region_info.SetLLDBPermissions(section_sp->GetPermissions()); + return Status(); + } + } + } } region_info.GetRange().SetRangeBase(load_addr); diff --git a/lldb/source/Plugins/Process/scripted/CMakeLists.txt b/lldb/source/Plugins/Process/scripted/CMakeLists.txt index 590166591a41..1516ad3132e3 100644 --- a/lldb/source/Plugins/Process/scripted/CMakeLists.txt +++ b/lldb/source/Plugins/Process/scripted/CMakeLists.txt @@ -1,6 +1,7 @@ add_lldb_library(lldbPluginScriptedProcess PLUGIN ScriptedProcess.cpp ScriptedThread.cpp + ScriptedFrame.cpp LINK_COMPONENTS BinaryFormat diff --git a/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp b/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp new file mode 100644 index 000000000000..6519df9185df --- /dev/null +++ b/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp @@ -0,0 +1,191 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ScriptedFrame.h" + +#include "lldb/Utility/DataBufferHeap.h" + +using namespace lldb; +using namespace lldb_private; + +void ScriptedFrame::CheckInterpreterAndScriptObject() const { + lldbassert(m_script_object_sp && "Invalid Script Object."); + lldbassert(GetInterface() && "Invalid Scripted Frame Interface."); +} + +llvm::Expected<std::shared_ptr<ScriptedFrame>> +ScriptedFrame::Create(ScriptedThread &thread, + StructuredData::DictionarySP args_sp, + StructuredData::Generic *script_object) { + if (!thread.IsValid()) + return llvm::createStringError("Invalid scripted thread."); + + thread.CheckInterpreterAndScriptObject(); + + auto scripted_frame_interface = + thread.GetInterface()->CreateScriptedFrameInterface(); + if (!scripted_frame_interface) + return llvm::createStringError("failed to create scripted frame interface"); + + llvm::StringRef frame_class_name; + if (!script_object) { + std::optional<std::string> class_name = + thread.GetInterface()->GetScriptedFramePluginName(); + if (!class_name || class_name->empty()) + return llvm::createStringError( + "failed to get scripted thread class name"); + frame_class_name = *class_name; + } + + ExecutionContext exe_ctx(thread); + auto obj_or_err = scripted_frame_interface->CreatePluginObject( + frame_class_name, exe_ctx, args_sp, script_object); + + if (!obj_or_err) + return llvm::createStringError( + "failed to create script object: %s", + llvm::toString(obj_or_err.takeError()).c_str()); + + StructuredData::GenericSP owned_script_object_sp = *obj_or_err; + + if (!owned_script_object_sp->IsValid()) + return llvm::createStringError("created script object is invalid"); + + lldb::user_id_t frame_id = scripted_frame_interface->GetID(); + + lldb::addr_t pc = scripted_frame_interface->GetPC(); + SymbolContext sc; + Address symbol_addr; + if (pc != LLDB_INVALID_ADDRESS) { + symbol_addr.SetLoadAddress(pc, &thread.GetProcess()->GetTarget()); + symbol_addr.CalculateSymbolContext(&sc); + } + + std::optional<SymbolContext> maybe_sym_ctx = + scripted_frame_interface->GetSymbolContext(); + if (maybe_sym_ctx) { + sc = *maybe_sym_ctx; + } + + StructuredData::DictionarySP reg_info = + scripted_frame_interface->GetRegisterInfo(); + + if (!reg_info) + return llvm::createStringError( + "failed to get scripted thread registers info"); + + std::shared_ptr<DynamicRegisterInfo> register_info_sp = + DynamicRegisterInfo::Create( + *reg_info, thread.GetProcess()->GetTarget().GetArchitecture()); + + lldb::RegisterContextSP reg_ctx_sp; + + std::optional<std::string> reg_data = + scripted_frame_interface->GetRegisterContext(); + if (reg_data) { + DataBufferSP data_sp( + std::make_shared<DataBufferHeap>(reg_data->c_str(), reg_data->size())); + + if (!data_sp->GetByteSize()) + return llvm::createStringError("failed to copy raw registers data"); + + std::shared_ptr<RegisterContextMemory> reg_ctx_memory = + std::make_shared<RegisterContextMemory>( + thread, frame_id, *register_info_sp, LLDB_INVALID_ADDRESS); + if (!reg_ctx_memory) + return llvm::createStringError("failed to create a register context."); + + reg_ctx_memory->SetAllRegisterData(data_sp); + reg_ctx_sp = reg_ctx_memory; + } + + return std::make_shared<ScriptedFrame>( + thread, scripted_frame_interface, frame_id, pc, sc, reg_ctx_sp, + register_info_sp, owned_script_object_sp); +} + +ScriptedFrame::ScriptedFrame(ScriptedThread &thread, + ScriptedFrameInterfaceSP interface_sp, + lldb::user_id_t id, lldb::addr_t pc, + SymbolContext &sym_ctx, + lldb::RegisterContextSP reg_ctx_sp, + std::shared_ptr<DynamicRegisterInfo> reg_info_sp, + StructuredData::GenericSP script_object_sp) + : StackFrame(thread.shared_from_this(), /*frame_idx=*/id, + /*concrete_frame_idx=*/id, /*reg_context_sp=*/reg_ctx_sp, + /*cfa=*/0, /*pc=*/pc, + /*behaves_like_zeroth_frame=*/!id, /*symbol_ctx=*/&sym_ctx), + m_scripted_frame_interface_sp(interface_sp), + m_script_object_sp(script_object_sp), m_register_info_sp(reg_info_sp) {} + +ScriptedFrame::~ScriptedFrame() {} + +const char *ScriptedFrame::GetFunctionName() { + CheckInterpreterAndScriptObject(); + std::optional<std::string> function_name = GetInterface()->GetFunctionName(); + if (!function_name) + return nullptr; + return ConstString(function_name->c_str()).AsCString(); +} + +const char *ScriptedFrame::GetDisplayFunctionName() { + CheckInterpreterAndScriptObject(); + std::optional<std::string> function_name = + GetInterface()->GetDisplayFunctionName(); + if (!function_name) + return nullptr; + return ConstString(function_name->c_str()).AsCString(); +} + +bool ScriptedFrame::IsInlined() { return GetInterface()->IsInlined(); } + +bool ScriptedFrame::IsArtificial() const { + return GetInterface()->IsArtificial(); +} + +bool ScriptedFrame::IsHidden() { return GetInterface()->IsHidden(); } + +lldb::ScriptedFrameInterfaceSP ScriptedFrame::GetInterface() const { + return m_scripted_frame_interface_sp; +} + +std::shared_ptr<DynamicRegisterInfo> ScriptedFrame::GetDynamicRegisterInfo() { + CheckInterpreterAndScriptObject(); + + if (!m_register_info_sp) { + StructuredData::DictionarySP reg_info = GetInterface()->GetRegisterInfo(); + + Status error; + if (!reg_info) + return ScriptedInterface::ErrorWithMessage< + std::shared_ptr<DynamicRegisterInfo>>( + LLVM_PRETTY_FUNCTION, "Failed to get scripted frame registers info.", + error, LLDBLog::Thread); + + ThreadSP thread_sp = m_thread_wp.lock(); + if (!thread_sp || !thread_sp->IsValid()) + return ScriptedInterface::ErrorWithMessage< + std::shared_ptr<DynamicRegisterInfo>>( + LLVM_PRETTY_FUNCTION, + "Failed to get scripted frame registers info: invalid thread.", error, + LLDBLog::Thread); + + ProcessSP process_sp = thread_sp->GetProcess(); + if (!process_sp || !process_sp->IsValid()) + return ScriptedInterface::ErrorWithMessage< + std::shared_ptr<DynamicRegisterInfo>>( + LLVM_PRETTY_FUNCTION, + "Failed to get scripted frame registers info: invalid process.", + error, LLDBLog::Thread); + + m_register_info_sp = DynamicRegisterInfo::Create( + *reg_info, process_sp->GetTarget().GetArchitecture()); + } + + return m_register_info_sp; +} diff --git a/lldb/source/Plugins/Process/scripted/ScriptedFrame.h b/lldb/source/Plugins/Process/scripted/ScriptedFrame.h new file mode 100644 index 000000000000..6e01e2fd7653 --- /dev/null +++ b/lldb/source/Plugins/Process/scripted/ScriptedFrame.h @@ -0,0 +1,63 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_SCRIPTED_FRAME_H +#define LLDB_SOURCE_PLUGINS_SCRIPTED_FRAME_H + +#include "Plugins/Process/Utility/RegisterContextMemory.h" +#include "ScriptedThread.h" +#include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Target/DynamicRegisterInfo.h" +#include "lldb/Target/StackFrame.h" +#include <string> + +namespace lldb_private { +class ScriptedThread; +} + +namespace lldb_private { + +class ScriptedFrame : public lldb_private::StackFrame { + +public: + ScriptedFrame(ScriptedThread &thread, + lldb::ScriptedFrameInterfaceSP interface_sp, + lldb::user_id_t frame_idx, lldb::addr_t pc, + SymbolContext &sym_ctx, lldb::RegisterContextSP reg_ctx_sp, + std::shared_ptr<DynamicRegisterInfo> reg_info_sp, + StructuredData::GenericSP script_object_sp = nullptr); + + ~ScriptedFrame() override; + + static llvm::Expected<std::shared_ptr<ScriptedFrame>> + Create(ScriptedThread &thread, StructuredData::DictionarySP args_sp, + StructuredData::Generic *script_object = nullptr); + + bool IsInlined() override; + bool IsArtificial() const override; + bool IsHidden() override; + const char *GetFunctionName() override; + const char *GetDisplayFunctionName() override; + +private: + void CheckInterpreterAndScriptObject() const; + lldb::ScriptedFrameInterfaceSP GetInterface() const; + + ScriptedFrame(const ScriptedFrame &) = delete; + const ScriptedFrame &operator=(const ScriptedFrame &) = delete; + + std::shared_ptr<DynamicRegisterInfo> GetDynamicRegisterInfo(); + + lldb::ScriptedFrameInterfaceSP m_scripted_frame_interface_sp; + lldb_private::StructuredData::GenericSP m_script_object_sp; + std::shared_ptr<DynamicRegisterInfo> m_register_info_sp; +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_SCRIPTED_FRAME_H diff --git a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp index d0d1508e8517..491efac5aade 100644 --- a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp +++ b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "ScriptedThread.h" +#include "ScriptedFrame.h" #include "Plugins/Process/Utility/RegisterContextThreadMemory.h" #include "Plugins/Process/Utility/StopInfoMachException.h" @@ -173,40 +174,101 @@ bool ScriptedThread::LoadArtificialStackFrames() { .str(), error, LLDBLog::Thread); - StackFrameListSP frames = GetStackFrameList(); - - for (size_t idx = 0; idx < arr_size; idx++) { + auto create_frame_from_dict = + [this, arr_sp](size_t idx) -> llvm::Expected<StackFrameSP> { + Status error; std::optional<StructuredData::Dictionary *> maybe_dict = arr_sp->GetItemAtIndexAsDictionary(idx); - if (!maybe_dict) - return ScriptedInterface::ErrorWithMessage<bool>( + if (!maybe_dict) { + ScriptedInterface::ErrorWithMessage<bool>( LLVM_PRETTY_FUNCTION, llvm::Twine( "Couldn't get artificial stackframe dictionary at index (" + llvm::Twine(idx) + llvm::Twine(") from stackframe array.")) .str(), error, LLDBLog::Thread); + return error.ToError(); + } StructuredData::Dictionary *dict = *maybe_dict; lldb::addr_t pc; - if (!dict->GetValueForKeyAsInteger("pc", pc)) - return ScriptedInterface::ErrorWithMessage<bool>( + if (!dict->GetValueForKeyAsInteger("pc", pc)) { + ScriptedInterface::ErrorWithMessage<bool>( LLVM_PRETTY_FUNCTION, "Couldn't find value for key 'pc' in stackframe dictionary.", error, LLDBLog::Thread); + return error.ToError(); + } Address symbol_addr; symbol_addr.SetLoadAddress(pc, &this->GetProcess()->GetTarget()); lldb::addr_t cfa = LLDB_INVALID_ADDRESS; bool cfa_is_valid = false; + const bool artificial = false; const bool behaves_like_zeroth_frame = false; SymbolContext sc; symbol_addr.CalculateSymbolContext(&sc); - StackFrameSP synth_frame_sp = std::make_shared<StackFrame>( - this->shared_from_this(), idx, idx, cfa, cfa_is_valid, pc, - StackFrame::Kind::Artificial, behaves_like_zeroth_frame, &sc); + return std::make_shared<StackFrame>(this->shared_from_this(), idx, idx, cfa, + cfa_is_valid, pc, + StackFrame::Kind::Synthetic, artificial, + behaves_like_zeroth_frame, &sc); + }; + + auto create_frame_from_script_object = + [this, arr_sp](size_t idx) -> llvm::Expected<StackFrameSP> { + Status error; + StructuredData::ObjectSP object_sp = arr_sp->GetItemAtIndex(idx); + if (!object_sp || !object_sp->GetAsGeneric()) { + ScriptedInterface::ErrorWithMessage<bool>( + LLVM_PRETTY_FUNCTION, + llvm::Twine("Couldn't get artificial stackframe object at index (" + + llvm::Twine(idx) + + llvm::Twine(") from stackframe array.")) + .str(), + error, LLDBLog::Thread); + return error.ToError(); + } + + auto frame_or_error = + ScriptedFrame::Create(*this, nullptr, object_sp->GetAsGeneric()); + + if (!frame_or_error) { + ScriptedInterface::ErrorWithMessage<bool>( + LLVM_PRETTY_FUNCTION, toString(frame_or_error.takeError()), error); + return error.ToError(); + } + + StackFrameSP frame_sp = frame_or_error.get(); + lldbassert(frame_sp && "Couldn't initialize scripted frame."); + + return frame_sp; + }; + + StackFrameListSP frames = GetStackFrameList(); + + for (size_t idx = 0; idx < arr_size; idx++) { + StackFrameSP synth_frame_sp = nullptr; + + auto frame_from_dict_or_err = create_frame_from_dict(idx); + if (!frame_from_dict_or_err) { + auto frame_from_script_obj_or_err = create_frame_from_script_object(idx); + + if (!frame_from_script_obj_or_err) { + return ScriptedInterface::ErrorWithMessage<bool>( + LLVM_PRETTY_FUNCTION, + llvm::Twine("Couldn't add artificial frame (" + llvm::Twine(idx) + + llvm::Twine(") to ScriptedThread StackFrameList.")) + .str(), + error, LLDBLog::Thread); + } else { + llvm::consumeError(frame_from_dict_or_err.takeError()); + synth_frame_sp = *frame_from_script_obj_or_err; + } + } else { + synth_frame_sp = *frame_from_dict_or_err; + } if (!frames->SetFrameAtIndex(static_cast<uint32_t>(idx), synth_frame_sp)) return ScriptedInterface::ErrorWithMessage<bool>( diff --git a/lldb/source/Plugins/Process/scripted/ScriptedThread.h b/lldb/source/Plugins/Process/scripted/ScriptedThread.h index cd224d60ceef..ee5ace405967 100644 --- a/lldb/source/Plugins/Process/scripted/ScriptedThread.h +++ b/lldb/source/Plugins/Process/scripted/ScriptedThread.h @@ -15,11 +15,12 @@ #include "Plugins/Process/Utility/RegisterContextMemory.h" #include "lldb/Interpreter/ScriptInterpreter.h" -#include "lldb/Target//DynamicRegisterInfo.h" +#include "lldb/Target/DynamicRegisterInfo.h" #include "lldb/Target/Thread.h" namespace lldb_private { class ScriptedProcess; +class ScriptedFrame; } namespace lldb_private { @@ -61,6 +62,8 @@ public: StructuredData::ObjectSP FetchThreadExtendedInfo() override; private: + friend class ScriptedFrame; + void CheckInterpreterAndScriptObject() const; lldb::ScriptedThreadInterfaceSP GetInterface() const; diff --git a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp index c359663239dc..dc18c8e06803 100644 --- a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp +++ b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp @@ -10,14 +10,15 @@ #include "Resource.h" #include "Tool.h" #include "lldb/Core/PluginManager.h" -#include "lldb/Protocol/MCP/MCPError.h" -#include "lldb/Protocol/MCP/Tool.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Protocol/MCP/Server.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Error.h" #include "llvm/Support/Threading.h" #include <thread> -#include <variant> using namespace lldb_private; using namespace lldb_private::mcp; @@ -26,24 +27,10 @@ using namespace llvm; LLDB_PLUGIN_DEFINE(ProtocolServerMCP) -static constexpr size_t kChunkSize = 1024; static constexpr llvm::StringLiteral kName = "lldb-mcp"; static constexpr llvm::StringLiteral kVersion = "0.1.0"; -ProtocolServerMCP::ProtocolServerMCP() - : ProtocolServer(), - lldb_protocol::mcp::Server(std::string(kName), std::string(kVersion)) { - AddNotificationHandler("notifications/initialized", - [](const lldb_protocol::mcp::Notification &) { - LLDB_LOG(GetLog(LLDBLog::Host), - "MCP initialization complete"); - }); - - AddTool( - std::make_unique<CommandTool>("lldb_command", "Run an lldb command.")); - - AddResourceProvider(std::make_unique<DebuggerResourceProvider>()); -} +ProtocolServerMCP::ProtocolServerMCP() : ProtocolServer() {} ProtocolServerMCP::~ProtocolServerMCP() { llvm::consumeError(Stop()); } @@ -53,6 +40,8 @@ void ProtocolServerMCP::Initialize() { } void ProtocolServerMCP::Terminate() { + if (llvm::Error error = ProtocolServer::Terminate()) + LLDB_LOG_ERROR(GetLog(LLDBLog::Host), std::move(error), "{0}"); PluginManager::UnregisterPlugin(CreateInstance); } @@ -64,57 +53,37 @@ llvm::StringRef ProtocolServerMCP::GetPluginDescriptionStatic() { return "MCP Server."; } +void ProtocolServerMCP::Extend(lldb_protocol::mcp::Server &server) const { + server.AddNotificationHandler("notifications/initialized", + [](const lldb_protocol::mcp::Notification &) { + LLDB_LOG(GetLog(LLDBLog::Host), + "MCP initialization complete"); + }); + server.AddTool( + std::make_unique<CommandTool>("lldb_command", "Run an lldb command.")); + server.AddResourceProvider(std::make_unique<DebuggerResourceProvider>()); +} + void ProtocolServerMCP::AcceptCallback(std::unique_ptr<Socket> socket) { - LLDB_LOG(GetLog(LLDBLog::Host), "New MCP client ({0}) connected", - m_clients.size() + 1); + Log *log = GetLog(LLDBLog::Host); + std::string client_name = llvm::formatv("client_{0}", m_instances.size() + 1); + LLDB_LOG(log, "New MCP client connected: {0}", client_name); lldb::IOObjectSP io_sp = std::move(socket); - auto client_up = std::make_unique<Client>(); - client_up->io_sp = io_sp; - Client *client = client_up.get(); - - Status status; - auto read_handle_up = m_loop.RegisterReadObject( - io_sp, - [this, client](MainLoopBase &loop) { - if (llvm::Error error = ReadCallback(*client)) { - LLDB_LOG_ERROR(GetLog(LLDBLog::Host), std::move(error), "{0}"); - client->read_handle_up.reset(); - } - }, - status); - if (status.Fail()) + auto transport_up = std::make_unique<lldb_protocol::mcp::Transport>( + io_sp, io_sp, [client_name](llvm::StringRef message) { + LLDB_LOG(GetLog(LLDBLog::Host), "{0}: {1}", client_name, message); + }); + auto instance_up = std::make_unique<lldb_protocol::mcp::Server>( + std::string(kName), std::string(kVersion), std::move(transport_up), + m_loop); + Extend(*instance_up); + llvm::Error error = instance_up->Run(); + if (error) { + LLDB_LOG_ERROR(log, std::move(error), "Failed to run MCP server: {0}"); return; - - client_up->read_handle_up = std::move(read_handle_up); - m_clients.emplace_back(std::move(client_up)); -} - -llvm::Error ProtocolServerMCP::ReadCallback(Client &client) { - char chunk[kChunkSize]; - size_t bytes_read = sizeof(chunk); - if (Status status = client.io_sp->Read(chunk, bytes_read); status.Fail()) - return status.takeError(); - client.buffer.append(chunk, bytes_read); - - for (std::string::size_type pos; - (pos = client.buffer.find('\n')) != std::string::npos;) { - llvm::Expected<std::optional<lldb_protocol::mcp::Message>> message = - HandleData(StringRef(client.buffer.data(), pos)); - client.buffer = client.buffer.erase(0, pos + 1); - if (!message) - return message.takeError(); - - if (*message) { - std::string Output; - llvm::raw_string_ostream OS(Output); - OS << llvm::formatv("{0}", toJSON(**message)) << '\n'; - size_t num_bytes = Output.size(); - return client.io_sp->Write(Output.data(), num_bytes).takeError(); - } } - - return llvm::Error::success(); + m_instances.push_back(std::move(instance_up)); } llvm::Error ProtocolServerMCP::Start(ProtocolServer::Connection connection) { @@ -138,7 +107,19 @@ llvm::Error ProtocolServerMCP::Start(ProtocolServer::Connection connection) { if (llvm::Error error = handles.takeError()) return error; + auto listening_uris = m_listener->GetListeningConnectionURI(); + if (listening_uris.empty()) + return createStringError("failed to get listening connections"); + std::string address = + llvm::join(m_listener->GetListeningConnectionURI(), ", "); + + ServerInfo info{listening_uris[0]}; + llvm::Expected<ServerInfoHandle> handle = ServerInfo::Write(info); + if (!handle) + return handle.takeError(); + m_running = true; + m_server_info_handle = std::move(*handle); m_listen_handlers = std::move(*handles); m_loop_thread = std::thread([=] { llvm::set_thread_name("protocol-server.mcp"); @@ -158,27 +139,15 @@ llvm::Error ProtocolServerMCP::Stop() { // Stop the main loop. m_loop.AddPendingCallback( - [](MainLoopBase &loop) { loop.RequestTermination(); }); + [](lldb_private::MainLoopBase &loop) { loop.RequestTermination(); }); // Wait for the main loop to exit. if (m_loop_thread.joinable()) m_loop_thread.join(); - { - std::lock_guard<std::mutex> guard(m_mutex); - m_listener.reset(); - m_listen_handlers.clear(); - m_clients.clear(); - } + m_listen_handlers.clear(); + m_server_info_handle = ServerInfoHandle(); + m_instances.clear(); return llvm::Error::success(); } - -lldb_protocol::mcp::Capabilities ProtocolServerMCP::GetCapabilities() { - lldb_protocol::mcp::Capabilities capabilities; - capabilities.tools.listChanged = true; - // FIXME: Support sending notifications when a debugger/target are - // added/removed. - capabilities.resources.listChanged = false; - return capabilities; -} diff --git a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h index 7fe909a728b8..0251664a2acc 100644 --- a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h +++ b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h @@ -18,8 +18,7 @@ namespace lldb_private::mcp { -class ProtocolServerMCP : public ProtocolServer, - public lldb_protocol::mcp::Server { +class ProtocolServerMCP : public ProtocolServer { public: ProtocolServerMCP(); virtual ~ProtocolServerMCP() override; @@ -39,26 +38,25 @@ public: Socket *GetSocket() const override { return m_listener.get(); } +protected: + // This adds tools and resource providers that + // are specific to this server. Overridable by the unit tests. + virtual void Extend(lldb_protocol::mcp::Server &server) const; + private: void AcceptCallback(std::unique_ptr<Socket> socket); - lldb_protocol::mcp::Capabilities GetCapabilities() override; - bool m_running = false; - MainLoop m_loop; + lldb_protocol::mcp::ServerInfoHandle m_server_info_handle; + lldb_private::MainLoop m_loop; std::thread m_loop_thread; + std::mutex m_mutex; std::unique_ptr<Socket> m_listener; - std::vector<MainLoopBase::ReadHandleUP> m_listen_handlers; - struct Client { - lldb::IOObjectSP io_sp; - MainLoopBase::ReadHandleUP read_handle_up; - std::string buffer; - }; - llvm::Error ReadCallback(Client &client); - std::vector<std::unique_ptr<Client>> m_clients; + std::vector<MainLoopBase::ReadHandleUP> m_listen_handlers; + std::vector<std::unique_ptr<lldb_protocol::mcp::Server>> m_instances; }; } // namespace lldb_private::mcp diff --git a/lldb/source/Plugins/Protocol/MCP/Resource.cpp b/lldb/source/Plugins/Protocol/MCP/Resource.cpp index e94d2cdd65e0..581424510d4c 100644 --- a/lldb/source/Plugins/Protocol/MCP/Resource.cpp +++ b/lldb/source/Plugins/Protocol/MCP/Resource.cpp @@ -8,7 +8,6 @@ #include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" #include "lldb/Protocol/MCP/MCPError.h" -#include "lldb/Target/Platform.h" using namespace lldb_private; using namespace lldb_private::mcp; @@ -124,7 +123,7 @@ DebuggerResourceProvider::GetResources() const { return resources; } -llvm::Expected<lldb_protocol::mcp::ResourceResult> +llvm::Expected<lldb_protocol::mcp::ReadResourceResult> DebuggerResourceProvider::ReadResource(llvm::StringRef uri) const { auto [protocol, path] = uri.split("://"); @@ -161,7 +160,7 @@ DebuggerResourceProvider::ReadResource(llvm::StringRef uri) const { return ReadDebuggerResource(uri, debugger_idx); } -llvm::Expected<lldb_protocol::mcp::ResourceResult> +llvm::Expected<lldb_protocol::mcp::ReadResourceResult> DebuggerResourceProvider::ReadDebuggerResource(llvm::StringRef uri, lldb::user_id_t debugger_id) { lldb::DebuggerSP debugger_sp = Debugger::FindDebuggerWithID(debugger_id); @@ -173,17 +172,17 @@ DebuggerResourceProvider::ReadDebuggerResource(llvm::StringRef uri, debugger_resource.name = debugger_sp->GetInstanceName(); debugger_resource.num_targets = debugger_sp->GetTargetList().GetNumTargets(); - lldb_protocol::mcp::ResourceContents contents; + lldb_protocol::mcp::TextResourceContents contents; contents.uri = uri; contents.mimeType = kMimeTypeJSON; contents.text = llvm::formatv("{0}", toJSON(debugger_resource)); - lldb_protocol::mcp::ResourceResult result; + lldb_protocol::mcp::ReadResourceResult result; result.contents.push_back(contents); return result; } -llvm::Expected<lldb_protocol::mcp::ResourceResult> +llvm::Expected<lldb_protocol::mcp::ReadResourceResult> DebuggerResourceProvider::ReadTargetResource(llvm::StringRef uri, lldb::user_id_t debugger_id, size_t target_idx) { @@ -209,12 +208,12 @@ DebuggerResourceProvider::ReadTargetResource(llvm::StringRef uri, if (lldb::PlatformSP platform_sp = target_sp->GetPlatform()) target_resource.platform = platform_sp->GetName(); - lldb_protocol::mcp::ResourceContents contents; + lldb_protocol::mcp::TextResourceContents contents; contents.uri = uri; contents.mimeType = kMimeTypeJSON; contents.text = llvm::formatv("{0}", toJSON(target_resource)); - lldb_protocol::mcp::ResourceResult result; + lldb_protocol::mcp::ReadResourceResult result; result.contents.push_back(contents); return result; } diff --git a/lldb/source/Plugins/Protocol/MCP/Resource.h b/lldb/source/Plugins/Protocol/MCP/Resource.h index e2382a74f796..0c6576602905 100644 --- a/lldb/source/Plugins/Protocol/MCP/Resource.h +++ b/lldb/source/Plugins/Protocol/MCP/Resource.h @@ -11,7 +11,11 @@ #include "lldb/Protocol/MCP/Protocol.h" #include "lldb/Protocol/MCP/Resource.h" -#include "lldb/lldb-private.h" +#include "lldb/lldb-forward.h" +#include "lldb/lldb-types.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" +#include <cstddef> #include <vector> namespace lldb_private::mcp { @@ -21,9 +25,8 @@ public: using ResourceProvider::ResourceProvider; virtual ~DebuggerResourceProvider() = default; - virtual std::vector<lldb_protocol::mcp::Resource> - GetResources() const override; - virtual llvm::Expected<lldb_protocol::mcp::ResourceResult> + std::vector<lldb_protocol::mcp::Resource> GetResources() const override; + llvm::Expected<lldb_protocol::mcp::ReadResourceResult> ReadResource(llvm::StringRef uri) const override; private: @@ -31,9 +34,9 @@ private: static lldb_protocol::mcp::Resource GetTargetResource(size_t target_idx, Target &target); - static llvm::Expected<lldb_protocol::mcp::ResourceResult> + static llvm::Expected<lldb_protocol::mcp::ReadResourceResult> ReadDebuggerResource(llvm::StringRef uri, lldb::user_id_t debugger_id); - static llvm::Expected<lldb_protocol::mcp::ResourceResult> + static llvm::Expected<lldb_protocol::mcp::ReadResourceResult> ReadTargetResource(llvm::StringRef uri, lldb::user_id_t debugger_id, size_t target_idx); }; diff --git a/lldb/source/Plugins/Protocol/MCP/Tool.cpp b/lldb/source/Plugins/Protocol/MCP/Tool.cpp index 143470702a6f..2f451bf76e81 100644 --- a/lldb/source/Plugins/Protocol/MCP/Tool.cpp +++ b/lldb/source/Plugins/Protocol/MCP/Tool.cpp @@ -7,9 +7,9 @@ //===----------------------------------------------------------------------===// #include "Tool.h" -#include "lldb/Core/Module.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Protocol/MCP/Protocol.h" using namespace lldb_private; using namespace lldb_protocol; @@ -29,10 +29,10 @@ bool fromJSON(const llvm::json::Value &V, CommandToolArguments &A, O.mapOptional("arguments", A.arguments); } -/// Helper function to create a TextResult from a string output. -static lldb_protocol::mcp::TextResult createTextResult(std::string output, - bool is_error = false) { - lldb_protocol::mcp::TextResult text_result; +/// Helper function to create a CallToolResult from a string output. +static lldb_protocol::mcp::CallToolResult +createTextResult(std::string output, bool is_error = false) { + lldb_protocol::mcp::CallToolResult text_result; text_result.content.emplace_back( lldb_protocol::mcp::TextContent{{std::move(output)}}); text_result.isError = is_error; @@ -41,7 +41,7 @@ static lldb_protocol::mcp::TextResult createTextResult(std::string output, } // namespace -llvm::Expected<lldb_protocol::mcp::TextResult> +llvm::Expected<lldb_protocol::mcp::CallToolResult> CommandTool::Call(const lldb_protocol::mcp::ToolArguments &args) { if (!std::holds_alternative<json::Value>(args)) return createStringError("CommandTool requires arguments"); diff --git a/lldb/source/Plugins/Protocol/MCP/Tool.h b/lldb/source/Plugins/Protocol/MCP/Tool.h index b7b1756eb38d..1886525b9168 100644 --- a/lldb/source/Plugins/Protocol/MCP/Tool.h +++ b/lldb/source/Plugins/Protocol/MCP/Tool.h @@ -9,11 +9,11 @@ #ifndef LLDB_PLUGINS_PROTOCOL_MCP_TOOL_H #define LLDB_PLUGINS_PROTOCOL_MCP_TOOL_H -#include "lldb/Core/Debugger.h" #include "lldb/Protocol/MCP/Protocol.h" #include "lldb/Protocol/MCP/Tool.h" +#include "llvm/Support/Error.h" #include "llvm/Support/JSON.h" -#include <string> +#include <optional> namespace lldb_private::mcp { @@ -22,10 +22,10 @@ public: using lldb_protocol::mcp::Tool::Tool; ~CommandTool() = default; - virtual llvm::Expected<lldb_protocol::mcp::TextResult> + llvm::Expected<lldb_protocol::mcp::CallToolResult> Call(const lldb_protocol::mcp::ToolArguments &args) override; - virtual std::optional<llvm::json::Value> GetSchema() const override; + std::optional<llvm::json::Value> GetSchema() const override; }; } // namespace lldb_private::mcp diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt index 04370940423a..09103573b89c 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt @@ -22,6 +22,7 @@ endif() add_lldb_library(lldbPluginScriptInterpreterPythonInterfaces PLUGIN OperatingSystemPythonInterface.cpp ScriptInterpreterPythonInterfaces.cpp + ScriptedFramePythonInterface.cpp ScriptedPlatformPythonInterface.cpp ScriptedProcessPythonInterface.cpp ScriptedPythonInterface.cpp diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h index 02dc06507caf..3814f4661507 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h @@ -17,6 +17,7 @@ #include "OperatingSystemPythonInterface.h" #include "ScriptedBreakpointPythonInterface.h" +#include "ScriptedFramePythonInterface.h" #include "ScriptedPlatformPythonInterface.h" #include "ScriptedProcessPythonInterface.h" #include "ScriptedStopHookPythonInterface.h" diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.cpp new file mode 100644 index 000000000000..20ca7a2c0135 --- /dev/null +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.cpp @@ -0,0 +1,157 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/Config.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Utility/Log.h" +#include "lldb/lldb-enumerations.h" + +#if LLDB_ENABLE_PYTHON + +// LLDB Python header must be included first +#include "../lldb-python.h" + +#include "../SWIGPythonBridge.h" +#include "../ScriptInterpreterPythonImpl.h" +#include "ScriptedFramePythonInterface.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::python; +using Locker = ScriptInterpreterPythonImpl::Locker; + +ScriptedFramePythonInterface::ScriptedFramePythonInterface( + ScriptInterpreterPythonImpl &interpreter) + : ScriptedFrameInterface(), ScriptedPythonInterface(interpreter) {} + +llvm::Expected<StructuredData::GenericSP> +ScriptedFramePythonInterface::CreatePluginObject( + const llvm::StringRef class_name, ExecutionContext &exe_ctx, + StructuredData::DictionarySP args_sp, StructuredData::Generic *script_obj) { + ExecutionContextRefSP exe_ctx_ref_sp = + std::make_shared<ExecutionContextRef>(exe_ctx); + StructuredDataImpl sd_impl(args_sp); + return ScriptedPythonInterface::CreatePluginObject(class_name, script_obj, + exe_ctx_ref_sp, sd_impl); +} + +lldb::user_id_t ScriptedFramePythonInterface::GetID() { + Status error; + StructuredData::ObjectSP obj = Dispatch("get_id", error); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) + return LLDB_INVALID_FRAME_ID; + + return obj->GetUnsignedIntegerValue(LLDB_INVALID_FRAME_ID); +} + +lldb::addr_t ScriptedFramePythonInterface::GetPC() { + Status error; + StructuredData::ObjectSP obj = Dispatch("get_pc", error); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) + return LLDB_INVALID_ADDRESS; + + return obj->GetUnsignedIntegerValue(LLDB_INVALID_ADDRESS); +} + +std::optional<SymbolContext> ScriptedFramePythonInterface::GetSymbolContext() { + Status error; + auto sym_ctx = Dispatch<SymbolContext>("get_symbol_context", error); + + if (error.Fail()) { + return ErrorWithMessage<SymbolContext>(LLVM_PRETTY_FUNCTION, + error.AsCString(), error); + } + + return sym_ctx; +} + +std::optional<std::string> ScriptedFramePythonInterface::GetFunctionName() { + Status error; + StructuredData::ObjectSP obj = Dispatch("get_function_name", error); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) + return {}; + + return obj->GetStringValue().str(); +} + +std::optional<std::string> +ScriptedFramePythonInterface::GetDisplayFunctionName() { + Status error; + StructuredData::ObjectSP obj = Dispatch("get_display_function_name", error); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) + return {}; + + return obj->GetStringValue().str(); +} + +bool ScriptedFramePythonInterface::IsInlined() { + Status error; + StructuredData::ObjectSP obj = Dispatch("is_inlined", error); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) + return false; + + return obj->GetBooleanValue(); +} + +bool ScriptedFramePythonInterface::IsArtificial() { + Status error; + StructuredData::ObjectSP obj = Dispatch("is_artificial", error); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) + return false; + + return obj->GetBooleanValue(); +} + +bool ScriptedFramePythonInterface::IsHidden() { + Status error; + StructuredData::ObjectSP obj = Dispatch("is_hidden", error); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) + return false; + + return obj->GetBooleanValue(); +} + +StructuredData::DictionarySP ScriptedFramePythonInterface::GetRegisterInfo() { + Status error; + StructuredData::DictionarySP dict = + Dispatch<StructuredData::DictionarySP>("get_register_info", error); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict, + error)) + return {}; + + return dict; +} + +std::optional<std::string> ScriptedFramePythonInterface::GetRegisterContext() { + Status error; + StructuredData::ObjectSP obj = Dispatch("get_register_context", error); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) + return {}; + + return obj->GetAsString()->GetValue().str(); +} + +#endif diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.h new file mode 100644 index 000000000000..3aff237ae65d --- /dev/null +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.h @@ -0,0 +1,59 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDFRAMEPYTHONINTERFACE_H +#define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDFRAMEPYTHONINTERFACE_H + +#include "lldb/Host/Config.h" + +#if LLDB_ENABLE_PYTHON + +#include "ScriptedPythonInterface.h" +#include "lldb/Interpreter/Interfaces/ScriptedFrameInterface.h" +#include <optional> + +namespace lldb_private { +class ScriptedFramePythonInterface : public ScriptedFrameInterface, + public ScriptedPythonInterface { +public: + ScriptedFramePythonInterface(ScriptInterpreterPythonImpl &interpreter); + + llvm::Expected<StructuredData::GenericSP> + CreatePluginObject(llvm::StringRef class_name, ExecutionContext &exe_ctx, + StructuredData::DictionarySP args_sp, + StructuredData::Generic *script_obj = nullptr) override; + + llvm::SmallVector<AbstractMethodRequirement> + GetAbstractMethodRequirements() const override { + return llvm::SmallVector<AbstractMethodRequirement>({{"get_id"}}); + } + + lldb::user_id_t GetID() override; + + lldb::addr_t GetPC() override; + + std::optional<SymbolContext> GetSymbolContext() override; + + std::optional<std::string> GetFunctionName() override; + + std::optional<std::string> GetDisplayFunctionName() override; + + bool IsInlined() override; + + bool IsArtificial() override; + + bool IsHidden() override; + + StructuredData::DictionarySP GetRegisterInfo() override; + + std::optional<std::string> GetRegisterContext() override; +}; +} // namespace lldb_private + +#endif // LLDB_ENABLE_PYTHON +#endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDFRAMEPYTHONINTERFACE_H diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp index b49d1d82fe53..8083ccae0402 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp @@ -167,7 +167,8 @@ ScriptedPythonInterface::ExtractValueFromPythonObject< if (!sb_mem_reg_info) { error = Status::FromErrorStringWithFormat( - "Couldn't cast lldb::SBMemoryRegionInfo to lldb::MemoryRegionInfoSP."); + "Couldn't cast lldb::SBMemoryRegionInfo to " + "lldb_private::MemoryRegionInfo."); return {}; } diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.cpp index 8af89d761764..fd4d231a747f 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.cpp @@ -144,4 +144,21 @@ StructuredData::ArraySP ScriptedThreadPythonInterface::GetExtendedInfo() { return arr; } +std::optional<std::string> +ScriptedThreadPythonInterface::GetScriptedFramePluginName() { + Status error; + StructuredData::ObjectSP obj = Dispatch("get_scripted_frame_plugin", error); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) + return {}; + + return obj->GetStringValue().str(); +} + +lldb::ScriptedFrameInterfaceSP +ScriptedThreadPythonInterface::CreateScriptedFrameInterface() { + return m_interpreter.CreateScriptedFrameInterface(); +} + #endif diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.h index 1fb23b39c707..043557a82746 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.h @@ -51,6 +51,11 @@ public: std::optional<std::string> GetRegisterContext() override; StructuredData::ArraySP GetExtendedInfo() override; + + std::optional<std::string> GetScriptedFramePluginName() override; + +protected: + lldb::ScriptedFrameInterfaceSP CreateScriptedFrameInterface() override; }; } // namespace lldb_private diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp index 9330a634489a..73c5c72932ff 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp @@ -1521,6 +1521,11 @@ ScriptInterpreterPythonImpl::CreateScriptedThreadInterface() { return std::make_shared<ScriptedThreadPythonInterface>(*this); } +ScriptedFrameInterfaceSP +ScriptInterpreterPythonImpl::CreateScriptedFrameInterface() { + return std::make_shared<ScriptedFramePythonInterface>(*this); +} + ScriptedThreadPlanInterfaceSP ScriptInterpreterPythonImpl::CreateScriptedThreadPlanInterface() { return std::make_shared<ScriptedThreadPlanPythonInterface>(*this); diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h index 83b64b85faeb..dedac280788f 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h @@ -99,6 +99,8 @@ public: lldb::ScriptedThreadInterfaceSP CreateScriptedThreadInterface() override; + lldb::ScriptedFrameInterfaceSP CreateScriptedFrameInterface() override; + lldb::ScriptedThreadPlanInterfaceSP CreateScriptedThreadPlanInterface() override; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index a429ea848b7f..5ffb4423969c 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -252,7 +252,7 @@ static unsigned GetCXXMethodCVQuals(const DWARFDIE &subprogram, } static std::string MakeLLDBFuncAsmLabel(const DWARFDIE &die) { - char const *name = die.GetMangledName(/*substitute_name_allowed*/ false); + const char *name = die.GetMangledName(/*substitute_name_allowed*/ false); if (!name) return {}; @@ -286,7 +286,9 @@ static std::string MakeLLDBFuncAsmLabel(const DWARFDIE &die) { if (die_id == LLDB_INVALID_UID) return {}; - return FunctionCallLabel{/*module_id=*/module_id, + // Note, discriminator is added by Clang during mangling. + return FunctionCallLabel{/*discriminator=*/{}, + /*module_id=*/module_id, /*symbol_id=*/die_id, /*.lookup_name=*/name} .toString(); @@ -2230,6 +2232,18 @@ bool DWARFASTParserClang::CompleteRecordType(const DWARFDIE &die, for (DelayedAddObjCClassProperty &property : delayed_properties) property.Finalize(); } + } else if (Language::LanguageIsObjC( + static_cast<LanguageType>(die.GetAttributeValueAsUnsigned( + DW_AT_APPLE_runtime_class, eLanguageTypeUnknown)))) { + /// The forward declaration was C++ but the definition is Objective-C. + /// We currently don't handle such situations. In such cases, keep the + /// forward declaration without a definition to avoid violating Clang AST + /// invariants. + LLDB_LOG(GetLog(LLDBLog::Expressions), + "WARNING: Type completion aborted because forward declaration for " + "'{0}' is C++ while definition is Objective-C.", + llvm::StringRef(die.GetName())); + return {}; } if (!bases.empty()) { diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp index b15e0c15fedb..d3d0110d5e30 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -7,11 +7,14 @@ //===----------------------------------------------------------------------===// #include "SymbolFileDWARF.h" +#include "clang/Basic/ABI.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/DebugInfo/DWARF/DWARFAddressRange.h" #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" #include "llvm/Support/Casting.h" +#include "llvm/Support/Error.h" #include "llvm/Support/FileUtilities.h" #include "llvm/Support/FormatAdapters.h" #include "llvm/Support/Threading.h" @@ -23,6 +26,7 @@ #include "lldb/Core/Progress.h" #include "lldb/Core/Section.h" #include "lldb/Core/Value.h" +#include "lldb/Expression/Expression.h" #include "lldb/Utility/ArchSpec.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/RegularExpression.h" @@ -79,6 +83,7 @@ #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" +#include "llvm/Demangle/Demangle.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/FormatVariadic.h" @@ -2484,34 +2489,148 @@ bool SymbolFileDWARF::ResolveFunction(const DWARFDIE &orig_die, return false; } -DWARFDIE -SymbolFileDWARF::FindFunctionDefinition(const FunctionCallLabel &label) { - DWARFDIE definition; - Module::LookupInfo info(ConstString(label.lookup_name), - lldb::eFunctionNameTypeFull, - lldb::eLanguageTypeUnknown); - - m_index->GetFunctions(info, *this, {}, [&](DWARFDIE entry) { - if (entry.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0)) - return IterationAction::Continue; +static llvm::StringRef ClangToItaniumCtorKind(clang::CXXCtorType kind) { + switch (kind) { + case clang::CXXCtorType::Ctor_Complete: + return "C1"; + case clang::CXXCtorType::Ctor_Base: + return "C2"; + case clang::CXXCtorType::Ctor_Unified: + return "C4"; + case clang::CXXCtorType::Ctor_CopyingClosure: + case clang::CXXCtorType::Ctor_DefaultClosure: + case clang::CXXCtorType::Ctor_Comdat: + llvm_unreachable("Unexpected constructor kind."); + } +} - // We don't check whether the specification DIE for this function - // corresponds to the declaration DIE because the declaration might be in - // a type-unit but the definition in the compile-unit (and it's - // specifcation would point to the declaration in the compile-unit). We - // rely on the mangled name within the module to be enough to find us the - // unique definition. - definition = entry; - return IterationAction::Stop; - }); +static llvm::StringRef ClangToItaniumDtorKind(clang::CXXDtorType kind) { + switch (kind) { + case clang::CXXDtorType::Dtor_Deleting: + return "D0"; + case clang::CXXDtorType::Dtor_Complete: + return "D1"; + case clang::CXXDtorType::Dtor_Base: + return "D2"; + case clang::CXXDtorType::Dtor_Unified: + return "D4"; + case clang::CXXDtorType::Dtor_Comdat: + llvm_unreachable("Unexpected destructor kind."); + } +} + +static llvm::StringRef +GetItaniumCtorDtorVariant(llvm::StringRef discriminator) { + const bool is_ctor = discriminator.consume_front("C"); + if (!is_ctor && !discriminator.consume_front("D")) + return {}; + + uint64_t structor_kind; + if (!llvm::to_integer(discriminator, structor_kind)) + return {}; + + if (is_ctor) { + if (structor_kind > clang::CXXCtorType::Ctor_Unified) + return {}; + + return ClangToItaniumCtorKind( + static_cast<clang::CXXCtorType>(structor_kind)); + } + + if (structor_kind > clang::CXXDtorType::Dtor_Unified) + return {}; + + return ClangToItaniumDtorKind(static_cast<clang::CXXDtorType>(structor_kind)); +} + +llvm::Expected<DWARFDIE> +SymbolFileDWARF::FindFunctionDefinition(const FunctionCallLabel &label, + const DWARFDIE &declaration) { + auto do_lookup = [this](llvm::StringRef lookup_name) -> DWARFDIE { + DWARFDIE found; + Module::LookupInfo info(ConstString(lookup_name), + lldb::eFunctionNameTypeFull, + lldb::eLanguageTypeUnknown); + + m_index->GetFunctions(info, *this, {}, [&](DWARFDIE entry) { + if (entry.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0)) + return IterationAction::Continue; + + found = entry; + return IterationAction::Stop; + }); + + return found; + }; + + DWARFDIE definition = do_lookup(label.lookup_name); + if (definition.IsValid()) + return definition; + + // This is not a structor lookup. Nothing else to be done here. + if (label.discriminator.empty()) + return llvm::createStringError( + "no definition DIE found in this SymbolFile"); + + // We're doing a structor lookup. Maybe we didn't find the structor variant + // because the complete object structor was aliased to the base object + // structor. Try finding the alias instead. + // + // TODO: there are other reasons for why a subprogram definition might be + // missing. Ideally DWARF would tell us more details about which structor + // variant a DIE corresponds to and whether it's an alias. + auto subst_or_err = + CPlusPlusLanguage::SubstituteStructorAliases_ItaniumMangle( + label.lookup_name); + if (!subst_or_err) + return subst_or_err.takeError(); + + definition = do_lookup(*subst_or_err); + + if (!definition.IsValid()) + return llvm::createStringError( + "failed to find definition DIE for structor alias in fallback lookup"); return definition; } llvm::Expected<SymbolContext> -SymbolFileDWARF::ResolveFunctionCallLabel(const FunctionCallLabel &label) { +SymbolFileDWARF::ResolveFunctionCallLabel(FunctionCallLabel &label) { std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + if (!label.discriminator.empty()) { + llvm::StringRef from = label.discriminator[0] == 'C' ? "C4" : "D4"; + + llvm::StringRef variant = GetItaniumCtorDtorVariant(label.discriminator); + if (variant.empty()) + return llvm::createStringError( + "failed to get Itanium variant for discriminator"); + + if (from == variant) + return llvm::createStringError( + "tried substituting unified structor variant into label"); + + // If we failed to substitute unified mangled name, don't try to do a lookup + // using the unified name because there may be multiple definitions for it + // in the index, and we wouldn't know which one to choose. + auto subst_or_err = CPlusPlusLanguage::SubstituteStructor_ItaniumMangle( + label.lookup_name, from, variant); + if (!subst_or_err) + return llvm::joinErrors( + llvm::createStringError(llvm::formatv( + "failed to substitute {0} for {1} in mangled name {2}:", from, + variant, label.lookup_name)), + subst_or_err.takeError()); + + if (!*subst_or_err) + return llvm::createStringError( + llvm::formatv("got invalid substituted mangled named (substituted " + "{0} for {1} in mangled name {2})", + from, variant, label.lookup_name)); + + label.lookup_name = subst_or_err->GetStringRef(); + } + DWARFDIE die = GetDIE(label.symbol_id); if (!die.IsValid()) return llvm::createStringError( @@ -2520,11 +2639,13 @@ SymbolFileDWARF::ResolveFunctionCallLabel(const FunctionCallLabel &label) { // Label was created using a declaration DIE. Need to fetch the definition // to resolve the function call. if (die.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0)) { - auto definition = FindFunctionDefinition(label); - if (!definition) - return llvm::createStringError("failed to find definition DIE"); + auto die_or_err = FindFunctionDefinition(label, die); + if (!die_or_err) + return llvm::joinErrors( + llvm::createStringError("failed to find definition DIE:"), + die_or_err.takeError()); - die = std::move(definition); + die = std::move(*die_or_err); } SymbolContextList sc_list; @@ -4502,9 +4623,8 @@ void SymbolFileDWARF::GetCompileOptions( } } -std::pair<uint32_t, uint32_t> SymbolFileDWARF::GetDwoFileCounts() { - uint32_t total_dwo_count = 0; - uint32_t loaded_dwo_count = 0; +DWOStats SymbolFileDWARF::GetDwoStats() { + DWOStats stats; DWARFDebugInfo &info = DebugInfo(); const size_t num_cus = info.GetNumUnits(); @@ -4517,16 +4637,21 @@ std::pair<uint32_t, uint32_t> SymbolFileDWARF::GetDwoFileCounts() { if (!dwarf_cu->GetDWOId().has_value()) continue; - total_dwo_count++; + stats.dwo_file_count++; // If we have a DWO symbol file, that means we were able to successfully // load it. SymbolFile *dwo_symfile = dwarf_cu->GetDwoSymbolFile(/*load_all_debug_info=*/false); if (dwo_symfile) { - loaded_dwo_count++; + stats.loaded_dwo_file_count++; } + + // Check if this unit has a DWO load error, false by default. + const Status &dwo_error = dwarf_cu->GetDwoError(); + if (dwo_error.Fail()) + stats.dwo_error_count++; } - return {loaded_dwo_count, total_dwo_count}; + return stats; } diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h index d7db8a3c0869..85306d8b4fb5 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h @@ -283,10 +283,11 @@ public: bool GetSeparateDebugInfo(StructuredData::Dictionary &d, bool errors_only, bool load_all_debug_info = false) override; - // Gets a pair of loaded and total dwo file counts. - // For split-dwarf files, this reports the counts for successfully loaded DWO - // CUs and total DWO CUs. For non-split-dwarf files, this reports 0 for both. - std::pair<uint32_t, uint32_t> GetDwoFileCounts() override; + /// Gets statistics about dwo files associated with this symbol file. + /// For split-dwarf files, this reports the counts for successfully loaded DWO + /// CUs, total DWO CUs, and the number of DWO CUs with loading errors. + /// For non-split-dwarf files, this reports 0 for all. + DWOStats GetDwoStats() override; DWARFContext &GetDWARFContext() { return m_context; } @@ -378,7 +379,9 @@ private: /// SymbolFile. /// /// \returns A valid definition DIE on success. - DWARFDIE FindFunctionDefinition(const FunctionCallLabel &label); + llvm::Expected<DWARFDIE> + FindFunctionDefinition(const FunctionCallLabel &label, + const DWARFDIE &declaration); protected: SymbolFileDWARF(const SymbolFileDWARF &) = delete; @@ -445,7 +448,7 @@ protected: DIEArray &&variable_dies); llvm::Expected<SymbolContext> - ResolveFunctionCallLabel(const FunctionCallLabel &label) override; + ResolveFunctionCallLabel(FunctionCallLabel &label) override; // Given a die_offset, figure out the symbol context representing that die. bool ResolveFunction(const DWARFDIE &die, bool include_inlines, diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp index 9d7452a1988f..8b8229a7020c 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp @@ -1603,8 +1603,8 @@ void SymbolFileDWARFDebugMap::GetCompileOptions( }); } -llvm::Expected<SymbolContext> SymbolFileDWARFDebugMap::ResolveFunctionCallLabel( - const FunctionCallLabel &label) { +llvm::Expected<SymbolContext> +SymbolFileDWARFDebugMap::ResolveFunctionCallLabel(FunctionCallLabel &label) { const uint64_t oso_idx = GetOSOIndexFromUserID(label.symbol_id); SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx); if (!oso_dwarf) diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h index e1f1df23951c..bce1ed2671af 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h @@ -145,7 +145,7 @@ public: GetCompileOptions(std::unordered_map<lldb::CompUnitSP, Args> &args) override; llvm::Expected<SymbolContext> - ResolveFunctionCallLabel(const FunctionCallLabel &label) override; + ResolveFunctionCallLabel(FunctionCallLabel &label) override; protected: enum { kHaveInitializedOSOs = (1 << 0), kNumFlags }; diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp index 709281cb3270..933c4361d93d 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp @@ -1169,6 +1169,7 @@ clang::QualType PdbAstBuilder::CreateEnumType(PdbTypeSymId id, clang::QualType PdbAstBuilder::CreateArrayType(const ArrayRecord &ar) { clang::QualType element_type = GetOrCreateType(ar.ElementType); + TypeSystemClang::RequireCompleteType(ToCompilerType(element_type)); SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( m_clang.GetSymbolFile()->GetBackingSymbolFile()); diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp index 112eb06e462f..e99c585d7eb1 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp @@ -1624,6 +1624,8 @@ size_t SymbolFileNativePDB::ParseBlocksRecursive(Function &func) { for (uint64_t uid : remove_uids) { m_inline_sites.erase(uid); } + + func.GetBlock(false).SetBlockInfoHasBeenParsed(true, true); return count; } @@ -1655,22 +1657,62 @@ void SymbolFileNativePDB::DumpClangAST(Stream &s, llvm::StringRef filter) { clang->GetNativePDBParser()->Dump(s, filter); } -void SymbolFileNativePDB::CacheFunctionNames() { - if (!m_func_full_names.IsEmpty()) +void SymbolFileNativePDB::CacheGlobalBaseNames() { + if (!m_func_full_names.IsEmpty() || !m_global_variable_base_names.IsEmpty()) return; // (segment, code offset) -> gid - std::map<std::pair<uint16_t, uint32_t>, uint32_t> addr_ids; + std::map<std::pair<uint16_t, uint32_t>, uint32_t> func_addr_ids; - // First, find all function references in the globals table. + // First, look through all items in the globals table. for (const uint32_t gid : m_index->globals().getGlobalsTable()) { - CVSymbol ref_sym = m_index->symrecords().readRecord(gid); - auto kind = ref_sym.kind(); + CVSymbol sym = m_index->symrecords().readRecord(gid); + auto kind = sym.kind(); + + // If this is a global variable, we only need to look at the name + llvm::StringRef name; + switch (kind) { + case SymbolKind::S_GDATA32: + case SymbolKind::S_LDATA32: { + DataSym data = + llvm::cantFail(SymbolDeserializer::deserializeAs<DataSym>(sym)); + name = data.Name; + break; + } + case SymbolKind::S_GTHREAD32: + case SymbolKind::S_LTHREAD32: { + ThreadLocalDataSym data = llvm::cantFail( + SymbolDeserializer::deserializeAs<ThreadLocalDataSym>(sym)); + name = data.Name; + break; + } + case SymbolKind::S_CONSTANT: { + ConstantSym data = + llvm::cantFail(SymbolDeserializer::deserializeAs<ConstantSym>(sym)); + name = data.Name; + break; + } + default: + break; + } + + if (!name.empty()) { + llvm::StringRef base = MSVCUndecoratedNameParser::DropScope(name); + if (base.empty()) + base = name; + + m_global_variable_base_names.Append(ConstString(base), gid); + continue; + } + if (kind != S_PROCREF && kind != S_LPROCREF) continue; + // For functions, we need to follow the reference to the procedure and look + // at the type + ProcRefSym ref = - llvm::cantFail(SymbolDeserializer::deserializeAs<ProcRefSym>(ref_sym)); + llvm::cantFail(SymbolDeserializer::deserializeAs<ProcRefSym>(sym)); if (ref.Name.empty()) continue; @@ -1694,7 +1736,7 @@ void SymbolFileNativePDB::CacheFunctionNames() { // The function/procedure symbol only contains the demangled name. // The mangled names are in the publics table. Save the address of this // function to lookup the mangled name later. - addr_ids.emplace(std::make_pair(proc.Segment, proc.CodeOffset), gid); + func_addr_ids.emplace(std::make_pair(proc.Segment, proc.CodeOffset), gid); llvm::StringRef basename = MSVCUndecoratedNameParser::DropScope(proc.Name); if (basename.empty()) @@ -1729,43 +1771,47 @@ void SymbolFileNativePDB::CacheFunctionNames() { continue; // Check if this symbol is for one of our functions. - auto it = addr_ids.find({pub.Segment, pub.Offset}); - if (it != addr_ids.end()) + auto it = func_addr_ids.find({pub.Segment, pub.Offset}); + if (it != func_addr_ids.end()) m_func_full_names.Append(ConstString(pub.Name), it->second); } // Sort them before value searching is working properly. - m_func_full_names.Sort(); + m_func_full_names.Sort(std::less<uint32_t>()); m_func_full_names.SizeToFit(); - m_func_method_names.Sort(); + m_func_method_names.Sort(std::less<uint32_t>()); m_func_method_names.SizeToFit(); - m_func_base_names.Sort(); + m_func_base_names.Sort(std::less<uint32_t>()); m_func_base_names.SizeToFit(); + m_global_variable_base_names.Sort(std::less<uint32_t>()); + m_global_variable_base_names.SizeToFit(); } void SymbolFileNativePDB::FindGlobalVariables( ConstString name, const CompilerDeclContext &parent_decl_ctx, uint32_t max_matches, VariableList &variables) { std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); - using SymbolAndOffset = std::pair<uint32_t, llvm::codeview::CVSymbol>; - std::vector<SymbolAndOffset> results = m_index->globals().findRecordsByName( - name.GetStringRef(), m_index->symrecords()); - for (const SymbolAndOffset &result : results) { - switch (result.second.kind()) { - case SymbolKind::S_GDATA32: - case SymbolKind::S_LDATA32: - case SymbolKind::S_GTHREAD32: - case SymbolKind::S_LTHREAD32: - case SymbolKind::S_CONSTANT: { - PdbGlobalSymId global(result.first, false); - if (VariableSP var = GetOrCreateGlobalVariable(global)) - variables.AddVariable(var); - break; - } - default: + CacheGlobalBaseNames(); + + std::vector<uint32_t> results; + m_global_variable_base_names.GetValues(name, results); + + size_t n_matches = 0; + for (uint32_t gid : results) { + PdbGlobalSymId global(gid, false); + + if (parent_decl_ctx.IsValid() && + GetDeclContextContainingUID(toOpaqueUid(global)) != parent_decl_ctx) continue; - } + + VariableSP var = GetOrCreateGlobalVariable(global); + if (!var) + continue; + variables.AddVariable(var); + + if (++n_matches >= max_matches) + break; } } @@ -1783,7 +1829,7 @@ void SymbolFileNativePDB::FindFunctions( name_type_mask & eFunctionNameTypeBase || name_type_mask & eFunctionNameTypeMethod)) return; - CacheFunctionNames(); + CacheGlobalBaseNames(); std::set<uint32_t> resolved_ids; // avoid duplicate lookups auto resolve_from = [&](UniqueCStringMap<uint32_t> &Names) { @@ -2426,7 +2472,7 @@ void SymbolFileNativePDB::BuildParentMap() { // After calling Append(), the type-name map needs to be sorted again to be // able to look up a type by its name. - m_type_base_names.Sort(); + m_type_base_names.Sort(std::less<uint32_t>()); // Now that we know the forward -> full mapping of all type indices, we can // re-write all the indices. At the end of this process, we want a mapping diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h index cfa00416d967..095b40c72c52 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h +++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h @@ -260,7 +260,10 @@ private: std::vector<CompilerContext> GetContextForType(llvm::codeview::TypeIndex ti); - void CacheFunctionNames(); + /// Caches the basenames of symbols found in the globals stream. + /// + /// This includes functions and global variables + void CacheGlobalBaseNames(); void CacheUdtDeclarations(); llvm::Expected<Declaration> ResolveUdtDeclaration(PdbTypeSymId type_id); @@ -306,6 +309,9 @@ private: lldb_private::UniqueCStringMap<uint32_t> m_func_base_names; /// method basename -> Global ID(s) lldb_private::UniqueCStringMap<uint32_t> m_func_method_names; + + /// global variable basename -> Global ID(s) + lldb_private::UniqueCStringMap<uint32_t> m_global_variable_base_names; }; } // namespace npdb diff --git a/lldb/source/Protocol/MCP/CMakeLists.txt b/lldb/source/Protocol/MCP/CMakeLists.txt index a73e7e6a7cab..5258cb61a7d1 100644 --- a/lldb/source/Protocol/MCP/CMakeLists.txt +++ b/lldb/source/Protocol/MCP/CMakeLists.txt @@ -3,10 +3,12 @@ add_lldb_library(lldbProtocolMCP NO_PLUGIN_DEPENDENCIES Protocol.cpp Server.cpp Tool.cpp + Transport.cpp LINK_COMPONENTS Support LINK_LIBS + lldbHost lldbUtility ) diff --git a/lldb/source/Protocol/MCP/Protocol.cpp b/lldb/source/Protocol/MCP/Protocol.cpp index 65ddfaee7016..0988f456adc2 100644 --- a/lldb/source/Protocol/MCP/Protocol.cpp +++ b/lldb/source/Protocol/MCP/Protocol.cpp @@ -167,32 +167,6 @@ bool operator==(const Notification &a, const Notification &b) { return a.method == b.method && a.params == b.params; } -llvm::json::Value toJSON(const ToolCapability &TC) { - return llvm::json::Object{{"listChanged", TC.listChanged}}; -} - -bool fromJSON(const llvm::json::Value &V, ToolCapability &TC, - llvm::json::Path P) { - llvm::json::ObjectMapper O(V, P); - return O && O.map("listChanged", TC.listChanged); -} - -llvm::json::Value toJSON(const ResourceCapability &RC) { - return llvm::json::Object{{"listChanged", RC.listChanged}, - {"subscribe", RC.subscribe}}; -} - -bool fromJSON(const llvm::json::Value &V, ResourceCapability &RC, - llvm::json::Path P) { - llvm::json::ObjectMapper O(V, P); - return O && O.map("listChanged", RC.listChanged) && - O.map("subscribe", RC.subscribe); -} - -llvm::json::Value toJSON(const Capabilities &C) { - return llvm::json::Object{{"tools", C.tools}, {"resources", C.resources}}; -} - bool fromJSON(const llvm::json::Value &V, Resource &R, llvm::json::Path P) { llvm::json::ObjectMapper O(V, P); return O && O.map("uri", R.uri) && O.map("name", R.name) && @@ -209,30 +183,25 @@ llvm::json::Value toJSON(const Resource &R) { return Result; } -bool fromJSON(const llvm::json::Value &V, Capabilities &C, llvm::json::Path P) { - llvm::json::ObjectMapper O(V, P); - return O && O.map("tools", C.tools); -} - -llvm::json::Value toJSON(const ResourceContents &RC) { +llvm::json::Value toJSON(const TextResourceContents &RC) { llvm::json::Object Result{{"uri", RC.uri}, {"text", RC.text}}; if (!RC.mimeType.empty()) Result.insert({"mimeType", RC.mimeType}); return Result; } -bool fromJSON(const llvm::json::Value &V, ResourceContents &RC, +bool fromJSON(const llvm::json::Value &V, TextResourceContents &RC, llvm::json::Path P) { llvm::json::ObjectMapper O(V, P); return O && O.map("uri", RC.uri) && O.map("text", RC.text) && O.mapOptional("mimeType", RC.mimeType); } -llvm::json::Value toJSON(const ResourceResult &RR) { +llvm::json::Value toJSON(const ReadResourceResult &RR) { return llvm::json::Object{{"contents", RR.contents}}; } -bool fromJSON(const llvm::json::Value &V, ResourceResult &RR, +bool fromJSON(const llvm::json::Value &V, ReadResourceResult &RR, llvm::json::Path P) { llvm::json::ObjectMapper O(V, P); return O && O.map("contents", RR.contents); @@ -247,15 +216,6 @@ bool fromJSON(const llvm::json::Value &V, TextContent &TC, llvm::json::Path P) { return O && O.map("text", TC.text); } -llvm::json::Value toJSON(const TextResult &TR) { - return llvm::json::Object{{"content", TR.content}, {"isError", TR.isError}}; -} - -bool fromJSON(const llvm::json::Value &V, TextResult &TR, llvm::json::Path P) { - llvm::json::ObjectMapper O(V, P); - return O && O.map("content", TR.content) && O.map("isError", TR.isError); -} - llvm::json::Value toJSON(const ToolDefinition &TD) { llvm::json::Object Result{{"name", TD.name}}; if (!TD.description.empty()) @@ -325,4 +285,159 @@ bool fromJSON(const llvm::json::Value &V, Message &M, llvm::json::Path P) { return false; } +json::Value toJSON(const Implementation &I) { + json::Object result{{"name", I.name}, {"version", I.version}}; + + if (!I.title.empty()) + result.insert({"title", I.title}); + + return result; +} + +bool fromJSON(const json::Value &V, Implementation &I, json::Path P) { + json::ObjectMapper O(V, P); + return O && O.map("name", I.name) && O.mapOptional("title", I.title) && + O.mapOptional("version", I.version); +} + +json::Value toJSON(const ClientCapabilities &C) { return json::Object{}; } + +bool fromJSON(const json::Value &, ClientCapabilities &, json::Path) { + return true; +} + +json::Value toJSON(const ServerCapabilities &C) { + json::Object result{}; + + if (C.supportsToolsList) + result.insert({"tools", json::Object{{"listChanged", true}}}); + + if (C.supportsResourcesList || C.supportsResourcesSubscribe) { + json::Object resources; + if (C.supportsResourcesList) + resources.insert({"listChanged", true}); + if (C.supportsResourcesSubscribe) + resources.insert({"subscribe", true}); + result.insert({"resources", std::move(resources)}); + } + + if (C.supportsCompletions) + result.insert({"completions", json::Object{}}); + + if (C.supportsLogging) + result.insert({"logging", json::Object{}}); + + return result; +} + +bool fromJSON(const json::Value &V, ServerCapabilities &C, json::Path P) { + const json::Object *O = V.getAsObject(); + if (!O) { + P.report("expected object"); + return false; + } + + if (O->find("tools") != O->end()) + C.supportsToolsList = true; + + return true; +} + +json::Value toJSON(const InitializeParams &P) { + return json::Object{ + {"protocolVersion", P.protocolVersion}, + {"capabilities", P.capabilities}, + {"clientInfo", P.clientInfo}, + }; +} + +bool fromJSON(const json::Value &V, InitializeParams &I, json::Path P) { + json::ObjectMapper O(V, P); + return O && O.map("protocolVersion", I.protocolVersion) && + O.map("capabilities", I.capabilities) && + O.map("clientInfo", I.clientInfo); +} + +json::Value toJSON(const InitializeResult &R) { + json::Object result{{"protocolVersion", R.protocolVersion}, + {"capabilities", R.capabilities}, + {"serverInfo", R.serverInfo}}; + + if (!R.instructions.empty()) + result.insert({"instructions", R.instructions}); + + return result; +} + +bool fromJSON(const json::Value &V, InitializeResult &R, json::Path P) { + json::ObjectMapper O(V, P); + return O && O.map("protocolVersion", R.protocolVersion) && + O.map("capabilities", R.capabilities) && + O.map("serverInfo", R.serverInfo) && + O.mapOptional("instructions", R.instructions); +} + +json::Value toJSON(const ListToolsResult &R) { + return json::Object{{"tools", R.tools}}; +} + +bool fromJSON(const json::Value &V, ListToolsResult &R, json::Path P) { + json::ObjectMapper O(V, P); + return O && O.map("tools", R.tools); +} + +json::Value toJSON(const CallToolResult &R) { + json::Object result{{"content", R.content}}; + + if (R.isError) + result.insert({"isError", R.isError}); + if (R.structuredContent) + result.insert({"structuredContent", *R.structuredContent}); + + return result; +} + +bool fromJSON(const json::Value &V, CallToolResult &R, json::Path P) { + json::ObjectMapper O(V, P); + return O && O.map("content", R.content) && + O.mapOptional("isError", R.isError) && + mapRaw(V, "structuredContent", R.structuredContent, P); +} + +json::Value toJSON(const CallToolParams &R) { + json::Object result{{"name", R.name}}; + + if (R.arguments) + result.insert({"arguments", *R.arguments}); + + return result; +} + +bool fromJSON(const json::Value &V, CallToolParams &R, json::Path P) { + json::ObjectMapper O(V, P); + return O && O.map("name", R.name) && mapRaw(V, "arguments", R.arguments, P); +} + +json::Value toJSON(const ReadResourceParams &R) { + return json::Object{{"uri", R.uri}}; +} + +bool fromJSON(const json::Value &V, ReadResourceParams &R, json::Path P) { + json::ObjectMapper O(V, P); + return O && O.map("uri", R.uri); +} + +json::Value toJSON(const ListResourcesResult &R) { + return json::Object{{"resources", R.resources}}; +} + +bool fromJSON(const json::Value &V, ListResourcesResult &R, json::Path P) { + json::ObjectMapper O(V, P); + return O && O.map("resources", R.resources); +} + +json::Value toJSON(const Void &R) { return json::Object{}; } + +bool fromJSON(const json::Value &V, Void &R, json::Path P) { return true; } + } // namespace lldb_protocol::mcp diff --git a/lldb/source/Protocol/MCP/Server.cpp b/lldb/source/Protocol/MCP/Server.cpp index a9c1482e3e37..f3489c620832 100644 --- a/lldb/source/Protocol/MCP/Server.cpp +++ b/lldb/source/Protocol/MCP/Server.cpp @@ -7,13 +7,115 @@ //===----------------------------------------------------------------------===// #include "lldb/Protocol/MCP/Server.h" +#include "lldb/Host/File.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/JSONTransport.h" #include "lldb/Protocol/MCP/MCPError.h" +#include "lldb/Protocol/MCP/Protocol.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/Signals.h" -using namespace lldb_protocol::mcp; using namespace llvm; +using namespace lldb_private; +using namespace lldb_protocol::mcp; + +ServerInfoHandle::ServerInfoHandle() : ServerInfoHandle("") {} + +ServerInfoHandle::ServerInfoHandle(StringRef filename) : m_filename(filename) { + if (!m_filename.empty()) + sys::RemoveFileOnSignal(m_filename); +} + +ServerInfoHandle::~ServerInfoHandle() { + if (m_filename.empty()) + return; + + sys::fs::remove(m_filename); + sys::DontRemoveFileOnSignal(m_filename); + m_filename.clear(); +} + +ServerInfoHandle::ServerInfoHandle(ServerInfoHandle &&other) + : m_filename(other.m_filename) { + *this = std::move(other); +} + +ServerInfoHandle & +ServerInfoHandle::operator=(ServerInfoHandle &&other) noexcept { + m_filename = other.m_filename; + other.m_filename.clear(); + return *this; +} + +json::Value lldb_protocol::mcp::toJSON(const ServerInfo &SM) { + return json::Object{{"connection_uri", SM.connection_uri}}; +} + +bool lldb_protocol::mcp::fromJSON(const json::Value &V, ServerInfo &SM, + json::Path P) { + json::ObjectMapper O(V, P); + return O && O.map("connection_uri", SM.connection_uri); +} -Server::Server(std::string name, std::string version) - : m_name(std::move(name)), m_version(std::move(version)) { +Expected<ServerInfoHandle> ServerInfo::Write(const ServerInfo &info) { + std::string buf = formatv("{0}", toJSON(info)).str(); + size_t num_bytes = buf.size(); + + FileSpec user_lldb_dir = HostInfo::GetUserLLDBDir(); + + Status error(sys::fs::create_directory(user_lldb_dir.GetPath())); + if (error.Fail()) + return error.takeError(); + + FileSpec mcp_registry_entry_path = user_lldb_dir.CopyByAppendingPathComponent( + formatv("lldb-mcp-{0}.json", getpid()).str()); + + const File::OpenOptions flags = File::eOpenOptionWriteOnly | + File::eOpenOptionCanCreate | + File::eOpenOptionTruncate; + Expected<lldb::FileUP> file = + FileSystem::Instance().Open(mcp_registry_entry_path, flags); + if (!file) + return file.takeError(); + if (llvm::Error error = (*file)->Write(buf.data(), num_bytes).takeError()) + return error; + return ServerInfoHandle{mcp_registry_entry_path.GetPath()}; +} + +Expected<std::vector<ServerInfo>> ServerInfo::Load() { + namespace path = llvm::sys::path; + FileSpec user_lldb_dir = HostInfo::GetUserLLDBDir(); + FileSystem &fs = FileSystem::Instance(); + std::error_code EC; + vfs::directory_iterator it = fs.DirBegin(user_lldb_dir, EC); + vfs::directory_iterator end; + std::vector<ServerInfo> infos; + for (; it != end && !EC; it.increment(EC)) { + auto &entry = *it; + auto path = entry.path(); + auto name = path::filename(path); + if (!name.starts_with("lldb-mcp-") || !name.ends_with(".json")) + continue; + + auto buffer = fs.CreateDataBuffer(path); + auto info = json::parse<ServerInfo>(toStringRef(buffer->GetData())); + if (!info) + return info.takeError(); + + infos.emplace_back(std::move(*info)); + } + + return infos; +} + +Server::Server(std::string name, std::string version, + std::unique_ptr<MCPTransport> transport_up, + lldb_private::MainLoop &loop) + : m_name(std::move(name)), m_version(std::move(version)), + m_transport_up(std::move(transport_up)), m_loop(loop) { AddRequestHandlers(); } @@ -30,7 +132,7 @@ void Server::AddRequestHandlers() { this, std::placeholders::_1)); } -llvm::Expected<Response> Server::Handle(Request request) { +llvm::Expected<Response> Server::Handle(const Request &request) { auto it = m_request_handlers.find(request.method); if (it != m_request_handlers.end()) { llvm::Expected<Response> response = it->second(request); @@ -44,7 +146,7 @@ llvm::Expected<Response> Server::Handle(Request request) { llvm::formatv("no handler for request: {0}", request.method).str()); } -void Server::Handle(Notification notification) { +void Server::Handle(const Notification ¬ification) { auto it = m_notification_handlers.find(notification.method); if (it != m_notification_handlers.end()) { it->second(notification); @@ -52,49 +154,7 @@ void Server::Handle(Notification notification) { } } -llvm::Expected<std::optional<Message>> -Server::HandleData(llvm::StringRef data) { - auto message = llvm::json::parse<Message>(/*JSON=*/data); - if (!message) - return message.takeError(); - - if (const Request *request = std::get_if<Request>(&(*message))) { - llvm::Expected<Response> response = Handle(*request); - - // Handle failures by converting them into an Error message. - if (!response) { - Error protocol_error; - llvm::handleAllErrors( - response.takeError(), - [&](const MCPError &err) { protocol_error = err.toProtocolError(); }, - [&](const llvm::ErrorInfoBase &err) { - protocol_error.code = MCPError::kInternalError; - protocol_error.message = err.message(); - }); - Response error_response; - error_response.id = request->id; - error_response.result = std::move(protocol_error); - return error_response; - } - - return *response; - } - - if (const Notification *notification = - std::get_if<Notification>(&(*message))) { - Handle(*notification); - return std::nullopt; - } - - if (std::get_if<Response>(&(*message))) - return llvm::createStringError("unexpected MCP message: response"); - - llvm_unreachable("all message types handled"); -} - void Server::AddTool(std::unique_ptr<Tool> tool) { - std::lock_guard<std::mutex> guard(m_mutex); - if (!tool) return; m_tools[tool->GetName()] = std::move(tool); @@ -102,42 +162,39 @@ void Server::AddTool(std::unique_ptr<Tool> tool) { void Server::AddResourceProvider( std::unique_ptr<ResourceProvider> resource_provider) { - std::lock_guard<std::mutex> guard(m_mutex); - if (!resource_provider) return; m_resource_providers.push_back(std::move(resource_provider)); } void Server::AddRequestHandler(llvm::StringRef method, RequestHandler handler) { - std::lock_guard<std::mutex> guard(m_mutex); m_request_handlers[method] = std::move(handler); } void Server::AddNotificationHandler(llvm::StringRef method, NotificationHandler handler) { - std::lock_guard<std::mutex> guard(m_mutex); m_notification_handlers[method] = std::move(handler); } llvm::Expected<Response> Server::InitializeHandler(const Request &request) { Response response; - response.result = llvm::json::Object{ - {"protocolVersion", mcp::kProtocolVersion}, - {"capabilities", GetCapabilities()}, - {"serverInfo", - llvm::json::Object{{"name", m_name}, {"version", m_version}}}}; + InitializeResult result; + result.protocolVersion = mcp::kProtocolVersion; + result.capabilities = GetCapabilities(); + result.serverInfo.name = m_name; + result.serverInfo.version = m_version; + response.result = std::move(result); return response; } llvm::Expected<Response> Server::ToolsListHandler(const Request &request) { Response response; - llvm::json::Array tools; + ListToolsResult result; for (const auto &tool : m_tools) - tools.emplace_back(toJSON(tool.second->GetDefinition())); + result.tools.emplace_back(tool.second->GetDefinition()); - response.result = llvm::json::Object{{"tools", std::move(tools)}}; + response.result = std::move(result); return response; } @@ -147,16 +204,12 @@ llvm::Expected<Response> Server::ToolsCallHandler(const Request &request) { if (!request.params) return llvm::createStringError("no tool parameters"); + CallToolParams params; + json::Path::Root root("params"); + if (!fromJSON(request.params, params, root)) + return root.getError(); - const json::Object *param_obj = request.params->getAsObject(); - if (!param_obj) - return llvm::createStringError("no tool parameters"); - - const json::Value *name = param_obj->get("name"); - if (!name) - return llvm::createStringError("no tool name"); - - llvm::StringRef tool_name = name->getAsString().value_or(""); + llvm::StringRef tool_name = params.name; if (tool_name.empty()) return llvm::createStringError("no tool name"); @@ -165,10 +218,10 @@ llvm::Expected<Response> Server::ToolsCallHandler(const Request &request) { return llvm::createStringError(llvm::formatv("no tool \"{0}\"", tool_name)); ToolArguments tool_args; - if (const json::Value *args = param_obj->get("arguments")) - tool_args = *args; + if (params.arguments) + tool_args = *params.arguments; - llvm::Expected<TextResult> text_result = it->second->Call(tool_args); + llvm::Expected<CallToolResult> text_result = it->second->Call(tool_args); if (!text_result) return text_result.takeError(); @@ -180,15 +233,13 @@ llvm::Expected<Response> Server::ToolsCallHandler(const Request &request) { llvm::Expected<Response> Server::ResourcesListHandler(const Request &request) { Response response; - llvm::json::Array resources; - - std::lock_guard<std::mutex> guard(m_mutex); + ListResourcesResult result; for (std::unique_ptr<ResourceProvider> &resource_provider_up : - m_resource_providers) { + m_resource_providers) for (const Resource &resource : resource_provider_up->GetResources()) - resources.push_back(resource); - } - response.result = llvm::json::Object{{"resources", std::move(resources)}}; + result.resources.push_back(resource); + + response.result = std::move(result); return response; } @@ -199,22 +250,18 @@ llvm::Expected<Response> Server::ResourcesReadHandler(const Request &request) { if (!request.params) return llvm::createStringError("no resource parameters"); - const json::Object *param_obj = request.params->getAsObject(); - if (!param_obj) - return llvm::createStringError("no resource parameters"); - - const json::Value *uri = param_obj->get("uri"); - if (!uri) - return llvm::createStringError("no resource uri"); + ReadResourceParams params; + json::Path::Root root("params"); + if (!fromJSON(request.params, params, root)) + return root.getError(); - llvm::StringRef uri_str = uri->getAsString().value_or(""); + llvm::StringRef uri_str = params.uri; if (uri_str.empty()) return llvm::createStringError("no resource uri"); - std::lock_guard<std::mutex> guard(m_mutex); for (std::unique_ptr<ResourceProvider> &resource_provider_up : m_resource_providers) { - llvm::Expected<ResourceResult> result = + llvm::Expected<ReadResourceResult> result = resource_provider_up->ReadResource(uri_str); if (result.errorIsA<UnsupportedURI>()) { llvm::consumeError(result.takeError()); @@ -232,3 +279,71 @@ llvm::Expected<Response> Server::ResourcesReadHandler(const Request &request) { llvm::formatv("no resource handler for uri: {0}", uri_str).str(), MCPError::kResourceNotFound); } + +ServerCapabilities Server::GetCapabilities() { + lldb_protocol::mcp::ServerCapabilities capabilities; + capabilities.supportsToolsList = true; + // FIXME: Support sending notifications when a debugger/target are + // added/removed. + capabilities.supportsResourcesList = false; + return capabilities; +} + +llvm::Error Server::Run() { + auto handle = m_transport_up->RegisterMessageHandler(m_loop, *this); + if (!handle) + return handle.takeError(); + + lldb_private::Status status = m_loop.Run(); + if (status.Fail()) + return status.takeError(); + + return llvm::Error::success(); +} + +void Server::Received(const Request &request) { + auto SendResponse = [this](const Response &response) { + if (llvm::Error error = m_transport_up->Send(response)) + m_transport_up->Log(llvm::toString(std::move(error))); + }; + + llvm::Expected<Response> response = Handle(request); + if (response) + return SendResponse(*response); + + lldb_protocol::mcp::Error protocol_error; + llvm::handleAllErrors( + response.takeError(), + [&](const MCPError &err) { protocol_error = err.toProtocolError(); }, + [&](const llvm::ErrorInfoBase &err) { + protocol_error.code = MCPError::kInternalError; + protocol_error.message = err.message(); + }); + Response error_response; + error_response.id = request.id; + error_response.result = std::move(protocol_error); + SendResponse(error_response); +} + +void Server::Received(const Response &response) { + m_transport_up->Log("unexpected MCP message: response"); +} + +void Server::Received(const Notification ¬ification) { + Handle(notification); +} + +void Server::OnError(llvm::Error error) { + m_transport_up->Log(llvm::toString(std::move(error))); + TerminateLoop(); +} + +void Server::OnClosed() { + m_transport_up->Log("EOF"); + TerminateLoop(); +} + +void Server::TerminateLoop() { + m_loop.AddPendingCallback( + [](lldb_private::MainLoopBase &loop) { loop.RequestTermination(); }); +} diff --git a/lldb/source/Protocol/MCP/Transport.cpp b/lldb/source/Protocol/MCP/Transport.cpp new file mode 100644 index 000000000000..cccdc3b5bd65 --- /dev/null +++ b/lldb/source/Protocol/MCP/Transport.cpp @@ -0,0 +1,23 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Protocol/MCP/Transport.h" +#include "llvm/ADT/StringRef.h" +#include <utility> + +using namespace lldb_protocol::mcp; +using namespace llvm; + +Transport::Transport(lldb::IOObjectSP in, lldb::IOObjectSP out, + LogCallback log_callback) + : JSONRPCTransport(in, out), m_log_callback(std::move(log_callback)) {} + +void Transport::Log(StringRef message) { + if (m_log_callback) + m_log_callback(message); +} diff --git a/lldb/source/Symbol/ObjectFile.cpp b/lldb/source/Symbol/ObjectFile.cpp index 7efce2a03550..9a79b3c62762 100644 --- a/lldb/source/Symbol/ObjectFile.cpp +++ b/lldb/source/Symbol/ObjectFile.cpp @@ -663,7 +663,7 @@ ObjectFile::GetDWARFSectionTypeFromName(llvm::StringRef name) { .Case("rnglists.dwo", eSectionTypeDWARFDebugRngListsDwo) .Case("str", eSectionTypeDWARFDebugStr) .Case("str.dwo", eSectionTypeDWARFDebugStrDwo) - .Case("str_offsets", eSectionTypeDWARFDebugStrOffsets) + .Cases("str_offsets", "str_offs", eSectionTypeDWARFDebugStrOffsets) .Case("str_offsets.dwo", eSectionTypeDWARFDebugStrOffsetsDwo) .Case("tu_index", eSectionTypeDWARFDebugTuIndex) .Case("types", eSectionTypeDWARFDebugTypes) diff --git a/lldb/source/Target/ABI.cpp b/lldb/source/Target/ABI.cpp index b86fef6bf03e..3c5107434014 100644 --- a/lldb/source/Target/ABI.cpp +++ b/lldb/source/Target/ABI.cpp @@ -232,13 +232,13 @@ bool ABI::GetFallbackRegisterLocation( } std::unique_ptr<llvm::MCRegisterInfo> ABI::MakeMCRegisterInfo(const ArchSpec &arch) { - std::string triple = arch.GetTriple().getTriple(); + const llvm::Triple &triple = arch.GetTriple(); std::string lookup_error; const llvm::Target *target = llvm::TargetRegistry::lookupTarget(triple, lookup_error); if (!target) { LLDB_LOG(GetLog(LLDBLog::Process), - "Failed to create an llvm target for {0}: {1}", triple, + "Failed to create an llvm target for {0}: {1}", triple.str(), lookup_error); return nullptr; } diff --git a/lldb/source/Target/InstrumentationRuntimeStopInfo.cpp b/lldb/source/Target/InstrumentationRuntimeStopInfo.cpp index 7f82581cc601..aef895def793 100644 --- a/lldb/source/Target/InstrumentationRuntimeStopInfo.cpp +++ b/lldb/source/Target/InstrumentationRuntimeStopInfo.cpp @@ -8,13 +8,20 @@ #include "lldb/Target/InstrumentationRuntimeStopInfo.h" +#include "lldb/Core/Module.h" #include "lldb/Target/InstrumentationRuntime.h" #include "lldb/Target/Process.h" +#include "lldb/lldb-enumerations.h" #include "lldb/lldb-private.h" using namespace lldb; using namespace lldb_private; +static bool IsStoppedInDarwinSanitizer(Thread &thread, Module &module) { + return module.GetFileSpec().GetFilename().GetStringRef().starts_with( + "libclang_rt."); +} + InstrumentationRuntimeStopInfo::InstrumentationRuntimeStopInfo( Thread &thread, std::string description, StructuredData::ObjectSP additional_data) @@ -34,3 +41,38 @@ InstrumentationRuntimeStopInfo::CreateStopReasonWithInstrumentationData( return StopInfoSP( new InstrumentationRuntimeStopInfo(thread, description, additionalData)); } + +std::optional<uint32_t> +InstrumentationRuntimeStopInfo::GetSuggestedStackFrameIndex( + bool inlined_stack) { + ThreadSP thread_sp = GetThread(); + if (!thread_sp) + return std::nullopt; + + // Defensive upper-bound of when we stop walking up the frames in + // case we somehow ended up looking at an infinite recursion. + constexpr size_t max_stack_depth = 128; + + // Start at parent frame. + size_t stack_idx = 1; + StackFrameSP most_relevant_frame_sp = + thread_sp->GetStackFrameAtIndex(stack_idx); + + while (most_relevant_frame_sp && stack_idx <= max_stack_depth) { + auto const &sc = + most_relevant_frame_sp->GetSymbolContext(lldb::eSymbolContextModule); + + if (!sc.module_sp) + return std::nullopt; + + // Found a frame outside of the sanitizer runtime libraries. + // That's the one we want to display. + if (!IsStoppedInDarwinSanitizer(*thread_sp, *sc.module_sp)) + return stack_idx; + + ++stack_idx; + most_relevant_frame_sp = thread_sp->GetStackFrameAtIndex(stack_idx); + } + + return stack_idx; +} diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index ff9e5fc12059..3176852f0b72 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -4269,6 +4269,14 @@ bool Process::ProcessEventData::ShouldStop(Event *event_ptr, // appropriately. We also need to stop processing actions, since they // aren't expecting the target to be running. + // Clear the selected frame which may have been set as part of utility + // expressions that have been run as part of this stop. If we didn't + // clear this, then StopInfo::GetSuggestedStackFrameIndex would not + // take affect when we next called SelectMostRelevantFrame. + // PerformAction should not be the one setting a selected frame, instead + // this should be done via GetSuggestedStackFrameIndex. + thread_sp->ClearSelectedFrameIndex(); + // FIXME: we might have run. if (stop_info_sp->HasTargetRunSinceMe()) { SetRestarted(true); diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index bcf1297f2114..787eb94be3b4 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -293,6 +293,9 @@ void RegisterContextUnwind::InitializeZerothFrame() { return; } + // Give the Architecture a chance to replace the UnwindPlan. + TryAdoptArchitectureUnwindPlan(); + UnwindLogMsg("initialized frame current pc is 0x%" PRIx64 " cfa is 0x%" PRIx64 " afa is 0x%" PRIx64 " using %s UnwindPlan", (uint64_t)m_current_pc.GetLoadAddress(exe_ctx.GetTargetPtr()), @@ -482,6 +485,9 @@ void RegisterContextUnwind::InitializeNonZerothFrame() { } } + // Give the Architecture a chance to replace the UnwindPlan. + TryAdoptArchitectureUnwindPlan(); + UnwindLogMsg("initialized frame cfa is 0x%" PRIx64 " afa is 0x%" PRIx64, (uint64_t)m_cfa, (uint64_t)m_afa); return; @@ -686,6 +692,9 @@ void RegisterContextUnwind::InitializeNonZerothFrame() { } } + // Give the Architecture a chance to replace the UnwindPlan. + TryAdoptArchitectureUnwindPlan(); + UnwindLogMsg("initialized frame current pc is 0x%" PRIx64 " cfa is 0x%" PRIx64 " afa is 0x%" PRIx64, (uint64_t)m_current_pc.GetLoadAddress(exe_ctx.GetTargetPtr()), @@ -1717,6 +1726,41 @@ RegisterContextUnwind::SavedLocationForRegister( return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; } +UnwindPlanSP RegisterContextUnwind::TryAdoptArchitectureUnwindPlan() { + if (!m_full_unwind_plan_sp) + return {}; + ProcessSP process_sp = m_thread.GetProcess(); + if (!process_sp) + return {}; + + UnwindPlanSP arch_override_plan_sp; + if (Architecture *arch = process_sp->GetTarget().GetArchitecturePlugin()) + arch_override_plan_sp = + arch->GetArchitectureUnwindPlan(m_thread, this, m_full_unwind_plan_sp); + + if (arch_override_plan_sp) { + m_full_unwind_plan_sp = arch_override_plan_sp; + PropagateTrapHandlerFlagFromUnwindPlan(m_full_unwind_plan_sp); + m_registers.clear(); + if (GetLog(LLDBLog::Unwind)) { + UnwindLogMsg( + "Replacing Full Unwindplan with Architecture UnwindPlan, '%s'", + m_full_unwind_plan_sp->GetSourceName().AsCString()); + const UnwindPlan::Row *active_row = + m_full_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset); + if (active_row) { + StreamString active_row_strm; + active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(), + &m_thread, + m_start_pc.GetLoadAddress(&process_sp->GetTarget())); + UnwindLogMsg("%s", active_row_strm.GetData()); + } + } + } + + return {}; +} + // TryFallbackUnwindPlan() -- this method is a little tricky. // // When this is called, the frame above -- the caller frame, the "previous" diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp index 40049d40aa41..2ed58c5331df 100644 --- a/lldb/source/Target/StackFrame.cpp +++ b/lldb/source/Target/StackFrame.cpp @@ -57,14 +57,14 @@ using namespace lldb_private; StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx, user_id_t unwind_frame_index, addr_t cfa, bool cfa_is_valid, addr_t pc, StackFrame::Kind kind, - bool behaves_like_zeroth_frame, + bool artificial, bool behaves_like_zeroth_frame, const SymbolContext *sc_ptr) : m_thread_wp(thread_sp), m_frame_index(frame_idx), m_concrete_frame_index(unwind_frame_index), m_reg_context_sp(), m_id(pc, cfa, nullptr, thread_sp->GetProcess().get()), m_frame_code_addr(pc), m_sc(), m_flags(), m_frame_base(), m_frame_base_error(), m_cfa_is_valid(cfa_is_valid), - m_stack_frame_kind(kind), + m_stack_frame_kind(kind), m_artificial(artificial), m_behaves_like_zeroth_frame(behaves_like_zeroth_frame), m_variable_list_sp(), m_variable_list_value_objects(), m_recognized_frame_sp(), m_disassembly(), m_mutex() { @@ -92,7 +92,7 @@ StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx, m_id(pc, cfa, nullptr, thread_sp->GetProcess().get()), m_frame_code_addr(pc), m_sc(), m_flags(), m_frame_base(), m_frame_base_error(), m_cfa_is_valid(true), - m_stack_frame_kind(StackFrame::Kind::Regular), + m_stack_frame_kind(StackFrame::Kind::Regular), m_artificial(false), m_behaves_like_zeroth_frame(behaves_like_zeroth_frame), m_variable_list_sp(), m_variable_list_value_objects(), m_recognized_frame_sp(), m_disassembly(), m_mutex() { @@ -120,7 +120,7 @@ StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx, nullptr, thread_sp->GetProcess().get()), m_frame_code_addr(pc_addr), m_sc(), m_flags(), m_frame_base(), m_frame_base_error(), m_cfa_is_valid(true), - m_stack_frame_kind(StackFrame::Kind::Regular), + m_stack_frame_kind(StackFrame::Kind::Regular), m_artificial(false), m_behaves_like_zeroth_frame(behaves_like_zeroth_frame), m_variable_list_sp(), m_variable_list_value_objects(), m_recognized_frame_sp(), m_disassembly(), m_mutex() { @@ -266,6 +266,7 @@ bool StackFrame::ChangePC(addr_t pc) { const char *StackFrame::Disassemble() { std::lock_guard<std::recursive_mutex> guard(m_mutex); + if (!m_disassembly.Empty()) return m_disassembly.GetData(); @@ -440,10 +441,10 @@ VariableList *StackFrame::GetVariableList(bool get_file_globals, const bool get_child_variables = true; const bool can_create = true; const bool stop_if_child_block_is_inlined_function = true; - frame_block->AppendBlockVariables(can_create, get_child_variables, - stop_if_child_block_is_inlined_function, - [](Variable *v) { return true; }, - m_variable_list_sp.get()); + frame_block->AppendBlockVariables( + can_create, get_child_variables, + stop_if_child_block_is_inlined_function, + [](Variable *v) { return true; }, m_variable_list_sp.get()); } } @@ -1227,10 +1228,12 @@ StackFrame::GetValueObjectForFrameVariable(const VariableSP &variable_sp, VariableList *var_list = GetVariableList(true, nullptr); if (var_list) { // Make sure the variable is a frame variable - const uint32_t var_idx = var_list->FindIndexForVariable(variable_sp.get()); + const uint32_t var_idx = + var_list->FindIndexForVariable(variable_sp.get()); const uint32_t num_variables = var_list->GetSize(); if (var_idx < num_variables) { - valobj_sp = m_variable_list_value_objects.GetValueObjectAtIndex(var_idx); + valobj_sp = + m_variable_list_value_objects.GetValueObjectAtIndex(var_idx); if (!valobj_sp) { if (m_variable_list_value_objects.GetSize() < num_variables) m_variable_list_value_objects.Resize(num_variables); @@ -1261,10 +1264,12 @@ bool StackFrame::IsHistorical() const { return m_stack_frame_kind == StackFrame::Kind::History; } -bool StackFrame::IsArtificial() const { - return m_stack_frame_kind == StackFrame::Kind::Artificial; +bool StackFrame::IsSynthetic() const { + return m_stack_frame_kind == StackFrame::Kind::Synthetic; } +bool StackFrame::IsArtificial() const { return m_artificial; } + bool StackFrame::IsHidden() { if (auto recognized_frame_sp = GetRecognizedFrame()) return recognized_frame_sp->ShouldHide(); @@ -1764,11 +1769,9 @@ lldb::ValueObjectSP DoGuessValueAt(StackFrame &frame, ConstString reg, if (clobbered_reg_matcher(operands[0])) { origin_operand = &operands[1]; - } - else if (clobbered_reg_matcher(operands[1])) { + } else if (clobbered_reg_matcher(operands[1])) { origin_operand = &operands[0]; - } - else { + } else { continue; } @@ -1794,8 +1797,7 @@ lldb::ValueObjectSP DoGuessValueAt(StackFrame &frame, ConstString reg, if (!source_path) { continue; } - source_path = - GetValueForDereferincingOffset(frame, source_path, offset); + source_path = GetValueForDereferincingOffset(frame, source_path, offset); } if (source_path) { @@ -1805,7 +1807,7 @@ lldb::ValueObjectSP DoGuessValueAt(StackFrame &frame, ConstString reg, return ValueObjectSP(); } -} +} // namespace lldb::ValueObjectSP StackFrame::GuessValueForRegisterAndOffset(ConstString reg, int64_t offset) { diff --git a/lldb/source/Target/StackFrameList.cpp b/lldb/source/Target/StackFrameList.cpp index aedfc52cfb4c..fa5d159c0c91 100644 --- a/lldb/source/Target/StackFrameList.cpp +++ b/lldb/source/Target/StackFrameList.cpp @@ -321,13 +321,14 @@ void StackFrameList::SynthesizeTailCallFrames(StackFrame &next_frame) { addr_t pc = calleeInfo.address; // If the callee address refers to the call instruction, we do not want to // subtract 1 from this value. + const bool artificial = true; const bool behaves_like_zeroth_frame = calleeInfo.address_type == CallEdge::AddrType::Call; SymbolContext sc; callee->CalculateSymbolContext(&sc); auto synth_frame = std::make_shared<StackFrame>( m_thread.shared_from_this(), frame_idx, concrete_frame_idx, cfa, - cfa_is_valid, pc, StackFrame::Kind::Artificial, + cfa_is_valid, pc, StackFrame::Kind::Regular, artificial, behaves_like_zeroth_frame, &sc); m_frames.push_back(synth_frame); LLDB_LOG(log, "Pushed frame {0} at {1:x}", callee->GetDisplayName(), pc); @@ -470,7 +471,8 @@ bool StackFrameList::FetchFramesUpTo(uint32_t end_idx, const bool cfa_is_valid = true; unwind_frame_sp = std::make_shared<StackFrame>( m_thread.shared_from_this(), m_frames.size(), idx, cfa, cfa_is_valid, - pc, StackFrame::Kind::Regular, behaves_like_zeroth_frame, nullptr); + pc, StackFrame::Kind::Regular, false, behaves_like_zeroth_frame, + nullptr); // Create synthetic tail call frames between the previous frame and the // newly-found frame. The new frame's index may change after this call, @@ -942,3 +944,5 @@ size_t StackFrameList::GetStatus(Stream &strm, uint32_t first_frame, strm.IndentLess(); return num_frames_displayed; } + +void StackFrameList::ClearSelectedFrameIndex() { m_selected_frame_idx.reset(); } diff --git a/lldb/source/Target/StackID.cpp b/lldb/source/Target/StackID.cpp index b1795970802a..f879276527dd 100644 --- a/lldb/source/Target/StackID.cpp +++ b/lldb/source/Target/StackID.cpp @@ -63,16 +63,7 @@ bool lldb_private::operator==(const StackID &lhs, const StackID &rhs) { } bool lldb_private::operator!=(const StackID &lhs, const StackID &rhs) { - if (lhs.GetCallFrameAddress() != rhs.GetCallFrameAddress()) - return true; - - SymbolContextScope *lhs_scope = lhs.GetSymbolContextScope(); - SymbolContextScope *rhs_scope = rhs.GetSymbolContextScope(); - - if (lhs_scope == nullptr && rhs_scope == nullptr) - return lhs.GetPC() != rhs.GetPC(); - - return lhs_scope != rhs_scope; + return !(lhs == rhs); } bool lldb_private::operator<(const StackID &lhs, const StackID &rhs) { diff --git a/lldb/source/Target/Statistics.cpp b/lldb/source/Target/Statistics.cpp index 909f335687b2..8ad8d507268e 100644 --- a/lldb/source/Target/Statistics.cpp +++ b/lldb/source/Target/Statistics.cpp @@ -73,8 +73,9 @@ json::Value ModuleStats::ToJSON() const { debug_info_had_incomplete_types); module.try_emplace("symbolTableStripped", symtab_stripped); module.try_emplace("symbolTableSymbolCount", symtab_symbol_count); - module.try_emplace("dwoFileCount", dwo_file_count); - module.try_emplace("loadedDwoFileCount", loaded_dwo_file_count); + module.try_emplace("dwoFileCount", dwo_stats.dwo_file_count); + module.try_emplace("loadedDwoFileCount", dwo_stats.loaded_dwo_file_count); + module.try_emplace("dwoErrorCount", dwo_stats.dwo_error_count); if (!symbol_locator_time.map.empty()) { json::Object obj; @@ -324,8 +325,7 @@ llvm::json::Value DebuggerStats::ReportStatistics( uint32_t num_modules_with_incomplete_types = 0; uint32_t num_stripped_modules = 0; uint32_t symtab_symbol_count = 0; - uint32_t total_loaded_dwo_file_count = 0; - uint32_t total_dwo_file_count = 0; + DWOStats total_dwo_stats; for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) { Module *module = target != nullptr ? target->GetImages().GetModuleAtIndex(image_idx).get() @@ -357,10 +357,9 @@ llvm::json::Value DebuggerStats::ReportStatistics( for (const auto &symbol_module : symbol_modules.Modules()) module_stat.symfile_modules.push_back((intptr_t)symbol_module.get()); } - std::tie(module_stat.loaded_dwo_file_count, module_stat.dwo_file_count) = - sym_file->GetDwoFileCounts(); - total_dwo_file_count += module_stat.dwo_file_count; - total_loaded_dwo_file_count += module_stat.loaded_dwo_file_count; + DWOStats current_dwo_stats = sym_file->GetDwoStats(); + module_stat.dwo_stats += current_dwo_stats; + total_dwo_stats += current_dwo_stats; module_stat.debug_info_index_loaded_from_cache = sym_file->GetDebugInfoIndexWasLoadedFromCache(); if (module_stat.debug_info_index_loaded_from_cache) @@ -435,8 +434,9 @@ llvm::json::Value DebuggerStats::ReportStatistics( {"totalDebugInfoEnabled", num_debug_info_enabled_modules}, {"totalSymbolTableStripped", num_stripped_modules}, {"totalSymbolTableSymbolCount", symtab_symbol_count}, - {"totalLoadedDwoFileCount", total_loaded_dwo_file_count}, - {"totalDwoFileCount", total_dwo_file_count}, + {"totalLoadedDwoFileCount", total_dwo_stats.loaded_dwo_file_count}, + {"totalDwoFileCount", total_dwo_stats.dwo_file_count}, + {"totalDwoErrorCount", total_dwo_stats.dwo_error_count}, }; if (include_targets) { diff --git a/lldb/source/Target/StopInfo.cpp b/lldb/source/Target/StopInfo.cpp index ddf8c62e969e..f47dae2b2465 100644 --- a/lldb/source/Target/StopInfo.cpp +++ b/lldb/source/Target/StopInfo.cpp @@ -108,8 +108,7 @@ public: void StoreBPInfo() { ThreadSP thread_sp(m_thread_wp.lock()); if (thread_sp) { - BreakpointSiteSP bp_site_sp( - thread_sp->GetProcess()->GetBreakpointSiteList().FindByID(m_value)); + BreakpointSiteSP bp_site_sp = GetBreakpointSiteSP(); if (bp_site_sp) { uint32_t num_constituents = bp_site_sp->GetNumberOfConstituents(); if (num_constituents == 1) { @@ -139,8 +138,7 @@ public: bool IsValidForOperatingSystemThread(Thread &thread) override { ProcessSP process_sp(thread.GetProcess()); if (process_sp) { - BreakpointSiteSP bp_site_sp( - process_sp->GetBreakpointSiteList().FindByID(m_value)); + BreakpointSiteSP bp_site_sp = GetBreakpointSiteSP(); if (bp_site_sp) return bp_site_sp->ValidForThisThread(thread); } @@ -154,8 +152,7 @@ public: if (thread_sp) { if (!m_should_stop_is_valid) { // Only check once if we should stop at a breakpoint - BreakpointSiteSP bp_site_sp( - thread_sp->GetProcess()->GetBreakpointSiteList().FindByID(m_value)); + BreakpointSiteSP bp_site_sp = GetBreakpointSiteSP(); if (bp_site_sp) { ExecutionContext exe_ctx(thread_sp->GetStackFrameAtIndex(0)); StoppointCallbackContext context(event_ptr, exe_ctx, true); @@ -186,8 +183,7 @@ public: if (m_description.empty()) { ThreadSP thread_sp(m_thread_wp.lock()); if (thread_sp) { - BreakpointSiteSP bp_site_sp( - thread_sp->GetProcess()->GetBreakpointSiteList().FindByID(m_value)); + BreakpointSiteSP bp_site_sp = GetBreakpointSiteSP(); if (bp_site_sp) { StreamString strm; // If we have just hit an internal breakpoint, and it has a kind @@ -247,6 +243,35 @@ public: return m_description.c_str(); } + uint32_t GetStopReasonDataCount() const override { + lldb::BreakpointSiteSP bp_site_sp = GetBreakpointSiteSP(); + if (bp_site_sp) + return bp_site_sp->GetNumberOfConstituents() * 2; + return 0; // Breakpoint must have cleared itself... + } + + uint64_t GetStopReasonDataAtIndex(uint32_t idx) override { + lldb::BreakpointSiteSP bp_site_sp = GetBreakpointSiteSP(); + if (bp_site_sp) { + uint32_t bp_index = idx / 2; + BreakpointLocationSP bp_loc_sp( + bp_site_sp->GetConstituentAtIndex(bp_index)); + if (bp_loc_sp) { + if (idx & 1) { + // FIXME: This might be a Facade breakpoint, so we need to fetch + // the one that the thread actually hit, not the native loc ID. + + // Odd idx, return the breakpoint location ID + return bp_loc_sp->GetID(); + } else { + // Even idx, return the breakpoint ID + return bp_loc_sp->GetBreakpoint().GetID(); + } + } + } + return LLDB_INVALID_BREAK_ID; + } + std::optional<uint32_t> GetSuggestedStackFrameIndex(bool inlined_stack) override { if (!inlined_stack) @@ -255,8 +280,7 @@ public: ThreadSP thread_sp(m_thread_wp.lock()); if (!thread_sp) return {}; - BreakpointSiteSP bp_site_sp( - thread_sp->GetProcess()->GetBreakpointSiteList().FindByID(m_value)); + BreakpointSiteSP bp_site_sp = GetBreakpointSiteSP(); if (!bp_site_sp) return {}; @@ -297,8 +321,7 @@ protected: return; } - BreakpointSiteSP bp_site_sp( - thread_sp->GetProcess()->GetBreakpointSiteList().FindByID(m_value)); + BreakpointSiteSP bp_site_sp = GetBreakpointSiteSP(); std::unordered_set<break_id_t> precondition_breakpoints; // Breakpoints that fail their condition check are not considered to // have been hit. If the only locations at this site have failed their @@ -629,6 +652,20 @@ protected: } private: + BreakpointSiteSP GetBreakpointSiteSP() const { + if (m_value == LLDB_INVALID_BREAK_ID) + return {}; + + ThreadSP thread_sp = GetThread(); + if (!thread_sp) + return {}; + ProcessSP process_sp = thread_sp->GetProcess(); + if (!process_sp) + return {}; + + return process_sp->GetBreakpointSiteList().FindByID(m_value); + } + bool m_should_stop; bool m_should_stop_is_valid; bool m_should_perform_action; // Since we are trying to preserve the "state" @@ -699,6 +736,13 @@ public: StopReason GetStopReason() const override { return eStopReasonWatchpoint; } + uint32_t GetStopReasonDataCount() const override { return 1; } + uint64_t GetStopReasonDataAtIndex(uint32_t idx) override { + if (idx == 0) + return GetValue(); + return 0; + } + const char *GetDescription() override { if (m_description.empty()) { StreamString strm; @@ -1139,6 +1183,13 @@ public: bool ShouldSelect() const override { return IsShouldStopSignal(); } + uint32_t GetStopReasonDataCount() const override { return 1; } + uint64_t GetStopReasonDataAtIndex(uint32_t idx) override { + if (idx == 0) + return GetValue(); + return 0; + } + private: // In siginfo_t terms, if m_value is si_signo, m_code is si_code. std::optional<int> m_code; @@ -1171,6 +1222,14 @@ public: } return m_description.c_str(); } + + uint32_t GetStopReasonDataCount() const override { return 1; } + uint64_t GetStopReasonDataAtIndex(uint32_t idx) override { + if (idx == 0) + return GetValue(); + else + return 0; + } }; // StopInfoTrace @@ -1249,6 +1308,13 @@ public: else return m_description.c_str(); } + uint32_t GetStopReasonDataCount() const override { return 1; } + uint64_t GetStopReasonDataAtIndex(uint32_t idx) override { + if (idx == 0) + return GetValue(); + else + return 0; + } }; // StopInfoProcessorTrace @@ -1390,6 +1456,14 @@ public: const char *GetDescription() override { return "fork"; } + uint32_t GetStopReasonDataCount() const override { return 1; } + uint64_t GetStopReasonDataAtIndex(uint32_t idx) override { + if (idx == 0) + return GetValue(); + else + return 0; + } + protected: void PerformAction(Event *event_ptr) override { // Only perform the action once @@ -1424,6 +1498,13 @@ public: const char *GetDescription() override { return "vfork"; } + uint32_t GetStopReasonDataCount() const override { return 1; } + uint64_t GetStopReasonDataAtIndex(uint32_t idx) override { + if (idx == 0) + return GetValue(); + return 0; + } + protected: void PerformAction(Event *event_ptr) override { // Only perform the action once diff --git a/lldb/source/Utility/ArchSpec.cpp b/lldb/source/Utility/ArchSpec.cpp index 8f14cf6e3943..1b8dae39735d 100644 --- a/lldb/source/Utility/ArchSpec.cpp +++ b/lldb/source/Utility/ArchSpec.cpp @@ -260,9 +260,9 @@ static_assert(sizeof(g_core_definitions) / sizeof(CoreDefinition) == struct ArchDefinitionEntry { ArchSpec::Core core; uint32_t cpu; - uint32_t sub; - uint32_t cpu_mask; - uint32_t sub_mask; + uint32_t sub = LLDB_INVALID_CPUTYPE; + uint32_t cpu_mask = UINT32_MAX; + uint32_t sub_mask = UINT32_MAX; }; struct ArchDefinition { @@ -357,7 +357,8 @@ static const ArchDefinitionEntry g_macho_arch_entries[] = { {ArchSpec::eCore_riscv32, llvm::MachO::CPU_TYPE_RISCV, CPU_ANY, UINT32_MAX, SUBTYPE_MASK}, // Catch any unknown mach architectures so we can always use the object and symbol mach-o files {ArchSpec::eCore_uknownMach32, 0, 0, 0xFF000000u, 0x00000000u}, - {ArchSpec::eCore_uknownMach64, llvm::MachO::CPU_ARCH_ABI64, 0, 0xFF000000u, 0x00000000u}}; + {ArchSpec::eCore_uknownMach64, llvm::MachO::CPU_ARCH_ABI64, 0, 0xFF000000u, 0x00000000u} +}; // clang-format on static const ArchDefinition g_macho_arch_def = {eArchTypeMachO, @@ -369,72 +370,41 @@ static const ArchDefinition g_macho_arch_def = {eArchTypeMachO, // convert cpu type and subtypes to architecture names, and to convert // architecture names to cpu types and subtypes. The ordering is important and // allows the precedence to be set when the table is built. +// clang-format off static const ArchDefinitionEntry g_elf_arch_entries[] = { - {ArchSpec::eCore_sparc_generic, llvm::ELF::EM_SPARC, LLDB_INVALID_CPUTYPE, - 0xFFFFFFFFu, 0xFFFFFFFFu}, // Sparc - {ArchSpec::eCore_x86_32_i386, llvm::ELF::EM_386, LLDB_INVALID_CPUTYPE, - 0xFFFFFFFFu, 0xFFFFFFFFu}, // Intel 80386 - {ArchSpec::eCore_x86_32_i486, llvm::ELF::EM_IAMCU, LLDB_INVALID_CPUTYPE, - 0xFFFFFFFFu, 0xFFFFFFFFu}, // Intel MCU // FIXME: is this correct? - {ArchSpec::eCore_ppc_generic, llvm::ELF::EM_PPC, LLDB_INVALID_CPUTYPE, - 0xFFFFFFFFu, 0xFFFFFFFFu}, // PowerPC - {ArchSpec::eCore_ppc64le_generic, llvm::ELF::EM_PPC64, - ArchSpec::eCore_ppc64le_generic, 0xFFFFFFFFu, 0xFFFFFFFFu}, // PowerPC64le - {ArchSpec::eCore_ppc64_generic, llvm::ELF::EM_PPC64, - ArchSpec::eCore_ppc64_generic, 0xFFFFFFFFu, 0xFFFFFFFFu}, // PowerPC64 - {ArchSpec::eCore_arm_generic, llvm::ELF::EM_ARM, LLDB_INVALID_CPUTYPE, - 0xFFFFFFFFu, 0xFFFFFFFFu}, // ARM - {ArchSpec::eCore_arm_aarch64, llvm::ELF::EM_AARCH64, LLDB_INVALID_CPUTYPE, - 0xFFFFFFFFu, 0xFFFFFFFFu}, // ARM64 - {ArchSpec::eCore_s390x_generic, llvm::ELF::EM_S390, LLDB_INVALID_CPUTYPE, - 0xFFFFFFFFu, 0xFFFFFFFFu}, // SystemZ - {ArchSpec::eCore_sparc9_generic, llvm::ELF::EM_SPARCV9, - LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // SPARC V9 - {ArchSpec::eCore_x86_64_x86_64, llvm::ELF::EM_X86_64, LLDB_INVALID_CPUTYPE, - 0xFFFFFFFFu, 0xFFFFFFFFu}, // AMD64 - {ArchSpec::eCore_mips32, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips32, - 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32 - {ArchSpec::eCore_mips32r2, llvm::ELF::EM_MIPS, - ArchSpec::eMIPSSubType_mips32r2, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32r2 - {ArchSpec::eCore_mips32r6, llvm::ELF::EM_MIPS, - ArchSpec::eMIPSSubType_mips32r6, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32r6 - {ArchSpec::eCore_mips32el, llvm::ELF::EM_MIPS, - ArchSpec::eMIPSSubType_mips32el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32el - {ArchSpec::eCore_mips32r2el, llvm::ELF::EM_MIPS, - ArchSpec::eMIPSSubType_mips32r2el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32r2el - {ArchSpec::eCore_mips32r6el, llvm::ELF::EM_MIPS, - ArchSpec::eMIPSSubType_mips32r6el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32r6el - {ArchSpec::eCore_mips64, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips64, - 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64 - {ArchSpec::eCore_mips64r2, llvm::ELF::EM_MIPS, - ArchSpec::eMIPSSubType_mips64r2, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64r2 - {ArchSpec::eCore_mips64r6, llvm::ELF::EM_MIPS, - ArchSpec::eMIPSSubType_mips64r6, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64r6 - {ArchSpec::eCore_mips64el, llvm::ELF::EM_MIPS, - ArchSpec::eMIPSSubType_mips64el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64el - {ArchSpec::eCore_mips64r2el, llvm::ELF::EM_MIPS, - ArchSpec::eMIPSSubType_mips64r2el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64r2el - {ArchSpec::eCore_mips64r6el, llvm::ELF::EM_MIPS, - ArchSpec::eMIPSSubType_mips64r6el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64r6el - {ArchSpec::eCore_msp430, llvm::ELF::EM_MSP430, LLDB_INVALID_CPUTYPE, - 0xFFFFFFFFu, 0xFFFFFFFFu}, // MSP430 - {ArchSpec::eCore_hexagon_generic, llvm::ELF::EM_HEXAGON, - LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // HEXAGON - {ArchSpec::eCore_arc, llvm::ELF::EM_ARC_COMPACT2, LLDB_INVALID_CPUTYPE, - 0xFFFFFFFFu, 0xFFFFFFFFu}, // ARC - {ArchSpec::eCore_avr, llvm::ELF::EM_AVR, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, - 0xFFFFFFFFu}, // AVR - {ArchSpec::eCore_riscv32, llvm::ELF::EM_RISCV, - ArchSpec::eRISCVSubType_riscv32, 0xFFFFFFFFu, 0xFFFFFFFFu}, // riscv32 - {ArchSpec::eCore_riscv64, llvm::ELF::EM_RISCV, - ArchSpec::eRISCVSubType_riscv64, 0xFFFFFFFFu, 0xFFFFFFFFu}, // riscv64 - {ArchSpec::eCore_loongarch32, llvm::ELF::EM_LOONGARCH, - ArchSpec::eLoongArchSubType_loongarch32, 0xFFFFFFFFu, - 0xFFFFFFFFu}, // loongarch32 - {ArchSpec::eCore_loongarch64, llvm::ELF::EM_LOONGARCH, - ArchSpec::eLoongArchSubType_loongarch64, 0xFFFFFFFFu, - 0xFFFFFFFFu}, // loongarch64 + {ArchSpec::eCore_sparc_generic, llvm::ELF::EM_SPARC }, // Sparc + {ArchSpec::eCore_x86_32_i386, llvm::ELF::EM_386 }, // Intel 80386 + {ArchSpec::eCore_x86_32_i486, llvm::ELF::EM_IAMCU }, // Intel MCU // FIXME: is this correct? + {ArchSpec::eCore_ppc_generic, llvm::ELF::EM_PPC }, // PowerPC + {ArchSpec::eCore_ppc64le_generic, llvm::ELF::EM_PPC64, ArchSpec::eCore_ppc64le_generic}, // PowerPC64le + {ArchSpec::eCore_ppc64_generic, llvm::ELF::EM_PPC64, ArchSpec::eCore_ppc64_generic}, // PowerPC64 + {ArchSpec::eCore_arm_generic, llvm::ELF::EM_ARM }, // ARM + {ArchSpec::eCore_arm_aarch64, llvm::ELF::EM_AARCH64 }, // ARM64 + {ArchSpec::eCore_s390x_generic, llvm::ELF::EM_S390 }, // SystemZ + {ArchSpec::eCore_sparc9_generic, llvm::ELF::EM_SPARCV9 }, // SPARC V9 + {ArchSpec::eCore_x86_64_x86_64, llvm::ELF::EM_X86_64 }, // AMD64 + {ArchSpec::eCore_mips32, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips32}, // mips32 + {ArchSpec::eCore_mips32r2, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips32r2}, // mips32r2 + {ArchSpec::eCore_mips32r6, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips32r6}, // mips32r6 + {ArchSpec::eCore_mips32el, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips32el}, // mips32el + {ArchSpec::eCore_mips32r2el, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips32r2el}, // mips32r2el + {ArchSpec::eCore_mips32r6el, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips32r6el}, // mips32r6el + {ArchSpec::eCore_mips64, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips64}, + {ArchSpec::eCore_mips64r2, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips64r2}, // mips64r2 + {ArchSpec::eCore_mips64r6, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips64r6}, // mips64r6 + {ArchSpec::eCore_mips64el, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips64el}, // mips64el + {ArchSpec::eCore_mips64r2el, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips64r2el}, // mips64r2el + {ArchSpec::eCore_mips64r6el, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips64r6el}, // mips64r6el + {ArchSpec::eCore_msp430, llvm::ELF::EM_MSP430 }, // MSP430 + {ArchSpec::eCore_hexagon_generic, llvm::ELF::EM_HEXAGON }, // HEXAGON + {ArchSpec::eCore_arc, llvm::ELF::EM_ARC_COMPACT2}, // ARC + {ArchSpec::eCore_avr, llvm::ELF::EM_AVR }, // AVR + {ArchSpec::eCore_riscv32, llvm::ELF::EM_RISCV, ArchSpec::eRISCVSubType_riscv32}, // riscv32 + {ArchSpec::eCore_riscv64, llvm::ELF::EM_RISCV, ArchSpec::eRISCVSubType_riscv64}, // riscv64 + {ArchSpec::eCore_loongarch32, llvm::ELF::EM_LOONGARCH, ArchSpec::eLoongArchSubType_loongarch32}, // loongarch32 + {ArchSpec::eCore_loongarch64, llvm::ELF::EM_LOONGARCH, ArchSpec::eLoongArchSubType_loongarch64}, // loongarch64 }; +// clang-format on static const ArchDefinition g_elf_arch_def = { eArchTypeELF, @@ -442,25 +412,18 @@ static const ArchDefinition g_elf_arch_def = { g_elf_arch_entries, "elf", }; - +// clang-format off static const ArchDefinitionEntry g_coff_arch_entries[] = { - {ArchSpec::eCore_x86_32_i386, llvm::COFF::IMAGE_FILE_MACHINE_I386, - LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // Intel 80x86 - {ArchSpec::eCore_ppc_generic, llvm::COFF::IMAGE_FILE_MACHINE_POWERPC, - LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // PowerPC - {ArchSpec::eCore_ppc_generic, llvm::COFF::IMAGE_FILE_MACHINE_POWERPCFP, - LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // PowerPC (with FPU) - {ArchSpec::eCore_arm_generic, llvm::COFF::IMAGE_FILE_MACHINE_ARM, - LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // ARM - {ArchSpec::eCore_arm_armv7, llvm::COFF::IMAGE_FILE_MACHINE_ARMNT, - LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // ARMv7 - {ArchSpec::eCore_thumb, llvm::COFF::IMAGE_FILE_MACHINE_THUMB, - LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // ARMv7 - {ArchSpec::eCore_x86_64_x86_64, llvm::COFF::IMAGE_FILE_MACHINE_AMD64, - LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // AMD64 - {ArchSpec::eCore_arm_arm64, llvm::COFF::IMAGE_FILE_MACHINE_ARM64, - LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu} // ARM64 + {ArchSpec::eCore_x86_32_i386, llvm::COFF::IMAGE_FILE_MACHINE_I386}, // Intel 80x86 + {ArchSpec::eCore_ppc_generic, llvm::COFF::IMAGE_FILE_MACHINE_POWERPC}, // PowerPC + {ArchSpec::eCore_ppc_generic, llvm::COFF::IMAGE_FILE_MACHINE_POWERPCFP}, // PowerPC (with FPU) + {ArchSpec::eCore_arm_generic, llvm::COFF::IMAGE_FILE_MACHINE_ARM}, // ARM + {ArchSpec::eCore_arm_armv7, llvm::COFF::IMAGE_FILE_MACHINE_ARMNT}, // ARMv7 + {ArchSpec::eCore_thumb, llvm::COFF::IMAGE_FILE_MACHINE_THUMB}, // ARMv7 + {ArchSpec::eCore_x86_64_x86_64, llvm::COFF::IMAGE_FILE_MACHINE_AMD64}, // AMD64 + {ArchSpec::eCore_arm_arm64, llvm::COFF::IMAGE_FILE_MACHINE_ARM64} // ARM64 }; +// clang-format on static const ArchDefinition g_coff_arch_def = { eArchTypeCOFF, @@ -469,11 +432,12 @@ static const ArchDefinition g_coff_arch_def = { "pe-coff", }; +// clang-format off static const ArchDefinitionEntry g_xcoff_arch_entries[] = { - {ArchSpec::eCore_ppc_generic, llvm::XCOFF::TCPU_COM, LLDB_INVALID_CPUTYPE, - 0xFFFFFFFFu, 0xFFFFFFFFu}, - {ArchSpec::eCore_ppc64_generic, llvm::XCOFF::TCPU_PPC64, - LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}}; + {ArchSpec::eCore_ppc_generic, llvm::XCOFF::TCPU_COM}, + {ArchSpec::eCore_ppc64_generic, llvm::XCOFF::TCPU_PPC64} +}; +// clang-format on static const ArchDefinition g_xcoff_arch_def = { eArchTypeXCOFF, @@ -695,13 +659,9 @@ uint32_t ArchSpec::GetMachOCPUSubType() const { return LLDB_INVALID_CPUTYPE; } -uint32_t ArchSpec::GetDataByteSize() const { - return 1; -} +uint32_t ArchSpec::GetDataByteSize() const { return 1; } -uint32_t ArchSpec::GetCodeByteSize() const { - return 1; -} +uint32_t ArchSpec::GetCodeByteSize() const { return 1; } llvm::Triple::ArchType ArchSpec::GetMachine() const { const CoreDefinition *core_def = FindCoreDefinition(m_core); @@ -1170,8 +1130,8 @@ static bool cores_match(const ArchSpec::Core core1, const ArchSpec::Core core2, break; // v. https://en.wikipedia.org/wiki/ARM_Cortex-M#Silicon_customization - // Cortex-M0 - ARMv6-M - armv6m - // Cortex-M3 - ARMv7-M - armv7m + // Cortex-M0 - ARMv6-M - armv6m + // Cortex-M3 - ARMv7-M - armv7m // Cortex-M4 - ARMv7E-M - armv7em case ArchSpec::eCore_arm_armv7em: if (!enforce_exact_match) { @@ -1188,8 +1148,8 @@ static bool cores_match(const ArchSpec::Core core1, const ArchSpec::Core core2, break; // v. https://en.wikipedia.org/wiki/ARM_Cortex-M#Silicon_customization - // Cortex-M0 - ARMv6-M - armv6m - // Cortex-M3 - ARMv7-M - armv7m + // Cortex-M0 - ARMv6-M - armv6m + // Cortex-M3 - ARMv7-M - armv7m // Cortex-M4 - ARMv7E-M - armv7em case ArchSpec::eCore_arm_armv7m: if (!enforce_exact_match) { @@ -1206,8 +1166,8 @@ static bool cores_match(const ArchSpec::Core core1, const ArchSpec::Core core2, break; // v. https://en.wikipedia.org/wiki/ARM_Cortex-M#Silicon_customization - // Cortex-M0 - ARMv6-M - armv6m - // Cortex-M3 - ARMv7-M - armv7m + // Cortex-M0 - ARMv6-M - armv6m + // Cortex-M3 - ARMv7-M - armv7m // Cortex-M4 - ARMv7E-M - armv7em case ArchSpec::eCore_arm_armv6m: if (!enforce_exact_match) { @@ -1434,7 +1394,6 @@ bool lldb_private::operator<(const ArchSpec &lhs, const ArchSpec &rhs) { return lhs_core < rhs_core; } - bool lldb_private::operator==(const ArchSpec &lhs, const ArchSpec &rhs) { return lhs.GetCore() == rhs.GetCore(); } diff --git a/lldb/source/ValueObject/DILAST.cpp b/lldb/source/ValueObject/DILAST.cpp index b1cd824c2299..70564663a62c 100644 --- a/lldb/source/ValueObject/DILAST.cpp +++ b/lldb/source/ValueObject/DILAST.cpp @@ -37,4 +37,13 @@ BitFieldExtractionNode::Accept(Visitor *v) const { return v->Visit(this); } +llvm::Expected<lldb::ValueObjectSP> +IntegerLiteralNode::Accept(Visitor *v) const { + return v->Visit(this); +} + +llvm::Expected<lldb::ValueObjectSP> FloatLiteralNode::Accept(Visitor *v) const { + return v->Visit(this); +} + } // namespace lldb_private::dil diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp index 6f28434c646c..c6cf41ee9e9e 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -7,7 +7,9 @@ //===----------------------------------------------------------------------===// #include "lldb/ValueObject/DILEval.h" +#include "lldb/Core/Module.h" #include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/TypeSystem.h" #include "lldb/Symbol/VariableList.h" #include "lldb/Target/RegisterContext.h" #include "lldb/ValueObject/DILAST.h" @@ -330,40 +332,135 @@ Interpreter::Visit(const ArraySubscriptNode *node) { return lhs_or_err; lldb::ValueObjectSP base = *lhs_or_err; - // Check to see if 'base' has a synthetic value; if so, try using that. + StreamString var_expr_path_strm; uint64_t child_idx = node->GetIndex(); - if (lldb::ValueObjectSP synthetic = base->GetSyntheticValue()) { - llvm::Expected<uint32_t> num_children = - synthetic->GetNumChildren(child_idx + 1); - if (!num_children) - return llvm::make_error<DILDiagnosticError>( - m_expr, toString(num_children.takeError()), node->GetLocation()); - if (child_idx >= *num_children) { - std::string message = llvm::formatv( - "array index {0} is not valid for \"({1}) {2}\"", child_idx, + lldb::ValueObjectSP child_valobj_sp; + + bool is_incomplete_array = false; + CompilerType base_type = base->GetCompilerType().GetNonReferenceType(); + base->GetExpressionPath(var_expr_path_strm); + + if (base_type.IsPointerType()) { + bool is_objc_pointer = true; + + if (base->GetCompilerType().GetMinimumLanguage() != lldb::eLanguageTypeObjC) + is_objc_pointer = false; + else if (!base->GetCompilerType().IsPointerType()) + is_objc_pointer = false; + + if (!m_use_synthetic && is_objc_pointer) { + std::string err_msg = llvm::formatv( + "\"({0}) {1}\" is an Objective-C pointer, and cannot be subscripted", base->GetTypeName().AsCString("<invalid type>"), - base->GetName().AsCString()); - return llvm::make_error<DILDiagnosticError>(m_expr, message, + var_expr_path_strm.GetData()); + return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), node->GetLocation()); } - if (lldb::ValueObjectSP child_valobj_sp = - synthetic->GetChildAtIndex(child_idx)) + if (is_objc_pointer) { + lldb::ValueObjectSP synthetic = base->GetSyntheticValue(); + if (!synthetic || synthetic == base) { + std::string err_msg = + llvm::formatv("\"({0}) {1}\" is not an array type", + base->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), + node->GetLocation()); + } + if (static_cast<uint32_t>(child_idx) >= + synthetic->GetNumChildrenIgnoringErrors()) { + std::string err_msg = llvm::formatv( + "array index {0} is not valid for \"({1}) {2}\"", child_idx, + base->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), + node->GetLocation()); + } + child_valobj_sp = synthetic->GetChildAtIndex(child_idx); + if (!child_valobj_sp) { + std::string err_msg = llvm::formatv( + "array index {0} is not valid for \"({1}) {2}\"", child_idx, + base->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), + node->GetLocation()); + } + if (m_use_dynamic != lldb::eNoDynamicValues) { + if (auto dynamic_sp = child_valobj_sp->GetDynamicValue(m_use_dynamic)) + child_valobj_sp = std::move(dynamic_sp); + } return child_valobj_sp; - } + } - auto base_type = base->GetCompilerType().GetNonReferenceType(); - if (!base_type.IsPointerType() && !base_type.IsArrayType()) - return llvm::make_error<DILDiagnosticError>( - m_expr, "subscripted value is not an array or pointer", - node->GetLocation()); - if (base_type.IsPointerToVoid()) - return llvm::make_error<DILDiagnosticError>( - m_expr, "subscript of pointer to incomplete type 'void'", - node->GetLocation()); + child_valobj_sp = base->GetSyntheticArrayMember(child_idx, true); + if (!child_valobj_sp) { + std::string err_msg = llvm::formatv( + "failed to use pointer as array for index {0} for " + "\"({1}) {2}\"", + child_idx, base->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + if (base_type.IsPointerToVoid()) + err_msg = "subscript of pointer to incomplete type 'void'"; + return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), + node->GetLocation()); + } + } else if (base_type.IsArrayType(nullptr, nullptr, &is_incomplete_array)) { + child_valobj_sp = base->GetChildAtIndex(child_idx); + if (!child_valobj_sp && (is_incomplete_array || m_use_synthetic)) + child_valobj_sp = base->GetSyntheticArrayMember(child_idx, true); + if (!child_valobj_sp) { + std::string err_msg = llvm::formatv( + "array index {0} is not valid for \"({1}) {2}\"", child_idx, + base->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), + node->GetLocation()); + } + } else if (base_type.IsScalarType()) { + child_valobj_sp = + base->GetSyntheticBitFieldChild(child_idx, child_idx, true); + if (!child_valobj_sp) { + std::string err_msg = llvm::formatv( + "bitfield range {0}-{1} is not valid for \"({2}) {3}\"", child_idx, + child_idx, base->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), + node->GetLocation(), 1); + } + } else { + lldb::ValueObjectSP synthetic = base->GetSyntheticValue(); + if (!m_use_synthetic || !synthetic || synthetic == base) { + std::string err_msg = + llvm::formatv("\"{0}\" is not an array type", + base->GetTypeName().AsCString("<invalid type>")); + return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), + node->GetLocation(), 1); + } + if (static_cast<uint32_t>(child_idx) >= + synthetic->GetNumChildrenIgnoringErrors(child_idx + 1)) { + std::string err_msg = llvm::formatv( + "array index {0} is not valid for \"({1}) {2}\"", child_idx, + base->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), + node->GetLocation(), 1); + } + child_valobj_sp = synthetic->GetChildAtIndex(child_idx); + if (!child_valobj_sp) { + std::string err_msg = llvm::formatv( + "array index {0} is not valid for \"({1}) {2}\"", child_idx, + base->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), + node->GetLocation(), 1); + } + } - if (base_type.IsArrayType()) { - if (lldb::ValueObjectSP child_valobj_sp = base->GetChildAtIndex(child_idx)) - return child_valobj_sp; + if (child_valobj_sp) { + if (m_use_dynamic != lldb::eNoDynamicValues) { + if (auto dynamic_sp = child_valobj_sp->GetDynamicValue(m_use_dynamic)) + child_valobj_sp = std::move(dynamic_sp); + } + return child_valobj_sp; } int64_t signed_child_idx = node->GetIndex(); @@ -402,4 +499,107 @@ Interpreter::Visit(const BitFieldExtractionNode *node) { return child_valobj_sp; } +static llvm::Expected<lldb::TypeSystemSP> +GetTypeSystemFromCU(std::shared_ptr<StackFrame> ctx) { + SymbolContext symbol_context = + ctx->GetSymbolContext(lldb::eSymbolContextCompUnit); + lldb::LanguageType language = symbol_context.comp_unit->GetLanguage(); + + symbol_context = ctx->GetSymbolContext(lldb::eSymbolContextModule); + return symbol_context.module_sp->GetTypeSystemForLanguage(language); +} + +static CompilerType GetBasicType(lldb::TypeSystemSP type_system, + lldb::BasicType basic_type) { + if (type_system) + return type_system.get()->GetBasicTypeFromAST(basic_type); + + return CompilerType(); +} + +llvm::Expected<CompilerType> +Interpreter::PickIntegerType(lldb::TypeSystemSP type_system, + std::shared_ptr<ExecutionContextScope> ctx, + const IntegerLiteralNode *literal) { + // Binary, Octal, Hexadecimal and literals with a U suffix are allowed to be + // an unsigned integer. + bool unsigned_is_allowed = literal->IsUnsigned() || literal->GetRadix() != 10; + llvm::APInt apint = literal->GetValue(); + + llvm::SmallVector<std::pair<lldb::BasicType, lldb::BasicType>, 3> candidates; + if (literal->GetTypeSuffix() <= IntegerTypeSuffix::None) + candidates.emplace_back(lldb::eBasicTypeInt, + unsigned_is_allowed ? lldb::eBasicTypeUnsignedInt + : lldb::eBasicTypeInvalid); + if (literal->GetTypeSuffix() <= IntegerTypeSuffix::Long) + candidates.emplace_back(lldb::eBasicTypeLong, + unsigned_is_allowed ? lldb::eBasicTypeUnsignedLong + : lldb::eBasicTypeInvalid); + candidates.emplace_back(lldb::eBasicTypeLongLong, + lldb::eBasicTypeUnsignedLongLong); + for (auto [signed_, unsigned_] : candidates) { + CompilerType signed_type = type_system->GetBasicTypeFromAST(signed_); + if (!signed_type) + continue; + llvm::Expected<uint64_t> size = signed_type.GetBitSize(ctx.get()); + if (!size) + return size.takeError(); + if (!literal->IsUnsigned() && apint.isIntN(*size - 1)) + return signed_type; + if (unsigned_ != lldb::eBasicTypeInvalid && apint.isIntN(*size)) + return type_system->GetBasicTypeFromAST(unsigned_); + } + + return llvm::make_error<DILDiagnosticError>( + m_expr, + "integer literal is too large to be represented in any integer type", + literal->GetLocation()); +} + +llvm::Expected<lldb::ValueObjectSP> +Interpreter::Visit(const IntegerLiteralNode *node) { + llvm::Expected<lldb::TypeSystemSP> type_system = + GetTypeSystemFromCU(m_exe_ctx_scope); + if (!type_system) + return type_system.takeError(); + + llvm::Expected<CompilerType> type = + PickIntegerType(*type_system, m_exe_ctx_scope, node); + if (!type) + return type.takeError(); + + Scalar scalar = node->GetValue(); + // APInt from StringRef::getAsInteger comes with just enough bitwidth to + // hold the value. This adjusts APInt bitwidth to match the compiler type. + llvm::Expected<uint64_t> type_bitsize = + type->GetBitSize(m_exe_ctx_scope.get()); + if (!type_bitsize) + return type_bitsize.takeError(); + scalar.TruncOrExtendTo(*type_bitsize, false); + return ValueObject::CreateValueObjectFromScalar(m_target, scalar, *type, + "result"); +} + +llvm::Expected<lldb::ValueObjectSP> +Interpreter::Visit(const FloatLiteralNode *node) { + llvm::Expected<lldb::TypeSystemSP> type_system = + GetTypeSystemFromCU(m_exe_ctx_scope); + if (!type_system) + return type_system.takeError(); + + bool isFloat = + &node->GetValue().getSemantics() == &llvm::APFloat::IEEEsingle(); + lldb::BasicType basic_type = + isFloat ? lldb::eBasicTypeFloat : lldb::eBasicTypeDouble; + CompilerType type = GetBasicType(*type_system, basic_type); + + if (!type) + return llvm::make_error<DILDiagnosticError>( + m_expr, "unable to create a const literal", node->GetLocation()); + + Scalar scalar = node->GetValue(); + return ValueObject::CreateValueObjectFromScalar(m_target, scalar, type, + "result"); +} + } // namespace lldb_private::dil diff --git a/lldb/source/ValueObject/DILLexer.cpp b/lldb/source/ValueObject/DILLexer.cpp index eaefaf484bc1..0b2288a9d923 100644 --- a/lldb/source/ValueObject/DILLexer.cpp +++ b/lldb/source/ValueObject/DILLexer.cpp @@ -28,18 +28,23 @@ llvm::StringRef Token::GetTokenName(Kind kind) { return "coloncolon"; case Kind::eof: return "eof"; + case Kind::float_constant: + return "float_constant"; case Kind::identifier: return "identifier"; + case Kind::integer_constant: + return "integer_constant"; case Kind::l_paren: return "l_paren"; case Kind::l_square: return "l_square"; case Kind::minus: return "minus"; - case Kind::numeric_constant: - return "numeric_constant"; case Kind::period: return "period"; + return "l_square"; + case Kind::plus: + return "plus"; case Kind::r_paren: return "r_paren"; case Kind::r_square: @@ -70,13 +75,32 @@ static std::optional<llvm::StringRef> IsWord(llvm::StringRef expr, return candidate; } -static bool IsNumberBodyChar(char ch) { return IsDigit(ch) || IsLetter(ch); } +static bool IsNumberBodyChar(char ch) { + return IsDigit(ch) || IsLetter(ch) || ch == '.'; +} -static std::optional<llvm::StringRef> IsNumber(llvm::StringRef expr, - llvm::StringRef &remainder) { - if (IsDigit(remainder[0])) { - llvm::StringRef number = remainder.take_while(IsNumberBodyChar); - remainder = remainder.drop_front(number.size()); +static std::optional<llvm::StringRef> IsNumber(llvm::StringRef &remainder, + bool &isFloat) { + llvm::StringRef tail = remainder; + llvm::StringRef body = tail.take_while(IsNumberBodyChar); + size_t dots = body.count('.'); + if (dots > 1 || dots == body.size()) + return std::nullopt; + if (IsDigit(body.front()) || (body[0] == '.' && IsDigit(body[1]))) { + isFloat = dots == 1; + tail = tail.drop_front(body.size()); + bool isHex = body.contains_insensitive('x'); + bool hasExp = !isHex && body.contains_insensitive('e'); + bool hasHexExp = isHex && body.contains_insensitive('p'); + if (hasExp || hasHexExp) { + isFloat = true; // This marks numbers like 0x1p1 and 1e1 as float + if (body.ends_with_insensitive("e") || body.ends_with_insensitive("p")) + if (tail.consume_front("+") || tail.consume_front("-")) + tail = tail.drop_while(IsNumberBodyChar); + } + size_t number_length = remainder.size() - tail.size(); + llvm::StringRef number = remainder.take_front(number_length); + remainder = remainder.drop_front(number_length); return number; } return std::nullopt; @@ -106,18 +130,21 @@ llvm::Expected<Token> DILLexer::Lex(llvm::StringRef expr, return Token(Token::eof, "", (uint32_t)expr.size()); uint32_t position = cur_pos - expr.begin(); - std::optional<llvm::StringRef> maybe_number = IsNumber(expr, remainder); - if (maybe_number) - return Token(Token::numeric_constant, maybe_number->str(), position); + bool isFloat = false; + std::optional<llvm::StringRef> maybe_number = IsNumber(remainder, isFloat); + if (maybe_number) { + auto kind = isFloat ? Token::float_constant : Token::integer_constant; + return Token(kind, maybe_number->str(), position); + } std::optional<llvm::StringRef> maybe_word = IsWord(expr, remainder); if (maybe_word) return Token(Token::identifier, maybe_word->str(), position); constexpr std::pair<Token::Kind, const char *> operators[] = { - {Token::amp, "&"}, {Token::arrow, "->"}, {Token::coloncolon, "::"}, - {Token::l_paren, "("}, {Token::l_square, "["}, {Token::minus, "-"}, - {Token::period, "."}, {Token::r_paren, ")"}, {Token::r_square, "]"}, - {Token::star, "*"}, + {Token::amp, "&"}, {Token::arrow, "->"}, {Token::coloncolon, "::"}, + {Token::l_paren, "("}, {Token::l_square, "["}, {Token::minus, "-"}, + {Token::period, "."}, {Token::plus, "+"}, {Token::r_paren, ")"}, + {Token::r_square, "]"}, {Token::star, "*"}, }; for (auto [kind, str] : operators) { if (remainder.consume_front(str)) diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp index eac41fab9076..8c4f7fdb25be 100644 --- a/lldb/source/ValueObject/DILParser.cpp +++ b/lldb/source/ValueObject/DILParser.cpp @@ -179,10 +179,13 @@ ASTNodeUP DILParser::ParsePostfixExpression() { // Parse a primary_expression. // // primary_expression: +// numeric_literal // id_expression // "(" expression ")" // ASTNodeUP DILParser::ParsePrimaryExpression() { + if (CurToken().IsOneOf({Token::integer_constant, Token::float_constant})) + return ParseNumericLiteral(); if (CurToken().IsOneOf( {Token::coloncolon, Token::identifier, Token::l_paren})) { // Save the source location for the diagnostics message. @@ -346,6 +349,7 @@ void DILParser::BailOut(const std::string &error, uint32_t loc, m_dil_lexer.ResetTokenIdx(m_dil_lexer.NumLexedTokens() - 1); } +// FIXME: Remove this once subscript operator uses ScalarLiteralNode. // Parse a integer_literal. // // integer_literal: @@ -370,6 +374,69 @@ std::optional<int64_t> DILParser::ParseIntegerConstant() { return std::nullopt; } +// Parse a numeric_literal. +// +// numeric_literal: +// ? Token::integer_constant ? +// ? Token::floating_constant ? +// +ASTNodeUP DILParser::ParseNumericLiteral() { + ASTNodeUP numeric_constant; + if (CurToken().Is(Token::integer_constant)) + numeric_constant = ParseIntegerLiteral(); + else + numeric_constant = ParseFloatingPointLiteral(); + if (!numeric_constant) { + BailOut(llvm::formatv("Failed to parse token as numeric-constant: {0}", + CurToken()), + CurToken().GetLocation(), CurToken().GetSpelling().length()); + return std::make_unique<ErrorNode>(); + } + m_dil_lexer.Advance(); + return numeric_constant; +} + +ASTNodeUP DILParser::ParseIntegerLiteral() { + Token token = CurToken(); + auto spelling = token.GetSpelling(); + llvm::StringRef spelling_ref = spelling; + + auto radix = llvm::getAutoSenseRadix(spelling_ref); + IntegerTypeSuffix type = IntegerTypeSuffix::None; + bool is_unsigned = false; + if (spelling_ref.consume_back_insensitive("u")) + is_unsigned = true; + if (spelling_ref.consume_back_insensitive("ll")) + type = IntegerTypeSuffix::LongLong; + else if (spelling_ref.consume_back_insensitive("l")) + type = IntegerTypeSuffix::Long; + // Suffix 'u' can be only specified only once, before or after 'l' + if (!is_unsigned && spelling_ref.consume_back_insensitive("u")) + is_unsigned = true; + + llvm::APInt raw_value; + if (!spelling_ref.getAsInteger(radix, raw_value)) + return std::make_unique<IntegerLiteralNode>(token.GetLocation(), raw_value, + radix, is_unsigned, type); + return nullptr; +} + +ASTNodeUP DILParser::ParseFloatingPointLiteral() { + Token token = CurToken(); + auto spelling = token.GetSpelling(); + llvm::StringRef spelling_ref = spelling; + + llvm::APFloat raw_float(llvm::APFloat::IEEEdouble()); + if (spelling_ref.consume_back_insensitive("f")) + raw_float = llvm::APFloat(llvm::APFloat::IEEEsingle()); + + auto StatusOrErr = raw_float.convertFromString( + spelling_ref, llvm::APFloat::rmNearestTiesToEven); + if (!errorToBool(StatusOrErr.takeError())) + return std::make_unique<FloatLiteralNode>(token.GetLocation(), raw_float); + return nullptr; +} + void DILParser::Expect(Token::Kind kind) { if (CurToken().IsNot(kind)) { BailOut(llvm::formatv("expected {0}, got: {1}", kind, CurToken()), |
