From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 13511 invoked from network); 10 Aug 2008 01:12:09 -0000 X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on f.primenet.com.au X-Spam-Level: X-Spam-Status: No, score=-2.6 required=5.0 tests=AWL,BAYES_00 autolearn=ham version=3.2.5 Received: from news.dotsrc.org (HELO a.mx.sunsite.dk) (130.225.247.88) by ns1.primenet.com.au with SMTP; 10 Aug 2008 01:12:09 -0000 Received-SPF: none (ns1.primenet.com.au: domain at sunsite.dk does not designate permitted sender hosts) Received: (qmail 4804 invoked from network); 10 Aug 2008 01:11:44 -0000 Received: from sunsite.dk (130.225.247.90) by a.mx.sunsite.dk with SMTP; 10 Aug 2008 01:11:44 -0000 Received: (qmail 4163 invoked by alias); 10 Aug 2008 01:11:26 -0000 Mailing-List: contact zsh-workers-help@sunsite.dk; run by ezmlm Precedence: bulk X-No-Archive: yes X-Seq: 25428 Received: (qmail 4137 invoked from network); 10 Aug 2008 01:11:23 -0000 Received: from bifrost.dotsrc.org (130.225.254.106) by sunsite.dk with SMTP; 10 Aug 2008 01:11:23 -0000 Received: from rv-out-0506.google.com (rv-out-0506.google.com [209.85.198.226]) by bifrost.dotsrc.org (Postfix) with ESMTP id F3F0B809A196 for ; Sun, 10 Aug 2008 03:11:16 +0200 (CEST) Received: by rv-out-0506.google.com with SMTP id g37so1387336rvb.21 for ; Sat, 09 Aug 2008 18:11:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:received:received:message-id:date:from:to :subject:in-reply-to:mime-version:content-type :content-transfer-encoding:content-disposition:references; bh=tuCclZTYF9RcL1BfAvAMYETXPB4Ecmirlwy9dDmweds=; b=QHqajbLELd4eXkV6jbT2YLMhtkfakRiDV5qZYnte9UgdrQYrlqdeTh4kpJyip/fXzX rY7P9ActmqvNcGrv7XO5UwzFVQZ5O56b2OIQbLClTsdjutHjAFgtZCJlFjmXo9w3PdJs cd1W8oQo6/+DTlwtjmbrAiFir0tMnV/K7VkUY= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=message-id:date:from:to:subject:in-reply-to:mime-version :content-type:content-transfer-encoding:content-disposition :references; b=g5iYnuyV6BuGAioL+PLisJG0Ph4DznBkJkEJbQa3rD2+dg9BJAPB2wccun+XPerO4R lhbHqG+2Vf+jxX0F3tXmNpCegVTVn0JcBDZ9rKiwKPQDMifiWgrjctVZCSnqcvVDP79s BvkL9FF2Xt3UfsFG6DR5WpSfvOEFme6yCKWnw= Received: by 10.115.14.1 with SMTP id r1mr2716381wai.206.1218330675359; Sat, 09 Aug 2008 18:11:15 -0700 (PDT) Received: by 10.114.159.2 with HTTP; Sat, 9 Aug 2008 18:11:15 -0700 (PDT) Message-ID: <6cd6de210808091811pa8ddf92oc66a7a10d4cc3904@mail.gmail.com> Date: Sat, 9 Aug 2008 21:11:15 -0400 From: "Rocky Bernstein" To: zsh-workers@sunsite.dk Subject: Re: Getting source file and line number of a function. In-Reply-To: <20080809192111.7213e919@pws-pc> MIME-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Content-Disposition: inline References: <6cd6de210807261806r7ff184fdtcc7859cca0a98aef@mail.gmail.com> <200807280834.m6S8YEVo026326@news01.csr.com> <6cd6de210807280546k14a1a59fo40cb6ed968b1227b@mail.gmail.com> <20080809192111.7213e919@pws-pc> X-Virus-Scanned: ClamAV 0.92.1/7996/Sat Aug 9 22:46:47 2008 on bifrost X-Virus-Status: Clean Great! Let me know when code is committed into CVS which includes tracking source calls, and I'll revise the zshdb code then. Ditto for instruction skipping and/or showing the command to be run. Thanks. On Sat, Aug 9, 2008 at 2:21 PM, Peter Stephenson wrote: > On Mon, 28 Jul 2008 08:46:43 -0400 > "Rocky Bernstein" wrote: >> Patch should be added. It's not the only way to do this and it changes >> the functrace (which could easily be addressed, so there might be some >> discussion. > > I've finally got something that's at least consistent. Whether it does > what is needed is another matter, but I suspect it's at least going in > the right direction. It's a little different from Rocky's original. > > Instead of hijacking $functrace, I've added another array, > $funcfiletrace. I've made their behaviours' parallel one another, > i.e. they're always the same length. > > What was really doing my brain in was working out what was supposed to > be relative to what, given that I wanted to keep $functrace doing what > it always did unless that really proved to be senseless (and I don't > think it did). Eventually, I realised $functrace doesn't give you the > current context, it gives you the calling context (i.e. if you want the > current $LINENO or function name you get it some other way---the may be > more work to do here about the name part). So I've made $funcfiletrace > do the same, except that it's the context where the function was > originally defined, not the point of the current call. Example below. > > In more detail, this modified version checks for a NULL scriptfilename, > sets and also restores it in some more places (in particular autoload > files), frees the filename from the Shfunc structure, tries to ensure > that that is NULL if there isn't a name (that meant tracking down some > more places where Shfunc's are created), handles NULL filenames in > parameter.c, and fixes up an additional oddity with autoload (we don't > know on the first execution of the function where the file is, so we > need to fix up funcstack when it's already in use). > > I haven't done anything about the sourced script tracing yet. It's > probably a good idea; we should obviously make sure it's consistent. > > There could be more stuff I've missed. For example, I haven't looked at > whether ksh-style autoloads need something different. It's perfectly > possible we'll need additional variables in zsh/parameter. It would be > nice to have some way of telling what sort of context we were in > (function defined in line or autloaded, sourced file, script). For now > I'm happy if this looks like it's going the right way. > > Anyway, here's an example script showing this in action: > > > ## start > print Started functrace.zsh > zmodload zsh/parameter > > print $LINENO; print $functrace; print $funcfiletrace > > fn() { > print Inside function $0 > print $LINENO; print $functrace; print $funcfiletrace > } > > fn > > fpath=(. $fpath) > > echo 'print Inside $0 > print $LINENO; print $functrace; print $funcfiletrace > ' >autofn > > autoload autofn > > autofn > autofn > ## end > > > Output: > > > ## start > Started functrace.zsh > 4 > > > Inside function fn > 2 > ../functrace.zsh:11 > ../functrace.zsh:6 > Inside autofn > 2 > ../functrace.zsh:21 > ./autofn:0 > Inside autofn > 2 > ../functrace.zsh:22 > ./autofn:0 > ## end > > > Notes: > - (As I said above) only when we're inside a function do $functrace and > $funcfiletrace come alive, and then they give the calling context. > > - $functrace says where the function got called. $funcfiletrace says where > the function got defined. > > - The line number 0 for the autoloaded function is not an error. The > file is autofn; this is a zsh autoload, so the line that started > the definition was not in the file at all. In other words, this > is telling you the function was autoloaded from the entire contents of > that file. It's a relative path here because it got picked up from > "." in fpath; that's an unusual case in practice, so although it's > true that the file name could be ambiguous I haven't felt like > sanitizing the path. > > - The two autofn's are there to check the problem I noted above with > the use of funcstack the first time. > > > Index: Src/exec.c > =================================================================== > RCS file: /cvsroot/zsh/zsh/Src/exec.c,v > retrieving revision 1.138 > diff -u -r1.138 exec.c > --- Src/exec.c 7 Aug 2008 16:25:16 -0000 1.138 > +++ Src/exec.c 9 Aug 2008 18:10:14 -0000 > @@ -191,7 +191,7 @@ > parse_string(char *s) > { > Eprog p; > - int oldlineno = lineno; > + zlong oldlineno = lineno; > > lexsave(); > inpush(s, INP_LINENO, NULL); > @@ -1016,7 +1016,8 @@ > Wordcode next; > wordcode code; > int ret, cj, csp, ltype; > - int old_pline_level, old_list_pipe, oldlineno; > + int old_pline_level, old_list_pipe; > + zlong oldlineno; > /* > * ERREXIT only forces the shell to exit if the last command in a && > * or || fails. This is the case even if an earlier command is a > @@ -3961,6 +3962,8 @@ > shf = (Shfunc) zalloc(sizeof(*shf)); > shf->funcdef = prog; > shf->node.flags = 0; > + shf->filename = ztrdup(scriptfilename); > + shf->lineno = lineno; > > if (!names) { > /* > @@ -4059,15 +4062,24 @@ > execautofn(Estate state, UNUSED(int do_exec)) > { > Shfunc shf; > - char *oldscriptname; > + char *oldscriptname, *oldscriptfilename; > > if (!(shf = loadautofn(state->prog->shf, 1, 0))) > return 1; > > + /* > + * Probably we didn't know the filename where this function was > + * defined yet. > + */ > + if (funcstack && !funcstack->filename) > + funcstack->filename = dupstring(shf->filename); > + > oldscriptname = scriptname; > - scriptname = dupstring(shf->node.nam); > + oldscriptfilename = scriptfilename; > + scriptname = scriptfilename = dupstring(shf->node.nam); > execode(shf->funcdef, 1, 0); > scriptname = oldscriptname; > + scriptfilename = oldscriptfilename; > > return lastval; > } > @@ -4078,11 +4090,12 @@ > { > int noalias = noaliases, ksh = 1; > Eprog prog; > + char *fname; > > pushheap(); > > noaliases = (shf->node.flags & PM_UNALIASED); > - prog = getfpfunc(shf->node.nam, &ksh); > + prog = getfpfunc(shf->node.nam, &ksh, &fname); > noaliases = noalias; > > if (ksh == 1) { > @@ -4112,6 +4125,7 @@ > else > shf->funcdef = dupeprog(prog, 0); > shf->node.flags &= ~PM_UNDEFINED; > + shf->filename = fname; > } else { > VARARR(char, n, strlen(shf->node.nam) + 1); > strcpy(n, shf->node.nam); > @@ -4123,6 +4137,7 @@ > zwarn("%s: function not defined by file", n); > locallevel++; > popheap(); > + zsfree(fname); > return NULL; > } > } > @@ -4133,6 +4148,7 @@ > else > shf->funcdef = dupeprog(stripkshdef(prog, shf->node.nam), 0); > shf->node.flags &= ~PM_UNDEFINED; > + shf->filename = fname; > } > popheap(); > > @@ -4172,6 +4188,7 @@ > #ifdef MAX_FUNCTION_DEPTH > static int funcdepth; > #endif > + Shfunc shf; > > pushheap(); > > @@ -4243,6 +4260,15 @@ > fstack.prev = funcstack; > funcstack = &fstack; > > + if ((shf = (Shfunc) shfunctab->getnode(shfunctab, name))) { > + fstack.flineno = shf->lineno; > + fstack.filename = dupstring(shf->filename); > + } else { > + fstack.flineno = 0; > + fstack.filename = dupstring(fstack.caller); > + } > + > + > if (prog->flags & EF_RUN) { > Shfunc shf; > > @@ -4362,7 +4388,7 @@ > > /**/ > Eprog > -getfpfunc(char *s, int *ksh) > +getfpfunc(char *s, int *ksh, char **fname) > { > char **pp, buf[PATH_MAX]; > off_t len; > @@ -4397,6 +4423,9 @@ > r = parse_string(d); > scriptname = oldscriptname; > > + if (fname) > + *fname = ztrdup(buf); > + > zfree(d, len + 1); > > return r; > Index: Src/hashtable.c > =================================================================== > RCS file: /cvsroot/zsh/zsh/Src/hashtable.c,v > retrieving revision 1.27 > diff -u -r1.27 hashtable.c > --- Src/hashtable.c 1 Nov 2007 10:59:40 -0000 1.27 > +++ Src/hashtable.c 9 Aug 2008 18:10:14 -0000 > @@ -852,6 +852,7 @@ > zsfree(shf->node.nam); > if (shf->funcdef) > freeeprog(shf->funcdef); > + zsfree(shf->filename); > zfree(shf, sizeof(struct shfunc)); > } > > Index: Src/init.c > =================================================================== > RCS file: /cvsroot/zsh/zsh/Src/init.c,v > retrieving revision 1.91 > diff -u -r1.91 init.c > --- Src/init.c 7 Aug 2008 16:25:16 -0000 1.91 > +++ Src/init.c 9 Aug 2008 18:10:14 -0000 > @@ -268,7 +268,7 @@ > /* -c command */ > cmd = *argv; > opts[INTERACTIVE] &= 1; > - scriptname = ztrdup("zsh"); > + scriptname = scriptfilename = ztrdup("zsh"); > } else if (**argv == 'o') { > if (!*++*argv) > argv++; > @@ -325,6 +325,7 @@ > } > opts[INTERACTIVE] &= 1; > argzero = *argv; > + scriptfilename = argzero; > argv++; > } > while (*argv) > @@ -1051,10 +1052,12 @@ > source(char *s) > { > Eprog prog; > - int tempfd = -1, fd, cj, oldlineno; > + int tempfd = -1, fd, cj; > + zlong oldlineno; > int oldshst, osubsh, oloops; > FILE *obshin; > char *old_scriptname = scriptname, *us; > + char *old_scriptfilename = scriptfilename; > unsigned char *ocs; > int ocsp; > int otrap_return = trap_return, otrap_state = trap_state; > @@ -1087,6 +1090,7 @@ > loops = 0; > dosetopt(SHINSTDIN, 0, 1); > scriptname = s; > + scriptfilename = s; > > /* > * The special return behaviour of traps shouldn't > @@ -1096,6 +1100,17 @@ > trap_state = TRAP_STATE_INACTIVE; > > sourcelevel++; > + /* { */ > + /* struct funcstack fstack; */ > + /* fstack.name = dupstring("source"); */ > + /* fstack.caller = dupstring(scriptfilename); */ > + /* fstack.flineno = oldlineno; */ > + /* fstack.lineno = oldlineno; */ > + /* fstack.filename = NULL; */ > + /* fstack.prev = funcstack; */ > + /* funcstack = &fstack; */ > + /* } */ > + > if (prog) { > pushheap(); > errflag = 0; > @@ -1103,6 +1118,7 @@ > popheap(); > } else > loop(0, 0); /* loop through the file to be sourced */ > + /* funcstack = funcstack->prev; */ > sourcelevel--; > > trap_state = otrap_state; > @@ -1126,6 +1142,7 @@ > if (!exit_pending) > retflag = 0; > scriptname = old_scriptname; > + scriptfilename = old_scriptfilename; > free(cmdstack); > cmdstack = ocs; > cmdsp = ocsp; > Index: Src/parse.c > =================================================================== > RCS file: /cvsroot/zsh/zsh/Src/parse.c,v > retrieving revision 1.70 > diff -u -r1.70 parse.c > --- Src/parse.c 1 Jul 2008 18:38:40 -0000 1.70 > +++ Src/parse.c 9 Aug 2008 18:10:15 -0000 > @@ -720,7 +720,8 @@ > static int > par_pline(int *complex) > { > - int p, line = lineno; > + int p; > + zlong line = lineno; > > p = ecadd(0); > > @@ -1414,8 +1415,9 @@ > static void > par_funcdef(void) > { > - int oecused = ecused, oldlineno = lineno, num = 0, onp, p, c = 0; > + int oecused = ecused, num = 0, onp, p, c = 0; > int so, oecssub = ecssub; > + zlong oldlineno = lineno; > > lineno = 0; > nocorrect = 1; > @@ -1646,7 +1648,8 @@ > p += nrediradd; > sr += nrediradd; > } else if (tok == INOUTPAR) { > - int oldlineno = lineno, onp, so, oecssub = ecssub; > + zlong oldlineno = lineno; > + int onp, so, oecssub = ecssub; > > *complex = c; > lineno = 0; > @@ -2860,7 +2863,8 @@ > return 1; > } > noaliases = (shf->node.flags & PM_UNALIASED); > - if (!(prog = getfpfunc(shf->node.nam, NULL)) || prog == &dummy_eprog) { > + if (!(prog = getfpfunc(shf->node.nam, NULL, NULL)) || > + prog == &dummy_eprog) { > noaliases = ona; > zwarnnam(nam, "can't load function: %s", shf->node.nam); > return 1; > Index: Src/utils.c > =================================================================== > RCS file: /cvsroot/zsh/zsh/Src/utils.c,v > retrieving revision 1.198 > diff -u -r1.198 utils.c > --- Src/utils.c 31 Jul 2008 08:44:21 -0000 1.198 > +++ Src/utils.c 9 Aug 2008 18:10:17 -0000 > @@ -33,7 +33,10 @@ > /* name of script being sourced */ > > /**/ > -mod_export char *scriptname; > +mod_export char *scriptname; /* is sometimes a function name */ > + > +/**/ > +mod_export char *scriptfilename; > > #ifdef MULTIBYTE_SUPPORT > struct widechar_array { > Index: Src/zsh.h > =================================================================== > RCS file: /cvsroot/zsh/zsh/Src/zsh.h,v > retrieving revision 1.140 > diff -u -r1.140 zsh.h > --- Src/zsh.h 7 Aug 2008 16:25:16 -0000 1.140 > +++ Src/zsh.h 9 Aug 2008 18:10:18 -0000 > @@ -1061,6 +1061,8 @@ > > struct shfunc { > struct hashnode node; > + char *filename; /* Name of file located in */ > + int lineno; /* line number in above file */ > Eprog funcdef; /* function definition */ > }; > > @@ -1079,8 +1081,10 @@ > struct funcstack { > Funcstack prev; /* previous in stack */ > char *name; /* name of function called */ > + char *filename; /* file function resides in */ > char *caller; /* name of caller */ > - int lineno; /* line number in file */ > + zlong flineno; /* line number in file */ > + zlong lineno; /* line offset from beginning of function */ > }; > > /* node in list of function call wrappers */ > Index: Src/Modules/parameter.c > =================================================================== > RCS file: /cvsroot/zsh/zsh/Src/Modules/parameter.c,v > retrieving revision 1.45 > diff -u -r1.45 parameter.c > --- Src/Modules/parameter.c 5 Sep 2007 16:16:17 -0000 1.45 > +++ Src/Modules/parameter.c 9 Aug 2008 18:10:18 -0000 > @@ -286,7 +286,7 @@ > zsfree(val); > return; > } > - shf = (Shfunc) zalloc(sizeof(*shf)); > + shf = (Shfunc) zshcalloc(sizeof(*shf)); > shf->funcdef = dupeprog(prog, 0); > shf->node.flags = dis; > > @@ -529,7 +529,35 @@ > char *colonpair; > > colonpair = zhalloc(strlen(f->caller) + (f->lineno > 9999 ? 24 : 6)); > - sprintf(colonpair, "%s:%d", f->caller, f->lineno); > + sprintf(colonpair, "%s:%ld", f->caller, (long)f->lineno); > + > + *p = colonpair; > + } > + *p = NULL; > + > + return ret; > +} > + > +/* Functions for the funcfiletrace special parameter. */ > + > +/**/ > +static char ** > +funcfiletracegetfn(UNUSED(Param pm)) > +{ > + Funcstack f; > + int num; > + char **ret, **p; > + > + for (f = funcstack, num = 0; f; f = f->prev, num++); > + > + ret = (char **) zhalloc((num + 1) * sizeof(char *)); > + > + for (f = funcstack, p = ret; f; f = f->prev, p++) { > + char *colonpair; > + char *fname = f->filename ? f->filename : ""; > + > + colonpair = zhalloc(strlen(fname) + (f->flineno > 9999 ? 24 : 6)); > + sprintf(colonpair, "%s:%ld", fname, (long)f->flineno); > > *p = colonpair; > } > @@ -1773,6 +1801,8 @@ > { funcstackgetfn, arrsetfn, stdunsetfn }; > static const struct gsu_array functrace_gsu = > { functracegetfn, arrsetfn, stdunsetfn }; > +static const struct gsu_array funcfiletrace_gsu = > +{ funcfiletracegetfn, arrsetfn, stdunsetfn }; > static const struct gsu_array reswords_gsu = > { reswordsgetfn, arrsetfn, stdunsetfn }; > static const struct gsu_array disreswords_gsu = > @@ -1807,6 +1837,8 @@ > scanpmfunctions), > SPECIALPMDEF("functrace", PM_ARRAY|PM_READONLY, > &functrace_gsu, NULL, NULL), > + SPECIALPMDEF("funcfiletrace", PM_ARRAY|PM_READONLY, > + &funcfiletrace_gsu, NULL, NULL), > SPECIALPMDEF("galiases", 0, > &pmgaliases_gsu, getpmgalias, scanpmgaliases), > SPECIALPMDEF("history", PM_READONLY, > -- > Peter Stephenson > Web page now at http://homepage.ntlworld.com/p.w.stephenson/ >