NAME
    Linux::Smaps - a Perl interface to /proc/PID/smaps

SYNOPSIS
      use Linux::Smaps;
      my $map=Linux::Smaps->new($pid);
      my @maps=$map->maps;
      my $private_dirty=$map->private_dirty;
      ...

DESCRIPTION
    The /proc/PID/smaps files in modern linuxes provides very detailed
    information about a processes memory consumption. It particularly
    includes a way to estimate the effect of copy-on-write. This module
    implements a Perl interface.

  CONSTRUCTOR, OBJECT INITIALIZATION, etc.
    Linux::Smaps->new
    Linux::Smaps->new($pid)
    Linux::Smaps->new(pid=>$pid, procdir=>'/proc')
    Linux::Smaps->new(filename=>'/proc/self/smaps')
        creates and initializes a "Linux::Smaps" object. On error an
        exception is thrown. "new()" may fail if the smaps file is not
        readable or if the file format is wrong.

        "new()" without parameter is equivalent to "new('self')" or
        "new(pid=>'self')". With the "procdir" parameter the mount point of
        the proc filesystem can be set if it differs from the standard
        "/proc".

        The "filename" parameter sets the name of the smaps file directly.
        This way also files outside the standard "/proc" tree can be
        analyzed.

    Linux::Smaps->new(uninitialized=>1)
        returns an uninitialized object. This makes "new()" simply skip the
        "update()" call after setting all parameters. Additional parameters
        like "pid", "procdir" or "filename" can be passed.

    $self->pid($pid) or $self->pid=$pid
    $self->procdir($dir) or $self->procdir=$dir
    $self->filename($name) or $self->filename=$name
        get/set parameters.

    $self->update
        reinitializes the object; rereads the underlying file. Returns the
        object or "undef" on error. The actual reason can be obtained via
        "lasterror()".

    $self->lasterror
        "update()" and "new()" return "undef" on failure. "lasterror()"
        returns a more verbose reason. Also $! can be checked.

  INFORMATION RETRIEVAL
    $self->vmas
        returns a list of "Linux::Smaps::VMA" objects each describing a vm
        area, see below.

    $self->size
    $self->rss
    $self->shared_clean
    $self->shared_dirty
    $self->private_clean
    $self->private_dirty
        these methods compute the sums of the appropriate values of all
        vmas.

    $self->stack
    $self->heap
    $self->vdso
        these are shortcuts to the appropiate "Linux::Smaps::VMA" objects.

    $self->all
    $self->named
    $self->unnamed
        In array context these functions return a list of
        "Linux::Smaps::VMA" objects representing named or unnamed maps or
        simply all vmas. Thus, in array context "all()" is equivalent to
        "vmas()".

        In scalar context these functions create a fake "Linux::Smaps::VMA"
        object containing the summaries of the "size", "rss",
        "shared_clean", "shared_dirty", "private_clean" and "private_dirty"
        fields.

    $self->names
        returns a list of vma names, i.e. the files that are mapped.

    ($new, $diff, $old)=$self->diff( $other )
        $other is assumed to be also a "Linux::Smaps" instance. 3 arrays are
        returned. The first one ($new) is a list of vmas that are contained
        in $self but not in $other. The second one ($diff) contains a list
        of pairs (2-element arrays) of vmas that differ between $self and
        $other. The 3rd one ($old) is a list of vmas that are contained in
        $other but not in $self.

        Vmas are identified as corresponding if their "vma_start" fields
        match. They are considered different if they differ in one of the
        following fields: "vma_end", "r", "w", "x", "mayshare", "file_off",
        "dev_major", "dev_minor", "inode", "file_name", "shared_clean",
        "shared_diry", "private_clean" and "private_dirty".

Linux::Smaps::VMA objects
    normally these objects represent a single vm area:

    $self->vma_start
    $self->vma_end
        start and end address

    $self->r
    $self->w
    $self->x
    $self->mayshare
        these correspond to the VM_READ, VM_WRITE, VM_EXEC and VM_MAYSHARE
        flags. see Linux kernel for more information.

    $self->file_off
    $self->dev_major
    $self->dev_minor
    $self->inode
    $self->file_name
        describe the file area that is mapped.

    $self->size
        the same as vma_end - vma_start but in kB.

    $self->rss
        what part is resident.

    $self->shared_clean
    $self->shared_dirty
    $self->private_clean
    $self->private_dirty
        "shared" means "page_count(page)>=2" (see Linux kernel), i.e. the
        page is shared between several processes. "private" pages belong
        only to one process.

        "dirty" pages are written to in RAM but not to the corresponding
        file.

