TTRuntimeInfoTracker.h 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #pragma once
  6. //This file contains definitions for data structures that track info about the runtime (ThreadContext and ScriptContexts)
  7. //that are needed by other parts of the TTD system
  8. #if ENABLE_TTD
  9. //default capcities for our core object maps/lists
  10. #define TTD_CORE_OBJECT_COUNT 1028
  11. #define TTD_CORE_FUNCTION_BODY_COUNT 512
  12. #define MAX_CONTEXT_COUNT 32
  13. namespace TTD
  14. {
  15. //This class implements the data structures and algorithms needed to manage the ThreadContext TTD runtime info.
  16. //Basically we don't want to add a lot of size/complexity to the ThreadContext object/class if it isn't perf critical
  17. class ThreadContextTTD
  18. {
  19. private:
  20. typedef JsUtil::WeaklyReferencedKeyDictionary<Js::RecyclableObject, bool, RecyclerPointerComparer<const Js::RecyclableObject*>> RecordRootMap;
  21. ThreadContext* m_threadCtx;
  22. void* m_runtimeHandle;
  23. //A flag that we set during replay when a script context is created or destroyed
  24. bool m_contextCreatedOrDestoyedInReplay;
  25. //A list of contexts that are being run in TTD mode and the currently active context (may be null)
  26. Js::ScriptContext* m_activeContext;
  27. JsUtil::List<Js::ScriptContext*, HeapAllocator> m_contextList;
  28. //The pin set we have for the external contexts that are created
  29. //We add ref to the thread context instead of having a pin set during replay
  30. JsUtil::BaseDictionary<Js::ScriptContext*, FinalizableObject*, HeapAllocator> m_ttdContextToExternalRefMap;
  31. //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
  32. JsUtil::BaseDictionary<TTD_LOG_PTR_ID, Js::RecyclableObject*, HeapAllocator> m_ttdRootTagToObjectMap;
  33. JsUtil::BaseDictionary<TTD_LOG_PTR_ID, bool, HeapAllocator> m_ttdMayBeLongLivedRoot;
  34. //In record we add refs to this weak map and the contents represent the live accessible roots (at loop yield/snapshot points)
  35. RecyclerRootPtr<RecordRootMap> m_ttdRecordRootWeakMap;
  36. //In replay we explicitly keep root objects alive -- this is the pin set and map from LOG_PTR_ID values to the actual objects
  37. RecyclerRootPtr<ObjectPinSet> m_ttdReplayRootPinSet;
  38. void AddNewScriptContext_Helper(Js::ScriptContext* ctx, HostScriptContextCallbackFunctor& callbackFunctor, bool noNative, bool debugMode);
  39. //Clean any empty slots out of the weak record map so we aren't leaking space
  40. void CleanRecordWeakRootMap();
  41. public:
  42. uint32 SnapInterval;
  43. uint32 SnapHistoryLength;
  44. //Callback functions provided by the host for writing info to some type of storage location
  45. TTDataIOInfo TTDataIOInfo;
  46. //Callback functions provided by the host for creating external objects
  47. ExternalObjectFunctions TTDExternalObjectFunctions;
  48. ThreadContextTTD(ThreadContext* threadContext, void* runtimeHandle, uint32 snapInterval, uint32 snapHistoryLength);
  49. ~ThreadContextTTD();
  50. ThreadContext* GetThreadContext();
  51. void* GetRuntimeHandle();
  52. FinalizableObject* GetRuntimeContextForScriptContext(Js::ScriptContext* ctx);
  53. bool ContextCreatedOrDestoyedInReplay() const;
  54. void ResetContextCreatedOrDestoyedInReplay();
  55. //Get the list of all the context that we are currently tracking
  56. const JsUtil::List<Js::ScriptContext*, HeapAllocator>& GetTTDContexts() const;
  57. void AddNewScriptContextRecord(FinalizableObject* externalCtx, Js::ScriptContext* ctx, HostScriptContextCallbackFunctor& callbackFunctor, bool noNative, bool debugMode);
  58. void AddNewScriptContextReplay(FinalizableObject* externalCtx, Js::ScriptContext* ctx, HostScriptContextCallbackFunctor& callbackFunctor, bool noNative, bool debugMode);
  59. void SetActiveScriptContext(Js::ScriptContext* ctx);
  60. Js::ScriptContext* GetActiveScriptContext();
  61. //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
  62. void NotifyCtxDestroyInRecord(Js::ScriptContext* ctx);
  63. void ClearContextsForSnapRestore(JsUtil::List<FinalizableObject*, HeapAllocator>& deadCtxs);
  64. void AddRootRef_Record(TTD_LOG_PTR_ID origId, Js::RecyclableObject* newRoot)
  65. {
  66. this->m_ttdRecordRootWeakMap->Item(newRoot, true);
  67. this->m_ttdMayBeLongLivedRoot.Item(origId, true);
  68. this->m_ttdRootTagToObjectMap.Item(origId, newRoot);
  69. }
  70. void AddRootRef_Replay(TTD_LOG_PTR_ID origId, Js::RecyclableObject* newRoot)
  71. {
  72. this->m_ttdReplayRootPinSet->AddNew(newRoot);
  73. this->m_ttdMayBeLongLivedRoot.Item(origId, true);
  74. this->m_ttdRootTagToObjectMap.Item(origId, newRoot);
  75. }
  76. void AddLocalRoot(TTD_LOG_PTR_ID origId, Js::RecyclableObject* newRoot)
  77. {
  78. this->m_ttdReplayRootPinSet->AddNew(newRoot);
  79. this->m_ttdRootTagToObjectMap.Item(origId, newRoot);
  80. }
  81. void ClearLocalRootsAndRefreshMap_Replay();
  82. const JsUtil::BaseDictionary<TTD_LOG_PTR_ID, Js::RecyclableObject*, HeapAllocator>& GetRootTagToObjectMap() const { return this->m_ttdRootTagToObjectMap; }
  83. //Get any ref counter id for a log ptr id reference (0 if there are no string/weak refs)
  84. bool ResolveIsLongLivedForExtract(TTD_LOG_PTR_ID origId) const { return this->m_ttdMayBeLongLivedRoot.LookupWithKey(origId, false); }
  85. void LoadInvertedRootMap(JsUtil::BaseDictionary<Js::RecyclableObject*, TTD_LOG_PTR_ID, HeapAllocator>& objToLogIdMap) const;
  86. Js::RecyclableObject* LookupObjectForLogID(TTD_LOG_PTR_ID origId);
  87. void ClearRootsForSnapRestore();
  88. void ForceSetRootInfoInRestore(TTD_LOG_PTR_ID logid, Js::RecyclableObject* obj, bool maybeLongLived);
  89. //Sync up the root set information before we do a snapshot in record mode
  90. void SyncRootsBeforeSnapshot_Record();
  91. //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)
  92. void SyncCtxtsAndRootsWithSnapshot_Replay(uint32 liveContextCount, TTD_LOG_PTR_ID* liveContextIdArray, uint32 liveRootCount, TTD_LOG_PTR_ID* liveRootIdArray);
  93. Js::ScriptContext* LookupContextForScriptId(TTD_LOG_PTR_ID ctxId) const;
  94. };
  95. //This struct stores the info for pending async mutations to array buffer objects
  96. struct TTDPendingAsyncBufferModification
  97. {
  98. Js::Var ArrayBufferVar; //the actual array buffer that is pending
  99. uint32 Index; //the start index that we are monitoring from
  100. };
  101. //This class implements the data structures and algorithms needed to manage the ScriptContext TTD runtime info.
  102. //Basically we don't want to add a lot of size/complexity to the ScriptContext object/class if it isn't perf critical
  103. class ScriptContextTTD
  104. {
  105. private:
  106. Js::ScriptContext* m_ctx;
  107. //List of pending async modifications to array buffers
  108. JsUtil::List<TTDPendingAsyncBufferModification, HeapAllocator> m_ttdPendingAsyncModList;
  109. //The lists containing the top-level code that is loaded in this context
  110. JsUtil::BaseDictionary<TTD_PTR_ID, TTD::TopLevelFunctionInContextRelation, HeapAllocator> m_ttdTopLevelScriptLoad;
  111. JsUtil::BaseDictionary<TTD_PTR_ID, TTD::TopLevelFunctionInContextRelation, HeapAllocator> m_ttdTopLevelNewFunction;
  112. JsUtil::BaseDictionary<TTD_PTR_ID, TTD::TopLevelFunctionInContextRelation, HeapAllocator> m_ttdTopLevelEval;
  113. //need to add back pin set for functionBody to make sure they don't get collected on us
  114. RecyclerRootPtr<TTD::FunctionBodyPinSet> m_ttdPinnedRootFunctionSet;
  115. JsUtil::BaseDictionary<Js::FunctionBody*, Js::FunctionBody*, HeapAllocator> m_ttdFunctionBodyParentMap;
  116. public:
  117. //
  118. //TODO: this results in a memory leak for programs with weak collections -- we should fix this
  119. //
  120. RecyclerRootPtr<ObjectPinSet> TTDWeakReferencePinSet;
  121. ScriptContextTTD(Js::ScriptContext* ctx);
  122. ~ScriptContextTTD();
  123. //Keep track of pending async ArrayBuffer modification
  124. void AddToAsyncPendingList(Js::ArrayBuffer* trgt, uint32 index);
  125. void GetFromAsyncPendingList(TTDPendingAsyncBufferModification* pendingInfo, byte* finalModPos);
  126. const JsUtil::List<TTDPendingAsyncBufferModification, HeapAllocator>& GetPendingAsyncModListForSnapshot() const;
  127. void ClearPendingAsyncModListForSnapRestore();
  128. //Get all of the root level sources evaluated in this script context (source text & root function returned)
  129. void GetLoadedSources(const JsUtil::BaseHashSet<Js::FunctionBody*, HeapAllocator>* onlyLiveTopLevelBodies, JsUtil::List<TTD::TopLevelFunctionInContextRelation, HeapAllocator>& topLevelScriptLoad, JsUtil::List<TTD::TopLevelFunctionInContextRelation, HeapAllocator>& topLevelNewFunction, JsUtil::List<TTD::TopLevelFunctionInContextRelation, HeapAllocator>& topLevelEval);
  130. //To support cases where we may get cached function bodies ('new Function' & eval) check if we already know of a top-level body
  131. bool IsBodyAlreadyLoadedAtTopLevel(Js::FunctionBody* body) const;
  132. //force parsing and load up the parent maps etc.
  133. void ProcessFunctionBodyOnLoad(Js::FunctionBody* body, Js::FunctionBody* parent);
  134. void ProcessFunctionBodyOnUnLoad(Js::FunctionBody* body, Js::FunctionBody* parent);
  135. void RegisterLoadedScript(Js::FunctionBody* body, uint32 bodyCtrId);
  136. void RegisterNewScript(Js::FunctionBody* body, uint32 bodyCtrId);
  137. void RegisterEvalScript(Js::FunctionBody* body, uint32 bodyCtrId);
  138. void CleanUnreachableTopLevelBodies(const JsUtil::BaseHashSet<Js::FunctionBody*, HeapAllocator>& liveTopLevelBodies);
  139. //Lookup the parent body for a function body (or null for global code)
  140. Js::FunctionBody* ResolveParentBody(Js::FunctionBody* body) const;
  141. //Helpers for resolving top level bodies accross snapshots
  142. uint32 FindTopLevelCtrForBody(Js::FunctionBody* body) const;
  143. Js::FunctionBody* FindRootBodyByTopLevelCtr(uint32 bodyCtrId) const;
  144. void ClearLoadedSourcesForSnapshotRestore();
  145. };
  146. //////////////////
  147. //This class implements the data structures and algorithms needed to manage the ScriptContext core image
  148. class RuntimeContextInfo
  149. {
  150. private:
  151. ////
  152. //code for performing well known object walk
  153. //A worklist to use for the core obj processing
  154. JsUtil::Queue<Js::RecyclableObject*, HeapAllocator> m_worklist;
  155. //A null string we use in a number of places
  156. UtilSupport::TTAutoString m_nullString;
  157. //A dictionary which contains the paths for "core" image objects and function bodies
  158. JsUtil::BaseDictionary<Js::RecyclableObject*, UtilSupport::TTAutoString*, HeapAllocator> m_coreObjToPathMap;
  159. JsUtil::BaseDictionary<Js::FunctionBody*, UtilSupport::TTAutoString*, HeapAllocator> m_coreBodyToPathMap;
  160. JsUtil::BaseDictionary<Js::DebuggerScope*, UtilSupport::TTAutoString*, HeapAllocator> m_coreDbgScopeToPathMap;
  161. JsUtil::List<Js::RecyclableObject*, HeapAllocator> m_sortedObjectList;
  162. JsUtil::List<Js::FunctionBody*, HeapAllocator> m_sortedFunctionBodyList;
  163. JsUtil::List<Js::DebuggerScope*, HeapAllocator> m_sortedDbgScopeList;
  164. //Build a path string based on a given name
  165. void BuildPathString(UtilSupport::TTAutoString, const char16* name, const char16* optaccessortag, UtilSupport::TTAutoString& into);
  166. //Ensure that when we do our core visit make sure that the properties always appear in the same order
  167. static void LoadAndOrderPropertyNames(Js::RecyclableObject* obj, JsUtil::List<const Js::PropertyRecord*, HeapAllocator>& propertyList);
  168. static bool PropertyNameCmp(const Js::PropertyRecord* p1, const Js::PropertyRecord* p2);
  169. ////
  170. public:
  171. RuntimeContextInfo();
  172. ~RuntimeContextInfo();
  173. //Mark all the well-known objects/values/types from this script context
  174. void MarkWellKnownObjects_TTD(MarkTable& marks) const;
  175. //Get the path name for a known path object (or function body)
  176. TTD_WELLKNOWN_TOKEN ResolvePathForKnownObject(Js::RecyclableObject* obj) const;
  177. TTD_WELLKNOWN_TOKEN ResolvePathForKnownFunctionBody(Js::FunctionBody* fbody) const;
  178. TTD_WELLKNOWN_TOKEN ResolvePathForKnownDbgScopeIfExists(Js::DebuggerScope* dbgScope) const;
  179. //Given a path name string lookup the coresponding object
  180. Js::RecyclableObject* LookupKnownObjectFromPath(TTD_WELLKNOWN_TOKEN pathIdString) const;
  181. Js::FunctionBody* LookupKnownFunctionBodyFromPath(TTD_WELLKNOWN_TOKEN pathIdString) const;
  182. Js::DebuggerScope* LookupKnownDebuggerScopeFromPath(TTD_WELLKNOWN_TOKEN pathIdString) const;
  183. //Walk the "known names" we use and fill the map with the objects at said names
  184. void GatherKnownObjectToPathMap(Js::ScriptContext* ctx);
  185. ////
  186. //Enqueue a root object in our core path walk
  187. void EnqueueRootPathObject(const char16* rootName, Js::RecyclableObject* obj);
  188. //Enqueue a child object that is stored at the given property in the parent
  189. void EnqueueNewPathVarAsNeeded(Js::RecyclableObject* parent, Js::Var val, const Js::PropertyRecord* prop, const char16* optacessortag = nullptr);
  190. void EnqueueNewPathVarAsNeeded(Js::RecyclableObject* parent, Js::Var val, const char16* propName, const char16* optacessortag = nullptr);
  191. //Enqueue a child object that is stored at a special named location in the parent object
  192. void EnqueueNewFunctionBodyObject(Js::RecyclableObject* parent, Js::FunctionBody* fbody, const char16* name);
  193. //Add a well known token for a debugger scope object (in a slot array)
  194. void AddWellKnownDebuggerScopePath(Js::RecyclableObject* parent, Js::DebuggerScope* dbgScope, uint32 index);
  195. //Build a path string based on a root path and an array index
  196. void BuildArrayIndexBuffer(uint32 arrayidx, UtilSupport::TTAutoString& res);
  197. //Build a path string based on a root path and an environment index
  198. void BuildEnvironmentIndexBuffer(uint32 envidx, UtilSupport::TTAutoString& res);
  199. //Build a path string based on an environment index and a slot index
  200. void BuildEnvironmentIndexAndSlotBuffer(uint32 envidx, uint32 slotidx, UtilSupport::TTAutoString& res);
  201. };
  202. //////////////////
  203. //Algorithms for sorting searching a list based on lexo-order from names in a map
  204. template <typename T>
  205. void SortDictIntoListOnNames(const JsUtil::BaseDictionary<T, UtilSupport::TTAutoString*, HeapAllocator>& objToNameMap, JsUtil::List<T, HeapAllocator>& sortedObjList, const UtilSupport::TTAutoString& nullString)
  206. {
  207. TTDAssert(sortedObjList.Count() == 0, "This should be empty.");
  208. objToNameMap.Map([&](T key, UtilSupport::TTAutoString* value)
  209. {
  210. sortedObjList.Add(key);
  211. });
  212. //now sort the list so the traversal order is stable
  213. //Rock a custom shell sort!!!!
  214. const int32 gaps[8] = { 701, 301, 132, 57, 23, 10, 4, 1 };
  215. int32 llen = sortedObjList.Count();
  216. for(uint32 gapi = 0; gapi < 8; ++gapi)
  217. {
  218. int32 gap = gaps[gapi];
  219. for(int32 i = gap; i < llen; i++)
  220. {
  221. T temp = sortedObjList.Item(i);
  222. const UtilSupport::TTAutoString* tempStr = objToNameMap.Item(temp);
  223. int32 j = 0;
  224. for(j = i; j >= gap && (wcscmp(objToNameMap.Item(sortedObjList.Item(j - gap))->GetStrValue(), tempStr->GetStrValue()) > 0); j -= gap)
  225. {
  226. T shiftElem = sortedObjList.Item(j - gap);
  227. sortedObjList.SetItem(j, shiftElem);
  228. }
  229. sortedObjList.SetItem(j, temp);
  230. }
  231. }
  232. }
  233. template <typename T, bool mustFind>
  234. int32 LookupPositionInDictNameList(const char16* key, const JsUtil::BaseDictionary<T, UtilSupport::TTAutoString*, HeapAllocator>& objToNameMap, const JsUtil::List<T, HeapAllocator>& sortedObjList, const UtilSupport::TTAutoString& nullString)
  235. {
  236. AssertMsg(sortedObjList.Count() != 0, "We are using this for matching so obviously no match and there is a problem.");
  237. int32 imin = 0;
  238. int32 imax = sortedObjList.Count() - 1;
  239. while(imin < imax)
  240. {
  241. int imid = (imin + imax) / 2;
  242. const UtilSupport::TTAutoString* imidStr = objToNameMap.Item(sortedObjList.Item(imid));
  243. AssertMsg(imid < imax, "Something went wrong with our indexing.");
  244. int32 scmpval = wcscmp(imidStr->GetStrValue(), key);
  245. if(scmpval < 0)
  246. {
  247. imin = imid + 1;
  248. }
  249. else
  250. {
  251. imax = imid;
  252. }
  253. }
  254. TTDAssert(imin == imax, "Something went wrong!!!");
  255. const UtilSupport::TTAutoString* resStr = objToNameMap.Item(sortedObjList.Item(imin));
  256. if(mustFind)
  257. {
  258. TTDAssert(wcscmp(resStr->GetStrValue(), key) == 0, "We are missing something");
  259. return imin;
  260. }
  261. else
  262. {
  263. return (wcscmp(resStr->GetStrValue(), key) == 0) ? imin : -1;
  264. }
  265. }
  266. }
  267. #endif