ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent/lib/AnyEvent/Debug.pm
(Generate patch)

Comparing AnyEvent/lib/AnyEvent/Debug.pm (file contents):
Revision 1.25 by root, Sun Aug 21 03:12:50 2011 UTC vs.
Revision 1.41 by root, Tue Sep 25 17:21:07 2012 UTC

24 24
25=cut 25=cut
26 26
27package AnyEvent::Debug; 27package AnyEvent::Debug;
28 28
29use B ();
29use Carp (); 30use Carp ();
30use Errno (); 31use Errno ();
31 32
32use AnyEvent (); BEGIN { AnyEvent::common_sense } 33use AnyEvent (); BEGIN { AnyEvent::common_sense }
33use AnyEvent::Util (); 34use AnyEvent::Util ();
34use AnyEvent::Socket (); 35use AnyEvent::Socket ();
35use AnyEvent::Log (); 36use AnyEvent::Log ();
36 37
38our $TRACE = 1; # trace status
39
37our ($TRACE_LOGGER, $TRACE_ENABLED); 40our ($TRACE_LOGGER, $TRACE_ENABLED);
38 41
39# cache often-used strings, purely to save memory, at the expense of speed 42# cache often-used strings, purely to save memory, at the expense of speed
40our %STRCACHE; 43our %STRCACHE;
41 44
42=item $shell = AnyEvent;::Debug::shell $host, $service 45=item $shell = AnyEvent::Debug::shell $host, $service
43 46
44This function binds on the given host and service port and returns a 47This function binds on the given host and service port and returns a
45shell object, which determines the lifetime of the shell. Any number 48shell object, which determines the lifetime of the shell. Any number
46of conenctions are accepted on the port, and they will give you a very 49of conenctions are accepted on the port, and they will give you a very
47primitive shell that simply executes every line you enter. 50primitive shell that simply executes every line you enter.
48 51
49All commands will be executed "blockingly" with the socket C<select>ed for 52All commands will be executed "blockingly" with the socket C<select>ed for
50output. For a less "blocking" interface see L<Coro::Debug>. 53output. For a less "blocking" interface see L<Coro::Debug>.
51 54
52The commands will be executed in the C<AnyEvent::Debug::shell> package, 55The commands will be executed in the C<AnyEvent::Debug::shell> package,
53which currently has "help", "wl" and "wlv" commands, and can be freely 56which currently has "help" and a few other commands, and can be freely
54modified by all shells. Code is evaluated under C<use strict 'subs'>. 57modified by all shells. Code is evaluated under C<use strict 'subs'>.
55 58
56Consider the beneficial aspects of using more global (our) variables than 59Every shell has a logging context (C<$LOGGER>) that is attached to
57local ones (my) in package scope: Earlier all my modules tended to hide 60C<$AnyEvent::Log::COLLECT>), which is especially useful to gether debug
58internal variables inside C<my> variables, so users couldn't accidentally 61and trace messages.
59access them. Having interactive access to your programs changed that: 62
63As a general programming guide, consider the beneficial aspects of
64using more global (C<our>) variables than local ones (C<my>) in package
65scope: Earlier all my modules tended to hide internal variables inside
66C<my> variables, so users couldn't accidentally access them. Having
67interactive access to your programs changed that: having internal
60having internal variables still in the global scope means you can debug 68variables still in the global scope means you can debug them easier.
61them easier.
62 69
63As no authentication is done, in most cases it is best not to use a TCP 70As no authentication is done, in most cases it is best not to use a TCP
64port, but a unix domain socket, whcih can be put wherever you can access 71port, but a unix domain socket, whcih can be put wherever you can access
65it, but not others: 72it, but not others:
66 73
88 telnet localhost 1357 95 telnet localhost 1357
89 96
90=cut 97=cut
91 98
92sub shell($$) { 99sub shell($$) {
100 local $TRACE = 0;
101
93 AnyEvent::Socket::tcp_server $_[0], $_[1], sub { 102 AnyEvent::Socket::tcp_server $_[0], $_[1], sub {
94 my ($fh, $host, $port) = @_; 103 my ($fh, $host, $port) = @_;
95 104
96 syswrite $fh, "Welcome, $host:$port, use 'help' for more info!\015\012> "; 105 syswrite $fh, "Welcome, $host:$port, use 'help' for more info!\015\012> ";
97 my $rbuf; 106 my $rbuf;
107
108 my $logger = new AnyEvent::Log::Ctx
109 log_cb => sub {
110 syswrite $fh, shift;
111 0
112 };
113
114 my $logger_guard = AnyEvent::Util::guard {
115 $AnyEvent::Log::COLLECT->detach ($logger);
116 };
117 $AnyEvent::Log::COLLECT->attach ($logger);
118
119 local $TRACE = 0;
98 my $rw; $rw = AE::io $fh, 0, sub { 120 my $rw; $rw = AE::io $fh, 0, sub {
99 my $len = sysread $fh, $rbuf, 1024, length $rbuf; 121 my $len = sysread $fh, $rbuf, 1024, length $rbuf;
122
123 $logger_guard if 0; # reference it
100 124
101 if (defined $len ? $len == 0 : $! != Errno::EAGAIN) { 125 if (defined $len ? $len == 0 : $! != Errno::EAGAIN) {
102 undef $rw; 126 undef $rw;
103 } else { 127 } else {
104 while ($rbuf =~ s/^(.*)\015?\012//) { 128 while ($rbuf =~ s/^(.*)\015?\012//) {
110 syswrite $fh, "sorry, no... if you want to execute exit, try CORE::exit.\015\012"; 134 syswrite $fh, "sorry, no... if you want to execute exit, try CORE::exit.\015\012";
111 } else { 135 } else {
112 package AnyEvent::Debug::shell; 136 package AnyEvent::Debug::shell;
113 137
114 no strict 'vars'; 138 no strict 'vars';
139 local $LOGGER = $logger;
115 my $old_stdout = select $fh; 140 my $old_stdout = select $fh;
116 local $| = 1; 141 local $| = 1;
117 142
118 my @res = eval $line; 143 my @res = eval $line;
119 144
137} 162}
138 163
139{ 164{
140 package AnyEvent::Debug::shell; 165 package AnyEvent::Debug::shell;
141 166
167 our $LOGGER;
168
142 sub help() { 169 sub help() {
143 <<EOF 170 <<EOF
144help this command 171help this command
145wr [level] sets wrap level to level (or toggles if missing) 172wr [level] sets wrap level to level (or toggles if missing)
146v [level] sets verbosity (or toggles if missing) 173v [level] sets verbosity (or toggles between 0 and 9 if missing)
147wl 'regex' print wrapped watchers matching the regex (or all if missing) 174wl 'regex' print wrapped watchers matching the regex (or all if missing)
148w id,... prints the watcher with the given ids in more detail 175i id,... prints the watcher with the given ids in more detail
176t enable tracing for newly created watchers (enabled by default)
177ut disable tracing for newly created watchers
178t id,... enable tracing for the given watcher (enabled by default)
179ut id,... disable tracing for the given watcher
180w id,... converts the watcher ids to watcher objects (for scripting)
149EOF 181EOF
150 } 182 }
151 183
152 sub wl(;$) { 184 sub wl(;$) {
153 my $re = @_ ? qr<$_[0]>i : qr<.>; 185 my $re = @_ ? qr<$_[0]>i : qr<.>;
161 } 193 }
162 194
163 join "", map "$res{$_} $_\n", sort keys %res 195 join "", map "$res{$_} $_\n", sort keys %res
164 } 196 }
165 197
166 sub w(@) { 198 sub w {
167 my $res; 199 map {
168
169 for my $id (@_) {
170 if (my $w = $AnyEvent::Debug::Wrapped{$id}) { 200 $AnyEvent::Debug::Wrapped{$_} || do {
171 $res .= "$id $w\n" . $w->verbose;
172 } else {
173 $res .= "$id: no such wrapped watcher.\n"; 201 print "$_: no such wrapped watcher.\n";
202 ()
174 } 203 }
175 } 204 } @_
205 }
176 206
177 $res 207 sub i {
208 join "",
209 map $_->id . " $_\n" . $_->verbose . "\n",
210 &w
178 } 211 }
179 212
180 sub wr { 213 sub wr {
181 AnyEvent::Debug::wrap (@_); 214 AnyEvent::Debug::wrap (@_);
182 215
183 "wrap level now $AnyEvent::Debug::WRAP_LEVEL" 216 "wrap level now $AnyEvent::Debug::WRAP_LEVEL"
184 } 217 }
185 218
219 sub t {
220 if (@_) {
221 @_ = &w;
222 $_->trace (1)
223 for @_;
224 "tracing enabled for @_."
225 } else {
226 $AnyEvent::Debug::TRACE = 1;
227 "tracing for newly created watchers is now enabled."
228 }
229 }
230
231 sub u {
232 if (@_) {
233 @_ = &w;
234 $_->trace (0)
235 for @_;
236 "tracing disabled for @_."
237 } else {
238 $AnyEvent::Debug::TRACE = 0;
239 "tracing for newly created watchers is now disabled."
240 }
241 }
242
186 sub v { 243 sub v {
187 #TODO 244 $LOGGER->level (@_ ? $_[0] : $LOGGER->[1] ? 0 : 9);
188 $AnyEvent::VERBOSE = @_ ? shift : $AnyEvent::VERBOSE ? 0 : 9;
189 245
190 "verbosity level now $AnyEvent::VEBROSE" 246 "verbose logging is now " . ($LOGGER->[1] ? "enabled" : "disabled") . "."
191 } 247 }
192} 248}
193 249
194=item AnyEvent::Debug::wrap [$level] 250=item AnyEvent::Debug::wrap [$level]
195 251
201C<$ENV{PERL_ANYEVENT_DEBUG_WRAP}> specifies. 257C<$ENV{PERL_ANYEVENT_DEBUG_WRAP}> specifies.
202 258
203A level of C<0> disables wrapping, i.e. AnyEvent works normally, and in 259A level of C<0> disables wrapping, i.e. AnyEvent works normally, and in
204its most efficient mode. 260its most efficient mode.
205 261
206A level of C<1> enables wrapping, which replaces all watchers by 262A level of C<1> or higher enables wrapping, which replaces all watchers
207AnyEvent::Debug::Wrapped objects, stores the location where a watcher 263by AnyEvent::Debug::Wrapped objects, stores the location where a
208was created and wraps the callback to log all invocations at "trace" 264watcher was created and wraps the callback to log all invocations at
265"trace" loglevel if tracing is enabled fore the watcher. The initial
266state of tracing when creating a watcher is taken from the global
267variable C<$AnyEvent:Debug::TRACE>. The default value of that variable
268is C<1>, but it can make sense to set it to C<0> and then do C<< local
269$AnyEvent::Debug::TRACE = 1 >> in a block where you create "interesting"
270watchers. Tracing can also be enabled and disabled later by calling the
271watcher's C<trace> method.
272
209loglevel. The wrapper will also count how many times the callback was 273The wrapper will also count how many times the callback was invoked and
210invoked and will record up to ten runtime errors with corresponding 274will record up to ten runtime errors with corresponding backtraces. It
211backtraces. It will also log runtime errors at "error" loglevel. 275will also log runtime errors at "error" loglevel.
212 276
213To see the trace messages, you can invoke your program with 277To see the trace messages, you can invoke your program with
214C<PERL_ANYEVENT_VERBOSE=9>, or you can use AnyEvent::Log to divert 278C<PERL_ANYEVENT_VERBOSE=9>, or you can use AnyEvent::Log to divert
215the trace messages in any way you like (the EXAMPLES section in 279the trace messages in any way you like (the EXAMPLES section in
216L<AnyEvent::Log> has some examples). 280L<AnyEvent::Log> has some examples).
218A level of C<2> does everything that level C<1> does, but also stores a 282A level of C<2> does everything that level C<1> does, but also stores a
219full backtrace of the location the watcher was created, which slows down 283full backtrace of the location the watcher was created, which slows down
220watcher creation considerably. 284watcher creation considerably.
221 285
222Every wrapped watcher will be linked into C<%AnyEvent::Debug::Wrapped>, 286Every wrapped watcher will be linked into C<%AnyEvent::Debug::Wrapped>,
223with its address as key. The C<wl> command in the debug shell cna be used 287with its address as key. The C<wl> command in the debug shell can be used
224to list watchers. 288to list watchers.
225 289
226Instrumenting can increase the size of each watcher multiple times, and, 290Instrumenting can increase the size of each watcher multiple times, and,
227especially when backtraces are involved, also slows down watcher creation 291especially when backtraces are involved, also slows down watcher creation
228a lot. 292a lot.
257 undef $POST_DETECT; 321 undef $POST_DETECT;
258 return unless $WRAP_LEVEL; 322 return unless $WRAP_LEVEL;
259 323
260 (my $level, $WRAP_LEVEL) = ($WRAP_LEVEL, undef); 324 (my $level, $WRAP_LEVEL) = ($WRAP_LEVEL, undef);
261 325
262 require AnyEvent::Strict; 326 require AnyEvent::Strict unless $AnyEvent::Strict::VERSION;
263 327
264 AnyEvent::post_detect { # make sure we run after AnyEvent::Strict 328 AnyEvent::post_detect { # make sure we run after AnyEvent::Strict
265 wrap ($level); 329 wrap ($level);
266 }; 330 };
267 }; 331 };
308=cut 372=cut
309 373
310sub cb2str($) { 374sub cb2str($) {
311 my $cb = shift; 375 my $cb = shift;
312 376
313 require B;
314
315 "CODE" eq ref $cb 377 "CODE" eq ref $cb
316 or return "$cb"; 378 or return "$cb";
317 379
380 eval {
318 my $cv = B::svref_2object ($cb); 381 my $cv = B::svref_2object ($cb);
319 382
320 my $gv = $cv->GV 383 my $gv = $cv->GV
321 or return "$cb"; 384 or return "$cb";
322 385
386 my $name = $gv->NAME;
387
323 return (AnyEvent::Debug::path2mod $gv->FILE) . ":" . $gv->LINE 388 return (AnyEvent::Debug::path2mod $gv->FILE) . ":" . $gv->LINE
324 if $gv->NAME eq "__ANON__"; 389 if $name eq "__ANON__";
325 390
326 return $gv->STASH->NAME . "::" . $gv->NAME; 391 $gv->STASH->NAME . "::" . $name;
392 } || "$cb"
327} 393}
328 394
329sub sv2str($) { 395sub sv2str($) {
330 if (ref $_[0]) { 396 if (ref $_[0]) {
331 if (ref $_[0] eq "CODE") { 397 if (ref $_[0] eq "CODE") {
344 410
345=item AnyEvent::Debug::backtrace [$skip] 411=item AnyEvent::Debug::backtrace [$skip]
346 412
347Creates a backtrace (actually an AnyEvent::Debug::Backtrace object 413Creates a backtrace (actually an AnyEvent::Debug::Backtrace object
348that you can stringify), not unlike the Carp module would. Unlike the 414that you can stringify), not unlike the Carp module would. Unlike the
349Carp module it resolves some references (euch as callbacks) to more 415Carp module it resolves some references (such as callbacks) to more
350user-friendly strings, has a more succinct output format and most 416user-friendly strings, has a more succinct output format and most
351importantly: doesn't leak memory like hell. 417importantly: doesn't leak memory like hell.
352 418
353The reason it creates an object is to save time, as formatting can be 419The reason it creates an object is to save time, as formatting can be
354done at a later time. Still, creating a backtrace is a relatively slow 420done at a later time. Still, creating a backtrace is a relatively slow
410 *$name = sub { 476 *$name = sub {
411 my ($self, %arg) = @_; 477 my ($self, %arg) = @_;
412 478
413 my $w; 479 my $w;
414 480
481 my $t = $TRACE;
482
415 my ($pkg, $file, $line, $sub); 483 my ($pkg, $file, $line, $sub);
416 484
417 $w = 0; 485 $w = 0;
418 do { 486 do {
419 ($pkg, $file, $line) = caller $w++; 487 ($pkg, $file, $line) = caller $w++;
425 $arg{cb} = sub { 493 $arg{cb} = sub {
426 ++$w->{called}; 494 ++$w->{called};
427 495
428 local $TRACE_CUR = $w; 496 local $TRACE_CUR = $w;
429 497
430 $TRACE_LOGGER->("enter $w") if $TRACE_ENABLED; 498 $TRACE_LOGGER->("enter $w") if $TRACE_ENABLED && $t;
431 eval { 499 eval {
432 local $SIG{__DIE__} = sub { 500 local $SIG{__DIE__} = sub {
433 die $_[0] . AnyEvent::Debug::backtrace 501 die $_[0] . AnyEvent::Debug::backtrace
434 if defined $^S; 502 if defined $^S;
435 }; 503 };
436 &$cb; 504 &$cb;
437 }; 505 };
438 if ($@) { 506 if ($@) {
507 my $err = "$@";
439 push @{ $w->{error} }, [AE::now, "$@"] 508 push @{ $w->{error} }, [AE::now, $err]
440 if @{ $w->{error} } < 10; 509 if @{ $w->{error} } < 10;
441 AE::log die => "($w) $@" 510 AE::log die => "($w) $err"
442 or warn "($w) $@"; 511 or warn "($w) $err";
443 } 512 }
444 $TRACE_LOGGER->("leave $w") if $TRACE_ENABLED; 513 $TRACE_LOGGER->("leave $w") if $TRACE_ENABLED && $t;
445 }; 514 };
446 515
447 $self = bless { 516 $self = bless {
448 type => $name, 517 type => $name,
449 w => $self->$super (%arg), 518 w => $self->$super (%arg),
453 cur => "$TRACE_CUR", 522 cur => "$TRACE_CUR",
454 now => AE::now, 523 now => AE::now,
455 arg => \%arg, 524 arg => \%arg,
456 cb => $cb, 525 cb => $cb,
457 called => 0, 526 called => 0,
527 rt => \$t,
458 }, "AnyEvent::Debug::Wrapped"; 528 }, "AnyEvent::Debug::Wrapped";
459 529
460 delete $arg{cb}; 530 delete $arg{cb};
461 531
462 $self->{bt} = AnyEvent::Debug::backtrace 1 532 $self->{bt} = AnyEvent::Debug::backtrace 1
463 if $WRAP_LEVEL >= 2; 533 if $WRAP_LEVEL >= 2;
464 534
465 Scalar::Util::weaken ($w = $self); 535 Scalar::Util::weaken ($w = $self);
466 Scalar::Util::weaken ($AnyEvent::Debug::Wrapped{Scalar::Util::refaddr $self} = $self); 536 Scalar::Util::weaken ($AnyEvent::Debug::Wrapped{Scalar::Util::refaddr $self} = $self);
467 537
468 $TRACE_LOGGER->("creat $w") if $TRACE_ENABLED; 538 $TRACE_LOGGER->("creat $w") if $TRACE_ENABLED && $t;
469 539
470 $self 540 $self
471 }; 541 };
472 } 542 }
473} 543}
479All watchers created while the wrap level is non-zero will be wrapped 549All watchers created while the wrap level is non-zero will be wrapped
480inside an AnyEvent::Debug::Wrapped object. The address of the 550inside an AnyEvent::Debug::Wrapped object. The address of the
481wrapped watcher will become its ID - every watcher will be stored in 551wrapped watcher will become its ID - every watcher will be stored in
482C<$AnyEvent::Debug::Wrapped{$id}>. 552C<$AnyEvent::Debug::Wrapped{$id}>.
483 553
484These wrapper objects, as of now, can be stringified, and you can call the 554These wrapper objects can be stringified and have some methods defined on
485C<< ->verbose >> method to get a multiline string describing the watcher 555them.
486in great detail, but otherwise has no other public methods.
487 556
488For debugging, of course, it can be helpful to look into these objects, 557For debugging, of course, it can be helpful to look into these objects,
489which is why this is documented here, but this might change at any time in 558which is why this is documented here, but this might change at any time in
490future versions. 559future versions.
491 560
500 now => the timestamp (AE::now) when the watcher was created 569 now => the timestamp (AE::now) when the watcher was created
501 arg => the arguments used to create the watcher (sans C<cb>) 570 arg => the arguments used to create the watcher (sans C<cb>)
502 cb => the original callback used to create the watcher 571 cb => the original callback used to create the watcher
503 called => the number of times the callback was called 572 called => the number of times the callback was called
504 573
574Each object supports the following mehtods (warning: these are only
575available on wrapped watchers, so are best for interactive use via the
576debug shell).
577
578=over 4
579
505=cut 580=cut
506 581
507use AnyEvent (); BEGIN { AnyEvent::common_sense } 582use AnyEvent (); BEGIN { AnyEvent::common_sense }
508 583
509use overload 584use overload
523 . (AnyEvent::Debug::cb2str $_[0]{cb}) 598 . (AnyEvent::Debug::cb2str $_[0]{cb})
524 }; 599 };
525 }, 600 },
526 fallback => 1, 601 fallback => 1,
527; 602;
603
604=item $w->id
605
606Returns the numerical id of the watcher, as used in the debug shell.
607
608=cut
609
610sub id {
611 Scalar::Util::refaddr shift
612}
613
614=item $w->verbose
615
616Returns a multiline textual description of the watcher, including the
617first ten exceptions caught while executing the callback.
618
619=cut
528 620
529sub verbose { 621sub verbose {
530 my ($self) = @_; 622 my ($self) = @_;
531 623
532 my $res = "type: $self->{type} watcher\n" 624 my $res = "type: $self->{type} watcher\n"
534 . "created: " . (AnyEvent::Log::ft $self->{now}) . " ($self->{now})\n" 626 . "created: " . (AnyEvent::Log::ft $self->{now}) . " ($self->{now})\n"
535 . "file: ${ $self->{rfile} }\n" 627 . "file: ${ $self->{rfile} }\n"
536 . "line: $self->{line}\n" 628 . "line: $self->{line}\n"
537 . "subname: $self->{sub}\n" 629 . "subname: $self->{sub}\n"
538 . "context: $self->{cur}\n" 630 . "context: $self->{cur}\n"
631 . "tracing: " . (${ $self->{rt} } ? "enabled" : "disabled") . "\n"
539 . "cb: $self->{cb} (" . (AnyEvent::Debug::cb2str $self->{cb}) . ")\n" 632 . "cb: $self->{cb} (" . (AnyEvent::Debug::cb2str $self->{cb}) . ")\n"
540 . "invoked: $self->{called} times\n"; 633 . "invoked: $self->{called} times\n";
541 634
542 if (exists $self->{bt}) { 635 if (exists $self->{bt}) {
543 $res .= "created\n$self->{bt}"; 636 $res .= "created\n$self->{bt}";
551 } 644 }
552 645
553 $res 646 $res
554} 647}
555 648
649=item $w->trace ($on)
650
651Enables (C<$on> is true) or disables (C<$on> is false) tracing on this
652watcher.
653
654To get tracing messages, both the global logging settings must have trace
655messages enabled for the context C<AnyEvent::Debug> and tracing must be
656enabled for the wrapped watcher.
657
658To enable trace messages globally, the simplest way is to start the
659program with C<PERL_ANYEVENT_VERBOSE=9> in the environment.
660
661Tracing for each individual watcher is enabled by default (unless
662C<$AnyEvent::Debug::TRACE> has been set to false).
663
664=cut
665
666sub trace {
667 ${ $_[0]{rt} } = $_[1];
668}
669
556sub DESTROY { 670sub DESTROY {
557 $TRACE_LOGGER->("dstry $_[0]") if $TRACE_ENABLED; 671 $TRACE_LOGGER->("dstry $_[0]") if $TRACE_ENABLED && ${ $_[0]{rt} };
558 672
559 delete $AnyEvent::Debug::Wrapped{Scalar::Util::refaddr $_[0]}; 673 delete $AnyEvent::Debug::Wrapped{Scalar::Util::refaddr $_[0]};
560} 674}
675
676=back
677
678=cut
561 679
562package AnyEvent::Debug::Backtrace; 680package AnyEvent::Debug::Backtrace;
563 681
564use AnyEvent (); BEGIN { AnyEvent::common_sense } 682use AnyEvent (); BEGIN { AnyEvent::common_sense }
565 683
572 for (@$self) { 690 for (@$self) {
573 my ($rpath, $line, $sub) = @$_; 691 my ($rpath, $line, $sub) = @$_;
574 692
575 $rpath = (AnyEvent::Debug::path2mod $$rpath) . " line $line"; 693 $rpath = (AnyEvent::Debug::path2mod $$rpath) . " line $line";
576 $modlen = length $rpath if $modlen < length $rpath; 694 $modlen = length $rpath if $modlen < length $rpath;
695
696 $sub =~ s/\r/\\r/g;
697 $sub =~ s/\n/\\n/g;
698 $sub =~ s/([\x00-\x1f\x7e-\xff])/sprintf "\\x%02x", ord $1/ge;
699 $sub =~ s/([^\x20-\x7e])/sprintf "\\x{%x}", ord $1/ge;
577 700
578 push @bt, [$rpath, $sub]; 701 push @bt, [$rpath, $sub];
579 } 702 }
580 703
581 join "", 704 join "",
586use overload 709use overload
587 '""' => \&as_string, 710 '""' => \&as_string,
588 fallback => 1, 711 fallback => 1,
589; 712;
590 713
5911;
592
593=head1 AUTHOR 714=head1 AUTHOR
594 715
595 Marc Lehmann <schmorp@schmorp.de> 716 Marc Lehmann <schmorp@schmorp.de>
596 http://home.schmorp.de/ 717 http://anyevent.schmorp.de
597 718
598=cut 719=cut
599 720
7211
722

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines