Subversion Repositories Kolibri OS

Rev

Rev 3584 | Blame | Last modification | View Log | RSS feed

  1. /** \file
  2.  * Heap debugging functions (implementation).
  3.  *
  4.  * Based on memdebug.c from curl (see below), with the following modifications:
  5.  *
  6.  * - renamed functions from curl_ to memdebug_
  7.  * - added memdebug_strndup
  8.  * - added guard bytes before and after each block to help detect overflows
  9.  * - if a guard byte is corrupted during free, dumps the DA to file
  10.  */
  11.  
  12. /***************************************************************************
  13.  *                                  _   _ ____  _
  14.  *  Project                     ___| | | |  _ \| |
  15.  *                             / __| | | | |_) | |
  16.  *                            | (__| |_| |  _ <| |___
  17.  *                             \___|\___/|_| \_\_____|
  18.  *
  19.  * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
  20.  *
  21.  * This software is licensed as described in the file COPYING, which
  22.  * you should have received as part of this distribution. The terms
  23.  * are also available at http://curl.haxx.se/docs/copyright.html.
  24.  *
  25.  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
  26.  * copies of the Software, and permit persons to whom the Software is
  27.  * furnished to do so, under the terms of the COPYING file.
  28.  *
  29.  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  30.  * KIND, either express or implied.
  31.  *
  32.  * $Id: memdebug.c,v 1.1 2004/07/28 22:35:02 bursa Exp $
  33.  ***************************************************************************/
  34.  
  35. #include <assert.h>
  36. #include <stdbool.h>
  37. #include <stdio.h>
  38. #include <string.h>
  39. #include <stdlib.h>
  40. #include <unistd.h>
  41. #include <sys/socket.h>
  42. #ifdef riscos
  43. #include <unixlib/local.h>
  44.  
  45. #include "oslib/os.h"
  46. #include "oslib/osfile.h"
  47. #endif
  48.  
  49. #include "memdebug.h"
  50.  
  51. #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
  52.  
  53. #define MAGIC 0x34343434
  54. #define GUARD 0x34
  55.  
  56. #if defined(riscos) && !defined(__ELF__)
  57. extern int __dynamic_num;
  58. #endif
  59.  
  60. struct memdebug {
  61.   size_t size;
  62.   unsigned int magic;
  63.   double mem[1];
  64.   /* I'm hoping this is the thing with the strictest alignment
  65.    * requirements.  That also means we waste some space :-( */
  66. };
  67.  
  68. /*
  69.  * Note that these debug functions are very simple and they are meant to
  70.  * remain so. For advanced analysis, record a log file and write perl scripts
  71.  * to analyze them!
  72.  *
  73.  * Don't use these with multithreaded test programs!
  74.  */
  75.  
  76. #define logfile memdebug_debuglogfile
  77. FILE *memdebug_debuglogfile;
  78. static bool memlimit; /* enable memory limit */
  79. static long memsize;  /* set number of mallocs allowed */
  80.  
  81. /* this sets the log file name */
  82. void memdebug_memdebug(const char *logname)
  83. {
  84.   if(logname)
  85.     logfile = fopen(logname, "w");
  86.   else
  87.     logfile = stderr;
  88. }
  89.  
  90. /* This function sets the number of malloc() calls that should return
  91.    successfully! */
  92. void memdebug_memlimit(long limit)
  93. {
  94.   memlimit = true;
  95.   memsize = limit;
  96. }
  97.  
  98. /* returns true if this isn't allowed! */
  99. static bool countcheck(const char *func, int line, const char *source)
  100. {
  101.   /* if source is NULL, then the call is made internally and this check
  102.      should not be made */
  103.   if(memlimit && source) {
  104.     if(!memsize) {
  105.       if(logfile && source)
  106.         fprintf(logfile, "LIMIT %s:%d %s reached memlimit\n",
  107.                 source, line, func);
  108.       if(source)
  109.         fprintf(stderr, "LIMIT %s:%d %s reached memlimit\n",
  110.                 source, line, func);
  111.       return true; /* RETURN ERROR! */
  112.     }
  113.     else
  114.       memsize--; /* countdown */
  115.  
  116.     /* log the countdown */
  117.     if(logfile && source)
  118.       fprintf(logfile, "LIMIT %s:%d %ld ALLOCS left\n",
  119.               source, line, memsize);
  120.  
  121.   }
  122.  
  123.   return false; /* allow this */
  124. }
  125.  
  126. void *memdebug_malloc(size_t wantedsize, int line, const char *source)
  127. {
  128.   struct memdebug *mem;
  129.   size_t size;
  130.  
  131.   if(countcheck("malloc", line, source))
  132.     return NULL;
  133.  
  134.   /* alloc at least 64 bytes */
  135.   size = sizeof(struct memdebug)+wantedsize + 8;
  136.  
  137.   mem=(struct memdebug *)(malloc)(size);
  138.   if(mem) {
  139.     unsigned int i;
  140.     /* fill memory with junk */
  141.     memset(mem->mem, 0xA5, wantedsize);
  142.     mem->size = wantedsize;
  143.     mem->magic = MAGIC;
  144.     for (i = 0; i != 8; i++)
  145.       ((char *) mem->mem)[wantedsize + i] = GUARD;
  146.   }
  147.  
  148.   if(logfile && source)
  149.     fprintf(logfile, "MEM %s:%d malloc(%zu) = %p\n",
  150.             source, line, wantedsize, mem ? mem->mem : 0);
  151.   return (mem ? mem->mem : NULL);
  152. }
  153.  
  154. void *memdebug_calloc(size_t wanted_elements, size_t wanted_size,
  155.                     int line, const char *source)
  156. {
  157.   struct memdebug *mem;
  158.   size_t size, user_size;
  159.  
  160.   if(countcheck("calloc", line, source))
  161.     return NULL;
  162.  
  163.   /* alloc at least 64 bytes */
  164.   user_size = wanted_size * wanted_elements;
  165.   size = sizeof(struct memdebug) + user_size + 8;
  166.  
  167.   mem = (struct memdebug *)(malloc)(size);
  168.   if(mem) {
  169.     unsigned int i;
  170.     /* fill memory with zeroes */
  171.     memset(mem->mem, 0, user_size);
  172.     mem->size = user_size;
  173.     mem->magic = MAGIC;
  174.     for (i = 0; i != 8; i++)
  175.       ((char *) mem->mem)[mem->size + i] = GUARD;
  176.   }
  177.  
  178.   if(logfile && source)
  179.     fprintf(logfile, "MEM %s:%d calloc(%zu,%zu) = %p\n",
  180.             source, line, wanted_elements, wanted_size, mem ? mem->mem : 0);
  181.   return (mem ? mem->mem : NULL);
  182. }
  183.  
  184. char *memdebug_strdup(const char *str, int line, const char *source)
  185. {
  186.   char *mem;
  187.   size_t len;
  188.  
  189.   assert(str != NULL);
  190.  
  191.   if(countcheck("strdup", line, source))
  192.     return NULL;
  193.  
  194.   len=strlen(str)+1;
  195.  
  196.   mem=memdebug_malloc(len, 0, NULL); /* NULL prevents logging */
  197.   if (mem)
  198.   memcpy(mem, str, len);
  199.  
  200.   if(logfile)
  201.     fprintf(logfile, "MEM %s:%d strdup(%p) (%zu) = %p\n",
  202.             source, line, str, len, mem);
  203.  
  204.   return mem;
  205. }
  206.  
  207. char *memdebug_strndup(const char *str, size_t size, int line, const char *source)
  208. {
  209.   char *mem;
  210.   size_t len;
  211.  
  212.   assert(str != NULL);
  213.  
  214.   if(countcheck("strndup", line, source))
  215.     return NULL;
  216.  
  217.   len=strlen(str)+1;
  218.   if (size < len - 1)
  219.     len = size + 1;
  220.  
  221.   mem=memdebug_malloc(len, 0, NULL); /* NULL prevents logging */
  222.   if (mem) {
  223.   memcpy(mem, str, len);
  224.   mem[len - 1] = 0;
  225.   }
  226.  
  227.   if(logfile)
  228.     fprintf(logfile, "MEM %s:%d strndup(%p, %zd) (%zu) = %p\n",
  229.             source, line, str, size, len, mem);
  230.  
  231.   return mem;
  232. }
  233.  
  234. /* We provide a realloc() that accepts a NULL as pointer, which then
  235.    performs a malloc(). In order to work with ares. */
  236. void *memdebug_realloc(void *ptr, size_t wantedsize,
  237.                      int line, const char *source)
  238. {
  239.   unsigned int i;
  240.   struct memdebug *mem=NULL;
  241.  
  242.   size_t size = sizeof(struct memdebug)+wantedsize+8;
  243.  
  244.   if(countcheck("realloc", line, source))
  245.     return NULL;
  246.  
  247.   if(ptr) {
  248.     mem = (struct memdebug *)(void *)
  249.                 ((char *)ptr - offsetof(struct memdebug, mem));
  250.   }
  251.  
  252.   if(logfile) {
  253.     if (mem && mem->magic != MAGIC)
  254.       fprintf(logfile, "MAGIC match failed!\n");
  255.     for (i = 0; mem && i != 8; i++)
  256.       if (((char *) mem->mem)[mem->size + i] != GUARD)
  257.         fprintf(logfile, "GUARD %u match failed!\n", i);
  258.     fprintf(logfile, "MEM %s:%d realloc(%p, %zu) = ",
  259.             source, line, ptr, wantedsize);
  260.     fflush(logfile);
  261.   }
  262.  
  263.   mem=(struct memdebug *)(realloc)(mem, size);
  264.   if(logfile)
  265.     fprintf(logfile, "%p\n", mem?mem->mem:NULL);
  266.  
  267.   if(mem) {
  268.     mem->size = wantedsize;
  269.     mem->magic = MAGIC;
  270.     for (i = 0; i != 8; i++)
  271.       ((char *) mem->mem)[wantedsize + i] = GUARD;
  272.     return mem->mem;
  273.   }
  274.  
  275.   return NULL;
  276. }
  277.  
  278. void memdebug_free(void *ptr, int line, const char *source)
  279. {
  280.   unsigned int i;
  281.   struct memdebug *mem;
  282.  
  283.   if (!ptr)
  284.     return;
  285.  
  286.   assert(ptr != NULL);
  287.  
  288.   mem = (struct memdebug *)(void *)
  289.                 ((char *)ptr - offsetof(struct memdebug, mem));
  290.   if(logfile) {
  291.     fprintf(logfile, "MEM %s:%d free(%p)\n", source, line, ptr);
  292.     if (mem->magic != MAGIC) {
  293.       fprintf(logfile, "MAGIC match failed!\n");
  294. #ifdef riscos
  295.   #ifndef __ELF__
  296.       if (__dynamic_num != -1) {
  297.         int size;
  298.         byte *base_address;
  299.         xosdynamicarea_read(__dynamic_num, &size, &base_address,
  300.             0, 0, 0, 0, 0);
  301.         fprintf(logfile, "saving DA %i %p %x\n", __dynamic_num, base_address,
  302.             size);
  303.         xosfile_save("core", (bits) base_address, 0, base_address,
  304.             base_address + size);
  305.       }
  306.   #else
  307.       __unixlib_write_coredump(NULL);
  308.   #endif
  309. #endif
  310.     }
  311.     fflush(logfile);
  312.     for (i = 0; i != 8; i++)
  313.       if (((char *) mem->mem)[mem->size + i] != GUARD)
  314.         fprintf(logfile, "GUARD %u match failed!\n", i);
  315.     fflush(logfile);
  316.   }
  317.  
  318.   /* destroy  */
  319.   memset(mem->mem, 0x13, mem->size);
  320.   mem->magic = 0x13131313;
  321.   for (i = 0; i != 8; i++)
  322.     ((char *) mem->mem)[mem->size + i] = 0x13;
  323.  
  324.   /* free for real */
  325.   (free)(mem);
  326. }
  327.  
  328. int memdebug_socket(int domain, int type, int protocol, int line,
  329.                 const char *source)
  330. {
  331.   int sockfd=(socket)(domain, type, protocol);
  332.   if(logfile && (sockfd!=-1))
  333.     fprintf(logfile, "FD %s:%d socket() = %d\n",
  334.             source, line, sockfd);
  335.   return sockfd;
  336. }
  337.  
  338. int memdebug_accept(int s, void *saddr, void *saddrlen,
  339.                 int line, const char *source)
  340. {
  341.   struct sockaddr *addr = (struct sockaddr *)saddr;
  342.   socklen_t *addrlen = (socklen_t *)saddrlen;
  343.   int sockfd=(accept)(s, addr, addrlen);
  344.   if(logfile)
  345.     fprintf(logfile, "FD %s:%d accept() = %d\n",
  346.             source, line, sockfd);
  347.   return sockfd;
  348. }
  349.  
  350. /* this is our own defined way to close sockets on *ALL* platforms */
  351. int memdebug_sclose(int sockfd, int line, const char *source)
  352. {
  353.   int res=sclose(sockfd);
  354.   if(logfile)
  355.     fprintf(logfile, "FD %s:%d sclose(%d)\n",
  356.             source, line, sockfd);
  357.   return res;
  358. }
  359.  
  360. FILE *memdebug_fopen(const char *file, const char *mode,
  361.                  int line, const char *source)
  362. {
  363.   FILE *res=(fopen)(file, mode);
  364.   if(logfile)
  365.     fprintf(logfile, "FILE %s:%d fopen(\"%s\",\"%s\") = %p\n",
  366.             source, line, file, mode, res);
  367.   return res;
  368. }
  369.  
  370. int memdebug_fclose(FILE *file, int line, const char *source)
  371. {
  372.   int res;
  373.  
  374.   assert(file != NULL);
  375.  
  376.   res=(fclose)(file);
  377.   if(logfile)
  378.     fprintf(logfile, "FILE %s:%d fclose(%p)\n",
  379.             source, line, file);
  380.   return res;
  381. }
  382.