Skip to content

[lldb] Add symbol/table count into statistics #136226

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 21, 2025

Conversation

royitaqi
Copy link
Contributor

@royitaqi royitaqi commented Apr 17, 2025

New stats

The following stats are added and are available in both "statistics dump" command and in python API.

  1. In summary:
    1. Add totalSymbolsLoaded. The total number of symbols loaded in all modules.
    2. Add totalSymbolTablesLoaded . The total number symbol tables loaded in all modules.
  2. In each module's stats:
    1. Add symbolsLoaded. The number of symbols loaded in the current module.

Example

Example statistics dump output:

(lldb) statistics dump
{
  ...,
  "modules": [
    {
      "path": "/Users/<username>/demo/simple/a.out",
      "symbolsLoaded": 6,
      ...
    },
    ...
  ],
  ...
  "totalSymbolTablesLoaded": 42,
  "totalSymbolsLoaded": 32198
}

Tests

Manual test: Built and ran lldb on a helloworld program. Ran statistics dump. Verified the above stats.

Unit test: Ran the following tests:

$ bin/lldb-dotest -p TestStats.py ~/llvm-sand/external/llvm-project/lldb/test/API/commands/statistics/basic/
...
Ran 18 tests in 192.676s

OK (skipped=3)

@royitaqi royitaqi requested a review from JDevlieghere as a code owner April 17, 2025 23:35
@llvmbot llvmbot added the lldb label Apr 17, 2025
@llvmbot
Copy link
Member

llvmbot commented Apr 17, 2025

@llvm/pr-subscribers-lldb

Author: None (royitaqi)

Changes

These stats are added or changed:

  1. In summary:
    1. Add totalSymbolsLoaded. The total number of symbols loaded in all modules.
    2. Add totalSymbolTablesLoaded . The total number symbol tables loaded in all modules.
  2. In each module's stats:
    1. Add symbolsLoaded. The number of symbols loaded in the current module.

--

Still running tests. Will update here.


Full diff: https://github.com/llvm/llvm-project/pull/136226.diff

3 Files Affected:

  • (modified) lldb/include/lldb/Target/Statistics.h (+1)
  • (modified) lldb/source/Target/Statistics.cpp (+13-5)
  • (modified) lldb/test/API/commands/statistics/basic/TestStats.py (+18-5)
