99using System . Runtime . InteropServices ;
1010using System . Text ;
1111using Microsoft . Diagnostics . Runtime ;
12+ using Microsoft . Diagnostics . Runtime . Utilities ;
1213using Microsoft . SymbolStore ;
1314using Microsoft . SymbolStore . KeyGenerators ;
1415
@@ -20,14 +21,14 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
2021 public class Runtime : IRuntime , IDisposable
2122 {
2223 private readonly ClrInfo _clrInfo ;
23- private readonly IHostAssetResolver _hostAssetResolver ;
2424 private readonly ISettingsService _settingsService ;
2525 private readonly ISymbolService _symbolService ;
2626 private Version _runtimeVersion ;
2727 private ClrRuntime _clrRuntime ;
28+ private IClrDataProcess _clrDataProcess ;
29+ private int ? _cdacActivationResult ;
2830 private string _dacFilePath ;
29- private bool _verifySignature ; // This only applies to the regular DAC, not the CDAC
30- private string _cdacFilePath ;
31+ private bool _verifySignature ;
3132 private string _dbiFilePath ;
3233
3334 protected readonly ServiceContainer _serviceContainer ;
@@ -37,10 +38,6 @@ public Runtime(IServiceProvider services, int id, ClrInfo clrInfo)
3738 Target = services . GetService < ITarget > ( ) ?? throw new DiagnosticsException ( "Dump or live session target required" ) ;
3839 Id = id ;
3940 _clrInfo = clrInfo ?? throw new ArgumentNullException ( nameof ( clrInfo ) ) ;
40- // IHostAssetResolver is optional: it is registered by the SOS hosting layer to locate
41- // the bundled cDAC. When absent (hosts without SOS.Hosting, e.g. some test hosts), cDAC
42- // resolution returns null and the in-box DAC is used.
43- _hostAssetResolver = services . GetService < IHostAssetResolver > ( ) ;
4441 _settingsService = services . GetService < ISettingsService > ( ) ?? throw new ArgumentException ( "ISettingsService required" ) ;
4542 _symbolService = services . GetService < ISymbolService > ( ) ?? throw new ArgumentException ( "ISymbolService required" ) ;
4643
@@ -64,6 +61,9 @@ void IDisposable.Dispose()
6461 _clrRuntime = null ;
6562 _serviceContainer . RemoveService ( typeof ( IRuntime ) ) ;
6663 _serviceContainer . DisposeServices ( ) ;
64+ _clrDataProcess ? . Dispose ( ) ;
65+ _clrDataProcess = null ;
66+ _cdacActivationResult = null ;
6767 }
6868
6969 #region IRuntime
@@ -111,24 +111,22 @@ public string GetDacFilePath(out bool verifySignature)
111111 return _dacFilePath ;
112112 }
113113
114- public string GetCDacFilePath ( )
114+ public int GetClrDataProcessFromCDac ( out IntPtr clrDataProcess )
115115 {
116- // ShouldUseCDac() evaluates the cDAC loading policy. When it returns false the caller
117- // uses the in-box DAC from GetDacFilePath instead.
118- if ( ! ShouldUseCDac ( ) )
116+ if ( ! _cdacActivationResult . HasValue )
119117 {
120- return null ;
118+ IClrDataProcessActivator activator = Services . GetService < IClrDataProcessActivator > ( ) ;
119+ _cdacActivationResult = activator ? . CreateClrDataProcessFromCDac ( this , out _clrDataProcess ) ?? HResult . E_NOINTERFACE ;
121120 }
122121
123- // The cDAC is bundled with the diagnostics tool and is never downloaded, so a missing
124- // path means it isn't available for this host.
125- _cdacFilePath ??= GetLibraryPath ( DebugLibraryKind . CDac ) ;
126- if ( _cdacFilePath is null && _settingsService . CDacLoadPolicy == CDacLoadPolicy . UseCDac )
122+ clrDataProcess = _clrDataProcess ? . Interface ?? IntPtr . Zero ;
123+ int result = _cdacActivationResult ?? HResult . E_NOINTERFACE ;
124+ if ( result >= 0 && clrDataProcess == IntPtr . Zero )
127125 {
128- // The cDAC was explicitly forced but isn't bundled with this tool.
129- throw new DiagnosticsException ( $ "The cDAC was explicitly requested but no matching cDAC is available for this runtime: { RuntimeModule . FileName } " ) ;
126+ result = HResult . E_NOINTERFACE ;
127+ _cdacActivationResult = result ;
130128 }
131- return _cdacFilePath ;
129+ return result ;
132130 }
133131
134132 public string GetDbiFilePath ( )
@@ -140,82 +138,106 @@ public string GetDbiFilePath()
140138 #endregion
141139
142140 /// <summary>
143- /// The minimum runtime major version that supports the cDAC.
144- /// </summary>
145- private const int MinCDacRuntimeMajorVersion = 11 ;
146-
147- /// <summary>
148- /// Evaluates the cDAC loading policy for this runtime. This is the single place that
149- /// decides whether the diagnostics tool should load the cDAC itself in place of the
150- /// in-box DAC, based on the <see cref="ISettingsService.CDacLoadPolicy"/> setting and the
151- /// target runtime version.
141+ /// Create ClrRuntime instance
152142 /// </summary>
153- private bool ShouldUseCDac ( )
143+ private ClrRuntime CreateRuntime ( )
154144 {
155- return _settingsService . CDacLoadPolicy switch
145+ CDacLoadPolicy policy = _settingsService . CDacLoadPolicy ;
146+ bool useCDac = CDacPolicy . ShouldTryCDac ( policy ) ;
147+ Trace . TraceInformation ( $ "Runtime #{ Id } data-access: begin (cDAC policy={ policy } , cDAC attempted={ useCDac } )") ;
148+
149+ if ( useCDac )
150+ {
151+ int hr = GetClrDataProcessFromCDac ( out IntPtr clrDataProcess ) ;
152+ if ( hr >= 0 && clrDataProcess != IntPtr . Zero )
153+ {
154+ Trace . TraceInformation ( $ "Runtime #{ Id } data-access: received an IXCLRDataProcess") ;
155+ return CreateRuntimeFromClrDataProcess ( ) ;
156+ }
157+ Trace . TraceInformation ( $ "Runtime #{ Id } data-access: IXCLRDataProcess activation failed { hr : X8} ") ;
158+ }
159+
160+ if ( policy == CDacLoadPolicy . OnlyUseCDac )
161+ {
162+ Trace . TraceError ( $ "Runtime #{ Id } data-access: cDAC was required but could not service this runtime: { RuntimeModule . FileName } ") ;
163+ return null ;
164+ }
165+
166+ // We ignore the dac signature verification param since it's already set as part of the CLRMD DataTarget creation
167+ // now (it's a global setting to the session).
168+ string dacFilePath = GetDacFilePath ( out _ ) ;
169+ if ( dacFilePath is not null )
156170 {
157- CDacLoadPolicy . UseLegacyDac => false , // Never load the cDAC.
158- CDacLoadPolicy . UseCDac => true , // Always use the cDAC, regardless of the runtime version. Availability is
159- // checked by the caller (a missing forced cDAC is a hard error).
160- _ => ShouldUseCDacByDefault ( ) , // No explicit setting: evaluate the default policy.
161- } ;
171+ Trace . TraceInformation ( $ "Runtime #{ Id } data-access: falling back to the in-box DAC { dacFilePath } ") ;
172+ return TryCreateRuntimeFromDac ( dacFilePath ) ;
173+ }
174+
175+ Trace . TraceError ( $ "Runtime #{ Id } data-access: could not find or download a matching DAC for this runtime: { RuntimeModule . FileName } ") ;
176+ return null ;
162177 }
163178
164179 /// <summary>
165- /// The default cDAC policy used when <see cref="ISettingsService.CDacLoadPolicy"/> is not set .
180+ /// Creates a ClrRuntime with the specified DAC .
166181 /// </summary>
167- private bool ShouldUseCDacByDefault ( )
182+ private ClrRuntime TryCreateRuntimeFromDac ( string dacFilePath )
168183 {
169- // When DOTNET_ENABLE_CDAC is requested, the in-box (legacy) DAC loads and drives the
170- // cDAC contract reader itself, including its own dac-vs-cdac fallback/comparison
171- // (see CDAC_NO_FALLBACK). Defer to that mechanism rather than loading the cDAC
172- // directly so those scenarios (for example, the runtime's cDAC test pipeline that
173- // points at a freshly built cDAC via -liveruntimedir) keep working.
174- if ( Environment . GetEnvironmentVariable ( "DOTNET_ENABLE_CDAC" ) == "1"
175- || Environment . GetEnvironmentVariable ( "COMPlus_ENABLE_CDAC" ) == "1" )
184+ Trace . TraceInformation ( $ "Creating ClrRuntime #{ Id } { dacFilePath } ") ;
185+ try
176186 {
177- return false ;
187+ // Ignore the DAC version mismatch that can happen because the clrmd ELF dump reader
188+ // returns 0.0.0.0 for the runtime module that the DAC is matched against.
189+ return _clrRuntime = _clrInfo . CreateRuntime ( dacFilePath , ignoreMismatch : true ) ;
190+ }
191+ catch ( Exception ex ) when
192+ ( ex is DllNotFoundException or
193+ FileNotFoundException or
194+ InvalidOperationException or
195+ InvalidDataException or
196+ ClrDiagnosticsException )
197+ {
198+ Trace . TraceError ( "CreateRuntime FAILED: {0}" , ex . ToString ( ) ) ;
199+ return null ;
178200 }
179-
180- // Default policy: use the cDAC only for runtimes that support it. This needs to be
181- // changed to consider native AOT and singlefile. This is a dummy policy for work
182- // we will offload to dbgshim.
183- return RuntimeVersion is not null && RuntimeVersion . Major >= MinCDacRuntimeMajorVersion ;
184201 }
185202
186203 /// <summary>
187- /// Create ClrRuntime instance
204+ /// Creates a ClrRuntime with the supplied IXCLRDataProcess.
188205 /// </summary>
189- private ClrRuntime CreateRuntime ( )
206+ private ClrRuntime CreateRuntimeFromClrDataProcess ( )
190207 {
191- // Prefer the cDAC for the ClrMD data-access path when policy selects it; fall back to the in-box DAC.
192- // We ignore the dac verification param since it's already set as part of the CLRMD DataTarget creation
193- // now (it's a global setting to the session).
194- string dacFilePath = GetCDacFilePath ( ) ?? GetDacFilePath ( out _ ) ;
195- if ( dacFilePath is not null )
208+ try
196209 {
197- Trace . TraceInformation ( $ "Creating ClrRuntime #{ Id } { dacFilePath } ") ;
198- try
199- {
200- // Ignore the DAC version mismatch that can happen because the clrmd ELF dump reader
201- // returns 0.0.0.0 for the runtime module that the DAC is matched against.
202- return _clrRuntime = _clrInfo . CreateRuntime ( dacFilePath , ignoreMismatch : true ) ;
203- }
204- catch ( Exception ex ) when
205- ( ex is DllNotFoundException or
206- FileNotFoundException or
207- InvalidOperationException or
208- InvalidDataException or
209- ClrDiagnosticsException )
210- {
211- Trace . TraceError ( "CreateRuntime FAILED: {0}" , ex . ToString ( ) ) ;
212- }
210+ _clrInfo . DataTarget . AddLoadedRuntime ( _clrInfo , _clrDataProcess . Interface ) ;
213211 }
214- else
212+ catch ( Exception ex ) when
213+ ( ex is DllNotFoundException or
214+ FileNotFoundException or
215+ InvalidOperationException or
216+ InvalidDataException or
217+ ClrDiagnosticsException )
215218 {
216- Trace . TraceError ( $ "Could not find or download matching DAC for this runtime: { RuntimeModule . FileName } ") ;
219+ Trace . TraceError ( "Register IXCLRDataProcess FAILED: {0}" , ex . ToString ( ) ) ;
220+ _clrDataProcess . Dispose ( ) ;
221+ _clrDataProcess = null ;
222+ _cdacActivationResult = null ;
223+ return null ;
224+ }
225+
226+ try
227+ {
228+ Trace . TraceInformation ( $ "Creating ClrRuntime #{ Id } from IXCLRDataProcess") ;
229+ return _clrRuntime = _clrInfo . CreateRuntime ( ) ;
230+ }
231+ catch ( Exception ex ) when
232+ ( ex is DllNotFoundException or
233+ FileNotFoundException or
234+ InvalidOperationException or
235+ InvalidDataException or
236+ ClrDiagnosticsException )
237+ {
238+ Trace . TraceError ( "CreateRuntime from registered IXCLRDataProcess FAILED: {0}" , ex . ToString ( ) ) ;
239+ return null ;
217240 }
218- return null ;
219241 }
220242
221243 private string GetLibraryPath ( DebugLibraryKind kind )
@@ -232,13 +254,6 @@ private string GetLibraryPath(DebugLibraryKind kind)
232254 {
233255 break ;
234256 }
235- // The cDAC is an analyzer-host artifact shipped inside the diagnostics tool
236- // (next to sos.dll, matching the host's RID). It is not symbol-store indexed
237- // by the target runtime, so never attempt to download it.
238- if ( libraryInfo . Kind == DebugLibraryKind . CDac )
239- {
240- continue ;
241- }
242257 if ( libraryInfo . ArchivedUnder != SymbolProperties . None )
243258 {
244259 libraryPath = DownloadFile ( libraryInfo ) ;
@@ -256,24 +271,13 @@ private string GetLibraryPath(DebugLibraryKind kind)
256271 private string GetLocalPath ( DebugLibraryInfo libraryInfo )
257272 {
258273 string localFilePath ;
259- if ( libraryInfo . Kind == DebugLibraryKind . CDac )
274+ if ( ! string . IsNullOrEmpty ( RuntimeModuleDirectory ) )
260275 {
261- // The cDAC ships next to the native sos module. Ask the host asset resolver where it
262- // is rather than reasoning about layouts here (ClrMD's DebuggingLibraries entry points
263- // at the managed-assembly base directory, so it is ignored). The shared existence
264- // check below verifies the path, so the in-box DAC is used when the cDAC isn't bundled.
265- localFilePath = _hostAssetResolver ? . GetCDacPath ( ) ;
276+ localFilePath = Path . Combine ( RuntimeModuleDirectory , Path . GetFileName ( libraryInfo . FileName ) ) ;
266277 }
267278 else
268279 {
269- if ( ! string . IsNullOrEmpty ( RuntimeModuleDirectory ) )
270- {
271- localFilePath = Path . Combine ( RuntimeModuleDirectory , Path . GetFileName ( libraryInfo . FileName ) ) ;
272- }
273- else
274- {
275- localFilePath = Path . Combine ( Path . GetDirectoryName ( RuntimeModule . FileName ) , Path . GetFileName ( libraryInfo . FileName ) ) ;
276- }
280+ localFilePath = Path . Combine ( Path . GetDirectoryName ( RuntimeModule . FileName ) , Path . GetFileName ( libraryInfo . FileName ) ) ;
277281 }
278282 if ( localFilePath is null || ! File . Exists ( localFilePath ) )
279283 {
@@ -411,11 +415,6 @@ public override string ToString()
411415 string verify = _verifySignature ? "(verify)" : "(don't verify)" ;
412416 sb . Append ( $ " DAC: { _dacFilePath } { verify } ") ;
413417 }
414- if ( _cdacFilePath is not null )
415- {
416- sb . AppendLine ( ) ;
417- sb . Append ( $ " CDAC: { _cdacFilePath } ") ;
418- }
419418 if ( _dbiFilePath is not null )
420419 {
421420 sb . AppendLine ( ) ;
0 commit comments