ashgti updated this revision to Diff 545278.
ashgti added a comment.

Adjusting the unit test to only check 2 frames instead of 3 and adjusted the 
assertions.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D156493/new/

https://reviews.llvm.org/D156493

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/coreFile/TestVSCode_coreFile.py
  lldb/test/API/tools/lldb-vscode/disassemble/Makefile
  lldb/test/API/tools/lldb-vscode/disassemble/TestVSCode_disassemble.py
  lldb/test/API/tools/lldb-vscode/disassemble/main.c
  lldb/tools/lldb-vscode/JSONUtils.cpp
  lldb/tools/lldb-vscode/JSONUtils.h
  lldb/tools/lldb-vscode/SourceReference.h
  lldb/tools/lldb-vscode/VSCode.cpp
  lldb/tools/lldb-vscode/VSCode.h
  lldb/tools/lldb-vscode/VSCodeForward.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
@@ -50,6 +50,7 @@
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/ScopeExit.h"
+#include "llvm/ADT/StringExtras.h"
 #include "llvm/Option/Arg.h"
 #include "llvm/Option/ArgList.h"
 #include "llvm/Option/Option.h"
@@ -1563,6 +1564,8 @@
   body.try_emplace("supportsStepInTargetsRequest", false);
   // The debug adapter supports the completions request.
   body.try_emplace("supportsCompletionsRequest", true);
+  // The debug adapter supports the disassembly request.
+  body.try_emplace("supportsDisassembleRequest", true);
 
   llvm::json::Array completion_characters;
   completion_characters.emplace_back(".");
@@ -2588,18 +2591,7 @@
 void request_source(const llvm::json::Object &request) {
   llvm::json::Object response;
   FillResponse(request, response);
-  llvm::json::Object body;
-
-  auto arguments = request.getObject("arguments");
-  auto source = arguments->getObject("source");
-  auto sourceReference = GetSigned(source, "sourceReference", -1);
-  auto pos = g_vsc.source_map.find((lldb::addr_t)sourceReference);
-  if (pos != g_vsc.source_map.end()) {
-    EmplaceSafeString(body, "content", pos->second.content);
-  } else {
-    response["success"] = llvm::json::Value(false);
-  }
-  EmplaceSafeString(body, "mimeType", "text/x-lldb.disassembly");
+  llvm::json::Object body{{"content", ""}};
   response.try_emplace("body", std::move(body));
   g_vsc.SendJSON(llvm::json::Value(std::move(response)));
 }
@@ -3290,6 +3282,211 @@
   g_vsc.SendJSON(llvm::json::Value(std::move(response)));
 }
 
+// "DisassembleRequest": {
+//   "allOf": [ { "$ref": "#/definitions/Request" }, {
+//     "type": "object",
+//     "description": "Disassembles code stored at the provided
+//     location.\nClients should only call this request if the corresponding
+//     capability `supportsDisassembleRequest` is true.", "properties": {
+//       "command": {
+//         "type": "string",
+//         "enum": [ "disassemble" ]
+//       },
+//       "arguments": {
+//         "$ref": "#/definitions/DisassembleArguments"
+//       }
+//     },
+//     "required": [ "command", "arguments" ]
+//   }]
+// },
+// "DisassembleArguments": {
+//   "type": "object",
+//   "description": "Arguments for `disassemble` request.",
+//   "properties": {
+//     "memoryReference": {
+//       "type": "string",
+//       "description": "Memory reference to the base location containing the
+//       instructions to disassemble."
+//     },
+//     "offset": {
+//       "type": "integer",
+//       "description": "Offset (in bytes) to be applied to the reference
+//       location before disassembling. Can be negative."
+//     },
+//     "instructionOffset": {
+//       "type": "integer",
+//       "description": "Offset (in instructions) to be applied after the byte
+//       offset (if any) before disassembling. Can be negative."
+//     },
+//     "instructionCount": {
+//       "type": "integer",
+//       "description": "Number of instructions to disassemble starting at the
+//       specified location and offset.\nAn adapter must return exactly this
+//       number of instructions - any unavailable instructions should be
+//       replaced with an implementation-defined 'invalid instruction' value."
+//     },
+//     "resolveSymbols": {
+//       "type": "boolean",
+//       "description": "If true, the adapter should attempt to resolve memory
+//       addresses and other values to symbolic names."
+//     }
+//   },
+//   "required": [ "memoryReference", "instructionCount" ]
+// },
+// "DisassembleResponse": {
+//   "allOf": [ { "$ref": "#/definitions/Response" }, {
+//     "type": "object",
+//     "description": "Response to `disassemble` request.",
+//     "properties": {
+//       "body": {
+//         "type": "object",
+//         "properties": {
+//           "instructions": {
+//             "type": "array",
+//             "items": {
+//               "$ref": "#/definitions/DisassembledInstruction"
+//             },
+//             "description": "The list of disassembled instructions."
+//           }
+//         },
+//         "required": [ "instructions" ]
+//       }
+//     }
+//   }]
+// }
+void request_disassemble(const llvm::json::Object &request) {
+  llvm::json::Object response;
+  FillResponse(request, response);
+  auto arguments = request.getObject("arguments");
+
+  auto memoryReference = GetString(arguments, "memoryReference");
+  lldb::addr_t addr_ptr;
+  if (memoryReference.consumeInteger(0, addr_ptr)) {
+    response["success"] = false;
+    response["message"] =
+        "Malformed memory reference: " + memoryReference.str();
+    g_vsc.SendJSON(llvm::json::Value(std::move(response)));
+    return;
+  }
+
+  addr_ptr += GetSigned(arguments, "instructionOffset", 0);
+  lldb::SBAddress addr(addr_ptr, g_vsc.target);
+  if (!addr.IsValid()) {
+    response["success"] = false;
+    response["message"] = "Memory reference not found in the current binary.";
+    g_vsc.SendJSON(llvm::json::Value(std::move(response)));
+    return;
+  }
+
+  const auto inst_count = GetUnsigned(arguments, "instructionCount", 0);
+  lldb::SBInstructionList insts =
+      g_vsc.target.ReadInstructions(addr, inst_count);
+
+  if (!insts.IsValid()) {
+    response["success"] = false;
+    response["message"] = "Failed to find instructions for memory address.";
+    g_vsc.SendJSON(llvm::json::Value(std::move(response)));
+    return;
+  }
+
+  const bool resolveSymbols = GetBoolean(arguments, "resolveSymbols", false);
+  llvm::json::Array instructions;
+  const auto num_insts = insts.GetSize();
+  for (size_t i = 0; i < num_insts; ++i) {
+    lldb::SBInstruction inst = insts.GetInstructionAtIndex(i);
+    auto addr = inst.GetAddress();
+    const auto inst_addr = addr.GetLoadAddress(g_vsc.target);
+    const char *m = inst.GetMnemonic(g_vsc.target);
+    const char *o = inst.GetOperands(g_vsc.target);
+    const char *c = inst.GetComment(g_vsc.target);
+    auto d = inst.GetData(g_vsc.target);
+
+    std::string bytes;
+    llvm::raw_string_ostream sb(bytes);
+    for (unsigned i = 0; i < inst.GetByteSize(); i++) {
+      lldb::SBError error;
+      uint8_t b = d.GetUnsignedInt8(error, i);
+      if (error.Success()) {
+        sb << llvm::format("%2.2x ", b);
+      }
+    }
+    sb.flush();
+
+    llvm::json::Object disassembled_inst{
+        {"address", "0x" + llvm::utohexstr(inst_addr)},
+        {"instructionBytes",
+         bytes.size() > 0 ? bytes.substr(0, bytes.size() - 1) : ""},
+    };
+
+    std::string instruction;
+    llvm::raw_string_ostream si(instruction);
+
+    lldb::SBSymbol symbol = addr.GetSymbol();
+    // Only add the symbol on the first line of the function.
+    if (symbol.IsValid() && symbol.GetStartAddress() == addr) {
+      // If we have a valid symbol, append it as a label prefix for the first
+      // instruction. This is so you can see the start of a function/callsite
+      // in the assembly, at the moment VS Code (1.80) does not visualize the
+      // symbol associated with the assembly instruction.
+      si << (symbol.GetMangledName() != nullptr ? symbol.GetMangledName()
+                                                : symbol.GetName())
+         << ": ";
+
+      if (resolveSymbols) {
+        disassembled_inst.try_emplace("symbol", symbol.GetDisplayName());
+      }
+    }
+
+    si << llvm::formatv("{0,7} {1,12}", m, o);
+    if (c && c[0]) {
+      si << " ; " << c;
+    }
+    si.flush();
+
+    disassembled_inst.try_emplace("instruction", instruction);
+
+    auto line_entry = addr.GetLineEntry();
+    // If the line number is 0 then the entry represents a compiler generated
+    // location.
+    if (line_entry.GetStartAddress() == addr && line_entry.IsValid() &&
+        line_entry.GetFileSpec().IsValid() && line_entry.GetLine() != 0) {
+      auto source = CreateSource(line_entry);
+      disassembled_inst.try_emplace("location", source);
+
+      const auto line = line_entry.GetLine();
+      if (line && line != LLDB_INVALID_LINE_NUMBER) {
+        disassembled_inst.try_emplace("line", line);
+      }
+      const auto column = line_entry.GetColumn();
+      if (column && column != LLDB_INVALID_COLUMN_NUMBER) {
+        disassembled_inst.try_emplace("column", column);
+      }
+
+      auto end_line_entry = line_entry.GetEndAddress().GetLineEntry();
+      if (end_line_entry.IsValid() &&
+          end_line_entry.GetFileSpec() == line_entry.GetFileSpec()) {
+        const auto end_line = end_line_entry.GetLine();
+        if (end_line && end_line != LLDB_INVALID_LINE_NUMBER &&
+            end_line != line) {
+          disassembled_inst.try_emplace("endLine", end_line);
+
+          const auto end_column = end_line_entry.GetColumn();
+          if (end_column && end_column != LLDB_INVALID_COLUMN_NUMBER &&
+              end_column != column) {
+            disassembled_inst.try_emplace("endColumn", end_column - 1);
+          }
+        }
+      }
+    }
+
+    instructions.emplace_back(std::move(disassembled_inst));
+  }
+
+  llvm::json::Object body;
+  body.try_emplace("instructions", std::move(instructions));
+  response.try_emplace("body", std::move(body));
+  g_vsc.SendJSON(llvm::json::Value(std::move(response)));
+}
 // A request used in testing to get the details on all breakpoints that are
 // currently set in the target. This helps us to test "setBreakpoints" and
 // "setFunctionBreakpoints" requests to verify we have the correct set of
