NAME

Carp::Capture - Capture callstacks for later presentation.

SYNOPSIS

    use Carp::Capture;

    my $cc = Carp::Capture->new();
    my $id = $cc->capture;
    ...
    print scalar $cc->stacktrace( $id );

DESCRIPTION

Perl's standard library module Carp provides the confess() function. Carp::confess() throws an exception containing a stacktrace. Stacktraces can be valuable debugging aids by describing where an exception happened and how the program got there. Carp::Capture does not throw exceptions. Instead, it separates the capturing of a callstack, from its presentation as a stacktrace.

A Carp::Capture object holds compact representations of captured callstacks. An integer identifier is returned each time capturing is requested. The identifier can be used to produce a stacktrace.

The need for callstack capturing came about during the development of a compiler application. Several types of user errors could not be detected at specification time (during API calls). Conflicts revealed themselves only after the specifications began to execute. By tagging each specification with a captured callstack we were able to pinpoint conflict origins, back in user code.

CALLSTACK-CAPTURING

Invoking capture() initiates stack traversal, using Perl's caller(). The subroutine, line number and filename components of each stackframe are harvested from the caller() result. The chain of harvested frames is stored in the Carp::Capture object. An integer identifier is returned for future reference of the captured callstack.

SELECTIVE-CAPTURE

The module attempts to be fast and to use minimal storage. That being said, each capture takes time and consumes space. Sections of code that are not under suspicion can disable capturing.

Carp::Capture provides a stack mechanism for enabling and disabling callstack captures. When the top of the stack indicates that capturing is disabled, calls to capture() return immediately. In this case, the returned identifier is a constant that indicates an 'uncaptured' callstack.

Capturing can be dynamically enabled and disabled with enable() and disable(). These methods push new values onto an internal stack. The revert() method pops the stack, restoring capture status to whatever it was before the most recent push.

Capture status is enabled when the object is first constructed.

STACKTRACE-RENDERING

A stacktrace, in string form, can be produced by invoking stacktrace() in a scalar context. The result is a string, with one line for each stackframe, similar to Carp::confess():

    <TAB><subr1>() called at <file1> line <line1>
    <TAB><subr2>() called at <file2> line <line2>
    ...

The data fields of subr, file and line are also available in raw form to support user-designed formatting. In list context, the stacktrace() method returns a list of HashRefs. Each HashRef has three keys: subr, file and line. There is one HashRef for each stackframe, with the first HashRef corresponding to the innermost subroutine call.

Attempting to produce a stacktrace from an 'uncaptured' identifier results in an empty string from stacktrace(), or an empty list from stacktrace().

DISAMBIGUATION

If capture() is invoked from within a loop then the callstack is the same for each iteration and hence the returned identifiers will all be the same as well.

    my $cc = Carp::Capture->new;
    my @ids;

    foreach my $letter (qw( a b c d )) {

        push @ids, $cc->capture;
    }

    # Prints "1 1 1 1"
    say "@ids";

capture() accepts an optional argument, called an annotation, to help distinguish one iteration from another.

A common tactic is to provide the loop iteration value as the annotation, but any scalar is acceptable.

If an annotation is provided to capture() then a copy of the annotation will be stored inside the Carp::Capture object. Returned identifiers will be always be unique when an annotation is provided.

Use retrieve_annotation() to fetch the stored annotation.

    my $cc = Carp::Capture->new;
    my @ids;

    foreach my $letter (qw( a b c d )) {

        push @ids, $cc->capture( $letter );
    }

    # Prints "2 3 4 5"
    say "@ids";

    # Prints "True"
    say "True"
        if $cc->stacktrace( $ids[0] ) eq
           $cc->stacktrace( $ids[1] );

    # Prints "c"
    say $cc->retrieve_annotation( $ids[2] );

EXPORTS

None.

METHODS

In the descriptions below, $cc refers to a Carp::Capture object returned by new(). $id refers to the identifier returned by capture(). Return value types are shown in angle brackets, like <void>.

new

 Usage:
    $cc = Carp::Capture->new();

new() creates the object that holds stackframe components, callstack representations and the capture-status stack. All attributes are private so there are no initializer arguments.

capture

 Usage:
    $id = $cc->capture;
    -or-
    $id = $cc->capture( $annotation );

The capture() method traverses the current callstack and harvests stackframe components along the way. The data is stored in the $cc object. The return value, an integer, identifies the internal representation of the captured callstack.

If capturing has been disabled (See SELECTIVE-CAPTURE) then the returned integer is a constant that represents an uncaptured stack.

The $annotation argument is optional. If $annotation is provided then $annotation is captured along with the callstack. The intent is to distinguish similar callstacks, such as might be captured from within a loop. See DISAMBIGUATION.

stacktrace

 Usage:
    <String> = $cc->stacktrace( $id );
    -or-
    <ListOfHashRefs> = $cc->stacktrace( $id );

In Scalar Context, stacktrace() returns a string representation of the callstack corresponding to the callstack identified by $id.

There is one line in the returned string for each stackframe in the captured callstack. Stack frames are ordered from the innermost outward, just like Carp::confess. Lines are of the form:

    <TAB><subroutine> called at <file> line <line>

In List Context, stacktrace() returns callstack components as a data structure. The callstack identified by $id, is returned as a list of HashRefs. There is one element in the list for each frame in the captured callstack. Stack frames are ordered from the innermost outward, just like Carp::confess. Each HashRef element has three keys: 'file', 'line' and 'subr'.

If $id identifies the 'uncaptured' callstack (See SELECTIVE-CAPTURING) then nothing was captured. In Scalar Context, the empty string will be returned. In List Context an empty list will be returned.

enable

 Usage:
    <void> $cc->enable;

Carp::Capture objects maintain an internal stack that controls whether capturing is performed. enable() pushes a new 'Enabled' value on top of the stack.

Invoking revert() restores capture status to whatever it was before the last push. Capturing starts out as 'Enabled'.

disable

 Usage:
    <void> $cc->disable;

Carp::Capture objects maintain an internal stack that controls whether capturing is performed. disable() pushes a new 'Disabled' value on top of the stack.

Invoking revert() restores capture status to whatever it was before the last push. Capturing starts out as 'Enabled'.

revert

 Usage:
    <void> $cc->revert;

Carp::Capture objects maintain an internal stack that controls whether capturing is performed. revert() pops and discards the current capture status. The previous status is restored.

retrieve_annotation

 Usage:
    <Scalar> = $cc->retrieve_annotation( $id );

If an annotation value was provided in the call to capture() then it can be retrieved with the same identifier used to generate stacktraces.

An exception is thrown if $id originated from a capture that was disabled, or from one in which no annotation was provided.

uncaptured

 Usage:
    <Integer> = $cc->uncaptured;

The magic value used to indicate that no callstack was captured, is returned by uncaptured(). Testing an identifier for equality with the Uncaptured value is a way to query the identifier's status.

    my $cc = Carp::Capture->new;
    my $uncaptured = $cc->uncaptured;

    # Prints "On"
    say $uncaptured == $cc->capture ? 'Off' : 'On';

    $cc->disable;

    # Prints "Off"
    say $uncaptured == $cc->capture ? 'Off' : 'On';

The Uncaptured value might also be useful as a default in a data structure. Capturing can be gated with a conditional that overwrites the default when active.

BUGS AND LIMITATIONS

Please report any bugs or feature requests to bug-carp-capture at rt.cpan.org, or through the web interface at

http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Carp-Capture.

I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.

SUPPORT

You can find documentation for this module with the perldoc command.

    perldoc Carp::Capture

You can also look for information at:

DEPENDENCIES

All of these are available from CPAN:

 Moose
 Carp::Proxy

SEE ALSO

  • See 'perldoc -f caller' for information on accessing the current callstack from Perl.

  • See 'perldoc Carp' for documentation on confess()

LICENSE AND COPYRIGHT

Copyright 2014-2015 Paul Liebert.

This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License.

See http://dev.perl.org/licenses/ for more information.