diff options
author | Thays Grazia <thaystg@gmail.com> | 2022-06-24 20:04:17 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-06-24 20:04:17 +0300 |
commit | 74da8b11c514d57edd21379c860d289ddc228c30 (patch) | |
tree | fc15050184ab8e1bbe939cd3b55e3fce44f927ab | |
parent | 7cc29ac19969ffdda3844efd8f9984c90df7430e (diff) |
Support new hotreload features and support line changes (#365)
* About HotReload now we can do in an android app and the debug will continue working:
1) add lines before the breakpoint and the breakpoint will continue working
2) remove lines before the breakpoint and the breakpoint will continue working
3) add new static methods
4) add new static fields
5) add new classes
Also we check what is supported by runtime to make it possible from debugger.
* Addressing @lewing comments.
* Fix wrong implementation.
* Changing enum as it was changed on runtime
* Addressing @nosami comments.
11 files changed, 216 insertions, 41 deletions
diff --git a/Mono.Debugger.Soft/Mono.Debugger.Soft.csproj b/Mono.Debugger.Soft/Mono.Debugger.Soft.csproj index 263a216..94045d1 100644 --- a/Mono.Debugger.Soft/Mono.Debugger.Soft.csproj +++ b/Mono.Debugger.Soft/Mono.Debugger.Soft.csproj @@ -27,6 +27,7 @@ <ItemGroup> <PackageReference Include="Mono.Cecil" Version="$(NuGetVersionCecil)" PrivateAssets="all" /> <PackageReference Include="System.Runtime" Version="4.3.1" /> + <PackageReference Include="System.Reflection.Metadata" Version="5.0" /> </ItemGroup> </Project> diff --git a/Mono.Debugger.Soft/Mono.Debugger.Soft/AssemblyMirror.cs b/Mono.Debugger.Soft/Mono.Debugger.Soft/AssemblyMirror.cs index 4d2b091..5cb4d33 100644 --- a/Mono.Debugger.Soft/Mono.Debugger.Soft/AssemblyMirror.cs +++ b/Mono.Debugger.Soft/Mono.Debugger.Soft/AssemblyMirror.cs @@ -4,6 +4,9 @@ using Mono.Debugger; using System.Collections.Generic; using System.IO; +using System.Reflection.Metadata;
+using System.Reflection.Metadata.Ecma335;
+
#if ENABLE_CECIL using Mono.Cecil; #endif @@ -28,7 +31,7 @@ namespace Mono.Debugger.Soft Dictionary<uint, long> tokenMethodCache = new Dictionary<uint, long> (); #if ENABLE_CECIL - AssemblyDefinition meta; + Mono.Cecil.AssemblyDefinition meta; #endif internal AssemblyMirror (VirtualMachine vm, long id) : base (vm, id) { @@ -130,7 +133,7 @@ namespace Mono.Debugger.Soft * An optional Cecil assembly which could be used to access metadata instead * of reading it from the debuggee. */ - public AssemblyDefinition Metadata { + public Mono.Cecil.AssemblyDefinition Metadata { get { if (meta != null) return meta; @@ -147,12 +150,12 @@ namespace Mono.Debugger.Soft // Read assembly metadata from the debuggee // Since protocol version 2.47 - public AssemblyDefinition GetMetadata () { + public Mono.Cecil.AssemblyDefinition GetMetadata () { if (IsDynamic) throw new NotSupportedException (); using (var ms = new MemoryStream (GetMetadataBlob ())) - return meta = AssemblyDefinition.ReadAssembly (ms); + return meta = Mono.Cecil.AssemblyDefinition.ReadAssembly (ms); } #endif @@ -237,5 +240,40 @@ namespace Mono.Debugger.Soft return has_debug_info.Value; } } + + public void ApplyChanges_DebugInformation (byte[] metadataDelta, byte[] pdbDelta) + { + var asmStream = new MemoryStream (metadataDelta); + MetadataReader asmMetadataReaderParm = MetadataReaderProvider.FromMetadataStream (asmStream).GetMetadataReader (); + var pdbStream = new MemoryStream (pdbDelta); + MetadataReader pdbMetadataReaderParm = MetadataReaderProvider.FromPortablePdbStream (pdbStream).GetMetadataReader (); + + TypeInfo typeInfo = null; + int methodIdxAsm = 1; + foreach (var entry in asmMetadataReaderParm.GetEditAndContinueLogEntries ()) { + if (entry.Operation == EditAndContinueOperation.AddField) { + var typeHandle = (TypeDefinitionHandle)entry.Handle; + try + { + GetType ((uint)MetadataTokens.GetToken (asmMetadataReaderParm, typeHandle)).ClearCachedDebugInfo (); + } + catch (Exception) {
+ //type does not exist yet
+ }
+ }
+ } + + foreach (var entry in pdbMetadataReaderParm.GetEditAndContinueMapEntries ()) {
+ if (entry.Kind == HandleKind.MethodDebugInformation) {
+ try {
+ var methodToken = (pdbMetadataReaderParm.GetRowNumber (entry) << 24) | (6);
+ this.GetMethod ((uint)methodToken).SetUpdatedByEnC ();
+ }
+ catch (Exception) {
+ //method does not exist yet
+ }
+ }
+ }
+ } } } diff --git a/Mono.Debugger.Soft/Mono.Debugger.Soft/Connection.cs b/Mono.Debugger.Soft/Mono.Debugger.Soft/Connection.cs index 1c7d5d1..caa1ac7 100644 --- a/Mono.Debugger.Soft/Mono.Debugger.Soft/Connection.cs +++ b/Mono.Debugger.Soft/Mono.Debugger.Soft/Connection.cs @@ -514,7 +514,8 @@ namespace Mono.Debugger.Soft GET_TYPES = 12, INVOKE_METHODS = 13, START_BUFFERING = 14, - STOP_BUFFERING = 15 + STOP_BUFFERING = 15, + GET_ENC_CAPABILITIES = 21 } enum CmdEvent { @@ -1961,6 +1962,12 @@ namespace Mono.Debugger.Soft } } + internal string VM_EnCCapabilities ()
+ { + PacketReader r = SendReceive (CommandSet.VM, (int)CmdVM.GET_ENC_CAPABILITIES, new PacketWriter ()); + return r.ReadString (); + } + internal delegate void InvokeMethodCallback (ValueImpl v, ValueImpl exc, ValueImpl out_this, ValueImpl[] out_args, ErrorCode error, object state); void read_invoke_res (PacketReader r, out ValueImpl v, out ValueImpl exc, out ValueImpl out_this, out ValueImpl[] out_args) { diff --git a/Mono.Debugger.Soft/Mono.Debugger.Soft/EncEvents.cs b/Mono.Debugger.Soft/Mono.Debugger.Soft/EncEvents.cs index 3228175..9eff4da 100644 --- a/Mono.Debugger.Soft/Mono.Debugger.Soft/EncEvents.cs +++ b/Mono.Debugger.Soft/Mono.Debugger.Soft/EncEvents.cs @@ -9,7 +9,6 @@ namespace Mono.Debugger.Soft { this.id = id; method = vm.GetMethod (id); - method.ClearCachedLocalsDebugInfo (); } public MethodMirror GetMethod()
diff --git a/Mono.Debugger.Soft/Mono.Debugger.Soft/Location.cs b/Mono.Debugger.Soft/Mono.Debugger.Soft/Location.cs index 6833e9a..be3a8cb 100644 --- a/Mono.Debugger.Soft/Mono.Debugger.Soft/Location.cs +++ b/Mono.Debugger.Soft/Mono.Debugger.Soft/Location.cs @@ -48,7 +48,10 @@ namespace Mono.Debugger.Soft get { return line_number; } - } + set {
+ this.line_number = value;
+ }
+ } // Since protocol version 2.19, 0 in earlier protocol versions public int ColumnNumber { diff --git a/Mono.Debugger.Soft/Mono.Debugger.Soft/MethodMirror.cs b/Mono.Debugger.Soft/Mono.Debugger.Soft/MethodMirror.cs index 1500aa4..f64f7d8 100644 --- a/Mono.Debugger.Soft/Mono.Debugger.Soft/MethodMirror.cs +++ b/Mono.Debugger.Soft/Mono.Debugger.Soft/MethodMirror.cs @@ -25,7 +25,8 @@ namespace Mono.Debugger.Soft MethodBodyMirror body; MethodMirror gmd; TypeMirror[] type_args; - + bool updatedByEnC; +
#if ENABLE_CECIL C.MethodDefinition meta; #endif @@ -254,9 +255,12 @@ namespace Mono.Debugger.Soft public void ClearCachedLocalsDebugInfo ()
{
- locals = null;
- debug_info = null;
- locations = null;
+ if (updatedByEnC) {
+ locals = null;
+ debug_info = null;
+ locations = null;
+ }
+ updatedByEnC = false;
} public LocalScope [] GetScopes () { @@ -460,6 +464,32 @@ namespace Mono.Debugger.Soft var interp = new ILInterpreter (this); return interp.Evaluate (this_val, args); - } + }
+
+ internal void ApplySourceChanges (SourceUpdate sourceUpdate)
+ {
+ if (updatedByEnC)
+ return;
+ var lineDiff = 0;
+ if (Locations.Count > 0) {
+ lineDiff = sourceUpdate.FindLineDiff (locations[0].LineNumber);
+ }
+ if (lineDiff != 0)
+ {
+ foreach (var location in locations)
+ {
+ location.LineNumber += lineDiff;
+ }
+ for (int i = 0; i < debug_info.end_line_numbers.Count(); i++) {
+ debug_info.end_line_numbers[i] += lineDiff;
+ debug_info.line_numbers[i] += lineDiff;
+ }
+ }
+ }
+
+ internal void SetUpdatedByEnC ()
+ {
+ updatedByEnC = true;
+ }
} } diff --git a/Mono.Debugger.Soft/Mono.Debugger.Soft/TypeMirror.cs b/Mono.Debugger.Soft/Mono.Debugger.Soft/TypeMirror.cs index a13543a..b15e68a 100644 --- a/Mono.Debugger.Soft/Mono.Debugger.Soft/TypeMirror.cs +++ b/Mono.Debugger.Soft/Mono.Debugger.Soft/TypeMirror.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq;
using System.Reflection; using System.Threading.Tasks; @@ -16,7 +17,7 @@ namespace Mono.Debugger.Soft */ public class TypeMirror : Mirror, IInvokable { - MethodMirror[] methods; + List<MethodMirror> methods; AssemblyMirror ass; ModuleMirror module; FieldInfoMirror[] fields; @@ -416,20 +417,26 @@ namespace Mono.Debugger.Soft public MethodMirror[] GetMethods () { if (methods == null) { long[] ids = vm.conn.Type_GetMethods (id); - MethodMirror[] m = new MethodMirror [ids.Length]; + methods = new List<MethodMirror>(ids.Length); for (int i = 0; i < ids.Length; ++i) { - m [i] = vm.GetMethod (ids [i]); + methods.Add(vm.GetMethod (ids [i])); } - methods = m; } - return methods; - } - - // FIXME: Sync this with Type + return methods.ToArray(); + } + public void AddMethodIfNotExist (MethodMirror method)
+ {
+ if (!Array.Exists(GetMethods (), (m => m.GetId() == method.GetId()))) {
+ //is EnC
+ methods.Add (method);
+ }
+ }
+
+ // FIXME: Sync this with Type
public MethodMirror GetMethod (string name) { - foreach (var m in GetMethods ()) - if (m.Name == name) - return m; + foreach (var m in GetMethods ())
+ if (m.Name == name)
+ return m;
return null; } @@ -450,6 +457,12 @@ namespace Mono.Debugger.Soft return fields; } + public void ClearCachedDebugInfo ()
+ {
+ fields = null;
+ properties = null;
+ }
+ public FieldInfoMirror GetField (string name) { if (name == null) throw new ArgumentNullException ("name"); @@ -896,10 +909,19 @@ namespace Mono.Debugger.Soft if (!iface_map.TryGetValue (interfaceType, out res)) throw new ArgumentException ("Interface not found", "interfaceType"); return res; - } - - // Return whenever the type initializer of this type has ran - // Since protocol version 2.23 + }
+
+ public void ApplySourceChanges (SourceUpdate sourceUpdate)
+ {
+ var methods = GetMethods ();
+ foreach (var method in methods)
+ {
+ method.ApplySourceChanges (sourceUpdate);
+ }
+ }
+
+ // Return whenever the type initializer of this type has ran
+ // Since protocol version 2.23
public bool IsInitialized { get { vm.CheckProtocolVersion (2, 23); @@ -908,5 +930,25 @@ namespace Mono.Debugger.Soft return inited; } } - } + }
+
+ public class SourceUpdate
+ {
+ public List<Tuple<int, int>> LineUpdates { get; } //Tuple<oldLine, newLine>
+ public string FileName { get; }
+
+ public SourceUpdate (string fileName)
+ {
+ this.FileName = fileName;
+ LineUpdates = new List<Tuple<int, int>> ();
+ }
+
+ internal int FindLineDiff (int lineNumber)
+ {
+ var line = LineUpdates.FirstOrDefault (item => item.Item1 + 1 == lineNumber);
+ if (line != null)
+ return line.Item2 - line.Item1;
+ return 0;
+ }
+ } } diff --git a/Mono.Debugger.Soft/Mono.Debugger.Soft/VirtualMachine.cs b/Mono.Debugger.Soft/Mono.Debugger.Soft/VirtualMachine.cs index 3d51655..078a0c9 100644 --- a/Mono.Debugger.Soft/Mono.Debugger.Soft/VirtualMachine.cs +++ b/Mono.Debugger.Soft/Mono.Debugger.Soft/VirtualMachine.cs @@ -792,8 +792,15 @@ namespace Mono.Debugger.Soft internal void CheckProtocolVersion (int major, int minor) { if (!conn.Version.AtLeast (major, minor)) throw new NotSupportedException ("This request is not supported by the protocol version implemented by the debuggee."); - } - } + }
+
+ public string GetEnCCapabilities ()
+ {
+ if (conn.Version.AtLeast (2, 61))
+ return conn.VM_EnCCapabilities ();
+ return "Baseline";
+ }
+ } class EventHandler : MarshalByRefObject, IEventHandler { diff --git a/Mono.Debugging.Soft/SoftDebuggerSession.cs b/Mono.Debugging.Soft/SoftDebuggerSession.cs index a173d25..ecd28ca 100644 --- a/Mono.Debugging.Soft/SoftDebuggerSession.cs +++ b/Mono.Debugging.Soft/SoftDebuggerSession.cs @@ -48,6 +48,7 @@ using Mono.Debugging.Client; using Mono.Debugging.Evaluation; using StackFrame = Mono.Debugger.Soft.StackFrame; +using System.Collections.Immutable;
namespace Mono.Debugging.Soft { @@ -76,6 +77,7 @@ namespace Mono.Debugging.Soft bool loggedSymlinkedRuntimesBug; SoftDebuggerStartArgs startArgs; List<string> userAssemblyNames; + List<SourceUpdate> sourceUpdates; ThreadInfo[] current_threads; string remoteProcessName; long currentAddress = -1; @@ -99,6 +101,7 @@ namespace Mono.Debugging.Soft Adaptor = CreateSoftDebuggerAdaptor (); Adaptor.BusyStateChanged += (sender, e) => SetBusyState (e); Adaptor.DebuggerSession = this; + sourceUpdates = new List<SourceUpdate> (); } protected virtual SoftDebuggerAdaptor CreateSoftDebuggerAdaptor () @@ -1193,12 +1196,12 @@ namespace Mono.Debugging.Soft found = true; breakInfo.SetStatus (BreakEventStatus.Bound, null); } - lock (pending_bes) { pending_bes.Add (breakInfo); } if (!found) { + breakInfo.Breakpoint = breakpoint; if (insideLoadedRange) breakInfo.SetStatus (BreakEventStatus.Invalid, null); else @@ -2415,15 +2418,33 @@ namespace Mono.Debugging.Soft void HandleMethodUpdateEvents(MethodUpdateEvent[] methods)
{
- foreach (var method in methods)
+ foreach (var method in methods) //add new methods to type
{
- foreach (var bp in breakpoints) {
- if (bp.Value.Location.Method.GetId() == method.GetMethod().GetId())
- {
- bool dummy = false;
- var l = FindLocationByMethod (bp.Value.Location.Method, bp.Value.Location.SourceFile, bp.Value.Location.LineNumber, bp.Value.Location.ColumnNumber, ref dummy);
- bp.Value.Location = l;
- UpdateBreakpoint ((Breakpoint)bp.Value.BreakEvent, bp.Value);
+ method.GetMethod ().ClearCachedLocalsDebugInfo ();
+ method.GetMethod ().DeclaringType.AddMethodIfNotExist (method.GetMethod ());
+ }
+ foreach (var breakpoint in breakpoints) {
+ Breakpoint bp = ((Breakpoint)breakpoint.Value.BreakEvent);
+ if (bp.UpdatedByEnC) {
+ bool dummy = false;
+ var l = FindLocationByMethod (breakpoint.Value.Location.Method, bp.FileName, bp.Line, bp.Column, ref dummy);
+ if (l != null) {
+ breakpoint.Value.Location = l;
+ UpdateBreakpoint (bp, breakpoint.Value);
+ bp.UpdatedByEnC = false;
+ }
+ }
+ }
+ foreach (var bp in pending_bes) {
+ if (bp.Status != BreakEventStatus.Bound) {
+ foreach (var location in FindLocationsByFile (bp.Breakpoint.FileName, bp.Breakpoint.Line, bp.Breakpoint.Column, out _, out bool insideLoadedRange)) {
+ OnDebuggerOutput (false, string.Format ("Resolved pending breakpoint at '{0}:{1},{2}' to {3} [0x{4:x5}].\n",
+ bp.Breakpoint.FileName, bp.Breakpoint.Line, bp.Breakpoint.Column,
+ GetPrettyMethodName (location.Method), location.ILOffset));
+
+ bp.Location = location;
+ InsertBreakpoint (bp.Breakpoint, bp);
+ bp.SetStatus (BreakEventStatus.Bound, null);
}
}
}
@@ -3342,6 +3363,14 @@ namespace Mono.Debugging.Soft return lines.ToArray (); } + public void AddSourceUpdate (string fileName)
+ {
+ sourceUpdates.Add (new SourceUpdate(fileName));
+ } + public void AddLineUpdate (int oldLine, int newLine)
+ {
+ sourceUpdates.Last().LineUpdates.Add (new Tuple<int, int>(oldLine, newLine));
+ } public void ApplyChanges (ModuleMirror module, byte[] metadataDelta, byte[] ilDelta, byte[] pdbDelta = null) { var rootDomain = VirtualMachine.RootDomain; @@ -3353,7 +3382,22 @@ namespace Mono.Debugging.Soft else pdbArray = rootDomain.CreateByteArray (pdbDelta); - module.ApplyChanges (metadataArray, ilArray, pdbArray); + module.Assembly.ApplyChanges_DebugInformation (metadataDelta, pdbDelta);
+ module.ApplyChanges (metadataArray, ilArray, pdbArray);
+ foreach (var sourceUpdate in sourceUpdates) + { + var types = VirtualMachine.GetTypesForSourceFile (sourceUpdate.FileName, false); + foreach (var type in types) + { + type.ApplySourceChanges (sourceUpdate); + } + } + sourceUpdates.Clear (); + } + + public string GetEnCCapabilities()
+ {
+ return vm.GetEnCCapabilities ();
} static string EscapeString (string text) { diff --git a/Mono.Debugging/Mono.Debugging.Client/BreakEventInfo.cs b/Mono.Debugging/Mono.Debugging.Client/BreakEventInfo.cs index 074d098..dfda575 100644 --- a/Mono.Debugging/Mono.Debugging.Client/BreakEventInfo.cs +++ b/Mono.Debugging/Mono.Debugging.Client/BreakEventInfo.cs @@ -52,6 +52,8 @@ namespace Mono.Debugging.Client /// </summary> public BreakEventStatus Status { get; private set; } + public Breakpoint Breakpoint { get; set; }
+
/// <summary> /// Gets a description of the status /// </summary> diff --git a/Mono.Debugging/Mono.Debugging.Client/Breakpoint.cs b/Mono.Debugging/Mono.Debugging.Client/Breakpoint.cs index 8770b1d..f201f2c 100644 --- a/Mono.Debugging/Mono.Debugging.Client/Breakpoint.cs +++ b/Mono.Debugging/Mono.Debugging.Client/Breakpoint.cs @@ -117,7 +117,9 @@ namespace Mono.Debugging.Client ResetAdjustedColumn (); column = newColumn; } - + + public bool UpdatedByEnC { get; set; } + public void SetLine (int newLine) { ResetAdjustedLine (); |