Example #1
0
        internal DebugFrame CreateFrameForGenerator(FunctionInfo func)
        {
            DebugThread thread = GetCurrentThread();
            DebugFrame  frame  = new DebugFrame(thread, func);

            return(frame);
        }
        public void TestDecode()
        {
            ShortHeaderPacket shp = new ShortHeaderPacket();

            shp.PacketNumber = 42;
            shp.DCID         = new byte[] { 0x00, 0x00, 0x00, 0x7b };
            shp.AddFrame(new DebugFrame {
                Message = "Message"
            });

            byte[] pack = shp.Encode();

            Packet p = Packet.Unpack(pack);

            Assert.AreEqual(p.GetType(), typeof(ShortHeaderPacket));
            ShortHeaderPacket sh = p as ShortHeaderPacket;

            CollectionAssert.AreEqual(sh.DCID, new byte[] { 0x00, 0x00, 0x00, 0x7b });
            Assert.AreEqual(sh.PacketNumber, (UInt64)42);
            Assert.AreEqual(sh.Spin, false);
            Assert.AreEqual(sh.KeyPhase, false);
            Assert.AreEqual(sh.PacketNumberLength, (UInt32)4);

            foreach (Frame f in p.Frames)
            {
                Assert.AreEqual(f.Type, 0x1e);
                DebugFrame fd = f as DebugFrame;
                Assert.AreEqual(fd.Message, "Message");
            }
        }
Example #3
0
 static public void Push(String strName)
 {
     lock ( _stackDebugFrames ) {
         Debug.WriteLine(strName + " {");
         Debug.Indent();
         //Debug.Flush();
         DebugFrame frame = new DebugFrame(strName);
         _stackDebugFrames.Push(frame);
     }
 }
        internal void DispatchDebugEvent(DebugThread thread, int debugMarker, TraceEventKind eventKind, object payload)
        {
            DebugFrame leafFrame      = null;
            bool       hasFrameObject = false;

            FunctionInfo functionInfo;
            int          stackDepth;

            if (eventKind != TraceEventKind.ThreadExit)
            {
                functionInfo = thread.GetLeafFrameFunctionInfo(out stackDepth);
            }
            else
            {
                stackDepth   = Int32.MaxValue;
                functionInfo = null;
            }

            if (eventKind == TraceEventKind.Exception || eventKind == TraceEventKind.ExceptionUnwind)
            {
                thread.ThrownException = (Exception)payload;
            }
            thread.IsInTraceback = true;

            try {
                // Fire the event
                IDebugCallback traceHook = _traceHook;
                if (traceHook != null)
                {
                    traceHook.OnDebugEvent(eventKind, thread, functionInfo, debugMarker, stackDepth, payload);
                }

                // Check if the frame object is created after the traceback.  If it's created - then we need
                // to check if we need to remap
                hasFrameObject = thread.TryGetLeafFrame(ref leafFrame);
                if (hasFrameObject)
                {
                    Debug.Assert(!leafFrame.InGeneratorLoop || (leafFrame.InGeneratorLoop && !leafFrame.ForceSwitchToGeneratorLoop));

                    if (leafFrame.ForceSwitchToGeneratorLoop && !leafFrame.InGeneratorLoop)
                    {
                        throw new ForceToGeneratorLoopException();
                    }
                }
            } finally {
                if (hasFrameObject)
                {
                    leafFrame.IsInTraceback = false;
                }

                thread.IsInTraceback   = false;
                thread.ThrownException = null;
            }
        }
