1#/**
    2# A single shared memory ring buffer for diagnosis/debug of Perl scripts.
    3# Uses IPC::Mmap to create/access/manage a memory mapped file (or namespace
    4# on Win32) as a ring buffer structure that can be used by "applications
    5# under test" that use an appropriate debug module (e.g., Devel::STrace)
    6# along with an external monitoring application
    7# (e.g., Devel::STrace::Monitor).
    8# <p>
    9# Note that significant functionality is written in XS/C in order to minimize
   10# tracing/debugging overhead.
   11# <p>
   12# Permission is granted to use this software under the same terms as Perl itself.
   13# Refer to the <a href='http://perldoc.perl.org/perlartistic.html'>Perl Artistic License</a>
   14# for details.
   15#
   16# @author D. Arnold
   17# @since 2006-05-01
   18# @self $self
   19#*/
   20package    Devel::RingBuffer::Ring;
   21
   22use threads;
   23use threads::shared;
   24use Time::HiRes qw(time);
   25use Exporter;
   26
   27BEGIN {
   28our @ISA = qw(Exporter);
   29#
   30#    consts for member indexes
   31#
   32use constant RINGBUF_RING_BUFFER => 0;
   33use constant RINGBUF_RING_SLOTS => 1;
   34#
   35# !!!+++!+!+!+!+!+!+!+!+!+!+!+
   36#    !!!DON'T CHANGE THIS INDEX UNLESS YOU CHANGE THE XS CODE TOO!!!!
   37# !!!+++!+!+!+!+!+!+!+!+!+!+!+
   38#
   39use constant RINGBUF_RING_ADDR => 2;
   40
   41use constant RINGBUF_RING_PID => 3;
   42use constant RINGBUF_RING_TID => 4;
   43use constant RINGBUF_RING_SLOT => 5;
   44use constant RINGBUF_RING_DEPTH => 6;
   45use constant RINGBUF_RING_INDEX => 7;
   46use constant RINGBUF_RING_MSGSZ => 8;
   47use constant RINGBUF_RING_HDRSZ => 9;
   48use constant RINGBUF_BASE_ADDR => 10;
   49
   50use constant RINGBUF_RING_WAIT => 0.3;
   51
   52our @EXPORT = ();
   53our @EXPORT_OK = ();
   54our %EXPORT_TAGS = (
   55    ring_members => [
   56    qw/RINGBUF_RING_BUFFER RINGBUF_RING_SLOTS RINGBUF_RING_ADDR
   57        RINGBUF_RING_PID RINGBUF_RING_TID RINGBUF_RING_SLOT RINGBUF_RING_DEPTH
   58        RINGBUF_RING_INDEX RINGBUF_RING_MSGSZ RINGBUF_RING_HDRSZ
   59        RINGBUF_BASE_ADDR/
   60    ],
   61);
   62
   63Exporter::export_tags(keys %EXPORT_TAGS);
   64
   65};
   66
   67use Devel::RingBuffer;    # to bootstrap
   68use Devel::RingBuffer qw(:ringbuffer_consts);
   69
   70use strict;
   71use warnings;
   72
   73our $VERSION = '1.01';
   74#/**
   75# Constructor. Allocates a ring buffer, and initializes its header
   76# and control variables.
   77#
   78# @param $ringbuffer    the Devel::RingBuffer object
   79# @param $ringaddr        the base address of this ring
   80# @param $baseaddr        base address of the complete ring buffer structure
   81# @param $ringnum        the number (i.e., positional index) of this ring
   82# @param $slots            number of slots per ring
   83# @param $msgareasz        size of the per-thread message area
   84#
   85# @return Devel::RingBuffer::Ring object on success; undef on failure
   86#*/
   87sub new {
   88    my $class = shift;
   89    my $ringbuffer = shift;
   90    my $ringaddr = shift;
   91    my $baseaddr = shift;
   92    my $ringnum = shift;
   93    my $slots = shift;
   94    my $msgareasz = shift;
   95#
   96#    @_ now only has DB control variables
   97#
   98
   99    my $tid = threads->self->tid;
  100    _init_ring($ringaddr, $$, $tid, $baseaddr, @_);
  101
  102    return bless [
  103        $ringbuffer,
  104        $slots,
  105        $ringaddr,
  106        $$,
  107        $tid,
  108        -1,
  109        0,
  110        $ringnum,
  111        $msgareasz,
  112        RINGBUF_BUFHDR_SZ + $msgareasz,
  113        $baseaddr
  114    ], $class;
  115}
  116#/**
  117# Constructor. Allocates a ring buffer, and initializes its header
  118# and control variables. Called when the AUT object (e.g., DB)
  119# is CLONE'd, so that a new ring can be assigned to the new thread
  120#
  121# @return    the Devel::RingBuffer::Ring object
  122#*/
  123sub clone {
  124    my $self = shift;
  125#
  126#    @_ now only has DB control variables
  127#
  128
  129    my $tid = threads->self->tid;
  130    my ($ringnum, $ringaddr) = $self->[RINGBUF_RING_BUFFER]->reallocate();
  131    return undef unless defined($ringnum);
  132    $self->[RINGBUF_RING_ADDR] = $ringaddr;
  133    $self->[RINGBUF_RING_INDEX] = $ringnum;
  134    _init_ring($ringaddr, $$, $tid, $self->[RINGBUF_BASE_ADDR], @_);
  135    return $self;
  136}
  137#/**
  138# Constructor. Opens an existing ring buffer for read-only access.
  139#
  140# @param $ringbuffer    the Devel::RingBuffer object
  141# @param $ringaddr        the base address of this ring
  142# @param $baseaddr        base address of the complete ring buffer structure
  143# @param $ringnum        the number (i.e., positional index) of this ring
  144# @param $slots            number of slots per ring
  145# @param $msgareasz        size of the per-thread message area
  146#
  147# @return Devel::RingBuffer::Ring object on success; undef on failure
  148#*/
  149sub open {
  150    my ($class, $ringbuffer, $ringaddr, $baseaddr, $ringnum, $slots, $msgareasz) = @_;
  151
  152    my ($pid, $tid, $slot, $depth) = _get_header($ringaddr);
  153
  154    return bless [
  155        $ringbuffer,
  156        $slots,
  157        $ringaddr,
  158        $pid,
  159        $tid,
  160        $slot,
  161        $depth,
  162        $ringnum,
  163        $msgareasz,
  164        RINGBUF_BUFHDR_SZ + $msgareasz,
  165        $baseaddr
  166    ], $class;
  167}
  168#/**
  169# Update the current slot. Only updates linenumber and timestamp.
  170# May be called as either object or class method; in the latter case,
  171# caller must supply the ring's base address <i>(used within DB::DB()
  172# to optimize access speed)</i>
  173#
  174# @param $address        <b><i>class method calls only</i></b>: base address of the ring
  175# @param $linenumber    linenumber of current statement
  176#
  177# @return the Devel::RingBuffer::Ring object
  178#*/
  179# @xs updateSlot
  180
  181#/**
  182# @xs nextSlot
  183# Allocate and initialize the next slot. If the stack depth is
  184# greater than the configured number of slots, the oldest
  185# in-use slot is used, overwriting its current contents.
  186# May be called as either object or class method; in the latter case,
  187# caller must supply the ring's base address <i>(used within DB::sub()
  188# to optimize access speed)</i>
  189# <p>
  190# <i>Note: In future, this should return prior contents so we can restore
  191#    on de-wrapping.</i>
  192#
  193# @param $address        <b><i>class method calls only</i></b>: base address of the ring
  194# @param $entry        subroutine name (from $DB::sub)
  195#
  196# @return the stack depth after the slot is allocated.
  197#*/
  198# @xs nextSlot
  199
  200#/**
  201# @xs freeSlot
  202# Free the current slot and invalidates its contents.
  203# May be called as either object or class method; in the latter case,
  204# caller must supply the ring's base address <i>(used within DB::sub()
  205# to optimize access speed)</i>
  206#
  207# @param $address        <b><i>class method calls only</i></b>: base address of the ring
  208#
  209# @return the stack depth after the slot is freed.
  210#*/
  211# @xs freeSlot
  212
  213#/**
  214# Get the ring header values. Header fields returned are
  215# <p>
  216# <ol>
  217# <li>pid - PID of the ring owner
  218# <li>tid - TID of the ring owner
  219# <li>currSlot - current top slot
  220# <li>depth - current stack depth
  221# </ol>
  222#
  223# @return list of header values
  224#*/
  225sub getHeader {
  226    return _get_header($_[0]->[RINGBUF_RING_ADDR]);
  227}
  228
  229#/**
  230# Get the ring number (i.e., positional index)
  231#
  232# @return the ring number
  233#*/
  234sub getIndex { return $_[0]->[RINGBUF_RING_INDEX]; }
  235
  236#/**
  237# Get the ring base address
  238#
  239# @return the ring base address
  240#*/
  241sub getAddress { return $_[0]->[RINGBUF_RING_ADDR]; }
  242
  243#/**
  244# Get the contents of the specified slot.
  245#
  246# @param $slot the number of the slot to return
  247#
  248# @return the line number, timestamp, and subroutine name from the slot
  249#*/
  250sub getSlot {
  251    my ($self, $slot) = @_;
  252
  253    return (-1, 0, '(Invalid slot; ring has been wrapped)')
  254        if ($slot < 0) || ($slot > $self->[RINGBUF_RING_SLOTS]);
  255
  256    return _get_slot($self->[RINGBUF_RING_ADDR], $slot);
  257}
  258#/**
  259# Get the ring's trace flag
  260#
  261# @return the ring's trace flag
  262#*/
  263sub getTrace {
  264    return _get_trace($_[0]->[RINGBUF_RING_ADDR]);
  265}
  266
  267#/**
  268# Set the ring's trace flag
  269#
  270# @param $trace the value to set
  271#
  272# @return the prior value of the ring's trace flag
  273#*/
  274sub setTrace {
  275    return _set_trace($_[0]->[RINGBUF_RING_ADDR], $_[1]);
  276}
  277
  278#/**
  279# Get the ring's signal flag
  280#
  281# @return the ring's signal flag
  282#*/
  283sub getSignal {
  284    return _get_single($_[0]->[RINGBUF_RING_ADDR]);
  285}
  286
  287#/**
  288# Set the ring's signal flag
  289#
  290# @param $signal the value to set
  291#
  292# @return the prior value of the ring's signal flag
  293#*/
  294sub setSignal {
  295    return _set_signal($_[0]->[RINGBUF_RING_ADDR], $_[1]);
  296}
  297
  298#/**
  299# Post a command to the ring's command/message area
  300#
  301# @param $command the command value to set; must be no more than 3 bytes
  302# @param $msg    an optional message associated with the command; max length
  303#                is determined by configuration settings
  304#
  305# @return the ring object
  306#*/
  307sub postCommand { return postCmdEvent(@_, 1); }
  308
  309#/**
  310# Post a response to the ring's command/message area
  311#
  312# @param $response the response value to set; must be no more than 3 bytes
  313# @param $msg    an optional message associated with the response; max length
  314#                is determined by configuration settings
  315#
  316# @return the ring object
  317#*/
  318sub postResponse { return postCmdEvent(@_, 0); }
  319
  320sub postCmdEvent {
  321    my ($self, $cmd, $msg, $state) = @_;
  322    _post_cmd_msg($self->[RINGBUF_RING_ADDR], $cmd, $msg, $state);
  323
  324    return $self;
  325}
  326
  327#/**
  328# Wait indefinitely for a command to be posted to the ring's command/message area.
  329#
  330# @return the posted command and message
  331#*/
  332sub waitForCommand {
  333    return waitForCmdEvent(@_, 1);
  334}
  335
  336#/**
  337# Wait indefinitely for a response to be posted to the ring's command/message area.
  338#
  339# @return the posted response and message
  340#*/
  341sub waitForResponse {
  342    return waitForCmdEvent(@_, 0);
  343}
  344
  345sub waitForCmdEvent {
  346    my ($cmd, $msg);
  347    while (1) {
  348        ($cmd, $msg) = _check_for_cmd_msg($_[0]->[RINGBUF_RING_ADDR], $_[1]);
  349        last if defined($cmd);
  350        sleep RINGBUF_RING_WAIT;
  351    }
  352    return ($cmd, $msg);
  353}
  354
  355#/**
  356# Test if a command is available in the ring's command/message area.
  357#
  358# @return if available, the posted command and message; otherwise an empty list
  359#*/
  360sub checkCommand {
  361    return checkCmdEvent(@_, 1);
  362}
  363
  364#/**
  365# Test if a response is available in the ring's command/message area.
  366#
  367# @return if available, the posted response and message; otherwise an empty list
  368#*/
  369sub checkResponse {
  370    return checkCmdEvent(@_, 0);
  371}
  372
  373sub checkCmdEvent {
  374    return _check_for_cmd_msg($_[0]->[RINGBUF_RING_ADDR], $_[1]);
  375}
  376#/**
  377# Allocate and initialize a watchlist entry. Sets the watch expression.
  378#
  379# @param $expr    expression to set
  380#
  381# @return allocated watchlist entry number on success; undef on failure
  382#*/
  383sub addWatch {
  384    return _add_watch_expr($_[0]->[RINGBUF_RING_ADDR], $_[1]);
  385}
  386
  387#/**
  388# Free a watchlist entry.
  389#
  390# @param $watch    the watchlist entry number to free
  391#
  392#*/
  393sub freeWatch {
  394    return _free_watch_expr($_[0]->[RINGBUF_RING_ADDR], $_[1]);
  395}
  396
  397#/**
  398# Get a watchlist expression entry.
  399#
  400# @param $watch    the watchlist entry number to get
  401#
  402# @return the expression in the watchlist entry, if any; undef otherwise
  403#*/
  404sub getWatchExpr {
  405    return $_[0]->[RINGBUF_RING_BUFFER] ?
  406        _get_watch_expr($_[0]->[RINGBUF_RING_ADDR], $_[1]) :
  407        undef;
  408}
  409
  410#/**
  411# Set a watchlist result entry.
  412#
  413# @param $watch    the watchlist entry number to set
  414# @param $result the result of the expression evaluation
  415# @param $error error string if expression evaluation fails
  416#*/
  417sub setWatchResult {
  418    my ($self, $watch, $result, $error) = @_;
  419
  420    return $self->[RINGBUF_RING_BUFFER] ?
  421        _set_watch_result($self->[RINGBUF_RING_ADDR], $watch, $result, $error) :
  422        undef;
  423}
  424#/**
  425# Get a watchlist expression entry. If the length of the result exceeds
  426# the configured message size, the result is truncated. If the result is
  427# undef, the length will zero, and both the result and error will be undef.
  428# If the evaluation caused a failure, the length indicates the length of
  429# the error string, and result will be undef.
  430#
  431# @param $watch    the watchlist entry number to get
  432#
  433# @return the complete length of the result, the (possibly truncated) result value,
  434#            and the (possibly truncated) error message (if the evaluation failed).
  435#*/
  436sub getWatchResult {
  437    return $_[0]->[RINGBUF_RING_BUFFER] ?
  438        _get_watch_result($_[0]->[RINGBUF_RING_ADDR], $_[1]) :
  439        (undef, undef, undef);
  440}
  441#/**
  442#    Destructor. Updates the Devel::RingBuffer container object's free ring map,
  443#    <i>but only if executed in the same process/thread that it was allocated'd in.</i>
  444# (Note that due to threads CLONE, a ring object may be cloned with PID/TID
  445# of another thread, and thus DESTROY() could cause an invalid destruction)
  446# <p>
  447#    A future enhancement will add a flag to indicate to preserve
  448#    the ring on exit for post-mortem analysis
  449#*/
  450sub DESTROY {
  451#
  452#    for some reason we're getting leakage of ring objects into
  453#    the root thread, so only destroy in the thread its created
  454#
  455#    return unless defined($_[0]->[RINGBUF_RING_BUFFER]) &&
  456#        ($_[0]->[RINGBUF_RING_PID] == $$) &&
  457#        ($_[0]->[RINGBUF_RING_TID] == threads->self()->tid());
  458    return unless defined($_[0]->[RINGBUF_RING_BUFFER]);
  459    my @hdr = _get_header($_[0]->[RINGBUF_RING_ADDR]);
  460
  461    return
  462        unless ($hdr[0] == $$) && ($hdr[1] == threads->self()->tid());
  463    $_[0]->[RINGBUF_RING_BUFFER]->free($_[0]->[RINGBUF_RING_INDEX]);
  464}
  465
  4661;
  467