Merge pull request #1048 from DavidKarlas/cacheThreadsAndBulkFetch
authorJeffrey Stedfast <jeff@xamarin.com>
Wed, 28 May 2014 17:59:27 +0000 (13:59 -0400)
committerJeffrey Stedfast <jeff@xamarin.com>
Wed, 28 May 2014 17:59:27 +0000 (13:59 -0400)
[Mono.Debugger.Soft] Caching threads and bulk fetching StackFrames

mcs/class/Mono.Debugger.Soft/Mono.Debugger.Soft/Connection.cs
mcs/class/Mono.Debugger.Soft/Mono.Debugger.Soft/ObjectMirror.cs
mcs/class/Mono.Debugger.Soft/Mono.Debugger.Soft/ThreadMirror.cs
mcs/class/Mono.Debugger.Soft/Mono.Debugger.Soft/VirtualMachine.cs

index 6d196aabbab374ff4ff9f70ce974cd3e7c9210eb..03d2d3af13a6ef5279ff0d0d3ea057c0105e6397 100644 (file)
@@ -1432,12 +1432,12 @@ namespace Mono.Debugger.Soft
                //
                public void StartBuffering () {
                        buffer_packets = true;
-                       if (Version.AtLeast (3, 34))
+                       if (Version.AtLeast (2, 34))
                                VM_StartBuffering ();
                }
 
                public void StopBuffering () {
-                       if (Version.AtLeast (3, 34))
+                       if (Version.AtLeast (2, 34))
                                VM_StopBuffering ();
                        buffer_packets = false;
 
@@ -1983,21 +1983,20 @@ namespace Mono.Debugger.Soft
                        return SendReceive (CommandSet.THREAD, (int)CmdThread.GET_NAME, new PacketWriter ().WriteId (id)).ReadString ();
                }
 