Example #5
0
        private void SerialReplication(ProcedureNode procNode, ref int exeblock, int ci, int fi, DebugFrame debugFrame = null)
        {
            // TODO: Decide where to insert this common code block for Serial mode and Debugging - pratapa
            if (core.Options.ExecutionMode == ProtoCore.ExecutionMode.Serial || core.Options.IDEDebugMode)
            {
                RX = CallSite.PerformReturnTypeCoerce(procNode, core, RX);

                core.ContinuationStruct.RunningResult.Add(RX);
                core.ContinuationStruct.Result = RX;

                pc = core.ContinuationStruct.InitialPC;

                if (core.ContinuationStruct.Done)
                {
                    RX = HeapUtils.StoreArray(core.ContinuationStruct.RunningResult.ToArray(), null, core);
                    GCUtils.GCRetain(RX, core);

                    core.ContinuationStruct.RunningResult.Clear();
                    core.ContinuationStruct.IsFirstCall = true;

                    if (core.Options.IDEDebugMode)
                    {
                        // If stepping over function call in debug mode
                        if (core.DebugProps.RunMode == Runmode.StepNext)
                        {
                            // if stepping over outermost function call
                            if (!core.DebugProps.DebugStackFrameContains(DebugProperties.StackFrameFlagOptions.IsFunctionStepOver))
                            {
                                core.DebugProps.SetUpStepOverFunctionCalls(core, procNode, debugFrame.ExecutingGraphNode, debugFrame.HasDebugInfo);
                            }
                        }
                        // The DebugFrame passed here is the previous one that was popped off before this call
                        // In the case of Dot call the debugFrame obtained here is the one for the member function
                        // for both Break and non Break cases - pratapa
                        DebugPerformCoercionAndGC(debugFrame);

                        // If call returns to Dot Call, restore debug props for Dot call
                        debugFrame = core.DebugProps.DebugStackFrame.Peek();
                        if (debugFrame.IsDotCall)
                        {
                            List<Instruction> instructions = istream.instrList;
                            bool wasPopped = RestoreDebugPropsOnReturnFromBuiltIns(ref exeblock, ref instructions);
                            if (wasPopped)
                            {
                                executingBlock = exeblock;
                                core.DebugProps.CurrentBlockId = exeblock;
                            }
                            else
                            {
                                core.DebugProps.RestoreCallrForNoBreak(core, procNode, false);
                            }
                            DebugPerformCoercionAndGC(debugFrame);
                        }

                        //core.DebugProps.DebugEntryPC = currentPC;
                    }
                    // Perform return type coercion, GC and/or GC for Dot methods for Non-debug, Serial mode replication case
                    else
                    {
                        // If member function
                        // 1. Release array arguments to Member function
                        // 2. Release this pointer
                        bool isBaseCall = false;
                        StackValue? thisPtr = null;
                        if (thisPtr != null)
                        {
                            // Replicating member function
                            PerformCoercionAndGC(null, false, thisPtr, core.ContinuationStruct.InitialArguments, core.ContinuationStruct.InitialDotCallDimensions);

                            // Perform coercion and GC for Dot call
                            ProcedureNode dotCallprocNode = null;
                            List<StackValue> dotCallArgs = new List<StackValue>();
                            List<StackValue> dotCallDimensions = new List<StackValue>();
                            PerformCoercionAndGC(dotCallprocNode, false, null, dotCallArgs, dotCallDimensions);
                        }
                        else
                        {
                            PerformCoercionAndGC(procNode, isBaseCall, null, core.ContinuationStruct.InitialArguments, core.ContinuationStruct.InitialDotCallDimensions);
                        }
                    }

                    pc++;
                    return;

                }
                else
                {
                    // Jump back to Callr to call ResolveForReplication and recompute fep with next argument
                    core.ContinuationStruct.IsFirstCall = false;

                    ReturnToCallSiteForReplication(procNode, ci, fi);
                    return;
                }

            }
        }
