Просмотр исходного кода

Revise code around record/replay of ScriptContext enter/exit.

Mark Marron 9 лет назад
Родитель
Сommit
73751c2461

Разница между файлами не показана из-за своего большого размера
+ 227 - 225
lib/Jsrt/Jsrt.cpp


+ 43 - 0
lib/Jsrt/JsrtInternal.h

@@ -303,3 +303,46 @@ void HandleScriptCompileError(Js::ScriptContext * scriptContext, CompileScriptEx
 #define BEGIN_JSRT_NO_EXCEPTION  BEGIN_NO_EXCEPTION
 #define END_JSRT_NO_EXCEPTION    END_NO_EXCEPTION return JsNoError;
 #define RETURN_NO_EXCEPTION(x)   _PREPARE_RETURN_NO_EXCEPTION return x
+
+////
+//Define compact TTD macros for use in the JSRT API's
+#if ENABLE_TTD
+#define PERFORM_JSRT_TTD_RECORD_ACTION_CHECK(CTX) (CTX)->ShouldPerformRecordAction()
+
+#define PERFORM_JSRT_TTD_RECORD_ACTION(WRAPPER_TAG, CTX, ACTION_CODE, ...) TTD::TTDJsRTActionResultAutoRecorder _actionEntryPopper(WRAPPER_TAG); \
+    if(PERFORM_JSRT_TTD_RECORD_ACTION_CHECK(CTX)) \
+    { \
+        (CTX)->GetThreadContext()->TTDLog->##ACTION_CODE##(_actionEntryPopper, ##__VA_ARGS__); \
+    }
+
+#define PERFORM_JSRT_TTD_RECORD_ACTION_STD_GLOBALWRAPPER(ACTION_CODE, ...) PERFORM_JSRT_TTD_RECORD_ACTION(TTD::ContextWrapperEnterExitStatus::EnterGlobalAPIWrapper, scriptContext, ACTION_CODE, ##__VA_ARGS__)
+#define PERFORM_JSRT_TTD_RECORD_ACTION_STD_CONTEXTWRAPPER(ACTION_CODE, ...) PERFORM_JSRT_TTD_RECORD_ACTION(TTD::ContextWrapperEnterExitStatus::EnterContextAPIWrapper, scriptContext, ACTION_CODE, ##__VA_ARGS__)
+#define PERFORM_JSRT_TTD_RECORD_ACTION_STD_NOSCRIPTWRAPPER(ACTION_CODE, ...) PERFORM_JSRT_TTD_RECORD_ACTION(TTD::ContextWrapperEnterExitStatus::EnterContextAPINoScriptWrapper, scriptContext, ACTION_CODE, ##__VA_ARGS__)
+
+#define PERFORM_JSRT_TTD_RECORD_ACTION_RESULT(RESULT) if(_actionEntryPopper.IsSetForRecord()) \
+        { \
+            _actionEntryPopper.NormalCompletionWResult(RESULT); \
+        }
+
+#define PERFORM_JSRT_TTD_RECORD_ACTION_COMPLETE_NO_RESULT() if(_actionEntryPopper.IsSetForRecord()) \
+        { \
+            _actionEntryPopper.NormalCompletion(); \
+        }
+
+//TODO: find and replace all of the occourences of this in jsrt.cpp
+#define PERFORM_JSRT_TTD_RECORD_ACTION_NOT_IMPLEMENTED(CTX) if(PERFORM_JSRT_TTD_RECORD_ACTION_CHECK(CTX)) { AssertMsg(false, "Need to implement support here!!!"); }
+#else
+#define PERFORM_JSRT_TTD_RECORD_ACTION_CHECK(CTX) false
+
+#define PERFORM_JSRT_TTD_RECORD_ACTION(WRAPPER_TAG, CTX, ACTION_CODE, ...) 
+
+#define PERFORM_JSRT_TTD_RECORD_ACTION_STD_GLOBALWRAPPER(ACTION_CODE, ...) 
+#define PERFORM_JSRT_TTD_RECORD_ACTION_STD_CONTEXTWRAPPER(ACTION_CODE, ...) 
+#define PERFORM_JSRT_TTD_RECORD_ACTION_STD_NOSCRIPTWRAPPER(ACTION_CODE, ...) 
+
+#define PERFORM_JSRT_TTD_RECORD_ACTION_RESULT(RESULT) 
+#define PERFORM_JSRT_TTD_RECORD_ACTION_COMPLETE_NO_RESULT() 
+
+//TODO: find and replace all of the occourences of this in jsrt.cpp
+#define PERFORM_JSRT_TTD_RECORD_ACTION_NOT_IMPLEMENTED(CTX) 
+#endif

+ 14 - 0
lib/Runtime/Base/ScriptContext.h

@@ -1117,6 +1117,20 @@ private:
             return (this->TTDMode & TTD::TTDMode::Detached) != TTD::TTDMode::Invalid;
         }
 
+        //
+        //TODO: there is a memory leak associated with this we need to relax later
+        //
+        //A special record check because we want to pin weak references even if we aren't actively logging (but are planning to do so in the future)
+        bool ShouldPerformWeakRefPinAction() const
+        {
+            bool modeIsPending = (this->TTDMode & TTD::TTDMode::Pending) == TTD::TTDMode::Pending;
+            bool modeIsRecord = (this->TTDMode & TTD::TTDMode::RecordEnabled) == TTD::TTDMode::RecordEnabled;
+            bool modeIsDebugging = (this->TTDMode & TTD::TTDMode::DebuggingEnabled) == TTD::TTDMode::DebuggingEnabled;
+            bool inDebugableCode = (this->TTDMode & TTD::TTDMode::ExcludedExecution) == TTD::TTDMode::Invalid;
+
+            return ((modeIsPending | modeIsRecord | modeIsDebugging) & inDebugableCode);
+        }
+
         //A special record check because we want to record code load even if we aren't actively logging (but are planning to do so in the future)
         bool ShouldPerformRecordTopLevelFunction() const
         {

+ 88 - 80
lib/Runtime/Debug/TTActionEvents.cpp

@@ -10,17 +10,6 @@ namespace TTD
 {
     namespace NSLogEvents
     {
-        bool IsJsRTActionExecutedInScriptWrapper(EventKind tag)
-        {
-            switch(tag)
-            {
-            case EventKind::GetAndClearExceptionActionTag:
-                return false;
-            default:
-                return true;
-            }
-        }
-
         bool IsJsRTActionRootCall(const EventLogEntry* evt)
         {
             if(evt->EventKind != NSLogEvents::EventKind::CallExistingFunctionActionTag)
@@ -339,6 +328,18 @@ namespace TTD
             JsRTActionHandleResultForReplay<JsRTVarsArgumentAction, EventKind::GetAndClearExceptionActionTag>(ctx, evt, exception);
         }
 
+        void SetExceptionAction_Execute(const EventLogEntry* evt, Js::ScriptContext* ctx)
+        {
+            const JsRTVarsWithIntegralUnionArgumentAction* action = GetInlineEventDataAs<JsRTVarsWithIntegralUnionArgumentAction, EventKind::SetExceptionActionTag>(evt);
+            Js::Var exception = InflateVarInReplay(ctx, action->Var1);
+            bool propagateToDebugger = action->u_bVal ? true : false;
+
+            Js::JavascriptExceptionObject *exceptionObject;
+            exceptionObject = RecyclerNew(ctx->GetRecycler(), Js::JavascriptExceptionObject, exception, ctx, nullptr);
+
+            ctx->RecordException(exceptionObject, propagateToDebugger);
+        }
+
         void GetPropertyAction_Execute(const EventLogEntry* evt, Js::ScriptContext* ctx)
         {
             const JsRTVarsWithIntegralUnionArgumentAction* action = GetInlineEventDataAs<JsRTVarsWithIntegralUnionArgumentAction, EventKind::GetPropertyActionTag>(evt);
@@ -406,7 +407,10 @@ namespace TTD
             Js::Var propertyDescriptor = InflateVarInReplay(ctx, action->Var2);
 
             Js::PropertyDescriptor propertyDescriptorValue;
-            Js::JavascriptOperators::ToPropertyDescriptor(propertyDescriptor, &propertyDescriptorValue, ctx);
+            if(!Js::JavascriptOperators::ToPropertyDescriptor(propertyDescriptor, &propertyDescriptorValue, ctx))
+            {
+                return;
+            }
 
             Js::JavascriptOperators::DefineOwnPropertyDescriptor(Js::RecyclableObject::FromVar(object), action->u_pid, propertyDescriptorValue, true, ctx);
         }
@@ -457,7 +461,12 @@ namespace TTD
             Js::TypedArrayBase* typedArrayBase = Js::TypedArrayBase::FromVar(var);
             Js::Var res = typedArrayBase->GetArrayBuffer();
 
-            JsRTActionHandleResultForReplay<JsRTVarsArgumentAction, EventKind::GetTypedArrayInfoActionTag>(ctx, evt, res);
+            //Need to enter since JsRTActionHandleResultForReplay may allocate but GetTypedArrayInfo does not enter runtime
+            BEGIN_JS_RUNTIME_CALL(ctx);
+            {
+                JsRTActionHandleResultForReplay<JsRTVarsArgumentAction, EventKind::GetTypedArrayInfoActionTag>(ctx, evt, res);
+            }
+            END_JS_RUNTIME_CALL(ctx);
         }
 
         //////////////////
@@ -702,6 +711,12 @@ namespace TTD
 #endif
         }
 
+        void JsRTCodeParseAction_SetBodyCtrId(EventLogEntry* parseEvent, uint64 bodyCtrId)
+        {
+            JsRTCodeParseAction* cpAction = GetInlineEventDataAs<JsRTCodeParseAction, EventKind::CodeParseActionTag>(parseEvent);
+            cpAction->BodyCtrId = bodyCtrId;
+        }
+
         void JsRTCodeParseAction_Execute(const EventLogEntry* evt, Js::ScriptContext* ctx)
         {
             const JsRTCodeParseAction* cpAction = GetInlineEventDataAs<JsRTCodeParseAction, EventKind::CodeParseActionTag>(evt);
@@ -738,19 +753,19 @@ namespace TTD
 
             Js::Utf8SourceInfo* utf8SourceInfo = nullptr;
             CompileScriptException se;
-            BEGIN_LEAVE_SCRIPT_WITH_EXCEPTION(ctx)
-            {
-                function = ctx->LoadScript(script, scriptByteLength, &si, &se, &utf8SourceInfo, Js::Constants::GlobalCode, cpInfo->LoadFlag);
-            }
-            END_LEAVE_SCRIPT_WITH_EXCEPTION(ctx);
+            function = ctx->LoadScript(script, scriptByteLength, &si, &se, &utf8SourceInfo, Js::Constants::GlobalCode, cpInfo->LoadFlag);
             AssertMsg(function != nullptr, "Something went wrong");
 
             Js::FunctionBody* fb = TTD::JsSupport::ForceAndGetFunctionBody(function->GetParseableFunctionInfo());
 
             ////
             //We don't do this automatically in the eval helper so do it here
-            ctx->TTDContextInfo->ProcessFunctionBodyOnLoad(fb, nullptr);
-            ctx->TTDContextInfo->RegisterLoadedScript(fb, cpAction->BodyCtrId);
+            BEGIN_JS_RUNTIME_CALL(ctx);
+            {
+                ctx->TTDContextInfo->ProcessFunctionBodyOnLoad(fb, nullptr);
+                ctx->TTDContextInfo->RegisterLoadedScript(fb, cpAction->BodyCtrId);
+            }
+            END_JS_RUNTIME_CALL(ctx);
 
             const HostScriptContextCallbackFunctor& hostFunctor = ctx->TTDHostCallbackFunctor;
             if(hostFunctor.pfOnScriptLoadCallback != nullptr)
@@ -843,11 +858,10 @@ namespace TTD
             cfAction->AdditionalInfo->LastNestedEvent = TTD_EVENT_MAXTIME;
         }
 
-        void JsRTCallFunctionAction_ProcessDiagInfoPost(EventLogEntry* evt, double wallTime, int64 lastNestedEvent)
+        void JsRTCallFunctionAction_ProcessDiagInfoPost(EventLogEntry* evt, int64 lastNestedEvent)
         {
             JsRTCallFunctionAction* cfAction = GetInlineEventDataAs<JsRTCallFunctionAction, EventKind::CallExistingFunctionActionTag>(evt);
 
-            cfAction->AdditionalInfo->EndTime = wallTime;
             cfAction->AdditionalInfo->LastNestedEvent = lastNestedEvent;
         }
 #endif
@@ -879,22 +893,7 @@ namespace TTD
             cfAction->AdditionalInfo->MarkedAsJustMyCode = false;
             cfAction->AdditionalInfo->LastExecutedLocation.Initialize();
 
-            //Set values in case we terminate in this handler without completing (e.g. exit(1))
-            cfAction->Result = nullptr;
-
-            cfAction->AdditionalInfo->HasScriptException = false;
-            cfAction->AdditionalInfo->HasTerminiatingException = false;
-        }
-
-        void JsRTCallFunctionAction_ProcessReturn(EventLogEntry* evt, Js::Var res, bool hasScriptException, bool hasTerminiatingException)
-        {
-            JsRTCallFunctionAction* cfAction = GetInlineEventDataAs<JsRTCallFunctionAction, EventKind::CallExistingFunctionActionTag>(evt);
-            JsRTCallFunctionAction_AdditionalInfo* cfInfo = cfAction->AdditionalInfo;
-
-            cfAction->Result = TTD_CONVERT_JSVAR_TO_TTDVAR(res);
-
-            cfInfo->HasScriptException = hasScriptException;
-            cfInfo->HasTerminiatingException = hasTerminiatingException;
+            //Result is initialized when we register this with the popper
         }
 
         void JsRTCallFunctionAction_Execute(const EventLogEntry* evt, Js::ScriptContext* ctx)
@@ -915,57 +914,72 @@ namespace TTD
             }
             Js::Arguments jsArgs(callInfo, cfAction->AdditionalInfo->ExecArgs);
 
-            if(cfAction->CallbackDepth == 0)
+            //If this isn't a root function then just call it -- don't need to reset anything and exceptions can just continue
+            if(cfAction->CallbackDepth != 0)
             {
-                threadContext->TTDLog->ResetCallStackForTopLevelCall(cfInfo->TopLevelCallbackEventTime);
-            }
+                Js::Var result = jsFunction->CallRootFunction(jsArgs, ctx, true);
 
-            Js::Var result = nullptr;
-            try
-            {
-                result = jsFunction->CallRootFunction(jsArgs, ctx, true);
-            }
-            catch(Js::JavascriptExceptionObject*  exceptionObject)
-            {
-                AssertMsg(threadContext->GetRecordedException() == nullptr, "Not sure if this is true or not but seems like a reasonable requirement.");
+                //since we tag in JsRT we need to tag here too
+                JsRTActionHandleResultForReplay<JsRTCallFunctionAction, EventKind::CallExistingFunctionActionTag>(ctx, evt, result);
 
-                threadContext->SetRecordedException(exceptionObject);
+                AssertMsg(EventCompletesScriptContextNormally(evt), "Why did we get a different completion");
             }
-            catch(Js::ScriptAbortException)
+            else
             {
-                AssertMsg(threadContext->GetRecordedException() == nullptr, "Not sure if this is true or not but seems like a reasonable requirement.");
+                threadContext->TTDLog->ResetCallStackForTopLevelCall(cfInfo->TopLevelCallbackEventTime);
 
-                threadContext->SetRecordedException(threadContext->GetPendingTerminatedErrorObject());
-            }
-            catch(TTDebuggerAbortException)
-            {
-                throw; //re-throw my abort exception up to the top-level.
-            }
-            catch(...)
-            {
-                AssertMsg(false, "What else if dying here?");
+                try
+                {
+                    Js::Var result = jsFunction->CallRootFunction(jsArgs, ctx, true);
 
-                //not sure of our best strategy so just run for now
-            }
+                    //since we tag in JsRT we need to tag here too
+                    JsRTActionHandleResultForReplay<JsRTCallFunctionAction, EventKind::CallExistingFunctionActionTag>(ctx, evt, result);
 
-            //since we tag in JsRT we need to tag here too
-            JsRTActionHandleResultForReplay<JsRTCallFunctionAction, EventKind::CallExistingFunctionActionTag>(ctx, evt, result);
+                    AssertMsg(EventCompletesScriptContextNormally(evt), "Why did we get a different completion");
+                }
+                catch(Js::JavascriptExceptionObject*)
+                {
+#if ENABLE_TTD_DEBUGGING
+                    AssertMsg(EventCompletesScriptContextWithException(evt), "Why did we get a different exception");
+
+                    //convert to uncaught debugger exception for host
+                    bool markedAsJustMyCode = false;
+                    TTDebuggerSourceLocation lastLocation;
+                    threadContext->TTDLog->GetLastExecutedTimeAndPositionForDebugger(&markedAsJustMyCode, lastLocation);
+                    JsRTCallFunctionAction_SetLastExecutedStatementAndFrameInfo(const_cast<EventLogEntry*>(evt), markedAsJustMyCode, lastLocation);
 
+                    throw TTDebuggerAbortException::CreateUncaughtExceptionAbortRequest(lastLocation.GetRootEventTime(), _u("Uncaught JavaScript exception -- Propagate to top-level."));
+#else
+                    throw;
+#endif
+                }
+                catch(Js::ScriptAbortException)
+                {
 #if ENABLE_TTD_DEBUGGING
-            if(cfAction->CallbackDepth == 0)
-            {
-                bool markedAsJustMyCode = false;
-                TTDebuggerSourceLocation lastLocation;
-                threadContext->TTDLog->GetLastExecutedTimeAndPositionForDebugger(&markedAsJustMyCode, lastLocation);
+                    AssertMsg(EventCompletesScriptContextWithException(evt), "Why did we get a different exception");
 
-                JsRTCallFunctionAction_SetLastExecutedStatementAndFrameInfo(const_cast<EventLogEntry*>(evt), markedAsJustMyCode, lastLocation);
+                    //convert to uncaught debugger exception for host
+                    bool markedAsJustMyCode = false;
+                    TTDebuggerSourceLocation lastLocation;
+                    threadContext->TTDLog->GetLastExecutedTimeAndPositionForDebugger(&markedAsJustMyCode, lastLocation);
+                    JsRTCallFunctionAction_SetLastExecutedStatementAndFrameInfo(const_cast<EventLogEntry*>(evt), markedAsJustMyCode, lastLocation);
 
-                if(cfInfo->HasScriptException || cfInfo->HasTerminiatingException)
+                    throw TTDebuggerAbortException::CreateUncaughtExceptionAbortRequest(lastLocation.GetRootEventTime(), _u("Uncaught Script exception -- Propagate to top-level."));
+#else
+                    throw;
+#endif
+                }
+                catch(...)
                 {
-                    throw TTDebuggerAbortException::CreateUncaughtExceptionAbortRequest(lastLocation.GetRootEventTime(), _u("Uncaught exception -- Propagate to top-level."));
+#if ENABLE_TTD_DEBUGGING
+                    bool markedAsJustMyCode = false;
+                    TTDebuggerSourceLocation lastLocation;
+                    threadContext->TTDLog->GetLastExecutedTimeAndPositionForDebugger(&markedAsJustMyCode, lastLocation);
+                    JsRTCallFunctionAction_SetLastExecutedStatementAndFrameInfo(const_cast<EventLogEntry*>(evt), markedAsJustMyCode, lastLocation);
+#endif
+                    throw;
                 }
             }
-#endif
         }
 
         void JsRTCallFunctionAction_UnloadEventMemory(EventLogEntry* evt, UnlinkableSlabAllocator& alloc)
@@ -1022,9 +1036,6 @@ namespace TTD
 
             writer->WriteInt64(NSTokens::Key::eventTime, cfInfo->TopLevelCallbackEventTime, NSTokens::Separator::CommaSeparator);
 
-            writer->WriteBool(NSTokens::Key::boolVal, cfInfo->HasScriptException, NSTokens::Separator::CommaSeparator);
-            writer->WriteBool(NSTokens::Key::boolVal, cfInfo->HasTerminiatingException, NSTokens::Separator::CommaSeparator);
-
 #if ENABLE_TTD_INTERNAL_DIAGNOSTICS
             writer->WriteInt64(NSTokens::Key::i64Val, cfInfo->LastNestedEvent, NSTokens::Separator::CommaSeparator);
             writer->WriteString(NSTokens::Key::name, cfInfo->FunctionName, NSTokens::Separator::CommaSeparator);
@@ -1059,9 +1070,6 @@ namespace TTD
 
             cfInfo->TopLevelCallbackEventTime = reader->ReadInt64(NSTokens::Key::eventTime, true);
 
-            cfInfo->HasScriptException = reader->ReadBool(NSTokens::Key::boolVal, true);
-            cfInfo->HasTerminiatingException = reader->ReadBool(NSTokens::Key::boolVal, true);
-
             cfInfo->RtRSnap = nullptr;
             cfInfo->ExecArgs = (cfAction->ArgCount > 1) ? alloc.SlabAllocateArray<Js::Var>(cfAction->ArgCount - 1) : nullptr; //ArgCount includes slot for function which we don't use in exec
 

+ 4 - 15
lib/Runtime/Debug/TTActionEvents.h

@@ -10,9 +10,6 @@ namespace TTD
 {
     namespace NSLogEvents
     {
-        //true if this is exectued in the script context JsRT wrapper
-        bool IsJsRTActionExecutedInScriptWrapper(EventKind tag);
-
         //true if this is a root call function
         bool IsJsRTActionRootCall(const EventLogEntry* evt);
 
@@ -313,6 +310,7 @@ namespace TTD
 
         void HostProcessExitAction_Execute(const EventLogEntry* evt, Js::ScriptContext* ctx);
         void GetAndClearExceptionAction_Execute(const EventLogEntry* evt, Js::ScriptContext* ctx);
+        void SetExceptionAction_Execute(const EventLogEntry* evt, Js::ScriptContext* ctx);
 
         void GetPropertyAction_Execute(const EventLogEntry* evt, Js::ScriptContext* ctx);
         void GetIndexAction_Execute(const EventLogEntry* evt, Js::ScriptContext* ctx);
@@ -456,10 +454,6 @@ namespace TTD
         //A struct for additional info associated with calls to script parse
         struct JsRTCodeParseAction_AdditionalInfo
         {
-            //
-            //TODO: it kinda sucks to copy all the source here when we have it in the Log as well maybe we can just record the bodyCtrId and look up the other info during replay?
-            //
-
             //Is the code utf8
             bool IsUtf8;
 
@@ -487,6 +481,8 @@ namespace TTD
             JsRTCodeParseAction_AdditionalInfo* AdditionalInfo;
         };
 
+        void JsRTCodeParseAction_SetBodyCtrId(EventLogEntry* parseEvent, uint64 bodyCtrId);
+
         void JsRTCodeParseAction_Execute(const EventLogEntry* evt, Js::ScriptContext* ctx);
         void JsRTCodeParseAction_UnloadEventMemory(EventLogEntry* evt, UnlinkableSlabAllocator& alloc);
         void JsRTCodeParseAction_Emit(const EventLogEntry* evt, FileWriter* writer, ThreadContext* threadContext);
@@ -504,12 +500,6 @@ namespace TTD
             //The event time that corresponds to the top-level event time around this call
             int64 TopLevelCallbackEventTime;
 
-            //
-            //TODO: later we should record more detail on the script exception for inflation if needed
-            //
-            bool HasScriptException;
-            bool HasTerminiatingException;
-
             //ready-to-run snapshot information -- null if not set and if we want to unload it we just throw it away
             SnapShot* RtRSnap;
 
@@ -549,11 +539,10 @@ namespace TTD
         int64 JsRTCallFunctionAction_GetLastNestedEventTime(const EventLogEntry* evt);
 
         void JsRTCallFunctionAction_ProcessDiagInfoPre(EventLogEntry* evt, Js::JavascriptFunction* function, UnlinkableSlabAllocator& alloc);
-        void JsRTCallFunctionAction_ProcessDiagInfoPost(EventLogEntry* evt, double wallTime, int64 lastNestedEvent);
+        void JsRTCallFunctionAction_ProcessDiagInfoPost(EventLogEntry* evt, int64 lastNestedEvent);
 #endif
 
         void JsRTCallFunctionAction_ProcessArgs(EventLogEntry* evt, int32 rootDepth, int64 callEventTime, Js::JavascriptFunction* function, uint32 argc, Js::Var* argv, double wallTime, int64 topLevelCallbackEventTime, UnlinkableSlabAllocator& alloc);
-        void JsRTCallFunctionAction_ProcessReturn(EventLogEntry* evt, Js::Var res, bool hasScriptException, bool hasTerminiatingException);
 
         void JsRTCallFunctionAction_Execute(const EventLogEntry* evt, Js::ScriptContext* ctx);
         void JsRTCallFunctionAction_UnloadEventMemory(EventLogEntry* evt, UnlinkableSlabAllocator& alloc);

Разница между файлами не показана из-за своего большого размера
+ 477 - 256
lib/Runtime/Debug/TTEventLog.cpp


+ 88 - 119
lib/Runtime/Debug/TTEventLog.h

@@ -4,31 +4,6 @@
 //-------------------------------------------------------------------------------------------------------
 #pragma once
 
-////
-//Define compact macros for use in the JSRT API's
-#if ENABLE_TTD
-#define PERFORM_JSRT_TTD_RECORD_ACTION_CHECK(CTX) (CTX)->ShouldPerformRecordAction()
-
-#define PERFORM_JSRT_TTD_RECORD_ACTION_NORESULT(CTX, ACTION_CODE) if(PERFORM_JSRT_TTD_RECORD_ACTION_CHECK(CTX)) { (ACTION_CODE); }
-#define PERFORM_JSRT_TTD_RECORD_ACTION_WRESULT(CTX, ACTION_CODE) TTD::TTDVar* __ttd_resultPtr = nullptr; if(PERFORM_JSRT_TTD_RECORD_ACTION_CHECK(CTX)) { (ACTION_CODE); }
-#define PERFORM_JSRT_TTD_RECORD_ACTION_PROCESS_RESULT(RESULT) if((__ttd_resultPtr != nullptr) & ((RESULT) != nullptr)) { *__ttd_resultPtr = TTD_CONVERT_JSVAR_TO_TTDVAR(*(RESULT)); }
-
-//TODO: find and replace all of the occourences of this in jsrt.cpp
-#define PERFORM_JSRT_TTD_RECORD_ACTION_NOT_IMPLEMENTED(CTX) if(PERFORM_JSRT_TTD_RECORD_ACTION_CHECK(CTX)) { AssertMsg(false, "Need to implement support here!!!"); }
-#else
-#define PERFORM_JSRT_TTD_RECORD_ACTION_CHECK(CTX) false
-
-#define PERFORM_JSRT_TTD_RECORD_ACTION_NORESULT(CTX, ACTION_CODE) 
-#define PERFORM_JSRT_TTD_RECORD_ACTION_WRESULT(CTX, ACTION_CODE) 
-#define PERFORM_JSRT_TTD_RECORD_ACTION_PROCESS_RESULT(RESULT) 
-
-//TODO: find and replace all of the occourences of this in jsrt.cpp
-#define PERFORM_JSRT_TTD_RECORD_ACTION_NOT_IMPLEMENTED(CTX) 
-#endif
-
-////
-//Begin the regular TTD code
-
 #if ENABLE_TTD
 
 #define TTD_EVENTLOG_LIST_BLOCK_SIZE 4096
@@ -50,43 +25,48 @@ namespace TTD
         void PopInfo();
     };
 
-    //A class to ensure that even when exceptions are thrown we record the time difference info
-    class TTDRecordExternalFunctionCallActionPopper
+    //A class to ensure that even when exceptions are thrown we increment/decrement the root nesting depth
+    class TTDNestingDepthAutoAdjuster
     {
     private:
-        Js::JavascriptFunction* m_function;
-        NSLogEvents::EventLogEntry* m_callAction;
+        Js::ScriptContext* m_ctx;
 
     public:
-        TTDRecordExternalFunctionCallActionPopper(Js::JavascriptFunction* function, NSLogEvents::EventLogEntry* callAction);
-        ~TTDRecordExternalFunctionCallActionPopper();
-
-        void NormalReturn(bool checkException, Js::Var returnValue);
+        TTDNestingDepthAutoAdjuster(Js::ScriptContext* ctx);
+        ~TTDNestingDepthAutoAdjuster();
     };
 
-    //A class to ensure that even when exceptions are thrown we record the time difference info
-    class TTDReplayExternalFunctionCallActionPopper
+    //A class to ensure that even when exceptions are thrown we update any event recording info we were in the middle of
+    class TTDJsRTActionResultAutoRecorder
     {
     private:
-        Js::JavascriptFunction* m_function;
+        NSLogEvents::EventLogEntry* m_actionEvent;
+        TTDVar* m_resultPtr;
+        ContextWrapperEnterExitStatus m_contextEnterTag;
 
     public:
-        TTDReplayExternalFunctionCallActionPopper(Js::JavascriptFunction* function);
-        ~TTDReplayExternalFunctionCallActionPopper();
+        TTDJsRTActionResultAutoRecorder(ContextWrapperEnterExitStatus contextEnterTag);
+        ~TTDJsRTActionResultAutoRecorder();
+
+        bool IsSetForRecord() const { return this->m_actionEvent != nullptr; }
+
+        void InitializeWithEventAndEnter(NSLogEvents::EventLogEntry* actionEvent);
+        void NormalCompletion();
+
+        void InitializeWithEventAndEnterWResult(NSLogEvents::EventLogEntry* actionEvent, TTDVar* resultPtr);
+        void NormalCompletionWResult(Js::Var* result);
     };
 
     //A class to ensure that even when exceptions are thrown we record the time difference info
-    class TTDRecordJsRTFunctionCallActionPopper
+    class TTDJsRTFunctionCallActionPopperRecorder
     {
     private:
         Js::ScriptContext* m_ctx;
         NSLogEvents::EventLogEntry* m_callAction;
 
     public:
-        TTDRecordJsRTFunctionCallActionPopper(Js::ScriptContext* ctx, NSLogEvents::EventLogEntry* callAction);
-        ~TTDRecordJsRTFunctionCallActionPopper();
-
-        void NormalReturn(Js::Var returnValue);
+        TTDJsRTFunctionCallActionPopperRecorder(Js::ScriptContext* ctx, NSLogEvents::EventLogEntry* callAction);
+        ~TTDJsRTFunctionCallActionPopperRecorder();
     };
 
 #if ENABLE_TTD_DEBUGGING
@@ -223,7 +203,7 @@ namespace TTD
         JsUtil::List<SingleCallCounter, HeapAllocator> m_callStack;
 
         //The current mode the system is running in (and a stack of mode push/pops that we use to generate it)
-        JsUtil::List<TTDMode, HeapAllocator> m_modeStack;
+        TTModeStack m_modeStack;
         TTDMode m_currentMode;
 
         //A list of contexts that are being run in TTD mode (and the associated callback functors) -- We assume the host creates a single context for now 
@@ -320,35 +300,23 @@ namespace TTD
         //Replay an event loop yield point event
         void ReplayEventLoopYieldPointEvent();
 
-        //A helper for initializing and type casting EventLogEntry data for record
-        template <typename T, NSLogEvents::EventKind tag>
-        T* RecordGetInitializedEvent_Helper()
-        {
-            NSLogEvents::EventLogEntry* evt = this->m_eventList.GetNextAvailableEntry();
-            NSLogEvents::EventLogEntry_Initialize(evt, tag, this->GetCurrentEventTimeAndAdvance());
-
-            return NSLogEvents::GetInlineEventDataAs<T, tag>(evt);
-        }
-
         template <typename T, NSLogEvents::EventKind tag>
-        T* RecordGetInitializedEvent_HelperWithMainEvent(NSLogEvents::EventLogEntry** evt)
+        NSLogEvents::EventLogEntry* RecordGetInitializedEvent(T** extraData)
         {
-            *evt = this->m_eventList.GetNextAvailableEntry();
-            NSLogEvents::EventLogEntry_Initialize(*evt, tag, this->GetCurrentEventTimeAndAdvance());
+            NSLogEvents::EventLogEntry* res = this->m_eventList.GetNextAvailableEntry();
+            NSLogEvents::EventLogEntry_Initialize(res, tag, this->GetCurrentEventTimeAndAdvance());
 
-            return NSLogEvents::GetInlineEventDataAs<T, tag>(*evt);
+            *extraData = NSLogEvents::GetInlineEventDataAs<T, tag>(res);
+            return res;
         }
 
         template <typename T, NSLogEvents::EventKind tag>
-        T* RecordGetInitializedEvent_HelperWithResultPtr(TTDVar** resultPtr)
+        T* RecordGetInitializedEvent_DataOnly()
         {
-            NSLogEvents::EventLogEntry* evt = this->m_eventList.GetNextAvailableEntry();
-            NSLogEvents::EventLogEntry_Initialize(evt, tag, this->GetCurrentEventTimeAndAdvance());
+            NSLogEvents::EventLogEntry* res = this->m_eventList.GetNextAvailableEntry();
+            NSLogEvents::EventLogEntry_Initialize(res, tag, this->GetCurrentEventTimeAndAdvance());
 
-            T* eventData = NSLogEvents::GetInlineEventDataAs<T, tag>(evt);
-            *resultPtr = &(eventData->Result);
-
-            return eventData;
+            return NSLogEvents::GetInlineEventDataAs<T, tag>(res);
         }
 
         //Sometimes we need to abort replay and immediately return to the top-level host (debugger) so it can decide what to do next
@@ -491,7 +459,7 @@ namespace TTD
 
         //Log a value event for return from an external call
         NSLogEvents::EventLogEntry* RecordExternalCallEvent(Js::JavascriptFunction* func, int32 rootDepth, uint32 argc, Js::Var* argv);
-        void RecordExternalCallEvent_Complete(NSLogEvents::EventLogEntry* evt, Js::JavascriptFunction* func, bool normalReturn, bool checkException, Js::Var result);
+        void RecordExternalCallEvent_Complete(Js::JavascriptFunction* efunction, NSLogEvents::EventLogEntry* evt, Js::Var result);
 
         //replay an external return event (which should be the current event)
         void ReplayExternalCallEvent(Js::JavascriptFunction* function, uint32 argc, Js::Var* argv, Js::Var* result);
@@ -620,14 +588,17 @@ namespace TTD
         //Do the inflation of the snapshot that is at the given event time
         void DoSnapshotInflate(int64 etime);
 
-        //For replay the from the current event (should either be a top-level call/code-load action or a snapshot)
-        void ReplaySingleEntry();
+        //Run execute top level event calls until the given time is reached
+        void ReplayRootEventsToTime(int64 eventTime);
+
+        //For a single root level event -- snapshot, yield point, or ActionEvent
+        void ReplaySingleRootEntry();
 
-        //Run until the given top-level call event time
-        void ReplayToTime(int64 eventTime);
+        //When we have an externalFunction (or promise register) we exit script context and need to play until the event time counts up to (and including) the given eventTime
+        void ReplayActionEventSequenceThroughTime(int64 eventTime);
 
-        //For debugging replay the full trace from the current event
-        void ReplayFullTrace();
+        //Replay the enter/exit and any iteration need to discharge all the effects of a single ActionEvent
+        void ReplaySingleActionEventEntry();
 
         ////////////////////////////////
         //Host API record & replay support
@@ -642,83 +613,81 @@ namespace TTD
         int64 GetLastEventTime() const;
 
 #if !INT32VAR
-        void RecordJsRTCreateInteger(Js::ScriptContext* ctx, int value, TTDVar** resultVarPtr);
+        void RecordJsRTCreateInteger(TTDJsRTActionResultAutoRecorder& actionPopper, int value);
 #endif
 
         //Record creation operations
-        void RecordJsRTCreateNumber(Js::ScriptContext* ctx, double value, TTDVar** resultVarPtr);
-        void RecordJsRTCreateBoolean(Js::ScriptContext* ctx, bool value, TTDVar** resultVarPtr);
-        void RecordJsRTCreateString(Js::ScriptContext* ctx, const char16* stringValue, size_t stringLength, TTDVar** resultVarPtr);
-        void RecordJsRTCreateSymbol(Js::ScriptContext* ctx, Js::Var var, TTDVar** resultVarPtr);
+        void RecordJsRTCreateNumber(TTDJsRTActionResultAutoRecorder& actionPopper, double value);
+        void RecordJsRTCreateBoolean(TTDJsRTActionResultAutoRecorder& actionPopper, bool value);
+        void RecordJsRTCreateString(TTDJsRTActionResultAutoRecorder& actionPopper, const char16* stringValue, size_t stringLength);
+        void RecordJsRTCreateSymbol(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var var);
 
         //Record error creation
-        void RecordJsRTCreateError(Js::ScriptContext* ctx, Js::Var msg, TTDVar** resultVarPtr);
-        void RecordJsRTCreateRangeError(Js::ScriptContext* ctx, Js::Var vmsg, TTDVar** resultVarPtr);
-        void RecordJsRTCreateReferenceError(Js::ScriptContext* ctx, Js::Var msg, TTDVar** resultVarPtr);
-        void RecordJsRTCreateSyntaxError(Js::ScriptContext* ctx, Js::Var msg, TTDVar** resultVarPtr);
-        void RecordJsRTCreateTypeError(Js::ScriptContext* ctx, Js::Var msg, TTDVar** resultVarPtr);
-        void RecordJsRTCreateURIError(Js::ScriptContext* ctx, Js::Var msg, TTDVar** resultVarPtr);
+        void RecordJsRTCreateError(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var msg);
+        void RecordJsRTCreateRangeError(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var vmsg);
+        void RecordJsRTCreateReferenceError(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var msg);
+        void RecordJsRTCreateSyntaxError(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var msg);
+        void RecordJsRTCreateTypeError(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var msg);
+        void RecordJsRTCreateURIError(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var msg);
 
         //Record conversions
-        void RecordJsRTVarToNumberConversion(Js::ScriptContext* ctx, Js::Var var, TTDVar** resultVarPtr);
-        void RecordJsRTVarToBooleanConversion(Js::ScriptContext* ctx, Js::Var var, TTDVar** resultVarPtr);
-        void RecordJsRTVarToStringConversion(Js::ScriptContext* ctx, Js::Var var, TTDVar** resultVarPtr);
-        void RecordJsRTVarToObjectConversion(Js::ScriptContext* ctx, Js::Var var, TTDVar** resultVarPtr);
+        void RecordJsRTVarToNumberConversion(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var var);
+        void RecordJsRTVarToBooleanConversion(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var var);
+        void RecordJsRTVarToStringConversion(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var var);
+        void RecordJsRTVarToObjectConversion(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var var);
 
         //Record lifetime management events
-        void RecordJsRTAddRootRef(Js::ScriptContext* ctx, Js::Var var);
-        void RecordJsRTRemoveRootRef(Js::ScriptContext* ctx, Js::Var var);
-        void RecordJsRTEventLoopYieldPoint(Js::ScriptContext* ctx);
+        void RecordJsRTAddRootRef(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var var);
+        void RecordJsRTRemoveRootRef(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var var);
+        void RecordJsRTEventLoopYieldPoint();
 
         //Record object allocate operations
-        void RecordJsRTAllocateBasicObject(Js::ScriptContext* ctx, TTDVar** resultVarPtr);
-        void RecordJsRTAllocateExternalObject(Js::ScriptContext* ctx, TTDVar** resultVarPtr);
-        void RecordJsRTAllocateBasicArray(Js::ScriptContext* ctx, uint32 length, TTDVar** resultVarPtr);
-        void RecordJsRTAllocateArrayBuffer(Js::ScriptContext* ctx, uint32 size, TTDVar** resultVarPtr);
-        void RecordJsRTAllocateExternalArrayBuffer(Js::ScriptContext* ctx, byte* buff, uint32 size, TTDVar** resultVarPtr);
-        void RecordJsRTAllocateFunction(Js::ScriptContext* ctx, bool isNamed, Js::Var optName, TTDVar** resultVarPtr);
+        void RecordJsRTAllocateBasicObject(TTDJsRTActionResultAutoRecorder& actionPopper);
+        void RecordJsRTAllocateExternalObject(TTDJsRTActionResultAutoRecorder& actionPopper);
+        void RecordJsRTAllocateBasicArray(TTDJsRTActionResultAutoRecorder& actionPopper, uint32 length);
+        void RecordJsRTAllocateArrayBuffer(TTDJsRTActionResultAutoRecorder& actionPopper, uint32 size);
+        void RecordJsRTAllocateExternalArrayBuffer(TTDJsRTActionResultAutoRecorder& actionPopper, byte* buff, uint32 size);
+        void RecordJsRTAllocateFunction(TTDJsRTActionResultAutoRecorder& actionPopper, bool isNamed, Js::Var optName);
 
         //Record GetAndClearException
-        void RecordJsRTHostExitProcess(Js::ScriptContext* ctx, int32 exitCode);
-        void RecordJsRTGetAndClearException(Js::ScriptContext* ctx, TTDVar** resultVarPtr);
+        void RecordJsRTHostExitProcess(TTDJsRTActionResultAutoRecorder& actionPopper, int32 exitCode);
+        void RecordJsRTGetAndClearException();
+        void RecordJsRTSetException(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var var, bool propagateToDebugger);
 
         //Record Object Getters
-        void RecordJsRTGetProperty(Js::ScriptContext* ctx, Js::PropertyId pid, Js::Var var, TTDVar** resultVarPtr);
-        void RecordJsRTGetIndex(Js::ScriptContext* ctx, Js::Var index, Js::Var var, TTDVar** resultVarPtr);
-        void RecordJsRTGetOwnPropertyInfo(Js::ScriptContext* ctx, Js::PropertyId pid, Js::Var var, TTDVar** resultVarPtr);
-        void RecordJsRTGetOwnPropertyNamesInfo(Js::ScriptContext* ctx, Js::Var var, TTDVar** resultVarPtr);
-        void RecordJsRTGetOwnPropertySymbolsInfo(Js::ScriptContext* ctx, Js::Var var, TTDVar** resultVarPtr);
+        void RecordJsRTGetProperty(TTDJsRTActionResultAutoRecorder& actionPopper, Js::PropertyId pid, Js::Var var);
+        void RecordJsRTGetIndex(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var index, Js::Var var);
+        void RecordJsRTGetOwnPropertyInfo(TTDJsRTActionResultAutoRecorder& actionPopper, Js::PropertyId pid, Js::Var var);
+        void RecordJsRTGetOwnPropertyNamesInfo(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var var);
+        void RecordJsRTGetOwnPropertySymbolsInfo(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var var);
 
         //Record Object Setters
-        void RecordJsRTDefineProperty(Js::ScriptContext* ctx, Js::Var var, Js::PropertyId pid, Js::Var propertyDescriptor);
-        void RecordJsRTDeleteProperty(Js::ScriptContext* ctx, Js::Var var, Js::PropertyId pid, bool useStrictRules, TTDVar** resultVarPtr);
-        void RecordJsRTSetPrototype(Js::ScriptContext* ctx, Js::Var var, Js::Var proto);
-        void RecordJsRTSetProperty(Js::ScriptContext* ctx, Js::Var var, Js::PropertyId pid, Js::Var val, bool useStrictRules);
-        void RecordJsRTSetIndex(Js::ScriptContext* ctx, Js::Var var, Js::Var index, Js::Var val);
+        void RecordJsRTDefineProperty(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var var, Js::PropertyId pid, Js::Var propertyDescriptor);
+        void RecordJsRTDeleteProperty(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var var, Js::PropertyId pid, bool useStrictRules);
+        void RecordJsRTSetPrototype(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var var, Js::Var proto);
+        void RecordJsRTSetProperty(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var var, Js::PropertyId pid, Js::Var val, bool useStrictRules);
+        void RecordJsRTSetIndex(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var var, Js::Var index, Js::Var val);
 
         //Record a get info from a typed array
-        void RecordJsRTGetTypedArrayInfo(Js::ScriptContext* ctx, Js::Var var, TTDVar** resultVarPtr);
+        void RecordJsRTGetTypedArrayInfo(Js::Var var, Js::Var result);
 
         //Record various raw byte* from ArrayBuffer manipulations
-        void RecordJsRTRawBufferCopySync(Js::ScriptContext* ctx, Js::Var dst, uint32 dstIndex, Js::Var src, uint32 srcIndex, uint32 length);
-        void RecordJsRTRawBufferModifySync(Js::ScriptContext* ctx, Js::Var dst, uint32 index, uint32 count);
-        Js::Var RecordJsRTRawBufferAsyncModificationRegister(Js::ScriptContext* ctx, Js::Var dst, byte* initialModPos);
-        Js::Var RecordJsRTRawBufferAsyncModifyComplete(Js::ScriptContext* ctx, byte* finalModPos);
+        void RecordJsRTRawBufferCopySync(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var dst, uint32 dstIndex, Js::Var src, uint32 srcIndex, uint32 length);
+        void RecordJsRTRawBufferModifySync(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var dst, uint32 index, uint32 count);
+        Js::Var RecordJsRTRawBufferAsyncModificationRegister(TTDJsRTActionResultAutoRecorder& actionPopper, Js::ScriptContext* ctx, Js::Var dst, byte* initialModPos);
+        Js::Var RecordJsRTRawBufferAsyncModifyComplete(TTDJsRTActionResultAutoRecorder& actionPopper, Js::ScriptContext* ctx, byte* finalModPos);
 
         //Record a constructor call from JsRT
-        void RecordJsRTConstructCall(Js::ScriptContext* ctx, Js::JavascriptFunction* func, uint32 argCount, Js::Var* args, TTDVar** resultVarPtr);
+        void RecordJsRTConstructCall(TTDJsRTActionResultAutoRecorder& actionPopper, Js::JavascriptFunction* func, uint32 argCount, Js::Var* args);
 
         //Record callback registration/cancelation
         void RecordJsRTCallbackOperation(Js::ScriptContext* ctx, bool isCreate, bool isCancel, bool isRepeating, Js::JavascriptFunction* func, int64 callbackId);
 
         //Record code parse
-        void RecordJsRTCodeParse(Js::ScriptContext* ctx, uint64 bodyCtrId, LoadScriptFlag loadFlag, Js::JavascriptFunction* func, bool isUft8, const byte* script, uint32 scriptByteLength, const char16* sourceUri, Js::JavascriptFunction* resultFunction);
+        NSLogEvents::EventLogEntry* RecordJsRTCodeParse(TTDJsRTActionResultAutoRecorder& actionPopper, LoadScriptFlag loadFlag, bool isUft8, const byte* script, uint32 scriptByteLength, DWORD_PTR sourceContextId, const char16* sourceUri);
 
         //Record callback of an existing function
-        NSLogEvents::EventLogEntry* RecordJsRTCallFunction(Js::ScriptContext* ctx, int32 rootDepth, Js::JavascriptFunction* func, uint32 argCount, Js::Var* args);
-
-        //Replay a sequence of JsRT actions until (and including) the one at eventTimeLimit
-        void ReplayActionLoopRange(int64 eventTimeLimit);
+        NSLogEvents::EventLogEntry* RecordJsRTCallFunction(TTDJsRTActionResultAutoRecorder& actionPopper, int32 rootDepth, Js::JavascriptFunction* func, uint32 argCount, Js::Var* args);
 
         ////////////////////////////////
         //Emit code and support

+ 40 - 10
lib/Runtime/Debug/TTEvents.cpp

@@ -388,9 +388,35 @@ namespace TTD
             }
         }
 
+        bool EventEntersScriptContext(const EventLogEntry* evt)
+        {
+            return (evt->ContextMoveStatus & ContextWrapperEnterExitStatus::Enter) == ContextWrapperEnterExitStatus::Enter;
+        }
+
+        bool EventCompletesScriptContext(const EventLogEntry* evt)
+        {
+            return EventCompletesScriptContextNormally(evt) | EventCompletesScriptContextWithException(evt);
+        }
+
+        bool EventCompletesScriptContextNormally(const EventLogEntry* evt)
+        {
+            return (evt->ContextMoveStatus & ContextWrapperEnterExitStatus::ExitNormal) == ContextWrapperEnterExitStatus::ExitNormal;
+        }
+
+        bool EventCompletesScriptContextWithException(const EventLogEntry* evt)
+        {
+            return (evt->ContextMoveStatus & ContextWrapperEnterExitStatus::ExitException) == ContextWrapperEnterExitStatus::ExitException;
+        }
+
+        ContextWrapperEnterExitStatus GetEventScriptContextEnterExitKind(const EventLogEntry* evt)
+        {
+            return (evt->ContextMoveStatus & ContextWrapperEnterExitStatus::ContextKindMask);
+        }
+
         void EventLogEntry_Initialize(EventLogEntry* evt, EventKind tag, int64 etime)
         {
             evt->EventKind = tag;
+            evt->ContextMoveStatus = ContextWrapperEnterExitStatus::Clear;
 
 #if ENABLE_TTD_INTERNAL_DIAGNOSTICS
             evt->EventTimeStamp = etime;
@@ -402,6 +428,7 @@ namespace TTD
             writer->WriteRecordStart(separator);
 
             writer->WriteTag<EventKind>(NSTokens::Key::eventKind, evt->EventKind);
+            writer->WriteTag<ContextWrapperEnterExitStatus>(NSTokens::Key::eventEnterExitStatus, evt->ContextMoveStatus, NSTokens::Separator::CommaSeparator);
 
 #if ENABLE_TTD_INTERNAL_DIAGNOSTICS
             writer->WriteInt64(NSTokens::Key::eventTime, evt->EventTimeStamp, NSTokens::Separator::CommaSeparator);
@@ -421,6 +448,7 @@ namespace TTD
             reader->ReadRecordStart(readSeperator);
 
             evt->EventKind = reader->ReadTag<EventKind>(NSTokens::Key::eventKind);
+            evt->ContextMoveStatus = reader->ReadTag<ContextWrapperEnterExitStatus>(NSTokens::Key::eventEnterExitStatus, true);
 
 #if ENABLE_TTD_INTERNAL_DIAGNOSTICS
             evt->EventTimeStamp = reader->ReadInt64(NSTokens::Key::eventTime, true);
@@ -708,7 +736,7 @@ namespace TTD
             return callEvt->AdditionalInfo->LastNestedEventTime;
         }
 
-        void ExternalCallEventLogEntry_ProcessArgs(EventLogEntry* evt, int32 rootDepth, Js::JavascriptFunction* function, uint32 argc, Js::Var* argv, UnlinkableSlabAllocator& alloc)
+        void ExternalCallEventLogEntry_ProcessArgs(EventLogEntry* evt, int32 rootDepth, Js::JavascriptFunction* function, uint32 argc, Js::Var* argv, double beginTime, UnlinkableSlabAllocator& alloc)
         {
             ExternalCallEventLogEntry* callEvt = GetInlineEventDataAs<ExternalCallEventLogEntry, EventKind::ExternalCallTag>(evt);
             callEvt->AdditionalInfo = alloc.SlabAllocateStruct<ExternalCallEventLogEntry_AdditionalInfo>();
@@ -723,19 +751,19 @@ namespace TTD
             js_memcpy_s(callEvt->ArgArray + 1, (callEvt->ArgCount - 1) * sizeof(TTDVar), argv, argc * sizeof(Js::Var));
 
             //Initialize this info in case we terminate without completing (e.g. exit(1))
+            callEvt->AdditionalInfo->BeginTime = beginTime;
+            callEvt->AdditionalInfo->EndTime = -1.0;
+
             callEvt->ReturnValue = nullptr;
-            callEvt->AdditionalInfo->HasScriptException = false;
-            callEvt->AdditionalInfo->HasTerminiatingException = false;
             callEvt->AdditionalInfo->LastNestedEventTime = TTD_EVENT_MAXTIME;
         }
 
-        void ExternalCallEventLogEntry_ProcessReturn(EventLogEntry* evt, Js::Var res, bool hasScriptException, bool hasTerminiatingException, int64 lastNestedEvent)
+        void ExternalCallEventLogEntry_ProcessReturn(EventLogEntry* evt, Js::Var res, int64 lastNestedEvent, double endTime)
         {
             ExternalCallEventLogEntry* callEvt = GetInlineEventDataAs<ExternalCallEventLogEntry, EventKind::ExternalCallTag>(evt);
 
+            callEvt->AdditionalInfo->EndTime = endTime;
             callEvt->ReturnValue = TTD_CONVERT_JSVAR_TO_TTDVAR(res);
-            callEvt->AdditionalInfo->HasScriptException = hasScriptException;
-            callEvt->AdditionalInfo->HasTerminiatingException = hasTerminiatingException;
             callEvt->AdditionalInfo->LastNestedEventTime = lastNestedEvent;
         }
 
@@ -774,9 +802,10 @@ namespace TTD
             writer->WriteKey(NSTokens::Key::argRetVal, NSTokens::Separator::CommaSeparator);
             NSSnapValues::EmitTTDVar(callEvt->ReturnValue, writer, NSTokens::Separator::NoSeparator);
 
-            writer->WriteBool(NSTokens::Key::boolVal, callEvt->AdditionalInfo->HasScriptException, NSTokens::Separator::CommaSeparator);
-            writer->WriteBool(NSTokens::Key::boolVal, callEvt->AdditionalInfo->HasTerminiatingException, NSTokens::Separator::CommaSeparator);
             writer->WriteInt64(NSTokens::Key::i64Val, callEvt->AdditionalInfo->LastNestedEventTime, NSTokens::Separator::CommaSeparator);
+
+            writer->WriteDouble(NSTokens::Key::beginTime, callEvt->AdditionalInfo->BeginTime, NSTokens::Separator::CommaSeparator);
+            writer->WriteDouble(NSTokens::Key::endTime, callEvt->AdditionalInfo->EndTime, NSTokens::Separator::CommaSeparator);
         }
 
         void ExternalCallEventLogEntry_Parse(EventLogEntry* evt, ThreadContext* threadContext, FileReader* reader, UnlinkableSlabAllocator& alloc)
@@ -803,9 +832,10 @@ namespace TTD
             reader->ReadKey(NSTokens::Key::argRetVal, true);
             callEvt->ReturnValue = NSSnapValues::ParseTTDVar(false, reader);
 
-            callEvt->AdditionalInfo->HasScriptException = reader->ReadBool(NSTokens::Key::boolVal, true);
-            callEvt->AdditionalInfo->HasTerminiatingException = reader->ReadBool(NSTokens::Key::boolVal, true);
             callEvt->AdditionalInfo->LastNestedEventTime = reader->ReadInt64(NSTokens::Key::i64Val, true);
+
+            callEvt->AdditionalInfo->BeginTime = reader->ReadDouble(NSTokens::Key::beginTime, true);
+            callEvt->AdditionalInfo->EndTime = reader->ReadDouble(NSTokens::Key::endTime, true);
         }
     }
 }

+ 40 - 8
lib/Runtime/Debug/TTEvents.h

@@ -131,10 +131,32 @@ namespace TTD
 
     //////////////////
 
+    //An enumeration that indicates if the event moves into/out-of context status 
+    enum class ContextWrapperEnterExitStatus : uint16
+    {
+        Clear = 0x0,
+
+        Enter = 0x1,
+        ExitNormal = 0x2,
+        ExitException = 0x4,
+
+        GlobalAPIWrapper = 0x10,
+        ContextAPIWrapper = 0x20,
+        ContextAPINoScriptWrapper = 0x40,
+
+        //handy combinations
+        NoNewEntry = Clear,
+        EnterGlobalAPIWrapper = Enter | GlobalAPIWrapper,
+        EnterContextAPIWrapper = Enter | ContextAPIWrapper,
+        EnterContextAPINoScriptWrapper = Enter | ContextAPINoScriptWrapper,
+        ContextKindMask = GlobalAPIWrapper | ContextAPIWrapper | ContextAPINoScriptWrapper
+    };
+    DEFINE_ENUM_FLAG_OPERATORS(ContextWrapperEnterExitStatus)
+
     namespace NSLogEvents
     {
         //An enumeration of the event kinds in the system
-        enum class EventKind : uint32
+        enum class EventKind : uint16
         {
             Invalid = 0x0,
             //Tags for internal engine events
@@ -184,6 +206,7 @@ namespace TTD
 
             HostExitProcessTag,
             GetAndClearExceptionActionTag,
+            SetExceptionActionTag,
 
             GetPropertyActionTag,
             GetIndexActionTag,
@@ -240,6 +263,9 @@ namespace TTD
             //The kind of the event
             EventKind EventKind;
 
+            //infor on the event moving into/out of script context mode
+            ContextWrapperEnterExitStatus ContextMoveStatus;
+
 #if ENABLE_TTD_INTERNAL_DIAGNOSTICS
             //The event time for this event
             int64 EventTimeStamp;
@@ -264,6 +290,14 @@ namespace TTD
             return reinterpret_cast<T*>(evt->EventData);
         }
 
+        bool EventEntersScriptContext(const EventLogEntry* evt);
+        bool EventCompletesScriptContext(const EventLogEntry* evt);
+        bool EventCompletesScriptContextNormally(const EventLogEntry* evt);
+        bool EventCompletesScriptContextWithException(const EventLogEntry* evt);
+
+        //Get the kind of the context we are enter/exiting without the entry/exit info
+        ContextWrapperEnterExitStatus GetEventScriptContextEnterExitKind(const EventLogEntry* evt);
+
         //Helpers for initializing, emitting and parsing the basic event data
         void EventLogEntry_Initialize(EventLogEntry* evt, EventKind tag, int64 etime);
         void EventLogEntry_Emit(const EventLogEntry* evt, EventLogEntryVTableEntry* evtFPVTable, FileWriter* writer, ThreadContext* threadContext, NSTokens::Separator separator);
@@ -415,11 +449,9 @@ namespace TTD
         //A struct containing additional information on the external call
         struct ExternalCallEventLogEntry_AdditionalInfo
         {
-            //
-            //TODO: later we should record more detail on the script exception for inflation if needed
-            //
-            bool HasScriptException;
-            bool HasTerminiatingException;
+            //The wall clock times for this action
+            double BeginTime;
+            double EndTime;
 
             //The last event time that is nested in this external call
             int64 LastNestedEventTime;
@@ -452,8 +484,8 @@ namespace TTD
 
         int64 ExternalCallEventLogEntry_GetLastNestedEventTime(const EventLogEntry* evt);
 
-        void ExternalCallEventLogEntry_ProcessArgs(EventLogEntry* evt, int32 rootDepth, Js::JavascriptFunction* function, uint32 argc, Js::Var* argv, UnlinkableSlabAllocator& alloc);
-        void ExternalCallEventLogEntry_ProcessReturn(EventLogEntry* evt, Js::Var res, bool hasScriptException, bool hasTerminiatingException, int64 lastNestedEvent);
+        void ExternalCallEventLogEntry_ProcessArgs(EventLogEntry* evt, int32 rootDepth, Js::JavascriptFunction* function, uint32 argc, Js::Var* argv, double beginTime, UnlinkableSlabAllocator& alloc);
+        void ExternalCallEventLogEntry_ProcessReturn(EventLogEntry* evt, Js::Var res, int64 lastNestedEvent, double endTime);
 
         void ExternalCallEventLogEntry_UnloadEventMemory(EventLogEntry* evt, UnlinkableSlabAllocator& alloc);
         void ExternalCallEventLogEntry_Emit(const EventLogEntry* evt, FileWriter* writer, ThreadContext* threadContext);

+ 1 - 0
lib/Runtime/Debug/TTSerializeEnum.h

@@ -118,6 +118,7 @@ ENTRY_SERIALIZE_ENUM(usedMemory)
 ENTRY_SERIALIZE_ENUM(reservedMemory)
 
 ENTRY_SERIALIZE_ENUM(eventKind)
+ENTRY_SERIALIZE_ENUM(eventEnterExitStatus)
 ENTRY_SERIALIZE_ENUM(eventTime)
 ENTRY_SERIALIZE_ENUM(functionTime)
 ENTRY_SERIALIZE_ENUM(loopTime)

+ 5 - 2
lib/Runtime/Debug/TTSnapValues.cpp

@@ -1508,9 +1508,12 @@ namespace TTD
 
             if(updateName)
             {
-                AssertMsg(wcsstr(fbInfo->FunctionName.Contents, _u("get ")) == 0 || wcsstr(fbInfo->FunctionName.Contents, _u("set ")) == 0, "Does not start with get or set");
+                uint32 suffixWDotPos = (fbInfo->FunctionName.Length - 4);
+                uint32 suffixPos = (fbInfo->FunctionName.Length - 3);
 
-                resfb->SetDisplayName(fbInfo->FunctionName.Contents,fbInfo->FunctionName.Length, 3, Js::FunctionProxy::SetDisplayNameFlagsRecyclerAllocated);
+                AssertMsg(wcsstr(fbInfo->FunctionName.Contents, _u(".get")) == (fbInfo->FunctionName.Contents + suffixWDotPos) || wcsstr(fbInfo->FunctionName.Contents, _u(".set")) == (fbInfo->FunctionName.Contents + suffixWDotPos), "Does not start with get or set");
+
+                resfb->SetDisplayName(fbInfo->FunctionName.Contents, fbInfo->FunctionName.Length, suffixPos, Js::FunctionProxy::SetDisplayNameFlagsRecyclerAllocated);
             }
 
             inflator->AddInflationFunctionBody(fbInfo->FunctionBodyId, resfb);

+ 10 - 3
lib/Runtime/Debug/TTSnapshot.cpp

@@ -667,9 +667,16 @@ namespace TTD
         }
 
         //Make sure all objects/values have been matched
-        compareMap.DiagnosticAssert(comparedSlotArrays == snap1->m_slotArrayEntries.Count() && comparedSlotArrays == snap2->m_slotArrayEntries.Count());
-        compareMap.DiagnosticAssert(comparedScopes == snap1->m_scopeEntries.Count() && comparedScopes == snap2->m_scopeEntries.Count());
-        compareMap.DiagnosticAssert(comparedObjects == snap1->m_compoundObjectList.Count() && comparedObjects == snap2->m_compoundObjectList.Count());
+        //
+        //TODO: this is weird we do a < since weak sets/maps can't be checked without backtracking or some topo ordering on the keys 
+        //
+        compareMap.DiagnosticAssert(comparedSlotArrays <= snap1->m_slotArrayEntries.Count() && comparedSlotArrays <= snap2->m_slotArrayEntries.Count());
+        compareMap.DiagnosticAssert(comparedScopes <= snap1->m_scopeEntries.Count() && comparedScopes <= snap2->m_scopeEntries.Count());
+        compareMap.DiagnosticAssert(comparedObjects <= snap1->m_compoundObjectList.Count() && comparedObjects <= snap2->m_compoundObjectList.Count());
+
+        compareMap.DiagnosticAssert(snap1->m_slotArrayEntries.Count() == snap2->m_slotArrayEntries.Count());
+        compareMap.DiagnosticAssert(snap1->m_scopeEntries.Count() == snap2->m_scopeEntries.Count());
+        compareMap.DiagnosticAssert(snap1->m_compoundObjectList.Count() == snap2->m_compoundObjectList.Count());
 
         //
         //TODO: if we missed something we may want to put code here to identify it

+ 62 - 0
lib/Runtime/Debug/TTSupport.cpp

@@ -8,6 +8,68 @@
 
 namespace TTD
 {
+    TTModeStack::TTModeStack()
+        : m_stackEntries(nullptr), m_stackTop(0), m_stackMax(16)
+    {
+        this->m_stackEntries = TT_HEAP_ALLOC_ARRAY_ZERO(TTDMode, 16);
+    }
+
+    TTModeStack::~TTModeStack()
+    {
+        TT_HEAP_FREE_ARRAY(TTDMode, this->m_stackEntries, this->m_stackMax);
+    }
+
+    uint32 TTModeStack::Count() const
+    {
+        return this->m_stackTop;
+    }
+
+    TTDMode TTModeStack::GetAt(uint32 index) const
+    {
+        AssertMsg(index < this->m_stackTop, "index is out of range");
+
+        return this->m_stackEntries[index];
+    }
+
+    void TTModeStack::SetAt(uint32 index, TTDMode m)
+    {
+        AssertMsg(index < this->m_stackTop, "index is out of range");
+
+        this->m_stackEntries[index] = m;
+    }
+
+    void TTModeStack::Push(TTDMode m)
+    {
+        if(this->m_stackTop == this->m_stackMax)
+        {
+            uint32 newMax = this->m_stackMax + 16;
+            TTDMode* newStack = TT_HEAP_ALLOC_ARRAY_ZERO(TTDMode, newMax);
+            js_memcpy_s(newStack, newMax * sizeof(TTDMode), this->m_stackEntries, this->m_stackMax * sizeof(TTDMode));
+
+            TT_HEAP_FREE_ARRAY(TTDMode, this->m_stackEntries, this->m_stackMax);
+
+            this->m_stackMax = newMax;
+            this->m_stackEntries = newStack;
+        }
+
+        this->m_stackEntries[this->m_stackTop] = m;
+        this->m_stackTop++;
+    }
+
+    TTDMode TTModeStack::Peek() const
+    {
+        AssertMsg(this->m_stackTop > 0, "Undeflow in stack pop.");
+
+        return this->m_stackEntries[this->m_stackTop - 1];
+    }
+
+    void TTModeStack::Pop()
+    {
+        AssertMsg(this->m_stackTop > 0, "Undeflow in stack pop.");
+
+        this->m_stackTop--;
+    }
+
     namespace UtilSupport
     {
         TTAutoString::TTAutoString()

+ 22 - 0
lib/Runtime/Debug/TTSupport.h

@@ -52,6 +52,7 @@ namespace TTD
     class TTDebuggerAbortException;
     class TTDebuggerSourceLocation;
 }
+
 ////////
 //Memory allocators used by the TT code
 #define TT_HEAP_NEW(T, ...) HeapNewNoThrow(T, __VA_ARGS__)
@@ -164,6 +165,27 @@ namespace TTD
     };
     DEFINE_ENUM_FLAG_OPERATORS(TTDMode)
 
+    class TTModeStack
+    {
+    private:
+        TTDMode* m_stackEntries;
+
+        uint32 m_stackTop;
+        uint32 m_stackMax;
+
+    public:
+        TTModeStack();
+        ~TTModeStack();
+
+        uint32 Count() const;
+        TTDMode GetAt(uint32 index) const;
+        void SetAt(uint32 index, TTDMode m);
+
+        void Push(TTDMode m);
+        TTDMode Peek() const;
+        void Pop();
+    };
+
     namespace NSSnapObjects
     {
         //An enumeration of tags for the SnapObjects (to support dispatch when parsing)

+ 15 - 15
lib/Runtime/Library/JavascriptExternalFunction.cpp

@@ -195,15 +195,15 @@ namespace Js
         //
         if(scriptContext->ShouldPerformDebugAction())
         {
-            TTD::TTDReplayExternalFunctionCallActionPopper logPopper(externalFunction);
-
+            TTD::TTDNestingDepthAutoAdjuster logPopper(scriptContext);
             scriptContext->GetThreadContext()->TTDLog->ReplayExternalCallEvent(externalFunction, args.Info.Count, args.Values, &result);
         }
         else if(scriptContext->ShouldPerformRecordAction())
         {
-            //Root nesting depth handled in logPopper constructor, destructor, and Normal return paths -- the increment of nesting is handled by the popper but we need to add 1 to the value we record (so it matches)
-            TTD::NSLogEvents::EventLogEntry* callEvent = scriptContext->GetThreadContext()->TTDLog->RecordExternalCallEvent(externalFunction, scriptContext->TTDRootNestingCount + 1, args.Info.Count, args.Values);
-            TTD::TTDRecordExternalFunctionCallActionPopper logPopper(externalFunction, callEvent);
+            TTD::EventLog* elog = scriptContext->GetThreadContext()->TTDLog;
+
+            TTD::TTDNestingDepthAutoAdjuster logPopper(scriptContext);
+            TTD::NSLogEvents::EventLogEntry* callEvent = elog->RecordExternalCallEvent(externalFunction, scriptContext->TTDRootNestingCount, args.Info.Count, args.Values);
 
             BEGIN_LEAVE_SCRIPT_WITH_EXCEPTION(scriptContext)
             {
@@ -212,8 +212,8 @@ namespace Js
             }
             END_LEAVE_SCRIPT_WITH_EXCEPTION(scriptContext);
 
-            //no exception check below so I assume the external call cannot have an exception registered
-            logPopper.NormalReturn(false, result);
+            //Exceptions should be prohibited so no need to do extra work
+            elog->RecordExternalCallEvent_Complete(externalFunction, callEvent, result);
         }
         else
         {
@@ -222,7 +222,7 @@ namespace Js
                 //The only way this should happen is if the debugger is requesting a value to display that is an external accessor 
                 //or the debugger is running something that can fail (and it is ok with that).
 
-                result = function->GetScriptContext()->GetLibrary()->GetUndefined();
+                result = scriptContext->GetLibrary()->GetUndefined();
             }
             else
             {
@@ -243,6 +243,7 @@ namespace Js
         }
         END_LEAVE_SCRIPT_WITH_EXCEPTION(scriptContext);
 #endif
+
         if (result == nullptr)
         {
 #pragma warning(push)
@@ -296,15 +297,15 @@ namespace Js
 #if ENABLE_TTD
         if(scriptContext->ShouldPerformDebugAction())
         {
-            TTD::TTDReplayExternalFunctionCallActionPopper logPopper(externalFunction);
-
+            TTD::TTDNestingDepthAutoAdjuster logPopper(scriptContext);
             scriptContext->GetThreadContext()->TTDLog->ReplayExternalCallEvent(externalFunction, args.Info.Count, args.Values, &result);
         }
         else if(scriptContext->ShouldPerformRecordAction())
         {
-            //Root nesting depth handled in logPopper constructor, destructor, and Normal return paths -- the increment of nesting is handled by the popper but we need to add 1 to the value we record (so it matches)
-            TTD::NSLogEvents::EventLogEntry* callEvent = scriptContext->GetThreadContext()->TTDLog->RecordExternalCallEvent(externalFunction, scriptContext->TTDRootNestingCount + 1, args.Info.Count, args.Values);
-            TTD::TTDRecordExternalFunctionCallActionPopper logPopper(externalFunction, callEvent);
+            TTD::EventLog* elog = scriptContext->GetThreadContext()->TTDLog;
+
+            TTD::TTDNestingDepthAutoAdjuster logPopper(scriptContext);
+            TTD::NSLogEvents::EventLogEntry* callEvent = elog->RecordExternalCallEvent(externalFunction, scriptContext->TTDRootNestingCount, args.Info.Count, args.Values);
 
             BEGIN_LEAVE_SCRIPT(scriptContext)
             {
@@ -312,8 +313,7 @@ namespace Js
             }
             END_LEAVE_SCRIPT(scriptContext);
 
-            //exception check is done explicitly below call can have an exception registered
-            logPopper.NormalReturn(true, result);
+            elog->RecordExternalCallEvent_Complete(externalFunction, callEvent, result);
         }
         else
         {

+ 10 - 1
lib/Runtime/Library/JavascriptWeakMap.cpp

@@ -249,7 +249,7 @@ namespace Js
         //TODO: This makes the map decidedly less weak -- forces it to only release when we clean the tracking set but determinizes the behavior nicely
         //      We want to improve this.
         //
-        if(scriptContext->ShouldPerformDebugAction() | scriptContext->ShouldPerformRecordAction())
+        if(scriptContext->ShouldPerformWeakRefPinAction())
         {
             scriptContext->TTDContextInfo->TTDWeakReferencePinSet->Add(keyObj);
         }
@@ -339,6 +339,15 @@ namespace Js
     }
 
 #if ENABLE_TTD
+    void JavascriptWeakMap::MarkVisitKindSpecificPtrs(TTD::SnapshotExtractor* extractor)
+    {
+        this->Map([&](DynamicObject* key, Js::Var value)
+        {
+            extractor->MarkVisitVar(key);
+            extractor->MarkVisitVar(value);
+        });
+    }
+
     TTD::NSSnapObjects::SnapObjectType JavascriptWeakMap::GetSnapTag_TTD() const
     {
         return TTD::NSSnapObjects::SnapObjectType::SnapMapObject;

+ 2 - 0
lib/Runtime/Library/JavascriptWeakMap.h

@@ -116,6 +116,8 @@ namespace Js
 
 #if ENABLE_TTD
     public:
+        virtual void MarkVisitKindSpecificPtrs(TTD::SnapshotExtractor* extractor) override;
+
         virtual TTD::NSSnapObjects::SnapObjectType GetSnapTag_TTD() const override;
         virtual void ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc) override;
 #endif

+ 9 - 1
lib/Runtime/Library/JavascriptWeakSet.cpp

@@ -107,7 +107,7 @@ namespace Js
         //This makes the set decidedly less weak -- forces it to only release when we clean the tracking set but determinizes the behavior nicely
         //      We want to improve this.
         //
-        if(scriptContext->ShouldPerformDebugAction() | scriptContext->ShouldPerformRecordAction())
+        if(scriptContext->ShouldPerformWeakRefPinAction())
         {
             scriptContext->TTDContextInfo->TTDWeakReferencePinSet->Add(keyObj);
         }
@@ -196,6 +196,14 @@ namespace Js
     }
 
 #if ENABLE_TTD
+    void JavascriptWeakSet::MarkVisitKindSpecificPtrs(TTD::SnapshotExtractor* extractor)
+    {
+        this->Map([&](DynamicObject* key)
+        {
+            extractor->MarkVisitVar(key);
+        });
+    }
+
     TTD::NSSnapObjects::SnapObjectType JavascriptWeakSet::GetSnapTag_TTD() const
     {
         return TTD::NSSnapObjects::SnapObjectType::SnapSetObject;

+ 2 - 0
lib/Runtime/Library/JavascriptWeakSet.h

@@ -56,6 +56,8 @@ namespace Js
 
 #if ENABLE_TTD
     public:
+        virtual void MarkVisitKindSpecificPtrs(TTD::SnapshotExtractor* extractor) override;
+
         virtual TTD::NSSnapObjects::SnapObjectType GetSnapTag_TTD() const override;
         virtual void ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc) override;
 #endif

+ 16 - 0
test/TTExecuteBasic/rlexe.xml

@@ -192,4 +192,20 @@
       <tags>exclude_dynapogo,exclude_jshost,exclude_snap,exclude_serialized</tags>
     </default>
   </test>
+  <test>
+    <default>
+      <files>try.js</files>
+      <compile-flags>-TTRecord=~tryTest -TTSnapInterval=0</compile-flags>
+      <baseline>tryRecord.baseline</baseline>
+      <tags>exclude_dynapogo,exclude_jshost,exclude_snap,exclude_serialized</tags>
+    </default>
+  </test>
+  <test>
+    <default>
+      <files>ttdSentinal.js</files>
+      <compile-flags>-TTDebug=~tryTest -TTDStartEvent=2</compile-flags>
+      <baseline>tryReplay.baseline</baseline>
+      <tags>exclude_dynapogo,exclude_jshost,exclude_snap,exclude_serialized</tags>
+    </default>
+  </test>
 </regress-exe>

+ 51 - 0
test/TTExecuteBasic/try.js

@@ -0,0 +1,51 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+var e = 8;
+
+function x() { throw 7; }
+
+function y() {
+    var i;
+    for (i = 0; i < 10; i++) {
+        try {
+            if (i % 2 == 0) {
+                try {
+                    x();
+                }
+                catch (e) {
+                    telemetryLog(`Inner catch: ${e}`, true);
+                    if (i % 3) {
+                        throw e;
+                    }
+                    if (i % 5) {
+                        return e;
+                    }
+                }
+                finally {
+                    telemetryLog(`Finally: ${i}`, true);
+                    continue;
+                }
+            }
+        }
+        catch (e) {
+            telemetryLog(`Outer catch: ${e}`, true);
+        }
+        finally {
+            telemetryLog(`Outer finally: ${i}`, true);
+            if (++i % 9 == 0)
+                return e;
+        }
+    }
+}
+
+WScript.SetTimeout(testFunction, 50);
+
+/////////////////
+
+function testFunction()
+{
+    y();
+}

+ 15 - 0
test/TTExecuteBasic/tryRecord.baseline

@@ -0,0 +1,15 @@
+Inner catch: 7
+Finally: 0
+Outer finally: 0
+Inner catch: 7
+Finally: 2
+Outer finally: 2
+Inner catch: 7
+Finally: 4
+Outer finally: 4
+Inner catch: 7
+Finally: 6
+Outer finally: 6
+Inner catch: 7
+Finally: 8
+Outer finally: 8

+ 17 - 0
test/TTExecuteBasic/tryReplay.baseline

@@ -0,0 +1,17 @@
+Inner catch: 7
+Finally: 0
+Outer finally: 0
+Inner catch: 7
+Finally: 2
+Outer finally: 2
+Inner catch: 7
+Finally: 4
+Outer finally: 4
+Inner catch: 7
+Finally: 6
+Outer finally: 6
+Inner catch: 7
+Finally: 8
+Outer finally: 8
+
+Reached end of Execution -- Exiting.

Некоторые файлы не были показаны из-за большого количества измененных файлов