… | |
… | |
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 | use POSIX (); |
31 | |
32 | |
32 | use AnyEvent (); BEGIN { AnyEvent::common_sense } |
33 | use AnyEvent (); BEGIN { AnyEvent::common_sense } |
33 | use AnyEvent::Util (); |
34 | use AnyEvent::Util (); |
… | |
… | |
227 | |
228 | |
228 | sub wrap(;$) { |
229 | sub wrap(;$) { |
229 | my $PREV_LEVEL = $WRAP_LEVEL; |
230 | my $PREV_LEVEL = $WRAP_LEVEL; |
230 | $WRAP_LEVEL = @_ ? 0+shift : $WRAP_LEVEL ? 0 : 1; |
231 | $WRAP_LEVEL = @_ ? 0+shift : $WRAP_LEVEL ? 0 : 1; |
231 | |
232 | |
232 | if (defined $AnyEvent::MODEL) { |
233 | if ($AnyEvent::MODEL) { |
233 | unless (defined $PREV_LEVEL) { |
|
|
234 | AnyEvent::Debug::Wrapped::_init (); |
|
|
235 | } |
|
|
236 | |
|
|
237 | if ($WRAP_LEVEL && !$PREV_LEVEL) { |
234 | if ($WRAP_LEVEL && !$PREV_LEVEL) { |
238 | AnyEvent::_isa_hook 1 => "AnyEvent::Debug::Wrap", 1; |
235 | AnyEvent::_isa_hook 1 => "AnyEvent::Debug::Wrap", 1; |
239 | AnyEvent::Debug::Wrap::_reset (); |
236 | AnyEvent::Debug::Wrap::_reset (); |
240 | } elsif (!$WRAP_LEVEL && $PREV_LEVEL) { |
237 | } elsif (!$WRAP_LEVEL && $PREV_LEVEL) { |
241 | AnyEvent::_isa_hook 0 => undef; |
238 | AnyEvent::_isa_hook 0 => undef; |
… | |
… | |
289 | |
286 | |
290 | Using various gambits, tries to convert a callback (e.g. a code reference) |
287 | Using various gambits, tries to convert a callback (e.g. a code reference) |
291 | into a more useful string. |
288 | into a more useful string. |
292 | |
289 | |
293 | Very useful if you debug a program and have some callback, but you want to |
290 | 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. |
291 | know where in the program the callback is actually defined. |
295 | |
292 | |
296 | =cut |
293 | =cut |
297 | |
294 | |
298 | sub cb2str($) { |
295 | sub cb2str($) { |
299 | my $cb = shift; |
296 | my $cb = shift; |
… | |
… | |
310 | |
307 | |
311 | return (AnyEvent::Debug::path2mod $gv->FILE) . ":" . $gv->LINE |
308 | return (AnyEvent::Debug::path2mod $gv->FILE) . ":" . $gv->LINE |
312 | if $gv->NAME eq "__ANON__"; |
309 | if $gv->NAME eq "__ANON__"; |
313 | |
310 | |
314 | return $gv->STASH->NAME . "::" . $gv->NAME; |
311 | return $gv->STASH->NAME . "::" . $gv->NAME; |
|
|
312 | } |
|
|
313 | |
|
|
314 | sub sv2str($) { |
|
|
315 | if (ref $_[0]) { |
|
|
316 | if (ref $_[0] eq "CODE") { |
|
|
317 | return "$_[0]=" . cb2str $_[0]; |
|
|
318 | } else { |
|
|
319 | return "$_[0]"; |
|
|
320 | } |
|
|
321 | } else { |
|
|
322 | for ("\'$_[0]\'") { # make copy |
|
|
323 | substr $_, $Carp::MaxArgLen, length, "'..." |
|
|
324 | if length > $Carp::MaxArgLen; |
|
|
325 | return $_; |
|
|
326 | } |
|
|
327 | } |
|
|
328 | } |
|
|
329 | |
|
|
330 | =item AnyEvent::Debug::backtrace |
|
|
331 | |
|
|
332 | Creates a backtrace (actually an AnyEvent::Debug::Backtrace object |
|
|
333 | that you can stringify), not unlike the Carp module would. Unlike the |
|
|
334 | Carp module it resolves some references (euch as callbacks) to more |
|
|
335 | user-friendly strings, has a more succinct output format and most |
|
|
336 | importantly: doesn't leak memory like hell. |
|
|
337 | |
|
|
338 | The reason it creates an object is to save time, as formatting can be |
|
|
339 | done at a later time. Still, creating a backtrace is a relatively slow |
|
|
340 | operation. |
|
|
341 | |
|
|
342 | =cut |
|
|
343 | |
|
|
344 | our %PATHCACHE; # purely to save memory |
|
|
345 | |
|
|
346 | sub backtrace() { |
|
|
347 | my (@bt, $w, @c); |
|
|
348 | my ($modlen, $sub); |
|
|
349 | |
|
|
350 | for (;;) { |
|
|
351 | # 0 1 2 3 4 5 6 7 8 9 10 |
|
|
352 | # ($package, $filename, $line, $subroutine, $hasargs, $wantarray, $evaltext, $is_require, $hints, $bitmask, $hinthash) |
|
|
353 | package DB; |
|
|
354 | @c = caller $w++ |
|
|
355 | or last; |
|
|
356 | package AnyEvent::Debug; # no block for speed reasons |
|
|
357 | |
|
|
358 | if ($c[7]) { |
|
|
359 | $sub = "require $c[6]"; |
|
|
360 | } elsif (defined $c[6]) { |
|
|
361 | $sub = "eval \"\""; |
|
|
362 | } else { |
|
|
363 | $sub = ($c[4] ? "" : "&") . $c[3]; |
|
|
364 | |
|
|
365 | $sub .= "(" |
|
|
366 | . (join ",", |
|
|
367 | map sv2str $DB::args[$_], |
|
|
368 | 0 .. (@DB::args < $Carp::MaxArgNums ? @DB::args : $Carp::MaxArgNums) - 1) |
|
|
369 | . ")" |
|
|
370 | if $c[4]; |
|
|
371 | } |
|
|
372 | |
|
|
373 | push @bt, [\($PATHCACHE{$c[1]} ||= $c[1]), $c[2], $sub]; |
|
|
374 | } |
|
|
375 | |
|
|
376 | @DB::args = (); |
|
|
377 | |
|
|
378 | bless \@bt, "AnyEvent::Debug::Backtrace" |
315 | } |
379 | } |
316 | |
380 | |
317 | # Format Time, not public - yet? |
381 | # Format Time, not public - yet? |
318 | sub ft($) { |
382 | sub ft($) { |
319 | my $t = shift; |
383 | my $t = shift; |
… | |
… | |
343 | $w = 0; |
407 | $w = 0; |
344 | do { |
408 | do { |
345 | ($pkg, $file, $line) = caller $w++; |
409 | ($pkg, $file, $line) = caller $w++; |
346 | } while $pkg =~ /^(?:AE|AnyEvent::(?:Socket|Handle|Util|Debug|Strict|Base|CondVar|CondVar::Base|Impl::.*))$/; |
410 | } while $pkg =~ /^(?:AE|AnyEvent::(?:Socket|Handle|Util|Debug|Strict|Base|CondVar|CondVar::Base|Impl::.*))$/; |
347 | |
411 | |
348 | $sub = (caller $w++)[3]; |
412 | $sub = (caller $w)[3]; |
349 | |
413 | |
350 | my $cb = $arg{cb}; |
414 | my $cb = $arg{cb}; |
351 | $arg{cb} = sub { |
415 | $arg{cb} = sub { |
352 | ++$w->{called}; |
416 | ++$w->{called}; |
353 | |
417 | |
… | |
… | |
355 | unless $TRACE_LEVEL; |
419 | unless $TRACE_LEVEL; |
356 | |
420 | |
357 | local $TRACE_CUR = "$w"; |
421 | local $TRACE_CUR = "$w"; |
358 | print AnyEvent::Debug::ft AE::now, " enter $TRACE_CUR\n" if $TRACE_LEVEL; |
422 | print AnyEvent::Debug::ft AE::now, " enter $TRACE_CUR\n" if $TRACE_LEVEL; |
359 | eval { |
423 | eval { |
360 | local $SIG{__DIE__} = sub { die Carp::longmess "$_[0]Backtrace starting" }; |
424 | local $SIG{__DIE__} = sub { die $_[0] . AnyEvent::Debug::backtrace }; |
361 | &$cb; |
425 | &$cb; |
362 | }; |
426 | }; |
363 | if ($@) { |
427 | if ($@) { |
364 | push @{ $w->{error} }, [AE::now, $@] |
428 | push @{ $w->{error} }, [AE::now, $@] |
365 | if @{ $w->{error} } < 10; |
429 | if @{ $w->{error} } < 10; |
… | |
… | |
381 | called => 0, |
445 | called => 0, |
382 | }, "AnyEvent::Debug::Wrapped"; |
446 | }, "AnyEvent::Debug::Wrapped"; |
383 | |
447 | |
384 | delete $arg{cb}; |
448 | delete $arg{cb}; |
385 | |
449 | |
386 | # backtraces leak like hell |
450 | $self->{bt} = AnyEvent::Debug::backtrace |
387 | $self->{bt} = Carp::longmess "" |
|
|
388 | if $WRAP_LEVEL >= 2; |
451 | if $WRAP_LEVEL >= 2; |
389 | |
452 | |
390 | Scalar::Util::weaken ($w = $self); |
453 | Scalar::Util::weaken ($w = $self); |
391 | Scalar::Util::weaken ($AnyEvent::Debug::Wrapped{Scalar::Util::refaddr $self} = $self); |
454 | Scalar::Util::weaken ($AnyEvent::Debug::Wrapped{Scalar::Util::refaddr $self} = $self); |
392 | |
455 | |
… | |
… | |
399 | |
462 | |
400 | package AnyEvent::Debug::Wrapped; |
463 | package AnyEvent::Debug::Wrapped; |
401 | |
464 | |
402 | use AnyEvent (); BEGIN { AnyEvent::common_sense } |
465 | use AnyEvent (); BEGIN { AnyEvent::common_sense } |
403 | |
466 | |
404 | sub _init { |
467 | use overload |
405 | require overload; |
|
|
406 | import overload |
|
|
407 | '""' => sub { |
468 | '""' => sub { |
408 | $_[0]{str} ||= do { |
469 | $_[0]{str} ||= do { |
409 | my ($pkg, $line) = @{ $_[0]{caller} }; |
470 | my ($pkg, $line) = @{ $_[0]{caller} }; |
410 | |
471 | |
411 | my $mod = AnyEvent::Debug::path2mod $_[0]{file}; |
472 | my $mod = AnyEvent::Debug::path2mod $_[0]{file}; |
412 | my $sub = $_[0]{sub}; |
473 | my $sub = $_[0]{sub}; |
413 | |
474 | |
414 | if (defined $sub) { |
475 | if (defined $sub) { |
415 | $sub =~ s/^\Q$mod\E:://; |
476 | $sub =~ s/^\Q$mod\E:://; |
416 | $sub = "($sub)"; |
477 | $sub = "($sub)"; |
417 | } |
478 | } |
418 | |
479 | |
419 | "$mod:$_[0]{line}$sub>$_[0]{type}>" |
480 | "$mod:$_[0]{line}$sub>$_[0]{type}>" |
420 | . (AnyEvent::Debug::cb2str $_[0]{cb}) |
481 | . (AnyEvent::Debug::cb2str $_[0]{cb}) |
421 | }; |
|
|
422 | }, |
482 | }; |
|
|
483 | }, |
423 | fallback => 1; |
484 | fallback => 1, |
424 | } |
485 | ; |
425 | |
486 | |
426 | sub verbose { |
487 | sub verbose { |
427 | my ($self) = @_; |
488 | my ($self) = @_; |
428 | |
489 | |
429 | my $res = "type: $self->{type} watcher\n" |
490 | my $res = "type: $self->{type} watcher\n" |
… | |
… | |
454 | print AnyEvent::Debug::ft AE::now, " dstry $_[0]\n" if $TRACE_LEVEL; |
515 | print AnyEvent::Debug::ft AE::now, " dstry $_[0]\n" if $TRACE_LEVEL; |
455 | |
516 | |
456 | delete $AnyEvent::Debug::Wrapped{Scalar::Util::refaddr $_[0]}; |
517 | delete $AnyEvent::Debug::Wrapped{Scalar::Util::refaddr $_[0]}; |
457 | } |
518 | } |
458 | |
519 | |
|
|
520 | package AnyEvent::Debug::Backtrace; |
|
|
521 | |
|
|
522 | use AnyEvent (); BEGIN { AnyEvent::common_sense } |
|
|
523 | |
|
|
524 | sub as_string { |
|
|
525 | my ($self) = @_; |
|
|
526 | |
|
|
527 | my @bt; |
|
|
528 | my $modlen; |
|
|
529 | |
|
|
530 | for (@$self) { |
|
|
531 | my ($rpath, $line, $sub) = @$_; |
|
|
532 | |
|
|
533 | $rpath = (AnyEvent::Debug::path2mod $$rpath) . " line $line"; |
|
|
534 | $modlen = length $rpath if $modlen < length $rpath; |
|
|
535 | |
|
|
536 | push @bt, [$rpath, $sub]; |
|
|
537 | } |
|
|
538 | |
|
|
539 | join "", |
|
|
540 | map { sprintf "%*s %s\n", -$modlen, $_->[0], $_->[1] } |
|
|
541 | @bt |
|
|
542 | } |
|
|
543 | |
|
|
544 | use overload |
|
|
545 | '""' => \&as_string, |
|
|
546 | fallback => 1, |
|
|
547 | ; |
|
|
548 | |
459 | 1; |
549 | 1; |
460 | |
550 | |
461 | =back |
551 | =back |
462 | |
552 | |
463 | =head1 AUTHOR |
553 | =head1 AUTHOR |