@@ -177,6 +177,8 @@ def test_default_no_run(self):
177
177
"totalDebugInfoIndexLoadedFromCache" ,
178
178
"totalDebugInfoIndexSavedToCache" ,
179
179
"totalDebugInfoParseTime" ,
180
+ "totalDwoFileCount" ,
181
+ "totalLoadedDwoFileCount" ,
180
182
]
181
183
self .verify_keys (debug_stats , '"debug_stats"' , debug_stat_keys , None )
182
184
if self .getPlatform () != "windows" :
@@ -287,6 +289,8 @@ def test_default_with_run(self):
287
289
"totalDebugInfoIndexLoadedFromCache" ,
288
290
"totalDebugInfoIndexSavedToCache" ,
289
291
"totalDebugInfoParseTime" ,
292
+ "totalDwoFileCount" ,
293
+ "totalLoadedDwoFileCount" ,
290
294
]
291
295
self .verify_keys (debug_stats , '"debug_stats"' , debug_stat_keys , None )
292
296
stats = debug_stats ["targets" ][0 ]
@@ -325,6 +329,8 @@ def test_memory(self):
325
329
"totalDebugInfoIndexLoadedFromCache" ,
326
330
"totalDebugInfoIndexSavedToCache" ,
327
331
"totalDebugInfoByteSize" ,
332
+ "totalDwoFileCount" ,
333
+ "totalLoadedDwoFileCount" ,
328
334
]
329
335
self .verify_keys (debug_stats , '"debug_stats"' , debug_stat_keys , None )
330
336
@@ -377,6 +383,8 @@ def test_modules(self):
377
383
"totalDebugInfoIndexLoadedFromCache" ,
378
384
"totalDebugInfoIndexSavedToCache" ,
379
385
"totalDebugInfoByteSize" ,
386
+ "totalDwoFileCount" ,
387
+ "totalLoadedDwoFileCount" ,
380
388
]
381
389
self .verify_keys (debug_stats , '"debug_stats"' , debug_stat_keys , None )
382
390
stats = debug_stats ["targets" ][0 ]
@@ -397,6 +405,8 @@ def test_modules(self):
397
405
"symbolTableLoadedFromCache" ,
398
406
"symbolTableParseTime" ,
399
407
"symbolTableSavedToCache" ,
408
+ "dwoFileCount" ,
409
+ "loadedDwoFileCount" ,
400
410
"triple" ,
401
411
"uuid" ,
402
412
]
@@ -485,6 +495,8 @@ def test_breakpoints(self):
485
495
"totalDebugInfoIndexLoadedFromCache" ,
486
496
"totalDebugInfoIndexSavedToCache" ,
487
497
"totalDebugInfoByteSize" ,
498
+ "totalDwoFileCount" ,
499
+ "totalLoadedDwoFileCount" ,
488
500
]
489
501
self .verify_keys (debug_stats , '"debug_stats"' , debug_stat_keys , None )
490
502
target_stats = debug_stats ["targets" ][0 ]
@@ -513,6 +525,136 @@ def test_breakpoints(self):
513
525
breakpoint , 'target_stats["breakpoints"]' , bp_keys_exist , None
514
526
)
515
527
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
+
516
658
@skipUnlessDarwin
517
659
@no_debug_info_test
518
660
def test_dsym_binary_has_symfile_in_stats (self ):
0 commit comments