================ @@ -170,1648 +169,171 @@ std::vector<const char *> MakeArgv(const llvm::ArrayRef<std::string> &strs) { argv.push_back(s.c_str()); argv.push_back(nullptr); return argv; -} - -// Send a "exited" event to indicate the process has exited. -void SendProcessExitedEvent(DAP &dap, lldb::SBProcess &process) { - llvm::json::Object event(CreateEventObject("exited")); - llvm::json::Object body; - body.try_emplace("exitCode", (int64_t)process.GetExitStatus()); - event.try_emplace("body", std::move(body)); - dap.SendJSON(llvm::json::Value(std::move(event))); -} - -void SendThreadExitedEvent(DAP &dap, lldb::tid_t tid) { - llvm::json::Object event(CreateEventObject("thread")); - llvm::json::Object body; - body.try_emplace("reason", "exited"); - body.try_emplace("threadId", (int64_t)tid); - event.try_emplace("body", std::move(body)); - dap.SendJSON(llvm::json::Value(std::move(event))); -} - -// Send a "continued" event to indicate the process is in the running state. -void SendContinuedEvent(DAP &dap) { - lldb::SBProcess process = dap.target.GetProcess(); - if (!process.IsValid()) { - return; - } - - // If the focus thread is not set then we haven't reported any thread status - // to the client, so nothing to report. - if (!dap.configuration_done_sent || dap.focus_tid == LLDB_INVALID_THREAD_ID) { - return; - } - - llvm::json::Object event(CreateEventObject("continued")); - llvm::json::Object body; - body.try_emplace("threadId", (int64_t)dap.focus_tid); - body.try_emplace("allThreadsContinued", true); - event.try_emplace("body", std::move(body)); - dap.SendJSON(llvm::json::Value(std::move(event))); -} - -// Send a thread stopped event for all threads as long as the process -// is stopped. -void SendThreadStoppedEvent(DAP &dap) { - lldb::SBProcess process = dap.target.GetProcess(); - if (process.IsValid()) { - auto state = process.GetState(); - if (state == lldb::eStateStopped) { - llvm::DenseSet<lldb::tid_t> old_thread_ids; - old_thread_ids.swap(dap.thread_ids); - uint32_t stop_id = process.GetStopID(); - const uint32_t num_threads = process.GetNumThreads(); - - // First make a pass through the threads to see if the focused thread - // has a stop reason. In case the focus thread doesn't have a stop - // reason, remember the first thread that has a stop reason so we can - // set it as the focus thread if below if needed. - lldb::tid_t first_tid_with_reason = LLDB_INVALID_THREAD_ID; - uint32_t num_threads_with_reason = 0; - bool focus_thread_exists = false; - for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) { - lldb::SBThread thread = process.GetThreadAtIndex(thread_idx); - const lldb::tid_t tid = thread.GetThreadID(); - const bool has_reason = ThreadHasStopReason(thread); - // If the focus thread doesn't have a stop reason, clear the thread ID - if (tid == dap.focus_tid) { - focus_thread_exists = true; - if (!has_reason) - dap.focus_tid = LLDB_INVALID_THREAD_ID; - } - if (has_reason) { - ++num_threads_with_reason; - if (first_tid_with_reason == LLDB_INVALID_THREAD_ID) - first_tid_with_reason = tid; - } - } - - // We will have cleared dap.focus_tid if the focus thread doesn't have - // a stop reason, so if it was cleared, or wasn't set, or doesn't exist, - // then set the focus thread to the first thread with a stop reason. - if (!focus_thread_exists || dap.focus_tid == LLDB_INVALID_THREAD_ID) - dap.focus_tid = first_tid_with_reason; - - // If no threads stopped with a reason, then report the first one so - // we at least let the UI know we stopped. - if (num_threads_with_reason == 0) { - lldb::SBThread thread = process.GetThreadAtIndex(0); - dap.focus_tid = thread.GetThreadID(); - dap.SendJSON(CreateThreadStopped(dap, thread, stop_id)); - } else { - for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) { - lldb::SBThread thread = process.GetThreadAtIndex(thread_idx); - dap.thread_ids.insert(thread.GetThreadID()); - if (ThreadHasStopReason(thread)) { - dap.SendJSON(CreateThreadStopped(dap, thread, stop_id)); - } - } - } - - for (auto tid : old_thread_ids) { - auto end = dap.thread_ids.end(); - auto pos = dap.thread_ids.find(tid); - if (pos == end) - SendThreadExitedEvent(dap, tid); - } - } else { - if (dap.log) - *dap.log << "error: SendThreadStoppedEvent() when process" - " isn't stopped (" - << lldb::SBDebugger::StateAsCString(state) << ')' << std::endl; - } - } else { - if (dap.log) - *dap.log << "error: SendThreadStoppedEvent() invalid process" - << std::endl; - } - dap.RunStopCommands(); -} - -// "ProcessEvent": { -// "allOf": [ -// { "$ref": "#/definitions/Event" }, -// { -// "type": "object", -// "description": "Event message for 'process' event type. The event -// indicates that the debugger has begun debugging a -// new process. Either one that it has launched, or one -// that it has attached to.", -// "properties": { -// "event": { -// "type": "string", -// "enum": [ "process" ] -// }, -// "body": { -// "type": "object", -// "properties": { -// "name": { -// "type": "string", -// "description": "The logical name of the process. This is -// usually the full path to process's executable -// file. Example: /home/myproj/program.js." -// }, -// "systemProcessId": { -// "type": "integer", -// "description": "The system process id of the debugged process. -// This property will be missing for non-system -// processes." -// }, -// "isLocalProcess": { -// "type": "boolean", -// "description": "If true, the process is running on the same -// computer as the debug adapter." -// }, -// "startMethod": { -// "type": "string", -// "enum": [ "launch", "attach", "attachForSuspendedLaunch" ], -// "description": "Describes how the debug engine started -// debugging this process.", -// "enumDescriptions": [ -// "Process was launched under the debugger.", -// "Debugger attached to an existing process.", -// "A project launcher component has launched a new process in -// a suspended state and then asked the debugger to attach." -// ] -// } -// }, -// "required": [ "name" ] -// } -// }, -// "required": [ "event", "body" ] -// } -// ] -// } -void SendProcessEvent(DAP &dap, LaunchMethod launch_method) { - lldb::SBFileSpec exe_fspec = dap.target.GetExecutable(); - char exe_path[PATH_MAX]; - exe_fspec.GetPath(exe_path, sizeof(exe_path)); - llvm::json::Object event(CreateEventObject("process")); - llvm::json::Object body; - EmplaceSafeString(body, "name", std::string(exe_path)); - const auto pid = dap.target.GetProcess().GetProcessID(); - body.try_emplace("systemProcessId", (int64_t)pid); - body.try_emplace("isLocalProcess", true); - const char *startMethod = nullptr; - switch (launch_method) { - case Launch: - startMethod = "launch"; - break; - case Attach: - startMethod = "attach"; - break; - case AttachForSuspendedLaunch: - startMethod = "attachForSuspendedLaunch"; - break; - } - body.try_emplace("startMethod", startMethod); - event.try_emplace("body", std::move(body)); - dap.SendJSON(llvm::json::Value(std::move(event))); -} - -// Grab any STDOUT and STDERR from the process and send it up to VS Code -// via an "output" event to the "stdout" and "stderr" categories. -void SendStdOutStdErr(DAP &dap, lldb::SBProcess &process) { - char buffer[OutputBufferSize]; - size_t count; - while ((count = process.GetSTDOUT(buffer, sizeof(buffer))) > 0) - dap.SendOutput(OutputType::Stdout, llvm::StringRef(buffer, count)); - while ((count = process.GetSTDERR(buffer, sizeof(buffer))) > 0) - dap.SendOutput(OutputType::Stderr, llvm::StringRef(buffer, count)); -} - -void ProgressEventThreadFunction(DAP &dap) { - llvm::set_thread_name(dap.name + ".progress_handler"); - lldb::SBListener listener("lldb-dap.progress.listener"); - dap.debugger.GetBroadcaster().AddListener( - listener, lldb::SBDebugger::eBroadcastBitProgress | - lldb::SBDebugger::eBroadcastBitExternalProgress); - dap.broadcaster.AddListener(listener, eBroadcastBitStopProgressThread); - lldb::SBEvent event; - bool done = false; - while (!done) { - if (listener.WaitForEvent(1, event)) { - const auto event_mask = event.GetType(); - if (event.BroadcasterMatchesRef(dap.broadcaster)) { - if (event_mask & eBroadcastBitStopProgressThread) { - done = true; - } - } else { - uint64_t progress_id = 0; - uint64_t completed = 0; - uint64_t total = 0; - bool is_debugger_specific = false; - const char *message = lldb::SBDebugger::GetProgressFromEvent( - event, progress_id, completed, total, is_debugger_specific); - if (message) - dap.SendProgressEvent(progress_id, message, completed, total); - } - } - } -} - -// All events from the debugger, target, process, thread and frames are -// received in this function that runs in its own thread. We are using a -// "FILE *" to output packets back to VS Code and they have mutexes in them -// them prevent multiple threads from writing simultaneously so no locking -// is required. -void EventThreadFunction(DAP &dap) { - llvm::set_thread_name(dap.name + ".event_handler"); - lldb::SBEvent event; - lldb::SBListener listener = dap.debugger.GetListener(); - dap.broadcaster.AddListener(listener, eBroadcastBitStopEventThread); - bool done = false; - while (!done) { - if (listener.WaitForEvent(1, event)) { - const auto event_mask = event.GetType(); - if (lldb::SBProcess::EventIsProcessEvent(event)) { - lldb::SBProcess process = lldb::SBProcess::GetProcessFromEvent(event); - if (event_mask & lldb::SBProcess::eBroadcastBitStateChanged) { - auto state = lldb::SBProcess::GetStateFromEvent(event); - switch (state) { - case lldb::eStateInvalid: - // Not a state event - break; - case lldb::eStateUnloaded: - break; - case lldb::eStateConnected: - break; - case lldb::eStateAttaching: - break; - case lldb::eStateLaunching: - break; - case lldb::eStateStepping: - break; - case lldb::eStateCrashed: - break; - case lldb::eStateDetached: - break; - case lldb::eStateSuspended: - break; - case lldb::eStateStopped: - // We launch and attach in synchronous mode then the first stop - // event will not be delivered. If we use "launchCommands" during a - // launch or "attachCommands" during an attach we might some process - // stop events which we do not want to send an event for. We will - // manually send a stopped event in request_configurationDone(...) - // so don't send any before then. - if (dap.configuration_done_sent) { - // Only report a stopped event if the process was not - // automatically restarted. - if (!lldb::SBProcess::GetRestartedFromEvent(event)) { - SendStdOutStdErr(dap, process); - SendThreadStoppedEvent(dap); - } - } - break; - case lldb::eStateRunning: - dap.WillContinue(); - SendContinuedEvent(dap); - break; - case lldb::eStateExited: - lldb::SBStream stream; - process.GetStatus(stream); - dap.SendOutput(OutputType::Console, stream.GetData()); - - // When restarting, we can get an "exited" event for the process we - // just killed with the old PID, or even with no PID. In that case - // we don't have to terminate the session. - if (process.GetProcessID() == LLDB_INVALID_PROCESS_ID || - process.GetProcessID() == dap.restarting_process_id) { - dap.restarting_process_id = LLDB_INVALID_PROCESS_ID; - } else { - // Run any exit LLDB commands the user specified in the - // launch.json - dap.RunExitCommands(); - SendProcessExitedEvent(dap, process); - dap.SendTerminatedEvent(); - done = true; - } - break; - } - } else if ((event_mask & lldb::SBProcess::eBroadcastBitSTDOUT) || - (event_mask & lldb::SBProcess::eBroadcastBitSTDERR)) { - SendStdOutStdErr(dap, process); - } - } else if (lldb::SBBreakpoint::EventIsBreakpointEvent(event)) { - if (event_mask & lldb::SBTarget::eBroadcastBitBreakpointChanged) { - auto event_type = - lldb::SBBreakpoint::GetBreakpointEventTypeFromEvent(event); - auto bp = Breakpoint( - dap, lldb::SBBreakpoint::GetBreakpointFromEvent(event)); - // If the breakpoint was originated from the IDE, it will have the - // BreakpointBase::GetBreakpointLabel() label attached. Regardless - // of wether the locations were added or removed, the breakpoint - // ins't going away, so we the reason is always "changed". - if ((event_type & lldb::eBreakpointEventTypeLocationsAdded || - event_type & lldb::eBreakpointEventTypeLocationsRemoved) && - bp.MatchesName(BreakpointBase::GetBreakpointLabel())) { - auto bp_event = CreateEventObject("breakpoint"); - llvm::json::Object body; - // As VSCode already knows the path of this breakpoint, we don't - // need to send it back as part of a "changed" event. This - // prevent us from sending to VSCode paths that should be source - // mapped. Note that CreateBreakpoint doesn't apply source mapping. - // Besides, the current implementation of VSCode ignores the - // "source" element of breakpoint events. - llvm::json::Value source_bp = CreateBreakpoint(&bp); - source_bp.getAsObject()->erase("source"); - - body.try_emplace("breakpoint", source_bp); - body.try_emplace("reason", "changed"); - bp_event.try_emplace("body", std::move(body)); - dap.SendJSON(llvm::json::Value(std::move(bp_event))); - } - } - } else if (event.BroadcasterMatchesRef(dap.broadcaster)) { - if (event_mask & eBroadcastBitStopEventThread) { - done = true; - } - } - } - } -} - -lldb::SBValue FindVariable(DAP &dap, uint64_t variablesReference, - llvm::StringRef name) { - lldb::SBValue variable; - if (lldb::SBValueList *top_scope = - GetTopLevelScope(dap, variablesReference)) { - bool is_duplicated_variable_name = name.contains(" @"); - // variablesReference is one of our scopes, not an actual variable it is - // asking for a variable in locals or globals or registers - int64_t end_idx = top_scope->GetSize(); - // Searching backward so that we choose the variable in closest scope - // among variables of the same name. - for (int64_t i = end_idx - 1; i >= 0; --i) { - lldb::SBValue curr_variable = top_scope->GetValueAtIndex(i); - std::string variable_name = CreateUniqueVariableNameForDisplay( - curr_variable, is_duplicated_variable_name); - if (variable_name == name) { - variable = curr_variable; - break; - } - } - } else { - // This is not under the globals or locals scope, so there are no duplicated - // names. - - // We have a named item within an actual variable so we need to find it - // withing the container variable by name. - lldb::SBValue container = dap.variables.GetVariable(variablesReference); - variable = container.GetChildMemberWithName(name.data()); - if (!variable.IsValid()) { - if (name.starts_with("[")) { - llvm::StringRef index_str(name.drop_front(1)); - uint64_t index = 0; - if (!index_str.consumeInteger(0, index)) { - if (index_str == "]") - variable = container.GetChildAtIndex(index); - } - } - } - } - return variable; -} - -// Both attach and launch take a either a sourcePath or sourceMap -// argument (or neither), from which we need to set the target.source-map. -void SetSourceMapFromArguments(DAP &dap, const llvm::json::Object &arguments) { - const char *sourceMapHelp = - "source must be be an array of two-element arrays, " - "each containing a source and replacement path string.\n"; - - std::string sourceMapCommand; - llvm::raw_string_ostream strm(sourceMapCommand); - strm << "settings set target.source-map "; - const auto sourcePath = GetString(arguments, "sourcePath"); - - // sourceMap is the new, more general form of sourcePath and overrides it. - constexpr llvm::StringRef sourceMapKey = "sourceMap"; - - if (const auto *sourceMapArray = arguments.getArray(sourceMapKey)) { - for (const auto &value : *sourceMapArray) { - const auto *mapping = value.getAsArray(); - if (mapping == nullptr || mapping->size() != 2 || - (*mapping)[0].kind() != llvm::json::Value::String || - (*mapping)[1].kind() != llvm::json::Value::String) { - dap.SendOutput(OutputType::Console, llvm::StringRef(sourceMapHelp)); - return; - } - const auto mapFrom = GetAsString((*mapping)[0]); - const auto mapTo = GetAsString((*mapping)[1]); - strm << "\"" << mapFrom << "\" \"" << mapTo << "\" "; - } - } else if (const auto *sourceMapObj = arguments.getObject(sourceMapKey)) { - for (const auto &[key, value] : *sourceMapObj) { - if (value.kind() == llvm::json::Value::String) { - strm << "\"" << key.str() << "\" \"" << GetAsString(value) << "\" "; - } - } - } else { - if (ObjectContainsKey(arguments, sourceMapKey)) { - dap.SendOutput(OutputType::Console, llvm::StringRef(sourceMapHelp)); - return; - } - if (sourcePath.empty()) - return; - // Do any source remapping needed before we create our targets - strm << "\".\" \"" << sourcePath << "\""; - } - if (!sourceMapCommand.empty()) { - dap.RunLLDBCommands("Setting source map:", {sourceMapCommand}); - } -} - -// Fill in the stack frames of the thread. -// -// Threads stacks may contain runtime specific extended backtraces, when -// constructing a stack trace first report the full thread stack trace then -// perform a breadth first traversal of any extended backtrace frames. -// -// For example: -// -// Thread (id=th0) stack=[s0, s1, s2, s3] -// \ Extended backtrace "libdispatch" Thread (id=th1) stack=[s0, s1] -// \ Extended backtrace "libdispatch" Thread (id=th2) stack=[s0, s1] -// \ Extended backtrace "Application Specific Backtrace" Thread (id=th3) -// stack=[s0, s1, s2] -// -// Which will flatten into: -// -// 0. th0->s0 -// 1. th0->s1 -// 2. th0->s2 -// 3. th0->s3 -// 4. label - Enqueued from th1, sf=-1, i=-4 -// 5. th1->s0 -// 6. th1->s1 -// 7. label - Enqueued from th2 -// 8. th2->s0 -// 9. th2->s1 -// 10. label - Application Specific Backtrace -// 11. th3->s0 -// 12. th3->s1 -// 13. th3->s2 -// -// s=3,l=3 = [th0->s3, label1, th1->s0] -bool FillStackFrames(DAP &dap, lldb::SBThread &thread, - llvm::json::Array &stack_frames, int64_t &offset, - const int64_t start_frame, const int64_t levels) { - bool reached_end_of_stack = false; - for (int64_t i = start_frame; - static_cast<int64_t>(stack_frames.size()) < levels; i++) { - if (i == -1) { - stack_frames.emplace_back( - CreateExtendedStackFrameLabel(thread, dap.frame_format)); - continue; - } - - lldb::SBFrame frame = thread.GetFrameAtIndex(i); - if (!frame.IsValid()) { - offset += thread.GetNumFrames() + 1 /* label between threads */; - reached_end_of_stack = true; - break; - } - - stack_frames.emplace_back(CreateStackFrame(frame, dap.frame_format)); - } - - if (dap.display_extended_backtrace && reached_end_of_stack) { - // Check for any extended backtraces. - for (uint32_t bt = 0; - bt < thread.GetProcess().GetNumExtendedBacktraceTypes(); bt++) { - lldb::SBThread backtrace = thread.GetExtendedBacktraceThread( - thread.GetProcess().GetExtendedBacktraceTypeAtIndex(bt)); - if (!backtrace.IsValid()) - continue; - - reached_end_of_stack = FillStackFrames( - dap, backtrace, stack_frames, offset, - (start_frame - offset) > 0 ? start_frame - offset : -1, levels); - if (static_cast<int64_t>(stack_frames.size()) >= levels) - break; - } - } - - return reached_end_of_stack; -} - -// "AttachRequest": { -// "allOf": [ { "$ref": "#/definitions/Request" }, { -// "type": "object", -// "description": "Attach request; value of command field is 'attach'.", -// "properties": { -// "command": { -// "type": "string", -// "enum": [ "attach" ] -// }, -// "arguments": { -// "$ref": "#/definitions/AttachRequestArguments" -// } -// }, -// "required": [ "command", "arguments" ] -// }] -// }, -// "AttachRequestArguments": { -// "type": "object", -// "description": "Arguments for 'attach' request.\nThe attach request has no -// standardized attributes." -// }, -// "AttachResponse": { -// "allOf": [ { "$ref": "#/definitions/Response" }, { -// "type": "object", -// "description": "Response to 'attach' request. This is just an -// acknowledgement, so no body field is required." -// }] -// } -void request_attach(DAP &dap, const llvm::json::Object &request) { - dap.is_attach = true; - dap.last_launch_or_attach_request = request; - llvm::json::Object response; - lldb::SBError error; - FillResponse(request, response); - lldb::SBAttachInfo attach_info; - const int invalid_port = 0; - const auto *arguments = request.getObject("arguments"); - const lldb::pid_t pid = - GetUnsigned(arguments, "pid", LLDB_INVALID_PROCESS_ID); - const auto gdb_remote_port = - GetUnsigned(arguments, "gdb-remote-port", invalid_port); - const auto gdb_remote_hostname = - GetString(arguments, "gdb-remote-hostname", "localhost"); - if (pid != LLDB_INVALID_PROCESS_ID) - attach_info.SetProcessID(pid); - const auto wait_for = GetBoolean(arguments, "waitFor", false); - attach_info.SetWaitForLaunch(wait_for, false /*async*/); - dap.init_commands = GetStrings(arguments, "initCommands"); - dap.pre_run_commands = GetStrings(arguments, "preRunCommands"); - dap.stop_commands = GetStrings(arguments, "stopCommands"); - dap.exit_commands = GetStrings(arguments, "exitCommands"); - dap.terminate_commands = GetStrings(arguments, "terminateCommands"); - auto attachCommands = GetStrings(arguments, "attachCommands"); - llvm::StringRef core_file = GetString(arguments, "coreFile"); - const uint64_t timeout_seconds = GetUnsigned(arguments, "timeout", 30); - dap.stop_at_entry = - core_file.empty() ? GetBoolean(arguments, "stopOnEntry", false) : true; - dap.post_run_commands = GetStrings(arguments, "postRunCommands"); - const llvm::StringRef debuggerRoot = GetString(arguments, "debuggerRoot"); - dap.enable_auto_variable_summaries = - GetBoolean(arguments, "enableAutoVariableSummaries", false); - dap.enable_synthetic_child_debugging = - GetBoolean(arguments, "enableSyntheticChildDebugging", false); - dap.display_extended_backtrace = - GetBoolean(arguments, "displayExtendedBacktrace", false); - dap.command_escape_prefix = GetString(arguments, "commandEscapePrefix", "`"); - dap.SetFrameFormat(GetString(arguments, "customFrameFormat")); - dap.SetThreadFormat(GetString(arguments, "customThreadFormat")); - - PrintWelcomeMessage(dap); - - // This is a hack for loading DWARF in .o files on Mac where the .o files - // in the debug map of the main executable have relative paths which require - // the lldb-dap binary to have its working directory set to that relative - // root for the .o files in order to be able to load debug info. - if (!debuggerRoot.empty()) - llvm::sys::fs::set_current_path(debuggerRoot); - - // Run any initialize LLDB commands the user specified in the launch.json - if (llvm::Error err = dap.RunInitCommands()) { - response["success"] = false; - EmplaceSafeString(response, "message", llvm::toString(std::move(err))); - dap.SendJSON(llvm::json::Value(std::move(response))); - return; - } - - SetSourceMapFromArguments(dap, *arguments); - - lldb::SBError status; - dap.SetTarget(dap.CreateTargetFromArguments(*arguments, status)); - if (status.Fail()) { - response["success"] = llvm::json::Value(false); - EmplaceSafeString(response, "message", status.GetCString()); - dap.SendJSON(llvm::json::Value(std::move(response))); - return; - } - - // Run any pre run LLDB commands the user specified in the launch.json - if (llvm::Error err = dap.RunPreRunCommands()) { - response["success"] = false; - EmplaceSafeString(response, "message", llvm::toString(std::move(err))); - dap.SendJSON(llvm::json::Value(std::move(response))); - return; - } - - if ((pid == LLDB_INVALID_PROCESS_ID || gdb_remote_port == invalid_port) && - wait_for) { - char attach_msg[256]; - auto attach_msg_len = snprintf(attach_msg, sizeof(attach_msg), - "Waiting to attach to \"%s\"...", - dap.target.GetExecutable().GetFilename()); - dap.SendOutput(OutputType::Console, - llvm::StringRef(attach_msg, attach_msg_len)); - } - if (attachCommands.empty()) { - // No "attachCommands", just attach normally. - // Disable async events so the attach will be successful when we return from - // the launch call and the launch will happen synchronously - dap.debugger.SetAsync(false); - if (core_file.empty()) { - if ((pid != LLDB_INVALID_PROCESS_ID) && - (gdb_remote_port != invalid_port)) { - // If both pid and port numbers are specified. - error.SetErrorString("The user can't specify both pid and port"); - } else if (gdb_remote_port != invalid_port) { - // If port is specified and pid is not. - lldb::SBListener listener = dap.debugger.GetListener(); - - // If the user hasn't provided the hostname property, default localhost - // being used. - std::string connect_url = - llvm::formatv("connect://{0}:", gdb_remote_hostname); - connect_url += std::to_string(gdb_remote_port); - dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote", - error); - } else { - // Attach by process name or id. - dap.target.Attach(attach_info, error); - } - } else - dap.target.LoadCore(core_file.data(), error); - // Reenable async events - dap.debugger.SetAsync(true); - } else { - // We have "attachCommands" that are a set of commands that are expected - // to execute the commands after which a process should be created. If there - // is no valid process after running these commands, we have failed. - if (llvm::Error err = dap.RunAttachCommands(attachCommands)) { - response["success"] = false; - EmplaceSafeString(response, "message", llvm::toString(std::move(err))); - dap.SendJSON(llvm::json::Value(std::move(response))); - return; - } - // The custom commands might have created a new target so we should use the - // selected target after these commands are run. - dap.target = dap.debugger.GetSelectedTarget(); - - // Make sure the process is attached and stopped before proceeding as the - // the launch commands are not run using the synchronous mode. - error = dap.WaitForProcessToStop(timeout_seconds); - } - - if (error.Success() && core_file.empty()) { - auto attached_pid = dap.target.GetProcess().GetProcessID(); - if (attached_pid == LLDB_INVALID_PROCESS_ID) { - if (attachCommands.empty()) - error.SetErrorString("failed to attach to a process"); - else - error.SetErrorString("attachCommands failed to attach to a process"); - } - } - - if (error.Fail()) { - response["success"] = llvm::json::Value(false); - EmplaceSafeString(response, "message", std::string(error.GetCString())); - } else { - dap.RunPostRunCommands(); - } - - dap.SendJSON(llvm::json::Value(std::move(response))); - if (error.Success()) { - SendProcessEvent(dap, Attach); - dap.SendJSON(CreateEventObject("initialized")); - } -} - -// "BreakpointLocationsRequest": { -// "allOf": [ { "$ref": "#/definitions/Request" }, { -// "type": "object", -// "description": "The `breakpointLocations` request returns all possible -// locations for source breakpoints in a given range.\nClients should only -// call this request if the corresponding capability -// `supportsBreakpointLocationsRequest` is true.", -// "properties": { -// "command": { -// "type": "string", -// "enum": [ "breakpointLocations" ] -// }, -// "arguments": { -// "$ref": "#/definitions/BreakpointLocationsArguments" -// } -// }, -// "required": [ "command" ] -// }] -// }, -// "BreakpointLocationsArguments": { -// "type": "object", -// "description": "Arguments for `breakpointLocations` request.", -// "properties": { -// "source": { -// "$ref": "#/definitions/Source", -// "description": "The source location of the breakpoints; either -// `source.path` or `source.sourceReference` must be specified." -// }, -// "line": { -// "type": "integer", -// "description": "Start line of range to search possible breakpoint -// locations in. If only the line is specified, the request returns all -// possible locations in that line." -// }, -// "column": { -// "type": "integer", -// "description": "Start position within `line` to search possible -// breakpoint locations in. It is measured in UTF-16 code units and the -// client capability `columnsStartAt1` determines whether it is 0- or -// 1-based. If no column is given, the first position in the start line is -// assumed." -// }, -// "endLine": { -// "type": "integer", -// "description": "End line of range to search possible breakpoint -// locations in. If no end line is given, then the end line is assumed to -// be the start line." -// }, -// "endColumn": { -// "type": "integer", -// "description": "End position within `endLine` to search possible -// breakpoint locations in. It is measured in UTF-16 code units and the -// client capability `columnsStartAt1` determines whether it is 0- or -// 1-based. If no end column is given, the last position in the end line -// is assumed." -// } -// }, -// "required": [ "source", "line" ] -// }, -// "BreakpointLocationsResponse": { -// "allOf": [ { "$ref": "#/definitions/Response" }, { -// "type": "object", -// "description": "Response to `breakpointLocations` request.\nContains -// possible locations for source breakpoints.", -// "properties": { -// "body": { -// "type": "object", -// "properties": { -// "breakpoints": { -// "type": "array", -// "items": { -// "$ref": "#/definitions/BreakpointLocation" -// }, -// "description": "Sorted set of possible breakpoint locations." -// } -// }, -// "required": [ "breakpoints" ] -// } -// }, -// "required": [ "body" ] -// }] -// }, -// "BreakpointLocation": { -// "type": "object", -// "description": "Properties of a breakpoint location returned from the -// `breakpointLocations` request.", -// "properties": { -// "line": { -// "type": "integer", -// "description": "Start line of breakpoint location." -// }, -// "column": { -// "type": "integer", -// "description": "The start position of a breakpoint location. Position -// is measured in UTF-16 code units and the client capability -// `columnsStartAt1` determines whether it is 0- or 1-based." -// }, -// "endLine": { -// "type": "integer", -// "description": "The end line of breakpoint location if the location -// covers a range." -// }, -// "endColumn": { -// "type": "integer", -// "description": "The end position of a breakpoint location (if the -// location covers a range). Position is measured in UTF-16 code units and -// the client capability `columnsStartAt1` determines whether it is 0- or -// 1-based." -// } -// }, -// "required": [ "line" ] -// }, -void request_breakpointLocations(DAP &dap, const llvm::json::Object &request) { - llvm::json::Object response; - FillResponse(request, response); - auto *arguments = request.getObject("arguments"); - auto *source = arguments->getObject("source"); - std::string path = GetString(source, "path").str(); - uint64_t start_line = GetUnsigned(arguments, "line", 0); - uint64_t start_column = GetUnsigned(arguments, "column", 0); - uint64_t end_line = GetUnsigned(arguments, "endLine", start_line); - uint64_t end_column = - GetUnsigned(arguments, "endColumn", std::numeric_limits<uint64_t>::max()); - - lldb::SBFileSpec file_spec(path.c_str(), true); - lldb::SBSymbolContextList compile_units = - dap.target.FindCompileUnits(file_spec); - - // Find all relevant lines & columns - llvm::SmallVector<std::pair<uint32_t, uint32_t>, 8> locations; - for (uint32_t c_idx = 0, c_limit = compile_units.GetSize(); c_idx < c_limit; - ++c_idx) { - const lldb::SBCompileUnit &compile_unit = - compile_units.GetContextAtIndex(c_idx).GetCompileUnit(); - if (!compile_unit.IsValid()) - continue; - lldb::SBFileSpec primary_file_spec = compile_unit.GetFileSpec(); - - // Go through the line table and find all matching lines / columns - for (uint32_t l_idx = 0, l_limit = compile_unit.GetNumLineEntries(); - l_idx < l_limit; ++l_idx) { - lldb::SBLineEntry line_entry = compile_unit.GetLineEntryAtIndex(l_idx); - - // Filter by line / column - uint32_t line = line_entry.GetLine(); - if (line < start_line || line > end_line) - continue; - uint32_t column = line_entry.GetColumn(); - if (column == LLDB_INVALID_COLUMN_NUMBER) - continue; - if (line == start_line && column < start_column) - continue; - if (line == end_line && column > end_column) - continue; - - // Make sure we are in the right file. - // We might have a match on line & column range and still - // be in the wrong file, e.g. for included files. - // Given that the involved pointers point into LLDB's string pool, - // we can directly compare the `const char*` pointers. - if (line_entry.GetFileSpec().GetFilename() != - primary_file_spec.GetFilename() || - line_entry.GetFileSpec().GetDirectory() != - primary_file_spec.GetDirectory()) - continue; - - locations.emplace_back(line, column); - } - } - - // The line entries are sorted by addresses, but we must return the list - // ordered by line / column position. - std::sort(locations.begin(), locations.end()); - locations.erase(std::unique(locations.begin(), locations.end()), - locations.end()); - - llvm::json::Array locations_json; - for (auto &l : locations) { - llvm::json::Object location; - location.try_emplace("line", l.first); - location.try_emplace("column", l.second); - locations_json.emplace_back(std::move(location)); - } - - llvm::json::Object body; - body.try_emplace("breakpoints", std::move(locations_json)); - response.try_emplace("body", std::move(body)); - dap.SendJSON(llvm::json::Value(std::move(response))); -} - -// "ContinueRequest": { -// "allOf": [ { "$ref": "#/definitions/Request" }, { -// "type": "object", -// "description": "Continue request; value of command field is 'continue'. -// The request starts the debuggee to run again.", -// "properties": { -// "command": { -// "type": "string", -// "enum": [ "continue" ] -// }, -// "arguments": { -// "$ref": "#/definitions/ContinueArguments" -// } -// }, -// "required": [ "command", "arguments" ] -// }] -// }, -// "ContinueArguments": { -// "type": "object", -// "description": "Arguments for 'continue' request.", -// "properties": { -// "threadId": { -// "type": "integer", -// "description": "Continue execution for the specified thread (if -// possible). If the backend cannot continue on a single -// thread but will continue on all threads, it should -// set the allThreadsContinued attribute in the response -// to true." -// } -// }, -// "required": [ "threadId" ] -// }, -// "ContinueResponse": { -// "allOf": [ { "$ref": "#/definitions/Response" }, { -// "type": "object", -// "description": "Response to 'continue' request.", -// "properties": { -// "body": { -// "type": "object", -// "properties": { -// "allThreadsContinued": { -// "type": "boolean", -// "description": "If true, the continue request has ignored the -// specified thread and continued all threads -// instead. If this attribute is missing a value -// of 'true' is assumed for backward -// compatibility." -// } -// } -// } -// }, -// "required": [ "body" ] -// }] -// } -void request_continue(DAP &dap, const llvm::json::Object &request) { - llvm::json::Object response; - FillResponse(request, response); - lldb::SBProcess process = dap.target.GetProcess(); - lldb::SBError error = process.Continue(); - llvm::json::Object body; - body.try_emplace("allThreadsContinued", true); - response.try_emplace("body", std::move(body)); - dap.SendJSON(llvm::json::Value(std::move(response))); -} - -// "ConfigurationDoneRequest": { -// "allOf": [ { "$ref": "#/definitions/Request" }, { -// "type": "object", -// "description": "ConfigurationDone request; value of command field -// is 'configurationDone'.\nThe client of the debug protocol must -// send this request at the end of the sequence of configuration -// requests (which was started by the InitializedEvent).", -// "properties": { -// "command": { -// "type": "string", -// "enum": [ "configurationDone" ] -// }, -// "arguments": { -// "$ref": "#/definitions/ConfigurationDoneArguments" -// } -// }, -// "required": [ "command" ] -// }] -// }, -// "ConfigurationDoneArguments": { -// "type": "object", -// "description": "Arguments for 'configurationDone' request.\nThe -// configurationDone request has no standardized attributes." -// }, -// "ConfigurationDoneResponse": { -// "allOf": [ { "$ref": "#/definitions/Response" }, { -// "type": "object", -// "description": "Response to 'configurationDone' request. This is -// just an acknowledgement, so no body field is required." -// }] -// }, -void request_configurationDone(DAP &dap, const llvm::json::Object &request) { - llvm::json::Object response; - FillResponse(request, response); - dap.SendJSON(llvm::json::Value(std::move(response))); - dap.configuration_done_sent = true; - if (dap.stop_at_entry) - SendThreadStoppedEvent(dap); - else - dap.target.GetProcess().Continue(); -} - -// "DisconnectRequest": { -// "allOf": [ { "$ref": "#/definitions/Request" }, { -// "type": "object", -// "description": "Disconnect request; value of command field is -// 'disconnect'.", -// "properties": { -// "command": { -// "type": "string", -// "enum": [ "disconnect" ] -// }, -// "arguments": { -// "$ref": "#/definitions/DisconnectArguments" -// } -// }, -// "required": [ "command" ] -// }] -// }, -// "DisconnectArguments": { -// "type": "object", -// "description": "Arguments for 'disconnect' request.", -// "properties": { -// "terminateDebuggee": { -// "type": "boolean", -// "description": "Indicates whether the debuggee should be terminated -// when the debugger is disconnected. If unspecified, -// the debug adapter is free to do whatever it thinks -// is best. A client can only rely on this attribute -// being properly honored if a debug adapter returns -// true for the 'supportTerminateDebuggee' capability." -// }, -// "restart": { -// "type": "boolean", -// "description": "Indicates whether the debuggee should be restart -// the process." -// } -// } -// }, -// "DisconnectResponse": { -// "allOf": [ { "$ref": "#/definitions/Response" }, { -// "type": "object", -// "description": "Response to 'disconnect' request. This is just an -// acknowledgement, so no body field is required." -// }] -// } -void request_disconnect(DAP &dap, const llvm::json::Object &request) { - llvm::json::Object response; - FillResponse(request, response); - const auto *arguments = request.getObject("arguments"); - - bool defaultTerminateDebuggee = dap.is_attach ? false : true; - bool terminateDebuggee = - GetBoolean(arguments, "terminateDebuggee", defaultTerminateDebuggee); - - lldb::SBError error = dap.Disconnect(terminateDebuggee); - if (error.Fail()) - EmplaceSafeString(response, "error", error.GetCString()); - - dap.SendJSON(llvm::json::Value(std::move(response))); -} - -// "ExceptionInfoRequest": { -// "allOf": [ { "$ref": "#/definitions/Request" }, { -// "type": "object", -// "description": "Retrieves the details of the exception that -// caused this event to be raised. Clients should only call this request if -// the corresponding capability `supportsExceptionInfoRequest` is true.", -// "properties": { -// "command": { -// "type": "string", -// "enum": [ "exceptionInfo" ] -// }, -// "arguments": { -// "$ref": "#/definitions/ExceptionInfoArguments" -// } -// }, -// "required": [ "command", "arguments" ] -// }] -// }, -// "ExceptionInfoArguments": { -// "type": "object", -// "description": "Arguments for `exceptionInfo` request.", -// "properties": { -// "threadId": { -// "type": "integer", -// "description": "Thread for which exception information should be -// retrieved." -// } -// }, -// "required": [ "threadId" ] -// }, -// "ExceptionInfoResponse": { -// "allOf": [ { "$ref": "#/definitions/Response" }, { -// "type": "object", -// "description": "Response to `exceptionInfo` request.", -// "properties": { -// "body": { -// "type": "object", -// "properties": { -// "exceptionId": { -// "type": "string", -// "description": "ID of the exception that was thrown." -// }, -// "description": { -// "type": "string", -// "description": "Descriptive text for the exception." -// }, -// "breakMode": { -// "$ref": "#/definitions/ExceptionBreakMode", -// "description": "Mode that caused the exception notification to -// be raised." -// }, -// "details": { -// "$ref": "#/definitions/ExceptionDetails", -// "description": "Detailed information about the exception." -// } -// }, -// "required": [ "exceptionId", "breakMode" ] -// } -// }, -// "required": [ "body" ] -// }] -// } -// "ExceptionDetails": { -// "type": "object", -// "description": "Detailed information about an exception that has -// occurred.", "properties": { -// "message": { -// "type": "string", -// "description": "Message contained in the exception." -// }, -// "typeName": { -// "type": "string", -// "description": "Short type name of the exception object." -// }, -// "fullTypeName": { -// "type": "string", -// "description": "Fully-qualified type name of the exception object." -// }, -// "evaluateName": { -// "type": "string", -// "description": "An expression that can be evaluated in the current -// scope to obtain the exception object." -// }, -// "stackTrace": { -// "type": "string", -// "description": "Stack trace at the time the exception was thrown." -// }, -// "innerException": { -// "type": "array", -// "items": { -// "$ref": "#/definitions/ExceptionDetails" -// }, -// "description": "Details of the exception contained by this exception, -// if any." -// } -// } -// }, -void request_exceptionInfo(DAP &dap, const llvm::json::Object &request) { - llvm::json::Object response; - FillResponse(request, response); - const auto *arguments = request.getObject("arguments"); - llvm::json::Object body; - lldb::SBThread thread = dap.GetLLDBThread(*arguments); - if (thread.IsValid()) { - auto stopReason = thread.GetStopReason(); - if (stopReason == lldb::eStopReasonSignal) - body.try_emplace("exceptionId", "signal"); - else if (stopReason == lldb::eStopReasonBreakpoint) { - ExceptionBreakpoint *exc_bp = dap.GetExceptionBPFromStopReason(thread); - if (exc_bp) { - EmplaceSafeString(body, "exceptionId", exc_bp->filter); - EmplaceSafeString(body, "description", exc_bp->label); - } else { - body.try_emplace("exceptionId", "exception"); - } - } else { - body.try_emplace("exceptionId", "exception"); - } - if (!ObjectContainsKey(body, "description")) { - char description[1024]; - if (thread.GetStopDescription(description, sizeof(description))) { - EmplaceSafeString(body, "description", std::string(description)); - } - } - body.try_emplace("breakMode", "always"); - auto exception = thread.GetCurrentException(); - if (exception.IsValid()) { - llvm::json::Object details; - lldb::SBStream stream; - if (exception.GetDescription(stream)) { - EmplaceSafeString(details, "message", stream.GetData()); - } - - auto exceptionBacktrace = thread.GetCurrentExceptionBacktrace(); - if (exceptionBacktrace.IsValid()) { - lldb::SBStream stream; - exceptionBacktrace.GetDescription(stream); - for (uint32_t i = 0; i < exceptionBacktrace.GetNumFrames(); i++) { - lldb::SBFrame frame = exceptionBacktrace.GetFrameAtIndex(i); - frame.GetDescription(stream); - } - EmplaceSafeString(details, "stackTrace", stream.GetData()); - } - - body.try_emplace("details", std::move(details)); - } - // auto excInfoCount = thread.GetStopReasonDataCount(); - // for (auto i=0; i<excInfoCount; ++i) { - // uint64_t exc_data = thread.GetStopReasonDataAtIndex(i); - // } - } else { - response["success"] = llvm::json::Value(false); - } - response.try_emplace("body", std::move(body)); - dap.SendJSON(llvm::json::Value(std::move(response))); -} - -// "CompletionsRequest": { -// "allOf": [ { "$ref": "#/definitions/Request" }, { -// "type": "object", -// "description": "Returns a list of possible completions for a given caret -// position and text.\nThe CompletionsRequest may only be called if the -// 'supportsCompletionsRequest' capability exists and is true.", -// "properties": { -// "command": { -// "type": "string", -// "enum": [ "completions" ] -// }, -// "arguments": { -// "$ref": "#/definitions/CompletionsArguments" -// } -// }, -// "required": [ "command", "arguments" ] -// }] -// }, -// "CompletionsArguments": { -// "type": "object", -// "description": "Arguments for 'completions' request.", -// "properties": { -// "frameId": { -// "type": "integer", -// "description": "Returns completions in the scope of this stack frame. -// If not specified, the completions are returned for the global scope." -// }, -// "text": { -// "type": "string", -// "description": "One or more source lines. Typically this is the text a -// user has typed into the debug console before he asked for completion." -// }, -// "column": { -// "type": "integer", -// "description": "The character position for which to determine the -// completion proposals." -// }, -// "line": { -// "type": "integer", -// "description": "An optional line for which to determine the completion -// proposals. If missing the first line of the text is assumed." -// } -// }, -// "required": [ "text", "column" ] -// }, -// "CompletionsResponse": { -// "allOf": [ { "$ref": "#/definitions/Response" }, { -// "type": "object", -// "description": "Response to 'completions' request.", -// "properties": { -// "body": { -// "type": "object", -// "properties": { -// "targets": { -// "type": "array", -// "items": { -// "$ref": "#/definitions/CompletionItem" -// }, -// "description": "The possible completions for ." -// } -// }, -// "required": [ "targets" ] -// } -// }, -// "required": [ "body" ] -// }] -// }, -// "CompletionItem": { -// "type": "object", -// "description": "CompletionItems are the suggestions returned from the -// CompletionsRequest.", "properties": { -// "label": { -// "type": "string", -// "description": "The label of this completion item. By default this is -// also the text that is inserted when selecting this completion." -// }, -// "text": { -// "type": "string", -// "description": "If text is not falsy then it is inserted instead of the -// label." -// }, -// "sortText": { -// "type": "string", -// "description": "A string that should be used when comparing this item -// with other items. When `falsy` the label is used." -// }, -// "type": { -// "$ref": "#/definitions/CompletionItemType", -// "description": "The item's type. Typically the client uses this -// information to render the item in the UI with an icon." -// }, -// "start": { -// "type": "integer", -// "description": "This value determines the location (in the -// CompletionsRequest's 'text' attribute) where the completion text is -// added.\nIf missing the text is added at the location specified by the -// CompletionsRequest's 'column' attribute." -// }, -// "length": { -// "type": "integer", -// "description": "This value determines how many characters are -// overwritten by the completion text.\nIf missing the value 0 is assumed -// which results in the completion text being inserted." -// } -// }, -// "required": [ "label" ] -// }, -// "CompletionItemType": { -// "type": "string", -// "description": "Some predefined types for the CompletionItem. Please note -// that not all clients have specific icons for all of them.", "enum": [ -// "method", "function", "constructor", "field", "variable", "class", -// "interface", "module", "property", "unit", "value", "enum", "keyword", -// "snippet", "text", "color", "file", "reference", "customcolor" ] -// } -void request_completions(DAP &dap, const llvm::json::Object &request) { - llvm::json::Object response; - FillResponse(request, response); - llvm::json::Object body; - const auto *arguments = request.getObject("arguments"); - - // If we have a frame, try to set the context for variable completions. - lldb::SBFrame frame = dap.GetLLDBFrame(*arguments); - if (frame.IsValid()) { - frame.GetThread().GetProcess().SetSelectedThread(frame.GetThread()); - frame.GetThread().SetSelectedFrame(frame.GetFrameID()); - } +} - std::string text = GetString(arguments, "text").str(); - auto original_column = GetSigned(arguments, "column", text.size()); - auto original_line = GetSigned(arguments, "line", 1); - auto offset = original_column - 1; - if (original_line > 1) { - llvm::SmallVector<::llvm::StringRef, 2> lines; - llvm::StringRef(text).split(lines, '\n'); - for (int i = 0; i < original_line - 1; i++) { - offset += lines[i].size(); +lldb::SBValue FindVariable(DAP &dap, uint64_t variablesReference, + llvm::StringRef name) { + lldb::SBValue variable; + if (lldb::SBValueList *top_scope = + GetTopLevelScope(dap, variablesReference)) { + bool is_duplicated_variable_name = name.contains(" @"); + // variablesReference is one of our scopes, not an actual variable it is + // asking for a variable in locals or globals or registers + int64_t end_idx = top_scope->GetSize(); + // Searching backward so that we choose the variable in closest scope + // among variables of the same name. + for (int64_t i = end_idx - 1; i >= 0; --i) { + lldb::SBValue curr_variable = top_scope->GetValueAtIndex(i); + std::string variable_name = CreateUniqueVariableNameForDisplay( + curr_variable, is_duplicated_variable_name); + if (variable_name == name) { + variable = curr_variable; + break; + } } - } - llvm::json::Array targets; - - bool had_escape_prefix = - llvm::StringRef(text).starts_with(dap.command_escape_prefix); - ReplMode completion_mode = dap.DetectReplMode(frame, text, true); - - // Handle the offset change introduced by stripping out the - // `command_escape_prefix`. - if (had_escape_prefix) { - if (offset < static_cast<int64_t>(dap.command_escape_prefix.size())) { - body.try_emplace("targets", std::move(targets)); - response.try_emplace("body", std::move(body)); - dap.SendJSON(llvm::json::Value(std::move(response))); - return; + } else { + // This is not under the globals or locals scope, so there are no duplicated + // names. + + // We have a named item within an actual variable so we need to find it + // withing the container variable by name. + lldb::SBValue container = dap.variables.GetVariable(variablesReference); + variable = container.GetChildMemberWithName(name.data()); + if (!variable.IsValid()) { + if (name.starts_with("[")) { + llvm::StringRef index_str(name.drop_front(1)); + uint64_t index = 0; + if (!index_str.consumeInteger(0, index)) { + if (index_str == "]") + variable = container.GetChildAtIndex(index); + } + } } - offset -= dap.command_escape_prefix.size(); } + return variable; +} - // While the user is typing then we likely have an incomplete input and cannot - // reliably determine the precise intent (command vs variable), try completing - // the text as both a command and variable expression, if applicable. - const std::string expr_prefix = "expression -- "; - std::array<std::tuple<ReplMode, std::string, uint64_t>, 2> exprs = { - {std::make_tuple(ReplMode::Command, text, offset), - std::make_tuple(ReplMode::Variable, expr_prefix + text, - offset + expr_prefix.size())}}; - for (const auto &[mode, line, cursor] : exprs) { - if (completion_mode != ReplMode::Auto && completion_mode != mode) - continue; - - lldb::SBStringList matches; - lldb::SBStringList descriptions; - if (!dap.debugger.GetCommandInterpreter().HandleCompletionWithDescriptions( - line.c_str(), cursor, 0, 100, matches, descriptions)) - continue; +// Both attach and launch take a either a sourcePath or sourceMap +// argument (or neither), from which we need to set the target.source-map. +void SetSourceMapFromArguments(DAP &dap, const llvm::json::Object &arguments) { ---------------- ashgti wrote:
Is this repeated? Or I think maybe this is still here for the existing `request_launch` function that hasn't moved over yet. Should this move to the a RequestHelpers.h like we have for EventHelpers.h? https://github.com/llvm/llvm-project/pull/128262 _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits