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