#!/usr/bin/env python3 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								# 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								# Copyright (C) 2016 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. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								# 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								""" utils.py: export utility functions. 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								""" 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								from  __future__  import  print_function 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								import  logging 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								import  os 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								import  os . path 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								import  re 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								import  shutil 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								import  subprocess 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								import  sys 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								import  time 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								def  get_script_dir ( ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  os . path . dirname ( os . path . realpath ( __file__ ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								def  is_windows ( ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  sys . platform  ==  ' win32 '  or  sys . platform  ==  ' cygwin ' 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								def  is_darwin ( ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  sys . platform  ==  ' darwin ' 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								def  get_platform ( ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  is_windows ( ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        return  ' windows ' 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  is_darwin ( ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        return  ' darwin ' 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  ' linux ' 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								def  is_python3 ( ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  sys . version_info  > =  ( 3 ,  0 ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								def  log_debug ( msg ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    logging . debug ( msg ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								def  log_info ( msg ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    logging . info ( msg ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								def  log_warning ( msg ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    logging . warning ( msg ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								def  log_fatal ( msg ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    raise  Exception ( msg ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								def  log_exit ( msg ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    sys . exit ( msg ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								def  disable_debug_log ( ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    logging . getLogger ( ) . setLevel ( logging . WARN ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								def  str_to_bytes ( str ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  not  is_python3 ( ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        return  str 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    # In python 3, str are wide strings whereas the C api expects 8 bit strings, 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    # hence we have to convert. For now using utf-8 as the encoding. 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  str . encode ( ' utf-8 ' ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								def  bytes_to_str ( bytes ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  not  is_python3 ( ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        return  bytes 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  bytes . decode ( ' utf-8 ' ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								def  get_target_binary_path ( arch ,  binary_name ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  arch  ==  ' aarch64 ' : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        arch  =  ' arm64 ' 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    arch_dir  =  os . path . join ( get_script_dir ( ) ,  " bin " ,  " android " ,  arch ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  not  os . path . isdir ( arch_dir ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        log_fatal ( " can ' t find arch directory:  %s "  %  arch_dir ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    binary_path  =  os . path . join ( arch_dir ,  binary_name ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  not  os . path . isfile ( binary_path ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        log_fatal ( " can ' t find binary:  %s "  %  binary_path ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  binary_path 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								def  get_host_binary_path ( binary_name ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    dir  =  os . path . join ( get_script_dir ( ) ,  ' bin ' ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  is_windows ( ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  binary_name . endswith ( ' .so ' ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            binary_name  =  binary_name [ 0 : - 3 ]  +  ' .dll ' 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        elif  ' . '  not  in  binary_name : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            binary_name  + =  ' .exe ' 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        dir  =  os . path . join ( dir ,  ' windows ' ) 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    elif  sys . platform  ==  ' darwin ' :   # OSX 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  binary_name . endswith ( ' .so ' ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            binary_name  =  binary_name [ 0 : - 3 ]  +  ' .dylib ' 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        dir  =  os . path . join ( dir ,  ' darwin ' ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    else : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        dir  =  os . path . join ( dir ,  ' linux ' ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    dir  =  os . path . join ( dir ,  ' x86_64 '  if  sys . maxsize  >  2  * *  32  else  ' x86 ' ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    binary_path  =  os . path . join ( dir ,  binary_name ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  not  os . path . isfile ( binary_path ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        log_fatal ( " can ' t find binary:  %s "  %  binary_path ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  binary_path 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								def  is_executable_available ( executable ,  option = ' --help ' ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    """  Run an executable to see if it exists.  """ 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    try : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        subproc  =  subprocess . Popen ( [ executable ,  option ] ,  stdout = subprocess . PIPE , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                                   stderr = subprocess . PIPE ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        subproc . communicate ( ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        return  subproc . returncode  ==  0 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    except : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        return  False 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								DEFAULT_NDK_PATH  =  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    ' darwin ' :  ' Library/Android/sdk/ndk-bundle ' , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    ' linux ' :  ' Android/Sdk/ndk-bundle ' , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    ' windows ' :  ' AppData/Local/Android/sdk/ndk-bundle ' , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								EXPECTED_TOOLS  =  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    ' adb ' :  { 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        ' is_binutils ' :  False , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        ' test_option ' :  ' version ' , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        ' path_in_ndk ' :  ' ../platform-tools/adb ' , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    ' readelf ' :  { 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        ' is_binutils ' :  True , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        ' accept_tool_without_arch ' :  True , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    ' addr2line ' :  { 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        ' is_binutils ' :  True , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        ' accept_tool_without_arch ' :  True 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    ' objdump ' :  { 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        ' is_binutils ' :  True , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								def  _get_binutils_path_in_ndk ( toolname ,  arch ,  platform ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  not  arch : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        arch  =  ' arm64 ' 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  arch  ==  ' arm64 ' : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        name  =  ' aarch64-linux-android- '  +  toolname 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        path  =  ' toolchains/aarch64-linux-android-4.9/prebuilt/ %s -x86_64/bin/ %s '  %  ( platform ,  name ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    elif  arch  ==  ' arm ' : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        name  =  ' arm-linux-androideabi- '  +  toolname 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        path  =  ' toolchains/arm-linux-androideabi-4.9/prebuilt/ %s -x86_64/bin/ %s '  %  ( platform ,  name ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    elif  arch  ==  ' x86_64 ' : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        name  =  ' x86_64-linux-android- '  +  toolname 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        path  =  ' toolchains/x86_64-4.9/prebuilt/ %s -x86_64/bin/ %s '  %  ( platform ,  name ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    elif  arch  ==  ' x86 ' : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        name  =  ' i686-linux-android- '  +  toolname 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        path  =  ' toolchains/x86-4.9/prebuilt/ %s -x86_64/bin/ %s '  %  ( platform ,  name ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    else : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        log_fatal ( ' unexpected arch  %s '  %  arch ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  ( name ,  path ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								def  find_tool_path ( toolname ,  ndk_path = None ,  arch = None ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  toolname  not  in  EXPECTED_TOOLS : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        return  None 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    tool_info  =  EXPECTED_TOOLS [ toolname ] 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    is_binutils  =  tool_info [ ' is_binutils ' ] 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    test_option  =  tool_info . get ( ' test_option ' ,  ' --help ' ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    platform  =  get_platform ( ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  is_binutils : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        toolname_with_arch ,  path_in_ndk  =  _get_binutils_path_in_ndk ( toolname ,  arch ,  platform ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    else : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        toolname_with_arch  =  toolname 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        path_in_ndk  =  tool_info [ ' path_in_ndk ' ] 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    path_in_ndk  =  path_in_ndk . replace ( ' / ' ,  os . sep ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    # 1. Find tool in the given ndk path. 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ndk_path : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        path  =  os . path . join ( ndk_path ,  path_in_ndk ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  is_executable_available ( path ,  test_option ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            return  path 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    # 2. Find tool in the ndk directory containing simpleperf scripts. 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    path  =  os . path . join ( ' .. ' ,  path_in_ndk ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  is_executable_available ( path ,  test_option ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        return  path 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    # 3. Find tool in the default ndk installation path. 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    home  =  os . environ . get ( ' HOMEPATH ' )  if  is_windows ( )  else  os . environ . get ( ' HOME ' ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  home : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        default_ndk_path  =  os . path . join ( home ,  DEFAULT_NDK_PATH [ platform ] . replace ( ' / ' ,  os . sep ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        path  =  os . path . join ( default_ndk_path ,  path_in_ndk ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  is_executable_available ( path ,  test_option ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            return  path 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    # 4. Find tool in $PATH. 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  is_executable_available ( toolname_with_arch ,  test_option ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        return  toolname_with_arch 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    # 5. Find tool without arch in $PATH. 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  is_binutils  and  tool_info . get ( ' accept_tool_without_arch ' ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  is_executable_available ( toolname ,  test_option ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            return  toolname 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  None 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								class  AdbHelper ( object ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    def  __init__ ( self ,  enable_switch_to_root = True ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        adb_path  =  find_tool_path ( ' adb ' ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  not  adb_path : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            log_exit ( " Can ' t find adb in PATH environment. " ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        self . adb_path  =  adb_path 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        self . enable_switch_to_root  =  enable_switch_to_root 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    def  run ( self ,  adb_args ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        return  self . run_and_return_output ( adb_args ) [ 0 ] 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    def  run_and_return_output ( self ,  adb_args ,  stdout_file = None ,  log_output = True ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        adb_args  =  [ self . adb_path ]  +  adb_args 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        log_debug ( ' run adb cmd:  %s '  %  adb_args ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  stdout_file : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            with  open ( stdout_file ,  ' wb ' )  as  stdout_fh : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                returncode  =  subprocess . call ( adb_args ,  stdout = stdout_fh ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            stdoutdata  =  ' ' 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        else : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            subproc  =  subprocess . Popen ( adb_args ,  stdout = subprocess . PIPE ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            ( stdoutdata ,  _ )  =  subproc . communicate ( ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            returncode  =  subproc . returncode 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        result  =  ( returncode  ==  0 ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  stdoutdata  and  adb_args [ 1 ]  !=  ' push '  and  adb_args [ 1 ]  !=  ' pull ' : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            stdoutdata  =  bytes_to_str ( stdoutdata ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            if  log_output : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                log_debug ( stdoutdata ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        log_debug ( ' run adb cmd:  %s   [result  %s ] '  %  ( adb_args ,  result ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        return  ( result ,  stdoutdata ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    def  check_run ( self ,  adb_args ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        self . check_run_and_return_output ( adb_args ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    def  check_run_and_return_output ( self ,  adb_args ,  stdout_file = None ,  log_output = True ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        result ,  stdoutdata  =  self . run_and_return_output ( adb_args ,  stdout_file ,  log_output ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  not  result : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            log_exit ( ' run  " adb  %s "  failed '  %  adb_args ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        return  stdoutdata 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    def  _unroot ( self ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        result ,  stdoutdata  =  self . run_and_return_output ( [ ' shell ' ,  ' whoami ' ] ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  not  result : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            return 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  ' root '  not  in  stdoutdata : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            return 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        log_info ( ' unroot adb ' ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        self . run ( [ ' unroot ' ] ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        self . run ( [ ' wait-for-device ' ] ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        time . sleep ( 1 ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    def  switch_to_root ( self ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  not  self . enable_switch_to_root : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            self . _unroot ( ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            return  False 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        result ,  stdoutdata  =  self . run_and_return_output ( [ ' shell ' ,  ' whoami ' ] ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  not  result : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            return  False 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  ' root '  in  stdoutdata : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            return  True 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        build_type  =  self . get_property ( ' ro.build.type ' ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  build_type  ==  ' user ' : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            return  False 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        self . run ( [ ' root ' ] ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        time . sleep ( 1 ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        self . run ( [ ' wait-for-device ' ] ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        result ,  stdoutdata  =  self . run_and_return_output ( [ ' shell ' ,  ' whoami ' ] ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        return  result  and  ' root '  in  stdoutdata 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    def  get_property ( self ,  name ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        result ,  stdoutdata  =  self . run_and_return_output ( [ ' shell ' ,  ' getprop ' ,  name ] ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        return  stdoutdata  if  result  else  None 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    def  set_property ( self ,  name ,  value ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        return  self . run ( [ ' shell ' ,  ' setprop ' ,  name ,  value ] ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    def  get_device_arch ( self ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        output  =  self . check_run_and_return_output ( [ ' shell ' ,  ' uname ' ,  ' -m ' ] ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  ' aarch64 '  in  output : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            return  ' arm64 ' 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  ' arm '  in  output : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            return  ' arm ' 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  ' x86_64 '  in  output : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            return  ' x86_64 ' 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  ' 86 '  in  output : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            return  ' x86 ' 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        log_fatal ( ' unsupported architecture:  %s '  %  output . strip ( ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    def  get_android_version ( self ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        build_version  =  self . get_property ( ' ro.build.version.release ' ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        android_version  =  0 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  build_version : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            if  not  build_version [ 0 ] . isdigit ( ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                c  =  build_version [ 0 ] . upper ( ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                if  c . isupper ( )  and  c  > =  ' L ' : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                    android_version  =  ord ( c )  -  ord ( ' L ' )  +  5 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            else : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                strs  =  build_version . split ( ' . ' ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                if  strs : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                    android_version  =  int ( strs [ 0 ] ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        return  android_version 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								def  flatten_arg_list ( arg_list ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    res  =  [ ] 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  arg_list : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        for  items  in  arg_list : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            res  + =  items 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  res 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								def  remove ( dir_or_file ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  os . path . isfile ( dir_or_file ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        os . remove ( dir_or_file ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    elif  os . path . isdir ( dir_or_file ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        shutil . rmtree ( dir_or_file ,  ignore_errors = True ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								def  open_report_in_browser ( report_path ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  is_darwin ( ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        # On darwin 10.12.6, webbrowser can't open browser, so try `open` cmd first. 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        try : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            subprocess . check_call ( [ ' open ' ,  report_path ] ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            return 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        except : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            pass 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    import  webbrowser 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    try : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        # Try to open the report with Chrome 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        browser_key  =  ' ' 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        for  key ,  _  in  webbrowser . _browsers . items ( ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            if  ' chrome '  in  key : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                browser_key  =  key 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        browser  =  webbrowser . get ( browser_key ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        browser . open ( report_path ,  new = 0 ,  autoraise = True ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    except : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        # webbrowser.get() doesn't work well on darwin/windows. 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        webbrowser . open_new_tab ( report_path ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								def  find_real_dso_path ( dso_path_in_record_file ,  binary_cache_path ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    """  Given the path of a shared library in perf.data, find its real path in the file system.  """ 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  dso_path_in_record_file [ 0 ]  !=  ' / '  or  dso_path_in_record_file  ==  ' //anon ' : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        return  None 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  binary_cache_path : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        tmp_path  =  os . path . join ( binary_cache_path ,  dso_path_in_record_file [ 1 : ] ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  os . path . isfile ( tmp_path ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            return  tmp_path 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  os . path . isfile ( dso_path_in_record_file ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        return  dso_path_in_record_file 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  None 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								class  Addr2Nearestline ( object ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    """  Use addr2line to convert (dso_path, func_addr, addr) to (source_file, line) pairs. 
  
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        For  instructions  generated  by  C + +  compilers  without  a  matching  statement  in  source  code 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        ( like  stack  corruption  check ,  switch  optimization ,  etc . ) ,  addr2line  can ' t generate 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        line  information .  However ,  we  want  to  assign  the  instruction  to  the  nearest  line  before 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        the  instruction  ( just  like  objdump  - dl ) .  So  we  use  below  strategy : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        Instead  of  finding  the  exact  line  of  the  instruction  in  an  address ,  we  find  the  nearest 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        line  to  the  instruction  in  an  address .  If  an  address  doesn ' t have a line info, we find 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        the  line  info  of  address  -  1.  If  still  no  line  info ,  then  use  address  -  2 ,  address  -  3 , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        etc . 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        The  implementation  steps  are  as  below : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        1.  Collect  all  ( dso_path ,  func_addr ,  addr )  requests  before  converting .  This  saves  the 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        times  to  call  addr2line . 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        2.  Convert  addrs  to  ( source_file ,  line )  pairs  for  each  dso_path  as  below : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          2.1  Check  if  the  dso_path  has  . debug_line .  If  not ,  omit  its  conversion . 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          2.2  Get  arch  of  the  dso_path ,  and  decide  the  addr_step  for  it .  addr_step  is  the  step  we 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          change  addr  each  time .  For  example ,  since  instructions  of  arm64  are  all  4  bytes  long , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          addr_step  for  arm64  can  be  4. 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          2.3  Use  addr2line  to  find  line  info  for  each  addr  in  the  dso_path . 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          2.4  For  each  addr  without  line  info ,  use  addr2line  to  find  line  info  for 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								              range ( addr  -  addr_step ,  addr  -  addr_step  *  4  -  1 ,  - addr_step ) . 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          2.5  For  each  addr  without  line  info ,  use  addr2line  to  find  line  info  for 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								              range ( addr  -  addr_step  *  5 ,  addr  -  addr_step  *  128  -  1 ,  - addr_step ) . 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								              ( 128  is  a  guess  number .  A  nested  switch  statement  in 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								               system / core / demangle / Demangler . cpp  has  > 300  bytes  without  line  info  in  arm64 . ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    """ 
  
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    class  Dso ( object ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        """  Info of a dynamic shared library. 
  
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            addrs :  a  map  from  address  to  Addr  object  in  this  dso . 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        """ 
  
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        def  __init__ ( self ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            self . addrs  =  { } 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    class  Addr ( object ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        """  Info of an addr request. 
  
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            func_addr :  start_addr  of  the  function  containing  addr . 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            source_lines :  a  list  of  [ file_id ,  line_number ]  for  addr . 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                          source_lines [ : - 1 ]  are  all  for  inlined  functions . 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        """ 
  
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        def  __init__ ( self ,  func_addr ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            self . func_addr  =  func_addr 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            self . source_lines  =  None 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    def  __init__ ( self ,  ndk_path ,  binary_cache_path ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        self . addr2line_path  =  find_tool_path ( ' addr2line ' ,  ndk_path ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  not  self . addr2line_path : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            log_exit ( " Can ' t find addr2line. Please set ndk path with --ndk-path option. " ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        self . readelf  =  ReadElf ( ndk_path ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        self . dso_map  =  { }   # map from dso_path to Dso. 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        self . binary_cache_path  =  binary_cache_path 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        # Saving file names for each addr takes a lot of memory. So we store file ids in Addr, 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        # and provide data structures connecting file id and file name here. 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        self . file_name_to_id  =  { } 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        self . file_id_to_name  =  [ ] 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    def  add_addr ( self ,  dso_path ,  func_addr ,  addr ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        dso  =  self . dso_map . get ( dso_path ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  dso  is  None : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            dso  =  self . dso_map [ dso_path ]  =  self . Dso ( ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  addr  not  in  dso . addrs : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            dso . addrs [ addr ]  =  self . Addr ( func_addr ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    def  convert_addrs_to_lines ( self ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        for  dso_path  in  self . dso_map : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            self . _convert_addrs_in_one_dso ( dso_path ,  self . dso_map [ dso_path ] ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    def  _convert_addrs_in_one_dso ( self ,  dso_path ,  dso ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        real_path  =  find_real_dso_path ( dso_path ,  self . binary_cache_path ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  not  real_path : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            if  dso_path  not  in  [ ' //anon ' ,  ' unknown ' ,  ' [kernel.kallsyms] ' ] : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                log_debug ( " Can ' t find dso  %s "  %  dso_path ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            return 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  not  self . _check_debug_line_section ( real_path ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            log_debug ( " file  %s  doesn ' t contain .debug_line section. "  %  real_path ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            return 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        addr_step  =  self . _get_addr_step ( real_path ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        self . _collect_line_info ( dso ,  real_path ,  [ 0 ] ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        self . _collect_line_info ( dso ,  real_path ,  range ( - addr_step ,  - addr_step  *  4  -  1 ,  - addr_step ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        self . _collect_line_info ( dso ,  real_path , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                                range ( - addr_step  *  5 ,  - addr_step  *  128  -  1 ,  - addr_step ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    def  _check_debug_line_section ( self ,  real_path ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        return  ' .debug_line '  in  self . readelf . get_sections ( real_path ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    def  _get_addr_step ( self ,  real_path ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        arch  =  self . readelf . get_arch ( real_path ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  arch  ==  ' arm64 ' : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            return  4 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  arch  ==  ' arm ' : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            return  2 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        return  1 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    def  _collect_line_info ( self ,  dso ,  real_path ,  addr_shifts ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        """  Use addr2line to get line info in a dso, with given addr shifts.  """ 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        # 1. Collect addrs to send to addr2line. 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        addr_set  =  set ( ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        for  addr  in  dso . addrs : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            addr_obj  =  dso . addrs [ addr ] 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            if  addr_obj . source_lines :   # already has source line, no need to search. 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                continue 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            for  shift  in  addr_shifts : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                # The addr after shift shouldn't change to another function. 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                shifted_addr  =  max ( addr  +  shift ,  addr_obj . func_addr ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                addr_set . add ( shifted_addr ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                if  shifted_addr  ==  addr_obj . func_addr : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                    break 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  not  addr_set : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            return 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        addr_request  =  ' \n ' . join ( [ ' %x '  %  addr  for  addr  in  sorted ( addr_set ) ] ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        # 2. Use addr2line to collect line info. 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        try : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            subproc  =  subprocess . Popen ( [ self . addr2line_path ,  ' -ai ' ,  ' -e ' ,  real_path ] , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                                       stdin = subprocess . PIPE ,  stdout = subprocess . PIPE ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            ( stdoutdata ,  _ )  =  subproc . communicate ( str_to_bytes ( addr_request ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            stdoutdata  =  bytes_to_str ( stdoutdata ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        except : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            return 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        addr_map  =  { } 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        cur_line_list  =  None 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        for  line  in  stdoutdata . strip ( ) . split ( ' \n ' ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            if  line [ : 2 ]  ==  ' 0x ' : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                # a new address 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                cur_line_list  =  addr_map [ int ( line ,  16 ) ]  =  [ ] 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            else : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                # a file:line. 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                if  cur_line_list  is  None : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                    continue 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                # Handle lines like "C:\Users\...\file:32". 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                items  =  line . rsplit ( ' : ' ,  1 ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                if  len ( items )  !=  2 : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                    continue 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                if  ' ? '  in  line : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                    # if ? in line, it doesn't have a valid line info. 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                    # An addr can have a list of (file, line), when the addr belongs to an inlined 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                    # function. Sometimes only part of the list has ? mark. In this case, we think 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                    # the line info is valid if the first line doesn't have ? mark. 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                    if  not  cur_line_list : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                        cur_line_list  =  None 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                    continue 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                ( file_path ,  line_number )  =  items 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                line_number  =  line_number . split ( ) [ 0 ]   # Remove comments after line number 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                try : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                    line_number  =  int ( line_number ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                except  ValueError : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                    continue 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                file_id  =  self . _get_file_id ( file_path ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                cur_line_list . append ( ( file_id ,  line_number ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        # 3. Fill line info in dso.addrs. 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        for  addr  in  dso . addrs : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            addr_obj  =  dso . addrs [ addr ] 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            if  addr_obj . source_lines : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                continue 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            for  shift  in  addr_shifts : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                shifted_addr  =  max ( addr  +  shift ,  addr_obj . func_addr ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                lines  =  addr_map . get ( shifted_addr ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                if  lines : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                    addr_obj . source_lines  =  lines 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                    break 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                if  shifted_addr  ==  addr_obj . func_addr : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                    break 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    def  _get_file_id ( self ,  file_path ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        file_id  =  self . file_name_to_id . get ( file_path ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  file_id  is  None : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            file_id  =  self . file_name_to_id [ file_path ]  =  len ( self . file_id_to_name ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            self . file_id_to_name . append ( file_path ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        return  file_id 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    def  get_dso ( self ,  dso_path ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        return  self . dso_map . get ( dso_path ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    def  get_addr_source ( self ,  dso ,  addr ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        source  =  dso . addrs [ addr ] . source_lines 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  source  is  None : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            return  None 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        return  [ ( self . file_id_to_name [ file_id ] ,  line )  for  ( file_id ,  line )  in  source ] 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								class  Objdump ( object ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    """  A wrapper of objdump to disassemble code.  """ 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    def  __init__ ( self ,  ndk_path ,  binary_cache_path ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        self . ndk_path  =  ndk_path 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        self . binary_cache_path  =  binary_cache_path 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        self . readelf  =  ReadElf ( ndk_path ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        self . objdump_paths  =  { } 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    def  disassemble_code ( self ,  dso_path ,  start_addr ,  addr_len ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        """  Disassemble [start_addr, start_addr + addr_len] of dso_path. 
  
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            Return  a  list  of  pair  ( disassemble_code_line ,  addr ) . 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        """ 
  
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        # 1. Find real path. 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        real_path  =  find_real_dso_path ( dso_path ,  self . binary_cache_path ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  real_path  is  None : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            return  None 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        # 2. Get path of objdump. 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        arch  =  self . readelf . get_arch ( real_path ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  arch  ==  ' unknown ' : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            return  None 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        objdump_path  =  self . objdump_paths . get ( arch ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  not  objdump_path : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            objdump_path  =  find_tool_path ( ' objdump ' ,  self . ndk_path ,  arch ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            if  not  objdump_path : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                log_exit ( " Can ' t find objdump. Please set ndk path with --ndk_path option. " ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            self . objdump_paths [ arch ]  =  objdump_path 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        # 3. Run objdump. 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        args  =  [ objdump_path ,  ' -dlC ' ,  ' --no-show-raw-insn ' , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                ' --start-address=0x %x '  %  start_addr , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                ' --stop-address=0x %x '  %  ( start_addr  +  addr_len ) , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                real_path ] 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        try : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            subproc  =  subprocess . Popen ( args ,  stdin = subprocess . PIPE ,  stdout = subprocess . PIPE ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            ( stdoutdata ,  _ )  =  subproc . communicate ( ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            stdoutdata  =  bytes_to_str ( stdoutdata ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        except : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            return  None 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  not  stdoutdata : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            return  None 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        result  =  [ ] 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        for  line  in  stdoutdata . split ( ' \n ' ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            line  =  line . rstrip ( )   # Remove '\r' on Windows. 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            items  =  line . split ( ' : ' ,  1 ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            try : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                addr  =  int ( items [ 0 ] ,  16 ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            except  ValueError : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                addr  =  0 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            result . append ( ( line ,  addr ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        return  result 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								class  ReadElf ( object ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    """  A wrapper of readelf.  """ 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    def  __init__ ( self ,  ndk_path ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        self . readelf_path  =  find_tool_path ( ' readelf ' ,  ndk_path ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  not  self . readelf_path : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            log_exit ( " Can ' t find readelf. Please set ndk path with --ndk_path option. " ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    def  get_arch ( self ,  elf_file_path ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        """  Get arch of an elf file.  """ 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        try : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            output  =  subprocess . check_output ( [ self . readelf_path ,  ' -h ' ,  elf_file_path ] ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            if  output . find ( ' AArch64 ' )  !=  - 1 : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                return  ' arm64 ' 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            if  output . find ( ' ARM ' )  !=  - 1 : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                return  ' arm ' 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            if  output . find ( ' X86-64 ' )  !=  - 1 : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                return  ' x86_64 ' 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            if  output . find ( ' 80386 ' )  !=  - 1 : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                return  ' x86 ' 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        except  subprocess . CalledProcessError : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            pass 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        return  ' unknown ' 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    def  get_build_id ( self ,  elf_file_path ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        """  Get build id of an elf file.  """ 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        try : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            output  =  subprocess . check_output ( [ self . readelf_path ,  ' -n ' ,  elf_file_path ] ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            output  =  bytes_to_str ( output ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            result  =  re . search ( r ' Build ID: \ s*( \ S+) ' ,  output ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            if  result : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                build_id  =  result . group ( 1 ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                if  len ( build_id )  <  40 : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                    build_id  + =  ' 0 '  *  ( 40  -  len ( build_id ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                else : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                    build_id  =  build_id [ : 40 ] 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                build_id  =  ' 0x '  +  build_id 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                return  build_id 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        except  subprocess . CalledProcessError : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            pass 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        return  " " 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    def  get_sections ( self ,  elf_file_path ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        """  Get sections of an elf file.  """ 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        section_names  =  [ ] 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        try : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            output  =  subprocess . check_output ( [ self . readelf_path ,  ' -SW ' ,  elf_file_path ] ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            output  =  bytes_to_str ( output ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            for  line  in  output . split ( ' \n ' ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                # Parse line like:" [ 1] .note.android.ident NOTE  0000000000400190 ...". 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                result  =  re . search ( r ' ^ \ s+ \ [ \ s* \ d+ \ ] \ s(.+?) \ s ' ,  line ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                if  result : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                    section_name  =  result . group ( 1 ) . strip ( ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                    if  section_name : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                        section_names . append ( section_name ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        except  subprocess . CalledProcessError : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            pass 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        return  section_names 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								def  extant_dir ( arg ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    """ ArgumentParser type that only accepts extant directories. 
  
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    Args : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        arg :  The  string  argument  given  on  the  command  line . 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    Returns :  The  argument  as  a  realpath . 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    Raises : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        argparse . ArgumentTypeError :  The  given  path  isn ' t a directory. 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    """ 
  
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    path  =  os . path . realpath ( arg ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  not  os . path . isdir ( path ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        import  argparse 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        raise  argparse . ArgumentTypeError ( ' {}  is not a directory. ' . format ( path ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  path 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								logging . getLogger ( ) . setLevel ( logging . DEBUG )