레이블이 adb인 게시물을 표시합니다. 모든 게시물 표시
레이블이 adb인 게시물을 표시합니다. 모든 게시물 표시

2022년 5월 15일 일요일

python adb adbutils 로그 획득하기 bugreportz 사용법

 python으로 adb를 이용하여 자동화 test를 구현하고 있습니다.

adbutils 구현중에 로그 획득하는 부분이 없어서 adbutils 을 래핑해서 구현해봤습니다.


1. property 구해서 dict type에 리턴받기

이번 시간에 구현할 함수는 프로퍼티 얻는 함수 입니다. 이미 adbutils 에 있긴하지만 전체 목록을 얻어서 dict type으로 저장하는 함수를 구현하였습니다.

1.1 adbuilts로 property 가져오기


ret = self.d.shell("getprop")


1.2 dict type으로 저장하기

획득한 property 형태는 [xxxx]: [yyyy] 형태로 되어있습니다. 

그런데 이것이 yyyy에 개행이 있는 경우도 있고 내부에 : , [ , ] 등의 문자가 포함되어 있을 수도 있습니다. 그래서 이부분을 정규식이나 단순히 [ ] 검색해서 뽑아내기가 복잡합니다.

여기에서는 약간의 편법을 사용하였는데 replace("]\n","]\0") 이용하여 하나의 property에 대해서 \0으로 치환하는 방법을 사용하였습니다. ([xxxx1]: [yyyy1]\n [xxxx2]: [yyyy2] 를 [xxxx1]: [yyyy1]\0 [xxxx2]: [yyyy2] 으로 치환함)

또한 하나의 property 안에서는 replace("]: [","]"+"\0"+"[") 두개의 값을 \0로 치환하였습니다. ([xxxx1]: [yyyy1] => [xxxx1]\0[yyyy1] 치환함)

이것을 구현한 내용입니다.


	def get_prop(self, keyname=None):
		ret_dict = {}
		# 전체 poperty 는 getprop 명령으로 획득 가능합니다.
		ret = self.d.shell("getprop")
		#print(ret)
		retlines = ret.replace("]\n","]\0")
		retlines = retlines.split("\0")
		
		for one_line in retlines:
			one_line = one_line.replace("]: [","]"+"\0"+"[")
			dat = one_line.split("\0")
			#print(dat)
			dat1 = dat[1].strip()
			ret_dict[dat[0][1:len(dat[0])-1]] = dat1[1:len(dat1)-1]
			#print(dat[0][1:len(dat[0])-1],ret_dict[dat[0][1:len(dat[0])-1]])
		
		if keyname==None:
			return ret_dict
		
		return ret_dict.get(keyname)


2. event log

android 단말기에서 오류가 발생하였다는것은 event log로 쉽게 알 수 있습니다. event log는 로그가 많이 로깅되지는 않기 때문에 비교적 오랜 시간 저장되어 있음을 알 수 있습니다.

먼저 event log를 획득하는 방법입니다.

2.1 event log를 획득하기


	#EVENT LOG (logcat -b events -v threadtime -v printable -v uid -d *:v)
	def get_event_log(self):
		ret = self.d.shell("logcat -b events -v threadtime -v printable -v uid -d *:v")
		#print(ret)
		return ret


2.1 event log에서 오류가 발생했는지 확인 하는 방법

android 단말에서는 두가지 형태의 오류가 있습니다. 첫번째는 단말 기본 동작 오류 일명 앱 강제 종료라고 불리는 Forced Close 입니다. 이런 동작은 메소드를 실행하지 못할때 일반적으로 발생합니다. 이때 이벤트 로그에서는 am_crash 로그가 찍히게 됩니다.

두번째로는 앱이 응답 없는 경우입니다. 앱들은 여러 프로세서와 통신을 하기 때문에 한쪽 시스템이 늦어지면 이렇현상이 빠질 수 있는데 이때 ANR이라고 불리는 am_anr 이 이벤트 로그에 찍힙니다.

