… | |
… | |
24 | |
24 | |
25 | =cut |
25 | =cut |
26 | |
26 | |
27 | package AnyEvent::Debug; |
27 | package AnyEvent::Debug; |
28 | |
28 | |
|
|
29 | use Carp (); |
29 | use Errno (); |
30 | use Errno (); |
30 | use POSIX (); |
|
|
31 | |
31 | |
32 | use AnyEvent (); BEGIN { AnyEvent::common_sense } |
32 | use AnyEvent (); BEGIN { AnyEvent::common_sense } |
33 | use AnyEvent::Util (); |
33 | use AnyEvent::Util (); |
34 | use AnyEvent::Socket (); |
34 | use AnyEvent::Socket (); |
|
|
35 | use AnyEvent::Log (); |
|
|
36 | |
|
|
37 | our ($TRACE_LOGGER, $TRACE_ENABLED); |
|
|
38 | |
|
|
39 | # cache often-used strings, purely to save memory, at the expense of speed |
|
|
40 | our %STRCACHE; |
35 | |
41 | |
36 | =item $shell = AnyEvent;::Debug::shell $host, $service |
42 | =item $shell = AnyEvent;::Debug::shell $host, $service |
37 | |
43 | |
38 | This function binds on the given host and service port and returns a |
44 | This function binds on the given host and service port and returns a |
39 | shell object, which determines the lifetime of the shell. Any number |
45 | shell object, which determines the lifetime of the shell. Any number |
… | |
… | |
135 | |
141 | |
136 | sub help() { |
142 | sub help() { |
137 | <<EOF |
143 | <<EOF |
138 | help this command |
144 | help this command |
139 | wr [level] sets wrap level to level (or toggles if missing) |
145 | wr [level] sets wrap level to level (or toggles if missing) |
140 | t [level] sets trace level (or toggles if missing) |
146 | v [level] sets verbosity (or toggles if missing) |
141 | wl 'regex' print wrapped watchers matching the regex (or all if missing) |
147 | wl 'regex' print wrapped watchers matching the regex (or all if missing) |
142 | w id,... prints the watcher with the given ids in more detail |
148 | w id,... prints the watcher with the given ids in more detail |
143 | EOF |
149 | EOF |
144 | } |
150 | } |
145 | |
151 | |
… | |
… | |
175 | AnyEvent::Debug::wrap (@_); |
181 | AnyEvent::Debug::wrap (@_); |
176 | |
182 | |
177 | "wrap level now $AnyEvent::Debug::WRAP_LEVEL" |
183 | "wrap level now $AnyEvent::Debug::WRAP_LEVEL" |
178 | } |
184 | } |
179 | |
185 | |
180 | sub t { |
186 | sub v { |
181 | $AnyEvent::Debug::TRACE_LEVEL = @_ ? shift : $AnyEvent::Debug::TRACE_LEVEL ? 0 : 9; |
187 | #TODO |
|
|
188 | $AnyEvent::VERBOSE = @_ ? shift : $AnyEvent::VERBOSE ? 0 : 9; |
182 | |
189 | |
183 | "trace level now $AnyEvent::Debug::TRACE_LEVEL" |
190 | "verbosity level now $AnyEvent::VEBROSE" |
184 | } |
191 | } |
185 | } |
192 | } |
186 | |
193 | |
187 | =item AnyEvent::Debug::wrap [$level] |
194 | =item AnyEvent::Debug::wrap [$level] |
188 | |
195 | |
… | |
… | |
195 | |
202 | |
196 | A level of C<0> disables wrapping, i.e. AnyEvent works normally, and in |
203 | A level of C<0> disables wrapping, i.e. AnyEvent works normally, and in |
197 | its most efficient mode. |
204 | its most efficient mode. |
198 | |
205 | |
199 | A level of C<1> enables wrapping, which replaces all watchers by |
206 | A level of C<1> enables wrapping, which replaces all watchers by |
200 | AnyEvent::Debug::Wrapped objects, stores the location where a watcher was |
207 | AnyEvent::Debug::Wrapped objects, stores the location where a watcher |
201 | created and wraps the callback so invocations of it can be traced. |
208 | was created and wraps the callback to log all invocations at "trace" |
|
|
209 | loglevel. The wrapper will also count how many times the callback was |
|
|
210 | invoked and will record up to ten runtime errors with corresponding |
|
|
211 | backtraces. It will also log runtime errors at "error" loglevel. |
|
|
212 | |
|
|
213 | To see the trace messages, you can invoke your program with |
|
|
214 | C<PERL_ANYEVENT_VERBOSE=9>, or you can use AnyEvent::Log to divert |
|
|
215 | the trace messages in any way you like (the EXAMPLES section in |
|
|
216 | L<AnyEvent::Log> has some examples). |
202 | |
217 | |
203 | A level of C<2> does everything that level C<1> does, but also stores a |
218 | A level of C<2> does everything that level C<1> does, but also stores a |
204 | full backtrace of the location the watcher was created. |
219 | full backtrace of the location the watcher was created, which slows down |
|
|
220 | watcher creation considerably. |
205 | |
221 | |
206 | Every wrapped watcher will be linked into C<%AnyEvent::Debug::Wrapped>, |
222 | Every wrapped watcher will be linked into C<%AnyEvent::Debug::Wrapped>, |
207 | with its address as key. The C<wl> command in the debug shell cna be used |
223 | with its address as key. The C<wl> command in the debug shell cna be used |
208 | to list watchers. |
224 | to list watchers. |
209 | |
225 | |
… | |
… | |
213 | |
229 | |
214 | Also, enabling and disabling instrumentation will not recover the full |
230 | Also, enabling and disabling instrumentation will not recover the full |
215 | performance that you had before wrapping (the AE::xxx functions will stay |
231 | performance that you had before wrapping (the AE::xxx functions will stay |
216 | slower, for example). |
232 | slower, for example). |
217 | |
233 | |
218 | Currently, enabling wrapping will also load AnyEvent::Strict, but this is |
234 | If you are developing your program, also consider using AnyEvent::Strict |
219 | not be relied upon. |
235 | to check for common mistakes. |
220 | |
236 | |
221 | =cut |
237 | =cut |
222 | |
238 | |
223 | our $WRAP_LEVEL; |
239 | our $WRAP_LEVEL; |
224 | our $TRACE_LEVEL; |
|
|
225 | our $TRACE_CUR; |
240 | our $TRACE_CUR; |
226 | our $POST_DETECT; |
241 | our $POST_DETECT; |
227 | |
242 | |
228 | sub wrap(;$) { |
243 | sub wrap(;$) { |
229 | my $PREV_LEVEL = $WRAP_LEVEL; |
244 | my $PREV_LEVEL = $WRAP_LEVEL; |
230 | $WRAP_LEVEL = @_ ? 0+shift : $WRAP_LEVEL ? 0 : 1; |
245 | $WRAP_LEVEL = @_ ? 0+shift : $WRAP_LEVEL ? 0 : 1; |
231 | |
246 | |
232 | if (defined $AnyEvent::MODEL) { |
247 | if ($AnyEvent::MODEL) { |
233 | unless (defined $PREV_LEVEL) { |
|
|
234 | AnyEvent::Debug::Wrapped::_init (); |
|
|
235 | } |
|
|
236 | |
|
|
237 | if ($WRAP_LEVEL && !$PREV_LEVEL) { |
248 | if ($WRAP_LEVEL && !$PREV_LEVEL) { |
|
|
249 | $TRACE_LOGGER = AnyEvent::Log::logger trace => \$TRACE_ENABLED; |
238 | AnyEvent::_isa_hook 1 => "AnyEvent::Debug::Wrap", 1; |
250 | AnyEvent::_isa_hook 0 => "AnyEvent::Debug::Wrap", 1; |
239 | AnyEvent::Debug::Wrap::_reset (); |
251 | AnyEvent::Debug::Wrap::_reset (); |
240 | } elsif (!$WRAP_LEVEL && $PREV_LEVEL) { |
252 | } elsif (!$WRAP_LEVEL && $PREV_LEVEL) { |
241 | AnyEvent::_isa_hook 0 => undef; |
253 | AnyEvent::_isa_hook 0 => undef; |
242 | } |
254 | } |
243 | } else { |
255 | } else { |
… | |
… | |
289 | |
301 | |
290 | Using various gambits, tries to convert a callback (e.g. a code reference) |
302 | Using various gambits, tries to convert a callback (e.g. a code reference) |
291 | into a more useful string. |
303 | into a more useful string. |
292 | |
304 | |
293 | Very useful if you debug a program and have some callback, but you want to |
305 | Very useful if you debug a program and have some callback, but you want to |
294 | know where in the program the callbakc is actually defined. |
306 | know where in the program the callback is actually defined. |
295 | |
307 | |
296 | =cut |
308 | =cut |
297 | |
309 | |
298 | sub cb2str($) { |
310 | sub cb2str($) { |
299 | my $cb = shift; |
311 | my $cb = shift; |
… | |
… | |
312 | if $gv->NAME eq "__ANON__"; |
324 | if $gv->NAME eq "__ANON__"; |
313 | |
325 | |
314 | return $gv->STASH->NAME . "::" . $gv->NAME; |
326 | return $gv->STASH->NAME . "::" . $gv->NAME; |
315 | } |
327 | } |
316 | |
328 | |
317 | # Format Time, not public - yet? |
|
|
318 | sub ft($) { |
329 | sub sv2str($) { |
|
|
330 | if (ref $_[0]) { |
|
|
331 | if (ref $_[0] eq "CODE") { |
|
|
332 | return "$_[0]=" . cb2str $_[0]; |
|
|
333 | } else { |
|
|
334 | return "$_[0]"; |
|
|
335 | } |
|
|
336 | } else { |
|
|
337 | for ("\'$_[0]\'") { # make copy |
|
|
338 | substr $_, $Carp::MaxArgLen, length, "'..." |
|
|
339 | if length > $Carp::MaxArgLen; |
|
|
340 | return $_; |
|
|
341 | } |
|
|
342 | } |
|
|
343 | } |
|
|
344 | |
|
|
345 | =item AnyEvent::Debug::backtrace [$skip] |
|
|
346 | |
|
|
347 | Creates a backtrace (actually an AnyEvent::Debug::Backtrace object |
|
|
348 | that you can stringify), not unlike the Carp module would. Unlike the |
|
|
349 | Carp module it resolves some references (euch as callbacks) to more |
|
|
350 | user-friendly strings, has a more succinct output format and most |
|
|
351 | importantly: doesn't leak memory like hell. |
|
|
352 | |
|
|
353 | The reason it creates an object is to save time, as formatting can be |
|
|
354 | done at a later time. Still, creating a backtrace is a relatively slow |
|
|
355 | operation. |
|
|
356 | |
|
|
357 | =cut |
|
|
358 | |
|
|
359 | sub backtrace(;$) { |
319 | my $t = shift; |
360 | my $w = shift; |
320 | my $i = int $t; |
|
|
321 | my $f = sprintf "%06d", 1e6 * ($t - $i); |
|
|
322 | |
361 | |
323 | POSIX::strftime "%Y-%m-%d %H:%M:%S.$f %z", localtime $i |
362 | my (@bt, @c); |
|
|
363 | my ($modlen, $sub); |
|
|
364 | |
|
|
365 | for (;;) { |
|
|
366 | # 0 1 2 3 4 5 6 7 8 9 10 |
|
|
367 | # ($package, $filename, $line, $subroutine, $hasargs, $wantarray, $evaltext, $is_require, $hints, $bitmask, $hinthash) |
|
|
368 | package DB; |
|
|
369 | @c = caller $w++ |
|
|
370 | or last; |
|
|
371 | package AnyEvent::Debug; # no block for speed reasons |
|
|
372 | |
|
|
373 | if ($c[7]) { |
|
|
374 | $sub = "require $c[6]"; |
|
|
375 | } elsif (defined $c[6]) { |
|
|
376 | $sub = "eval \"\""; |
|
|
377 | } else { |
|
|
378 | $sub = ($c[4] ? "" : "&") . $c[3]; |
|
|
379 | |
|
|
380 | $sub .= "(" |
|
|
381 | . (join ",", |
|
|
382 | map sv2str $DB::args[$_], |
|
|
383 | 0 .. (@DB::args < $Carp::MaxArgNums ? @DB::args : $Carp::MaxArgNums) - 1) |
|
|
384 | . ")" |
|
|
385 | if $c[4]; |
|
|
386 | } |
|
|
387 | |
|
|
388 | push @bt, [\($STRCACHE{$c[1]} ||= $c[1]), $c[2], $sub]; |
|
|
389 | } |
|
|
390 | |
|
|
391 | @DB::args = (); |
|
|
392 | |
|
|
393 | bless \@bt, "AnyEvent::Debug::Backtrace" |
324 | } |
394 | } |
|
|
395 | |
|
|
396 | =back |
|
|
397 | |
|
|
398 | =cut |
325 | |
399 | |
326 | package AnyEvent::Debug::Wrap; |
400 | package AnyEvent::Debug::Wrap; |
327 | |
401 | |
328 | use AnyEvent (); BEGIN { AnyEvent::common_sense } |
402 | use AnyEvent (); BEGIN { AnyEvent::common_sense } |
329 | use Scalar::Util (); |
403 | use Scalar::Util (); |
… | |
… | |
341 | my ($pkg, $file, $line, $sub); |
415 | my ($pkg, $file, $line, $sub); |
342 | |
416 | |
343 | $w = 0; |
417 | $w = 0; |
344 | do { |
418 | do { |
345 | ($pkg, $file, $line) = caller $w++; |
419 | ($pkg, $file, $line) = caller $w++; |
346 | } while $pkg =~ /^(?:AE|AnyEvent::(?:Socket|Util|Debug|Strict|Base|CondVar|CondVar::Base|Impl::.*))$/; |
420 | } while $pkg =~ /^(?:AE|AnyEvent::(?:Socket|Handle|Util|Debug|Strict|Base|CondVar|CondVar::Base|Impl::.*)|Coro::AnyEvent::CondVar)$/; |
347 | |
421 | |
348 | $sub = (caller $w++)[3]; |
422 | $sub = (caller $w)[3]; |
349 | |
423 | |
350 | my $cb = $arg{cb}; |
424 | my $cb = $arg{cb}; |
351 | $arg{cb} = sub { |
425 | $arg{cb} = sub { |
352 | ++$w->{called}; |
426 | ++$w->{called}; |
353 | |
427 | |
354 | return &$cb |
|
|
355 | unless $TRACE_LEVEL; |
|
|
356 | |
|
|
357 | local $TRACE_CUR = "$w"; |
428 | local $TRACE_CUR = $w; |
358 | print AnyEvent::Debug::ft AE::now, " enter $TRACE_CUR\n" if $TRACE_LEVEL; |
429 | |
|
|
430 | $TRACE_LOGGER->("enter $w") if $TRACE_ENABLED; |
359 | eval { |
431 | eval { |
360 | local $SIG{__DIE__} = sub { die Carp::longmess "$_[0]Backtrace starting" }; |
432 | local $SIG{__DIE__} = sub { |
|
|
433 | die $_[0] . AnyEvent::Debug::backtrace |
|
|
434 | if defined $^S; |
|
|
435 | }; |
361 | &$cb; |
436 | &$cb; |
362 | }; |
437 | }; |
363 | if ($@) { |
438 | if ($@) { |
364 | push @{ $w->{error} }, [AE::now, $@] |
439 | push @{ $w->{error} }, [AE::now, "$@"] |
365 | if @{ $w->{error} } < 10; |
440 | if @{ $w->{error} } < 10; |
366 | print AnyEvent::Debug::ft AE::now, " ERROR $TRACE_CUR $@"; |
441 | AE::log die => "($w) $@" |
|
|
442 | or warn "($w) $@"; |
367 | } |
443 | } |
368 | print AnyEvent::Debug::ft AE::now, " leave $TRACE_CUR\n" if $TRACE_LEVEL; |
444 | $TRACE_LOGGER->("leave $w") if $TRACE_ENABLED; |
369 | }; |
445 | }; |
370 | |
446 | |
371 | $self = bless { |
447 | $self = bless { |
372 | type => $name, |
448 | type => $name, |
373 | w => $self->$super (%arg), |
449 | w => $self->$super (%arg), |
374 | file => $file, |
450 | rfile => \($STRCACHE{$file} ||= $file), |
375 | line => $line, |
451 | line => $line, |
376 | sub => $sub, |
452 | sub => $sub, |
377 | cur => $TRACE_CUR, |
453 | cur => "$TRACE_CUR", |
378 | now => AE::now, |
454 | now => AE::now, |
379 | arg => \%arg, |
455 | arg => \%arg, |
380 | cb => $cb, |
456 | cb => $cb, |
381 | called => 0, |
457 | called => 0, |
382 | }, "AnyEvent::Debug::Wrapped"; |
458 | }, "AnyEvent::Debug::Wrapped"; |
383 | |
459 | |
384 | delete $arg{cb}; |
460 | delete $arg{cb}; |
385 | |
461 | |
386 | # backtraces leak like hell |
462 | $self->{bt} = AnyEvent::Debug::backtrace 1 |
387 | $self->{bt} = Carp::longmess "" |
|
|
388 | if $WRAP_LEVEL >= 2; |
463 | if $WRAP_LEVEL >= 2; |
389 | |
464 | |
390 | Scalar::Util::weaken ($w = $self); |
465 | Scalar::Util::weaken ($w = $self); |
391 | Scalar::Util::weaken ($AnyEvent::Debug::Wrapped{Scalar::Util::refaddr $self} = $self); |
466 | Scalar::Util::weaken ($AnyEvent::Debug::Wrapped{Scalar::Util::refaddr $self} = $self); |
392 | |
467 | |
393 | print AnyEvent::Debug::ft AE::now, " creat $w\n" if $TRACE_LEVEL; |
468 | $TRACE_LOGGER->("creat $w") if $TRACE_ENABLED; |
394 | |
469 | |
395 | $self |
470 | $self |
396 | }; |
471 | }; |
397 | } |
472 | } |
398 | } |
473 | } |
399 | |
474 | |
400 | package AnyEvent::Debug::Wrapped; |
475 | package AnyEvent::Debug::Wrapped; |
401 | |
476 | |
|
|
477 | =head1 THE AnyEvent::Debug::Wrapped CLASS |
|
|
478 | |
|
|
479 | All watchers created while the wrap level is non-zero will be wrapped |
|
|
480 | inside an AnyEvent::Debug::Wrapped object. The address of the |
|
|
481 | wrapped watcher will become its ID - every watcher will be stored in |
|
|
482 | C<$AnyEvent::Debug::Wrapped{$id}>. |
|
|
483 | |
|
|
484 | These wrapper objects, as of now, can be stringified, and you can call the |
|
|
485 | C<< ->verbose >> method to get a multiline string describing the watcher |
|
|
486 | in great detail, but otherwise has no other public methods. |
|
|
487 | |
|
|
488 | For debugging, of course, it can be helpful to look into these objects, |
|
|
489 | which is why this is documented here, but this might change at any time in |
|
|
490 | future versions. |
|
|
491 | |
|
|
492 | Each object is a relatively standard hash with the following members: |
|
|
493 | |
|
|
494 | type => name of the method used ot create the watcher (e.g. C<io>, C<timer>). |
|
|
495 | w => the actual watcher |
|
|
496 | rfile => reference to the filename of the file the watcher was created in |
|
|
497 | line => line number where it was created |
|
|
498 | sub => function name (or a special string) which created the watcher |
|
|
499 | cur => if created inside another watcher callback, this is the string rep of the other watcher |
|
|
500 | now => the timestamp (AE::now) when the watcher was created |
|
|
501 | arg => the arguments used to create the watcher (sans C<cb>) |
|
|
502 | cb => the original callback used to create the watcher |
|
|
503 | called => the number of times the callback was called |
|
|
504 | |
|
|
505 | =cut |
|
|
506 | |
402 | use AnyEvent (); BEGIN { AnyEvent::common_sense } |
507 | use AnyEvent (); BEGIN { AnyEvent::common_sense } |
403 | |
508 | |
404 | sub _init { |
509 | use overload |
405 | require overload; |
|
|
406 | import overload |
|
|
407 | '""' => sub { |
510 | '""' => sub { |
408 | $_[0]{str} ||= do { |
511 | $_[0]{str} ||= do { |
409 | my ($pkg, $line) = @{ $_[0]{caller} }; |
512 | my ($pkg, $line) = @{ $_[0]{caller} }; |
410 | |
513 | |
411 | my $mod = AnyEvent::Debug::path2mod $_[0]{file}; |
514 | my $mod = AnyEvent::Debug::path2mod ${ $_[0]{rfile} }; |
412 | my $sub = $_[0]{sub}; |
515 | my $sub = $_[0]{sub}; |
413 | |
516 | |
414 | if (defined $sub) { |
517 | if (defined $sub) { |
415 | $sub =~ s/^\Q$mod\E:://; |
518 | $sub =~ s/^\Q$mod\E:://; |
416 | $sub = "($sub)"; |
519 | $sub = "($sub)"; |
417 | } |
520 | } |
418 | |
521 | |
419 | "$mod:$_[0]{line}$sub>$_[0]{type}>" |
522 | "$mod:$_[0]{line}$sub>$_[0]{type}>" |
420 | . (AnyEvent::Debug::cb2str $_[0]{cb}) |
523 | . (AnyEvent::Debug::cb2str $_[0]{cb}) |
421 | }; |
|
|
422 | }, |
524 | }; |
|
|
525 | }, |
423 | fallback => 1; |
526 | fallback => 1, |
424 | } |
527 | ; |
425 | |
528 | |
426 | sub verbose { |
529 | sub verbose { |
427 | my ($self) = @_; |
530 | my ($self) = @_; |
428 | |
531 | |
429 | my $res = "type: $self->{type} watcher\n" |
532 | my $res = "type: $self->{type} watcher\n" |
430 | . "args: " . (join " ", %{ $self->{arg} }) . "\n" # TODO: decode fh? |
533 | . "args: " . (join " ", %{ $self->{arg} }) . "\n" # TODO: decode fh? |
431 | . "created: " . (AnyEvent::Debug::ft $self->{now}) . " ($self->{now})\n" |
534 | . "created: " . (AnyEvent::Log::ft $self->{now}) . " ($self->{now})\n" |
432 | . "file: $self->{file}\n" |
535 | . "file: ${ $self->{rfile} }\n" |
433 | . "line: $self->{line}\n" |
536 | . "line: $self->{line}\n" |
434 | . "subname: $self->{sub}\n" |
537 | . "subname: $self->{sub}\n" |
435 | . "context: $self->{cur}\n" |
538 | . "context: $self->{cur}\n" |
436 | . "cb: $self->{cb} (" . (AnyEvent::Debug::cb2str $self->{cb}) . ")\n" |
539 | . "cb: $self->{cb} (" . (AnyEvent::Debug::cb2str $self->{cb}) . ")\n" |
437 | . "invoked: $self->{called} times\n"; |
540 | . "invoked: $self->{called} times\n"; |
438 | |
541 | |
439 | if (exists $self->{bt}) { |
542 | if (exists $self->{bt}) { |
440 | $res .= "created$self->{bt}"; |
543 | $res .= "created\n$self->{bt}"; |
441 | } |
544 | } |
442 | |
545 | |
443 | if (exists $self->{error}) { |
546 | if (exists $self->{error}) { |
444 | $res .= "errors: " . @{$self->{error}} . "\n"; |
547 | $res .= "errors: " . @{$self->{error}} . "\n"; |
445 | |
548 | |
446 | $res .= "error: " . (AnyEvent::Debug::ft $_->[0]) . " ($_->[0]) $_->[1]\n" |
549 | $res .= "error: " . (AnyEvent::Log::ft $_->[0]) . " ($_->[0]) $_->[1]\n" |
447 | for @{$self->{error}}; |
550 | for @{$self->{error}}; |
448 | } |
551 | } |
449 | |
552 | |
450 | $res |
553 | $res |
451 | } |
554 | } |
452 | |
555 | |
453 | sub DESTROY { |
556 | sub DESTROY { |
454 | print AnyEvent::Debug::ft AE::now, " dstry $_[0]\n" if $TRACE_LEVEL; |
557 | $TRACE_LOGGER->("dstry $_[0]") if $TRACE_ENABLED; |
455 | |
558 | |
456 | delete $AnyEvent::Debug::Wrapped{Scalar::Util::refaddr $_[0]}; |
559 | delete $AnyEvent::Debug::Wrapped{Scalar::Util::refaddr $_[0]}; |
457 | } |
560 | } |
458 | |
561 | |
|
|
562 | package AnyEvent::Debug::Backtrace; |
|
|
563 | |
|
|
564 | use AnyEvent (); BEGIN { AnyEvent::common_sense } |
|
|
565 | |
|
|
566 | sub as_string { |
|
|
567 | my ($self) = @_; |
|
|
568 | |
|
|
569 | my @bt; |
|
|
570 | my $modlen; |
|
|
571 | |
|
|
572 | for (@$self) { |
|
|
573 | my ($rpath, $line, $sub) = @$_; |
|
|
574 | |
|
|
575 | $rpath = (AnyEvent::Debug::path2mod $$rpath) . " line $line"; |
|
|
576 | $modlen = length $rpath if $modlen < length $rpath; |
|
|
577 | |
|
|
578 | push @bt, [$rpath, $sub]; |
|
|
579 | } |
|
|
580 | |
|
|
581 | join "", |
|
|
582 | map { sprintf "%*s %s\n", -$modlen, $_->[0], $_->[1] } |
|
|
583 | @bt |
|
|
584 | } |
|
|
585 | |
|
|
586 | use overload |
|
|
587 | '""' => \&as_string, |
|
|
588 | fallback => 1, |
|
|
589 | ; |
|
|
590 | |
459 | 1; |
591 | 1; |
460 | |
|
|
461 | =back |
|
|
462 | |
592 | |
463 | =head1 AUTHOR |
593 | =head1 AUTHOR |
464 | |
594 | |
465 | Marc Lehmann <schmorp@schmorp.de> |
595 | Marc Lehmann <schmorp@schmorp.de> |
466 | http://home.schmorp.de/ |
596 | http://home.schmorp.de/ |