diff --git a/lldb/test/API/tools/lldb-vscode/variables/TestVSCode_variables.py b/lldb/test/API/tools/lldb-vscode/variables/TestVSCode_variables.py --- a/lldb/test/API/tools/lldb-vscode/variables/TestVSCode_variables.py +++ b/lldb/test/API/tools/lldb-vscode/variables/TestVSCode_variables.py @@ -30,8 +30,8 @@ verify_value = verify[key] actual_value = actual[key] self.assertEqual(verify_value, actual_value, - '"%s" keys don\'t match (%s != %s)' % ( - key, actual_value, verify_value)) + '"%s" keys don\'t match (%s != %s) from:\n%s' % ( + key, actual_value, verify_value, actual)) if 'startswith' in verify_dict: verify = verify_dict['startswith'] for key in verify: @@ -43,6 +43,11 @@ ' "%s")') % ( key, actual_value, verify_value)) + if 'missing' in verify_dict: + missing = verify_dict['missing'] + for key in missing: + self.assertTrue(key not in actual, + 'key "%s" is not expected in %s' % (key, actual)) hasVariablesReference = 'variablesReference' in actual varRef = None if hasVariablesReference: @@ -310,22 +315,39 @@ locals = self.vscode.get_local_variables() buffer_children = make_buffer_verify_dict(0, 32) verify_locals = { - "argc": {"equals": {"type": "int", "value": "1"}}, + "argc": { + "equals": {"type": "int", "value": "1"}, + "missing": ["indexedVariables"], + }, "argv": { "equals": {"type": "const char **"}, "startswith": {"value": "0x"}, "hasVariablesReference": True, + "missing": ["indexedVariables"], }, "pt": { "equals": {"type": "PointType"}, "hasVariablesReference": True, + "missing": ["indexedVariables"], "children": { - "x": {"equals": {"type": "int", "value": "11"}}, - "y": {"equals": {"type": "int", "value": "22"}}, - "buffer": {"children": buffer_children}, + "x": { + "equals": {"type": "int", "value": "11"}, + "missing": ["indexedVariables"], + }, + "y": { + "equals": {"type": "int", "value": "22"}, + "missing": ["indexedVariables"], + }, + "buffer": { + "children": buffer_children, + "equals": {"indexedVariables": 32} + }, }, }, - "x": {"equals": {"type": "int"}}, + "x": { + "equals": {"type": "int"}, + "missing": ["indexedVariables"], + }, } self.verify_variables(verify_locals, locals) @@ -336,6 +358,7 @@ "response": { "equals": {"type": "PointType"}, "startswith": {"result": "PointType @ 0x"}, + "missing": ["indexedVariables"], "hasVariablesReference": True, }, "children": { @@ -408,3 +431,36 @@ self.assertEquals(scope.get("presentationHint"), "locals") if scope["name"] == "Registers": self.assertEquals(scope.get("presentationHint"), "registers") + + + @skipIfWindows + @skipIfRemote + def test_indexedVariables(self): + """ + Tests that arrays and lldb.SBValue objects that have synthetic child + providers have "indexedVariables" key/value pairs. This helps the IDE + not to fetch too many children all at once. + """ + program = self.getBuildArtifact("a.out") + self.build_and_launch(program) + source = "main.cpp" + breakpoint1_line = line_number(source, "// breakpoint 4") + lines = [breakpoint1_line] + # Set breakpoint in the thread function so we can step the threads + breakpoint_ids = self.set_source_breakpoints(source, lines) + self.assertEqual( + len(breakpoint_ids), len(lines), "expect correct number of breakpoints" + ) + self.continue_to_breakpoints(breakpoint_ids) + + # Verify locals + locals = self.vscode.get_local_variables() + buffer_children = make_buffer_verify_dict(0, 32) + verify_locals = { + "small_array": {"equals": {"indexedVariables": 5}}, + "large_array": {"equals": {"indexedVariables": 200}}, + "small_vector": {"equals": {"indexedVariables": 5}}, + "large_vector": {"equals": {"indexedVariables": 200}}, + "pt": {"missing": ["indexedVariables"]}, + } + self.verify_variables(verify_locals, locals) diff --git a/lldb/test/API/tools/lldb-vscode/variables/main.cpp b/lldb/test/API/tools/lldb-vscode/variables/main.cpp --- a/lldb/test/API/tools/lldb-vscode/variables/main.cpp +++ b/lldb/test/API/tools/lldb-vscode/variables/main.cpp @@ -5,10 +5,10 @@ int y; int buffer[BUFFER_SIZE]; }; - +#include int g_global = 123; static int s_global = 234; - +int test_indexedVariables(); int main(int argc, char const *argv[]) { static float s_local = 2.25; PointType pt = { 11,22, {0}}; @@ -22,5 +22,15 @@ s_global = x; // breakpoint 2 } } - return 0; // breakpoint 3 + return test_indexedVariables(); // breakpoint 3 +} + +int test_indexedVariables() { + int small_array[5] = {1, 2, 3, 4, 5}; + int large_array[200]; + std::vector small_vector; + std::vector large_vector; + small_vector.assign(5, 0); + large_vector.assign(200, 0); + return 0; // breakpoint 4 } diff --git a/lldb/tools/lldb-vscode/JSONUtils.cpp b/lldb/tools/lldb-vscode/JSONUtils.cpp --- a/lldb/tools/lldb-vscode/JSONUtils.cpp +++ b/lldb/tools/lldb-vscode/JSONUtils.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include "llvm/ADT/Optional.h" #include "llvm/Support/FormatAdapters.h" @@ -1024,7 +1025,37 @@ if (format_hex) v.SetFormat(lldb::eFormatHex); SetValueForKey(v, object, "value"); - auto type_cstr = v.GetType().GetDisplayTypeName(); + auto type_obj = v.GetType(); + auto type_cstr = type_obj.GetDisplayTypeName(); + // If we have a type with many many children, we would like to be able to + // give a hint to the IDE that the type has indexed children so that the + // request can be broken up in grabbing only a few children at a time. We want + // to be careful and only call "v.GetNumChildren()" if we have an array type + // or if we have a synthetic child provider. We don't want to call + // "v.GetNumChildren()" on all objects as class, struct and union types don't + // need to be completed if they are never expanded. So we want to avoid + // calling this to only cases where we it makes sense to keep performance high + // during normal debugging. + + // If we have an array type, say that it is indexed and provide the number of + // children in case we have a huge array. If we don't do this, then we might + // take a while to produce all children at onces which can delay your debug + // session. + const bool is_array = type_obj.IsArrayType(); + const bool is_synthetic = v.IsSynthetic(); + if (is_array || is_synthetic) { + const auto num_children = v.GetNumChildren(); + if (is_array) { + object.try_emplace("indexedVariables", num_children); + } else { + // If a type has a synthetic child provider, then the SBType of "v" won't + // tell us anything about what might be displayed. So we can check if the + // first child's name is "[0]" and then we can say it is indexed. + const char *first_child_name = v.GetChildAtIndex(0).GetName(); + if (first_child_name && strcmp(first_child_name, "[0]") == 0) + object.try_emplace("indexedVariables", num_children); + } + } EmplaceSafeString(object, "type", type_cstr ? type_cstr : NO_TYPENAME); if (varID != INT64_MAX) object.try_emplace("id", varID); 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 @@ -43,7 +43,7 @@ reverse_request_seq(0), waiting_for_run_in_terminal(false), progress_event_reporter( [&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }) { - const char *log_file_path = getenv("LLDBVSCODE_LOG"); + const char *log_file_path = "/tmp/vscode.txt"; // 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