-               internal FrameInfo[] Thread_GetFrameInfo (long id, int start_frame, int length) {
-                       var res = SendReceive (CommandSet.THREAD, (int)CmdThread.GET_FRAME_INFO, new PacketWriter ().WriteId (id).WriteInt (start_frame).WriteInt (length));
-                       int count = res.ReadInt ();
-
-                       var frames = new FrameInfo [count];
-                       for (int i = 0; i < count; ++i) {
-                               var f = new FrameInfo ();
-                               f.id = res.ReadInt ();
-                               f.method = res.ReadId ();
-                               f.il_offset = res.ReadInt ();
-                               f.flags = (StackFrameFlags)res.ReadByte ();
-                               frames [i] = f;
-                       }
-
-                       return frames;
+               internal void Thread_GetFrameInfo (long id, int start_frame, int length, Action<FrameInfo[]> resultCallaback) {
+                       Send (CommandSet.THREAD, (int)CmdThread.GET_FRAME_INFO, new PacketWriter ().WriteId (id).WriteInt (start_frame).WriteInt (length), (res) => {
+                               int count = res.ReadInt ();
+                               var frames = new FrameInfo[count];
+                               for (int i = 0; i < count; ++i) {
+                                       var f = new FrameInfo ();
+                                       f.id = res.ReadInt ();
+                                       f.method = res.ReadId ();
+                                       f.il_offset = res.ReadInt ();
+                                       f.flags = (StackFrameFlags)res.ReadByte ();
+                                       frames [i] = f;
+                               }
+                               resultCallaback (frames);
+                       }, 1);
                }
 
                internal int Thread_GetState (long id) {
index f3eedccaf5b24592ce35e5b1cc1c0af05df033ac..b877e2402f529ba82ea3124bb3848aceb4bf9864 100644 (file)
@@ -262,7 +262,7 @@ namespace Mono.Debugger.Soft
                                f |= InvokeFlags.SINGLE_THREADED;
 
                        InvokeAsyncResult r = new InvokeAsyncResult { AsyncState = state, AsyncWaitHandle = new ManualResetEvent (false), VM = vm, Thread = thread, Callback = callback };
-
+                       thread.InvalidateFrames ();
                        r.ID = vm.conn.VM_BeginInvokeMethod (thread.Id, method.Id, this_obj != null ? vm.EncodeValue (this_obj) : vm.EncodeValue (vm.CreateValue (null)), vm.EncodeValues (arguments), f, InvokeCB, r);
 
                        return r;
@@ -370,6 +370,7 @@ namespace Mono.Debugger.Soft
                        var args = new List<ValueImpl[]> ();
                        for (int i = 0; i < methods.Length; ++i)
                                args.Add (vm.EncodeValues (arguments [i]));
+                       thread.InvalidateFrames ();
                        r.ID = vm.conn.VM_BeginInvokeMethods (thread.Id, mids, this_obj != null ? vm.EncodeValue (this_obj) : vm.EncodeValue (vm.CreateValue (null)), args, f, InvokeMultipleCB, r);
 
                        return r;
index 38c733ccaf5e07e60681abcd77cf7c58be440608..bf2482786901af7a3330b60837d753d76c11f809 100644 (file)
@@ -7,7 +7,12 @@ namespace Mono.Debugger.Soft
        public class ThreadMirror : ObjectMirror
        {
                string name;
+               bool framesCacheIsInvalid = true;
+               bool fetchingInProgress;
+               object fetchingLocker = new object ();
+               ManualResetEvent fetchingEvent = new ManualResetEvent (false);
                ThreadInfo info;
+               StackFrame[] frames;
 
                internal ThreadMirror (VirtualMachine vm, long id) : base (vm, id) {
                }
@@ -15,22 +20,58 @@ namespace Mono.Debugger.Soft
                internal ThreadMirror (VirtualMachine vm, long id, TypeMirror type, AppDomainMirror domain) : base (vm, id, type, domain) {
                }
 
-               // FIXME: Cache, invalidate when the thread/runtime is resumed
                public StackFrame[] GetFrames () {
-                       FrameInfo[] frame_info = vm.conn.Thread_GetFrameInfo (id, 0, -1);
+                       FetchFrames (true);
+                       fetchingEvent.WaitOne ();
+                       return frames;
+               }
 
-                       var frames = new List<StackFrame> ();
+               internal void InvalidateFrames () {
+                       framesCacheIsInvalid = true;
+               }
 
-                       for (int i = 0; i < frame_info.Length; ++i) {
-                               FrameInfo info = (FrameInfo)frame_info [i];
-                               MethodMirror method = vm.GetMethod (info.method);
-                               var f = new StackFrame (vm, info.id, this, method, info.il_offset, info.flags);
-                               if (!(f.IsNativeTransition && !NativeTransitions))
-                                       frames.Add (f);
+               internal void FetchFrames (bool mustFetch = false) {
+                       lock (fetchingLocker) {
+                               if (fetchingInProgress || !framesCacheIsInvalid)
+                                       return;
+                               framesCacheIsInvalid = false;
+                               fetchingInProgress = true;
+                               fetchingEvent.Reset ();
                        }
+                       vm.conn.Thread_GetFrameInfo (id, 0, -1, (frame_info) => {
+                               var framesList = new List<StackFrame> ();
+                               for (int i = 0; i < frame_info.Length; ++i) {
+                                       var frameInfo = (FrameInfo)frame_info [i];
+                                       var method = vm.GetMethod (frameInfo.method);
+                                       var f = new StackFrame (vm, frameInfo.id, this, method, frameInfo.il_offset, frameInfo.flags);
+                                       if (!(f.IsNativeTransition && !NativeTransitions))
+                                               framesList.Add (f);
+                               }
+                               lock (fetchingLocker) {
+                                       vm.AddThreadToInvalidateList (this);
+                                       fetchingInProgress = false;
+                                       //In case it was invalidated during waiting for response from
+                                       //runtime and mustFetch was set refetch
+                                       if (framesCacheIsInvalid && mustFetch) {
+                                               FetchFrames (mustFetch);
+                                               return;
+                                       }
+                                       frames = framesList.ToArray ();
+                                       fetchingEvent.Set ();
+                               }
+                       });
+               }
 
-                       return frames.ToArray ();
-           }
+               public static void FetchFrames(IList<ThreadMirror> threads)
+               {
+                       if (threads.Count == 0)
+                               return;
+                       threads [0].vm.conn.StartBuffering ();
+                       foreach (var thread in threads) {
+                               thread.FetchFrames ();
+                       }
+                       threads [0].vm.conn.StopBuffering ();
+               }
 
                public string Name {
                        get {
@@ -38,7 +79,7 @@ namespace Mono.Debugger.Soft
                                        name = vm.conn.Thread_GetName (id);
                                return name;
                        }
-           }
+               }
 
                public new long Id {
                        get {
index 61ee11c18314fb401424ade351529c4fa6e29d81..af027d808a367e63e594eace723027c60e8f05dd 100644 (file)
@@ -121,6 +121,7 @@ namespace Mono.Debugger.Soft
 
                public void Resume () {
                        try {
+                               InvalidateThreadsAndFramesCache ();
                                conn.VM_Resume ();
                        } catch (CommandException ex) {
                                if (ex.ErrorCode == ErrorCode.NOT_SUSPENDED)
@@ -151,12 +152,44 @@ namespace Mono.Debugger.Soft
                        conn.ForceDisconnect ();
                }
 
+               HashSet<ThreadMirror> threadsToInvalidate = new HashSet<ThreadMirror> ();
+               ThreadMirror[] threadsCache;
+               object threadCacheLocker=new object();
+
+               internal void InvalidateThreadsAndFramesCache () {
+                       lock (threadsToInvalidate) {
+                               foreach (var thread in threadsToInvalidate)
+                                       thread.InvalidateFrames ();
+                               threadsToInvalidate.Clear ();
+                       }
+                       lock (threadCacheLocker) {
+                               threadsCache = null;
+                       }
+               }
+
+               internal void InvalidateThreadsCache () {
+                       lock (threadCacheLocker) {
+                               threadsCache = null;
+                       }
+               }
+
+               internal void AddThreadToInvalidateList (ThreadMirror threadMirror)
+               {
+                       lock (threadsToInvalidate) {
+                               threadsToInvalidate.Add (threadMirror);
+                       }
+               }
+
                public IList<ThreadMirror> GetThreads () {
-                       long[] ids = vm.conn.VM_GetThreads ();
-                       ThreadMirror[] res = new ThreadMirror [ids.Length];
-                       for (int i = 0; i < ids.Length; ++i)
-                               res [i] = GetThread (ids [i]);
-                       return res;
+                       lock (threadCacheLocker) {
+                               if (threadsCache == null) {
+                                       long[] ids = vm.conn.VM_GetThreads ();
+                                       threadsCache = new ThreadMirror [ids.Length];
+                                       for (int i = 0; i < ids.Length; ++i)
+                                               threadsCache [i] = GetThread (ids [i]);
+                               }
+                               return threadsCache;
+                       }
                }
 
                // Same as the mirrorOf methods in JDI
@@ -675,9 +708,11 @@ namespace Mono.Debugger.Soft
                                        vm.notify_vm_event (EventType.VMDeath, suspend_policy, req_id, thread_id, null, ei.ExitCode);
                                        break;
                                case EventType.ThreadStart:
+                                       vm.InvalidateThreadsCache ();
                                        l.Add (new ThreadStartEvent (vm, req_id, id));
                                        break;
                                case EventType.ThreadDeath:
+                                       vm.InvalidateThreadsCache ();
                                        l.Add (new ThreadDeathEvent (vm, req_id, id));
                                        break;
                                case EventType.AssemblyLoad: