@@ -177,6 +177,8 @@ def test_default_no_run(self):
177177 "totalDebugInfoIndexLoadedFromCache" ,
178178 "totalDebugInfoIndexSavedToCache" ,
179179 "totalDebugInfoParseTime" ,
180+ "totalDwoFileCount" ,
181+ "totalLoadedDwoFileCount" ,
180182 ]
181183 self .verify_keys (debug_stats , '"debug_stats"' , debug_stat_keys , None )
182184 if self .getPlatform () != "windows" :
@@ -287,6 +289,8 @@ def test_default_with_run(self):
287289 "totalDebugInfoIndexLoadedFromCache" ,
288290 "totalDebugInfoIndexSavedToCache" ,
289291 "totalDebugInfoParseTime" ,
292+ "totalDwoFileCount" ,
293+ "totalLoadedDwoFileCount" ,
290294 ]
291295 self .verify_keys (debug_stats , '"debug_stats"' , debug_stat_keys , None )
292296 stats = debug_stats ["targets" ][0 ]
@@ -325,6 +329,8 @@ def test_memory(self):
325329 "totalDebugInfoIndexLoadedFromCache" ,
326330 "totalDebugInfoIndexSavedToCache" ,
327331 "totalDebugInfoByteSize" ,
332+ "totalDwoFileCount" ,
333+ "totalLoadedDwoFileCount" ,
328334 ]
329335 self .verify_keys (debug_stats , '"debug_stats"' , debug_stat_keys , None )
330336
@@ -377,6 +383,8 @@ def test_modules(self):
377383 "totalDebugInfoIndexLoadedFromCache" ,
378384 "totalDebugInfoIndexSavedToCache" ,
379385 "totalDebugInfoByteSize" ,
386+ "totalDwoFileCount" ,
387+ "totalLoadedDwoFileCount" ,
380388 ]
381389 self .verify_keys (debug_stats , '"debug_stats"' , debug_stat_keys , None )
382390 stats = debug_stats ["targets" ][0 ]
@@ -397,6 +405,8 @@ def test_modules(self):
397405 "symbolTableLoadedFromCache" ,
398406 "symbolTableParseTime" ,
399407 "symbolTableSavedToCache" ,
408+ "dwoFileCount" ,
409+ "loadedDwoFileCount" ,
400410 "triple" ,
401411 "uuid" ,
402412 ]
@@ -485,6 +495,8 @@ def test_breakpoints(self):
485495 "totalDebugInfoIndexLoadedFromCache" ,
486496 "totalDebugInfoIndexSavedToCache" ,
487497 "totalDebugInfoByteSize" ,
498+ "totalDwoFileCount" ,
499+ "totalLoadedDwoFileCount" ,
488500 ]
489501 self .verify_keys (debug_stats , '"debug_stats"' , debug_stat_keys , None )
490502 target_stats = debug_stats ["targets" ][0 ]
@@ -513,6 +525,136 @@ def test_breakpoints(self):
513525 breakpoint , 'target_stats["breakpoints"]' , bp_keys_exist , None
514526 )
515527
528+ @add_test_categories (["dwo" ])
529+ def test_non_split_dwarf_has_no_dwo_files (self ):
530+ """
531+ Test "statistics dump" and the dwo file count.
532+ Builds a binary without split-dwarf mode, and then
533+ verifies the dwo file count is zero after running "statistics dump"
534+ """
535+ da = {"CXX_SOURCES" : "third.cpp baz.cpp" , "EXE" : self .getBuildArtifact ("a.out" )}
536+ self .build (dictionary = da , debug_info = ["debug_names" ])
537+ self .addTearDownCleanup (dictionary = da )
538+ exe = self .getBuildArtifact ("a.out" )
539+ target = self .createTestTarget (file_path = exe )
540+ debug_stats = self .get_stats ()
541+ self .assertIn ("totalDwoFileCount" , debug_stats )
542+ self .assertIn ("totalLoadedDwoFileCount" , debug_stats )
543+
544+ # Verify that the dwo file count is zero
545+ self .assertEqual (debug_stats ["totalDwoFileCount" ], 0 )
546+ self .assertEqual (debug_stats ["totalLoadedDwoFileCount" ], 0 )
547+
548+ @add_test_categories (["dwo" ])
549+ def test_no_debug_names_eager_loads_dwo_files (self ):
550+ """
551+ Test the eager loading behavior of DWO files when debug_names is absent by
552+ building a split-dwarf binary without debug_names and then running "statistics dump".
553+ DWO file loading behavior:
554+ - With debug_names: DebugNamesDWARFIndex allows for lazy loading.
555+ DWO files are loaded on-demand when symbols are actually looked up
556+ - Without debug_names: ManualDWARFIndex uses eager loading.
557+ All DWO files are loaded upfront during the first symbol lookup to build a manual index.
558+ """
559+ da = {"CXX_SOURCES" : "third.cpp baz.cpp" , "EXE" : self .getBuildArtifact ("a.out" )}
560+ self .build (dictionary = da , debug_info = ["dwo" ])
561+ self .addTearDownCleanup (dictionary = da )
562+ exe = self .getBuildArtifact ("a.out" )
563+ target = self .createTestTarget (file_path = exe )
564+ debug_stats = self .get_stats ()
565+ self .assertIn ("totalDwoFileCount" , debug_stats )
566+ self .assertIn ("totalLoadedDwoFileCount" , debug_stats )
567+
568+ # Verify that all DWO files are loaded
569+ self .assertEqual (debug_stats ["totalDwoFileCount" ], 2 )
570+ self .assertEqual (debug_stats ["totalLoadedDwoFileCount" ], 2 )
571+
572+ @add_test_categories (["dwo" ])
573+ def test_split_dwarf_dwo_file_count (self ):
574+ """
575+ Test "statistics dump" and the dwo file count.
576+ Builds a binary w/ separate .dwo files and debug_names, and then
577+ verifies the loaded dwo file count is the expected count after running
578+ various commands
579+ """
580+ da = {"CXX_SOURCES" : "third.cpp baz.cpp" , "EXE" : self .getBuildArtifact ("a.out" )}
581+ # -gsplit-dwarf creates separate .dwo files,
582+ # -gpubnames enables the debug_names accelerator tables for faster symbol lookup
583+ # and lazy loading of DWO files
584+ # Expected output: third.dwo (contains main) and baz.dwo (contains Baz struct/function)
585+ self .build (dictionary = da , debug_info = ["dwo" , "debug_names" ])
586+ self .addTearDownCleanup (dictionary = da )
587+ exe = self .getBuildArtifact ("a.out" )
588+ target = self .createTestTarget (file_path = exe )
589+ debug_stats = self .get_stats ()
590+
591+ # 1) 2 DWO files available but none loaded yet
592+ self .assertEqual (len (debug_stats ["modules" ]), 1 )
593+ self .assertIn ("totalLoadedDwoFileCount" , debug_stats )
594+ self .assertIn ("totalDwoFileCount" , debug_stats )
595+ self .assertEqual (debug_stats ["totalLoadedDwoFileCount" ], 0 )
596+ self .assertEqual (debug_stats ["totalDwoFileCount" ], 2 )
597+
598+ # Since there's only one module, module stats should have the same counts as total counts
599+ self .assertIn ("dwoFileCount" , debug_stats ["modules" ][0 ])
600+ self .assertIn ("loadedDwoFileCount" , debug_stats ["modules" ][0 ])
601+ self .assertEqual (debug_stats ["modules" ][0 ]["loadedDwoFileCount" ], 0 )
602+ self .assertEqual (debug_stats ["modules" ][0 ]["dwoFileCount" ], 2 )
603+
604+ # 2) Setting breakpoint in main triggers loading of third.dwo (contains main function)
605+ self .runCmd ("b main" )
606+ debug_stats = self .get_stats ()
607+ self .assertEqual (debug_stats ["totalLoadedDwoFileCount" ], 1 )
608+ self .assertEqual (debug_stats ["totalDwoFileCount" ], 2 )
609+
610+ self .assertEqual (debug_stats ["modules" ][0 ]["loadedDwoFileCount" ], 1 )
611+ self .assertEqual (debug_stats ["modules" ][0 ]["dwoFileCount" ], 2 )
612+
613+ # 3) Type lookup forces loading of baz.dwo (contains struct Baz definition)
614+ self .runCmd ("type lookup Baz" )
615+ debug_stats = self .get_stats ()
616+ self .assertEqual (debug_stats ["totalLoadedDwoFileCount" ], 2 )
617+ self .assertEqual (debug_stats ["totalDwoFileCount" ], 2 )
618+
619+ self .assertEqual (debug_stats ["modules" ][0 ]["loadedDwoFileCount" ], 2 )
620+ self .assertEqual (debug_stats ["modules" ][0 ]["dwoFileCount" ], 2 )
621+
622+ @add_test_categories (["dwo" ])
623+ def test_dwp_dwo_file_count (self ):
624+ """
625+ Test "statistics dump" and the loaded dwo file count.
626+ Builds a binary w/ a separate .dwp file and debug_names, and then
627+ verifies the loaded dwo file count is the expected count after running
628+ various commands.
629+
630+ We expect the DWO file counters to reflect the number of compile units
631+ loaded from the DWP file (each representing what was originally a separate DWO file)
632+ """
633+ da = {"CXX_SOURCES" : "third.cpp baz.cpp" , "EXE" : self .getBuildArtifact ("a.out" )}
634+ self .build (dictionary = da , debug_info = ["dwp" , "debug_names" ])
635+ self .addTearDownCleanup (dictionary = da )
636+ exe = self .getBuildArtifact ("a.out" )
637+ target = self .createTestTarget (file_path = exe )
638+ debug_stats = self .get_stats ()
639+
640+ # Initially: 2 DWO files available but none loaded yet
641+ self .assertIn ("totalLoadedDwoFileCount" , debug_stats )
642+ self .assertIn ("totalDwoFileCount" , debug_stats )
643+ self .assertEqual (debug_stats ["totalLoadedDwoFileCount" ], 0 )
644+ self .assertEqual (debug_stats ["totalDwoFileCount" ], 2 )
645+
646+ # Setting breakpoint in main triggers parsing of the CU within a.dwp corresponding to third.dwo (contains main function)
647+ self .runCmd ("b main" )
648+ debug_stats = self .get_stats ()
649+ self .assertEqual (debug_stats ["totalLoadedDwoFileCount" ], 1 )
650+ self .assertEqual (debug_stats ["totalDwoFileCount" ], 2 )
651+
652+ # Type lookup forces parsing of the CU within a.dwp corresponding to baz.dwo (contains struct Baz definition)
653+ self .runCmd ("type lookup Baz" )
654+ debug_stats = self .get_stats ()
655+ self .assertEqual (debug_stats ["totalDwoFileCount" ], 2 )
656+ self .assertEqual (debug_stats ["totalLoadedDwoFileCount" ], 2 )
657+
516658 @skipUnlessDarwin
517659 @no_debug_info_test
518660 def test_dsym_binary_has_symfile_in_stats (self ):
0 commit comments