diff --git a/lldb/tools/lldb-vscode/VSCode.h b/lldb/tools/lldb-vscode/VSCode.h --- a/lldb/tools/lldb-vscode/VSCode.h +++ b/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 "" 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 expandableVariables; + llvm::DenseMap 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 log; diff --git a/lldb/tools/lldb-vscode/VSCode.cpp b/lldb/tools/lldb-vscode/VSCode.cpp --- a/lldb/tools/lldb-vscode/VSCode.cpp +++ b/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 diff --git a/lldb/tools/lldb-vscode/lldb-vscode.cpp b/lldb/tools/lldb-vscode/lldb-vscode.cpp --- a/lldb/tools/lldb-vscode/lldb-vscode.cpp +++ b/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 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)); }