Changeset 225937 in webkit
- Timestamp:
- Dec 14, 2017, 3:26:12 PM (7 years ago)
- Location:
- trunk/Tools
- Files:
-
- 7 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Tools/ChangeLog
r225935 r225937 1 2017-12-14 Alexey Proskuryakov <ap@apple.com> 2 3 Improve leaks detector output 4 https://bugs.webkit.org/show_bug.cgi?id=180828 5 6 Reviewed by Joseph Pecoraro. 7 8 Fixing two issues: 9 1. run-leaks omits many lines from leaks tool output, making it incompatible with 10 other tools. Notably, symbolication cannot be performed. 11 2. run-leaks output goes to "run-webkit-tests --debug-rwt-logging" output. This 12 makes it much longer than needed, sometimes even overloading buildbot. We don't 13 need full output in test log, as separate files are created for each of these. 14 15 * Scripts/run-leaks: Represent each line in leaks output when parsing, and print 16 everything except for explicitly excluded leaks. From my testing and reading 17 the code, it looks like none of our tools should be broken by this change. 18 19 * Scripts/webkitpy/port/leakdetector.py: I couldn't find a way to run a helper tool 20 without dumping all of its output to debug log, so switched to using a file for leaks. 21 22 * Scripts/webkitperl/run-leaks_unittest/run-leaks-report-v1.0.pl: 23 * Scripts/webkitperl/run-leaks_unittest/run-leaks-report-v2.0-new.pl: 24 * Scripts/webkitperl/run-leaks_unittest/run-leaks-report-v2.0-old.pl: 25 * Scripts/webkitpy/port/leakdetector_unittest.py: 26 Updated tests for new behavior. 27 1 28 2017-12-14 Brady Eidson <beidson@apple.com> 2 29 -
trunk/Tools/Scripts/run-leaks
r165676 r225937 47 47 " --exclude-callstack regexp Exclude leaks whose call stacks match the regular expression 'regexp'.\n" . 48 48 " --exclude-type regexp Exclude leaks whose data types match the regular expression 'regexp'.\n" . 49 " --output-file path Store result in a file. If not specified, standard output is used.\n" . 49 50 " --help Show this help message.\n"; 50 51 51 52 my @callStacksToExclude = (); 52 53 my @typesToExclude = (); 54 my $outputPath; 53 55 my $help = 0; 54 56 … … 56 58 'exclude-callstack:s' => \@callStacksToExclude, 57 59 'exclude-type:s' => \@typesToExclude, 60 'output-file:s' => \$outputPath, 58 61 'help' => \$help 59 62 ); … … 77 80 } 78 81 79 my $ leakList = parseLeaksOutput(@$leaksOutput);80 if (!$ leakList) {82 my $parsedOutput = parseLeaksOutput(@$leaksOutput); 83 if (!$parsedOutput) { 81 84 return 1; 82 85 } 83 86 84 87 # Filter output. 85 my $leakCount = @$leakList; 86 removeMatchingRecords(@$leakList, "callStack", @callStacksToExclude); 87 removeMatchingRecords(@$leakList, "type", @typesToExclude); 88 my $excludeCount = $leakCount - @$leakList; 89 90 # Dump results. 91 print $leaksOutput->[0]; 92 print $leaksOutput->[1]; 93 foreach my $leak (@$leakList) { 94 print $leak->{"leaksOutput"}; 88 # FIXME: Adjust the overall leak count in the output accordingly. This will affect callers, notably leakdetector.py. 89 my $parsedLineCount = @$parsedOutput; 90 removeMatchingRecords(@$parsedOutput, "callStack", @callStacksToExclude); 91 removeMatchingRecords(@$parsedOutput, "type", @typesToExclude); 92 my $excludeCount = $parsedLineCount - @$parsedOutput; 93 94 my $outputFH; 95 if (defined $outputPath) { 96 open($outputFH , '>', $outputPath) or die "Could not open $outputPath for writing"; 97 } else { 98 $outputFH = *STDOUT; 99 } 100 101 foreach my $leak (@$parsedOutput) { 102 print $outputFH $leak->{"leaksOutput"}; 95 103 } 96 104 97 105 if ($excludeCount) { 98 print "$excludeCount leaks excluded (not printed)\n";106 print $outputFH "$excludeCount leaks excluded (not printed)\n"; 99 107 } 100 108 … … 133 141 # We treat every line except for Process 00000: and Leak: as optional 134 142 135 # Skip header section until the first two "Process " lines. 136 # FIXME: In the future we may wish to propagate the header section through to our output. 137 until ($leaksOutput->[0] =~ /^Process /) { 138 shift @$leaksOutput; 139 } 140 141 my ($leakCount) = ($leaksOutput->[1] =~ /[[:blank:]]+([0-9]+)[[:blank:]]+leaks?/); 142 if (!defined($leakCount)) { 143 reportError("Could not parse leak count reported by leaks tool."); 144 return; 145 } 146 147 my @leakList = (); 143 my $leakCount; 144 my @parsedOutput = (); 145 my $parsingLeak = 0; 146 my $parsedLeakCount = 0; 148 147 for my $line (@$leaksOutput) { 149 next if $line =~ /^Process/; 150 next if $line =~ /^node buffer added/; 151 148 if ($line =~ /^Process \d+: (\d+) leaks?/) { 149 $leakCount = $1; 150 } 151 152 if ($line eq "\n") { 153 $parsingLeak = 0; 154 } 155 152 156 if ($line =~ /^Leak: /) { 157 $parsingLeak = 1; 158 $parsedLeakCount++; 153 159 my ($address) = ($line =~ /Leak: ([[:xdigit:]x]+)/); 154 160 if (!defined($address)) { … … 163 169 } 164 170 171 # FIXME: This code seems wrong, the leaks tool doesn't actually use single quotes. 172 # We should reconcile with other format changes that happened since, such as the 173 # addition of zone information. 165 174 my ($type) = ($line =~ /'([^']+)'/); #' 166 175 if (!defined($type)) { … … 175 184 "leaksOutput" => $line 176 185 ); 177 push(@leakList, \%leak); 186 push(@parsedOutput, \%leak); 187 } elsif ($parsingLeak) { 188 $parsedOutput[$#parsedOutput]->{"leaksOutput"} .= $line; 189 if ($line =~ /Call stack:/) { 190 $parsedOutput[$#parsedOutput]->{"callStack"} = $line; 191 } 178 192 } else { 179 $leakList[$#leakList]->{"leaksOutput"} .= $line; 180 if ($line =~ /Call stack:/) { 181 $leakList[$#leakList]->{"callStack"} = $line; 182 } 183 } 184 } 185 186 if (@leakList != $leakCount) { 187 my $parsedLeakCount = @leakList; 188 reportError("Parsed leak count($parsedLeakCount) does not match leak count reported by leaks tool($leakCount)."); 193 my %nonLeakLine = ( 194 "leaksOutput" => $line 195 ); 196 push(@parsedOutput, \%nonLeakLine); 197 } 198 } 199 200 if ($parsedLeakCount != $leakCount) { 201 reportError("Parsed leak count ($parsedLeakCount) does not match leak count reported by leaks tool ($leakCount)."); 189 202 return; 190 203 } 191 204 192 return \@ leakList;205 return \@parsedOutput; 193 206 } 194 207 … … 201 214 202 215 foreach my $regexp (@$regexpList) { 203 if ( $record->{$key} =~ $regexp) {216 if (defined $record->{$key} and $record->{$key} =~ $regexp) { 204 217 splice(@$recordList, $i, 1); 205 218 next RECORD; -
trunk/Tools/Scripts/webkitperl/run-leaks_unittest/run-leaks-report-v1.0.pl
r103338 r225937 63 63 my $expectedOutput = 64 64 [ 65 { 66 'leaksOutput' => 'Process 1602: 86671 nodes malloced for 13261 KB' 67 }, 68 { 69 'leaksOutput' => 'Process 1602: 8 leaks for 160 total leaked bytes.' 70 }, 65 71 { 66 72 'leaksOutput' => join('', split(/\n/, <<EOF)), -
trunk/Tools/Scripts/webkitperl/run-leaks_unittest/run-leaks-report-v2.0-new.pl
r103338 r225937 79 79 [ 80 80 { 81 'leaksOutput' => 'Process: DumpRenderTree [29903]' 82 }, 83 { 84 'leaksOutput' => 'Path: /Volumes/Data/Build/Debug/DumpRenderTree' 85 }, 86 { 87 'leaksOutput' => 'Load Address: 0x102116000' 88 }, 89 { 90 'leaksOutput' => 'Identifier: DumpRenderTree' 91 }, 92 { 93 'leaksOutput' => 'Version: ??? (???)' 94 }, 95 { 96 'leaksOutput' => 'Code Type: X86-64 (Native)' 97 }, 98 { 99 'leaksOutput' => 'Parent Process: Python [29892]' 100 }, 101 { 102 'leaksOutput' => '' 103 }, 104 { 105 'leaksOutput' => 'Date/Time: 2011-11-14 11:12:45.706 -0800' 106 }, 107 { 108 'leaksOutput' => 'OS Version: Mac OS X 10.7.2 (11C74)' 109 }, 110 { 111 'leaksOutput' => 'Report Version: 7' 112 }, 113 { 114 'leaksOutput' => '' 115 }, 116 { 117 'leaksOutput' => 'leaks Report Version: 2.0' 118 }, 119 { 120 'leaksOutput' => 'leaks(12871,0xacdfa2c0) malloc: process 89617 no longer exists, stack logs deleted from /tmp/stack-logs.89617.DumpRenderTree.A2giy6.index' 121 }, 122 { 123 'leaksOutput' => 'Process 29903: 60015 nodes malloced for 7290 KB' 124 }, 125 { 126 'leaksOutput' => 'Process 29903: 2 leaks for 1008 total leaked bytes.' 127 }, 128 { 81 129 'leaksOutput' => join('', split(/\n/, <<EOF)), 82 130 Leak: 0x7f9a3a612810 size=576 zone: DefaultMallocZone_0x10227b000 URLConnectionLoader::LoaderConnectionEventQueue C++ CFNetwork -
trunk/Tools/Scripts/webkitperl/run-leaks_unittest/run-leaks-report-v2.0-old.pl
r103338 r225937 59 59 [ 60 60 { 61 'leaksOutput' => 'leaks Report Version: 2.0' 62 }, 63 { 64 'leaksOutput' => 'Process: Safari [53606]' 65 }, 66 { 67 'leaksOutput' => 'Path: /Applications/Safari.app/Contents/MacOS/Safari' 68 }, 69 { 70 'leaksOutput' => 'Load Address: 0x100000000' 71 }, 72 { 73 'leaksOutput' => 'Identifier: com.apple.Safari' 74 }, 75 { 76 'leaksOutput' => 'Version: 5.0 (6533.9)' 77 }, 78 { 79 'leaksOutput' => 'Build Info: WebBrowser-75330900~1' 80 }, 81 { 82 'leaksOutput' => 'Code Type: X86-64 (Native)' 83 }, 84 { 85 'leaksOutput' => 'Parent Process: perl5.10.0 [53599]' 86 }, 87 { 88 'leaksOutput' => '' 89 }, 90 { 91 'leaksOutput' => 'Date/Time: 2010-05-27 11:42:27.356 -0700' 92 }, 93 { 94 'leaksOutput' => 'OS Version: Mac OS X 10.6.3 (10D571)' 95 }, 96 { 97 'leaksOutput' => 'Report Version: 6' 98 }, 99 { 100 'leaksOutput' => '' 101 }, 102 { 103 'leaksOutput' => 'Process 53606: 112295 nodes malloced for 22367 KB' 104 }, 105 { 106 'leaksOutput' => 'Process 53606: 1 leak for 32 total leaked bytes.' 107 }, 108 { 61 109 'leaksOutput' => join('', split(/\n/, <<EOF)), 62 110 Leak: 0x1118c0e60 size=32 zone: DefaultMallocZone_0x105a92000 string 'com.apple.quarantine' -
trunk/Tools/Scripts/webkitpy/port/leakdetector.py
r225733 r225937 63 63 return callstacks 64 64 65 def _leaks_args(self, pid ):65 def _leaks_args(self, pid, leaks_output_path): 66 66 leaks_args = [] 67 67 for callstack in self._callstacks_to_exclude_from_leaks(): … … 69 69 for excluded_type in self._types_to_exclude_from_leaks(): 70 70 leaks_args += ['--exclude-type=%s' % excluded_type] 71 leaks_args += ['--output-file=%s' % leaks_output_path] 71 72 leaks_args.append(pid) 72 73 return leaks_args … … 114 115 def check_for_leaks(self, process_name, process_pid): 115 116 _log.debug("Checking for leaks in %s" % process_name) 117 leaks_filename = self.leaks_file_name(process_name, process_pid) 118 leaks_output_path = self._filesystem.join(self._port.results_directory(), leaks_filename) 116 119 try: 117 120 # Oddly enough, run-leaks (or the underlying leaks tool) does not seem to always output utf-8, 118 121 # thus we pass decode_output=False. Without this code we've seen errors like: 119 122 # "UnicodeDecodeError: 'utf8' codec can't decode byte 0x88 in position 779874: unexpected code byte" 120 leaks_output = self._port._run_script("run-leaks", self._leaks_args(process_pid), include_configuration_arguments=False, decode_output=False) 123 self._port._run_script("run-leaks", self._leaks_args(process_pid, leaks_output_path), include_configuration_arguments=False, decode_output=False) 124 leaks_output = self._filesystem.read_binary_file(leaks_output_path) 121 125 except ScriptError as e: 122 126 _log.warn("Failed to run leaks tool: %s" % e.message_with_output()) … … 127 131 adjusted_count = count - excluded 128 132 if not adjusted_count: 133 self._filesystem.remove(leaks_output_path) 129 134 return 130 131 leaks_filename = self.leaks_file_name(process_name, process_pid)132 leaks_output_path = self._filesystem.join(self._port.results_directory(), leaks_filename)133 self._filesystem.write_binary_file(leaks_output_path, leaks_output)134 135 135 136 # FIXME: Ideally we would not be logging from the worker process, but rather pass the leak -
trunk/Tools/Scripts/webkitpy/port/leakdetector_unittest.py
r225698 r225937 51 51 detector._callstacks_to_exclude_from_leaks = lambda: ['foo bar', 'BAZ'] 52 52 detector._types_to_exclude_from_leaks = lambda: ['abcdefg', 'hi jklmno'] 53 expected_args = ['--exclude-callstack=foo bar', '--exclude-callstack=BAZ', '--exclude-type=abcdefg', '--exclude-type=hi jklmno', 1234]54 self.assertEqual(detector._leaks_args(1234 ), expected_args)53 expected_args = ['--exclude-callstack=foo bar', '--exclude-callstack=BAZ', '--exclude-type=abcdefg', '--exclude-type=hi jklmno', '--output-file=leaks.txt', 1234] 54 self.assertEqual(detector._leaks_args(1234, 'leaks.txt'), expected_args) 55 55 56 56 example_leaks_output = """Process 5122: 663744 nodes malloced for 78683 KB
Note:
See TracChangeset
for help on using the changeset viewer.