File Coverage

blib/lib/Devel/RingBuffer/Ring.pm
Criterion Covered Total %
statement 94 117 80.3
branch 10 24 41.6
condition 4 9 44.4
subroutine 38 47 80.8
pod 0 25 0.0
total 146 222 65.7


line stmt bran cond sub pod time code
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             #

9             # Note that significant functionality is written in XS/C in order to minimize
10             # tracing/debugging overhead.
11             #

12             # Permission is granted to use this software under the same terms as Perl itself.
13             # Refer to the Perl Artistic License
14             # for details.
15             #
16             # @author D. Arnold
17             # @since 2006-05-01
18             # @self $self
19             #*/
20             package Devel::RingBuffer::Ring;
21              
22             #use threads;
23 7     7   42 use Time::HiRes qw(time);
  7         15  
  7         69  
24 7     7   1041 use Exporter;
  7         15  
  7         527  
25              
26             BEGIN {
27 7     7   169 our @ISA = qw(Exporter);
28             #
29             # consts for member indexes
30             #
31 7     7   42 use constant RINGBUF_RING_BUFFER => 0;
  7         20  
  7         453  
32 7     7   41 use constant RINGBUF_RING_SLOTS => 1;
  7         8  
  7         347  
33             #
34             # !!!+++!+!+!+!+!+!+!+!+!+!+!+
35             # !!!DON'T CHANGE THIS INDEX UNLESS YOU CHANGE THE XS CODE TOO!!!!
36             # !!!+++!+!+!+!+!+!+!+!+!+!+!+
37             #
38 7     7   36 use constant RINGBUF_RING_ADDR => 2;
  7         21  
  7         297  
39              
40 7     7   41 use constant RINGBUF_RING_PID => 3;
  7         56  
  7         334  
41 7     7   47 use constant RINGBUF_RING_TID => 4;
  7         20  
  7         339  
42 7     7   36 use constant RINGBUF_RING_SLOT => 5;
  7         14  
  7         367  
43 7     7   43 use constant RINGBUF_RING_DEPTH => 6;
  7         8  
  7         375  
44 7     7   44 use constant RINGBUF_RING_INDEX => 7;
  7         14  
  7         538  
45 7     7   42 use constant RINGBUF_RING_MSGSZ => 8;
  7         8  
  7         271  
46 7     7   36 use constant RINGBUF_RING_HDRSZ => 9;
  7         14  
  7         345  
47 7     7   31 use constant RINGBUF_BASE_ADDR => 10;
  7         14  
  7         356  
48              
49 7     7   36 use constant RINGBUF_RING_WAIT => 0.3;
  7         15  
  7         864  
50              
51 7         21 our @EXPORT = ();
52 7         13 our @EXPORT_OK = ();
53 7         29 our %EXPORT_TAGS = (
54             ring_members => [
55             qw/RINGBUF_RING_BUFFER RINGBUF_RING_SLOTS RINGBUF_RING_ADDR
56             RINGBUF_RING_PID RINGBUF_RING_TID RINGBUF_RING_SLOT RINGBUF_RING_DEPTH
57             RINGBUF_RING_INDEX RINGBUF_RING_MSGSZ RINGBUF_RING_HDRSZ
58             RINGBUF_BASE_ADDR/
59             ],
60             );
61              
62 7         426 Exporter::export_tags(keys %EXPORT_TAGS);
63              
64             };
65              
66 7     7   45 use Config;
  7         14  
  7         297  
67 7     7   36 use Devel::RingBuffer; # to bootstrap
  7         14  
  7         1576  
68 7     7   37 use Devel::RingBuffer qw(:ringbuffer_consts);
  7         14  
  7         4849  
69              
70             our $hasThreads;
71              
72             BEGIN {
73 7 50 33 7   258 if ($Config{useithreads} && (!$ENV{DEVEL_RINGBUF_NOTHREADS})) {
74 0         0 require Devel::RingBuffer::ThreadFacade;
75 0         0 $hasThreads = 1;
76             }
77             }
78              
79 7     7   49 use strict;
  7         14  
  7         279  
80 7     7   42 use warnings;
  7         20  
  7         21456  
