RecyclerTestObject.h 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #include "stdafx.h"
  6. #include "Core/RecyclerHeapMarkingContext.h"
  7. class RecyclerTestObject : public IRecyclerVisitedObject
  8. {
  9. protected:
  10. RecyclerTestObject()
  11. {
  12. generation = currentGeneration;
  13. }
  14. public:
  15. // IRecyclerVisitedObject implementation. We don't use FinalizableObject here as
  16. // RecyclerVisitedObjects need to have Trace called on them, which is not allowed for
  17. // FinalizableObject.
  18. virtual void Finalize(bool isShutdown) override { VerifyCondition(false); };
  19. virtual void Dispose(bool isShutdown) override { VerifyCondition(false); };
  20. virtual void OnMark() override {}
  21. virtual void Mark(RecyclerHeapHandle recycler) override { Mark(static_cast<Recycler*>(recycler)); };
  22. virtual void Trace(IRecyclerHeapMarkingContext* markContext) override { VerifyCondition(false); };
  23. virtual void Mark(Recycler * recycler) { VerifyCondition(false); };
  24. public:
  25. static void BeginWalk()
  26. {
  27. currentGeneration++;
  28. walkObjectCount = 0;
  29. walkScannedByteCount = 0;
  30. walkBarrierByteCount = 0;
  31. walkTrackedByteCount = 0;
  32. walkFinalizedByteCount = 0;
  33. walkRecyclerVisitedByteCount = 0;
  34. walkLeafByteCount = 0;
  35. maxWalkDepth = 0;
  36. currentWalkDepth = 0;
  37. wprintf(_u("-------------------------------------------\n"));
  38. wprintf(_u("Full heap walk starting. Current generation: %12llu\n"), (unsigned long long) currentGeneration);
  39. }
  40. static void WalkReference(RecyclerTestObject * object)
  41. {
  42. if (object != nullptr)
  43. {
  44. // See if we've already seen the location in this traversal.
  45. if (object->generation != currentGeneration)
  46. {
  47. // Haven't see it yet. Must be from the previous generation; increment generation and validate that.
  48. // Update to current generation to indicate we've seen it
  49. object->generation++;
  50. VerifyCondition(object->generation == currentGeneration);
  51. walkObjectCount++;
  52. currentWalkDepth++;
  53. maxWalkDepth = max(currentWalkDepth, maxWalkDepth);
  54. // Call virtual for object-specific behavior
  55. object->DoWalkObject();
  56. currentWalkDepth--;
  57. }
  58. }
  59. }
  60. static void EndWalk()
  61. {
  62. VerifyCondition(currentWalkDepth == 0);
  63. wprintf(_u("Full heap walk finished\n"));
  64. wprintf(_u("Object Count: %12llu\n"), (unsigned long long) walkObjectCount);
  65. wprintf(_u("Scanned Bytes: %12llu\n"), (unsigned long long) walkScannedByteCount);
  66. wprintf(_u("Barrier Bytes: %12llu\n"), (unsigned long long) walkBarrierByteCount);
  67. wprintf(_u("Tracked Bytes: %12llu\n"), (unsigned long long) walkTrackedByteCount);
  68. wprintf(_u("Finalized Bytes: %12llu\n"), (unsigned long long) walkFinalizedByteCount);
  69. wprintf(_u("RecyclerVisited Bytes: %12llu\n"), (unsigned long long) walkRecyclerVisitedByteCount);
  70. wprintf(_u("Leaf Bytes: %12llu\n"), (unsigned long long) walkLeafByteCount);
  71. wprintf(_u("Total Bytes: %12llu\n"), (unsigned long long) (walkScannedByteCount + walkBarrierByteCount + walkTrackedByteCount + walkFinalizedByteCount + walkLeafByteCount + walkRecyclerVisitedByteCount));
  72. wprintf(_u("Max Depth: %12llu\n"), (unsigned long long) maxWalkDepth);
  73. }
  74. // Virtual methods
  75. virtual bool TryGetRandomLocation(Location * location)
  76. {
  77. // Return false to indicate no internal locations
  78. // Subclasses can override this to handle their internal locations as appropriate
  79. return false;
  80. }
  81. virtual void DoWalkObject() = 0;
  82. protected:
  83. // Global variables
  84. // This global variable contains the "generation" of GC objects
  85. // It is used to validate the correctness of GC objects
  86. // It is assigned initially during object creation, and
  87. // updated when we walk the entire object graph in TraverseAllObjects
  88. static size_t currentGeneration;
  89. // These globals contain statistical data generated by WalkAllReferences
  90. static size_t walkObjectCount;
  91. static size_t walkScannedByteCount;
  92. static size_t walkLeafByteCount;
  93. static size_t walkBarrierByteCount;
  94. static size_t walkTrackedByteCount;
  95. static size_t walkFinalizedByteCount;
  96. static size_t walkRecyclerVisitedByteCount;
  97. static size_t currentWalkDepth;
  98. static size_t maxWalkDepth;
  99. private:
  100. // Instance variables
  101. // See comments above re currentGeneration
  102. Field(size_t) generation;
  103. };
  104. template <unsigned int minCount, unsigned int maxCount>
  105. class LeafObject : public RecyclerTestObject
  106. {
  107. private:
  108. LeafObject(unsigned int count) :
  109. count(count)
  110. {
  111. for (unsigned int i = 0; i < count; i++)
  112. {
  113. data[i] = i;
  114. }
  115. }
  116. public:
  117. static RecyclerTestObject * New()
  118. {
  119. unsigned int count = minCount + GetRandomInteger(maxCount - minCount + 1);
  120. return RecyclerNewPlusLeaf(recyclerInstance, sizeof(size_t) * count, LeafObject, count);
  121. }
  122. protected:
  123. virtual void DoWalkObject() override
  124. {
  125. walkLeafByteCount += sizeof(LeafObject) + count * sizeof(size_t);
  126. }
  127. private:
  128. Field(unsigned int) count;
  129. Field(size_t ) data[0];
  130. };
  131. template <unsigned int minCount, unsigned int maxCount>
  132. class ScannedObject : public RecyclerTestObject
  133. {
  134. private:
  135. ScannedObject(unsigned int count) :
  136. count(count)
  137. {
  138. for (unsigned int i = 0; i < count; i++)
  139. {
  140. references[i] = nullptr;
  141. }
  142. }
  143. public:
  144. static RecyclerTestObject * New()
  145. {
  146. unsigned int count = minCount + GetRandomInteger(maxCount - minCount + 1);
  147. return RecyclerNewPlus(recyclerInstance, sizeof(RecyclerTestObject *) * count, ScannedObject, count);
  148. }
  149. virtual bool TryGetRandomLocation(Location * location) override
  150. {
  151. // Get a random slot and construct a Location for it
  152. *location = Location::Scanned(&references[GetRandomInteger(count)]);
  153. return true;
  154. }
  155. protected:
  156. virtual void DoWalkObject() override
  157. {
  158. walkScannedByteCount += sizeof(ScannedObject) + count * sizeof(RecyclerTestObject *);
  159. for (unsigned int i = 0; i < count; i++)
  160. {
  161. RecyclerTestObject::WalkReference(references[i]);
  162. }
  163. }
  164. private:
  165. Field(unsigned int) count;
  166. FieldNoBarrier(RecyclerTestObject *) references[0]; // SWB-TODO: is this correct?
  167. };
  168. template <unsigned int minCount, unsigned int maxCount>
  169. class BarrierObject : public RecyclerTestObject
  170. {
  171. private:
  172. BarrierObject(unsigned int count) :
  173. count(count)
  174. {
  175. for (unsigned int i = 0; i < count; i++)
  176. {
  177. references[i] = nullptr;
  178. }
  179. }
  180. public:
  181. static RecyclerTestObject * New()
  182. {
  183. unsigned int count = minCount + GetRandomInteger(maxCount - minCount + 1);
  184. return RecyclerNewWithBarrierPlus(recyclerInstance, sizeof(RecyclerTestObject *) * count, BarrierObject, count);
  185. }
  186. virtual bool TryGetRandomLocation(Location * location) override
  187. {
  188. // Get a random slot and construct a Location for it
  189. *location = Location::Barrier(&references[GetRandomInteger(count)]);
  190. return true;
  191. }
  192. protected:
  193. virtual void DoWalkObject() override
  194. {
  195. walkBarrierByteCount += sizeof(BarrierObject) + count * sizeof(RecyclerTestObject *);
  196. for (unsigned int i = 0; i < count; i++)
  197. {
  198. RecyclerTestObject::WalkReference(references[i]);
  199. }
  200. }
  201. private:
  202. Field(unsigned int) count;
  203. FieldNoBarrier(RecyclerTestObject *) references[0]; // SWB-TODO: is this correct?
  204. };
  205. // TrackedObject must be a FinalizableObject (in order to be 'new'ed with RecyclerNewTrackedLeafPlusZ)
  206. // but it also must be a RecyclerTestObject to participate in GCStress. It must inherit from RecyclerTestObject
  207. // first so that the algined pointer is returned from New.
  208. // Fortunately, the v-tables for RecyclerTestObject and FinalizableObject line up, so the
  209. // IRecyclerVisitedObject/FinalizableObject calls end up in the right place.
  210. template <unsigned int minCount, unsigned int maxCount>
  211. class TrackedObject : public RecyclerTestObject, public FinalizableObject
  212. {
  213. private:
  214. TrackedObject(unsigned int count) :
  215. count(count)
  216. {
  217. for (unsigned int i = 0; i < count; i++)
  218. {
  219. references[i] = nullptr;
  220. }
  221. }
  222. public:
  223. static RecyclerTestObject * New()
  224. {
  225. unsigned int count = minCount + GetRandomInteger(maxCount - minCount + 1);
  226. return RecyclerNewTrackedLeafPlusZ(recyclerInstance, sizeof(RecyclerTestObject *) * count, TrackedObject, count);
  227. }
  228. virtual bool TryGetRandomLocation(Location * location) override
  229. {
  230. // Get a random slot and construct a Location for it
  231. *location = Location::Tagged(&references[GetRandomInteger(count)]);
  232. return true;
  233. }
  234. // Tracked object implementation
  235. virtual void Mark(Recycler * recycler) override
  236. {
  237. for (unsigned int i = 0; i < count; i++)
  238. {
  239. RecyclerTestObject * object = Location::Untag(references[i]);
  240. if (object != nullptr)
  241. {
  242. recycler->TryMarkNonInterior(object);
  243. }
  244. }
  245. };
  246. // Tracked objects are always finalize as well. Just do nothing.
  247. virtual void Finalize(bool isShutdown) override { }
  248. virtual void Dispose(bool isShutdown) override { };
  249. protected:
  250. virtual void DoWalkObject() override
  251. {
  252. walkTrackedByteCount += sizeof(TrackedObject) + count * sizeof(RecyclerTestObject *);
  253. for (unsigned int i = 0; i < count; i++)
  254. {
  255. RecyclerTestObject::WalkReference(Location::Untag(references[i]));
  256. }
  257. }
  258. private:
  259. Field(unsigned int) count;
  260. FieldNoBarrier(RecyclerTestObject *) references[0]; // SWB-TODO: is this correct?
  261. };
  262. // A type of object that is finalizable, but not traced/tracked so that it can be used to test finalization
  263. // for LargeHeapBlock (which currently supports the FinalizeBit, but not TrackBit)
  264. template <unsigned int minCount, unsigned int maxCount>
  265. class FinalizedObject : public RecyclerTestObject, public FinalizableObject
  266. {
  267. private:
  268. FinalizedObject(unsigned int count) :
  269. count(count)
  270. {
  271. for (unsigned int i = 0; i < count; i++)
  272. {
  273. references[i] = nullptr;
  274. }
  275. }
  276. public:
  277. static RecyclerTestObject * New()
  278. {
  279. unsigned int count = minCount + GetRandomInteger(maxCount - minCount + 1);
  280. return RecyclerNewFinalizedPlus(recyclerInstance, sizeof(RecyclerTestObject *) * count, FinalizedObject, count);
  281. }
  282. virtual bool TryGetRandomLocation(Location * location) override
  283. {
  284. // Get a random slot and construct a Location for it
  285. *location = Location::Scanned(&references[GetRandomInteger(count)]);
  286. return true;
  287. }
  288. virtual void Mark(Recycler * recycler) override { VerifyCondition(false); };
  289. // Finalize implementation.
  290. virtual void Finalize(bool isShutdown) override { }
  291. virtual void Dispose(bool isShutdown) override { }
  292. protected:
  293. virtual void DoWalkObject() override
  294. {
  295. walkFinalizedByteCount += sizeof(FinalizedObject) + count * sizeof(RecyclerTestObject *);
  296. for (unsigned int i = 0; i < count; i++)
  297. {
  298. RecyclerTestObject::WalkReference(references[i]);
  299. }
  300. }
  301. private:
  302. Field(unsigned int) count;
  303. FieldNoBarrier(RecyclerTestObject *) references[0]; // SWB-TODO: is this correct?
  304. };
  305. #ifdef RECYCLER_VISITED_HOST
  306. template <unsigned int minCount, unsigned int maxCount>
  307. class RecyclerVisitedObject : public RecyclerTestObject
  308. {
  309. public:
  310. static RecyclerTestObject * New()
  311. {
  312. // Determine a random amount of RecyclerTestObject* references to influence the size of this object.
  313. const unsigned int count = minCount + GetRandomInteger(maxCount - minCount + 1);
  314. void* mem = nullptr;
  315. const size_t size = sizeof(RecyclerVisitedObject) + (sizeof(RecyclerTestObject*) * count);
  316. // Randomly select the type of object to create
  317. AllocationType allocType = static_cast<AllocationType>(GetRandomInteger(static_cast<unsigned int>(AllocationType::Count)));
  318. switch (allocType)
  319. {
  320. case AllocationType::TraceAndFinalized:
  321. mem = RecyclerAllocVisitedHostTracedAndFinalized(recyclerInstance, size);
  322. break;
  323. case AllocationType::TraceOnly:
  324. mem = RecyclerAllocVisitedHostTraced(recyclerInstance, size);
  325. break;
  326. case AllocationType::FinalizeLeaf:
  327. mem = RecyclerAllocVisitedHostFinalized(recyclerInstance, size);
  328. break;
  329. default:
  330. Assert(allocType == AllocationType::Leaf);
  331. mem = RecyclerAllocLeaf(recyclerInstance, size);
  332. }
  333. // Construct the v-table, allocType, and count information for the new object.
  334. RecyclerVisitedObject* obj = new (mem) RecyclerVisitedObject(allocType, count);
  335. return obj;
  336. }
  337. virtual bool TryGetRandomLocation(Location * location) override
  338. {
  339. // Leaf types should not return a location
  340. if (type == AllocationType::Leaf || type == AllocationType::FinalizeLeaf)
  341. {
  342. return false;
  343. }
  344. // Get a random slot and construct a Location for it
  345. // Make this a Tagged location so that we won't inadvertently keep objects alive
  346. // in the case where this object gets put on the wrong mark stack.
  347. *location = Location::Tagged(&references[GetRandomInteger(count)]);
  348. return true;
  349. }
  350. virtual void Trace(IRecyclerHeapMarkingContext* markContext) override
  351. {
  352. VerifyCondition(type == AllocationType::TraceAndFinalized || type == AllocationType::TraceOnly);
  353. // Note that the pointers in the references arrary are technically tagged. However, this is ok
  354. // as the Mark that we're performing is an interior mark, which gets us to the right object(s).
  355. markContext->MarkObjects(reinterpret_cast<void**>(&references[0]), count, this);
  356. }
  357. virtual void Finalize(bool isShutdown) override
  358. {
  359. // Only types that request finalization should have Finalize called
  360. VerifyCondition(IsFinalizable());
  361. }
  362. virtual void Dispose(bool isShutdown) override
  363. {
  364. // Only types that request finalization should have Finalize called
  365. VerifyCondition(IsFinalizable());
  366. VerifyCondition(unmanagedResource != nullptr);
  367. BOOL success = ::HeapFree(GetProcessHeap(), 0, unmanagedResource);
  368. VerifyCondition(success != FALSE);
  369. unmanagedResource = nullptr;
  370. }
  371. protected:
  372. virtual void DoWalkObject() override
  373. {
  374. walkRecyclerVisitedByteCount += sizeof(RecyclerVisitedObject) + count * sizeof(RecyclerTestObject *);
  375. for (unsigned int i = 0; i < count; i++)
  376. {
  377. RecyclerTestObject::WalkReference(Location::Untag(references[i]));
  378. }
  379. }
  380. private:
  381. enum class AllocationType : unsigned int
  382. {
  383. TraceAndFinalized = 0,
  384. TraceOnly,
  385. FinalizeLeaf,
  386. Leaf,
  387. Count,
  388. };
  389. bool IsFinalizable() const { return type == AllocationType::TraceAndFinalized || type == AllocationType::FinalizeLeaf; }
  390. RecyclerVisitedObject(AllocationType allocType, unsigned int count) :
  391. count(count),
  392. type(allocType)
  393. {
  394. for (unsigned int i = 0; i < count; i++)
  395. {
  396. references[i] = nullptr;
  397. }
  398. if (IsFinalizable())
  399. {
  400. unmanagedResource = ::HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, GetRandomInteger(1024));
  401. VerifyCondition(unmanagedResource != nullptr);
  402. }
  403. }
  404. Field(AllocationType) type;
  405. Field(void*) unmanagedResource;
  406. Field(unsigned int) count;
  407. FieldNoBarrier(RecyclerTestObject *) references[0]; // SWB-TODO: is this correct? (copied from TrackedObject)
  408. };
  409. #endif