Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister

How to debug unknown dynamic code?

by gaspodethewonderdog (Monk)
on Dec 27, 2000 at 22:25 UTC ( [id://48474] : perlquestion . print w/replies, xml ) Need Help??

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

Alright... it started off as a simple little problem. Just use somebody else's code and not have to worry about how things get done. Apparently I was oh so very wrong. The problem I have is that the engineer who wrote the code seems to have learned some new tricks and decided to put them where they don't belong, and to top it off left for vacation... hurray! hehe

Anyways... there are two types of code that I need to be able to break on and haven't come up with a good way to break in them. One is an eval, which using my deductive reasoning is probably stepable through but I haven't tried yet.

The other code I'm having trouble with though is that the engineer has used modules (not the problem) but has used references to the other module functions elsewhere to essentially do switches in his code. I'm not familiar with the more powerful stuff with debugging so I don't know how to break on something like:

&$function($arg1, $arg2);
I would just set a breakpoint on a line number but this is in another module and the path to get there is a little bizarre. I tried setting a breakpoint on "Module::function" but that didn't stop it. Which leads me to believe that either breakpoints don't work that way, or with my luck I've isolated a chunk of code that I thought did something that actually never gets called.

... This is slowly become a nightmare and I guess I need to learn some tricks for using the debugger, because to top it off I can't check out this code to put in print statements or anything... and as the final blow to making anything even understandable this is a client/server program, with both in the same code base and the code that is run for the client or the server is indistiguishable from the other.

So any monks out there have any words of wisdom or advice (other than quit your job... hehe) I'd really appreciate it. Thanks!!!

Replies are listed 'Best First'.
Are debuggers good?
by tilly (Archbishop) on Dec 27, 2000 at 22:52 UTC
    A while ago on another site I had an interesting discussion on exactly this topic. I find it amazing how many good programmers have all sorts of uses for interactive debuggers - but say that debugging is not one of them! It certainly isn't for me. And in this problem in particular, the code you are describing sounds like stuff which I would suggest that you not use a debugger on.

    So how would I approach this problem?

    First of all I would use strict. If his code cannot readily be cleaned up to pass that, then he is likely doing some nasty stuff. I would prefer to rewrite than maintain dynamic symbolic references.

    Otherwise I would then step back and set out to understand the code on a conceptual level. It will take a while. But that is the main problem with debuggers - they make you think about problems at too low a level.

    Now you say you cannot check his code out. Do you have a development environment that is separate from production? If so then develop in that. If not then tell the rest of us where you work so we know to avoid the company, then try to figure out how you can set up a small test environment.

    If you can't do that, then read the code anyways. And let this be a lesson for you in the future. Don't use stuff that you don't really understand which you are not completely confident about... :-)

      I'm amazed at how many people "just add prints" when they need to debug. I believe this has to do with not wanting to take the time to learn how to use the debugger.

      Even if you only want to "just add prints", then you can use the debugger to simply add prints dynamically.

      And using Data::Dumper to debug? I find an interactive "x $obj" requires much less typing, gives me a chance to look at related data if $obj shows something interesting, lets me change $obj to test if a suspected bug fix will actually get the proper output (often allowing me to fix more than one bug in a single run), doesn't force me to either page through lots of debug output nor try multiple runs trying to get the debug output right, and on and on.

      If you've never used the Perl debugger for debugging, I urge you to give it a try the next time you need to debug.

      I guess I've been "blessed" by having to track down non-trivial bugs that would be horrible to find by just adding trace so I've always managed to learn how to use the debugger for most of the programming environments I work in for very long.

      I just can't believe that the process of "Gee, which things should I print out in the debug trace..." or the effort of printing out "everything" and then slogging through the output is more appealing or more efficient than setting a break point at the place were you wanted the trace and then just typing "x $x,$y", "No, those look right", type "|x $obj", "Hmm, why is 'color' undefined?", etc.

              - tye (but my friends call me "Tye")
        I just can't believe all of the people who use debuggers who think that people like Randal and I are just taking a position out of ignorance of how to use a debugger!

        Follow through the thread of discussion in the conversation that I pointed to. The exact issue of Kernel Threads under discussion which I pointed to is here, check out sections 1 and 4. In particular the following posts stand out as being particularly relevant. First the one where Linus says, "I am a bastard, and that's OK", and IBM's experience.

        Both of those articles are written by competent programmers with years of experience developing in a very complex environment who both know debuggers. Both claim that while debuggers look easy, neither think that they really help. If you read those threads you will find many competent people who absolutely disagree with that position. There are people on either side.

        Note that I didn't say that debuggers are useless for debugging. I said I am constantly amazed at how many good programmers don't like them for that purpose but have plenty of other uses. It should be obvious that good programmers who have found other uses for a debugger actually understand what they are and how to use them. Ignorance is not the reason for not choosing to use them.

        No, the reason is more subtle. The limit to our ability to develop interesting things lies in our ability to comprehend. Debuggers focus intention at the wrong point. They let us blindly trace through code to find the symptoms of problems. That doesn't help us one bit to notice more subtle symptoms of structural problems in the code. It doesn't help one bit in giving us encouragement to be more careful. It doesn't encourage reflection on the process. It doesn't give us reason to think about the poor schmuck that has to handle our code afterwards.

        In short it encourages missing the forest for the trees. But in the end projects are felled by overgrown thickets, not by single trees.

        Now I know you will likely disagree. This is an opinion which many people are divided on. But my opinion just happens to be shared by a lot of very competent people. It isn't based on ignorance. Instead it is based on experience and thought about the process. I have repeated this point to plenty of friends. Some make you go, "hmmm". Like the guy who said, "That is true. I have written debuggers. I used to use them all of the time. But then I thought about my own thinking process, and tried not using it for a bit. After that I never looked back."

        However one thing we will both agree on. If you don't think about your own development process on an ongoing basics, then there is no question that a debugger would be helpful. They are effective tools, no question about that.

        Let me concur with your statement and add a few cents on my part. I too started out with 'just print statements'. And, oh boy, it was getting on my nerves many a times. I did print statements since the very first time I started coding (at age 13 I believe) with BASIC. I then used prints in Pascal code (I didn't have a good debugger back than). Even when I moved to C I used prints. In many cases it's 'alright', but nothing more.... just about 'alright' to use. However, (and I assume for many it would seem like a mute point) I didn't find putting random (well, at least the first time I need to work on a piece of code that I've never 'debugged' that way before) print statements as helpful and efficient as setting a few breakpoints throughout my code and letting the program run until these points are reached. The problem that I experienced with the print statements was that I'd always have to re-run the code a number of times to just get my print statements placed in the right place and output just the right stuff to get at the root of a bug. This wasn't good for me. Recently (when I started working full-time) this wasn't good for my boss since I found myself wasting too much time playing with prints etc (on systems exceeding 30,000 lines of code in particular).

        So, a few years back when I first started with Perl, I thought that this is the one language where I'll try to do it right (and different!) When I had my first look at perl interactive debugger, I immediately thought "Who in the world could you such a horrible and complex tool?!". Well, to sum up, I was utterly wrong. Once I played with it a bit more, I figured that it was more useful to my productivity as a programmer than my old limbo with prints etc. I frankly find it hard to understand marlyn and others who say that doing away with prints is much easier. See, I was in your camp at one point as well. However, how easier could it be to simply place a few break pionts in your code and let the program run until these points are reached and do your testing (on 'faulty/suspicious' data) right there, poke it around, execute different variants of a program statement that you suspect might have a bug, etc. There's really that much use in a debugger. Now, I'm not even talking of those point and click debuggers. I don't like those in particular. However, perl's interactive debugger (where I can get away with not using my mouse) is superb for many debugging needs one might have. It's pretty quick.... there's much I could say about various debugging techniques that the perl debugger allows for and that I've come to appreciate.

        "There is no system but GNU, and Linux is one of its kernels." -- Confession of Faith
      I'm not into debugging. I can't recall ever invoking the Perl debugger except to try out snippets of code at a prompt (for which I type perl -dead usually), or in class while I'm trying to teach the debugger (which I do poorly, since I don't "debug" my code with a debugger). But never for debugging. (Larry is also like this, I'm told.)

      So the question is, what do I use to debug?

      Well, the first answer (and most important answer) is that I don't have to debug what doesn't contain bugs. Don't put bugs in. That's easier than it sounds, if you code in small chunks, slowly adding code one understandable step at a time. Never type anything into a program if you can't completely run it in your head.

      The second part of the answer is that I started programming long before we had all these fancy GUIs and drag-n-drool debuggers. So when I had code go off the deep end, all I could do is keep adding print statements until it worked. As I got better, the number of prints got smaller, and then eventually I could just code the whole thing without adding any debugging.

      With Perl, I throw in a random "die", sometimes with the result of Data::Dumper::Dumper in its mouth, but that's usually only once every five or ten times as I extend the program.

      So what's all this about a Perl debugger? Why do people use it? Have they not learned the more effective ways of never letting your code get ahead of you, and trying to reduce the problem to something a die can show you? I'm truly puzzled.

      -- Randal L. Schwartz, Perl hacker

        I wonder if a lot of your opinion stems from the kind of coding that you do merlyn. Seems to me that you're lucky enough to be able to work on your own code most of the time. And would it fair to say that most of the programs that you write are small demos of particular Perl features for training courses or articles?

        If I was writing programs under those conditions, I don't think I'd have very much use for the debugger either. Unfortunately, I don't work like that. I spend three or six months on a client's site and the first thing they invariably do is throw a piece of the most horrible Perl you've ever seen at me and ask me to either fix or enhance it. In cases like these I find that running the code through the debugger is an invaluable way to get an in-depth look at exactly how the progrma works.

        I think that not enough people use the debugger. I think that this is often because Perl programming is seen as 'scripting' and not important enough to justify that level of work. I further think that this point of view is completely wrong, but that it explains a lot of the very dodgy Perl code that you can find out there.


        "Perl makes the fun jobs fun
        and the boring jobs bearable" - me

        I'm somewhat offended by your characterization of people who use the Perl debugger for debugging. One could just as easily look at it from the opposite viewpoint:

        What's all this about debugging with print statements? Why do people do that? Have they not learned the more effective ways of using a real debugger? I'm truly puzzled.

        As I see it, the only advantage to print statements is that they're simple to use. An actual debugger is better in all other aspects.

        Oh how I concur - actually, I was quite shocked to hear this coming from you, merlyn - no particular reason.

        When I was studying CS in college, I was a lab assistant. I learned very quickly to code in small chunks - and save often - revision control was also a big plus.

        I learned this, but most of the first and second year CS majors refused to work in this manner. It was a shame to watch them type about 100 lines of code, try to compile it, and then ask me to help figure out what these 200 lines of errors meant.

        To tell the truth, the only debugger I found helpfull is the one that is available for Visual Basic.


        (the triplet paradiddle)
        Two Whoops for this. I've never found the debugger to be remotely useful. The pain of using it outweighs the value. I use the debugger to understand behavior of code or algos, I use prints or warn logging to find bugs when I'm too lazy to refactor the code that got out of hand.

        About half the time, I just rewrite the section in multiple pieces and the bug "goes away". Heck, I rarely open the debugger on _C_ code unless I need to find out what was on the stack at a core. Perl is ever so much better than that, I just can't imagine that it is very useful.

        Update I wouldn't go so far as to say that gdb rocks my world but it has sure made working with big bad C quite a bit easier. Still, I usually don't pick it up will there is a nasty problem.

        $you = new YOU;
        honk() if $you->love(perl)

        I occasionally use the debugger for verifying small programs. However in large systems, rather than printing program information to stdout, I use a log file framework to capture diagnostic information. By using a log, you can capture information over time. Of course like print statements, there is an art to adding log statements at appropriate times.

        I remember having a heated discussion with a 3rd party equipment vendor that started with me asking why they didn't produce logs. It finally came out that he didn't think that logging was a legitimate diagnostic tool because the technique is never taught in school. He encouraged his developers to use the debugger whenever possible to diagnose problems. I can tell you for a fact that their code was extremely buggy. It would take several hours of runtime before the bugs would propagate enough to affect the overall performance of the system.

        The problem with debugging is that it can only give you information on how a program is running at an instant. That may be acceptable for a CGI that gets restarted every few minutes, however it won't help you find a creeping memory leak that appears over the course of 200 hours. Printing or logging, if done correctly, can give you information on how a program is running over days, months or years.

        Holy cr@p I'm still Unemployed!

      I have not used a debugger for perl code, but I have used them in the past. Particularly in OO programming, where there are a heirarchy of classes, it is good to step through a particularly troublesome piece of code to see if it is doing what you though it should ("Hmmm - someone is not playing the cards I dealt them").

      Several years ago, I developed systems in Gupta's SQL Windows. The built in debugger was powerful - and very useful.

(tye)Re: How to debug unknown dynamic code?
by tye (Sage) on Dec 27, 2000 at 22:33 UTC

    Well, you can step through eval'd code, but you probably won't see the source lines since the debugger gets access to the source lines by reading from the file that Perl read from before.

    Breaking on Module::function should work provided that the module has already been required. Of course, there are lots of fun things that modules can do that make this break. Autoloading is the most common. For example, much of CGI is generated when you need it, which makes debugging that code nearly impossible (because the code for most of the subroutines was eval'd).

            - tye (but my friends call me "Tye")
Re: How to debug unknown dynamic code?
by runrig (Abbot) on Dec 28, 2000 at 11:58 UTC
    In addition to what tilly says, why not just break on the line that contains '&$function($arg1, $arg2)' and then step to the next statement and see where it goes. Which brings me to why I've forced myself to learn the debugger (with which I am now mediocre). merlyn says just don't put bugs in your code, and write small bits of code that work, which is great when you're writing your own code.

    When you're maintaining someone elses 3000+ line (not including modules) spaghetti code that uses neither strict nor warnings, and you're wondering how the program got from point A to point B and how some global variable seemed to get magically populated (by some module C), to me there's nothing like a decent debugger to trace the (@#$@#!!) damn thing. And sometimes I don't want to put in print statements to see what "$a" is at point A, because then I just have to remove (or comment out) the print statement later, or by the time I get to point A, I find that I really wanted to see what "$b" was, and with a debugger I can do that without restarting the whole program.

    I've worked with other programmers (in another language, 4gl if you must know) that couldn't use a debugger, and stared at a problem for days wondering what was going on, when with the help of the debugger the problem was found (in some deep dark library) in an hour or two.

    I think that the only thing (that I can think of at the moment) that the perl debugger needs is the ability to save break points and/or accept some input script (like break at line 100; step 1 line (requiring some module), then break at some function in that module if $i > 54). If someone knows how to do this, please let me know :)
Re: How to debug unknown dynamic code?
by chipmunk (Parson) on Dec 27, 2000 at 23:56 UTC
    If you're not able to check out the code to add print statements, how will you fix any bugs that you find?

    Otherwise I was going to suggest that, at the points in the module where you want the debugger to break, you add the line: $DB::single = 1 (That's like hitting 's' in the debugger right at that line.)

    This might help with setting breakpoints in the module: you can change which file you're looking at/setting breakpoints in/etc. by using the debugger's 'f' command.