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()



댓글 없음:

댓글 쓰기