1+ import glob
12import json
23import os
34import re
5+ import shutil
46
57import lldb
68from lldbsuite .test .decorators import *
@@ -179,6 +181,7 @@ def test_default_no_run(self):
179181 "totalDebugInfoParseTime" ,
180182 "totalDwoFileCount" ,
181183 "totalLoadedDwoFileCount" ,
184+ "totalDwoLoadErrorCount" ,
182185 ]
183186 self .verify_keys (debug_stats , '"debug_stats"' , debug_stat_keys , None )
184187 if self .getPlatform () != "windows" :
@@ -291,6 +294,7 @@ def test_default_with_run(self):
291294 "totalDebugInfoParseTime" ,
292295 "totalDwoFileCount" ,
293296 "totalLoadedDwoFileCount" ,
297+ "totalDwoLoadErrorCount" ,
294298 ]
295299 self .verify_keys (debug_stats , '"debug_stats"' , debug_stat_keys , None )
296300 stats = debug_stats ["targets" ][0 ]
@@ -331,6 +335,7 @@ def test_memory(self):
331335 "totalDebugInfoByteSize" ,
332336 "totalDwoFileCount" ,
333337 "totalLoadedDwoFileCount" ,
338+ "totalDwoLoadErrorCount" ,
334339 ]
335340 self .verify_keys (debug_stats , '"debug_stats"' , debug_stat_keys , None )
336341
@@ -385,6 +390,7 @@ def test_modules(self):
385390 "totalDebugInfoByteSize" ,
386391 "totalDwoFileCount" ,
387392 "totalLoadedDwoFileCount" ,
393+ "totalDwoLoadErrorCount" ,
388394 ]
389395 self .verify_keys (debug_stats , '"debug_stats"' , debug_stat_keys , None )
390396 stats = debug_stats ["targets" ][0 ]
@@ -407,6 +413,7 @@ def test_modules(self):
407413 "symbolTableSavedToCache" ,
408414 "dwoFileCount" ,
409415 "loadedDwoFileCount" ,
416+ "dwoLoadErrorCount" ,
410417 "triple" ,
411418 "uuid" ,
412419 ]
@@ -497,6 +504,7 @@ def test_breakpoints(self):
497504 "totalDebugInfoByteSize" ,
498505 "totalDwoFileCount" ,
499506 "totalLoadedDwoFileCount" ,
507+ "totalDwoLoadErrorCount" ,
500508 ]
501509 self .verify_keys (debug_stats , '"debug_stats"' , debug_stat_keys , None )
502510 target_stats = debug_stats ["targets" ][0 ]
@@ -655,6 +663,113 @@ def test_dwp_dwo_file_count(self):
655663 self .assertEqual (debug_stats ["totalDwoFileCount" ], 2 )
656664 self .assertEqual (debug_stats ["totalLoadedDwoFileCount" ], 2 )
657665
666+ @add_test_categories (["dwo" ])
667+ def test_dwo_missing_error_stats (self ):
668+ """
669+ Test that DWO missing errors are reported correctly in statistics.
670+ This test:
671+ 1) Builds a program with split DWARF (.dwo files)
672+ 2) Delete all two .dwo files
673+ 3) Verify that 2 DWO load errors are reported in statistics
674+ """
675+ da = {
676+ "CXX_SOURCES" : "dwo_error_main.cpp dwo_error_foo.cpp" ,
677+ "EXE" : self .getBuildArtifact ("a.out" ),
678+ }
679+ # -gsplit-dwarf creates separate .dwo files,
680+ # Expected output: dwo_error_main.dwo (contains main) and dwo_error_foo.dwo (contains foo struct/function)
681+ self .build (dictionary = da , debug_info = "dwo" )
682+ self .addTearDownCleanup (dictionary = da )
683+ exe = self .getBuildArtifact ("a.out" )
684+
685+ # Remove the two .dwo files to trigger a DWO load error
686+ dwo_files = glob .glob (self .getBuildArtifact ("*.dwo" ))
687+ for dwo_file in dwo_files :
688+ os .rename (dwo_file , dwo_file + ".bak" )
689+
690+ target = self .createTestTarget (file_path = exe )
691+ debug_stats = self .get_stats ()
692+
693+ # Check DWO load error statistics are reported
694+ self .assertIn ("totalDwoLoadErrorCount" , debug_stats )
695+ self .assertEqual (debug_stats ["totalDwoLoadErrorCount" ], 2 )
696+
697+ # Since there's only one module, module stats should have the same count as total count
698+ self .assertIn ("dwoLoadErrorCount" , debug_stats ["modules" ][0 ])
699+ self .assertEqual (debug_stats ["modules" ][0 ]["dwoLoadErrorCount" ], 2 )
700+
701+ # Restore the original .dwo file
702+ for dwo_file in dwo_files :
703+ os .rename (dwo_file + ".bak" , dwo_file )
704+
705+ @add_test_categories (["dwo" ])
706+ def test_dwo_id_mismatch_error_stats (self ):
707+ """
708+ Test that DWO ID mismatch errors are reported correctly in statistics.
709+ This test:
710+ 1) Builds a program with split DWARF (.dwo files)
711+ 2) Change one of the source file content and rebuild
712+ 3) Replace the new .dwo file with the original one to create a DWO ID mismatch
713+ 4) Verifies that a DWO load error is reported in statistics
714+ 5) Restores the original source file
715+ """
716+ da = {
717+ "CXX_SOURCES" : "dwo_error_main.cpp dwo_error_foo.cpp" ,
718+ "EXE" : self .getBuildArtifact ("a.out" ),
719+ }
720+ # -gsplit-dwarf creates separate .dwo files,
721+ # Expected output: dwo_error_main.dwo (contains main) and dwo_error_foo.dwo (contains foo struct/function)
722+ self .build (dictionary = da , debug_info = "dwo" )
723+ self .addTearDownCleanup (dictionary = da )
724+ exe = self .getBuildArtifact ("a.out" )
725+
726+ # Find and make a backup of the original .dwo file
727+ dwo_files = glob .glob (self .getBuildArtifact ("*.dwo" ))
728+
729+ original_dwo_file = dwo_files [1 ]
730+ original_dwo_backup = original_dwo_file + ".bak"
731+ shutil .copy2 (original_dwo_file , original_dwo_backup )
732+
733+ target = self .createTestTarget (file_path = exe )
734+ initial_stats = self .get_stats ()
735+ self .assertIn ("totalDwoLoadErrorCount" , initial_stats )
736+ self .assertEqual (initial_stats ["totalDwoLoadErrorCount" ], 0 )
737+ self .dbg .DeleteTarget (target )
738+
739+ # Get the original file size before modification
740+ source_file_path = self .getSourcePath ("dwo_error_foo.cpp" )
741+ original_size = os .path .getsize (source_file_path )
742+
743+ try :
744+ # Modify the source code and rebuild
745+ with open (source_file_path , "a" ) as f :
746+ f .write ("\n void additional_foo(){}\n " )
747+
748+ # Rebuild and replace the new .dwo file with the original one
749+ self .build (dictionary = da , debug_info = "dwo" )
750+ shutil .copy2 (original_dwo_backup , original_dwo_file )
751+
752+ # Create a new target and run to a breakpoint to force DWO file loading
753+ target = self .createTestTarget (file_path = exe )
754+ debug_stats = self .get_stats ()
755+
756+ # Check that DWO load error statistics are reported
757+ self .assertIn ("totalDwoLoadErrorCount" , debug_stats )
758+ self .assertEqual (debug_stats ["totalDwoLoadErrorCount" ], 1 )
759+
760+ # Since there's only one module, module stats should have the same count as total count
761+ self .assertIn ("dwoLoadErrorCount" , debug_stats ["modules" ][0 ])
762+ self .assertEqual (debug_stats ["modules" ][0 ]["dwoLoadErrorCount" ], 1 )
763+
764+ finally :
765+ # Remove the appended content
766+ with open (source_file_path , "a" ) as f :
767+ f .truncate (original_size )
768+
769+ # Restore the original .dwo file
770+ if os .path .exists (original_dwo_backup ):
771+ os .unlink (original_dwo_backup )
772+
658773 @skipUnlessDarwin
659774 @no_debug_info_test
660775 def test_dsym_binary_has_symfile_in_stats (self ):
0 commit comments