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

I'm trying to debug some ithreaded apps/modules on WinXP using trusty old perl -dt. I've found the new e/E commands, tho they are of limited help (just display the current threadid). The problem is that all threads are hooked to the same TTY (er, "Command Prompt"), so even simple threaded apps get pretty confusing to debug (one thread controls the command prompt, the other sits around waiting to get a 'c', 'n', or 's' command).

Ideally, I'd be able to use something akin to $DB::fork_TTY/get_fork_TTY(). I've been casting about on Google, MailArchives, perldoc, and here, for a couple hours now, and can't find anything explicit.

And FWIW I can't even remember how to apply $DB::fork_TTY to fork()'ing on Win32, tho I'm certain I've used it somehow a year or so ago (middle age and homebrew beer is apparently taking a toll on my synapses).

So I guess I've got multiple questions:

  1. Is there a $DB::fork_TTY equivalent for ithreads (regardless of the OS) ?
  2. How can I use $DB::fork_TTY on Win32 ?
  3. Is there a decent alternative debugger that handles threads in a user-friendly way ? (e.g., does Komodo handle threads nicely ?)
Any assistance much appreciated.

Update: After some more study (mostly of perl5db), I've answered most of my own questions (tho most were not the answers I wanted...<sigh/>):

1) Nope, no DB::fork_TTY/get_fork_TTY() for threads

2) Sorry, can't. (I must've been running on cygwin or SFU before)

3) Sorry, no. I tried Komodo and it was actually worse (in some respects) at handling threads than regular command line debugging.

I also learned that the e/E commands were just introduced in 5.8.6.

So...since my only options are to move the entire project to Java or (god forbid) C++, or to solve this sticky issue, I guess I'll be spending the next few days hacking up perl5db.pl to try to solve some of these issues.

I'm likely to try to permit Win32 support via Win32::Process opening a new command prompt window ala xterm_get_fork_TTY().

As to opening a new window, I'm thinking of keeping a shared hash mapping tid's to IN/OUT handles, tho the threading issue may be more challenging than that, as perl5db indicates that once a thread is running, it runs to completion without permitting another thread to run. (However, I've noticed that when I omit -dt, and just use -d, it does switch between threads, tho unpredictably, similar to the behavior when fork()'ed processes use the same TTY.

I'm also hoping to figger out the Perl/Tk hooks so this might be applied to ptkdb, but thats probably wishful thinking at this point.

Anyhow, if any monks (saints?) can shed light on the right way to do this, and exactly where in perl5db to make the incisions, I'd be much obliged.

  • Comment on Win32, $DB::fork_TTY, and a threads equivalent

Replies are listed 'Best First'.
Re: Win32, $DB::fork_TTY, and a threads equivalent
by BrowserUk (Patriarch) on Sep 19, 2005 at 05:56 UTC

    Historically, debugger support for threads is rarely helpful for debugging threaded apps, regardless of the language.

    Bugs in threaded apps can be roughly divided into two groups:

    • Those that are bugs even without threading

      This group is by far the larger, incorporating all the types of bugs that can manifest in any program.

      The correct way to debug these is to disable the threading and ensure that the code performs correctly when it encounters each of the possible states that can arise once threaded.

      This sounds hard, but it consists of ensuring that each thread will do the correct thing for each combination of shared state it might encounter. The beauty of the ithreads paradigm is that the programmer has complete control over what is shared.

      1. So, to test your code, don't start the other thread.

        Manually set the shared state (shared variables) to each possible combination of values that might be in existance when a thread is run and ensure that it takes the correct action when that combination occurs.

      2. Then start the second thread, and have the first thread set the shared state to known values before falling into an indefinite sleep state.

        Ensure that the second thread does the correct thing for each combination.

      Rinse and repeat for all combinations of shared state.

      This may sound laborious, but in reality, you would find yourself having to do exactly the same steps via the debugger assuming it has a "suspend thread" facility, or manually if it does not.

      Unless your debugger can step backwards--which is almost impossible to provide in a threaded environment, and pretty meaningless in the non-determanistic of threads anyway. If you backstep, do you backstep one thread or all threads?--then trying to reach any particular point in your program in a known state will itself be very time consuming and laborious.

      And, having reached a particular point of interest; and witnessed what you think is the source of the bug; and corrected it; trying to re-run the corrected code back to the same place, with the same shared state, to verify the correction, is almost impossible.

      The keys to successful threaded code are:

      1. Share as little state as possible.
      2. Minimise the number of possible states--combinations of shared values.
      3. Use locking sparingly.
      4. Do not try to synchronised threads.

       

    • Those that only occur as a result of threading. These are basically "timing problems".

      A threading debugger is almost completely useless for detecting and solving this type of problem as the very fact that you are single stepping one or more of the threads means that the random dynamics that create this type of problem, are extremely difficult to reproduce under the auspices of the debugger.

      The only notionally useful debugger facility in threaded code, is a 'watch' facility. The ability to have the debugger break out of the code, suspending *all* threads for inspection, when a particular combination of (shared) variable values occur. However, to provide this facility requires that the debugger itself perform so much locking and synchronisation, that the dynamic of the running program is never likely to be the same as when the debugger is not involved and therefore is unlikely to detect time dependant errors. Or if it does, they will be different from those that occur in non-debug runs.

    The correct way to write threaded applications, is to treat each threads function as a separate program that when it runs, can find itself with a very limited set of states, and correctly deals with each of those possible states.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
    "Science is about questioning the status quo. Questioning authority".
    The "good enough" maybe good enough for the now, and perfection maybe unobtainable, but that should not preclude us from striving for perfection, when time, circumstance or desire allow.
      I agree entirely with your assertions; however, eventually I (or, more importantly, my customers, and for that matter, the perl community at large) will need to debug an app in a threaded environment, if only as a unit testing exersize. (Thread::Apartment makes that need more critical, tho it does provide an i/f to transparently revert to single-threaded mode) The current perl debuggers are simply not useable for that scenario.

      I'm working on a solution (working title Devel::Nimbus) similar to ptkdb, but simplified to a notebook with a page for each thread/process, and operating via a sockets i/f to a hacked perl5db. Hopefully it will be sufficient to deal with the issues, and portable enough to be generally useful.