diff --git a/lib/android.js b/lib/android.js index d023166..6b40f24 100644 --- a/lib/android.js +++ b/lib/android.js @@ -108,6 +108,7 @@ let MethodMangler = null; let artController = null; const inlineHooks = []; const patchedClasses = new Map(); +const linearAllocClones = new Map(); const artQuickInterceptors = []; let thunkPage = null; let thunkOffset = 0; @@ -291,6 +292,9 @@ function _getApi () { }; }, + // userfaultfd GC for cloneArtMethod() + _ZN3art7Runtime22CreateCalleeSaveMethodEv: ['art::Runtime::CreateCalleeSaveMethod', 'pointer', ['pointer']], + _ZN3art3Dbg14SetJdwpAllowedEb: ['art::Dbg::SetJdwpAllowed', 'void', ['bool']], _ZN3art3Dbg13ConfigureJdwpERKNS_4JDWP11JdwpOptionsE: ['art::Dbg::ConfigureJdwp', 'void', ['pointer']], _ZN3art31InternalDebuggerControlCallback13StartDebuggerEv: ['art::InternalDebuggerControlCallback::StartDebugger', 'void', ['pointer']], @@ -325,6 +329,9 @@ function _getApi () { }, _ZN3art3Dbg15gDebuggerActiveE: function (address) { this.isDebuggerActive = () => !!address.readU8(); + }, + _ZN3art15gUseUserfaultfdE: function (address) { + this.isUserfaultfdGcActive = !!address.readU8(); } }, optionals: new Set([ @@ -343,6 +350,7 @@ function _getApi () { '_ZN3art6mirror6Object5CloneEPNS_6ThreadE', '_ZN3art6mirror6Object5CloneEPNS_6ThreadEm', '_ZN3art6mirror6Object5CloneEPNS_6ThreadEj', + '_ZN3art7Runtime22CreateCalleeSaveMethodEv', '_ZN3art22IndirectReferenceTable3AddEjPNS_6mirror6ObjectE', '_ZN3art22IndirectReferenceTable3AddENS_15IRTSegmentStateENS_6ObjPtrINS_6mirror6ObjectEEE', '_ZN3art2gc4Heap12VisitObjectsEPFvPNS_6mirror6ObjectEPvES5_', @@ -363,6 +371,7 @@ function _getApi () { '_ZN3art3Dbg13ConfigureJdwpERKNS_4JDWP11JdwpOptionsE', '_ZN3art31InternalDebuggerControlCallback13StartDebuggerEv', '_ZN3art3Dbg15gDebuggerActiveE', + '_ZN3art15gUseUserfaultfdE', '_ZN3art15instrumentation15Instrumentation20EnableDeoptimizationEv', '_ZN3art15instrumentation15Instrumentation20DeoptimizeEverythingEPKc', '_ZN3art15instrumentation15Instrumentation20DeoptimizeEverythingEv', @@ -3972,7 +3981,27 @@ function cloneArtMethod (method, vm) { return api['art::mirror::Object::Clone'](method, thread); } - return Memory.dup(method, getArtMethodSpec(vm).size); + const artMethodSize = getArtMethodSpec(vm).size; + + if (api.isUserfaultfdGcActive) { + // The CMC GC only updates declaring_class_ of ArtMethods in LinearAlloc, so a clone elsewhere goes stale on + // compaction. CreateCalleeSaveMethod() is exported and allocates its ArtMethod in the runtime's LinearAlloc. + const key = method.toString(); + let artMethodClone = linearAllocClones.get(key); + // LinearAlloc never frees, so each method keeps its clone; re-copying it would race with calls still in flight. + if (artMethodClone === undefined) { + vm.perform(env => { + withRunnableArtThread(vm, env, () => { + artMethodClone = api['art::Runtime::CreateCalleeSaveMethod'](api.artRuntime); + Memory.copy(artMethodClone, method, artMethodSize); + }); + }); + linearAllocClones.set(key, artMethodClone); + } + return artMethodClone; + } + + return Memory.dup(method, artMethodSize); } export function deoptimizeMethod (vm, env, method) {