MemoryInformation.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. #include "il2cpp-config.h"
  2. #include "MemoryInformation.h"
  3. #include "gc/GarbageCollector.h"
  4. #include "gc/GCHandle.h"
  5. #include "metadata/ArrayMetadata.h"
  6. #include "metadata/GenericMetadata.h"
  7. #include "vm/Assembly.h"
  8. #include "vm/Class.h"
  9. #include "vm/MetadataCache.h"
  10. #include "vm/Type.h"
  11. #include "utils/Memory.h"
  12. #include "il2cpp-class-internals.h"
  13. #include "il2cpp-object-internals.h"
  14. #include "il2cpp-tabledefs.h"
  15. #include <map>
  16. #include <limits>
  17. namespace il2cpp
  18. {
  19. namespace vm
  20. {
  21. namespace MemoryInformation
  22. {
  23. struct GatherMetadataContext
  24. {
  25. uint32_t currentIndex;
  26. std::map<Il2CppClass*, uint32_t> allTypes;
  27. };
  28. static void GatherMetadataCallback(Il2CppClass* type, void* context)
  29. {
  30. if (type->initialized)
  31. {
  32. GatherMetadataContext* ctx = static_cast<GatherMetadataContext*>(context);
  33. ctx->allTypes.insert(std::make_pair(type, ctx->currentIndex++));
  34. }
  35. }
  36. static inline int FindTypeInfoIndexInMap(const std::map<Il2CppClass*, uint32_t>& allTypes, Il2CppClass* typeInfo)
  37. {
  38. std::map<Il2CppClass*, uint32_t>::const_iterator it = allTypes.find(typeInfo);
  39. if (it == allTypes.end())
  40. return -1;
  41. return it->second;
  42. }
  43. static inline void GatherMetadata(Il2CppMetadataSnapshot& metadata)
  44. {
  45. GatherMetadataContext gatherMetadataContext = { 0 };
  46. const AssemblyVector* allAssemblies = Assembly::GetAllAssemblies();
  47. for (AssemblyVector::const_iterator it = allAssemblies->begin(); it != allAssemblies->end(); it++)
  48. {
  49. const Il2CppImage& image = *(*it)->image;
  50. for (uint32_t i = 0; i < image.typeCount; i++)
  51. {
  52. Il2CppClass* type = MetadataCache::GetTypeInfoFromHandle(MetadataCache::GetAssemblyTypeHandle(&image, i));
  53. if (type->initialized)
  54. gatherMetadataContext.allTypes.insert(std::make_pair(type, gatherMetadataContext.currentIndex++));
  55. }
  56. }
  57. metadata::ArrayMetadata::WalkArrays(GatherMetadataCallback, &gatherMetadataContext);
  58. metadata::ArrayMetadata::WalkSZArrays(GatherMetadataCallback, &gatherMetadataContext);
  59. metadata::GenericMetadata::WalkAllGenericClasses(GatherMetadataCallback, &gatherMetadataContext);
  60. MetadataCache::WalkPointerTypes(GatherMetadataCallback, &gatherMetadataContext);
  61. const std::map<Il2CppClass*, uint32_t>& allTypes = gatherMetadataContext.allTypes;
  62. metadata.typeCount = static_cast<uint32_t>(allTypes.size());
  63. metadata.types = static_cast<Il2CppMetadataType*>(IL2CPP_CALLOC(metadata.typeCount, sizeof(Il2CppMetadataType)));
  64. for (std::map<Il2CppClass*, uint32_t>::const_iterator it = allTypes.begin(); it != allTypes.end(); it++)
  65. {
  66. Il2CppClass* typeInfo = it->first;
  67. uint32_t index = it->second;
  68. Il2CppMetadataType& type = metadata.types[index];
  69. if (typeInfo->rank > 0)
  70. {
  71. type.flags = static_cast<Il2CppMetadataTypeFlags>(kArray | (kArrayRankMask & (typeInfo->rank << 16)));
  72. type.baseOrElementTypeIndex = FindTypeInfoIndexInMap(allTypes, typeInfo->element_class);
  73. }
  74. else
  75. {
  76. type.flags = (typeInfo->byval_arg.valuetype || typeInfo->byval_arg.type == IL2CPP_TYPE_PTR) ? kValueType : kNone;
  77. type.fieldCount = 0;
  78. if (typeInfo->field_count > 0)
  79. {
  80. type.fields = static_cast<Il2CppMetadataField*>(IL2CPP_CALLOC(typeInfo->field_count, sizeof(Il2CppMetadataField)));
  81. for (int i = 0; i < typeInfo->field_count; i++)
  82. {
  83. Il2CppMetadataField& field = metadata.types[index].fields[type.fieldCount];
  84. FieldInfo* fieldInfo = typeInfo->fields + i;
  85. field.typeIndex = FindTypeInfoIndexInMap(allTypes, Class::FromIl2CppType(fieldInfo->type));
  86. // This will happen if fields type is not initialized
  87. // It's OK to skip it, because it means the field is guaranteed to be null on any object
  88. if (field.typeIndex == -1)
  89. continue;
  90. //literals have no actual storage, and are not relevant in this context.
  91. if ((fieldInfo->type->attrs & FIELD_ATTRIBUTE_LITERAL) != 0)
  92. continue;
  93. field.isStatic = (fieldInfo->type->attrs & FIELD_ATTRIBUTE_STATIC) != 0;
  94. field.offset = fieldInfo->offset;
  95. field.name = fieldInfo->name;
  96. type.fieldCount++;
  97. }
  98. }
  99. type.staticsSize = typeInfo->static_fields_size;
  100. if (type.staticsSize > 0 && typeInfo->static_fields != NULL)
  101. {
  102. type.statics = static_cast<uint8_t*>(IL2CPP_MALLOC(type.staticsSize));
  103. memcpy(type.statics, typeInfo->static_fields, type.staticsSize);
  104. }
  105. Il2CppClass* baseType = Class::GetParent(typeInfo);
  106. type.baseOrElementTypeIndex = baseType != NULL ? FindTypeInfoIndexInMap(allTypes, baseType) : -1;
  107. }
  108. type.assemblyName = typeInfo->image->assembly->aname.name;
  109. std::string typeName = Type::GetName(&typeInfo->byval_arg, IL2CPP_TYPE_NAME_FORMAT_IL);
  110. type.name = static_cast<char*>(IL2CPP_CALLOC(typeName.length() + 1, sizeof(char)));
  111. memcpy(type.name, typeName.c_str(), typeName.length() + 1);
  112. type.typeInfoAddress = reinterpret_cast<uint64_t>(typeInfo);
  113. type.size = (typeInfo->byval_arg.valuetype) != 0 ? (typeInfo->instance_size - sizeof(Il2CppObject)) : typeInfo->instance_size;
  114. }
  115. }
  116. struct SectionIterationContext
  117. {
  118. Il2CppManagedMemorySection* currentSection;
  119. };
  120. static void AllocateMemoryForSection(void* context, void* sectionStart, void* sectionEnd)
  121. {
  122. SectionIterationContext* ctx = static_cast<SectionIterationContext*>(context);
  123. Il2CppManagedMemorySection& section = *ctx->currentSection;
  124. section.sectionStartAddress = reinterpret_cast<uint64_t>(sectionStart);
  125. ptrdiff_t sectionSize = static_cast<uint8_t*>(sectionEnd) - static_cast<uint8_t*>(sectionStart);
  126. if (sizeof(void*) > 4) // This assert is only valid on 64-bit
  127. IL2CPP_ASSERT(sectionSize <= static_cast<ptrdiff_t>(std::numeric_limits<uint32_t>::max()));
  128. section.sectionSize = static_cast<uint32_t>(sectionSize);
  129. section.sectionBytes = static_cast<uint8_t*>(IL2CPP_MALLOC(section.sectionSize));
  130. ctx->currentSection++;
  131. }
  132. static void CopyHeapSection(void* context, void* sectionStart, void* sectionEnd)
  133. {
  134. SectionIterationContext* ctx = static_cast<SectionIterationContext*>(context);
  135. Il2CppManagedMemorySection& section = *ctx->currentSection;
  136. IL2CPP_ASSERT(section.sectionStartAddress == reinterpret_cast<uint64_t>(sectionStart));
  137. IL2CPP_ASSERT(section.sectionSize == static_cast<uint8_t*>(sectionEnd) - static_cast<uint8_t*>(sectionStart));
  138. memcpy(section.sectionBytes, sectionStart, section.sectionSize);
  139. ctx->currentSection++;
  140. }
  141. static void* CaptureHeapInfo(void* voidManagedHeap)
  142. {
  143. Il2CppManagedHeap& heap = *(Il2CppManagedHeap*)voidManagedHeap;
  144. heap.sectionCount = static_cast<uint32_t>(il2cpp::gc::GarbageCollector::GetSectionCount());
  145. heap.sections = static_cast<Il2CppManagedMemorySection*>(IL2CPP_CALLOC(heap.sectionCount, sizeof(Il2CppManagedMemorySection)));
  146. SectionIterationContext iterationContext = { heap.sections };
  147. il2cpp::gc::GarbageCollector::ForEachHeapSection(&iterationContext, AllocateMemoryForSection);
  148. return NULL;
  149. }
  150. static void FreeIL2CppManagedHeap(Il2CppManagedHeap& heap)
  151. {
  152. for (uint32_t i = 0; i < heap.sectionCount; i++)
  153. {
  154. IL2CPP_FREE(heap.sections[i].sectionBytes);
  155. }
  156. IL2CPP_FREE(heap.sections);
  157. }
  158. struct VerifyHeapSectionStillValidIterationContext
  159. {
  160. Il2CppManagedMemorySection* currentSection;
  161. bool wasValid;
  162. };
  163. static void VerifyHeapSectionIsStillValid(void* context, void* sectionStart, void* sectionEnd)
  164. {
  165. VerifyHeapSectionStillValidIterationContext* iterationContext = (VerifyHeapSectionStillValidIterationContext*)context;
  166. if (iterationContext->currentSection->sectionSize != static_cast<uint8_t*>(sectionEnd) - static_cast<uint8_t*>(sectionStart))
  167. iterationContext->wasValid = false;
  168. else if (iterationContext->currentSection->sectionStartAddress != reinterpret_cast<uint64_t>(sectionStart))
  169. iterationContext->wasValid = false;
  170. iterationContext->currentSection++;
  171. }
  172. static bool IsIL2CppManagedHeapStillValid(Il2CppManagedHeap& heap)
  173. {
  174. if (heap.sectionCount != static_cast<uint32_t>(il2cpp::gc::GarbageCollector::GetSectionCount()))
  175. return false;
  176. VerifyHeapSectionStillValidIterationContext iterationContext = { heap.sections, true };
  177. il2cpp::gc::GarbageCollector::ForEachHeapSection(&iterationContext, VerifyHeapSectionIsStillValid);
  178. return iterationContext.wasValid;
  179. }
  180. // The difficulty in capturing the managed snapshot is that we need to do quite some work with the world stopped,
  181. // to make sure that our snapshot is "valid", and didn't change as we were copying it. However, stopping the world,
  182. // makes it so you cannot take any lock or allocations. We deal with it like this:
  183. //
  184. // 1) We take note of the amount of heap sections and their sizes, and we allocate memory to copy them into.
  185. // 2) We stop the world.
  186. // 3) We check if the amount of heapsections and their sizes didn't change in the mean time. If they did, try again.
  187. // 4) Now, with the world still stopped, we memcpy() the memory from the real heapsections, into the memory that we
  188. // allocated for their copies.
  189. // 5) Start the world again.
  190. static inline void CaptureManagedHeap(Il2CppManagedHeap& heap)
  191. {
  192. for (;;)
  193. {
  194. il2cpp::gc::GarbageCollector::CallWithAllocLockHeld(CaptureHeapInfo, &heap);
  195. il2cpp::gc::GarbageCollector::StopWorld();
  196. if (IsIL2CppManagedHeapStillValid(heap))
  197. break;
  198. il2cpp::gc::GarbageCollector::StartWorld();
  199. FreeIL2CppManagedHeap(heap);
  200. }
  201. SectionIterationContext iterationContext = { heap.sections };
  202. il2cpp::gc::GarbageCollector::ForEachHeapSection(&iterationContext, CopyHeapSection);
  203. il2cpp::gc::GarbageCollector::StartWorld();
  204. }
  205. struct GCHandleTargetIterationContext
  206. {
  207. std::vector<Il2CppObject*> managedObjects;
  208. };
  209. static void GCHandleIterationCallback(Il2CppObject* managedObject, void* context)
  210. {
  211. GCHandleTargetIterationContext* ctx = static_cast<GCHandleTargetIterationContext*>(context);
  212. ctx->managedObjects.push_back(managedObject);
  213. }
  214. static inline void CaptureGCHandleTargets(Il2CppGCHandles& gcHandles)
  215. {
  216. GCHandleTargetIterationContext gcHandleTargetIterationContext;
  217. il2cpp::gc::GCHandle::WalkStrongGCHandleTargets(GCHandleIterationCallback, &gcHandleTargetIterationContext);
  218. const std::vector<Il2CppObject*>& trackedObjects = gcHandleTargetIterationContext.managedObjects;
  219. gcHandles.trackedObjectCount = static_cast<uint32_t>(trackedObjects.size());
  220. gcHandles.pointersToObjects = static_cast<uint64_t*>(IL2CPP_CALLOC(gcHandles.trackedObjectCount, sizeof(uint64_t)));
  221. for (uint32_t i = 0; i < gcHandles.trackedObjectCount; i++)
  222. gcHandles.pointersToObjects[i] = reinterpret_cast<uint64_t>(trackedObjects[i]);
  223. }
  224. void FillRuntimeInformation(Il2CppRuntimeInformation& runtimeInfo)
  225. {
  226. runtimeInfo.pointerSize = static_cast<uint32_t>(sizeof(void*));
  227. runtimeInfo.objectHeaderSize = static_cast<uint32_t>(sizeof(Il2CppObject));
  228. runtimeInfo.arrayHeaderSize = static_cast<uint32_t>(kIl2CppSizeOfArray);
  229. runtimeInfo.arraySizeOffsetInHeader = kIl2CppOffsetOfArrayLength;
  230. runtimeInfo.arrayBoundsOffsetInHeader = kIl2CppOffsetOfArrayBounds;
  231. runtimeInfo.allocationGranularity = static_cast<uint32_t>(2 * sizeof(void*));
  232. }
  233. struct il2cpp_heap_chunk
  234. {
  235. void* start;
  236. uint32_t size;
  237. };
  238. void ReportIL2CppClasses(ClassReportFunc callback, void* context)
  239. {
  240. const AssemblyVector* allAssemblies = Assembly::GetAllAssemblies();
  241. for (AssemblyVector::const_iterator it = allAssemblies->begin(); it != allAssemblies->end(); it++)
  242. {
  243. const Il2CppImage& image = *(*it)->image;
  244. for (uint32_t i = 0; i < image.typeCount; i++)
  245. {
  246. Il2CppClass* type = MetadataCache::GetTypeInfoFromHandle(MetadataCache::GetAssemblyTypeHandle(&image, i));
  247. if (type->initialized)
  248. callback(type, context);
  249. }
  250. }
  251. metadata::ArrayMetadata::WalkArrays(callback, context);
  252. metadata::ArrayMetadata::WalkSZArrays(callback, context);
  253. metadata::GenericMetadata::WalkAllGenericClasses(callback, context);
  254. MetadataCache::WalkPointerTypes(callback, context);
  255. }
  256. void ReportGcHeapSection(void * context, void * start, void * end)
  257. {
  258. il2cpp_heap_chunk chunk;
  259. chunk.start = start;
  260. //todo: change back to size_t once we change the memory profiler format and mono to use size_t for reporting chunk size
  261. chunk.size = (uint32_t)((uint8_t *)end - (uint8_t *)start);
  262. IterationContext* ctxPtr = reinterpret_cast<IterationContext*>(context);
  263. ctxPtr->callback(&chunk, ctxPtr->userData);
  264. }
  265. void ReportGcHandleTarget(Il2CppObject * obj, void * context)
  266. {
  267. IterationContext* ctxPtr = reinterpret_cast<IterationContext*>(context);
  268. ctxPtr->callback(obj, ctxPtr->userData);
  269. }
  270. Il2CppManagedMemorySnapshot* CaptureManagedMemorySnapshot()
  271. {
  272. Il2CppManagedMemorySnapshot* snapshot = static_cast<Il2CppManagedMemorySnapshot*>(IL2CPP_MALLOC_ZERO(sizeof(Il2CppManagedMemorySnapshot)));
  273. GatherMetadata(snapshot->metadata);
  274. CaptureManagedHeap(snapshot->heap);
  275. CaptureGCHandleTargets(snapshot->gcHandles);
  276. FillRuntimeInformation(snapshot->runtimeInformation);
  277. return snapshot;
  278. }
  279. void FreeCapturedManagedMemorySnapshot(Il2CppManagedMemorySnapshot* snapshot)
  280. {
  281. FreeIL2CppManagedHeap(snapshot->heap);
  282. IL2CPP_FREE(snapshot->gcHandles.pointersToObjects);
  283. Il2CppMetadataSnapshot& metadata = snapshot->metadata;
  284. for (uint32_t i = 0; i < metadata.typeCount; i++)
  285. {
  286. if ((metadata.types[i].flags & kArray) == 0)
  287. {
  288. IL2CPP_FREE(metadata.types[i].fields);
  289. IL2CPP_FREE(metadata.types[i].statics);
  290. }
  291. IL2CPP_FREE(metadata.types[i].name);
  292. }
  293. IL2CPP_FREE(metadata.types);
  294. IL2CPP_FREE(snapshot);
  295. }
  296. } // namespace MemoryInformation
  297. } // namespace vm
  298. } // namespace il2cpp