aelitashen updated this revision to Diff 286885.
aelitashen added a comment.
@Walter Please help me fix the tests. Looks like in the new added runInTerminal
tests, it cannot hit the breakpoint and capture the local variable.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D84974/new/
https://reviews.llvm.org/D84974
Files:
lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py
lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py
lldb/test/API/tools/lldb-vscode/runInTerminal/Makefile
lldb/test/API/tools/lldb-vscode/runInTerminal/TestVSCode_runInTerminal.py
lldb/test/API/tools/lldb-vscode/runInTerminal/main.c
lldb/tools/lldb-vscode/VSCode.cpp
lldb/tools/lldb-vscode/VSCode.h
lldb/tools/lldb-vscode/lldb-vscode.cpp
Index: lldb/tools/lldb-vscode/lldb-vscode.cpp
===================================================================
--- lldb/tools/lldb-vscode/lldb-vscode.cpp
+++ lldb/tools/lldb-vscode/lldb-vscode.cpp
@@ -343,7 +343,7 @@
char buffer[1024];
size_t count;
while ((count = process.GetSTDOUT(buffer, sizeof(buffer))) > 0)
- g_vsc.SendOutput(OutputType::Stdout, llvm::StringRef(buffer, count));
+ g_vsc.SendOutput(OutputType::Stdout, llvm::StringRef(buffer, count));
while ((count = process.GetSTDERR(buffer, sizeof(buffer))) > 0)
g_vsc.SendOutput(OutputType::Stderr, llvm::StringRef(buffer, count));
}
@@ -448,10 +448,10 @@
if (event_mask & lldb::SBTarget::eBroadcastBitModulesLoaded) {
body.try_emplace("reason", "new");
} else if (event_mask &
- lldb::SBTarget::eBroadcastBitModulesUnloaded) {
+ lldb::SBTarget::eBroadcastBitModulesUnloaded) {
body.try_emplace("reason", "removed");
} else if (event_mask &
- lldb::SBTarget::eBroadcastBitSymbolsLoaded) {
+ lldb::SBTarget::eBroadcastBitSymbolsLoaded) {
body.try_emplace("reason", "changed");
}
body.try_emplace("module", module_value);
@@ -873,7 +873,9 @@
// "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.",
+// "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",
@@ -892,19 +894,23 @@
// "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."
+// "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."
+// "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."
+// "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."
+// "description": "An optional line for which to determine the completion
+// proposals. If missing the first line of the text is assumed."
// }
// },
// "required": [ "text", "column" ]
@@ -933,39 +939,51 @@
// },
// "CompletionItem": {
// "type": "object",
-// "description": "CompletionItems are the suggestions returned from the CompletionsRequest.",
-// "properties": {
+// "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."
+// "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."
+// "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."
+// "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."
+// "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."
+// "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."
+// "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" ]
+// "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(const llvm::json::Object &request) {
llvm::json::Object response;
@@ -992,9 +1010,7 @@
lldb::SBStringList matches;
lldb::SBStringList descriptions;
g_vsc.debugger.GetCommandInterpreter().HandleCompletionWithDescriptions(
- text.c_str(),
- actual_column,
- 0, -1, matches, descriptions);
+ text.c_str(), actual_column, 0, -1, matches, descriptions);
size_t count = std::min((uint32_t)100, matches.GetSize());
targets.reserve(count);
for (size_t i = 0; i < count; i++) {
@@ -1004,8 +1020,8 @@
llvm::json::Object item;
llvm::StringRef match_ref = match;
- for(llvm::StringRef commit_point: {".", "->"}) {
- if (match_ref.contains(commit_point)){
+ for (llvm::StringRef commit_point : {".", "->"}) {
+ if (match_ref.contains(commit_point)) {
match_ref = match_ref.rsplit(commit_point).second;
}
}
@@ -1160,7 +1176,8 @@
} else {
SetValueForKey(value, body, "result");
auto value_typename = value.GetType().GetDisplayTypeName();
- EmplaceSafeString(body, "type", value_typename ? value_typename : NO_TYPENAME);
+ EmplaceSafeString(body, "type",
+ value_typename ? value_typename : NO_TYPENAME);
if (value.MightHaveChildren()) {
auto variablesReference = VARIDX_TO_VARREF(g_vsc.variables.GetSize());
g_vsc.variables.Append(value);
@@ -1229,8 +1246,8 @@
if (module_id == curr_module.GetUUIDString()) {
int num_units = curr_module.GetNumCompileUnits();
for (int j = 0; j < num_units; j++) {
- auto curr_unit = curr_module.GetCompileUnitAtIndex(j);\
- units.emplace_back(CreateCompileUnit(curr_unit));\
+ auto curr_unit = curr_module.GetCompileUnitAtIndex(j);
+ units.emplace_back(CreateCompileUnit(curr_unit));
}
body.try_emplace("compileUnits", std::move(units));
break;
@@ -1357,6 +1374,9 @@
filters.emplace_back(CreateExceptionBreakpointFilter(exc_bp));
}
body.try_emplace("exceptionBreakpointFilters", std::move(filters));
+ // The debug adapter supports launching a debugee in intergrated VSCode
+ // terminal.
+ body.try_emplace("supportsRunInTerminalRequest", true);
// The debug adapter supports stepping back via the stepBack and
// reverseContinue requests.
body.try_emplace("supportsStepBack", false);
@@ -1416,6 +1436,89 @@
g_vsc.SendJSON(llvm::json::Value(std::move(response)));
}
+void request_runInTerminal(const llvm::json::Object &request,
+ llvm::json::Object &launch_response) {
+ // We have already created a target that has a valid "program" path to the
+ // executable. We will attach to the next process whose basename matches that
+ // of the target's executable by waiting for the next process to launch that
+ // matches. This help LLDB ignore any existing processes that are already
+ // running that match this executable name and wait for the next one to
+ // launch.
+ g_vsc.is_attach = true;
+ lldb::SBError error;
+ lldb::SBAttachInfo attach_info;
+ attach_info.SetWaitForLaunch(true, false);
+ // Manually set stopOnEntry as lldb cannot gather this info from the request
+ g_vsc.stop_at_entry = true;
+ // Here we launch a thread to do the attach. We disable async events so
+ // that when we call g_vsc.target.Attach(...) it will not return unless the
+ // attach succeeds. In the event the attach never happens, the user will be
+ // able to hit the square stop button in the debug session and it will kill
+ // lldb-vscode. If this ever changes in the IDE we will need to do more work
+ // to ensure we can nicely timeout from the attach after a set period of time.
+ std::thread attacher([&]() {
+ g_vsc.debugger.SetAsync(false);
+ g_vsc.target.Attach(attach_info, error);
+ g_vsc.debugger.SetAsync(true);
+ });
+
+ // Send reverse request for run in terminal
+ auto arguments = request.getObject("arguments");
+ llvm::json::Object reverseRequest;
+ reverseRequest.try_emplace("type", "request");
+ reverseRequest.try_emplace("command", "runInTerminal");
+ llvm::json::Object runInTerminalArgs;
+ runInTerminalArgs.try_emplace("kind", "integrated");
+ const auto cwd = GetString(arguments, "cwd");
+ if (!cwd.empty())
+ runInTerminalArgs.try_emplace("cwd", cwd);
+
+ std::vector<std::string> commands = GetStrings(arguments, "args");
+ commands.insert(commands.begin(),
+ std::string(GetString(arguments, "program").data()));
+ runInTerminalArgs.try_emplace("args", commands);
+ std::vector<std::string> envVars = GetStrings(arguments, "env");
+ llvm::json::Object environment;
+ for (std::string envVar : envVars) {
+ size_t ind = envVar.find("=");
+ environment.try_emplace(envVar.substr(0, ind), envVar.substr(ind + 1));
+ }
+ runInTerminalArgs.try_emplace("env",
+ llvm::json::Value(std::move(environment)));
+ reverseRequest.try_emplace("arguments",
+ llvm::json::Value(std::move(runInTerminalArgs)));
+ llvm::json::Object reverseResponse;
+ lldb_vscode::VSCode::PacketStatus status =
+ g_vsc.SendReverseRequest(reverseRequest, reverseResponse);
+ if (status == lldb_vscode::VSCode::PacketStatus::Success) {
+ attacher.join();
+ } else {
+ // Reverse Request handshake fails
+ error.SetErrorString("Process cannot be launched by IDE.");
+ }
+
+ if (error.Success()) {
+ // IDE doesn't respond back the pid of the target from the runInTerminal
+ // response, so we have lldb attach by name and wait, so grab the process
+ // ID from the target.
+ auto attached_pid = g_vsc.target.GetProcess().GetProcessID();
+ if (attached_pid == LLDB_INVALID_PROCESS_ID)
+ error.SetErrorString("Failed to attach to a process");
+ else
+ SendProcessEvent(Attach);
+ }
+
+ // Check error again as it might have been modified with a new error above.
+ if (error.Fail()) {
+ launch_response["success"] = llvm::json::Value(false);
+ EmplaceSafeString(launch_response, "message",
+ std::string(error.GetCString()));
+ } else {
+ launch_response["success"] = llvm::json::Value(true);
+ }
+ g_vsc.SendJSON(CreateEventObject("initialized"));
+}
+
// "LaunchRequest": {
// "allOf": [ { "$ref": "#/definitions/Request" }, {
// "type": "object",
@@ -1488,6 +1591,11 @@
return;
}
+ if (GetBoolean(arguments, "runInTerminal", false)) {
+ request_runInTerminal(request, response);
+ g_vsc.SendJSON(llvm::json::Value(std::move(response)));
+ return;
+ }
// Instantiate a launch info instance for the target.
auto launch_info = g_vsc.target.GetLaunchInfo();
@@ -1524,15 +1632,15 @@
// Run any pre run LLDB commands the user specified in the launch.json
g_vsc.RunPreRunCommands();
if (launchCommands.empty()) {
- // Disable async events so the launch will be successful when we return from
- // the launch call and the launch will happen synchronously
+ // Disable async events so the launch will be successful when we return
+ // from the launch call and the launch will happen synchronously
g_vsc.debugger.SetAsync(false);
g_vsc.target.Launch(launch_info, error);
g_vsc.debugger.SetAsync(true);
} else {
g_vsc.RunLLDBCommands("Running launchCommands:", launchCommands);
- // The custom commands might have created a new target so we should use the
- // selected target after these commands are run.
+ // The custom commands might have created a new target so we should use
+ // the selected target after these commands are run.
g_vsc.target = g_vsc.debugger.GetSelectedTarget();
}
@@ -1540,8 +1648,8 @@
response["success"] = llvm::json::Value(false);
EmplaceSafeString(response, "message", std::string(error.GetCString()));
}
- g_vsc.SendJSON(llvm::json::Value(std::move(response)));
+ g_vsc.SendJSON(llvm::json::Value(std::move(response)));
SendProcessEvent(Launch);
g_vsc.SendJSON(llvm::json::Value(CreateEventObject("initialized")));
// Reenable async events and start the event thread to catch async events.
@@ -2080,7 +2188,7 @@
// Disable any function breakpoints that aren't in the request_bps.
// There is no call to remove function breakpoints other than calling this
// function with a smaller or empty "breakpoints" list.
- for (auto &pair: g_vsc.function_breakpoints) {
+ for (auto &pair : g_vsc.function_breakpoints) {
auto request_pos = request_bps.find(pair.first());
if (request_pos == request_bps.end()) {
// This function breakpoint no longer exists delete it from LLDB
@@ -2098,7 +2206,7 @@
}
}
// Remove any breakpoints that are no longer in our list
- for (const auto &name: remove_names)
+ for (const auto &name : remove_names)
g_vsc.function_breakpoints.erase(name);
// Any breakpoints that are left in "request_bps" are breakpoints that
@@ -2814,39 +2922,35 @@
g_vsc.SendJSON(llvm::json::Value(std::move(response)));
}
-const std::map<std::string, RequestCallback> &GetRequestHandlers() {
-#define REQUEST_CALLBACK(name) \
- { #name, request_##name }
- static std::map<std::string, RequestCallback> g_request_handlers = {
- // VSCode Debug Adaptor requests
- REQUEST_CALLBACK(attach),
- REQUEST_CALLBACK(completions),
- REQUEST_CALLBACK(continue),
- REQUEST_CALLBACK(configurationDone),
- REQUEST_CALLBACK(disconnect),
- REQUEST_CALLBACK(evaluate),
- REQUEST_CALLBACK(exceptionInfo),
- REQUEST_CALLBACK(getCompileUnits),
- REQUEST_CALLBACK(initialize),
- REQUEST_CALLBACK(launch),
- REQUEST_CALLBACK(next),
- REQUEST_CALLBACK(pause),
- REQUEST_CALLBACK(scopes),
- REQUEST_CALLBACK(setBreakpoints),
- REQUEST_CALLBACK(setExceptionBreakpoints),
- REQUEST_CALLBACK(setFunctionBreakpoints),
- REQUEST_CALLBACK(setVariable),
- REQUEST_CALLBACK(source),
- REQUEST_CALLBACK(stackTrace),
- REQUEST_CALLBACK(stepIn),
- REQUEST_CALLBACK(stepOut),
- REQUEST_CALLBACK(threads),
- REQUEST_CALLBACK(variables),
- // Testing requests
- REQUEST_CALLBACK(_testGetTargetBreakpoints),
- };
-#undef REQUEST_CALLBACK
- return g_request_handlers;
+const void RegisterRequestCallbacks() {
+ g_vsc.RegisterRequestCallback("attach", request_attach);
+ g_vsc.RegisterRequestCallback("completions", request_completions);
+ g_vsc.RegisterRequestCallback("continue", request_continue);
+ g_vsc.RegisterRequestCallback("configurationDone", request_configurationDone);
+ g_vsc.RegisterRequestCallback("disconnect", request_disconnect);
+ g_vsc.RegisterRequestCallback("evaluate", request_evaluate);
+ g_vsc.RegisterRequestCallback("exceptionInfo", request_exceptionInfo);
+ g_vsc.RegisterRequestCallback("getCompileUnits", request_getCompileUnits);
+ g_vsc.RegisterRequestCallback("initialize", request_initialize);
+ g_vsc.RegisterRequestCallback("launch", request_launch);
+ g_vsc.RegisterRequestCallback("next", request_next);
+ g_vsc.RegisterRequestCallback("pause", request_pause);
+ g_vsc.RegisterRequestCallback("scopes", request_scopes);
+ g_vsc.RegisterRequestCallback("setBreakpoints", request_setBreakpoints);
+ g_vsc.RegisterRequestCallback("setExceptionBreakpoints",
+ request_setExceptionBreakpoints);
+ g_vsc.RegisterRequestCallback("setFunctionBreakpoints",
+ request_setFunctionBreakpoints);
+ g_vsc.RegisterRequestCallback("setVariable", request_setVariable);
+ g_vsc.RegisterRequestCallback("source", request_source);
+ g_vsc.RegisterRequestCallback("stackTrace", request_stackTrace);
+ g_vsc.RegisterRequestCallback("stepIn", request_stepIn);
+ g_vsc.RegisterRequestCallback("stepOut", request_stepOut);
+ g_vsc.RegisterRequestCallback("threads", request_threads);
+ g_vsc.RegisterRequestCallback("variables", request_variables);
+ // Testing requests
+ g_vsc.RegisterRequestCallback("_testGetTargetBreakpoints",
+ request__testGetTargetBreakpoints);
}
} // anonymous namespace
@@ -2874,7 +2978,7 @@
}
int main(int argc, char *argv[]) {
-
+ RegisterRequestCallbacks();
// Initialize LLDB first before we do anything.
lldb::SBDebugger::Initialize();
@@ -2920,48 +3024,17 @@
g_vsc.output.descriptor =
StreamDescriptor::from_file(fileno(stdout), false);
}
- auto request_handlers = GetRequestHandlers();
uint32_t packet_idx = 0;
while (!g_vsc.sent_terminated_event) {
- std::string json = g_vsc.ReadJSON();
- if (json.empty())
+ llvm::json::Object object;
+ lldb_vscode::VSCode::PacketStatus status = g_vsc.GetObject(object);
+ if (status == lldb_vscode::VSCode::PacketStatus::EndOfFile)
break;
+ if (status != lldb_vscode::VSCode::PacketStatus::Success)
+ return 1; // Fatal error
- llvm::StringRef json_sref(json);
- llvm::Expected<llvm::json::Value> json_value = llvm::json::parse(json_sref);
- if (!json_value) {
- auto error = json_value.takeError();
- if (g_vsc.log) {
- std::string error_str;
- llvm::raw_string_ostream strm(error_str);
- strm << error;
- strm.flush();
-
- *g_vsc.log << "error: failed to parse JSON: " << error_str << std::endl
- << json << std::endl;
- }
- return 1;
- }
-
- auto object = json_value->getAsObject();
- if (!object) {
- if (g_vsc.log)
- *g_vsc.log << "error: json packet isn't a object" << std::endl;
+ if (!g_vsc.HandleObject(object))
return 1;
- }
-
- const auto packet_type = GetString(object, "type");
- if (packet_type == "request") {
- const auto command = GetString(object, "command");
- auto handler_pos = request_handlers.find(std::string(command));
- if (handler_pos != request_handlers.end()) {
- handler_pos->second(*object);
- } else {
- if (g_vsc.log)
- *g_vsc.log << "error: unhandled command \"" << command.data() << std::endl;
- return 1;
- }
- }
++packet_idx;
}
Index: lldb/tools/lldb-vscode/VSCode.h
===================================================================
--- lldb/tools/lldb-vscode/VSCode.h
+++ lldb/tools/lldb-vscode/VSCode.h
@@ -19,6 +19,7 @@
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/JSON.h"
#include "llvm/Support/raw_ostream.h"
#include "lldb/API/SBAttachInfo.h"
@@ -61,6 +62,7 @@
typedef llvm::DenseMap<uint32_t, SourceBreakpoint> SourceBreakpointMap;
typedef llvm::StringMap<FunctionBreakpoint> FunctionBreakpointMap;
+typedef void (*RequestCallback)(const llvm::json::Object &command);
enum class OutputType { Console, Stdout, Stderr, Telemetry };
enum VSCodeBroadcasterBits { eBroadcastBitStopEventThread = 1u << 0 };
@@ -91,6 +93,14 @@
bool sent_terminated_event;
bool stop_at_entry;
bool is_attach;
+ uint32_t reverse_request_seq = 0;
+ enum class PacketStatus {
+ Success = 0,
+ EndOfFile,
+ JSONMalformed,
+ JSONNotObject
+ };
+ std::map<std::string, RequestCallback> m_request_handlers;
// Keep track of the last stop thread index IDs as threads won't go away
// unless we send a "thread" event to indicate the thread exited.
llvm::DenseSet<lldb::tid_t> thread_ids;
@@ -146,13 +156,22 @@
///
/// \return
/// An SBTarget object.
- lldb::SBTarget CreateTargetFromArguments(
- const llvm::json::Object &arguments,
- lldb::SBError &error);
+ lldb::SBTarget CreateTargetFromArguments(const llvm::json::Object &arguments,
+ lldb::SBError &error);
/// Set given target object as a current target for lldb-vscode and start
/// listeing for its breakpoint events.
void SetTarget(const lldb::SBTarget target);
+
+ const std::map<std::string, RequestCallback> &GetRequestHandlers();
+
+ PacketStatus GetObject(llvm::json::Object &object);
+ bool HandleObject(const llvm::json::Object &object);
+
+ PacketStatus SendReverseRequest(llvm::json::Object request,
+ llvm::json::Object &response);
+
+ void RegisterRequestCallback(std::string request, RequestCallback callback);
};
extern VSCode g_vsc;
Index: lldb/tools/lldb-vscode/VSCode.cpp
===================================================================
--- lldb/tools/lldb-vscode/VSCode.cpp
+++ lldb/tools/lldb-vscode/VSCode.cpp
@@ -6,9 +6,9 @@
//
//===----------------------------------------------------------------------===//
-#include <stdarg.h>
#include <fstream>
#include <mutex>
+#include <stdarg.h>
#include "LLDBUtils.h"
#include "VSCode.h"
@@ -16,9 +16,9 @@
#if defined(_WIN32)
#define NOMINMAX
-#include <windows.h>
#include <fcntl.h>
#include <io.h>
+#include <windows.h>
#endif
using namespace lldb_vscode;
@@ -41,9 +41,9 @@
stop_at_entry(false), is_attach(false) {
const char *log_file_path = getenv("LLDBVSCODE_LOG");
#if defined(_WIN32)
-// Windows opens stdout and stdin in text mode which converts \n to 13,10
-// while the value is just 10 on Darwin/Linux. Setting the file mode to binary
-// fixes this.
+ // Windows opens stdout and stdin in text mode which converts \n to 13,10
+ // while the value is just 10 on Darwin/Linux. Setting the file mode to binary
+ // fixes this.
int result = _setmode(fileno(stdout), _O_BINARY);
assert(result);
result = _setmode(fileno(stdin), _O_BINARY);
@@ -54,8 +54,7 @@
log.reset(new std::ofstream(log_file_path));
}
-VSCode::~VSCode() {
-}
+VSCode::~VSCode() {}
int64_t VSCode::GetLineForPC(int64_t sourceReference, lldb::addr_t pc) const {
auto pos = source_map.find(sourceReference);
@@ -232,8 +231,8 @@
va_start(args, format);
int actual_length = vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
- SendOutput(o, llvm::StringRef(buffer,
- std::min<int>(actual_length, sizeof(buffer))));
+ SendOutput(
+ o, llvm::StringRef(buffer, std::min<int>(actual_length, sizeof(buffer))));
}
int64_t VSCode::GetNextSourceReference() {
@@ -313,9 +312,9 @@
RunLLDBCommands("Running terminateCommands:", terminate_commands);
}
-lldb::SBTarget VSCode::CreateTargetFromArguments(
- const llvm::json::Object &arguments,
- lldb::SBError &error) {
+lldb::SBTarget
+VSCode::CreateTargetFromArguments(const llvm::json::Object &arguments,
+ lldb::SBError &error) {
// Grab the name of the program we need to debug and create a target using
// the given program as an argument. Executable file can be a source of target
// architecture and platform, if they differ from the host. Setting exe path
@@ -330,18 +329,15 @@
llvm::StringRef platform_name = GetString(arguments, "platformName");
llvm::StringRef program = GetString(arguments, "program");
auto target = this->debugger.CreateTarget(
- program.data(),
- target_triple.data(),
- platform_name.data(),
- true, // Add dependent modules.
- error
- );
+ program.data(), target_triple.data(), platform_name.data(),
+ true, // Add dependent modules.
+ error);
if (error.Fail()) {
// Update message if there was an error.
error.SetErrorStringWithFormat(
- "Could not create a target for a program '%s': %s.",
- program.data(), error.GetCString());
+ "Could not create a target for a program '%s': %s.", program.data(),
+ error.GetCString());
}
return target;
@@ -359,11 +355,83 @@
listener.StartListeningForEvents(this->broadcaster,
eBroadcastBitStopEventThread);
listener.StartListeningForEvents(
- this->target.GetBroadcaster(),
- lldb::SBTarget::eBroadcastBitModulesLoaded |
- lldb::SBTarget::eBroadcastBitModulesUnloaded |
- lldb::SBTarget::eBroadcastBitSymbolsLoaded);
+ this->target.GetBroadcaster(),
+ lldb::SBTarget::eBroadcastBitModulesLoaded |
+ lldb::SBTarget::eBroadcastBitModulesUnloaded |
+ lldb::SBTarget::eBroadcastBitSymbolsLoaded);
+ }
+}
+
+VSCode::PacketStatus VSCode::GetObject(llvm::json::Object &object) {
+ std::string json = ReadJSON();
+ if (json.empty())
+ return PacketStatus::EndOfFile;
+
+ llvm::StringRef json_sref(json);
+ llvm::Expected<llvm::json::Value> json_value = llvm::json::parse(json_sref);
+ if (!json_value) {
+ auto error = json_value.takeError();
+ if (log) {
+ std::string error_str;
+ llvm::raw_string_ostream strm(error_str);
+ strm << error;
+ strm.flush();
+ *log << "error: failed to parse JSON: " << error_str << std::endl
+ << json << std::endl;
+ }
+ return PacketStatus::JSONMalformed;
}
+ object = *json_value->getAsObject();
+ if (!json_value->getAsObject()) {
+ if (log)
+ *log << "error: json packet isn't a object" << std::endl;
+ return PacketStatus::JSONNotObject;
+ }
+ return PacketStatus::Success;
+}
+
+bool VSCode::HandleObject(const llvm::json::Object &object) {
+ const auto packet_type = GetString(object, "type");
+ if (packet_type == "request") {
+ const auto command = GetString(object, "command");
+ auto handler_pos = m_request_handlers.find(std::string(command));
+ if (handler_pos != m_request_handlers.end()) {
+ handler_pos->second(object);
+ return true; // Success
+ } else {
+ if (log)
+ *log << "error: unhandled command \"" << command.data() << std::endl;
+ return false; // Fail
+ }
+ }
+ return false;
+}
+
+VSCode::PacketStatus VSCode::SendReverseRequest(llvm::json::Object request,
+ llvm::json::Object &response) {
+ // Put the right "seq" into the packet here, so we don't have to do it from
+ // where we send the reverse request.
+ request.try_emplace("seq", ++reverse_request_seq);
+ SendJSON(llvm::json::Value(std::move(request)));
+ bool got_response = false;
+ while (!got_response) {
+ PacketStatus status = GetObject(response);
+ const auto packet_type = GetString(response, "type");
+ if (packet_type == "response") {
+ if (status == PacketStatus::Success) {
+ return status;
+ // Not our response, we got another packet
+ HandleObject(response);
+ } else {
+ return status;
+ }
+ }
+ }
+}
+
+void VSCode::RegisterRequestCallback(std::string request,
+ RequestCallback callback) {
+ m_request_handlers[request] = callback;
}
} // namespace lldb_vscode
Index: lldb/test/API/tools/lldb-vscode/runInTerminal/main.c
===================================================================
--- /dev/null
+++ lldb/test/API/tools/lldb-vscode/runInTerminal/main.c
@@ -0,0 +1,10 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+int main() {
+ for (int counter = 1; ; counter++) {
+ sleep(1); // breakpoint
+ }
+ return 0;
+}
Index: lldb/test/API/tools/lldb-vscode/runInTerminal/TestVSCode_runInTerminal.py
===================================================================
--- /dev/null
+++ lldb/test/API/tools/lldb-vscode/runInTerminal/TestVSCode_runInTerminal.py
@@ -0,0 +1,35 @@
+"""
+Test lldb-vscode setBreakpoints request
+"""
+
+
+import unittest2
+import vscode
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+import lldbvscode_testcase
+import time
+import os
+
+
+class TestVSCode_launch(lldbvscode_testcase.VSCodeTestCaseBase):
+
+ mydir = TestBase.compute_mydir(__file__)
+
+ @skipIfWindows
+ @skipIfRemote
+ def test_runInTerminal(self):
+ '''
+ Tests the default launch of a simple program that stops at the
+ entry point instead of continuing.
+ '''
+ program = self.getBuildArtifact("a.out")
+ source = 'main.c'
+ self.build_and_launch(program, stopOnEntry=True, runInTerminal=True)
+ breakpoint_line = line_number(source, '// breakpoint')
+ breakpoint_ids = self.set_source_breakpoints(source, [breakpoint_line])
+ # self.continue_to_next_stop()
+ self.continue_to_breakpoints(breakpoint_ids)
+ counter = int(self.vscode.get_local_variable_value('counter'))
+ self.assertTrue(counter > 0)
Index: lldb/test/API/tools/lldb-vscode/runInTerminal/Makefile
===================================================================
--- /dev/null
+++ lldb/test/API/tools/lldb-vscode/runInTerminal/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
Index: lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py
===================================================================
--- lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py
+++ lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py
@@ -249,6 +249,7 @@
json_str = json.dumps(command_dict, separators=(',', ':'))
if self.trace_file:
self.trace_file.write('to adaptor:\n%s\n' % (json_str))
+ print("SEND SOMETHING!", json_str, command_dict)
length = len(json_str)
if length > 0:
# Send the encoded JSON packet and flush the 'send' file
@@ -299,13 +300,29 @@
events list in this object'''
self.send_packet(command)
done = False
+ print("COMMAND!!")
+ print(command)
+ # if 'runInTerminal' in command['arguments'].keys() and command['arguments']['runInTerminal']:
+ # reverse_request = self.recv_packet(filter_type='request')
+ # print(reverse_request)
+ # if reverse_request is None:
+ # desc = 'No reverse request for runInTerminal'
+ # raise ValueError(desc)
while not done:
- response = self.recv_packet(filter_type='response')
- if response is None:
+ response_or_request = self.recv_packet(filter_type=['response', 'request'])
+ print("RESPONSE!!!")
+ print(response_or_request)
+ if response_or_request is None:
desc = 'no response for "%s"' % (command['command'])
raise ValueError(desc)
- self.validate_response(command, response)
- return response
+ if response_or_request['type'] == 'response':
+ self.validate_response(command, response_or_request)
+ return response_or_request
+ else:
+ if response_or_request['command'] == 'runInTerminal':
+ time.sleep(10)
+ subprocess.Popen(response_or_request['arguments']['args'])
+ self.send_packet({"type":"response","seq":-1,"request_seq":1,"success":True,"command":"runInTerminal","body":{}}, False)
return None
def wait_for_event(self, filter=None, timeout=None):
@@ -599,7 +616,7 @@
trace=False, initCommands=None, preRunCommands=None,
stopCommands=None, exitCommands=None,
terminateCommands=None ,sourcePath=None,
- debuggerRoot=None, launchCommands=None, sourceMap=None):
+ debuggerRoot=None, runInTerminal=False, launchCommands=None, sourceMap=None):
args_dict = {
'program': program
}
@@ -611,6 +628,8 @@
args_dict['env'] = env
if stopOnEntry:
args_dict['stopOnEntry'] = stopOnEntry
+ if runInTerminal:
+ args_dict['runInTerminal'] = runInTerminal
if disableASLR:
args_dict['disableASLR'] = disableASLR
if disableSTDIO:
Index: lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py
===================================================================
--- lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py
+++ lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py
@@ -281,8 +281,8 @@
disableSTDIO=False, shellExpandArguments=False,
trace=False, initCommands=None, preRunCommands=None,
stopCommands=None, exitCommands=None, terminateCommands=None,
- sourcePath=None, debuggerRoot=None, launchCommands=None,
- sourceMap=None, disconnectAutomatically=True):
+ sourcePath=None, debuggerRoot=None, runInTerminal=False,
+ launchCommands=None, sourceMap=None, disconnectAutomatically=True):
'''Sending launch request to vscode
'''
@@ -315,6 +315,7 @@
terminateCommands=terminateCommands,
sourcePath=sourcePath,
debuggerRoot=debuggerRoot,
+ runInTerminal=runInTerminal,
launchCommands=launchCommands,
sourceMap=sourceMap)
if not (response and response['success']):
@@ -327,7 +328,7 @@
trace=False, initCommands=None, preRunCommands=None,
stopCommands=None, exitCommands=None,
terminateCommands=None, sourcePath=None,
- debuggerRoot=None):
+ debuggerRoot=None, runInTerminal=False):
'''Build the default Makefile target, create the VSCode debug adaptor,
and launch the process.
'''
@@ -337,4 +338,4 @@
self.launch(program, args, cwd, env, stopOnEntry, disableASLR,
disableSTDIO, shellExpandArguments, trace,
initCommands, preRunCommands, stopCommands, exitCommands,
- terminateCommands, sourcePath, debuggerRoot)
+ terminateCommands, sourcePath, debuggerRoot, runInTerminal)
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits