gcsx_mem.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. /* GCSx
  2. ** MEM.CPP
  3. **
  4. ** Memory functions that perform full error reporting and garbage collection
  5. */
  6. /*****************************************************************************
  7. ** Copyright (C) 2003-2006 Janson
  8. **
  9. ** This program is free software; you can redistribute it and/or modify
  10. ** it under the terms of the GNU General Public License as published by
  11. ** the Free Software Foundation; either version 2 of the License, or
  12. ** (at your option) any later version.
  13. **
  14. ** This program is distributed in the hope that it will be useful,
  15. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. ** GNU General Public License for more details.
  18. **
  19. ** You should have received a copy of the GNU General Public License
  20. ** along with this program; if not, write to the Free Software
  21. ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
  22. *****************************************************************************/
  23. #include "all.h"
  24. const char memFail[] = "Out of Memory Error";
  25. #define MAX_DETAILS 255
  26. char details[MAX_DETAILS];
  27. char details2[MAX_DETAILS * 2];
  28. void fatalCrash(int memoryError, const char* title, ...) {
  29. va_list arglist;
  30. va_start(arglist, title);
  31. vsnprintf(details, MAX_DETAILS, title, arglist);
  32. snprintf(details2, MAX_DETAILS * 2, PRODUCT_NAME " will shutdown. Details:\n%s\n\nYour work has been saved as:\n@TODO:", details);
  33. // @TODO: Attempt to save work in progress (at least if !memoryError)
  34. // We'll have to check other places this comes from to make sure there's
  35. // no saving restrictions (such as fatalCrash during another save)
  36. systemErrorBox(details2, "Fatal Error");
  37. exit(-1);
  38. // (never reach here)
  39. va_end(arglist);
  40. }
  41. #ifndef MEMDEBUG
  42. // These versions are non-debugging and slim- eventually, there may be
  43. // garbage collection (e.g. clearing of unneeded memory when an alloc fails)
  44. void* operator new(std::size_t size, const std::nothrow_t&) {
  45. void* ptr = malloc(size);
  46. return ptr;
  47. }
  48. void* operator new(std::size_t size) {
  49. void* ptr = malloc(size);
  50. if (ptr == NULL) {
  51. fatalCrash(1, memFail);
  52. }
  53. return ptr;
  54. }
  55. void* operator new[](std::size_t size, const std::nothrow_t&) {
  56. void* ptr = malloc(size);
  57. return ptr;
  58. }
  59. void* operator new[](std::size_t size) {
  60. void* ptr = malloc(size);
  61. if (ptr == NULL) {
  62. fatalCrash(1, memFail);
  63. }
  64. return ptr;
  65. }
  66. void operator delete(void* ptr, const std::nothrow_t&) {
  67. if (ptr == NULL) return;
  68. free(ptr);
  69. }
  70. void operator delete(void* ptr) {
  71. if (ptr == NULL) return;
  72. free(ptr);
  73. }
  74. void operator delete[](void* ptr, const std::nothrow_t&) {
  75. if (ptr == NULL) return;
  76. free(ptr);
  77. }
  78. void operator delete[](void* ptr) {
  79. if (ptr == NULL) return;
  80. free(ptr);
  81. }
  82. #else
  83. volatile std::size_t memThreshold = 0;
  84. const char* memFromFile = NULL;
  85. int memMaintainList = 0;
  86. int memDebugErrors = 0;
  87. int function_begin_end::level = 0;
  88. void setMemThreshold(std::size_t thresh) {
  89. memThreshold = thresh;
  90. }
  91. function_begin_end::function_begin_end(const char* n) {
  92. previous = memFromFile;
  93. memFromFile = n;
  94. name = n;
  95. debugStdout(DEBUG_STACK, "trace: (%d) begin %s", ++level, n);
  96. }
  97. function_begin_end::~function_begin_end() {
  98. debugStdout(DEBUG_STACK, "trace: (%d) end %s", level--, name);
  99. memFromFile = previous;
  100. }
  101. void enableMemListTrack() {
  102. memMaintainList = 1;
  103. }
  104. int memDebugAnyErrors() {
  105. return memDebugErrors;
  106. }
  107. struct memDebug {
  108. public:
  109. // Maintain circular linked list of all memory allocations
  110. memDebug* previousAlloc;
  111. memDebug* nextAlloc;
  112. enum {
  113. MEMDEBUG_STATUS_ARRAY = 1,
  114. MEMDEBUG_STATUS_DELETED = 2,
  115. MEMDEBUG_STATUS_MAX = 3,
  116. MEMDEBUG_MAGIC_COOKIE = 0x75BA23E1,
  117. MEMDEBUG_BOUNDS_PADDING = 16,
  118. MEMDEBUG_BOUNDS_COOKIE = 0x55,
  119. };
  120. // 32 bits to keep structure sized evenly
  121. Uint32 status;
  122. // Size of allocation NOT including structure/bounds
  123. Uint32 size;
  124. Uint32 magicCookie;
  125. const char* fromFile;
  126. };
  127. // First link in list
  128. memDebug firstMemDebug = { NULL, NULL, 0, 0, memDebug::MEMDEBUG_MAGIC_COOKIE, NULL };
  129. void memLeakCheck() {
  130. if ((memMaintainList) && (firstMemDebug.nextAlloc)) {
  131. memDebugErrors = 1;
  132. debugStdout("undeallocated memory: (leaks)");
  133. memDebug* report = firstMemDebug.nextAlloc;
  134. int leaks = 0;
  135. while ((report != &firstMemDebug) && (report)) {
  136. const char* error = NULL;
  137. if ((report->magicCookie == memDebug::MEMDEBUG_MAGIC_COOKIE) &&
  138. (report->status <= memDebug::MEMDEBUG_STATUS_MAX)) {
  139. // Marked as deleted?
  140. if (report->status & memDebug::MEMDEBUG_STATUS_DELETED) {
  141. error = "corrupted list (deleted item in list)";
  142. }
  143. else {
  144. // Verify circular link integrity
  145. if ((report->nextAlloc->previousAlloc != report) ||
  146. (report->previousAlloc->nextAlloc != report)) {
  147. error = "corruption in circular links";
  148. }
  149. }
  150. }
  151. else {
  152. error = "corrupted list (corrupted item in list)";
  153. }
  154. if (error) {
  155. debugStdout("error in list: %s", error);
  156. report = NULL;
  157. }
  158. else {
  159. debugStdout("allocation %p for %d bytes", report, report->size);
  160. debugDump(report, min(report->size, (Uint32)32), 1);
  161. if (report->fromFile) {
  162. debugStdout("allocation from: %s", report->fromFile);
  163. }
  164. else {
  165. debugStdout("(unknown allocation location)");
  166. }
  167. report = report->nextAlloc;
  168. ++leaks;
  169. }
  170. }
  171. debugStdout("%d leaks listed", leaks);
  172. memMaintainList = 0;
  173. }
  174. else if (memMaintainList) {
  175. debugStdout("No memory leaks detected");
  176. }
  177. }
  178. /*
  179. // Non-inline versions, if needed
  180. void* operator new(std::size_t size, const std::nothrow_t&) {
  181. return new_debug(size, 0, 1);
  182. }
  183. void* operator new(std::size_t size) {
  184. return new_debug(size, 0, 0);
  185. }
  186. void* operator new[](std::size_t size, const std::nothrow_t&) {
  187. return new_debug(size, 1, 1);
  188. }
  189. void* operator new[](std::size_t size) {
  190. return new_debug(size, 1, 0);
  191. }
  192. void operator delete(void* ptr, const std::nothrow_t&) {
  193. new_delete(ptr, 0);
  194. }
  195. void operator delete(void* ptr) {
  196. new_delete(ptr, 0);
  197. }
  198. void operator delete[](void* ptr, const std::nothrow_t&) {
  199. new_delete(ptr, 1);
  200. }
  201. void operator delete[](void* ptr) {
  202. new_delete(ptr, 1);
  203. }
  204. */
  205. void* new_debug(std::size_t size, int array, int noThrow) {
  206. if (memThreshold) {
  207. if (size > memThreshold) {
  208. if (noThrow) return NULL;
  209. fatalCrash(1, memFail);
  210. }
  211. }
  212. void* ptr = malloc(size + sizeof(memDebug) + memDebug::MEMDEBUG_BOUNDS_PADDING * 2);
  213. if (ptr == NULL) {
  214. if (noThrow) return NULL;
  215. fatalCrash(1, memFail);
  216. }
  217. // Maintain circular list
  218. if (memMaintainList) {
  219. if (firstMemDebug.previousAlloc) {
  220. ((memDebug*)ptr)->previousAlloc = firstMemDebug.previousAlloc;
  221. ((memDebug*)ptr)->nextAlloc = &firstMemDebug;
  222. firstMemDebug.previousAlloc->nextAlloc = (memDebug*)ptr;
  223. firstMemDebug.previousAlloc = (memDebug*)ptr;
  224. }
  225. else {
  226. // First item in list
  227. ((memDebug*)ptr)->previousAlloc = &firstMemDebug;
  228. ((memDebug*)ptr)->nextAlloc = &firstMemDebug;
  229. firstMemDebug.previousAlloc = (memDebug*)ptr;
  230. firstMemDebug.nextAlloc = (memDebug*)ptr;
  231. }
  232. }
  233. else {
  234. ((memDebug*)ptr)->previousAlloc = NULL;
  235. ((memDebug*)ptr)->nextAlloc = NULL;
  236. }
  237. // Bounds checking
  238. Uint8* start = ((Uint8*)ptr) + sizeof(memDebug);
  239. for (int i = 0; i < memDebug::MEMDEBUG_BOUNDS_PADDING; ++i, ++start) {
  240. *start = memDebug::MEMDEBUG_BOUNDS_COOKIE;
  241. }
  242. start += size;
  243. for (int i = 0; i < memDebug::MEMDEBUG_BOUNDS_PADDING; ++i, ++start) {
  244. *start = memDebug::MEMDEBUG_BOUNDS_COOKIE;
  245. }
  246. // Structure data
  247. ((memDebug*)ptr)->status = array ? memDebug::MEMDEBUG_STATUS_ARRAY : 0;
  248. ((memDebug*)ptr)->size = size;
  249. ((memDebug*)ptr)->magicCookie = memDebug::MEMDEBUG_MAGIC_COOKIE;
  250. if (memFromFile) {
  251. ((memDebug*)ptr)->fromFile = memFromFile;
  252. }
  253. else {
  254. ((memDebug*)ptr)->fromFile = NULL;
  255. }
  256. debugStdout(DEBUG_MEMORY, array ? "memory: new[] %p (%d)" : "memory: new %p (%d)", ptr, size);
  257. return ((Uint8*)ptr) + sizeof(memDebug) + memDebug::MEMDEBUG_BOUNDS_PADDING;
  258. }
  259. void new_delete(void* ptr, int array) {
  260. if (ptr == NULL) {
  261. debugStdout(DEBUG_MEMORY, array ? "memory: del[] NULL" : "memory: del NULL");
  262. return;
  263. }
  264. ptr = ((Uint8*)ptr) - sizeof(memDebug) - memDebug::MEMDEBUG_BOUNDS_PADDING;
  265. // Verify structure data
  266. const char* error = NULL;
  267. const char* fromFile = NULL;
  268. if ((((memDebug*)ptr)->magicCookie == memDebug::MEMDEBUG_MAGIC_COOKIE) &&
  269. (((memDebug*)ptr)->status <= memDebug::MEMDEBUG_STATUS_MAX)) {
  270. // (now that we know we can probably trust this pointer)
  271. fromFile = ((memDebug*)ptr)->fromFile;
  272. // Marked as deleted?
  273. if (((memDebug*)ptr)->status & memDebug::MEMDEBUG_STATUS_DELETED) {
  274. error = "duplicate deletion";
  275. }
  276. else {
  277. // Verify circular link integrity
  278. if ((((memDebug*)ptr)->nextAlloc) && (((memDebug*)ptr)->previousAlloc)) {
  279. if ((((memDebug*)ptr)->nextAlloc->previousAlloc == (memDebug*)ptr) &&
  280. (((memDebug*)ptr)->previousAlloc->nextAlloc == (memDebug*)ptr)) {
  281. // Remove from circular linked list
  282. if (((memDebug*)ptr)->nextAlloc == ((memDebug*)ptr)->previousAlloc) {
  283. // First item in list
  284. firstMemDebug.previousAlloc = NULL;
  285. firstMemDebug.nextAlloc = NULL;
  286. }
  287. else {
  288. ((memDebug*)ptr)->previousAlloc->nextAlloc = ((memDebug*)ptr)->nextAlloc;
  289. ((memDebug*)ptr)->nextAlloc->previousAlloc = ((memDebug*)ptr)->previousAlloc;
  290. }
  291. }
  292. else {
  293. error = "corruption in circular links (probably duplicate deletion)";
  294. }
  295. }
  296. if (!error) {
  297. // Check bounds
  298. Uint8* start = ((Uint8*)ptr) + sizeof(memDebug);
  299. for (int i = 0; i < memDebug::MEMDEBUG_BOUNDS_PADDING; ++i, ++start) {
  300. if (*start != memDebug::MEMDEBUG_BOUNDS_COOKIE) {
  301. error = "lower bound overflow";
  302. break;
  303. }
  304. }
  305. start += ((memDebug*)ptr)->size;
  306. for (int i = 0; i < memDebug::MEMDEBUG_BOUNDS_PADDING; ++i, ++start) {
  307. if (*start != memDebug::MEMDEBUG_BOUNDS_COOKIE) {
  308. error = "upper bound overflow";
  309. break;
  310. }
  311. }
  312. // Check type
  313. if ((!array) && (((memDebug*)ptr)->status & memDebug::MEMDEBUG_STATUS_ARRAY)) {
  314. error = "delete used on array";
  315. }
  316. if ((array) && (!(((memDebug*)ptr)->status & memDebug::MEMDEBUG_STATUS_ARRAY))) {
  317. error = "delete[] used on non-array";
  318. }
  319. }
  320. // Mark as deleted
  321. ((memDebug*)ptr)->status |= memDebug::MEMDEBUG_STATUS_DELETED;
  322. }
  323. }
  324. else {
  325. error = "deleting non-allocated pointer (possibly duplicate deletion)";
  326. }
  327. if (error) {
  328. memDebugErrors = 1;
  329. debugStdout("memory error: %s", error);
  330. if (fromFile) {
  331. debugStdout("allocation from: %s", fromFile);
  332. if (memFromFile) {
  333. debugStdout("deallocation at: %s", memFromFile);
  334. }
  335. }
  336. else {
  337. debugStdout("(unknown allocation location)");
  338. if (memFromFile) {
  339. debugStdout("deallocation might be at: %s", memFromFile);
  340. }
  341. }
  342. }
  343. debugStdout(DEBUG_MEMORY, array ? "memory: del[] %p" : "memory: del %p", ptr);
  344. free(ptr);
  345. }
  346. #endif