1
0

MallocAllocator.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. /* vim: set expandtab ts=4 sw=4: */
  2. /*
  3. * You may redistribute this program and/or modify it under the terms of
  4. * the GNU General Public License as published by the Free Software Foundation,
  5. * either version 3 of the License, or (at your option) any later version.
  6. *
  7. * This program is distributed in the hope that it will be useful,
  8. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. * GNU General Public License for more details.
  11. *
  12. * You should have received a copy of the GNU General Public License
  13. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. #define string_strrchr
  16. #include "util/platform/libc/string.h"
  17. #include <inttypes.h>
  18. #include <stdio.h>
  19. #include "memory/Allocator.h"
  20. #include "memory/MallocAllocator.h"
  21. #include "memory/MallocAllocator_pvt.h"
  22. #include "util/Bits.h"
  23. #include "util/log/Log.h"
  24. /** This provides the padding for each line based on the depth in the stack. */
  25. struct Unroller;
  26. struct Unroller
  27. {
  28. const char* const content;
  29. const struct Unroller* const last;
  30. };
  31. static void writeUnroller(const struct Unroller* unroller)
  32. {
  33. if (unroller) {
  34. writeUnroller(unroller->last);
  35. fprintf(stderr, "%s", unroller->content);
  36. }
  37. }
  38. static void unroll(struct MallocAllocator_pvt* context, struct Unroller* unroller)
  39. {
  40. writeUnroller(unroller);
  41. const char* ident = (context->identFile) ? strrchr(context->identFile, '/') : " UNKNOWN";
  42. ident = ident ? ident + 1 : context->identFile;
  43. fprintf(stderr, "%s:%d [%" PRIu64 "] bytes\n",
  44. ident,
  45. context->identLine,
  46. (uint64_t)context->allocatedHere);
  47. if (context->firstChild) {
  48. unroll(context->firstChild, &(struct Unroller) {
  49. .content = ((context->nextSibling) ? "| " : " "),
  50. .last = unroller
  51. });
  52. }
  53. if (context->nextSibling) {
  54. unroll(context->nextSibling, unroller);
  55. }
  56. }
  57. Gcc_NORETURN
  58. static void failure(struct MallocAllocator_pvt* context,
  59. const char* message,
  60. const char* identFile,
  61. int identLine)
  62. {
  63. // get the root allocator.
  64. struct MallocAllocator_pvt* rootAlloc = context;
  65. while (rootAlloc->lastSibling) {
  66. rootAlloc = rootAlloc->lastSibling;
  67. }
  68. // can't use this allocator because it failed.
  69. unroll(rootAlloc, NULL);
  70. fprintf(stderr, "%s:%d Fatal error: [%s] spaceAvailable [%" PRIu64 "]\n",
  71. identFile,
  72. identLine,
  73. message,
  74. *context->spaceAvailable);
  75. exit(0);
  76. }
  77. static inline void* newAllocation(struct MallocAllocator_pvt* context,
  78. size_t size,
  79. const char* identFile,
  80. int identLine)
  81. {
  82. int64_t realSize = sizeof(struct MallocAllocator_Allocation) + size;
  83. if (*(context->spaceAvailable) <= realSize) {
  84. failure(context, "Out of memory, limit exceeded.", identFile, identLine);
  85. }
  86. *(context->spaceAvailable) -= realSize;
  87. context->allocatedHere += realSize;
  88. struct MallocAllocator_Allocation* alloc = malloc(realSize);
  89. if (alloc == NULL) {
  90. failure(context, "Out of memory, malloc() returned NULL.", identFile, identLine);
  91. }
  92. alloc->next = context->allocations;
  93. alloc->size = realSize;
  94. context->allocations = alloc;
  95. return (void*) (alloc + 1);
  96. }
  97. /** @see Allocator->free() */
  98. static void freeAllocator(const struct Allocator* allocator, const char* identFile, int identLine)
  99. {
  100. struct MallocAllocator_pvt* context = Identity_cast((struct MallocAllocator_pvt*) allocator);
  101. // Do the onFree jobs.
  102. struct MallocAllocator_OnFreeJob* job = context->onFree;
  103. while (job != NULL) {
  104. job->callback(job->callbackContext);
  105. job = job->next;
  106. }
  107. // Free all of the child allocators.
  108. struct MallocAllocator_pvt* child = context->firstChild;
  109. while (child != NULL) {
  110. struct MallocAllocator_pvt* nextChild = child->nextSibling;
  111. freeAllocator(&child->pub, identFile, identLine);
  112. child = nextChild;
  113. }
  114. // Remove this allocator from the sibling list.
  115. if (context->lastSibling != NULL &&
  116. context->lastSibling->nextSibling == context) {
  117. context->lastSibling->nextSibling = context->nextSibling;
  118. } else if (context->lastSibling != NULL &&
  119. context->lastSibling->firstChild == context) {
  120. context->lastSibling->firstChild = context->nextSibling;
  121. } else if (context->lastSibling != NULL) {
  122. failure(context, "The last sibling of this allocator has no reference to it.",
  123. identFile, identLine);
  124. }
  125. if (context->nextSibling != NULL) {
  126. context->nextSibling->lastSibling = context->lastSibling;
  127. }
  128. // Free all of the allocations including the one which holds the allocator.
  129. struct MallocAllocator_Allocation* loc = context->allocations;
  130. while (loc != NULL) {
  131. *(context->spaceAvailable) += loc->size;
  132. struct MallocAllocator_Allocation* nextLoc = loc->next;
  133. // TODO: make this optional.
  134. Bits_memset(loc, 0xff, loc->size);
  135. free(loc);
  136. loc = nextLoc;
  137. }
  138. }
  139. /** @see Allocator->malloc() */
  140. static void* allocatorMalloc(size_t length,
  141. const struct Allocator* allocator,
  142. const char* identFile,
  143. int identLine)
  144. {
  145. struct MallocAllocator_pvt* ctx = Identity_cast((struct MallocAllocator_pvt*) allocator);
  146. return newAllocation(ctx, length, identFile, identLine);
  147. }
  148. /** @see Allocator->calloc() */
  149. static void* allocatorCalloc(size_t length,
  150. size_t count,
  151. const struct Allocator* allocator,
  152. const char* identFile,
  153. int identLine)
  154. {
  155. void* pointer = allocatorMalloc(length * count, allocator, identFile, identLine);
  156. Bits_memset(pointer, 0, length * count);
  157. return pointer;
  158. }
  159. /** @see Allocator->clone() */
  160. static void* allocatorClone(size_t length,
  161. const struct Allocator* allocator,
  162. const void* toClone,
  163. const char* identFile,
  164. int identLine)
  165. {
  166. void* pointer = allocatorMalloc(length, allocator, identFile, identLine);
  167. Bits_memcpy(pointer, toClone, length);
  168. return pointer;
  169. }
  170. /** @see Allocator->realloc() */
  171. static void* allocatorRealloc(const void* original,
  172. size_t size,
  173. const struct Allocator* allocator,
  174. const char* identFile,
  175. int identLine)
  176. {
  177. if (original == NULL) {
  178. return allocatorMalloc(size, allocator, identFile, identLine);
  179. }
  180. struct MallocAllocator_pvt* context = Identity_cast((struct MallocAllocator_pvt*) allocator);
  181. struct MallocAllocator_Allocation** locPtr = &context->allocations;
  182. struct MallocAllocator_Allocation* origLoc =
  183. ((struct MallocAllocator_Allocation*) original) - 1;
  184. for (;;) {
  185. struct MallocAllocator_Allocation* loc = *locPtr;
  186. if (loc == NULL) {
  187. failure(context,
  188. "Reallocation of memory which was not allocated using this allocator.",
  189. identFile,
  190. identLine);
  191. }
  192. if (loc == origLoc) {
  193. break;
  194. }
  195. locPtr = &loc->next;
  196. }
  197. struct MallocAllocator_Allocation* nextLoc = origLoc->next;
  198. size_t realSize = sizeof(struct MallocAllocator_Allocation) + size;
  199. if (*(context->spaceAvailable) + origLoc->size < realSize) {
  200. failure(context, "Out of memory, limit exceeded.", identFile, identLine);
  201. }
  202. *(context->spaceAvailable) += origLoc->size;
  203. *(context->spaceAvailable) -= realSize;
  204. context->allocatedHere -= origLoc->size;
  205. context->allocatedHere += realSize;
  206. struct MallocAllocator_Allocation* alloc = realloc(origLoc, realSize);
  207. if (alloc == NULL) {
  208. failure(context, "Out of memory, realloc() returned NULL.", identFile, identLine);
  209. }
  210. alloc->next = nextLoc;
  211. alloc->size = realSize;
  212. *locPtr = alloc;
  213. return (void*) (alloc + 1);
  214. }
  215. /** @see Allocator_child() */
  216. static struct Allocator* childAllocator(const struct Allocator* allocator,
  217. const char* identFile,
  218. int identLine)
  219. {
  220. struct MallocAllocator_pvt* context = Identity_cast((struct MallocAllocator_pvt*) allocator);
  221. uint32_t allocSize =
  222. sizeof(struct MallocAllocator_FirstCtx) + sizeof(struct MallocAllocator_Allocation);
  223. if (*(context->spaceAvailable) <= allocSize) {
  224. failure(context, "Out of memory, limit exceeded.", identFile, identLine);
  225. }
  226. struct Allocator* childAlloc = MallocAllocator_new(allocSize);
  227. *(context->spaceAvailable) -= allocSize;
  228. struct MallocAllocator_pvt* child = (struct MallocAllocator_pvt*) childAlloc;
  229. child->maxSpace = context->maxSpace;
  230. child->lastSibling = context;
  231. child->nextSibling = context->firstChild;
  232. if (context->firstChild != NULL) {
  233. context->firstChild->lastSibling = child;
  234. }
  235. child->spaceAvailable = context->spaceAvailable;
  236. child->identFile = identFile;
  237. child->identLine = identLine;
  238. context->firstChild = child;
  239. return childAlloc;
  240. }
  241. /** @see Allocator->onFree() */
  242. static void* addOnFreeJob(void (* callback)(void* callbackContext),
  243. void* callbackContext,
  244. const struct Allocator* allocator)
  245. {
  246. struct MallocAllocator_pvt* context = Identity_cast((struct MallocAllocator_pvt*) allocator);
  247. struct MallocAllocator_OnFreeJob* newJob =
  248. Allocator_calloc(allocator, sizeof(struct MallocAllocator_OnFreeJob), 1);
  249. newJob->callback = callback;
  250. newJob->callbackContext = callbackContext;
  251. struct MallocAllocator_OnFreeJob* job = context->onFree;
  252. if (job == NULL) {
  253. context->onFree = newJob;
  254. } else {
  255. while (job->next != NULL) {
  256. job = job->next;
  257. }
  258. job->next = newJob;
  259. }
  260. return newJob;
  261. }
  262. static bool removeOnFreeJob(void* toRemove, struct Allocator* alloc)
  263. {
  264. struct MallocAllocator_pvt* context = Identity_cast((struct MallocAllocator_pvt*) alloc);
  265. struct MallocAllocator_OnFreeJob** jobPtr = &(context->onFree);
  266. while (*jobPtr != NULL) {
  267. if (*jobPtr == toRemove) {
  268. *jobPtr = (*jobPtr)->next;
  269. return true;
  270. }
  271. jobPtr = &(*jobPtr)->next;
  272. }
  273. return false;
  274. }
  275. /** @see MallocAllocator.h */
  276. struct Allocator* MallocAllocator_newWithIdentity(size_t sizeLimit,
  277. const char* identFile,
  278. int identLine)
  279. {
  280. struct MallocAllocator_FirstCtx stackContext = {
  281. .spaceAvailable = (sizeLimit == 0) ? SIZE_MAX : sizeLimit +
  282. sizeof(struct MallocAllocator_FirstCtx) + sizeof(struct Allocator),
  283. .context = {
  284. .identFile = identFile,
  285. .identLine = identLine
  286. }
  287. };
  288. stackContext.context.spaceAvailable = &stackContext.spaceAvailable;
  289. struct MallocAllocator_FirstCtx* firstContext =
  290. newAllocation(&stackContext.context,
  291. sizeof(struct MallocAllocator_FirstCtx),
  292. identFile,
  293. identLine);
  294. Bits_memcpyConst(firstContext, &stackContext, sizeof(struct MallocAllocator_FirstCtx));
  295. struct MallocAllocator_pvt* context = &firstContext->context;
  296. context->spaceAvailable = &firstContext->spaceAvailable;
  297. context->maxSpace = firstContext->spaceAvailable;
  298. struct Allocator allocator = {
  299. .context = context,
  300. .free = freeAllocator,
  301. .malloc = allocatorMalloc,
  302. .calloc = allocatorCalloc,
  303. .clone = allocatorClone,
  304. .realloc = allocatorRealloc,
  305. .child = childAllocator,
  306. .onFree = addOnFreeJob,
  307. .notOnFree = removeOnFreeJob
  308. };
  309. Bits_memcpyConst(&context->pub, &allocator, sizeof(struct Allocator));
  310. Identity_set(context);
  311. return &context->pub;
  312. }
  313. size_t MallocAllocator_bytesAllocated(struct Allocator* allocator)
  314. {
  315. struct MallocAllocator_pvt* context = Identity_cast((struct MallocAllocator_pvt*) allocator);
  316. return context->maxSpace - *context->spaceAvailable;
  317. }