def _monkey_thread_func(self,save_dir): '''获取monkey线程,保存monkey日志,monkey Crash日志暂不处理,后续有需要再处理 ''' self.append_log_line_num = 0 self.file_log_line_num = 0 self.log_file_create_time = None log_is_none = 0 logs = [] logger.debug("monkey_thread_func") if RuntimeData.start_time is None: RuntimeData.start_time = TimeUtils.getCurrentTime() while self.running: try: log = self._log_pipe.stdout.readline().strip() if not isinstance(log, str): # 先编码为unicode try: log = str(log, "utf8") except Exception as e: log = repr(log) logger.error('str error:' + log) logger.error(e) if log: logs.append(log) self.append_log_line_num = self.append_log_line_num + 1 self.file_log_line_num = self.file_log_line_num + 1 # if self.append_log_line_num > 1000: if self.append_log_line_num > 100: if not self.log_file_create_time: self.log_file_create_time = TimeUtils.getCurrentTimeUnderline() log_file = os.path.join(save_dir, 'monkey_%s.log' % self.log_file_create_time) self.append_log_line_num = 0 # 降低音量,避免音量过大,导致语音指令失败 self.device.adb.run_shell_cmd("input keyevent 25") self.save(log_file, logs) logs = [] # 新建文件 if self.file_log_line_num > 600000: # if self.file_log_line_num > 200: self.file_log_line_num = 0 self.log_file_create_time = TimeUtils.getCurrentTimeUnderline() log_file = os.path.join(save_dir, 'monkey_%s.log' % self.log_file_create_time) self.save(log_file, logs) logs = [] else: log_is_none = log_is_none + 1 if log_is_none % 1000 == 0: logger.info("log is none") if not self.device.adb.is_process_running("com.android.commands.monkey") and self.running: self.device.adb.kill_process("com.android.commands.monkey") self._log_pipe = self.device.adb.run_shell_cmd(self.monkey_cmd, sync=False) except: logger.error("an exception hanpend in monkey thread, reason unkown!") s = traceback.format_exc() logger.debug(s)
def __init__(self, csv_dir, packages=[]): os.chdir(csv_dir) # 需要画曲线的csv文件名 self.summary_csf_file = { "cpuinfo.csv": { "table_name": "pid_cpu", "x_axis": "datatime", "y_axis": "%", "values": ["pid_cpu%", "total_pid_cpu%"] }, "meminfo.csv": { "table_name": "pid_pss", "x_axis": "datatime", "y_axis": "mem(MB)", "values": ["pid_pss(MB)", "total_pss(MB)"] }, "pid_change.csv": { "table_name": "pid", "x_axis": "datatime", "y_axis": "pid_num", "values": ["pid"] }, } self.packages = packages if len(self.packages) > 0: for package in self.packages: pss_detail_dic = { "table_name": "pss_detail", "x_axis": "datatime", "y_axis": "mem(MB)", "values": ["pss", "java_heap", "native_heap", "system"] } # 文件名太长会导致写excel失败 self.summary_csf_file["pss_%s.csv" % package.split(".")[-1].replace( ":", "_")] = pss_detail_dic logger.debug(self.packages) logger.debug(self.summary_csf_file) logger.info('create report for %s' % csv_dir) file_names = self.filter_file_names(csv_dir) logger.debug('%s' % file_names) if file_names: book_name = 'summary_%s.xlsx' % TimeUtils.getCurrentTimeUnderline() excel = Excel(book_name) for file_name in file_names: logger.debug('get csv %s to excel' % file_name) values = self.summary_csf_file[file_name] excel.csv_to_xlsx(file_name, values["table_name"], values["x_axis"], values["y_axis"], values["values"]) logger.info('wait to save %s' % book_name) excel.save()
def run_shell_cmd(self, cmd, **kwds): '''执行 adb shell 命令 ''' # 如果失去连接后,adb又正常连接了 if not self.before_connect and self.after_connect: cpu_uptime_file = os.path.join(RuntimeData.package_save_path, "uptime.txt") with open(cpu_uptime_file, "a+",encoding = "utf-8") as writer: writer.write(TimeUtils.getCurrentTimeUnderline() + " /proc/uptime:" + self.run_adb_cmd("shell cat /proc/uptime") + "\n") self.before_connect = True ret = self.run_adb_cmd('shell', '%s' % cmd, **kwds) # 当 adb 命令传入 sync=False时,ret是Poen对象 if ret == None: logger.error(u'adb cmd failed:%s ' % cmd) return ret
def handle_exception(self, log_line): ''' 这个方法在每次有log时回调 :param log_line:最近一条的log 内容 异常日志写一个文件 :return:void ''' for tag in self.exception_log_list: if tag in log_line: logger.debug("exception Info: " + log_line) tmp_file = os.path.join(RuntimeData.package_save_path, 'exception.log') with open(tmp_file, 'a+') as f: f.write(log_line + '\n') # 这个路径 空格会有影响 process_stack_log_file = os.path.join( RuntimeData.package_save_path, 'process_stack_%s_%s.log' % (self.package, TimeUtils.getCurrentTimeUnderline())) # 如果进程挂了,pid会变 ,抓变后进程pid的堆栈没有意义 # self.logmonitor.device.adb.get_process_stack(self.package,process_stack_log_file) if RuntimeData.old_pid: self.device.adb.get_process_stack_from_pid( RuntimeData.old_pid, process_stack_log_file)
def run(self, time_out=None): self.clear_heapdump() # objgraph.show_growth() # 对设备连接情况的检查 if not self.serialnum: # androiddevice 没传 serialnum,默认执行adb shell logger.info( "serialnum in config file is null,default get connected phone") is_device_connect = False for i in range(0, 5): if self.device.adb.is_connected(self.serialnum): is_device_connect = True break else: logger.error("device not found:" + self.serialnum) time.sleep(2) if not is_device_connect: logger.error("after 5 times check,device not found:" + self.serialnum) return # 对是否安装被测app的检查 只在最开始检查一次 if not self.device.adb.is_app_installed(self.packages[0]): logger.error("test app not installed:" + self.packages[0]) return try: #初始化数据处理的类,将没有消息队列传递过去,以便获取数据,并处理 # datahandle = DataWorker(self.get_queue_dic()) # 将queue传进去,与datahandle那个线程交互 self.add_monitor( CpuMonitor(self.serialnum, self.packages, self.frequency, self.timeout)) self.add_monitor( MemMonitor(self.serialnum, self.packages, self.frequency, self.timeout)) self.add_monitor( TrafficMonitor(self.serialnum, self.packages[0], self.frequency, self.timeout)) # 软件方式 获取电量不准,已用硬件方案测试功耗 # self.add_monitor(PowerMonitor(self.serialnum, self.frequency,self.timeout)) self.add_monitor( FPSMonitor(self.serialnum, self.packages[0], self.frequency, self.timeout)) # 6.0以下能采集到fd数据,7.0以上没权限 if self.device.adb.get_sdk_version() <= 23: self.add_monitor( FdMonitor(self.serialnum, self.packages[0], self.frequency, self.timeout)) self.add_monitor( ThreadNumMonitor(self.serialnum, self.packages[0], self.frequency, self.timeout)) if self.config_dic["monkey"] == "true": self.add_monitor(Monkey(self.serialnum, self.packages[0])) if self.config_dic["main_activity"] and self.config_dic[ "activity_list"]: self.add_monitor( DeviceMonitor(self.serialnum, self.packages[0], self.frequency, self.config_dic["main_activity"], self.config_dic["activity_list"], RuntimeData.exit_event)) if len(self.monitors): start_time = TimeUtils.getCurrentTimeUnderline() RuntimeData.start_time = start_time if self.config_dic["save_path"]: RuntimeData.package_save_path = os.path.join( self.config_dic["save_path"], self.packages[0], start_time) else: RuntimeData.package_save_path = os.path.join( RuntimeData.top_dir, 'results', self.packages[0], start_time) FileUtils.makedir(RuntimeData.package_save_path) self.save_device_info() for monitor in self.monitors: #启动所有的monitors try: monitor.start(start_time) except Exception as e: logger.error(e) # logcat的代码可能会引起死锁,拎出来单独处理logcat try: self.logcat_monitor = LogcatMonitor( self.serialnum, self.packages[0]) # 如果有异常日志标志,才启动这个模块 if self.exceptionlog_list: self.logcat_monitor.set_exception_list( self.exceptionlog_list) self.logcat_monitor.add_log_handle( self.logcat_monitor.handle_exception) time.sleep(1) self.logcat_monitor.start(start_time) except Exception as e: logger.error(e) timeout = time_out if time_out != None else self.config_dic[ 'timeout'] endtime = time.time() + timeout while (time.time() < endtime): #吊着主线程防止线程中断 # 时间到或测试过程中检测到异常 if self.check_exit_signal_quit(): logger.error("app " + str(self.packages[0]) + " exit signal, quit!") break time.sleep(self.frequency) logger.debug("time is up,finish!!!") self.stop() # try: # datahandle.stop() # time.sleep(self.frequency*2) # # 延迟一点时间结束上报,已让数据上报完 # # report.stop() # except: # logger.debug("report or datahandle stop exception") # finally: # logger.info("time is up, end") # os._exit(0) except KeyboardInterrupt: #捕获键盘异常的事件,例如ctrl c logger.debug(" catch keyboardInterrupt, goodbye!!!") # 收尾工作 self.stop() os._exit(0) except Exception as e: logger.error("Exception in run") logger.error(e)
def _calculator_thread(self, start_time): '''处理surfaceflinger数据 ''' fps_file = os.path.join(RuntimeData.package_save_path, 'fps.csv') if self.use_legacy_method: fps_title = ['datetime', 'fps'] else: fps_title = ['datetime', "activity window", 'fps', 'jank'] try: with open(fps_file, 'a+') as df: csv.writer(df, lineterminator='\n').writerow(fps_title) if self.fps_queue: fps_file_dic = {'fps_file': fps_file} self.fps_queue.put(fps_file_dic) except RuntimeError as e: logger.exception(e) while True: try: data = self.data_queue.get() if isinstance(data, str) and data == 'Stop': break before = time.time() if self.use_legacy_method: td = data['timestamp'] - self.surface_before['timestamp'] seconds = td.seconds + td.microseconds / 1e6 frame_count = (data['page_flip_count'] - self.surface_before['page_flip_count']) fps = int(round(frame_count / seconds)) if fps > 60: fps = 60 self.surface_before = data logger.debug('FPS:%2s' % fps) tmp_list = [TimeUtils.getCurrentTimeUnderline(), fps] try: with open(fps_file, 'a+') as f: # tmp_list[0] = TimeUtils.formatTimeStamp(tmp_list[0]) csv.writer(f, lineterminator='\n').writerow(tmp_list) except RuntimeError as e: logger.exception(e) else: refresh_period = data[0] timestamps = data[1] collect_time = data[2] fps, jank = self._calculate_results( refresh_period, timestamps) logger.debug('FPS:%2s Jank:%s' % (fps, jank)) fps_list = [collect_time, self.focus_window, fps, jank] if self.fps_queue: self.fps_queue.put(fps_list) if not self.fps_queue: #为了让单个脚本运行时保存数据 try: with open(fps_file, 'a+') as f: tmp_list = copy.deepcopy(fps_list) tmp_list[0] = TimeUtils.formatTimeStamp( tmp_list[0]) csv.writer( f, lineterminator='\n').writerow(tmp_list) except RuntimeError as e: logger.exception(e) time_consume = time.time() - before delta_inter = self.frequency - time_consume if delta_inter > 0: time.sleep(delta_inter) except: logger.error( "an exception hanpend in fps _calculator_thread ,reason unkown!" ) s = traceback.format_exc() logger.debug(s) if self.fps_queue: self.fps_queue.task_done()
def save(self): pass def parse(self, file_path): '''解析 :param str file_path: 要解析数据文件的路径 ''' pass def get_fps_collector(self): '''获得fps收集器,收集器里保存着time fps jank的列表 :return: fps收集器 :rtype: SurfaceStatsCollector ''' return self.fpscollector if __name__ == '__main__': # tulanduo android8.0 api level 27 monitor = FPSMonitor('TC79SSDMO7HEY5Z9', "com.alibaba.ailabs.genie.smartapp", 1) # mate 9 android8.0 # monitor = FPSMonitor('MKJNW18226007860',"com.sankuai.meituan",2) # android8.0 Google Pixel 2 # monitor = FPSMonitor('HT7B81A05143',package_name = "com.alibaba.ailibs.genie.contacts",1) monitor.start(TimeUtils.getCurrentTimeUnderline()) time.sleep(600) monitor.stop()
def dump_native_heap(self, package, save_path): native_heap_file = "/data/local/tmp/%s_native_heap_%s.txt" % (package, TimeUtils.getCurrentTimeUnderline()) self.run_shell_cmd("am dumpheap -n %s %s" % (package, native_heap_file))
def dumpheap(self, package, save_path): heapfile = "/data/local/tmp/%s_dumpheap_%s.hprof" % (package, TimeUtils.getCurrentTimeUnderline()) self.run_shell_cmd("am dumpheap %s %s" % (package, heapfile)) time.sleep(10) self.pull_file(heapfile,save_path)
def _logcat_thread_func(self, save_dir, process_list, params=""): '''获取logcat线程 ''' self.append_log_line_num = 0 self.file_log_line_num = 0 self.log_file_create_time = None logs = [] logger.debug("logcat_thread_func") log_is_none = 0 while self._logcat_running: try: log = self._log_pipe.stdout.readline().strip() if not isinstance(log, str): try: log = str(log,"utf8") except Exception as e: log = repr(log) logger.error('str error:'+log) logger.error(e) if log: log_is_none = 0 # logger.debug(log) logs.append(log) # if self._log_pipe.poll() != None: # logger.debug('process:%s have exited' % self._log_pipe.pid) # if self._logcat_running : # self._log_pipe = self.run_shell_cmd('logcat ' + params, sync=False) # else : # break for _handle in self._logcat_handle: try: _handle(log) except Exception as e: logger.error("an exception happen in logcat handle log , reason unkown!, e:") logger.error(e) self.append_log_line_num = self.append_log_line_num + 1 self.file_log_line_num = self.file_log_line_num + 1 # if self.append_log_line_num > 1000: if self.append_log_line_num > 100: if not self.log_file_create_time: self.log_file_create_time = TimeUtils.getCurrentTimeUnderline() logcat_file = os.path.join(save_dir, 'logcat_%s.log' % self.log_file_create_time) self.append_log_line_num = 0 self.save(logcat_file, logs) logs = [] # 新建文件 if self.file_log_line_num > 600000: # if self.file_log_line_num > 200: self.file_log_line_num = 0 self.log_file_create_time = TimeUtils.getCurrentTimeUnderline() logcat_file = os.path.join(save_dir, 'logcat_%s.log' % self.log_file_create_time) self.save(logcat_file, logs) logs = [] else: log_is_none = log_is_none + 1 if log_is_none % 1000 == 0: logger.info("log is none") self._log_pipe = self.run_shell_cmd('logcat -v threadtime ' + params, sync=False) except: logger.error("an exception hanpend in logcat thread, reason unkown!") s = traceback.format_exc() logger.debug(s)
"time": timestamp, "act_name": content[1], "this_time": content[2], "total_time": content[3], "launch_type": content[4] } perf_data['launch_time'].append(dic) # perf_queue.put(perf_data) with open(tmp_file, "a+") as f: csvwriter = csv.writer(f, lineterminator='\n') #这种方式可以去除csv的空行 logger.debug("save launchtime data to csv: " + str(self.launch_list)) csvwriter.writerows(self.launch_list) del self.launch_list[:] if __name__ == '__main__': logcat_monitor = LogcatMonitor("85I7UO4PFQCINJL7", "com.yunos.tv.alitvasr") # 如果有异常日志标志,才启动这个模块 exceptionlog_list = ["fatal exception", "has died"] if exceptionlog_list: logcat_monitor.set_exception_list(exceptionlog_list) logcat_monitor.add_log_handle(logcat_monitor.handle_exception) start_time = TimeUtils.getCurrentTimeUnderline() RuntimeData.package_save_path = os.path.join(FileUtils.get_top_dir(), 'results', "com.yunos.tv.alitvasr", start_time) logcat_monitor.start(start_time)