Example #6
0
        /// <summary>
        /// Restores Debug properties from function call and/or from Dot call
        /// </summary>
        /// <param name="currentPC"></param>
        /// <param name="exeblock"></param>
        /// <param name="ci"></param>
        /// <param name="fi"></param>
        /// <param name="isReplicating"></param>
        /// <returns></returns>
        private bool DebugReturnFromFunctionCall(int currentPC, ref int exeblock, out int ci, out int fi, out bool isReplicating, out DebugFrame debugFrame)
        {
            DebugFrame tempFrame = null;

            tempFrame = core.DebugProps.DebugStackFrame.Peek();

            List<Instruction> instructions = istream.instrList;

            bool waspopped = RestoreDebugPropsOnReturnFromFunctionCall(ref exeblock, ref instructions, out ci, out fi, out isReplicating, out debugFrame);

            // TODO: If return from previous function calls "_Dispose", and we have stepped into it, 
            // we need to restore the caller stackframe - pratapa
            if (tempFrame.IsDisposeCall)
            {
                // TODO: If we have stepped inside _Dispose and are resuming from it - pratapa
                if (!terminate)
                {
                    // 1. Call everything after RETURNSITEGC in OpCode.RETURN/ OpCode.RETC
                    // 2. Call RestoreDebugPropsOnReturnFromFunctionCall() for caller function
                    // 3. Return address from _Dispose is one more than the correct value and therefore needs to be fixed
                }
                // TODO: This works assuming debugging inside _Dispose functions is disabled
                // ie stepping over _Dispose - pratapa
                core.DebugProps.DebugEntryPC = core.DebugProps.ReturnPCFromDispose;
                //break;
            }
            else
            {
#if __DEBUG_REPLICATE
                // When debugging replication, we must pop off the DebugFrame for the Dot call only after replication is complete
                // (after ContinuationStruct.Done == true) - pratapa
                if (!isReplicating)
#endif
                {
                    debugFrame = core.DebugProps.DebugStackFrame.Peek();
                    // If call returns to Dot Call, restore debug props for Dot call
                    if (debugFrame.IsDotCall)
                    {
                        waspopped = RestoreDebugPropsOnReturnFromBuiltIns(ref exeblock, ref instructions);
                    }
                }
                core.DebugProps.DebugEntryPC = currentPC;
            }

            return waspopped;
        }
Example #7
0
        bool RestoreDebugPropsOnReturnFromFunctionCall(ref int exeblock, ref List<Instruction> instructions, out int ci, out int fi, out bool isReplicating,
            out DebugFrame debugFrame)
        {
            //
            // TODO: Aparajit, Jun - Determine an alternative to the waspopped flag
            //
            bool waspopped = false;
            Validity.Assert(core.DebugProps.DebugStackFrame.Count > 0);

            debugFrame = core.DebugProps.DebugStackFrame.Peek();

            isReplicating = debugFrame.IsReplicating;

#if !__DEBUG_REPLICATE
            if (!isReplicating)
#endif
            {
                bool isResume = debugFrame.IsResume;

                // Comment Jun: Since we dont step into _Dispose() calls, then its debugframe should not be popped off here.
                bool isDispose = debugFrame.IsDisposeCall;

                // RestoreCallrForNoBreak and PerformReturnTypeCoerce are NOT called if this is true
                // or for base class ctor calls and therefore need to be taken care of here
                if ((isResume || debugFrame.IsBaseCall) && !isDispose)
                {
                    debugFrame = core.DebugProps.DebugStackFrame.Pop();
                    waspopped = true;

                    if (isResume)
                    {
                        if (core.DebugProps.DebugStackFrame.Count > 1)
                        {
                            DebugFrame frame = core.DebugProps.DebugStackFrame.Peek();
                            frame.IsResume = true;
                        }
                    }

#if __DEBUG_REPLICATE
                    // Return type coercion and function call GC for replicating case takes place separately 
                    // in SerialReplication() when ContinuationStruct.Done == true - pratapa
                    if (!isReplicating)
#endif
                    {
                        DebugPerformCoercionAndGC(debugFrame);
                    }

                    // Restore registers except RX on popping of function stackframe
                    ResumeRegistersFromStackExceptRX();

                    terminate = false;
                }

                Properties.executingGraphNode = debugFrame.ExecutingGraphNode;

                if (core.DebugProps.RunMode.Equals(Runmode.StepOut) && pc == core.DebugProps.StepOutReturnPC)
                {
                    core.Breakpoints.Clear();
                    core.Breakpoints.AddRange(core.DebugProps.AllbreakPoints);
                }
            }

            // Restore return address and lang block
            pc = (int)rmem.GetAtRelative(StackFrame.kFrameIndexReturnAddress).opdata;
            exeblock = (int)rmem.GetAtRelative(StackFrame.kFrameIndexFunctionCallerBlock).opdata;

            istream = exe.instrStreamList[exeblock];
            instructions = istream.instrList;
            executingLanguage = istream.language;

            ci = (int)rmem.GetAtRelative(ProtoCore.DSASM.StackFrame.kFrameIndexClass).opdata;
            fi = (int)rmem.GetAtRelative(ProtoCore.DSASM.StackFrame.kFrameIndexFunction).opdata;

            int localCount = 0;
            int paramCount = 0;

            int blockId = (int)rmem.GetAtRelative(StackFrame.kFrameIndexFunctionBlock).opdata;

            GetLocalAndParamCount(blockId, ci, fi, out localCount, out paramCount);

            // Pop function stackframe as this is not allowed in Ret/Retc in debug mode
            rmem.FramePointer = (int)rmem.GetAtRelative(ProtoCore.DSASM.StackFrame.kFrameIndexFramePointer).opdata;
            rmem.PopFrame(ProtoCore.DSASM.StackFrame.kStackFrameSize + localCount + paramCount);


            ResumeRegistersFromStackExceptRX();

            //StackValue svFrameType = rmem.GetAtRelative(ProtoCore.DSASM.StackFrame.kFrameIndexCallerStackFrameType);
            StackValue svFrameType = rmem.GetAtRelative(ProtoCore.DSASM.StackFrame.kFrameIndexStackFrameType);
            StackFrameType frametype = (StackFrameType)svFrameType.opdata;
            if (frametype == StackFrameType.kTypeLanguage)
            {
                bounceType = (ProtoCore.DSASM.CallingConvention.BounceType)TX.opdata;
            }
            return waspopped;
        }
