Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

Auto-generating annoted call trees

by Ytrew (Pilgrim)
on Sep 13, 2004 at 17:11 UTC ( [id://390609]=perlquestion: print w/replies, xml ) Need Help??

Ytrew has asked for the wisdom of the Perl Monks concerning the following question:

Hi, everyone, and thanks for reading.

I'm working on a little program that automatically generates an indented function call tree for a given program, and pulls out a one line description of each function called from the perldoc comments. (It pays to standardize your perldoc!) The idea is to present a quick overview of an entire program's calling hierarchy, without exposing too much detail at once.

I'm currently parsing the output of the B::Xref module to get the functions called, and then printing the tree.

My questions are:

1) Is there an easier (existing?) way to determine the function call stack, perhaps hidden somewhere in the B modules? I've looked through several, but I haven't made an exhaustive search. Suggestions would be appreciated.

2) Method calls are an interesting wrinkle in this whole design. The class name of an object would be a nice thing to print, along with the name of the method called.

However, the class of a given object is not guaranteed to be known at compile time.

This is a design issue that I've so far dealt with by ignoring it :-) Comments/suggestions are appreciated.

3) I can detect "dead code" in the form of subroutines that are never called, by subroutines) by looking at which subroutines are listed in the defintions section versus which are actually called. I'm really only interested in the functions within the main package, for now.

However, I'm not really interested in hearing about functions which were exported into the main package, and weren't used: I'm more interested in knowing which functions that were defined in the given *file* weren't ever called.

Short of playing games with a custom "Exporter" module, is there a way to determine which subroutines were exported into the main package, and which ones were not?

Any comments/suggestions/criticisms are welcome!

Thanks in advance,

-- Ytreq Q. Uiop

Replies are listed 'Best First'.
Re: Auto-generating annoted call trees
by kvale (Monsignor) on Sep 13, 2004 at 17:45 UTC
    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?

    To capture this information, it seems that you would have to instrument the program and run it to gather all the info that you need. Two ways of instrumenting the program come to mind.

    First, you could profile the program (with say, Devel::DProf, etc.). It provides a call tree and tells you which code actually gets executed.

    Another possibility is to use the debugger and have it store relevant info, breaking at each statement. Post processing the data collected will give you a call tree and list of executed routines.

    -Mark

      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

        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

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://390609]
Approved by Corion
Front-paged by stvn
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others pondering the Monastery: (5)
As of 2024-04-23 07:01 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found