private void InitializeRegisters() { WorkerThread.RunOperation(async() => { if (_registers != null) { return; // already initialized } string[] names = await MICommandFactory.DataListRegisterNames(); if (_registers != null) { return; // already initialized } EngineUtils.RegisterNameMap nameMap = EngineUtils.RegisterNameMap.Create(names); List <RegisterDescription> desc = new List <RegisterDescription>(); var registerGroups = new List <RegisterGroup>(); for (int i = 0; i < names.Length; ++i) { if (String.IsNullOrEmpty(names[i])) { continue; // ignore the empty names } RegisterGroup grp = GetGroupForRegister(registerGroups, names[i], nameMap); desc.Add(new RegisterDescription(names[i], grp, i)); } _registerGroups = registerGroups.AsReadOnly(); _registers = desc.AsReadOnly(); }); }
public async Task Step(int threadId, enum_STEPKIND kind, enum_STEPUNIT unit) { if ((unit == enum_STEPUNIT.STEP_LINE) || (unit == enum_STEPUNIT.STEP_STATEMENT)) { switch (kind) { case enum_STEPKIND.STEP_INTO: await MICommandFactory.ExecStep(threadId); break; case enum_STEPKIND.STEP_OVER: await MICommandFactory.ExecNext(threadId); break; case enum_STEPKIND.STEP_OUT: await MICommandFactory.ExecFinish(threadId); break; default: throw new NotImplementedException(); } } else if (unit == enum_STEPUNIT.STEP_INSTRUCTION) { await MICommandFactory.ExecStepInstruction(threadId); } else { throw new NotImplementedException(); } }
public async Task <List <ulong> > StartAddressesForLine(string file, uint line) { List <ulong> addresses = new List <ulong>(); var srcLines = await SourceLineCache.GetLinesForFile(file); if (srcLines == null || srcLines.Length == 0) { srcLines = await SourceLineCache.GetLinesForFile(System.IO.Path.GetFileName(file)); } if (srcLines != null && srcLines.Length > 0) { bool gotoNextFunc = false; foreach (var l in srcLines) { if (gotoNextFunc) { if (l.Line == 0) { gotoNextFunc = false; } } else if (line == l.Line) { addresses.Add(l.AddrStart); gotoNextFunc = true; } } } if (addresses.Count == 0) { // ask the underlying debugger for the line info addresses = await MICommandFactory.StartAddressesForLine(EscapePath(file), line); } return(addresses); }
public ExceptionManager(MICommandFactory commandFactory, WorkerThread worker, ISampleEngineCallback callback, /*OPTIONAL*/ string registryRoot) { Debug.Assert(commandFactory != null, "Missing commandFactory"); Debug.Assert(worker != null, "Missing worker"); Debug.Assert(callback != null, "Missing callback"); _commandFactory = commandFactory; _worker = worker; _callback = callback; _categoryMap = ReadDefaultSettings(registryRoot); }
public ExceptionManager(MICommandFactory commandFactory, WorkerThread worker, ISampleEngineCallback callback, /*OPTIONAL*/ HostConfigurationStore configStore) { Debug.Assert(commandFactory != null, "Missing commandFactory"); Debug.Assert(worker != null, "Missing worker"); Debug.Assert(callback != null, "Missing callback"); _commandFactory = commandFactory; _worker = worker; _callback = callback; _categoryMap = ReadDefaultSettings(configStore); }
//This method gets the value/type info for the method parameters without creating an MI debugger varialbe for them. For use in the callstack window //NOTE: eval is not called public async Task <List <SimpleVariableInformation> > GetParameterInfoOnly(AD7Thread thread, ThreadContext ctx) { List <SimpleVariableInformation> parameters = new List <SimpleVariableInformation>(); ValueListValue localAndParameters = await MICommandFactory.StackListVariables(PrintValues.SimpleValues, thread.Id, ctx.Level); foreach (var results in localAndParameters.Content.Where(r => r.TryFindString("arg") == "1")) { parameters.Add(new SimpleVariableInformation(results.FindString("name"), /*isParam*/ true, results.FindString("value"), results.FindString("type"))); } return(parameters); }
public async Task <Tuple <int, string>[]> GetRegisters(int threadId, uint level) { TupleValue[] values = await MICommandFactory.DataListRegisterValues(threadId); Tuple <int, string>[] regValues = new Tuple <int, string> [values.Length]; for (int i = 0; i < values.Length; ++i) { int index = values[i].FindInt("number"); string regContent = values[i].FindString("value"); regValues[i] = new Tuple <int, string>(index, regContent); } return(regValues); }
public void SendDebuggerAborted(MICommandFactory commandFactory, string lastSentCommandName, /*OPTIONAL*/ string debuggerExitCode) { List <KeyValuePair <string, object> > eventProperties = new List <KeyValuePair <string, object> >(); eventProperties.Add(new KeyValuePair <string, object>(Property_DebuggerName, commandFactory.Name)); eventProperties.Add(new KeyValuePair <string, object>(Property_LastSentCommandName, lastSentCommandName)); if (!string.IsNullOrEmpty(debuggerExitCode)) { eventProperties.Add(new KeyValuePair <string, object>(Property_DebuggerExitCode, debuggerExitCode)); } HostTelemetry.SendEvent(Event_DebuggerAborted, eventProperties.ToArray()); }
public async Task ResumeFromLaunch() { _connected = true; // Send any strings we got before the process came up if (_pendingMessages?.Length != 0) { _callback.OnOutputString(_pendingMessages.ToString()); _pendingMessages = null; } await this.ExceptionManager.EnsureSettingsUpdated(); if (_initialBreakArgs != null) { await CheckModules(); _libraryLoaded.Clear(); await HandleBreakModeEvent(_initialBreakArgs); _initialBreakArgs = null; } else { switch (_launchOptions.LaunchCompleteCommand) { case LaunchCompleteCommand.ExecRun: await MICommandFactory.ExecRun(); break; case LaunchCompleteCommand.ExecContinue: await MICommandFactory.ExecContinue(); break; case LaunchCompleteCommand.None: break; default: Debug.Fail("Not implemented enum code for LaunchCompleteCommand??"); throw new NotImplementedException(); } FireDeviceAppLauncherResume(); } }
//This method gets the locals and parameters and creates an MI debugger variable for each one so that we can manipulate them (and expand children, etc.) //NOTE: Eval is called internal async Task <List <VariableInformation> > GetLocalsAndParameters(AD7Thread thread, ThreadContext ctx) { List <VariableInformation> variables = new List <VariableInformation>(); ValueListValue localsAndParameters = await MICommandFactory.StackListVariables(PrintValues.NoValues, thread.Id, ctx.Level); foreach (var localOrParamResult in localsAndParameters.Content) { string name = localOrParamResult.FindString("name"); bool isParam = localOrParamResult.TryFindString("arg") == "1"; SimpleVariableInformation simpleInfo = new SimpleVariableInformation(name, isParam); VariableInformation vi = await simpleInfo.CreateMIDebuggerVariable(ctx, Engine, thread); variables.Add(vi); } return(variables); }
public async Task ResumeFromLaunch() { _connected = true; if (_initialBreakArgs != null) { await CheckModules(); _libraryLoaded.Clear(); await HandleBreakModeEvent(_initialBreakArgs); _initialBreakArgs = null; } else { switch (_launchOptions.LaunchCompleteCommand) { case LaunchCompleteCommand.ExecRun: await MICommandFactory.ExecRun(); break; case LaunchCompleteCommand.ExecContinue: await MICommandFactory.ExecContinue(); break; case LaunchCompleteCommand.None: break; default: Debug.Fail("Not implemented enum code for LaunchCompleteCommand??"); throw new NotImplementedException(); } FireDeviceAppLauncherResume(); } }
//This method gets the value/type info for the method parameters of all frames without creating an mi debugger varialbe for them. For use in the callstack window //NOTE: eval is not called public async Task <List <ArgumentList> > GetParameterInfoOnly(AD7Thread thread, bool values, bool types, uint low, uint high) { var frames = await MICommandFactory.StackListArguments(values || types?PrintValues.SimpleValues : PrintValues.NoValues, thread.Id, low, high); List <ArgumentList> parameters = new List <ArgumentList>(); foreach (var f in frames) { int level = f.FindInt("level"); ListValue argList = null; f.TryFind <ListValue>("args", out argList); List <SimpleVariableInformation> args = new List <SimpleVariableInformation>(); if (argList != null) { if (argList is ValueListValue) // a tuple for each arg { foreach (var arg in ((ValueListValue)argList).Content) { args.Add(new SimpleVariableInformation(arg.FindString("name"), /*isParam*/ true, arg.TryFindString("value"), arg.TryFindString("type"))); } } else { // simple arg name list string[] names = ((ResultListValue)argList).FindAllStrings("name"); foreach (var n in names) { args.Add(new SimpleVariableInformation(n, /*isParam*/ true, null, null)); } } } parameters.Add(new ArgumentList(level, args)); } return(parameters); }
private async Task CheckModules() { // NOTE: The version of GDB that comes in the Android SDK doesn't support -file-list-shared-library // so we need to use the console command //string results = await MICommandFactory.GetSharedLibrary(); string results = await ConsoleCmdAsync("info sharedlibrary"); using (StringReader stringReader = new StringReader(results)) { while (true) { string line = stringReader.ReadLine(); if (line == null) { break; } ulong startAddr = 0; ulong endAddr = 0; if (line.StartsWith("From")) // header line, ignore { continue; } else if (line.StartsWith("0x")) // module with load address { // line format: 0x<hex start addr> 0x<hex end addr> [ Yes | No ] <filename> line = MICommandFactory.SpanNextAddr(line, out startAddr); if (line == null) { continue; } line = MICommandFactory.SpanNextAddr(line, out endAddr); if (line == null || endAddr < startAddr) { continue; } } line = line.Trim(); bool symbolsLoaded; if (line.StartsWith("Yes")) { symbolsLoaded = true; line = line.Substring(3); } else if (line.StartsWith("No")) { symbolsLoaded = false; line = line.Substring(2); } else { continue; } line = line.Trim(); line = line.TrimEnd(new char[] { '"', '\n' }); var module = FindModule(line); if (module == null) { module = new DebuggedModule(line, line, startAddr, endAddr - startAddr, symbolsLoaded, line, _loadOrder++); lock (_moduleList) { _moduleList.Add(module); } _callback.OnModuleLoad(module); } } } }
public DebuggedProcess(bool bLaunched, LaunchOptions launchOptions, ISampleEngineCallback callback, WorkerThread worker, BreakpointManager bpman, AD7Engine engine) { uint processExitCode = 0; g_Process = this; _bStarted = false; _pendingMessages = new StringBuilder(400); _worker = worker; _launchOptions = launchOptions; _breakpointManager = bpman; Engine = engine; _libraryLoaded = new List <string>(); _loadOrder = 0; MICommandFactory = MICommandFactory.GetInstance(launchOptions.DebuggerMIMode, this); _waitDialog = MICommandFactory.SupportsStopOnDynamicLibLoad() ? new WaitDialog(ResourceStrings.LoadingSymbolMessage, ResourceStrings.LoadingSymbolCaption) : null; Natvis = new Natvis.Natvis(this); // we do NOT have real Win32 process IDs, so we use a guid AD_PROCESS_ID pid = new AD_PROCESS_ID(); pid.ProcessIdType = (int)enum_AD_PROCESS_ID.AD_PROCESS_ID_GUID; pid.guidProcessId = Guid.NewGuid(); this.Id = pid; SourceLineCache = new SourceLineCache(this); _callback = callback; _moduleList = new List <DebuggedModule>(); ThreadCache = new ThreadCache(callback, this); Disassembly = new Disassembly(this); VariablesToDelete = new List <string>(); MessageEvent += delegate(object o, string message) { // We can get messages before we have started the process // but we can't send them on until it is if (_bStarted) { _callback.OnOutputString(message); } else { _pendingMessages.Append(message); } }; LibraryLoadEvent += delegate(object o, EventArgs args) { ResultEventArgs results = args as MICore.Debugger.ResultEventArgs; string file = results.Results.TryFindString("host-name"); if (!string.IsNullOrEmpty(file) && MICommandFactory.SupportsStopOnDynamicLibLoad()) { _libraryLoaded.Add(file); if (_waitDialog != null) { _waitDialog.ShowWaitDialog(file); } } else if (this.MICommandFactory.Mode == MIMode.Clrdbg) { string id = results.Results.FindString("id"); ulong baseAddr = results.Results.FindAddr("base-address"); uint size = results.Results.FindUint("size"); bool symbolsLoaded = results.Results.FindInt("symbols-loaded") != 0; var module = new DebuggedModule(id, file, baseAddr, size, symbolsLoaded, string.Empty, _loadOrder++); lock (_moduleList) { _moduleList.Add(module); } _callback.OnModuleLoad(module); } else if (!string.IsNullOrEmpty(file)) { string addr = results.Results.TryFindString("loaded_addr"); if (string.IsNullOrEmpty(addr) || addr == "-") { return; // identifies the exe, not a real load } // generate module string id = results.Results.TryFindString("name"); bool symsLoaded = true; string symPath = null; if (results.Results.Contains("symbols-path")) { symPath = results.Results.FindString("symbols-path"); if (string.IsNullOrEmpty(symPath)) { symsLoaded = false; } } else { symPath = file; } ulong loadAddr = results.Results.FindAddr("loaded_addr"); uint size = results.Results.FindUint("size"); if (String.IsNullOrEmpty(id)) { id = file; } var module = FindModule(id); if (module == null) { module = new DebuggedModule(id, file, loadAddr, size, symsLoaded, symPath, _loadOrder++); lock (_moduleList) { _moduleList.Add(module); } _callback.OnModuleLoad(module); } } }; if (_launchOptions is LocalLaunchOptions) { this.Init(new MICore.LocalTransport(), _launchOptions); } else if (_launchOptions is PipeLaunchOptions) { this.Init(new MICore.PipeTransport(), _launchOptions); } else if (_launchOptions is TcpLaunchOptions) { this.Init(new MICore.TcpTransport(), _launchOptions); } else if (_launchOptions is SerialLaunchOptions) { string port = ((SerialLaunchOptions)_launchOptions).Port; this.Init(new MICore.SerialTransport(port), _launchOptions); } else { throw new ArgumentOutOfRangeException("LaunchInfo.options"); } MIDebugCommandDispatcher.AddProcess(this); // When the debuggee exits, we need to exit the debugger ProcessExitEvent += delegate(object o, EventArgs args) { // NOTE: Exceptions leaked from this method may cause VS to crash, be careful ResultEventArgs results = args as MICore.Debugger.ResultEventArgs; if (results.Results.Contains("exit-code")) { processExitCode = results.Results.FindUint("exit-code"); } // quit MI Debugger _worker.PostOperation(CmdExitAsync); if (_waitDialog != null) { _waitDialog.EndWaitDialog(); } }; // When the debugger exits, we tell AD7 we are done DebuggerExitEvent += delegate(object o, EventArgs args) { // NOTE: Exceptions leaked from this method may cause VS to crash, be careful // this is the last AD7 Event we can ever send // Also the transport is closed when this returns _callback.OnProcessExit(processExitCode); Dispose(); }; DebuggerAbortedEvent += delegate(object o, EventArgs args) { // NOTE: Exceptions leaked from this method may cause VS to crash, be careful // The MI debugger process unexpectedly exited. _worker.PostOperation(() => { // If the MI Debugger exits before we get a resume call, we have no way of sending program destroy. So just let start debugging fail. if (!_connected) { return; } _callback.OnError(MICoreResources.Error_MIDebuggerExited); _callback.OnProcessExit(uint.MaxValue); Dispose(); }); }; ModuleLoadEvent += async delegate(object o, EventArgs args) { // NOTE: This is an async void method, so make sure exceptions are caught and somehow reported if (_libraryLoaded.Count != 0) { string moduleNames = string.Join(", ", _libraryLoaded); try { _libraryLoaded.Clear(); SourceLineCache.OnLibraryLoad(); await _breakpointManager.BindAsync(); await CheckModules(); _bLastModuleLoadFailed = false; } catch (Exception e) { if (this.ProcessState == MICore.ProcessState.Exited) { return; // ignore exceptions after the process has exited } string exceptionDescription = EngineUtils.GetExceptionDescription(e); string message = string.Format(CultureInfo.CurrentCulture, MICoreResources.Error_ExceptionProcessingModules, moduleNames, exceptionDescription); // to avoid spamming the user, if the last module failed, we send the next failure to the output windiw instead of a message box if (!_bLastModuleLoadFailed) { _callback.OnError(message); _bLastModuleLoadFailed = true; } else { _callback.OnOutputMessage(message, enum_MESSAGETYPE.MT_OUTPUTSTRING); } } } if (_waitDialog != null) { _waitDialog.EndWaitDialog(); } if (MICommandFactory.SupportsStopOnDynamicLibLoad()) { CmdContinueAsync(); } }; // When we break we need to gather information BreakModeEvent += async delegate(object o, EventArgs args) { // NOTE: This is an async void method, so make sure exceptions are caught and somehow reported ResultEventArgs results = args as MICore.Debugger.ResultEventArgs; if (_waitDialog != null) { _waitDialog.EndWaitDialog(); } if (!this._connected) { _initialBreakArgs = results; return; } try { await HandleBreakModeEvent(results); } catch (Exception e) { if (this.ProcessState == MICore.ProcessState.Exited) { return; // ignore exceptions after the process has exited } string exceptionDescription = EngineUtils.GetExceptionDescription(e); string message = string.Format(CultureInfo.CurrentCulture, MICoreResources.Error_FailedToEnterBreakState, exceptionDescription); _callback.OnError(message); Terminate(); } }; RunModeEvent += delegate(object o, EventArgs args) { // NOTE: Exceptions leaked from this method may cause VS to crash, be careful if (!_bStarted) { _bStarted = true; // Send any strings we got before the process came up if (_pendingMessages.Length != 0) { try { _callback.OnOutputString(_pendingMessages.ToString()); } catch { // If something goes wrong sending the output, lets not crash VS } } _pendingMessages = null; } }; ErrorEvent += delegate(object o, EventArgs args) { // NOTE: Exceptions leaked from this method may cause VS to crash, be careful ResultEventArgs result = (ResultEventArgs)args; _callback.OnError(result.Results.FindString("msg")); }; ThreadCreatedEvent += delegate(object o, EventArgs args) { ResultEventArgs result = (ResultEventArgs)args; ThreadCache.ThreadEvent(result.Results.FindInt("id"), /*deleted */ false); }; ThreadExitedEvent += delegate(object o, EventArgs args) { ResultEventArgs result = (ResultEventArgs)args; ThreadCache.ThreadEvent(result.Results.FindInt("id"), /*deleted*/ true); }; BreakChangeEvent += _breakpointManager.BreakpointModified; }
private async Task HandleBreakModeEvent(ResultEventArgs results) { string reason = results.Results.TryFindString("reason"); int tid; if (!results.Results.Contains("thread-id")) { Results res = await MICommandFactory.ThreadInfo(); tid = res.FindInt("id"); } else { tid = results.Results.FindInt("thread-id"); } ThreadCache.MarkDirty(); MICommandFactory.DefineCurrentThread(tid); DebuggedThread thread = await ThreadCache.GetThread(tid); await ThreadCache.StackFrames(thread); // prepopulate the break thread in the thread cache ThreadContext cxt = await ThreadCache.GetThreadContext(thread); ThreadCache.SendThreadEvents(this, null); // make sure that new threads have been pushed to the UI //always delete breakpoints pending deletion on break mode //the flag tells us if we hit an existing breakpoint pending deletion that we need to continue await _breakpointManager.DeleteBreakpointsPendingDeletion(); //delete varialbes that have been GC'd List <string> variablesToDelete = new List <string>(); lock (VariablesToDelete) { foreach (var variable in VariablesToDelete) { variablesToDelete.Add(variable); } VariablesToDelete.Clear(); } foreach (var variable in variablesToDelete) { try { await MICommandFactory.VarDelete(variable); } catch (MIException) { //not much to do really, we're leaking MI debugger variables. Debug.Fail("Failed to delete variable: " + variable + ". This is leaking memory in the MI Debugger."); } } if (String.IsNullOrWhiteSpace(reason) && !_bEntrypointHit) { // CLRDBG TODO: Try to verify this code path _bEntrypointHit = true; CmdContinueAsync(); FireDeviceAppLauncherResume(); } else if (reason == "breakpoint-hit") { string bkptno = results.Results.FindString("bkptno"); ulong addr = cxt.pc ?? 0; AD7BoundBreakpoint bkpt = null; bool fContinue; TupleValue frame = results.Results.TryFind <TupleValue>("frame"); bkpt = _breakpointManager.FindHitBreakpoint(bkptno, addr, frame, out fContinue); // use breakpoint number to resolve breakpoint if (bkpt != null) { List <object> bplist = new List <object>(); bplist.Add(bkpt); _callback.OnBreakpoint(thread, bplist.AsReadOnly()); } else if (!_bEntrypointHit) { _bEntrypointHit = true; _callback.OnEntryPoint(thread); } else { if (fContinue) { //we hit a bp pending deletion //post the CmdContinueAsync operation so it does not happen until we have deleted all the pending deletes CmdContinueAsync(); } else { // not one of our breakpoints, so stop with a message _callback.OnException(thread, "Unknown breakpoint", "", 0); } } } else if (reason == "end-stepping-range" || reason == "function-finished") { _callback.OnStepComplete(thread); } else if (reason == "signal-received") { string name = results.Results.TryFindString("signal-name"); if ((name == "SIG32") || (name == "SIG33")) { // we are going to ignore these (Sigma) signals for now CmdContinueAsync(); } else if (MICommandFactory.IsAsyncBreakSignal(results.Results)) { _callback.OnAsyncBreakComplete(thread); } else { uint code = 0; string sigName = results.Results.TryFindString("signal-name"); code = results.Results.Contains("signal") ? results.Results.FindUint("signal") : 0; if (String.IsNullOrEmpty(sigName) && code != 0 && EngineUtils.SignalMap.Instance.ContainsValue(code)) { sigName = EngineUtils.SignalMap.Instance.First((p) => p.Value == code).Key; } else if (!String.IsNullOrEmpty(sigName) && code == 0 && EngineUtils.SignalMap.Instance.ContainsKey(sigName)) { code = EngineUtils.SignalMap.Instance[sigName]; } _callback.OnException(thread, sigName, results.Results.TryFindString("signal-meaning"), code); } } else if (reason == "exception-received") { string exception = results.Results.FindString("exception"); _callback.OnException(thread, "Exception", exception, 0); } else { Debug.Fail("Unknown stopping reason"); _callback.OnException(thread, "Unknown", "Unknown stopping event", 0); } }