Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation


by ceedee (Sexton)
on Jun 22, 2002 at 08:44 UTC ( #176463=perlquestion: print w/replies, xml ) Need Help??

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

Hello monks,

I am faced with the task of having to be introduced to a series of scripts which have no documentation. After spending the last few days reading through them and tracing back and forth between required scripts, their structure is beginning to make some sense.

What I am after is some advice (nodes, links, anything) in terms of inspecting the structure of the existing scripts. I would like to write a script capable of peering into scripts loaded via require or use, distinguishing the difference between itself and the namespaces of the included scripts and recording the subs (and the arguments required for those subs if possible). Maybe someone can point me in the right direction.

I will be refering to the %INC collection as I trace through these scripts (since it is difficult to find the right script containing the correct subroutine...the collection of scripts is enormous the result of over a years work by a team of three). However I am thinking about keeping track of references to subroutines as they are executed, is there a variable which stores this sort of information during execution time such as defined subroutines and currently executing functions (maybe a stack of namespaces)?

I don't expect to be able to gain a complete picture of this collection, however I do want to be able to record some indication which can aid in the process of reading through them.

Replies are listed 'Best First'.
Re: Inspection/Introspection
by rinceWind (Monsignor) on Jun 22, 2002 at 09:36 UTC
    A question for you, ceedee.

    Do these scripts use strict and warnings? If they don't, it would be a good idea to make them do so. This will considerably simplify your maintenance task in future. See my node Thoughts on revisiting old code.

    Secondly, a profiler such as Devel::DProf can help with your subroutines and %INC. You might also consider Devel::SmallProf which gives execution times and counts of each line of code. BTW you don't want the profiler turned on normally, as it considerably slows down execution of the scripts.

      Unfortunately strict is not used in any of these scripts. When I began writing a script using strict it became obvious that the scripts would have plenty of warnings (it would be nice to avoid having to write in a compatible way with this system but I'm afraid thats a can't do). I have been advised not to use strict by one of the original developers of the code. However I am fortunate enough to also be working on a part of the system which can be relatively separate from the rest, but I am hoping to extract what is reusable from the bulk of the system and write those into packages (a project which has been started).

      The existing system uses alot of global values which to an outsider like myself are ambiguous at first until I follow through their use (makes for plenty of reading). Alot of these globals refer to local directory structures, database settings and a global array stores the most recent results from database queries. These are not passed between subroutines but are frequently assumed to be defined. Similarly references to external subroutines do not follow the $object->dosomething() layout but imply that the external subroutines have been imported and are able to be called similarly as if they were included in the body of the calling script. This makes the collection very tangled indeed (grep has become a close friend).

      I will have a read through the links you've supplied.
Re: Inspection/Introspection
by mattr (Curate) on Jun 22, 2002 at 14:23 UTC
    While I cannot recommend from personal experience, these seem to be the links you need. Though I actually have a porting project that could use them I end up writing little greppy scripts that make text reports of what calls what. So for my current project besides just grepping the names of all subs in a file at the command line, I have written some utilities based on File::Find. For example make a list of all the files in all folders, sort by suffix, find callers, find all html files that call a given cgi, etc. This info is important for me to refactor it all.

    But I was moved to check again since I remember finding (and I think mentioning on PM, somewhere..) someone who had done a cool demonstration of graphing of his perl program at runtime. I couldn't find it but think this is probably GraphViz..

    Answer: Generate UML from Perl code? includes four responses including autodia.
    Generate a Graph-ical call tree for your *.pm perl modules uses graphviz at AT&T.
    An article at DDJ about using graphviz.
    cpan search for graphviz which showed me some other things like GraphViz::ISA and Joe McMahon's site on GraphViz::Data::Structure

    Hope this helps.

      These are very good, thanks.
      When I come up with a solution I'll post it. Even if its just implemented similarly to the above examples.
      There's alot to digest.
Re: Inspection/Introspection
by Aristotle (Chancellor) on Jun 22, 2002 at 15:52 UTC

    Unfortunately since there's no such thing as named parameters in Perl there's no real way to find out about a function's parameters either. (Update: That is, there is no introspective data structure where parameters could be read from; you have, as described below, to hook the function calls and dump the passed data to find out what's going on.)

    A namespace's globals are stored in a hash that is named after it, with the double doublecolon; for example, %main::. To find out all the globals defined in package main, you could iterate over keys %main::, and query its type using f.ex ref $main::{some_global}.

    A very useful module that comes to mind is Hook::WrapSub.

    You could iterate over the keys in %SomeNamespace::, find which ones are subs, and wrap those in a function that logs debugging information, including the parameters in @_. You could also tie the global variables of the namespaces to wrapper classes that log their use.

    It's going to be a fair bit of work though.

    Makeshifts last the longest.

      Unfortunately since there's no such thing as named parameters in Perl there's no real way to find out about a function's parameters either.
      true, and false!

      there are no named params in perl 5 (they're coming in perl 6.) you can find out about a function's parameters, though. use Devel::TraceSubs, my first cpan module (you'll have to install Hook::LexWrap first.) you specify namespaces in which you want to trace the subroutines, it reports in text or wrapped (read html) format. optionally, you can see the parameters passed to the subs. version 0.01 prints only scalars, so you won't see data inside references.

      you can see the stack depth for each call, and figure out how each piece of code relates to the rest.

      i'm working on changes (thanks to Jenda,) which i hope to put in version 0.02 (this weekend?) you'll be able to see params passed through Data::Dumper, and sub entry/exit timestamps. it will also fix some other bugs related to modules it's not allowed to trace (Carp, Data::Dumper, and any others i find.)

      oh, and i think you'll be better able to solve your overall problem by reading up on refactoring. Martin Fowler's book "Refactoring: Improving the Design of Existing Code", web site, buy it is a really good one. in case you don't get a chance to read it, i'll give you a few hints: don't refactor and add functionality at the same time, and take small steps. read the book, it's tremendous.

      ~Particle *accelerates*

        Sounds much like the approach I described. :) When I said there's no way to find out about a function's formal parameters, I meant that there's no introspective data structure anywhere that could provide this information, the way the namespace hashes provide information about the defined globals. Note I did point out you can wrap the traced subs with a debugger function that dumps the parameters to and results from them.

        Makeshifts last the longest.

Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://176463]
Approved by cjf
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others pondering the Monastery: (5)
As of 2023-12-04 04:07 GMT
Find Nodes?
    Voting Booth?
    What's your preferred 'use VERSION' for new CPAN modules in 2023?

    Results (21 votes). Check out past polls.