File Coverage

blib/lib/Devel/RingBuffer.pm
Criterion Covered Total %
statement 268 348 77.0
branch 16 52 30.7
condition 11 20 55.0
subroutine 74 90 82.2
pod 0 25 0.0
total 369 535 68.9


line stmt bran cond sub pod time code
1             #/**
2             # Shared memory ring buffers 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 (e.g., Devel::STrace::Monitor).
7             #

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

11             # Permission is granted to use this software under the same terms as Perl itself.
12             # Refer to the Perl Artistic License
13             # for details.
14             #
15             # @author D. Arnold
16             # @since 2006-05-01
17             # @self $self
18             #*/
19             package Devel::RingBuffer;
20              
21 7     7   86591 use Carp qw(cluck carp confess);
  7         26  
  7         4334  
22             #use threads;
23             #use threads::shared;
24 7     7   55 use Config;
  7         15  
  7         2875  
25 7     7   20887 use IPC::Mmap;
  7         452366  
  7         599  
26 7     7   72 use DynaLoader;
  7         19  
  7         184  
27 7     7   35 use Exporter;
  7         20  
  7         366  
28              
29             BEGIN {
30 7     7   182 our @ISA = qw(Exporter DynaLoader);
31              
32             #
33             # offset of global fields
34             #
35 7     7   41 use constant RINGBUF_SINGLE => 0;
  7         9  
  7         442  
36 7     7   36 use constant RINGBUF_MSGAREA_SZ => 4;
  7         14  
  7         257  
37 7     7   36 use constant RINGBUF_BUFFERS => 8;
  7         8  
  7         268  
38 7     7   29 use constant RINGBUF_SLOTS => 12;
  7         13  
  7         255  
39 7     7   83 use constant RINGBUF_SLOT_SZ => 16;
  7         8  
  7         280  
40 7     7   30 use constant RINGBUF_CREATE_STOP => 20;
  7         14  
  7         243  
41 7     7   29 use constant RINGBUF_CREATE_TRACE => 24;
  7         8  
  7         242  
42 7     7   29 use constant RINGBUF_GLOBAL_SZ => 28;
  7         20  
  7         220  
43 7     7   40 use constant RINGBUF_TOTALMSG_SZ => 32;
  7         14  
  7         262  
44 7     7   35 use constant RINGBUF_GLOBMSG_SZ => 36;
  7         14  
  7         302  
45 7     7   36 use constant RINGBUF_GLOBAL_MSG => 40;
  7         14  
  7         272  
46 7     7   29 use constant RINGBUF_RINGHDR_SZ => 40;
  7         14  
  7         243  
47             #
48             # offsets of watchlist members
49             #
50 7     7   29 use constant RINGBUF_WATCH_INUSE => 0;
  7         13  
  7         326  
51 7     7   35 use constant RINGBUF_WATCH_EXPRLEN => 4;
  7         14  
  7         241  
52 7     7   30 use constant RINGBUF_WATCH_EXPR => 8;
  7         14  
  7         331  
53 7     7   36 use constant RINGBUF_WATCH_READY => 264;
  7         8  
  7         347  
54 7     7   29 use constant RINGBUF_WATCH_RESLEN => 268;
  7         14  
  7         233  
55 7     7   65 use constant RINGBUF_WATCH_RESULT => 272;
  7         14  
  7         256  
56 7     7   35 use constant RINGBUF_WATCH_SZ => 784;
  7         8  
  7         299  
57 7     7   30 use constant RINGBUF_WATCH_CNT => 4;
  7         13  
  7         248  
58 7     7   34 use constant RINGBUF_WATCH_EXPRSZ => 256;
  7         8  
  7         305  
59 7     7   35 use constant RINGBUF_WATCH_RESSZ => 512;
  7         14  
  7         353  
60             #
61             # offsets of ring buffer members
62             #
63 7     7   35 use constant RINGBUF_PID => 0;
  7         8  
  7         273  
64 7     7   35 use constant RINGBUF_TID => 4;
  7         14  
  7         301  
65 7     7   41 use constant RINGBUF_CURRSLOT => 8;
  7         13  
  7         319  
66 7     7   36 use constant RINGBUF_DEPTH => 12;
  7         104  
  7         342  
67 7     7   41 use constant RINGBUF_TRACE => 16;
  7         9992  
  7         6545  
68 7     7   1049 use constant RINGBUF_SIGNAL => 20;
  7         20  
  7         6648  
69 7     7   1019 use constant RINGBUF_BASEADDR => 24;
  7         987  
  7         8733  
70 7     7   59 use constant RINGBUF_WATCH_OFFSET => 28;
  7         23  
  7         355  
71 7     7   34 use constant RINGBUF_BUFHDR_SZ => 28;
  7         17  
  7         280  
72              
73 7     7   29 use constant RINGBUF_DFLT_SLOTSZ => 214;
  7         14  
  7         387  
74 7     7   29 use constant RINGBUF_ENTRY_SZ => 200;
  7         14  
  7         339  
75 7     7   34 use constant RINGBUF_SLOT_PACKSTR => 'l d S/a*';
  7         14  
  7         290  
76             #
77             # consts for member indexes
78             #
79 7     7   35 use constant RINGBUF_FILENAME => 0;
  7         8  
  7         291  
80 7     7   29 use constant RINGBUF_SIZE => 1;
  7         14  
  7         378  
81 7     7   35 use constant RINGBUF_COUNT => 2;
  7         19  
  7         671  
82 7     7   59 use constant RINGBUF_BUFSIZE => 3;
  7         14  
  7         324  
83 7     7   48 use constant RINGBUF_SLOT_CNT => 4;
  7         8  
  7         324  
84 7     7   42 use constant RINGBUF_FLD_TID => 5;
  7         14  
  7         336  
85 7     7   79 use constant RINGBUF_FLD_PID => 6;
  7         13  
  7         338  
86 7     7   36 use constant RINGBUF_RING => 7;
  7         14  
  7         342  
87 7     7   35 use constant RINGBUF_FH => 8;
  7         21  
  7         295  
88 7     7   36 use constant RINGBUF_FLD_MSGAREA_SZ => 9;
  7         8  
  7         319  
89 7     7   36 use constant RINGBUF_FLD_GLOBAL_SZ => 10;
  7         15  
  7         334  
90 7     7   42 use constant RINGBUF_MAP_OFFSET => 11;
  7         15  
  7         305  
91 7     7   36 use constant RINGBUF_RINGS_OFFSET => 12;
  7         8  
  7         391  
92 7     7   41 use constant RINGBUF_MAP_ADDR => 13;
  7         15  
  7         336  
93 7     7   37 use constant RINGBUF_RINGS_ADDR => 14;
  7         8  
  7         324  
94 7     7   42 use constant RINGBUF_ADDRESS => 15;
  7         13  
  7         342  
95 7     7   41 use constant RINGBUF_SLOT_SIZE => 16;
  7         15  
  7         445  
96 7     7   37 use constant RINGBUF_NEXT_IDX => 17;
  7         14  
  7         327  
97              
98 7     7   42 use constant RINGBUF_RING_WAIT => 0.3;
  7         8  
  7         1263  
99              
100 7         20 our @EXPORT = ();
101 7         15 our @EXPORT_OK = ();
102 7         83 our %EXPORT_TAGS = (
103             ringbuffer_consts => [
104             qw/RINGBUF_SINGLE RINGBUF_MSGAREA_SZ RINGBUF_BUFFERS RINGBUF_SLOTS
105             RINGBUF_SLOT_SZ RINGBUF_CREATE_STOP RINGBUF_CREATE_TRACE RINGBUF_GLOBAL_SZ
106             RINGBUF_TOTALMSG_SZ RINGBUF_GLOBMSG_SZ
107             RINGBUF_GLOBAL_MSG RINGBUF_RINGHDR_SZ RINGBUF_WATCH_INUSE
108             RINGBUF_WATCH_EXPRLEN RINGBUF_WATCH_EXPR RINGBUF_WATCH_READY
109             RINGBUF_WATCH_RESLEN RINGBUF_WATCH_RESULT RINGBUF_WATCH_SZ
110             RINGBUF_WATCH_CNT RINGBUF_PID RINGBUF_TID RINGBUF_CURRSLOT
111             RINGBUF_DEPTH RINGBUF_TRACE RINGBUF_SIGNAL RINGBUF_WATCH_OFFSET
112             RINGBUF_BUFHDR_SZ RINGBUF_DFLT_SLOTSZ RINGBUF_ENTRY_SZ RINGBUF_SLOT_PACKSTR/
113             ],
114              
115             ringbuffer_members => [
116             qw/RINGBUF_FILENAME RINGBUF_SIZE RINGBUF_COUNT RINGBUF_BUFSIZE RINGBUF_SLOT_CNT
117             RINGBUF_FLD_TID RINGBUF_FLD_PID RINGBUF_RING RINGBUF_FH
118             RINGBUF_FLD_MSGAREA_SZ RINGBUF_FLD_GLOBAL_SZ RINGBUF_MAP_OFFSET
119             RINGBUF_RINGS_OFFSET RINGBUF_MAP_ADDR RINGBUF_RINGS_ADDR RINGBUF_ADDRESS
120             RINGBUF_SLOT_SIZE RINGBUF_NEXT_IDX/
121             ],
122             );
123              
124 7         1330 Exporter::export_tags(keys %EXPORT_TAGS);
125              
126             };
127              
128             our $VERSION = '0.31';
129             our $hasThreads;
130              
131             BEGIN {
132 7 50 33 7   293 if ($Config{useithreads} && (!$ENV{DEVEL_RINGBUF_NOTHREADS})) {
133 0         0 require Devel::RingBuffer::ThreadFacade;
134 0         0 $hasThreads = 1;
135             }
136             }
137              
138 7     7   16065 use threads::shared;
  7         16325  
  7         44  