@@ -3334,6 +3531,7 @@
   g_vsc.RegisterRequestCallback("stepOut", request_stepOut);
   g_vsc.RegisterRequestCallback("threads", request_threads);
   g_vsc.RegisterRequestCallback("variables", request_variables);
+  g_vsc.RegisterRequestCallback("disassemble", request_disassemble);
   // Custom requests
   g_vsc.RegisterRequestCallback("compileUnits", request_compileUnits);
   g_vsc.RegisterRequestCallback("modules", request_modules);
Index: lldb/tools/lldb-vscode/VSCodeForward.h
===================================================================
--- lldb/tools/lldb-vscode/VSCodeForward.h
+++ lldb/tools/lldb-vscode/VSCodeForward.h
@@ -14,7 +14,6 @@
 struct ExceptionBreakpoint;
 struct FunctionBreakpoint;
 struct SourceBreakpoint;
-struct SourceReference;
 } // namespace lldb_vscode
 
 namespace lldb {
Index: lldb/tools/lldb-vscode/VSCode.h
===================================================================
--- lldb/tools/lldb-vscode/VSCode.h
+++ lldb/tools/lldb-vscode/VSCode.h
@@ -55,7 +55,6 @@
 #include "ProgressEvent.h"
 #include "RunInTerminal.h"
 #include "SourceBreakpoint.h"
-#include "SourceReference.h"
 
 #define VARREF_LOCALS (int64_t)1
 #define VARREF_GLOBALS (int64_t)2
@@ -153,8 +152,6 @@
   std::thread event_thread;
   std::thread progress_event_thread;
   std::unique_ptr<std::ofstream> log;
-  llvm::DenseMap<lldb::addr_t, int64_t> addr_to_source_ref;
-  llvm::DenseMap<int64_t, SourceReference> source_map;
   llvm::StringMap<SourceBreakpointMap> source_breakpoints;
   FunctionBreakpointMap function_breakpoints;
   std::vector<ExceptionBreakpoint> exception_breakpoints;
@@ -194,7 +191,6 @@
   ~VSCode();
   VSCode(const VSCode &rhs) = delete;
   void operator=(const VSCode &rhs) = delete;
-  int64_t GetLineForPC(int64_t sourceReference, lldb::addr_t pc) const;
   ExceptionBreakpoint *GetExceptionBreakpoint(const std::string &filter);
   ExceptionBreakpoint *GetExceptionBreakpoint(const lldb::break_id_t bp_id);
 
Index: lldb/tools/lldb-vscode/VSCode.cpp
===================================================================
--- lldb/tools/lldb-vscode/VSCode.cpp
+++ lldb/tools/lldb-vscode/VSCode.cpp
@@ -64,13 +64,6 @@
 
 VSCode::~VSCode() = default;
 
-int64_t VSCode::GetLineForPC(int64_t sourceReference, lldb::addr_t pc) const {
-  auto pos = source_map.find(sourceReference);
-  if (pos != source_map.end())
-    return pos->second.GetLineForPC(pc);
-  return 0;
-}
-
 ExceptionBreakpoint *VSCode::GetExceptionBreakpoint(const std::string &filter) {
   for (auto &bp : exception_breakpoints) {
     if (bp.filter == filter)
@@ -341,11 +334,6 @@
       o, llvm::StringRef(buffer, std::min<int>(actual_length, sizeof(buffer))));
 }
 
-int64_t VSCode::GetNextSourceReference() {
-  static int64_t ref = 0;
-  return ++ref;
-}
-
 ExceptionBreakpoint *
 VSCode::GetExceptionBPFromStopReason(lldb::SBThread &thread) {
   const auto num = thread.GetStopReasonDataCount();
Index: lldb/tools/lldb-vscode/SourceReference.h
===================================================================
--- lldb/tools/lldb-vscode/SourceReference.h
+++ /dev/null
@@ -1,32 +0,0 @@
-//===-- SourceReference.h ---------------------------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLDB_TOOLS_LLDB_VSCODE_SOURCEREFERENCE_H
-#define LLDB_TOOLS_LLDB_VSCODE_SOURCEREFERENCE_H
-
-#include "lldb/lldb-types.h"
-#include "llvm/ADT/DenseMap.h"
-#include <string>
-
-namespace lldb_vscode {
-
-struct SourceReference {
-  std::string content;
-  llvm::DenseMap<lldb::addr_t, int64_t> addr_to_line;
-
-  int64_t GetLineForPC(lldb::addr_t pc) const {
-    auto addr_line = addr_to_line.find(pc);
-    if (addr_line != addr_to_line.end())
-      return addr_line->second;
-    return 0;
-  }
-};
-
-} // namespace lldb_vscode
-
-#endif
Index: lldb/tools/lldb-vscode/JSONUtils.h
===================================================================
--- lldb/tools/lldb-vscode/JSONUtils.h
+++ lldb/tools/lldb-vscode/JSONUtils.h
@@ -324,31 +324,6 @@
 ///     definition outlined by Microsoft.
 llvm::json::Value CreateSource(llvm::StringRef source_path);
 
-/// Create a "Source" object for a given frame.
-///
-/// When there is no source file information for a stack frame, we will
-/// create disassembly for a function and store a permanent
-/// "sourceReference" that contains the textual disassembly for a
-/// function along with address to line information. The "Source" object
-/// that is created will contain a "sourceReference" that the VSCode
-/// protocol can later fetch as text in order to display disassembly.
-/// The PC will be extracted from the frame and the disassembly line
-/// within the source referred to by "sourceReference" will be filled
-/// in.
-///
-/// \param[in] frame
-///     The LLDB stack frame to use when populating out the "Source"
-///     object.
-///
-/// \param[out] disasm_line
-///     The line within the "sourceReference" file that the PC from
-///     \a frame matches.
-///
-/// \return
-///     A "Source" JSON object with that follows the formal JSON
-///     definition outlined by Microsoft.
-llvm::json::Value CreateSource(lldb::SBFrame &frame, int64_t &disasm_line);
-
 /// Create a "StackFrame" object for a LLDB frame object.
 ///
 /// This function will fill in the following keys in the returned
Index: lldb/tools/lldb-vscode/JSONUtils.cpp
===================================================================
--- lldb/tools/lldb-vscode/JSONUtils.cpp
+++ lldb/tools/lldb-vscode/JSONUtils.cpp
@@ -342,6 +342,9 @@
     object.try_emplace("source", CreateSource(*request_path));
 
   if (bp_addr.IsValid()) {
+    std::string formatted_addr =
+        "0x" + llvm::utohexstr(bp_addr.GetLoadAddress(g_vsc.target));
+    object.try_emplace("instructionReference", formatted_addr);
     auto line_entry = bp_addr.GetLineEntry();
     const auto line = line_entry.GetLine();
     if (line != UINT32_MAX)
@@ -600,8 +603,8 @@
     if (name)
       EmplaceSafeString(object, "name", name);
     char path[PATH_MAX] = "";
-    file.GetPath(path, sizeof(path));
-    if (path[0]) {
+    if (file.GetPath(path, sizeof(path)) &&
+        lldb::SBFileSpec::ResolvePath(path, path, PATH_MAX)) {
       EmplaceSafeString(object, "path", std::string(path));
     }
   }
@@ -616,97 +619,14 @@
   return llvm::json::Value(std::move(source));
 }
 
-llvm::json::Value CreateSource(lldb::SBFrame &frame, int64_t &disasm_line) {
-  disasm_line = 0;
+std::optional<llvm::json::Value> CreateSource(lldb::SBFrame &frame) {
   auto line_entry = frame.GetLineEntry();
   // A line entry of 0 indicates the line is compiler generated i.e. no source
-  // file so don't return early with the line entry.
+  // file is associated with the frame.
   if (line_entry.GetFileSpec().IsValid() && line_entry.GetLine() != 0)
     return CreateSource(line_entry);
 
-  llvm::json::Object object;
-  const auto pc = frame.GetPC();
-
-  lldb::SBInstructionList insts;
-  lldb::SBFunction function = frame.GetFunction();
-  lldb::addr_t low_pc = LLDB_INVALID_ADDRESS;
-  if (function.IsValid()) {
-    low_pc = function.GetStartAddress().GetLoadAddress(g_vsc.target);
-    auto addr_srcref = g_vsc.addr_to_source_ref.find(low_pc);
-    if (addr_srcref != g_vsc.addr_to_source_ref.end()) {
-      // We have this disassembly cached already, return the existing
-      // sourceReference
-      object.try_emplace("sourceReference", addr_srcref->second);
-      disasm_line = g_vsc.GetLineForPC(addr_srcref->second, pc);
-    } else {
-      insts = function.GetInstructions(g_vsc.target);
-    }
-  } else {
-    lldb::SBSymbol symbol = frame.GetSymbol();
-    if (symbol.IsValid()) {
-      low_pc = symbol.GetStartAddress().GetLoadAddress(g_vsc.target);
-      auto addr_srcref = g_vsc.addr_to_source_ref.find(low_pc);
-      if (addr_srcref != g_vsc.addr_to_source_ref.end()) {
-        // We have this disassembly cached already, return the existing
-        // sourceReference
-        object.try_emplace("sourceReference", addr_srcref->second);
-        disasm_line = g_vsc.GetLineForPC(addr_srcref->second, pc);
-      } else {
-        insts = symbol.GetInstructions(g_vsc.target);
-      }
-    }
-  }
-  const auto num_insts = insts.GetSize();
-  if (low_pc != LLDB_INVALID_ADDRESS && num_insts > 0) {
-    if (line_entry.GetLine() == 0) {
-      EmplaceSafeString(object, "name", "<compiler-generated>");
-    } else {
-      EmplaceSafeString(object, "name", frame.GetDisplayFunctionName());
-    }
-    SourceReference source;
-    llvm::raw_string_ostream src_strm(source.content);
-    std::string line;
-    for (size_t i = 0; i < num_insts; ++i) {
-      lldb::SBInstruction inst = insts.GetInstructionAtIndex(i);
-      const auto inst_addr = inst.GetAddress().GetLoadAddress(g_vsc.target);
-      const char *m = inst.GetMnemonic(g_vsc.target);
-      const char *o = inst.GetOperands(g_vsc.target);
-      const char *c = inst.GetComment(g_vsc.target);
-      if (pc == inst_addr)
-        disasm_line = i + 1;
-      const auto inst_offset = inst_addr - low_pc;
-      int spaces = 0;
-      if (inst_offset < 10)
-        spaces = 3;
-      else if (inst_offset < 100)
-        spaces = 2;
-      else if (inst_offset < 1000)
-        spaces = 1;
-      line.clear();
-      llvm::raw_string_ostream line_strm(line);
-      line_strm << llvm::formatv("{0:X+}: <{1}> {2} {3,12} {4}", inst_addr,
-                                 inst_offset, llvm::fmt_repeat(' ', spaces), m,
-                                 o);
-
-      // If there is a comment append it starting at column 60 or after one
-      // space past the last char
-      const uint32_t comment_row = std::max(line_strm.str().size(), (size_t)60);
-      if (c && c[0]) {
-        if (line.size() < comment_row)
-          line_strm.indent(comment_row - line_strm.str().size());
-        line_strm << " # " << c;
-      }
-      src_strm << line_strm.str() << "\n";
-      source.addr_to_line[inst_addr] = i + 1;
-    }
-    // Flush the source stream
-    src_strm.str();
-    auto sourceReference = VSCode::GetNextSourceReference();
-    g_vsc.source_map[sourceReference] = std::move(source);
-    g_vsc.addr_to_source_ref[low_pc] = sourceReference;
-    object.try_emplace("sourceReference", sourceReference);
-  }
-  return llvm::json::Value(std::move(object));
+  return {};
 }
 
 // "StackFrame": {
@@ -748,6 +668,12 @@
 //       "description": "An optional end column of the range covered by the
 //                       stack frame."
 //     },
+//     "instructionPointerReference": {
+// 	     "type": "string",
+// 	     "description": "A memory reference for the current instruction
+// pointer
+//                       in this frame."
+//     },
 //     "moduleId": {
 //       "type": ["integer", "string"],
 //       "description": "The module associated with this frame, if any."
@@ -770,30 +696,37 @@
   int64_t frame_id = MakeVSCodeFrameID(frame);
   object.try_emplace("id", frame_id);
 
-  std::string frame_name;
-  const char *func_name = frame.GetFunctionName();
-  if (func_name)
-    frame_name = func_name;
-  else
+  std::string frame_name = frame.GetDisplayFunctionName();
+  if (frame_name.empty())
     frame_name = "<unknown>";
   bool is_optimized = frame.GetFunction().GetIsOptimized();
   if (is_optimized)
     frame_name += " [opt]";
   EmplaceSafeString(object, "name", frame_name);
 
-  int64_t disasm_line = 0;
-  object.try_emplace("source", CreateSource(frame, disasm_line));
+  auto source = CreateSource(frame);
 
-  auto line_entry = frame.GetLineEntry();
-  if (disasm_line > 0) {
-    object.try_emplace("line", disasm_line);
-  } else {
+  if (source) {
+    object.try_emplace("source", *source);
+    auto line_entry = frame.GetLineEntry();
     auto line = line_entry.GetLine();
-    if (line == UINT32_MAX)
-      line = 0;
-    object.try_emplace("line", line);
+    if (line && line != LLDB_INVALID_LINE_NUMBER)
+      object.try_emplace("line", line);
+    auto column = line_entry.GetColumn();
+    if (column && column != LLDB_INVALID_COLUMN_NUMBER)
+      object.try_emplace("column", column);
+  } else {
+    object.try_emplace("line", 0);
+    object.try_emplace("column", 0);
+    object.try_emplace("presentationHint", "subtle");
+  }
+
+  const auto pc = frame.GetPC();
+  if (pc != LLDB_INVALID_ADDRESS) {
+    std::string formatted_addr = "0x" + llvm::utohexstr(pc);
+    object.try_emplace("instructionPointerReference", formatted_addr);
   }
-  object.try_emplace("column", line_entry.GetColumn());
+
   return llvm::json::Value(std::move(object));
 }
 
Index: lldb/test/API/tools/lldb-vscode/disassemble/main.c
===================================================================
--- /dev/null
+++ lldb/test/API/tools/lldb-vscode/disassemble/main.c
@@ -0,0 +1,30 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+ 
+int compare_ints(const void* a, const void* b)
+{
+    int arg1 = *(const int*)a;
+    int arg2 = *(const int*)b; 
+    
+    // breakpoint 1
+ 
+    if (arg1 < arg2) return -1;
+    if (arg1 > arg2) return 1;
+    return 0;
+}
+ 
+int main(void)
+{
+    int ints[] = { -2, 99, 0, -743, 2, INT_MIN, 4 };
+    int size = sizeof ints / sizeof *ints;
+ 
+    qsort(ints, size, sizeof(int), compare_ints);
+ 
+    for (int i = 0; i < size; i++) {
+        printf("%d ", ints[i]);
+    }
+ 
+    printf("\n");
+    return 0;
+}
\ No newline at end of file
Index: lldb/test/API/tools/lldb-vscode/disassemble/TestVSCode_disassemble.py
===================================================================
--- /dev/null
+++ lldb/test/API/tools/lldb-vscode/disassemble/TestVSCode_disassemble.py
@@ -0,0 +1,41 @@
+"""
+Test lldb-vscode disassemble request
+"""
+
+
+import vscode
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+import lldbvscode_testcase
+import os
+
+
+class TestVSCode_disassemble(lldbvscode_testcase.VSCodeTestCaseBase):
+    @skipIfWindows
+    @skipIfRemote
+    def test_disassemble(self):
+        """
+        Tests the 'disassemble' request.
+        """
+        program = self.getBuildArtifact("a.out")
+        self.build_and_launch(program)
+        source = "main.c"
+        self.source_path = os.path.join(os.getcwd(), source)
+        self.set_source_breakpoints(
+            source,
+            [
+                line_number(source, "// breakpoint 1"),
+            ],
+        )
+        self.continue_to_next_stop()
+
+        pc_assembly = self.disassemble(frameIndex=0)
+        self.assertTrue("location" in pc_assembly, "Source location missing.")
+        self.assertTrue("instruction" in pc_assembly, "Assembly instruction missing.")
+        
+        # The calling frame (qsort) is coming from a system library, as a result
+        # we should not have a source location.
+        qsort_assembly = self.disassemble(frameIndex=1)
+        self.assertFalse("location" in qsort_assembly, "Source location not expected.")
+        self.assertTrue("instruction" in pc_assembly, "Assembly instruction missing.")
Index: lldb/test/API/tools/lldb-vscode/disassemble/Makefile
===================================================================
--- /dev/null
+++ lldb/test/API/tools/lldb-vscode/disassemble/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
Index: lldb/test/API/tools/lldb-vscode/coreFile/TestVSCode_coreFile.py
===================================================================
--- lldb/test/API/tools/lldb-vscode/coreFile/TestVSCode_coreFile.py
+++ lldb/test/API/tools/lldb-vscode/coreFile/TestVSCode_coreFile.py
@@ -25,25 +25,25 @@
 
         expected_frames = [
             {
-                "column": 0,
                 "id": 524288,
                 "line": 4,
                 "name": "bar",
                 "source": {"name": "main.c", "path": "/home/labath/test/main.c"},
+                "instructionPointerReference": "0x40011C",
             },
             {
-                "column": 0,
                 "id": 524289,
                 "line": 10,
                 "name": "foo",
                 "source": {"name": "main.c", "path": "/home/labath/test/main.c"},
+                "instructionPointerReference": "0x400142",
             },
             {
-                "column": 0,
                 "id": 524290,
                 "line": 16,
                 "name": "_start",
                 "source": {"name": "main.c", "path": "/home/labath/test/main.c"},
+                "instructionPointerReference": "0x40015F",
             },
         ]
 
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
@@ -135,6 +135,7 @@
         self.configuration_done_sent = False
         self.frame_scopes = {}
         self.init_commands = init_commands
+        self.disassembled_instructions = {}
 
     @classmethod
     def encode_content(cls, s):
@@ -427,7 +428,7 @@
 
     def get_stackFrame(self, frameIndex=0, threadId=None):
         """Get a single "StackFrame" object from a "stackTrace" request and
-        return the "StackFrame as a python dictionary, or None on failure
+        return the "StackFrame" as a python dictionary, or None on failure
         """
         if threadId is None:
             threadId = self.get_thread_id()
@@ -647,6 +648,22 @@
             "arguments": args_dict,
         }
         return self.send_recv(command_dict)
+    
+    def request_disassemble(self, memoryReference, offset=-50, instructionCount=200, resolveSymbols=True):
+        args_dict = {
+            "memoryReference": memoryReference,
+            "offset": offset,
+            "instructionCount": instructionCount,
+            "resolveSymbols": resolveSymbols
+        }
+        command_dict = {
+            "command": "disassemble",
+            "type": "request",
+            "arguments": args_dict,
+        }
+        instructions = self.send_recv(command_dict)["body"]["instructions"]
+        for inst in instructions:
+            self.disassembled_instructions[inst["address"]] = inst
 
     def request_evaluate(self, expression, frameIndex=0, threadId=None, context=None):
         stackFrame = self.get_stackFrame(frameIndex=frameIndex, threadId=threadId)
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
@@ -257,6 +257,19 @@
             "exitCode == %i" % (exitCode),
         )
 
+    def disassemble(self, threadId=None, frameIndex=None):
+        stackFrames = self.get_stackFrames(
+            threadId=threadId, startFrame=frameIndex, levels=1
+        )
+        self.assertIsNotNone(stackFrames)
+        memoryReference = stackFrames[0]["instructionPointerReference"]
+        self.assertIsNotNone(memoryReference)
+
+        if memoryReference not in self.vscode.disassembled_instructions:
+            self.vscode.request_disassemble(memoryReference=memoryReference)
+        
+        return self.vscode.disassembled_instructions[memoryReference]
+
     def attach(
         self,
         program=None,
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to