1. Trang chủ
  2. » Công Nghệ Thông Tin

perl the complete reference second edition phần 7 pot

125 353 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Perl: The Complete Reference
Trường học Standard University
Chuyên ngành Computer Science
Thể loại Sách
Năm xuất bản 2023
Thành phố New York
Định dạng
Số trang 125
Dung lượng 1,16 MB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

The find2perl script takes the command line options you would normally supply to the find command, and generates a Perl script that will perform the same function.. There arecommand line

Trang 1

710 P e r l : T h e C o m p l e t e R e f e r e n c e

find

The find command does not really have a language, but it does have a complex array

of command line options that can specify, fairly expertly, the definition of the files

you want to find The find2perl script takes the command line options you would normally supply to the find command, and generates a Perl script that will perform the same function The script produced actually makes use of the File::Find module,

which provides a mechanism for parsing a directory tree, following all the subdirectories.For each file or directory found, a user-specified function is called, with the name and

location of the current file being available via the $_ variable and via some variables located within the File::Find module.

The result is that Perl has the ability not only to locate a file within the currentdirectory structure, but also to do any number of other operations to convert, translate,summarize, and so on, the contents of the files found

The find2perl script does the basics of the file specification process for you,

producing a script that you can modify to your own ends If you know how to use the

find command, then using the find2perl script should be easy The command

$ find2perl / -name '*bin*' -type d -print

produces

#!/usr/local/bin/perl

Option Description

-Dx Sets debugging, using a value of x The value affects the output

produced by the conversion process, and adds a number ofadditional statements to the script to output debugging informationduring the script’s progress

-Fc Specifies that the awk script was always invoked with a -F switch,

which changes the default input field separator to c.

-nfields Specifies the names of the input fields, rather than automatically

using a value of $Fld1, $Fld2, and so on Fields can be separated by

any of the normal separation characters

-number Forces a2p to assume that the input is always made up of the number

of fields specified by number.

Table 20-11 Command Line Options to the awk Converter

Team-Fly®

Trang 2

You can also specify more complex constructs directly to the find2perl script

without having to modify the code There are two options: one to create a tar file and

the other to specify a Perl-specific evaluation for the file

The -tar option takes a file name and adds the necessary code to the Perl script to

generate a file list to a piped tar command that then generates the tar file.

The -eval option takes a string that will be evaluated as a Perl statement; if it

returns true, the file will be considered as a match

Converting Perl to Other Languages

With Perl 5, the facilities have been put in place to resolve a Perl script to its lowest

common denominator—the string of optimized opcodes that are executed by the Perl

interpreter proper At the moment, two modules (B and O) provide a Perl interface to

the internals of a Perl script The result is that the internal opcode tree can be converted

and parsed into a number of different formats to provide a range of different pieces

of information

At present, this is limited to more extensive debugging features and the

cross-referencing abilities that are often available to other languages The same interface also

provides you with the ability to generate a file in binary format called bytecode This

binary code can then be executed directly by a special Perl interpreter The code has

already been parsed and optimized, and much of the typical interpretation process has

already taken place This makes the code execution much faster and also ensures, to a

greater or lesser extent, that the Perl source is hidden from casual view

The most interesting feature of the B and O modules, however, is that they can

generate raw C code, which can then be compiled into a stand-alone executable The

Trang 3

712 P e r l : T h e C o m p l e t e R e f e r e n c e

final executable does not require Perl and cannot be reverse engineered The performancebenefits are debatable, but the distribution and security offered by the process areobvious advantages

Because this is a significant part of the entire Perl environment, it’s discussed morefully in Chapter 22

Calling Other Languages from Perl

You have seen many times how Perl can call and interact with an external program Insome cases, the level of interaction has been as simple as calling the program with somespecified options There is no reason why, with the use of dual pipes, you couldn’t calland interact with another program, or even another programming language

The most obvious road to cooperating with another language from Perl, however,

is to use Perl as a high-level environment that generates an optimized or customizedprogram that can then be executed via a separate language Perl has many featuresthat make the manipulation of data, particularly strings, significantly easier; and if youwant to produce customized or optimized code automatically, it makes sense to use

a textual development environment to produce it

When dealing with a CGI or other web-based system, you can generate the

JavaScript code that’s embedded into pages—you can even dynamically generate thecode to enable different features in the final page What follows from this is the generalability of Perl to generate the code for any language—it’s perfectly possible to create aPerl application that generates, and even compiles, C source code into a final application

In fact, this is exactly what some parts of the XS language and the Perl compiler (seeChapter 22) actually do

The trick is to make the best use of the Perl environment and, especially, make use

of the here document to create customized source code to be passed to the program orlanguage interpreter in question Although it is possible to make use of pipes, mostlanguages accept an input file as their source Remember that “compiled” languages—such as C, C++, and Pascal—will require an external compiler, as well as additionalprocesses between code production and the final execution stages; but this shouldnot present too much difficulty

If it truly is interaction with another language that you require, then the obviousmethod is to set up some form of communication channel over which you can exchangerequests and requirements All modern languages, including Java, Python, Rebol, andPerl—provide the ability to open a network socket and exchange information

Some platforms provide an interface through a Perl module for communicating with

other languages For example, the Win32::OLE module allows you to communicate with

Windows objects, which in turn means that you can control the operation of Word, Excel,and other Windows applications using Visual Basic semantics Under Mac OS, you cancommunicate directly with AppleScript, which in turn allows you to communicate withthe operating system and the applications, and through an application like Word toVisual Basic again See Appendix B in this book and Web Appendix B on the Web at

www.osborne.comfor details on some of the platform-specific capabilities and the

modules that support them

Trang 4

Part IV

Fine-Tuning Applications

Copyright 2001 The McGraw-Hill Companies, Inc Click Here for Terms of Use

Trang 5

This page intentionally left blank.

Trang 7

Once you have completed your application, there are a number of things that

you might want to do before finally releasing your code to the world One ofthe most obvious procedures is to debug your code Despite your best efforts,there are bound to be bugs and problems in your code that you probably didn’t realizewere there, and certainly never intended to introduce

Debugging under Perl can be handled in a number of different ways There arecommand line switches to output different levels of information, there’s the outputfrom the Perl compiler, which can be a useful addition to the debugger’s toolkit, andthere’s even an interactive debugger that you can use to execute and monitor theprogress of your Perl script’s execution

There are also other steps that you need to take before you can release your code.Documentation is always a good idea, not only as pointers for your end-users, but also

as a tool for you, so that when you go back to the code a few months later, you knowwhat it does, how, and why

If your script is a general purpose tool, then you can make it appeal to a larger usergroup by making it cross-platform aware, and if possible, compatible Knowing whichfunctions, facilities, and systems are available on each platform is a good start, butthere are also tricks that you can apply to make the process easier

Finally, Perl includes some tools that make the distribution and installation ofmodules and applications easier Learning how to make the best use of these modulescan dramatically increase the ease of use and installation by your end-users

The other chapters in this section cover these latter parts of the application

development process In this chapter, we’re going to concentrate purely on the

processes and tools available for debugging and optimizing the applications, scripts,and modules that you write

Debugging is a time-consuming and laborious process When I was at college, I wastaught that the proper ratio was 80 percent testing and 20 percent development time,and after many years in the programming business, I can affirm that that ratio is aboutright Even in a very simple piece of code, it’s possible to introduce some potentialbugs and problems For every bug you find and eliminate, I can guarantee that twomore will be waiting in the wings Furthermore, the solution to one bug may wellintroduce new bugs that you aren’t aware of

There is, however, more to debugging than simply finding the bugs and squishingthem A good programmer will take a more pragmatic approach, and try to develop asystem that will trap most of the errors before they cause any problems Remember thatthe purpose of debugging, and in turn, error trapping, is to ensure that the software doeswhat it is supposed to do, while simultaneously ensuring that it doesn’t do anything itshouldn’t A simple log-processing script should not delete or corrupt the log in theprocess, for example

We’ve already seen some examples of basic error trapping in Chapter 9—youshould use Chapter 9 as a guide to isolating potential problems before they become realones In this chapter, we’ll look at two basic procedures for debugging The second is

the use of more simplified debugging processes, such as using print and caller to

716 P e r l : T h e C o m p l e t e R e f e r e n c e

Trang 8

provide a history of a script’s execution, and therefore to help you to identify where

things have gone wrong The final, but certainly not least important, option that we

will look at is the use of the Perl built-in debugger

The final stage of any debugging process is probably the one least followed—

optimization Although many people do not consider it a bug, a badly optimized script is

as much a danger as one that overwrites files it shouldn’t have access to A simple CGI

script that processes a form shouldn’t take hours doing so—monitoring the execution time

can often give you a good idea of where the problems lie Within Perl, there are a few

solutions to that particular problem, including an extension to the core debugging

extensions, the Perl Profiler This monitors the execution time of each subroutine in your

script and gives you an excellent idea of where there could be problems

We’ll also look at some of the manual techniques available for improving

performance, and at some of the more obvious resource sinks that can slow execution

It’s impossible in a single chapter to cover everything you need to know about trapping

errors and debugging For a more detailed look at debugging with Perl, see my

Debugging Perl: Troubleshooting for Programmers (Osborne/McGraw-Hill, 2000),

from which a lot of the material in this chapter comes See Appendix C for more

information.

Debugging Techniques

There are three basic methods for debugging a script The first two are modifications

on a single theme—the primary tool is just to use print to output the information The

second uses caller, which returns more extended information about the caller of the

subroutine, and then uses print to output the information The last is to use one of the

full-blown debuggers Perl actually comes with its own debugger, which is basically a

script and module combination called when you use the -d command line option to

Perl If you use the ActiveState version of Perl under Windows, then you also have a

GUI-based debugger

Just to add to the confusion, there is a fourth debugging system built into Perl—the

-Dcommand line option—but this doesn’t often give any useful information to the

“end-user programmer.” Most of the detail given by -D is intended for those dealing

with the internal workings of the Perl compiler and interpreter See Chapter 15 for

more information on the available options supported by the -D option For a more

detailed look at what each of the options does, check out the Debugging Perl title (see

Appendix C for more information on this book)

Using print

To me, print statements have always seemed easy, and, providing you’re careful, they can

usually provide enough information for you to trace the bug without having to resort to a

full-blown debugger In fact, the easiest way to use the humble print statement is during

Trang 9

the development of a script—just inserting a quick “this variable has this value” is an easyway for you to check that your script is doing what you think.

You can also use the print statement as a way of reporting debugging information

in the final version of the script You usually use this method in combination with aglobal variable, perhaps set via the script’s command line, to enable or disable somesimple debugging The benefits of a script that outputs debugging information in thisway is that it allows both the user and programmer to perform a post-mortem debug

on the script The only place where they are often useless is within a loop, because theyproduce a voluminous amount of information that needs to be processed manuallyafter the execution is complete On occasion, the loop mechanism can prove useful ifyou want to continually monitor a single variable as it is processed, or when you want

to monitor the input or output to a filehandle

Usage is pathetically simple By using a statement such as this,

print "Got here!\n";

you can trace the execution path of a program You can use the same basic layout forany valid piece of data that you want to output

Because you can print whatever you like, you can be quite explicit about the

information:

print "Data before processing: $data\n";

#process some data

print "Fieldname: $field, data: $data, leftovers: $leftover\n";

More usually, though, you’ll want to be a bit more explicit about the location in

which the debug report occurred Here you can use the LINE and FILE

directives, which insert the line number and current file in which the message wasprinted, respectively For example,

print FILE ,'(', LINE ,"): Data before processing $data\n";

might print out like this:

process.pl(19): Data before processing Name: Martin

Note that the FILE and LINE tokens must be outside of quotes in order for

them to be included in the printed line

718 P e r l : T h e C o m p l e t e R e f e r e n c e

Trang 10

Quoting Information

When using print to output data, it’s a good idea to delimit the information that you

are outputting This limit helps to make it clear exactly what the actual data was For

example, this report line:

process.pl(19): Data before processing Name: Martin

doesn’t tell us if there are any trailing tabs or spaces to the information, which may or

may not be important A simple set of braces on either side of the data highlights the

full data string:

process.pl(19): Data before processing [Name: Martin ]

Here it’s obvious that we have some trailing spaces

Don’t use angle brackets, <>, to delimit information, especially when displaying

debugging data within a CGI or other HTML script The HTML parser may either

identify the entry as a proper HTML tag or simply ignore the data entirely!

You can also go one stage further and quote the special characters The following

script defines two functions—the interface to the whole thing is the mapascii function.

This takes a string and then converts the special characters into their equivalent

\0 [SOH] [STX] [ETX] [EOT] [ENQ] [ACK] \a \b \t \n \v \f \r

[SO] [SI] [DCE] [DC1] [DC2] [DC3] [DC4] [SYN] [ETB] [CAN]

[EM] [SUB] [ESC] [FS] [GS] [RS] [US]

/;

return $map[ord($char)] if (exists($map[ord($char)]));

return $char;

}

Trang 11

sub mapascii{

my ($string) = @_;

join('',map { $_ = mapasciichar($_) } split //,$string);

}

print mapascii("Hello\nThis is a raw test\t\r\n"),"\n";

When you run the script as a whole, you get this:

Hello\nThis is a raw test\t\r\n

Other control characters will also be printed out with their ASCII names or in theformat that you would normally use when interpolating special characters into strings.Tracing Execution

The line and file directives that we saw earlier provide a useful way of isolating theexact script position that raised a particular error Of course, it makes more sense toinclude a more detailed report, such that the output produced is as detailed asnecessary and describes exactly what the script was doing at the point of the error

Here’s the output from a print-debugged script that processes a file The output

includes the contents of the variables that we are using to process the data:

$ foo.plOpened file (input.dat)Read data (hello)

Read data (world)Closed file

Remember at all times that when producing a debug report in this way, you should

be producing an annotated sequence of events Doing this will help you and your users

to understand what is going on without having to look back at the script

It’s a good idea to switch off the file buffering when outputting this information, so that it’s updated in real time, rather than in chunks The easiest way to do this is

Trang 12

Using caller

Printing your own debug information requires a lot of manual entry if you are trying to

trace the execution path through a program For each print statement you include in

your source, you will need to include a reference about the location of the statement in

order for your debug output to make sense

To ease the process, you can use the caller function, which returns the current

context information for a subroutine The information includes details about the

subroutine (or eval or require) statement:

caller EXPR

caller

In a scalar context, it simply returns the package name In a simple list context, the

function returns the package name, file name, and line of the caller of the current

subroutine or eval or require statement:

($package, $filename, $line) = caller;

If EXPR is specified, caller returns extended information The value of EXPR

should be the number of frames on the stack to go back to before the current one That

is, if you specify a value of 1, the parent subroutine information will be printed, a value

of 2 will print the grandparent subroutine, and so forth The information returned is

($package, $filename, $line, $subroutine,

$hasargs, $wantarray, $evaltext, $is_require) = caller($i);

The $evaltext and $is_require values are only returned when the subroutine being

examined is actually the result of an eval() statement As an example, examine this script:

Trang 13

package Top;

sub top

{

my $level = 0;

print "Top of the world, Ma!\n";

while ((($package, $file, $line,

$subname, $hasargs, $wantarray) = caller($level++))){

$hasargs = $hasargs ? 'Yes' : 'No';

if (defined($wantarray)){

$wantarray = 'Yes';

}else{

$wantarray = 'No';

}print <<EOF;

Stack:

Package: $packageFile: $fileLine: $lineSubroutine: $subnameHas Arguments?: $hasargsWants Array?: $wantarrayEOF

}}

When the code is executed, the resultant information shows the stack trace for the

top function, including its original call from main and from the bar function:

Top of the world, Ma!

Stack:

Package: mainFile: /./t.plLine: 5

Subroutine: Top::topHas Arguments?: YesWants Array?: No

722 P e r l : T h e C o m p l e t e R e f e r e n c e

Trang 14

Package: mainFile: /./t.plLine: 8

Subroutine: main::bar

Has Arguments?: Yes

Wants Array?: No

The information provided should enable you to pinpoint the location within a

script If you want to report the information to a log, you may want to introduce a

wrapper function, like this one:

to report the information for the current stack trace Note that you can’t directly use the

information from callerlog, since doing so would introduce its own frame of information

at location zero within the stack You could, however, use a modified form of the

callerlogfunction that returns the stack trace from frame one onward:

The information provided by caller is actually used by the Perl debugger to

provide the tracing information used in the debugging environment

Trang 15

Using eval

The eval function provides a very simple way of checking certain events without

affecting the overall execution of your script In essence, the eval function just initiates a

new instance of the Perl interpreter in order to evaluate a particular string or block It’sused in all sorts of places within Perl—including in the debugger where it allows you toexecute those arbitrary statements—but it can also be employed as a debugging tool

Because eval evaluates a Perl statement or block within its own interpreter, we can use

it in situations that might otherwise cause the Perl interpreter to fail This process works

because an embedded eval block reports any errors raised by a call to die through the $@ variable In fact, any exit is reported through eval to the $@ special variable.

We can demonstrate this with a simple eval block used to test the existence of a

print "Error: Module failed to load ($@)" if $@;

This outputs the following:

Armed with this information, we can now check anything (except statements executed

at compile time, such as use) that might raise an error, and we can trap and report the

Trang 16

But it’s slightly more convenient to do this:

eval { $a/$b };

print $@ if $@;

Here’s another example, this time of a function that uses an eval to execute its

statements so that it can return a single error back to the caller instead of simply calling

dieand executing the script completely:

if ($error = writedata("debug", "some error text\n"))

open (FILE, ">$file") or die "Can't open file ($file): $!";

print FILE $data or die "Can't write to file ($file): $!";

close(FILE) or die "Can't close file ($file): $!";

};

