////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    public CLangDebuggeeModule (DebugEngine engine, MiAsyncRecord asyncRecord)
      : base (engine)
    {
      try
      {
        if (asyncRecord == null)
        {
          throw new ArgumentNullException ("asyncRecord");
        }

        Name = asyncRecord ["id"] [0].GetString ();

        RemotePath = asyncRecord ["target-name"] [0].GetString ();

        RemoteLoadAddress = 0;

        SymbolsPath = asyncRecord ["host-name"] [0].GetString ();

        // 
        // The 'symbols-loaded' field is emitted only for backward compatibility and should not be relied on to convey any useful information.
        // 

        if ((!string.IsNullOrEmpty (SymbolsPath)) && File.Exists (SymbolsPath))
        {
          SymbolsLoaded = true;
        }
      }
      catch (Exception e)
      {
        LoggingUtils.HandleException (e);

        throw;
      }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    public DebuggeeExpression (DebugEngine engine, DebuggeeStackFrame stackFrame, string expression, uint radix)
    {
      m_debugEngine = engine;

      m_stackFrame = stackFrame;

      m_expression = expression;

      m_radix = radix;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    public DebuggeeDocumentContext (DebugEngine engine, string fileName, TEXT_POSITION beginPosition, TEXT_POSITION endPosition)
    {
      m_engine = engine;

      m_fileName = PathUtils.ConvertPathCygwinToWindows (fileName);

      m_beginPosition = beginPosition;

      m_endPosition = endPosition;

      m_codeContext = null;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    public DebugEngineCallback (DebugEngine engine, IDebugEventCallback2 ad7EventCallback)
    {
      Engine = engine;

      m_ad7EventCallback = ad7EventCallback;

      m_cLangEventCallback = new CLangDebuggerCallback (engine);

      m_javaLangEventCallback = new JavaLangDebuggerCallback (engine);

      // 
      // Register function handlers for specific events.
      // 

      m_debuggerCallback = new Dictionary<Guid, DebuggerEventDelegate> ();

      m_debuggerCallback.Add (ComUtils.GuidOf (typeof (DebugEngineEvent.BreakpointHit)), OnBreakpoint);

      m_debuggerCallback.Add (ComUtils.GuidOf (typeof (DebugEngineEvent.BreakpointBound)), OnBreakpointBound);

      m_debuggerCallback.Add (ComUtils.GuidOf (typeof (DebugEngineEvent.BreakpointError)), OnBreakpointError);

      m_debuggerCallback.Add (ComUtils.GuidOf (typeof (DebugEngineEvent.Error)), OnError);

      m_debuggerCallback.Add (ComUtils.GuidOf (typeof (DebugEngineEvent.Exception)), OnException);

      m_debuggerCallback.Add (ComUtils.GuidOf (typeof (DebugEngineEvent.LoadComplete)), OnLoadComplete);

      m_debuggerCallback.Add (ComUtils.GuidOf (typeof (DebugEngineEvent.ModuleLoad)), OnModuleLoad);

      m_debuggerCallback.Add (ComUtils.GuidOf (typeof (DebugEngineEvent.OutputString)), OnOutputString);

      m_debuggerCallback.Add (ComUtils.GuidOf (typeof (DebugEngineEvent.ProcessCreate)), OnProgramCreate);

      m_debuggerCallback.Add (ComUtils.GuidOf (typeof (DebugEngineEvent.ProgramDestroy)), OnProgramDestroy);

      m_debuggerCallback.Add (ComUtils.GuidOf (typeof (DebugEngineEvent.StepComplete)), OnStepComplete);

      m_debuggerCallback.Add (ComUtils.GuidOf (typeof (DebugEngineEvent.SymbolSearch)), OnSymbolSearch);

      m_debuggerCallback.Add (ComUtils.GuidOf (typeof (DebugEngineEvent.ThreadCreate)), OnThreadCreate);

      m_debuggerCallback.Add (ComUtils.GuidOf (typeof (DebugEngineEvent.ThreadDestroy)), OnThreadDestroy);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    public JavaLangDebugger (DebugEngine debugEngine, DebuggeeProgram debugProgram)
    {
      Engine = debugEngine;

      m_javaLangCallback = new JavaLangDebuggerCallback (debugEngine);

      JavaProgram = new JavaLangDebuggeeProgram (this, debugProgram);

      m_jdbSetup = new JdbSetup (debugProgram.DebugProcess.NativeProcess);

      Engine.Broadcast (new DebugEngineEvent.DebuggerConnectionEvent (DebugEngineEvent.DebuggerConnectionEvent.EventType.LogStatus, string.Format ("Configuring JDB for {0}:{1}...", m_jdbSetup.Host, m_jdbSetup.Port)), null, null);

      JdbClient = new JdbClient (m_jdbSetup);

      JdbClient.OnAsyncStdout = OnClientAsyncOutput;

      JdbClient.OnAsyncStderr = OnClientAsyncOutput;

      JdbClient.Start ();
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    public JavaLangDebuggerCallback (DebugEngine engine)
    {
      m_debugEngine = engine;

      // 
      // Register function handlers for specific events.
      // 

      m_debuggerCallback = new Dictionary<Guid, JavaLangDebuggerEventDelegate> ();

      m_debuggerCallback.Add (ComUtils.GuidOf (typeof (JavaLangDebuggerEvent.AttachClient)), OnAttachClient);

      m_debuggerCallback.Add (ComUtils.GuidOf (typeof (JavaLangDebuggerEvent.DetachClient)), OnDetachClient);

      m_debuggerCallback.Add (ComUtils.GuidOf (typeof (JavaLangDebuggerEvent.StopClient)), OnStopClient);

      m_debuggerCallback.Add (ComUtils.GuidOf (typeof (JavaLangDebuggerEvent.ContinueClient)), OnContinueClient);

      m_debuggerCallback.Add (ComUtils.GuidOf (typeof (JavaLangDebuggerEvent.TerminateClient)), OnTerminateClient);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    public DebugBreakpointManager (DebugEngine engine)
    {
      Engine = engine;

      m_pendingBreakpoints = new List<DebuggeeBreakpointPending> ();
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    public CLangDebugger (DebugEngine debugEngine, LaunchConfiguration launchConfiguration, DebuggeeProgram debugProgram)
    {
      Engine = debugEngine;

      m_launchConfiguration = launchConfiguration;

      NativeProgram = new CLangDebuggeeProgram (this, debugProgram);

      NativeMemoryBytes = new CLangDebuggeeMemoryBytes (this);

      VariableManager = new CLangDebuggerVariableManager (this);

      // 
      // Evaluate target device's architecture triple.
      // 

      string preferedGdbAbiToolPrefix = string.Empty;

      bool allow64BitAbis = true;

      switch (debugProgram.DebugProcess.NativeProcess.PrimaryCpuAbi)
      {
        case "armeabi":
        case "armeabi-v7a":
        {
          preferedGdbAbiToolPrefix = "arm-linux-androideabi";

          break;
        }

        case "arm64-v8a":
        {
          if (allow64BitAbis)
          {
            preferedGdbAbiToolPrefix = "aarch64-linux-android";
          }
          else
          {
            preferedGdbAbiToolPrefix = "arm-linux-androideabi";
          }

          break;
        }

        case "x86":
        {
          preferedGdbAbiToolPrefix = "i686-linux-android";

          break;
        }

        case "x86_64":
        {
          if (allow64BitAbis)
          {
            preferedGdbAbiToolPrefix = "x86_64-linux-android";
          }
          else
          {
            preferedGdbAbiToolPrefix = "i686-linux-android";
          }

          break;
        }

        case "mips":
        {
          preferedGdbAbiToolPrefix = "mipsel-linux-android";

          break;
        }

        case "mips64":
        {
          if (allow64BitAbis)
          {
            preferedGdbAbiToolPrefix = "mips64el-linux-android";
          }
          else
          {
            preferedGdbAbiToolPrefix = "mipsel-linux-android";
          }

          break;
        }
      }

      if (string.IsNullOrEmpty (preferedGdbAbiToolPrefix))
      {
        throw new InvalidOperationException (string.Format ("Unrecognised target primary CPU ABI: {0}", debugProgram.DebugProcess.NativeProcess.PrimaryCpuAbi));
      }

      bool preferedGdbAbiIs64Bit = preferedGdbAbiToolPrefix.Contains ("64");

      Engine.Broadcast (new DebugEngineEvent.DebuggerConnectionEvent (DebugEngineEvent.DebuggerConnectionEvent.EventType.LogStatus, string.Format ("Configuring GDB for '{0}' target...", preferedGdbAbiToolPrefix)), null, null);

      // 
      // Android++ bundles its own copies of GDB to get round various NDK issues. Search for these.
      // 

      string androidPlusPlusRoot = Environment.GetEnvironmentVariable ("ANDROID_PLUS_PLUS");

      // 
      // Build GDB version permutations.
      // 

      List<string> gdbToolPermutations = new List<string> ();

      string [] availableHostArchitectures = new string [] { "x86", "x86_64" };

      foreach (string arch in availableHostArchitectures)
      {
        if (arch.Contains ("64") && !Environment.Is64BitOperatingSystem)
        {
          continue;
        }

        string gdbToolFilePattern = string.Format ("{0}-gdb.cmd", preferedGdbAbiToolPrefix);

        string [] availableVersionPaths = Directory.GetDirectories (Path.Combine (androidPlusPlusRoot, "contrib", "gdb", "bin", arch), "*.*.*", SearchOption.TopDirectoryOnly);

        foreach (string versionPath in availableVersionPaths)
        {
          string [] gdbToolMatches = Directory.GetFiles (versionPath, gdbToolFilePattern, SearchOption.TopDirectoryOnly);

          foreach (string tool in gdbToolMatches)
          {
            gdbToolPermutations.Add (tool);
          }
        }
      }

      if (gdbToolPermutations.Count == 0)
      {
        throw new InvalidOperationException ("Could not locate required 32/64-bit GDB deployments.");
      }
      else
      {
        // 
        // Pick the oldest GDB version available if running 'Jelly Bean' or below.
        // 

        bool forceNdkR9dClient = (debugProgram.DebugProcess.NativeProcess.HostDevice.SdkVersion <= AndroidSettings.VersionCode.JELLY_BEAN);

        if (forceNdkR9dClient)
        {
          m_gdbSetup = new GdbSetup (debugProgram.DebugProcess.NativeProcess, gdbToolPermutations [0]);
        }
        else
        {
          m_gdbSetup = new GdbSetup (debugProgram.DebugProcess.NativeProcess, gdbToolPermutations [gdbToolPermutations.Count - 1]);
        }

        // 
        // A symbolic link to 'share' is placed in the architecture directory, provide GDB with that location.
        // 

        string architecturePath = Path.GetDirectoryName (Path.GetDirectoryName (m_gdbSetup.GdbToolPath));

        string pythonGdbScriptsPath = Path.Combine (architecturePath, "share", "gdb");

        m_gdbSetup.GdbToolArguments += " --data-directory " + PathUtils.SantiseWindowsPath (pythonGdbScriptsPath);
      }

      if (m_gdbSetup == null)
      {
        throw new InvalidOperationException ("Could not evaluate a suitable GDB instance. Ensure you have the correct NDK deployment for your system's architecture.");
      }

      if (m_launchConfiguration != null)
      {
        string launchDirectory;

        if (m_launchConfiguration.TryGetValue ("LaunchSuspendedDir", out launchDirectory))
        {
          m_gdbSetup.SymbolDirectories.Add (launchDirectory);
        }
      }

      GdbServer = new GdbServer (m_gdbSetup);

      GdbClient = new GdbClient (m_gdbSetup);

      GdbClient.OnResultRecord = OnClientResultRecord;

      GdbClient.OnAsyncRecord = OnClientAsyncRecord;

      GdbClient.OnStreamRecord = OnClientStreamRecord;

      GdbClient.Start ();
    }