81              
82             our $VERSION = '0.31';
83             #/**
84             # Constructor. Allocates a ring buffer, and initializes its header
85             # and control variables.
86             #
87             # @param $ringbuffer the Devel::RingBuffer object
88             # @param $ringaddr the base address of this ring
89             # @param $baseaddr base address of the complete ring buffer structure
90             # @param $ringnum the number (i.e., positional index) of this ring
91             # @param $slots number of slots per ring
92             # @param $msgareasz size of the per-thread message area
93             #
94             # @return Devel::RingBuffer::Ring object on success; undef on failure
95             #*/
96             sub new {
97 24     24 0 132 my ($class, $ringbuffer, $ringaddr, $baseaddr, $ringnum, $slots, $msgareasz) = @_;
98              
99 24 50       69 my $tid = ($hasThreads ? Devel::RingBuffer::ThreadFacade->tid() : 0);
100 24         433 _init_ring($ringaddr, $$, $tid, $baseaddr);
101              
102 24         365 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             #*/
123             sub clone {
124 0     0 0 0 my $self = shift;
125              
126 0 0       0 my $tid = ($hasThreads ? Devel::RingBuffer::ThreadFacade->tid() : 0);
127 0         0 my ($ringnum, $ringaddr) = $self->[RINGBUF_RING_BUFFER]->reallocate();
128 0 0       0 return undef unless defined($ringnum);
129 0         0 $self->[RINGBUF_RING_ADDR] = $ringaddr;
130 0         0 $self->[RINGBUF_RING_INDEX] = $ringnum;
131 0         0 _init_ring($ringaddr, $$, $tid, $self->[RINGBUF_BASE_ADDR]);
132 0         0 return $self;
133             }
134             #/**
135             # Constructor. Opens an existing ring buffer for read-only access.
136             #
137             # @param $ringbuffer the Devel::RingBuffer object
138             # @param $ringaddr the base address of this ring
139             # @param $baseaddr base address of the complete ring buffer structure
140             # @param $ringnum the number (i.e., positional index) of this ring
141             # @param $slots number of slots per ring
142             # @param $msgareasz size of the per-thread message area
143             #
144             # @return Devel::RingBuffer::Ring object on success; undef on failure
145             #*/
146             sub open {
147 19     19 0 62 my ($class, $ringbuffer, $ringaddr, $baseaddr, $ringnum, $slots, $msgareasz) = @_;
148              
149 19         88 my ($pid, $tid, $slot, $depth) = _get_header($ringaddr);
150              
151 19         174 return bless [
152             $ringbuffer,
153             $slots,
154             $ringaddr,
155             $pid,
156             $tid,
157             $slot,
158             $depth,
159             $ringnum,
160             $msgareasz,
161             RINGBUF_BUFHDR_SZ + $msgareasz,
162             $baseaddr
163             ], $class;
164             }
165             #/**
166             # Update the current slot. Only updates linenumber and timestamp.
167             # May be called as either object or class method; in the latter case,
168             # caller must supply the ring's base address (used within DB::DB()
169             # to optimize access speed)
170             #
171             # @param $address class method calls only: base address of the ring
172             # @param $linenumber linenumber of current statement
173             #
174             # @return the Devel::RingBuffer::Ring object
175             #*/
176             # @xs updateSlot
177              
178             #/**
179             # @xs nextSlot
180             # Allocate and initialize the next slot. If the stack depth is
181             # greater than the configured number of slots, the oldest
182             # in-use slot is used, overwriting its current contents.
183             # May be called as either object or class method; in the latter case,
184             # caller must supply the ring's base address (used within DB::sub()
185             # to optimize access speed)
186             #

187             # Note: In future, this should return prior contents so we can restore
188             # on de-wrapping.
189             #
190             # @param $address class method calls only: base address of the ring
191             # @param $entry subroutine name (from $DB::sub)
192             #
193             # @return the stack depth after the slot is allocated.
194             #*/
195             # @xs nextSlot
196              
197             #/**
198             # @xs freeSlot
199             # Free the current slot and invalidates its contents.
200             # May be called as either object or class method; in the latter case,
201             # caller must supply the ring's base address (used within DB::sub()
202             # to optimize access speed)
203             #
204             # @param $address class method calls only: base address of the ring
205             #
206             # @return the stack depth after the slot is freed.
207             #*/
208             # @xs freeSlot
209              
210             #/**
211             # Get the ring header values. Header fields returned are
212             #

213             #
214             #
  • pid - PID of the ring owner
  • 215             #
  • tid - TID of the ring owner
  • 216             #
  • currSlot - current top slot
  • 217             #
  • depth - current stack depth
  • 218             #
    219             #
    220             # @return list of header values
    221             #*/
    222             sub getHeader {
    223 18     18 0 794 return _get_header($_[0]->[RINGBUF_RING_ADDR]);
    224             }
    225              
    226             #/**
    227             # Get the ring number (i.e., positional index)
    228             #
    229             # @return the ring number
    230             #*/
    231 86     86 0 1387 sub getIndex { return $_[0]->[RINGBUF_RING_INDEX]; }
    232              
    233             #/**
    234             # Get the ring base address
    235             #
    236             # @return the ring base address
    237             #*/
    238 0     0 0 0 sub getAddress { return $_[0]->[RINGBUF_RING_ADDR]; }
    239              
    240             #/**
    241             # Get the contents of the specified slot.
    242             #
    243             # @param $slot the number of the slot to return
    244             #
    245             # @return the line number, timestamp, and subroutine name from the slot
    246             #*/
    247             sub getSlot {
    248 26     26 0 782 my ($self, $slot) = @_;
    249              
    250 26 50 33     217 return (-1, 0, '(Invalid slot; ring has been wrapped)')
    251             if ($slot < 0) || ($slot > $self->[RINGBUF_RING_SLOTS]);
    252              
    253 26         139 return _get_slot($self->[RINGBUF_RING_ADDR], $slot);
    254             }
    255             #/**
    256             # Get the ring's trace flag
    257             #
    258             # @return the ring's trace flag
    259             #*/
    260             sub getTrace {
    261 0     0 0 0 return _get_trace($_[0]->[RINGBUF_RING_ADDR]);
    262             }
    263              
    264             #/**
    265             # Set the ring's trace flag
    266             #
    267             # @param $trace the value to set
    268             #
    269             # @return the prior value of the ring's trace flag
    270             #*/
    271             sub setTrace {
    272 0     0 0 0 return _set_trace($_[0]->[RINGBUF_RING_ADDR], $_[1]);
    273             }
    274              
    275             #/**
    276             # Get the ring's signal flag
    277             #
    278             # @return the ring's signal flag
    279             #*/
    280             sub getSignal {
    281 0     0 0 0 return _get_single($_[0]->[RINGBUF_RING_ADDR]);
    282             }
    283              
    284             #/**
    285             # Set the ring's signal flag
    286             #
    287             # @param $signal the value to set
    288             #
    289             # @return the prior value of the ring's signal flag
    290             #*/
    291             sub setSignal {
    292 0     0 0 0 return _set_signal($_[0]->[RINGBUF_RING_ADDR], $_[1]);
    293             }
    294              
    295             #/**
    296             # Post a command to the ring's command/message area
    297             #
    298             # @param $command the command value to set; must be no more than 3 bytes
    299             # @param $msg an optional message associated with the command; max length
    300             # is determined by configuration settings
    301             #
    302             # @return the ring object
    303             #*/
    304 4     4 0 49 sub postCommand { return postCmdEvent(@_, 1); }
    305              
    306             #/**
    307             # Post a response to the ring's command/message area
    308             #
    309             # @param $response the response value to set; must be no more than 3 bytes
    310             # @param $msg an optional message associated with the response; max length
    311             # is determined by configuration settings
    312             #
    313             # @return the ring object
    314             #*/
    315 4     4 0 40 sub postResponse { return postCmdEvent(@_, 0); }
    316              
    317             sub postCmdEvent {
    318 8     8 0 13 my ($self, $cmd, $msg, $state) = @_;
    319 8         29 _post_cmd_msg($self->[RINGBUF_RING_ADDR], $cmd, $msg, $state);
    320              
    321 8         14 return $self;
    322             }
    323              
    324             #/**
    325             # Wait indefinitely for a command to be posted to the ring's command/message area.
    326             #
    327             # @return the posted command and message
    328             #*/
    329             sub waitForCommand {
    330 0     0 0 0 return waitForCmdEvent(@_, 1);
    331             }
    332              
    333             #/**
    334             # Wait indefinitely for a response to be posted to the ring's command/message area.
    335             #
    336             # @return the posted response and message
    337             #*/
    338             sub waitForResponse {
    339 0     0 0 0 return waitForCmdEvent(@_, 0);
    340             }
    341              
    342             sub waitForCmdEvent {
    343 0     0 0 0 my ($cmd, $msg);
    344 0         0 while (1) {
    345 0         0 ($cmd, $msg) = _check_for_cmd_msg($_[0]->[RINGBUF_RING_ADDR], $_[1]);
    346 0 0       0 last if defined($cmd);
    347 0         0 sleep RINGBUF_RING_WAIT;
    348             }
    349 0         0 return ($cmd, $msg);
    350             }
    351              
    352             #/**
    353             # Test if a command is available in the ring's command/message area.
    354             #
    355             # @return if available, the posted command and message; otherwise an empty list
    356             #*/
    357             sub checkCommand {
    358 4     4 0 47 return checkCmdEvent(@_, 1);
    359             }
    360              
    361             #/**
    362             # Test if a response is available in the ring's command/message area.
    363             #
    364             # @return if available, the posted response and message; otherwise an empty list
    365             #*/
    366             sub checkResponse {
    367 4     4 0 41 return checkCmdEvent(@_, 0);
    368             }
    369              
    370             sub checkCmdEvent {
    371 8     8 0 35 return _check_for_cmd_msg($_[0]->[RINGBUF_RING_ADDR], $_[1]);
    372             }
    373             #/**
    374             # Allocate and initialize a watchlist entry. Sets the watch expression.
    375             #
    376             # @param $expr expression to set
    377             #
    378             # @return allocated watchlist entry number on success; undef on failure
    379             #*/
    380             sub addWatch {
    381 4     4 0 50 return _add_watch_expr($_[0]->[RINGBUF_RING_ADDR], $_[1]);
    382             }
    383              
    384             #/**
    385             # Free a watchlist entry.
    386             #
    387             # @param $watch the watchlist entry number to free
    388             #
    389             #*/
    390             sub freeWatch {
    391 4     4 0 40 return _free_watch_expr($_[0]->[RINGBUF_RING_ADDR], $_[1]);
    392             }
    393              
    394             #/**
    395             # Get a watchlist expression entry.
    396             #
    397             # @param $watch the watchlist entry number to get
    398             #
    399             # @return the expression in the watchlist entry, if any; undef otherwise
    400             #*/
    401             sub getWatchExpr {
    402 4 50   4 0 63 return $_[0]->[RINGBUF_RING_BUFFER] ?
    403             _get_watch_expr($_[0]->[RINGBUF_RING_ADDR], $_[1]) :
    404             undef;
    405             }
    406              
    407             #/**
    408             # Set a watchlist result entry.
    409             #
    410             # @param $watch the watchlist entry number to set
    411             # @param $result the result of the expression evaluation
    412             # @param $error error string if expression evaluation fails
    413             #*/
    414             sub setWatchResult {
    415 4     4 0 46 my ($self, $watch, $result, $error) = @_;
    416              
    417 4 50       19 return $self->[RINGBUF_RING_BUFFER] ?
    418             _set_watch_result($self->[RINGBUF_RING_ADDR], $watch, $result, $error) :
    419             undef;
    420             }
    421             #/**
    422             # Get a watchlist expression entry. If the length of the result exceeds
    423             # the configured message size, the result is truncated. If the result is
    424             # undef, the length will zero, and both the result and error will be undef.
    425             # If the evaluation caused a failure, the length indicates the length of
    426             # the error string, and result will be undef.
    427             #
    428             # @param $watch the watchlist entry number to get
    429             #
    430             # @return the complete length of the result, the (possibly truncated) result value,
    431             # and the (possibly truncated) error message (if the evaluation failed).
    432             #*/
    433             sub getWatchResult {
    434 4 50   4 0 61 return $_[0]->[RINGBUF_RING_BUFFER] ?
    435             _get_watch_result($_[0]->[RINGBUF_RING_ADDR], $_[1]) :
    436             (undef, undef, undef);
    437             }
    438             #/**
    439             # Destructor. Updates the Devel::RingBuffer container object's free ring map,
    440             # but only if executed in the same process/thread that it was allocated'd in.
    441             # (Note that due to threads CLONE, a ring object may be cloned with PID/TID
    442             # of another thread, and thus DESTROY() could cause an invalid destruction)
    443             #

    444             # A future enhancement will add a flag to indicate to preserve
    445             # the ring on exit for post-mortem analysis
    446             #*/
    447             sub DESTROY {
    448             #
    449             # for some reason we're getting leakage of ring objects into
    450             # the root thread, so only destroy in the thread its created
    451             #
    452             # return unless defined($_[0]->[RINGBUF_RING_BUFFER]) &&
    453             # ($_[0]->[RINGBUF_RING_PID] == $$) &&
    454             # ($_[0]->[RINGBUF_RING_TID] == threads->self()->tid());
    455 43 50   43   206853 return unless defined($_[0]->[RINGBUF_RING_BUFFER]);
    456 43         230 my @hdr = _get_header($_[0]->[RINGBUF_RING_ADDR]);
    457 43 50       204 my $tid = ($hasThreads ? Devel::RingBuffer::ThreadFacade->tid() : 0);
    458              
    459             return
    460 43 100 66     821 unless ($hdr[0] == $$) && ($hdr[1] == $tid);
    461 23         244 $_[0]->[RINGBUF_RING_BUFFER]->free($_[0]->[RINGBUF_RING_INDEX]);
    462             }
    463              
    464             1;