blob: d097b89189988791df07ea159043b17d86a222dd [file] [log] [blame]
#!/usr/bin/perl
# Copyright (C) 2018 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
use strict;
use Getopt::Long;
use Pod::Usage;
=pod
=head1 DESCRIPTION
This script parses API violations from C<adb logcat>. Output is in CSV format
with columns C<package>, C<symbol>, C<list>.
The package name is mapped from a PID, parsed from the same log. To ensure you
get all packages names, you should process the logcat from device boot time.
=head1 SYNOPSIS
adb logcat | perl find_api_violations.pl > violations.csv
cat bugreport.txt | perl find_api_violations.pl --bugreport > violations.csv
=head1 OPTIONS
=over
=item --[no]unsupported
(Don't) show unsupported API accesses (default true)
=item --[no]max-target
(Don't) show APIs blocked by apps target SDK (default true)
=item --[no]blocked
(Don't) show blocked list accesses (default true)
=item --bugreport|-b
Process a bugreport, rather than raw logcat
=item --short|-s
Output API signatures only, don't include CSV header/package names/list name.
=item --help
=back
=cut
my $unsupported = 1;
my $maxtarget = 1;
my $blocked = 1;
my $bugreport = 0;
my $short = 0;
my $help = 0;
GetOptions("unsupported!" => \$unsupported,
"max-target!" => \$maxtarget,
"blocked!" => \$blocked,
"bugreport|b" => \$bugreport,
"short|s" => \$short,
"help" => \$help)
or pod2usage(q(-verbose) => 1);
pod2usage(q(-verbose) => 2) if ($help);
my $in;
if ($bugreport) {
my $found_main = 0;
while (my $line = <>) {
chomp $line;
if ($line =~ m/^------ SYSTEM LOG /) {
$found_main = 1;
last;
}
}
if (!$found_main) {
die "Couldn't find main log in bugreport\n";
}
}
my $procmap = {};
print "package,symbol,list\n" unless $short;
while (my $line = <>) {
chomp $line;
last if $bugreport and $line =~ m/^------ \d+\.\d+s was the duration of 'SYSTEM LOG' ------/;
next if $line =~ m/^--------- beginning of/;
unless ($line =~ m/^\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{3}\s+(?:\w+\s+)?(\d+)\s+(\d+)\s+([VWIDE])\s+(.*?): (.*)$/) {
die "Cannot match line: $line\n";
next;
}
my ($pid, $tid, $class, $tag, $msg) = ($1, $2, $3, $4, $5);
if ($tag eq "ActivityManager" && $msg =~ m/^Start proc (\d+):(.*?) for /) {
my ($new_pid, $proc_name) = ($1, $2);
my $package;
if ($proc_name =~ m/^(.*?)(:.*?)?\/(.*)$/) {
$package = $1;
} else {
$package = $proc_name;
}
$procmap->{$new_pid} = $package;
}
if ($msg =~ m/Accessing hidden (\w+) (L.*?) \((.*list), (.*?)\)/) {
my ($member_type, $symbol, $list, $access_type) = ($1, $2, $3, $4);
my $package = $procmap->{$pid} || "unknown($pid)";
if (($list =~ m/unsupported/ && $unsupported)
|| ($list =~ m/max-target/ && $maxtarget)
|| ($list =~ m/blocked/ && $blocked)) {
if ($short) {
print "$symbol\n";
} else {
print "$package,$symbol,$list\n"
}
}
}
}