1#!/usr/bin/perl
2# Copyright (C) 2018 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#      http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16use strict;
17use Getopt::Long;
18use Pod::Usage;
19
20=pod
21
22=head1 DESCRIPTION
23
24This script parses API violations from C<adb logcat>. Output is in CSV format
25with columns C<package>, C<symbol>, C<list>.
26
27The package name is mapped from a PID, parsed from the same log. To ensure you
28get all packages names, you should process the logcat from device boot time.
29
30=head1 SYNOPSIS
31
32  adb logcat | perl find_api_violations.pl > violations.csv
33  cat bugreport.txt | perl find_api_violations.pl --bugreport > violations.csv
34
35=head1 OPTIONS
36
37=over
38
39=item --[no]lightgrey
40
41(Don't) show light grey list accesses (default true)
42
43=item --[no]darkgrey
44
45(Don't) show dark grey list accesses (default true)
46
47=item --[no]black
48
49(Don't) show black list accesses (default true)
50
51=item --bugreport|-b
52
53Process a bugreport, rather than raw logcat
54
55=item --short|-s
56
57Output API signatures only, don't include CSV header/package names/list name.
58
59=item --help
60
61=back
62
63=cut
64
65my $lightgrey = 1;
66my $darkgrey = 1;
67my $black = 1;
68my $bugreport = 0;
69my $short = 0;
70my $help = 0;
71
72GetOptions("lightgrey!"  => \$lightgrey,
73           "darkgrey!"   => \$darkgrey,
74           "black!"      => \$black,
75           "bugreport|b" => \$bugreport,
76           "short|s"     => \$short,
77           "help"        => \$help)
78  or pod2usage(q(-verbose) => 1);
79
80pod2usage(q(-verbose) => 2) if ($help);
81
82my $in;
83
84if ($bugreport) {
85  my $found_main = 0;
86  while (my $line = <>) {
87    chomp $line;
88    if ($line =~ m/^------ SYSTEM LOG /) {
89      $found_main = 1;
90      last;
91    }
92  }
93  if (!$found_main) {
94    die "Couldn't find main log in bugreport\n";
95  }
96}
97
98my $procmap = {};
99print "package,symbol,list\n" unless $short;
100while (my $line = <>) {
101  chomp $line;
102  last if $bugreport and $line =~ m/^------ \d+\.\d+s was the duration of 'SYSTEM LOG' ------/;
103  next if $line =~ m/^--------- beginning of/;
104  unless ($line =~ m/^\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{3}\s+(?:\w+\s+)?(\d+)\s+(\d+)\s+([VWIDE])\s+(.*?): (.*)$/) {
105    die "Cannot match line: $line\n";
106    next;
107  }
108  my ($pid, $tid, $class, $tag, $msg) = ($1, $2, $3, $4, $5);
109  if ($tag eq "ActivityManager" && $msg =~ m/^Start proc (\d+):(.*?) for /) {
110    my ($new_pid, $proc_name) = ($1, $2);
111    my $package;
112    if ($proc_name =~ m/^(.*?)(:.*?)?\/(.*)$/) {
113      $package = $1;
114    } else {
115      $package = $proc_name;
116    }
117    $procmap->{$new_pid} = $package;
118  }
119  if ($msg =~ m/Accessing hidden (\w+) (L.*?) \((.*list), (.*?)\)/) {
120    my ($member_type, $symbol, $list, $access_type) = ($1, $2, $3, $4);
121    my $package = $procmap->{$pid} || "unknown($pid)";
122    if (($list =~ m/light/ && $lightgrey)
123      || ($list =~ m/dark/ && $darkgrey)
124      || ($list =~ m/black/ && $black)) {
125      if ($short) {
126        print "$symbol\n";
127      } else {
128        print "$package,$symbol,$list\n"
129      }
130    }
131  }
132}
133
134