yinghuitan created this revision.
yinghuitan added reviewers: clayborg, wallace.
Herald added a subscriber: jfb.
yinghuitan requested review of this revision.
Herald added a project: LLDB.
Herald added a subscriber: lldb-commits.

VScode now sends a "scopes" DAP request immediately after any expression 
evaluation. 
This scopes request would clear and invalidate any non-scoped expandable 
variables in g_vsc.variables, causing later "variables" request to return empty 
result.
The symptom is that any expandable variables in VScode watch window/debug 
console UI to return empty content.

This diff fixes this issue by only clearing the expandable variables at process 
continue time. To achieve this, we have to repopulate all scoped variables 
during context switch for each "scopes" request without clearing global 
expandable variables. 
So the PR puts the scoped variables into its own locals/globals/registers; and 
all expandable variables into separate "expandableVariables" list.
Also, instead of using the variable index for "variableReference", it generates 
a new variableReference id each time as the key of "expandableVariables".

As a further new feature, this PR adds a new "expandablePermanentVariables" 
which has the lifetime of debug session. Any expandable variables from debug 
console
are added into this list. This enables users to snapshot expanable old variable 
in debug console and compare with new variables if desire.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D105166

Files:
  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
@@ -107,6 +107,25 @@
 
 enum LaunchMethod { Launch, Attach, AttachForSuspendedLaunch };
 
+// Debuggee will continue from stopped state.
+void willContinue() {
+  g_vsc.variableHolder.clear();
+}
+
+lldb::SBValueList* getScopeRef(int64_t variablesReference) {
+  assert(VARREF_IS_SCOPE(variablesReference));
+  switch (variablesReference) {
+    case VARREF_LOCALS:
+      return &g_vsc.variableHolder.locals;
+    case VARREF_GLOBALS:
+      return &g_vsc.variableHolder.globals;
+    case VARREF_REGS:
+      return &g_vsc.variableHolder.registers;
+    default:
+      return nullptr;
+    }
+}
+
 SOCKET AcceptConnection(int portno) {
   // Accept a socket connection from any host on "portno".
   SOCKET newsockfd = -1;
@@ -727,6 +746,7 @@
   // Remember the thread ID that caused the resume so we can set the
   // "threadCausedFocus" boolean value in the "stopped" events.
   g_vsc.focus_tid = GetUnsigned(arguments, "threadId", LLDB_INVALID_THREAD_ID);
+  willContinue();
   lldb::SBError error = process.Continue();
   llvm::json::Object body;
   body.try_emplace("allThreadsContinued", true);
@@ -1215,9 +1235,8 @@
       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);
-        body.try_emplace("variablesReference", variablesReference);
+        auto variableReference = g_vsc.variableHolder.insertNewExpandableVariable(value, /*isPermanent*/context == "repl");
+        body.try_emplace("variablesReference", variableReference);
       } else {
         body.try_emplace("variablesReference", (int64_t)0);
       }
@@ -1769,6 +1788,7 @@
     // Remember the thread ID that caused the resume so we can set the
     // "threadCausedFocus" boolean value in the "stopped" events.
     g_vsc.focus_tid = thread.GetThreadID();
+    willContinue();
     thread.StepOver();
   } else {
     response["success"] = llvm::json::Value(false);
@@ -1895,20 +1915,15 @@
     frame.GetThread().GetProcess().SetSelectedThread(frame.GetThread());
     frame.GetThread().SetSelectedFrame(frame.GetFrameID());
   }
-  g_vsc.variables.Clear();
-  g_vsc.variables.Append(frame.GetVariables(true,   // arguments
+  g_vsc.variableHolder.locals = frame.GetVariables(true,   // arguments
                                             true,   // locals
                                             false,  // statics
-                                            true)); // in_scope_only
-  g_vsc.num_locals = g_vsc.variables.GetSize();
-  g_vsc.variables.Append(frame.GetVariables(false,  // arguments
+                                            true); // in_scope_only
+  g_vsc.variableHolder.globals = frame.GetVariables(false,  // arguments
                                             false,  // locals
                                             true,   // statics
-                                            true)); // in_scope_only
-  g_vsc.num_globals = g_vsc.variables.GetSize() - (g_vsc.num_locals);
-  g_vsc.variables.Append(frame.GetRegisters());
-  g_vsc.num_regs =
-      g_vsc.variables.GetSize() - (g_vsc.num_locals + g_vsc.num_globals);
+                                            true); // in_scope_only
+  g_vsc.variableHolder.registers = frame.GetRegisters();
   body.try_emplace("scopes", g_vsc.CreateTopLevelScopes());
   response.try_emplace("body", std::move(body));
   g_vsc.SendJSON(llvm::json::Value(std::move(response)));
@@ -2523,6 +2538,7 @@
     // Remember the thread ID that caused the resume so we can set the
     // "threadCausedFocus" boolean value in the "stopped" events.
     g_vsc.focus_tid = thread.GetThreadID();
+    willContinue();
     thread.StepInto();
   } else {
     response["success"] = llvm::json::Value(false);
@@ -2575,6 +2591,7 @@
     // Remember the thread ID that caused the resume so we can set the
     // "threadCausedFocus" boolean value in the "stopped" events.
     g_vsc.focus_tid = thread.GetThreadID();
+    willContinue();
     thread.StepOut();
   } else {
     response["success"] = llvm::json::Value(false);
@@ -2750,37 +2767,22 @@
   // of the variable within the g_vsc.variables list.
   const auto id_value = GetUnsigned(arguments, "id", UINT64_MAX);
   if (id_value != UINT64_MAX) {
-    variable = g_vsc.variables.GetValueAtIndex(id_value);
+    variable = g_vsc.variableHolder.getVariableFromVariableReference(id_value);
   } else if (VARREF_IS_SCOPE(variablesReference)) {
     // variablesReference is one of our scopes, not an actual variable it is
     // asking for a variable in locals or globals or registers
     int64_t start_idx = 0;
     int64_t end_idx = 0;
-    switch (variablesReference) {
-    case VARREF_LOCALS:
-      start_idx = 0;
-      end_idx = start_idx + g_vsc.num_locals;
-      break;
-    case VARREF_GLOBALS:
-      start_idx = g_vsc.num_locals;
-      end_idx = start_idx + g_vsc.num_globals;
-      break;
-    case VARREF_REGS:
-      start_idx = g_vsc.num_locals + g_vsc.num_globals;
-      end_idx = start_idx + g_vsc.num_regs;
-      break;
-    default:
-      break;
-    }
+    lldb::SBValueList *pScopeRef = getScopeRef(variablesReference);
 
     for (int64_t i = end_idx - 1; i >= start_idx; --i) {
-      lldb::SBValue curr_variable = g_vsc.variables.GetValueAtIndex(i);
+      lldb::SBValue curr_variable = pScopeRef->GetValueAtIndex(i);
       std::string variable_name = CreateUniqueVariableNameForDisplay(
           curr_variable, is_duplicated_variable_name);
       if (variable_name == name) {
         variable = curr_variable;
         if (curr_variable.MightHaveChildren())
-          newVariablesReference = i;
+          newVariablesReference = g_vsc.variableHolder.getNewVariableRefence(true);
         break;
       }
     }
@@ -2790,8 +2792,7 @@
 
     // We have a named item within an actual variable so we need to find it
     // withing the container variable by name.
-    const int64_t var_idx = VARREF_TO_VARIDX(variablesReference);
-    lldb::SBValue container = g_vsc.variables.GetValueAtIndex(var_idx);
+    lldb::SBValue container = g_vsc.variableHolder.getVariableFromVariableReference(variablesReference);
     variable = container.GetChildMemberWithName(name.data());
     if (!variable.IsValid()) {
       if (name.startswith("[")) {
@@ -2807,8 +2808,8 @@
     // We don't know the index of the variable in our g_vsc.variables
     if (variable.IsValid()) {
       if (variable.MightHaveChildren()) {
-        newVariablesReference = VARIDX_TO_VARREF(g_vsc.variables.GetSize());
-        g_vsc.variables.Append(variable);
+        auto isPermanent = g_vsc.variableHolder.isPermanentVariableReference(variablesReference);
+        g_vsc.variableHolder.insertNewExpandableVariable(variable, isPermanent);
       }
     }
   }
@@ -2924,28 +2925,16 @@
     // asking for the list of args, locals or globals.
     int64_t start_idx = 0;
     int64_t num_children = 0;
-    switch (variablesReference) {
-    case VARREF_LOCALS:
-      start_idx = start;
-      num_children = g_vsc.num_locals;
-      break;
-    case VARREF_GLOBALS:
-      start_idx = start + g_vsc.num_locals + start;
-      num_children = g_vsc.num_globals;
-      break;
-    case VARREF_REGS:
-      start_idx = start + g_vsc.num_locals + g_vsc.num_globals;
-      num_children = g_vsc.num_regs;
-      break;
-    default:
-      break;
-    }
+    lldb::SBValueList *pScopeRef = getScopeRef(variablesReference);
+    assert(pScopeRef != nullptr);
+    num_children = pScopeRef->GetSize();
     const int64_t end_idx = start_idx + ((count == 0) ? num_children : count);
 
     // We first find out which variable names are duplicated
     std::map<std::string, int> variable_name_counts;
     for (auto i = start_idx; i < end_idx; ++i) {
-      lldb::SBValue variable = g_vsc.variables.GetValueAtIndex(i);
+
+      lldb::SBValue variable = pScopeRef->GetValueAtIndex(i);
       if (!variable.IsValid())
         break;
       variable_name_counts[GetNonNullVariableName(variable)]++;
@@ -2953,19 +2942,23 @@
 
     // Now we construct the result with unique display variable names
     for (auto i = start_idx; i < end_idx; ++i) {
-      lldb::SBValue variable = g_vsc.variables.GetValueAtIndex(i);
+      lldb::SBValue variable = pScopeRef->GetValueAtIndex(i);
 
       if (!variable.IsValid())
         break;
-      variables.emplace_back(CreateVariable(variable, VARIDX_TO_VARREF(i), i,
+
+      int64_t variableReference = 0;
+      if (variable.MightHaveChildren()) {
+        variableReference = g_vsc.variableHolder.insertNewExpandableVariable(variable, /*isPermanent*/false);
+      }
+      variables.emplace_back(CreateVariable(variable, variableReference, variableReference != 0? variableReference: UINT64_MAX,
                                             hex,
           variable_name_counts[GetNonNullVariableName(variable)] > 1));
     }
   } else {
     // We are expanding a variable that has children, so we will return its
     // children.
-    const int64_t var_idx = VARREF_TO_VARIDX(variablesReference);
-    lldb::SBValue variable = g_vsc.variables.GetValueAtIndex(var_idx);
+    lldb::SBValue variable = g_vsc.variableHolder.getVariableFromVariableReference(variablesReference);
     if (variable.IsValid()) {
       const auto num_children = variable.GetNumChildren();
       const int64_t end_idx = start + ((count == 0) ? num_children : count);
@@ -2974,11 +2967,10 @@
         if (!child.IsValid())
           break;
         if (child.MightHaveChildren()) {
-          const int64_t var_idx = g_vsc.variables.GetSize();
-          auto childVariablesReferences = VARIDX_TO_VARREF(var_idx);
+          auto isPermanent = g_vsc.variableHolder.isPermanentVariableReference(variablesReference);
+          auto childVariablesReferences = g_vsc.variableHolder.insertNewExpandableVariable(child, isPermanent);
           variables.emplace_back(
-              CreateVariable(child, childVariablesReferences, var_idx, hex));
-          g_vsc.variables.Append(child);
+              CreateVariable(child, childVariablesReferences, childVariablesReferences, hex));
         } else {
           variables.emplace_back(CreateVariable(child, 0, INT64_MAX, hex));
         }
Index: lldb/tools/lldb-vscode/VSCode.h
===================================================================
--- lldb/tools/lldb-vscode/VSCode.h
+++ lldb/tools/lldb-vscode/VSCode.h
@@ -59,8 +59,6 @@
 #define VARREF_REGS (int64_t)3
 #define VARREF_FIRST_VAR_IDX (int64_t)4
 #define VARREF_IS_SCOPE(v) (VARREF_LOCALS <= 1 && v < VARREF_FIRST_VAR_IDX)
-#define VARIDX_TO_VARREF(i) ((i) + VARREF_FIRST_VAR_IDX)
-#define VARREF_TO_VARIDX(v) ((v)-VARREF_FIRST_VAR_IDX)
 #define NO_TYPENAME "<no-type>"
 
 namespace lldb_vscode {
@@ -83,17 +81,43 @@
   JSONNotObject
 };
 
+struct VariablesHolder {
+  // Bit tag bit to tell if a variableReference is inside expandablePermanentVariables or not.
+  static constexpr int PermanentVariableReferenceTagBit = 32;
+
+  lldb::SBValueList locals;
+  lldb::SBValueList globals;
+  lldb::SBValueList registers;
+
+  int64_t nextVariableReferenceSeed{VARREF_FIRST_VAR_IDX};
+  llvm::DenseMap<int64_t, lldb::SBValue> expandableVariables;
+  llvm::DenseMap<int64_t, lldb::SBValue> expandablePermanentVariables;
+
+  // Check if \p variableReference points to variable in a expandablePermanentVariables.
+  bool isPermanentVariableReference(int64_t variableReference);
+
+  // \return a new variableReference. If isPermanent is true the returned
+  // value will have PermanentVariableReferenceTagBit set.
+  int64_t getNewVariableRefence(bool isPermanent);
+
+  // \return the expandable variable corresponding with variableReference value of \p value.
+  lldb::SBValue getVariableFromVariableReference(int64_t value);
+
+  // Insert a new \p expandableVariable.
+  int64_t insertNewExpandableVariable(lldb::SBValue expandableVariable, bool isPermanent);
+
+  // Clear all scope variables and non-permanent expandable variables.
+  void clear();
+};
+
 struct VSCode {
   std::string debug_adaptor_path;
   InputStream input;
   OutputStream output;
   lldb::SBDebugger debugger;
   lldb::SBTarget target;
-  lldb::SBValueList variables;
+  VariablesHolder variableHolder;
   lldb::SBBroadcaster broadcaster;
-  int64_t num_regs;
-  int64_t num_locals;
-  int64_t num_globals;
   std::thread event_thread;
   std::thread progress_event_thread;
   std::unique_ptr<std::ofstream> log;
Index: lldb/tools/lldb-vscode/VSCode.cpp
===================================================================
--- lldb/tools/lldb-vscode/VSCode.cpp
+++ lldb/tools/lldb-vscode/VSCode.cpp
@@ -30,8 +30,7 @@
 VSCode g_vsc;
 
 VSCode::VSCode()
-    : variables(), broadcaster("lldb-vscode"), num_regs(0), num_locals(0),
-      num_globals(0), log(),
+    : broadcaster("lldb-vscode"), log(),
       exception_breakpoints(
           {{"cpp_catch", "C++ Catch", lldb::eLanguageTypeC_plus_plus},
            {"cpp_throw", "C++ Throw", lldb::eLanguageTypeC_plus_plus},
@@ -382,10 +381,10 @@
 
 llvm::json::Value VSCode::CreateTopLevelScopes() {
   llvm::json::Array scopes;
-  scopes.emplace_back(CreateScope("Locals", VARREF_LOCALS, num_locals, false));
+  scopes.emplace_back(CreateScope("Locals", VARREF_LOCALS, g_vsc.variableHolder.locals.GetSize(), false));
   scopes.emplace_back(
-      CreateScope("Globals", VARREF_GLOBALS, num_globals, false));
-  scopes.emplace_back(CreateScope("Registers", VARREF_REGS, num_regs, false));
+      CreateScope("Globals", VARREF_GLOBALS, g_vsc.variableHolder.globals.GetSize(), false));
+  scopes.emplace_back(CreateScope("Registers", VARREF_REGS, g_vsc.variableHolder.registers.GetSize(), false));
   return llvm::json::Value(std::move(scopes));
 }
 
@@ -527,4 +526,42 @@
   request_handlers[request] = callback;
 }
 
+void VariablesHolder::clear() {
+  this->locals.Clear();
+  this->globals.Clear();
+  this->registers.Clear();
+  this->expandableVariables.clear();
+}
+
+int64_t VariablesHolder::getNewVariableRefence(bool isPermanent) {
+  const int64_t newVariableReference = isPermanent ? ((1ll << PermanentVariableReferenceTagBit) | nextVariableReferenceSeed) : nextVariableReferenceSeed;
+  ++nextVariableReferenceSeed;
+  return newVariableReference;
+}
+
+bool VariablesHolder::isPermanentVariableReference(int64_t variableReference) {
+  return (variableReference & (1ll << PermanentVariableReferenceTagBit)) != 0;
+}
+
+lldb::SBValue VariablesHolder::getVariableFromVariableReference(int64_t value) {
+  lldb::SBValue variable;
+  auto isPermanent = isPermanentVariableReference(value);
+  if (isPermanent) {
+    variable = expandablePermanentVariables.find(value)->second;
+  } else {
+    variable = expandableVariables.find(value)->second;
+  }
+  return variable;
+}
+
+int64_t VariablesHolder::insertNewExpandableVariable(lldb::SBValue expandableVariable, bool isPermanent) {
+  auto newVariablesReferences = getNewVariableRefence(isPermanent);
+  if (isPermanent) {
+    expandablePermanentVariables.insert(std::make_pair(newVariablesReferences, expandableVariable));
+  } else {
+    expandableVariables.insert(std::make_pair(newVariablesReferences, expandableVariable));
+  }
+  return newVariablesReferences;
+}
+
 } // namespace lldb_vscode
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to