Example #8
0
        /// <summary>
        /// Performs type coercion of returned value and GC of arguments, this ptr and Dot methods
        /// </summary>
        /// <param name="finalFep"></param>
        /// <param name="debugFrame"></param>
        /// <param name="Arguments"></param>
        /// <param name="DotCallDimensions"></param>
        private void DebugPerformCoercionAndGC(DebugFrame debugFrame)
        {
            ProcedureNode procNode = debugFrame.FinalFepChosen != null ? debugFrame.FinalFepChosen.procedureNode : null;

            PerformCoercionAndGC(procNode, debugFrame.IsBaseCall, debugFrame.ThisPtr, debugFrame.Arguments, debugFrame.DotCallDimensions);
        }
Example #9
0
 internal DebugFrame CreateFrameForGenerator(FunctionInfo func) {
     DebugThread thread = GetCurrentThread();
     DebugFrame frame = new DebugFrame(thread, func);
     return frame;
 }
Example #10
0
        bool RestoreDebugPropsOnReturnFromFunctionCall(ref int exeblock, ref List<Instruction> instructions, out int ci, out int fi, out bool isReplicating,
            out DebugFrame debugFrame)
        {
            //
            // TODO: Aparajit, Jun - Determine an alternative to the waspopped flag
            //
            bool waspopped = false;
            Validity.Assert(runtimeCore.DebugProps.DebugStackFrame.Count > 0);

            debugFrame = runtimeCore.DebugProps.DebugStackFrame.Peek();

            isReplicating = debugFrame.IsReplicating;

            if (!isReplicating)
            {
                bool isResume = debugFrame.IsResume;

                // Comment Jun: Since we dont step into _Dispose() calls, then its debugframe should not be popped off here.
                bool isDispose = debugFrame.IsDisposeCall;

                // RestoreCallrForNoBreak and PerformReturnTypeCoerce are NOT called if this is true
                // or for base class ctor calls and therefore need to be taken care of here
                if ((isResume || debugFrame.IsBaseCall) && !isDispose)
                {
                    debugFrame = runtimeCore.DebugProps.DebugStackFrame.Pop();
                    waspopped = true;

                    if (isResume)
                    {
                        if (runtimeCore.DebugProps.DebugStackFrame.Count > 1)
                        {
                            DebugFrame frame = runtimeCore.DebugProps.DebugStackFrame.Peek();
                            frame.IsResume = true;
                        }
                    }

                    DebugPerformCoercionAndGC(debugFrame);

                    // Restore registers except RX on popping of function stackframe
                    ResumeRegistersFromStackExceptRX();

                    terminate = false;
                }

                Properties.executingGraphNode = debugFrame.ExecutingGraphNode;

                if (runtimeCore.DebugProps.RunMode.Equals(Runmode.StepOut) && pc == runtimeCore.DebugProps.StepOutReturnPC)
                {
                    runtimeCore.Breakpoints.Clear();
                    runtimeCore.Breakpoints.AddRange(runtimeCore.DebugProps.AllbreakPoints);
                }
            }

            // Restore return address and lang block
            pc = (int)rmem.GetAtRelative(StackFrame.FrameIndexReturnAddress).IntegerValue;
            exeblock = rmem.GetAtRelative(StackFrame.FrameIndexCallerBlockIndex).BlockIndex;

            istream = exe.instrStreamList[exeblock];
            instructions = istream.instrList;
            executingLanguage = istream.language;

            ci = rmem.GetAtRelative(StackFrame.FrameIndexClassIndex).ClassIndex;
            fi = rmem.GetAtRelative(StackFrame.FrameIndexFunctionIndex).FunctionIndex;

            int localCount;
            int paramCount;
            int blockId = rmem.GetAtRelative(StackFrame.FrameIndexFunctionBlockIndex).BlockIndex;
            GetLocalAndParamCount(blockId, ci, fi, out localCount, out paramCount);

            // Get execution states
            List<bool> execStateRestore = new List<bool>();
            execStateRestore = RetrieveExecutionStatesFromStack(localCount, paramCount);

            // Pop function stackframe as this is not allowed in Ret/Retc in debug mode
            rmem.FramePointer = (int)rmem.GetAtRelative(StackFrame.FrameIndexFramePointer).IntegerValue;

            rmem.PopFrame(StackFrame.StackFrameSize + localCount + paramCount + execStateRestore.Count); 

            ResumeRegistersFromStackExceptRX();

            //StackValue svFrameType = rmem.GetAtRelative(StackFrame.kFrameIndexCallerStackFrameType);
            StackValue svFrameType = rmem.GetAtRelative(StackFrame.FrameIndexStackFrameType);
            StackFrameType frametype = svFrameType.FrameType;
            if (frametype == StackFrameType.LanguageBlock)
            {
                bounceType = TX.BounceType;
            }
            return waspopped;
        }