diff --git a/lldb/include/lldb/Target/Statistics.h b/lldb/include/lldb/Target/Statistics.h
index ee365357fcf31..b87a12a8ab9cd 100644
--- a/lldb/include/lldb/Target/Statistics.h
+++ b/lldb/include/lldb/Target/Statistics.h
@@ -120,6 +120,7 @@ struct ModuleStats {
   llvm::StringMap<llvm::json::Value> type_system_stats;
   double symtab_parse_time = 0.0;
   double symtab_index_time = 0.0;
+  uint32_t num_symbols_loaded = 0;
   double debug_parse_time = 0.0;
   double debug_index_time = 0.0;
   uint64_t debug_info_size = 0;
diff --git a/lldb/source/Target/Statistics.cpp b/lldb/source/Target/Statistics.cpp
index b5d2e7bda1edf..f70fb529544a5 100644
--- a/lldb/source/Target/Statistics.cpp
+++ b/lldb/source/Target/Statistics.cpp
@@ -71,6 +71,7 @@ json::Value ModuleStats::ToJSON() const {
   module.try_emplace("debugInfoHadIncompleteTypes",
                      debug_info_had_incomplete_types);
   module.try_emplace("symbolTableStripped", symtab_stripped);
+  module.try_emplace("symbolsLoaded", num_symbols_loaded);
   if (!symfile_path.empty())
     module.try_emplace("symbolFilePath", symfile_path);
 
@@ -293,7 +294,8 @@ llvm::json::Value DebuggerStats::ReportStatistics(
   double debug_parse_time = 0.0;
   double debug_index_time = 0.0;
   uint32_t symtabs_loaded = 0;
-  uint32_t symtabs_saved = 0;
+  uint32_t symtabs_loaded_from_cache = 0;
+  uint32_t symtabs_saved_to_cache = 0;
   uint32_t debug_index_loaded = 0;
   uint32_t debug_index_saved = 0;
   uint64_t debug_info_size = 0;
@@ -309,6 +311,7 @@ llvm::json::Value DebuggerStats::ReportStatistics(
   uint32_t num_modules_with_variable_errors = 0;
   uint32_t num_modules_with_incomplete_types = 0;
   uint32_t num_stripped_modules = 0;
+  uint32_t num_symbols_loaded = 0;
   for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) {
     Module *module = target != nullptr
                          ? target->GetImages().GetModuleAtIndex(image_idx).get()
@@ -318,12 +321,15 @@ llvm::json::Value DebuggerStats::ReportStatistics(
     module_stat.symtab_index_time = module->GetSymtabIndexTime().get().count();
     Symtab *symtab = module->GetSymtab(/*can_create=*/false);
     if (symtab) {
+      module_stat.num_symbols_loaded = symtab->GetNumSymbols();
+      num_symbols_loaded += module_stat.num_symbols_loaded;
+      symtabs_loaded++;
       module_stat.symtab_loaded_from_cache = symtab->GetWasLoadedFromCache();
       if (module_stat.symtab_loaded_from_cache)
-        ++symtabs_loaded;
+        ++symtabs_loaded_from_cache;
       module_stat.symtab_saved_to_cache = symtab->GetWasSavedToCache();
       if (module_stat.symtab_saved_to_cache)
-        ++symtabs_saved;
+        ++symtabs_saved_to_cache;
     }
     SymbolFile *sym_file = module->GetSymbolFile(/*can_create=*/false);
     if (sym_file) {
@@ -393,8 +399,9 @@ llvm::json::Value DebuggerStats::ReportStatistics(
   json::Object global_stats{
       {"totalSymbolTableParseTime", symtab_parse_time},
       {"totalSymbolTableIndexTime", symtab_index_time},
-      {"totalSymbolTablesLoadedFromCache", symtabs_loaded},
-      {"totalSymbolTablesSavedToCache", symtabs_saved},
+      {"totalSymbolTablesLoaded", symtabs_loaded},
+      {"totalSymbolTablesLoadedFromCache", symtabs_loaded_from_cache},
+      {"totalSymbolTablesSavedToCache", symtabs_saved_to_cache},
       {"totalDebugInfoParseTime", debug_parse_time},
       {"totalDebugInfoIndexTime", debug_index_time},
       {"totalDebugInfoIndexLoadedFromCache", debug_index_loaded},
@@ -407,6 +414,7 @@ llvm::json::Value DebuggerStats::ReportStatistics(
        num_modules_with_incomplete_types},
       {"totalDebugInfoEnabled", num_debug_info_enabled_modules},
       {"totalSymbolTableStripped", num_stripped_modules},
+      {"totalSymbolsLoaded", num_symbols_loaded},
   };
 
   if (include_targets) {
diff --git a/lldb/test/API/commands/statistics/basic/TestStats.py b/lldb/test/API/commands/statistics/basic/TestStats.py
index 54881c13bcb68..5dcad88af7b84 100644
--- a/lldb/test/API/commands/statistics/basic/TestStats.py
+++ b/lldb/test/API/commands/statistics/basic/TestStats.py
@@ -159,6 +159,8 @@ def test_default_no_run(self):
         """
         self.build()
         target = self.createTestTarget()
+
+        # Verify top-level keys.
         debug_stats = self.get_stats()
         debug_stat_keys = [
             "memory",
@@ -168,6 +170,7 @@ def test_default_no_run(self):
             "totalSymbolTableIndexTime",
             "totalSymbolTablesLoadedFromCache",
             "totalSymbolTablesSavedToCache",
+            "totalSymbolsLoaded",
             "totalDebugInfoByteSize",
             "totalDebugInfoIndexTime",
             "totalDebugInfoIndexLoadedFromCache",
@@ -175,16 +178,26 @@ def test_default_no_run(self):
             "totalDebugInfoParseTime",
         ]
         self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
-        stats = debug_stats["targets"][0]
-        keys_exist = [
+        
+        # Verify target stats keys.
+        target_stats = debug_stats["targets"][0]
+        target_stat_keys_exist = [
             "expressionEvaluation",
             "frameVariable",
             "moduleIdentifiers",
             "targetCreateTime",
         ]
-        keys_missing = ["firstStopTime", "launchOrAttachTime"]
-        self.verify_keys(stats, '"stats"', keys_exist, keys_missing)
-        self.assertGreater(stats["targetCreateTime"], 0.0)
+        target_stat_keys_missing = ["firstStopTime", "launchOrAttachTime"]
+        self.verify_keys(target_stats, '"target_stats"', target_stat_keys, target_stat_keys_missing)
+        self.assertGreater(target_stats["targetCreateTime"], 0.0)
+
+        # Verify module stats keys.
+        for module_stats in debug_stats["modules"]:
+            module_stat_keys_exist = [
+                "symbolsLoaded",
+            ]
+            self.verify_keys(module_stats, '"module_stats"', module_stat_keys_exist, None)
+
 
     def test_default_with_run(self):
         """Test "statistics dump" when running the target to a breakpoint.

Copy link

github-actions bot commented Apr 17, 2025

✅ With the latest revision this PR passed the Python code formatter.

@royitaqi royitaqi requested review from dmpots and clayborg April 17, 2025 23:41
…dule and target

Summary:

Test Plan:

Reviewers:

Subscribers:

Tasks:

Tags:

Differential Revision: https://phabricator.intern.facebook.com/D73117709
@royitaqi royitaqi requested a review from dmpots April 18, 2025 01:42
Copy link
Contributor

@dmpots dmpots left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@royitaqi
Copy link
Contributor Author

royitaqi commented Apr 18, 2025

Will wait until tomorrow morning/noon before merging, so that:

  1. Other folks will have a chance to review.
  2. Avoid failing tests after work hour so that folks can sleep.

@royitaqi
Copy link
Contributor Author

Merging this PR, because it will make it easier for me to add a test case in #136236

@royitaqi royitaqi merged commit c873ca2 into llvm:main Apr 21, 2025
10 checks passed
@omjavaid
Copy link
Contributor

this PR caused lldb windows buildbot to fail

https://lab.llvm.org/buildbot/#/builders/141/builds/8084

@omjavaid
Copy link
Contributor

the reported number of symbols is zero on Windows

"symbolsLoaded": 0,
...
},
...
],
...
"totalSymbolTablesLoaded": 0,
"totalSymbolsLoaded": 0
}

@DavidSpickett
Copy link
Collaborator

Might be another case of us building with clang, producing DWARF, then linking with link.exe which discards the DWARF. I've skipped a few tests for this already.

(though I wonder if we should just be using lld anyway)

omjavaid added a commit that referenced this pull request Apr 22, 2025
This patch temporarily silences a LLDB test failure caused by PR 136226.
The PR added symbol/table count statistics but caused failures in the
lldb-aarch64-windows buildbot where the reported number of symbols and
symbol tables were incorrectly showing as 0.

https://lab.llvm.org/buildbot/#/builders/141/builds/8084
@omjavaid
Copy link
Contributor

I have tested the using clang and used lld-link as the default linker the symbols are still zero. For now I have added a bypass commit for this to make buildbot happy.

@royitaqi
Copy link
Contributor Author

@dmpots: See the comments above and the revert of #136236. Both are about the symbol count being inconsistent under different configurations (os, linker, debug info plug-in etc; so far it's not clear what is causing the difference).

I propose that for both PR we should remove the verification about the symbol count. Reasons:

  1. This PR([lldb] Add symbol/table count into statistics #136226)'s goal is to add symbol count to stats dump. The goal isn't to assure that the count is zero or not.
  2. In [lldb] Avoid force loading symbols in statistics collection #136236, we should drop the test case in TestStats.py, because there is no guarantee that a different configuration won't cause the symbols to be loaded during target creation.

WDYT?

@clayborg
Copy link
Collaborator

Can we renamed "symbolsLoaded" to "symbolTableSymbolCount" and "totalSymbolsLoaded" to "totalSymbolTableSymbolCount"? This would be more consistent with the other symbol table statistics

royitaqi added a commit that referenced this pull request Apr 25, 2025
…lection (#136795)

Fix a [test
failure](#136236 (comment))
in #136236, apply a minor renaming of statistics, and remerge. See
details below.

# Changes in #136236

Currently, `DebuggerStats::ReportStatistics()` calls
`Module::GetSymtab(/*can_create=*/false)`, but then the latter calls
`SymbolFile::GetSymtab()`. This will load symbols if haven't yet. See
stacktrace below.

The problem is that `DebuggerStats::ReportStatistics` should be
read-only. This is especially important because it reports stats for
symtab parsing/indexing time, which could be affected by the reporting
itself if it's not read-only.

This patch fixes this problem by adding an optional parameter
`SymbolFile::GetSymtab(bool can_create = true)` and receiving the
`false` value passed down from `Module::GetSymtab(/*can_create=*/false)`
when the call is initiated from `DebuggerStats::ReportStatistics()`.

---

Notes about the following stacktrace:
1. This can be reproduced. Create a helloworld program on **macOS** with
dSYM, add `settings set target.preload-symbols false` to `~/.lldbinit`,
do `lldb a.out`, then `statistics dump`.
2. `ObjectFile::GetSymtab` has `llvm::call_once`. So the fact that it
called into `ObjectFileMachO::ParseSymtab` means that the symbol table
is actually being parsed.

```
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = step over
    frame #0: 0x0000000124c4d5a0 LLDB`ObjectFileMachO::ParseSymtab(this=0x0000000111504e40, symtab=0x0000600000a05e00) at ObjectFileMachO.cpp:2259:44
  * frame #1: 0x0000000124fc50a0 LLDB`lldb_private::ObjectFile::GetSymtab()::$_0::operator()(this=0x000000016d35c858) const at ObjectFile.cpp:761:9
    frame #5: 0x0000000124fc4e68 LLDB`void std::__1::__call_once_proxy[abi:v160006]<std::__1::tuple<lldb_private::ObjectFile::GetSymtab()::$_0&&>>(__vp=0x000000016d35c7f0) at mutex:652:5
    frame #6: 0x0000000198afb99c libc++.1.dylib`std::__1::__call_once(unsigned long volatile&, void*, void (*)(void*)) + 196
    frame #7: 0x0000000124fc4dd0 LLDB`void std::__1::call_once[abi:v160006]<lldb_private::ObjectFile::GetSymtab()::$_0>(__flag=0x0000600003920080, __func=0x000000016d35c858) at mutex:670:9
    frame #8: 0x0000000124fc3cb0 LLDB`void llvm::call_once<lldb_private::ObjectFile::GetSymtab()::$_0>(flag=0x0000600003920080, F=0x000000016d35c858) at Threading.h:88:5
    frame #9: 0x0000000124fc2bc4 LLDB`lldb_private::ObjectFile::GetSymtab(this=0x0000000111504e40) at ObjectFile.cpp:755:5
    frame #10: 0x0000000124fe0a28 LLDB`lldb_private::SymbolFileCommon::GetSymtab(this=0x0000000104865200) at SymbolFile.cpp:158:39
    frame #11: 0x0000000124d8fedc LLDB`lldb_private::Module::GetSymtab(this=0x00000001113041a8, can_create=false) at Module.cpp:1027:21
    frame #12: 0x0000000125125bdc LLDB`lldb_private::DebuggerStats::ReportStatistics(debugger=0x000000014284d400, target=0x0000000115808200, options=0x000000014195d6d1) at Statistics.cpp:329:30
    frame #13: 0x0000000125672978 LLDB`CommandObjectStatsDump::DoExecute(this=0x000000014195d540, command=0x000000016d35d820, result=0x000000016d35e150) at CommandObjectStats.cpp:144:18
    frame #14: 0x0000000124f29b40 LLDB`lldb_private::CommandObjectParsed::Execute(this=0x000000014195d540, args_string="", result=0x000000016d35e150) at CommandObject.cpp:832:9
    frame #15: 0x0000000124efbd70 LLDB`lldb_private::CommandInterpreter::HandleCommand(this=0x0000000141b22f30, command_line="statistics dump", lazy_add_to_history=eLazyBoolCalculate, result=0x000000016d35e150, force_repeat_command=false) at CommandInterpreter.cpp:2134:14
    frame #16: 0x0000000124f007f4 LLDB`lldb_private::CommandInterpreter::IOHandlerInputComplete(this=0x0000000141b22f30, io_handler=0x00000001419b2aa8, line="statistics dump") at CommandInterpreter.cpp:3251:3
    frame #17: 0x0000000124d7b5ec LLDB`lldb_private::IOHandlerEditline::Run(this=0x00000001419b2aa8) at IOHandler.cpp:588:22
    frame #18: 0x0000000124d1e8fc LLDB`lldb_private::Debugger::RunIOHandlers(this=0x000000014284d400) at Debugger.cpp:1225:16
    frame #19: 0x0000000124f01f74 LLDB`lldb_private::CommandInterpreter::RunCommandInterpreter(this=0x0000000141b22f30, options=0x000000016d35e63c) at CommandInterpreter.cpp:3543:16
    frame #20: 0x0000000122840294 LLDB`lldb::SBDebugger::RunCommandInterpreter(this=0x000000016d35ebd8, auto_handle_events=true, spawn_thread=false) at SBDebugger.cpp:1212:42
    frame #21: 0x0000000102aa6d28 lldb`Driver::MainLoop(this=0x000000016d35ebb8) at Driver.cpp:621:18
    frame #22: 0x0000000102aa75b0 lldb`main(argc=1, argv=0x000000016d35f548) at Driver.cpp:829:26
    frame #23: 0x0000000198858274 dyld`start + 2840
```

# Changes in this PR top of the above

Fix a [test
failure](#136236 (comment))
in `TestStats.py`. The original version of the added test checks that
all modules have symbol count zero when `target.preload-symbols ==
false`. The test failed on macOS. Due to various reasons, on macOS,
symbols can be loaded for dylibs even with that setting, but not for the
main module. For now, the fix of the test is to limit the assertion to
only the main module. The test now passes on macOS. In the future, when
we have a way to control a specific list of plug-ins to be loaded, there
may be a configuration that this test can use to assert that all modules
have symbol count zero.

Apply a minor renaming of statistics, per the
[suggestion](#136226 (comment))
in #136226 after merge.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants