PageRenderTime 37ms CodeModel.GetById 13ms app.highlight 20ms RepoModel.GetById 0ms app.codeStats 1ms

/Debugger/Debugger.Core/Module.cs

http://github.com/icsharpcode/ILSpy
C# | 409 lines | 321 code | 53 blank | 35 comment | 39 complexity | 0be172a4efdbe68a895f1466d07fad75 MD5 | raw file
  1// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
  2// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
  3
  4using System;
  5using System.Collections.Generic;
  6using System.Runtime.InteropServices;
  7
  8using Debugger.Interop;
  9using Debugger.Interop.CorDebug;
 10using Debugger.Interop.CorSym;
 11using Debugger.Interop.MetaData;
 12using Debugger.MetaData;
 13
 14namespace Debugger
 15{
 16	public class Module: DebuggerObject, IDisposable
 17	{
 18		AppDomain appDomain;
 19		Process   process;
 20		
 21		bool   unloaded = false;
 22		string name;
 23		string fullPath = string.Empty;
 24		
 25		int orderOfLoading = 0;
 26		ICorDebugModule corModule;
 27		ISymUnmanagedReader symReader;
 28		MetaDataImport metaData;
 29		
 30		internal Dictionary<string, DebugType> LoadedDebugTypes = new Dictionary<string, DebugType>();
 31		
 32		/// <summary>
 33		/// Occurs when symbols are loaded or unloaded (for memory modules)
 34		/// </summary>
 35		public event EventHandler<ModuleEventArgs> SymbolsUpdated;
 36		
 37		public AppDomain AppDomain {
 38			get { return appDomain; }
 39		}
 40		
 41		public Process Process {
 42			get { return process; }
 43		}
 44		
 45		NDebugger Debugger {
 46			get { return this.AppDomain.Process.Debugger; }
 47		}
 48		
 49		[Debugger.Tests.Ignore]
 50		public MetaDataImport MetaData {
 51			get {
 52				return metaData;
 53			}
 54		}
 55		
 56		public bool Unloaded {
 57			get {
 58				return unloaded;
 59			}
 60		}
 61		
 62		[Debugger.Tests.Ignore]
 63		public ISymUnmanagedReader SymReader {
 64			get {
 65				return symReader;
 66			}
 67		}
 68		
 69		[Debugger.Tests.Ignore]
 70		public ISymUnmanagedDocument[] SymDocuments {
 71			get {
 72				ISymUnmanagedDocument[] docs;
 73				uint maxCount = 2;
 74				uint fetched;
 75				do {
 76					maxCount *= 8;
 77					docs = new ISymUnmanagedDocument[maxCount];
 78					symReader.GetDocuments(maxCount, out fetched, docs);
 79				} while (fetched == maxCount);
 80				Array.Resize(ref docs, (int)fetched);
 81				return docs;
 82			}
 83		}
 84		
 85		[Debugger.Tests.Ignore]
 86		public ICorDebugModule CorModule {
 87			get { return corModule; }
 88		}
 89		
 90		[Debugger.Tests.Ignore]
 91		public ICorDebugModule2 CorModule2 {
 92			get { return (ICorDebugModule2)corModule; }
 93		}
 94		
 95		[Debugger.Tests.Ignore]
 96		public ulong BaseAdress {
 97			get {
 98				return this.CorModule.GetBaseAddress();
 99			}
100		}
101		
102		public bool IsDynamic {
103			get {
104				return this.CorModule.IsDynamic() == 1;
105			}
106		}
107		
108		public bool IsInMemory {
109			get {
110				return this.CorModule.IsInMemory() == 1;
111			}
112		}
113		
114		internal uint AppDomainID {
115			get {
116				return this.CorModule.GetAssembly().GetAppDomain().GetID();
117			}
118		}
119		
120		public string Name {
121			get {
122				return name;
123			}
124		}
125		
126		[Debugger.Tests.Ignore]
127		public string FullPath {
128			get {
129				return fullPath;
130			}
131		}
132		
133		public bool HasSymbols {
134			get {
135				return symReader != null;
136			}
137		}
138		
139		public int OrderOfLoading {
140			get {
141				return orderOfLoading;
142			}
143			set {
144				orderOfLoading = value;
145			}
146		}
147		
148		[Debugger.Tests.Ignore]
149		public CorDebugJITCompilerFlags JITCompilerFlags
150		{
151			get
152			{
153				uint retval = ((ICorDebugModule2)corModule).GetJITCompilerFlags();
154				return (CorDebugJITCompilerFlags)retval;
155			}
156			set
157			{
158				// ICorDebugModule2.SetJITCompilerFlags can return successful HRESULTS other than S_OK.
159				// Since we have asked the COMInterop layer to preservesig, we need to marshal any failing HRESULTS.
160				((ICorDebugModule2)corModule).SetJITCompilerFlags((uint)value);
161			}
162		}
163		
164		/// <summary> Returns all non-generic types defined in the module </summary>
165		/// <remarks> Generic types can not be returned, because we do not know how to instanciate them </remarks>
166		public List<DebugType> GetDefinedTypes()
167		{
168			List<DebugType> types = new List<DebugType>();
169			foreach(TypeDefProps typeDef in this.MetaData.EnumTypeDefProps()) {
170				if (this.MetaData.EnumGenericParams(typeDef.Token).Length == 0) {
171					types.Add(DebugType.CreateFromTypeDefOrRef(this, null, typeDef.Token, null));
172				}
173			}
174			return types;
175		}
176		
177		/// <summary> Get names of all generic and non-generic types defined in this module </summary>
178		public List<string> GetNamesOfDefinedTypes()
179		{
180			List<string> names = new List<string>();
181			foreach(TypeDefProps typeProps in this.MetaData.EnumTypeDefProps()) {
182				names.Add(typeProps.Name);
183			}
184			return names;
185		}
186		
187		internal Module(AppDomain appDomain, ICorDebugModule corModule)
188		{
189			this.appDomain = appDomain;
190			this.process = appDomain.Process;
191			this.corModule = corModule;
192			
193			metaData = new MetaDataImport(corModule);
194			
195			if (IsDynamic || IsInMemory) {
196				name     = corModule.GetName();
197			} else {
198				fullPath = corModule.GetName();
199				name     = System.IO.Path.GetFileName(FullPath);
200			}
201			
202			SetJITCompilerFlags();
203			
204			LoadSymbolsFromDisk(process.Options.SymbolsSearchPaths);
205			ResetJustMyCodeStatus();
206		}
207		
208		public void UnloadSymbols()
209		{
210			if (symReader != null) {
211				// The interface is not always supported, I did not manage to reproduce it, but the
212				// last callbacks in the user's log were UnloadClass and UnloadModule so I guess
213				// it has something to do with dynamic modules.
214				if (symReader is ISymUnmanagedDispose) {
215					((ISymUnmanagedDispose)symReader).Destroy();
216				}
217				symReader = null;
218			}
219		}
220		
221		/// <summary>
222		/// Load symblos for on-disk module
223		/// </summary>
224		public void LoadSymbolsFromDisk(string[] searchPath)
225		{
226			if (!IsDynamic && !IsInMemory) {
227				if (symReader == null) {
228					symReader = metaData.GetSymReader(fullPath, string.Join("; ", searchPath ?? new string[0]));
229					if (symReader != null) {
230						process.TraceMessage("Loaded symbols from disk for " + this.Name);
231						OnSymbolsUpdated();
232					}
233				}
234			}
235		}
236		
237		/// <summary>
238		/// Load symbols for in-memory module
239		/// </summary>
240		public void LoadSymbolsFromMemory(IStream pSymbolStream)
241		{
242			if (this.IsInMemory) {
243				UnloadSymbols();
244				
245				symReader = metaData.GetSymReader(pSymbolStream);
246				if (symReader != null) {
247					process.TraceMessage("Loaded symbols from memory for " + this.Name);
248				} else {
249					process.TraceMessage("Failed to load symbols from memory");
250				}
251				
252				OnSymbolsUpdated();
253			}
254		}
255		
256		/// <summary>
257		/// Load symbols for dynamic module
258		/// (as of .NET 4.0)
259		/// </summary>
260		public void LoadSymbolsDynamic()
261		{
262			if (this.CorModule is ICorDebugModule3 && this.IsDynamic) {
263				Guid guid = new Guid(0, 0, 0, 0xc0, 0, 0, 0, 0, 0, 0, 70);
264				try {
265					symReader = (ISymUnmanagedReader)((ICorDebugModule3)this.CorModule).CreateReaderForInMemorySymbols(guid);
266				} catch (COMException e) {
267					// 0x80131C3B The application did not supply symbols when it loaded or created this module, or they are not yet available.
268					if ((uint)e.ErrorCode == 0x80131C3B) {
269						process.TraceMessage("Failed to load dynamic symbols for " + this.Name);
270						return;
271					}
272					throw;
273				}
274				TrackedComObjects.Track(symReader);
275				process.TraceMessage("Loaded dynamic symbols for " + this.Name);
276				OnSymbolsUpdated();
277			}
278		}
279		
280		void OnSymbolsUpdated()
281		{
282			SetBreakpoints();
283			ResetJustMyCodeStatus();
284			if (SymbolsUpdated != null) {
285				SymbolsUpdated(this, new ModuleEventArgs(this));
286			}
287		}
288		
289		void SetBreakpoints()
290		{
291			if (this.HasSymbols) {
292				// This is in case that the client modifies the collection as a response to set breakpoint
293				// NB: If client adds new breakpoint, it will be set directly as a result of his call, not here (because module is already loaded)
294				List<Breakpoint> collection = new List<Breakpoint>();
295				collection.AddRange(this.Debugger.Breakpoints);
296				
297				foreach (Breakpoint b in collection) {
298					b.SetBreakpoint(this);
299				}
300			}
301		}
302		
303		void SetJITCompilerFlags()
304		{
305			if (Process.DebugMode != DebugModeFlag.Default) {
306				// translate DebugModeFlags to JITCompilerFlags
307				CorDebugJITCompilerFlags jcf = MapDebugModeToJITCompilerFlags(Process.DebugMode);
308
309				try
310				{
311					this.JITCompilerFlags = jcf;
312
313					// Flags may succeed but not set all bits, so requery.
314					CorDebugJITCompilerFlags jcfActual = this.JITCompilerFlags;
315					
316					#if DEBUG
317					if (jcf != jcfActual)
318						Console.WriteLine("Couldn't set all flags. Actual flags:" + jcfActual.ToString());
319					else
320						Console.WriteLine("Actual flags:" + jcfActual.ToString());
321					#endif
322				}
323				catch (COMException ex)
324				{
325					// we'll ignore the error if we cannot set the jit flags
326					Console.WriteLine(string.Format("Failed to set flags with hr=0x{0:x}", ex.ErrorCode));
327				}
328			}
329		}
330		
331		/// <summary> Sets all code as being 'my code'.  The code will be gradually
332		/// set to not-user-code as encountered acording to stepping options </summary>
333		public void ResetJustMyCodeStatus()
334		{
335			uint unused = 0;
336			if (process.Options.StepOverNoSymbols && !this.HasSymbols) {
337				// Optimization - set the code as non-user right away
338				this.CorModule2.SetJMCStatus(0, 0, ref unused);
339				return;
340			}
341			try {
342				this.CorModule2.SetJMCStatus(1, 0, ref unused);
343			} catch (COMException e) {
344				// Cannot use JMC on this code (likely wrong JIT settings).
345				if ((uint)e.ErrorCode == 0x80131323) {
346					process.TraceMessage("Cannot use JMC on this code.  Release build?");
347					return;
348				}
349				throw;
350			}
351		}
352		
353		public void ApplyChanges(byte[] metadata, byte[] il)
354		{
355			this.CorModule2.ApplyChanges((uint)metadata.Length, metadata, (uint)il.Length, il);
356		}
357		
358		public void Dispose()
359		{
360			UnloadSymbols();
361			unloaded = true;
362		}
363		
364		public override string ToString()
365		{
366			return string.Format("{0}", this.Name);
367		}
368		
369		public static CorDebugJITCompilerFlags MapDebugModeToJITCompilerFlags(DebugModeFlag debugMode)
370		{
371			CorDebugJITCompilerFlags jcf;
372			switch (debugMode)
373			{
374				case DebugModeFlag.Optimized:
375					jcf = CorDebugJITCompilerFlags.CORDEBUG_JIT_DEFAULT; // DEFAULT really means force optimized.
376					break;
377				case DebugModeFlag.Debug:
378					jcf = CorDebugJITCompilerFlags.CORDEBUG_JIT_DISABLE_OPTIMIZATION;
379					break;
380				case DebugModeFlag.Enc:
381					jcf = CorDebugJITCompilerFlags.CORDEBUG_JIT_ENABLE_ENC;
382					break;
383				default:
384					// we don't have mapping from default to "default",
385					// therefore we'll use DISABLE_OPTIMIZATION.
386					jcf = CorDebugJITCompilerFlags.CORDEBUG_JIT_DISABLE_OPTIMIZATION;
387					break;
388			}
389			return jcf;
390		}
391	}
392	
393	[Serializable]
394	public class ModuleEventArgs : ProcessEventArgs
395	{
396		Module module;
397		
398		public Module Module {
399			get {
400				return module;
401			}
402		}
403		
404		public ModuleEventArgs(Module module): base(module.Process)
405		{
406			this.module = module;
407		}
408	}
409}