void CodeGenWorkItem::OnAddToJitQueue() { Assert(!this->isInJitQueue); this->isInJitQueue = true; VerifyJitMode(); this->entryPointInfo->SetCodeGenQueued(); if(IS_JS_ETW(EventEnabledJSCRIPT_FUNCTION_JIT_QUEUED())) { WCHAR displayNameBuffer[256]; WCHAR* displayName = displayNameBuffer; size_t sizeInChars = this->GetDisplayName(displayName, 256); if(sizeInChars > 256) { displayName = HeapNewArray(WCHAR, sizeInChars); this->GetDisplayName(displayName, 256); } JS_ETW(EventWriteJSCRIPT_FUNCTION_JIT_QUEUED( this->GetFunctionNumber(), displayName, this->GetScriptContext(), this->GetInterpretedCount())); if(displayName != displayNameBuffer) { HeapDeleteArray(sizeInChars, displayName); } } }
SnapShot* SnapShot::Parse(LPCWSTR sourceDir, DWORD snapId, ThreadContext* threadContext) { wchar* snapIdString = HeapNewArrayZ(wchar, 64); swprintf_s(snapIdString, 64, _u("%u"), snapId); HANDLE snapHandle = threadContext->TTDStreamFunctions.pfGetSnapshotStream(sourceDir, snapIdString, true, false); TTD_SNAP_READER snapreader(snapHandle, TTD_COMPRESSED_OUTPUT, threadContext->TTDStreamFunctions.pfReadBytesFromStream, threadContext->TTDStreamFunctions.pfFlushAndCloseStream); SnapShot* snap = SnapShot::ParseSnapshotFromFile(&snapreader); HeapDeleteArray(64, snapIdString); return snap; }
void SnapShot::EmitSnapshot(LPCWSTR sourceDir, DWORD snapId, ThreadContext* threadContext) const { wchar* snapIdString = HeapNewArrayZ(wchar, 64); swprintf_s(snapIdString, 64, _u("%u"), snapId); HANDLE snapHandle = threadContext->TTDStreamFunctions.pfGetSnapshotStream(sourceDir, snapIdString, false, true); TTD_SNAP_WRITER snapwriter(snapHandle, TTD_COMPRESSED_OUTPUT, threadContext->TTDStreamFunctions.pfWriteBytesToStream, threadContext->TTDStreamFunctions.pfFlushAndCloseStream); this->EmitSnapshotToFile(&snapwriter, threadContext); snapwriter.FlushAndClose(); HeapDeleteArray(64, snapIdString); }
BackgroundJobProcessor::~BackgroundJobProcessor() { // This should appear to be called from the same thread from which this instance was created Assert(IsClosed()); if (parallelThreadData) { for (unsigned int i = 0; i < this->threadCount; i++) { HeapDelete(parallelThreadData[i]); } HeapDeleteArray(this->maxThreadCount, parallelThreadData); } }
// // Log loop body load event to VTune // void VTuneChakraProfile::LogLoopBodyLoadEvent(Js::FunctionBody* body, Js::LoopHeader* loopHeader, Js::LoopEntryPointInfo* entryPoint, uint16 loopNumber) { #if ENABLE_NATIVE_CODEGEN if (isJitProfilingActive) { iJIT_Method_Load methodInfo; memset(&methodInfo, 0, sizeof(iJIT_Method_Load)); const char16* methodName = body->GetExternalDisplayName(); size_t methodLength = wcslen(methodName); methodLength = min(methodLength, (size_t)UINT_MAX); // Just truncate if it is too big size_t length = methodLength * 3 + /* spaces */ 2 + _countof(LoopStr) + /*size of loop number*/ 10 + /*NULL*/ 1; utf8char_t* utf8MethodName = HeapNewNoThrowArray(utf8char_t, length); if(utf8MethodName) { methodInfo.method_id = iJIT_GetNewMethodID(); size_t len = utf8::EncodeInto(utf8MethodName, methodName, (charcount_t)methodLength); sprintf_s((char*)(utf8MethodName + len), length - len," %s %d", LoopStr, loopNumber + 1); methodInfo.method_name = (char*)utf8MethodName; methodInfo.method_load_address = (void*)entryPoint->GetNativeAddress(); methodInfo.method_size = (uint)entryPoint->GetCodeSize(); // Size in memory - Must be exact size_t urlLength = 0; utf8char_t* utf8Url = GetUrl(body, &urlLength); methodInfo.source_file_name = (char*)utf8Url; iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, &methodInfo); OUTPUT_TRACE(Js::ProfilerPhase, _u("Loop body load event: %s Loop %d\n"), methodName, loopNumber + 1); if(urlLength > 0) { HeapDeleteArray(urlLength, utf8Url); } HeapDeleteArray(length, utf8MethodName); } } #endif }
void CodeGenWorkItem::OnRemoveFromJitQueue(NativeCodeGenerator* generator) { // This is called from within the lock this->isInJitQueue = false; this->entryPointInfo->SetCodeGenPending(); functionBody->GetScriptContext()->GetThreadContext()->UnregisterCodeGenRecyclableData(this->recyclableData); this->recyclableData = nullptr; if(IS_JS_ETW(EventEnabledJSCRIPT_FUNCTION_JIT_DEQUEUED())) { WCHAR displayNameBuffer[256]; WCHAR* displayName = displayNameBuffer; size_t sizeInChars = this->GetDisplayName(displayName, 256); if(sizeInChars > 256) { displayName = HeapNewArray(WCHAR, sizeInChars); this->GetDisplayName(displayName, 256); } JS_ETW(EventWriteJSCRIPT_FUNCTION_JIT_DEQUEUED( this->GetFunctionNumber(), displayName, this->GetScriptContext(), this->GetInterpretedCount())); if(displayName != displayNameBuffer) { HeapDeleteArray(sizeInChars, displayName); } } if(this->Type() == JsLoopBodyWorkItemType) { // Go ahead and delete it and let it re-queue if more interpreting of the loop happens auto loopBodyWorkItem = static_cast<JsLoopBodyCodeGen*>(this); loopBodyWorkItem->loopHeader->ResetInterpreterCount(); loopBodyWorkItem->GetEntryPoint()->Reset(); HeapDelete(loopBodyWorkItem); } else { Assert(GetJitMode() == ExecutionMode::FullJit); // simple JIT work items are not removed from the queue GetFunctionBody()->OnFullJitDequeued(static_cast<Js::FunctionEntryPointInfo *>(GetEntryPoint())); // Add it back to the list of available functions to be jitted generator->AddWorkItem(this); } }
void SharedArrayBuffer::FreeBuffer(BYTE* buffer, uint32 length, uint32 maxLength) { Unused(maxLength); // WebAssembly only #if ENABLE_FAST_ARRAYBUFFER //AsmJS Virtual Free if (this->IsValidVirtualBufferLength(length)) { FreeMemAlloc(buffer); } else #endif { HeapDeleteArray(length, buffer); } }
void WebAssemblySharedArrayBuffer::FreeBuffer(BYTE* buffer, uint32 length, uint32 maxLength) { if (IsValidVirtualBufferLength(length)) { FreeMemAlloc(buffer); } else { HeapDeleteArray(maxLength, buffer); AssertOrFailFast(maxLength >= length); // JavascriptSharedArrayBuffer::Finalize will only report freeing `length`, we have to take care of the balance uint32 additionalSize = maxLength - length; if (additionalSize > 0) { Recycler* recycler = GetType()->GetLibrary()->GetRecycler(); recycler->ReportExternalMemoryFree(additionalSize); } } }
void BackgroundJobProcessor::InitializeParallelThreadDataForThreadServiceCallBack(AllocationPolicyManager* policyManager) { //thread is provided by service callback, no need to create thread here. Currently only one thread in service callback supported. this->maxThreadCount = 1; this->parallelThreadData = HeapNewArrayZ(ParallelThreadData *, this->maxThreadCount); this->parallelThreadData[0] = HeapNewNoThrow(ParallelThreadData, policyManager); if (this->parallelThreadData[0] == nullptr) { HeapDeleteArray(this->maxThreadCount, this->parallelThreadData); Js::Throw::OutOfMemory(); } this->parallelThreadData[0]->processor = this; this->parallelThreadData[0]->isWaitingForJobs = true; #if DBG_DUMP this->parallelThreadData[0]->backgroundPageAllocator.debugName = _u("BackgroundJobProcessor"); #endif this->threadCount = 1; return; }
void JavascriptExternalFunction::PrepareExternalCall(Js::Arguments * args) { ScriptContext * scriptContext = this->type->GetScriptContext(); Assert(!scriptContext->GetThreadContext()->IsDisableImplicitException()); scriptContext->VerifyAlive(); Assert(scriptContext->GetThreadContext()->IsScriptActive()); if (args->Info.Count == 0) { JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined); } Var &thisVar = args->Values[0]; Js::TypeId typeId = Js::JavascriptOperators::GetTypeId(thisVar); this->callCount++; if (IS_JS_ETW(EventEnabledJSCRIPT_HOSTING_EXTERNAL_FUNCTION_CALL_START())) { JavascriptFunction* caller = nullptr; // Lot the caller function if the call count of the external function pass certain threshold (randomly pick 256) // we don't want to call stackwalk too often. The threshold can be adjusted as needed. if (callCount >= ETW_MIN_COUNT_FOR_CALLER && ((callCount % ETW_MIN_COUNT_FOR_CALLER) == 0)) { Js::JavascriptStackWalker stackWalker(scriptContext); bool foundScriptCaller = false; while(stackWalker.GetCaller(&caller)) { if(caller != nullptr && Js::ScriptFunction::Is(caller)) { foundScriptCaller = true; break; } } if(foundScriptCaller) { Var sourceString = caller->EnsureSourceString(); Assert(JavascriptString::Is(sourceString)); const char16* callerString = Js::JavascriptString::FromVar(sourceString)->GetSz(); char16* outString = (char16*)callerString; int length = 0; if (wcschr(callerString, _u('\n')) != NULL || wcschr(callerString, _u('\n')) != NULL) { length = Js::JavascriptString::FromVar(sourceString)->GetLength(); outString = HeapNewArray(char16, length+1); int j = 0; for (int i = 0; i < length; i++) { if (callerString[i] != _u('\n') && callerString[i] != L'\r') { outString[j++] = callerString[i]; } } outString[j] = L'\0'; } JS_ETW(EventWriteJSCRIPT_HOSTING_CALLER_TO_EXTERNAL(scriptContext, this, typeId, outString, callCount)); if (outString != callerString) { HeapDeleteArray(length+1, outString); } #if DBG_DUMP if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::HostPhase)) { Output::Print(_u("Large number of Call to trampoline: methodAddr= %p, Object typeid= %d, caller method= %s, callcount= %d\n"), this, typeId, callerString, callCount); } #endif } } JS_ETW(EventWriteJSCRIPT_HOSTING_EXTERNAL_FUNCTION_CALL_START(scriptContext, this, typeId)); #if DBG_DUMP if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::HostPhase)) { Output::Print(_u("Call to trampoline: methodAddr= %p, Object typeid= %d\n"), this, typeId); } #endif } Js::RecyclableObject* directHostObject = nullptr; switch(typeId) { case TypeIds_Integer: #if FLOATVAR case TypeIds_Number: #endif // FLOATVAR Assert(!Js::RecyclableObject::Is(thisVar)); break; default: { Assert(Js::RecyclableObject::Is(thisVar)); ScriptContext* scriptContextThisVar = Js::RecyclableObject::FromVar(thisVar)->GetScriptContext(); // We need to verify "this" pointer is active as well. The problem is that DOM prototype functions are // the same across multiple frames, and caller can do function.call(closedthis) Assert(!scriptContext->GetThreadContext()->IsDisableImplicitException()); scriptContextThisVar->VerifyAlive(); // translate direct host for fastDOM. switch(typeId) { case Js::TypeIds_GlobalObject: { Js::GlobalObject* srcGlobalObject = static_cast<Js::GlobalObject*>(thisVar); directHostObject = srcGlobalObject->GetDirectHostObject(); // For jsrt, direct host object can be null. If thats the case don't change it. if (directHostObject != nullptr) { thisVar = directHostObject; } } break; case Js::TypeIds_Undefined: case Js::TypeIds_Null: { // Call to DOM function with this as "undefined" or "null" // This should be converted to Global object Js::GlobalObject* srcGlobalObject = scriptContextThisVar->GetGlobalObject() ; directHostObject = srcGlobalObject->GetDirectHostObject(); // For jsrt, direct host object can be null. If thats the case don't change it. if (directHostObject != nullptr) { thisVar = directHostObject; } } break; } } break; } }
// // Log JIT method native load event to VTune // void VTuneChakraProfile::LogMethodNativeLoadEvent(Js::FunctionBody* body, Js::FunctionEntryPointInfo* entryPoint) { #if ENABLE_NATIVE_CODEGEN if (isJitProfilingActive) { iJIT_Method_Load methodInfo; memset(&methodInfo, 0, sizeof(iJIT_Method_Load)); const char16* methodName = body->GetExternalDisplayName(); // Append function line number info to method name so that VTune can distinguish between polymorphic methods char16 methodNameBuffer[_MAX_PATH]; ULONG lineNumber = body->GetLineNumber(); char16 numberBuffer[20]; _ltow_s(lineNumber, numberBuffer, 10); wcscpy_s(methodNameBuffer, methodName); if (entryPoint->GetJitMode() == ExecutionMode::SimpleJit) { wcscat_s(methodNameBuffer, _u(" Simple")); } wcscat_s(methodNameBuffer, _u(" {line:")); wcscat_s(methodNameBuffer, numberBuffer); wcscat_s(methodNameBuffer, _u("}")); size_t methodLength = wcslen(methodNameBuffer); Assert(methodLength < _MAX_PATH); size_t length = methodLength * 3 + 1; utf8char_t* utf8MethodName = HeapNewNoThrowArray(utf8char_t, length); if (utf8MethodName) { methodInfo.method_id = iJIT_GetNewMethodID(); utf8::EncodeIntoAndNullTerminate(utf8MethodName, methodNameBuffer, (charcount_t)methodLength); methodInfo.method_name = (char*)utf8MethodName; methodInfo.method_load_address = (void*)entryPoint->GetNativeAddress(); methodInfo.method_size = (uint)entryPoint->GetCodeSize(); // Size in memory - Must be exact LineNumberInfo numberInfo[1]; uint lineCount = (entryPoint->GetNativeOffsetMapCount()) * 2 + 1; // may need to record both .begin and .end for all elements LineNumberInfo* pLineInfo = HeapNewNoThrowArray(LineNumberInfo, lineCount); if (pLineInfo == NULL || Js::Configuration::Global.flags.DisableVTuneSourceLineInfo) { // resort to original implementation, attribute all samples to first line numberInfo[0].LineNumber = lineNumber; numberInfo[0].Offset = 0; methodInfo.line_number_size = 1; methodInfo.line_number_table = numberInfo; } else { int size = entryPoint->PopulateLineInfo(pLineInfo, body); methodInfo.line_number_size = size; methodInfo.line_number_table = pLineInfo; } size_t urlLength = 0; utf8char_t* utf8Url = GetUrl(body, &urlLength); methodInfo.source_file_name = (char*)utf8Url; OUTPUT_TRACE(Js::ProfilerPhase, _u("Method load event: %s\n"), methodNameBuffer); iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, &methodInfo); HeapDeleteArray(lineCount, pLineInfo); if (urlLength > 0) { HeapDeleteArray(urlLength, utf8Url); } HeapDeleteArray(length, utf8MethodName); } } #endif }
void BackgroundJobProcessor::InitializeParallelThreadData(AllocationPolicyManager* policyManager, bool disableParallelThreads) { if (!disableParallelThreads) { InitializeThreadCount(); } else { this->maxThreadCount = 1; } Assert(this->maxThreadCount >= 1); this->parallelThreadData = HeapNewArrayZ(ParallelThreadData*, this->maxThreadCount); for (uint i = 0; i < this->maxThreadCount; i++) { this->parallelThreadData[i] = HeapNewNoThrow(ParallelThreadData, policyManager); if (this->parallelThreadData[i] == nullptr) { if (i == 0) { HeapDeleteArray(this->maxThreadCount, this->parallelThreadData); Js::Throw::OutOfMemory(); } // At least one thread is created, continue break; } this->parallelThreadData[i]->processor = this; // Make sure to create the thread suspended so the thread handle can be assigned before the thread starts running this->parallelThreadData[i]->threadHandle = reinterpret_cast<HANDLE>(PlatformAgnostic::Thread::Create(0, &StaticThreadProc, this->parallelThreadData[i], PlatformAgnostic::Thread::ThreadInitCreateSuspended)); if (!this->parallelThreadData[i]->threadHandle) { HeapDelete(parallelThreadData[i]); parallelThreadData[i] = nullptr; if (i == 0) { Js::Throw::OutOfMemory(); } // At least one thread is created, continue break; } if (ResumeThread(this->parallelThreadData[i]->threadHandle) == static_cast<DWORD>(-1)) { CloseHandle(this->parallelThreadData[i]->threadHandle); HeapDelete(parallelThreadData[i]); this->parallelThreadData[i] = nullptr; if (i == 0) { Js::Throw::OutOfMemory(); } // At least one thread is created, continue break; } this->threadCount++; // Wait for the thread to fully start. This is necessary because Close may be called before the thread starts and if // Close is called while holding the loader lock during DLL_THREAD_DETACH, the thread may be stuck waiting for the // loader lock for DLL_THREAD_ATTACH to start up, and Close would then end up waiting forever, causing a deadlock. WaitWithThreadForThreadStartedOrClosingEvent(this->parallelThreadData[i]); this->parallelThreadData[i]->threadStartedOrClosing.Reset(); // after this, the event will be used to wait for the thread to close #if DBG_DUMP if (i < (sizeof(DebugThreadNames) / sizeof(DebugThreadNames[i]))) { this->parallelThreadData[i]->backgroundPageAllocator.debugName = DebugThreadNames[i]; } else { this->parallelThreadData[i]->backgroundPageAllocator.debugName = _u("BackgroundJobProcessor thread"); } #endif } Assert(this->threadCount >= 1); }
Var JavascriptSIMDObject::ToLocaleString(const Var* args, uint numArgs, const char16 *typeString, const T (&laneValues)[N], CallInfo* callInfo, ScriptContext* scriptContext) const { Assert(args); Assert(N == 4 || N == 8 || N == 16); if (typeDescriptor == TypeIds_SIMDBool8x16 || typeDescriptor == TypeIds_SIMDBool16x8 || typeDescriptor == TypeIds_SIMDBool32x4) { return ToString(scriptContext); //Boolean types does not have toLocaleString. } // Creating a new arguments list for the JavascriptNumber generated from each lane.The optional SIMDToLocaleString Args are //added to this argument list. Var* newArgs = HeapNewArray(Var, numArgs); switch (numArgs) { case 1: break; case 2: newArgs[1] = args[1]; break; case 3: newArgs[1] = args[1]; newArgs[2] = args[2]; break; default: Assert(UNREACHED); } //Locale specifc seperator?? JavascriptString *seperator = JavascriptString::NewWithSz(_u(", "), scriptContext); uint idx = 0; Var laneVar = nullptr; BEGIN_TEMP_ALLOCATOR(tempAllocator, scriptContext, _u("fromCodePoint")); char16* stringBuffer = AnewArray(tempAllocator, char16, SIMD_STRING_BUFFER_MAX); JavascriptString *result = nullptr; swprintf_s(stringBuffer, 1024, typeString); result = JavascriptString::NewCopySzFromArena(stringBuffer, scriptContext, scriptContext->GeneralAllocator()); if (typeDescriptor == TypeIds_SIMDFloat32x4) { for (; idx < numLanes - 1; ++idx) { laneVar = JavascriptNumber::ToVarWithCheck(laneValues[idx], scriptContext); newArgs[0] = laneVar; JavascriptString *laneValue = JavascriptNumber::ToLocaleStringIntl(newArgs, *callInfo, scriptContext); result = JavascriptString::Concat(result, laneValue); result = JavascriptString::Concat(result, seperator); } laneVar = JavascriptNumber::ToVarWithCheck(laneValues[idx], scriptContext); newArgs[0] = laneVar; result = JavascriptString::Concat(result, JavascriptNumber::ToLocaleStringIntl(newArgs, *callInfo, scriptContext)); } else if (typeDescriptor == TypeIds_SIMDInt8x16 || typeDescriptor == TypeIds_SIMDInt16x8 || typeDescriptor == TypeIds_SIMDInt32x4) { for (; idx < numLanes - 1; ++idx) { laneVar = JavascriptNumber::ToVar(static_cast<int>(laneValues[idx]), scriptContext); newArgs[0] = laneVar; JavascriptString *laneValue = JavascriptNumber::ToLocaleStringIntl(newArgs, *callInfo, scriptContext); result = JavascriptString::Concat(result, laneValue); result = JavascriptString::Concat(result, seperator); } laneVar = JavascriptNumber::ToVar(static_cast<int>(laneValues[idx]), scriptContext); newArgs[0] = laneVar; result = JavascriptString::Concat(result, JavascriptNumber::ToLocaleStringIntl(newArgs, *callInfo, scriptContext)); } else { Assert((typeDescriptor == TypeIds_SIMDUint8x16 || typeDescriptor == TypeIds_SIMDUint16x8 || typeDescriptor == TypeIds_SIMDUint32x4)); for (; idx < numLanes - 1; ++idx) { laneVar = JavascriptNumber::ToVar(static_cast<uint>(laneValues[idx]), scriptContext); newArgs[0] = laneVar; JavascriptString *laneValue = JavascriptNumber::ToLocaleStringIntl(newArgs, *callInfo, scriptContext); result = JavascriptString::Concat(result, laneValue); result = JavascriptString::Concat(result, seperator); } laneVar = JavascriptNumber::ToVar(static_cast<uint>(laneValues[idx]), scriptContext); newArgs[0] = laneVar; result = JavascriptString::Concat(result, JavascriptNumber::ToLocaleStringIntl(newArgs, *callInfo, scriptContext)); } HeapDeleteArray(numArgs, newArgs); END_TEMP_ALLOCATOR(tempAllocator, scriptContext); return JavascriptString::Concat(result, JavascriptString::NewWithSz(_u(")"), scriptContext)); }