139              
140 7     7   576 use strict;
  7         9  
  7         206  
141 7     7   42 use warnings;
  7         15  
  7         437  
142              
143             bootstrap Devel::RingBuffer $VERSION;
144              
145 7     7   4997 use Devel::RingBuffer::Ring;
  7         27  
  7         29009  
146              
147             our $thrdlock = undef;
148              
149             #/**
150             # Constructor. Using a combination of the optional C<%args> and
151             # various environment variables, creates and initializes a
152             # mmap'ed file in read/write mode with the ring buffer structures.
153             #
154             # @param File name of the file to be created for memory mapping.
155             # @param GlobalSize size of global monitor <=> AUT message buffer.
156             # @param MessageSize size of per-thread monitor <=> AUT message buffer.
157             # @param Rings Number of rings to create in the ring buffer.
158             # @param Slots Number of slots per ring.
159             # @param SlotSize Slot size in bytes.
160             # @param StopOnCreate Initial value for stop_on_create flag.
161             # @param TraceOnCreate Initial value for trace_on_create flag.
162             #
163             # @return Devel::RingBuffer object on success; undef on failure
164             #*/
165             sub new {
166 7     7 0 5542 my $class = shift;
167              
168 7         36 my %args = @_;
169              
170 7   66     77 my $file = $args{File} || $ENV{DEVEL_RINGBUF_FILE};
171 7         14 my $anon;
172 7 100       41 unless (defined($file)) {
173 6         60 my @paths = split(/[\/\\]/, $0);
174 6         18 $file = pop @paths;
175 6 50       54 if ($^O eq 'MSWin32') {
176 0         0 $anon = 1;
177             }
178             else {
179 6 50       36 $file = defined($ENV{TEMP}) ? "$ENV{TEMP}/$file" : "/tmp/$file";
180             }
181 6         54 $file=~s/^(.+)\..+/$1/;
182             #
183             # use timestamp sans weekday and year
184             #
185 6         1278 my @pieces = split(/\s+/, scalar localtime);
186 6         24 pop @pieces; # get rid of year
187 6         48 $pieces[0] = $$; # replace weekday w/ PID
188 6         18 $pieces[-1]=~tr/:/_/; # Win32 can't handle colons in filenames
189 6         42 $file .= '.' . join('_', @pieces);
190             }
191              
192             #print STDERR "RingBuffer new: args:", join(', ', keys %args), "\n";
193              
194 7   50     99 my $ringslots = $args{Slots} || $ENV{DEVEL_RINGBUF_SLOTS} || 10;
195 7   50     22304 my $slotsz = $args{SlotSize} || $ENV{DEVEL_RINGBUF_SLOTSZ} || 200;
196 7   50     134 my $ringcount = $args{Rings} || $ENV{DEVEL_RINGBUF_BUFFERS} || 20;
197 7   50     102 my $ringmsgsz = $args{MessageSize} || $ENV{DEVEL_RINGBUF_MSGSZ} || 256;
198 7   50     89 my $globmsgsz = $args{GlobalSize} || $ENV{DEVEL_RINGBUF_GLOBALSZ} || (16 * 1024);
199 7   100     200 my $create_stop = $args{StopOnCreate} || $ENV{DEVEL_RINGBUF_SOC} || 0;
200 7   50     79 my $create_trace = $args{TraceOnCreate} || $ENV{DEVEL_RINGBUF_TOC} || 0;
201             #
202             # in order to avoid issues with word alignment, we'll always
203             # force slotsz, msg size, and global size to be word aligned
204             # (who knows, we may need to be 8 byte aligned on some platforms)
205             #
206 7 50       41 $slotsz += (4 - ($slotsz & 3)) if ($slotsz & 3);
207 7 50       42 $ringmsgsz += (4 - ($ringmsgsz & 3)) if ($ringmsgsz & 3);
208 7 50       28 $globmsgsz += (4 - ($globmsgsz & 3)) if ($globmsgsz & 3);
209              
210 7         21 my $freemap_offs = RINGBUF_RINGHDR_SZ + $globmsgsz;
211              
212 7         55 my $ringbufsz = _get_ring_size($ringslots, $slotsz, $ringmsgsz);
213              
214 7         42 my $ringsize = _get_total_size($ringcount, $ringslots, $slotsz, $ringmsgsz, $globmsgsz) +
215             1024; # Win32 needs some extra room
216              
217 7 50       149 my $self = bless [
218             $file,
219             $ringsize,
220             $ringcount,
221             $ringbufsz,
222             $ringslots,
223             ($hasThreads ? Devel::RingBuffer::ThreadFacade->tid() : 0),
224             $$,
225             undef,
226             undef,
227             $ringmsgsz,
228             $globmsgsz,
229             $freemap_offs,
230             _get_rings_addr(0, $ringcount, $globmsgsz),
231             $freemap_offs,
232             _get_rings_addr(0, $ringcount, $globmsgsz),
233             0,
234             $slotsz
235             ], $class;
236             #
237             # create the mmap'ed ring
238             #
239             #cluck "file is $file\n";
240 7 50       41 if ($anon) {
241             #
242             # on Win32 only...anonymous mmap is useless to us on POSIX
243             #
244 0 0       0 $self->[RINGBUF_RING] = IPC::Mmap->new($file, $ringsize,
245             PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON)
246             or die "Can't open mmap file $file: $!";
247             }
248             else {
249 7 50       1578 open(FH, ">$file") ||
250             confess "Can't open mmap file $file: $!";
251 7         18569 print FH "\0" x $ringsize;
252 7         386 close FH;
253              
254 7 50       76 $self->[RINGBUF_RING] = IPC::Mmap->new($file, $ringsize,
255             PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FILE)
256             or die "Can't open mmap file $file: $!";
257             }
258             #
259             # share the thrdlock
260             #
261 7 50       76664 if ($hasThreads) {
262             # print STDERR "we're shared\n";
263 0         0 share($thrdlock);
264             }
265             #
266             # clear the ringbuffer (Win32 needs this)
267             #
268 7         38 my $ringbuffer = $self->[RINGBUF_RING] ;
269 7         1685 my $var = "\0" x ($ringsize - 1024);
270 7         121 $ringbuffer->write($var, 0, $ringsize - 1024);
271 7         1281 my $ringslotsz = $ringslots * $slotsz;
272             #
273             # then init it
274             #
275             return undef
276 7 50       77 unless $ringbuffer->pack(0, 'l l l l l l l l l',
277             0, $ringmsgsz, $ringcount, $ringslots, $slotsz, $create_stop, $create_trace, $globmsgsz, 0);
278              
279 7         251 my $addr = $self->[RINGBUF_ADDRESS] = $self->[RINGBUF_RING]->getAddress();
280              
281 7         49 $self->[RINGBUF_MAP_ADDR] += $addr;
282 7         14 $self->[RINGBUF_RINGS_ADDR] += $addr;
283              
284 7         27 my $mapaddr = $self->[RINGBUF_MAP_ADDR];
285 7         20 my $ringsaddr = $self->[RINGBUF_RINGS_ADDR];
286             #
287             # let XS do init
288             #
289             _free_ring($mapaddr, $ringsaddr, $ringbufsz, $_)
290 7         225 foreach (0..$ringcount-1);
291             #
292             # for unknown reasons, the first map doesn't take ... so remap
293             #
294             # $self->remmap();
295              
296 7         73 return $self;
297             }
298              
299             #/**
300             # Get the name of the mmap'ed file.
301             #
302             # @return the name of the mmap'ed file
303             #*/
304 6     6 0 78 sub getName { return $_[0]->[RINGBUF_FILENAME]; }
305              
306             #/**
307             # Get base address of the mmap'ed file.
308             #
309             # @return the address of the mmap'ed file
310             #*/
311 0     0 0 0 sub getAddress { return $_[0]->[RINGBUF_ADDRESS]; }
312              
313             #/**
314             # Allocate a ring buffer. Should only be used on ringbuffers created with new().
315             #
316             # @return a Devel::RingBuffer::Ring object on success.
317             # If no rings are available, returns undef.
318             #*/
319             sub allocate {
320 24     24 0 130057 my $self = shift;
321             #
322             # allocate a ring buffer and init it
323             #
324             # unless (($self->[RINGBUF_FLD_TID] == threads->self()->tid()) ||
325             # ($self->[RINGBUF_FLD_PID] == $$)) {
326             # On Win32, the fork() emulation means we shouldn't remap!!!
327             #
328 24         153 if (0) {
329             unless ($self->[RINGBUF_FLD_PID] == $$) {
330             #
331             # this probably isn't needed anymore for threads, but may be for
332             # processes...
333             #
334             my $file = $self->[RINGBUF_FILENAME];
335             my $ringsize = $self->[RINGBUF_SIZE];
336             $self->[RINGBUF_RING] = IPC::Mmap->new($file, $ringsize,
337             PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FILE) ||
338             die "Can't mmap file $file: $!";
339             $self->[RINGBUF_FLD_TID] = ($hasThreads ? Devel::RingBuffer::ThreadFacade->tid() : 0);
340             $self->[RINGBUF_FLD_PID] = $$;
341             }
342             }
343              
344 24         109 my $ring = 0;
345 24         91 my $ringbuffer = $self->[RINGBUF_RING];
346 24         420 $ringbuffer->lock();
347             {
348 24         1105 lock($thrdlock);
  24         115  
349             #
350             # use XS to find free ring (for performance reasons)
351             #
352 24         312 $ring = _alloc_ring($self->[RINGBUF_MAP_ADDR], $self->[RINGBUF_COUNT]);
353             }
354 24         153 $ringbuffer->unlock();
355              
356 24         346 my $ringsaddr = $self->[RINGBUF_RINGS_ADDR];
357              
358 24 50       1210 return defined($ring) ?
359             Devel::RingBuffer::Ring->new(
360             $self,
361             _get_ring_addr($self->[RINGBUF_RINGS_ADDR],
362             $ring,
363             $self->[RINGBUF_SLOT_CNT],
364             $self->[RINGBUF_SLOT_SIZE],
365             $self->[RINGBUF_FLD_MSGAREA_SZ]),
366             $self->[RINGBUF_ADDRESS],
367             $ring,
368             $self->[RINGBUF_SLOT_CNT],
369             $self->[RINGBUF_FLD_MSGAREA_SZ],
370             ) :
371             undef;
372             }
373              
374             #/**
375             # Re-allocates a ring buffer. Required to handle threads' CLONE()
376             # of the existing ring buffer object when a new thread is created.
377             # C simply allocates a ring buffer and returns its
378             # ring number, and its base address; the caller than updates
379             # an existing ring object with the returned values.
380             #
381             # @return the allocated ring index and address
382             #*/
383             sub reallocate {
384 0     0 0 0 my $self = shift;
385              
386 0         0 my $newring = 0;
387 0         0 my $ringbuffer = $self->[RINGBUF_RING];
388 0         0 $ringbuffer->lock();
389             {
390 0         0 lock($thrdlock);
  0         0  
391             #
392             # use XS to find free ring (for performance reasons)
393             #
394 0         0 $newring = _alloc_ring($self->[RINGBUF_MAP_ADDR], $self->[RINGBUF_COUNT]);
395             }
396 0         0 $ringbuffer->unlock();
397              
398 0 0       0 return defined($newring) ?
399             ($newring,
400             _get_ring_addr(
401             $self->[RINGBUF_RINGS_ADDR],
402             $newring,
403             $self->[RINGBUF_SLOT_CNT],
404             $self->[RINGBUF_SLOT_SIZE],
405             $self->[RINGBUF_FLD_MSGAREA_SZ])) :
406             ();
407             }
408              
409             #/**
410             # Constructor. Opens an existing mmap'd file for read/write
411             # access (for interactive debuggers)
412             #
413             # @param $file optional name of mmap'ed file (or namespace for Win32)
414             #
415             # @return Devel::RingBuffer object on success; undef on failure
416             #*/
417             sub open {
418 0     0 0 0 return _lcl_open(@_, PROT_READ|PROT_WRITE);
419             }
420              
421             #/**
422             # Constructor. Opens an existing mmap'd file for read-only
423             # access (for simple monitor applications)
424             #
425             # @param $file optional name of mmap'ed file (or namespace for Win32)
426             #
427             # @return Devel::RingBuffer object on success; undef on failure
428             #*/
429             sub monitor {
430 0     0 0 0 return _lcl_open(@_, PROT_READ);
431             }
432              
433             sub _lcl_open {
434 0     0   0 my ($class, $file, $mode) = @_;
435             #
436             # open twice: first to get config params, then
437             # to map the whole file
438             #
439             # use anonymous open for Win32
440             #
441 0 0       0 my $flags = ($^O eq 'MSWin32') ?
442             MAP_SHARED | MAP_ANON :
443             MAP_SHARED | MAP_FILE;
444              
445 0 0       0 my $ringbuffer =
446             IPC::Mmap->new($file, RINGBUF_RINGHDR_SZ, PROT_READ, $flags) or
447             die "Can't mmap file $file: $!";
448              
449 0         0 my ($msgareasz, $count, $slots, $slotsz, $stop, $trace, $globmsgsz) =
450             $ringbuffer->unpack(4, 28, 'l7');
451              
452 0         0 my $freemap_offs = RINGBUF_RINGHDR_SZ + $globmsgsz;
453              
454 0         0 my $ringbufsz = _get_ring_size($slots, $slotsz, $msgareasz);
455              
456 0         0 my $ringsize = _get_total_size($count, $slots, $slotsz, $msgareasz, $globmsgsz) +
457             1024; # Win32 needs some extra room
458              
459 0         0 $ringbuffer->close();
460              
461 0 0       0 $ringbuffer = IPC::Mmap->new($file, $ringsize, $mode, $flags)
462             or die "Can't mmap file $file: $!";
463              
464 0 0       0 return bless [
465             $file,
466             $ringsize,
467             $count,
468             $ringbufsz,
469             $slots,
470             ($hasThreads ? Devel::RingBuffer::ThreadFacade->tid() : 0),
471             $$,
472             $ringbuffer,
473             undef,
474             $msgareasz,
475             $globmsgsz,
476             $freemap_offs,
477             _get_rings_addr(0, $count, $globmsgsz),
478             $ringbuffer->getAddress() + $freemap_offs,
479             _get_rings_addr($ringbuffer->getAddress(), $count, $globmsgsz),
480             $ringbuffer->getAddress(),
481             $slotsz
482             ], $class;
483             }
484              
485             #/**
486             # Get the free buffer map
487             #
488             # @return list of bytes, one per ring; if and element is 'true', the associated
489             # ring is free; otherwise the ring is in use.
490             #*/
491             sub getMap {
492 1     1 0 29091 return $_[0]->[RINGBUF_RING]->unpack(
493             $_[0]->[RINGBUF_MAP_OFFSET],
494             $_[0]->[RINGBUF_COUNT],
495             'C' . $_[0]->[RINGBUF_COUNT] );
496             }
497              
498             #/**
499             # Get the RingBuffer global header fields. The fields
500             # returned include:
501             #

