Newer
Older
project / modules / libscpp / bkup / scpp_memory.cpp
Nomura Kei on 26 Sep 2022 17 KB UPDATE
  1. /**
  2. * @file scpp_memory.cpp
  3. * @bried メモリ管理モジュール
  4. * @author Nomura Kei
  5. * @copyright 2003 - 2022 Nomura Kei
  6. */
  7. #include <iostream>
  8. #include <cstdlib>
  9. #include <cstring>
  10. #include <cerrno>
  11. #include <mutex>
  12.  
  13.  
  14. // 本来の malloc, free を利用するため、ENABLED_MEMORY_MANAGE を無効にする
  15. #ifdef ENABLED_MEMORY_MANAGE
  16. #undef ENABLED_MEMORY_MANAGE
  17. #endif
  18. #include <scpp_memory.hpp>
  19.  
  20.  
  21.  
  22. using namespace scpp;
  23. namespace
  24. {
  25. int const PADDING = sizeof(int) * 2;
  26. int const MARK_ALLOCATED = 0x12340000;
  27. int const MARK_ALLOCATED_NEW = 0x12341111;
  28. int const MARK_ALLOCATED_NEW_ARRAY = 0x12342222;
  29. int const MARK_DELETED = 0xFEDCBA00;
  30.  
  31. bool infoIsInitialized = false;
  32. std::mutex memMtx; //< 管理メモリ操作用 Mutex
  33. MemoryInfo infoHead; //< 管理メモリ先頭
  34. MemoryInfo infoTail; //< 管理メモリ末尾
  35. MemoryInfo infoError; //< エラー発生時用
  36. MemoryListener* listener = 0; //< 通知リスナ
  37.  
  38. // プロトタイプ宣言
  39. void init();
  40. void add(MemoryInfo* entry);
  41. void remove(MemoryInfo* entry);
  42. void* allocate(std::size_t n, int mark, const char* file, int line, const char* func);
  43. void* reallocate(void* ptr, std::size_t n, int mark, const char* file, int line, const char* func, bool isMng);
  44.  
  45. /**
  46. * new ハンドラを取得します。
  47. *
  48. * @return new ハンドラ
  49. */
  50. std::new_handler getNewHandler()
  51. {
  52. std::new_handler p = std::set_new_handler(0);
  53. std::set_new_handler(p);
  54. return p;
  55. }
  56.  
  57. /**
  58. * メモリ管理初期化.
  59. */
  60. void init()
  61. {
  62. if (!infoIsInitialized)
  63. {
  64. infoIsInitialized = true;
  65. infoHead.fileName = infoTail.fileName = 0;
  66. infoHead.lineNumber = infoTail.lineNumber = 0;
  67. infoHead.functionName = infoTail.functionName = 0;
  68. infoHead.size = infoTail.size = 0;
  69. infoHead._prev = infoHead._next = &infoTail;
  70. infoTail._prev = infoTail._next = &infoHead;
  71. infoHead._mark = infoTail._mark = MARK_DELETED;
  72. // エラー処理用
  73. infoError.fileName = 0;
  74. infoError.lineNumber = 0;
  75. infoError.functionName = 0;
  76. infoError.size = 0;
  77. infoError._prev = 0;
  78. infoError._next = 0;
  79. infoError._mark = MARK_DELETED;
  80. }
  81. }
  82. /**
  83. * メモリエントリを追加します.
  84. *
  85. * @param entry 追加するエントリ
  86. */
  87. void add(MemoryInfo* entry)
  88. {
  89. std::lock_guard<std::mutex> lock(memMtx);
  90. init();
  91. entry->_next = &infoTail;
  92. entry->_prev = infoTail._prev;
  93. infoTail._prev->_next = entry;
  94. infoTail._prev = entry;
  95. if (listener)
  96. { // ハンドラが設定されていれば実行する
  97. listener->notifyAlloc(*entry);
  98. }
  99. }
  100. /**
  101. * メモリエントリを削除します.
  102. *
  103. * @param entry 削除するエントリ
  104. */
  105. void remove(MemoryInfo* entry)
  106. {
  107. std::lock_guard<std::mutex> lock(memMtx);
  108. entry->_prev->_next = entry->_next;
  109. entry->_next->_prev = entry->_prev;
  110. if (listener)
  111. { // ハンドラが設定されていれば実行する
  112. listener->notifyFree(*entry);
  113. }
  114. }
  115. /**
  116. * 指定されたサイズのメモリを確保します.
  117. *
  118. * @param n 確保するメモリサイズ
  119. * @param mark マーク
  120. * @param file ファイル
  121. * @param line 行番号
  122. * @param func 関数名
  123. */
  124. void* allocate(std::size_t n, int mark, const char* file, int line, const char* func)
  125. {
  126. MemoryInfo* entry;
  127. // メモリ確保 [@see C++ Programming Language 3rd $14.4.5]
  128. for (;;)
  129. {
  130. entry = static_cast<MemoryInfo*> (::malloc(n + sizeof(MemoryInfo) + PADDING));
  131. if (entry != 0) { break; }
  132. if (std::new_handler nhandler = getNewHandler())
  133. {
  134. nhandler();
  135. }
  136. else
  137. {
  138. return 0;
  139. }
  140. }
  141. void* ptr = (entry + 1);
  142. entry->fileName = file;
  143. entry->lineNumber = line;
  144. entry->functionName = func;
  145. entry->size = n;
  146. entry->_mark = mark;
  147. entry->_data = ptr;
  148. // エントリに追加
  149. add(entry);
  150. return ptr;
  151. }
  152. /**
  153. * 指定されたサイズのメモリを確保します.
  154. *
  155. * @param n 確保するメモリサイズ
  156. * @param mark マーク
  157. * @param file ファイル
  158. * @param line 行番号
  159. * @param func 関数名
  160. */
  161. void* reallocate(void* ptr, std::size_t n, int mark, const char* file, int line, const char* func, bool isMng)
  162. {
  163. if (isMng)
  164. { // 管理メモリの場合, いったん削除
  165. remove(static_cast<MemoryInfo*>(ptr));
  166. }
  167. MemoryInfo* entry = static_cast<MemoryInfo*> (::realloc(ptr, n + sizeof(MemoryInfo) + PADDING));
  168. if (entry == 0)
  169. {
  170. // メモリ確保失敗 -> 元の領域は開放されないので再管理する
  171. add(static_cast<MemoryInfo*>(ptr));
  172. errno = ENOMEM;
  173. if (listener)
  174. { // エラーハンドラが登録されている
  175. infoError.size = n;
  176. infoError.fileName = file;
  177. infoError.lineNumber = line;
  178. infoError.functionName = func;
  179. listener->notifyError(infoError, "can't realloc");
  180. }
  181. return 0;
  182. }
  183. if (!isMng)
  184. { // 管理対象外メモリの場合
  185. // |<---- 新たな確保領域 ---->|
  186. // +----------+---------------+
  187. // |元々の領域|追加分+管理領域|
  188. // +----------+---------------+
  189. // ↓
  190. // +--------+----------+------+
  191. // |管理領域|元々の領域|追加分|
  192. // +--------+----------+------+
  193. memmove((entry + 1), entry, n);
  194. }
  195. // 管理領域に情報を書き込む
  196. void* nptr = (entry + 1);
  197. entry->fileName = file;
  198. entry->lineNumber = line;
  199. entry->functionName = func;
  200. entry->size = n;
  201. entry->_mark = mark;
  202. entry->_data = nptr;
  203. // エントリに追加
  204. add(entry);
  205. return nptr;
  206. }
  207. } // namespace 無名
  208. ////////////////////////////////////////////////////////////////////////////////
  209. //
  210. // 以下, new / delete のオーバライド
  211. //
  212. void* operator new(std::size_t size)
  213. {
  214. void* p = allocate(size, MARK_ALLOCATED_NEW,
  215. MemoryManager::fileName,
  216. MemoryManager::lineNumber,
  217. MemoryManager::functionName);
  218. if (p == 0)
  219. {
  220. throw std::bad_alloc();
  221. }
  222. return p;
  223. }
  224.  
  225. void* operator new[](std::size_t size)
  226. {
  227. void* p = allocate(size, MARK_ALLOCATED_NEW_ARRAY,
  228. MemoryManager::fileName,
  229. MemoryManager::lineNumber,
  230. MemoryManager::functionName);
  231. if (p == 0)
  232. {
  233. throw std::bad_alloc();
  234. }
  235. return p;
  236. }
  237. void* operator new(std::size_t size, const std::nothrow_t&) noexcept
  238. {
  239. void* p = allocate(size, MARK_ALLOCATED_NEW,
  240. MemoryManager::fileName,
  241. MemoryManager::lineNumber,
  242. MemoryManager::functionName);
  243. return p;
  244. }
  245. void* operator new[](std::size_t size, const std::nothrow_t&) noexcept
  246. {
  247. void* p = allocate(size, MARK_ALLOCATED_NEW_ARRAY,
  248. MemoryManager::fileName,
  249. MemoryManager::lineNumber,
  250. MemoryManager::functionName);
  251. return p;
  252. }
  253.  
  254. void operator delete(void* p) noexcept
  255. {
  256. if (p == 0)
  257. { // null ポインタの場合なにもしない
  258. return;
  259. }
  260. MemoryInfo* entry = (MemoryInfo*) p;
  261. entry--;
  262. switch (entry->_mark)
  263. {
  264. case MARK_ALLOCATED: // malloc で確保されたメモリ
  265. errno = EINVAL;
  266. if (listener) { listener->notifyError(*entry, "please use free"); }
  267. break;
  268. case MARK_DELETED: // 削除済みのメモリ
  269. errno = EINVAL;
  270. if (listener) { listener->notifyError(*entry, "deleted pointer"); }
  271. break;
  272. case MARK_ALLOCATED_NEW: // new で確保したメモリ
  273. remove(entry);
  274. entry->_mark = MARK_DELETED;
  275. entry->size = 0;
  276. ::free(entry);
  277. break;
  278. case MARK_ALLOCATED_NEW_ARRAY: // new[] で確保したメモリ
  279. errno = EINVAL;
  280. if (listener) { listener->notifyError(*entry, "please use delete[]"); }
  281. break;
  282. default: // 管理外メモリ開放
  283. entry++;
  284. ::free(entry);
  285. break;
  286. }
  287. }
  288. void operator delete(void* p, std::size_t) noexcept
  289. {
  290. operator delete(p);
  291. }
  292.  
  293. void operator delete(void* p, const std::nothrow_t&) noexcept
  294. {
  295. operator delete(p);
  296. }
  297. void operator delete[](void* p) noexcept
  298. {
  299. if (p == 0)
  300. { // null ポインタの場合なにもしない
  301. return;
  302. }
  303. MemoryInfo* entry = (MemoryInfo*) p;
  304. entry--;
  305. switch (entry->_mark)
  306. {
  307. case MARK_ALLOCATED: // malloc で確保されたメモリ
  308. errno = EINVAL;
  309. if (listener) { listener->notifyError(*entry, "please use free"); }
  310. break;
  311. case MARK_DELETED: // 削除済みのメモリ
  312. errno = EINVAL;
  313. if (listener) { listener->notifyError(*entry, "deleted pointer"); }
  314. break;
  315. case MARK_ALLOCATED_NEW: // new で確保したメモリ
  316. errno = EINVAL;
  317. if (listener) { listener->notifyError(*entry, "please use delete"); }
  318. break;
  319. case MARK_ALLOCATED_NEW_ARRAY: // new[] で確保したメモリ
  320. remove(entry);
  321. entry->_mark = MARK_DELETED;
  322. entry->size = 0;
  323. ::free(entry);
  324. break;
  325. default: // 管理外メモリ開放
  326. entry++;
  327. ::free(entry);
  328. break;
  329. }
  330. }
  331.  
  332. void operator delete[](void* p, std::size_t) noexcept
  333. {
  334. operator delete[](p);
  335. }
  336.  
  337. void operator delete[](void* p, const std::nothrow_t&) noexcept
  338. {
  339. operator delete[](p);
  340. }
  341. namespace scpp
  342. {
  343. ////////////////////////////////////////////////////////////////////////////////
  344. //
  345. // デフォルトのメモリ確保/開放時に実行されるリスナ
  346. //
  347. DefaultMemoryListener::DefaultMemoryListener()
  348. {
  349. // NOP
  350. }
  351. DefaultMemoryListener::~DefaultMemoryListener()
  352. {
  353. // NOP
  354. }
  355. /**
  356. * メモリが確保された際に実行されるリスナ.
  357. * デフォルトのリスナでは何もしません.
  358. *
  359. * @param info メモリ情報
  360. */
  361. void DefaultMemoryListener::notifyAlloc(const MemoryInfo&)
  362. {
  363. // NOP
  364. }
  365. /**
  366. * メモリが開放された際に実行されるリスナ.
  367. * デフォルトのリスナでは何もしません.
  368. *
  369. * @param info メモリ情報
  370. */
  371. void DefaultMemoryListener::notifyFree(const MemoryInfo&)
  372. {
  373. // NOP
  374. }
  375. /**
  376. * メモリの確保/開放でエラーが発生した際に実行されるリスナ.
  377. * エラー内容をエラー出力します.
  378. *
  379. * @param info メモリ情報
  380. * @param msg メッセージ
  381. */
  382. void DefaultMemoryListener::notifyError(const MemoryInfo& info, const std::string& msg)
  383. {
  384. std::cerr << "MemoryError: " << msg << std::endl;
  385. std::cerr << "\tat " << info.fileName << ":" << info.lineNumber << ":" << info.functionName
  386. << " (size = " << info.size << ")" << std::endl;
  387. }
  388. ////////////////////////////////////////////////////////////////////////////////
  389. //
  390. // メモリ管理
  391. //
  392. /**
  393. * メモリ管理.
  394. * メモリの確保, 開放を行います.
  395. * リスナを登録することで, メモリ確保, 開放, エラー発生時の通知を
  396. * 受信することができます.
  397. *
  398. * 以下のようにメモリを管理しています.
  399. * @code
  400. *
  401. * |<----管理メモリ------------->|
  402. * +--------+--------------------+
  403. * |管理領域|ユーザ使用メモリ領域|
  404. * +--------+--------------------+
  405. * ↑ユーザにはこの位置を返す
  406. *
  407. * c++ なので std::list 等を使っても良いが,
  408. * list のメモリ確保でエラーがでると本末転倒なので,
  409. * メモリ確保時に管理領域をまとめて確保&管理する.
  410. * @endcode
  411. *
  412. */
  413. namespace MemoryManager
  414. {
  415. thread_local const char* fileName = ""; // 名前一時保存用
  416. thread_local int lineNumber = 0; // 行番号一時保存用
  417. thread_local const char* functionName = ""; // 関数名一時保存用
  418. /**
  419. * メモリの確保,開放,エラー発生の通知を受信するリスナを登録します.
  420. *
  421. * @param l リスナ
  422. */
  423. void setListener(MemoryListener* l)
  424. {
  425. listener = l;
  426. }
  427. /**
  428. * 管理している全メモリエントリを引数に指定されたハンドラを実行します.
  429. * デフォルトで用意しているハンドラ printMemoryInfo を使用できます.
  430. *
  431. * @param handler ハンドラ
  432. */
  433. void entries(void (*handler)(const MemoryInfo& info))
  434. {
  435. if (!infoIsInitialized) { return; }
  436. MemoryInfo* info = infoHead._next;
  437. while (info != &infoTail)
  438. {
  439. handler(*info);
  440. info = info->_next;
  441. }
  442. }
  443. /**
  444. * 指定されたメソッドが true を返す管理メモリをクリア(削除)します.
  445. * 本メソッド実行時, 管理しているメモリの情報を引数に
  446. * 指定された関数が実行されます.
  447. *
  448. * [注意事項]
  449. * 本メソッドでメモリはクリアしますが, デストラクタは呼び出されないので注意
  450. *
  451. * 使用例)
  452. * @code
  453. * bool isCleanup(const scpp::MemoryInfo& info)
  454. * { // 管理しているメモリ全てクリアする
  455. * return true;
  456. * }
  457. * scpp::MemoryManager::cleanup(&isCleanup);
  458. * @endcode
  459. *
  460. * @param isCleanup クリアするか否かを返す関数ポインタ
  461. */
  462. void cleanup(bool (*isCleanup)(const MemoryInfo& info))
  463. {
  464. if (!infoIsInitialized) { return; }
  465. MemoryInfo* info = infoHead._next;
  466. while (info != &infoTail)
  467. {
  468. bool ret = isCleanup(*info);
  469. if (ret)
  470. { // クリア対象
  471. MemoryInfo* tmpInfo;
  472. tmpInfo = info;
  473. info = info->_next;
  474. remove(tmpInfo);
  475. tmpInfo->size = 0;
  476. tmpInfo->_mark = MARK_DELETED;
  477. free(tmpInfo);
  478. }
  479. else
  480. {
  481. info = info->_next;
  482. }
  483. }
  484. }
  485. /**
  486. * 指定されたメモリの情報を出力します.
  487. *
  488. * @param info メモリの情報
  489. */
  490. void printMemoryInfo(const MemoryInfo& info)
  491. {
  492. std::cout << "[MemoryInfo] "
  493. << info.fileName << ":"
  494. << info.lineNumber << ":"
  495. << info.functionName << " (size = "
  496. << info.size << ")"
  497. << std::endl;
  498. }
  499. /**
  500. * 指定されたサイズのメモリを確保します.
  501. *
  502. * @param size 確保するメモリサイズ
  503. * @param file 呼び出し元ソースファイル名
  504. * @param line 呼び出し元ソース行番号
  505. * @param func 呼び出し元関数名
  506. * @return 確保したメモリへのポインタ
  507. */
  508. void* malloc(std::size_t size, const char* file, int line, const char* func)
  509. {
  510. void* ptr = allocate(size, MARK_ALLOCATED, file, line, func);
  511. return ptr;
  512. }
  513. /**
  514. * 指定されたサイズのメモリを確保します.
  515. *
  516. * @param nmemb 確保するメモリの要素数
  517. * @param size 確保するメモリサイズ
  518. * @param file 呼び出し元ソースファイル
  519. * @param line 呼び出し元ソース行番号
  520. * @param func 呼び出し元関数名
  521. * @return 確保したメモリへのポインタ
  522. */
  523. void* calloc(std::size_t nmemb, std::size_t size, const char* file, int line, const char* func)
  524. {
  525. std::size_t n = nmemb * size;
  526. void* ptr = allocate(n, MARK_ALLOCATED, file, line, func);
  527. if (ptr)
  528. { // callc なのでゼロクリアする
  529. memset(ptr, 0x00, n);
  530. }
  531. return ptr;
  532. }
  533. /**
  534. * ポインタが示すメモリブロックのサイズを変更します.
  535. *
  536. * @param ptr ポインタ
  537. * @param size 確保するメモリのサイズ
  538. * @param file 呼び出し元ソースファイル
  539. * @param line 呼び出し元ソース行番号
  540. * @param func 呼び出し元関数名
  541. * @return 確保したメモリへのポインタ
  542. */
  543. void* realloc(void* ptr, std::size_t size, const char* file, int line, const char* func)
  544. {
  545. if (ptr == 0)
  546. { // ptr が null の場合, malloc と等価
  547. ptr = malloc(size, file, line, func);
  548. return ptr;
  549. }
  550. if (size == 0)
  551. { // size が 0 の場合, free と等価
  552. free(ptr);
  553. return 0;
  554. }
  555. // メモリ管理領域を取得
  556. MemoryInfo* entry = static_cast<MemoryInfo*> (ptr);
  557. entry--;
  558. void* nptr = 0;
  559. switch (entry->_mark)
  560. {
  561. case MARK_ALLOCATED: // 管理メモリ
  562. nptr = reallocate(entry, size, MARK_ALLOCATED, file, line, func, true);
  563. break;
  564. case MARK_DELETED: // 削除済みメモリ
  565. nptr = malloc(size, file, line, func);
  566. break;
  567. case MARK_ALLOCATED_NEW: // new で確保されたメモリ
  568. errno = EINVAL;
  569. if (listener) { listener->notifyError(*entry, "alloced new : can't realloc"); }
  570. break;
  571. case MARK_ALLOCATED_NEW_ARRAY:
  572. errno = EINVAL;
  573. if (listener) { listener->notifyError(*entry, "alloced new[] : can't realloc"); }
  574. break;
  575. default: // 管理対象外メモリ
  576. nptr = reallocate(ptr, size, MARK_ALLOCATED, file, line, func, false);
  577. }
  578. return nptr;
  579. }
  580. /**
  581. * 指定されたメモリを開放します.
  582. *
  583. * @param ptr 開放するメモリへのポインタ
  584. */
  585. void free(void* ptr)
  586. {
  587. MemoryInfo* entry;
  588. if (ptr == 0)
  589. { // null ポインタの場合なにもしない
  590. return;
  591. }
  592. // メモリ管理領域を取得
  593. entry = (MemoryInfo*) ptr;
  594. entry--;
  595. switch (entry->_mark)
  596. {
  597. case MARK_ALLOCATED: // 確保されたメモリ
  598. remove(entry);
  599. entry->_mark = MARK_DELETED;
  600. entry->size = 0;
  601. ::free(entry);
  602. break;
  603. case MARK_DELETED: // 削除済みのメモリ
  604. errno = EINVAL;
  605. if (listener) { listener->notifyError(*entry, "deleted pointer"); }
  606. break;
  607. case MARK_ALLOCATED_NEW: // new で確保したメモリ
  608. errno = EINVAL;
  609. if (listener) { listener->notifyError(*entry, "please use delete"); }
  610. break;
  611. case MARK_ALLOCATED_NEW_ARRAY: // new[] で確保したメモリ
  612. errno = EINVAL;
  613. if (listener) { listener->notifyError(*entry, "please use delete[]"); }
  614. break;
  615. default: // 管理外メモリ開放
  616. entry++;
  617. ::free(entry);
  618. break;
  619. }
  620. }
  621. } // namespace MemoryManager
  622. } // namespace scpp