return $@ if ($@);

}

Here we’ve got essentially three places where we could potentially drop out of

the script By embedding all three statements into an eval block and then checking the

return value from the whole function, we’ve managed to trap what might otherwise

be a critical failure into something that we can safely recover from This capability

becomes incredibly useful in situations where you need to continue working even

if a particular option fails, but you want to avoid producing your own

exception-and error-hexception-andling system

In this example, just running the script produces the following:

Raised an error writing to the log:

Can't open file (debug): Permission denied at eval.pl line 15

Continuing

Trang 17

In this case, we’ve just ignored the error, but you could redirect the sequence and errorreport to a Tk widget or another HTML page if this was a CGI script.

The same trick also provides an ideal way of converting an existing script from a command-line basis to a CGI or Tk basis without having to make major changes to the

code For example, we could take out the embedded eval from the writedata function

and instead move it to surround the function call:

eval{writedata("debug", "some error text\n")}

print("Raised an error writing to the log: \n",$error,

with the caller function earlier in this chapter.

There are a number of different ways in which we can do this At the simplest level,

you can just use print to send the output somewhere else For a more comprehensive solution, you should think about redirection This approach will ensure that warn and die also output their information to the log, rather to than to the real STDERR The final solution is to report information to the syslog daemon on a Unix machine, or to

the Event Log on a Windows machine This procedure is useful for scripts that play avital role in an administration environment, where you need the information to bereported to a central, rather than an application-specific, log

Redirecting STDOUT/STDERR

The simplest way of creating a log without seriously affecting the output of your script

is to simply redirect the default STDOUT and STDERR to somewhere else, which you

can do like this:

Trang 18

If you’re going to redirect the complete output of a script, consider placing the

redirection statement into a BEGIN block, so that everything is redirected to

the log files, even if an error is raised by an imported module.

In fact, it’s probably best to follow those statements with

select SECOUT;

to ensure that standard print and other calls send their output to the real STDOUT and

not the redirected one

It’s a good idea to switch off the file buffering when sending information to a log; doing

this prevents overruns and ensures that data is written, even if the script crashes.

Since you will have control over the filehandle on which you provide the user

interface, but not the filehandle used to print the debugging information from the

contributed module, redirecting only STDERR is often a more practical solution.

It’s also possible, using this method, to create a log file of the debug output you

create This file is especially useful when using an indirectly accessed script, such as

that used on a web server Here, printing the debug information to the standard output

will cause the information to be included as part of the document that is sent back to

the user’s web browser

Using a Logging Function

If you don’t want to redirect the STDOUT and STDERR filehandles, the other solution

is to create a function that opens and then writes the information you supply directly to

or die "Can't open debug log!: $!\n";

printf LOGFILE ($format,@args);

close LOGFILE;

}

Now you can just make calls to the writelog function:

writelog("Had an error in the line %s from %s", $line, $file);

Trang 19

A more efficient solution is to use a global variable as the filehandle and thenchange the function so that it only opens the file if it isn’t already open:

open($logfile,">>debug.log")

or die "Can't open debug log!: $!\n";

}printf $logfile ($format,@args);

}

Now the file will be opened once—when the first call to writelog is made—and log

information will then just be appended for each subsequent call

Note that we’ve removed the call to close; I suggest that you instead put it into an

Debug Logs in the Real World

It’s probably a good idea to keep some form of logging system in a major

application By logging the output of your application, you can track problems

as the application is used by the end-users Then, when it comes to tracking areported problem, you have all of the information to hand

To get the best use out of a debug log, make sure you also record the timethat the error was reported, the process ID, and, if relevant, the machine and/

or user name When the user reports the error, get them to email you the log.That way, when you examine the log, it should be easier to determine why theerror occurred

Trang 20

Reporting to syslog

Perl comes with the Sys::Syslog module, which provides a very simple interface for