502             #
503             #
  • single - global control variable
  • 504             #
  • msgarea_sz - size of per-thread message area
  • 505             #
  • max_buffer - number of configured rings
  • 506             #
  • slots - number of slots per ring
  • 507             #
  • slot_sz - size of each slot (excluding linenumber and timestamp header)
  • 508             #
  • stop_on_create - 1 => new threads created with signal = 1
  • 509             #
  • trace_on_create - 1 => new threads created with trace = 1
  • 510             #
  • global_sz - size of global message buffer
  • 511             #
  • globmsg_total - size of complete global message contents
  • 512             #
  • globmsg_sz - size of current global message fragment
  • 513             #
    514             #
    515             # @return list of the specified header values
    516             #*/
    517             sub getHeader {
    518 0     0 0 0 return $_[0]->[RINGBUF_RING]->unpack(0, 40, 'l10');
    519             }
    520             #/**
    521             # Open and return a Devel::RingBuffer::Ring object
    522             # for the specified ring number.
    523             #
    524             # @param $ringnum number of ring to be opened
    525             #
    526             # @return Devel::RingBuffer::Ring object
    527             #*/
    528             sub getRing {
    529 19     19 0 214 my ($self, $ringnum) = @_;
    530 19         171 return Devel::RingBuffer::Ring->open(
    531             $self,
    532             _get_ring_addr(
    533             $self->[RINGBUF_RINGS_ADDR],
    534             $ringnum,
    535             $self->[RINGBUF_SLOT_CNT],
    536             $self->[RINGBUF_SLOT_SIZE],
    537             $self->[RINGBUF_FLD_MSGAREA_SZ]),
    538             $self->[RINGBUF_ADDRESS],
    539             $ringnum,
    540             $self->[RINGBUF_SLOT_CNT],
    541             $self->[RINGBUF_FLD_MSGAREA_SZ]
    542             );
    543             }
    544              
    545             #/**
    546             # Get the configured number of slots per ring.
    547             #
    548             # @return the number of slots configured for the ring buffer.
    549             #*/
    550 0     0 0 0 sub getSlots { return $_[0]->[RINGBUF_SLOT_CNT]; }
    551             #/**
    552             # Get the configured size of slots.
    553             #
    554             # @return the slot size
    555             #*/
    556 0     0 0 0 sub getSlotSize { return $_[0]->[RINGBUF_SLOT_SIZE]; }
    557             #/**
    558             # Get the number of configured rings.
    559             #
    560             # @return the count of rings
    561             #*/
    562 6     6 0 2556 sub getCount { return $_[0]->[RINGBUF_COUNT]; }
    563             #/**
    564             # Close the ring buffer.
    565             #
    566             # @deprecated
    567             #*/
    568             sub close {
    569 1     1 0 2735359 my $self = shift;
    570 1         18 my $ring = delete $self->[RINGBUF_RING];
    571 1         32 return 1;
    572             }
    573             #/**
    574             # Free a ring. Returns a ring to the free list
    575             #
    576             # @param $ring the ring object to be freed
    577             #*/
    578             sub free {
    579 23     23 0 2711 my ($self, $ring) = @_;
    580             #print STDERR "freeing ring $ring\n";
    581 23 50       88 return 1 unless $self->[RINGBUF_RING];
    582              
    583 23         42 my $ringbuffer = $self->[RINGBUF_RING];
    584 23         180 $ringbuffer->lock();
    585             {
    586 23         488 lock($thrdlock);
      23         66  
    587             #
    588             # XS handles everything but the locks
    589             #
    590 23         140 _free_ring($self->[RINGBUF_MAP_ADDR],
    591             $self->[RINGBUF_RINGS_ADDR],
    592             $self->[RINGBUF_BUFSIZE],
    593             $ring);
    594             }
    595              
    596 23         98 $ringbuffer->unlock();
    597             }
    598             #/**
    599             # Get the IPC::Mmap object used to store the ringbuffer.
    600             #
    601             # @return the IPC::Mmap object
    602             #*/
    603 0     0 0 0 sub getMmap { return $_[0]->[RINGBUF_RING]; }
    604             #
    605             # just check for the current thread/process's ring instance;
    606             # note this can be a lengthy process, since we must
    607             # scan the mmap'd ring buffer headers for matching PID/TID,
    608             # and then free it
    609             #
    610             # !!!DPERECATED!!! We can't permit DESTROY if cloned versions
    611             # might destroy things; just let process run down deal with
    612             # closing the file
    613             #
    614             sub OLDDESTROY {
    615 0     0 0 0 my $self = shift;
    616 0 0       0 my $tid = ($hasThreads ? Devel::RingBuffer::ThreadFacade->tid() : 0);
    617              
    618 0 0       0 return unless $self->[RINGBUF_RING];
    619              
    620 0         0 print STDERR "RingBuffer DESTROYING in thread $tid\n";
    621              
    622 0         0 my $ringbuffer = $self->[RINGBUF_RING];
    623 0         0 $ringbuffer->lock();
    624             {
    625 0         0 lock($thrdlock);
      0         0  
    626             #
    627             # XS handles everything but the locks
    628             #
    629 0         0 my $ring = _find_ring($self->[RINGBUF_RINGS_ADDR],
    630             $self->[RINGBUF_BUFSIZE], $self->[RINGBUF_COUNT], $$, $tid);
    631 0 0       0 _free_ring($self->[RINGBUF_MAP_ADDR],
    632             $self->[RINGBUF_RINGS_ADDR],
    633             $self->[RINGBUF_BUFSIZE],
    634             $ring)
    635             if defined($ring);
    636             }
    637 0         0 $ringbuffer->unlock();
    638             }
    639              
    640             #/**
    641             # Sets the value of the global single field.
    642             #
    643             # @param value to set
    644             #
    645             # @return the prior value of the field.
    646             #*/
    647             sub setSingle {
    648 0     0 0 0 return $_[0]->[RINGBUF_RING]->pack(0, 'l', $_[1]);
    649             }
    650              
    651             #/**
    652             # Gets the value of the global single field.
    653             #
    654             # @return the value of the field.
    655             #*/
    656             sub getSingle {
    657 0     0 0 0 return $_[0]->[RINGBUF_RING]->unpack(0, 4, 'l');
    658             }
    659              
    660             #/**
    661             # Sets the value of the stop_on_create field.
    662             #
    663             # @return the prior value of the field.
    664             #*/
    665             sub setStopOnCreate {
    666 0     0 0 0 return $_[0]->[RINGBUF_RING]->pack(RINGBUF_CREATE_STOP, 'l', $_[1]);
    667             }
    668              
    669             #/**
    670             # Get the value of the stop_on_create field.
    671             #
    672             # @return the current value of the field.
    673             #*/
    674             sub getStopOnCreate {
    675 1     1 0 58 return $_[0]->[RINGBUF_RING]->unpack(RINGBUF_CREATE_STOP, 4, 'l');
    676             }
    677              
    678             #/**
    679             # Sets the value of the trace_on_create field.
    680             #
    681             # @param $trace_on_create value to set
    682             # @return the prior value of the field
    683             #*/
    684             sub setTraceOnCreate {
    685 0     0 0 0 return $_[0]->[RINGBUF_RING]->pack(RINGBUF_CREATE_TRACE, 'l', $_[1]);
    686             }
    687              
    688             #/**
    689             # Get the value of the trace_on_create field.
    690             #
    691             # @return the value of the field
    692             #*/
    693             sub getTraceOnCreate {
    694 1     1 0 36 return $_[0]->[RINGBUF_RING]->unpack(RINGBUF_CREATE_TRACE, 4, 'l');
    695             }
    696              
    697             #/**
    698             # Sets a message into the global message area. Note that
    699             # this operation requires locking the entire ring buffer
    700             # header until the message is completely transfered.
    701             # Messages larger than the configured global message size
    702             # will be transfered in chunks; each chunk must back ACK'd by
    703             # the message receiver.
    704             #
    705             # @param $msg the message to send
    706             #
    707             # @return the RingBuffer object
    708             #*/
    709             sub setGlobalMsg {
    710 0     0 0   my $self = shift;
    711 0           my $ringbuffer = $self->[RINGBUF_RING];
    712 0           my $globsz = $self->[RINGBUF_FLD_GLOBAL_SZ];
    713 0           my $first = 1;
    714 0           $ringbuffer->lock();
    715             {
    716 0           lock($thrdlock);
      0            
    717 0           my ($t, $frag) = (0,0);
    718 0           my $len = length($_[0]);
    719 0           while ($len) {
    720             #
    721             # may need to fragment
    722             #
    723 0 0         $t = ($len > $globsz) ? $globsz : $len;
    724 0           $ringbuffer->write(substr($_[0], $frag, $t), RINGBUF_GLOBAL_MSG, $t);
    725 0           $ringbuffer->pack(RINGBUF_GLOBMSG_SZ, 'l', $t);
    726             #
    727             # set this last so reader doesn't read to soon
    728             #
    729 0 0         $ringbuffer->pack(RINGBUF_TOTALMSG_SZ, 'l', $len),
    730             $first = undef
    731             if $first;
    732              
    733 0           $len -= $t;
    734 0           $frag += $t;
    735             #
    736             # wait for ACK that its been read
    737             #
    738 0           sleep RINGBUF_RING_WAIT,
    739             $t = $ringbuffer->unpack(RINGBUF_GLOBMSG_SZ, 4, 'l')
    740             while $t;
    741             }
    742 0           $ringbuffer->pack(RINGBUF_TOTALMSG_SZ, 'l', 0);
    743             }
    744 0           $ringbuffer->unlock();
    745 0           return $self;
    746             }
    747              
    748             #/**
    749             # Gets a message from the global message area. Note that
    750             # this operation B lock the entire ring buffer
    751             # header, but instead relies on signalling of the message
    752             # chunk lengths.
    753             # Messages larger than the configured global message size
    754             # will be received in chunks; each chunk must back ACK'd by
    755             # the message receiver.
    756             #
    757             # @return the re-assembled global message buffer contents
    758             #
    759             #*/
    760             sub getGlobalMsg {
    761 0     0 0   my $self = shift;
    762 0           my $ringbuffer = $self->[RINGBUF_RING];
    763 0           my $globsz = $self->[RINGBUF_FLD_GLOBAL_SZ];
    764 0           my $result = '';
    765 0           my $frag;
    766             my $t;
    767             #
    768             # wait for indication that msg is available
    769             #
    770 0           my $len = $ringbuffer->unpack(RINGBUF_TOTALMSG_SZ, 4, 'l');
    771              
    772 0           sleep RINGBUF_RING_WAIT,
    773             $len = $ringbuffer->unpack(RINGBUF_TOTALMSG_SZ, 4, 'l')
    774             until $len;
    775              
    776 0           while ($len) {
    777             #
    778             # may be fragmented
    779             # wait for length field
    780             #
    781 0           sleep RINGBUF_RING_WAIT,
    782             $t = $ringbuffer->unpack(RINGBUF_GLOBMSG_SZ, 4, 'l')
    783             until $t;
    784              
    785 0           $ringbuffer->read($frag, RINGBUF_GLOBAL_MSG, $t);
    786 0           $len -= $t;
    787 0           $result .= $frag;
    788             #
    789             # ACK it
    790             #
    791 0           $ringbuffer->pack(RINGBUF_GLOBMSG_SZ, 'l', 0);
    792             }
    793 0           return $result;
    794             }
    795              
    796             1;