How can I troubleshoot my Perl CGI script?

前端 未结 8 1278
深忆病人
深忆病人 2020-11-22 02:43

I have a Perl script that isn\'t working and I don\'t know how to start narrowing down the problem. What can I do?


Note: I\'m adding the question because I rea

8条回答
  •  感动是毒
    2020-11-22 03:06

    I wonder how come no-one mentioned the PERLDB_OPTS option called RemotePort; although admittedly, there aren't many working examples on the web (RemotePort isn't even mentioned in perldebug) - and it was kinda problematic for me to come up with this one, but here it goes (it being a Linux example).

    To do a proper example, first I needed something that can do a very simple simulation of a CGI web server, preferably through a single command line. After finding Simple command line web server for running cgis. (perlmonks.org), I found the IO::All - A Tiny Web Server to be applicable for this test.

    Here, I'll work in the /tmp directory; the CGI script will be /tmp/test.pl (included below). Note that the IO::All server will only serve executable files in the same directory as CGI, so chmod +x test.pl is required here. So, to do the usual CGI test run, I change directory to /tmp in the terminal, and run the one-liner web server there:

    $ cd /tmp
    $ perl -MIO::All -e 'io(":8080")->fork->accept->(sub { $_[0] < io(-x $1 ? "./$1 |" : $1) if /^GET \/(.*) / })'
    

    The webserver command will block in the terminal, and will otherwise start the web server locally (on 127.0.0.1 or localhost) - afterwards, I can go to a web browser, and request this address:

    http://127.0.0.1:8080/test.pl
    

    ... and I should observe the prints made by test.pl being loaded - and shown - in the web browser.


    Now, to debug this script with RemotePort, first we need a listener on the network, through which we will interact with the Perl debugger; we can use the command line tool netcat (nc, saw that here: Perl如何remote debug?). So, first run the netcat listener in one terminal - where it will block and wait for connections on port 7234 (which will be our debug port):

    $ nc -l 7234
    

    Then, we'd want perl to start in debug mode with RemotePort, when the test.pl has been called (even in CGI mode, through the server). This, in Linux, can be done using the following "shebang wrapper" script - which here also needs to be in /tmp, and must be made executable:

    cd /tmp
    
    cat > perldbgcall.sh <<'EOF'
    #!/bin/bash
    PERLDB_OPTS="RemotePort=localhost:7234" perl -d -e "do '$@'"
    EOF
    
    chmod +x perldbgcall.sh
    

    This is kind of a tricky thing - see shell script - How can I use environment variables in my shebang? - Unix & Linux Stack Exchange. But, the trick here seems to be not to fork the perl interpreter which handles test.pl - so once we hit it, we don't exec, but instead we call perl "plainly", and basically "source" our test.pl script using do (see How do I run a Perl script from within a Perl script?).

    Now that we have perldbgcall.sh in /tmp - we can change the test.pl file, so that it refers to this executable file on its shebang line (instead of the usual Perl interpreter) - here is /tmp/test.pl modified thus:

    #!./perldbgcall.sh
    
    # this is test.pl
    
    use 5.10.1;
    use warnings;
    use strict;
    
    my $b = '1';
    my $a = sub { "hello $b there" };
    $b = '2';
    print "YEAH " . $a->() . " CMON\n";
    $b = '3';
    print "CMON " . &$a . " YEAH\n";
    
    $DB::single=1;  # BREAKPOINT
    
    $b = '4';
    print "STEP " . &$a . " NOW\n";
    $b = '5';
    print "STEP " . &$a . " AGAIN\n";
    

    Now, both test.pl and its new shebang handler, perldbgcall.sh, are in /tmp; and we have nc listening for debug connections on port 7234 - so we can finally open another terminal window, change directory to /tmp, and run the one-liner webserver (which will listen for web connections on port 8080) there:

    cd /tmp
    perl -MIO::All -e 'io(":8080")->fork->accept->(sub { $_[0] < io(-x $1 ? "./$1 |" : $1) if /^GET \/(.*) / })'
    

    After this is done, we can go to our web browser, and request the same address, http://127.0.0.1:8080/test.pl. However, now when the webserver tries to execute the script, it will do so through perldbgcall.sh shebang - which will start perl in remote debugger mode. Thus, the script execution will pause - and so the web browser will lock, waiting for data. We can now switch to the netcat terminal, and we should see the familiar Perl debugger text - however, output through nc:

    $ nc -l 7234
    
    Loading DB routines from perl5db.pl version 1.32
    Editor support available.
    
    Enter h or `h h' for help, or `man perldebug' for more help.
    
    main::(-e:1):   do './test.pl'
      DB<1> r
    main::(./test.pl:29):   $b = '4';
      DB<1>
    

    As the snippet shows, we now basically use nc as a "terminal" - so we can type r (and Enter) for "run" - and the script will run up do the breakpoint statement (see also In perl, what is the difference between $DB::single = 1 and 2?), before stopping again (note at that point, the browser will still lock).

    So, now we can, say, step through the rest of test.pl, through the nc terminal:

    ....
    main::(./test.pl:29):   $b = '4';
      DB<1> n
    main::(./test.pl:30):   print "STEP " . &$a . " NOW\n";
      DB<1> n
    main::(./test.pl:31):   $b = '5';
      DB<1> n
    main::(./test.pl:32):   print "STEP " . &$a . " AGAIN\n";
      DB<1> n
    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>
    

    ... however, also at this point, the browser locks and waits for data. Only after we exit the debugger with q:

      DB<1> q
    $
    

    ... does the browser stop locking - and finally displays the (complete) output of test.pl:

    YEAH hello 2 there CMON
    CMON hello 3 there YEAH
    STEP hello 4 there NOW
    STEP hello 5 there AGAIN
    

    Of course, this kind of debug can be done even without running the web server - however, the neat thing here, is that we don't touch the web server at all; we trigger execution "natively" (for CGI) from a web browser - and the only change needed in the CGI script itself, is the change of shebang (and of course, the presence of the shebang wrapper script, as executable file in the same directory).

    Well, hope this helps someone - I sure would have loved to have stumbled upon this, instead of writing it myself :)
    Cheers!

提交回复
热议问题