99using System . Reflection . PortableExecutable ;
1010using System . Text . RegularExpressions ;
1111using Coverlet . Core . Abstracts ;
12+ using Coverlet . Core . Logging ;
1213
1314namespace Coverlet . Core . Helpers
1415{
1516 internal class InstrumentationHelper : IInstrumentationHelper
1617 {
1718 private readonly ConcurrentDictionary < string , string > _backupList = new ConcurrentDictionary < string , string > ( ) ;
1819 private readonly IRetryHelper _retryHelper ;
20+ private readonly IFileSystem _fileSystem ;
1921
20- public InstrumentationHelper ( IProcessExitHandler processExitHandler , IRetryHelper retryHelper )
22+ public InstrumentationHelper ( IProcessExitHandler processExitHandler , IRetryHelper retryHelper , IFileSystem fileSystem )
2123 {
2224 processExitHandler . Add ( ( s , e ) => RestoreOriginalModules ( ) ) ;
2325 _retryHelper = retryHelper ;
26+ _fileSystem = fileSystem ;
2427 }
2528
2629 public string [ ] GetCoverableModules ( string module , string [ ] directories , bool includeTestAssembly )
@@ -93,8 +96,9 @@ public bool HasPdb(string module, out bool embedded)
9396 }
9497 }
9598
96- public bool EmbeddedPortablePdbHasLocalSource ( string module )
99+ public bool EmbeddedPortablePdbHasLocalSource ( string module , out string firstNotFoundDocument )
97100 {
101+ firstNotFoundDocument = "" ;
98102 using ( FileStream moduleStream = File . OpenRead ( module ) )
99103 using ( var peReader = new PEReader ( moduleStream ) )
100104 {
@@ -115,6 +119,7 @@ public bool EmbeddedPortablePdbHasLocalSource(string module)
115119 // Btw check for all possible extension could be weak approach
116120 if ( ! File . Exists ( docName ) )
117121 {
122+ firstNotFoundDocument = docName ;
118123 return false ;
119124 }
120125 }
@@ -128,6 +133,41 @@ public bool EmbeddedPortablePdbHasLocalSource(string module)
128133 return true ;
129134 }
130135
136+ public bool PortablePdbHasLocalSource ( string module , out string firstNotFoundDocument )
137+ {
138+ firstNotFoundDocument = "" ;
139+ using ( var moduleStream = File . OpenRead ( module ) )
140+ using ( var peReader = new PEReader ( moduleStream ) )
141+ {
142+ foreach ( var entry in peReader . ReadDebugDirectory ( ) )
143+ {
144+ if ( entry . Type == DebugDirectoryEntryType . CodeView )
145+ {
146+ var codeViewData = peReader . ReadCodeViewDebugDirectoryData ( entry ) ;
147+ using FileStream pdbStream = new FileStream ( codeViewData . Path , FileMode . Open ) ;
148+ using MetadataReaderProvider metadataReaderProvider = MetadataReaderProvider . FromPortablePdbStream ( pdbStream ) ;
149+ MetadataReader metadataReader = metadataReaderProvider . GetMetadataReader ( ) ;
150+ foreach ( DocumentHandle docHandle in metadataReader . Documents )
151+ {
152+ Document document = metadataReader . GetDocument ( docHandle ) ;
153+ string docName = metadataReader . GetString ( document . Name ) ;
154+
155+ // We verify all docs and return false if not all are present in local
156+ // We could have false negative if doc is not a source
157+ // Btw check for all possible extension could be weak approach
158+ if ( ! _fileSystem . Exists ( docName ) )
159+ {
160+ firstNotFoundDocument = docName ;
161+ return false ;
162+ }
163+ }
164+ }
165+ }
166+ }
167+
168+ return true ;
169+ }
170+
131171 public void BackupOriginalModule ( string module , string identifier )
132172 {
133173 var backupPath = GetBackupPath ( module , identifier ) ;
0 commit comments