#!@@PERL@@ -w # -*- perl -*- use strict; use warnings; =head1 NAME memory - Plugin to monitor memory usage =head1 CONFIGURATION No configuration =head2 Warning and Critical Values Warning and critical values can be passed in environment variables as byte values or they can be percentages using a % sign and the value will be calculated dynamically by the plugin. Thus, to warn on swap at 50% the configuration is: [memory] env.swap_warning 50% Note that swap is calculated against the total swap amount (SwapTotal), and all other fields are calculated against the total amount of memory (MemTotal). =head1 AUTHORS Original Author: Jimmy Olsen =head2 CONTRIBUTORS =over 4 =item Mike Fedyk Slab, SwapCached, PageTables, VmallocUsed, Mapped, Active, Inactive, 2.4 Rmap & 2.6 =item Juha-Matti Tapio Input on swap_cache and VMalloc Used on 64 bit machines. =item Nicolai Langfeldt Editor =back =head1 LICENSE GPLv2 =head1 MAGIC MARKERS #%# family=auto #%# capabilities=autoconf =cut use Munin::Plugin; if ($ARGV[0] and $ARGV[0] eq "autoconf") { if (-r "/proc/meminfo") { print "yes\n"; exit 0; } else { print "no (/proc/meminfo not found)\n"; exit 0; } } my %mems; &fetch_meminfo; if ($ARGV[0] and $ARGV[0] eq "config") { print "graph_args --base 1024 -l 0 --upper-limit ",$mems{'MemTotal'},"\n"; print "graph_vlabel Bytes\n"; print "graph_title Memory usage\n"; print "graph_category system\n"; print "graph_info This graph shows what the machine uses memory for.\n"; print "graph_order ", "apps "; print "page_tables " if exists $mems{'PageTables'}; print "per_cpu " if exists $mems{'Percpu'}; print "swap_cache " if exists $mems{'SwapCached'}; #print "vmalloc_used " if exists $mems{'VmallocUsed'}; print "slab " if exists $mems{'Slab'}; print "shmem " if exists $mems{'Shmem'}; print "cached ", "buffers ", "free ", "swap ", "\n"; print "apps.label apps\n"; print "apps.draw AREA\n"; print "apps.info Memory used by user-space applications.\n"; print "apps.colour COLOUR0\n"; print "buffers.label buffers\n"; print "buffers.draw STACK\n"; print "buffers.info Block device (e.g. harddisk) cache. ", "Also where \"dirty\" blocks are stored until written.\n"; print "buffers.colour COLOUR5\n"; print "swap.label swap\n"; print "swap.draw STACK\n"; print "swap.info Swap space used.\n"; # Fixed color for swap: red. Otherwise the above conditional fields could # lead to color shifts due to changes of the kernel or other environment # situations. print "swap.colour COLOUR7\n"; print "cached.label cache\n"; print "cached.draw STACK\n"; print "cached.info Parked file data (file content) cache.\n"; print "cached.colour COLOUR4\n"; print "free.label unused\n"; print "free.draw STACK\n"; print "free.info Wasted memory. Memory that is not ", "used for anything at all.\n"; print "free.colour COLOUR6\n"; if (exists $mems{'Shmem'}) { print "shmem.label shmem\n"; print "shmem.draw STACK\n"; print "shmem.info Shared Memory (SYSV SHM segments, tmpfs).\n"; print "shmem.colour COLOUR9\n"; } if (exists $mems{'Slab'}) { print "slab.label slab_cache\n"; print "slab.draw STACK\n"; print "slab.info Memory used by the kernel (major users ", " are caches like inode, dentry, etc).\n"; print "slab.colour COLOUR3\n"; } if (exists $mems{'SwapCached'}) { print "swap_cache.label swap_cache\n"; print "swap_cache.draw STACK\n"; print "swap_cache.info A piece of memory that keeps track of pages ", "that have been fetched from swap but not yet been modified.\n"; print "swap_cache.colour COLOUR2\n"; } if (exists $mems{'PageTables'}) { print "page_tables.label page_tables\n"; print "page_tables.draw STACK\n"; print "page_tables.info Memory used to map between virtual and ", "physical memory addresses.\n"; print "page_tables.colour COLOUR1\n"; } if (exists $mems{'Percpu'}) { print "per_cpu.label per_cpu\n"; print "per_cpu.draw STACK\n"; print "per_cpu.info Per CPU allocations\n"; print "per_cpu.colour COLOUR20\n"; } if (exists $mems{'VmallocUsed'}) { print "vmalloc_used.label vmalloc_used\n"; # print "vmalloc_used.draw STACK\n"; print "vmalloc_used.draw LINE2\n"; print "vmalloc_used.info 'VMalloc' (kernel) memory used\n"; print "vmalloc_used.colour COLOUR8\n"; } if (exists $mems{'Committed_AS'}) { print "committed.label committed\n"; print "committed.draw LINE2\n"; # Linux machines frequently overcommit - this is not a error # condition or even worrying. But sometimes overcommit shows # memory leaks so we want to graph it. # print "committed.warning ", $mems{'SwapTotal'} + $mems{'MemTotal'}, "\n"; print "committed.info The amount of memory allocated to programs. ", "Overcommitting is normal, but may indicate memory leaks.\n"; print "committed.colour COLOUR10\n"; } if (exists $mems{'Mapped'}) { print "mapped.label mapped\n"; print "mapped.draw LINE2\n"; print "mapped.info All mmap()ed pages.\n"; print "mapped.colour COLOUR11\n"; } if (exists $mems{'Active'}) { print "active.label active\n"; print "active.draw LINE2\n"; print "active.info Memory recently used. Not reclaimed unless ", "absolutely necessary.\n"; print "active.colour COLOUR12\n"; } if (exists $mems{'ActiveAnon'}) { print "active_anon.label active_anon\n"; print "active_anon.draw LINE1\n"; print "active_anon.colour COLOUR13\n"; } if (exists $mems{'ActiveCache'}) { print "active_cache.label active_cache\n"; print "active_cache.draw LINE1\n"; print "active_cache.colour COLOUR14\n"; } if (exists $mems{'Inactive'}) { print "inactive.label inactive\n"; print "inactive.draw LINE2\n"; print "inactive.info Memory not currently used.\n"; print "inactive.colour COLOUR15\n"; } if (exists $mems{'Inact_dirty'}) { print "inact_dirty.label inactive_dirty\n"; print "inact_dirty.draw LINE1\n"; print "inact_dirty.info Memory not currently used, but in need of ", "being written to disk.\n"; print "inact_dirty.colour COLOUR16\n"; } if (exists $mems{'Inact_laundry'}) { print "inact_laundry.label inactive_laundry\n"; print "inact_laundry.draw LINE1\n"; print "inact_laundry.colour COLOUR17\n"; } if (exists $mems{'Inact_clean'}) { print "inact_clean.label inactive_clean\n"; print "inact_clean.draw LINE1\n"; print "inact_clean.info Memory not currently used.\n"; print "inact_clean.colour COLOUR18\n"; } if (exists $mems{'KSM'}) { print "ksm_sharing.label KSM sharing\n"; print "ksm_sharing.draw LINE2\n"; print "ksm_sharing.colour FFBFFF\n"; print "ksm_sharing.info Memory saved by KSM sharing\n"; print "ksm_sharing.colour COLOUR19\n"; } for my $field (qw(apps buffers swap cached free slab swap_cache page_tables per_cpu vmalloc_used committed mapped active active_anon active_cache inactive inact_dirty inact_laundry inact_clean shmem)) { my ($warning, $critical) = get_thresholds($field); my $total = $mems{MemTotal}; $total = $mems{SwapTotal} if($field eq "swap"); $warning = adjust_threshold($warning, $total); $critical = adjust_threshold($critical, $total); print "$field.warning $warning\n" if defined $warning; print "$field.critical $critical\n" if defined $critical; } exit 0; } # Any optional value needs to be initialized to zero if it's used in a calculation below # and is has not been set by &fetch_meminfo if (exists $mems{'Slab'}) { print "slab.value ", $mems{'Slab'}, "\n"; } else { $mems{'Slab'} = 0; } if (exists $mems{'SwapCached'}) { print "swap_cache.value ", $mems{'SwapCached'}, "\n"; } else { $mems{'SwapCached'} = 0; } if (exists $mems{'PageTables'}) { print "page_tables.value ", $mems{'PageTables'}, "\n"; } else { $mems{'PageTables'} = 0; } if (exists $mems{'Percpu'}) { print "per_cpu.value ", $mems{'Percpu'}, "\n"; } else { $mems{'Percpu'} = 0; } if (exists $mems{'VmallocUsed'}) { print "vmalloc_used.value ", $mems{'VmallocUsed'}, "\n"; } else { $mems{'VmallocUsed'} = 0; } # Having default values for mandatory fields, even when they are not on the system $mems{'MemFree'} ||= 0; $mems{'Buffers'} ||= 0; $mems{'Cached'} ||= 0; $mems{'Slab'} ||= 0; $mems{'PageTables'} ||= 0; $mems{'Percpu'} ||= 0; $mems{'SwapCached'} ||= 0; $mems{'SwapFree'} ||= 0; $mems{'SwapTotal'} ||= 0; print "apps.value ", $mems{'MemTotal'} -$mems{'MemFree'} -$mems{'Buffers'} -$mems{'Cached'} -$mems{'Slab'} -$mems{'PageTables'} -$mems{'Percpu'} -$mems{'SwapCached'} ,"\n"; print "free.value ", $mems{'MemFree'}, "\n"; print "buffers.value ", $mems{'Buffers'}, "\n"; print "cached.value ", $mems{'Cached'} - (defined($mems{'Shmem'}) ? $mems{'Shmem'} : 0), "\n"; print "swap.value ", $mems{'SwapTotal'} - $mems{'SwapFree'}, "\n"; print "shmem.value ", $mems{'Shmem'}, "\n" if exists $mems{'Shmem'}; print "committed.value ", $mems{'Committed_AS'}, "\n" if exists $mems{'Committed_AS'}; print "mapped.value ", $mems{'Mapped'}, "\n" if exists $mems{'Mapped'}; print "active.value ", $mems{'Active'}, "\n" if exists $mems{'Active'}; print "active_anon.value ", $mems{'ActiveAnon'}, "\n" if exists $mems{'ActiveAnon'}; print "active_cache.value ", $mems{'ActiveCache'}, "\n" if exists $mems{'ActiveCache'}; print "inactive.value ", $mems{'Inactive'}, "\n" if exists $mems{'Inactive'}; print "inact_dirty.value ", $mems{'Inact_dirty'}, "\n" if exists $mems{'Inact_dirty'}; print "inact_laundry.value ", $mems{'Inact_laundry'}, "\n" if exists $mems{'Inact_laundry'}; print "inact_clean.value ", $mems{'Inact_clean'}, "\n" if exists $mems{'Inact_clean'}; print "ksm_sharing.value ", $mems{'KSM'}, "\n" if exists $mems{'KSM'}; exit 0; sub fetch_meminfo { open (IN, "/proc/meminfo") || die "Could not open /proc/meminfo for reading: $!"; while () { if (/^(\w+):\s*(\d+)\s+kb/i) { $mems{"$1"} = $2 * 1024; } } close (IN); # Only 2.6 and above has slab reported in meminfo, so read # slabinfo if it isn't in meminfo if (!$mems{Slab}) { &fetch_slabinfo; } # Support 2.4 Rmap VM based kernels if (!$mems{'Inactive'} && $mems{'Inact_dirty'} && $mems{'Inact_laundry'} && $mems{'Inact_clean'}) { $mems{'Inactive'} = $mems{'Inact_dirty'} + $mems{'Inact_laundry'} + $mems{'Inact_clean'}; } &fetch_ksm; } sub fetch_slabinfo { # In 2.0 there is no slabinfo file, so return if the file doesn't open open (IN, "/proc/slabinfo") || return; my @slabinfo; my $tot_slab_pages = 0; my $slab_version = ; if ($slab_version =~ /^slabinfo - version: 1.1/) { while () { if (!/^slabinfo/) { @slabinfo = split; $tot_slab_pages += $slabinfo[5]; } } } close (IN); $mems{'Slab'} = $tot_slab_pages * 4096 if $tot_slab_pages gt 0; } sub fetch_ksm { open (IN, "/sys/kernel/mm/ksm/run") || return; my $value = ; if (defined $value) { chomp $value; return unless $value; } close (IN); open (IN, "/sys/kernel/mm/ksm/pages_sharing") || return; $value = ; if (defined $value) { chomp $value; $mems{'KSM'} = $value * 4096; } close (IN); }