TTRuntmeInfoTracker.cpp 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894
  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. #include "RuntimeDebugPch.h"
  6. #if ENABLE_TTD
  7. namespace TTD
  8. {
  9. void ThreadContextTTD::AddNewScriptContext_Helper(Js::ScriptContext* ctx, HostScriptContextCallbackFunctor& callbackFunctor, bool noNative, bool debugMode)
  10. {
  11. ////
  12. //First just setup the standard things needed for a script context
  13. ctx->TTDHostCallbackFunctor = callbackFunctor;
  14. if(noNative)
  15. {
  16. ctx->ForceNoNative();
  17. }
  18. if(debugMode)
  19. {
  20. #ifdef _WIN32
  21. ctx->InitializeDebugging();
  22. #else
  23. //
  24. //TODO: x-plat does not like some parts of initiallize debugging so just set the flag we need
  25. //
  26. ctx->GetDebugContext()->SetDebuggerMode(Js::DebuggerMode::Debugging);
  27. #endif
  28. }
  29. ctx->InitializeCoreImage_TTD();
  30. TTDAssert(!this->m_contextList.Contains(ctx), "We should only be adding at creation time!!!");
  31. this->m_contextList.Add(ctx);
  32. }
  33. void ThreadContextTTD::CleanRecordWeakRootMap()
  34. {
  35. this->m_ttdRecordRootWeakMap->Map([&](Js::RecyclableObject* key, bool, const RecyclerWeakReference<Js::RecyclableObject>*)
  36. {
  37. ; //nop just map to force the clean
  38. });
  39. }
  40. ThreadContextTTD::ThreadContextTTD(ThreadContext* threadContext, void* runtimeHandle, uint32 snapInterval, uint32 snapHistoryLength)
  41. : m_threadCtx(threadContext), m_runtimeHandle(runtimeHandle), m_contextCreatedOrDestoyedInReplay(false),
  42. SnapInterval(snapInterval), SnapHistoryLength(snapHistoryLength),
  43. m_activeContext(nullptr), m_contextList(&HeapAllocator::Instance), m_ttdContextToExternalRefMap(&HeapAllocator::Instance),
  44. m_ttdRootTagToObjectMap(&HeapAllocator::Instance), m_ttdMayBeLongLivedRoot(&HeapAllocator::Instance),
  45. m_ttdRecordRootWeakMap(),m_ttdReplayRootPinSet(),
  46. TTDataIOInfo({ 0 }), TTDExternalObjectFunctions({ 0 })
  47. {
  48. Recycler* tctxRecycler = this->m_threadCtx->GetRecycler();
  49. this->m_ttdRecordRootWeakMap.Root(RecyclerNew(tctxRecycler, RecordRootMap, tctxRecycler), tctxRecycler);
  50. this->m_ttdReplayRootPinSet.Root(RecyclerNew(tctxRecycler, ObjectPinSet, tctxRecycler), tctxRecycler);
  51. }
  52. ThreadContextTTD::~ThreadContextTTD()
  53. {
  54. for(auto iter = this->m_ttdContextToExternalRefMap.GetIterator(); iter.IsValid(); iter.MoveNext())
  55. {
  56. this->m_threadCtx->GetRecycler()->RootRelease(iter.CurrentValue());
  57. }
  58. this->m_ttdContextToExternalRefMap.Clear();
  59. this->m_activeContext = nullptr;
  60. this->m_contextList.Clear();
  61. this->m_ttdRootTagToObjectMap.Clear();
  62. this->m_ttdMayBeLongLivedRoot.Clear();
  63. if(this->m_ttdRecordRootWeakMap != nullptr)
  64. {
  65. this->m_ttdRecordRootWeakMap.Unroot(this->m_threadCtx->GetRecycler());
  66. }
  67. if(this->m_ttdReplayRootPinSet != nullptr)
  68. {
  69. this->m_ttdReplayRootPinSet.Unroot(this->m_threadCtx->GetRecycler());
  70. }
  71. }
  72. ThreadContext* ThreadContextTTD::GetThreadContext()
  73. {
  74. return this->m_threadCtx;
  75. }
  76. void* ThreadContextTTD::GetRuntimeHandle()
  77. {
  78. return this->m_runtimeHandle;
  79. }
  80. FinalizableObject* ThreadContextTTD::GetRuntimeContextForScriptContext(Js::ScriptContext* ctx)
  81. {
  82. return this->m_ttdContextToExternalRefMap.Lookup(ctx, nullptr);
  83. }
  84. bool ThreadContextTTD::ContextCreatedOrDestoyedInReplay() const
  85. {
  86. return this->m_contextCreatedOrDestoyedInReplay;
  87. }
  88. void ThreadContextTTD::ResetContextCreatedOrDestoyedInReplay()
  89. {
  90. this->m_contextCreatedOrDestoyedInReplay = false;
  91. }
  92. const JsUtil::List<Js::ScriptContext*, HeapAllocator>& ThreadContextTTD::GetTTDContexts() const
  93. {
  94. return this->m_contextList;
  95. }
  96. void ThreadContextTTD::AddNewScriptContextRecord(FinalizableObject* externalCtx, Js::ScriptContext* ctx, HostScriptContextCallbackFunctor& callbackFunctor, bool noNative, bool debugMode)
  97. {
  98. this->AddNewScriptContext_Helper(ctx, callbackFunctor, noNative, debugMode);
  99. this->AddRootRef_Record(TTD_CONVERT_OBJ_TO_LOG_PTR_ID(ctx->GetGlobalObject()), ctx->GetGlobalObject());
  100. ctx->ScriptContextLogTag = TTD_CONVERT_OBJ_TO_LOG_PTR_ID(ctx->GetGlobalObject());
  101. this->AddRootRef_Record(TTD_CONVERT_OBJ_TO_LOG_PTR_ID(ctx->GetLibrary()->GetUndefined()), ctx->GetLibrary()->GetUndefined());
  102. this->AddRootRef_Record(TTD_CONVERT_OBJ_TO_LOG_PTR_ID(ctx->GetLibrary()->GetNull()), ctx->GetLibrary()->GetNull());
  103. this->AddRootRef_Record(TTD_CONVERT_OBJ_TO_LOG_PTR_ID(ctx->GetLibrary()->GetTrue()), ctx->GetLibrary()->GetTrue());
  104. this->AddRootRef_Record(TTD_CONVERT_OBJ_TO_LOG_PTR_ID(ctx->GetLibrary()->GetFalse()), ctx->GetLibrary()->GetFalse());
  105. }
  106. void ThreadContextTTD::AddNewScriptContextReplay(FinalizableObject* externalCtx, Js::ScriptContext* ctx, HostScriptContextCallbackFunctor& callbackFunctor, bool noNative, bool debugMode)
  107. {
  108. this->AddNewScriptContext_Helper(ctx, callbackFunctor, noNative, debugMode);
  109. this->m_contextCreatedOrDestoyedInReplay = true;
  110. this->m_threadCtx->GetRecycler()->RootAddRef(externalCtx);
  111. this->m_ttdContextToExternalRefMap.Add(ctx, externalCtx);
  112. }
  113. void ThreadContextTTD::SetActiveScriptContext(Js::ScriptContext* ctx)
  114. {
  115. TTDAssert(ctx == nullptr || this->m_contextList.Contains(ctx), "Missing value!!!");
  116. this->m_activeContext = ctx;
  117. }
  118. Js::ScriptContext* ThreadContextTTD::GetActiveScriptContext()
  119. {
  120. return this->m_activeContext;
  121. }
  122. void ThreadContextTTD::NotifyCtxDestroyInRecord(Js::ScriptContext* ctx)
  123. {
  124. if(this->m_contextList.Contains(ctx))
  125. {
  126. this->m_contextList.Remove(ctx);
  127. }
  128. }
  129. void ThreadContextTTD::ClearContextsForSnapRestore(JsUtil::List<FinalizableObject*, HeapAllocator>& deadCtxs)
  130. {
  131. for(int32 i = 0; i < this->m_contextList.Count(); ++i)
  132. {
  133. Js::ScriptContext* ctx = this->m_contextList.Item(i);
  134. FinalizableObject* externalCtx = this->m_ttdContextToExternalRefMap.Item(ctx);
  135. deadCtxs.Add(externalCtx);
  136. }
  137. this->m_ttdContextToExternalRefMap.Clear();
  138. this->m_contextList.Clear();
  139. this->m_activeContext = nullptr;
  140. }
  141. void ThreadContextTTD::ClearLocalRootsAndRefreshMap_Replay()
  142. {
  143. JsUtil::BaseHashSet<Js::RecyclableObject*, HeapAllocator> image(&HeapAllocator::Instance);
  144. this->m_ttdRootTagToObjectMap.MapAndRemoveIf([&](JsUtil::SimpleDictionaryEntry<TTD_LOG_PTR_ID, Js::RecyclableObject*>& entry) -> bool
  145. {
  146. bool localonly = !this->m_ttdMayBeLongLivedRoot.LookupWithKey(entry.Key(), false);
  147. if(!localonly)
  148. {
  149. image.AddNew(entry.Value());
  150. }
  151. return localonly;
  152. });
  153. this->m_ttdReplayRootPinSet->MapAndRemoveIf([&](Js::RecyclableObject* value) -> bool
  154. {
  155. return !image.Contains(value);
  156. });
  157. }
  158. void ThreadContextTTD::LoadInvertedRootMap(JsUtil::BaseDictionary<Js::RecyclableObject*, TTD_LOG_PTR_ID, HeapAllocator>& objToLogIdMap) const
  159. {
  160. for(auto iter = this->m_ttdRootTagToObjectMap.GetIterator(); iter.IsValid(); iter.MoveNext())
  161. {
  162. objToLogIdMap.AddNew(iter.CurrentValue(), iter.CurrentKey());
  163. }
  164. }
  165. Js::RecyclableObject* ThreadContextTTD::LookupObjectForLogID(TTD_LOG_PTR_ID origId)
  166. {
  167. //Local root always has mappings for all the ids
  168. return this->m_ttdRootTagToObjectMap.Item(origId);
  169. }
  170. void ThreadContextTTD::ClearRootsForSnapRestore()
  171. {
  172. this->m_ttdRootTagToObjectMap.Clear();
  173. this->m_ttdMayBeLongLivedRoot.Clear();
  174. this->m_ttdReplayRootPinSet->Clear();
  175. }
  176. void ThreadContextTTD::ForceSetRootInfoInRestore(TTD_LOG_PTR_ID logid, Js::RecyclableObject* obj, bool maybeLongLived)
  177. {
  178. this->m_ttdRootTagToObjectMap.Item(logid, obj);
  179. if(maybeLongLived)
  180. {
  181. this->m_ttdMayBeLongLivedRoot.Item(logid, maybeLongLived);
  182. }
  183. this->m_ttdReplayRootPinSet->AddNew(obj);
  184. }
  185. void ThreadContextTTD::SyncRootsBeforeSnapshot_Record()
  186. {
  187. this->CleanRecordWeakRootMap();
  188. this->m_ttdRootTagToObjectMap.MapAndRemoveIf([&](JsUtil::SimpleDictionaryEntry<TTD_LOG_PTR_ID, Js::RecyclableObject*>& entry) -> bool
  189. {
  190. return !this->m_ttdRecordRootWeakMap->Lookup(entry.Value(), false);
  191. });
  192. this->m_ttdMayBeLongLivedRoot.MapAndRemoveIf([&](JsUtil::SimpleDictionaryEntry<TTD_LOG_PTR_ID, bool>& entry) -> bool
  193. {
  194. return !this->m_ttdRootTagToObjectMap.ContainsKey(entry.Key());
  195. });
  196. }
  197. void ThreadContextTTD::SyncCtxtsAndRootsWithSnapshot_Replay(uint32 liveContextCount, TTD_LOG_PTR_ID* liveContextIdArray, uint32 liveRootCount, TTD_LOG_PTR_ID* liveRootIdArray)
  198. {
  199. //First sync up the context list -- releasing any contexts that are not also there in our initial recording
  200. JsUtil::BaseHashSet<TTD_LOG_PTR_ID, HeapAllocator> lctxSet(&HeapAllocator::Instance);
  201. for(uint32 i = 0; i < liveContextCount; ++i)
  202. {
  203. lctxSet.AddNew(liveContextIdArray[i]);
  204. }
  205. int32 ctxpos = 0;
  206. while(ctxpos < this->m_contextList.Count())
  207. {
  208. Js::ScriptContext* ctx = this->m_contextList.Item(ctxpos);
  209. if(lctxSet.Contains(ctx->ScriptContextLogTag))
  210. {
  211. ctxpos++;
  212. }
  213. else
  214. {
  215. this->m_contextCreatedOrDestoyedInReplay = true;
  216. this->m_contextList.Remove(ctx);
  217. FinalizableObject* externalCtx = this->m_ttdContextToExternalRefMap.Item(ctx);
  218. this->m_ttdContextToExternalRefMap.Remove(ctx);
  219. this->m_threadCtx->GetRecycler()->RootRelease(externalCtx);
  220. //no inc of ctxpos since list go shorter!!!
  221. }
  222. }
  223. //Now sync up the root list wrt. long lived roots that we recorded
  224. JsUtil::BaseHashSet<TTD_LOG_PTR_ID, HeapAllocator> refInfoMap(&HeapAllocator::Instance);
  225. for(uint32 i = 0; i < liveRootCount; ++i)
  226. {
  227. refInfoMap.AddNew(liveRootIdArray[i]);
  228. }
  229. this->m_ttdMayBeLongLivedRoot.MapAndRemoveIf([&](JsUtil::SimpleDictionaryEntry<TTD_LOG_PTR_ID, bool>& entry) -> bool
  230. {
  231. return !refInfoMap.Contains(entry.Key());
  232. });
  233. this->ClearLocalRootsAndRefreshMap_Replay();
  234. }
  235. Js::ScriptContext* ThreadContextTTD::LookupContextForScriptId(TTD_LOG_PTR_ID ctxId) const
  236. {
  237. for(int i = 0; i < this->m_contextList.Count(); ++i)
  238. {
  239. if(this->m_contextList.Item(i)->ScriptContextLogTag == ctxId)
  240. {
  241. return this->m_contextList.Item(i);
  242. }
  243. }
  244. return nullptr;
  245. }
  246. ScriptContextTTD::ScriptContextTTD(Js::ScriptContext* ctx)
  247. : m_ctx(ctx),
  248. m_ttdPendingAsyncModList(&HeapAllocator::Instance),
  249. m_ttdTopLevelScriptLoad(&HeapAllocator::Instance), m_ttdTopLevelNewFunction(&HeapAllocator::Instance), m_ttdTopLevelEval(&HeapAllocator::Instance),
  250. m_ttdFunctionBodyParentMap(&HeapAllocator::Instance)
  251. {
  252. Recycler* ctxRecycler = this->m_ctx->GetRecycler();
  253. this->m_ttdPinnedRootFunctionSet.Root(RecyclerNew(ctxRecycler, TTD::FunctionBodyPinSet, ctxRecycler), ctxRecycler);
  254. this->TTDWeakReferencePinSet.Root(RecyclerNew(ctxRecycler, TTD::ObjectPinSet, ctxRecycler), ctxRecycler);
  255. }
  256. ScriptContextTTD::~ScriptContextTTD()
  257. {
  258. this->m_ttdPendingAsyncModList.Clear();
  259. this->m_ttdTopLevelScriptLoad.Clear();
  260. this->m_ttdTopLevelNewFunction.Clear();
  261. this->m_ttdTopLevelEval.Clear();
  262. if(this->m_ttdPinnedRootFunctionSet != nullptr)
  263. {
  264. this->m_ttdPinnedRootFunctionSet.Unroot(this->m_ttdPinnedRootFunctionSet->GetAllocator());
  265. }
  266. this->m_ttdFunctionBodyParentMap.Clear();
  267. if(this->TTDWeakReferencePinSet != nullptr)
  268. {
  269. this->TTDWeakReferencePinSet.Unroot(this->TTDWeakReferencePinSet->GetAllocator());
  270. }
  271. }
  272. void ScriptContextTTD::AddToAsyncPendingList(Js::ArrayBuffer* trgt, uint32 index)
  273. {
  274. TTDPendingAsyncBufferModification pending = { trgt, index };
  275. this->m_ttdPendingAsyncModList.Add(pending);
  276. }
  277. void ScriptContextTTD::GetFromAsyncPendingList(TTDPendingAsyncBufferModification* pendingInfo, byte* finalModPos)
  278. {
  279. pendingInfo->ArrayBufferVar = nullptr;
  280. pendingInfo->Index = 0;
  281. const byte* currentBegin = nullptr;
  282. int32 pos = -1;
  283. for(int32 i = 0; i < this->m_ttdPendingAsyncModList.Count(); ++i)
  284. {
  285. const TTDPendingAsyncBufferModification& pi = this->m_ttdPendingAsyncModList.Item(i);
  286. const Js::ArrayBuffer* pbuff = Js::ArrayBuffer::FromVar(pi.ArrayBufferVar);
  287. const byte* pbuffBegin = pbuff->GetBuffer() + pi.Index;
  288. const byte* pbuffMax = pbuff->GetBuffer() + pbuff->GetByteLength();
  289. //if the final mod is less than the start of this buffer + index or off then end then this definitely isn't it so skip
  290. if(pbuffBegin > finalModPos || pbuffMax < finalModPos)
  291. {
  292. continue;
  293. }
  294. //it is in the right range so now we assume non-overlapping so we see if this pbuffBegin is closer than the current best
  295. TTDAssert(finalModPos != currentBegin, "We have something strange!!!");
  296. if(currentBegin == nullptr || finalModPos < currentBegin)
  297. {
  298. currentBegin = pbuffBegin;
  299. pos = (int32)i;
  300. }
  301. }
  302. TTDAssert(pos != -1, "Missing matching register!!!");
  303. *pendingInfo = this->m_ttdPendingAsyncModList.Item(pos);
  304. this->m_ttdPendingAsyncModList.RemoveAt(pos);
  305. }
  306. const JsUtil::List<TTDPendingAsyncBufferModification, HeapAllocator>& ScriptContextTTD::GetPendingAsyncModListForSnapshot() const
  307. {
  308. return this->m_ttdPendingAsyncModList;
  309. }
  310. void ScriptContextTTD::ClearPendingAsyncModListForSnapRestore()
  311. {
  312. this->m_ttdPendingAsyncModList.Clear();
  313. }
  314. void ScriptContextTTD::GetLoadedSources(JsUtil::List<TTD::TopLevelFunctionInContextRelation, HeapAllocator>& topLevelScriptLoad, JsUtil::List<TTD::TopLevelFunctionInContextRelation, HeapAllocator>& topLevelNewFunction, JsUtil::List<TTD::TopLevelFunctionInContextRelation, HeapAllocator>& topLevelEval)
  315. {
  316. TTDAssert(topLevelScriptLoad.Count() == 0 && topLevelNewFunction.Count() == 0 && topLevelEval.Count() == 0, "Should be empty when you call this.");
  317. topLevelScriptLoad.AddRange(this->m_ttdTopLevelScriptLoad);
  318. topLevelNewFunction.AddRange(this->m_ttdTopLevelNewFunction);
  319. topLevelEval.AddRange(this->m_ttdTopLevelEval);
  320. }
  321. bool ScriptContextTTD::IsBodyAlreadyLoadedAtTopLevel(Js::FunctionBody* body) const
  322. {
  323. return this->m_ttdPinnedRootFunctionSet->Contains(body);
  324. }
  325. void ScriptContextTTD::ProcessFunctionBodyOnLoad(Js::FunctionBody* body, Js::FunctionBody* parent)
  326. {
  327. //if this is a root (parent is null) then put this in the rootbody pin set so it isn't reclaimed on us
  328. if(parent == nullptr)
  329. {
  330. TTDAssert(!this->m_ttdPinnedRootFunctionSet->Contains(body), "We already added this function!!!");
  331. this->m_ttdPinnedRootFunctionSet->AddNew(body);
  332. }
  333. this->m_ttdFunctionBodyParentMap.AddNew(body, parent);
  334. for(uint32 i = 0; i < body->GetNestedCount(); ++i)
  335. {
  336. Js::ParseableFunctionInfo* pfiMid = body->GetNestedFunctionForExecution(i);
  337. Js::FunctionBody* currfb = TTD::JsSupport::ForceAndGetFunctionBody(pfiMid);
  338. this->ProcessFunctionBodyOnLoad(currfb, body);
  339. }
  340. }
  341. void ScriptContextTTD::RegisterLoadedScript(Js::FunctionBody* body, uint32 bodyCtrId)
  342. {
  343. TTD::TopLevelFunctionInContextRelation relation;
  344. relation.TopLevelBodyCtr = bodyCtrId;
  345. relation.ContextSpecificBodyPtrId = TTD_CONVERT_FUNCTIONBODY_TO_PTR_ID(body);
  346. this->m_ttdTopLevelScriptLoad.Add(relation);
  347. }
  348. void ScriptContextTTD::RegisterNewScript(Js::FunctionBody* body, uint32 bodyCtrId)
  349. {
  350. TTD::TopLevelFunctionInContextRelation relation;
  351. relation.TopLevelBodyCtr = bodyCtrId;
  352. relation.ContextSpecificBodyPtrId = TTD_CONVERT_FUNCTIONBODY_TO_PTR_ID(body);
  353. this->m_ttdTopLevelNewFunction.Add(relation);
  354. }
  355. void ScriptContextTTD::RegisterEvalScript(Js::FunctionBody* body, uint32 bodyCtrId)
  356. {
  357. TTD::TopLevelFunctionInContextRelation relation;
  358. relation.TopLevelBodyCtr = bodyCtrId;
  359. relation.ContextSpecificBodyPtrId = TTD_CONVERT_FUNCTIONBODY_TO_PTR_ID(body);
  360. this->m_ttdTopLevelEval.Add(relation);
  361. }
  362. Js::FunctionBody* ScriptContextTTD::ResolveParentBody(Js::FunctionBody* body) const
  363. {
  364. //Want to return null if this has no parent (or this is an internal function and we don't care about parents)
  365. return this->m_ttdFunctionBodyParentMap.LookupWithKey(body, nullptr);
  366. }
  367. uint32 ScriptContextTTD::FindTopLevelCtrForBody(Js::FunctionBody* body) const
  368. {
  369. Js::FunctionBody* rootBody = body;
  370. while(this->ResolveParentBody(rootBody) != nullptr)
  371. {
  372. rootBody = this->ResolveParentBody(rootBody);
  373. }
  374. TTD_PTR_ID trgtid = TTD_CONVERT_FUNCTIONBODY_TO_PTR_ID(rootBody);
  375. for(int32 i = 0; i < this->m_ttdTopLevelScriptLoad.Count(); ++i)
  376. {
  377. if(this->m_ttdTopLevelScriptLoad.Item(i).ContextSpecificBodyPtrId == trgtid)
  378. {
  379. return this->m_ttdTopLevelScriptLoad.Item(i).TopLevelBodyCtr;
  380. }
  381. }
  382. for(int32 i = 0; i < this->m_ttdTopLevelNewFunction.Count(); ++i)
  383. {
  384. if(this->m_ttdTopLevelNewFunction.Item(i).ContextSpecificBodyPtrId == trgtid)
  385. {
  386. return this->m_ttdTopLevelNewFunction.Item(i).TopLevelBodyCtr;
  387. }
  388. }
  389. for(int32 i = 0; i < this->m_ttdTopLevelEval.Count(); ++i)
  390. {
  391. if(this->m_ttdTopLevelEval.Item(i).ContextSpecificBodyPtrId == trgtid)
  392. {
  393. return this->m_ttdTopLevelEval.Item(i).TopLevelBodyCtr;
  394. }
  395. }
  396. TTDAssert(false, "We are missing a top-level function reference.");
  397. return 0;
  398. }
  399. Js::FunctionBody* ScriptContextTTD::FindRootBodyByTopLevelCtr(uint32 bodyCtrId) const
  400. {
  401. for(int32 i = 0; i < this->m_ttdTopLevelScriptLoad.Count(); ++i)
  402. {
  403. if(this->m_ttdTopLevelScriptLoad.Item(i).TopLevelBodyCtr == bodyCtrId)
  404. {
  405. return TTD_COERCE_PTR_ID_TO_FUNCTIONBODY(this->m_ttdTopLevelScriptLoad.Item(i).ContextSpecificBodyPtrId);
  406. }
  407. }
  408. for(int32 i = 0; i < this->m_ttdTopLevelNewFunction.Count(); ++i)
  409. {
  410. if(this->m_ttdTopLevelNewFunction.Item(i).TopLevelBodyCtr == bodyCtrId)
  411. {
  412. return TTD_COERCE_PTR_ID_TO_FUNCTIONBODY(this->m_ttdTopLevelNewFunction.Item(i).ContextSpecificBodyPtrId);
  413. }
  414. }
  415. for(int32 i = 0; i < this->m_ttdTopLevelEval.Count(); ++i)
  416. {
  417. if(this->m_ttdTopLevelEval.Item(i).TopLevelBodyCtr == bodyCtrId)
  418. {
  419. return TTD_COERCE_PTR_ID_TO_FUNCTIONBODY(this->m_ttdTopLevelEval.Item(i).ContextSpecificBodyPtrId);
  420. }
  421. }
  422. //TTDAssert(false, "We are missing a top level body counter.");
  423. return nullptr;
  424. }
  425. void ScriptContextTTD::ClearLoadedSourcesForSnapshotRestore()
  426. {
  427. this->m_ttdTopLevelScriptLoad.Clear();
  428. this->m_ttdTopLevelNewFunction.Clear();
  429. this->m_ttdTopLevelEval.Clear();
  430. this->m_ttdPinnedRootFunctionSet->Clear();
  431. this->m_ttdFunctionBodyParentMap.Clear();
  432. }
  433. //////////////////
  434. void RuntimeContextInfo::BuildPathString(UtilSupport::TTAutoString rootpath, const char16* name, const char16* optaccessortag, UtilSupport::TTAutoString& into)
  435. {
  436. into.Append(rootpath);
  437. into.Append(_u("."));
  438. into.Append(name);
  439. if(optaccessortag != nullptr)
  440. {
  441. into.Append(optaccessortag);
  442. }
  443. }
  444. void RuntimeContextInfo::LoadAndOrderPropertyNames(Js::RecyclableObject* obj, JsUtil::List<const Js::PropertyRecord*, HeapAllocator>& propertyList)
  445. {
  446. TTDAssert(propertyList.Count() == 0, "This should be empty.");
  447. Js::ScriptContext* ctx = obj->GetScriptContext();
  448. uint32 propcount = (uint32)obj->GetPropertyCount();
  449. //get all of the properties
  450. for(uint32 i = 0; i < propcount; ++i)
  451. {
  452. Js::PropertyIndex propertyIndex = (Js::PropertyIndex)i;
  453. Js::PropertyId propertyId = obj->GetPropertyId(propertyIndex);
  454. if((propertyId != Js::Constants::NoProperty) & (!Js::IsInternalPropertyId(propertyId)))
  455. {
  456. TTDAssert(obj->HasOwnProperty(propertyId), "We are assuming this is own property count.");
  457. propertyList.Add(ctx->GetPropertyName(propertyId));
  458. }
  459. }
  460. //now sort the list so the traversal order is stable
  461. //Rock a custom shell sort!!!!
  462. const int32 gaps[6] = { 132, 57, 23, 10, 4, 1 };
  463. int32 llen = propertyList.Count();
  464. for(uint32 gapi = 0; gapi < 6; ++gapi)
  465. {
  466. int32 gap = gaps[gapi];
  467. for(int32 i = gap; i < llen; i++)
  468. {
  469. const Js::PropertyRecord* temp = propertyList.Item(i);
  470. int32 j = 0;
  471. for(j = i; j >= gap && PropertyNameCmp(propertyList.Item(j - gap), temp); j -= gap)
  472. {
  473. const Js::PropertyRecord* shiftElem = propertyList.Item(j - gap);
  474. propertyList.SetItem(j, shiftElem);
  475. }
  476. propertyList.SetItem(j, temp);
  477. }
  478. }
  479. }
  480. bool RuntimeContextInfo::PropertyNameCmp(const Js::PropertyRecord* p1, const Js::PropertyRecord* p2)
  481. {
  482. if(p1->GetLength() != p2->GetLength())
  483. {
  484. return p1->GetLength() > p2->GetLength();
  485. }
  486. else
  487. {
  488. return wcscmp(p1->GetBuffer(), p2->GetBuffer()) > 0;
  489. }
  490. }
  491. RuntimeContextInfo::RuntimeContextInfo()
  492. : m_worklist(&HeapAllocator::Instance), m_nullString(),
  493. m_coreObjToPathMap(&HeapAllocator::Instance, TTD_CORE_OBJECT_COUNT), m_coreBodyToPathMap(&HeapAllocator::Instance, TTD_CORE_FUNCTION_BODY_COUNT), m_coreDbgScopeToPathMap(&HeapAllocator::Instance, TTD_CORE_FUNCTION_BODY_COUNT),
  494. m_sortedObjectList(&HeapAllocator::Instance, TTD_CORE_OBJECT_COUNT), m_sortedFunctionBodyList(&HeapAllocator::Instance, TTD_CORE_FUNCTION_BODY_COUNT), m_sortedDbgScopeList(&HeapAllocator::Instance, TTD_CORE_FUNCTION_BODY_COUNT)
  495. {
  496. ;
  497. }
  498. RuntimeContextInfo::~RuntimeContextInfo()
  499. {
  500. for(auto iter = this->m_coreObjToPathMap.GetIterator(); iter.IsValid(); iter.MoveNext())
  501. {
  502. TT_HEAP_DELETE(UtilSupport::TTAutoString, iter.CurrentValue());
  503. }
  504. for(auto iter = this->m_coreBodyToPathMap.GetIterator(); iter.IsValid(); iter.MoveNext())
  505. {
  506. TT_HEAP_DELETE(UtilSupport::TTAutoString, iter.CurrentValue());
  507. }
  508. for(auto iter = this->m_coreDbgScopeToPathMap.GetIterator(); iter.IsValid(); iter.MoveNext())
  509. {
  510. TT_HEAP_DELETE(UtilSupport::TTAutoString, iter.CurrentValue());
  511. }
  512. }
  513. //Mark all the well-known objects/values/types from this script context
  514. void RuntimeContextInfo::MarkWellKnownObjects_TTD(MarkTable& marks) const
  515. {
  516. for(int32 i = 0; i < this->m_sortedObjectList.Count(); ++i)
  517. {
  518. Js::RecyclableObject* obj = this->m_sortedObjectList.Item(i);
  519. marks.MarkAddrWithSpecialInfo<MarkTableTag::JsWellKnownObj>(obj);
  520. }
  521. for(int32 i = 0; i < this->m_sortedFunctionBodyList.Count(); ++i)
  522. {
  523. Js::FunctionBody* body = this->m_sortedFunctionBodyList.Item(i);
  524. marks.MarkAddrWithSpecialInfo<MarkTableTag::JsWellKnownObj>(body);
  525. }
  526. }
  527. TTD_WELLKNOWN_TOKEN RuntimeContextInfo::ResolvePathForKnownObject(Js::RecyclableObject* obj) const
  528. {
  529. const UtilSupport::TTAutoString* res = this->m_coreObjToPathMap.Item(obj);
  530. return res->GetStrValue();
  531. }
  532. TTD_WELLKNOWN_TOKEN RuntimeContextInfo::ResolvePathForKnownFunctionBody(Js::FunctionBody* fbody) const
  533. {
  534. const UtilSupport::TTAutoString* res = this->m_coreBodyToPathMap.Item(fbody);
  535. return res->GetStrValue();
  536. }
  537. TTD_WELLKNOWN_TOKEN RuntimeContextInfo::ResolvePathForKnownDbgScopeIfExists(Js::DebuggerScope* dbgScope) const
  538. {
  539. const UtilSupport::TTAutoString* res = this->m_coreDbgScopeToPathMap.LookupWithKey(dbgScope, nullptr);
  540. if(res == nullptr)
  541. {
  542. return nullptr;
  543. }
  544. return res->GetStrValue();
  545. }
  546. Js::RecyclableObject* RuntimeContextInfo::LookupKnownObjectFromPath(TTD_WELLKNOWN_TOKEN pathIdString) const
  547. {
  548. int32 pos = LookupPositionInDictNameList<Js::RecyclableObject*, true>(pathIdString, this->m_coreObjToPathMap, this->m_sortedObjectList, this->m_nullString);
  549. TTDAssert(pos != -1, "This isn't a well known object!");
  550. return this->m_sortedObjectList.Item(pos);
  551. }
  552. Js::FunctionBody* RuntimeContextInfo::LookupKnownFunctionBodyFromPath(TTD_WELLKNOWN_TOKEN pathIdString) const
  553. {
  554. int32 pos = LookupPositionInDictNameList<Js::FunctionBody*, true>(pathIdString, this->m_coreBodyToPathMap, this->m_sortedFunctionBodyList, this->m_nullString);
  555. TTDAssert(pos != -1, "Missing function.");
  556. return (pos != -1) ? this->m_sortedFunctionBodyList.Item(pos) : nullptr;
  557. }
  558. Js::DebuggerScope* RuntimeContextInfo::LookupKnownDebuggerScopeFromPath(TTD_WELLKNOWN_TOKEN pathIdString) const
  559. {
  560. int32 pos = LookupPositionInDictNameList<Js::DebuggerScope*, true>(pathIdString, this->m_coreDbgScopeToPathMap, this->m_sortedDbgScopeList, this->m_nullString);
  561. TTDAssert(pos != -1, "Missing debug scope.");
  562. return (pos != -1) ? this->m_sortedDbgScopeList.Item(pos) : nullptr;
  563. }
  564. void RuntimeContextInfo::GatherKnownObjectToPathMap(Js::ScriptContext* ctx)
  565. {
  566. JsUtil::List<const Js::PropertyRecord*, HeapAllocator> propertyRecordList(&HeapAllocator::Instance);
  567. this->EnqueueRootPathObject(_u("global"), ctx->GetGlobalObject());
  568. this->EnqueueRootPathObject(_u("null"), ctx->GetLibrary()->GetNull());
  569. this->EnqueueRootPathObject(_u("_defaultAccessor"), ctx->GetLibrary()->GetDefaultAccessorFunction());
  570. if(ctx->GetConfig()->IsErrorStackTraceEnabled())
  571. {
  572. this->EnqueueRootPathObject(_u("_stackTraceAccessor"), ctx->GetLibrary()->GetStackTraceAccessorFunction());
  573. }
  574. this->EnqueueRootPathObject(_u("_throwTypeErrorRestrictedPropertyAccessor"), ctx->GetLibrary()->GetThrowTypeErrorRestrictedPropertyAccessorFunction());
  575. if(ctx->GetConfig()->IsES6PromiseEnabled())
  576. {
  577. this->EnqueueRootPathObject(_u("_identityFunction"), ctx->GetLibrary()->GetIdentityFunction());
  578. this->EnqueueRootPathObject(_u("_throwerFunction"), ctx->GetLibrary()->GetThrowerFunction());
  579. }
  580. this->EnqueueRootPathObject(_u("_arrayIteratorPrototype"), ctx->GetLibrary()->GetArrayIteratorPrototype());
  581. this->EnqueueRootPathObject(_u("_mapIteratorPrototype"), ctx->GetLibrary()->GetMapIteratorPrototype());
  582. this->EnqueueRootPathObject(_u("_setIteratorPrototype"), ctx->GetLibrary()->GetSetIteratorPrototype());
  583. this->EnqueueRootPathObject(_u("_stringIteratorPrototype"), ctx->GetLibrary()->GetStringIteratorPrototype());
  584. uint32 counter = 0;
  585. while(!this->m_worklist.Empty())
  586. {
  587. Js::RecyclableObject* curr = this->m_worklist.Dequeue();
  588. counter++;
  589. ////
  590. //Handle the standard properties for all object types
  591. //load propArray with all property names
  592. propertyRecordList.Clear();
  593. LoadAndOrderPropertyNames(curr, propertyRecordList);
  594. //access each property and process the target objects as needed
  595. for(int32 i = 0; i < propertyRecordList.Count(); ++i)
  596. {
  597. const Js::PropertyRecord* precord = propertyRecordList.Item(i);
  598. Js::Var getter = nullptr;
  599. Js::Var setter = nullptr;
  600. if(curr->GetAccessors(precord->GetPropertyId(), &getter, &setter, ctx))
  601. {
  602. if(getter != nullptr && !Js::JavascriptOperators::IsUndefinedObject(getter))
  603. {
  604. TTDAssert(Js::JavascriptFunction::Is(getter), "The getter is not a function?");
  605. this->EnqueueNewPathVarAsNeeded(curr, getter, precord, _u(">"));
  606. }
  607. if(setter != nullptr && !Js::JavascriptOperators::IsUndefinedObject(setter))
  608. {
  609. TTDAssert(Js::JavascriptFunction::Is(setter), "The setter is not a function?");
  610. this->EnqueueNewPathVarAsNeeded(curr, Js::RecyclableObject::FromVar(setter), precord, _u("<"));
  611. }
  612. }
  613. else
  614. {
  615. Js::Var pitem = nullptr;
  616. BOOL isproperty = Js::JavascriptOperators::GetOwnProperty(curr, precord->GetPropertyId(), &pitem, ctx);
  617. TTDAssert(isproperty, "Not sure what went wrong.");
  618. this->EnqueueNewPathVarAsNeeded(curr, pitem, precord, nullptr);
  619. }
  620. }
  621. //shouldn't have any dynamic array valued properties
  622. TTDAssert(!Js::DynamicType::Is(curr->GetTypeId()) || (Js::DynamicObject::FromVar(curr))->GetObjectArray() == nullptr || (Js::DynamicObject::FromVar(curr))->GetObjectArray()->GetLength() == 0, "Shouldn't have any dynamic array valued properties at this point.");
  623. Js::RecyclableObject* proto = curr->GetPrototype();
  624. bool skipProto = (proto == nullptr) || Js::JavascriptOperators::IsUndefinedOrNullType(proto->GetTypeId());
  625. if(!skipProto)
  626. {
  627. this->EnqueueNewPathVarAsNeeded(curr, proto, _u("_proto_"));
  628. }
  629. curr->ProcessCorePaths();
  630. }
  631. SortDictIntoListOnNames<Js::RecyclableObject*>(this->m_coreObjToPathMap, this->m_sortedObjectList, this->m_nullString);
  632. SortDictIntoListOnNames<Js::FunctionBody*>(this->m_coreBodyToPathMap, this->m_sortedFunctionBodyList, this->m_nullString);
  633. SortDictIntoListOnNames<Js::DebuggerScope*>(this->m_coreDbgScopeToPathMap, this->m_sortedDbgScopeList, this->m_nullString);
  634. }
  635. ////
  636. void RuntimeContextInfo::EnqueueRootPathObject(const char16* rootName, Js::RecyclableObject* obj)
  637. {
  638. this->m_worklist.Enqueue(obj);
  639. UtilSupport::TTAutoString* rootStr = TT_HEAP_NEW(UtilSupport::TTAutoString, rootName);
  640. TTDAssert(!this->m_coreObjToPathMap.ContainsKey(obj), "Already in map!!!");
  641. this->m_coreObjToPathMap.AddNew(obj, rootStr);
  642. }
  643. void RuntimeContextInfo::EnqueueNewPathVarAsNeeded(Js::RecyclableObject* parent, Js::Var val, const Js::PropertyRecord* prop, const char16* optacessortag)
  644. {
  645. this->EnqueueNewPathVarAsNeeded(parent, val, prop->GetBuffer(), optacessortag);
  646. }
  647. void RuntimeContextInfo::EnqueueNewPathVarAsNeeded(Js::RecyclableObject* parent, Js::Var val, const char16* propName, const char16* optacessortag)
  648. {
  649. if(JsSupport::IsVarTaggedInline(val))
  650. {
  651. return;
  652. }
  653. if(JsSupport::IsVarPrimitiveKind(val) && !Js::GlobalObject::Is(parent))
  654. {
  655. return; //we keep primitives from global object only -- may need others but this is a simple way to start to get undefined, null, infy, etc.
  656. }
  657. Js::RecyclableObject* obj = Js::RecyclableObject::FromVar(val);
  658. if(!this->m_coreObjToPathMap.ContainsKey(obj))
  659. {
  660. const UtilSupport::TTAutoString* ppath = this->m_coreObjToPathMap.Item(parent);
  661. this->m_worklist.Enqueue(obj);
  662. UtilSupport::TTAutoString* tpath = TT_HEAP_NEW(UtilSupport::TTAutoString, *ppath);
  663. tpath->Append(_u("."));
  664. tpath->Append(propName);
  665. if(optacessortag != nullptr)
  666. {
  667. tpath->Append(optacessortag);
  668. }
  669. TTDAssert(!this->m_coreObjToPathMap.ContainsKey(obj), "Already in map!!!");
  670. this->m_coreObjToPathMap.AddNew(obj, tpath);
  671. }
  672. }
  673. void RuntimeContextInfo::EnqueueNewFunctionBodyObject(Js::RecyclableObject* parent, Js::FunctionBody* fbody, const char16* name)
  674. {
  675. if(!this->m_coreBodyToPathMap.ContainsKey(fbody))
  676. {
  677. fbody->EnsureDeserialized();
  678. const UtilSupport::TTAutoString* ppath = this->m_coreObjToPathMap.Item(parent);
  679. UtilSupport::TTAutoString* fpath = TT_HEAP_NEW(UtilSupport::TTAutoString, *ppath);
  680. fpath->Append(_u("."));
  681. fpath->Append(name);
  682. this->m_coreBodyToPathMap.AddNew(fbody, fpath);
  683. }
  684. }
  685. void RuntimeContextInfo::AddWellKnownDebuggerScopePath(Js::RecyclableObject* parent, Js::DebuggerScope* dbgScope, uint32 index)
  686. {
  687. if(!this->m_coreDbgScopeToPathMap.ContainsKey(dbgScope))
  688. {
  689. const UtilSupport::TTAutoString* ppath = this->m_coreObjToPathMap.Item(parent);
  690. UtilSupport::TTAutoString* scpath = TT_HEAP_NEW(UtilSupport::TTAutoString, *ppath);
  691. scpath->Append(_u(".!scope["));
  692. scpath->Append(index);
  693. scpath->Append(_u("]"));
  694. this->m_coreDbgScopeToPathMap.AddNew(dbgScope, scpath);
  695. }
  696. }
  697. void RuntimeContextInfo::BuildArrayIndexBuffer(uint32 arrayidx, UtilSupport::TTAutoString& res)
  698. {
  699. res.Append(_u("!arrayContents["));
  700. res.Append(arrayidx);
  701. res.Append(_u("]"));
  702. }
  703. void RuntimeContextInfo::BuildEnvironmentIndexBuffer(uint32 envidx, UtilSupport::TTAutoString& res)
  704. {
  705. res.Append(_u("!env["));
  706. res.Append(envidx);
  707. res.Append(_u("]"));
  708. }
  709. void RuntimeContextInfo::BuildEnvironmentIndexAndSlotBuffer(uint32 envidx, uint32 slotidx, UtilSupport::TTAutoString& res)
  710. {
  711. res.Append(_u("!env["));
  712. res.Append(envidx);
  713. res.Append(_u("].!slot["));
  714. res.Append(slotidx);
  715. res.Append(_u("]"));
  716. }
  717. }
  718. #endif