mirror of
git://git.code.sf.net/p/zsh/code
synced 2025-09-03 10:21:46 +02:00
29267: add -enable-zsh-debug and use for debugging completion matcher groups
This commit is contained in:
parent
9c0a19669b
commit
506615ff94
9 changed files with 406 additions and 7 deletions
|
@ -1,3 +1,10 @@
|
|||
2011-05-14 Peter Stephenson <p.w.stephenson@ntlworld.com>
|
||||
|
||||
* 29267: configure.ac, Src/mem.c, Src/zsh.h, Src/Zle/comp.h,
|
||||
Src/Zle/compcore.c, Src/Zle/compctl.c, Src/Zle/complist.c,
|
||||
Src/Zle/compresult.c: add --enable-zsh-hash-debug and use
|
||||
for debugging completion matcher groups.
|
||||
|
||||
2011-05-13 Peter Stephenson <p.w.stephenson@ntlworld.com>
|
||||
|
||||
* Danek: 29254: Src/cond.c, Src/Builtins/rlimits.c,
|
||||
|
@ -14688,5 +14695,5 @@
|
|||
|
||||
*****************************************************
|
||||
* This is used by the shell to define $ZSH_PATCHLEVEL
|
||||
* $Revision: 1.5298 $
|
||||
* $Revision: 1.5299 $
|
||||
*****************************************************
|
||||
|
|
|
@ -76,6 +76,9 @@ struct cmgroup {
|
|||
int totl; /* total length */
|
||||
int shortest; /* length of shortest match */
|
||||
Cmgroup perm; /* perm. alloced version of this group */
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
Heapid heap_id;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -405,6 +405,11 @@ do_completion(UNUSED(Hookdef dummy), Compldat dat)
|
|||
} else if (nmatches == 1 || (nmatches > 1 && !diffmatches)) {
|
||||
/* Only one match. */
|
||||
Cmgroup m = amatches;
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
if (memory_validate(m->heap_id)) {
|
||||
HEAP_ERROR(m->heap_id);
|
||||
}
|
||||
#endif
|
||||
|
||||
while (!m->mcount)
|
||||
m = m->next;
|
||||
|
@ -509,6 +514,11 @@ after_complete(UNUSED(Hookdef dummy), int *dat)
|
|||
int ret;
|
||||
|
||||
cdat.matches = amatches;
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
if (memory_validate(cdat.matches->heap_id)) {
|
||||
HEAP_ERROR(cdat.matches->heap_id);
|
||||
}
|
||||
#endif
|
||||
cdat.num = nmatches;
|
||||
cdat.nmesg = nmessages;
|
||||
cdat.cur = NULL;
|
||||
|
@ -987,6 +997,11 @@ makecomplist(char *s, int incmd, int lst)
|
|||
diffmatches = odm;
|
||||
validlist = 1;
|
||||
amatches = lastmatches;
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
if (memory_validate(amatches->heap_id)) {
|
||||
HEAP_ERROR(amatches->heap_id);
|
||||
}
|
||||
#endif
|
||||
lmatches = lastlmatches;
|
||||
if (pmatches) {
|
||||
freematches(pmatches, 1);
|
||||
|
@ -2959,6 +2974,11 @@ begcmgroup(char *n, int flags)
|
|||
Cmgroup p = amatches;
|
||||
|
||||
while (p) {
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
if (memory_validate(p->heap_id)) {
|
||||
HEAP_ERROR(p->heap_id);
|
||||
}
|
||||
#endif
|
||||
if (p->name &&
|
||||
flags == (p->flags & (CGF_NOSORT|CGF_UNIQALL|CGF_UNIQCON)) &&
|
||||
!strcmp(n, p->name)) {
|
||||
|
@ -2975,6 +2995,9 @@ begcmgroup(char *n, int flags)
|
|||
}
|
||||
}
|
||||
mgroup = (Cmgroup) zhalloc(sizeof(struct cmgroup));
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
mgroup->heap_id = last_heap_id;
|
||||
#endif
|
||||
mgroup->name = dupstring(n);
|
||||
mgroup->lcount = mgroup->llcount = mgroup->mcount = mgroup->ecount =
|
||||
mgroup->ccount = 0;
|
||||
|
@ -3295,6 +3318,11 @@ permmatches(int last)
|
|||
fi = 1;
|
||||
}
|
||||
while (g) {
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
if (memory_validate(g->heap_id)) {
|
||||
HEAP_ERROR(g->heap_id);
|
||||
}
|
||||
#endif
|
||||
if (fi != ofi || !g->perm || g->new) {
|
||||
if (fi)
|
||||
/* We have no matches, try ignoring fignore. */
|
||||
|
@ -3323,6 +3351,9 @@ permmatches(int last)
|
|||
diffmatches = 1;
|
||||
|
||||
n = (Cmgroup) zshcalloc(sizeof(struct cmgroup));
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
n->heap_id = HEAPID_PERMANENT;
|
||||
#endif
|
||||
|
||||
if (g->perm) {
|
||||
g->perm->next = NULL;
|
||||
|
|
|
@ -1838,6 +1838,11 @@ ccmakehookfn(UNUSED(Hookdef dummy), struct ccmakedat *dat)
|
|||
diffmatches = odm;
|
||||
validlist = 1;
|
||||
amatches = lastmatches;
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
if (memory_validate(amatches->heap_id)) {
|
||||
HEAP_ERROR(amatches->heap_id);
|
||||
}
|
||||
#endif
|
||||
lmatches = lastlmatches;
|
||||
if (pmatches) {
|
||||
freematches(pmatches, 1);
|
||||
|
|
|
@ -1363,6 +1363,11 @@ compprintlist(int showall)
|
|||
while (g) {
|
||||
char **pp = g->ylist;
|
||||
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
if (memory_validate(g->heap_id)) {
|
||||
HEAP_ERROR(g->heap_id);
|
||||
}
|
||||
#endif
|
||||
if ((e = g->expls)) {
|
||||
int l;
|
||||
|
||||
|
@ -1952,6 +1957,11 @@ complistmatches(UNUSED(Hookdef dummy), Chdata dat)
|
|||
Cmgroup oamatches = amatches;
|
||||
|
||||
amatches = dat->matches;
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
if (memory_validate(amatches->heap_id)) {
|
||||
HEAP_ERROR(amatches->heap_id);
|
||||
}
|
||||
#endif
|
||||
|
||||
noselect = 0;
|
||||
|
||||
|
@ -2640,6 +2650,11 @@ domenuselect(Hookdef dummy, Chdata dat)
|
|||
s->mlbeg = mlbeg;
|
||||
memcpy(&(s->info), &minfo, sizeof(struct menuinfo));
|
||||
s->amatches = amatches;
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
if (memory_validate(amatches->heap_id)) {
|
||||
HEAP_ERROR(amatches->heap_id);
|
||||
}
|
||||
#endif
|
||||
s->pmatches = pmatches;
|
||||
s->lastmatches = lastmatches;
|
||||
s->lastlmatches = lastlmatches;
|
||||
|
@ -2835,6 +2850,11 @@ domenuselect(Hookdef dummy, Chdata dat)
|
|||
if (lastmatches)
|
||||
freematches(lastmatches, 0);
|
||||
amatches = u->amatches;
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
if (memory_validate(amatches->heap_id)) {
|
||||
HEAP_ERROR(amatches->heap_id);
|
||||
}
|
||||
#endif
|
||||
pmatches = u->pmatches;
|
||||
lastmatches = u->lastmatches;
|
||||
lastlmatches = u->lastlmatches;
|
||||
|
|
|
@ -906,7 +906,14 @@ do_allmatches(UNUSED(int end))
|
|||
|
||||
for (minfo.group = amatches;
|
||||
minfo.group && !(minfo.group)->mcount;
|
||||
minfo.group = (minfo.group)->next);
|
||||
minfo.group = (minfo.group)->next) {
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
if (memory_validate(minfo.group->heap_id)) {
|
||||
HEAP_ERROR(minfo.group->heap_id);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
mc = (minfo.group)->matches;
|
||||
|
||||
|
@ -1172,6 +1179,11 @@ do_single(Cmatch m)
|
|||
struct chdata dat;
|
||||
|
||||
dat.matches = amatches;
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
if (memory_validate(dat.matches->heap_id)) {
|
||||
HEAP_ERROR(dat.matches->heap_id);
|
||||
}
|
||||
#endif
|
||||
dat.num = nmatches;
|
||||
dat.cur = m;
|
||||
|
||||
|
@ -1210,8 +1222,14 @@ do_menucmp(int lst)
|
|||
do {
|
||||
if (!*++(minfo.cur)) {
|
||||
do {
|
||||
if (!(minfo.group = (minfo.group)->next))
|
||||
if (!(minfo.group = (minfo.group)->next)) {
|
||||
minfo.group = amatches;
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
if (memory_validate(minfo.group->heap_id)) {
|
||||
HEAP_ERROR(minfo.group->heap_id);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} while (!(minfo.group)->mcount);
|
||||
minfo.cur = minfo.group->matches;
|
||||
}
|
||||
|
@ -1291,12 +1309,18 @@ accept_last(void)
|
|||
Cmgroup g;
|
||||
Cmatch *m;
|
||||
|
||||
for (g = amatches, m = NULL; g && (!m || !*m); g = g->next)
|
||||
for (g = amatches, m = NULL; g && (!m || !*m); g = g->next) {
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
if (memory_validate(g->heap_id)) {
|
||||
HEAP_ERROR(g->heap_id);
|
||||
}
|
||||
#endif
|
||||
for (m = g->matches; *m; m++)
|
||||
if (!hasbrpsfx(*m, minfo.prebr, minfo.postbr)) {
|
||||
showinglist = -2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
menuacc++;
|
||||
|
@ -1381,7 +1405,13 @@ do_ambig_menu(void)
|
|||
insgnum = comp_mod(insgnum, lastpermgnum);
|
||||
for (minfo.group = amatches;
|
||||
minfo.group && (minfo.group)->num != insgnum + 1;
|
||||
minfo.group = (minfo.group)->next);
|
||||
minfo.group = (minfo.group)->next) {
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
if (memory_validate(minfo.group->heap_id)) {
|
||||
HEAP_ERROR(minfo.group->heap_id);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (!minfo.group || !(minfo.group)->mcount) {
|
||||
minfo.cur = NULL;
|
||||
minfo.asked = 0;
|
||||
|
@ -1393,8 +1423,14 @@ do_ambig_menu(void)
|
|||
insmnum = comp_mod(insmnum, lastpermmnum);
|
||||
for (minfo.group = amatches;
|
||||
minfo.group && (minfo.group)->mcount <= insmnum;
|
||||
minfo.group = (minfo.group)->next)
|
||||
minfo.group = (minfo.group)->next) {
|
||||
insmnum -= (minfo.group)->mcount;
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
if (memory_validate(minfo.group->heap_id)) {
|
||||
HEAP_ERROR(minfo.group->heap_id);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (!minfo.group) {
|
||||
minfo.cur = NULL;
|
||||
minfo.asked = 0;
|
||||
|
@ -1483,6 +1519,11 @@ calclist(int showall)
|
|||
int nl = 0, l, glong = 1, gshort = zterm_columns, ndisp = 0, totl = 0;
|
||||
int hasf = 0;
|
||||
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
if (memory_validate(g->heap_id)) {
|
||||
HEAP_ERROR(g->heap_id);
|
||||
}
|
||||
#endif
|
||||
g->flags |= CGF_PACKED | CGF_ROWS;
|
||||
|
||||
if (!onlyexpl && pp) {
|
||||
|
@ -1624,6 +1665,11 @@ calclist(int showall)
|
|||
for (g = amatches; g; g = g->next) {
|
||||
glines = 0;
|
||||
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
if (memory_validate(g->heap_id)) {
|
||||
HEAP_ERROR(g->heap_id);
|
||||
}
|
||||
#endif
|
||||
zfree(g->widths, 0);
|
||||
g->widths = NULL;
|
||||
|
||||
|
@ -1858,6 +1904,11 @@ calclist(int showall)
|
|||
else
|
||||
for (g = amatches; g; g = g->next)
|
||||
{
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
if (memory_validate(g->heap_id)) {
|
||||
HEAP_ERROR(g->heap_id);
|
||||
}
|
||||
#endif
|
||||
zfree(g->widths, 0);
|
||||
g->widths = NULL;
|
||||
}
|
||||
|
@ -1945,6 +1996,11 @@ printlist(int over, CLPrintFunc printm, int showall)
|
|||
for (g = amatches; g; g = g->next) {
|
||||
char **pp = g->ylist;
|
||||
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
if (memory_validate(g->heap_id)) {
|
||||
HEAP_ERROR(g->heap_id);
|
||||
}
|
||||
#endif
|
||||
if ((e = g->expls)) {
|
||||
int l;
|
||||
|
||||
|
@ -2144,7 +2200,13 @@ bld_all_str(Cmatch all)
|
|||
|
||||
buf[0] = '\0';
|
||||
|
||||
for (g = amatches; g && !g->mcount; g = g->next);
|
||||
for (g = amatches; g && !g->mcount; g = g->next) {
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
if (memory_validate(g->heap_id)) {
|
||||
HEAP_ERROR(g->heap_id);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
mp = g->matches;
|
||||
while (1) {
|
||||
|
@ -2262,6 +2324,11 @@ list_matches(UNUSED(Hookdef dummy), UNUSED(void *dummy2))
|
|||
#endif
|
||||
|
||||
dat.matches = amatches;
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
if (memory_validate(dat.matches->heap_id)) {
|
||||
HEAP_ERROR(dat.matches->heap_id);
|
||||
}
|
||||
#endif
|
||||
dat.num = nmatches;
|
||||
dat.cur = NULL;
|
||||
ret = runhookdef(COMPLISTMATCHESHOOK, (void *) &dat);
|
||||
|
|
194
Src/mem.c
194
Src/mem.c
|
@ -123,6 +123,57 @@ static Heap heaps;
|
|||
|
||||
static Heap fheap;
|
||||
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
/*
|
||||
* The heap ID we'll allocate next.
|
||||
*
|
||||
* We'll avoid using 0 as that means zero-initialised memory
|
||||
* containing a heap ID is (correctly) marked as invalid.
|
||||
*/
|
||||
static Heapid next_heap_id = (Heapid)1;
|
||||
|
||||
/*
|
||||
* The ID of the heap from which we last allocated heap memory.
|
||||
* In theory, since we carefully avoid allocating heap memory during
|
||||
* interrupts, after any call to zhalloc() or wrappers this should
|
||||
* be the ID of the heap containing the memory just returned.
|
||||
*/
|
||||
/**/
|
||||
mod_export Heapid last_heap_id;
|
||||
|
||||
/*
|
||||
* Stack of heaps saved by new_heaps().
|
||||
* Assumes old_heaps() will come along and restore it later
|
||||
* (outputs an error if old_heaps() is called out of sequence).
|
||||
*/
|
||||
LinkList heaps_saved;
|
||||
|
||||
/*
|
||||
* Debugging verbosity. This must be set from a debugger.
|
||||
* An 'or' of bits from the enum heap_debug_verbosity.
|
||||
*/
|
||||
volatile int heap_debug_verbosity;
|
||||
|
||||
/*
|
||||
* Generate a heap identifier that's unique up to unsigned integer wrap.
|
||||
*
|
||||
* For the purposes of debugging we won't bother trying to make a
|
||||
* heap_id globally unique, which would require checking all existing
|
||||
* heaps every time we create an ID and still wouldn't do what we
|
||||
* ideally want, which is to make sure the IDs of valid heaps are
|
||||
* different from the IDs of no-longer-valid heaps. Given that,
|
||||
* we'll just assume that if we haven't tracked the problem when the
|
||||
* ID wraps we're out of luck. We could change the type to a long long
|
||||
* if we wanted more room
|
||||
*/
|
||||
|
||||
static Heapid
|
||||
new_heap_id(void)
|
||||
{
|
||||
return next_heap_id++;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Use new heaps from now on. This returns the old heap-list. */
|
||||
|
||||
/**/
|
||||
|
@ -137,6 +188,15 @@ new_heaps(void)
|
|||
fheap = heaps = NULL;
|
||||
unqueue_signals();
|
||||
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
if (heap_debug_verbosity & HDV_NEW) {
|
||||
fprintf(stderr, "HEAP DEBUG: heap " HEAPID_FMT
|
||||
" saved, new heaps created.\n", h->heap_id);
|
||||
}
|
||||
if (!heaps_saved)
|
||||
heaps_saved = znewlinklist();
|
||||
zpushnode(heaps_saved, h);
|
||||
#endif
|
||||
return h;
|
||||
}
|
||||
|
||||
|
@ -152,6 +212,12 @@ old_heaps(Heap old)
|
|||
for (h = heaps; h; h = n) {
|
||||
n = h->next;
|
||||
DPUTS(h->sp, "BUG: old_heaps() with pushed heaps");
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
if (heap_debug_verbosity & HDV_FREE) {
|
||||
fprintf(stderr, "HEAP DEBUG: heap " HEAPID_FMT
|
||||
"freed in old_heaps().\n", h->heap_id);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_MMAP
|
||||
munmap((void *) h, h->size);
|
||||
#else
|
||||
|
@ -159,6 +225,21 @@ old_heaps(Heap old)
|
|||
#endif
|
||||
}
|
||||
heaps = old;
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
if (heap_debug_verbosity & HDV_OLD) {
|
||||
fprintf(stderr, "HEAP DEBUG: heap " HEAPID_FMT
|
||||
"restored.\n", heaps->heap_id);
|
||||
}
|
||||
{
|
||||
Heap myold = heaps_saved ? getlinknode(heaps_saved) : NULL;
|
||||
if (old != myold)
|
||||
{
|
||||
fprintf(stderr, "HEAP DEBUG: invalid old heap " HEAPID_FMT
|
||||
", expecting " HEAPID_FMT ".\n", old->heap_id,
|
||||
myold->heap_id);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
fheap = NULL;
|
||||
unqueue_signals();
|
||||
}
|
||||
|
@ -174,6 +255,12 @@ switch_heaps(Heap new)
|
|||
queue_signals();
|
||||
h = heaps;
|
||||
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
if (heap_debug_verbosity & HDV_SWITCH) {
|
||||
fprintf(stderr, "HEAP DEBUG: heap temporarily switched from "
|
||||
HEAPID_FMT " to " HEAPID_FMT ".\n", h->heap_id, new->heap_id);
|
||||
}
|
||||
#endif
|
||||
heaps = new;
|
||||
fheap = NULL;
|
||||
unqueue_signals();
|
||||
|
@ -202,6 +289,15 @@ pushheap(void)
|
|||
hs->next = h->sp;
|
||||
h->sp = hs;
|
||||
hs->used = h->used;
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
hs->heap_id = h->heap_id;
|
||||
h->heap_id = new_heap_id();
|
||||
if (heap_debug_verbosity & HDV_PUSH) {
|
||||
fprintf(stderr, "HEAP DEBUG: heap " HEAPID_FMT " pushed, new id is "
|
||||
HEAPID_FMT ".\n",
|
||||
hs->heap_id, h->heap_id);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
unqueue_signals();
|
||||
}
|
||||
|
@ -251,6 +347,22 @@ freeheap(void)
|
|||
if (!fheap && h->used < ARENA_SIZEOF(h))
|
||||
fheap = h;
|
||||
hl = h;
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
/*
|
||||
* As the free makes the heap invalid, give it a new
|
||||
* identifier. We're not popping it, so don't use
|
||||
* the one in the heap stack.
|
||||
*/
|
||||
{
|
||||
Heapid new_id = new_heap_id();
|
||||
if (heap_debug_verbosity & HDV_FREE) {
|
||||
fprintf(stderr, "HEAP DEBUG: heap " HEAPID_FMT
|
||||
" freed, new id is " HEAPID_FMT ".\n",
|
||||
h->heap_id, new_id);
|
||||
}
|
||||
h->heap_id = new_id;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
#ifdef USE_MMAP
|
||||
munmap((void *) h, h->size);
|
||||
|
@ -291,6 +403,14 @@ popheap(void)
|
|||
memset(arena(h) + hs->used, 0xff, h->used - hs->used);
|
||||
#endif
|
||||
h->used = hs->used;
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
if (heap_debug_verbosity & HDV_POP) {
|
||||
fprintf(stderr, "HEAP DEBUG: heap " HEAPID_FMT
|
||||
" popped, old heap was " HEAPID_FMT ".\n",
|
||||
h->heap_id, hs->heap_id);
|
||||
}
|
||||
h->heap_id = hs->heap_id;
|
||||
#endif
|
||||
if (!fheap && h->used < ARENA_SIZEOF(h))
|
||||
fheap = h;
|
||||
zfree(hs, sizeof(*hs));
|
||||
|
@ -393,6 +513,13 @@ zhalloc(size_t size)
|
|||
h->used = n;
|
||||
ret = arena(h) + n - size;
|
||||
unqueue_signals();
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
last_heap_id = h->heap_id;
|
||||
if (heap_debug_verbosity & HDV_ALLOC) {
|
||||
fprintf(stderr, "HEAP DEBUG: allocated memory from heap "
|
||||
HEAPID_FMT ".\n", h->heap_id);
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
@ -424,6 +551,13 @@ zhalloc(size_t size)
|
|||
h->used = size;
|
||||
h->next = NULL;
|
||||
h->sp = NULL;
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
h->heap_id = new_heap_id();
|
||||
if (heap_debug_verbosity & HDV_CREATE) {
|
||||
fprintf(stderr, "HEAP DEBUG: create new heap " HEAPID_FMT ".\n",
|
||||
h->heap_id);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (hp)
|
||||
hp->next = h;
|
||||
|
@ -432,6 +566,13 @@ zhalloc(size_t size)
|
|||
fheap = h;
|
||||
|
||||
unqueue_signals();
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
last_heap_id = h->heap_id;
|
||||
if (heap_debug_verbosity & HDV_ALLOC) {
|
||||
fprintf(stderr, "HEAP DEBUG: allocated memory from heap "
|
||||
HEAPID_FMT ".\n", h->heap_id);
|
||||
}
|
||||
#endif
|
||||
return arena(h);
|
||||
}
|
||||
}
|
||||
|
@ -495,6 +636,9 @@ hrealloc(char *p, size_t old, size_t new)
|
|||
* don't use the heap for anything else.)
|
||||
*/
|
||||
if (p == arena(h)) {
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
Heapid heap_id = h->heap_id;
|
||||
#endif
|
||||
/*
|
||||
* Zero new seems to be a special case saying we've finished
|
||||
* with the specially reallocated memory, see scanner() in glob.c.
|
||||
|
@ -554,6 +698,9 @@ hrealloc(char *p, size_t old, size_t new)
|
|||
heaps = h;
|
||||
}
|
||||
h->used = new;
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
h->heap_id = heap_id;
|
||||
#endif
|
||||
unqueue_signals();
|
||||
return arena(h);
|
||||
}
|
||||
|
@ -576,6 +723,53 @@ hrealloc(char *p, size_t old, size_t new)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
/*
|
||||
* Check if heap_id is the identifier of a currently valid heap,
|
||||
* including any heap buried on the stack, or of permanent memory.
|
||||
* Return 0 if so, else 1.
|
||||
*
|
||||
* This gets confused by use of switch_heaps(). That's because so do I.
|
||||
*/
|
||||
|
||||
/**/
|
||||
mod_export int
|
||||
memory_validate(Heapid heap_id)
|
||||
{
|
||||
Heap h;
|
||||
Heapstack hs;
|
||||
LinkNode node;
|
||||
|
||||
if (heap_id == HEAPID_PERMANENT)
|
||||
return 0;
|
||||
|
||||
queue_signals();
|
||||
for (h = heaps; h; h = h->next) {
|
||||
if (h->heap_id == heap_id)
|
||||
return 0;
|
||||
for (hs = heaps->sp; hs; hs = hs->next) {
|
||||
if (hs->heap_id == heap_id)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (heaps_saved) {
|
||||
for (node = firstnode(heaps_saved); node; incnode(node)) {
|
||||
for (h = (Heap)getdata(node); h; h = h->next) {
|
||||
if (h->heap_id == heap_id)
|
||||
return 0;
|
||||
for (hs = heaps->sp; hs; hs = hs->next) {
|
||||
if (hs->heap_id == heap_id)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* allocate memory from the current memory pool and clear it */
|
||||
|
||||
/**/
|
||||
|
|
60
Src/zsh.h
60
Src/zsh.h
|
@ -2327,11 +2327,67 @@ enum {
|
|||
* Memory management *
|
||||
*********************/
|
||||
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
/*
|
||||
* A Heapid is a type for identifying, uniquely up to the point where
|
||||
* the count of new identifiers wraps. all heaps that are or
|
||||
* (importantly) have been valid. Each valid heap is given an
|
||||
* identifier, and every time we push a heap we save the old identifier
|
||||
* and give the heap a new identifier so that when the heap is popped
|
||||
* or freed we can spot anything using invalid memory from the popped
|
||||
* heap.
|
||||
*
|
||||
* We could make this unsigned long long if we wanted a big range.
|
||||
*/
|
||||
typedef unsigned int Heapid;
|
||||
|
||||
/* printf format specifier corresponding to Heapid */
|
||||
#define HEAPID_FMT "%x"
|
||||
|
||||
/* Marker that memory is permanently allocated */
|
||||
#define HEAPID_PERMANENT (UINT_MAX)
|
||||
|
||||
/*
|
||||
* Heap debug verbosity.
|
||||
* Bits to be 'or'ed into the variable also called heap_debug_verbosity.
|
||||
*/
|
||||
enum heap_debug_verbosity {
|
||||
/* Report when we push a heap */
|
||||
HDV_PUSH = 0x01,
|
||||
/* Report when we pop a heap */
|
||||
HDV_POP = 0x02,
|
||||
/* Report when we create a new heap from which to allocate */
|
||||
HDV_CREATE = 0x04,
|
||||
/* Report every time we free a complete heap */
|
||||
HDV_FREE = 0x08,
|
||||
/* Report when we temporarily install a new set of heaps */
|
||||
HDV_NEW = 0x10,
|
||||
/* Report when we restore an old set of heaps */
|
||||
HDV_OLD = 0x20,
|
||||
/* Report when we temporarily switch heaps */
|
||||
HDV_SWITCH = 0x40,
|
||||
/*
|
||||
* Report every time we allocate memory from the heap.
|
||||
* This is very verbose, and arguably not very useful: we
|
||||
* would expect to allocate memory from a heap we create.
|
||||
* For much debugging heap_debug_verbosity = 0x7f should be sufficient.
|
||||
*/
|
||||
HDV_ALLOC = 0x80
|
||||
};
|
||||
|
||||
#define HEAP_ERROR(heap_id) \
|
||||
fprintf(stderr, "%s:%d: HEAP DEBUG: invalid heap: " HEAPID_FMT ".\n", \
|
||||
__FILE__, __LINE__, heap_id)
|
||||
#endif
|
||||
|
||||
/* heappush saves the current heap state using this structure */
|
||||
|
||||
struct heapstack {
|
||||
struct heapstack *next; /* next one in list for this heap */
|
||||
size_t used;
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
Heapid heap_id;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* A zsh heap. */
|
||||
|
@ -2342,6 +2398,10 @@ struct heap {
|
|||
size_t used; /* bytes used from the heap */
|
||||
struct heapstack *sp; /* used by pushheap() to save the value used */
|
||||
|
||||
#ifdef ZSH_HEAP_DEBUG
|
||||
unsigned int heap_id;
|
||||
#endif
|
||||
|
||||
/* Uncomment the following if the struct needs padding to 64-bit size. */
|
||||
/* Make sure sizeof(heap) is a multiple of 8
|
||||
#if defined(PAD_64_BIT) && !defined(__GNUC__)
|
||||
|
|
12
configure.ac
12
configure.ac
|
@ -105,6 +105,18 @@ AC_HELP_STRING([--enable-zsh-secure-free], [turn on error checking for free()]),
|
|||
AC_DEFINE(ZSH_SECURE_FREE)
|
||||
fi])
|
||||
|
||||
dnl Do you want to debug zsh heap allocation?
|
||||
dnl Does not depend on zsh-mem.
|
||||
ifdef([zsh-heap-debug],[undefine([zsh-heap-debug])])dnl
|
||||
AH_TEMPLATE([ZSH_HEAP_DEBUG],
|
||||
[Define to 1 if you want to turn on error checking for heap allocation.])
|
||||
AC_ARG_ENABLE(zsh-heap-debug,
|
||||
AC_HELP_STRING([--enable-zsh-heap-debug],
|
||||
[turn on error checking for heap allocation]),
|
||||
[if test x$enableval = xyes; then
|
||||
AC_DEFINE(ZSH_HEAP_DEBUG)
|
||||
fi])
|
||||
|
||||
dnl Do you want debugging information on internal hash tables.
|
||||
dnl This turns on the `hashinfo' builtin command.
|
||||
ifdef([zsh-hash-debug],[undefine([zsh-hash-debug])])dnl
|
||||
|
|
Loading…
Reference in a new issue