xref: /llvm-project/lldb/test/API/commands/statistics/basic/TestStats.py (revision 24feaab8380c69d5fa3eb8c21ef2d660913fd4a9)
1d7b33853SGreg Claytonimport json
23db7cc1bSGreg Claytonimport os
370f41a8cSroyitaqiimport re
4*24feaab8Sjeffreytan81
5*24feaab8Sjeffreytan81import lldb
66bbdecc5SRaphael Isemannfrom lldbsuite.test.decorators import *
76bbdecc5SRaphael Isemannfrom lldbsuite.test.lldbtest import *
86bbdecc5SRaphael Isemannfrom lldbsuite.test import lldbutil
999451b44SJordan Rupprecht
106bbdecc5SRaphael Isemann
112238dcc3SJonas Devlieghereclass TestCase(TestBase):
12d7b33853SGreg Clayton    NO_DEBUG_INFO_TESTCASE = True
13d7b33853SGreg Clayton
14d7b33853SGreg Clayton    def test_enable_disable(self):
15d7b33853SGreg Clayton        """
16d7b33853SGreg Clayton        Test "statistics disable" and "statistics enable". These don't do
17d7b33853SGreg Clayton        anything anymore for cheap to gather statistics. In the future if
18d7b33853SGreg Clayton        statistics are expensive to gather, we can enable the feature inside
19d7b33853SGreg Clayton        of LLDB and test that enabling and disabling stops expesive information
20d7b33853SGreg Clayton        from being gathered.
21d7b33853SGreg Clayton        """
223db7cc1bSGreg Clayton        self.build()
23d7b33853SGreg Clayton        target = self.createTestTarget()
246bbdecc5SRaphael Isemann
252238dcc3SJonas Devlieghere        self.expect(
262238dcc3SJonas Devlieghere            "statistics disable",
272238dcc3SJonas Devlieghere            substrs=["need to enable statistics before disabling"],
282238dcc3SJonas Devlieghere            error=True,
292238dcc3SJonas Devlieghere        )
306bbdecc5SRaphael Isemann        self.expect("statistics enable")
312238dcc3SJonas Devlieghere        self.expect("statistics enable", substrs=["already enabled"], error=True)
326bbdecc5SRaphael Isemann        self.expect("statistics disable")
332238dcc3SJonas Devlieghere        self.expect(
342238dcc3SJonas Devlieghere            "statistics disable",
352238dcc3SJonas Devlieghere            substrs=["need to enable statistics before disabling"],
362238dcc3SJonas Devlieghere            error=True,
372238dcc3SJonas Devlieghere        )
386bbdecc5SRaphael Isemann
39d7b33853SGreg Clayton    def verify_key_in_dict(self, key, d, description):
401eeeab82SJordan Rupprecht        self.assertIn(
411eeeab82SJordan Rupprecht            key, d, 'make sure key "%s" is in dictionary %s' % (key, description)
422238dcc3SJonas Devlieghere        )
43d7b33853SGreg Clayton
44d7b33853SGreg Clayton    def verify_key_not_in_dict(self, key, d, description):
451eeeab82SJordan Rupprecht        self.assertNotIn(
461eeeab82SJordan Rupprecht            key, d, 'make sure key "%s" is in dictionary %s' % (key, description)
472238dcc3SJonas Devlieghere        )
48d7b33853SGreg Clayton
49d7b33853SGreg Clayton    def verify_keys(self, dict, description, keys_exist, keys_missing=None):
50d7b33853SGreg Clayton        """
51d7b33853SGreg Clayton        Verify that all keys in "keys_exist" list are top level items in
52d7b33853SGreg Clayton        "dict", and that all keys in "keys_missing" do not exist as top
53d7b33853SGreg Clayton        level items in "dict".
54d7b33853SGreg Clayton        """
55d7b33853SGreg Clayton        if keys_exist:
56d7b33853SGreg Clayton            for key in keys_exist:
57d7b33853SGreg Clayton                self.verify_key_in_dict(key, dict, description)
58d7b33853SGreg Clayton        if keys_missing:
59d7b33853SGreg Clayton            for key in keys_missing:
60d7b33853SGreg Clayton                self.verify_key_not_in_dict(key, dict, description)
61d7b33853SGreg Clayton
62d7b33853SGreg Clayton    def verify_success_fail_count(self, stats, key, num_successes, num_fails):
63d7b33853SGreg Clayton        self.verify_key_in_dict(key, stats, 'stats["%s"]' % (key))
64d7b33853SGreg Clayton        success_fail_dict = stats[key]
652238dcc3SJonas Devlieghere        self.assertEqual(
662238dcc3SJonas Devlieghere            success_fail_dict["successes"], num_successes, "make sure success count"
672238dcc3SJonas Devlieghere        )
682238dcc3SJonas Devlieghere        self.assertEqual(
692238dcc3SJonas Devlieghere            success_fail_dict["failures"], num_fails, "make sure success count"
702238dcc3SJonas Devlieghere        )
71d7b33853SGreg Clayton
72c571988eSGreg Clayton    def get_target_stats(self, debug_stats):
73c571988eSGreg Clayton        if "targets" in debug_stats:
74c571988eSGreg Clayton            return debug_stats["targets"][0]
75c571988eSGreg Clayton        return None
76c571988eSGreg Clayton
7776706090Sjeffreytan81    def get_command_stats(self, debug_stats):
7876706090Sjeffreytan81        if "commands" in debug_stats:
7976706090Sjeffreytan81            return debug_stats["commands"]
8076706090Sjeffreytan81        return None
8176706090Sjeffreytan81
82d7b33853SGreg Clayton    def test_expressions_frame_var_counts(self):
833db7cc1bSGreg Clayton        self.build()
842238dcc3SJonas Devlieghere        lldbutil.run_to_source_breakpoint(
8522144e20SJacob Lalonde            self, "// break here", lldb.SBFileSpec("main.cpp")
862238dcc3SJonas Devlieghere        )
87d7b33853SGreg Clayton
882238dcc3SJonas Devlieghere        self.expect("expr patatino", substrs=["27"])
89c571988eSGreg Clayton        stats = self.get_target_stats(self.get_stats())
902238dcc3SJonas Devlieghere        self.verify_success_fail_count(stats, "expressionEvaluation", 1, 0)
912238dcc3SJonas Devlieghere        self.expect(
922238dcc3SJonas Devlieghere            "expr doesnt_exist",
932238dcc3SJonas Devlieghere            error=True,
942238dcc3SJonas Devlieghere            substrs=["undeclared identifier 'doesnt_exist'"],
952238dcc3SJonas Devlieghere        )
966a4905aeSRaphael Isemann        # Doesn't successfully execute.
976a4905aeSRaphael Isemann        self.expect("expr int *i = nullptr; *i", error=True)
98910838f0SGreg Clayton        # Interpret an integer as an array with 3 elements is a failure for
99910838f0SGreg Clayton        # the "expr" command, but the expression evaluation will succeed and
100910838f0SGreg Clayton        # be counted as a success even though the "expr" options will for the
101910838f0SGreg Clayton        # command to fail. It is more important to track expression evaluation
102910838f0SGreg Clayton        # from all sources instead of just through the command, so this was
103910838f0SGreg Clayton        # changed. If we want to track command success and fails, we can do
104910838f0SGreg Clayton        # so using another metric.
1052238dcc3SJonas Devlieghere        self.expect(
1062238dcc3SJonas Devlieghere            "expr -Z 3 -- 1",
1072238dcc3SJonas Devlieghere            error=True,
1082238dcc3SJonas Devlieghere            substrs=["expression cannot be used with --element-count"],
1092238dcc3SJonas Devlieghere        )
1106a4905aeSRaphael Isemann        # We should have gotten 3 new failures and the previous success.
111c571988eSGreg Clayton        stats = self.get_target_stats(self.get_stats())
1122238dcc3SJonas Devlieghere        self.verify_success_fail_count(stats, "expressionEvaluation", 2, 2)
1136bbdecc5SRaphael Isemann
1146bbdecc5SRaphael Isemann        self.expect("statistics enable")
1156bbdecc5SRaphael Isemann        # 'frame var' with enabled statistics will change stats.
1162238dcc3SJonas Devlieghere        self.expect("frame var", substrs=["27"])
117c571988eSGreg Clayton        stats = self.get_target_stats(self.get_stats())
1182238dcc3SJonas Devlieghere        self.verify_success_fail_count(stats, "frameVariable", 1, 0)
119d7b33853SGreg Clayton
120dbd36e1eSGreg Clayton        # Test that "stopCount" is available when the process has run
1211eeeab82SJordan Rupprecht        self.assertIn("stopCount", stats, 'ensure "stopCount" is in target JSON')
1222238dcc3SJonas Devlieghere        self.assertGreater(
1232238dcc3SJonas Devlieghere            stats["stopCount"], 0, 'make sure "stopCount" is greater than zero'
1242238dcc3SJonas Devlieghere        )
125dbd36e1eSGreg Clayton
126d7b33853SGreg Clayton    def test_default_no_run(self):
127d7b33853SGreg Clayton        """Test "statistics dump" without running the target.
128d7b33853SGreg Clayton
129d7b33853SGreg Clayton        When we don't run the target, we expect to not see any 'firstStopTime'
130d7b33853SGreg Clayton        or 'launchOrAttachTime' top level keys that measure the launch or
131d7b33853SGreg Clayton        attach of the target.
132d7b33853SGreg Clayton
133d7b33853SGreg Clayton        Output expected to be something like:
134d7b33853SGreg Clayton
135d7b33853SGreg Clayton        (lldb) statistics dump
136d7b33853SGreg Clayton        {
137cd8122b2SJonas Devlieghere          "memory" : {...},
138c571988eSGreg Clayton          "modules" : [...],
139c571988eSGreg Clayton          "targets" : [
140c571988eSGreg Clayton            {
141d7b33853SGreg Clayton                "targetCreateTime": 0.26566899599999999,
142d7b33853SGreg Clayton                "expressionEvaluation": {
143d7b33853SGreg Clayton                    "failures": 0,
144d7b33853SGreg Clayton                    "successes": 0
145d7b33853SGreg Clayton                },
146d7b33853SGreg Clayton                "frameVariable": {
147d7b33853SGreg Clayton                    "failures": 0,
148d7b33853SGreg Clayton                    "successes": 0
149d7b33853SGreg Clayton                },
150c571988eSGreg Clayton                "moduleIdentifiers": [...],
151c571988eSGreg Clayton            }
152c571988eSGreg Clayton          ],
1532887d9fdSGreg Clayton          "totalDebugInfoByteSize": 182522234,
1542887d9fdSGreg Clayton          "totalDebugInfoIndexTime": 2.33343,
1552887d9fdSGreg Clayton          "totalDebugInfoParseTime": 8.2121400240000071,
156c571988eSGreg Clayton          "totalSymbolTableParseTime": 0.123,
157c571988eSGreg Clayton          "totalSymbolTableIndexTime": 0.234,
158d7b33853SGreg Clayton        }
159d7b33853SGreg Clayton        """
1603db7cc1bSGreg Clayton        self.build()
161d7b33853SGreg Clayton        target = self.createTestTarget()
162c571988eSGreg Clayton        debug_stats = self.get_stats()
163c571988eSGreg Clayton        debug_stat_keys = [
1642238dcc3SJonas Devlieghere            "memory",
1652238dcc3SJonas Devlieghere            "modules",
1662238dcc3SJonas Devlieghere            "targets",
1672238dcc3SJonas Devlieghere            "totalSymbolTableParseTime",
1682238dcc3SJonas Devlieghere            "totalSymbolTableIndexTime",
1692238dcc3SJonas Devlieghere            "totalSymbolTablesLoadedFromCache",
1702238dcc3SJonas Devlieghere            "totalSymbolTablesSavedToCache",
1712238dcc3SJonas Devlieghere            "totalDebugInfoByteSize",
1722238dcc3SJonas Devlieghere            "totalDebugInfoIndexTime",
1732238dcc3SJonas Devlieghere            "totalDebugInfoIndexLoadedFromCache",
1742238dcc3SJonas Devlieghere            "totalDebugInfoIndexSavedToCache",
1752238dcc3SJonas Devlieghere            "totalDebugInfoParseTime",
176c571988eSGreg Clayton        ]
177c571988eSGreg Clayton        self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
1782238dcc3SJonas Devlieghere        stats = debug_stats["targets"][0]
179d7b33853SGreg Clayton        keys_exist = [
1802238dcc3SJonas Devlieghere            "expressionEvaluation",
1812238dcc3SJonas Devlieghere            "frameVariable",
1822238dcc3SJonas Devlieghere            "moduleIdentifiers",
1832238dcc3SJonas Devlieghere            "targetCreateTime",
184d7b33853SGreg Clayton        ]
1852238dcc3SJonas Devlieghere        keys_missing = ["firstStopTime", "launchOrAttachTime"]
186d7b33853SGreg Clayton        self.verify_keys(stats, '"stats"', keys_exist, keys_missing)
1872238dcc3SJonas Devlieghere        self.assertGreater(stats["targetCreateTime"], 0.0)
188d7b33853SGreg Clayton
189d7b33853SGreg Clayton    def test_default_with_run(self):
190d7b33853SGreg Clayton        """Test "statistics dump" when running the target to a breakpoint.
191d7b33853SGreg Clayton
192d7b33853SGreg Clayton        When we run the target, we expect to see 'launchOrAttachTime' and
193d7b33853SGreg Clayton        'firstStopTime' top level keys.
194d7b33853SGreg Clayton
195d7b33853SGreg Clayton        Output expected to be something like:
196d7b33853SGreg Clayton
197d7b33853SGreg Clayton        (lldb) statistics dump
198d7b33853SGreg Clayton        {
199cd8122b2SJonas Devlieghere          "memory" : {...},
200c571988eSGreg Clayton          "modules" : [...],
201c571988eSGreg Clayton          "targets" : [
202c571988eSGreg Clayton                {
203d7b33853SGreg Clayton                    "firstStopTime": 0.34164492800000001,
204d7b33853SGreg Clayton                    "launchOrAttachTime": 0.31969605400000001,
205c571988eSGreg Clayton                    "moduleIdentifiers": [...],
206d7b33853SGreg Clayton                    "targetCreateTime": 0.0040863039999999998
207d7b33853SGreg Clayton                    "expressionEvaluation": {
208d7b33853SGreg Clayton                        "failures": 0,
209d7b33853SGreg Clayton                        "successes": 0
210d7b33853SGreg Clayton                    },
211d7b33853SGreg Clayton                    "frameVariable": {
212d7b33853SGreg Clayton                        "failures": 0,
213d7b33853SGreg Clayton                        "successes": 0
214d7b33853SGreg Clayton                    },
215d7b33853SGreg Clayton                }
216c571988eSGreg Clayton            ],
2172887d9fdSGreg Clayton            "totalDebugInfoByteSize": 182522234,
2182887d9fdSGreg Clayton            "totalDebugInfoIndexTime": 2.33343,
2192887d9fdSGreg Clayton            "totalDebugInfoParseTime": 8.2121400240000071,
220c571988eSGreg Clayton            "totalSymbolTableParseTime": 0.123,
221c571988eSGreg Clayton            "totalSymbolTableIndexTime": 0.234,
222c571988eSGreg Clayton        }
223d7b33853SGreg Clayton
224d7b33853SGreg Clayton        """
2253db7cc1bSGreg Clayton        self.build()
226d7b33853SGreg Clayton        target = self.createTestTarget()
2272238dcc3SJonas Devlieghere        lldbutil.run_to_source_breakpoint(
22822144e20SJacob Lalonde            self, "// break here", lldb.SBFileSpec("main.cpp")
2292238dcc3SJonas Devlieghere        )
230c571988eSGreg Clayton        debug_stats = self.get_stats()
231c571988eSGreg Clayton        debug_stat_keys = [
2322238dcc3SJonas Devlieghere            "memory",
2332238dcc3SJonas Devlieghere            "modules",
2342238dcc3SJonas Devlieghere            "targets",
2352238dcc3SJonas Devlieghere            "totalSymbolTableParseTime",
2362238dcc3SJonas Devlieghere            "totalSymbolTableIndexTime",
2372238dcc3SJonas Devlieghere            "totalSymbolTablesLoadedFromCache",
2382238dcc3SJonas Devlieghere            "totalSymbolTablesSavedToCache",
2392238dcc3SJonas Devlieghere            "totalDebugInfoByteSize",
2402238dcc3SJonas Devlieghere            "totalDebugInfoIndexTime",
2412238dcc3SJonas Devlieghere            "totalDebugInfoIndexLoadedFromCache",
2422238dcc3SJonas Devlieghere            "totalDebugInfoIndexSavedToCache",
2432238dcc3SJonas Devlieghere            "totalDebugInfoParseTime",
244c571988eSGreg Clayton        ]
245c571988eSGreg Clayton        self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
2462238dcc3SJonas Devlieghere        stats = debug_stats["targets"][0]
247d7b33853SGreg Clayton        keys_exist = [
2482238dcc3SJonas Devlieghere            "expressionEvaluation",
2492238dcc3SJonas Devlieghere            "firstStopTime",
2502238dcc3SJonas Devlieghere            "frameVariable",
2512238dcc3SJonas Devlieghere            "launchOrAttachTime",
2522238dcc3SJonas Devlieghere            "moduleIdentifiers",
2532238dcc3SJonas Devlieghere            "targetCreateTime",
25422144e20SJacob Lalonde            "summaryProviderStatistics",
255d7b33853SGreg Clayton        ]
256d7b33853SGreg Clayton        self.verify_keys(stats, '"stats"', keys_exist, None)
2572238dcc3SJonas Devlieghere        self.assertGreater(stats["firstStopTime"], 0.0)
2582238dcc3SJonas Devlieghere        self.assertGreater(stats["launchOrAttachTime"], 0.0)
2592238dcc3SJonas Devlieghere        self.assertGreater(stats["targetCreateTime"], 0.0)
260c571988eSGreg Clayton
261cd8122b2SJonas Devlieghere    def test_memory(self):
262cd8122b2SJonas Devlieghere        """
263cd8122b2SJonas Devlieghere        Test "statistics dump" and the memory information.
264cd8122b2SJonas Devlieghere        """
2653db7cc1bSGreg Clayton        self.build()
266cd8122b2SJonas Devlieghere        exe = self.getBuildArtifact("a.out")
267cd8122b2SJonas Devlieghere        target = self.createTestTarget(file_path=exe)
268cd8122b2SJonas Devlieghere        debug_stats = self.get_stats()
269cd8122b2SJonas Devlieghere        debug_stat_keys = [
2702238dcc3SJonas Devlieghere            "memory",
2712238dcc3SJonas Devlieghere            "modules",
2722238dcc3SJonas Devlieghere            "targets",
2732238dcc3SJonas Devlieghere            "totalSymbolTableParseTime",
2742238dcc3SJonas Devlieghere            "totalSymbolTableIndexTime",
2752238dcc3SJonas Devlieghere            "totalSymbolTablesLoadedFromCache",
2762238dcc3SJonas Devlieghere            "totalSymbolTablesSavedToCache",
2772238dcc3SJonas Devlieghere            "totalDebugInfoParseTime",
2782238dcc3SJonas Devlieghere            "totalDebugInfoIndexTime",
2792238dcc3SJonas Devlieghere            "totalDebugInfoIndexLoadedFromCache",
2802238dcc3SJonas Devlieghere            "totalDebugInfoIndexSavedToCache",
2812238dcc3SJonas Devlieghere            "totalDebugInfoByteSize",
282cd8122b2SJonas Devlieghere        ]
283cd8122b2SJonas Devlieghere        self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
284cd8122b2SJonas Devlieghere
2852238dcc3SJonas Devlieghere        memory = debug_stats["memory"]
286cd8122b2SJonas Devlieghere        memory_keys = [
2872238dcc3SJonas Devlieghere            "strings",
288cd8122b2SJonas Devlieghere        ]
289cd8122b2SJonas Devlieghere        self.verify_keys(memory, '"memory"', memory_keys, None)
290cd8122b2SJonas Devlieghere
2912238dcc3SJonas Devlieghere        strings = memory["strings"]
292cd8122b2SJonas Devlieghere        strings_keys = [
2932238dcc3SJonas Devlieghere            "bytesTotal",
2942238dcc3SJonas Devlieghere            "bytesUsed",
2952238dcc3SJonas Devlieghere            "bytesUnused",
296cd8122b2SJonas Devlieghere        ]
297cd8122b2SJonas Devlieghere        self.verify_keys(strings, '"strings"', strings_keys, None)
298cd8122b2SJonas Devlieghere
299c571988eSGreg Clayton    def find_module_in_metrics(self, path, stats):
3002238dcc3SJonas Devlieghere        modules = stats["modules"]
301c571988eSGreg Clayton        for module in modules:
3022238dcc3SJonas Devlieghere            if module["path"] == path:
303c571988eSGreg Clayton                return module
304c571988eSGreg Clayton        return None
305c571988eSGreg Clayton
3063db7cc1bSGreg Clayton    def find_module_by_id_in_metrics(self, id, stats):
3072238dcc3SJonas Devlieghere        modules = stats["modules"]
3083db7cc1bSGreg Clayton        for module in modules:
3092238dcc3SJonas Devlieghere            if module["identifier"] == id:
3103db7cc1bSGreg Clayton                return module
3113db7cc1bSGreg Clayton        return None
3123db7cc1bSGreg Clayton
313c571988eSGreg Clayton    def test_modules(self):
314c571988eSGreg Clayton        """
315c571988eSGreg Clayton        Test "statistics dump" and the module information.
316c571988eSGreg Clayton        """
3173db7cc1bSGreg Clayton        self.build()
318c571988eSGreg Clayton        exe = self.getBuildArtifact("a.out")
319c571988eSGreg Clayton        target = self.createTestTarget(file_path=exe)
320c571988eSGreg Clayton        debug_stats = self.get_stats()
321c571988eSGreg Clayton        debug_stat_keys = [
3222238dcc3SJonas Devlieghere            "memory",
3232238dcc3SJonas Devlieghere            "modules",
3242238dcc3SJonas Devlieghere            "targets",
3252238dcc3SJonas Devlieghere            "totalSymbolTableParseTime",
3262238dcc3SJonas Devlieghere            "totalSymbolTableIndexTime",
3272238dcc3SJonas Devlieghere            "totalSymbolTablesLoadedFromCache",
3282238dcc3SJonas Devlieghere            "totalSymbolTablesSavedToCache",
3292238dcc3SJonas Devlieghere            "totalDebugInfoParseTime",
3302238dcc3SJonas Devlieghere            "totalDebugInfoIndexTime",
3312238dcc3SJonas Devlieghere            "totalDebugInfoIndexLoadedFromCache",
3322238dcc3SJonas Devlieghere            "totalDebugInfoIndexSavedToCache",
3332238dcc3SJonas Devlieghere            "totalDebugInfoByteSize",
334c571988eSGreg Clayton        ]
335c571988eSGreg Clayton        self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
3362238dcc3SJonas Devlieghere        stats = debug_stats["targets"][0]
337c571988eSGreg Clayton        keys_exist = [
3382238dcc3SJonas Devlieghere            "moduleIdentifiers",
339c571988eSGreg Clayton        ]
340c571988eSGreg Clayton        self.verify_keys(stats, '"stats"', keys_exist, None)
341c571988eSGreg Clayton        exe_module = self.find_module_in_metrics(exe, debug_stats)
342c571988eSGreg Clayton        module_keys = [
3432238dcc3SJonas Devlieghere            "debugInfoByteSize",
3442238dcc3SJonas Devlieghere            "debugInfoIndexLoadedFromCache",
3452238dcc3SJonas Devlieghere            "debugInfoIndexTime",
3462238dcc3SJonas Devlieghere            "debugInfoIndexSavedToCache",
3472238dcc3SJonas Devlieghere            "debugInfoParseTime",
3482238dcc3SJonas Devlieghere            "identifier",
3492238dcc3SJonas Devlieghere            "path",
3502238dcc3SJonas Devlieghere            "symbolTableIndexTime",
3512238dcc3SJonas Devlieghere            "symbolTableLoadedFromCache",
3522238dcc3SJonas Devlieghere            "symbolTableParseTime",
3532238dcc3SJonas Devlieghere            "symbolTableSavedToCache",
3542238dcc3SJonas Devlieghere            "triple",
3552238dcc3SJonas Devlieghere            "uuid",
356c571988eSGreg Clayton        ]
357c571988eSGreg Clayton        self.assertNotEqual(exe_module, None)
358c571988eSGreg Clayton        self.verify_keys(exe_module, 'module dict for "%s"' % (exe), module_keys)
359fb254968SGreg Clayton
36076706090Sjeffreytan81    def test_commands(self):
36176706090Sjeffreytan81        """
36276706090Sjeffreytan81        Test "statistics dump" and the command information.
36376706090Sjeffreytan81        """
36476706090Sjeffreytan81        self.build()
36576706090Sjeffreytan81        exe = self.getBuildArtifact("a.out")
36676706090Sjeffreytan81        target = self.createTestTarget(file_path=exe)
36776706090Sjeffreytan81
36876706090Sjeffreytan81        interp = self.dbg.GetCommandInterpreter()
36976706090Sjeffreytan81        result = lldb.SBCommandReturnObject()
37076706090Sjeffreytan81        interp.HandleCommand("target list", result)
37176706090Sjeffreytan81        interp.HandleCommand("target list", result)
37276706090Sjeffreytan81
37376706090Sjeffreytan81        debug_stats = self.get_stats()
37476706090Sjeffreytan81
37576706090Sjeffreytan81        command_stats = self.get_command_stats(debug_stats)
37676706090Sjeffreytan81        self.assertNotEqual(command_stats, None)
37776706090Sjeffreytan81        self.assertEqual(command_stats["target list"], 2)
37876706090Sjeffreytan81
379fb254968SGreg Clayton    def test_breakpoints(self):
380fb254968SGreg Clayton        """Test "statistics dump"
381fb254968SGreg Clayton
382fb254968SGreg Clayton        Output expected to be something like:
383fb254968SGreg Clayton
384fb254968SGreg Clayton        {
385cd8122b2SJonas Devlieghere          "memory" : {...},
386fb254968SGreg Clayton          "modules" : [...],
387fb254968SGreg Clayton          "targets" : [
388fb254968SGreg Clayton                {
389fb254968SGreg Clayton                    "firstStopTime": 0.34164492800000001,
390fb254968SGreg Clayton                    "launchOrAttachTime": 0.31969605400000001,
391fb254968SGreg Clayton                    "moduleIdentifiers": [...],
392fb254968SGreg Clayton                    "targetCreateTime": 0.0040863039999999998
393fb254968SGreg Clayton                    "expressionEvaluation": {
394fb254968SGreg Clayton                        "failures": 0,
395fb254968SGreg Clayton                        "successes": 0
396fb254968SGreg Clayton                    },
397fb254968SGreg Clayton                    "frameVariable": {
398fb254968SGreg Clayton                        "failures": 0,
399fb254968SGreg Clayton                        "successes": 0
400fb254968SGreg Clayton                    },
401fb254968SGreg Clayton                    "breakpoints": [
402fb254968SGreg Clayton                        {
403fb254968SGreg Clayton                            "details": {...},
404fb254968SGreg Clayton                            "id": 1,
405fb254968SGreg Clayton                            "resolveTime": 2.65438675
406fb254968SGreg Clayton                        },
407fb254968SGreg Clayton                        {
408fb254968SGreg Clayton                            "details": {...},
409fb254968SGreg Clayton                            "id": 2,
410fb254968SGreg Clayton                            "resolveTime": 4.3632581669999997
411fb254968SGreg Clayton                        }
412fb254968SGreg Clayton                    ]
413fb254968SGreg Clayton                }
414fb254968SGreg Clayton            ],
415fb254968SGreg Clayton            "totalDebugInfoByteSize": 182522234,
416fb254968SGreg Clayton            "totalDebugInfoIndexTime": 2.33343,
417fb254968SGreg Clayton            "totalDebugInfoParseTime": 8.2121400240000071,
418fb254968SGreg Clayton            "totalSymbolTableParseTime": 0.123,
419fb254968SGreg Clayton            "totalSymbolTableIndexTime": 0.234,
420fb254968SGreg Clayton            "totalBreakpointResolveTime": 7.0176449170000001
421fb254968SGreg Clayton        }
422fb254968SGreg Clayton
423fb254968SGreg Clayton        """
4243db7cc1bSGreg Clayton        self.build()
425fb254968SGreg Clayton        target = self.createTestTarget()
426fb254968SGreg Clayton        self.runCmd("b main.cpp:7")
427fb254968SGreg Clayton        self.runCmd("b a_function")
428fb254968SGreg Clayton        debug_stats = self.get_stats()
429fb254968SGreg Clayton        debug_stat_keys = [
4302238dcc3SJonas Devlieghere            "memory",
4312238dcc3SJonas Devlieghere            "modules",
4322238dcc3SJonas Devlieghere            "targets",
4332238dcc3SJonas Devlieghere            "totalSymbolTableParseTime",
4342238dcc3SJonas Devlieghere            "totalSymbolTableIndexTime",
4352238dcc3SJonas Devlieghere            "totalSymbolTablesLoadedFromCache",
4362238dcc3SJonas Devlieghere            "totalSymbolTablesSavedToCache",
4372238dcc3SJonas Devlieghere            "totalDebugInfoParseTime",
4382238dcc3SJonas Devlieghere            "totalDebugInfoIndexTime",
4392238dcc3SJonas Devlieghere            "totalDebugInfoIndexLoadedFromCache",
4402238dcc3SJonas Devlieghere            "totalDebugInfoIndexSavedToCache",
4412238dcc3SJonas Devlieghere            "totalDebugInfoByteSize",
442fb254968SGreg Clayton        ]
443fb254968SGreg Clayton        self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
4442238dcc3SJonas Devlieghere        target_stats = debug_stats["targets"][0]
445fb254968SGreg Clayton        keys_exist = [
4462238dcc3SJonas Devlieghere            "breakpoints",
4472238dcc3SJonas Devlieghere            "expressionEvaluation",
4482238dcc3SJonas Devlieghere            "frameVariable",
4492238dcc3SJonas Devlieghere            "targetCreateTime",
4502238dcc3SJonas Devlieghere            "moduleIdentifiers",
4512238dcc3SJonas Devlieghere            "totalBreakpointResolveTime",
45222144e20SJacob Lalonde            "summaryProviderStatistics",
453fb254968SGreg Clayton        ]
454fb254968SGreg Clayton        self.verify_keys(target_stats, '"stats"', keys_exist, None)
4552238dcc3SJonas Devlieghere        self.assertGreater(target_stats["totalBreakpointResolveTime"], 0.0)
4562238dcc3SJonas Devlieghere        breakpoints = target_stats["breakpoints"]
457fb254968SGreg Clayton        bp_keys_exist = [
4582238dcc3SJonas Devlieghere            "details",
4592238dcc3SJonas Devlieghere            "id",
4602238dcc3SJonas Devlieghere            "internal",
4612238dcc3SJonas Devlieghere            "numLocations",
4622238dcc3SJonas Devlieghere            "numResolvedLocations",
4632238dcc3SJonas Devlieghere            "resolveTime",
464fb254968SGreg Clayton        ]
465fb254968SGreg Clayton        for breakpoint in breakpoints:
4662238dcc3SJonas Devlieghere            self.verify_keys(
4672238dcc3SJonas Devlieghere                breakpoint, 'target_stats["breakpoints"]', bp_keys_exist, None
4682238dcc3SJonas Devlieghere            )
4693db7cc1bSGreg Clayton
4703db7cc1bSGreg Clayton    @skipUnlessDarwin
4713db7cc1bSGreg Clayton    @no_debug_info_test
4723db7cc1bSGreg Clayton    def test_dsym_binary_has_symfile_in_stats(self):
4733db7cc1bSGreg Clayton        """
4743db7cc1bSGreg Clayton        Test that if our executable has a stand alone dSYM file containing
4753db7cc1bSGreg Clayton        debug information, that the dSYM file path is listed as a key/value
4763db7cc1bSGreg Clayton        pair in the "a.out" binaries module stats. Also verify the the main
4773db7cc1bSGreg Clayton        executable's module statistics has a debug info size that is greater
4783db7cc1bSGreg Clayton        than zero as the dSYM contains debug info.
4793db7cc1bSGreg Clayton        """
4803db7cc1bSGreg Clayton        self.build(debug_info="dsym")
4812238dcc3SJonas Devlieghere        exe_name = "a.out"
4823db7cc1bSGreg Clayton        exe = self.getBuildArtifact(exe_name)
4833db7cc1bSGreg Clayton        dsym = self.getBuildArtifact(exe_name + ".dSYM")
4843db7cc1bSGreg Clayton        # Make sure the executable file exists after building.
4851eeeab82SJordan Rupprecht        self.assertTrue(os.path.exists(exe))
4863db7cc1bSGreg Clayton        # Make sure the dSYM file exists after building.
4871eeeab82SJordan Rupprecht        self.assertTrue(os.path.isdir(dsym))
4883db7cc1bSGreg Clayton
4893db7cc1bSGreg Clayton        # Create the target
4903db7cc1bSGreg Clayton        target = self.createTestTarget(file_path=exe)
4913db7cc1bSGreg Clayton
4923db7cc1bSGreg Clayton        debug_stats = self.get_stats()
4933db7cc1bSGreg Clayton
4943db7cc1bSGreg Clayton        exe_stats = self.find_module_in_metrics(exe, debug_stats)
4953db7cc1bSGreg Clayton        # If we have a dSYM file, there should be a key/value pair in the module
4963db7cc1bSGreg Clayton        # statistics and the path should match the dSYM file path in the build
4973db7cc1bSGreg Clayton        # artifacts.
4982238dcc3SJonas Devlieghere        self.assertIn("symbolFilePath", exe_stats)
4992238dcc3SJonas Devlieghere        stats_dsym = exe_stats["symbolFilePath"]
5003db7cc1bSGreg Clayton
5013db7cc1bSGreg Clayton        # Make sure main executable's module info has debug info size that is
5023db7cc1bSGreg Clayton        # greater than zero as the dSYM file and main executable work together
5033db7cc1bSGreg Clayton        # in the lldb.SBModule class to provide the data.
5042238dcc3SJonas Devlieghere        self.assertGreater(exe_stats["debugInfoByteSize"], 0)
5053db7cc1bSGreg Clayton
5063db7cc1bSGreg Clayton        # The "dsym" variable contains the bundle directory for the dSYM, while
5073db7cc1bSGreg Clayton        # the "stats_dsym" will have the
5083db7cc1bSGreg Clayton        self.assertIn(dsym, stats_dsym)
5093db7cc1bSGreg Clayton        # Since we have a dSYM file, we should not be loading DWARF from the .o
5103db7cc1bSGreg Clayton        # files and the .o file module identifiers should NOT be in the module
5113db7cc1bSGreg Clayton        # statistics.
5122238dcc3SJonas Devlieghere        self.assertNotIn("symbolFileModuleIdentifiers", exe_stats)
5133db7cc1bSGreg Clayton
5143db7cc1bSGreg Clayton    @skipUnlessDarwin
5153db7cc1bSGreg Clayton    @no_debug_info_test
5163db7cc1bSGreg Clayton    def test_no_dsym_binary_has_symfile_identifiers_in_stats(self):
5173db7cc1bSGreg Clayton        """
5183db7cc1bSGreg Clayton        Test that if our executable loads debug info from the .o files,
5193db7cc1bSGreg Clayton        that the module statistics contains a 'symbolFileModuleIdentifiers'
5203db7cc1bSGreg Clayton        key which is a list of module identifiers, and verify that the
5213db7cc1bSGreg Clayton        module identifier can be used to find the .o file's module stats.
5223db7cc1bSGreg Clayton        Also verify the the main executable's module statistics has a debug
5233db7cc1bSGreg Clayton        info size that is zero, as the main executable itself has no debug
5243db7cc1bSGreg Clayton        info, but verify that the .o files have debug info size that is
5253db7cc1bSGreg Clayton        greater than zero. This test ensures that we don't double count
5263db7cc1bSGreg Clayton        debug info.
5273db7cc1bSGreg Clayton        """
5283db7cc1bSGreg Clayton        self.build(debug_info="dwarf")
5292238dcc3SJonas Devlieghere        exe_name = "a.out"
5303db7cc1bSGreg Clayton        exe = self.getBuildArtifact(exe_name)
5313db7cc1bSGreg Clayton        dsym = self.getBuildArtifact(exe_name + ".dSYM")
5323db7cc1bSGreg Clayton        # Make sure the executable file exists after building.
5331eeeab82SJordan Rupprecht        self.assertTrue(os.path.exists(exe))
5343db7cc1bSGreg Clayton        # Make sure the dSYM file doesn't exist after building.
5351eeeab82SJordan Rupprecht        self.assertFalse(os.path.isdir(dsym))
5363db7cc1bSGreg Clayton
5373db7cc1bSGreg Clayton        # Create the target
5383db7cc1bSGreg Clayton        target = self.createTestTarget(file_path=exe)
5393db7cc1bSGreg Clayton
5403db7cc1bSGreg Clayton        # Force the 'main.o' .o file's DWARF to be loaded so it will show up
5413db7cc1bSGreg Clayton        # in the stats.
5423db7cc1bSGreg Clayton        self.runCmd("b main.cpp:7")
5433db7cc1bSGreg Clayton
544*24feaab8Sjeffreytan81        debug_stats = self.get_stats("--all-targets")
5453db7cc1bSGreg Clayton
5463db7cc1bSGreg Clayton        exe_stats = self.find_module_in_metrics(exe, debug_stats)
5473db7cc1bSGreg Clayton        # If we don't have a dSYM file, there should not be a key/value pair in
5483db7cc1bSGreg Clayton        # the module statistics.
5492238dcc3SJonas Devlieghere        self.assertNotIn("symbolFilePath", exe_stats)
5503db7cc1bSGreg Clayton
5513db7cc1bSGreg Clayton        # Make sure main executable's module info has debug info size that is
5523db7cc1bSGreg Clayton        # zero as there is no debug info in the main executable, only in the
5533db7cc1bSGreg Clayton        # .o files. The .o files will also only be loaded if something causes
5543db7cc1bSGreg Clayton        # them to be loaded, so we set a breakpoint to force the .o file debug
5553db7cc1bSGreg Clayton        # info to be loaded.
5562238dcc3SJonas Devlieghere        self.assertEqual(exe_stats["debugInfoByteSize"], 0)
5573db7cc1bSGreg Clayton
5583db7cc1bSGreg Clayton        # When we don't have a dSYM file, the SymbolFileDWARFDebugMap class
5593db7cc1bSGreg Clayton        # should create modules for each .o file that contains DWARF that the
5603db7cc1bSGreg Clayton        # symbol file creates, so we need to verify that we have a valid module
5613db7cc1bSGreg Clayton        # identifier for main.o that is we should not be loading DWARF from the .o
5623db7cc1bSGreg Clayton        # files and the .o file module identifiers should NOT be in the module
5633db7cc1bSGreg Clayton        # statistics.
5642238dcc3SJonas Devlieghere        self.assertIn("symbolFileModuleIdentifiers", exe_stats)
5653db7cc1bSGreg Clayton
5662238dcc3SJonas Devlieghere        symfileIDs = exe_stats["symbolFileModuleIdentifiers"]
5673db7cc1bSGreg Clayton        for symfileID in symfileIDs:
5683db7cc1bSGreg Clayton            o_module = self.find_module_by_id_in_metrics(symfileID, debug_stats)
5693db7cc1bSGreg Clayton            self.assertNotEqual(o_module, None)
5703db7cc1bSGreg Clayton            # Make sure each .o file has some debug info bytes.
5712238dcc3SJonas Devlieghere            self.assertGreater(o_module["debugInfoByteSize"], 0)
5728f893513SGreg Clayton
5738f893513SGreg Clayton    @skipUnlessDarwin
5748f893513SGreg Clayton    @no_debug_info_test
5758f893513SGreg Clayton    def test_had_frame_variable_errors(self):
5768f893513SGreg Clayton        """
5778f893513SGreg Clayton        Test that if we have frame variable errors that we see this in the
5788f893513SGreg Clayton        statistics for the module that had issues.
5798f893513SGreg Clayton        """
5808f893513SGreg Clayton        self.build(debug_info="dwarf")
5812238dcc3SJonas Devlieghere        exe_name = "a.out"
5828f893513SGreg Clayton        exe = self.getBuildArtifact(exe_name)
5838f893513SGreg Clayton        dsym = self.getBuildArtifact(exe_name + ".dSYM")
5842238dcc3SJonas Devlieghere        main_obj = self.getBuildArtifact("main.o")
5858f893513SGreg Clayton        # Make sure the executable file exists after building.
5861eeeab82SJordan Rupprecht        self.assertTrue(os.path.exists(exe))
5878f893513SGreg Clayton        # Make sure the dSYM file doesn't exist after building.
5881eeeab82SJordan Rupprecht        self.assertFalse(os.path.isdir(dsym))
5898f893513SGreg Clayton        # Make sure the main.o object file exists after building.
5901eeeab82SJordan Rupprecht        self.assertTrue(os.path.exists(main_obj))
5918f893513SGreg Clayton
5928f893513SGreg Clayton        # Delete the main.o file that contains the debug info so we force an
5938f893513SGreg Clayton        # error when we run to main and try to get variables
5948f893513SGreg Clayton        os.unlink(main_obj)
5958f893513SGreg Clayton
5962238dcc3SJonas Devlieghere        (target, process, thread, bkpt) = lldbutil.run_to_name_breakpoint(self, "main")
5978f893513SGreg Clayton
5988f893513SGreg Clayton        # Get stats and verify we had errors.
599aac1c3b1SGreg Clayton        stats = self.get_stats()
600aac1c3b1SGreg Clayton        exe_stats = self.find_module_in_metrics(exe, stats)
6019c246882SJordan Rupprecht        self.assertIsNotNone(exe_stats)
6028f893513SGreg Clayton
6038f893513SGreg Clayton        # Make sure we have "debugInfoHadVariableErrors" variable that is set to
6048f893513SGreg Clayton        # false before failing to get local variables due to missing .o file.
6051eeeab82SJordan Rupprecht        self.assertFalse(exe_stats["debugInfoHadVariableErrors"])
6068f893513SGreg Clayton
607aac1c3b1SGreg Clayton        # Verify that the top level statistic that aggregates the number of
608aac1c3b1SGreg Clayton        # modules with debugInfoHadVariableErrors is zero
6092238dcc3SJonas Devlieghere        self.assertEqual(stats["totalModuleCountWithVariableErrors"], 0)
610aac1c3b1SGreg Clayton
6118f893513SGreg Clayton        # Try and fail to get variables
6128f893513SGreg Clayton        vars = thread.GetFrameAtIndex(0).GetVariables(True, True, False, True)
6138f893513SGreg Clayton
6148f893513SGreg Clayton        # Make sure we got an error back that indicates that variables were not
6158f893513SGreg Clayton        # available
6168f893513SGreg Clayton        self.assertTrue(vars.GetError().Fail())
6178f893513SGreg Clayton
6188f893513SGreg Clayton        # Get stats and verify we had errors.
619aac1c3b1SGreg Clayton        stats = self.get_stats()
620aac1c3b1SGreg Clayton        exe_stats = self.find_module_in_metrics(exe, stats)
6219c246882SJordan Rupprecht        self.assertIsNotNone(exe_stats)
6228f893513SGreg Clayton
6238f893513SGreg Clayton        # Make sure we have "hadFrameVariableErrors" variable that is set to
6248f893513SGreg Clayton        # true after failing to get local variables due to missing .o file.
6251eeeab82SJordan Rupprecht        self.assertTrue(exe_stats["debugInfoHadVariableErrors"])
626aac1c3b1SGreg Clayton
627aac1c3b1SGreg Clayton        # Verify that the top level statistic that aggregates the number of
628aac1c3b1SGreg Clayton        # modules with debugInfoHadVariableErrors is greater than zero
6292238dcc3SJonas Devlieghere        self.assertGreater(stats["totalModuleCountWithVariableErrors"], 0)
630c2d061daSroyitaqi
631c2d061daSroyitaqi    def test_transcript_happy_path(self):
632c2d061daSroyitaqi        """
633c2d061daSroyitaqi        Test "statistics dump" and the transcript information.
634c2d061daSroyitaqi        """
635c2d061daSroyitaqi        self.build()
636c2d061daSroyitaqi        exe = self.getBuildArtifact("a.out")
637c2d061daSroyitaqi        target = self.createTestTarget(file_path=exe)
638c2d061daSroyitaqi        self.runCmd("settings set interpreter.save-transcript true")
639c2d061daSroyitaqi        self.runCmd("version")
640c2d061daSroyitaqi
641c2d061daSroyitaqi        # Verify the output of a first "statistics dump"
64270f41a8cSroyitaqi        debug_stats = self.get_stats("--transcript true")
643c2d061daSroyitaqi        self.assertIn("transcript", debug_stats)
644c2d061daSroyitaqi        transcript = debug_stats["transcript"]
645c2d061daSroyitaqi        self.assertEqual(len(transcript), 2)
646c2d061daSroyitaqi        self.assertEqual(transcript[0]["commandName"], "version")
647c2d061daSroyitaqi        self.assertEqual(transcript[1]["commandName"], "statistics dump")
648c2d061daSroyitaqi        # The first "statistics dump" in the transcript should have no output
649c2d061daSroyitaqi        self.assertNotIn("output", transcript[1])
650c2d061daSroyitaqi
651c2d061daSroyitaqi        # Verify the output of a second "statistics dump"
65270f41a8cSroyitaqi        debug_stats = self.get_stats("--transcript true")
653c2d061daSroyitaqi        self.assertIn("transcript", debug_stats)
654c2d061daSroyitaqi        transcript = debug_stats["transcript"]
655c2d061daSroyitaqi        self.assertEqual(len(transcript), 3)
656c2d061daSroyitaqi        self.assertEqual(transcript[0]["commandName"], "version")
657c2d061daSroyitaqi        self.assertEqual(transcript[1]["commandName"], "statistics dump")
658c2d061daSroyitaqi        # The first "statistics dump" in the transcript should have output now
659c2d061daSroyitaqi        self.assertIn("output", transcript[1])
660c2d061daSroyitaqi        self.assertEqual(transcript[2]["commandName"], "statistics dump")
661c2d061daSroyitaqi        # The second "statistics dump" in the transcript should have no output
662c2d061daSroyitaqi        self.assertNotIn("output", transcript[2])
663c2d061daSroyitaqi
66470f41a8cSroyitaqi    def verify_stats(self, stats, expectation, options):
66570f41a8cSroyitaqi        for field_name in expectation:
66670f41a8cSroyitaqi            idx = field_name.find(".")
66770f41a8cSroyitaqi            if idx == -1:
66870f41a8cSroyitaqi                # `field_name` is a top-level field
66970f41a8cSroyitaqi                exists = field_name in stats
67070f41a8cSroyitaqi                should_exist = expectation[field_name]
67170f41a8cSroyitaqi                should_exist_string = "" if should_exist else "not "
67270f41a8cSroyitaqi                self.assertEqual(
67370f41a8cSroyitaqi                    exists,
67470f41a8cSroyitaqi                    should_exist,
67570f41a8cSroyitaqi                    f"'{field_name}' should {should_exist_string}exist for 'statistics dump{options}'",
67670f41a8cSroyitaqi                )
67770f41a8cSroyitaqi            else:
67870f41a8cSroyitaqi                # `field_name` is a string of "<top-level field>.<second-level field>"
67970f41a8cSroyitaqi                top_level_field_name = field_name[0:idx]
68070f41a8cSroyitaqi                second_level_field_name = field_name[idx + 1 :]
68170f41a8cSroyitaqi                for top_level_field in (
68270f41a8cSroyitaqi                    stats[top_level_field_name] if top_level_field_name in stats else {}
68370f41a8cSroyitaqi                ):
68470f41a8cSroyitaqi                    exists = second_level_field_name in top_level_field
68570f41a8cSroyitaqi                    should_exist = expectation[field_name]
68670f41a8cSroyitaqi                    should_exist_string = "" if should_exist else "not "
68770f41a8cSroyitaqi                    self.assertEqual(
68870f41a8cSroyitaqi                        exists,
68970f41a8cSroyitaqi                        should_exist,
69070f41a8cSroyitaqi                        f"'{field_name}' should {should_exist_string}exist for 'statistics dump{options}'",
69170f41a8cSroyitaqi                    )
69270f41a8cSroyitaqi
69370f41a8cSroyitaqi    def get_test_cases_for_sections_existence(self):
69470f41a8cSroyitaqi        should_always_exist_or_not = {
69570f41a8cSroyitaqi            "totalDebugInfoEnabled": True,
69670f41a8cSroyitaqi            "memory": True,
69770f41a8cSroyitaqi        }
69870f41a8cSroyitaqi        test_cases = [
69970f41a8cSroyitaqi            {  # Everything mode
70070f41a8cSroyitaqi                "command_options": "",
70170f41a8cSroyitaqi                "api_options": {},
70270f41a8cSroyitaqi                "expect": {
70370f41a8cSroyitaqi                    "commands": True,
70470f41a8cSroyitaqi                    "targets": True,
70570f41a8cSroyitaqi                    "targets.moduleIdentifiers": True,
70670f41a8cSroyitaqi                    "targets.breakpoints": True,
70770f41a8cSroyitaqi                    "targets.expressionEvaluation": True,
708f65a52abSroyitaqi                    "targets.frameVariable": True,
709f65a52abSroyitaqi                    "targets.totalSharedLibraryEventHitCount": True,
71070f41a8cSroyitaqi                    "modules": True,
71170f41a8cSroyitaqi                    "transcript": True,
71270f41a8cSroyitaqi                },
71370f41a8cSroyitaqi            },
71470f41a8cSroyitaqi            {  # Summary mode
71570f41a8cSroyitaqi                "command_options": " --summary",
71670f41a8cSroyitaqi                "api_options": {
71770f41a8cSroyitaqi                    "SetSummaryOnly": True,
71870f41a8cSroyitaqi                },
71970f41a8cSroyitaqi                "expect": {
72070f41a8cSroyitaqi                    "commands": False,
721f65a52abSroyitaqi                    "targets": True,
72270f41a8cSroyitaqi                    "targets.moduleIdentifiers": False,
72370f41a8cSroyitaqi                    "targets.breakpoints": False,
72470f41a8cSroyitaqi                    "targets.expressionEvaluation": False,
725f65a52abSroyitaqi                    "targets.frameVariable": False,
726f65a52abSroyitaqi                    "targets.totalSharedLibraryEventHitCount": True,
72770f41a8cSroyitaqi                    "modules": False,
72870f41a8cSroyitaqi                    "transcript": False,
72970f41a8cSroyitaqi                },
73070f41a8cSroyitaqi            },
73170f41a8cSroyitaqi            {  # Summary mode with targets
73270f41a8cSroyitaqi                "command_options": " --summary --targets=true",
73370f41a8cSroyitaqi                "api_options": {
73470f41a8cSroyitaqi                    "SetSummaryOnly": True,
73570f41a8cSroyitaqi                    "SetIncludeTargets": True,
73670f41a8cSroyitaqi                },
73770f41a8cSroyitaqi                "expect": {
73870f41a8cSroyitaqi                    "commands": False,
73970f41a8cSroyitaqi                    "targets": True,
74070f41a8cSroyitaqi                    "targets.moduleIdentifiers": False,
74170f41a8cSroyitaqi                    "targets.breakpoints": False,
74270f41a8cSroyitaqi                    "targets.expressionEvaluation": False,
743f65a52abSroyitaqi                    "targets.frameVariable": False,
74470f41a8cSroyitaqi                    "targets.totalSharedLibraryEventHitCount": True,
74570f41a8cSroyitaqi                    "modules": False,
74670f41a8cSroyitaqi                    "transcript": False,
74770f41a8cSroyitaqi                },
74870f41a8cSroyitaqi            },
749f65a52abSroyitaqi            {  # Summary mode without targets
750f65a52abSroyitaqi                "command_options": " --summary --targets=false",
751f65a52abSroyitaqi                "api_options": {
752f65a52abSroyitaqi                    "SetSummaryOnly": True,
753f65a52abSroyitaqi                    "SetIncludeTargets": False,
754f65a52abSroyitaqi                },
755f65a52abSroyitaqi                "expect": {
756f65a52abSroyitaqi                    "commands": False,
757f65a52abSroyitaqi                    "targets": False,
758f65a52abSroyitaqi                    "modules": False,
759f65a52abSroyitaqi                    "transcript": False,
760f65a52abSroyitaqi                },
761f65a52abSroyitaqi            },
76270f41a8cSroyitaqi            {  # Summary mode with modules
76370f41a8cSroyitaqi                "command_options": " --summary --modules=true",
76470f41a8cSroyitaqi                "api_options": {
76570f41a8cSroyitaqi                    "SetSummaryOnly": True,
76670f41a8cSroyitaqi                    "SetIncludeModules": True,
76770f41a8cSroyitaqi                },
76870f41a8cSroyitaqi                "expect": {
76970f41a8cSroyitaqi                    "commands": False,
770f65a52abSroyitaqi                    "targets": True,
77170f41a8cSroyitaqi                    "targets.moduleIdentifiers": False,
77270f41a8cSroyitaqi                    "targets.breakpoints": False,
77370f41a8cSroyitaqi                    "targets.expressionEvaluation": False,
774f65a52abSroyitaqi                    "targets.frameVariable": False,
775f65a52abSroyitaqi                    "targets.totalSharedLibraryEventHitCount": True,
77670f41a8cSroyitaqi                    "modules": True,
77770f41a8cSroyitaqi                    "transcript": False,
77870f41a8cSroyitaqi                },
77970f41a8cSroyitaqi            },
780f65a52abSroyitaqi            {  # Default mode without modules and transcript
78170f41a8cSroyitaqi                "command_options": " --modules=false --transcript=false",
78270f41a8cSroyitaqi                "api_options": {
78370f41a8cSroyitaqi                    "SetIncludeModules": False,
78470f41a8cSroyitaqi                    "SetIncludeTranscript": False,
78570f41a8cSroyitaqi                },
78670f41a8cSroyitaqi                "expect": {
78770f41a8cSroyitaqi                    "commands": True,
78870f41a8cSroyitaqi                    "targets": True,
78970f41a8cSroyitaqi                    "targets.moduleIdentifiers": False,
79070f41a8cSroyitaqi                    "targets.breakpoints": True,
79170f41a8cSroyitaqi                    "targets.expressionEvaluation": True,
792f65a52abSroyitaqi                    "targets.frameVariable": True,
793f65a52abSroyitaqi                    "targets.totalSharedLibraryEventHitCount": True,
79470f41a8cSroyitaqi                    "modules": False,
79570f41a8cSroyitaqi                    "transcript": False,
79670f41a8cSroyitaqi                },
79770f41a8cSroyitaqi            },
798f65a52abSroyitaqi            {  # Default mode without modules
79970f41a8cSroyitaqi                "command_options": " --modules=false",
80070f41a8cSroyitaqi                "api_options": {
80170f41a8cSroyitaqi                    "SetIncludeModules": False,
80270f41a8cSroyitaqi                },
80370f41a8cSroyitaqi                "expect": {
80470f41a8cSroyitaqi                    "commands": True,
80570f41a8cSroyitaqi                    "targets": True,
80670f41a8cSroyitaqi                    "targets.moduleIdentifiers": False,
80770f41a8cSroyitaqi                    "targets.breakpoints": True,
80870f41a8cSroyitaqi                    "targets.expressionEvaluation": True,
809f65a52abSroyitaqi                    "targets.frameVariable": True,
810f65a52abSroyitaqi                    "targets.totalSharedLibraryEventHitCount": True,
81170f41a8cSroyitaqi                    "modules": False,
81270f41a8cSroyitaqi                    "transcript": True,
81370f41a8cSroyitaqi                },
81470f41a8cSroyitaqi            },
81570f41a8cSroyitaqi        ]
81670f41a8cSroyitaqi        return (should_always_exist_or_not, test_cases)
81770f41a8cSroyitaqi
81870f41a8cSroyitaqi    def test_sections_existence_through_command(self):
819c2d061daSroyitaqi        """
82070f41a8cSroyitaqi        Test "statistics dump" and the existence of sections when different
82170f41a8cSroyitaqi        options are given through the command line (CLI or HandleCommand).
822c2d061daSroyitaqi        """
823c2d061daSroyitaqi        self.build()
824c2d061daSroyitaqi        exe = self.getBuildArtifact("a.out")
825c2d061daSroyitaqi        target = self.createTestTarget(file_path=exe)
82670f41a8cSroyitaqi
82770f41a8cSroyitaqi        # Create some transcript so that it can be tested.
828c2d061daSroyitaqi        self.runCmd("settings set interpreter.save-transcript true")
829c2d061daSroyitaqi        self.runCmd("version")
83070f41a8cSroyitaqi        self.runCmd("b main")
83170f41a8cSroyitaqi        # Then disable transcript so that it won't change during verification
83270f41a8cSroyitaqi        self.runCmd("settings set interpreter.save-transcript false")
833c2d061daSroyitaqi
83470f41a8cSroyitaqi        # Expectation
83570f41a8cSroyitaqi        (
83670f41a8cSroyitaqi            should_always_exist_or_not,
83770f41a8cSroyitaqi            test_cases,
83870f41a8cSroyitaqi        ) = self.get_test_cases_for_sections_existence()
83970f41a8cSroyitaqi
84070f41a8cSroyitaqi        # Verification
84170f41a8cSroyitaqi        for test_case in test_cases:
84270f41a8cSroyitaqi            options = test_case["command_options"]
84370f41a8cSroyitaqi            # Get statistics dump result
84470f41a8cSroyitaqi            stats = self.get_stats(options)
84570f41a8cSroyitaqi            # Verify that each field should exist (or not)
84670f41a8cSroyitaqi            expectation = {**should_always_exist_or_not, **test_case["expect"]}
84770f41a8cSroyitaqi            self.verify_stats(stats, expectation, options)
84870f41a8cSroyitaqi
84970f41a8cSroyitaqi    def test_sections_existence_through_api(self):
85070f41a8cSroyitaqi        """
85170f41a8cSroyitaqi        Test "statistics dump" and the existence of sections when different
85270f41a8cSroyitaqi        options are given through the public API.
85370f41a8cSroyitaqi        """
85470f41a8cSroyitaqi        self.build()
85570f41a8cSroyitaqi        exe = self.getBuildArtifact("a.out")
85670f41a8cSroyitaqi        target = self.createTestTarget(file_path=exe)
85770f41a8cSroyitaqi
85870f41a8cSroyitaqi        # Create some transcript so that it can be tested.
85970f41a8cSroyitaqi        self.runCmd("settings set interpreter.save-transcript true")
86070f41a8cSroyitaqi        self.runCmd("version")
86170f41a8cSroyitaqi        self.runCmd("b main")
86270f41a8cSroyitaqi        # But disable transcript so that it won't change during verification
86370f41a8cSroyitaqi        self.runCmd("settings set interpreter.save-transcript false")
86470f41a8cSroyitaqi
86570f41a8cSroyitaqi        # Expectation
86670f41a8cSroyitaqi        (
86770f41a8cSroyitaqi            should_always_exist_or_not,
86870f41a8cSroyitaqi            test_cases,
86970f41a8cSroyitaqi        ) = self.get_test_cases_for_sections_existence()
87070f41a8cSroyitaqi
87170f41a8cSroyitaqi        # Verification
87270f41a8cSroyitaqi        for test_case in test_cases:
87370f41a8cSroyitaqi            # Create options
87470f41a8cSroyitaqi            options = test_case["api_options"]
87570f41a8cSroyitaqi            sb_options = lldb.SBStatisticsOptions()
87670f41a8cSroyitaqi            for method_name, param_value in options.items():
87770f41a8cSroyitaqi                getattr(sb_options, method_name)(param_value)
87870f41a8cSroyitaqi            # Get statistics dump result
87970f41a8cSroyitaqi            stream = lldb.SBStream()
88070f41a8cSroyitaqi            target.GetStatistics(sb_options).GetAsJSON(stream)
88170f41a8cSroyitaqi            stats = json.loads(stream.GetData())
88270f41a8cSroyitaqi            # Verify that each field should exist (or not)
88370f41a8cSroyitaqi            expectation = {**should_always_exist_or_not, **test_case["expect"]}
88470f41a8cSroyitaqi            self.verify_stats(stats, expectation, options)
88570f41a8cSroyitaqi
88670f41a8cSroyitaqi    def test_order_of_options_do_not_matter(self):
88770f41a8cSroyitaqi        """
88870f41a8cSroyitaqi        Test "statistics dump" and the order of options.
88970f41a8cSroyitaqi        """
89070f41a8cSroyitaqi        self.build()
89170f41a8cSroyitaqi        exe = self.getBuildArtifact("a.out")
89270f41a8cSroyitaqi        target = self.createTestTarget(file_path=exe)
89370f41a8cSroyitaqi
89470f41a8cSroyitaqi        # Create some transcript so that it can be tested.
89570f41a8cSroyitaqi        self.runCmd("settings set interpreter.save-transcript true")
89670f41a8cSroyitaqi        self.runCmd("version")
89770f41a8cSroyitaqi        self.runCmd("b main")
89870f41a8cSroyitaqi        # Then disable transcript so that it won't change during verification
89970f41a8cSroyitaqi        self.runCmd("settings set interpreter.save-transcript false")
90070f41a8cSroyitaqi
90170f41a8cSroyitaqi        # The order of the following options shouldn't matter
90270f41a8cSroyitaqi        test_cases = [
90370f41a8cSroyitaqi            (" --summary", " --targets=true"),
90470f41a8cSroyitaqi            (" --summary", " --targets=false"),
90570f41a8cSroyitaqi            (" --summary", " --modules=true"),
90670f41a8cSroyitaqi            (" --summary", " --modules=false"),
90770f41a8cSroyitaqi            (" --summary", " --transcript=true"),
90870f41a8cSroyitaqi            (" --summary", " --transcript=false"),
90970f41a8cSroyitaqi        ]
91070f41a8cSroyitaqi
91170f41a8cSroyitaqi        # Verification
91270f41a8cSroyitaqi        for options in test_cases:
91370f41a8cSroyitaqi            debug_stats_0 = self.get_stats(options[0] + options[1])
91470f41a8cSroyitaqi            debug_stats_1 = self.get_stats(options[1] + options[0])
91570f41a8cSroyitaqi            # Redact all numbers
91670f41a8cSroyitaqi            debug_stats_0 = re.sub(r"\d+", "0", json.dumps(debug_stats_0))
91770f41a8cSroyitaqi            debug_stats_1 = re.sub(r"\d+", "0", json.dumps(debug_stats_1))
91870f41a8cSroyitaqi            # Verify that the two output are the same
91970f41a8cSroyitaqi            self.assertEqual(
92070f41a8cSroyitaqi                debug_stats_0,
92170f41a8cSroyitaqi                debug_stats_1,
92270f41a8cSroyitaqi                f"The order of options '{options[0]}' and '{options[1]}' should not matter",
92370f41a8cSroyitaqi            )
92422144e20SJacob Lalonde
92510c04d98SJacob Lalonde    @skipIfWindows
92622144e20SJacob Lalonde    def test_summary_statistics_providers(self):
92722144e20SJacob Lalonde        """
92822144e20SJacob Lalonde        Test summary timing statistics is included in statistics dump when
92922144e20SJacob Lalonde        a type with a summary provider exists, and is evaluated.
93022144e20SJacob Lalonde        """
93122144e20SJacob Lalonde
93222144e20SJacob Lalonde        self.build()
93322144e20SJacob Lalonde        target = self.createTestTarget()
93422144e20SJacob Lalonde        lldbutil.run_to_source_breakpoint(
93522144e20SJacob Lalonde            self, "// stop here", lldb.SBFileSpec("main.cpp")
93622144e20SJacob Lalonde        )
93722144e20SJacob Lalonde        self.expect("frame var", substrs=["hello world"])
93822144e20SJacob Lalonde        stats = self.get_target_stats(self.get_stats())
93922144e20SJacob Lalonde        self.assertIn("summaryProviderStatistics", stats)
94022144e20SJacob Lalonde        summary_providers = stats["summaryProviderStatistics"]
94122144e20SJacob Lalonde        # We don't want to take a dependency on the type name, so we just look
94222144e20SJacob Lalonde        # for string and that it was called once.
94322144e20SJacob Lalonde        summary_provider_str = str(summary_providers)
94422144e20SJacob Lalonde        self.assertIn("string", summary_provider_str)
94522144e20SJacob Lalonde        self.assertIn("'count': 1", summary_provider_str)
94622144e20SJacob Lalonde        self.assertIn("'totalTime':", summary_provider_str)
94722144e20SJacob Lalonde        # We may hit the std::string C++ provider, or a summary provider string
94822144e20SJacob Lalonde        self.assertIn("'type':", summary_provider_str)
94922144e20SJacob Lalonde        self.assertTrue(
95022144e20SJacob Lalonde            "c++" in summary_provider_str or "string" in summary_provider_str
95122144e20SJacob Lalonde        )
95222144e20SJacob Lalonde
95322144e20SJacob Lalonde        self.runCmd("continue")
95422144e20SJacob Lalonde        self.runCmd("command script import BoxFormatter.py")
95522144e20SJacob Lalonde        self.expect("frame var", substrs=["box = [27]"])
95622144e20SJacob Lalonde        stats = self.get_target_stats(self.get_stats())
95722144e20SJacob Lalonde        self.assertIn("summaryProviderStatistics", stats)
95822144e20SJacob Lalonde        summary_providers = stats["summaryProviderStatistics"]
95922144e20SJacob Lalonde        summary_provider_str = str(summary_providers)
96022144e20SJacob Lalonde        self.assertIn("BoxFormatter.summary", summary_provider_str)
96122144e20SJacob Lalonde        self.assertIn("'count': 1", summary_provider_str)
96222144e20SJacob Lalonde        self.assertIn("'totalTime':", summary_provider_str)
96322144e20SJacob Lalonde        self.assertIn("'type': 'python'", summary_provider_str)
96422144e20SJacob Lalonde
96510c04d98SJacob Lalonde    @skipIfWindows
96622144e20SJacob Lalonde    def test_summary_statistics_providers_vec(self):
96722144e20SJacob Lalonde        """
96822144e20SJacob Lalonde        Test summary timing statistics is included in statistics dump when
96922144e20SJacob Lalonde        a type with a summary provider exists, and is evaluated. This variation
97022144e20SJacob Lalonde        tests that vector recurses into it's child type.
97122144e20SJacob Lalonde        """
97222144e20SJacob Lalonde        self.build()
97322144e20SJacob Lalonde        target = self.createTestTarget()
97422144e20SJacob Lalonde        lldbutil.run_to_source_breakpoint(
97522144e20SJacob Lalonde            self, "// stop vector", lldb.SBFileSpec("main.cpp")
97622144e20SJacob Lalonde        )
97722144e20SJacob Lalonde        self.expect(
97822144e20SJacob Lalonde            "frame var", substrs=["int_vec", "double_vec", "[0] = 1", "[7] = 8"]
97922144e20SJacob Lalonde        )
98022144e20SJacob Lalonde        stats = self.get_target_stats(self.get_stats())
98122144e20SJacob Lalonde        self.assertIn("summaryProviderStatistics", stats)
98222144e20SJacob Lalonde        summary_providers = stats["summaryProviderStatistics"]
98322144e20SJacob Lalonde        summary_provider_str = str(summary_providers)
98422144e20SJacob Lalonde        self.assertIn("'count': 2", summary_provider_str)
98522144e20SJacob Lalonde        self.assertIn("'totalTime':", summary_provider_str)
98622144e20SJacob Lalonde        self.assertIn("'type':", summary_provider_str)
98722144e20SJacob Lalonde        # We may hit the std::vector C++ provider, or a summary provider string
98822144e20SJacob Lalonde        if "c++" in summary_provider_str:
98922144e20SJacob Lalonde            self.assertIn("std::vector", summary_provider_str)
990*24feaab8Sjeffreytan81
991*24feaab8Sjeffreytan81    @skipIfWindows
992*24feaab8Sjeffreytan81    def test_multiple_targets(self):
993*24feaab8Sjeffreytan81        """
994*24feaab8Sjeffreytan81        Test statistics dump only reports the stats from current target and
995*24feaab8Sjeffreytan81        "statistics dump --all-targets" includes all target stats.
996*24feaab8Sjeffreytan81        """
997*24feaab8Sjeffreytan81        da = {"CXX_SOURCES": "main.cpp", "EXE": self.getBuildArtifact("a.out")}
998*24feaab8Sjeffreytan81        self.build(dictionary=da)
999*24feaab8Sjeffreytan81        self.addTearDownCleanup(dictionary=da)
1000*24feaab8Sjeffreytan81
1001*24feaab8Sjeffreytan81        db = {"CXX_SOURCES": "second.cpp", "EXE": self.getBuildArtifact("second.out")}
1002*24feaab8Sjeffreytan81        self.build(dictionary=db)
1003*24feaab8Sjeffreytan81        self.addTearDownCleanup(dictionary=db)
1004*24feaab8Sjeffreytan81
1005*24feaab8Sjeffreytan81        main_exe = self.getBuildArtifact("a.out")
1006*24feaab8Sjeffreytan81        second_exe = self.getBuildArtifact("second.out")
1007*24feaab8Sjeffreytan81
1008*24feaab8Sjeffreytan81        (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
1009*24feaab8Sjeffreytan81            self, "// break here", lldb.SBFileSpec("main.cpp"), None, "a.out"
1010*24feaab8Sjeffreytan81        )
1011*24feaab8Sjeffreytan81        debugger_stats1 = self.get_stats()
1012*24feaab8Sjeffreytan81        self.assertIsNotNone(self.find_module_in_metrics(main_exe, debugger_stats1))
1013*24feaab8Sjeffreytan81        self.assertIsNone(self.find_module_in_metrics(second_exe, debugger_stats1))
1014*24feaab8Sjeffreytan81
1015*24feaab8Sjeffreytan81        (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
1016*24feaab8Sjeffreytan81            self, "// break here", lldb.SBFileSpec("second.cpp"), None, "second.out"
1017*24feaab8Sjeffreytan81        )
1018*24feaab8Sjeffreytan81        debugger_stats2 = self.get_stats()
1019*24feaab8Sjeffreytan81        self.assertIsNone(self.find_module_in_metrics(main_exe, debugger_stats2))
1020*24feaab8Sjeffreytan81        self.assertIsNotNone(self.find_module_in_metrics(second_exe, debugger_stats2))
1021*24feaab8Sjeffreytan81
1022*24feaab8Sjeffreytan81        all_targets_stats = self.get_stats("--all-targets")
1023*24feaab8Sjeffreytan81        self.assertIsNotNone(self.find_module_in_metrics(main_exe, all_targets_stats))
1024*24feaab8Sjeffreytan81        self.assertIsNotNone(self.find_module_in_metrics(second_exe, all_targets_stats))
1025