reporting information to the syslog system, which in turn is written to the system logs,

often held at /var/log/syslog (or similar) The best way to use the system if you intend

to log information in this way is to call the openlog function at the start of your script,

and then use syslog, which actually writes log entries, as a replacement for warn or

die Remember to call closelog at the end to close the connection to syslogd (the syslog

daemon) A full example looks like

use Sys::Syslog;

openlog('perl', 'cons,pid', 'user');

syslog('warning' ,'Something happened');

closelog();

The preceding example produces the following entry on a Solaris system:

Jul 19 11:13:57 twinspark perl[2686]: Something happened

See Appendix B and the Sys::Syslog module for more information.

Reporting to the Windows NT/2000 Event Log

The Windows NT Event Log is a central logging system similar in principle to the

syslog system, but slightly more advanced Primarily, the Event Log is used to record

errors and warnings, but it can also be used just to store simple informational messages

and also audit events—that is, those events configured by the administrator to be

specifically tracked and recorded

The Event Log also stores a lot more information than syslog does in its standard

format For example, the syslog system can be configured and set to report the computer

and user information, but it’s not enforced With the Event Log, this information is

automatically recorded In addition, you can include any data that was being processed

at the time, extended message strings, categories, and event types

For example, this snippet

use Win32::EventLog;

my $eventlog = new Win32::EventLog('Application');

%event = (Data => 'Some data',

Source => 'Perl',

Trang 21

EventID => 1,EventType => EVENTLOG_WARNING_TYPE,Strings => 'I failed to get the info I was expecting',Category => 0);

$eventlog->Report(\%event);

will log an Application error into the Event Log Note here that I’ve used a hash and

then called the Report method instead of embedding the information directly—it’s

quicker and easier to use the same hash and update only the relevant informationwhen you need to report a new error

Using a Debugger

There are three basic tools available to you when you are using a debugger: watches,stepping, and breakpoints We’ll have a look at each of these tools and describe howthey can best be used to help you when debugging your scripts

Watches

Watches are the variables that you want to monitor as you execute a script You set a

watch on a variable, and then, for each statement that is executed, you are shown thecorresponding value of the variable By using watches, you can monitor how a variable

is updated and isolate those instances where a variable is modified without yourealizing it

Stepping

Stepping is the action of executing Perl statements, either individually or as a group

(as when you execute an entire function call in one go) By stepping, you can monitorthe execution and variables used and affected by a statement on a line-by-line basis.There are three basic step commands, although some debuggers will offer someadditional options:

■ Step Into executes the current statement, following the execution of any

functions or methods found within the statement Execution goes as far ascalling the function or method, bypassing any variable initialization, andstopping at the first executable statement within the called function

■ Step Over executes the current statement Any functions or methods that are

called are executed without being processed by the debugger, so executionstops on the next executable statement within the current file

730 P e r l : T h e C o m p l e t e R e f e r e n c e

TE AM

FL Y

Team-Fly®

Trang 22

■ Step Out continues execution until the current function or method ends.

Execution stops at the next executable statement, either within the next

function call of the current line from the calling script or on the next statement

from the caller

The advantage of stepping over breakpoints is that it allows you to monitor each

line individually This capability is particularly useful when you want to study a

sequence or each iteration of a loop in detail

Breakpoints

Breakpoints offer a different approach Instead of laboriously stepping through each

line of a script, you can set a breakpoint at a future point in the script and then start

execution The debugger will execute all of the lines up until the given breakpoint In

addition, you can also set a breakpoint to be triggered only when a variable matches a

certain condition

For example, imagine you are having trouble within a loop, but only when the

loop counter reaches 1,000; you can set a breakpoint to be triggered when the counter

value is greater than or equal to 1,000 The loop will parse and execute 1,000 times, and

then the debugger will halt to allow you to process each individual line until you trace

the problem

The Perl Debugger

The name Perl Debugger is a bit of a misnomer The debugger is in fact just a suite of

modules and a script that ends up sitting almost simultaneously between and behind

the script you are attempting to run and the Perl interpreter that will execute it By

sitting in this position, the debugger script can extract the individual lines of your

source file and incrementally execute each one—the stepping process

In addition, the debugger allows you to set watches and breakpoints and provides you

with a way of directly executing Perl statements that can interface with the underlying

script For example, when reaching a breakpoint, you might want to perform a simple

calculation on a value generated by the script

The main difference between Perl and many other languages is that you can run

the debugger directly—in fact, straight from the command line There isn’t a separate

application for doing the debugging, and there’s no reason to make any changes to

your code

The User Interface

To start the debugger, you need to specify the -d option on the command line to the

Perl interpreter:

perl -d t.pl

Trang 23

Alternatively, it can be used with a dummy -e statement to drop you straight into a

dummy debugger environment:

perl -de 1

Once the debugger is invoked, you are placed into it at the first executable

statement within the defined script:

Loading DB routines from perl5db.pl version 1.0401

Emacs support available.

Enter h or `h h' for help.

main::(-e:1): 1

DB<1>

The value in the angle brackets—1, in this example—is the number of the debugger

command This can be recalled with the ! debugger command The number of angle brackets shows the current depth of the debugger Calling a new subroutine via an s, n,

or t command will introduce a new set of brackets as a new execution path is created within the script You can specify multiline commands by using the \ character, which

has the effect of escaping the newline that would ordinarily end the command

Rather confusingly, the line that is displayed before the prompt is the line that is

about to be executed, rather than the line that has been executed Therefore, on first

entry into the debugger, no lines (other than compiler directives and package imports)have actually been executed

The normal operation is to set a breakpoint on a line or statement that you want to

monitor, and then use the T command to produce a stack trace For example:

DB<4> b 16

DB<5> r

Top of the world, Ma!

main::callerlog(t.pl:16): print join(' ',@data),":$reference\n"; DB<6> T

= main::callerlog('Printed Message') called from file 't.pl' line 23

= main::top() called from file 't.pl' line 5

= main::bar() called from file 't.pl' line 8

The actual execution process for each line in the script is as follows:

1 Check for a breakpoint

2 Print the line, using tracing if the AutoTrace option has been set.

3 Execute any actions defined

732 P e r l : T h e C o m p l e t e R e f e r e n c e

Trang 24

4 Prompt the user if there is a breakpoint or single step.

5 Evaluate the line

6 Print any modified watchpoints

Once the execution has halted, you can step through the script, either by every line,

using the s command, or by each line, stepping over subroutine calls, using the n

command

Note that compile-time statements are not trapped by the debugger, which means

that those enclosed in a BEGIN block, or statements such as use, are not stopped by the

debugger The best method for trapping them is to specify the value of the $DB::single

variable that is part of the Perl debugger Although it requires modification of the code,

it does not affect anything if the debugger is not running A value of 1 for the

$DB::single variable is equivalent to having just typed s to the debugger A value of 2

indicates that n should be used Alternatively, you can monitor the status of the

commands using the AutoTrace option.

You can set watchpoints, which display the value of a variable if it has been

modified in the just-executed statement For example, in the script,

The debugger supports a wide range of commands that are outlined next As a general

rule, anything that is not immediately identified as a command, or alternatively any

input line beginning with a space, is interpreted as a Perl statement that is executed via

an eval function.

Any debugger command can be piped through an external program by using the pipe

symbol, just as at a Unix shell prompt This feature is primarily useful for parsing

output through a pager, but could be used for anything.

Trang 25

734 P e r l : T h e C o m p l e t e R e f e r e n c e

Prints out help information for COMMAND or general help if COMMAND is not specified If you use the special h h command, a condensed version of the general help

is printed—it should fit onto a standard screen without scrolling See the O command

later for details on how to change the default paging program

The usual rules for the print function apply—nested structures and objects will not

be printed correctly (See the x command for a more useful version of this command.)

x

x expr

Evaluates its expression in list context and dumps out the result in a pretty printed

fashion Nested data structures are printed out recursively, unlike with the print

function See the options in Table 21-1, further on in the chapter

V

V PACKAGE VARS

V PACKAGE

V

Displays the list of variables specified in VARS within the package PACKAGE

if both are specified If VARS is omitted, all variables for PACKAGE are printed.

If no arguments are specified, it prints out all the variables for the main package.

Information is intelligently printed, with the values of arrays and hashes and nestedstructures being formatted before being output Control characters are also convertedinto a printable format

If you specify the variables, you should omit the variable type characters ($, @, or

%) You can also specify a pattern to match, or a pattern not to match, using

~PATTERN and !PATTERN arguments.

Trang 26

Prints a stack backtrace, as determined by the caller function and the value of the

current stack frame array

s

s EXPR

s

Executes only the next statement (single step), following subroutine calls if necessary If

EXPR is supplied, it then executes EXPR once, descending into subroutine calls as

necessary This process can be used to drop directly into a subroutine outside of the

normal execution process

n

n EXPR

n

Single-steps the next statement, but steps over the subroutines instead of stepping into

them If EXPR is specified, then any subroutines are stepped into.

Carriage Return Repeats the last n or s command.

Trang 27

Continues execution (all statements) until the next configured breakpoint or the end of

the script If LINE or SUB is specified, then a breakpoint, active for one break only, is inserted before LINE or the subroutine SUB.

Lists the first page of lines for the subroutine SUB.

− Lists the previous page of lines

w

w LINE

w

Lists a page of lines surrounding the current line, or LINE if specified.

. Returns the line pointer to the last line executed and prints it out

f

f FILENAME

736 P e r l : T h e C o m p l e t e R e f e r e n c e

Trang 28

Changes the file currently being viewed to FILENAME The value of FILENAME

should match either the main script or the name of a file identifiable within the %INC

variable If still not found, then it is interpreted as a regular expression that should

resolve to a file name

/PATTERN/ Searches forward within the current file for the regular expression

Lists all subroutines matching the regular expression PATTERN If PATTERN is

preceded by an exclamation mark, then lists those not matching the regular expression

PATTERN.

t

t EXPR

t

Toggles trace mode Trace mode enables the printing of each statement as it is

executed If EXPR is specified, traces the execution of EXPR See also the AutoTrace

Trang 29

then each time the breakpoint is reached, it breaks execution only if the condition

resolves to true The CONDITION does not use an if statement; it is purely the test If you use /PATTERN/, then the breakpoint breaks only if the statement matches the regular expression PATTERN.

b SUB CONDITION

b SUB

Sets a breakpoint on subroutine SUB, using CONDITION if specified.

b postpone SUB CONDITION

Trang 30

Deletes the breakpoint specified on LINE, or the breakpoint on the line that is about to

be executed if LINE is omitted.

D Deletes all the currently set breakpoints

a

a LINE COMMAND

a COMMAND

Sets the action specified by COMMAND to be executed before the current line, or the

line specified by LINE, is executed For example, this can be used to print the value of a

variable before it is used in a calculation

A Deletes all currently installed actions

W

W EXPR

W

Sets a watch on the variable specified by EXPR A change to the specified variable will

be printed before the next line to be executed is printed If EXPR is not specified, then

all watchpoints are deleted

O

O OPT?

O OPT=VALUE

O

The first form, O OPT?, prints the value of the option named OPT The second format

specifies the value for OPT; if no value is specified, it defaults to 1 If no arguments are

Trang 31

given, then the values of all the current options are printed The option name can be

abbreviated to the minimum identifiable name; for example, the pager option can be reduced to p.

A list of the most commonly used options is shown in Table 21-1 For others, refer

to the perldebug man page

740 P e r l : T h e C o m p l e t e R e f e r e n c e

RecallCommand The character(s) used to recall a command

ShellBang The character(s) used to spawn a shell

Pager The program to use for paging the output using the |

command within the debugger The value of the PAGER

environment variable will be used by default

TkRunning Run Tk when prompting (See the “Alternative Interfaces”

section later in this chapter for a discussion of the Tkinterface to the Perl debugger.)

SignalLevel The level of verbosity applied to signals Default operation is

to print a message when an uncaught signal is received Set

to 0 to switch this off

WarnLevel The level of verbosity applied to warnings Default

operation is to print a backtrace when a warning is printedout Set to 0 to switch this off

DieLevel The level of verbosity applied to warnings Default

operation is to print a backtrace when a warning is printedout Set this option to a value of 2 to enable messages to be

printed by surrounding eval statements Set to 0 to switch

this off

AutoTrace Trace mode, identical to the t option on the command line.

Set to 0 to disable tracing

LineInfo The file or pipe to print line-number information to This is

used by debugger interfaces with a pipe to enable them toobtain the information

inhibit_exit When set to 0, allows you to step to a point beyond the

normal end of the script

Table 21-1 Internal Options for the Debugger

Team-Fly®

Trang 32

PrintRet When set to 0, does not print the return value resolved when

the r command is used When set to 1 (the default), the

return value is printed

Frame Controls how messages are printed during the entry and exit

process from subroutines The value is numeric, basedagainst a bitset If the value is 0, then messages are printedonly on entry to a new subroutine If bit 1 (value of 2) is set,then both entry and exit to the subroutine is printed If bit 2(value of 4) is set, then the arguments to the subroutine are

printed, and bit 4 (value of 8) prints the values parsed to tied

functions and methods Bit 5 (value of 16) also causes thereturn value from the subroutine to be printed.Thus, a value

of 18 prints the entry and exit to a subroutine with thereturned value

MaxTraceLen The maximum number of arguments printed when bit 4 of

the frame option is set.

ArrayDepth The maximum number of elements printed from an array

An empty string prints all elements

HashDepth The maximum number of keys and values printed from a

hash An empty string prints all keys

CompactDump Sets the style of the array or hash dump Short arrays may be

printed on a single line

VeryCompact Sets the style of the array or hash dump to be very compact

GlobPrint Sets whether the resolved file name globs are printed

TTY The TTY device to use for debugging I/O

NoTTY If set, goes into a nonstop debugging mode, as if there were

no controlling terminal See the examples under the O

command for more information

ReadLine When set to 0, disables readline support within the debugger,

so that scripts that use ReadLine can be debugged.

NonStop Automatically set by noTTY; sets the debugger to

non-interactive mode

Table 21-1 Internal Options for the Debugger (continued)

Trang 33

The default values for the options can be obtained by typing O into a new

debugger process:

perl -de 1

Loading DB routines from perl5db.pl version 1.0401

Emacs support available

Enter h or `h h' for help

