gwmem-check.c
上传用户:gzpyjq
上传日期:2013-01-31
资源大小:1852k
文件大小:18k
源码类别:

手机WAP编程

开发平台:

WINDOWS

  1. /*
  2.  * gwmem-check.c - memory management wrapper functions, check flavor
  3.  *
  4.  * This implementation of the gwmem.h interface checks for writes to
  5.  * non-allocated areas, and fills freshly allocated and freshly freed
  6.  * areas with garbage to prevent their use.  It also reports memory
  7.  * leaks.
  8.  *
  9.  * Design: Memory is allocated with markers before and after the
  10.  * area to be used.  These markers can be checked to see if anything
  11.  * has written to them.  There is a table of all allocated areas,
  12.  * which is used to detect memory leaks and which contains context
  13.  * information about each area.
  14.  *
  15.  * The start marker contains the index into this table, so that it
  16.  * can be looked up quickly -- but if the start marker has been damaged,
  17.  * the index can still be found by searching the table.
  18.  *
  19.  * Enlarging an area with realloc is handled by allocating new area,
  20.  * copying the old data, and freeing the old area.  This is an expensive
  21.  * operation which is avoided by reserving extra space (up to the nearest
  22.  * power of two), and only enlarging the area if the requested space is
  23.  * larger than this extra space.  The markers are still placed at exactly
  24.  * the size requested, so every realloc does mean moving the end marker.
  25.  *
  26.  * When data is freed, it is overwritten with 0xdeadbeef, so that code
  27.  * that tries to use it after freeing will likely crash.  The freed area
  28.  * is kept around for a while, to see if anything tries to write to it
  29.  * after it's been freed.
  30.  * 
  31.  * Richard Braakman
  32.  */
  33. #include <stdlib.h>
  34. #include <errno.h>
  35. #include <string.h>
  36. #include "gwlib.h"
  37. /* In this module, we must use the real versions so let's undefine the
  38.  * accident protectors. */
  39. #undef malloc
  40. #undef realloc
  41. #undef free
  42. /* Freshly malloced space is filled with NEW_AREA_PATTERN, to break
  43.  * code that assumes it is filled with zeroes. */
  44. #define NEW_AREA_PATTERN 0xcafebabe
  45. /* Freed space is filled with FREE_AREA_PATTERN, to break code that
  46.  * tries to read from it after freeing. */
  47. #define FREE_AREA_PATTERN 0xdeadbeef
  48. /* The marker before an area is filled with START_MARK_PATTERN
  49.  * (except for some bookkeeping bytes at the start of the marker). */
  50. #define START_MARK_PATTERN 0xdadaface
  51. /* The marker beyond an area is filled with END_MARK_PATTERN. */
  52. #define END_MARK_PATTERN 0xadadafec
  53. /* How many bytes to dump when listing unfreed areas. */
  54. #define MAX_DUMP 16
  55. static int initialized = 0;
  56. /* Use slower, more reliable method of detecting memory corruption. */
  57. static int slow = 0;
  58. /* We have to use a static mutex here, because otherwise the mutex_create
  59.  * call would try to allocate memory with gw_malloc before we're
  60.  * initialized. */
  61. static Mutex gwmem_lock;
  62. struct location
  63. {
  64.     const char *filename;
  65.     long lineno;
  66.     const char *function;
  67. };
  68. /* Duplicating the often-identical location information in every table
  69.  * entry uses a lot of memory, but saves the effort of maintaining a
  70.  * data structure for it, and keeps access to it fast. */
  71. struct area
  72. {
  73.     void *area;    /* The allocated memory area, as seen by caller */
  74.     size_t area_size;   /* Size requested by caller */
  75.     size_t max_size;    /* Size we can expand area to when reallocing */
  76.     struct location allocator;     /* Caller that alloced area */
  77.     struct location reallocator;   /* Caller that last realloced area */
  78.     struct location claimer;       /* Owner of area, set by caller */
  79. };
  80. /* Number of bytes to reserve on either side of each allocated area,
  81.  * to detect writes just outside the area.  It must be at least large
  82.  * enough to hold a long. */
  83. #define MARKER_SIZE 16
  84. #define MAX_TAB_SIZE (1024*1024L)
  85. #define MAX_ALLOCATIONS ((long) (MAX_TAB_SIZE/sizeof(struct area)))
  86. /* Freed areas are thrown into the free ring.  They are not released
  87.  * back to the system until FREE_RING_SIZE other allocations have been
  88.  * made.  This is more effective at finding bugs than releasing them
  89.  * immediately, because when we eventually release them we can check
  90.  * that they have not been tampered with in that time. */
  91. #define FREE_RING_SIZE 1024
  92. static struct area allocated[MAX_ALLOCATIONS];
  93. static struct area free_ring[FREE_RING_SIZE];
  94. /* Current number of allocations in the "allocated" table.  They are
  95.  * always consecutive and start at the beginning of the table. */
  96. static long num_allocations;
  97. /* The free ring can wrap around the edges of its array. */
  98. static long free_ring_start;
  99. static long free_ring_len;
  100. /* The next three are used for informational messages at shutdown */
  101. /* Largest number of allocations we've had at one time */
  102. static long highest_num_allocations;
  103. /* Largest value of the sum of allocated areas we've had at one time */
  104. static long highest_total_size;
  105. /* Current sum of allocated areas */
  106. static long total_size;
  107. /* Static functions */
  108. static void lock(void)
  109. {
  110.     mutex_lock(&gwmem_lock);
  111. }
  112. static void unlock(void)
  113. {
  114.     mutex_unlock(&gwmem_lock);
  115. }
  116. static unsigned long round_pow2(unsigned long num)
  117. {
  118.     unsigned long i;
  119.     if (num <= 16)
  120.         return 16;
  121.     for (i = 32; i < 0x80000000L; i <<= 1) {
  122.         if (num <= i)
  123.             return i;
  124.     }
  125.     /* We have to handle this case separately; the loop cannot go that
  126.      * far because i would overflow. */
  127.     if (num <= 0x80000000L)
  128.         return 0x80000000L;
  129.     return 0xffffffffL;
  130. }
  131. /* Fill a memory area with a bit pattern */
  132. static void fill(unsigned char *p, size_t bytes, long pattern)
  133. {
  134.     while (bytes > sizeof(pattern)) {
  135.         memcpy(p, &pattern, sizeof(pattern));
  136.         p += sizeof(pattern);
  137.         bytes -= sizeof(pattern);
  138.     }
  139.     if (bytes > 0)
  140.         memcpy(p, &pattern, bytes);
  141. }
  142. /* Check that a filled memory area has not changed */
  143. static int untouched(unsigned char *p, size_t bytes, long pattern)
  144. {
  145.     while (bytes > sizeof(pattern)) {
  146.         if (memcmp(p, &pattern, sizeof(pattern)) != 0)
  147.             return 0;
  148.         p += sizeof(pattern);
  149.         bytes -= sizeof(pattern);
  150.     }
  151.     if (bytes > 0 && memcmp(p, &pattern, bytes) != 0)
  152.         return 0;
  153.     return 1;
  154. }
  155. /* Fill the end marker for this area */
  156. static void endmark(unsigned char *p, size_t size)
  157. {
  158.     fill(p + size, MARKER_SIZE, END_MARK_PATTERN);
  159. }
  160. /* Fill the start marker for this area, and assign an number to the
  161.  * area which can be used for quick lookups later.  The number must
  162.  * not be negative. */
  163. static void startmark(unsigned char *p, long number)
  164. {
  165.     gw_assert(MARKER_SIZE >= sizeof(long));
  166.     gw_assert(number >= 0);
  167.     fill(p - MARKER_SIZE, sizeof(long), number);
  168.     fill(p - MARKER_SIZE + sizeof(long),
  169.          MARKER_SIZE - sizeof(long), START_MARK_PATTERN);
  170. }
  171. /* Check that the start marker for this area are intact, and return the
  172.  * marker number if it seems intact.  Return a negative number if
  173.  * it does not seem intact. */
  174. static long check_startmark(unsigned char *p)
  175. {
  176.     long number;
  177.     if (!untouched(p - MARKER_SIZE + sizeof(long),
  178.                    MARKER_SIZE - sizeof(long), START_MARK_PATTERN))
  179.         return -1;
  180.     memcpy(&number, p - MARKER_SIZE, sizeof(number));
  181.     return number;
  182. }
  183. static int check_endmark(unsigned char *p, size_t size)
  184. {
  185.     if (!untouched(p + size, MARKER_SIZE, END_MARK_PATTERN))
  186.         return -1;
  187.     return 0;
  188. }
  189. static int check_marks(struct area *area, long index)
  190. {
  191.     int result = 0;
  192.     if (check_startmark(area->area) != index) {
  193.         error(0, "Start marker was damaged for area %ld", index);
  194.         result = -1;
  195.     }
  196.     if (check_endmark(area->area, area->area_size) < 0) {
  197.         error(0, "End marker was damaged for area %ld", index);
  198.         result = -1;
  199.     }
  200.     return result;
  201. }
  202. static void dump_area(struct area *area)
  203. {
  204.     debug("gwlib.gwmem", 0, "Area %p, size %ld, max_size %ld",
  205.           area->area, (long) area->area_size, (long) area->max_size);
  206.     debug("gwlib.gwmem", 0, "Allocated by %s at %s:%ld",
  207.           area->allocator.function,
  208.           area->allocator.filename,
  209.           area->allocator.lineno);
  210.     if (area->reallocator.function) {
  211.         debug("gwlib.gwmem", 0, "Re-allocated by %s at %s:%ld",
  212.               area->reallocator.function,
  213.               area->reallocator.filename,
  214.               area->reallocator.lineno);
  215.     }
  216.     if (area->claimer.function) {
  217.         debug("gwlib.gwmem", 0, "Claimed by %s at %s:%ld",
  218.               area->claimer.function,
  219.               area->claimer.filename,
  220.               area->claimer.lineno);
  221.     }
  222.     if (area->area_size > 0) {
  223. size_t i;
  224. unsigned char *p;
  225. char buf[MAX_DUMP * 3 + 1];
  226.      p = area->area;
  227. buf[0] = '';
  228. for (i = 0; i < area->area_size && i < MAX_DUMP; ++i)
  229.     sprintf(strchr(buf, ''), "%02x ", p[i]);
  230.      debug("gwlib.gwmem", 0, "Contents of area (first %d bytes):",
  231.       MAX_DUMP);
  232. debug("gwlib.gwmem", 0, "  %s", buf);
  233.     }
  234. }
  235. static struct area *find_area(unsigned char *p)
  236. {
  237.     long index;
  238.     struct area *area;
  239.     long suspicious_pointer;
  240.     unsigned long p_ul;
  241.     gw_assert(p != NULL);
  242.     p_ul = (unsigned long) p;
  243.     suspicious_pointer =
  244.         (sizeof(p) == sizeof(long) &&
  245.          (p_ul == NEW_AREA_PATTERN || p_ul == FREE_AREA_PATTERN ||
  246.   p_ul == START_MARK_PATTERN || p_ul == END_MARK_PATTERN));
  247.     if (slow || suspicious_pointer) {
  248.         /* Extra check, which does not touch the (perhaps not allocated)
  249.  * memory area.  It's slow, but may help pinpoint problems that
  250.  * would otherwise cause segfaults. */
  251.         for (index = 0; index < num_allocations; index++) {
  252.             if (allocated[index].area == p)
  253.                 break;
  254.         }
  255.         if (index == num_allocations) {
  256.             error(0, "Area %p not found in allocation table.", p);
  257.             return NULL;
  258.         }
  259.     }
  260.     index = check_startmark(p);
  261.     if (index >= 0 && index < num_allocations &&
  262.         allocated[index].area == p) {
  263.         area = &allocated[index];
  264.         if (check_endmark(p, area->area_size) < 0) {
  265.             error(0, "End marker was damaged for area %p", p);
  266.             dump_area(area);
  267.         }
  268.         return area;
  269.     }
  270.     error(0, "Start marker was damaged for area %p", p);
  271.     for (index = 0; index < num_allocations; index++) {
  272.         if (allocated[index].area == p) {
  273.             area = &allocated[index];
  274.             dump_area(area);
  275.             return area;
  276.         }
  277.     }
  278.     error(0, "Could not find area information.");
  279.     return NULL;
  280. }
  281. static void change_total_size(long change)
  282. {
  283.     total_size += change;
  284.     if (total_size > highest_total_size)
  285.         highest_total_size = total_size;
  286. }
  287. static struct area *record_allocation(unsigned char *p, size_t size,
  288.                                                   const char *filename, long lineno, const char *function)
  289. {
  290.     struct area *area;
  291.     static struct area empty_area;
  292.     if (num_allocations == MAX_ALLOCATIONS) {
  293.         panic(0, "Too many concurrent allocations.");
  294.     }
  295.     area = &allocated[num_allocations];
  296.     *area = empty_area;
  297.     area->area = p;
  298.     area->area_size = size;
  299.     area->max_size = size;
  300.     area->allocator.filename = filename;
  301.     area->allocator.lineno = lineno;
  302.     area->allocator.function = function;
  303.     startmark(area->area, num_allocations);
  304.     endmark(area->area, area->area_size);
  305.     num_allocations++;
  306.     if (num_allocations > highest_num_allocations)
  307.         highest_num_allocations = num_allocations;
  308.     change_total_size(size);
  309.     return area;
  310. }
  311. static void remove_allocation(struct area *area)
  312. {
  313.     change_total_size(-1*area->area_size);
  314.     num_allocations--;
  315.     if (area == &allocated[num_allocations])
  316.         return;
  317.     check_marks(&allocated[num_allocations], num_allocations);
  318.     *area = allocated[num_allocations];
  319.     startmark(area->area, area - allocated);
  320. }
  321. static void drop_from_free_ring(long index)
  322. {
  323.     struct area *area;
  324.     area = &free_ring[index];
  325.     if (check_marks(area, index) < 0 ||
  326.         !untouched(area->area, area->area_size, FREE_AREA_PATTERN)) {
  327.         error(0, "Freed area %p has been tampered with.", area->area);
  328.         dump_area(area);
  329.     }
  330.     free((unsigned char *)area->area - MARKER_SIZE);
  331. }
  332. static void put_on_free_ring(struct area *area)
  333. {
  334.     /* Simple case: We're still filling the free ring. */
  335.     if (free_ring_len < FREE_RING_SIZE) {
  336.         free_ring[free_ring_len] = *area;
  337.         startmark(area->area, free_ring_len);
  338.         free_ring_len++;
  339.         return;
  340.     }
  341.     /* Normal case: We need to check and release a free ring entry,
  342.      * then put this one in its place. */
  343.     drop_from_free_ring(free_ring_start);
  344.     free_ring[free_ring_start] = *area;
  345.     startmark(area->area, free_ring_start);
  346.     free_ring_start = (free_ring_start + 1) % FREE_RING_SIZE;
  347. }
  348. static void free_area(struct area *area)
  349. {
  350.     fill(area->area, area->area_size, FREE_AREA_PATTERN);
  351.     put_on_free_ring(area);
  352.     remove_allocation(area);
  353. }
  354. void gw_check_init_mem(int slow_flag)
  355. {
  356.     mutex_init_static(&gwmem_lock);
  357.     slow = slow_flag;
  358.     initialized = 1;
  359. }
  360. void gw_check_shutdown(void)
  361. {
  362.     mutex_destroy(&gwmem_lock);
  363.     initialized = 0;
  364. }
  365. void *gw_check_malloc(size_t size, const char *filename, long lineno,
  366.                       const char *function)
  367. {
  368.     unsigned char *p;
  369.     gw_assert(initialized);
  370.     /* ANSI C89 says malloc(0) is implementation-defined.  Avoid it. */
  371.     gw_assert(size > 0);
  372.     p = malloc(size + 2 * MARKER_SIZE);
  373.     if (p == NULL)
  374.         panic(errno, "Memory allocation of %d bytes failed.", size);
  375.     p += MARKER_SIZE;
  376.     lock();
  377.     fill(p, size, NEW_AREA_PATTERN);
  378.     record_allocation(p, size, filename, lineno, function);
  379.     unlock();
  380.     return p;
  381. }
  382. void *gw_check_realloc(void *p, size_t size, const char *filename,
  383.                        long lineno, const char *function)
  384. {
  385.     struct area *area;
  386.     if (p == NULL)
  387.         return gw_check_malloc(size, filename, lineno, function);
  388.     gw_assert(initialized);
  389.     gw_assert(size > 0);
  390.     lock();
  391.     area = find_area(p);
  392.     if (!area) {
  393.         unlock();
  394.         panic(0, "Realloc called on non-allocated area");
  395.     }
  396.     if (size == area->area_size) {
  397.         /* No changes */
  398.     } else if (size <= area->max_size) {
  399.         change_total_size(size - area->area_size);
  400.         area->area_size = size;
  401.         endmark(p, size);
  402.     } else if (size > area->max_size) {
  403.         /* The current block is not large enough for the reallocation.
  404.          * We will allocate a new block, copy the data over, and free
  405.          * the old block.  We round the size up to a power of two,
  406.          * to prevent frequent reallocations. */
  407.         struct area *new_area;
  408.         size_t new_size;
  409.         unsigned char *new_p;
  410.         new_size = round_pow2(size + 2 * MARKER_SIZE);
  411.         new_p = malloc(new_size);
  412.         new_size -= 2 * MARKER_SIZE;
  413.         new_p += MARKER_SIZE;
  414.         memcpy(new_p, p, area->area_size);
  415.         fill(new_p + area->area_size, size - area->area_size,
  416.              NEW_AREA_PATTERN);
  417.         new_area = record_allocation(new_p, size,
  418.                                      area->allocator.filename,
  419.                                      area->allocator.lineno,
  420.                                      area->allocator.function);
  421.         new_area->max_size = new_size;
  422.         free_area(area);
  423.         p = new_p;
  424.         area = new_area;
  425.     }
  426.     area->reallocator.filename = filename;
  427.     area->reallocator.lineno = lineno;
  428.     area->reallocator.function = function;
  429.     unlock();
  430.     return p;
  431. }
  432. void gw_check_free(void *p, const char *filename, long lineno,
  433.                    const char *function)
  434. {
  435.     struct area *area;
  436.     gw_assert(initialized);
  437.     if (p == NULL)
  438.         return;
  439.     lock();
  440.     area = find_area(p);
  441.     if (!area) {
  442.         unlock();
  443.         panic(0, "Free called on non-allocated area");
  444.     }
  445.     free_area(area);
  446.     unlock();
  447. }
  448. char *gw_check_strdup(const char *str, const char *filename, long lineno,
  449.                       const char *function)
  450. {
  451.     char *copy;
  452.     gw_assert(initialized);
  453.     gw_assert(str != NULL);
  454.     copy = gw_check_malloc(strlen(str) + 1, filename, lineno, function);
  455.     strcpy(copy, str);
  456.     return copy;
  457. }
  458. void *gw_check_claim_area(void *p, const char *filename, long lineno,
  459.                           const char *function)
  460. {
  461.     struct area *area;
  462.     /* Allow this for the convenience of wrapper macros. */
  463.     if (p == NULL)
  464.         return NULL;
  465.     lock();
  466.     area = find_area(p);
  467.     if (!area) {
  468.         unlock();
  469.         panic(0, "Claim_area called on non-allocated area");
  470.     }
  471.     area->claimer.filename = filename;
  472.     area->claimer.lineno = lineno;
  473.     area->claimer.function = function;
  474.     unlock();
  475.     /* For convenience of calling macros */
  476.     return p;
  477. }
  478. void gw_check_check_leaks(void)
  479. {
  480.     long calculated_size;
  481.     long index;
  482.     gw_assert(initialized);
  483.     lock();
  484.     for (index = 0; index < free_ring_len; index++) {
  485.         drop_from_free_ring(index);
  486.     }
  487.     free_ring_len = 0;
  488.     calculated_size = 0;
  489.     for (index = 0; index < num_allocations; index++) {
  490.         calculated_size += allocated[index].area_size;
  491.     }
  492.     gw_assert(calculated_size == total_size);
  493.     debug("gwlib.gwmem", 0, "Current allocations: %ld areas, %ld bytes",
  494.           num_allocations, total_size);
  495.     debug("gwlib.gwmem", 0, "Highest number of allocations: %ld areas",
  496.           highest_num_allocations);
  497.     debug("gwlib.gwmem", 0, "Highest memory usage: %ld bytes",
  498.           highest_total_size);
  499.     for (index = 0; index < num_allocations; index++) {
  500.         check_marks(&allocated[index], index);
  501.         dump_area(&allocated[index]);
  502.     }
  503.     unlock();
  504. }
  505. int gw_check_is_allocated(void *p)
  506. {
  507.     struct area *area;
  508.     lock();
  509.     area = find_area(p);
  510.     unlock();
  511.     return area != NULL;
  512. }
  513. long gw_check_area_size(void *p)
  514. {
  515.     struct area *area;
  516.     size_t size;
  517.     lock();
  518.     area = find_area(p);
  519.     if (!area) {
  520.         unlock();
  521.         warning(0, "Area_size called on non-allocated area %p", p);
  522.         return -1;
  523.     }
  524.     size = area->area_size;
  525.     unlock();
  526.     return size;
  527. }