diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py @@ -369,7 +369,13 @@ def wait_for_exited(self): event_dict = self.wait_for_event('exited') if event_dict is None: - raise ValueError("didn't get stopped event") + raise ValueError("didn't get exited event") + return event_dict + + def wait_for_terminated(self): + event_dict = self.wait_for_event('terminated') + if event_dict is None: + raise ValueError("didn't get terminated event") return event_dict def get_initialize_value(self, key): diff --git a/lldb/test/API/tools/lldb-vscode/terminated-event/Makefile b/lldb/test/API/tools/lldb-vscode/terminated-event/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/tools/lldb-vscode/terminated-event/Makefile @@ -0,0 +1,17 @@ +DYLIB_NAME := foo +DYLIB_CXX_SOURCES := foo.cpp +CXX_SOURCES := main.cpp + +LD_EXTRAS := -Wl,-rpath "-Wl,$(shell pwd)" +USE_LIBDL :=1 + +include Makefile.rules + +all: a.out.stripped + +a.out.stripped: + strip -o a.out.stripped a.out + +ifneq "$(CODESIGN)" "" + $(CODESIGN) -fs - a.out.stripped +endif \ No newline at end of file diff --git a/lldb/test/API/tools/lldb-vscode/terminated-event/TestVSCode_terminatedEvent.py b/lldb/test/API/tools/lldb-vscode/terminated-event/TestVSCode_terminatedEvent.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/tools/lldb-vscode/terminated-event/TestVSCode_terminatedEvent.py @@ -0,0 +1,64 @@ +""" +Test lldb-vscode terminated event +""" + +import vscode +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil +import lldbvscode_testcase +import re +import json + +class TestVSCode_terminatedEvent(lldbvscode_testcase.VSCodeTestCaseBase): + + @skipIfWindows + @skipIfRemote + def test_terminated_event(self): + ''' + Terminated Event + Now contains the statistics of a debug session: + metatdata: + totalDebugInfoByteSize > 0 + totalDebugInfoEnabled > 0 + totalModuleCountHasDebugInfo > 0 + ... + targetInfo: + totalBreakpointResolveTime > 0 + breakpoints: + recognize function breakpoint + recognize source line breakpoint + It should contains the breakpoints info: function bp & source line bp + ''' + + program_basename = "a.out.stripped" + program = self.getBuildArtifact(program_basename) + self.build_and_launch(program) + # Set breakpoints + functions = ['foo'] + breakpoint_ids = self.set_function_breakpoints(functions) + self.assertEquals(len(breakpoint_ids), len(functions), 'expect one breakpoint') + main_bp_line = line_number('main.cpp', '// main breakpoint 1') + breakpoint_ids.append(self.set_source_breakpoints('main.cpp', [main_bp_line])) + + self.continue_to_breakpoints(breakpoint_ids) + self.continue_to_exit() + + statistics = self.vscode.wait_for_terminated()['statistics'] + self.assertTrue(statistics['totalDebugInfoByteSize'] > 0) + self.assertTrue(statistics['totalDebugInfoEnabled'] > 0) + self.assertTrue(statistics['totalModuleCountHasDebugInfo'] > 0) + + self.assertIsNotNone(statistics['memory']) + self.assertNotIn('modules', statistics.keys()) + + # lldb-vscode debugs one target at a time + target = json.loads(statistics['targets'])[0] + self.assertTrue(target['totalBreakpointResolveTime'] > 0) + + breakpoints = target['breakpoints'] + self.assertIn('foo', + breakpoints[0]['details']['Breakpoint']['BKPTResolver']['Options']['SymbolNames'], + 'foo is a symbol breakpoint') + self.assertTrue(breakpoints[1]['details']['Breakpoint']['BKPTResolver']['Options']['FileName'].endswith('main.cpp'), + 'target has source line breakpoint in main.cpp') diff --git a/lldb/test/API/tools/lldb-vscode/terminated-event/foo.h b/lldb/test/API/tools/lldb-vscode/terminated-event/foo.h new file mode 100644 --- /dev/null +++ b/lldb/test/API/tools/lldb-vscode/terminated-event/foo.h @@ -0,0 +1 @@ +int foo(); diff --git a/lldb/test/API/tools/lldb-vscode/terminated-event/foo.cpp b/lldb/test/API/tools/lldb-vscode/terminated-event/foo.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/API/tools/lldb-vscode/terminated-event/foo.cpp @@ -0,0 +1,3 @@ +int foo() { + return 12; +} diff --git a/lldb/test/API/tools/lldb-vscode/terminated-event/main.cpp b/lldb/test/API/tools/lldb-vscode/terminated-event/main.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/API/tools/lldb-vscode/terminated-event/main.cpp @@ -0,0 +1,8 @@ +#include +#include "foo.h" + +int main(int argc, char const *argv[]) { + std::cout << "Hello World!" << std::endl; // main breakpoint 1 + foo(); + return 0; +} diff --git a/lldb/tools/lldb-vscode/JSONUtils.h b/lldb/tools/lldb-vscode/JSONUtils.h --- a/lldb/tools/lldb-vscode/JSONUtils.h +++ b/lldb/tools/lldb-vscode/JSONUtils.h @@ -485,6 +485,12 @@ llvm::StringRef debug_adaptor_path, llvm::StringRef comm_file); +/// Create a "Terminated" JSON object that contains statistics +/// +/// \return +/// A body JSON object with debug info and breakpoint info +llvm::json::Object CreateTerminatedEventObject(); + /// Convert a given JSON object to a string. std::string JSONToString(const llvm::json::Value &json); 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 @@ -19,6 +19,8 @@ #include "lldb/API/SBBreakpoint.h" #include "lldb/API/SBBreakpointLocation.h" #include "lldb/API/SBDeclaration.h" +#include "lldb/API/SBStringList.h" +#include "lldb/API/SBStructuredData.h" #include "lldb/API/SBValue.h" #include "lldb/Host/PosixApi.h" @@ -1140,6 +1142,73 @@ return reverse_request; } +// Keep all the top level items from the statistics dump, except for the +// "modules" array. It can be huge and cause delay +// Array and dictionary value will return as pairs +void FilterAndGetValueForKey(const lldb::SBStructuredData data, const char *key, + llvm::json::Object &out) { + lldb::SBStructuredData value = data.GetValueForKey(key); + std::string key_utf8 = llvm::json::fixUTF8(key); + if (strcmp(key, "modules") == 0) + return; + switch (value.GetType()) { + case lldb::eStructuredDataTypeFloat: + out.try_emplace(key_utf8, value.GetFloatValue()); + break; + case lldb::eStructuredDataTypeInteger: + out.try_emplace(key_utf8, value.GetIntegerValue()); + break; + case lldb::eStructuredDataTypeArray: { + lldb::SBStream contents; + value.GetAsJSON(contents); + out.try_emplace(key_utf8, llvm::json::fixUTF8(contents.GetData())); + } break; + case lldb::eStructuredDataTypeBoolean: + out.try_emplace(key_utf8, value.GetBooleanValue()); + break; + case lldb::eStructuredDataTypeString: { + // Get the string size before reading + const size_t str_length = value.GetStringValue(nullptr, 0); + std::string str(str_length + 1, 0); + value.GetStringValue(&str[0], str_length); + out.try_emplace(key_utf8, llvm::json::fixUTF8(str)); + } break; + case lldb::eStructuredDataTypeDictionary: { + lldb::SBStream contents; + value.GetAsJSON(contents); + out.try_emplace(key_utf8, llvm::json::fixUTF8(contents.GetData())); + } break; + case lldb::eStructuredDataTypeNull: + case lldb::eStructuredDataTypeGeneric: + case lldb::eStructuredDataTypeInvalid: + break; + } +} + +void addStatistic(llvm::json::Object &event) { + lldb::SBStructuredData statistics = g_vsc.target.GetStatistics(); + bool is_dictionary = + statistics.GetType() == lldb::eStructuredDataTypeDictionary; + if (!is_dictionary) + return; + llvm::json::Object stats_body; + + lldb::SBStringList keys; + if (!statistics.GetKeys(keys)) + return; + for (size_t i = 0; i < keys.GetSize(); i++) { + const char *key = keys.GetStringAtIndex(i); + FilterAndGetValueForKey(statistics, key, stats_body); + } + event.try_emplace("statistics", std::move(stats_body)); +} + +llvm::json::Object CreateTerminatedEventObject() { + llvm::json::Object event(CreateEventObject("terminated")); + addStatistic(event); + return event; +} + std::string JSONToString(const llvm::json::Value &json) { std::string data; llvm::raw_string_ostream os(data); 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 @@ -204,7 +204,7 @@ g_vsc.sent_terminated_event = true; g_vsc.RunTerminateCommands(); // Send a "terminated" event - llvm::json::Object event(CreateEventObject("terminated")); + llvm::json::Object event(CreateTerminatedEventObject()); g_vsc.SendJSON(llvm::json::Value(std::move(event))); } } @@ -2949,7 +2949,7 @@ const uint32_t addr_size = g_vsc.target.GetProcess().GetAddressByteSize(); lldb::SBValue reg_set = g_vsc.variables.registers.GetValueAtIndex(0); const uint32_t num_regs = reg_set.GetNumChildren(); - for (uint32_t reg_idx=0; reg_idx