//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public CLangDebuggeeProperty (CLangDebugger debugger, CLangDebuggeeStackFrame stackFrame, MiVariable gdbVariable) : base (debugger.Engine, stackFrame, gdbVariable.Expression, string.Empty) { m_debugger = debugger; m_gdbVariable = gdbVariable; }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public CLangDebuggeeBreakpointBound (CLangDebugger debugger, DebugBreakpointManager breakpointManager, DebuggeeBreakpointPending pendingBreakpoint, DebuggeeCodeContext codeContext, MiBreakpoint gdbBreakpoint) : base (breakpointManager, pendingBreakpoint, codeContext) { m_debugger = debugger; GdbBreakpoint = gdbBreakpoint; }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public CLangDebuggeeBreakpointError (CLangDebugger debugger, DebugBreakpointManager breakpointManager, DebuggeeBreakpointPending pendingBreakpoint, DebuggeeCodeContext codeContext, MiBreakpoint gdbBreakpoint, string error) : base (breakpointManager, pendingBreakpoint, codeContext, error) { m_debugger = debugger; GdbBreakpoint = gdbBreakpoint; }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public CLangDebuggeeProperty (CLangDebugger debugger, CLangDebuggeeStackFrame stackFrame, string expression, string value) : base (debugger.Engine, stackFrame, expression, value) { m_debugger = debugger; m_gdbVariable = null; }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public CLangDebuggeeDisassemblyStream (CLangDebugger debugger, enum_DISASSEMBLY_STREAM_SCOPE streamScope, IDebugCodeContext2 codeContext) { m_debugger = debugger; m_streamScope = streamScope; m_codeContext = codeContext as DebuggeeCodeContext; }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public CLangDebuggeeThread (CLangDebugger debugger, CLangDebuggeeProgram program, uint id) : base (program.DebugProgram, id, string.Format ("[Native-{0}]", id)) { m_debugger = debugger; NativeProgram = program; RequiresRefresh = true; }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public CLangDebuggeeProgram (CLangDebugger debugger, DebuggeeProgram debugProgram) { m_debugger = debugger; DebugProgram = debugProgram; IsRunning = false; m_debugModules = new Dictionary<string, DebuggeeModule> (); m_debugThreads = new Dictionary<uint, DebuggeeThread> (); }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public CLangDebuggeeStackFrame (CLangDebugger debugger, CLangDebuggeeThread thread, MiResultValueTuple frameTuple, string frameName) : base (debugger.Engine, thread as DebuggeeThread, frameName) { m_debugger = debugger; if (frameTuple == null) { throw new ArgumentNullException ("frameTuple"); } m_queriedRegisters = false; m_queriedArgumentsAndLocals = false; GetInfoFromCurrentLevel (frameTuple); }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public int OnDetachClient (CLangDebugger debugger) { LoggingUtils.PrintFunction (); try { bool shouldContinue = false; ManualResetEvent detachLock = new ManualResetEvent (false); debugger.RunInterruptOperation (delegate (CLangDebugger _debugger) { _debugger.GdbClient.Detach (); detachLock.Set (); }, shouldContinue); bool detachedSignaled = detachLock.WaitOne (1000); if (!detachedSignaled) { throw new InvalidOperationException ("Failed to detach GDB client"); } return Constants.S_OK; } catch (Exception e) { LoggingUtils.HandleException (e); throw; } }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public int OnAttachClient (CLangDebugger debugger) { LoggingUtils.PrintFunction (); try { GdbServer gdbServer = debugger.GdbServer; debugger.GdbClient.Attach (gdbServer); return Constants.S_OK; } catch (Exception e) { LoggingUtils.HandleException (e); throw; } }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public int OnTerminateServer (CLangDebugger debugger) { LoggingUtils.PrintFunction (); try { debugger.GdbServer.Kill (); return Constants.S_OK; } catch (Exception e) { LoggingUtils.HandleException (e); throw; } }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public CLangDebuggeeBreakpointPending (CLangDebugger debugger, DebugBreakpointManager breakpointManager, IDebugBreakpointRequest2 breakpointRequest) : base (breakpointManager, breakpointRequest) { m_debugger = debugger; }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 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 static CLangDebuggeeCodeContext GetCodeContextForDocumentContext (CLangDebugger debugger, DebuggeeDocumentContext documentContext) { LoggingUtils.PrintFunction (); string fileName; TEXT_POSITION [] startOffset = new TEXT_POSITION [1]; TEXT_POSITION [] endOffset = new TEXT_POSITION [1]; LoggingUtils.RequireOk (documentContext.GetName (enum_GETNAME_TYPE.GN_FILENAME, out fileName)); LoggingUtils.RequireOk (documentContext.GetStatementRange (startOffset, endOffset)); string location = string.Format ("\"{0}:{1}\"", fileName, startOffset [0].dwLine + 1); return GetCodeContextForLocation (debugger, location); }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 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; }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public int OnContinueClient (CLangDebugger debugger) { LoggingUtils.PrintFunction (); try { debugger.GdbClient.Continue (); return Constants.S_OK; } catch (Exception e) { LoggingUtils.HandleException (e); throw; } }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public int OnTerminateClient (CLangDebugger debugger) { LoggingUtils.PrintFunction (); try { debugger.GdbClient.Stop (); debugger.GdbClient.Terminate (); return Constants.S_OK; } catch (Exception e) { LoggingUtils.HandleException (e); return Constants.E_FAIL; } }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public CLangDebuggerVariableManager (CLangDebugger debugger) { m_debugger = debugger; m_trackedVariables = new Dictionary<string, MiVariable> (); }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #region IDebugEngine2 Members //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public int Attach (IDebugProgram2 [] rgpPrograms, IDebugProgramNode2 [] rgpProgramNodes, uint celtPrograms, IDebugEventCallback2 ad7Callback, enum_ATTACH_REASON dwReason) { // // Attach the debug engine to a program. // LoggingUtils.PrintFunction (); m_sdmCallback = new DebugEngineCallback (this, ad7Callback); m_cLangCallback = new CLangDebuggerCallback (this); m_javaLangCallback = new JavaLangDebuggerCallback (this); try { if ((rgpPrograms == null) || (rgpPrograms.Length == 0)) { throw new ApplicationException ("Attach failed. No target process specified."); } if (celtPrograms > 1) { throw new ApplicationException ("Attach failed. Can not debug multiple target processes concurrently."); } if (Program != null) { throw new ApplicationException ("Attach failed. Already attached to " + Program.DebugProcess.NativeProcess.Name); } AndroidAdb.Refresh (); Program = rgpPrograms [0] as DebuggeeProgram; Program.AttachedEngine = this; Program.DebugProcess.NativeProcess.RefreshPackageInfo (); Broadcast (new DebugEngineEvent.DebuggerConnectionEvent (DebugEngineEvent.DebuggerConnectionEvent.EventType.LogStatus, string.Format ("Starting GDB client...")), null, null); NativeDebugger = new CLangDebugger (this, m_launchConfiguration, Program); Broadcast (new DebugEngineEvent.DebuggerConnectionEvent (DebugEngineEvent.DebuggerConnectionEvent.EventType.LogStatus, string.Format ("Starting JDB client...")), null, null); JavaDebugger = new JavaLangDebugger (this, Program); ThreadPool.QueueUserWorkItem (delegate (object obj) { // // When this method is called, the DE needs to send these events in sequence: // 1. IDebugEngineCreate2 // 2. IDebugProgramCreateEvent2 // 3. IDebugLoadCompleteEvent2 // 4. (if enum_ATTACH_REASON.ATTACH_REASON_LAUNCH), IDebugEntryPointEvent2 // try { Broadcast (new DebugEngineEvent.EngineCreate (this), Program, null); // // Run a couple of tests which prevent the run-as tool from functioning properly: // // 1) Test if this device/emulator is susceptible to a (usually 4.3 specific) run-as permissions bug. // https://code.google.com/p/android/issues/detail?id=58373 // 2) Test if the installed package is not declared 'debuggable'. // AndroidDevice debuggingDevice = Program.DebugProcess.NativeProcess.HostDevice; string runasPackageFileList = debuggingDevice.Shell (string.Format ("run-as {0}", Program.DebugProcess.NativeProcess.Name), "ls -l"); if (runasPackageFileList.Contains (string.Format ("run-as: Package '{0}' is unknown", Program.DebugProcess.NativeProcess.Name))) { throw new InvalidOperationException ("Can not debug native code on this device/emulator.\nMore info: https://code.google.com/p/android/issues/detail?id=58373"); } else if (runasPackageFileList.Contains (string.Format ("run-as: Package '{0}' is not debuggable", Program.DebugProcess.NativeProcess.Name))) { throw new InvalidOperationException (string.Format ("Package '{0}' is not debuggable.\nPlease ensure you're trying to connect to a 'Debug' application.\nAlternatively, completely uninstall the current app and try again.", Program.DebugProcess.NativeProcess.Name)); } Broadcast (new DebugEngineEvent.DebuggerConnectionEvent (DebugEngineEvent.DebuggerConnectionEvent.EventType.LogStatus, string.Format ("Attaching to '{0}'...", Program.DebugProcess.NativeProcess.Name)), null, null); LoggingUtils.RequireOk (Program.Attach (m_sdmCallback), "Failed to attach to target application."); CLangDebuggeeThread currentThread = null; NativeDebugger.RunInterruptOperation (delegate (CLangDebugger debugger) { debugger.NativeProgram.RefreshAllThreads (); currentThread = debugger.NativeProgram.GetThread (debugger.NativeProgram.CurrentThreadId); if (currentThread == null) { // Lack of current thread is usually a good indication that connection/attaching failed. throw new InvalidOperationException (string.Format ("Failed to retrieve program's main thread (tid: {0}).", debugger.NativeProgram.CurrentThreadId)); } }); Broadcast (new DebugEngineEvent.ProgramCreate (), Program, null); Broadcast (new DebugEngineEvent.LoadComplete (), Program, currentThread); if (dwReason == enum_ATTACH_REASON.ATTACH_REASON_LAUNCH) { Broadcast (new DebugEngineEvent.EntryPoint (), Program, currentThread); } Broadcast (new DebugEngineEvent.AttachComplete (), Program, null); Broadcast (new DebugEngineEvent.DebuggerLogcatEvent (debuggingDevice), Program, null); Broadcast (new DebugEngineEvent.DebuggerConnectionEvent (DebugEngineEvent.DebuggerConnectionEvent.EventType.LogStatus, string.Format ("Attached successfully to '{0}'.", Program.DebugProcess.NativeProcess.Name)), null, null); Broadcast (new DebugEngineEvent.DebuggerConnectionEvent (DebugEngineEvent.DebuggerConnectionEvent.EventType.CloseDialog, string.Empty), null, null); } catch (Exception e) { LoggingUtils.HandleException (e); Broadcast (ad7Callback, new DebugEngineEvent.Error (e.Message, true), Program, null); Detach (Program); } }); return Constants.S_OK; } catch (Exception e) { LoggingUtils.HandleException (e); Broadcast (ad7Callback, new DebugEngineEvent.Error (e.Message, true), Program, null); Detach (Program); return Constants.E_FAIL; } }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public CLangDebuggeeMemoryBytes (CLangDebugger debugger) { m_debugger = debugger; }