in reply to Re: Auto-generating annoted call trees
in thread Auto-generating annoted call trees

It is hard to do a purely static analysis of a perl program to determine the functions and call trees. eval can dynamically create functions, there is polymorphism, and what about closures?

Intuitively, that sort of static analysis seems to me to be equivalent to the halting problem in the general case -- you'd have to determine the depth of recursive call chains, handle mutually recursive functions, and so on. Not that a general solution is necessarily required... but don't spend months on a problem that's known to be impossible to solve. :-)

As for capturing the call stack at runtime, that's probably next to impossible to do completely as well -- you'd have to test every configuration of the program -- but again, it's something for which a partial solution would be great. To ensure that you're not leaving anything out, you might want to mess around with Devel::Cover or something of the sort.

Be sure to post what you end up with! If nothing else, it sounds like a good debugging tool.

--
F o x t r o t U n i f o r m
Found a typo in this node? /msg me
% man 3 strfry

  • Comment on Re^2: Auto-generating annoted call trees

Replies are listed 'Best First'.
Re^3: Auto-generating annoted call trees
by Ytrew (Pilgrim) on Sep 14, 2004 at 15:46 UTC
    I'm definately not trying to solve the general case: I'm actively trying to duck issues like "eval" and closures because the code executed by them is often only known at run time.

    Similarly, with an object oriented approach, the same function may return objects of different classes depending on the data passed to it.

    In all those cases, I'm of the opinion that a call stack isn't going to help very much anyway: the details have been abstracted far enough away that a simple overview document will probably do a poor job of explaining them, anyway.

    Fortunately, the majority of the code I'm trying to document is code that I wrote myself, or that my co-workers did. We tend to write in a procedural style, and we tend to avoid evals and closures unless they really add something to the code. This makes "tricky stuff" rare in practice. As for recursion, I deal with it by only printing the call stack for a function the first time it's seen. This also saves space, making the overview document smaller, and hopefully, simpler.

    I did consider the run-time approach. It requires that we have a full data set of inputs that's known to cover all code paths. In practice, I doubt that will be the case, though of course it should be. It would also take a lot more development time, which is a bit of an issue, I think.

    I get to work on this at work because I figured out how to make it work (to the extent that it does) in a short amount of time, but it's not a long term project that I'm assigned to. So I probably can't put too many hours into it, unfortunately. :-(

    I started out by parsing the output of B::Concise, but found that B::Xref provided a closer fit for what I was trying to do, and was a lot simpler, so I'm using it now. If there's something that's more stable/gives file/line-number references, etc., I'd like to hear about it.

    However, the main issue I'm still really struggling with is how to tell whether a function was "born" in main, or whether it was exported there. Being able to detect un-called functions in code I'm developing is handy -- being swamped with agressively exported routines is not...

    Thanks to both of you for your replies... I think I agree with all your points, I'm just trying to weasle out of all the hard questions. :-)

    -- Ytrew Q. Uiop