main::(-e:1): 1

DB<1> O

hashDepth = 'N/A'arrayDepth = 'N/A'DumpDBFiles = 'N/A'DumpPackages = 'N/A'DumpReused = 'N/A'compactDump = 'N/A'veryCompact = 'N/A'

quote = 'N/A'HighBit = 'N/A'undefPrint = 'N/A'globPrint = 'N/A'PrintRet = '1'UsageOnly = 'N/A'frame = '0'AutoTrace = '0'

TTY = '/dev/tty'noTTY = ''

ReadLine = '1'NonStop = '0'LineInfo = '/dev/tty'maxTraceLen = '400'recallCommand = '!'ShellBang = '!'pager = '|more'tkRunning = ''ornaments = 'us,ue,md,me'signalLevel = '1'

warnLevel = '1'dieLevel = '1'inhibit_exit = '1'

742 P e r l : T h e C o m p l e t e R e f e r e n c e

Trang 34

Sets a Perl command, specified in EXPR, to be executed before each debugger prompt.

If EXPR is omitted, the list of statements is reset.

Sets the Perl command EXPR to be executed after each debugger prompt and after any

command on the prompt has been executed If EXPR is not specified, the list of

commands is reset

>>

>> EXPR

Sets the Perl command EXPR to be executed after each debugger prompt and after any

command on the prompt has been executed

