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

    public int CreateBoundBreakpoint (MiBreakpoint breakpoint, DebuggeeDocumentContext documentContext, DebuggeeCodeContext codeContext)
    {
      LoggingUtils.PrintFunction ();

      try
      {
        if (breakpoint == null)
        {
          throw new ArgumentNullException ("breakpoint");
        }

        if (breakpoint.IsPending ())
        {
          // 
          // Address can't be satisfied. Unsatisfied likely indicates the modules or symbols associated with the context aren't loaded, yet.
          // 

          DebuggeeAddress pendingAddress = new DebuggeeAddress (MiBreakpoint.Pending);

          DebuggeeCodeContext pendingContext = new CLangDebuggeeCodeContext (m_debugger, pendingAddress, documentContext);

          LoggingUtils.RequireOk (CreateErrorBreakpoint ("Additional library symbols required.", breakpoint, documentContext, pendingContext));
        }
        else if (breakpoint.IsMultiple ())
        {
          // 
          // Breakpoint satisfied to multiple locations, no single memory address available.
          // 

          CLangDebuggeeBreakpointBound boundBreakpoint = new CLangDebuggeeBreakpointBound (m_debugger, m_breakpointManager, this, codeContext, breakpoint);

          lock (m_boundBreakpoints)
          {
            m_boundBreakpoints.Clear ();

            m_boundBreakpoints.Add (boundBreakpoint);
          }

          m_debugger.Engine.Broadcast (new DebugEngineEvent.BreakpointBound (this, boundBreakpoint), m_debugger.NativeProgram.DebugProgram, m_debugger.NativeProgram.GetThread (m_debugger.NativeProgram.CurrentThreadId));
        }
        else
        {
          // 
          // Address satisfied, and the breakpoint is legitimately bound.
          // 

          DebuggeeAddress boundAddress = new DebuggeeAddress (breakpoint.Address);

          DebuggeeCodeContext addressContext = new CLangDebuggeeCodeContext (m_debugger, boundAddress, documentContext);

          CLangDebuggeeBreakpointBound boundBreakpoint = new CLangDebuggeeBreakpointBound (m_debugger, m_breakpointManager, this, addressContext, breakpoint);

          lock (m_boundBreakpoints)
          {
            m_boundBreakpoints.Clear ();

            m_boundBreakpoints.Add (boundBreakpoint);
          }

          m_debugger.Engine.Broadcast (new DebugEngineEvent.BreakpointBound (this, boundBreakpoint), m_debugger.NativeProgram.DebugProgram, m_debugger.NativeProgram.GetThread (m_debugger.NativeProgram.CurrentThreadId));
        }

        return Constants.S_OK;
      }
      catch (Exception e)
      {
        LoggingUtils.HandleException (e);

        return Constants.E_FAIL;
      }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    private void GetInfoFromCurrentLevel (MiResultValueTuple frameTuple)
    {
      LoggingUtils.PrintFunction ();

      try
      {
        if (frameTuple == null)
        {
          throw new ArgumentNullException ("frameTuple");
        }

        if (frameTuple.HasField ("level"))
        {
          m_stackLevel = frameTuple ["level"] [0].GetUnsignedInt ();
        }

        // 
        // Discover the function or shared library location.
        // 

        if (frameTuple.HasField ("addr"))
        {
          m_locationAddress = new DebuggeeAddress (frameTuple ["addr"] [0].GetString ());
        }
        else
        {
          m_locationAddress = new DebuggeeAddress ("0x0");
        }

        if (frameTuple.HasField ("func"))
        {
          m_locationFunction = frameTuple ["func"] [0].GetString ();
        }
        else
        {
          m_locationFunction = "??";
        }

        m_locationIsSymbolicated = !(m_locationFunction.Equals ("??"));

        if (frameTuple.HasField ("from"))
        {
          m_locationModule = Path.GetFileName (frameTuple ["from"] [0].GetString ());
        }
        else
        {
          m_locationModule = string.Empty;
        }

        // 
        // Generate code and document contexts for this frame location.
        // 

        if (frameTuple.HasField ("fullname") && frameTuple.HasField ("line"))
        {
          // 
          // If the symbol table isn't yet loaded, we'll need to specify exactly the location of this stack frame.
          // 

          TEXT_POSITION [] textPositions = new TEXT_POSITION [2];

          textPositions [0].dwLine = frameTuple ["line"] [0].GetUnsignedInt () - 1;

          textPositions [0].dwColumn = 0;

          textPositions [1].dwLine = textPositions [0].dwLine;

          textPositions [1].dwColumn = textPositions [0].dwColumn;

          string filename = PathUtils.ConvertPathCygwinToWindows (frameTuple ["fullname"] [0].GetString ());

          m_documentContext = new DebuggeeDocumentContext (m_debugger.Engine, filename, textPositions [0], textPositions [1]);

          m_codeContext = CLangDebuggeeCodeContext.GetCodeContextForLocation (m_debugger, m_locationAddress.ToString ());

          if (m_codeContext == null)
          {
            throw new InvalidOperationException ();
          }
        }
        else
        {
          m_codeContext = CLangDebuggeeCodeContext.GetCodeContextForLocation (m_debugger, m_locationAddress.ToString ());

          m_documentContext = (m_codeContext != null) ? m_codeContext.DocumentContext : null;
        }
      }
      catch (Exception e)
      {
        LoggingUtils.HandleException (e);
      }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    public CLangDebuggeeCodeContext (CLangDebugger debugger, DebuggeeAddress address, DebuggeeDocumentContext documentContext)
      : base (debugger.Engine, documentContext, address)
    {
      m_debugger = debugger;

      try
      {
        string command = string.Format ("-interpreter-exec console \"info symbol {0}\"", m_address.ToString ());

        MiResultRecord resultRecord = m_debugger.GdbClient.SendSyncCommand (command);

        MiResultRecord.RequireOk (resultRecord, command);

        string pattern = "(?<symbol>.+)( [\\+] (?<offset>[0-9]+))? (in section (?<section>[^ ]+) of) (?<module>.+)";

        Regex regExMatcher = new Regex (pattern, RegexOptions.IgnoreCase);

        foreach (MiStreamRecord record in resultRecord.Records)
        {
          if (!record.Stream.StartsWith ("No symbol"))
          {
            continue; // early rejection.
          }

          StringBuilder sanitisedStream = new StringBuilder (record.Stream);

          sanitisedStream.Length -= 2; // Strip trailing "\\n"

          Match regExLineMatch = regExMatcher.Match (sanitisedStream.ToString ());

          if (regExLineMatch.Success)
          {
            string symbol = regExLineMatch.Result ("${symbol}");

            string offset = regExLineMatch.Result ("${offset}");

            string section = regExLineMatch.Result ("${section}");

            string module = regExLineMatch.Result ("${module}");

            ulong addressOffset = 0ul;

            ulong.TryParse (offset, out addressOffset);

            m_symbolName = symbol;

            m_symbolOffset = addressOffset.ToString ();

            //string moduleFile = Path.GetFileName (module);

            //CLangDebuggeeModule module = m_debugger.NativeProgram.GetModule (moduleFile);

            //MODULE_INFO [] moduleArray = new MODULE_INFO [1];

            //LoggingUtils.RequireOk (module.GetInfo (enum_MODULE_INFO_FIELDS.MIF_URL, moduleArray));

            //infoArray [0].bstrModuleUrl = moduleArray [0].m_bstrUrl;

            m_symbolModule = PathUtils.ConvertPathMingwToWindows (module);
          }
        }
      }
      catch (Exception e)
      {
        LoggingUtils.HandleException (e);
      }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    public int Read (uint dwInstructions, enum_DISASSEMBLY_STREAM_FIELDS dwFields, out uint pdwInstructionsRead, DisassemblyData [] prgDisassembly)
    {
      // 
      // Reads instructions starting from the current position in the disassembly stream.
      // 

      LoggingUtils.PrintFunction ();

      try
      {
        ulong startAddress = m_codeContext.Address.MemoryAddress;

        ulong endAddress = startAddress + ((ulong)(dwInstructions) * 4); // TODO: 4 for a 32-bit instruction set?

        string disassemblyCommand = string.Format ("-data-disassemble -s 0x{0:X8} -e 0x{1:X8} -- 1", startAddress, endAddress);

        MiResultRecord resultRecord = m_debugger.GdbClient.SendSyncCommand (disassemblyCommand);

        MiResultRecord.RequireOk (resultRecord, disassemblyCommand);

        if (!resultRecord.HasField ("asm_insns"))
        {
          throw new InvalidOperationException ("-data-disassemble result missing 'asm_insns' field");
        }

        MiResultValueList assemblyRecords = (MiResultValueList) resultRecord ["asm_insns"] [0];

        long maxInstructions = Math.Min (assemblyRecords.Values.Count, dwInstructions);

        if (maxInstructions == 0)
        {
          throw new InvalidOperationException ();
        }

        int currentInstruction = 0;

        for (int i = 0; i < assemblyRecords.Values.Count; ++i)
        {
          MiResultValue recordValue = assemblyRecords [i];

          if (recordValue.Variable.Equals ("src_and_asm_line"))
          {
            // 
            // Parse mixed-mode disassembly reports.
            // 

            uint line = recordValue ["line"] [0].GetUnsignedInt ();

            string file = recordValue ["file"] [0].GetString ();

            MiResultValueList lineAsmInstructionValues = (MiResultValueList) recordValue ["line_asm_insn"] [0];

            foreach (MiResultValue instructionValue in lineAsmInstructionValues.Values)
            {
              string address = instructionValue ["address"] [0].GetString ();

              if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_ADDRESS) != 0)
              {
                prgDisassembly [currentInstruction].bstrAddress = address;

                prgDisassembly [currentInstruction].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_ADDRESS;
              }

              if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_ADDRESSOFFSET) != 0)
              {
                string offset = instructionValue ["offset"] [0].GetString ();

                prgDisassembly [currentInstruction].bstrAddressOffset = offset;

                prgDisassembly [currentInstruction].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_ADDRESSOFFSET;
              }

              if (((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_OPCODE) != 0) || ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_OPERANDS) != 0) || ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_OPERANDS_SYMBOLS) != 0))
              {
                string inst = instructionValue ["inst"] [0].GetString ();

                string [] operations = inst.Split (new string [] { "\\t" }, StringSplitOptions.None);

                if (operations.Length > 0)
                {
                  prgDisassembly [currentInstruction].bstrOpcode = operations [0];

                  prgDisassembly [currentInstruction].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_OPCODE;
                }

                if (operations.Length > 1)
                {
                  prgDisassembly [currentInstruction].bstrOperands = operations [1];

                  prgDisassembly [currentInstruction].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_OPERANDS;
                }

                if (operations.Length > 2)
                {
                  prgDisassembly [currentInstruction].bstrOperands += " " + operations [2];

                  prgDisassembly [currentInstruction].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_OPERANDS_SYMBOLS;
                }
              }

              if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_SYMBOL) != 0)
              {
                string functionName = instructionValue ["func-name"] [0].GetString ();

                prgDisassembly [currentInstruction].bstrSymbol = functionName;

                prgDisassembly [currentInstruction].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_SYMBOL;
              }

              if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_CODELOCATIONID) != 0)
              {
                DebuggeeAddress instructionAddress = new DebuggeeAddress (address);

                prgDisassembly [currentInstruction].uCodeLocationId = instructionAddress.MemoryAddress;

                prgDisassembly [currentInstruction].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_CODELOCATIONID;
              }

              if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_POSITION) != 0)
              {
                prgDisassembly [currentInstruction].posBeg.dwLine = line;

                prgDisassembly [currentInstruction].posBeg.dwColumn = 0;

                prgDisassembly [currentInstruction].posEnd.dwLine = line;

                prgDisassembly [currentInstruction].posEnd.dwColumn = uint.MaxValue;

                prgDisassembly [currentInstruction].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_POSITION;
              }

              if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_DOCUMENTURL) != 0)
              {
                prgDisassembly [currentInstruction].bstrDocumentUrl = "file://" + file;

                prgDisassembly [currentInstruction].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_DOCUMENTURL;
              }

              if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_BYTEOFFSET) != 0)
              {
                uint offset = instructionValue ["offset"] [0].GetUnsignedInt ();

                prgDisassembly [currentInstruction].dwByteOffset = offset;

                prgDisassembly [currentInstruction].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_BYTEOFFSET;
              }

              if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_FLAGS) != 0)
              {
                prgDisassembly [currentInstruction].dwFlags |= enum_DISASSEMBLY_FLAGS.DF_HASSOURCE;

                prgDisassembly [currentInstruction].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_FLAGS;
              }

              if (++currentInstruction >= maxInstructions)
              {
                break;
              }
            }
          }
        }

        pdwInstructionsRead = (uint) currentInstruction;

        return Constants.S_OK;
      }
      catch (Exception e)
      {
        LoggingUtils.HandleException (e);

        pdwInstructionsRead = 0;

        return Constants.E_FAIL;
      }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    public static CLangDebuggeeCodeContext GetCodeContextForLocation (CLangDebugger debugger, string location)
    {
      LoggingUtils.PrintFunction ();

      try
      {
        if (string.IsNullOrEmpty (location))
        {
          throw new ArgumentNullException ("location");
        }

        if (location.StartsWith ("0x"))
        {
          location = "*" + location;
        }
        else if (location.StartsWith ("\""))
        {
          location = location.Replace ("\\", "/");

          location = location.Replace ("\"", "\\\""); // required to escape the nested string.
        }

        string command = string.Format ("-interpreter-exec console \"info line {0}\"", location);

        MiResultRecord resultRecord = debugger.GdbClient.SendSyncCommand (command);

        MiResultRecord.RequireOk (resultRecord, command);

        string pattern = "Line (?<line>[0-9]+) of ([\\]*\"(?<file>.+)[\\]*[\"]+) starts at address (?<startaddr>[^ ]+) (?<startsym>[^+]+[+]?[0-9]*[>]?) (but contains no code|and ends at (?<endaddr>[^ ]+) (?<endsym>[^+]+[+]?[0-9]*[>]?)?)";

        pattern = pattern.Replace ("\\", "\\\\");

        Regex regExMatcher = new Regex (pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase);

        foreach (MiStreamRecord record in resultRecord.Records)
        {
          if (!record.Stream.StartsWith ("Line"))
          {
            continue; // early rejection.
          }

          StringBuilder sanitisedStream = new StringBuilder (record.Stream);

          sanitisedStream.Replace ("\\\"", "\"");

          sanitisedStream.Replace ("\\\\", "\\");

          Match regExLineMatch = regExMatcher.Match (sanitisedStream.ToString ());

          if (regExLineMatch.Success)
          {
            string line = regExLineMatch.Result ("${line}");

            string file = regExLineMatch.Result ("${file}");

            string startaddr = regExLineMatch.Result ("${startaddr}");

            string startsym = regExLineMatch.Result ("${startsym}");

            string endaddr = regExLineMatch.Result ("${endaddr}");

            string endsym = regExLineMatch.Result ("${endsym}");

            TEXT_POSITION [] documentPositions = new TEXT_POSITION [2];

            documentPositions [0].dwLine = uint.Parse (line) - 1;

            documentPositions [0].dwColumn = 0;

            documentPositions [1].dwLine = documentPositions [0].dwLine;

            documentPositions [1].dwColumn = uint.MaxValue;

            DebuggeeAddress startAddress = new DebuggeeAddress (startaddr);

            DebuggeeDocumentContext documentContext = new DebuggeeDocumentContext (debugger.Engine, file, documentPositions [0], documentPositions [1]);

            CLangDebuggeeCodeContext codeContext = new CLangDebuggeeCodeContext (debugger, startAddress, documentContext);

            documentContext.SetCodeContext (codeContext);

            return codeContext;
          }
        }
      }
      catch (Exception e)
      {
        LoggingUtils.HandleException (e);
      }

      return null;
    }