// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.IO;
using System.Text;
using System.Threading;
using Debugger.Interop;
using Debugger.Interop.CorDebug;
using Microsoft.Win32;
namespace Debugger
{
public class NDebugger: DebuggerObject
{
ICorDebug corDebug;
ManagedCallbackSwitch managedCallbackSwitch;
ManagedCallbackProxy managedCallbackProxy;
BreakpointCollection breakpoints;
ProcessCollection processes;
MTA2STA mta2sta = new MTA2STA();
string debuggeeVersion;
Options options = new Options();
public MTA2STA MTA2STA {
get {
return mta2sta;
}
}
internal ICorDebug CorDebug {
get {
return corDebug;
}
}
public string DebuggeeVersion {
get {
return debuggeeVersion;
}
}
public Options Options {
get { return options; }
set { options = value; }
}
public BreakpointCollection Breakpoints {
get { return breakpoints; }
}
public ProcessCollection Processes {
get { return processes; }
}
public NDebugger()
{
processes = new ProcessCollection(this);
breakpoints = new BreakpointCollection(this);
if (ApartmentState.STA == System.Threading.Thread.CurrentThread.GetApartmentState()) {
mta2sta.CallMethod = CallMethod.HiddenFormWithTimeout;
} else {
mta2sta.CallMethod = CallMethod.DirectCall;
}
}
///
/// Get the .NET version of the process that called this function
///
public string GetDebuggerVersion()
{
int size;
NativeMethods.GetCORVersion(null, 0, out size);
StringBuilder sb = new StringBuilder(size);
int hr = NativeMethods.GetCORVersion(sb, sb.Capacity, out size);
return sb.ToString();
}
///
/// Get the .NET version of a given program - eg. "v1.1.4322"
///
/// Returns empty string for unmanaged applications
public string GetProgramVersion(string exeFilename)
{
int size;
NativeMethods.GetRequestedRuntimeVersion(exeFilename, null, 0, out size);
StringBuilder sb = new StringBuilder(size);
NativeMethods.GetRequestedRuntimeVersion(exeFilename, sb, sb.Capacity, out size);
sb.Length = size;
return sb.ToString().TrimEnd('\0');
}
///
/// Prepares the debugger
///
/// Version of the program to debug - eg. "v1.1.4322"
/// If null, the version of the executing process will be used
internal void InitDebugger(string debuggeeVersion)
{
if (IsKernelDebuggerEnabled) {
throw new DebuggerException("Can not debug because kernel debugger is enabled");
}
if (string.IsNullOrEmpty(debuggeeVersion)) {
debuggeeVersion = GetDebuggerVersion();
TraceMessage("Debuggee version: Unknown (assuming " + debuggeeVersion + ")");
} else {
TraceMessage("Debuggee version: " + debuggeeVersion);
}
this.debuggeeVersion = debuggeeVersion;
int debuggerVersion;
// The CLR does not provide 4.0 debugger interface for older versions
if (debuggeeVersion.StartsWith("v1") || debuggeeVersion.StartsWith("v2")) {
debuggerVersion = 3; // 2.0 CLR
TraceMessage("Debugger interface version: v2.0");
} else {
debuggerVersion = 4; // 4.0 CLR
TraceMessage("Debugger interface version: v4.0");
}
corDebug = NativeMethods.CreateDebuggingInterfaceFromVersion(debuggerVersion, debuggeeVersion);
TrackedComObjects.Track(corDebug);
managedCallbackSwitch = new ManagedCallbackSwitch(this);
managedCallbackProxy = new ManagedCallbackProxy(this, managedCallbackSwitch);
corDebug.Initialize();
corDebug.SetManagedHandler(managedCallbackProxy);
TraceMessage("ICorDebug initialized");
}
internal void TerminateDebugger()
{
// Mark breakpints as deactivated
foreach (Breakpoint b in this.Breakpoints) {
b.MarkAsDeactivated();
}
TraceMessage("Reset done");
corDebug.Terminate();
TraceMessage("ICorDebug terminated");
int released = TrackedComObjects.ReleaseAll();
TraceMessage("Released " + released + " tracked COM objects");
}
///
/// Internal: Used to debug the debugger library.
///
public event EventHandler DebuggerTraceMessage;
protected internal virtual void OnDebuggerTraceMessage(MessageEventArgs e)
{
if (DebuggerTraceMessage != null) {
DebuggerTraceMessage(this, e);
}
}
internal void TraceMessage(string message)
{
System.Diagnostics.Debug.WriteLine("Debugger:" + message);
OnDebuggerTraceMessage(new MessageEventArgs(null, message));
}
public void StartWithoutDebugging(System.Diagnostics.ProcessStartInfo psi)
{
System.Diagnostics.Process process;
process = new System.Diagnostics.Process();
process.StartInfo = psi;
process.Start();
}
internal object ProcessIsBeingCreatedLock = new object();
public Process Start(string filename, string workingDirectory, string arguments)
{
InitDebugger(GetProgramVersion(filename));
lock(ProcessIsBeingCreatedLock) {
Process process = Process.CreateProcess(this, filename, workingDirectory, arguments);
// Expose a race conditon
System.Threading.Thread.Sleep(0);
this.Processes.Add(process);
return process;
}
}
public Process Attach(System.Diagnostics.Process existingProcess)
{
string mainModule = existingProcess.MainModule.FileName;
InitDebugger(GetProgramVersion(mainModule));
ICorDebugProcess corDebugProcess = corDebug.DebugActiveProcess((uint)existingProcess.Id, 0);
// TODO: Can we get the acutal working directory?
Process process = new Process(this, corDebugProcess, Path.GetDirectoryName(mainModule));
this.Processes.Add(process);
return process;
}
public void Detach()
{
// Deactivate breakpoints
foreach (Breakpoint b in this.Breakpoints) {
b.Deactivate();
}
// Detach all processes.
for (int i = 0; i < this.Processes.Count; ++i) {
Process process = this.Processes[i];
if (process == null || process.HasExited)
continue;
process.Detach();
}
}
public bool IsKernelDebuggerEnabled {
get {
string systemStartOptions = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\").GetValue("SystemStartOptions", string.Empty).ToString();
// XP does not have the slash, Vista does have it
systemStartOptions = ("/" + systemStartOptions).ToLower().Replace(" ", " /");
if (systemStartOptions.Contains("/nodebug")) {
// this option overrides the others
return false;
}
if (systemStartOptions.Contains("/debug") ||
systemStartOptions.Contains("/crashdebug") ||
systemStartOptions.Contains("/debugport") ||
systemStartOptions.Contains("/baudrate")) {
return true;
} else {
return false;
}
}
}
/// Try to load module symbols using the search path defined in the options
public void ReloadModuleSymbols()
{
foreach(Process process in this.Processes) {
foreach(Module module in process.Modules) {
module.LoadSymbolsFromDisk(process.Options.SymbolsSearchPaths);
}
}
TraceMessage("Reloaded symbols");
}
/// Reset the just my code status of modules. Use this after changing any stepping options.
public void ResetJustMyCodeStatus()
{
foreach(Process process in this.Processes) {
foreach(Module module in process.Modules) {
module.ResetJustMyCodeStatus();
}
}
TraceMessage("Just my code reseted");
}
}
[Serializable]
public class DebuggerEventArgs : EventArgs
{
NDebugger debugger;
public NDebugger Debugger {
get {
return debugger;
}
}
public DebuggerEventArgs(NDebugger debugger)
{
this.debugger = debugger;
}
}
[Serializable]
public class MessageEventArgs : ProcessEventArgs
{
int level;
string message;
string category;
public int Level {
get {
return level;
}
}
public string Message {
get {
return message;
}
}
public string Category {
get {
return category;
}
}
public MessageEventArgs(Process process, string message): this(process, 0, message, String.Empty)
{
this.message = message;
}
public MessageEventArgs(Process process, int level, string message, string category): base(process)
{
this.level = level;
this.message = message;
this.category = category;
}
}
}