{

{ EXPR

{

Sets a debugger command, specified in EXPR, to be executed before each debugger

prompt If EXPR is omitted, the list of statements is reset.

Trang 35

Displays the last EXPR commands—if EXPR is omitted, then it lists all of the

commands in the history

q or ^D Quits from the debugger

r Returns immediately from the current subroutine The remainder of the statementsare ignored

744 P e r l : T h e C o m p l e t e R e f e r e n c e

Trang 36

R Restarts the debugger Some options and history may be lost during the process,

although the current specification allows for histories, breakpoints, actions, and

debugger options to be retained Also, the command line options specified by -w, -I,

and -e are also retained.

Runs the command EXPR through the default pager, ensuring that the filehandle

DB::OUTis temporarily selected

=

= ALIAS EXPR

ALIAS

Assigns the value of EXPR to ALIAS, effectively defining a new command called

ALIAS If no arguments are specified, the current aliases are listed Note that the

aliases do not accept arguments, but you can simulate the effects of arguments by

defining EXPR as a regular expression:

$DB::alias{'strlen'} = 's/strlen(.*)/p length($1)/';

This effectively reexecutes the original strlen command as print length($1), where $1 is

the value within the first matching parentheses

Trang 37

746 P e r l : T h e C o m p l e t e R e f e r e n c e

Lists the available methods defined in PACKAGE.

Using Noninteractive Mode

The interactive interface is great if you’re trying to locate a very specific bug or problem,but it may be overkill if all you want is a quick guide or overview of the execution path of

a particular script There are other, perhaps better, tools—for example, the Perl Compiler

in Chapter 22 provides some other, often useful, information

You can get around this by using a “noninteractive” mode, which is basically just a

trick using the PERLDB_OPTS environment variable to get Perl to execute a series of

debugger commands when the debugger is started It’s not officially a way of

executing the debugger, but it is a solution when you want to print a stack trace or

watch variables during execution without having to manually introduce print

statements or having to drop into the interactive debugger interface

To do this, you need to set the value of the PERLDB_OPTS environment variable before running the debugger The following example, which assumes you have the bash

shell, switches on full frame information for called subroutines and runs the debuggerwithout human intervention, outputting the full trace to the standard output:

$ export set PERLDB_OPTS="N f=31 AutoTrace"

22: print "Top of the world, Ma!\n";

Top of the world, Ma!

16: print join(' ',@data),":$reference\n";

main t.pl 5 main::top 1 :Printed Message

14: while (((@data) = caller($level++)))15: {

16: print join(' ',@data),":$reference\n";

main t.pl 8 main::bar 1 :Printed Message

Trang 38

14: while (((@data) = caller($level++)))

15: {

out =main::callerlog('Printed Message') from t.pl:23

out =main::top() from t.pl:5

out =main::bar() from t.pl:8

The PERLDB_OPTS environment variable is actually part of the customization

system for the debugger, which we’ll have a look at separately

Customization

There are two ways of customizing the Perl debugger The first is to specify the internal

debugger options within the value of the PERLDB_OPTS environment variable, as

you have already seen The other option is to specify options and aliases and

commands to be executed when the debugger starts, by placing commands into the

.perldb file, which is parsed at the time of execution by the debugger module

The normal use for this file is to specify new aliases to the debugger, which you do

by specifying the keys and values of the %DB::alias hash The key is the name of the

alias, and the value is the command that should be executed See the = command in the

earlier “Debugger Commands” section for details

You can change options to the debugger by calling the parse_options function,

which takes a single argument—a string such as would be specified in the

PERLDB_OPTSvariable Note, however, that the definitions in perldb are parsed

before the string defined in the environment PERLDB_OPTS variable.

Alternative Interfaces

The emacs editor provides an interface to the Perl debugger that enables you to use the

emacseditor as a complete development and debugging environment There is also a

mode available that allows emacs to understand at least some of the debugger

commands that can be used during the debugging process

There are also a number of modules available on CPAN that provide Windows-based

interfaces to the Perl debugger The most friendly of the interfaces I have come across is

the ptkdb interface.

The ptkdb debugger interface uses the Tk interface system to provide a windowed

interface to the Perl debugger All of the normal interface elements are available, with

buttons and entry points for the most regularly used items You invoke the ptkdb

interface (once it has been installed) using the debug extension command line option:

$ perl -d:ptkdb t.pl

Trang 39

748 P e r l : T h e C o m p l e t e R e f e r e n c e

You can see in Figure 21-1 a sample window for the chapter’s example debugscript The left side of the window shows the listing of the original Perl script Theright panel displays variables, expressions, subroutines, and breakpoints for thecurrent debugger invocation The information printed is the same as that producedwithin the normal text-based debugger interface, albeit within a nice preformattedand windowed environment

The ActivePerl Debugger

While there is actually nothing wrong with the text-based debugger, for manyWindows-based programmers used to tools like CodeWarrior and Visual Studio, itwill feel a little restrictive and complicated to use The ActiveState Perl Debugger(APD), which comes with the Perl Development Kit (a chargeable extra), provides aGUI interface to a debugger that will be familiar to users of Visual Studio and otherintegrated development environments

Figure 21-1 The ptkdb debugger interface

Trang 40

The basic features of the ActivePerl debugger are essentially identical to those of

the core Perl debugger, but it’s augmented with a nice GUI in a similar way to the

ptkdbinterface You can actually see a sample of some of the options in Figure 21-2 In

use, there is little real difference between the standard and ActivePerl debuggers—you

have access to the same Step Into and Step Over features, and the Watches panel

displays a list of the variables that you want to monitor

Because it’s not a standard part of either the Perl or ActivePerl distributions, I won’t

go into any more detail here For more information on the ActivePerl debugger, see the

online documentation that comes with the Perl Development Kit, the Debugging Perl

book, or the ActivePerl Developer’s Guide book—see Appendix C for more information.

Figure 21-2 The ActivePerl Debugger

Ngày đăng: 13/08/2014, 22:21

TỪ KHÓA LIÊN QUAN