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

I'm trying to run an HTTP::Simple::Server based server (it's actually my still under development CGI::Application::Server::Dispatch) and I'd like to run it inside the perl debugger.

It seems that when I run it w/ perl -d it ends up listening to the console instead of opening a network socket and listening there.

Does anyone out there know a sufficient incantation that will let me play with my code inside the debugger?

I've tried explicitly setting $server->port() and $server->host() to no avail.

I also just figured out how to use the debugger's RemotePort option get the debugging io to occur in another xterm (using nc -l) and the server's IO *still* is connected to the debugging window.

Thanks,

g.

---------------------------------------------------

So, here's an answer. There's something funny about how HTTP::Server::Simple hooks stdin and stdout up to the socket it creates. I'm still trying to figure out if the problem only happens on my FreeBSD-STABLE system, or if it's a more generall bug. In the mean time, the following patch makes things work for me:


--- /usr/local/lib/perl5/site_perl/5.8.8/HTTP/Server/Simple.pm  Sat Nov 25 16:15:39 2006
+++ lib/HTTP/Server/Simple.pm   Sat Feb 24 14:33:12 2007
@@ -262,8 +262,18 @@
                 $self->accept_hook if $self->can("accept_hook");
 
 
-                *STDIN  = $self->stdin_handle();
-                *STDOUT = $self->stdout_handle();
+                # hooking stdio to the socket using dup2 lets everything work
+                # in the perl debugger.  
+                # cons'ed together w/ hints on dup2 usage from the qpsmtpd.
+                # hartzell@alerce.com --- Sat Feb 24 14:23:12 PST 2007
+                # *STDIN  = $self->stdin_handle();
+                # *STDOUT = $self->stdout_handle();
+                use POSIX;
+                POSIX::dup2(fileno($self->stdin_handle()), 0)
+                  || die "unable to duplicate filehandle to STDIN - $!";
+                POSIX::dup2(fileno($self->stdout_handle()), 1)
+                  || die "unable to duplicate filehandle to STDOUT - $!";
+
                 select STDOUT;   # required for HTTP::Server::Simple::Recorder
                                  # XXX TODO glasser: why?
                 $pkg->process_request;

  • Comment on Help running HTTP::Server::Simple server under the debugger?

