xref: /aosp_15_r20/external/angle/build/android/convert_dex_profile.py (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1*8975f5c5SAndroid Build Coastguard Worker#!/usr/bin/env vpython3
2*8975f5c5SAndroid Build Coastguard Worker#
3*8975f5c5SAndroid Build Coastguard Worker# Copyright 2018 The Chromium Authors
4*8975f5c5SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be
5*8975f5c5SAndroid Build Coastguard Worker# found in the LICENSE file.
6*8975f5c5SAndroid Build Coastguard Worker
7*8975f5c5SAndroid Build Coastguard Workerimport argparse
8*8975f5c5SAndroid Build Coastguard Workerimport collections
9*8975f5c5SAndroid Build Coastguard Workerimport functools
10*8975f5c5SAndroid Build Coastguard Workerimport logging
11*8975f5c5SAndroid Build Coastguard Workerimport re
12*8975f5c5SAndroid Build Coastguard Workerimport subprocess
13*8975f5c5SAndroid Build Coastguard Workerimport sys
14*8975f5c5SAndroid Build Coastguard Worker
15*8975f5c5SAndroid Build Coastguard WorkerDEX_CLASS_NAME_RE = re.compile(r'\'L(?P<class_name>[^;]+);\'')
16*8975f5c5SAndroid Build Coastguard WorkerDEX_METHOD_NAME_RE = re.compile(r'\'(?P<method_name>[^\']+)\'')
17*8975f5c5SAndroid Build Coastguard WorkerDEX_METHOD_TYPE_RE = re.compile( # type descriptor method signature re
18*8975f5c5SAndroid Build Coastguard Worker    r'\''
19*8975f5c5SAndroid Build Coastguard Worker    r'\('
20*8975f5c5SAndroid Build Coastguard Worker    r'(?P<method_params>[^)]*)'
21*8975f5c5SAndroid Build Coastguard Worker    r'\)'
22*8975f5c5SAndroid Build Coastguard Worker    r'(?P<method_return_type>[^\']+)'
23*8975f5c5SAndroid Build Coastguard Worker    r'\'')
24*8975f5c5SAndroid Build Coastguard WorkerDEX_METHOD_LINE_NR_RE = re.compile(r'line=(?P<line_number>\d+)')
25*8975f5c5SAndroid Build Coastguard Worker
26*8975f5c5SAndroid Build Coastguard WorkerPROFILE_METHOD_RE = re.compile(
27*8975f5c5SAndroid Build Coastguard Worker    r'(?P<tags>[HSP]+)' # tags such as H/S/P
28*8975f5c5SAndroid Build Coastguard Worker    r'(?P<class_name>L[^;]+;)' # class name in type descriptor format
29*8975f5c5SAndroid Build Coastguard Worker    r'->(?P<method_name>[^(]+)'
30*8975f5c5SAndroid Build Coastguard Worker    r'\((?P<method_params>[^)]*)\)'
31*8975f5c5SAndroid Build Coastguard Worker    r'(?P<method_return_type>.+)')
32*8975f5c5SAndroid Build Coastguard Worker
33*8975f5c5SAndroid Build Coastguard WorkerPROGUARD_CLASS_MAPPING_RE = re.compile(
34*8975f5c5SAndroid Build Coastguard Worker    r'(?P<original_name>[^ ]+)'
35*8975f5c5SAndroid Build Coastguard Worker    r' -> '
36*8975f5c5SAndroid Build Coastguard Worker    r'(?P<obfuscated_name>[^:]+):')
37*8975f5c5SAndroid Build Coastguard WorkerPROGUARD_METHOD_MAPPING_RE = re.compile(
38*8975f5c5SAndroid Build Coastguard Worker    # line_start:line_end: (optional)
39*8975f5c5SAndroid Build Coastguard Worker    r'((?P<line_start>\d+):(?P<line_end>\d+):)?'
40*8975f5c5SAndroid Build Coastguard Worker    r'(?P<return_type>[^ ]+)' # original method return type
41*8975f5c5SAndroid Build Coastguard Worker    # original method class name (if exists)
42*8975f5c5SAndroid Build Coastguard Worker    r' (?:(?P<original_method_class>[a-zA-Z_\d.$]+)\.)?'
43*8975f5c5SAndroid Build Coastguard Worker    r'(?P<original_method_name>[^.\(]+)'
44*8975f5c5SAndroid Build Coastguard Worker    r'\((?P<params>[^\)]*)\)' # original method params
45*8975f5c5SAndroid Build Coastguard Worker    r'(?:[^ ]*)' # original method line numbers (ignored)
46*8975f5c5SAndroid Build Coastguard Worker    r' -> '
47*8975f5c5SAndroid Build Coastguard Worker    r'(?P<obfuscated_name>.+)') # obfuscated method name
48*8975f5c5SAndroid Build Coastguard Worker
49*8975f5c5SAndroid Build Coastguard WorkerTYPE_DESCRIPTOR_RE = re.compile(
50*8975f5c5SAndroid Build Coastguard Worker    r'(?P<brackets>\[*)'
51*8975f5c5SAndroid Build Coastguard Worker    r'(?:'
52*8975f5c5SAndroid Build Coastguard Worker    r'(?P<class_name>L[^;]+;)'
53*8975f5c5SAndroid Build Coastguard Worker    r'|'
54*8975f5c5SAndroid Build Coastguard Worker    r'[VZBSCIJFD]'
55*8975f5c5SAndroid Build Coastguard Worker    r')')
56*8975f5c5SAndroid Build Coastguard Worker
57*8975f5c5SAndroid Build Coastguard WorkerDOT_NOTATION_MAP = {
58*8975f5c5SAndroid Build Coastguard Worker    '': '',
59*8975f5c5SAndroid Build Coastguard Worker    'boolean': 'Z',
60*8975f5c5SAndroid Build Coastguard Worker    'byte': 'B',
61*8975f5c5SAndroid Build Coastguard Worker    'void': 'V',
62*8975f5c5SAndroid Build Coastguard Worker    'short': 'S',
63*8975f5c5SAndroid Build Coastguard Worker    'char': 'C',
64*8975f5c5SAndroid Build Coastguard Worker    'int': 'I',
65*8975f5c5SAndroid Build Coastguard Worker    'long': 'J',
66*8975f5c5SAndroid Build Coastguard Worker    'float': 'F',
67*8975f5c5SAndroid Build Coastguard Worker    'double': 'D'
68*8975f5c5SAndroid Build Coastguard Worker}
69*8975f5c5SAndroid Build Coastguard Worker
70*8975f5c5SAndroid Build Coastguard Worker
71*8975f5c5SAndroid Build Coastguard Worker@functools.total_ordering
72*8975f5c5SAndroid Build Coastguard Workerclass Method:
73*8975f5c5SAndroid Build Coastguard Worker  def __init__(self, name, class_name, param_types=None, return_type=None):
74*8975f5c5SAndroid Build Coastguard Worker    self.name = name
75*8975f5c5SAndroid Build Coastguard Worker    self.class_name = class_name
76*8975f5c5SAndroid Build Coastguard Worker    self.param_types = param_types
77*8975f5c5SAndroid Build Coastguard Worker    self.return_type = return_type
78*8975f5c5SAndroid Build Coastguard Worker
79*8975f5c5SAndroid Build Coastguard Worker  def __str__(self):
80*8975f5c5SAndroid Build Coastguard Worker    return '{}->{}({}){}'.format(self.class_name, self.name,
81*8975f5c5SAndroid Build Coastguard Worker        self.param_types or '', self.return_type or '')
82*8975f5c5SAndroid Build Coastguard Worker
83*8975f5c5SAndroid Build Coastguard Worker  def __repr__(self):
84*8975f5c5SAndroid Build Coastguard Worker    return 'Method<{}->{}({}){}>'.format(self.class_name, self.name,
85*8975f5c5SAndroid Build Coastguard Worker        self.param_types or '', self.return_type or '')
86*8975f5c5SAndroid Build Coastguard Worker
87*8975f5c5SAndroid Build Coastguard Worker  @staticmethod
88*8975f5c5SAndroid Build Coastguard Worker  def serialize(method):
89*8975f5c5SAndroid Build Coastguard Worker    return (method.class_name, method.name, method.param_types,
90*8975f5c5SAndroid Build Coastguard Worker            method.return_type)
91*8975f5c5SAndroid Build Coastguard Worker
92*8975f5c5SAndroid Build Coastguard Worker  def __eq__(self, other):
93*8975f5c5SAndroid Build Coastguard Worker    return self.serialize(self) == self.serialize(other)
94*8975f5c5SAndroid Build Coastguard Worker
95*8975f5c5SAndroid Build Coastguard Worker  def __lt__(self, other):
96*8975f5c5SAndroid Build Coastguard Worker    return self.serialize(self) < self.serialize(other)
97*8975f5c5SAndroid Build Coastguard Worker
98*8975f5c5SAndroid Build Coastguard Worker  def __hash__(self):
99*8975f5c5SAndroid Build Coastguard Worker    # only hash name and class_name since other fields may not be set yet.
100*8975f5c5SAndroid Build Coastguard Worker    return hash((self.name, self.class_name))
101*8975f5c5SAndroid Build Coastguard Worker
102*8975f5c5SAndroid Build Coastguard Worker
103*8975f5c5SAndroid Build Coastguard Workerclass Class:
104*8975f5c5SAndroid Build Coastguard Worker  def __init__(self, name):
105*8975f5c5SAndroid Build Coastguard Worker    self.name = name
106*8975f5c5SAndroid Build Coastguard Worker    self._methods = []
107*8975f5c5SAndroid Build Coastguard Worker
108*8975f5c5SAndroid Build Coastguard Worker  def AddMethod(self, method, line_numbers):
109*8975f5c5SAndroid Build Coastguard Worker    self._methods.append((method, set(line_numbers)))
110*8975f5c5SAndroid Build Coastguard Worker
111*8975f5c5SAndroid Build Coastguard Worker  def FindMethodsAtLine(self, method_name, line_start, line_end=None):
112*8975f5c5SAndroid Build Coastguard Worker    """Searches through dex class for a method given a name and line numbers
113*8975f5c5SAndroid Build Coastguard Worker
114*8975f5c5SAndroid Build Coastguard Worker    The dex maps methods to line numbers, this method, given the a method name
115*8975f5c5SAndroid Build Coastguard Worker    in this class as well as a start line and an optional end line (which act as
116*8975f5c5SAndroid Build Coastguard Worker    hints as to which function in the class is being looked for), returns a list
117*8975f5c5SAndroid Build Coastguard Worker    of possible matches (or none if none are found).
118*8975f5c5SAndroid Build Coastguard Worker
119*8975f5c5SAndroid Build Coastguard Worker    Args:
120*8975f5c5SAndroid Build Coastguard Worker      method_name: name of method being searched for
121*8975f5c5SAndroid Build Coastguard Worker      line_start: start of hint range for lines in this method
122*8975f5c5SAndroid Build Coastguard Worker      line_end: end of hint range for lines in this method (optional)
123*8975f5c5SAndroid Build Coastguard Worker
124*8975f5c5SAndroid Build Coastguard Worker    Returns:
125*8975f5c5SAndroid Build Coastguard Worker      A list of Method objects that could match the hints given, or None if no
126*8975f5c5SAndroid Build Coastguard Worker      method is found.
127*8975f5c5SAndroid Build Coastguard Worker    """
128*8975f5c5SAndroid Build Coastguard Worker    found_methods = []
129*8975f5c5SAndroid Build Coastguard Worker    if line_end is None:
130*8975f5c5SAndroid Build Coastguard Worker      hint_lines = set([line_start])
131*8975f5c5SAndroid Build Coastguard Worker    else:
132*8975f5c5SAndroid Build Coastguard Worker      hint_lines = set(range(line_start, line_end+1))
133*8975f5c5SAndroid Build Coastguard Worker
134*8975f5c5SAndroid Build Coastguard Worker    named_methods = [(method, l) for method, l in self._methods
135*8975f5c5SAndroid Build Coastguard Worker                     if method.name == method_name]
136*8975f5c5SAndroid Build Coastguard Worker
137*8975f5c5SAndroid Build Coastguard Worker    if len(named_methods) == 1:
138*8975f5c5SAndroid Build Coastguard Worker      return [method for method, l in named_methods]
139*8975f5c5SAndroid Build Coastguard Worker    if len(named_methods) == 0:
140*8975f5c5SAndroid Build Coastguard Worker      return None
141*8975f5c5SAndroid Build Coastguard Worker
142*8975f5c5SAndroid Build Coastguard Worker    for method, line_numbers in named_methods:
143*8975f5c5SAndroid Build Coastguard Worker      if not hint_lines.isdisjoint(line_numbers):
144*8975f5c5SAndroid Build Coastguard Worker        found_methods.append(method)
145*8975f5c5SAndroid Build Coastguard Worker
146*8975f5c5SAndroid Build Coastguard Worker    if len(found_methods) > 0:
147*8975f5c5SAndroid Build Coastguard Worker      if len(found_methods) > 1:
148*8975f5c5SAndroid Build Coastguard Worker        logging.warning('ambigous methods in dex %s at lines %s in class "%s"',
149*8975f5c5SAndroid Build Coastguard Worker            found_methods, hint_lines, self.name)
150*8975f5c5SAndroid Build Coastguard Worker      return found_methods
151*8975f5c5SAndroid Build Coastguard Worker
152*8975f5c5SAndroid Build Coastguard Worker    for method, line_numbers in named_methods:
153*8975f5c5SAndroid Build Coastguard Worker      if (max(hint_lines) >= min(line_numbers)
154*8975f5c5SAndroid Build Coastguard Worker          and min(hint_lines) <= max(line_numbers)):
155*8975f5c5SAndroid Build Coastguard Worker        found_methods.append(method)
156*8975f5c5SAndroid Build Coastguard Worker
157*8975f5c5SAndroid Build Coastguard Worker    if len(found_methods) > 0:
158*8975f5c5SAndroid Build Coastguard Worker      if len(found_methods) > 1:
159*8975f5c5SAndroid Build Coastguard Worker        logging.warning('ambigous methods in dex %s at lines %s in class "%s"',
160*8975f5c5SAndroid Build Coastguard Worker            found_methods, hint_lines, self.name)
161*8975f5c5SAndroid Build Coastguard Worker      return found_methods
162*8975f5c5SAndroid Build Coastguard Worker    logging.warning(
163*8975f5c5SAndroid Build Coastguard Worker        'No method named "%s" in class "%s" is '
164*8975f5c5SAndroid Build Coastguard Worker        'mapped to lines %s', method_name, self.name, hint_lines)
165*8975f5c5SAndroid Build Coastguard Worker    return None
166*8975f5c5SAndroid Build Coastguard Worker
167*8975f5c5SAndroid Build Coastguard Worker
168*8975f5c5SAndroid Build Coastguard Workerclass Profile:
169*8975f5c5SAndroid Build Coastguard Worker  def __init__(self):
170*8975f5c5SAndroid Build Coastguard Worker    # {Method: set(char)}
171*8975f5c5SAndroid Build Coastguard Worker    self._methods = collections.defaultdict(set)
172*8975f5c5SAndroid Build Coastguard Worker    self._classes = []
173*8975f5c5SAndroid Build Coastguard Worker
174*8975f5c5SAndroid Build Coastguard Worker  def AddMethod(self, method, tags):
175*8975f5c5SAndroid Build Coastguard Worker    for tag in tags:
176*8975f5c5SAndroid Build Coastguard Worker      self._methods[method].add(tag)
177*8975f5c5SAndroid Build Coastguard Worker
178*8975f5c5SAndroid Build Coastguard Worker  def AddClass(self, cls):
179*8975f5c5SAndroid Build Coastguard Worker    self._classes.append(cls)
180*8975f5c5SAndroid Build Coastguard Worker
181*8975f5c5SAndroid Build Coastguard Worker  def WriteToFile(self, path):
182*8975f5c5SAndroid Build Coastguard Worker    with open(path, 'w') as output_profile:
183*8975f5c5SAndroid Build Coastguard Worker      for cls in sorted(self._classes):
184*8975f5c5SAndroid Build Coastguard Worker        output_profile.write(cls + '\n')
185*8975f5c5SAndroid Build Coastguard Worker      for method in sorted(self._methods):
186*8975f5c5SAndroid Build Coastguard Worker        tags = sorted(self._methods[method])
187*8975f5c5SAndroid Build Coastguard Worker        line = '{}{}\n'.format(''.join(tags), str(method))
188*8975f5c5SAndroid Build Coastguard Worker        output_profile.write(line)
189*8975f5c5SAndroid Build Coastguard Worker
190*8975f5c5SAndroid Build Coastguard Worker
191*8975f5c5SAndroid Build Coastguard Workerclass ProguardMapping:
192*8975f5c5SAndroid Build Coastguard Worker  def __init__(self):
193*8975f5c5SAndroid Build Coastguard Worker    # {Method: set(Method)}
194*8975f5c5SAndroid Build Coastguard Worker    self._method_mapping = collections.defaultdict(set)
195*8975f5c5SAndroid Build Coastguard Worker    # {String: String} String is class name in type descriptor format
196*8975f5c5SAndroid Build Coastguard Worker    self._class_mapping = dict()
197*8975f5c5SAndroid Build Coastguard Worker
198*8975f5c5SAndroid Build Coastguard Worker  def AddMethodMapping(self, from_method, to_method):
199*8975f5c5SAndroid Build Coastguard Worker    self._method_mapping[from_method].add(to_method)
200*8975f5c5SAndroid Build Coastguard Worker
201*8975f5c5SAndroid Build Coastguard Worker  def AddClassMapping(self, from_class, to_class):
202*8975f5c5SAndroid Build Coastguard Worker    self._class_mapping[from_class] = to_class
203*8975f5c5SAndroid Build Coastguard Worker
204*8975f5c5SAndroid Build Coastguard Worker  def GetMethodMapping(self, from_method):
205*8975f5c5SAndroid Build Coastguard Worker    return self._method_mapping.get(from_method)
206*8975f5c5SAndroid Build Coastguard Worker
207*8975f5c5SAndroid Build Coastguard Worker  def GetClassMapping(self, from_class):
208*8975f5c5SAndroid Build Coastguard Worker    return self._class_mapping.get(from_class, from_class)
209*8975f5c5SAndroid Build Coastguard Worker
210*8975f5c5SAndroid Build Coastguard Worker  def MapTypeDescriptor(self, type_descriptor):
211*8975f5c5SAndroid Build Coastguard Worker    match = TYPE_DESCRIPTOR_RE.search(type_descriptor)
212*8975f5c5SAndroid Build Coastguard Worker    assert match is not None
213*8975f5c5SAndroid Build Coastguard Worker    class_name = match.group('class_name')
214*8975f5c5SAndroid Build Coastguard Worker    if class_name is not None:
215*8975f5c5SAndroid Build Coastguard Worker      return match.group('brackets') + self.GetClassMapping(class_name)
216*8975f5c5SAndroid Build Coastguard Worker    # just a native type, return as is
217*8975f5c5SAndroid Build Coastguard Worker    return match.group()
218*8975f5c5SAndroid Build Coastguard Worker
219*8975f5c5SAndroid Build Coastguard Worker  def MapTypeDescriptorList(self, type_descriptor_list):
220*8975f5c5SAndroid Build Coastguard Worker    return TYPE_DESCRIPTOR_RE.sub(
221*8975f5c5SAndroid Build Coastguard Worker        lambda match: self.MapTypeDescriptor(match.group()),
222*8975f5c5SAndroid Build Coastguard Worker        type_descriptor_list)
223*8975f5c5SAndroid Build Coastguard Worker
224*8975f5c5SAndroid Build Coastguard Worker
225*8975f5c5SAndroid Build Coastguard Workerclass MalformedLineException(Exception):
226*8975f5c5SAndroid Build Coastguard Worker  def __init__(self, message, line_number):
227*8975f5c5SAndroid Build Coastguard Worker    super().__init__(message)
228*8975f5c5SAndroid Build Coastguard Worker    self.message = message
229*8975f5c5SAndroid Build Coastguard Worker    self.line_number = line_number
230*8975f5c5SAndroid Build Coastguard Worker
231*8975f5c5SAndroid Build Coastguard Worker  def __str__(self):
232*8975f5c5SAndroid Build Coastguard Worker    return self.message + ' at line {}'.format(self.line_number)
233*8975f5c5SAndroid Build Coastguard Worker
234*8975f5c5SAndroid Build Coastguard Worker
235*8975f5c5SAndroid Build Coastguard Workerclass MalformedProguardMappingException(MalformedLineException):
236*8975f5c5SAndroid Build Coastguard Worker  pass
237*8975f5c5SAndroid Build Coastguard Worker
238*8975f5c5SAndroid Build Coastguard Worker
239*8975f5c5SAndroid Build Coastguard Workerclass MalformedProfileException(MalformedLineException):
240*8975f5c5SAndroid Build Coastguard Worker  pass
241*8975f5c5SAndroid Build Coastguard Worker
242*8975f5c5SAndroid Build Coastguard Worker
243*8975f5c5SAndroid Build Coastguard Workerdef _RunDexDump(dexdump_path, dex_file_path):
244*8975f5c5SAndroid Build Coastguard Worker  return subprocess.check_output([dexdump_path,
245*8975f5c5SAndroid Build Coastguard Worker                                  dex_file_path]).decode('utf-8').splitlines()
246*8975f5c5SAndroid Build Coastguard Worker
247*8975f5c5SAndroid Build Coastguard Worker
248*8975f5c5SAndroid Build Coastguard Workerdef _ReadFile(file_path):
249*8975f5c5SAndroid Build Coastguard Worker  with open(file_path, 'r') as f:
250*8975f5c5SAndroid Build Coastguard Worker    return f.readlines()
251*8975f5c5SAndroid Build Coastguard Worker
252*8975f5c5SAndroid Build Coastguard Worker
253*8975f5c5SAndroid Build Coastguard Workerdef _ToTypeDescriptor(dot_notation):
254*8975f5c5SAndroid Build Coastguard Worker  """Parses a dot notation type and returns it in type descriptor format
255*8975f5c5SAndroid Build Coastguard Worker
256*8975f5c5SAndroid Build Coastguard Worker  eg:
257*8975f5c5SAndroid Build Coastguard Worker  org.chromium.browser.ChromeActivity -> Lorg/chromium/browser/ChromeActivity;
258*8975f5c5SAndroid Build Coastguard Worker  boolean -> Z
259*8975f5c5SAndroid Build Coastguard Worker  int[] -> [I
260*8975f5c5SAndroid Build Coastguard Worker
261*8975f5c5SAndroid Build Coastguard Worker  Args:
262*8975f5c5SAndroid Build Coastguard Worker    dot_notation: trimmed string with a single type in dot notation format
263*8975f5c5SAndroid Build Coastguard Worker
264*8975f5c5SAndroid Build Coastguard Worker  Returns:
265*8975f5c5SAndroid Build Coastguard Worker    A string with the type in type descriptor format
266*8975f5c5SAndroid Build Coastguard Worker  """
267*8975f5c5SAndroid Build Coastguard Worker  dot_notation = dot_notation.strip()
268*8975f5c5SAndroid Build Coastguard Worker  prefix = ''
269*8975f5c5SAndroid Build Coastguard Worker  while dot_notation.endswith('[]'):
270*8975f5c5SAndroid Build Coastguard Worker    prefix += '['
271*8975f5c5SAndroid Build Coastguard Worker    dot_notation = dot_notation[:-2]
272*8975f5c5SAndroid Build Coastguard Worker  if dot_notation in DOT_NOTATION_MAP:
273*8975f5c5SAndroid Build Coastguard Worker    return prefix + DOT_NOTATION_MAP[dot_notation]
274*8975f5c5SAndroid Build Coastguard Worker  return prefix + 'L' + dot_notation.replace('.', '/') + ';'
275*8975f5c5SAndroid Build Coastguard Worker
276*8975f5c5SAndroid Build Coastguard Worker
277*8975f5c5SAndroid Build Coastguard Workerdef _DotNotationListToTypeDescriptorList(dot_notation_list_string):
278*8975f5c5SAndroid Build Coastguard Worker  """Parses a param list of dot notation format and returns it in type
279*8975f5c5SAndroid Build Coastguard Worker  descriptor format
280*8975f5c5SAndroid Build Coastguard Worker
281*8975f5c5SAndroid Build Coastguard Worker  eg:
282*8975f5c5SAndroid Build Coastguard Worker  org.chromium.browser.ChromeActivity,boolean,int[] ->
283*8975f5c5SAndroid Build Coastguard Worker      Lorg/chromium/browser/ChromeActivity;Z[I
284*8975f5c5SAndroid Build Coastguard Worker
285*8975f5c5SAndroid Build Coastguard Worker  Args:
286*8975f5c5SAndroid Build Coastguard Worker    dot_notation_list_string: single string with multiple comma separated types
287*8975f5c5SAndroid Build Coastguard Worker                              in dot notation format
288*8975f5c5SAndroid Build Coastguard Worker
289*8975f5c5SAndroid Build Coastguard Worker  Returns:
290*8975f5c5SAndroid Build Coastguard Worker    A string with the param list in type descriptor format
291*8975f5c5SAndroid Build Coastguard Worker  """
292*8975f5c5SAndroid Build Coastguard Worker  return ''.join(_ToTypeDescriptor(param) for param in
293*8975f5c5SAndroid Build Coastguard Worker      dot_notation_list_string.split(','))
294*8975f5c5SAndroid Build Coastguard Worker
295*8975f5c5SAndroid Build Coastguard Worker
296*8975f5c5SAndroid Build Coastguard Workerdef ProcessDex(dex_dump):
297*8975f5c5SAndroid Build Coastguard Worker  """Parses dexdump output returning a dict of class names to Class objects
298*8975f5c5SAndroid Build Coastguard Worker
299*8975f5c5SAndroid Build Coastguard Worker  Parses output of the dexdump command on a dex file and extracts information
300*8975f5c5SAndroid Build Coastguard Worker  about classes and their respective methods and which line numbers a method is
301*8975f5c5SAndroid Build Coastguard Worker  mapped to.
302*8975f5c5SAndroid Build Coastguard Worker
303*8975f5c5SAndroid Build Coastguard Worker  Methods that are not mapped to any line number are ignored and not listed
304*8975f5c5SAndroid Build Coastguard Worker  inside their respective Class objects.
305*8975f5c5SAndroid Build Coastguard Worker
306*8975f5c5SAndroid Build Coastguard Worker  Args:
307*8975f5c5SAndroid Build Coastguard Worker    dex_dump: An array of lines of dexdump output
308*8975f5c5SAndroid Build Coastguard Worker
309*8975f5c5SAndroid Build Coastguard Worker  Returns:
310*8975f5c5SAndroid Build Coastguard Worker    A dict that maps from class names in type descriptor format (but without the
311*8975f5c5SAndroid Build Coastguard Worker    surrounding 'L' and ';') to Class objects.
312*8975f5c5SAndroid Build Coastguard Worker  """
313*8975f5c5SAndroid Build Coastguard Worker  # class_name: Class
314*8975f5c5SAndroid Build Coastguard Worker  classes_by_name = {}
315*8975f5c5SAndroid Build Coastguard Worker  current_class = None
316*8975f5c5SAndroid Build Coastguard Worker  current_method = None
317*8975f5c5SAndroid Build Coastguard Worker  reading_positions = False
318*8975f5c5SAndroid Build Coastguard Worker  reading_methods = False
319*8975f5c5SAndroid Build Coastguard Worker  method_line_numbers = []
320*8975f5c5SAndroid Build Coastguard Worker  for line in dex_dump:
321*8975f5c5SAndroid Build Coastguard Worker    line = line.strip()
322*8975f5c5SAndroid Build Coastguard Worker    if line.startswith('Class descriptor'):
323*8975f5c5SAndroid Build Coastguard Worker      # New class started, no longer reading methods.
324*8975f5c5SAndroid Build Coastguard Worker      reading_methods = False
325*8975f5c5SAndroid Build Coastguard Worker      current_class = Class(DEX_CLASS_NAME_RE.search(line).group('class_name'))
326*8975f5c5SAndroid Build Coastguard Worker      classes_by_name[current_class.name] = current_class
327*8975f5c5SAndroid Build Coastguard Worker    elif (line.startswith('Direct methods')
328*8975f5c5SAndroid Build Coastguard Worker          or line.startswith('Virtual methods')):
329*8975f5c5SAndroid Build Coastguard Worker      reading_methods = True
330*8975f5c5SAndroid Build Coastguard Worker    elif reading_methods and line.startswith('name'):
331*8975f5c5SAndroid Build Coastguard Worker      assert current_class is not None
332*8975f5c5SAndroid Build Coastguard Worker      current_method = Method(
333*8975f5c5SAndroid Build Coastguard Worker          DEX_METHOD_NAME_RE.search(line).group('method_name'),
334*8975f5c5SAndroid Build Coastguard Worker          "L" + current_class.name + ";")
335*8975f5c5SAndroid Build Coastguard Worker    elif reading_methods and line.startswith('type'):
336*8975f5c5SAndroid Build Coastguard Worker      assert current_method is not None
337*8975f5c5SAndroid Build Coastguard Worker      match = DEX_METHOD_TYPE_RE.search(line)
338*8975f5c5SAndroid Build Coastguard Worker      current_method.param_types = match.group('method_params')
339*8975f5c5SAndroid Build Coastguard Worker      current_method.return_type = match.group('method_return_type')
340*8975f5c5SAndroid Build Coastguard Worker    elif line.startswith('positions'):
341*8975f5c5SAndroid Build Coastguard Worker      assert reading_methods
342*8975f5c5SAndroid Build Coastguard Worker      reading_positions = True
343*8975f5c5SAndroid Build Coastguard Worker      method_line_numbers = []
344*8975f5c5SAndroid Build Coastguard Worker    elif reading_positions and line.startswith('0x'):
345*8975f5c5SAndroid Build Coastguard Worker      line_number = DEX_METHOD_LINE_NR_RE.search(line).group('line_number')
346*8975f5c5SAndroid Build Coastguard Worker      method_line_numbers.append(int(line_number))
347*8975f5c5SAndroid Build Coastguard Worker    elif reading_positions and line.startswith('locals'):
348*8975f5c5SAndroid Build Coastguard Worker      if len(method_line_numbers) > 0:
349*8975f5c5SAndroid Build Coastguard Worker        current_class.AddMethod(current_method, method_line_numbers)
350*8975f5c5SAndroid Build Coastguard Worker      # finished reading method line numbers
351*8975f5c5SAndroid Build Coastguard Worker      reading_positions = False
352*8975f5c5SAndroid Build Coastguard Worker  return classes_by_name
353*8975f5c5SAndroid Build Coastguard Worker
354*8975f5c5SAndroid Build Coastguard Worker
355*8975f5c5SAndroid Build Coastguard Workerdef ProcessProguardMapping(proguard_mapping_lines, dex):
356*8975f5c5SAndroid Build Coastguard Worker  """Parses a proguard mapping file
357*8975f5c5SAndroid Build Coastguard Worker
358*8975f5c5SAndroid Build Coastguard Worker  This takes proguard mapping file lines and then uses the obfuscated dex to
359*8975f5c5SAndroid Build Coastguard Worker  create a mapping of unobfuscated methods to obfuscated ones and vice versa.
360*8975f5c5SAndroid Build Coastguard Worker
361*8975f5c5SAndroid Build Coastguard Worker  The dex is used because the proguard mapping file only has the name of the
362*8975f5c5SAndroid Build Coastguard Worker  obfuscated methods but not their signature, thus the dex is read to look up
363*8975f5c5SAndroid Build Coastguard Worker  which method with a specific name was mapped to the lines mentioned in the
364*8975f5c5SAndroid Build Coastguard Worker  proguard mapping file.
365*8975f5c5SAndroid Build Coastguard Worker
366*8975f5c5SAndroid Build Coastguard Worker  Args:
367*8975f5c5SAndroid Build Coastguard Worker    proguard_mapping_lines: Array of strings, each is a line from the proguard
368*8975f5c5SAndroid Build Coastguard Worker                            mapping file (in order).
369*8975f5c5SAndroid Build Coastguard Worker    dex: a dict of class name (in type descriptor format but without the
370*8975f5c5SAndroid Build Coastguard Worker         enclosing 'L' and ';') to a Class object.
371*8975f5c5SAndroid Build Coastguard Worker  Returns:
372*8975f5c5SAndroid Build Coastguard Worker    Two dicts the first maps from obfuscated methods to a set of non-obfuscated
373*8975f5c5SAndroid Build Coastguard Worker    ones. It also maps the obfuscated class names to original class names, both
374*8975f5c5SAndroid Build Coastguard Worker    in type descriptor format (with the enclosing 'L' and ';')
375*8975f5c5SAndroid Build Coastguard Worker  """
376*8975f5c5SAndroid Build Coastguard Worker  mapping = ProguardMapping()
377*8975f5c5SAndroid Build Coastguard Worker  reverse_mapping = ProguardMapping()
378*8975f5c5SAndroid Build Coastguard Worker  to_be_obfuscated = []
379*8975f5c5SAndroid Build Coastguard Worker  current_class_orig = None
380*8975f5c5SAndroid Build Coastguard Worker  current_class_obfs = None
381*8975f5c5SAndroid Build Coastguard Worker  for index, line in enumerate(proguard_mapping_lines):
382*8975f5c5SAndroid Build Coastguard Worker    if line.strip() == '':
383*8975f5c5SAndroid Build Coastguard Worker      continue
384*8975f5c5SAndroid Build Coastguard Worker    if not line.startswith(' '):
385*8975f5c5SAndroid Build Coastguard Worker      match = PROGUARD_CLASS_MAPPING_RE.search(line)
386*8975f5c5SAndroid Build Coastguard Worker      if match is None:
387*8975f5c5SAndroid Build Coastguard Worker        raise MalformedProguardMappingException(
388*8975f5c5SAndroid Build Coastguard Worker            'Malformed class mapping', index)
389*8975f5c5SAndroid Build Coastguard Worker      current_class_orig = match.group('original_name')
390*8975f5c5SAndroid Build Coastguard Worker      current_class_obfs = match.group('obfuscated_name')
391*8975f5c5SAndroid Build Coastguard Worker      mapping.AddClassMapping(_ToTypeDescriptor(current_class_obfs),
392*8975f5c5SAndroid Build Coastguard Worker                              _ToTypeDescriptor(current_class_orig))
393*8975f5c5SAndroid Build Coastguard Worker      reverse_mapping.AddClassMapping(_ToTypeDescriptor(current_class_orig),
394*8975f5c5SAndroid Build Coastguard Worker                                      _ToTypeDescriptor(current_class_obfs))
395*8975f5c5SAndroid Build Coastguard Worker      continue
396*8975f5c5SAndroid Build Coastguard Worker
397*8975f5c5SAndroid Build Coastguard Worker    assert current_class_orig is not None
398*8975f5c5SAndroid Build Coastguard Worker    assert current_class_obfs is not None
399*8975f5c5SAndroid Build Coastguard Worker    line = line.strip()
400*8975f5c5SAndroid Build Coastguard Worker    match = PROGUARD_METHOD_MAPPING_RE.search(line)
401*8975f5c5SAndroid Build Coastguard Worker    # check if is a method mapping (we ignore field mappings)
402*8975f5c5SAndroid Build Coastguard Worker    if match is not None:
403*8975f5c5SAndroid Build Coastguard Worker      # check if this line is an inlining by reading ahead 1 line.
404*8975f5c5SAndroid Build Coastguard Worker      if index + 1 < len(proguard_mapping_lines):
405*8975f5c5SAndroid Build Coastguard Worker        next_match = PROGUARD_METHOD_MAPPING_RE.search(
406*8975f5c5SAndroid Build Coastguard Worker            proguard_mapping_lines[index+1].strip())
407*8975f5c5SAndroid Build Coastguard Worker        if (next_match and match.group('line_start') is not None
408*8975f5c5SAndroid Build Coastguard Worker            and next_match.group('line_start') == match.group('line_start')
409*8975f5c5SAndroid Build Coastguard Worker            and next_match.group('line_end') == match.group('line_end')):
410*8975f5c5SAndroid Build Coastguard Worker          continue # This is an inlining, skip
411*8975f5c5SAndroid Build Coastguard Worker
412*8975f5c5SAndroid Build Coastguard Worker      original_method = Method(
413*8975f5c5SAndroid Build Coastguard Worker          match.group('original_method_name'),
414*8975f5c5SAndroid Build Coastguard Worker          _ToTypeDescriptor(
415*8975f5c5SAndroid Build Coastguard Worker              match.group('original_method_class') or current_class_orig),
416*8975f5c5SAndroid Build Coastguard Worker          _DotNotationListToTypeDescriptorList(match.group('params')),
417*8975f5c5SAndroid Build Coastguard Worker          _ToTypeDescriptor(match.group('return_type')))
418*8975f5c5SAndroid Build Coastguard Worker
419*8975f5c5SAndroid Build Coastguard Worker      if match.group('line_start') is not None:
420*8975f5c5SAndroid Build Coastguard Worker        obfs_methods = (dex[current_class_obfs.replace('.', '/')]
421*8975f5c5SAndroid Build Coastguard Worker            .FindMethodsAtLine(
422*8975f5c5SAndroid Build Coastguard Worker                match.group('obfuscated_name'),
423*8975f5c5SAndroid Build Coastguard Worker                int(match.group('line_start')),
424*8975f5c5SAndroid Build Coastguard Worker                int(match.group('line_end'))))
425*8975f5c5SAndroid Build Coastguard Worker
426*8975f5c5SAndroid Build Coastguard Worker        if obfs_methods is None:
427*8975f5c5SAndroid Build Coastguard Worker          continue
428*8975f5c5SAndroid Build Coastguard Worker
429*8975f5c5SAndroid Build Coastguard Worker        for obfs_method in obfs_methods:
430*8975f5c5SAndroid Build Coastguard Worker          mapping.AddMethodMapping(obfs_method, original_method)
431*8975f5c5SAndroid Build Coastguard Worker          reverse_mapping.AddMethodMapping(original_method, obfs_method)
432*8975f5c5SAndroid Build Coastguard Worker      else:
433*8975f5c5SAndroid Build Coastguard Worker        to_be_obfuscated.append(
434*8975f5c5SAndroid Build Coastguard Worker            (original_method, match.group('obfuscated_name')))
435*8975f5c5SAndroid Build Coastguard Worker
436*8975f5c5SAndroid Build Coastguard Worker  for original_method, obfuscated_name in to_be_obfuscated:
437*8975f5c5SAndroid Build Coastguard Worker    obfuscated_method = Method(
438*8975f5c5SAndroid Build Coastguard Worker        obfuscated_name,
439*8975f5c5SAndroid Build Coastguard Worker        reverse_mapping.GetClassMapping(original_method.class_name),
440*8975f5c5SAndroid Build Coastguard Worker        reverse_mapping.MapTypeDescriptorList(original_method.param_types),
441*8975f5c5SAndroid Build Coastguard Worker        reverse_mapping.MapTypeDescriptor(original_method.return_type))
442*8975f5c5SAndroid Build Coastguard Worker    mapping.AddMethodMapping(obfuscated_method, original_method)
443*8975f5c5SAndroid Build Coastguard Worker    reverse_mapping.AddMethodMapping(original_method, obfuscated_method)
444*8975f5c5SAndroid Build Coastguard Worker  return mapping, reverse_mapping
445*8975f5c5SAndroid Build Coastguard Worker
446*8975f5c5SAndroid Build Coastguard Worker
447*8975f5c5SAndroid Build Coastguard Workerdef ProcessProfile(input_profile, proguard_mapping):
448*8975f5c5SAndroid Build Coastguard Worker  """Parses an android profile and uses the proguard mapping to (de)obfuscate it
449*8975f5c5SAndroid Build Coastguard Worker
450*8975f5c5SAndroid Build Coastguard Worker  This takes the android profile lines and for each method or class for the
451*8975f5c5SAndroid Build Coastguard Worker  profile, it uses the mapping to either obfuscate or deobfuscate (based on the
452*8975f5c5SAndroid Build Coastguard Worker  provided mapping) and returns a Profile object that stores this information.
453*8975f5c5SAndroid Build Coastguard Worker
454*8975f5c5SAndroid Build Coastguard Worker  Args:
455*8975f5c5SAndroid Build Coastguard Worker    input_profile: array of lines of the input profile
456*8975f5c5SAndroid Build Coastguard Worker    proguard_mapping: a proguard mapping that would map from the classes and
457*8975f5c5SAndroid Build Coastguard Worker                      methods in the input profile to the classes and methods
458*8975f5c5SAndroid Build Coastguard Worker                      that should be in the output profile.
459*8975f5c5SAndroid Build Coastguard Worker
460*8975f5c5SAndroid Build Coastguard Worker  Returns:
461*8975f5c5SAndroid Build Coastguard Worker    A Profile object that stores the information (ie list of mapped classes and
462*8975f5c5SAndroid Build Coastguard Worker    methods + tags)
463*8975f5c5SAndroid Build Coastguard Worker  """
464*8975f5c5SAndroid Build Coastguard Worker  profile = Profile()
465*8975f5c5SAndroid Build Coastguard Worker  for index, line in enumerate(input_profile):
466*8975f5c5SAndroid Build Coastguard Worker    line = line.strip()
467*8975f5c5SAndroid Build Coastguard Worker    if line.startswith('L'):
468*8975f5c5SAndroid Build Coastguard Worker      profile.AddClass(proguard_mapping.GetClassMapping(line))
469*8975f5c5SAndroid Build Coastguard Worker      continue
470*8975f5c5SAndroid Build Coastguard Worker    match = PROFILE_METHOD_RE.search(line)
471*8975f5c5SAndroid Build Coastguard Worker    if not match:
472*8975f5c5SAndroid Build Coastguard Worker      raise MalformedProfileException("Malformed line", index)
473*8975f5c5SAndroid Build Coastguard Worker
474*8975f5c5SAndroid Build Coastguard Worker    method = Method(
475*8975f5c5SAndroid Build Coastguard Worker        match.group('method_name'),
476*8975f5c5SAndroid Build Coastguard Worker        match.group('class_name'),
477*8975f5c5SAndroid Build Coastguard Worker        match.group('method_params'),
478*8975f5c5SAndroid Build Coastguard Worker        match.group('method_return_type'))
479*8975f5c5SAndroid Build Coastguard Worker
480*8975f5c5SAndroid Build Coastguard Worker    mapped_methods = proguard_mapping.GetMethodMapping(method)
481*8975f5c5SAndroid Build Coastguard Worker    if mapped_methods is None:
482*8975f5c5SAndroid Build Coastguard Worker      logging.warning('No method matching "%s" has been found in the proguard '
483*8975f5c5SAndroid Build Coastguard Worker                      'mapping file', method)
484*8975f5c5SAndroid Build Coastguard Worker      continue
485*8975f5c5SAndroid Build Coastguard Worker
486*8975f5c5SAndroid Build Coastguard Worker    for original_method in mapped_methods:
487*8975f5c5SAndroid Build Coastguard Worker      profile.AddMethod(original_method, match.group('tags'))
488*8975f5c5SAndroid Build Coastguard Worker
489*8975f5c5SAndroid Build Coastguard Worker  return profile
490*8975f5c5SAndroid Build Coastguard Worker
491*8975f5c5SAndroid Build Coastguard Worker
492*8975f5c5SAndroid Build Coastguard Workerdef ObfuscateProfile(nonobfuscated_profile, dex_file, proguard_mapping,
493*8975f5c5SAndroid Build Coastguard Worker                     dexdump_path, output_filename):
494*8975f5c5SAndroid Build Coastguard Worker  """Helper method for obfuscating a profile.
495*8975f5c5SAndroid Build Coastguard Worker
496*8975f5c5SAndroid Build Coastguard Worker  Args:
497*8975f5c5SAndroid Build Coastguard Worker    nonobfuscated_profile: a profile with nonobfuscated symbols.
498*8975f5c5SAndroid Build Coastguard Worker    dex_file: path to the dex file matching the mapping.
499*8975f5c5SAndroid Build Coastguard Worker    proguard_mapping: a mapping from nonobfuscated to obfuscated symbols used
500*8975f5c5SAndroid Build Coastguard Worker      in the dex file.
501*8975f5c5SAndroid Build Coastguard Worker    dexdump_path: path to the dexdump utility.
502*8975f5c5SAndroid Build Coastguard Worker    output_filename: output filename in which to write the obfuscated profile.
503*8975f5c5SAndroid Build Coastguard Worker  """
504*8975f5c5SAndroid Build Coastguard Worker  dexinfo = ProcessDex(_RunDexDump(dexdump_path, dex_file))
505*8975f5c5SAndroid Build Coastguard Worker  _, reverse_mapping = ProcessProguardMapping(
506*8975f5c5SAndroid Build Coastguard Worker      _ReadFile(proguard_mapping), dexinfo)
507*8975f5c5SAndroid Build Coastguard Worker  obfuscated_profile = ProcessProfile(
508*8975f5c5SAndroid Build Coastguard Worker      _ReadFile(nonobfuscated_profile), reverse_mapping)
509*8975f5c5SAndroid Build Coastguard Worker  obfuscated_profile.WriteToFile(output_filename)
510*8975f5c5SAndroid Build Coastguard Worker
511*8975f5c5SAndroid Build Coastguard Worker
512*8975f5c5SAndroid Build Coastguard Workerdef main(args):
513*8975f5c5SAndroid Build Coastguard Worker  parser = argparse.ArgumentParser()
514*8975f5c5SAndroid Build Coastguard Worker  parser.add_argument(
515*8975f5c5SAndroid Build Coastguard Worker      '--dexdump-path',
516*8975f5c5SAndroid Build Coastguard Worker      required=True,
517*8975f5c5SAndroid Build Coastguard Worker      help='Path to dexdump binary.')
518*8975f5c5SAndroid Build Coastguard Worker  parser.add_argument(
519*8975f5c5SAndroid Build Coastguard Worker      '--dex-path',
520*8975f5c5SAndroid Build Coastguard Worker      required=True,
521*8975f5c5SAndroid Build Coastguard Worker      help='Path to dex file corresponding to the proguard mapping file.')
522*8975f5c5SAndroid Build Coastguard Worker  parser.add_argument(
523*8975f5c5SAndroid Build Coastguard Worker      '--proguard-mapping-path',
524*8975f5c5SAndroid Build Coastguard Worker      required=True,
525*8975f5c5SAndroid Build Coastguard Worker      help='Path to input proguard mapping file corresponding to the dex file.')
526*8975f5c5SAndroid Build Coastguard Worker  parser.add_argument(
527*8975f5c5SAndroid Build Coastguard Worker      '--output-profile-path',
528*8975f5c5SAndroid Build Coastguard Worker      required=True,
529*8975f5c5SAndroid Build Coastguard Worker      help='Path to output profile.')
530*8975f5c5SAndroid Build Coastguard Worker  parser.add_argument(
531*8975f5c5SAndroid Build Coastguard Worker      '--input-profile-path',
532*8975f5c5SAndroid Build Coastguard Worker      required=True,
533*8975f5c5SAndroid Build Coastguard Worker      help='Path to output profile.')
534*8975f5c5SAndroid Build Coastguard Worker  parser.add_argument(
535*8975f5c5SAndroid Build Coastguard Worker      '--verbose',
536*8975f5c5SAndroid Build Coastguard Worker      action='store_true',
537*8975f5c5SAndroid Build Coastguard Worker      default=False,
538*8975f5c5SAndroid Build Coastguard Worker      help='Print verbose output.')
539*8975f5c5SAndroid Build Coastguard Worker  obfuscation = parser.add_mutually_exclusive_group(required=True)
540*8975f5c5SAndroid Build Coastguard Worker  obfuscation.add_argument('--obfuscate', action='store_true',
541*8975f5c5SAndroid Build Coastguard Worker      help='Indicates to output an obfuscated profile given a deobfuscated '
542*8975f5c5SAndroid Build Coastguard Worker     'one.')
543*8975f5c5SAndroid Build Coastguard Worker  obfuscation.add_argument('--deobfuscate', dest='obfuscate',
544*8975f5c5SAndroid Build Coastguard Worker      action='store_false', help='Indicates to output a deobfuscated profile '
545*8975f5c5SAndroid Build Coastguard Worker      'given an obfuscated one.')
546*8975f5c5SAndroid Build Coastguard Worker  options = parser.parse_args(args)
547*8975f5c5SAndroid Build Coastguard Worker
548*8975f5c5SAndroid Build Coastguard Worker  if options.verbose:
549*8975f5c5SAndroid Build Coastguard Worker    log_level = logging.WARNING
550*8975f5c5SAndroid Build Coastguard Worker  else:
551*8975f5c5SAndroid Build Coastguard Worker    log_level = logging.ERROR
552*8975f5c5SAndroid Build Coastguard Worker  logging.basicConfig(format='%(levelname)s: %(message)s', level=log_level)
553*8975f5c5SAndroid Build Coastguard Worker
554*8975f5c5SAndroid Build Coastguard Worker  dex = ProcessDex(_RunDexDump(options.dexdump_path, options.dex_path))
555*8975f5c5SAndroid Build Coastguard Worker  proguard_mapping, reverse_proguard_mapping = ProcessProguardMapping(
556*8975f5c5SAndroid Build Coastguard Worker      _ReadFile(options.proguard_mapping_path), dex)
557*8975f5c5SAndroid Build Coastguard Worker  if options.obfuscate:
558*8975f5c5SAndroid Build Coastguard Worker    profile = ProcessProfile(
559*8975f5c5SAndroid Build Coastguard Worker        _ReadFile(options.input_profile_path),
560*8975f5c5SAndroid Build Coastguard Worker        reverse_proguard_mapping)
561*8975f5c5SAndroid Build Coastguard Worker  else:
562*8975f5c5SAndroid Build Coastguard Worker    profile = ProcessProfile(
563*8975f5c5SAndroid Build Coastguard Worker        _ReadFile(options.input_profile_path),
564*8975f5c5SAndroid Build Coastguard Worker        proguard_mapping)
565*8975f5c5SAndroid Build Coastguard Worker  profile.WriteToFile(options.output_profile_path)
566*8975f5c5SAndroid Build Coastguard Worker
567*8975f5c5SAndroid Build Coastguard Worker
568*8975f5c5SAndroid Build Coastguard Workerif __name__ == '__main__':
569*8975f5c5SAndroid Build Coastguard Worker  main(sys.argv[1:])
570