diff --git a/lldb/include/lldb/Core/FileSpecList.h b/lldb/include/lldb/Core/FileSpecList.h --- a/lldb/include/lldb/Core/FileSpecList.h +++ b/lldb/include/lldb/Core/FileSpecList.h @@ -11,6 +11,7 @@ #if defined(__cplusplus) #include "lldb/Utility/FileSpec.h" +#include "llvm/Support/JSON.h" #include @@ -197,6 +198,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. }; diff --git a/lldb/include/lldb/Target/PathMappingList.h b/lldb/include/lldb/Target/PathMappingList.h --- a/lldb/include/lldb/Target/PathMappingList.h +++ b/lldb/include/lldb/Target/PathMappingList.h @@ -9,10 +9,11 @@ #ifndef LLDB_TARGET_PATHMAPPINGLIST_H #define LLDB_TARGET_PATHMAPPINGLIST_H -#include -#include #include "lldb/Utility/ConstString.h" #include "lldb/Utility/Status.h" +#include "llvm/Support/JSON.h" +#include +#include 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 pair; typedef std::vector collection; diff --git a/lldb/source/Core/FileSpecList.cpp b/lldb/source/Core/FileSpecList.cpp --- a/lldb/source/Core/FileSpecList.cpp +++ b/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)); +} diff --git a/lldb/source/Target/PathMappingList.cpp b/lldb/source/Target/PathMappingList.cpp --- a/lldb/source/Target/PathMappingList.cpp +++ b/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.GetStringRef()); + mapping.emplace_back(pair.second.GetStringRef()); + mappings.emplace_back(std::move(mapping)); + } + return llvm::json::Value(std::move(mappings)); +} diff --git a/lldb/source/Target/Statistics.cpp b/lldb/source/Target/Statistics.cpp --- a/lldb/source/Target/Statistics.cpp +++ b/lldb/source/Target/Statistics.cpp @@ -104,6 +104,40 @@ target_metrics_json.try_emplace("signals", unix_signals_sp->GetHitCountStatistics()); } + + json::Object 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(std::string(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()); + + target_metrics_json.try_emplace("settings", std::move(settings)); + target_metrics_json.try_emplace("breakpoints", std::move(breakpoints_array)); target_metrics_json.try_emplace("totalBreakpointResolveTime", totalBreakpointResolveTime); diff --git a/lldb/test/API/commands/statistics/basic/TestStats.py b/lldb/test/API/commands/statistics/basic/TestStats.py --- a/lldb/test/API/commands/statistics/basic/TestStats.py +++ b/lldb/test/API/commands/statistics/basic/TestStats.py @@ -1,5 +1,6 @@ import lldb import json +import os from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil @@ -154,7 +155,7 @@ target = self.createTestTarget() debug_stats = self.get_stats() debug_stat_keys = [ - 'modules', + 'modules', 'targets', 'totalSymbolTableParseTime', 'totalSymbolTableIndexTime', @@ -217,7 +218,7 @@ lldb.SBFileSpec("main.c")) debug_stats = self.get_stats() debug_stat_keys = [ - 'modules', + 'modules', 'targets', 'totalSymbolTableParseTime', 'totalSymbolTableIndexTime', @@ -255,7 +256,7 @@ target = self.createTestTarget(file_path=exe) debug_stats = self.get_stats() debug_stat_keys = [ - 'modules', + 'modules', 'targets', 'totalSymbolTableParseTime', 'totalSymbolTableIndexTime', @@ -315,7 +316,7 @@ "details": {...}, "id": 2, "resolveTime": 4.3632581669999997 - } + } ] } ], @@ -333,7 +334,7 @@ self.runCmd("b a_function") debug_stats = self.get_stats() debug_stat_keys = [ - 'modules', + 'modules', 'targets', 'totalSymbolTableParseTime', 'totalSymbolTableIndexTime', @@ -363,5 +364,134 @@ 'resolveTime' ] for breakpoint in breakpoints: - self.verify_keys(breakpoint, 'target_stats["breakpoints"]', + self.verify_keys(breakpoint, 'target_stats["breakpoints"]', bp_keys_exist, None) + + def test_settings(self): + """Test "statistics dump --settings" + + Output expected to be something like: + + (lldb) statistics dump --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/TestStats.test_settings/a.out", + "target.clang-module-search-paths": [], + "target.debug-file-search-paths": [], + "target.exec-search-paths": [ + "/.../commands/target/metrics/TestStats.test_settings" + ], + "target.inline-breakpoint-strategy": "always", + "target.preload-symbols": true, + "target.run-args": [], + "target.skip-prologue": true, + "target.source-map": [] + }, + } + + """ + exe = self.getBuildArtifact("a.out") + exe_dir = os.path.dirname(exe) + path1 = self.getSourceDir() + path2 = exe_dir + target = self.createTestTarget(file_path=exe) + debug_stats = self.get_stats(log_path="/tmp/a") + debug_stat_keys = [ + 'modules', + 'targets', + 'totalSymbolTableParseTime', + 'totalSymbolTableIndexTime', + 'totalDebugInfoParseTime', + 'totalDebugInfoIndexTime', + 'totalDebugInfoByteSize', + ] + self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None) + target_stats = debug_stats['targets'][0] + keys_exist = [ + 'settings' + ] + self.verify_keys(target_stats, '"stats"', keys_exist, None) + settings = target_stats['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_keys(settings, 'stats["settings"]', settings_keys_exist, + None) + + self.assertEqual(settings['target.arg0'], exe, + 'verify "target.arg0" setting is "%s"' % (exe)) + self.assertEqual(settings['target.clang-module-search-paths'], [], + 'verify "target.clang-module-search-paths" setting is []') + self.assertEqual(settings['target.debug-file-search-paths'], [], + 'verify "target.debug-file-search-paths" setting is []') + self.assertEqual(settings['target.exec-search-paths'], [exe_dir], + 'verify "target.exec-search-paths" setting is ["%s"]' % (exe_dir)) + self.assertEqual(settings['target.inline-breakpoint-strategy'], 'always', + 'verify "target.inline-breakpoint-strategy" setting is "always"') + self.assertEqual(settings['target.preload-symbols'], True, + 'verify "target.preload-symbols" setting is True') + self.assertEqual(settings['target.run-args'], [], + 'verify "target.run-args" setting is []') + self.assertEqual(settings['target.skip-prologue'], True, + 'verify "target.skip-prologue" setting is True') + self.assertEqual(settings['target.source-map'], [], + 'verify "target.source-map" setting is []') + + self.runCmd("settings set target.arg0 hello") + self.runCmd('settings set target.clang-module-search-paths "%s" "%s"' % (path1, path2)) + self.runCmd('settings set target.debug-file-search-paths "%s" "%s"' % (path1, path2)) + self.runCmd('settings set target.exec-search-paths "%s" "%s"' % (path1, path2)) + self.runCmd('settings set target.inline-breakpoint-strategy never') + self.runCmd('settings set target.preload-symbols false') + self.runCmd('settings set target.run-args a b c') + self.runCmd('settings set target.skip-prologue false') + self.runCmd('settings set target.source-map "%s" "%s"' % (path1, path2)) + + debug_stats = self.get_stats(log_path="/tmp/b") + target_stats = debug_stats['targets'][0] + settings = target_stats['settings'] + self.verify_keys(settings, 'stats["settings"]', settings_keys_exist, + None) + + self.assertEqual(settings['target.arg0'], 'hello', + 'verify "target.arg0" setting is "hello"') + self.assertEqual(settings['target.clang-module-search-paths'], [path1, path2], + 'verify "target.clang-module-search-paths" setting is ["%s", "%s"]' % (path1, path2)) + self.assertEqual(settings['target.debug-file-search-paths'], [path1, path2], + 'verify "target.debug-file-search-paths" setting is ["%s", "%s"]' % (path1, path2)) + self.assertEqual(settings['target.exec-search-paths'], [path1, path2], + 'verify "target.exec-search-paths" setting is ["%s", "%s"]' % (path1, path2)) + self.assertEqual(settings['target.inline-breakpoint-strategy'], 'never', + 'verify "target.inline-breakpoint-strategy" setting is "never"') + self.assertEqual(settings['target.preload-symbols'], False, + 'verify "target.preload-symbols" setting is False') + self.assertEqual(settings['target.run-args'], ["a", "b", "c"], + 'verify "target.run-args" setting is ["a", "b", "c"]') + self.assertEqual(settings['target.skip-prologue'], False, + 'verify "target.skip-prologue" setting is False') + self.assertEqual(settings['target.source-map'], [[path1, path2]], + 'verify "target.source-map" setting is [["%s", "%s"]]' % (path1, path2)) + + self.runCmd('settings set target.inline-breakpoint-strategy headers') + debug_stats = self.get_stats(log_path="/tmp/b") + target_stats = debug_stats['targets'][0] + settings = target_stats['settings'] + self.verify_keys(settings, 'stats["settings"]', settings_keys_exist, + None) + self.assertEqual(settings['target.inline-breakpoint-strategy'], 'headers', + 'verify "target.inline-breakpoint-strategy" setting is "headers"')