blob: e271048d620527fc7f0b7423545e8e620e8a27e3 [file]
//-------------------------------------------------------------------------------------------------------
// 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
#if ENABLE_TTD
#define TTD_EVENTLOG_LIST_BLOCK_SIZE 65536
namespace TTD
{
//A class to ensure that even when exceptions are thrown we increment/decrement the root nesting depth
class TTDNestingDepthAutoAdjuster
{
private:
ThreadContext* m_threadContext;
public:
TTDNestingDepthAutoAdjuster(ThreadContext* threadContext)
: m_threadContext(threadContext)
{
this->m_threadContext->TTDRootNestingCount++;
}
~TTDNestingDepthAutoAdjuster()
{
this->m_threadContext->TTDRootNestingCount--;
}
};
//A class to manage the recording of JsRT action call state
class TTDJsRTActionResultAutoRecorder
{
private:
NSLogEvents::EventLogEntry* m_actionEvent;
TTDVar* m_resultPtr;
public:
TTDJsRTActionResultAutoRecorder()
: m_actionEvent(nullptr), m_resultPtr(nullptr)
{
;
}
void InitializeWithEventAndEnter(NSLogEvents::EventLogEntry* actionEvent)
{
TTDAssert(this->m_actionEvent == nullptr, "Don't double initialize");
this->m_actionEvent = actionEvent;
}
void InitializeWithEventAndEnterWResult(NSLogEvents::EventLogEntry* actionEvent, TTDVar* resultPtr)
{
TTDAssert(this->m_actionEvent == nullptr, "Don't double initialize");
this->m_actionEvent = actionEvent;
this->m_resultPtr = resultPtr;
*(this->m_resultPtr) = (TTDVar)nullptr; //set the result field to a default value in case we fail during execution
}
void SetResult(Js::Var* result)
{
TTDAssert(this->m_resultPtr != nullptr, "Why are we calling this then???");
if(result != nullptr)
{
*(this->m_resultPtr) = TTD_CONVERT_JSVAR_TO_TTDVAR(*(result));
}
}
void CompleteWithStatusCode(int32 exitStatus)
{
if(this->m_actionEvent != nullptr)
{
TTDAssert(this->m_actionEvent->ResultStatus == -1, "Hmm this got changed somewhere???");
this->m_actionEvent->ResultStatus = exitStatus;
}
}
};
//A class to ensure that even when exceptions are thrown we record the time difference info
class TTDJsRTFunctionCallActionPopperRecorder
{
private:
Js::ScriptContext* m_ctx;
double m_beginTime;
NSLogEvents::EventLogEntry* m_callAction;
public:
TTDJsRTFunctionCallActionPopperRecorder();
~TTDJsRTFunctionCallActionPopperRecorder();
void InitializeForRecording(Js::ScriptContext* ctx, double beginWallTime, NSLogEvents::EventLogEntry* callAction);
};
//A list class for the events that we accumulate in the event log
class TTEventList
{
public:
struct TTEventListLink
{
//The current end of the allocated data in the block
size_t CurrPos;
//The First index that holds data
size_t StartPos;
//The actual block for the data
byte* BlockData;
//The next block in the list
TTEventListLink* Next;
TTEventListLink* Previous;
};
private:
//The the data in this
TTEventListLink* m_headBlock;
//the allocators we use for this work
UnlinkableSlabAllocator* m_alloc;
//The vtable with callbacks for the event log entries in the list
const NSLogEvents::EventLogEntryVTableEntry* m_vtable;
//Map from event entries to previous event entries -- only valid in replay mode otherwise empty
JsUtil::BaseDictionary<const NSLogEvents::EventLogEntry*, size_t, HeapAllocator> m_previousEventMap;
void AddArrayLink();
void RemoveArrayLink(TTEventListLink* block);
public:
TTEventList(UnlinkableSlabAllocator* alloc);
void SetVTable(const NSLogEvents::EventLogEntryVTableEntry* vtable);
void InitializePreviousEventMap();
void UnloadEventList();
//Add the entry to the list
template <typename T>
NSLogEvents::EventLogEntry* GetNextAvailableEntry()
{
const size_t esize = TTD_EVENT_PLUS_DATA_SIZE(T);
if((this->m_headBlock == nullptr) || (this->m_headBlock->CurrPos + esize >= TTD_EVENTLOG_LIST_BLOCK_SIZE))
{
this->AddArrayLink();
}
NSLogEvents::EventLogEntry* entry = reinterpret_cast<NSLogEvents::EventLogEntry*>(this->m_headBlock->BlockData + this->m_headBlock->CurrPos);
this->m_headBlock->CurrPos += esize;
return entry;
}
//Add the entry to the list
NSLogEvents::EventLogEntry* GetNextAvailableEntry(size_t requiredSize);
//Delete the entry from the list (must always be the first link/entry in the list)
//This also calls unload on the entry
void DeleteFirstEntry(TTEventListLink* block, NSLogEvents::EventLogEntry* data);
//Return true if this is empty
bool IsEmpty() const;
//NOT constant time
uint32 Count() const;
class Iterator
{
private:
TTEventListLink* m_currLink;
size_t m_currIdx;
const NSLogEvents::EventLogEntryVTableEntry* m_vtable;
const JsUtil::BaseDictionary<const NSLogEvents::EventLogEntry*, size_t, HeapAllocator>* m_previousEventMap;
public:
Iterator();
Iterator(TTEventListLink* head, size_t pos, const NSLogEvents::EventLogEntryVTableEntry* vtable, const JsUtil::BaseDictionary<const NSLogEvents::EventLogEntry*, size_t, HeapAllocator>* previousEventMap);
const NSLogEvents::EventLogEntry* Current() const;
NSLogEvents::EventLogEntry* Current();
//Get the underlying block for deletion support
TTEventListLink* GetBlock();
bool IsValid() const;
void MoveNext();
void MovePrevious_ReplayOnly();
};
Iterator GetIteratorAtFirst() const;
Iterator GetIteratorAtLast_ReplayOnly() const;
};
//A class that represents the event log for the program execution
class EventLog
{
private:
ThreadContext* m_threadContext;
//Allocator we use for all the events we see
UnlinkableSlabAllocator m_eventSlabAllocator;
//Allocator we use for all the property records
SlabAllocator m_miscSlabAllocator;
//The global event time variable and a high res timer we can use to extract some diagnostic timing info as we go
int64 m_eventTimeCtr;
//A high res timer we can use to extract some diagnostic timing info as we go
TTDTimer m_timer;
//Top-Level callback event time (or -1 if we are not in a callback)
int64 m_topLevelCallbackEventTime;
//The list of all the events and the iterator we use during replay
NSLogEvents::EventLogEntryVTableEntry* m_eventListVTable;
TTEventList m_eventList;
TTEventList::Iterator m_currentReplayEventIterator;
//The current mode the system is running in (and a stack of mode push/pops that we use to generate it)
TTModeStack m_modeStack;
TTDMode m_currentMode;
//The snapshot extractor that this log uses
SnapshotExtractor m_snapExtractor;
//The execution time that has elapsed since the last snapshot
double m_elapsedExecutionTimeSinceSnapshot;
//If we are inflating a snapshot multiple times we want to re-use the inflated objects when possible so keep this recent info
int64 m_lastInflateSnapshotTime;
InflateMap* m_lastInflateMap;
//Pin set of all property records created during this logging session
RecyclerRootPtr<PropertyRecordPinSet> m_propertyRecordPinSet;
UnorderedArrayList<NSSnapType::SnapPropertyRecord, TTD_ARRAY_LIST_SIZE_DEFAULT> m_propertyRecordList;
//The value of the threadContext sourceInfoCounter in record -- in replay initialize to this value to avoid collisions
uint32 m_sourceInfoCount;
//A list of all *root* scripts that have been loaded during this session
UnorderedArrayList<NSSnapValues::TopLevelScriptLoadFunctionBodyResolveInfo, TTD_ARRAY_LIST_SIZE_MID> m_loadedTopLevelScripts;
UnorderedArrayList<NSSnapValues::TopLevelNewFunctionBodyResolveInfo, TTD_ARRAY_LIST_SIZE_SMALL> m_newFunctionTopLevelScripts;
UnorderedArrayList<NSSnapValues::TopLevelEvalFunctionBodyResolveInfo, TTD_ARRAY_LIST_SIZE_SMALL> m_evalTopLevelScripts;
////
//Helper methods
//Get the current XTTDEventTime and advance the event time counter
int64 GetCurrentEventTimeAndAdvance()
{
return this->m_eventTimeCtr++;
}
//Advance the time and event position for replay
void AdvanceTimeAndPositionForReplay();
//Look at the stack to get the new computed mode
void UpdateComputedMode();
//A helper for extracting snapshots
SnapShot* DoSnapshotExtract_Helper(double gcTime);
//Replay a snapshot event -- either just advance the event position or, if running diagnostics, take new snapshot and compare
void ReplaySnapshotEvent();
//Replay an event loop yield point event
void ReplayEventLoopYieldPointEvent();
template <typename T, NSLogEvents::EventKind tag>
NSLogEvents::EventLogEntry* RecordGetInitializedEvent(T** extraData)
{
AssertMsg(TTD_EVENT_PLUS_DATA_SIZE_DIRECT(sizeof(T)) == this->m_eventListVTable[(uint32)tag].DataSize, "Computed and extracted data sizes dont' match!!!");
NSLogEvents::EventLogEntry* res = this->m_eventList.GetNextAvailableEntry<T>();
NSLogEvents::EventLogEntry_Initialize<tag>(res, this->GetCurrentEventTimeAndAdvance());
*extraData = NSLogEvents::GetInlineEventDataAs<T, tag>(res);
return res;
}
template <typename T, NSLogEvents::EventKind tag>
T* RecordGetInitializedEvent_DataOnly()
{
AssertMsg(TTD_EVENT_PLUS_DATA_SIZE_DIRECT(sizeof(T)) == this->m_eventListVTable[(uint32)tag].DataSize, "Computed and extracted data sizes dont' match!!!");
NSLogEvents::EventLogEntry* res = this->m_eventList.GetNextAvailableEntry<T>();
NSLogEvents::EventLogEntry_Initialize<tag>(res, this->GetCurrentEventTimeAndAdvance());
//For these operations are not allowed to fail so success is always 0
res->ResultStatus = 0;
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
// (1) If we are trying to replay something and we are at the end of the log then we need to terminate
// (2) If we are at a breakpoint and we want to step back (in some form) then we need to terminate
void AbortReplayReturnToHost();
//A helper for getting and doing some iterator manipulation during replay
template <typename T, NSLogEvents::EventKind tag>
const T* ReplayGetReplayEvent_Helper()
{
if(!this->m_currentReplayEventIterator.IsValid())
{
this->AbortReplayReturnToHost();
}
#if ENABLE_TTD_INTERNAL_DIAGNOSTICS
TTDAssert(this->m_currentReplayEventIterator.Current()->EventTimeStamp == this->m_eventTimeCtr, "Out of Sync!!!");
#endif
const NSLogEvents::EventLogEntry* evt = this->m_currentReplayEventIterator.Current();
this->AdvanceTimeAndPositionForReplay();
return NSLogEvents::GetInlineEventDataAs<T, tag>(evt);
}
//Initialize the vtable for the event list data
void InitializeEventListVTable();
public:
EventLog(ThreadContext* threadContext);
~EventLog();
//When we stop recording we want to unload all of the data in the log (otherwise we get strange transitions if we start again later)
void UnloadAllLogData();
//Initialize the log so that it is ready to perform TTD (record or replay) and set into the correct global mode
void InitForTTDRecord();
void InitForTTDReplay(TTDataIOInfo& iofp, const char* parseUri, size_t parseUriLength, bool debug);
//reset the bottom (global) mode with the specific value
void SetGlobalMode(TTDMode m);
//Mark that a snapshot is in (or or is now complete)
void SetSnapshotOrInflateInProgress(bool flag);
//push a new debugger mode
void PushMode(TTDMode m);
//pop the top debugger mode
void PopMode(TTDMode m);
//Get the current mode for TTD execution
TTDMode GetCurrentTTDMode() const;
//Set the mode flags on the script context based on the TTDMode in the Log
void SetModeFlagsOnContext(Js::ScriptContext* ctx);
//Get the global mode flags for creating a script context
void GetModesForExplicitContextCreate(bool& inRecord, bool& activelyRecording, bool& inReplay);
//Just check if the debug mode flag has been set (don't check any active or suppressed properties)
bool IsDebugModeFlagSet() const;
//A special check for to see if we want to push the supression flag for getter exection
bool ShouldDoGetterInvocationSupression() const;
//Add a property record to our pin set
void AddPropertyRecord(const Js::PropertyRecord* record);
//Add top level function load info to our sets
const NSSnapValues::TopLevelScriptLoadFunctionBodyResolveInfo* AddScriptLoad(Js::FunctionBody* fb, Js::ModuleID moduleId, uint64 sourceContextId, const byte* source, uint32 sourceLen, LoadScriptFlag loadFlag);
const NSSnapValues::TopLevelNewFunctionBodyResolveInfo* AddNewFunction(Js::FunctionBody* fb, Js::ModuleID moduleId, const char16* source, uint32 sourceLen);
const NSSnapValues::TopLevelEvalFunctionBodyResolveInfo* AddEvalFunction(Js::FunctionBody* fb, Js::ModuleID moduleId, const char16* source, uint32 sourceLen, uint32 grfscr, bool registerDocument, BOOL isIndirect, BOOL strictMode);
uint32 GetSourceInfoCount() const;
void RecordTopLevelCodeAction(uint32 bodyCtrId);
uint32 ReplayTopLevelCodeAction();
////////////////////////////////
//Logging support
//Log an event generated by user telemetry
void RecordTelemetryLogEvent(Js::JavascriptString* infoStringJs, bool doPrint);
//Replay a user telemetry event
void ReplayTelemetryLogEvent(Js::JavascriptString* infoStringJs);
//Log an event generated to write the log to a given uri
void RecordEmitLogEvent(Js::JavascriptString* uriString);
//Replay a event that writes the log to a given uri
void ReplayEmitLogEvent();
//Log a time that is fetched during date operations
void RecordDateTimeEvent(double time);
//Log a time (as a string) that is fetched during date operations
void RecordDateStringEvent(Js::JavascriptString* stringValue);
//replay date event (which should be the current event)
void ReplayDateTimeEvent(double* result);
//replay date event with a string result (which should be the current event)
void ReplayDateStringEvent(Js::ScriptContext* ctx, Js::JavascriptString** result);
//Log a random seed value that is being generated using external entropy
void RecordExternalEntropyRandomEvent(uint64 seed0, uint64 seed1);
//Replay a random seed value that is being generated using external entropy
void ReplayExternalEntropyRandomEvent(uint64* seed0, uint64* seed1);
//Log property enumeration step
void RecordPropertyEnumEvent(BOOL returnCode, Js::PropertyId pid, Js::PropertyAttributes attributes, Js::JavascriptString* propertyName);
//Replay a property enumeration step
void ReplayPropertyEnumEvent(Js::ScriptContext* requestContext, BOOL* returnCode, Js::BigPropertyIndex* newIndex, const Js::DynamicObject* obj, Js::PropertyId* pid, Js::PropertyAttributes* attributes, Js::JavascriptString** propertyName);
//Log symbol creation
void RecordSymbolCreationEvent(Js::PropertyId pid);
//Replay symbol creation
void ReplaySymbolCreationEvent(Js::PropertyId* pid);
//Log if a weak collection contained a value when an operation occours
void RecordWeakCollectionContainsEvent(bool contains);
//Replay a weak collection contained a value when an operation occours (return the truth value)
bool ReplayWeakCollectionContainsEvent();
//Log a value event for return from an external call
NSLogEvents::EventLogEntry* RecordExternalCallEvent(Js::JavascriptFunction* func, int32 rootDepth, uint32 argc, Js::Var* argv, bool checkExceptions);
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);
NSLogEvents::EventLogEntry* RecordEnqueueTaskEvent(Js::Var taskVar);
void RecordEnqueueTaskEvent_Complete(NSLogEvents::EventLogEntry* evt);
void ReplayEnqueueTaskEvent(Js::ScriptContext* ctx, Js::Var taskVar);
//Get the current top-level event time
int64 GetCurrentTopLevelEventTime() const;
//Get the event time corresponding to the first/last/k-th top-level event in the log
int64 GetFirstEventTimeInLog() const;
int64 GetLastEventTimeInLog() const;
int64 GetKthEventTimeInLog(uint32 k) const;
//Ensure the call stack is clear and counters are zeroed appropriately
void ResetCallStackForTopLevelCall(int64 topLevelCallbackEventTime);
//Check if we want to take a snapshot
bool IsTimeForSnapshot() const;
//After a snapshot we may want to discard old events so do that in here as needed
void PruneLogLength();
//Get/Increment the elapsed time since the last snapshot
void IncrementElapsedSnapshotTime(double addtlTime);
////////////////////////////////
//Snapshot and replay support
//Do the snapshot extraction
void DoSnapshotExtract();
//Take a ready-to-run snapshot for the event if needed
void DoRtrSnapIfNeeded();
//Find the event time that has the snapshot we want to inflate from in order to replay to the requested target time
//Return -1 if no such snapshot is available
int64 FindSnapTimeForEventTime(int64 targetTime, int64* optEndSnapTime);
//Find the enclosing snapshot interval for the specified event time
void GetSnapShotBoundInterval(int64 targetTime, int64* snapIntervalStart, int64* snapIntervalEnd) const;
//Find the snapshot start time for the previous interval return -1 if no such time exists
int64 GetPreviousSnapshotInterval(int64 currentSnapTime) const;
//Do the inflation of the snapshot that is at the given event time
void DoSnapshotInflate(int64 etime);
//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();
//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);
//Replay the enter/exit and any iteration need to discharge all the effects of a single ActionEvent
void ReplaySingleActionEventEntry();
////////////////////////////////
//Host API record & replay support
//Return true if this is a propertyRecord reference
bool IsPropertyRecordRef(void* ref) const;
//Get the current time from the hi res timer
double GetCurrentWallTime();
//Get the most recently assigned event time value
int64 GetLastEventTime() const;
NSLogEvents::EventLogEntry* RecordJsRTCreateScriptContext(TTDJsRTActionResultAutoRecorder& actionPopper);
void RecordJsRTCreateScriptContextResult(NSLogEvents::EventLogEntry* evt, Js::ScriptContext* newCtx);
void RecordJsRTSetCurrentContext(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var globalObject);
#if !INT32VAR
void RecordJsRTCreateInteger(TTDJsRTActionResultAutoRecorder& actionPopper, int value);
#endif
//Record creation operations
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(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(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(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var var);
void RecordJsRTAddWeakRootRef(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var var);
void RecordJsRTEventLoopYieldPoint();
//Record object allocate operations
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(TTDJsRTActionResultAutoRecorder& actionPopper, int32 exitCode);
void RecordJsRTGetAndClearExceptionWithMetadata(TTDJsRTActionResultAutoRecorder& actionPopper);
void RecordJsRTGetAndClearException(TTDJsRTActionResultAutoRecorder& actionPopper);
void RecordJsRTSetException(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var var, bool propagateToDebugger);
//Record query operations
void RecordJsRTHasProperty(TTDJsRTActionResultAutoRecorder& actionPopper, const Js::PropertyRecord* pRecord, Js::Var var);
void RecordJsRTInstanceOf(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var object, Js::Var constructor);
void RecordJsRTEquals(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var var1, Js::Var var2, bool doStrict);
//Record getters with native results
void RecordJsRTGetPropertyIdFromSymbol(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var sym);
//Record Object Getters
void RecordJsRTGetPrototype(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var var);
void RecordJsRTGetProperty(TTDJsRTActionResultAutoRecorder& actionPopper, const Js::PropertyRecord* pRecord, Js::Var var);
void RecordJsRTGetIndex(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var index, Js::Var var);
void RecordJsRTGetOwnPropertyInfo(TTDJsRTActionResultAutoRecorder& actionPopper, const Js::PropertyRecord* pRecord, Js::Var var);
void RecordJsRTGetOwnPropertyNamesInfo(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var var);
void RecordJsRTGetOwnPropertySymbolsInfo(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var var);
//Record Object Setters
void RecordJsRTDefineProperty(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var var, const Js::PropertyRecord* pRecord, Js::Var propertyDescriptor);
void RecordJsRTDeleteProperty(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var var, const Js::PropertyRecord* pRecord, bool useStrictRules);
void RecordJsRTSetPrototype(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var var, Js::Var proto);
void RecordJsRTSetProperty(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var var, const Js::PropertyRecord* pRecord, 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::Var var, Js::Var result);
//Record various raw byte* from ArrayBuffer manipulations
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);
void RecordJsRTRawBufferAsyncModificationRegister(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var dst, uint32 index);
void RecordJsRTRawBufferAsyncModifyComplete(TTDJsRTActionResultAutoRecorder& actionPopper, TTDPendingAsyncBufferModification& pendingAsyncInfo, byte* finalModPos);
//Record a constructor call from JsRT
void RecordJsRTConstructCall(TTDJsRTActionResultAutoRecorder& actionPopper, Js::Var funcVar, uint32 argCount, Js::Var* args);
//Record code parse
NSLogEvents::EventLogEntry* RecordJsRTCodeParse(TTDJsRTActionResultAutoRecorder& actionPopper, LoadScriptFlag loadFlag, bool isUft8, const byte* script, uint32 scriptByteLength, uint64 sourceContextId, const char16* sourceUri);
//Record callback of an existing function
NSLogEvents::EventLogEntry* RecordJsRTCallFunction(TTDJsRTActionResultAutoRecorder& actionPopper, int32 rootDepth, Js::Var funcVar, uint32 argCount, Js::Var* args);
////////////////////////////////
//Emit code and support
void EmitLog(const char* emitUri, size_t emitUriLength);
void ParseLogInto(TTDataIOInfo& iofp, const char* parseUri, size_t parseUriLength);
};
//In cases where we may have many exits where we need to pop something we pushed earlier (i.e. exceptions)
class TTModeStackAutoPopper
{
private:
EventLog* m_log;
TTDMode m_popMode; //the mode to pop or invalid if we don't need to pop anything
public:
TTModeStackAutoPopper(EventLog* log)
: m_log(log), m_popMode(TTDMode::Invalid)
{
;
}
void PushModeAndSetToAutoPop(TTDMode mode)
{
this->m_log->PushMode(mode);
this->m_popMode = mode;
}
~TTModeStackAutoPopper()
{
if(this->m_popMode != TTDMode::Invalid)
{
this->m_log->PopMode(this->m_popMode);
}
}
};
}
#endif