| Patch to improve MIPS call stack unwind performance by caching the results |
| of code reading. |
| by Dan Howell <dahowell@directv.com> |
| |
| diff -urN mpatrol-uclibc/src/stack.c mpatrol-unwindcache/src/stack.c |
| --- mpatrol-uclibc/src/stack.c 2006-06-22 15:39:04.000000000 -0700 |
| +++ mpatrol-unwindcache/src/stack.c 2006-06-22 15:42:20.000000000 -0700 |
| @@ -68,6 +68,7 @@ |
| #define ucontext asm_ucontext |
| #include <asm/ucontext.h> |
| #undef ucontext |
| +#include "heap.h" |
| #endif /* ARCH */ |
| #endif /* SYSTEM */ |
| #endif /* TARGET */ |
| @@ -280,6 +281,136 @@ |
| |
| #if !MP_BUILTINSTACK_SUPPORT && !MP_LIBRARYSTACK_SUPPORT |
| #if TARGET == TARGET_UNIX && ARCH == ARCH_MIPS |
| +/* Set up a tree to cache the results of code searching to determine the |
| + location of the return address for each code point encountered. */ |
| + |
| +/* An unwind node belongs to a binary search tree of nodes, ordered by |
| + * code address, and contains call stack unwinding details for a given |
| + * code address. An internal index node stores details of a single memory |
| + * block allocated for unwind node slots. |
| + */ |
| +typedef union unwindnode |
| +{ |
| + struct |
| + { |
| + treenode node; /* internal tree node */ |
| + void *block; /* pointer to block of memory */ |
| + size_t size; /* size of block of memory */ |
| + } |
| + index; |
| + struct |
| + { |
| + treenode node; /* tree node */ |
| + long p; /* return address offset in the stack */ |
| + long m; /* frame pointer offset in stack */ |
| + long s; /* stack pointer offset from previous frame */ |
| + unsigned long a; /* flags */ |
| + } |
| + data; |
| +} |
| +unwindnode; |
| + |
| +/* An unwindhead holds the table of address node slots as well as the |
| + * internal list of memory blocks allocated for address node slots. |
| + */ |
| +typedef struct unwindhead |
| +{ |
| + heaphead heap; /* pointer to heap */ |
| + slottable table; /* table of address nodes */ |
| + treeroot itree; /* internal list of memory blocks */ |
| + treeroot dtree; /* tree for sorting */ |
| + size_t size; /* memory used by internal blocks */ |
| + char init; /* initialization flag */ |
| +} |
| +unwindhead; |
| + |
| +static unwindhead unwindcache; |
| + |
| +/* Initialise the fields of an unwindhead so that there are no allocated, |
| + * freed or free blocks. |
| + */ |
| + |
| +static |
| +void |
| +newunwindcache(void) |
| +{ |
| + struct { char x; unwindnode y; } z; |
| + long n; |
| + |
| + __mp_newheap(&unwindcache.heap); |
| + /* Determine the minimum alignment for an unwind node on this |
| + * system and force the alignment to be a power of two. This |
| + * information is used when initialising the slot table. |
| + */ |
| + n = (char *) &z.y - &z.x; |
| + __mp_newslots(&unwindcache.table, sizeof(unwindnode), __mp_poweroftwo(n)); |
| + __mp_newtree(&unwindcache.itree); |
| + __mp_newtree(&unwindcache.dtree); |
| + unwindcache.size = 0; |
| + unwindcache.init = 1; |
| +} |
| + |
| + |
| +/* Forget all unwind information. |
| + */ |
| + |
| +static |
| +void |
| +deleteunwindcache(void) |
| +{ |
| + /* We don't need to explicitly free any memory as this is dealt with |
| + * at a lower level by the heap manager. |
| + */ |
| + __mp_deleteheap(&unwindcache.heap); |
| + unwindcache.table.free = NULL; |
| + unwindcache.table.size = 0; |
| + __mp_newtree(&unwindcache.itree); |
| + __mp_newtree(&unwindcache.dtree); |
| + unwindcache.size = 0; |
| + unwindcache.init = 0; |
| +} |
| + |
| + |
| +/* Allocate a new unwind node. |
| + */ |
| + |
| +static |
| +unwindnode * |
| +getunwindnode(void) |
| +{ |
| + unwindnode *n; |
| + heapnode *p; |
| + |
| + /* If we have no more allocation node slots left then we must allocate |
| + * some more memory for them. An extra MP_ALLOCFACTOR pages of memory |
| + * should suffice. |
| + */ |
| + if ((n = (unwindnode *) __mp_getslot(&unwindcache.table)) == NULL) |
| + { |
| + if ((p = __mp_heapalloc(&unwindcache.heap, unwindcache.heap.memory.page * MP_ALLOCFACTOR, |
| + unwindcache.table.entalign, 1)) == NULL) |
| + return NULL; |
| + __mp_initslots(&unwindcache.table, p->block, p->size); |
| + n = (unwindnode *) __mp_getslot(&unwindcache.table); |
| + __mp_treeinsert(&unwindcache.itree, &n->index.node, (unsigned long) p->block); |
| + n->index.block = p->block; |
| + n->index.size = p->size; |
| + unwindcache.size += p->size; |
| + n = (unwindnode *) __mp_getslot(&unwindcache.table); |
| + } |
| + return n; |
| +} |
| + |
| +/* Search for the unwind node associated with a given address. |
| + */ |
| +static |
| +unwindnode * |
| +findunwindnode(unsigned long p) |
| +{ |
| + return (unwindnode *) __mp_search(unwindcache.dtree.root, p); |
| +} |
| + |
| + |
| /* Determine the stack pointer and return address of the previous stack frame |
| * by performing code reading. |
| */ |
| @@ -289,8 +420,9 @@ |
| unwind(frameinfo *f) |
| { |
| long p, m, s; |
| - unsigned long a, i, q, t, b, r; |
| + unsigned long a, i, q, t, b, r, k; |
| unsigned short l, u; |
| + unwindnode *n = NULL; |
| |
| s = -1; |
| p = m = 0; |
| @@ -322,7 +454,23 @@ |
| #endif |
| /* Save initial code-reading starting point. |
| */ |
| - r = f->ra; |
| + r = k = f->ra; |
| + /* Create the cache if not yet created. |
| + */ |
| + if (!unwindcache.init) |
| + { |
| + newunwindcache(); |
| + __mp_atexit(deleteunwindcache); |
| + } |
| + if ((n = findunwindnode(f->ra)) != NULL) |
| + { |
| + /* We've been here before, so get the cached information. |
| + */ |
| + p = n->data.p; |
| + m = n->data.m; |
| + s = n->data.s; |
| + a = n->data.a; |
| + } |
| /* Search for the return address offset in the stack frame. |
| */ |
| while (!((a & RA_OFFSET) && (a & SP_OFFSET)) && (f->ra < q)) |
| @@ -478,6 +626,19 @@ |
| return 1; |
| } |
| #endif |
| + if (n == NULL) |
| + { |
| + if ((n = getunwindnode()) != NULL) |
| + { |
| + /* Cache the information we just got in the tree. |
| + */ |
| + n->data.p = p; |
| + n->data.m = m; |
| + n->data.s = s; |
| + n->data.a = a; |
| + __mp_treeinsert(&unwindcache.dtree, &n->data.node, k); |
| + } |
| + } |
| if (a & SP_IN_FP) |
| f->sp = f->fp; |
| if (m > 0) |