1563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark#!/usr/bin/perl 2563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 3563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Copyright (C) 2007 Apple Inc. All rights reserved. 4563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# 5563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Redistribution and use in source and binary forms, with or without 6563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# modification, are permitted provided that the following conditions 7563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# are met: 8563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# 9563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# 1. Redistributions of source code must retain the above copyright 10563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# notice, this list of conditions and the following disclaimer. 11563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# 2. Redistributions in binary form must reproduce the above copyright 12563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# notice, this list of conditions and the following disclaimer in the 13563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# documentation and/or other materials provided with the distribution. 14563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 15563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# its contributors may be used to endorse or promote products derived 16563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# from this software without specific prior written permission. 17563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# 18563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 19563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 22563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 29563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Script to run the Mac OS X leaks tool with more expressive '-exclude' lists. 30563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 31563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkuse strict; 32563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkuse warnings; 33563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 34563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkuse File::Basename; 35563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkuse Getopt::Long; 36563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 37563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub runLeaks($); 38563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub parseLeaksOutput(\@); 39563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub removeMatchingRecords(\@$\@); 40563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub reportError($); 41563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 42563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub main() 43563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark{ 44563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # Read options. 45563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $usage = 46563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark "Usage: " . basename($0) . " [options] pid | executable name\n" . 47563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark " --exclude-callstack regexp Exclude leaks whose call stacks match the regular expression 'regexp'.\n" . 48563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark " --exclude-type regexp Exclude leaks whose data types match the regular expression 'regexp'.\n" . 49563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark " --help Show this help message.\n"; 50563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 51563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my @callStacksToExclude = (); 52563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my @typesToExclude = (); 53563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $help = 0; 54563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 55563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $getOptionsResult = GetOptions( 56563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 'exclude-callstack:s' => \@callStacksToExclude, 57563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 'exclude-type:s' => \@typesToExclude, 58563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 'help' => \$help 59563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark ); 60563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $pidOrExecutableName = $ARGV[0]; 61563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 62563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark if (!$getOptionsResult || $help) { 63563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark print STDERR $usage; 64563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return 1; 65563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 66563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 67563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark if (!$pidOrExecutableName) { 68563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark reportError("Missing argument: pid | executable."); 69563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark print STDERR $usage; 70563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return 1; 71563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 72563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 73563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # Run leaks tool. 74563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $leaksOutput = runLeaks($pidOrExecutableName); 75563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark if (!$leaksOutput) { 76563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return 1; 77563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 78563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 79563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $leakList = parseLeaksOutput(@$leaksOutput); 80563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark if (!$leakList) { 81563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return 1; 82563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 83563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 84563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # Filter output. 85563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $leakCount = @$leakList; 86563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark removeMatchingRecords(@$leakList, "callStack", @callStacksToExclude); 87563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark removeMatchingRecords(@$leakList, "type", @typesToExclude); 88563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $excludeCount = $leakCount - @$leakList; 89563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 90563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # Dump results. 91563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark print $leaksOutput->[0]; 92563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark print $leaksOutput->[1]; 93563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark foreach my $leak (@$leakList) { 94563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark print $leak->{"leaksOutput"}; 95563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 96563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 97563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark if ($excludeCount) { 98563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark print "$excludeCount leaks excluded (not printed)\n"; 99563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 100563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 101563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return 0; 102563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark} 103563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 104563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkexit(main()); 105563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 106563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Returns the output of the leaks tool in list form. 107563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub runLeaks($) 108563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark{ 109563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my ($pidOrExecutableName) = @_; 110563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 111563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my @leaksOutput = `leaks $pidOrExecutableName`; 112563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark if (!@leaksOutput) { 113563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark reportError("Error running leaks tool."); 114563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return; 115563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 116563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 117563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return \@leaksOutput; 118563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark} 119563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 120563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Returns a list of hash references with the keys { address, size, type, callStack, leaksOutput } 121563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub parseLeaksOutput(\@) 122563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark{ 123563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my ($leaksOutput) = @_; 124563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 125563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # Format: 126563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # Process 00000: 1234 nodes malloced for 1234 KB 127563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # Process 00000: XX leaks for XXX total leaked bytes. 128563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # Leak: 0x00000000 size=1234 [instance of 'blah'] 129563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # 0x00000000 0x00000000 0x00000000 0x00000000 a..d.e.e 130563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # ... 131563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # Call stack: leak_caller() | leak() | malloc 132563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # 133563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # We treat every line except for Process 00000: and Leak: as optional 134563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 135d0825bca7fe65beaee391d30da42e937db621564Steve Block # Newer versions of the leaks output have a header section at the top, with the first line describing the version of the output format. 136d0825bca7fe65beaee391d30da42e937db621564Steve Block # If we detect the new format is being used then we eat all of the header section so the output matches the format of older versions. 137d0825bca7fe65beaee391d30da42e937db621564Steve Block # FIXME: In the future we may wish to propagate this section through to our output. 138d0825bca7fe65beaee391d30da42e937db621564Steve Block if ($leaksOutput->[0] =~ /^leaks Report Version:/) { 139d0825bca7fe65beaee391d30da42e937db621564Steve Block while ($leaksOutput->[0] !~ /^Process /) { 140d0825bca7fe65beaee391d30da42e937db621564Steve Block shift @$leaksOutput; 141d0825bca7fe65beaee391d30da42e937db621564Steve Block } 142d0825bca7fe65beaee391d30da42e937db621564Steve Block } 143d0825bca7fe65beaee391d30da42e937db621564Steve Block 144563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my ($leakCount) = ($leaksOutput->[1] =~ /[[:blank:]]+([0-9]+)[[:blank:]]+leaks?/); 145563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark if (!defined($leakCount)) { 146563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark reportError("Could not parse leak count reported by leaks tool."); 147563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return; 148563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 149563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 150563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my @leakList = (); 151563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark for my $line (@$leaksOutput) { 152563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark next if $line =~ /^Process/; 153563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark next if $line =~ /^node buffer added/; 154563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 155563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark if ($line =~ /^Leak: /) { 156563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my ($address) = ($line =~ /Leak: ([[:xdigit:]x]+)/); 157563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark if (!defined($address)) { 158563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark reportError("Could not parse Leak address."); 159563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return; 160563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 161563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 162563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my ($size) = ($line =~ /size=([[:digit:]]+)/); 163563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark if (!defined($size)) { 164563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark reportError("Could not parse Leak size."); 165563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return; 166563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 167563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 168563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my ($type) = ($line =~ /'([^']+)'/); #' 169563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark if (!defined($type)) { 170563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark $type = ""; # The leaks tool sometimes omits the type. 171563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 172563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 173563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my %leak = ( 174563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark "address" => $address, 175563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark "size" => $size, 176563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark "type" => $type, 177563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark "callStack" => "", # The leaks tool sometimes omits the call stack. 178563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark "leaksOutput" => $line 179563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark ); 180563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark push(@leakList, \%leak); 181563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } else { 182563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark $leakList[$#leakList]->{"leaksOutput"} .= $line; 183563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark if ($line =~ /Call stack:/) { 184563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark $leakList[$#leakList]->{"callStack"} = $line; 185563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 186563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 187563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 188563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 189563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark if (@leakList != $leakCount) { 190563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $parsedLeakCount = @leakList; 191563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark reportError("Parsed leak count($parsedLeakCount) does not match leak count reported by leaks tool($leakCount)."); 192563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return; 193563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 194563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 195563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return \@leakList; 196563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark} 197563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 198563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub removeMatchingRecords(\@$\@) 199563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark{ 200563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my ($recordList, $key, $regexpList) = @_; 201563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 202563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark RECORD: for (my $i = 0; $i < @$recordList;) { 203563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $record = $recordList->[$i]; 204563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 205563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark foreach my $regexp (@$regexpList) { 206563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark if ($record->{$key} =~ $regexp) { 207563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark splice(@$recordList, $i, 1); 208563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark next RECORD; 209563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 210563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 211563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 212563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark $i++; 213563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 214563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark} 215563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 216563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub reportError($) 217563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark{ 218563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my ($errorMessage) = @_; 219563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 220563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark print STDERR basename($0) . ": $errorMessage\n"; 221563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark} 222