Add test for gdb function calls.
authorTim Newsome <tim@sifive.com>
Tue, 28 Jun 2016 00:10:09 +0000 (17:10 -0700)
committerTim Newsome <tim@sifive.com>
Tue, 19 Jul 2016 18:24:25 +0000 (11:24 -0700)
debug/gdbserver.py
debug/programs/debug.c
debug/programs/tiny-malloc.c [new file with mode: 0644]

index f7d8d2ebfb6a83cfed233870b00b34eb25b1f097..d2508533e4866b07e9e00c69584a19bf4ee5db51 100755 (executable)
@@ -157,7 +157,11 @@ class InstantHaltTest(DeleteServer):
 
 class DebugTest(DeleteServer):
     def setUp(self):
-        self.binary = target.compile("programs/debug.c", "programs/checksum.c")
+        # Include malloc so that gdb can make function calls. I suspect this
+        # malloc will silently blow through the memory set aside for it, so be
+        # careful.
+        self.binary = target.compile("programs/debug.c", "programs/checksum.c",
+                "programs/tiny-malloc.c", "-DDEFINE_MALLOC", "-DDEFINE_FREE")
         self.server = target.server()
         self.gdb = testlib.Gdb()
         self.gdb.command("file %s" % self.binary)
@@ -173,6 +177,12 @@ class DebugTest(DeleteServer):
         # Use a0 until gdb can resolve "status"
         self.assertEqual(self.gdb.p("$a0") & 0xffffffff, 0xc86455d4)
 
+    def test_function_call(self):
+        text = "Howdy, Earth!"
+        gdb_length = self.gdb.p('strlen("%s")' % text)
+        self.assertEqual(gdb_length, len(text))
+        self.exit()
+
     def test_turbostep(self):
         """Single step a bunch of times."""
         self.gdb.command("p i=0");
index c7c23a6e83dea1bfe5b0c45058806c0110b274ed..7d0b8767c74140c64a685e5da24d9bed792bd158 100644 (file)
@@ -4,6 +4,8 @@
 
 unsigned int crc32a(uint8_t *message, unsigned int size);
 
+char __malloc_start[512];
+
 void rot13(char *buf)
 {
     while (*buf) {
diff --git a/debug/programs/tiny-malloc.c b/debug/programs/tiny-malloc.c
new file mode 100644 (file)
index 0000000..699660c
--- /dev/null
@@ -0,0 +1,600 @@
+// https://github.com/32bitmicro/newlib-nano-1.0/blob/master/newlib/libc/machine/xstormy16/tiny-malloc.c
+
+/* A replacement malloc with:
+   - Much reduced code size;
+   - Smaller RAM footprint;
+   - The ability to handle downward-growing heaps;
+   but
+   - Slower;
+   - Probably higher memory fragmentation;
+   - Doesn't support threads (but, if it did support threads,
+     it wouldn't need a global lock, only a compare-and-swap instruction);
+   - Assumes the maximum alignment required is the alignment of a pointer;
+   - Assumes that memory is already there and doesn't need to be allocated.
+
+* Synopsis of public routines
+
+  malloc(size_t n);
+     Return a pointer to a newly allocated chunk of at least n bytes, or null
+     if no space is available.
+  free(void* p);
+     Release the chunk of memory pointed to by p, or no effect if p is null.
+  realloc(void* p, size_t n);
+     Return a pointer to a chunk of size n that contains the same data
+     as does chunk p up to the minimum of (n, p's size) bytes, or null
+     if no space is available. The returned pointer may or may not be
+     the same as p. If p is null, equivalent to malloc.  Unless the
+     #define REALLOC_ZERO_BYTES_FREES below is set, realloc with a
+     size argument of zero (re)allocates a minimum-sized chunk.
+  memalign(size_t alignment, size_t n);
+     Return a pointer to a newly allocated chunk of n bytes, aligned
+     in accord with the alignment argument, which must be a power of
+     two.  Will fail if 'alignment' is too large.
+  calloc(size_t unit, size_t quantity);
+     Returns a pointer to quantity * unit bytes, with all locations
+     set to zero.
+  cfree(void* p);
+     Equivalent to free(p).
+  malloc_trim(size_t pad);
+     Release all but pad bytes of freed top-most memory back 
+     to the system. Return 1 if successful, else 0.
+  malloc_usable_size(void* p);
+     Report the number usable allocated bytes associated with allocated
+     chunk p. This may or may not report more bytes than were requested,
+     due to alignment and minimum size constraints.
+  malloc_stats();
+     Prints brief summary statistics on stderr.
+  mallinfo()
+     Returns (by copy) a struct containing various summary statistics.
+  mallopt(int parameter_number, int parameter_value)
+     Changes one of the tunable parameters described below. Returns
+     1 if successful in changing the parameter, else 0.  Actually, returns 0
+     always, as no parameter can be changed.
+*/
+
+#ifdef __xstormy16__
+#define MALLOC_DIRECTION -1
+#endif
+
+#ifndef MALLOC_DIRECTION
+#define MALLOC_DIRECTION 1
+#endif
+
+#include <stddef.h>
+
+void* malloc(size_t);
+void    free(void*);
+void* realloc(void*, size_t);
+void* memalign(size_t, size_t);
+void* valloc(size_t);
+void* pvalloc(size_t);
+void* calloc(size_t, size_t);
+void    cfree(void*);
+int     malloc_trim(size_t);
+size_t  malloc_usable_size(void*);
+void    malloc_stats(void);
+int     mallopt(int, int);
+struct mallinfo mallinfo(void);
+
+typedef struct freelist_entry {
+  size_t size;
+  struct freelist_entry *next;
+} *fle;
+
+extern void * __malloc_end;
+extern fle __malloc_freelist;
+
+/* Return the number of bytes that need to be added to X to make it
+   aligned to an ALIGN boundary.  ALIGN must be a power of 2.  */
+#define M_ALIGN(x, align) (-(size_t)(x) & ((align) - 1))
+
+/* Return the number of bytes that need to be subtracted from X to make it
+   aligned to an ALIGN boundary.  ALIGN must be a power of 2.  */
+#define M_ALIGN_SUB(x, align) ((size_t)(x) & ((align) - 1))
+
+extern char *__malloc_start;
+
+/* This is the minimum gap allowed between __malloc_end and the top of
+   the stack.  This is only checked for when __malloc_end is
+   decreased; if instead the stack grows into the heap, silent data
+   corruption will result.  */
+#define MALLOC_MINIMUM_GAP 32
+
+#ifdef __xstormy16__
+register void * stack_pointer asm ("r15");
+#define MALLOC_LIMIT stack_pointer
+#else
+#define MALLOC_LIMIT __builtin_frame_address (0)
+#endif
+
+#if MALLOC_DIRECTION < 0
+#define CAN_ALLOC_P(required)                          \
+  (((size_t) __malloc_end - (size_t)MALLOC_LIMIT       \
+    - MALLOC_MINIMUM_GAP) >= (required))
+#else
+#define CAN_ALLOC_P(required)                          \
+  (((size_t)MALLOC_LIMIT - (size_t) __malloc_end       \
+    - MALLOC_MINIMUM_GAP) >= (required))
+#endif
+
+/* real_size is the size we actually have to allocate, allowing for
+   overhead and alignment.  */
+#define REAL_SIZE(sz)                                          \
+  ((sz) < sizeof (struct freelist_entry) - sizeof (size_t)     \
+   ? sizeof (struct freelist_entry)                            \
+   : sz + sizeof (size_t) + M_ALIGN(sz, sizeof (size_t)))
+
+#ifdef DEFINE_MALLOC
+
+void * __malloc_end = &__malloc_start;
+fle __malloc_freelist;
+
+void *
+malloc (size_t sz)
+{
+  fle *nextfree;
+  fle block;
+
+  /* real_size is the size we actually have to allocate, allowing for
+     overhead and alignment.  */
+  size_t real_size = REAL_SIZE (sz);
+
+  /* Look for the first block on the freelist that is large enough.  */
+  for (nextfree = &__malloc_freelist; 
+       *nextfree; 
+       nextfree = &(*nextfree)->next)  
+    {
+      block = *nextfree;
+      
+      if (block->size >= real_size)
+       {
+         /* If the block found is just the right size, remove it from
+            the free list.  Otherwise, split it.  */
+         if (block->size < real_size + sizeof (struct freelist_entry))
+           {
+             *nextfree = block->next;
+             return (void *)&block->next;
+           }
+         else
+           {
+             size_t newsize = block->size - real_size;
+             fle newnext = block->next;
+             *nextfree = (fle)((size_t)block + real_size);
+             (*nextfree)->size = newsize;
+             (*nextfree)->next = newnext;
+             goto done;
+           }
+       }
+
+      /* If this is the last block on the freelist, and it was too small,
+        enlarge it.  */
+      if (! block->next
+         && __malloc_end == (void *)((size_t)block + block->size))
+       {
+         size_t moresize = real_size - block->size;
+         if (! CAN_ALLOC_P (moresize))
+           return NULL;
+         
+         *nextfree = NULL;
+         if (MALLOC_DIRECTION < 0)
+           {
+             block = __malloc_end = (void *)((size_t)block - moresize);
+           }
+         else
+           {
+             __malloc_end = (void *)((size_t)block + real_size);
+           }
+
+         goto done;
+       }
+    }
+
+  /* No free space at the end of the free list.  Allocate new space
+     and use that.  */
+
+  if (! CAN_ALLOC_P (real_size))
+    return NULL;
+
+  if (MALLOC_DIRECTION > 0)
+    {
+      block = __malloc_end;
+      __malloc_end = (void *)((size_t)__malloc_end + real_size);
+    }
+  else
+    {
+      block = __malloc_end = (void *)((size_t)__malloc_end - real_size);
+    }
+ done:
+  block->size = real_size;
+  return (void *)&block->next;
+}
+
+#endif
+
+#ifdef DEFINE_FREE
+
+void
+free (void *block_p)
+{
+  fle *nextfree;
+  fle block = (fle)((size_t) block_p - offsetof (struct freelist_entry, next));
+
+  if (block_p == NULL)
+    return;
+  
+  /* Look on the freelist to see if there's a free block just before
+     or just after this block.  */
+  for (nextfree = &__malloc_freelist; 
+       *nextfree; 
+       nextfree = &(*nextfree)->next)
+    {
+      fle thisblock = *nextfree;
+      if ((size_t)thisblock + thisblock->size == (size_t) block)
+       {
+         thisblock->size += block->size;
+         if (MALLOC_DIRECTION > 0
+             && thisblock->next
+             && (size_t) block + block->size == (size_t) thisblock->next)
+           {
+             thisblock->size += thisblock->next->size;
+             thisblock->next = thisblock->next->next;
+           }
+         return;
+       }
+      else if ((size_t) thisblock == (size_t) block + block->size)
+       {
+         if (MALLOC_DIRECTION < 0
+             && thisblock->next
+             && (size_t) block == ((size_t) thisblock->next 
+                                   + thisblock->next->size))
+           {
+             *nextfree = thisblock->next;
+             thisblock->next->size += block->size + thisblock->size;
+           }
+         else
+           {
+             block->size += thisblock->size;
+             block->next = thisblock->next;
+             *nextfree = block;
+           }
+         return;
+       }
+      else if ((MALLOC_DIRECTION > 0
+               && (size_t) thisblock > (size_t) block)
+              || (MALLOC_DIRECTION < 0
+                  && (size_t) thisblock < (size_t) block))
+       break;
+    }
+
+  block->next = *nextfree;
+  *nextfree = block;
+  return;
+}
+#endif
+
+#ifdef DEFINE_REALLOC
+void *
+realloc (void *block_p, size_t sz)
+{
+  fle block = (fle)((size_t) block_p - offsetof (struct freelist_entry, next));
+  size_t real_size = REAL_SIZE (sz);
+  size_t old_real_size;
+
+  if (block_p == NULL)
+    return malloc (sz);
+
+  old_real_size = block->size;
+
+  /* Perhaps we need to allocate more space.  */
+  if (old_real_size < real_size)
+    {
+      void *result;
+      size_t old_size = old_real_size - sizeof (size_t);
+      
+      /* Need to allocate, copy, and free.  */
+      result = malloc (sz);
+      if (result == NULL)
+       return NULL;
+      memcpy (result, block_p, old_size < sz ? old_size : sz);
+      free (block_p);
+      return result;
+    }
+  /* Perhaps we can free some space.  */
+  if (old_real_size - real_size >= sizeof (struct freelist_entry))
+    {
+      fle newblock = (fle)((size_t)block + real_size);
+      block->size = real_size;
+      newblock->size = old_real_size - real_size;
+      free (&newblock->next);
+    }
+  return block_p;
+}
+#endif
+
+#ifdef DEFINE_CALLOC
+void *
+calloc (size_t n, size_t elem_size)
+{
+  void *result;
+  size_t sz = n * elem_size;
+  result = malloc (sz);
+  if (result != NULL)
+    memset (result, 0, sz);
+  return result;
+}
+#endif
+
+#ifdef DEFINE_CFREE
+void
+cfree (void *p)
+{
+  free (p);
+}
+#endif
+
+#ifdef DEFINE_MEMALIGN
+void *
+memalign (size_t align, size_t sz)
+{
+  fle *nextfree;
+  fle block;
+
+  /* real_size is the size we actually have to allocate, allowing for
+     overhead and alignment.  */
+  size_t real_size = REAL_SIZE (sz);
+
+  /* Some sanity checking on 'align'. */
+  if ((align & (align - 1)) != 0
+      || align <= 0)
+    return NULL;
+
+  /* Look for the first block on the freelist that is large enough.  */
+  /* One tricky part is this: We want the result to be a valid pointer
+     to free.  That means that there has to be room for a size_t
+     before the block.  If there's additional space before the block,
+     it should go on the freelist, or it'll be lost---we could add it
+     to the size of the block before it in memory, but finding the
+     previous block is expensive.  */
+  for (nextfree = &__malloc_freelist; 
+       ; 
+       nextfree = &(*nextfree)->next)  
+    {
+      size_t before_size;
+      size_t old_size;
+
+      /* If we've run out of free blocks, allocate more space.  */
+      if (! *nextfree)
+       {
+         old_size = real_size;
+         if (MALLOC_DIRECTION < 0)
+           {
+             old_size += M_ALIGN_SUB (((size_t)__malloc_end 
+                                       - old_size + sizeof (size_t)),
+                                      align);
+             if (! CAN_ALLOC_P (old_size))
+               return NULL;
+             block = __malloc_end = (void *)((size_t)__malloc_end - old_size);
+           }
+         else
+           {
+             block = __malloc_end;
+             old_size += M_ALIGN ((size_t)__malloc_end + sizeof (size_t),
+                                  align);
+             if (! CAN_ALLOC_P (old_size))
+               return NULL;
+             __malloc_end = (void *)((size_t)__malloc_end + old_size);
+           }
+         *nextfree = block;
+         block->size = old_size;
+         block->next = NULL;
+       }
+      else
+       {
+         block = *nextfree;
+         old_size = block->size;
+       }
+      
+
+      before_size = M_ALIGN (&block->next, align);
+      if (before_size != 0)
+       before_size = sizeof (*block) + M_ALIGN (&(block+1)->next, align);
+
+      /* If this is the last block on the freelist, and it is too small,
+        enlarge it.  */
+      if (! block->next
+         && old_size < real_size + before_size
+         && __malloc_end == (void *)((size_t)block + block->size))
+       {
+         if (MALLOC_DIRECTION < 0)
+           {
+             size_t moresize = real_size - block->size;
+             moresize += M_ALIGN_SUB ((size_t)&block->next - moresize, align);
+             if (! CAN_ALLOC_P (moresize))
+               return NULL;
+             block = __malloc_end = (void *)((size_t)block - moresize);
+             block->next = NULL;
+             block->size = old_size = old_size + moresize;
+             before_size = 0;
+           }
+         else
+           {
+             if (! CAN_ALLOC_P (before_size + real_size - block->size))
+               return NULL;
+             __malloc_end = (void *)((size_t)block + before_size + real_size);
+             block->size = old_size = before_size + real_size;
+           }
+
+         /* Two out of the four cases below will now be possible; which
+            two depends on MALLOC_DIRECTION.  */
+       }
+
+      if (old_size >= real_size + before_size)
+       {
+         /* This block will do.  If there needs to be space before it, 
+            split the block.  */
+         if (before_size != 0)
+           {
+             fle old_block = block;
+
+             old_block->size = before_size;
+             block = (fle)((size_t)block + before_size);
+             
+             /* If there's no space after the block, we're now nearly
+                 done; just make a note of the size required.  
+                Otherwise, we need to create a new free space block.  */
+             if (old_size - before_size 
+                 <= real_size + sizeof (struct freelist_entry))
+               {
+                 block->size = old_size - before_size;
+                 return (void *)&block->next;
+               }
+             else 
+               {
+                 fle new_block;
+                 new_block = (fle)((size_t)block + real_size);
+                 new_block->size = old_size - before_size - real_size;
+                 if (MALLOC_DIRECTION > 0)
+                   {
+                     new_block->next = old_block->next;
+                     old_block->next = new_block;
+                   }
+                 else
+                   {
+                     new_block->next = old_block;
+                     *nextfree = new_block;
+                   }
+                 goto done;
+               }
+           }
+         else
+           {
+             /* If the block found is just the right size, remove it from
+                the free list.  Otherwise, split it.  */
+             if (old_size <= real_size + sizeof (struct freelist_entry))
+               {
+                 *nextfree = block->next;
+                 return (void *)&block->next;
+               }
+             else
+               {
+                 size_t newsize = old_size - real_size;
+                 fle newnext = block->next;
+                 *nextfree = (fle)((size_t)block + real_size);
+                 (*nextfree)->size = newsize;
+                 (*nextfree)->next = newnext;
+                 goto done;
+               }
+           }
+       }
+    }
+
+ done:
+  block->size = real_size;
+  return (void *)&block->next;
+}
+#endif
+
+#ifdef DEFINE_VALLOC
+void *
+valloc (size_t sz)
+{
+  return memalign (128, sz);
+}
+#endif
+#ifdef DEFINE_PVALLOC
+void *
+pvalloc (size_t sz)
+{
+  return memalign (128, sz + M_ALIGN (sz, 128));
+}
+#endif
+
+#ifdef DEFINE_MALLINFO
+#include "malloc.h"
+
+struct mallinfo 
+mallinfo (void)
+{
+  struct mallinfo r;
+  fle fr;
+  size_t free_size;
+  size_t total_size;
+  size_t free_blocks;
+
+  memset (&r, 0, sizeof (r));
+
+  free_size = 0;
+  free_blocks = 0;
+  for (fr = __malloc_freelist; fr; fr = fr->next)
+    {
+      free_size += fr->size;
+      free_blocks++;
+      if (! fr->next)
+       {
+         int atend;
+         if (MALLOC_DIRECTION > 0)
+           atend = (void *)((size_t)fr + fr->size) == __malloc_end;
+         else
+           atend = (void *)fr == __malloc_end;
+         if (atend)
+           r.keepcost = fr->size;
+       }
+    }
+
+  if (MALLOC_DIRECTION > 0)
+    total_size = (char *)__malloc_end - (char *)&__malloc_start;
+  else
+    total_size = (char *)&__malloc_start - (char *)__malloc_end;
+  
+#ifdef DEBUG
+  /* Fixme: should walk through all the in-use blocks and see if
+     they're valid.  */
+#endif
+
+  r.arena = total_size;
+  r.fordblks = free_size;
+  r.uordblks = total_size - free_size;
+  r.ordblks = free_blocks;
+  return r;
+}
+#endif
+
+#ifdef DEFINE_MALLOC_STATS
+#include "malloc.h"
+#include <stdio.h>
+
+void 
+malloc_stats(void)
+{
+  struct mallinfo i;
+  FILE *fp;
+  
+  fp = stderr;
+  i = mallinfo();
+  fprintf (fp, "malloc has reserved %u bytes between %p and %p\n",
+          i.arena, &__malloc_start, __malloc_end);
+  fprintf (fp, "there are %u bytes free in %u chunks\n",
+          i.fordblks, i.ordblks);
+  fprintf (fp, "of which %u bytes are at the end of the reserved space\n",
+          i.keepcost);
+  fprintf (fp, "and %u bytes are in use.\n", i.uordblks);
+}
+#endif
+
+#ifdef DEFINE_MALLOC_USABLE_SIZE
+size_t 
+malloc_usable_size (void *block_p)
+{
+  fle block = (fle)((size_t) block_p - offsetof (struct freelist_entry, next));
+  return block->size - sizeof (size_t);
+}
+#endif
+
+#ifdef DEFINE_MALLOPT
+int
+mallopt (int n, int v)
+{
+  (void)n; (void)v;
+  return 0;
+}
+#endif