@@ -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 ]
@@ -512,6 +524,132 @@ def test_breakpoints(self):
512524 self .verify_keys (
513525 breakpoint , 'target_stats["breakpoints"]' , bp_keys_exist , None
514526 )
527+ def test_non_split_dwarf_has_no_dwo_files (self ):
528+ """
529+ Test "statistics dump" and the dwo file count.
530+ Builds a binary without split-dwarf mode, and then
531+ verifies the dwo file count is zero after running "statistics dump"
532+ """
533+ da = {"CXX_SOURCES" : "third.cpp baz.cpp" , "EXE" : self .getBuildArtifact ("a.out" )}
534+ self .build (dictionary = da , debug_info = ["debug_names" ])
535+ self .addTearDownCleanup (dictionary = da )
536+ exe = self .getBuildArtifact ("a.out" )
537+ target = self .createTestTarget (file_path = exe )
538+ debug_stats = self .get_stats ()
539+ self .assertIn ("totalDwoFileCount" , debug_stats )
540+ self .assertIn ("totalLoadedDwoFileCount" , debug_stats )
541+
542+ # Verify that the dwo file count is zero
543+ self .assertEqual (debug_stats ["totalDwoFileCount" ], 0 )
544+ self .assertEqual (debug_stats ["totalLoadedDwoFileCount" ], 0 )
545+
546+ def test_no_debug_names_eager_loads_dwo_files (self ):
547+ """
548+ Test the eager loading behavior of DWO files when debug_names is absent by
549+ building a split-dwarf binary without debug_names and then running "statistics dump".
550+ DWO file loading behavior:
551+ - With debug_names: DebugNamesDWARFIndex allows for lazy loading.
552+ DWO files are loaded on-demand when symbols are actually looked up
553+ - Without debug_names: ManualDWARFIndex uses eager loading.
554+ All DWO files are loaded upfront during the first symbol lookup to build a manual index.
555+ """
556+ da = {"CXX_SOURCES" : "third.cpp baz.cpp" , "EXE" : self .getBuildArtifact ("a.out" )}
557+ self .build (dictionary = da , debug_info = ["dwo" ])
558+ self .addTearDownCleanup (dictionary = da )
559+ exe = self .getBuildArtifact ("a.out" )
560+ target = self .createTestTarget (file_path = exe )
561+ debug_stats = self .get_stats ()
562+ self .assertIn ("totalDwoFileCount" , debug_stats )
563+ self .assertIn ("totalLoadedDwoFileCount" , debug_stats )
564+
565+ # Verify that all DWO files are loaded
566+ self .assertEqual (debug_stats ["totalDwoFileCount" ], 2 )
567+ self .assertEqual (debug_stats ["totalLoadedDwoFileCount" ], 2 )
568+
569+ def test_split_dwarf_dwo_file_count (self ):
570+ """
571+ Test "statistics dump" and the dwo file count.
572+ Builds a binary w/ separate .dwo files and debug_names, and then
573+ verifies the loaded dwo file count is the expected count after running
574+ various commands
575+ """
576+ da = {"CXX_SOURCES" : "third.cpp baz.cpp" , "EXE" : self .getBuildArtifact ("a.out" )}
577+ # -gsplit-dwarf creates separate .dwo files,
578+ # -gpubnames enables the debug_names accelerator tables for faster symbol lookup
579+ # and lazy loading of DWO files
580+ # Expected output: third.dwo (contains main) and baz.dwo (contains Baz struct/function)
581+ self .build (dictionary = da , debug_info = ["dwo" , "debug_names" ])
582+ self .addTearDownCleanup (dictionary = da )
583+ exe = self .getBuildArtifact ("a.out" )
584+ target = self .createTestTarget (file_path = exe )
585+ debug_stats = self .get_stats ()
586+
587+ # 1) 2 DWO files available but none loaded yet
588+ self .assertEqual (len (debug_stats ["modules" ]), 1 )
589+ self .assertIn ("totalLoadedDwoFileCount" , debug_stats )
590+ self .assertIn ("totalDwoFileCount" , debug_stats )
591+ self .assertEqual (debug_stats ["totalLoadedDwoFileCount" ], 0 )
592+ self .assertEqual (debug_stats ["totalDwoFileCount" ], 2 )
593+
594+ # Since there's only one module, module stats should have the same counts as total counts
595+ self .assertIn ("dwoFileCount" , debug_stats ["modules" ][0 ])
596+ self .assertIn ("loadedDwoFileCount" , debug_stats ["modules" ][0 ])
597+ self .assertEqual (debug_stats ["modules" ][0 ]["loadedDwoFileCount" ], 0 )
598+ self .assertEqual (debug_stats ["modules" ][0 ]["dwoFileCount" ], 2 )
599+
600+ # 2) Setting breakpoint in main triggers loading of third.dwo (contains main function)
601+ self .runCmd ("b main" )
602+ debug_stats = self .get_stats ()
603+ self .assertEqual (debug_stats ["totalLoadedDwoFileCount" ], 1 )
604+ self .assertEqual (debug_stats ["totalDwoFileCount" ], 2 )
605+
606+ self .assertEqual (debug_stats ["modules" ][0 ]["loadedDwoFileCount" ], 1 )
607+ self .assertEqual (debug_stats ["modules" ][0 ]["dwoFileCount" ], 2 )
608+
609+ # 3) Type lookup forces loading of baz.dwo (contains struct Baz definition)
610+ self .runCmd ("type lookup Baz" )
611+ debug_stats = self .get_stats ()
612+ self .assertEqual (debug_stats ["totalLoadedDwoFileCount" ], 2 )
613+ self .assertEqual (debug_stats ["totalDwoFileCount" ], 2 )
614+
615+ self .assertEqual (debug_stats ["modules" ][0 ]["loadedDwoFileCount" ], 2 )
616+ self .assertEqual (debug_stats ["modules" ][0 ]["dwoFileCount" ], 2 )
617+
618+ def test_dwp_dwo_file_count (self ):
619+ """
620+ Test "statistics dump" and the loaded dwo file count.
621+ Builds a binary w/ a separate .dwp file and debug_names, and then
622+ verifies the loaded dwo file count is the expected count after running
623+ various commands.
624+
625+ We expect the DWO file counters to reflect the number of compile units
626+ loaded from the DWP file (each representing what was originally a separate DWO file)
627+ """
628+ da = {"CXX_SOURCES" : "third.cpp baz.cpp" , "EXE" : self .getBuildArtifact ("a.out" )}
629+ self .build (dictionary = da , debug_info = ["dwp" , "debug_names" ])
630+ self .addTearDownCleanup (dictionary = da )
631+ exe = self .getBuildArtifact ("a.out" )
632+ target = self .createTestTarget (file_path = exe )
633+ debug_stats = self .get_stats ()
634+
635+ # Initially: 2 DWO files available but none loaded yet
636+ self .assertIn ("totalLoadedDwoFileCount" , debug_stats )
637+ self .assertIn ("totalDwoFileCount" , debug_stats )
638+ self .assertEqual (debug_stats ["totalLoadedDwoFileCount" ], 0 )
639+ self .assertEqual (debug_stats ["totalDwoFileCount" ], 2 )
640+
641+ # Setting breakpoint in main triggers parsing of the CU within a.dwp corresponding to third.dwo (contains main function)
642+ self .runCmd ("b main" )
643+ debug_stats = self .get_stats ()
644+ self .assertEqual (debug_stats ["totalLoadedDwoFileCount" ], 1 )
645+ self .assertEqual (debug_stats ["totalDwoFileCount" ], 2 )
646+
647+ # Type lookup forces parsing of the CU within a.dwp corresponding to baz.dwo (contains struct Baz definition)
648+ self .runCmd ("type lookup Baz" )
649+ debug_stats = self .get_stats ()
650+ self .assertEqual (debug_stats ["totalDwoFileCount" ], 2 )
651+ self .assertEqual (debug_stats ["totalLoadedDwoFileCount" ], 2 )
652+
515653
516654 @skipUnlessDarwin
517655 @no_debug_info_test
0 commit comments