void DnDebugger_DebugCallbackEvent(DnDebugger dbg, DebugCallbackEventArgs e) { string msg; switch (e.Kind) { case DebugCallbackKind.CreateProcess: var cp = (CreateProcessDebugCallbackEventArgs)e; hProcess_debuggee = Native.NativeMethods.OpenProcess(Native.NativeMethods.PROCESS_QUERY_LIMITED_INFORMATION, false, (uint)(cp.CorProcess?.ProcessId ?? -1)); SendMessage(new DbgMessageConnected((uint)cp.CorProcess.ProcessId, GetMessageFlags())); e.AddPauseReason(DebuggerPauseReason.Other); break; case DebugCallbackKind.CreateAppDomain: // CreateProcess is too early, we must do this when the AppDomain gets created if (!clrDacInitd) { clrDacInitd = true; var p = dnDebugger.Processes.FirstOrDefault(); if (p != null) { clrDac = clrDacProvider.Create(p.ProcessId, dnDebugger.CLRPath, this); } } break; case DebugCallbackKind.Exception2: var e2 = (Exception2DebugCallbackEventArgs)e; DbgExceptionEventFlags exFlags; if (e2.EventType == CorDebugExceptionCallbackType.DEBUG_EXCEPTION_FIRST_CHANCE) { exFlags = DbgExceptionEventFlags.FirstChance; } else if (e2.EventType == CorDebugExceptionCallbackType.DEBUG_EXCEPTION_UNHANDLED) { exFlags = DbgExceptionEventFlags.SecondChance | DbgExceptionEventFlags.Unhandled; isUnhandledException = true; } else { break; } // Ignore exceptions when evaluating except if it's an unhandled exception, those must always be reported if (dbg.IsEvaluating && e2.EventType != CorDebugExceptionCallbackType.DEBUG_EXCEPTION_UNHANDLED) { break; } var exObj = e2.CorThread?.CurrentException; objectFactory.CreateException(new DbgExceptionId(PredefinedExceptionCategories.DotNet, TryGetExceptionName(exObj) ?? "???"), exFlags, TryGetExceptionMessage(exObj), TryGetThread(e2.CorThread), TryGetModule(e2.CorFrame, e2.CorThread), GetMessageFlags()); e.AddPauseReason(DebuggerPauseReason.Other); break; case DebugCallbackKind.MDANotification: if (dbg.IsEvaluating) { break; } var mdan = (MDANotificationDebugCallbackEventArgs)e; objectFactory.CreateException(new DbgExceptionId(PredefinedExceptionCategories.MDA, mdan.CorMDA?.Name ?? "???"), DbgExceptionEventFlags.FirstChance, mdan.CorMDA?.Description, TryGetThread(mdan.CorThread), TryGetModule(null, mdan.CorThread), GetMessageFlags()); e.AddPauseReason(DebuggerPauseReason.Other); break; case DebugCallbackKind.LogMessage: if (dbg.IsEvaluating) { break; } var lmsgArgs = (LogMessageDebugCallbackEventArgs)e; msg = lmsgArgs.Message; if (msg != null) { e.AddPauseReason(DebuggerPauseReason.Other); var thread = TryGetThread(lmsgArgs.CorThread); SendMessage(new DbgMessageProgramMessage(msg, thread, GetMessageFlags())); } break; case DebugCallbackKind.LoadClass: var lcArgs = (LoadClassDebugCallbackEventArgs)e; var cls = lcArgs.CorClass; Debug.Assert(cls != null); if (cls != null) { var dnModule = dbg.TryGetModule(lcArgs.CorAppDomain, cls); if (dnModule.IsDynamic) { UpdateDynamicModuleIds(dnModule); var module = TryGetModule(dnModule.CorModule); Debug.Assert(module != null); if (module != null) { dbgModuleMemoryRefreshedNotifier.RaiseModulesRefreshed(new[] { module }); } if (dnModule?.CorModuleDef != null && module != null) { if (TryGetModuleData(module, out var data)) { data.OnLoadClass(); } ClassLoaded?.Invoke(this, new ClassLoadedEventArgs(module, cls.Token)); } GetDynamicModuleHelper(dnModule).RaiseTypeLoaded(new DmdTypeLoadedEventArgs((int)cls.Token)); } } break; case DebugCallbackKind.DebuggerError: var deArgs = (DebuggerErrorDebugCallbackEventArgs)e; if (deArgs.HError == CordbgErrors.CORDBG_E_UNCOMPATIBLE_PLATFORMS) { msg = GetIncompatiblePlatformErrorMessage(); } else { msg = string.Format(dnSpy_Debugger_DotNet_CorDebug_Resources.Error_CLRDebuggerErrorOccurred, deArgs.HError, deArgs.ErrorCode); } SendMessage(new DbgMessageBreak(msg, GetMessageFlags(pause: true))); break; } }
// When a dynamic assembly is created with option Run, a module gets created and its // metadata name is "RefEmit_InMemoryManifestModule". Shortly thereafter, its name // gets changed to the name the user chose. // This name is also saved in ModuleIds, and used when setting breakpoints... // There's code that caches ModuleIds, but they don't cache it if IsDynamic is true. // This method updates the ModuleId and resets breakpoints in the module. void UpdateDynamicModuleIds(DnModule dnModule) { debuggerThread.VerifyAccess(); if (!dnModule.IsDynamic) { return; } var module = TryGetModule(dnModule.CorModule); if (module == null || !TryGetModuleData(module, out var data) || data.HasUpdatedModuleId) { return; } List <(DbgModule dbgModule, DnModule dnModule)> updatedModules = null; lock (lockObj) { if (toAssemblyModules.TryGetValue(dnModule.Assembly, out var modules)) { for (int i = 0; i < modules.Count; i++) { dnModule = modules[i]; if (!dnModule.IsDynamic) { continue; } if (!toEngineModule.TryGetValue(dnModule.CorModule, out var em)) { continue; } if (!TryGetModuleData(em.Module, out data)) { continue; } dnModule.CorModule.ClearCachedDnlibName(); var moduleId = dnModule.DnModuleId.ToModuleId(); if (data.ModuleId == moduleId) { continue; } data.UpdateModuleId(moduleId); if (dnModule.CorModuleDef != null) { //TODO: This doesn't update the treeview node dnModule.CorModuleDef.Name = moduleId.ModuleName; } if (updatedModules == null) { updatedModules = new List <(DbgModule, DnModule)>(); } updatedModules.Add((em.Module, dnModule)); } } } if (updatedModules != null) { foreach (var info in updatedModules) { var mdi = info.dnModule.CorModule.GetMetaDataInterface <IMetaDataImport2>(); var scopeName = MDAPI.GetModuleName(mdi) ?? string.Empty; ((DbgCorDebugInternalModuleImpl)info.dbgModule.InternalModule).ReflectionModule.ScopeName = scopeName; } dbgModuleMemoryRefreshedNotifier.RaiseModulesRefreshed(updatedModules.Select(a => a.dbgModule).ToArray()); } }