View on
Dean Arnold > Devel-RingBuffer-0.31 > Devel::RingBuffer



Annotate this POD

Related Modules



New  1
Open  0
View/Report Bugs
Module Version: 0.31   Source  


Devel::RingBuffer - Shared memory ring buffers for Perl scripts diagnosis/debug


    #    create ringbuffer
        use Devel::RingBuffer;
        use Devel::RingBuffer::TieInt;

        my $ringbuf = Devel::RingBuffer->new(
                File => 'somefile.trace',
                Rings => 20,
                Slots => 20,
                SlotSize => 300,
                MessageSize => 256,
                GlobalSize => 24 * 1024,
                StopOnCreate => 0,
                TraceOnCreate => 1) || die "Can't create a ring buffer.";

        my $ring = $ringbuf->allocate();


Provides shared memory structures (using memory mapped files via IPC::Mmap) to be used by diagnostic and debugger applications for Perl scripts (see Devel::STrace). Using XS/C code to maximize performance, creates a set of ringbuffers with a configurable number of slots. Each slot includes a field for a linenumber, a timestamp, and a fully qualified subroutine name. Each ring buffer also includes additional headers and fields to support diagnostic interfaces, e.g., watched expressions, command/reponse interfaces to the monitored applications, etc.


AUT - Application Under Test; the application started with -d:STrace
Monitor - monitoring application, i.e., the debugger view

Ring Buffer Structure

The ring buffer structure is configured externally using the following set of environment variables:


the number of ring buffers (default 20). In practice, each thread of each process of the AUT allocates its own buffer from this pool, so this needs to be large enough to handle the maximum number of concurrent processes/threads.


the number of slots per ring buffer (default 10)


Name of the file (or the Win32 namespace). Default is the script name, sans any file qualifiers, with the PID and timestamp as the file qualifier, created in the /tmp directory, e.g.,


size in bytes of the global message area aka GMA (default 16K). This area is a large buffer used for communicating large data between the monitor and AUT, and is shared by all threads of the AUT.


size in bytes of the per-thread command/response message area (default 256 bytes). Each ring buffer includes a 4 byte command/response tag, and an associated message area where control information can be exchanged between the AUT and the monitor. This area is used, e.g., to send breakpoint information from a monitor to the AUT.


Sets slot size. Default size is 200 bytes, plus the integer linenumber and double precision timestamp header.


Sets the global Stop On Create flag. This flag causes all newly created threads (including root threads of new processes) to have their $DB::signal flag set, thus causing them to stop in DB::DB() and wait for a command. Default is off.


Sets the global Trace On Create flag. This flag causes all newly created threads (including root threads of new processes) to have their $DB::trace flag set, thus causing them to enter DB::DB() for purposes of tracing current statement execution.

Note that monitored applications with large numbers of threads or processes, and that use large msgarea fields and/or large numbers of ring slots, can lead to very large memory mappings, which may cause memory management issues.

The following C type definitions represent the structure of the memory mapped ring buffer file (refer to ringbuffer.h for precise definitions):

    typedef struct {
        int single;                  /* tied to $DB::single (global) */
        int msgarea_sz;              /* size of RingBuffer.msgarea */
        int max_buffers;             /* max number of buffers available */
        int slots;                   /* number of slots per buffer */
        int slot_sz;                 /* size of each slot (plus linenumber and timestamp header) */
        int stop_on_create;          /* 1 => new threads created with ring.signal = 1 */
        int trace_on_create;         /* 1 => new threads created with ring.trace = 1 */
        int global_sz;               /* size of RingBuffers.global_buffer */
                int globmsg_sz;              /* size of current global msg contents */
                char global_buffer[];        /* global message buffer (large, >16K) */
        char free_map[];             /* booleans to indicate if the
                                        buffer of same index is free */
        ring_buffer_t rings[];       /* the ringbuffers */
    } ring_buffers_t;

A global file lock (or semaphore on Win32 platforms) is used control access to some members of the memory mapped file, specifically, the global message area (see below), the free ring map, and during initialization of the entire file. In addition, each process in the AUT uses an additional threads::shared locking variable to control thread-level access.

Note that any non-char fields in these structures are pack()'ed in platform specific format, and must be unpack()'d when read from the structures.

Per-thread Ring Buffer

Each thread of each process in the AUT allocates a ring buffer on its first pass through either DB::DB() or DB::sub() to communicate its execution state, and perform per-thread interactions with the Monitor:

    typedef struct {
        int pid;                    /* pid of slot buffer owner */
        int tid;                    /* tid of slot buffer owner */
        int currSlot;               /* current slot */
        int depth;                  /* current stack depth */
        int trace;                  /* tied to $DB::trace (per-thread/proc) */
        int signal;                 /* tied to $DB::signal (per-thread/proc) */
                int baseoff;                /* offset from this struct to entire ring buffer base */
                watch_expr_t watches[4];    /* watch expressions */
                int cmdready;               /* 1 => command sent; -2 => response ready; 0 => empty */
                char command[4];            /* ext. command entry */
                int msglen;                 /* length of msg */
                char msgarea[];             /* ext. message area */
        ring_slot_t slots[];        /* slots */
    } ring_buffer_t;

When a thread exits, the DESTROY() method of the Devel::RingBuffer or Devel::RingBuffer::Ring object (whichever occurs first) will free the allocated ring buffer.


        typedef struct {
                int linenumber;         /* current execution linenumber in subroutine */
                double timestamp;       /* Time::HiRes::time() timestamp of the execution */
                char subroutine[1];     /* name of subroutine (as reported by $DB::sub) */
                                /* size determine by the slot_sz parameter */
        } ring_slot_t;

Watch Expressions

Each ring includes a set of slots for use with watch expressions, defined as

        typedef struct {
        int inuse;          /* 1 => in use, -2 => freeing, 0 => freed */
        int exprlength;     /* length of expr text */
        char expr[256];     /* expr text */
        int resready;       /* 0 => monitor has read last result */
        int reslength;      /* result length before truncation;
                               length < 0 the expression eval failed, with
                               error text in the result area;
                               length == 0 means result was undef */
        char result[512];   /* result text */
        } watch_expr_t;

The Monitor can set an expression, and each AUT thread scans its watchlist for expressions to evaluate. A lazy concurrency control mechanism is used to minimize locking overhead:


Refer to included classdocs for summary and detailed descriptions of methods.







strace(1) (or truss(1))


Dean Arnold

Copyright(C) 2006, Dean Arnold, Presicient Corp., USA. All rights reserved.

Permission is granted to use this software under the same terms as Perl itself. Refer to the Perl Artistic License for details.

syntax highlighting: