void GetStackTrace(Request request) { _session.Machine.Registers.Read(); if (_session.Machine.Memory.ReadConfiguration()) { _session.Machine.Memory.Log(); } // disassemble from current PC var disassemblyUpdated = _session.Machine.UpdateDisassembly(_session.Machine.Registers.PC); var originBytes = new byte[4]; _stackInfo.Clear(); _stackFrames.Clear(); // add current PC as an entry to the stack frame _stackInfo.Add(new StackInfo(_session.Machine.Registers.PC)); var stackPos = _session.Machine.Registers.SP; // read bytes from SP onwards for analysis of the addresses var stackBytes = new byte[20]; var maxBytes = Math.Min(stackBytes.Length, 0xFFFF - stackPos); var bytes = _session.Machine.Memory.Read(_session.Machine.Registers.SP, stackBytes, 0, maxBytes); // turn the bytes into ushorts for (var i = 0; i < bytes; i += 2) { _stackInfo.Add(new StackInfo((ushort)((stackBytes[i + 1] << 8) | stackBytes[i]))); } // now check out each address for (var i = 0; i < _stackInfo.Count; i++) { // note: entry at i=0 is PC, so we don't need to get mem and we always show it var stackFrameId = i + 1; var addr = _stackInfo[i].Address; AddressDetails addressDetails = null; bool isCode = false; var symbolIcon = ""; if (i == 0) { // always try to get symbol for PC addressDetails = _session.Machine.GetAddressDetails(addr); isCode = true; } else { _session.Machine.Memory.Read((ushort)(addr - 3), originBytes, 0, 3); if (_callerOpcode3.Contains(originBytes[0])) { addr -= 3; addressDetails = _session.Machine.GetAddressDetails(addr); isCode = true; symbolIcon = " ↑"; // // we can get the original destination for the call here: // var callDest = (ushort) ( caller[2] << 8 | caller[1] ); // var callDestSymbol = GetPreviousSymbol( callDest, ref disassemblyUpdated ); // if( callDestSymbol != null ) // { // if( _stackFrames.Count > 0 ) // _stackFrames[_stackFrames.Count - 1].name = callDestSymbol + " -> " + _stackFrames[_stackFrames.Count - 1].name; // } } else if (_callerOpcode3.Contains(originBytes[1])) { addr -= 2; addressDetails = _session.Machine.GetAddressDetails(addr); isCode = true; } else if (_callerOpcode1.Contains(originBytes[2])) { addr -= 1; addressDetails = _session.Machine.GetAddressDetails(addr); isCode = true; symbolIcon += " ↖"; } _stackInfo[i].Address = addr; } if (addressDetails?.Labels != null && addressDetails.LabelledAddress != addressDetails.Address) { disassemblyUpdated |= _session.Machine.UpdateDisassembly(addressDetails.LabelledAddress); } var style = i == 0 ? "subtle" : "normal"; var text = addressDetails?.Labels?[0].Name ?? addr.ToHex(); if (addressDetails?.Source != null) { // got source // highlight the source line separately // we always trace through the disassembly _stackInfo[i].SourceFilename = addressDetails.Source.File.Filename; _stackInfo[i].SourceLine = addressDetails.Source.Line; } if (addressDetails != null) { // no source, but probably labels _stackFrames.Add( new StackFrame( stackFrameId, addressDetails.GetRelativeText() + " " + symbolIcon, DisassemblySource, 0, 0, style ) ); } else if (isCode) { // no labels, but it's code _stackFrames.Add( new StackFrame( stackFrameId, text + " " + symbolIcon, DisassemblySource, 0, 0, style ) ); } else { // not code, just a raw value _stackFrames.Add( new StackFrame( stackFrameId, addr.ToHex(), StackSource, 0, 0, style ) ); } } if (disassemblyUpdated) { _session.Machine.WriteDisassemblyFile(DisassemblyFile); } foreach (var frame in _stackFrames) { if (frame.source == DisassemblySource && frame.line == 0) { frame.line = _session.Machine.GetLineOfAddressInDisassembly(_stackInfo[frame.id - 1].Address) + 1; } } _session.VSCode.Send( request, new StackTraceResponseBody( _stackFrames ) ); foreach (var m in _memWatches) { Check_MemoryWatch(m.Value); } }