Example #11
0
        /// <summary>
        /// Performs type coercion of returned value and GC of arguments, this ptr and Dot methods
        /// </summary>
        private void DebugPerformCoercionAndGC(DebugFrame debugFrame)
        {
            ProcedureNode procNode = debugFrame.FinalFepChosen != null ? debugFrame.FinalFepChosen.procedureNode : null;
            if (!debugFrame.IsBaseCall)
            {
                RX = CallSite.PerformReturnTypeCoerce(procNode, runtimeCore, RX);

                if (debugFrame.ThisPtr == null && CoreUtils.IsDotMethod(procNode.Name))
                {
                    RX = IndexIntoArray(RX, debugFrame.DotCallDimensions);
                    rmem.PopFrame(Constants.kDotCallArgCount);
                }
            }
        }
        internal object GeneratorLoopProc(DebugFrame frame, out bool moveNext)
        {
            Debug.Assert(frame.Generator != null);

            moveNext = true;
            bool   skipTraceEvent = true;
            object retVal;

            if (frame.ForceSwitchToGeneratorLoop)
            {
                // Reset ForceSwitchToGeneratorLoop flag
                frame.ForceSwitchToGeneratorLoop = false;
            }

            while (true)
            {
                if (!skipTraceEvent)
                {
                    if (frame.FunctionInfo.SequencePoints[frame.CurrentLocationCookie].SourceFile.DebugMode == DebugMode.FullyEnabled ||
                        frame.FunctionInfo.SequencePoints[frame.CurrentLocationCookie].SourceFile.DebugMode == DebugMode.TracePoints && frame.FunctionInfo.GetTraceLocations()[frame.CurrentLocationCookie])
                    {
                        Debug.Assert(((IEnumerator)frame.Generator).Current == DebugYieldValue);
                        frame.InGeneratorLoop = true;
                        try {
                            DispatchDebugEvent(frame.Thread, frame.CurrentLocationCookie, TraceEventKind.TracePoint, null);
                        }
#if DEBUG
                        catch (ForceToGeneratorLoopException) {
                            Debug.Assert(false, "ForceToGeneratorLoopException thrown in generator loop");
                            throw;
                        }
#endif
                        finally {
                            frame.InGeneratorLoop = false;
                        }
                    }
                }
                else
                {
                    skipTraceEvent = false;
                }

                // Advance to next yield
                try {
                    moveNext = ((IEnumerator)frame.Generator).MoveNext();
                    object current = ((IEnumerator)frame.Generator).Current;

                    // Update the last known marker
                    if (frame.Generator.YieldMarkerLocation != Int32.MaxValue)
                    {
                        frame.LastKnownGeneratorYieldMarker = frame.Generator.YieldMarkerLocation;
                    }

                    // Check if this was a user-code yield or a debug yield
                    if (current != DebugYieldValue || !moveNext)
                    {
                        if (moveNext)
                        {
                            retVal = current;
                        }
                        else
                        {
                            retVal = null;
                        }

                        break;
                    }
                } catch (ForceToGeneratorLoopException) {
                    // We land here when an exception is thrown from a nested catch block and if that exception is being cancelled.
                    skipTraceEvent = true;
                } catch (Exception ex) {
                    if (frame.DebugContext.DebugMode != DebugMode.Disabled)
                    {
                        try {
                            frame.InGeneratorLoop = true;
                            DispatchDebugEvent(frame.Thread, frame.CurrentLocationCookie, TraceEventKind.ExceptionUnwind, ex);
                        } finally {
                            frame.InGeneratorLoop = false;
                        }
                    }
                    else
                    {
                        throw;
                    }

                    // Rethrow if the exception is not cancelled
                    if (frame.ThrownException != null)
                    {
                        throw;
                    }

                    skipTraceEvent = true;
                }
            }

            Debug.Assert(retVal != DebugYieldValue);
            return(retVal);
        }
        internal object GeneratorLoopProc(DebugFrame frame, out bool moveNext) {
            Debug.Assert(frame.Generator != null);

            moveNext = true;
            bool skipTraceEvent = true;
            object retVal;

            if (frame.ForceSwitchToGeneratorLoop) {
                // Reset ForceSwitchToGeneratorLoop flag
                frame.ForceSwitchToGeneratorLoop = false;
            }

            while (true) {
                if (!skipTraceEvent) {
                    if (frame.FunctionInfo.SequencePoints[frame.CurrentLocationCookie].SourceFile.DebugMode == DebugMode.FullyEnabled ||
                        frame.FunctionInfo.SequencePoints[frame.CurrentLocationCookie].SourceFile.DebugMode == DebugMode.TracePoints && frame.FunctionInfo.GetTraceLocations()[frame.CurrentLocationCookie]) {
                            Debug.Assert(((IEnumerator)frame.Generator).Current == DebugYieldValue);
                            frame.InGeneratorLoop = true;
                            try {
                                DispatchDebugEvent(frame.Thread, frame.CurrentLocationCookie, TraceEventKind.TracePoint, null);
                            }
#if DEBUG
                            catch (ForceToGeneratorLoopException) {
                                Debug.Assert(false, "ForceToGeneratorLoopException thrown in generator loop");
                                throw;
                            }
#endif
                            finally {
                                frame.InGeneratorLoop = false;
                            }
                    }
                } else {
                    skipTraceEvent = false;
                }

                // Advance to next yield
                try {
                    moveNext = ((IEnumerator)frame.Generator).MoveNext();
                    object current = ((IEnumerator)frame.Generator).Current;

                    // Update the last known marker
                    if (frame.Generator.YieldMarkerLocation != Int32.MaxValue)
                        frame.LastKnownGeneratorYieldMarker = frame.Generator.YieldMarkerLocation;

                    // Check if this was a user-code yield or a debug yield
                    if (current != DebugYieldValue || !moveNext) {
                        if (moveNext) {
                            retVal = current;
                        } else {
                            retVal = null;
                        }

                        break;
                    }
                } catch (ForceToGeneratorLoopException) {
                    // We land here when an exception is thrown from a nested catch block and if that exception is being cancelled.
                    skipTraceEvent = true;
                } catch (Exception ex) {
                    if (frame.DebugContext.DebugMode != DebugMode.Disabled) {
                        try {
                            frame.InGeneratorLoop = true;
                            DispatchDebugEvent(frame.Thread, frame.CurrentLocationCookie, TraceEventKind.ExceptionUnwind, ex);
                        } finally {
                            frame.InGeneratorLoop = false;
                        }
                    } else {
                        throw;
                    }

                    // Rethrow if the exception is not cancelled
                    if (frame.ThrownException != null)
                        throw;

                    skipTraceEvent = true;
                }
            }

            Debug.Assert(retVal != DebugYieldValue);
            return retVal;
        }
