clayborg updated this revision to Diff 376358.
clayborg added a comment.
Changes:
- Swithed from using "double" to using ElapsedTime::Duration
- Stop using assertTrue in tests, and use better assertXXX methods
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D110804/new/
https://reviews.llvm.org/D110804
Files:
lldb/include/lldb/Breakpoint/Breakpoint.h
lldb/include/lldb/Breakpoint/BreakpointLocation.h
lldb/include/lldb/Core/FileSpecList.h
lldb/include/lldb/Core/Module.h
lldb/include/lldb/Core/Section.h
lldb/include/lldb/Symbol/SymbolContext.h
lldb/include/lldb/Symbol/SymbolFile.h
lldb/include/lldb/Target/Metrics.h
lldb/include/lldb/Target/PathMappingList.h
lldb/include/lldb/Target/Process.h
lldb/include/lldb/Target/Target.h
lldb/include/lldb/Utility/ElapsedTime.h
lldb/include/lldb/lldb-forward.h
lldb/packages/Python/lldbsuite/test/lldbtest.py
lldb/source/Breakpoint/Breakpoint.cpp
lldb/source/Breakpoint/BreakpointLocation.cpp
lldb/source/Commands/CommandObjectTarget.cpp
lldb/source/Commands/Options.td
lldb/source/Core/FileSpecList.cpp
lldb/source/Core/Module.cpp
lldb/source/Core/Section.cpp
lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
lldb/source/Plugins/ObjectFile/JIT/ObjectFileJIT.cpp
lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp
lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp
lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h
lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h
lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp
lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp
lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp
lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.h
lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp
lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.h
lldb/source/Symbol/SymbolContext.cpp
lldb/source/Symbol/Symtab.cpp
lldb/source/Target/CMakeLists.txt
lldb/source/Target/Metrics.cpp
lldb/source/Target/PathMappingList.cpp
lldb/source/Target/Process.cpp
lldb/source/Target/Target.cpp
lldb/test/API/commands/target/metrics/Makefile
lldb/test/API/commands/target/metrics/TestTargetMetrics.py
lldb/test/API/commands/target/metrics/a.cpp
lldb/test/API/commands/target/metrics/main.cpp
Index: lldb/test/API/commands/target/metrics/main.cpp
===================================================================
--- /dev/null
+++ lldb/test/API/commands/target/metrics/main.cpp
@@ -0,0 +1,3 @@
+extern int a_function();
+
+int main(int argc, char const *argv[]) { return a_function(); }
Index: lldb/test/API/commands/target/metrics/a.cpp
===================================================================
--- /dev/null
+++ lldb/test/API/commands/target/metrics/a.cpp
@@ -0,0 +1 @@
+int a_function() { return 500; }
Index: lldb/test/API/commands/target/metrics/TestTargetMetrics.py
===================================================================
--- /dev/null
+++ lldb/test/API/commands/target/metrics/TestTargetMetrics.py
@@ -0,0 +1,359 @@
+"""
+Test that loading of dependents works correctly for all the potential
+combinations.
+"""
+
+
+import lldb
+import json
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+@skipIfWindows # Windows deals differently with shared libs.
+class TargetDependentsTestCase(TestBase):
+
+ mydir = TestBase.compute_mydir(__file__)
+
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def setUp(self):
+ TestBase.setUp(self)
+ self.build()
+
+ def has_exactly_one_image(self, matching, msg=""):
+ self.expect(
+ "image list",
+ "image list should contain at least one image",
+ substrs=['[ 0]'])
+ should_match = not matching
+ self.expect(
+ "image list", msg, matching=should_match, substrs=['[ 1]'])
+
+
+ def verify_key_presence(self, dict, description, keys_exist, keys_missing=None):
+ if keys_exist:
+ for key in keys_exist:
+ self.assertTrue(key in dict,
+ 'Make sure "%s" key exists in %s dictionary' % (key, description))
+ if keys_missing:
+ for key in keys_missing:
+ self.assertTrue(key not in dict,
+ 'Make sure "%s" key does not exists in %s dictionary' % (key, description))
+
+ def find_module_in_metrics(self, path, metrics):
+ modules = metrics['modules']
+ for module in modules:
+ if module['path'] == path:
+ return module
+ return None
+
+ def test_metrics_default(self):
+ """Test default behavior of "target metrics" command.
+
+ Output expected to be something like:
+
+ (lldb) target metrics
+ {
+ "targetCreateTime": 0.26566899599999999,
+ "totalBreakpointResolveTime": 0.0031409419999999999,
+ "totalDebugInfoIndexTime": 0,
+ "totalDebugInfoParseTime": 0,
+ "totalDebugInfoSize": 2193,
+ "totalSymbolTableIndexTime": 0.056834488000000009,
+ "totalSymbolTableParseTime": 0.093979421999999979
+ }
+
+ """
+ target = self.createTestTarget()
+ return_obj = lldb.SBCommandReturnObject()
+ self.ci.HandleCommand("target metrics", return_obj, False)
+ metrics_json = return_obj.GetOutput()
+ metrics = json.loads(metrics_json)
+ keys_exist = [
+ 'targetCreateTime',
+ 'totalBreakpointResolveTime',
+ 'totalDebugInfoIndexTime',
+ 'totalDebugInfoParseTime',
+ 'totalDebugInfoSize',
+ 'totalSymbolTableIndexTime',
+ 'totalSymbolTableParseTime',
+ ]
+ keys_missing = [
+ 'breakpoints',
+ 'modules',
+ 'settings'
+ ]
+ self.verify_key_presence(metrics, '"metrics"', keys_exist, keys_missing)
+ self.assertGreater(metrics['targetCreateTime'], 0.0)
+ self.assertEqual(metrics['totalBreakpointResolveTime'], 0.0)
+ self.assertGreater(metrics['totalDebugInfoSize'], 0)
+ self.assertGreater(metrics['totalSymbolTableIndexTime'], 0.0)
+ self.assertGreater(metrics['totalSymbolTableParseTime'], 0.0)
+
+ def test_metrics_modules(self):
+ """Test behavior of "target metrics --modules" command.
+
+ Output expected to be something like:
+
+ (lldb) target metrics --modules
+ {
+ "targetCreateTime": 0.26566899599999999,
+ "totalBreakpointResolveTime": 0.0031409419999999999,
+ "totalDebugInfoIndexTime": 0,
+ "totalDebugInfoParseTime": 0.00037288300000000001,
+ "totalDebugInfoSize": 2193,
+ "totalSymbolTableIndexTime": 0.056834488000000009,
+ "totalSymbolTableParseTime": 0.093979421999999979,
+ "modules": [
+ {
+ "debugInfoIndexTime": 0,
+ "debugInfoParseTime": 0,
+ "debugInfoSize": 1139,
+ "path": "/Users/gclayton/Documents/src/lldb/main/Debug/lldb-test-build.noindex/commands/target/metrics/TestTargetMetrics.test_metrics_modules/a.out",
+ "symbolTableIndexTime": 2.3816e-05,
+ "symbolTableParseTime": 0.000163747,
+ "triple": "x86_64-apple-macosx11.0.0",
+ "uuid": "10531B95-4DF4-3FFE-9D51-549DD17435E2"
+ },
+ {
+ "debugInfoIndexTime": 0,
+ "debugInfoParseTime": 0,
+ "debugInfoSize": 1054,
+ "path": "/Users/gclayton/Documents/src/lldb/main/Debug/lldb-test-build.noindex/commands/target/metrics/TestTargetMetrics.test_metrics_modules/libload_a.dylib",
+ "symbolTableIndexTime": 2.7852999999999999e-05,
+ "symbolTableParseTime": 0.000110246,
+ "triple": "x86_64-apple-macosx11.0.0",
+ "uuid": "F0974CDE-309A-3837-9593-385ABDC93803"
+ },
+ ]
+ }
+
+ """
+ exe = self.getBuildArtifact("a.out")
+ target = self.createTestTarget(file_path=exe)
+ return_obj = lldb.SBCommandReturnObject()
+ self.ci.HandleCommand("target metrics --modules", return_obj, False)
+ output = return_obj.GetOutput()
+ metrics = json.loads(output)
+ keys_exist = [
+ 'targetCreateTime',
+ 'totalBreakpointResolveTime',
+ 'totalDebugInfoIndexTime',
+ 'totalDebugInfoParseTime',
+ 'totalDebugInfoSize',
+ 'totalSymbolTableIndexTime',
+ 'totalSymbolTableParseTime',
+ 'modules',
+ ]
+ keys_missing = [
+ 'breakpoints',
+ 'settings'
+ ]
+ self.verify_key_presence(metrics, '"metrics"', keys_exist, keys_missing)
+ exe_module = self.find_module_in_metrics(exe, metrics)
+ module_keys = [
+ 'debugInfoIndexTime',
+ 'debugInfoParseTime',
+ 'debugInfoSize',
+ 'path',
+ 'symbolTableIndexTime',
+ 'symbolTableParseTime',
+ 'triple',
+ 'uuid',
+ ]
+ self.assertNotEqual(exe_module, None)
+ self.verify_key_presence(exe_module, 'module dict for "%s"' % (exe),
+ module_keys)
+ shlib_path = self.getShlibBuildArtifact('load_a')
+ shlib_module = self.find_module_in_metrics(shlib_path, metrics)
+ self.assertNotEqual(shlib_module, None)
+ self.verify_key_presence(shlib_module, 'module dict for "%s"' % (exe),
+ module_keys)
+
+ def test_metrics_breakpoints(self):
+ """Test behavior of "target metrics --breakpoints" command.
+
+ Output expected to be something like:
+
+ (lldb) target metrics --breakpoints
+ {
+ "targetCreateTime": 0.26566899599999999,
+ "totalBreakpointResolveTime": 0.0031409419999999999,
+ "totalDebugInfoIndexTime": 0,
+ "totalDebugInfoParseTime": 0.00037288300000000001,
+ "totalDebugInfoSize": 2193,
+ "totalSymbolTableIndexTime": 0.056834488000000009,
+ "totalSymbolTableParseTime": 0.093979421999999979
+ "breakpoints": [
+ {
+ "details": {...}.
+ "id": 1,
+ "resolveTime": 0.0029114369999999998
+ },
+ ]
+ }
+
+ """
+ target = self.createTestTarget()
+ self.runCmd("b main.cpp:7")
+ self.runCmd("b a_function")
+ return_obj = lldb.SBCommandReturnObject()
+ self.ci.HandleCommand("target metrics --breakpoints", return_obj, False)
+ metrics_json = return_obj.GetOutput()
+ metrics = json.loads(metrics_json)
+ keys_exist = [
+ 'targetCreateTime',
+ 'totalBreakpointResolveTime',
+ 'totalDebugInfoIndexTime',
+ 'totalDebugInfoParseTime',
+ 'totalDebugInfoSize',
+ 'totalSymbolTableIndexTime',
+ 'totalSymbolTableParseTime',
+ 'breakpoints',
+ ]
+ keys_missing = [
+ 'modules',
+ 'settings'
+ ]
+ self.verify_key_presence(metrics, '"metrics"', keys_exist, keys_missing)
+ self.assertGreater(metrics['totalBreakpointResolveTime'], 0.0)
+ self.assertGreater(metrics['totalDebugInfoParseTime'], 0.0)
+ breakpoints = metrics['breakpoints']
+ bp_keys_exist = [
+ 'details',
+ 'id',
+ 'resolveTime'
+ ]
+ bk_keys_missing = [
+ 'locations'
+ ]
+ for breakpoint in breakpoints:
+ self.verify_key_presence(breakpoint, 'metrics["breakpoints"]',
+ bp_keys_exist, bk_keys_missing)
+
+ def test_metrics_locations(self):
+ """Test behavior of "target metrics --locations" command.
+
+ Output expected to be something like:
+
+ (lldb) target metrics --locations
+ {
+ "targetCreateTime": 0.26566899599999999,
+ "totalBreakpointResolveTime": 0.0031409419999999999,
+ "totalDebugInfoIndexTime": 0,
+ "totalDebugInfoParseTime": 0.00037288300000000001,
+ "totalDebugInfoSize": 2193,
+ "totalSymbolTableIndexTime": 0.056834488000000009,
+ "totalSymbolTableParseTime": 0.093979421999999979
+ "breakpoints": [
+ {
+ "details": {...}.
+ "id": 1,
+ "locations": [...],
+ "resolveTime": 0.0029114369999999998
+ },
+ ]
+ }
+
+ """
+ target = self.createTestTarget()
+ self.runCmd("b main.cpp:7")
+ self.runCmd("b a_function")
+ return_obj = lldb.SBCommandReturnObject()
+ self.ci.HandleCommand("target metrics --locations", return_obj, False)
+ metrics_json = return_obj.GetOutput()
+ metrics = json.loads(metrics_json)
+ keys_exist = [
+ 'targetCreateTime',
+ 'totalBreakpointResolveTime',
+ 'totalDebugInfoIndexTime',
+ 'totalDebugInfoParseTime',
+ 'totalDebugInfoSize',
+ 'totalSymbolTableIndexTime',
+ 'totalSymbolTableParseTime',
+ 'breakpoints',
+ ]
+ keys_missing = [
+ 'modules',
+ 'settings'
+ ]
+ self.verify_key_presence(metrics, '"metrics"', keys_exist, keys_missing)
+ self.assertGreater(metrics['totalBreakpointResolveTime'], 0.0)
+ self.assertGreater(metrics['totalDebugInfoParseTime'], 0.0)
+ breakpoints = metrics['breakpoints']
+ bp_keys_exist = [
+ 'details',
+ 'id',
+ 'resolveTime',
+ 'locations'
+ ]
+ for breakpoint in breakpoints:
+ self.verify_key_presence(breakpoint, 'metrics["breakpoints"]',
+ bp_keys_exist, None)
+
+ def test_metrics_settings(self):
+ """Test behavior of "target metrics --settings" command.
+
+ Output expected to be something like:
+
+ (lldb) target metrics --settings
+ {
+ "targetCreateTime": 0.26566899599999999,
+ "totalBreakpointResolveTime": 0.0031409419999999999,
+ "totalDebugInfoIndexTime": 0,
+ "totalDebugInfoParseTime": 0.00037288300000000001,
+ "totalDebugInfoSize": 2193,
+ "totalSymbolTableIndexTime": 0.056834488000000009,
+ "totalSymbolTableParseTime": 0.093979421999999979
+ "settings": {
+ "target.arg0": "/.../commands/target/metrics/TestTargetMetrics.test_metrics_settings/a.out",
+ "target.clang-module-search-paths": [],
+ "target.debug-file-search-paths": [],
+ "target.exec-search-paths": [
+ "/.../commands/target/metrics/TestTargetMetrics.test_metrics_settings"
+ ],
+ "target.inline-breakpoint-strategy": "always",
+ "target.preload-symbols": true,
+ "target.run-args": [],
+ "target.skip-prologue": true,
+ "target.source-map": []
+ },
+ }
+
+ """
+ target = self.createTestTarget()
+ return_obj = lldb.SBCommandReturnObject()
+ self.ci.HandleCommand("target metrics --settings", return_obj, False)
+ metrics_json = return_obj.GetOutput()
+ f = open('/tmp/a', 'w')
+ f.write(metrics_json)
+ metrics = json.loads(metrics_json)
+ keys_exist = [
+ 'targetCreateTime',
+ 'totalBreakpointResolveTime',
+ 'totalDebugInfoIndexTime',
+ 'totalDebugInfoParseTime',
+ 'totalDebugInfoSize',
+ 'totalSymbolTableIndexTime',
+ 'totalSymbolTableParseTime',
+ 'settings'
+ ]
+ keys_missing = [
+ 'breakpoints',
+ 'modules',
+ ]
+ self.verify_key_presence(metrics, '"metrics"', keys_exist, keys_missing)
+ settings = metrics['settings']
+ settings_keys_exist = [
+ "target.arg0",
+ "target.clang-module-search-paths",
+ "target.debug-file-search-paths",
+ "target.exec-search-paths",
+ "target.inline-breakpoint-strategy",
+ "target.preload-symbols",
+ "target.run-args",
+ "target.skip-prologue",
+ "target.source-map",
+ ]
+ self.verify_key_presence(settings, 'metrics["settings"]', settings_keys_exist)
Index: lldb/test/API/commands/target/metrics/Makefile
===================================================================
--- /dev/null
+++ lldb/test/API/commands/target/metrics/Makefile
@@ -0,0 +1,10 @@
+LD_EXTRAS := -L. -lload_a
+CXX_SOURCES := main.cpp
+
+a.out: libload_a
+
+include Makefile.rules
+
+libload_a:
+ $(MAKE) -f $(MAKEFILE_RULES) \
+ DYLIB_ONLY=YES DYLIB_NAME=load_a DYLIB_CXX_SOURCES=a.cpp
Index: lldb/source/Target/Target.cpp
===================================================================
--- lldb/source/Target/Target.cpp
+++ lldb/source/Target/Target.cpp
@@ -1400,6 +1400,7 @@
ClearModules(false);
if (executable_sp) {
+ ElapsedTime elapsed(m_metrics.GetCreateTime());
LLDB_SCOPED_TIMERF("Target::SetExecutableModule (executable = '%s')",
executable_sp->GetFileSpec().GetPath().c_str());
@@ -2908,6 +2909,7 @@
void Target::ClearAllLoadedSections() { m_section_load_history.Clear(); }
Status Target::Launch(ProcessLaunchInfo &launch_info, Stream *stream) {
+ m_metrics.SetLaunchOrAttachTime();
Status error;
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_TARGET));
@@ -3119,6 +3121,7 @@
}
Status Target::Attach(ProcessAttachInfo &attach_info, Stream *stream) {
+ m_metrics.SetLaunchOrAttachTime();
auto state = eStateInvalid;
auto process_sp = GetProcessSP();
if (process_sp) {
@@ -4464,3 +4467,9 @@
else
return m_mutex;
}
+
+/// Get metrics associated with this target in JSON format.
+llvm::json::Value Target::GatherMetrics(const MetricDumpOptions &options) {
+ m_metrics.CollectModuleMetrics(*this);
+ return m_metrics.ToJSON(*this, options);
+}
Index: lldb/source/Target/Process.cpp
===================================================================
--- lldb/source/Target/Process.cpp
+++ lldb/source/Target/Process.cpp
@@ -1297,6 +1297,17 @@
}
void Process::SetPublicState(StateType new_state, bool restarted) {
+ const bool new_state_is_stopped = StateIsStoppedState(new_state, false);
+ if (new_state_is_stopped) {
+ // This will only set the time if the public stop time has no value, so
+ // it is ok to call this multiple times. With a public stop we can't look
+ // at the stop ID because many private stops might have happened, so we
+ // can't check for a stop ID of zero. This allows the "statistics" command
+ // to dump the time it takes to reach somewhere in your code, like a
+ // breakpoint you set.
+ GetTarget().GetMetrics().SetFirstPublicStopTime();
+ }
+
Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STATE |
LIBLLDB_LOG_PROCESS));
LLDB_LOGF(log, "Process::SetPublicState (state = %s, restarted = %i)",
@@ -1315,7 +1326,6 @@
m_public_run_lock.SetStopped();
} else {
const bool old_state_is_stopped = StateIsStoppedState(old_state, false);
- const bool new_state_is_stopped = StateIsStoppedState(new_state, false);
if ((old_state_is_stopped != new_state_is_stopped)) {
if (new_state_is_stopped && !restarted) {
LLDB_LOGF(log, "Process::SetPublicState (%s) -- unlocking run lock",
@@ -1446,7 +1456,9 @@
// before we get here.
m_thread_list.DidStop();
- m_mod_id.BumpStopID();
+ if (m_mod_id.BumpStopID() == 0)
+ GetTarget().GetMetrics().SetFirstPrivateStopTime();
+
if (!m_mod_id.IsLastResumeForUserExpression())
m_mod_id.SetStopEventForLastNaturalStopID(event_sp);
m_memory_cache.Clear();
Index: lldb/source/Target/PathMappingList.cpp
===================================================================
--- lldb/source/Target/PathMappingList.cpp
+++ lldb/source/Target/PathMappingList.cpp
@@ -297,3 +297,14 @@
}
return UINT32_MAX;
}
+
+llvm::json::Value PathMappingList::ToJSON() const {
+ llvm::json::Array mappings;
+ for (const auto &pair : m_pairs) {
+ llvm::json::Array mapping;
+ mapping.emplace_back(pair.first.GetCString());
+ mapping.emplace_back(pair.second.GetCString());
+ mappings.emplace_back(std::move(mapping));
+ }
+ return llvm::json::Value(std::move(mappings));
+}
Index: lldb/source/Target/Metrics.cpp
===================================================================
--- /dev/null
+++ lldb/source/Target/Metrics.cpp
@@ -0,0 +1,189 @@
+//===-- Metrics.cpp -------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/Metrics.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Target/Target.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace llvm;
+
+static void EmplaceSafeString(llvm::json::Object &obj, llvm::StringRef key,
+ const std::string &str) {
+ if (str.empty())
+ return;
+ if (LLVM_LIKELY(llvm::json::isUTF8(str)))
+ obj.try_emplace(key, str);
+ else
+ obj.try_emplace(key, llvm::json::fixUTF8(str));
+}
+
+void TargetMetrics::CollectModuleMetrics(Target &target) {
+ m_module_metrics.clear();
+ for (ModuleSP module_sp : target.GetImages().Modules()) {
+ ModuleMetrics module_metrics;
+ module_metrics.path = module_sp->GetFileSpec().GetPath();
+ module_metrics.uuid = module_sp->GetUUID().GetAsString();
+ module_metrics.triple = module_sp->GetArchitecture().GetTriple().str();
+ SymbolFile *sym_file = module_sp->GetSymbolFile();
+ if (sym_file) {
+ module_metrics.debug_index_time =
+ sym_file->GetDebugInfoIndexTime().count();
+ module_metrics.debug_parse_time =
+ sym_file->GetDebugInfoParseTime().count();
+ }
+ module_metrics.symtab_parse_time = module_sp->GetSymtabParseTime().count();
+ module_metrics.symtab_names_time = module_sp->GetSymtabNamesTime().count();
+ module_metrics.debug_info_size = module_sp->GetDebugInfoSize();
+ m_module_metrics.push_back(std::move(module_metrics));
+ }
+}
+
+static double elapsed(const std::chrono::steady_clock::time_point &start,
+ const std::chrono::steady_clock::time_point &end) {
+ std::chrono::duration<double> elapsed =
+ end.time_since_epoch() - start.time_since_epoch();
+ return elapsed.count();
+}
+
+json::Value TargetMetrics::ToJSON(Target &target,
+ const MetricDumpOptions &options) {
+ CollectModuleMetrics(target);
+
+ uint64_t debug_info_size = 0;
+ double debug_parse_time = 0.0;
+ double debug_index_time = 0.0;
+ double symtab_parse_time = 0.0;
+ double symtab_names_time = 0.0;
+
+ json::Array modules;
+ for (const auto &module_metrics : m_module_metrics) {
+ modules.push_back(module_metrics.ToJSON());
+ debug_info_size += module_metrics.debug_info_size;
+ debug_parse_time += module_metrics.debug_parse_time;
+ debug_index_time += module_metrics.debug_index_time;
+ symtab_parse_time += module_metrics.symtab_parse_time;
+ symtab_names_time += module_metrics.symtab_names_time;
+ }
+
+ json::Array breakpoints_array;
+ std::unique_lock<std::recursive_mutex> lock;
+ target.GetBreakpointList().GetListMutex(lock);
+
+ const BreakpointList &breakpoints = target.GetBreakpointList();
+ size_t num_breakpoints = breakpoints.GetSize();
+ double totalBreakpointResolveTime = 0;
+ for (size_t i = 0; i < num_breakpoints; i++) {
+ Breakpoint *bp = breakpoints.GetBreakpointAtIndex(i).get();
+ if (options.dump_breakpoints || options.dump_breakpoint_locations)
+ breakpoints_array.push_back(bp->GetMetrics(options));
+ totalBreakpointResolveTime += bp->GetResolveTime().count();
+ }
+
+ // Include settings that can change how executables, sources and breakpoints
+ // can be resolved.
+ json::Object settings;
+ if (options.dump_settings) {
+ settings.try_emplace("target.arg0", target.GetArg0());
+ Args target_args;
+ json::Array run_args;
+ target.GetRunArguments(target_args);
+ for (const auto &arg : target_args.entries())
+ run_args.emplace_back(arg.c_str());
+ settings.try_emplace("target.run-args", std::move(run_args));
+ settings.try_emplace("target.source-map",
+ target.GetSourcePathMap().ToJSON());
+ settings.try_emplace("target.exec-search-paths",
+ target.GetExecutableSearchPaths().ToJSON());
+ settings.try_emplace("target.debug-file-search-paths",
+ target.GetDebugFileSearchPaths().ToJSON());
+ settings.try_emplace("target.clang-module-search-paths",
+ target.GetClangModuleSearchPaths().ToJSON());
+ settings.try_emplace("target.preload-symbols", target.GetPreloadSymbols());
+ const char *strategy = nullptr;
+ switch (target.GetInlineStrategy()) {
+ case eInlineBreakpointsNever:
+ strategy = "never";
+ break;
+ case eInlineBreakpointsHeaders:
+ strategy = "headers";
+ break;
+ case eInlineBreakpointsAlways:
+ strategy = "always";
+ break;
+ }
+ settings.try_emplace("target.inline-breakpoint-strategy", strategy);
+ settings.try_emplace("target.skip-prologue", target.GetSkipPrologue());
+ }
+
+ json::Object target_metrics_json{
+ {"totalDebugInfoSize", (int64_t)debug_info_size},
+ {"totalDebugInfoParseTime", debug_parse_time},
+ {"totalDebugInfoIndexTime", debug_index_time},
+ {"totalSymbolTableParseTime", symtab_parse_time},
+ {"totalSymbolTableIndexTime", symtab_names_time},
+ {"totalBreakpointResolveTime", totalBreakpointResolveTime},
+ };
+ if (options.dump_breakpoints || options.dump_breakpoint_locations)
+ target_metrics_json.try_emplace("breakpoints",
+ std::move(breakpoints_array));
+ if (options.dump_modules)
+ target_metrics_json.try_emplace("modules", std::move(modules));
+ if (options.dump_settings)
+ target_metrics_json.try_emplace("settings", std::move(settings));
+ if (launch_or_attach_time.hasValue() && first_private_stop_time.hasValue()) {
+ double elapsed_time =
+ elapsed(*launch_or_attach_time, *first_private_stop_time);
+ target_metrics_json.try_emplace("launchOrAttachTime", elapsed_time);
+ }
+ if (launch_or_attach_time.hasValue() && first_public_stop_time.hasValue()) {
+ double elapsed_time =
+ elapsed(*launch_or_attach_time, *first_public_stop_time);
+ target_metrics_json.try_emplace("firstStopTime", elapsed_time);
+ }
+ target_metrics_json.try_emplace("targetCreateTime", create_time.count());
+
+ return json::Value(std::move(target_metrics_json));
+}
+
+void TargetMetrics::SetLaunchOrAttachTime() {
+ launch_or_attach_time = std::chrono::steady_clock::now();
+ first_private_stop_time = llvm::None;
+}
+
+void TargetMetrics::SetFirstPrivateStopTime() {
+ // Launching and attaching has many paths depending on if synchronous mode
+ // was used or if we are stopping at the entry point or not. Only set the
+ // first stop time if it hasn't already been set.
+ if (!first_private_stop_time.hasValue())
+ first_private_stop_time = std::chrono::steady_clock::now();
+}
+
+void TargetMetrics::SetFirstPublicStopTime() {
+ // Launching and attaching has many paths depending on if synchronous mode
+ // was used or if we are stopping at the entry point or not. Only set the
+ // first stop time if it hasn't already been set.
+ if (!first_public_stop_time.hasValue())
+ first_public_stop_time = std::chrono::steady_clock::now();
+}
+
+json::Value ModuleMetrics::ToJSON() const {
+ json::Object module_metrics;
+ EmplaceSafeString(module_metrics, "path", path);
+ EmplaceSafeString(module_metrics, "uuid", uuid);
+ EmplaceSafeString(module_metrics, "triple", triple);
+ module_metrics.try_emplace("debugInfoParseTime", debug_parse_time);
+ module_metrics.try_emplace("debugInfoIndexTime", debug_index_time);
+ module_metrics.try_emplace("symbolTableParseTime", symtab_parse_time);
+ module_metrics.try_emplace("symbolTableIndexTime", symtab_names_time);
+ module_metrics.try_emplace("debugInfoSize", (int64_t)debug_info_size);
+ return module_metrics;
+}
Index: lldb/source/Target/CMakeLists.txt
===================================================================
--- lldb/source/Target/CMakeLists.txt
+++ lldb/source/Target/CMakeLists.txt
@@ -19,6 +19,7 @@
Memory.cpp
MemoryHistory.cpp
MemoryRegionInfo.cpp
+ Metrics.cpp
ModuleCache.cpp
OperatingSystem.cpp
PathMappingList.cpp
Index: lldb/source/Symbol/Symtab.cpp
===================================================================
--- lldb/source/Symbol/Symtab.cpp
+++ lldb/source/Symbol/Symtab.cpp
@@ -265,6 +265,7 @@
// Protected function, no need to lock mutex...
if (!m_name_indexes_computed) {
m_name_indexes_computed = true;
+ ElapsedTime elapsed(m_objfile->GetModule()->GetSymtabNamesTime());
LLDB_SCOPED_TIMER();
// Collect all loaded language plugins.
Index: lldb/source/Symbol/SymbolContext.cpp
===================================================================
--- lldb/source/Symbol/SymbolContext.cpp
+++ lldb/source/Symbol/SymbolContext.cpp
@@ -916,6 +916,26 @@
return nullptr; // no error; we just didn't find anything
}
+llvm::json::Value SymbolContext::ToJSON() const {
+ llvm::json::Object json;
+ if (module_sp)
+ json.try_emplace("module", module_sp->GetUUID().GetAsString());
+ if (comp_unit)
+ json.try_emplace("compUnit", comp_unit->GetPrimaryFile().GetPath());
+ if (function && function->GetName())
+ json.try_emplace("function", function->GetName().AsCString());
+ if (line_entry.file)
+ json.try_emplace("sourceFile", line_entry.file.GetPath());
+ if (line_entry.file != line_entry.original_file && line_entry.original_file)
+ json.try_emplace("origSourceFile", line_entry.original_file.GetPath());
+ json.try_emplace("sourceLine", (int64_t)line_entry.line);
+ if (line_entry.column)
+ json.try_emplace("sourceColumn", (int64_t)line_entry.column);
+ if (symbol)
+ json.try_emplace("symbol", symbol->GetName().AsCString("<unknown>"));
+ return llvm::json::Value(std::move(json));
+}
+
//
// SymbolContextSpecifier
//
Index: lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.h
===================================================================
--- lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.h
+++ lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.h
@@ -45,6 +45,8 @@
uint32_t CalculateAbilities() override;
+ uint64_t GetDebugInfoSize() override;
+
// Compile Unit function calls
lldb::LanguageType
ParseLanguage(lldb_private::CompileUnit &comp_unit) override;
Index: lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp
===================================================================
--- lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp
+++ lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp
@@ -104,6 +104,13 @@
return abilities;
}
+uint64_t SymbolFileSymtab::GetDebugInfoSize() {
+ // This call isn't expected to look at symbols and figure out which ones are
+ // debug info or not, it is just to report sections full of debug info, so
+ // we should return 0 here.
+ return 0;
+}
+
uint32_t SymbolFileSymtab::CalculateNumCompileUnits() {
// If we don't have any source file symbols we will just have one compile
// unit for the entire object file
Index: lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.h
===================================================================
--- lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.h
+++ lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.h
@@ -55,6 +55,8 @@
uint32_t CalculateAbilities() override;
+ uint64_t GetDebugInfoSize() override;
+
void InitializeObject() override;
// Compile Unit function calls
Index: lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp
===================================================================
--- lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp
+++ lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp
@@ -2062,3 +2062,8 @@
return 0;
}
+
+uint64_t SymbolFilePDB::GetDebugInfoSize() {
+ // TODO: implement this
+ return 0;
+}
Index: lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
===================================================================
--- lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
+++ lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
@@ -73,6 +73,8 @@
uint32_t CalculateAbilities() override;
+ uint64_t GetDebugInfoSize() override;
+
void InitializeObject() override;
// Compile Unit function calls
Index: lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
===================================================================
--- lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
+++ lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
@@ -296,6 +296,11 @@
return abilities;
}
+uint64_t SymbolFileNativePDB::GetDebugInfoSize() {
+ // PDB files are a separate file that contains all debug info.
+ return m_index->pdb().getFileSize();
+}
+
void SymbolFileNativePDB::InitializeObject() {
m_obj_load_address = m_objfile_sp->GetModule()
->GetObjectFile()
Index: lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
===================================================================
--- lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
+++ lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
@@ -10,6 +10,7 @@
#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_SYMBOLFILEDWARFDEBUGMAP_H
#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Utility/ElapsedTime.h"
#include "lldb/Utility/RangeMap.h"
#include "llvm/Support/Chrono.h"
#include <bitset>
@@ -142,6 +143,10 @@
// PluginInterface protocol
lldb_private::ConstString GetPluginName() override;
+ uint64_t GetDebugInfoSize() override;
+ lldb_private::ElapsedTime::Duration GetDebugInfoParseTime() override;
+ lldb_private::ElapsedTime::Duration GetDebugInfoIndexTime() override;
+
protected:
enum { kHaveInitializedOSOs = (1 << 0), kNumFlags };
Index: lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
===================================================================
--- lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
+++ lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
@@ -1441,3 +1441,54 @@
}
return num_line_entries_added;
}
+
+uint64_t SymbolFileDWARFDebugMap::GetDebugInfoSize() {
+ uint64_t debug_info_size = 0;
+ ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool {
+ ObjectFile *oso_objfile = oso_dwarf->GetObjectFile();
+ if (!oso_objfile)
+ return false; // Keep iterating
+ ModuleSP module_sp = oso_objfile->GetModule();
+ if (!module_sp)
+ return false; // Keep iterating
+ SectionList *section_list = module_sp->GetSectionList();
+ if (section_list)
+ debug_info_size += section_list->GetDebugInfoSize();
+ return false; // Keep iterating
+ });
+ return debug_info_size;
+}
+
+ElapsedTime::Duration SymbolFileDWARFDebugMap::GetDebugInfoParseTime() {
+ ElapsedTime::Duration elapsed(0.0);
+ ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool {
+ ObjectFile *oso_objfile = oso_dwarf->GetObjectFile();
+ if (oso_objfile) {
+ ModuleSP module_sp = oso_objfile->GetModule();
+ if (module_sp) {
+ SymbolFile *symfile = module_sp->GetSymbolFile();
+ if (symfile)
+ elapsed += symfile->GetDebugInfoParseTime();
+ }
+ }
+ return false; // Keep iterating
+ });
+ return elapsed;
+}
+
+ElapsedTime::Duration SymbolFileDWARFDebugMap::GetDebugInfoIndexTime() {
+ ElapsedTime::Duration elapsed(0.0);
+ ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool {
+ ObjectFile *oso_objfile = oso_dwarf->GetObjectFile();
+ if (oso_objfile) {
+ ModuleSP module_sp = oso_objfile->GetModule();
+ if (module_sp) {
+ SymbolFile *symfile = module_sp->GetSymbolFile();
+ if (symfile)
+ elapsed += symfile->GetDebugInfoIndexTime();
+ }
+ }
+ return false; // Keep iterating
+ });
+ return elapsed;
+}
Index: lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
===================================================================
--- lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
+++ lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
@@ -25,6 +25,7 @@
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Symbol/SymbolFile.h"
#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/ElapsedTime.h"
#include "lldb/Utility/Flags.h"
#include "lldb/Utility/RangeMap.h"
#include "lldb/lldb-private.h"
@@ -318,6 +319,16 @@
/// Same as GetLanguage() but reports all C++ versions as C++ (no version).
static lldb::LanguageType GetLanguageFamily(DWARFUnit &unit);
+ uint64_t GetDebugInfoSize() override;
+ lldb_private::ElapsedTime::Duration GetDebugInfoParseTime() override {
+ return m_parse_time;
+ }
+ lldb_private::ElapsedTime::Duration GetDebugInfoIndexTime() override;
+
+ lldb_private::ElapsedTime::Duration &GetDebugInfoParseTimeRef() {
+ return m_parse_time;
+ }
+
protected:
typedef llvm::DenseMap<const DWARFDebugInfoEntry *, lldb_private::Type *>
DIEToTypePtr;
@@ -519,6 +530,7 @@
llvm::DenseMap<dw_offset_t, lldb_private::FileSpecList>
m_type_unit_support_files;
std::vector<uint32_t> m_lldb_cu_to_dwarf_unit;
+ lldb_private::ElapsedTime::Duration m_parse_time{0.0};
};
#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_SYMBOLFILEDWARF_H
Index: lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
===================================================================
--- lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -965,6 +965,7 @@
if (offset == DW_INVALID_OFFSET)
return false;
+ ElapsedTime elapsed(m_parse_time);
llvm::DWARFDebugLine::Prologue prologue;
if (!ParseLLVMLineTablePrologue(m_context, prologue, offset,
dwarf_cu.GetOffset()))
@@ -1013,6 +1014,7 @@
"SymbolFileDWARF::GetTypeUnitSupportFiles failed to parse "
"the line table prologue");
};
+ ElapsedTime elapsed(m_parse_time);
llvm::Error error = prologue.parse(data, &line_table_offset, report, ctx);
if (error) {
report(std::move(error));
@@ -1099,6 +1101,7 @@
if (offset == DW_INVALID_OFFSET)
return false;
+ ElapsedTime elapsed(m_parse_time);
llvm::DWARFDebugLine line;
const llvm::DWARFDebugLine::LineTable *line_table =
ParseLLVMLineTable(m_context, line, offset, dwarf_cu->GetOffset());
@@ -1147,6 +1150,7 @@
if (iter != m_debug_macros_map.end())
return iter->second;
+ ElapsedTime elapsed(m_parse_time);
const DWARFDataExtractor &debug_macro_data = m_context.getOrLoadMacroData();
if (debug_macro_data.GetByteSize() == 0)
return DebugMacrosSP();
@@ -3937,3 +3941,19 @@
lang = DW_LANG_C_plus_plus;
return LanguageTypeFromDWARF(lang);
}
+
+uint64_t SymbolFileDWARF::GetDebugInfoSize() {
+ ModuleSP module_sp(m_objfile_sp->GetModule());
+ if (!module_sp)
+ return 0;
+ const SectionList *section_list = module_sp->GetSectionList();
+ if (section_list)
+ return section_list->GetDebugInfoSize();
+ return 0;
+}
+
+ElapsedTime::Duration SymbolFileDWARF::GetDebugInfoIndexTime() {
+ if (m_index)
+ return m_index->GetIndexTime();
+ return ElapsedTime::Duration();
+}
Index: lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp
===================================================================
--- lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp
+++ lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp
@@ -30,6 +30,7 @@
SymbolFileDWARF &main_dwarf = *m_dwarf;
m_dwarf = nullptr;
+ ElapsedTime elapsed(m_index_time);
LLDB_SCOPED_TIMERF("%p", static_cast<void *>(&main_dwarf));
DWARFDebugInfo &main_info = main_dwarf.DebugInfo();
Index: lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp
===================================================================
--- lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp
+++ lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp
@@ -49,6 +49,7 @@
if (m_first_die)
return; // Already parsed
+ ElapsedTime elapsed(m_dwarf.GetDebugInfoParseTimeRef());
LLDB_SCOPED_TIMERF("%8.8x: DWARFUnit::ExtractUnitDIENoDwoIfNeeded()",
GetOffset());
@@ -195,7 +196,7 @@
// held R/W and m_die_array must be empty.
void DWARFUnit::ExtractDIEsRWLocked() {
llvm::sys::ScopedWriter first_die_lock(m_first_die_mutex);
-
+ ElapsedTime elapsed(m_dwarf.GetDebugInfoParseTimeRef());
LLDB_SCOPED_TIMERF("%8.8x: DWARFUnit::ExtractDIEsIfNeeded()", GetOffset());
// Set the offset to that of the first DIE and calculate the start of the
Index: lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h
===================================================================
--- lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h
+++ lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h
@@ -12,6 +12,7 @@
#include "Plugins/SymbolFile/DWARF/DIERef.h"
#include "Plugins/SymbolFile/DWARF/DWARFDIE.h"
#include "Plugins/SymbolFile/DWARF/DWARFFormValue.h"
+#include "lldb/Utility/ElapsedTime.h"
class DWARFDeclContext;
class DWARFDIE;
@@ -62,8 +63,11 @@
virtual void Dump(Stream &s) = 0;
+ ElapsedTime::Duration GetIndexTime() { return m_index_time; }
+
protected:
Module &m_module;
+ ElapsedTime::Duration m_index_time{0.0};
/// Helper function implementing common logic for processing function dies. If
/// the function given by "ref" matches search criteria given by
Index: lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h
===================================================================
--- lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h
+++ lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h
@@ -55,6 +55,8 @@
uint32_t CalculateAbilities() override;
+ uint64_t GetDebugInfoSize() override;
+
void InitializeObject() override {}
// Compile Unit function calls
Index: lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp
===================================================================
--- lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp
+++ lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp
@@ -188,6 +188,11 @@
return CompileUnits | Functions | LineTables;
}
+uint64_t SymbolFileBreakpad::GetDebugInfoSize() {
+ // Breakpad files are all debug info.
+ return m_objfile_sp->GetByteSize();
+}
+
uint32_t SymbolFileBreakpad::CalculateNumCompileUnits() {
ParseCUData();
return m_cu_data->GetSize();
Index: lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp
===================================================================
--- lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp
+++ lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp
@@ -600,6 +600,7 @@
if (module_sp) {
std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
if (m_symtab_up == nullptr) {
+ ElapsedTime elapsed(module_sp->GetSymtabParseTime());
SectionList *sect_list = GetSectionList();
m_symtab_up = std::make_unique<Symtab>(this);
std::lock_guard<std::recursive_mutex> guard(m_symtab_up->GetMutex());
Index: lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
===================================================================
--- lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
+++ lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
@@ -1307,6 +1307,10 @@
if (module_sp) {
std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
if (m_symtab_up == nullptr) {
+ // Aggregate the symbol table parse time into the module in case we have
+ // one object file for the module and one for the debug information, both
+ // of which can have symbol tables that might get parsed.
+ ElapsedTime elapsed(module_sp->GetSymtabParseTime());
m_symtab_up = std::make_unique<Symtab>(this);
std::lock_guard<std::recursive_mutex> symtab_guard(
m_symtab_up->GetMutex());
@@ -2229,7 +2233,6 @@
ModuleSP module_sp(GetModule());
if (!module_sp)
return 0;
-
Progress progress(llvm::formatv("Parsing symbol table for {0}",
m_file.GetFilename().AsCString("<Unknown>")));
@@ -2479,8 +2482,8 @@
// We shouldn't have exports data from both the LC_DYLD_INFO command
// AND the LC_DYLD_EXPORTS_TRIE command in the same binary:
- lldbassert(!((dyld_info.export_size > 0)
- && (exports_trie_load_command.datasize > 0)));
+ lldbassert(!((dyld_info.export_size > 0) &&
+ (exports_trie_load_command.datasize > 0)));
if (dyld_info.export_size > 0) {
dyld_trie_data.SetData(m_data, dyld_info.export_off,
dyld_info.export_size);
Index: lldb/source/Plugins/ObjectFile/JIT/ObjectFileJIT.cpp
===================================================================
--- lldb/source/Plugins/ObjectFile/JIT/ObjectFileJIT.cpp
+++ lldb/source/Plugins/ObjectFile/JIT/ObjectFileJIT.cpp
@@ -120,6 +120,7 @@
if (module_sp) {
std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
if (m_symtab_up == nullptr) {
+ ElapsedTime elapsed(module_sp->GetSymtabParseTime());
m_symtab_up = std::make_unique<Symtab>(this);
std::lock_guard<std::recursive_mutex> symtab_guard(
m_symtab_up->GetMutex());
Index: lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
===================================================================
--- lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
+++ lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
@@ -2707,9 +2707,6 @@
if (!module_sp)
return nullptr;
- Progress progress(llvm::formatv("Parsing symbol table for {0}",
- m_file.GetFilename().AsCString("<Unknown>")));
-
// We always want to use the main object file so we (hopefully) only have one
// cached copy of our symtab, dynamic sections, etc.
ObjectFile *module_obj_file = module_sp->GetObjectFile();
@@ -2717,6 +2714,10 @@
return module_obj_file->GetSymtab();
if (m_symtab_up == nullptr) {
+ Progress progress(
+ llvm::formatv("Parsing symbol table for {0}",
+ m_file.GetFilename().AsCString("<Unknown>")));
+ ElapsedTime elapsed(module_sp->GetSymtabParseTime());
SectionList *section_list = module_sp->GetSectionList();
if (!section_list)
return nullptr;
Index: lldb/source/Core/Section.cpp
===================================================================
--- lldb/source/Core/Section.cpp
+++ lldb/source/Core/Section.cpp
@@ -151,6 +151,75 @@
return "unknown";
}
+bool Section::ContainsOnlyDebugInfo() const {
+ switch (m_type) {
+ case eSectionTypeInvalid:
+ case eSectionTypeCode:
+ case eSectionTypeContainer:
+ case eSectionTypeData:
+ case eSectionTypeDataCString:
+ case eSectionTypeDataCStringPointers:
+ case eSectionTypeDataSymbolAddress:
+ case eSectionTypeData4:
+ case eSectionTypeData8:
+ case eSectionTypeData16:
+ case eSectionTypeDataPointers:
+ case eSectionTypeZeroFill:
+ case eSectionTypeDataObjCMessageRefs:
+ case eSectionTypeDataObjCCFStrings:
+ case eSectionTypeELFSymbolTable:
+ case eSectionTypeELFDynamicSymbols:
+ case eSectionTypeELFRelocationEntries:
+ case eSectionTypeELFDynamicLinkInfo:
+ case eSectionTypeEHFrame:
+ case eSectionTypeARMexidx:
+ case eSectionTypeARMextab:
+ case eSectionTypeCompactUnwind:
+ case eSectionTypeGoSymtab:
+ case eSectionTypeAbsoluteAddress:
+ case eSectionTypeOther:
+ return false;
+
+ case eSectionTypeDebug:
+ case eSectionTypeDWARFDebugAbbrev:
+ case eSectionTypeDWARFDebugAbbrevDwo:
+ case eSectionTypeDWARFDebugAddr:
+ case eSectionTypeDWARFDebugAranges:
+ case eSectionTypeDWARFDebugCuIndex:
+ case eSectionTypeDWARFDebugTuIndex:
+ case eSectionTypeDWARFDebugFrame:
+ case eSectionTypeDWARFDebugInfo:
+ case eSectionTypeDWARFDebugInfoDwo:
+ case eSectionTypeDWARFDebugLine:
+ case eSectionTypeDWARFDebugLineStr:
+ case eSectionTypeDWARFDebugLoc:
+ case eSectionTypeDWARFDebugLocDwo:
+ case eSectionTypeDWARFDebugLocLists:
+ case eSectionTypeDWARFDebugLocListsDwo:
+ case eSectionTypeDWARFDebugMacInfo:
+ case eSectionTypeDWARFDebugMacro:
+ case eSectionTypeDWARFDebugPubNames:
+ case eSectionTypeDWARFDebugPubTypes:
+ case eSectionTypeDWARFDebugRanges:
+ case eSectionTypeDWARFDebugRngLists:
+ case eSectionTypeDWARFDebugRngListsDwo:
+ case eSectionTypeDWARFDebugStr:
+ case eSectionTypeDWARFDebugStrDwo:
+ case eSectionTypeDWARFDebugStrOffsets:
+ case eSectionTypeDWARFDebugStrOffsetsDwo:
+ case eSectionTypeDWARFDebugTypes:
+ case eSectionTypeDWARFDebugTypesDwo:
+ case eSectionTypeDWARFDebugNames:
+ case eSectionTypeDWARFAppleNames:
+ case eSectionTypeDWARFAppleTypes:
+ case eSectionTypeDWARFAppleNamespaces:
+ case eSectionTypeDWARFAppleObjC:
+ case eSectionTypeDWARFGNUDebugAltLink:
+ return true;
+ }
+ return false;
+}
+
Section::Section(const ModuleSP &module_sp, ObjectFile *obj_file,
user_id_t sect_id, ConstString name,
SectionType sect_type, addr_t file_addr, addr_t byte_size,
@@ -599,3 +668,15 @@
}
return count;
}
+
+uint64_t SectionList::GetDebugInfoSize() const {
+ uint64_t debug_info_size = 0;
+ for (const auto §ion : m_sections) {
+ const SectionList &sub_sections = section->GetChildren();
+ if (sub_sections.GetSize() > 0)
+ debug_info_size += sub_sections.GetDebugInfoSize();
+ else if (section->ContainsOnlyDebugInfo())
+ debug_info_size += section->GetFileSize();
+ }
+ return debug_info_size;
+}
Index: lldb/source/Core/Module.cpp
===================================================================
--- lldb/source/Core/Module.cpp
+++ lldb/source/Core/Module.cpp
@@ -1659,3 +1659,10 @@
return false;
}
+
+uint64_t Module::GetDebugInfoSize() {
+ SymbolFile *symbols = GetSymbolFile();
+ if (symbols)
+ return symbols->GetDebugInfoSize();
+ return 0;
+}
Index: lldb/source/Core/FileSpecList.cpp
===================================================================
--- lldb/source/Core/FileSpecList.cpp
+++ lldb/source/Core/FileSpecList.cpp
@@ -119,3 +119,10 @@
FileSpecList &matches) {
return 0;
}
+
+llvm::json::Value FileSpecList::ToJSON() const {
+ llvm::json::Array files;
+ for (const auto &file : m_files)
+ files.emplace_back(file.GetPath());
+ return llvm::json::Value(std::move(files));
+}
Index: lldb/source/Commands/Options.td
===================================================================
--- lldb/source/Commands/Options.td
+++ lldb/source/Commands/Options.td
@@ -1290,3 +1290,16 @@
def trace_schema_verbose : Option<"verbose", "v">, Group<1>,
Desc<"Show verbose trace schema logging for debugging the plug-in.">;
}
+
+let Command = "target metrics" in {
+ def target_metrics_modules: Option<"modules", "m">, Group<1>,
+ Desc<"Include metrics for each module in the target.">;
+ def target_metrics_breakpoints: Option<"breakpoints", "b">, Group<1>,
+ Desc<"Include metrics for each breakpoint in the target.">;
+ def target_metrics_locations: Option<"locations", "l">, Group<1>,
+ Desc<"Include metrics for each breakpoint location in each breakpoint.">;
+ def target_metrics_settings: Option<"settings", "s">, Group<1>,
+ Desc<"Include important target settings in metrics.">;
+ def target_metrics_all: Option<"all", "a">, Group<1>,
+ Desc<"Include all metrics in the target.">;
+}
Index: lldb/source/Commands/CommandObjectTarget.cpp
===================================================================
--- lldb/source/Commands/CommandObjectTarget.cpp
+++ lldb/source/Commands/CommandObjectTarget.cpp
@@ -4990,6 +4990,77 @@
~CommandObjectMultiwordTargetStopHooks() override = default;
};
+#pragma mark CommandObjectTargetMetrics
+
+#define LLDB_OPTIONS_target_metrics
+#include "CommandOptions.inc"
+
+class CommandObjectTargetMetrics : public CommandObjectParsed {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() { OptionParsingStarting(nullptr); }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'a':
+ m_metric_options.SetAll();
+ break;
+ case 'b':
+ m_metric_options.dump_breakpoints = true;
+ break;
+ case 'l':
+ m_metric_options.dump_breakpoint_locations = true;
+ break;
+ case 'm':
+ m_metric_options.dump_modules = true;
+ break;
+ case 's':
+ m_metric_options.dump_settings = true;
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_metric_options = MetricDumpOptions();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_target_metrics_options);
+ }
+
+ MetricDumpOptions m_metric_options;
+ };
+
+ CommandObjectTargetMetrics(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "target metrics",
+ "Dump target metrics in JSON format", nullptr,
+ eCommandRequiresTarget),
+ m_options() {}
+
+ ~CommandObjectTargetMetrics() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target &target = m_exe_ctx.GetTargetRef();
+ result.AppendMessageWithFormatv(
+ "{0:2}", target.GatherMetrics(m_options.m_metric_options));
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+
+ CommandOptions m_options;
+};
+
#pragma mark CommandObjectMultiwordTarget
// CommandObjectMultiwordTarget
@@ -5005,6 +5076,8 @@
CommandObjectSP(new CommandObjectTargetDelete(interpreter)));
LoadSubCommand("list",
CommandObjectSP(new CommandObjectTargetList(interpreter)));
+ LoadSubCommand("metrics",
+ CommandObjectSP(new CommandObjectTargetMetrics(interpreter)));
LoadSubCommand("select",
CommandObjectSP(new CommandObjectTargetSelect(interpreter)));
LoadSubCommand("show-launch-environment",
Index: lldb/source/Breakpoint/BreakpointLocation.cpp
===================================================================
--- lldb/source/Breakpoint/BreakpointLocation.cpp
+++ lldb/source/Breakpoint/BreakpointLocation.cpp
@@ -667,3 +667,33 @@
m_is_indirect = swap_from->m_is_indirect;
m_user_expression_sp.reset();
}
+
+llvm::json::Value
+BreakpointLocation::GetMetrics(const MetricDumpOptions &options) {
+
+ llvm::json::Object loc_json;
+
+ const bool is_resolved = IsResolved();
+ const bool is_hardware = is_resolved && m_bp_site_sp->IsHardware();
+ loc_json.try_emplace("id", GetID());
+ loc_json.try_emplace("resolved", is_resolved);
+ loc_json.try_emplace("hardward", is_hardware);
+ loc_json.try_emplace("reexported", IsReExported());
+ loc_json.try_emplace("indirect", IsIndirect());
+ loc_json.try_emplace("enabled", IsEnabled());
+
+ if (m_address.IsSectionOffset()) {
+ SymbolContext sc;
+ m_address.CalculateSymbolContext(&sc);
+ loc_json.try_emplace("symbolContext", sc.ToJSON());
+ }
+
+ const addr_t load_addr = GetLoadAddress();
+ if (load_addr != LLDB_INVALID_ADDRESS)
+ loc_json.try_emplace("loadAddress", (int64_t)load_addr);
+ const addr_t file_addr = m_address.GetFileAddress();
+ if (file_addr != LLDB_INVALID_ADDRESS)
+ loc_json.try_emplace("fileAddress", (int64_t)file_addr);
+ loc_json.try_emplace("hitCount", (int64_t)GetHitCount());
+ return llvm::json::Value(std::move(loc_json));
+}
Index: lldb/source/Breakpoint/Breakpoint.cpp
===================================================================
--- lldb/source/Breakpoint/Breakpoint.cpp
+++ lldb/source/Breakpoint/Breakpoint.cpp
@@ -19,16 +19,17 @@
#include "lldb/Core/ModuleList.h"
#include "lldb/Core/SearchFilter.h"
#include "lldb/Core/Section.h"
-#include "lldb/Target/SectionLoadList.h"
#include "lldb/Symbol/CompileUnit.h"
#include "lldb/Symbol/Function.h"
#include "lldb/Symbol/Symbol.h"
#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/SectionLoadList.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/ThreadSpec.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/Stream.h"
#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/Timer.h"
#include <memory>
@@ -77,6 +78,44 @@
return bp;
}
+json::Value Breakpoint::GetMetrics(const MetricDumpOptions &options) {
+ json::Object bp;
+ bp.try_emplace("id", GetID());
+ if (!m_name_list.empty()) {
+ json::Array names;
+ for (const auto &name : m_name_list)
+ names.emplace_back(name);
+ bp.try_emplace("names", std::move(names));
+ }
+ if (options.dump_breakpoint_locations) {
+ json::Array locations;
+ const size_t num_locs = m_locations.GetSize();
+ for (size_t i = 0; i < num_locs; ++i) {
+ BreakpointLocationSP loc_sp = GetLocationAtIndex(i);
+ if (!loc_sp)
+ continue;
+ locations.emplace_back(loc_sp->GetMetrics(options));
+ }
+ bp.try_emplace("locations", std::move(locations));
+ }
+
+ if (m_resolve_time.count() > 0.0)
+ bp.try_emplace("resolveTime", m_resolve_time.count());
+ StructuredData::ObjectSP bp_data_sp = SerializeToStructuredData();
+ if (bp_data_sp) {
+ std::string buffer;
+ llvm::raw_string_ostream ss(buffer);
+ json::OStream json_os(ss);
+ bp_data_sp->Serialize(json_os);
+ if (llvm::Expected<llvm::json::Value> expected_value =
+ llvm::json::parse(ss.str()))
+ bp.try_emplace("details", std::move(*expected_value));
+ else
+ llvm::consumeError(expected_value.takeError());
+ }
+ return json::Value(std::move(bp));
+}
+
// Serialization
StructuredData::ObjectSP Breakpoint::SerializeToStructuredData() {
// Serialize the resolver:
@@ -439,22 +478,24 @@
const BreakpointOptions &Breakpoint::GetOptions() const { return m_options; }
void Breakpoint::ResolveBreakpoint() {
- if (m_resolver_sp)
+ if (m_resolver_sp) {
+ ElapsedTime elapsed(m_resolve_time);
m_resolver_sp->ResolveBreakpoint(*m_filter_sp);
+ }
}
void Breakpoint::ResolveBreakpointInModules(
ModuleList &module_list, BreakpointLocationCollection &new_locations) {
+ ElapsedTime elapsed(m_resolve_time);
m_locations.StartRecordingNewLocations(new_locations);
-
m_resolver_sp->ResolveBreakpointInModules(*m_filter_sp, module_list);
-
m_locations.StopRecordingNewLocations();
}
void Breakpoint::ResolveBreakpointInModules(ModuleList &module_list,
bool send_event) {
if (m_resolver_sp) {
+ ElapsedTime elapsed(m_resolve_time);
// If this is not an internal breakpoint, set up to record the new
// locations, then dispatch an event with the new locations.
if (!IsInternal() && send_event) {
@@ -522,12 +563,12 @@
locations_with_no_section.Add(break_loc_sp);
continue;
}
-
+
if (!break_loc_sp->IsEnabled())
continue;
-
+
SectionSP section_sp(section_addr.GetSection());
-
+
// If we don't have a Section, that means this location is a raw
// address that we haven't resolved to a section yet. So we'll have to
// look in all the new modules to resolve this location. Otherwise, if
@@ -544,9 +585,9 @@
}
}
}
-
+
size_t num_to_delete = locations_with_no_section.GetSize();
-
+
for (size_t i = 0; i < num_to_delete; i++)
m_locations.RemoveLocation(locations_with_no_section.GetByIndex(i));
Index: lldb/packages/Python/lldbsuite/test/lldbtest.py
===================================================================
--- lldb/packages/Python/lldbsuite/test/lldbtest.py
+++ lldb/packages/Python/lldbsuite/test/lldbtest.py
@@ -766,6 +766,20 @@
"""Return absolute path to an artifact in the test's build directory."""
return os.path.join(self.getBuildDir(), name)
+ def getShlibBuildArtifact(self, DYLIB_NAME):
+ """Return absolute path to a shared library artifact given the library
+ name as it is known from the Makefile in DYLIB_NAME.
+
+ Each platform has different prefixes and extensions for shared
+ librairies when they get built by the Makefile. This function
+ allows tests to specify the DYLIB_NAME from the Makefile and it
+ will return the correct path for the built shared library in the
+ build artifacts directory
+ """
+ ctx = self.platformContext
+ dylibName = ctx.shlib_prefix + DYLIB_NAME + '.' + ctx.shlib_extension
+ return self.getBuildArtifact(dylibName)
+
def getSourcePath(self, name):
"""Return absolute path to a file in the test's source directory."""
return os.path.join(self.getSourceDir(), name)
Index: lldb/include/lldb/lldb-forward.h
===================================================================
--- lldb/include/lldb/lldb-forward.h
+++ lldb/include/lldb/lldb-forward.h
@@ -118,6 +118,7 @@
class MemoryHistory;
class MemoryRegionInfo;
class MemoryRegionInfos;
+struct MetricDumpOptions;
class Module;
class ModuleList;
class ModuleSpec;
Index: lldb/include/lldb/Utility/ElapsedTime.h
===================================================================
--- /dev/null
+++ lldb/include/lldb/Utility/ElapsedTime.h
@@ -0,0 +1,56 @@
+//===-- ElapsedTime.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_UTILITY_ELAPSEDTIME_H
+#define LLDB_UTILITY_ELAPSEDTIME_H
+
+#include <chrono>
+namespace lldb_private {
+
+/// A class that measures elapsed time.
+///
+/// This class is designed to help gather timing metrics within LLDB where
+/// objects have optional Duration variables that get updated with elapsed
+/// times. This helps LLDB measure metrics for many things that are then
+/// reported in LLDB commands.
+///
+/// Objects that need to measure elapsed times should have a variable with of
+/// type "ElapsedTime::Duration m_time_xxx;" which can then be used in the
+/// constructor of this class inside a scope that wants to measure something:
+///
+/// ElapsedTime elapsed(m_time_xxx);
+/// // Do some work
+///
+/// This class will update the m_time_xxx variable with the elapsed time when
+/// the object goes out of scope. The "m_time_xxx" variable will be incremented
+/// when the class goes out of scope. This allows a variable to measure
+/// something that might happen in stages at different times, like resolving a
+/// breakpoint each time a new shared library is loaded.
+class ElapsedTime {
+public:
+ using Clock = std::chrono::high_resolution_clock;
+ using Duration = std::chrono::duration<double>;
+ using Timepoint = std::chrono::time_point<Clock>;
+ /// Set to the start time when the object is created.
+ Timepoint m_start_time;
+ /// The elapsed time in seconds to update when this object goes out of scope.
+ ElapsedTime::Duration &m_elapsed_time;
+
+public:
+ ElapsedTime(ElapsedTime::Duration &opt_time) : m_elapsed_time(opt_time) {
+ m_start_time = Clock::now();
+ }
+ ~ElapsedTime() {
+ Duration elapsed = Clock::now() - m_start_time;
+ m_elapsed_time += elapsed;
+ }
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_UTILITY_ELAPSEDTIME_H
Index: lldb/include/lldb/Target/Target.h
===================================================================
--- lldb/include/lldb/Target/Target.h
+++ lldb/include/lldb/Target/Target.h
@@ -26,6 +26,7 @@
#include "lldb/Host/ProcessLaunchInfo.h"
#include "lldb/Symbol/TypeSystem.h"
#include "lldb/Target/ExecutionContextScope.h"
+#include "lldb/Target/Metrics.h"
#include "lldb/Target/PathMappingList.h"
#include "lldb/Target/SectionLoadHistory.h"
#include "lldb/Target/ThreadSpec.h"
@@ -1178,6 +1179,30 @@
/// if none can be found.
llvm::Expected<lldb_private::Address> GetEntryPointAddress();
+ // Utilities for the `target metrics` command
+private:
+ // Target metrics storage.
+ TargetMetrics m_metrics;
+
+public:
+ /// Get metrics associated with this target in JSON format.
+ ///
+ /// Target metrics help measure timings and information that is contained in
+ /// a target. These are designed to help measure performance of a debug
+ /// session as well as represent the current state of the target, like
+ /// information on the currently modules, currently set breakpoints and more.
+ ///
+ /// \param [in] modules
+ /// If true, include an array of metrics for each module loaded in the
+ /// target.
+ //
+ /// \return
+ /// Returns a JSON value that contains all target metrics.
+ llvm::json::Value GatherMetrics(const MetricDumpOptions &options);
+
+ /// Accessor for the target metrics object.
+ TargetMetrics &GetMetrics() { return m_metrics; }
+
// Target Stop Hooks
class StopHook : public UserID {
public:
Index: lldb/include/lldb/Target/Process.h
===================================================================
--- lldb/include/lldb/Target/Process.h
+++ lldb/include/lldb/Target/Process.h
@@ -240,10 +240,11 @@
~ProcessModID() = default;
- void BumpStopID() {
- m_stop_id++;
+ uint32_t BumpStopID() {
+ const uint32_t prev_stop_id = m_stop_id++;
if (!IsLastResumeForUserExpression())
m_last_natural_stop_id++;
+ return prev_stop_id;
}
void BumpMemoryID() { m_memory_id++; }
Index: lldb/include/lldb/Target/PathMappingList.h
===================================================================
--- lldb/include/lldb/Target/PathMappingList.h
+++ lldb/include/lldb/Target/PathMappingList.h
@@ -9,10 +9,11 @@
#ifndef LLDB_TARGET_PATHMAPPINGLIST_H
#define LLDB_TARGET_PATHMAPPINGLIST_H
-#include <map>
-#include <vector>
#include "lldb/Utility/ConstString.h"
#include "lldb/Utility/Status.h"
+#include "llvm/Support/JSON.h"
+#include <map>
+#include <vector>
namespace lldb_private {
@@ -108,6 +109,11 @@
uint32_t GetModificationID() const { return m_mod_id; }
+ /// Return the path mappings as a JSON array.
+ ///
+ /// The array contains arrays of path mapping paths as strings.
+ llvm::json::Value ToJSON() const;
+
protected:
typedef std::pair<ConstString, ConstString> pair;
typedef std::vector<pair> collection;
Index: lldb/include/lldb/Target/Metrics.h
===================================================================
--- /dev/null
+++ lldb/include/lldb/Target/Metrics.h
@@ -0,0 +1,74 @@
+//===-- Metrics.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_TARGET_METRICS_H
+#define LLDB_TARGET_METRICS_H
+
+#include <chrono>
+#include <vector>
+
+#include "lldb/Utility/ElapsedTime.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/lldb-forward.h"
+#include "llvm/Support/JSON.h"
+
+namespace lldb_private {
+
+struct MetricDumpOptions {
+ bool dump_modules = false;
+ bool dump_breakpoints = false;
+ bool dump_breakpoint_locations = false;
+ bool dump_settings = false;
+
+ /// Set all dump options to true.
+ void SetAll() {
+ dump_modules = true;
+ dump_breakpoints = true;
+ dump_breakpoint_locations = true;
+ dump_settings = true;
+ }
+};
+
+struct ModuleMetrics {
+ llvm::json::Value ToJSON() const;
+
+ std::string path;
+ std::string uuid;
+ std::string triple;
+ uint64_t debug_info_size = 0;
+ double debug_parse_time = 0.0;
+ double debug_index_time = 0.0;
+ double symtab_parse_time = 0.0;
+ double symtab_names_time = 0.0;
+};
+
+class TargetMetrics {
+public:
+ llvm::json::Value ToJSON(Target &target, const MetricDumpOptions &options);
+
+ void CollectModuleMetrics(Target &target);
+
+ void SetLaunchOrAttachTime();
+
+ void SetFirstPrivateStopTime();
+
+ void SetFirstPublicStopTime();
+
+ ElapsedTime::Duration &GetCreateTime() { return create_time; }
+
+protected:
+ std::vector<ModuleMetrics> m_module_metrics;
+ ElapsedTime::Duration create_time{0.0};
+ llvm::Optional<std::chrono::steady_clock::time_point> launch_or_attach_time;
+ llvm::Optional<std::chrono::steady_clock::time_point> first_private_stop_time;
+ llvm::Optional<std::chrono::steady_clock::time_point> first_public_stop_time;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_TARGET_METRICS_H
Index: lldb/include/lldb/Symbol/SymbolFile.h
===================================================================
--- lldb/include/lldb/Symbol/SymbolFile.h
+++ lldb/include/lldb/Symbol/SymbolFile.h
@@ -19,6 +19,7 @@
#include "lldb/Symbol/Type.h"
#include "lldb/Symbol/TypeList.h"
#include "lldb/Symbol/TypeSystem.h"
+#include "lldb/Utility/ElapsedTime.h"
#include "lldb/Utility/XcodeSDK.h"
#include "lldb/lldb-private.h"
#include "llvm/ADT/DenseSet.h"
@@ -299,6 +300,35 @@
virtual void Dump(Stream &s);
+ /// Metrics gathering functions
+
+ /// Return the size in bytes of all debug information in the symbol file.
+ ///
+ /// If the debug information is contained in sections of an ObjectFile, then
+ /// this call should add the size of all sections that contain debug
+ /// information. Symbols the symbol tables are not considered debug
+ /// information for this call to make it easy and quick for this number to be
+ /// calculated. If the symbol file is all debug information, the size of the
+ /// entire file should be returned.
+ virtual uint64_t GetDebugInfoSize() = 0;
+
+ /// Return the time taken to parse the debug information.
+ ///
+ /// \returns llvm::None if no information has been parsed or if there is
+ /// no computational cost to parsing the debug information.
+ virtual ElapsedTime::Duration GetDebugInfoParseTime() {
+ return ElapsedTime::Duration(0.0);
+ }
+
+ /// Return the time it took to index the debug information in the object
+ /// file.
+ ///
+ /// \returns llvm::None if the file doesn't need to be indexed or if it
+ /// hasn't been indexed yet, or a valid duration if it has.
+ virtual ElapsedTime::Duration GetDebugInfoIndexTime() {
+ return ElapsedTime::Duration(0.0);
+ }
+
protected:
void AssertModuleLock();
virtual uint32_t CalculateNumCompileUnits() = 0;
Index: lldb/include/lldb/Symbol/SymbolContext.h
===================================================================
--- lldb/include/lldb/Symbol/SymbolContext.h
+++ lldb/include/lldb/Symbol/SymbolContext.h
@@ -18,6 +18,7 @@
#include "lldb/Symbol/LineEntry.h"
#include "lldb/Utility/Iterable.h"
#include "lldb/lldb-private.h"
+#include "llvm/Support/JSON.h"
namespace lldb_private {
@@ -313,6 +314,8 @@
SymbolContext &next_frame_sc,
Address &inlined_frame_addr) const;
+ llvm::json::Value ToJSON() const;
+
// Member variables
lldb::TargetSP target_sp; ///< The Target for a given query
lldb::ModuleSP module_sp; ///< The Module for a given query
Index: lldb/include/lldb/Core/Section.h
===================================================================
--- lldb/include/lldb/Core/Section.h
+++ lldb/include/lldb/Core/Section.h
@@ -89,6 +89,12 @@
void Clear() { m_sections.clear(); }
+ /// Get the debug information size from all sections that contain debug
+ /// information. Symbol tables are not considered part of the debug
+ /// information for this call, just known sections that contain debug
+ /// information.
+ uint64_t GetDebugInfoSize() const;
+
protected:
collection m_sections;
};
@@ -181,6 +187,13 @@
void SetIsThreadSpecific(bool b) { m_thread_specific = b; }
+ /// Returns true if this section contains debug information. Symbol tables
+ /// are not considered debug information since some symbols might contain
+ /// debug information (STABS, COFF) but not all symbols do, so to keep this
+ /// fast and simple only sections that contains only debug information should
+ /// return true.
+ bool ContainsOnlyDebugInfo() const;
+
/// Get the permissions as OR'ed bits from lldb::Permissions
uint32_t GetPermissions() const;
Index: lldb/include/lldb/Core/Module.h
===================================================================
--- lldb/include/lldb/Core/Module.h
+++ lldb/include/lldb/Core/Module.h
@@ -18,10 +18,11 @@
#include "lldb/Target/PathMappingList.h"
#include "lldb/Utility/ArchSpec.h"
#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/ElapsedTime.h"
#include "lldb/Utility/FileSpec.h"
#include "lldb/Utility/Status.h"
-#include "lldb/Utility/XcodeSDK.h"
#include "lldb/Utility/UUID.h"
+#include "lldb/Utility/XcodeSDK.h"
#include "lldb/lldb-defines.h"
#include "lldb/lldb-enumerations.h"
#include "lldb/lldb-forward.h"
@@ -933,6 +934,15 @@
bool m_match_name_after_lookup = false;
};
+ /// Get the symbol table parse time metric.
+ ///
+ /// The value is returned as a reference to allow it to be updated by the
+ /// ElapsedTime
+ ElapsedTime::Duration &GetSymtabParseTime() { return m_symtab_parse_time; }
+ ElapsedTime::Duration &GetSymtabNamesTime() { return m_symtab_names_time; }
+
+ uint64_t GetDebugInfoSize();
+
protected:
// Member Variables
mutable std::recursive_mutex m_mutex; ///< A mutex to keep this object happy
@@ -959,6 +969,14 @@
///by \a m_file.
uint64_t m_object_offset = 0;
llvm::sys::TimePoint<> m_object_mod_time;
+ /// We store a symbol table parse time duration here because we might have
+ /// an object file and a symbol file which both have symbol tables. The parse
+ /// time for the symbol tables can be aggregated here.
+ ElapsedTime::Duration m_symtab_parse_time{0.0};
+ /// We store a symbol named index time duration here because we might have
+ /// an object file and a symbol file which both have symbol tables. The parse
+ /// time for the symbol tables can be aggregated here.
+ ElapsedTime::Duration m_symtab_names_time{0.0};
/// DataBuffer containing the module image, if it was provided at
/// construction time. Otherwise the data will be retrieved by mapping
Index: lldb/include/lldb/Core/FileSpecList.h
===================================================================
--- lldb/include/lldb/Core/FileSpecList.h
+++ lldb/include/lldb/Core/FileSpecList.h
@@ -11,7 +11,7 @@
#if defined(__cplusplus)
#include "lldb/Utility/FileSpec.h"
-
+#include "llvm/Support/JSON.h"
#include <vector>
#include <cstddef>
@@ -197,6 +197,8 @@
const_iterator begin() const { return m_files.begin(); }
const_iterator end() const { return m_files.end(); }
+ llvm::json::Value ToJSON() const;
+
protected:
collection m_files; ///< A collection of FileSpec objects.
};
Index: lldb/include/lldb/Breakpoint/BreakpointLocation.h
===================================================================
--- lldb/include/lldb/Breakpoint/BreakpointLocation.h
+++ lldb/include/lldb/Breakpoint/BreakpointLocation.h
@@ -18,6 +18,8 @@
#include "lldb/Utility/UserID.h"
#include "lldb/lldb-private.h"
+#include "llvm/Support/JSON.h"
+
namespace lldb_private {
/// \class BreakpointLocation BreakpointLocation.h
@@ -230,7 +232,7 @@
/// \b true if the target should stop at this breakpoint and \b
/// false not.
bool InvokeCallback(StoppointCallbackContext *context);
-
+
/// Report whether the callback for this location is synchronous or not.
///
/// \return
@@ -279,6 +281,9 @@
/// Returns the breakpoint location ID.
lldb::break_id_t GetID() const { return m_loc_id; }
+ /// Returns the breakpoint location metrics as JSON.
+ llvm::json::Value GetMetrics(const MetricDumpOptions &options);
+
protected:
friend class BreakpointSite;
friend class BreakpointLocationList;
Index: lldb/include/lldb/Breakpoint/Breakpoint.h
===================================================================
--- lldb/include/lldb/Breakpoint/Breakpoint.h
+++ lldb/include/lldb/Breakpoint/Breakpoint.h
@@ -22,6 +22,7 @@
#include "lldb/Breakpoint/Stoppoint.h"
#include "lldb/Breakpoint/StoppointHitCounter.h"
#include "lldb/Core/SearchFilter.h"
+#include "lldb/Utility/ElapsedTime.h"
#include "lldb/Utility/Event.h"
#include "lldb/Utility/StringList.h"
#include "lldb/Utility/StructuredData.h"
@@ -576,6 +577,12 @@
static lldb::BreakpointSP CopyFromBreakpoint(lldb::TargetSP new_target,
const Breakpoint &bp_to_copy_from);
+ /// Get metrics associated with this breakpoint in JSON format.
+ llvm::json::Value GetMetrics(const MetricDumpOptions &options);
+
+ /// Get the time it took to resolve all locations in this breakpoint.
+ ElapsedTime::Duration GetResolveTime() const { return m_resolve_time; }
+
protected:
friend class Target;
// Protected Methods
@@ -653,6 +660,8 @@
BreakpointName::Permissions m_permissions;
+ ElapsedTime::Duration m_resolve_time{0.0};
+
void SendBreakpointChangedEvent(lldb::BreakpointEventType eventKind);
void SendBreakpointChangedEvent(BreakpointEventData *data);
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits