| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #pragma once
- //This file contains definitions for data structures that track info about the runtime (ThreadContext and ScriptContexts)
- //that are needed by other parts of the TTD system
- #if ENABLE_TTD
- //default capcities for our core object maps/lists
- #define TTD_CORE_OBJECT_COUNT 1028
- #define TTD_CORE_FUNCTION_BODY_COUNT 512
- #define MAX_CONTEXT_COUNT 32
- namespace TTD
- {
- //This class implements the data structures and algorithms needed to manage the ThreadContext TTD runtime info.
- //Basically we don't want to add a lot of size/complexity to the ThreadContext object/class if it isn't perf critical
- class ThreadContextTTD
- {
- private:
- typedef JsUtil::WeaklyReferencedKeyDictionary<Js::RecyclableObject, bool, RecyclerPointerComparer<const Js::RecyclableObject*>> RecordRootMap;
- ThreadContext* m_threadCtx;
- void* m_runtimeHandle;
- //A flag that we set during replay when a script context is created or destroyed
- bool m_contextCreatedOrDestoyedInReplay;
- //A list of contexts that are being run in TTD mode and the currently active context (may be null)
- Js::ScriptContext* m_activeContext;
- JsUtil::List<Js::ScriptContext*, HeapAllocator> m_contextList;
- //The pin set we have for the external contexts that are created
- //We add ref to the thread context instead of having a pin set during replay
- JsUtil::BaseDictionary<Js::ScriptContext*, FinalizableObject*, HeapAllocator> m_ttdContextToExternalRefMap;
- //Keep track of which log ptr ids map to which objects -- in record this is only for add ref or addhidden ref objects in replay it includes local as awell
- JsUtil::BaseDictionary<TTD_LOG_PTR_ID, Js::RecyclableObject*, HeapAllocator> m_ttdRootTagToObjectMap;
- JsUtil::BaseDictionary<TTD_LOG_PTR_ID, bool, HeapAllocator> m_ttdMayBeLongLivedRoot;
- //In record we add refs to this weak map and the contents represent the live accessible roots (at loop yield/snapshot points)
- RecyclerRootPtr<RecordRootMap> m_ttdRecordRootWeakMap;
- //In replay we explicitly keep root objects alive -- this is the pin set and map from LOG_PTR_ID values to the actual objects
- RecyclerRootPtr<ObjectPinSet> m_ttdReplayRootPinSet;
- void AddNewScriptContext_Helper(Js::ScriptContext* ctx, HostScriptContextCallbackFunctor& callbackFunctor, bool noNative, bool debugMode);
- //Clean any empty slots out of the weak record map so we aren't leaking space
- void CleanRecordWeakRootMap();
- public:
- uint32 SnapInterval;
- uint32 SnapHistoryLength;
- //Callback functions provided by the host for writing info to some type of storage location
- TTDataIOInfo TTDataIOInfo;
- //Callback functions provided by the host for creating external objects
- ExternalObjectFunctions TTDExternalObjectFunctions;
- ThreadContextTTD(ThreadContext* threadContext, void* runtimeHandle, uint32 snapInterval, uint32 snapHistoryLength);
- ~ThreadContextTTD();
- ThreadContext* GetThreadContext();
- void* GetRuntimeHandle();
- FinalizableObject* GetRuntimeContextForScriptContext(Js::ScriptContext* ctx);
- bool ContextCreatedOrDestoyedInReplay() const;
- void ResetContextCreatedOrDestoyedInReplay();
- //Get the list of all the context that we are currently tracking
- const JsUtil::List<Js::ScriptContext*, HeapAllocator>& GetTTDContexts() const;
- void AddNewScriptContextRecord(FinalizableObject* externalCtx, Js::ScriptContext* ctx, HostScriptContextCallbackFunctor& callbackFunctor, bool noNative, bool debugMode);
- void AddNewScriptContextReplay(FinalizableObject* externalCtx, Js::ScriptContext* ctx, HostScriptContextCallbackFunctor& callbackFunctor, bool noNative, bool debugMode);
- void SetActiveScriptContext(Js::ScriptContext* ctx);
- Js::ScriptContext* GetActiveScriptContext();
- //This is called from an excluded section of code (GC processing) so we can't check mode info, instead we must check if the ctx is in our map
- void NotifyCtxDestroyInRecord(Js::ScriptContext* ctx);
- void ClearContextsForSnapRestore(JsUtil::List<FinalizableObject*, HeapAllocator>& deadCtxs);
- void AddRootRef_Record(TTD_LOG_PTR_ID origId, Js::RecyclableObject* newRoot)
- {
- this->m_ttdRecordRootWeakMap->Item(newRoot, true);
- this->m_ttdMayBeLongLivedRoot.Item(origId, true);
- this->m_ttdRootTagToObjectMap.Item(origId, newRoot);
- }
- void AddRootRef_Replay(TTD_LOG_PTR_ID origId, Js::RecyclableObject* newRoot)
- {
- this->m_ttdReplayRootPinSet->AddNew(newRoot);
- this->m_ttdMayBeLongLivedRoot.Item(origId, true);
- this->m_ttdRootTagToObjectMap.Item(origId, newRoot);
- }
- void AddLocalRoot(TTD_LOG_PTR_ID origId, Js::RecyclableObject* newRoot)
- {
- this->m_ttdReplayRootPinSet->AddNew(newRoot);
- this->m_ttdRootTagToObjectMap.Item(origId, newRoot);
- }
- void ClearLocalRootsAndRefreshMap_Replay();
- const JsUtil::BaseDictionary<TTD_LOG_PTR_ID, Js::RecyclableObject*, HeapAllocator>& GetRootTagToObjectMap() const { return this->m_ttdRootTagToObjectMap; }
- //Get any ref counter id for a log ptr id reference (0 if there are no string/weak refs)
- bool ResolveIsLongLivedForExtract(TTD_LOG_PTR_ID origId) const { return this->m_ttdMayBeLongLivedRoot.LookupWithKey(origId, false); }
- void LoadInvertedRootMap(JsUtil::BaseDictionary<Js::RecyclableObject*, TTD_LOG_PTR_ID, HeapAllocator>& objToLogIdMap) const;
- Js::RecyclableObject* LookupObjectForLogID(TTD_LOG_PTR_ID origId);
- void ClearRootsForSnapRestore();
- void ForceSetRootInfoInRestore(TTD_LOG_PTR_ID logid, Js::RecyclableObject* obj, bool maybeLongLived);
- //Sync up the root set information before we do a snapshot in record mode
- void SyncRootsBeforeSnapshot_Record();
- //When we are replaying and hit a snapshot we need to sync up with the live script contexts and roots as in replay (so do that here)
- void SyncCtxtsAndRootsWithSnapshot_Replay(uint32 liveContextCount, TTD_LOG_PTR_ID* liveContextIdArray, uint32 liveRootCount, TTD_LOG_PTR_ID* liveRootIdArray);
- Js::ScriptContext* LookupContextForScriptId(TTD_LOG_PTR_ID ctxId) const;
- };
- //This struct stores the info for pending async mutations to array buffer objects
- struct TTDPendingAsyncBufferModification
- {
- Js::Var ArrayBufferVar; //the actual array buffer that is pending
- uint32 Index; //the start index that we are monitoring from
- };
- //This class implements the data structures and algorithms needed to manage the ScriptContext TTD runtime info.
- //Basically we don't want to add a lot of size/complexity to the ScriptContext object/class if it isn't perf critical
- class ScriptContextTTD
- {
- private:
- Js::ScriptContext* m_ctx;
- //List of pending async modifications to array buffers
- JsUtil::List<TTDPendingAsyncBufferModification, HeapAllocator> m_ttdPendingAsyncModList;
- //The lists containing the top-level code that is loaded in this context
- JsUtil::List<TTD::TopLevelFunctionInContextRelation, HeapAllocator> m_ttdTopLevelScriptLoad;
- JsUtil::List<TTD::TopLevelFunctionInContextRelation, HeapAllocator> m_ttdTopLevelNewFunction;
- JsUtil::List<TTD::TopLevelFunctionInContextRelation, HeapAllocator> m_ttdTopLevelEval;
- //need to add back pin set for functionBody to make sure they don't get collected on us
- RecyclerRootPtr<TTD::FunctionBodyPinSet> m_ttdPinnedRootFunctionSet;
- JsUtil::BaseDictionary<Js::FunctionBody*, Js::FunctionBody*, HeapAllocator> m_ttdFunctionBodyParentMap;
- public:
- //
- //TODO: this results in a memory leak for programs with weak collections -- we should fix this
- //
- RecyclerRootPtr<ObjectPinSet> TTDWeakReferencePinSet;
- ScriptContextTTD(Js::ScriptContext* ctx);
- ~ScriptContextTTD();
- //Keep track of pending async ArrayBuffer modification
- void AddToAsyncPendingList(Js::ArrayBuffer* trgt, uint32 index);
- void GetFromAsyncPendingList(TTDPendingAsyncBufferModification* pendingInfo, byte* finalModPos);
- const JsUtil::List<TTDPendingAsyncBufferModification, HeapAllocator>& GetPendingAsyncModListForSnapshot() const;
- void ClearPendingAsyncModListForSnapRestore();
- //Get all of the root level sources evaluated in this script context (source text & root function returned)
- void GetLoadedSources(JsUtil::List<TTD::TopLevelFunctionInContextRelation, HeapAllocator>& topLevelScriptLoad, JsUtil::List<TTD::TopLevelFunctionInContextRelation, HeapAllocator>& topLevelNewFunction, JsUtil::List<TTD::TopLevelFunctionInContextRelation, HeapAllocator>& topLevelEval);
- //To support cases where we may get cached function bodies ('new Function' & eval) check if we already know of a top-level body
- bool IsBodyAlreadyLoadedAtTopLevel(Js::FunctionBody* body) const;
- //force parsing and load up the parent maps etc.
- void ProcessFunctionBodyOnLoad(Js::FunctionBody* body, Js::FunctionBody* parent);
- void RegisterLoadedScript(Js::FunctionBody* body, uint32 bodyCtrId);
- void RegisterNewScript(Js::FunctionBody* body, uint32 bodyCtrId);
- void RegisterEvalScript(Js::FunctionBody* body, uint32 bodyCtrId);
- //Lookup the parent body for a function body (or null for global code)
- Js::FunctionBody* ResolveParentBody(Js::FunctionBody* body) const;
- //Helpers for resolving top level bodies accross snapshots
- uint32 FindTopLevelCtrForBody(Js::FunctionBody* body) const;
- Js::FunctionBody* FindRootBodyByTopLevelCtr(uint32 bodyCtrId) const;
- void ClearLoadedSourcesForSnapshotRestore();
- };
- //////////////////
- //This class implements the data structures and algorithms needed to manage the ScriptContext core image
- class RuntimeContextInfo
- {
- private:
- ////
- //code for performing well known object walk
- //A worklist to use for the core obj processing
- JsUtil::Queue<Js::RecyclableObject*, HeapAllocator> m_worklist;
- //A null string we use in a number of places
- UtilSupport::TTAutoString m_nullString;
- //A dictionary which contains the paths for "core" image objects and function bodies
- JsUtil::BaseDictionary<Js::RecyclableObject*, UtilSupport::TTAutoString*, HeapAllocator> m_coreObjToPathMap;
- JsUtil::BaseDictionary<Js::FunctionBody*, UtilSupport::TTAutoString*, HeapAllocator> m_coreBodyToPathMap;
- JsUtil::BaseDictionary<Js::DebuggerScope*, UtilSupport::TTAutoString*, HeapAllocator> m_coreDbgScopeToPathMap;
- JsUtil::List<Js::RecyclableObject*, HeapAllocator> m_sortedObjectList;
- JsUtil::List<Js::FunctionBody*, HeapAllocator> m_sortedFunctionBodyList;
- JsUtil::List<Js::DebuggerScope*, HeapAllocator> m_sortedDbgScopeList;
- //Build a path string based on a given name
- void BuildPathString(UtilSupport::TTAutoString, const char16* name, const char16* optaccessortag, UtilSupport::TTAutoString& into);
- //Ensure that when we do our core visit make sure that the properties always appear in the same order
- static void LoadAndOrderPropertyNames(Js::RecyclableObject* obj, JsUtil::List<const Js::PropertyRecord*, HeapAllocator>& propertyList);
- static bool PropertyNameCmp(const Js::PropertyRecord* p1, const Js::PropertyRecord* p2);
- ////
-
- public:
- RuntimeContextInfo();
- ~RuntimeContextInfo();
- //Mark all the well-known objects/values/types from this script context
- void MarkWellKnownObjects_TTD(MarkTable& marks) const;
- //Get the path name for a known path object (or function body)
- TTD_WELLKNOWN_TOKEN ResolvePathForKnownObject(Js::RecyclableObject* obj) const;
- TTD_WELLKNOWN_TOKEN ResolvePathForKnownFunctionBody(Js::FunctionBody* fbody) const;
- TTD_WELLKNOWN_TOKEN ResolvePathForKnownDbgScopeIfExists(Js::DebuggerScope* dbgScope) const;
- //Given a path name string lookup the coresponding object
- Js::RecyclableObject* LookupKnownObjectFromPath(TTD_WELLKNOWN_TOKEN pathIdString) const;
- Js::FunctionBody* LookupKnownFunctionBodyFromPath(TTD_WELLKNOWN_TOKEN pathIdString) const;
- Js::DebuggerScope* LookupKnownDebuggerScopeFromPath(TTD_WELLKNOWN_TOKEN pathIdString) const;
- //Walk the "known names" we use and fill the map with the objects at said names
- void GatherKnownObjectToPathMap(Js::ScriptContext* ctx);
- ////
- //Enqueue a root object in our core path walk
- void EnqueueRootPathObject(const char16* rootName, Js::RecyclableObject* obj);
- //Enqueue a child object that is stored at the given property in the parent
- void EnqueueNewPathVarAsNeeded(Js::RecyclableObject* parent, Js::Var val, const Js::PropertyRecord* prop, const char16* optacessortag = nullptr);
- void EnqueueNewPathVarAsNeeded(Js::RecyclableObject* parent, Js::Var val, const char16* propName, const char16* optacessortag = nullptr);
- //Enqueue a child object that is stored at a special named location in the parent object
- void EnqueueNewFunctionBodyObject(Js::RecyclableObject* parent, Js::FunctionBody* fbody, const char16* name);
- //Add a well known token for a debugger scope object (in a slot array)
- void AddWellKnownDebuggerScopePath(Js::RecyclableObject* parent, Js::DebuggerScope* dbgScope, uint32 index);
- //Build a path string based on a root path and an array index
- void BuildArrayIndexBuffer(uint32 arrayidx, UtilSupport::TTAutoString& res);
- //Build a path string based on a root path and an environment index
- void BuildEnvironmentIndexBuffer(uint32 envidx, UtilSupport::TTAutoString& res);
- //Build a path string based on an environment index and a slot index
- void BuildEnvironmentIndexAndSlotBuffer(uint32 envidx, uint32 slotidx, UtilSupport::TTAutoString& res);
- };
- //////////////////
- //Algorithms for sorting searching a list based on lexo-order from names in a map
- template <typename T>
- void SortDictIntoListOnNames(const JsUtil::BaseDictionary<T, UtilSupport::TTAutoString*, HeapAllocator>& objToNameMap, JsUtil::List<T, HeapAllocator>& sortedObjList, const UtilSupport::TTAutoString& nullString)
- {
- TTDAssert(sortedObjList.Count() == 0, "This should be empty.");
- objToNameMap.Map([&](T key, UtilSupport::TTAutoString* value)
- {
- sortedObjList.Add(key);
- });
- //now sort the list so the traversal order is stable
- //Rock a custom shell sort!!!!
- const int32 gaps[8] = { 701, 301, 132, 57, 23, 10, 4, 1 };
- int32 llen = sortedObjList.Count();
- for(uint32 gapi = 0; gapi < 8; ++gapi)
- {
- int32 gap = gaps[gapi];
- for(int32 i = gap; i < llen; i++)
- {
- T temp = sortedObjList.Item(i);
- const UtilSupport::TTAutoString* tempStr = objToNameMap.Item(temp);
- int32 j = 0;
- for(j = i; j >= gap && (wcscmp(objToNameMap.Item(sortedObjList.Item(j - gap))->GetStrValue(), tempStr->GetStrValue()) > 0); j -= gap)
- {
- T shiftElem = sortedObjList.Item(j - gap);
- sortedObjList.SetItem(j, shiftElem);
- }
- sortedObjList.SetItem(j, temp);
- }
- }
- }
- template <typename T, bool mustFind>
- int32 LookupPositionInDictNameList(const char16* key, const JsUtil::BaseDictionary<T, UtilSupport::TTAutoString*, HeapAllocator>& objToNameMap, const JsUtil::List<T, HeapAllocator>& sortedObjList, const UtilSupport::TTAutoString& nullString)
- {
- AssertMsg(sortedObjList.Count() != 0, "We are using this for matching so obviously no match and there is a problem.");
- int32 imin = 0;
- int32 imax = sortedObjList.Count() - 1;
- while(imin < imax)
- {
- int imid = (imin + imax) / 2;
- const UtilSupport::TTAutoString* imidStr = objToNameMap.Item(sortedObjList.Item(imid));
- AssertMsg(imid < imax, "Something went wrong with our indexing.");
- int32 scmpval = wcscmp(imidStr->GetStrValue(), key);
- if(scmpval < 0)
- {
- imin = imid + 1;
- }
- else
- {
- imax = imid;
- }
- }
- TTDAssert(imin == imax, "Something went wrong!!!");
-
- const UtilSupport::TTAutoString* resStr = objToNameMap.Item(sortedObjList.Item(imin));
- if(mustFind)
- {
- TTDAssert(wcscmp(resStr->GetStrValue(), key) == 0, "We are missing something");
- return imin;
- }
- else
- {
- return (wcscmp(resStr->GetStrValue(), key) == 0) ? imin : -1;
- }
- }
- }
- #endif
|