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