그래서 이것을 획득한 event log에서 정규식을 이용해서 찾는 방법을 알아보겠습니다.


	# @return
	# None : 오류 없음
	# else : 오류 있음
	def check_event_log(self):
		find_str = "(am_crash)|(am_anr)"
		ret = self.get_event_log()
		ret = " \n\n\n\n am_cras \n  am_anr "
		finded = re.search(find_str,ret)
		return finded


3. bugreportz 

예전 adb에서는 adb bugreport가 동작했겠지만, 지금은 bugreportz (압축형태) 로 대체되었습니다. 그런데 adb에서는 abd bugreportz 라고 실행하면 되지만, adbutils 에서는 해당 커멘드를 지원하지 않습니다. 따라서 shell 에서 실행해야하는데 즉, adb shell bugreportz 라고 실행하면 됩니다. 

3.1 bugreportz 특징

bugreportz 는 /data/user_de/0/com.android.shell/files/bugreports/ 에 로그를 생성하며 생성시 이전 로그가 삭제 됩니다. (로그가 쌓이는것으로 걱정하지 않아도 됩니다.)

리턴값은 OK:로 시작하며 뒤에는 경로명이 넘어 옵니다.

예) OK:/data/user_de/0/com.android.shell/files/bugreports/dumpstate-2022-05-14-17-33-03.zip

동작중 다시 호출하면 아래와 같은 오류가 발생합니다.

Previous sys dump or full dump is running, so skip this one

로그가 생성된 다음 adb pull 로 해당 로그를 가져 오면 됩니다. 아래 함수는 로그 생성뒤 특정 파일이름으로 로그를 가져오는 소스입니다.

	def make_bugreportz(self, log_zip_filename="log.zip"):
		# bugreportz 는 /data/user_de/0/com.android.shell/files/bugreports/ 에 로그를 생성하며 생성시 이전로그가 삭제 됩니다.
		#            리턴값은 OK:로 시작하며 뒤에는 경로명이 넘어 옵니다.
		#            예) OK:/data/user_de/0/com.android.shell/files/bugreports/dumpstate-2022-05-14-17-33-03.zip
		#
		# 동작중 다시 호출하면 아래와 같은 string이 리턴됩니다.
		# Previous sys dump or full dump is running, so skip this one

		ret = self.d.shell("bugreportz")
		print(ret)

		isok = ret.split(":")
		if len(isok)!=2 or isok[0]!='OK':
			return -1
		
		# return 은 int size가 넘어옵니다.
		# 파일이 없으면 exception 발생합니다.
		#    adbutils.errors.AdbError: open failed: No such file or directory
		ret=self.d.sync.pull(isok[1], log_zip_filename)
		if ret==0:
			return -2

		# int size 가 리턴됩니다.
		return ret


4. 전체 소스

지금까지 내용을 소스로 만들어 봤습니다.

make_bugreportz 함수는 직접호출해도 되긴하지만 event log에 뭔가 이상한점이 발견되면 호출하도록 만들어 봤습니다.


from adbutils import adb
import re

