Skip to content

Commit

Permalink
optionally generate a stack trace
Browse files Browse the repository at this point in the history
  • Loading branch information
plicease committed Oct 6, 2022
1 parent 7d23439 commit 0a85631
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 1 deletion.
1 change: 1 addition & 0 deletions Changes
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Revision history for {{$dist->name}}
- Documentation improvements (gh#5, gh#8)
- Will now detect if Carp::Always is enabled, and display an appropriately
verbose diagnostic when stringified if so (gh#3, gh#9)
- Added trace method (gh#2, gh#10)

0.01 2022-10-05 07:42:34 -0600
- initial version
Expand Down
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ handles determining the location of where the exception was thrown and will stri
in a way to look like a regular Perl string exception with the filename and line number
you would expect.

A stack trace can be generated, either on a per-subclass basis, or globally via an
environment variable. This is not done by default due to the overhead involved.
See the [trace method](#trace) for details.

This class will attempt to detect if [Carp::Always](https://metacpan.org/pod/Carp::Always) is running and produce a long message
when stringified, as it already does for regular string exceptions. By default it will
**only** do this if [Carp::Always](https://metacpan.org/pod/Carp::Always) is running when this module is loaded. Since
Expand Down Expand Up @@ -192,11 +196,39 @@ my $code = $ex->code;

The integer error code.

## trace

```perl
my $trace = $ex->trace;
```

This will return a [Devel::StackTrace](https://metacpan.org/pod/Devel::StackTrace) trace, if it was recorded when the exception was
thrown. Generally the trace will only be generated if `EXCEPTION_FFI_ERROR_CODE_STACK_TRACE`
set to a true value. Individual subclasses may also choose to always generate a stack
trace.

## get\_stack\_trace

```perl
my $trace = $ex->get_stack_trace;
```

This is the method that is called internally to generate a stack trace. By default this
is only done if `EXCEPTION_FFI_ERROR_CODE_STACK_TRACE` is set to true. If you want
a stack trace to **always** be generated, you can override this method in your subclass.

# CAVEATS

The [Carp::Always](https://metacpan.org/pod/Carp::Always) detection is pretty solid, but if [Carp::Always](https://metacpan.org/pod/Carp::Always) is off when the
exception is thrown but on when it is stringified then strange things might happen.

# ENVIRONMENT

- `EXCEPTION_FFI_ERROR_CODE_STACK_TRACE`

If this environment variable is set to a true value, then a stack trace will be generated
and attached to all exceptions managed by [Exception::FFI::ErrorCode](https://metacpan.org/pod/Exception::FFI::ErrorCode).

# SEE ALSO

- [FFI::Platypus](https://metacpan.org/pod/FFI::Platypus)
Expand Down
51 changes: 50 additions & 1 deletion lib/Exception/FFI/ErrorCode.pm
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ handles determining the location of where the exception was thrown and will stri
in a way to look like a regular Perl string exception with the filename and line number
you would expect.
A stack trace can be generated, either on a per-subclass basis, or globally via an
environment variable. This is not done by default due to the overhead involved.
See the L<trace method|/trace> for details.
This class will attempt to detect if L<Carp::Always> is running and produce a long message
when stringified, as it already does for regular string exceptions. By default it will
B<only> do this if L<Carp::Always> is running when this module is loaded. Since
Expand Down Expand Up @@ -178,11 +182,39 @@ The line number where the exception happened.
The integer error code.
=head2 trace
my $trace = $ex->trace;
This will return a L<Devel::StackTrace> trace, if it was recorded when the exception was
thrown. Generally the trace will only be generated if C<EXCEPTION_FFI_ERROR_CODE_STACK_TRACE>
set to a true value. Individual subclasses may also choose to always generate a stack
trace.
=head2 get_stack_trace
my $trace = $ex->get_stack_trace;
This is the method that is called internally to generate a stack trace. By default this
is only done if C<EXCEPTION_FFI_ERROR_CODE_STACK_TRACE> is set to true. If you want
a stack trace to B<always> be generated, you can override this method in your subclass.
=head1 CAVEATS
The L<Carp::Always> detection is pretty solid, but if L<Carp::Always> is off when the
exception is thrown but on when it is stringified then strange things might happen.
=head1 ENVIRONMENT
=over 4
=item C<EXCEPTION_FFI_ERROR_CODE_STACK_TRACE>
If this environment variable is set to a true value, then a stack trace will be generated
and attached to all exceptions managed by L<Exception::FFI::ErrorCode>.
=back
=head1 SEE ALSO
=over 4
Expand Down Expand Up @@ -252,7 +284,7 @@ exception is thrown but on when it is stringified then strange things might happ

sub _carp_always;

use Class::Tiny qw( package filename line code _longmess );
use Class::Tiny qw( package filename line code trace _longmess );
use Ref::Util qw( is_blessed_ref );
use overload
'""' => sub ($self,@) {
Expand Down Expand Up @@ -288,10 +320,27 @@ exception is thrown but on when it is stringified then strange things might happ
line => $line,
);
}
my $trace = $self->get_stack_trace;
$self->trace($trace) if $trace;
$self->_longmess(Carp::longmess($self->strerror)) if _carp_always;
die $self;
}

sub get_stack_trace ($)
{
if($ENV{EXCEPTION_FFI_ERROR_CODE_STACK_TRACE})
{
require Devel::StackTrace;
return Devel::StackTrace->new(
ignore_package => 'Exception::FFI::ErrorCode::Base',
);
}
else
{
return undef;
}
}

sub strerror ($self)
{
my $code = $self->code;
Expand Down
1 change: 1 addition & 0 deletions t/00_diag.t
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ my $post_diag;
$modules{$_} = $_ for qw(
Carp
Class::Tiny
Devel::StackTrace
ExtUtils::MakeMaker
Ref::Util
Sub::Identify
Expand Down
22 changes: 22 additions & 0 deletions t/exception_ffi_errorcode.t
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use Test2::V0 -no_srand => 1;

delete $ENV{EXCEPTION_FFI_ERROR_CODE_STACK_TRACE};

subtest 'basic' => sub {

local *Exception::FFI::ErrorCode::Base::_carp_always = sub { 0 };
Expand Down Expand Up @@ -38,6 +40,7 @@ subtest 'basic' => sub {
call strerror => 'human readable';
call as_string => "human readable at @{[ __FILE__ ]} line $line.";
call sub { "$_[0]" } => "human readable at @{[ __FILE__ ]} line $line.\n";
call trace => U();

},
'throws code 1 ok';
Expand Down Expand Up @@ -80,6 +83,25 @@ subtest 'basic' => sub {
},
'throws code 3 ok, fallback on diagnostic with integer code';

my $ex4 = dies {
local $ENV{EXCEPTION_FFI_ERROR_CODE_STACK_TRACE} = 1;
$line = __LINE__; Ex1->throw( code => 1 );
};

is
$ex4,
object {
call trace => object {
call [ isa => 'Devel::StackTrace' ] => T();
call next_frame => object {
call [ isa => 'Devel::StackTrace::Frame' ] => T();
call package => 'main';
call line => $line;
};
};
},
'stack trace on request';

};

subtest 'exceptions' => sub {
Expand Down

0 comments on commit 0a85631

Please sign in to comment.