|
35 | 35 | #include "env/j9methodServer.hpp"
|
36 | 36 | #include "env/JITServerPersistentCHTable.hpp"
|
37 | 37 | #include "env/JSR292Methods.h"
|
| 38 | +#include "env/StackMemoryRegion.hpp" |
38 | 39 | #include "env/TypeLayout.hpp"
|
39 | 40 | #include "env/ut_j9jit.h"
|
40 | 41 | #include "env/VerboseLog.hpp"
|
@@ -113,53 +114,69 @@ findField(J9VMThread *vmStruct, J9ConstantPool *constantPool, UDATA index, BOOLE
|
113 | 114 | static void
|
114 | 115 | handler_IProfiler_profilingSample(JITServer::ClientStream *client, TR_J9VM *fe, TR::Compilation *comp)
|
115 | 116 | {
|
116 |
| - auto recv = client->getRecvData<TR_OpaqueMethodBlock*, uint32_t, uintptr_t>(); |
| 117 | + auto recv = client->getRecvData<TR_OpaqueMethodBlock*, uint32_t, bool, bool>(); |
117 | 118 | auto method = std::get<0>(recv);
|
118 | 119 | auto bcIndex = std::get<1>(recv);
|
119 |
| - auto data = std::get<2>(recv); // data==1 means 'send info for 1 bytecode'; data==0 means 'send info for entire method if possible' |
| 120 | + auto wholeMethodInfo = std::get<2>(recv); // 'send info for entire method if possible' |
| 121 | + auto sharedProfile = std::get<3>(recv); |
120 | 122 |
|
121 | 123 | JITClientIProfiler *iProfiler = (JITClientIProfiler *)fe->getIProfiler();
|
122 | 124 |
|
123 | 125 | bool isCompiled = TR::CompilationInfo::isCompiled((J9Method*)method);
|
| 126 | + bool isQueued = TR::CompilationInfo::getJ9MethodVMExtra((J9Method *)method) == J9_JIT_QUEUED_FOR_COMPILATION; |
124 | 127 | bool isInProgress = comp->getMethodBeingCompiled()->getPersistentIdentifier() == method;
|
125 | 128 | bool abort = false;
|
126 |
| - // Used to tell the server if a profiled entry should be stored in persistent or heap memory |
127 |
| - bool usePersistentCache = isCompiled || isInProgress; |
128 |
| - bool wholeMethodInfo = data == 0; |
| 129 | + // Used to tell the server if a profiled entry should be stored in persistent or heap memory. |
| 130 | + // Note that if the method is queued for compilation, new interpreter samples will not be collected. |
| 131 | + bool usePersistentCache = isCompiled || isInProgress || isQueued; |
129 | 132 |
|
130 | 133 | if (wholeMethodInfo)
|
131 | 134 | {
|
132 | 135 | // Serialize all the information related to this method
|
133 |
| - abort = iProfiler->serializeAndSendIProfileInfoForMethod(method, comp, client, usePersistentCache, isCompiled); |
| 136 | + abort = iProfiler->serializeAndSendIProfileInfoForMethod(method, comp, client, usePersistentCache, isCompiled, sharedProfile); |
| 137 | + if (!abort) |
| 138 | + return; |
134 | 139 | }
|
135 |
| - if (!wholeMethodInfo || abort) // Send information just for this entry |
| 140 | + |
| 141 | + // Send information just for this entry |
| 142 | + std::vector<J9Class *> uncachedClasses; |
| 143 | + std::vector<JITServerHelpers::ClassInfoTuple> classInfoTuples; |
| 144 | + auto entry = iProfiler->profilingSample(method, bcIndex, comp, 0, /*addIt=*/false); |
| 145 | + if (entry && !entry->isInvalid()) |
136 | 146 | {
|
137 |
| - auto entry = iProfiler->profilingSample(method, bcIndex, comp, data, false); |
138 |
| - if (entry && !entry->isInvalid()) |
139 |
| - { |
140 |
| - uint32_t canPersist = entry->canBeSerialized(comp->getPersistentInfo()); // This may lock the entry |
141 |
| - if (canPersist == IPBC_ENTRY_CAN_PERSIST) |
142 |
| - { |
143 |
| - uint32_t bytes = entry->getBytesFootprint(); |
144 |
| - std::string entryBytes(bytes, '\0'); |
145 |
| - auto storage = (TR_IPBCDataStorageHeader*)&entryBytes[0]; |
146 |
| - uintptr_t methodStartAddress = (uintptr_t)TR::Compiler->mtd.bytecodeStart(method); |
147 |
| - entry->serialize(methodStartAddress, storage, comp->getPersistentInfo()); |
148 |
| - client->write(JITServer::MessageType::IProfiler_profilingSample, entryBytes, false, usePersistentCache, isCompiled); |
149 |
| - } |
150 |
| - else |
| 147 | + uint32_t canPersist = entry->canBeSerialized(comp->getPersistentInfo()); // This may lock the entry |
| 148 | + if (canPersist == IPBC_ENTRY_CAN_PERSIST) |
| 149 | + { |
| 150 | + uint32_t bytes = entry->getBytesFootprint(); |
| 151 | + std::string entryBytes(bytes, '\0'); |
| 152 | + auto storage = (TR_IPBCDataStorageHeader*)&entryBytes[0]; |
| 153 | + uintptr_t methodStartAddress = (uintptr_t)TR::Compiler->mtd.bytecodeStart(method); |
| 154 | + entry->serialize(methodStartAddress, storage, comp->getPersistentInfo()); |
| 155 | + |
| 156 | + // Collect info about the classes the server needs but does not yet have |
| 157 | + auto cgEntry = entry->asIPBCDataCallGraph(); |
| 158 | + if (cgEntry && sharedProfile) |
151 | 159 | {
|
152 |
| - client->write(JITServer::MessageType::IProfiler_profilingSample, std::string(), false, usePersistentCache, isCompiled); |
| 160 | + uncachedClasses.reserve(NUM_CS_SLOTS); |
| 161 | + classInfoTuples.reserve(NUM_CS_SLOTS); |
| 162 | + iProfiler->gatherUncachedClassesUsedInCGEntry(cgEntry, comp, uncachedClasses, classInfoTuples); |
153 | 163 | }
|
154 |
| - // Unlock the entry |
155 |
| - if (auto callGraphEntry = entry->asIPBCDataCallGraph()) |
156 |
| - if (canPersist != IPBC_ENTRY_PERSIST_LOCK && callGraphEntry->isLocked()) |
157 |
| - callGraphEntry->releaseEntry(); |
| 164 | + |
| 165 | + uint64_t totalSamples = entry->getNumSamples(); |
| 166 | + client->write(JITServer::MessageType::IProfiler_profilingSample, entryBytes, totalSamples, (size_t)1, /*wholeMethod=*/false, usePersistentCache, isCompiled, uncachedClasses, classInfoTuples); |
158 | 167 | }
|
159 |
| - else // No valid info for specified bytecode index |
| 168 | + else |
160 | 169 | {
|
161 |
| - client->write(JITServer::MessageType::IProfiler_profilingSample, std::string(), false, usePersistentCache, isCompiled); |
| 170 | + client->write(JITServer::MessageType::IProfiler_profilingSample, std::string(), (uint64_t)0, (size_t)0, /*wholeMethod=*/false, usePersistentCache, isCompiled, uncachedClasses, classInfoTuples); |
162 | 171 | }
|
| 172 | + // Unlock the entry |
| 173 | + if (auto callGraphEntry = entry->asIPBCDataCallGraph()) |
| 174 | + if (canPersist != IPBC_ENTRY_PERSIST_LOCK && callGraphEntry->isLocked()) |
| 175 | + callGraphEntry->releaseEntry(); |
| 176 | + } |
| 177 | + else // No valid info for specified bytecode index |
| 178 | + { |
| 179 | + client->write(JITServer::MessageType::IProfiler_profilingSample, std::string(), (uint64_t)0, (size_t)0, /*wholeMethod=*/false, usePersistentCache, isCompiled, uncachedClasses, classInfoTuples); |
163 | 180 | }
|
164 | 181 | }
|
165 | 182 |
|
@@ -3002,20 +3019,97 @@ handleServerMessage(JITServer::ClientStream *client, TR_J9VM *fe, JITServer::Mes
|
3002 | 3019 | break;
|
3003 | 3020 | case MessageType::AOTCache_getROMClassBatch:
|
3004 | 3021 | {
|
| 3022 | + // The server has asked about N classes but we may send ClassInfos for N+M classes with |
| 3023 | + // M being the number of base component classes that the server does not yet have. |
| 3024 | + // The classes for those extra elements are passed back in extraClasses. |
| 3025 | + std::vector<J9Class *> extraClasses; |
3005 | 3026 | auto recv = client->getRecvData<std::vector<J9Class *>>();
|
3006 |
| - auto &ramClasses = std::get<0>(recv); |
3007 |
| - std::vector<JITServerHelpers::ClassInfoTuple> classInfos; |
| 3027 | + auto &ramClasses = std::get<0>(recv); // The classses for which the server requests information |
| 3028 | + |
| 3029 | + std::vector<JITServerHelpers::ClassInfoTuple> classInfos; // This will be filled and returned to server |
3008 | 3030 | classInfos.reserve(ramClasses.size());
|
3009 | 3031 |
|
| 3032 | + TR::StackMemoryRegion region(*trMemory); |
| 3033 | + Vector<J9Class *> baseComponentClasses(region); // Temporary storage for base classes of arrays |
| 3034 | + baseComponentClasses.reserve(ramClasses.size()); |
| 3035 | + Vector<J9Class *> uncachedBaseComponentClasses(region); // Filtered set of base component classes |
| 3036 | + |
3010 | 3037 | for (J9Class *ramClass : ramClasses)
|
3011 |
| - classInfos.push_back(JITServerHelpers::packRemoteROMClassInfo(ramClass, fe->vmThread(), trMemory, true)); |
| 3038 | + { |
| 3039 | + classInfos.push_back(JITServerHelpers::packRemoteROMClassInfo(ramClass, vmThread, trMemory, true/*serializeClass*/)); |
| 3040 | + |
| 3041 | + // If this is an array class, remember its base component class for later |
| 3042 | + int32_t numDimensions = 0; |
| 3043 | + J9Class *baseComponent = (J9Class *)TR_J9VMBase::staticGetBaseComponentClass((TR_OpaqueClassBlock *)ramClass, numDimensions); |
| 3044 | + if (numDimensions) |
| 3045 | + baseComponentClasses.push_back(baseComponent); |
| 3046 | + } |
3012 | 3047 |
|
| 3048 | + // Determine which baseComponent classes the server does not yet have |
| 3049 | + uncachedBaseComponentClasses.reserve(baseComponentClasses.size()); |
| 3050 | + if (!baseComponentClasses.empty()) |
3013 | 3051 | {
|
3014 | 3052 | OMR::CriticalSection cs(compInfo->getclassesCachedAtServerMonitor());
|
3015 |
| - compInfo->getclassesCachedAtServer().insert(ramClasses.begin(), ramClasses.end()); |
| 3053 | + const auto &classesCachedAtServer = compInfo->getclassesCachedAtServer(); |
| 3054 | + for (J9Class *baseComponent : baseComponentClasses) |
| 3055 | + { |
| 3056 | + if (classesCachedAtServer.find(baseComponent) == classesCachedAtServer.end()) // server doesn't have it |
| 3057 | + { |
| 3058 | + uncachedBaseComponentClasses.push_back(baseComponent); |
| 3059 | + } |
| 3060 | + } |
3016 | 3061 | }
|
3017 | 3062 |
|
3018 |
| - client->write(response, classInfos); |
| 3063 | + // Add the uncached baseComponent classes to the classInfos vector |
| 3064 | + extraClasses.reserve(uncachedBaseComponentClasses.size()); |
| 3065 | + for (J9Class *baseComponent : uncachedBaseComponentClasses) |
| 3066 | + { |
| 3067 | + classInfos.push_back(JITServerHelpers::packRemoteROMClassInfo(baseComponent, vmThread, trMemory, true/*serializeClass*/)); |
| 3068 | + extraClasses.push_back(baseComponent); |
| 3069 | + } |
| 3070 | + |
| 3071 | + // Send the information to the server. |
| 3072 | + client->write(response, classInfos, extraClasses); |
| 3073 | + |
| 3074 | + // Finally, update client's view of classes cached by the server. |
| 3075 | + { |
| 3076 | + OMR::CriticalSection cs(compInfo->getclassesCachedAtServerMonitor()); |
| 3077 | + compInfo->getclassesCachedAtServer().insert(ramClasses.begin(), ramClasses.end()); |
| 3078 | + compInfo->getclassesCachedAtServer().insert(uncachedBaseComponentClasses.begin(), uncachedBaseComponentClasses.end()); |
| 3079 | + } // end critical section |
| 3080 | + } |
| 3081 | + break; |
| 3082 | + |
| 3083 | + case MessageType::AOTCache_getRAMClassFromClassRecordBatch: |
| 3084 | + { |
| 3085 | + // Convert several AOT cache class IDs to this client's RAMClasses |
| 3086 | + auto recv = client->getRecvData<std::vector<uintptr_t>, std::string>(); |
| 3087 | + auto &classIds = std::get<0>(recv); // vector of classIDs that need to be converted into j9classes |
| 3088 | + auto &recordsStr = std::get<1>(recv); // packed serialization records that the client is missing |
| 3089 | + |
| 3090 | + std::vector<J9Class *> ramClasses; |
| 3091 | + ramClasses.reserve(classIds.size()); |
| 3092 | + |
| 3093 | + if (auto deserializer = compInfo->getJITServerAOTDeserializer()) |
| 3094 | + { |
| 3095 | + bool wasReset = false; |
| 3096 | + deserializer->cacheRecords((const uint8_t *)recordsStr.data(), recordsStr.size(), comp, |
| 3097 | + /*ignoreFailures=*/true, wasReset); |
| 3098 | + if (!wasReset) |
| 3099 | + { |
| 3100 | + for (uintptr_t id : classIds) |
| 3101 | + { |
| 3102 | + J9Class *ramClass = deserializer->getRAMClass(id, comp, wasReset); |
| 3103 | + if (wasReset) |
| 3104 | + { |
| 3105 | + ramClasses.clear(); |
| 3106 | + break; |
| 3107 | + } |
| 3108 | + ramClasses.push_back(ramClass); // possibly NULL |
| 3109 | + } |
| 3110 | + } |
| 3111 | + } |
| 3112 | + client->write(response, ramClasses); |
3019 | 3113 | }
|
3020 | 3114 | break;
|
3021 | 3115 | default:
|
|
0 commit comments