Replies are listed 'Best First'.
Re: Help running HTTP::Server::Simple server under the debugger?
by almut (Canon) on Feb 25, 2007 at 06:04 UTC

    The problem seems to be that - when run in the debugger - aliasing socket filehandles (e.g. *STDOUT = $sock) does not work at all -- or the debugger is immediately resetting the standard filehandles to their standard filedescriptor numbers (STDIN = 0, STDOUT = 1, ...).

    The following minimal program demonstrates this.

    #!/usr/bin/perl use Socket; use FileHandle; socket(SOCK, PF_INET, SOCK_STREAM, getprotobyname("tcp")) or die "sock +et: $!"; setsockopt(SOCK, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) or warn "set +sockopt: $!"; bind(SOCK, sockaddr_in(8080, inet_aton("localhost"))) or die "bind: $! +"; listen(SOCK, SOMAXCONN) or die "listen: $!"; while ( accept(my $sock = FileHandle->new(), SOCK) ) { printf STDERR "before aliasing: sock=%d, STDIN=%d, STDOUT=%d\n", fileno($sock), fileno(STDIN), fileno(STDOUT); *STDIN = $sock; *STDOUT = $sock; printf STDERR "after aliasing: sock=%d, STDIN=%d, STDOUT=%d\n", fileno($sock), fileno(STDIN), fileno(STDOUT); $| = 1; print "enter something:\n"; my $r = <STDIN>; print "you typed: $r"; close $sock; }

    First without the debugger.   (After having started the command, run telnet localhost 8080 in another terminal, to connect to the socket...)

    $ perl ./aliasing1.pl before aliasing: sock=5, STDIN=0, STDOUT=1 after aliasing: sock=5, STDIN=5, STDOUT=5 ^C $

    The telnet session looks like this:

    $ telnet localhost 8080 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. enter something: foo you typed: foo Connection closed by foreign host.

    However, when running the same program in the debugger, STDIN/STDOUT do remain associated with filedescriptors 0/1:

    $ perl -d ./aliasing1.pl Loading DB routines from perl5db.pl version 1.28 Editor support available. Enter h or `h h' for help, or `man perldebug' for more help. main::(./aliasing1.pl:5): socket(SOCK, PF_INET, SOCK_STREAM, getpro +tobyname("tcp")) or die "socket: $!"; DB<1> c before aliasing: sock=9, STDIN=0, STDOUT=1 after aliasing: sock=9, STDIN=0, STDOUT=1 enter something: foo you typed: foo ^C IO::Handle::DESTROY(/usr/local/perl-5.8.8/lib/5.8.8/i686-linux/IO/Hand +le.pm:328): 328: sub DESTROY {} DB<1> q $

    As a result, IO is performed via the terminal (filedescriptors 0 and 1), instead of via the remote telnet session...

    To get this working in the debugger, you have to dup the socket to the filedescriptors 0 and 1 -- i.e. either (as suggested by the OP)

    POSIX::dup2(fileno($sock), 0) or die "Cannot dup sock to STDIN: $!"; POSIX::dup2(fileno($sock), 1) or die "Cannot dup sock to STDOUT: $!";

    or

    open STDIN, "<&", $sock or die "Cannot dup sock to STDIN: $!"; open STDOUT, ">&", $sock or die "Cannot dup sock to STDOUT: $!";

    Interestingly though, this seems to apply to sockets only. Aliasing does work just fine with regular-file filehandles, even when using the debugger:

    #!/usr/bin/perl my $file = "foo.out"; open my $fh, ">", $file or die "cannot open $file: $!"; printf STDERR "fds before aliasing: fh=%d, STDOUT=%d\n", fileno($fh), fileno(STDOUT); *STDOUT = $fh; printf STDERR "fds after aliasing: fh=%d, STDOUT=%d\n", fileno($fh), fileno(STDOUT); print "foo\n";

    Without the debugger:

    $ perl ./aliasing2.pl fds before aliasing: fh=3, STDOUT=1 fds after aliasing: fh=3, STDOUT=3

    With the debugger:

    $ perl -d ./aliasing2.pl Loading DB routines from perl5db.pl version 1.28 Editor support available. Enter h or `h h' for help, or `man perldebug' for more help. main::(./aliasing2.pl:3): my $file = "foo.out"; DB<1> c fds before aliasing: fh=8, STDOUT=1 fds after aliasing: fh=8, STDOUT=8 Debugged program terminated. Use q to quit or R to restart, use o inhibit_exit to avoid stopping after program termination, h q, h R or h o to get additional info. DB<1> q

    (Note that in both cases, fh and STDOUT have the same filedescriptor number after aliasing. And, as expected, the string "foo" always ends up in the file.)

    If anyone can shed some light on why the debugger is treating socket filehandles differently, please do so.

    P.S.   sorry for the length :)

      nothing to add to the discussion other than - wonderful work, a very simple, insightful little piece. Thank you.
Re: Help running HTTP::Server::Simple server under the debugger?
by andyford (Curate) on Feb 24, 2007 at 22:50 UTC

    I'm a logger rather than a debugger, so to speak.

    I'd recommend putting in Log::Log4perl, using that to debug, and leaving it there for your eventual users. There are several modules that are "log4perl-enabled", where the end user can, if desired, turn on logging by initializing log4perl in their application.

    A couple examples of "log4perl-enabled" modules are Sysadmin::Install and RRDTool::OO. Crib!

    non-Perl: Andy Ford

      Yeah, sometimes printf style debugging is a good way to go.

      I still believe that debuggers have their place though, which is why I use them and why I asked the question. g.