Example: The copy-on-write effect
     use strict;
     use Linux::Smaps;

     my $x="a"x(1024*1024);         # a long string of "a"
     if( fork ) {
       my $s=Linux::Smaps->new($$);
       my $before=$s->all;
       $x=~tr/a/b/;                 # change "a" to "b" in place
       #$x="b"x(1024*1024);         # assignment
       $s->update;
       my $after=$s->all;
       foreach my $n (qw{rss size shared_clean shared_dirty
                         private_clean private_dirty}) {
         print "$n: ",$before->$n," => ",$after->$n,": ",
                $after->$n-$before->$n,"\n";
       }
       wait;
     } else {
       sleep 1;
     }

    This script may give the following output:

     rss: 4160 => 4252: 92
     size: 6916 => 7048: 132
     shared_clean: 1580 => 1596: 16
     shared_dirty: 2412 => 1312: -1100
     private_clean: 0 => 0: 0
     private_dirty: 168 => 1344: 1176

    $x is changed in place. Hence, the overall process size (size and rss)
    would not grow much. But before the "tr" operation $x was shared by
    copy-on-write between the 2 processes. Hence, we see a loss of
    "shared_dirty" (only a little more than our 1024 kB string) and almost
    the same growth of "private_dirty".

    Exchanging the "tr"-operation to an assingment of a MB of "b" yields the
    following figures:

     rss: 4160 => 5276: 1116
     size: 6916 => 8076: 1160
     shared_clean: 1580 => 1592: 12
     shared_dirty: 2432 => 1304: -1128
     private_clean: 0 => 0: 0
     private_dirty: 148 => 2380: 2232

    Now we see the overall process size grows a little more than a MB.
    "shared_dirty" drops almost a MB and "private_dirty" adds almost 2 MB.
    That means perl first constructs a 1 MB string of "b". This adds 1 MB to
    "size", "rss" and "private_dirty" and then copies it to $x. This takes
    another MB from "shared_dirty" and adds it to "private_dirty".

A special note on copy on write measurements
    The proc filesystem reports a page as shared if it belongs multiple
    processes and as private if it belongs to only one process. But there is
    an exception. If a page is currently paged out (that means it is not in
    core) all its attributes including the reference count are paged out as
    well. So the reference count cannot be read without paging in the page.
    In this case a page is neither reported as private nor as shared. It is
    only included in the process size.

    Thus, to exaclty measure which pages are shared among N processes at
    least one of them must be completely in core. This way all pages that
    can possibly be shared are in core and their reference counts are
    accessible.

    The mlockall(2) syscall may help in this situation. It locks all pages
    of a process to main memory:

     require 'syscall.ph';
     require 'sys/mmap.ph';

     0==syscall &SYS_mlockall, &MCL_CURRENT | &MCL_FUTURE or
         die "ERROR: mlockall failed: $!\n";

    This snippet in one of the processes locks it to the main memory. If all
    processes are created from the same parent it is executed best just
    before the parent starts to fork off children. The memory lock is not
    inherited by the children. So all private pages of the children are
    swappable.

    Since we are talking about Linux only the snippet can be shortened:

     0==syscall 152, 3 or die "ERROR: mlockall failed: $!\n";

    which removes the dependencies from syscall.ph and sys/mmap.ph.

EXPORT
    Not an Exporter;

SEE ALSO
    Linux Kernel.

AUTHOR
    Torsten Foertsch, <torsten.foertsch@gmx.net>

COPYRIGHT AND LICENSE
    Copyright (C) 2005-2007 by Torsten Foertsch

    This library is free software; you can redistribute it and/or modify it
    under the same terms as Perl itself, either Perl version 5.8.5 or, at
    your option, any later version of Perl 5 you may have available.

INSTALLATION
     perl Makefile.PL
     make
     make test
     make install

DEPENDENCIES
     perl 5.8.0
     Class::Member 1.3

