Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: Getting source file and line number of a function.



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
<p.w.stephenson@xxxxxxxxxxxx> wrote:
> On Mon, 28 Jul 2008 08:46:43 -0400
> "Rocky Bernstein" <rocky.bernstein@xxxxxxxxx> 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 <p.w.stephenson@xxxxxxxxxxxx>
> Web page now at http://homepage.ntlworld.com/p.w.stephenson/
>



Messages sorted by: Reverse Date, Date, Thread, Author