Skip to content

Commit c706978

Browse files
committed
[MERGE chakra-core#4441 @Cellule] Serialize WorkItemData for repro
Merge pull request chakra-core#4441 from Cellule:backend_repro In debug build serialize WorkItemData from out of proc codegen calls. In crash dumps the encoded buffer will be available to allow repro in console.
2 parents 3c687f5 + 58232f9 commit c706978

25 files changed

Lines changed: 9958 additions & 9705 deletions

lib/Backend/BackendApi.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ RejitIRViewerFunction(NativeCodeGenerator *nativeCodeGen, Js::FunctionBody *fn,
7575
return nativeCodeGen->RejitIRViewerFunction(fn, scriptContext);
7676
}
7777
#endif
78+
#ifdef ALLOW_JIT_REPRO
79+
HRESULT JitFromEncodedWorkItem(NativeCodeGenerator *nativeCodeGen, _In_reads_(bufSize) const byte* buffer, _In_ uint bufferSize)
80+
{
81+
return nativeCodeGen->JitFromEncodedWorkItem(buffer, bufferSize);
82+
}
83+
#endif
7884

7985
void
8086
GenerateLoopBody(NativeCodeGenerator *nativeCodeGen, Js::FunctionBody *fn, Js::LoopHeader * loopHeader, Js::EntryPointInfo* info, uint localCount, Js::Var localSlots[])

lib/Backend/NativeCodeGenerator.cpp

Lines changed: 85 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,21 @@ NativeCodeGenerator::RejitIRViewerFunction(Js::FunctionBody *fn, Js::ScriptConte
469469
}
470470
#endif /* IR_VIEWER */
471471

472+
#ifdef ALLOW_JIT_REPRO
473+
HRESULT NativeCodeGenerator::JitFromEncodedWorkItem(_In_reads_(bufSize) const byte* buffer, _In_ uint bufferSize)
474+
{
475+
CodeGenWorkItemIDL* workItemData = nullptr;
476+
HRESULT hr = JITManager::DeserializeRPCData(buffer, bufferSize, &workItemData);
477+
if (FAILED(hr))
478+
{
479+
return hr;
480+
}
481+
JITOutputIDL jitOutput = { 0 };
482+
CodeGen(scriptContext->GetThreadContext()->GetPageAllocator(), workItemData, jitOutput, true);
483+
return S_OK;
484+
}
485+
#endif
486+
472487
///----------------------------------------------------------------------------
473488
///
474489
/// NativeCodeGenerator::GenerateFunction
@@ -791,6 +806,74 @@ NativeCodeGenerator::IsValidVar(const Js::Var var, Recycler *const recycler)
791806
volatile UINT_PTR NativeCodeGenerator::CodegenFailureSeed = 0;
792807
#endif
793808

809+
void NativeCodeGenerator::CodeGen(PageAllocator* pageAllocator, CodeGenWorkItemIDL* workItemData, _Out_ JITOutputIDL& jitWriteData, const bool foreground, Js::EntryPointInfo* epInfo /*= nullptr*/)
810+
{
811+
if (JITManager::GetJITManager()->IsOOPJITEnabled())
812+
{
813+
PSCRIPTCONTEXT_HANDLE remoteScriptContext = this->scriptContext->GetRemoteScriptAddr();
814+
if (!JITManager::GetJITManager()->IsConnected())
815+
{
816+
throw Js::OperationAbortedException();
817+
}
818+
HRESULT hr = JITManager::GetJITManager()->RemoteCodeGenCall(
819+
workItemData,
820+
remoteScriptContext,
821+
&jitWriteData);
822+
if (hr == E_ACCESSDENIED && scriptContext->IsClosed())
823+
{
824+
// script context may close after codegen call starts, consider this as aborted codegen
825+
hr = E_ABORT;
826+
}
827+
JITManager::HandleServerCallResult(hr, RemoteCallType::CodeGen);
828+
829+
if (!PreReservedVirtualAllocWrapper::IsInRange((void*)this->scriptContext->GetThreadContext()->GetPreReservedRegionAddr(), (void*)jitWriteData.codeAddress))
830+
{
831+
this->scriptContext->GetJitFuncRangeCache()->AddFuncRange((void*)jitWriteData.codeAddress, jitWriteData.codeSize);
832+
}
833+
Assert(jitWriteData.codeAddress);
834+
Assert(jitWriteData.codeSize);
835+
}
836+
else
837+
{
838+
InProcCodeGenAllocators *const allocators =
839+
foreground ? EnsureForegroundAllocators(pageAllocator) : GetBackgroundAllocator(pageAllocator); // okay to do outside lock since the respective function is called only from one thread
840+
NoRecoverMemoryJitArenaAllocator jitArena(_u("JITArena"), pageAllocator, Js::Throw::OutOfMemory);
841+
#if DBG
842+
jitArena.SetNeedsDelayFreeList();
843+
#endif
844+
JITTimeWorkItem * jitWorkItem = Anew(&jitArena, JITTimeWorkItem, workItemData);
845+
846+
#if !FLOATVAR
847+
CodeGenNumberAllocator* pNumberAllocator = nullptr;
848+
849+
// the number allocator needs to be on the stack so that if we are doing foreground JIT
850+
// the chunk allocated from the recycler will be stacked pinned
851+
CodeGenNumberAllocator numberAllocator(
852+
foreground ? nullptr : scriptContext->GetThreadContext()->GetCodeGenNumberThreadAllocator(),
853+
scriptContext->GetRecycler());
854+
pNumberAllocator = &numberAllocator;
855+
#endif
856+
Js::ScriptContextProfiler *const codeGenProfiler =
857+
#ifdef PROFILE_EXEC
858+
foreground ? EnsureForegroundCodeGenProfiler() : GetBackgroundCodeGenProfiler(pageAllocator); // okay to do outside lock since the respective function is called only from one thread
859+
#else
860+
nullptr;
861+
#endif
862+
863+
Func::Codegen(&jitArena, jitWorkItem, scriptContext->GetThreadContext(),
864+
scriptContext, &jitWriteData, epInfo, nullptr, jitWorkItem->GetPolymorphicInlineCacheInfo(), allocators,
865+
#if !FLOATVAR
866+
pNumberAllocator,
867+
#endif
868+
codeGenProfiler, !foreground);
869+
870+
if (!this->scriptContext->GetThreadContext()->GetPreReservedVirtualAllocator()->IsInRange((void*)jitWriteData.codeAddress))
871+
{
872+
this->scriptContext->GetJitFuncRangeCache()->AddFuncRange((void*)jitWriteData.codeAddress, jitWriteData.codeSize);
873+
}
874+
}
875+
}
876+
794877
void
795878
NativeCodeGenerator::CodeGen(PageAllocator * pageAllocator, CodeGenWorkItem* workItem, const bool foreground)
796879
{
@@ -889,70 +972,7 @@ NativeCodeGenerator::CodeGen(PageAllocator * pageAllocator, CodeGenWorkItem* wor
889972
LARGE_INTEGER start_time = { 0 };
890973
NativeCodeGenerator::LogCodeGenStart(workItem, &start_time);
891974
workItem->GetJITData()->startTime = (int64)start_time.QuadPart;
892-
if (JITManager::GetJITManager()->IsOOPJITEnabled())
893-
{
894-
PSCRIPTCONTEXT_HANDLE remoteScriptContext = this->scriptContext->GetRemoteScriptAddr();
895-
if (!JITManager::GetJITManager()->IsConnected())
896-
{
897-
throw Js::OperationAbortedException();
898-
}
899-
HRESULT hr = JITManager::GetJITManager()->RemoteCodeGenCall(
900-
workItem->GetJITData(),
901-
remoteScriptContext,
902-
&jitWriteData);
903-
if (hr == E_ACCESSDENIED && body->GetScriptContext()->IsClosed())
904-
{
905-
// script context may close after codegen call starts, consider this as aborted codegen
906-
hr = E_ABORT;
907-
}
908-
JITManager::HandleServerCallResult(hr, RemoteCallType::CodeGen);
909-
910-
if (!PreReservedVirtualAllocWrapper::IsInRange((void*)this->scriptContext->GetThreadContext()->GetPreReservedRegionAddr(), (void*)jitWriteData.codeAddress))
911-
{
912-
this->scriptContext->GetJitFuncRangeCache()->AddFuncRange((void*)jitWriteData.codeAddress, jitWriteData.codeSize);
913-
}
914-
Assert(jitWriteData.codeAddress);
915-
Assert(jitWriteData.codeSize);
916-
}
917-
else
918-
{
919-
InProcCodeGenAllocators *const allocators =
920-
foreground ? EnsureForegroundAllocators(pageAllocator) : GetBackgroundAllocator(pageAllocator); // okay to do outside lock since the respective function is called only from one thread
921-
NoRecoverMemoryJitArenaAllocator jitArena(_u("JITArena"), pageAllocator, Js::Throw::OutOfMemory);
922-
#if DBG
923-
jitArena.SetNeedsDelayFreeList();
924-
#endif
925-
JITTimeWorkItem * jitWorkItem = Anew(&jitArena, JITTimeWorkItem, workItem->GetJITData());
926-
927-
#if !FLOATVAR
928-
CodeGenNumberAllocator* pNumberAllocator = nullptr;
929-
930-
// the number allocator needs to be on the stack so that if we are doing foreground JIT
931-
// the chunk allocated from the recycler will be stacked pinned
932-
CodeGenNumberAllocator numberAllocator(
933-
foreground ? nullptr : scriptContext->GetThreadContext()->GetCodeGenNumberThreadAllocator(),
934-
scriptContext->GetRecycler());
935-
pNumberAllocator = &numberAllocator;
936-
#endif
937-
Js::ScriptContextProfiler *const codeGenProfiler =
938-
#ifdef PROFILE_EXEC
939-
foreground ? EnsureForegroundCodeGenProfiler() : GetBackgroundCodeGenProfiler(pageAllocator); // okay to do outside lock since the respective function is called only from one thread
940-
#else
941-
nullptr;
942-
#endif
943-
944-
Func::Codegen(&jitArena, jitWorkItem, scriptContext->GetThreadContext(),
945-
scriptContext, &jitWriteData, epInfo, nullptr, jitWorkItem->GetPolymorphicInlineCacheInfo(), allocators,
946-
#if !FLOATVAR
947-
pNumberAllocator,
948-
#endif
949-
codeGenProfiler, !foreground);
950-
951-
if (!this->scriptContext->GetThreadContext()->GetPreReservedVirtualAllocator()->IsInRange((void*)jitWriteData.codeAddress))
952-
{
953-
this->scriptContext->GetJitFuncRangeCache()->AddFuncRange((void*)jitWriteData.codeAddress, jitWriteData.codeSize);
954-
}
955-
}
975+
CodeGen(pageAllocator, workItem->GetJITData(), jitWriteData, foreground, epInfo);
956976

957977
if (JITManager::GetJITManager()->IsOOPJITEnabled() && PHASE_VERBOSE_TRACE(Js::BackEndPhase, workItem->GetFunctionBody()))
958978
{
@@ -1194,6 +1214,7 @@ NativeCodeGenerator::CodeGen(PageAllocator * pageAllocator, CodeGenWorkItem* wor
11941214
#endif
11951215
}
11961216

1217+
11971218
/* static */
11981219
void NativeCodeGenerator::LogCodeGenStart(CodeGenWorkItem * workItem, LARGE_INTEGER * start_time)
11991220
{

lib/Backend/NativeCodeGenerator.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ class NativeCodeGenerator sealed : public JsUtil::WaitableJobManager
4545
#ifdef IR_VIEWER
4646
Js::Var RejitIRViewerFunction(Js::FunctionBody *fn, Js::ScriptContext *scriptContext);
4747
#endif
48+
#ifdef ALLOW_JIT_REPRO
49+
HRESULT JitFromEncodedWorkItem(_In_reads_(bufSize) const byte* buf, _In_ uint bufSize);
50+
#endif
4851
void SetProfileMode(BOOL fSet);
4952
public:
5053
static Js::Var CheckCodeGenThunk(Js::RecyclableObject* function, Js::CallInfo callInfo, ...);
@@ -126,6 +129,7 @@ void SetProfileMode(BOOL fSet);
126129
private:
127130

128131
void CodeGen(PageAllocator * pageAllocator, CodeGenWorkItem* workItem, const bool foreground);
132+
void CodeGen(PageAllocator* pageAllocator, CodeGenWorkItemIDL* workItemData, _Out_ JITOutputIDL& jitWriteData, const bool foreground, Js::EntryPointInfo* epInfo = nullptr);
129133

130134
InProcCodeGenAllocators *CreateAllocators(PageAllocator *const pageAllocator)
131135
{

lib/Common/BackendApi.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ void GenerateAllFunctions(NativeCodeGenerator * nativeCodeGen, Js::FunctionBody
7272
#ifdef IR_VIEWER
7373
Js::Var RejitIRViewerFunction(NativeCodeGenerator *nativeCodeGen, Js::FunctionBody *fn, Js::ScriptContext *scriptContext);
7474
#endif
75+
#ifdef ALLOW_JIT_REPRO
76+
HRESULT JitFromEncodedWorkItem(NativeCodeGenerator *nativeCodeGen, _In_reads_(bufSize) const byte* buffer, _In_ uint bufferSize);
77+
#endif
7578

7679
BOOL IsIntermediateCodeGenThunk(Js::JavascriptMethod codeAddress);
7780
BOOL IsAsmJsCodeGenThunk(Js::JavascriptMethod codeAddress);

lib/Common/CommonDefines.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,10 @@
282282
#define ENABLE_BACKGROUND_PARSING 1
283283
#endif
284284

285+
#if ENABLE_DEBUG_CONFIG_OPTIONS
286+
#define ALLOW_JIT_REPRO
287+
#endif
288+
285289
#endif
286290

287291
#if ENABLE_NATIVE_CODEGEN

lib/Common/ConfigFlagsList.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,7 @@ PHASE(All)
539539
#else
540540
#define DEFAULT_CONFIG_JsBuiltIn (false)
541541
#endif
542-
542+
#define DEFAULT_CONFIG_JitRepro (false)
543543
#define DEFAULT_CONFIG_LdChakraLib (false)
544544

545545
// ES6 DEFAULT BEHAVIOR
@@ -1008,6 +1008,7 @@ FLAGR (Boolean, Intl , "Intl object support", DEFAULT_CONFIG_In
10081008
FLAGNR(Boolean, IntlBuiltIns , "Intl built-in function support", DEFAULT_CONFIG_IntlBuiltIns)
10091009

10101010
FLAGNR(Boolean, JsBuiltIn , "JS Built-in function support", DEFAULT_CONFIG_JsBuiltIn)
1011+
FLAGNR(Boolean, JitRepro , "Add Function.invokeJit to execute codegen on an encoded rpc buffer", DEFAULT_CONFIG_JitRepro)
10111012

10121013
FLAGNR(Boolean, LdChakraLib , "Access to the Chakra internal library with the __chakraLibrary keyword", DEFAULT_CONFIG_LdChakraLib)
10131014
// ES6 (BLUE+1) features/flags

lib/JITClient/JITManager.cpp

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,3 +676,126 @@ JITManager::IsInterpreterThunkAddr(
676676
return hr;
677677
}
678678
#endif
679+
680+
#ifdef ENABLE_DEBUG_CONFIG_OPTIONS
681+
HRESULT
682+
JITManager::DeserializeRPCData(
683+
_In_reads_(bufferSize) const byte* buffer,
684+
_In_ uint bufferSize,
685+
_Out_ CodeGenWorkItemIDL **workItemData
686+
)
687+
{
688+
RPC_STATUS status = RPC_S_OK;
689+
handle_t marshalHandle = nullptr;
690+
*workItemData = nullptr;
691+
__try
692+
{
693+
RpcTryExcept
694+
{
695+
status = MesDecodeBufferHandleCreate((char*)buffer, bufferSize, &marshalHandle);
696+
if (status != RPC_S_OK)
697+
{
698+
return status;
699+
}
700+
701+
pCodeGenWorkItemIDL_Decode(
702+
marshalHandle,
703+
workItemData);
704+
}
705+
RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
706+
{
707+
status = RpcExceptionCode();
708+
}
709+
RpcEndExcept;
710+
}
711+
__finally
712+
{
713+
MesHandleFree(marshalHandle);
714+
}
715+
return status;
716+
}
717+
718+
HRESULT
719+
JITManager::SerializeRPCData(_In_ CodeGenWorkItemIDL *workItemData, _Out_ size_t* bufferSize, _Outptr_result_buffer_(*bufferSize) const byte** outBuffer)
720+
{
721+
handle_t marshalHandle = nullptr;
722+
*bufferSize = 0;
723+
*outBuffer = nullptr;
724+
RPC_STATUS status = RPC_S_OK;
725+
__try
726+
{
727+
RpcTryExcept
728+
{
729+
char* data = nullptr;
730+
unsigned long encodedSize;
731+
status = MesEncodeDynBufferHandleCreate(
732+
&data,
733+
&encodedSize,
734+
&marshalHandle);
735+
if (status != RPC_S_OK)
736+
{
737+
return status;
738+
}
739+
740+
MIDL_ES_CODE encodeType = MES_ENCODE;
741+
#if TARGET_64
742+
encodeType = MES_ENCODE_NDR64;
743+
// We only support encode syntax NDR64, however MesEncodeDynBufferHandleCreate doesn't allow to specify it
744+
status = MesBufferHandleReset(
745+
marshalHandle,
746+
MES_DYNAMIC_BUFFER_HANDLE,
747+
encodeType,
748+
&data,
749+
0,
750+
&encodedSize
751+
);
752+
if (status != RPC_S_OK)
753+
{
754+
return status;
755+
}
756+
#endif
757+
758+
// Calculate how big we need to create the buffer
759+
size_t tmpBufSize = pCodeGenWorkItemIDL_AlignSize(marshalHandle, &workItemData);
760+
size_t alignedBufSize = Math::Align<size_t>(tmpBufSize, 16);
761+
data = HeapNewNoThrowArray(char, alignedBufSize);
762+
if (!data)
763+
{
764+
// Ran out of memory
765+
return E_OUTOFMEMORY;
766+
}
767+
768+
// Reset the buffer handle to a fixed buffer
769+
status = MesBufferHandleReset(
770+
marshalHandle,
771+
MES_FIXED_BUFFER_HANDLE,
772+
encodeType,
773+
&data,
774+
(unsigned long)alignedBufSize,
775+
&encodedSize
776+
);
777+
if (status != RPC_S_OK)
778+
{
779+
return status;
780+
}
781+
782+
pCodeGenWorkItemIDL_Encode(
783+
marshalHandle,
784+
&workItemData);
785+
*bufferSize = alignedBufSize;
786+
*outBuffer = (byte*)data;
787+
}
788+
RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
789+
{
790+
status = RpcExceptionCode();
791+
}
792+
RpcEndExcept;
793+
}
794+
__finally
795+
{
796+
MesHandleFree(marshalHandle);
797+
}
798+
799+
return status;
800+
}
801+
#endif

0 commit comments

Comments
 (0)