utils.c 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. // Copyright 2012 Google Inc. All Rights Reserved.
  2. //
  3. // Use of this source code is governed by a BSD-style license
  4. // that can be found in the COPYING file in the root of the source
  5. // tree. An additional intellectual property rights grant can be found
  6. // in the file PATENTS. All contributing project authors may
  7. // be found in the AUTHORS file in the root of the source tree.
  8. // -----------------------------------------------------------------------------
  9. //
  10. // Misc. common utility functions
  11. //
  12. // Author: Skal (pascal.massimino@gmail.com)
  13. #include <stdlib.h>
  14. #include "./utils.h"
  15. // If PRINT_MEM_INFO is defined, extra info (like total memory used, number of
  16. // alloc/free etc) is printed. For debugging/tuning purpose only (it's slow,
  17. // and not multi-thread safe!).
  18. // An interesting alternative is valgrind's 'massif' tool:
  19. // http://valgrind.org/docs/manual/ms-manual.html
  20. // Here is an example command line:
  21. /* valgrind --tool=massif --massif-out-file=massif.out \
  22. --stacks=yes --alloc-fn=WebPSafeAlloc --alloc-fn=WebPSafeCalloc
  23. ms_print massif.out
  24. */
  25. // In addition:
  26. // * if PRINT_MEM_TRAFFIC is defined, all the details of the malloc/free cycles
  27. // are printed.
  28. // * if MALLOC_FAIL_AT is defined, the global environment variable
  29. // $MALLOC_FAIL_AT is used to simulate a memory error when calloc or malloc
  30. // is called for the nth time. Example usage:
  31. // export MALLOC_FAIL_AT=50 && ./examples/cwebp input.png
  32. // * if MALLOC_LIMIT is defined, the global environment variable $MALLOC_LIMIT
  33. // sets the maximum amount of memory (in bytes) made available to libwebp.
  34. // This can be used to emulate environment with very limited memory.
  35. // Example: export MALLOC_LIMIT=64000000 && ./examples/dwebp picture.webp
  36. // #define PRINT_MEM_INFO
  37. // #define PRINT_MEM_TRAFFIC
  38. // #define MALLOC_FAIL_AT
  39. // #define MALLOC_LIMIT
  40. //------------------------------------------------------------------------------
  41. // Checked memory allocation
  42. #if defined(PRINT_MEM_INFO)
  43. #include <stdio.h>
  44. #include <stdlib.h> // for abort()
  45. static int num_malloc_calls = 0;
  46. static int num_calloc_calls = 0;
  47. static int num_free_calls = 0;
  48. static int countdown_to_fail = 0; // 0 = off
  49. typedef struct MemBlock MemBlock;
  50. struct MemBlock {
  51. void* ptr_;
  52. size_t size_;
  53. MemBlock* next_;
  54. };
  55. static MemBlock* all_blocks = NULL;
  56. static size_t total_mem = 0;
  57. static size_t total_mem_allocated = 0;
  58. static size_t high_water_mark = 0;
  59. static size_t mem_limit = 0;
  60. static int exit_registered = 0;
  61. static void PrintMemInfo(void) {
  62. fprintf(stderr, "\nMEMORY INFO:\n");
  63. fprintf(stderr, "num calls to: malloc = %4d\n", num_malloc_calls);
  64. fprintf(stderr, " calloc = %4d\n", num_calloc_calls);
  65. fprintf(stderr, " free = %4d\n", num_free_calls);
  66. fprintf(stderr, "total_mem: %u\n", (uint32_t)total_mem);
  67. fprintf(stderr, "total_mem allocated: %u\n", (uint32_t)total_mem_allocated);
  68. fprintf(stderr, "high-water mark: %u\n", (uint32_t)high_water_mark);
  69. while (all_blocks != NULL) {
  70. MemBlock* b = all_blocks;
  71. all_blocks = b->next_;
  72. free(b);
  73. }
  74. }
  75. static void Increment(int* const v) {
  76. if (!exit_registered) {
  77. #if defined(MALLOC_FAIL_AT)
  78. {
  79. const char* const malloc_fail_at_str = getenv("MALLOC_FAIL_AT");
  80. if (malloc_fail_at_str != NULL) {
  81. countdown_to_fail = atoi(malloc_fail_at_str);
  82. }
  83. }
  84. #endif
  85. #if defined(MALLOC_LIMIT)
  86. {
  87. const char* const malloc_limit_str = getenv("MALLOC_LIMIT");
  88. if (malloc_limit_str != NULL) {
  89. mem_limit = atoi(malloc_limit_str);
  90. }
  91. }
  92. #endif
  93. (void)countdown_to_fail;
  94. (void)mem_limit;
  95. atexit(PrintMemInfo);
  96. exit_registered = 1;
  97. }
  98. ++*v;
  99. }
  100. static void AddMem(void* ptr, size_t size) {
  101. if (ptr != NULL) {
  102. MemBlock* const b = (MemBlock*)malloc(sizeof(*b));
  103. if (b == NULL) abort();
  104. b->next_ = all_blocks;
  105. all_blocks = b;
  106. b->ptr_ = ptr;
  107. b->size_ = size;
  108. total_mem += size;
  109. total_mem_allocated += size;
  110. #if defined(PRINT_MEM_TRAFFIC)
  111. #if defined(MALLOC_FAIL_AT)
  112. fprintf(stderr, "fail-count: %5d [mem=%u]\n",
  113. num_malloc_calls + num_calloc_calls, (uint32_t)total_mem);
  114. #else
  115. fprintf(stderr, "Mem: %u (+%u)\n", (uint32_t)total_mem, (uint32_t)size);
  116. #endif
  117. #endif
  118. if (total_mem > high_water_mark) high_water_mark = total_mem;
  119. }
  120. }
  121. static void SubMem(void* ptr) {
  122. if (ptr != NULL) {
  123. MemBlock** b = &all_blocks;
  124. // Inefficient search, but that's just for debugging.
  125. while (*b != NULL && (*b)->ptr_ != ptr) b = &(*b)->next_;
  126. if (*b == NULL) {
  127. fprintf(stderr, "Invalid pointer free! (%p)\n", ptr);
  128. abort();
  129. }
  130. {
  131. MemBlock* const block = *b;
  132. *b = block->next_;
  133. total_mem -= block->size_;
  134. #if defined(PRINT_MEM_TRAFFIC)
  135. fprintf(stderr, "Mem: %u (-%u)\n",
  136. (uint32_t)total_mem, (uint32_t)block->size_);
  137. #endif
  138. free(block);
  139. }
  140. }
  141. }
  142. #else
  143. #define Increment(v) do {} while (0)
  144. #define AddMem(p, s) do {} while (0)
  145. #define SubMem(p) do {} while (0)
  146. #endif
  147. // Returns 0 in case of overflow of nmemb * size.
  148. static int CheckSizeArgumentsOverflow(uint64_t nmemb, size_t size) {
  149. const uint64_t total_size = nmemb * size;
  150. if (nmemb == 0) return 1;
  151. if ((uint64_t)size > WEBP_MAX_ALLOCABLE_MEMORY / nmemb) return 0;
  152. if (total_size != (size_t)total_size) return 0;
  153. #if defined(PRINT_MEM_INFO) && defined(MALLOC_FAIL_AT)
  154. if (countdown_to_fail > 0 && --countdown_to_fail == 0) {
  155. return 0; // fake fail!
  156. }
  157. #endif
  158. #if defined(MALLOC_LIMIT)
  159. if (mem_limit > 0 && total_mem + total_size >= mem_limit) {
  160. return 0; // fake fail!
  161. }
  162. #endif
  163. return 1;
  164. }
  165. void* WebPSafeMalloc(uint64_t nmemb, size_t size) {
  166. void* ptr;
  167. Increment(&num_malloc_calls);
  168. if (!CheckSizeArgumentsOverflow(nmemb, size)) return NULL;
  169. assert(nmemb * size > 0);
  170. ptr = malloc((size_t)(nmemb * size));
  171. AddMem(ptr, (size_t)(nmemb * size));
  172. return ptr;
  173. }
  174. void* WebPSafeCalloc(uint64_t nmemb, size_t size) {
  175. void* ptr;
  176. Increment(&num_calloc_calls);
  177. if (!CheckSizeArgumentsOverflow(nmemb, size)) return NULL;
  178. assert(nmemb * size > 0);
  179. ptr = calloc((size_t)nmemb, size);
  180. AddMem(ptr, (size_t)(nmemb * size));
  181. return ptr;
  182. }
  183. void WebPSafeFree(void* const ptr) {
  184. if (ptr != NULL) {
  185. Increment(&num_free_calls);
  186. SubMem(ptr);
  187. }
  188. free(ptr);
  189. }
  190. //------------------------------------------------------------------------------