class adb_utils_rp():
	def adb_connect(self, serial=None):
		self.d = adb.device(serial=serial)
		if self.d.serial==None :
			return None
		print("adb conneced",self.d.serial)
		return self.d

	def get_prop(self, keyname=None):
		ret_dict = {}
		# 전체 poperty 는 getprop 명령으로 획득 가능합니다.
		ret = self.d.shell("getprop")
		#print(ret)
		retlines = ret.replace("]\n","]\0")
		retlines = retlines.split("\0")
		
		for one_line in retlines:
			one_line = one_line.replace("]: [","]"+"\0"+"[")
			dat = one_line.split("\0")
			#print(dat)
			dat1 = dat[1].strip()
			ret_dict[dat[0][1:len(dat[0])-1]] = dat1[1:len(dat1)-1]
			#print(dat[0][1:len(dat[0])-1],ret_dict[dat[0][1:len(dat[0])-1]])
		
		if keyname==None:
			return ret_dict
		
		return ret_dict.get(keyname)

	def make_bugreportz(self, log_zip_filename="log.zip"):
		# bugreportz 는 /data/user_de/0/com.android.shell/files/bugreports/ 에 로그를 생성하며 생성시 이전로그가 삭제 됩니다.
		#            리턴값은 OK:로 시작하며 뒤에는 경로명이 넘어 옵니다.
		#            예) OK:/data/user_de/0/com.android.shell/files/bugreports/dumpstate-2022-05-14-17-33-03.zip
		#
		# 동작중 다시 호출하면 아래와 같은 string이 리턴됩니다.
		# Previous sys dump or full dump is running, so skip this one

		ret = self.d.shell("bugreportz")
		print(ret)

		isok = ret.split(":")
		if len(isok)!=2 or isok[0]!='OK':
			return -1
		
		# return 은 int size가 넘어옵니다.
		# 파일이 없으면 exception 발생합니다.
		#    adbutils.errors.AdbError: open failed: No such file or directory
		ret=self.d.sync.pull(isok[1], log_zip_filename)
		if ret==0:
			return -2

		# int size 가 리턴됩니다.
		return ret
		
	#EVENT LOG (logcat -b events -v threadtime -v printable -v uid -d *:v)
	def get_event_log(self):
		ret = self.d.shell("logcat -b events -v threadtime -v printable -v uid -d *:v")
		#print(ret)
		return ret
		
	# @return
	# None : 오류 없음
	# else : 오류 있음
	def check_event_log(self):
		find_str = "(am_crash)|(am_anr)"
		ret = self.get_event_log()
		finded = re.search(find_str,ret)
		return finded
		
if __name__ == "__main__":
	adbrp = adb_utils_rp()
	adbrp.adb_connect()
	print(adbrp.get_prop("ro.serialno"))
	print(adbrp.get_prop("ro.build.fingerprint"))
	#print(adbrp.make_bugreportz())
	print(adbrp.get_event_log())
	print(adbrp.check_event_log())
	if adbrp.check_event_log()!=None:
		adbrp.make_bugreportz()



2021년 8월 15일 일요일

adb, command, setting값 변경

android 에서 세팅 값을 변경을 원할때 아래와 같은 형태를 사용하면됩니다.

adb shell settings put <global|secure|system> <세팅값이름> <원하는값>


adb shell settings put global <세팅값이름> <원하는값>


shell에서 settings에 상세한 옵션에 대한 설명은 아래와 같습니다.

settings

$ settings
Settings provider (settings) commands:
  help
      Print this help text.
  get [--user <USER_ID> | current] NAMESPACE KEY
      Retrieve the current value of KEY.
  put [--user <USER_ID> | current] NAMESPACE KEY VALUE [TAG] [default]
      Change the contents of KEY to VALUE.
      TAG to associate with the setting.
      {default} to set as the default, case-insensitive only for global/secure namespace
  delete [--user <USER_ID> | current] NAMESPACE KEY
      Delete the entry for KEY.
  reset [--user <USER_ID> | current] NAMESPACE {PACKAGE_NAME | RESET_MODE}
      Reset the global/secure table for a package with mode.
      RESET_MODE is one of {untrusted_defaults, untrusted_clear, trusted_defaults}, case-insensitive
  list [--user <USER_ID> | current] NAMESPACE
      Print all defined keys.
      NAMESPACE is one of {system, secure, global}, case-insensitive


설정값 목록을 모를때에는 아래와 같은형태로 목록을 얻을 수 있습니다.

adb shell settings list secure

adb shell settings list global


Write 권한에 대해 알아보기위해 secure backup_auto_restore 항목에 대해 값을 써봤는데 정상적으로 변경이 되었다. 

o1s:/ $ settings get secure backup_auto_restore
1
o1s:/ $ settings put secure backup_auto_restore 0
o1s:/ $ settings get secure backup_auto_restore
0
o1s:/ $ settings put secure backup_auto_restore 1
o1s:/ $ settings get secure backup_auto_restore
1