Example #14
0
        static public void Pop()
        {
            lock ( _stackDebugFrames ) {
                Debug.Assert(_stackDebugFrames.Count > 0);
                DebugFrame frame = (DebugFrame)_stackDebugFrames.Pop();

                float fMilliseconds = 0;
                if (_showTime)
                {
                    fMilliseconds = frame.GetElapsedMillisecs();
                }
                long memoryDelta = 0;
                if (_showMemory)
                {
                    memoryDelta = frame.GetMemoryDelta();
                }

                Debug.Unindent();

                StringBuilder stringBuilder = new StringBuilder(50);
                stringBuilder.Append('}');

                if (_showTime || _showMemory)
                {
                    stringBuilder.Append("  [");
                }

                if (_showTime)
                {
                    stringBuilder.Append(" ");
                    if (fMilliseconds < 1000)
                    {
                        stringBuilder.Append(fMilliseconds);
                        stringBuilder.Append(" ms");
                    }
                    else if (fMilliseconds < 60000)
                    {
                        stringBuilder.Append(fMilliseconds / 1000);
                        stringBuilder.Append(" secs");
                    }
                    else
                    {
                        stringBuilder.Append(fMilliseconds / 60000);
                        stringBuilder.Append(" mins");
                    }
                    stringBuilder.Append(" ");
                }

                if (_showMemory)
                {
                    stringBuilder.Append(" ");
                    if (Math.Abs(memoryDelta) < 1000)
                    {
                        stringBuilder.Append(memoryDelta);
                        stringBuilder.Append(" Bytes");
                    }
                    else if (Math.Abs(memoryDelta) < 1000000)
                    {
                        stringBuilder.Append(((int)memoryDelta / 10) / 100f);
                        stringBuilder.Append(" KB");
                    }
                    else
                    {
                        stringBuilder.Append(((int)memoryDelta / 10000) / 100f);
                        stringBuilder.Append(" MB");
                    }
                    stringBuilder.Append(" ");
                }

                if (_showTime || _showMemory)
                {
                    stringBuilder.Append("]");
                }

                Debug.WriteLine(stringBuilder.ToString());
                //Debug.Flush();
            }
        }