[Lldb-commits] [lldb] [lldb-dap] Move requests into their own object/file (PR #128262)
John Harrison via lldb-commits
lldb-commits at lists.llvm.org
Sat Feb 22 21:45:49 PST 2025
================
@@ -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
More information about the lldb-commits
mailing list