diff --git a/cpu.py b/cpu.py index 96e925a..aad0d13 100644 --- a/cpu.py +++ b/cpu.py @@ -97,6 +97,9 @@ def __init__(self, package_id): # The average percentage of CPU used by the device. self._mean_device_usage = None + # The error message. + self.error = '' + # TODO(danduri@): Validate whether the "top" command is valid for # different android versions. # "adb shell top -bn1" output: @@ -145,6 +148,7 @@ def __init__(self, package_id): except subprocess.CalledProcessError: logging.error("Please check your ADB connection.") sys.exit(1) + logging.error('Test init CPUED') def _web_gather_cpu_usage(self): """Stores the percentage of CPU used by the application and the @@ -158,42 +162,22 @@ def _web_gather_cpu_usage(self): _app_values _device_values """ - - top_output = subprocess.check_output( - _ADB_SHELL_TOP.split()).strip().decode() - - app_matches = self._re_app.search(top_output) - if not app_matches: - return (f"\nRegex: Match for {self._package_name}" - " not found in top output") - - app_cpu = app_matches.group("cpu") - if not app_cpu: - return "Application CPU usage values are not recorded." - - self._app_usage = float(app_cpu) - - # Captures regex group(line) containing the CPU idle usage info. - cpu_matches = self._re_cpu.search(top_output) - if not cpu_matches: - return ("\nRegex: Match for (percentage)idle cpu usage not " - "found in top output") - - idle_cpu = float(cpu_matches.group("idle")) - self._max_cpu = float(cpu_matches.group("cpu")) - - if not idle_cpu: - return "CPU idle values are not recorded." - if not self._max_cpu: - return "CPU max value is not recorded." - self._device_usage = self._max_cpu - idle_cpu - usages = { - "app": self._app_usage, - "device": self._device_usage + stats = { + "app": 0, + "device": 0 } - return usages - - def _gather_cpu_usage(self) -> None: + + if self._gather_cpu_usage(): + stats['app'] = self._app_values[-1] + stats['device'] = self._device_values[-1] + return stats + + def get_error(self): + msg = self.error + self.error = "" + return msg + + def _gather_cpu_usage(self, cmdLine=True) -> None: """Stores the percentage of CPU used by the application and the device. @@ -211,31 +195,52 @@ def _gather_cpu_usage(self) -> None: app_matches = self._re_app.search(top_output) if not app_matches: - logging.error(f"\nRegex: Match for {self._package_name}" + msg = (f"\nRegex: Match for {self._package_name}" " not found in top output") - sys.exit(1) + self.error = msg + logging.error(msg) + if cmdLine: + sys.exit(1) + return False app_cpu = app_matches.group("cpu") - assert app_cpu, "Application CPU usage values are not recorded." - self._app_usage = float(app_cpu) - + if not app_cpu: + self.error = "Application CPU usage values are not recorded." + logging.error("Application CPU usage values are not recorded.") + if cmdLine: + sys.exit(1) + return False + # Captures regex group(line) containing the CPU idle usage info. cpu_matches = self._re_cpu.search(top_output) if not cpu_matches: - logging.error("\nRegex: Match for (percentage)idle cpu usage not " + msg = ("\nRegex: Match for (percentage)idle cpu usage not " "found in top output") - sys.exit(1) + self.error = msg + logging.error(msg) + if cmdLine: + sys.exit(1) + return False idle_cpu = float(cpu_matches.group("idle")) - self._max_cpu = float(cpu_matches.group("cpu")) + max_cpu = float(cpu_matches.group("cpu")) + + if not idle_cpu or not max_cpu: + self.error = "CPU idle/ max values are not recorded." + logging.error("CPU idle/ max values are not recorded.") + sys.exit(1) + return False - assert idle_cpu, "CPU idle values are not recorded." - assert self._max_cpu, "CPU max value is not recorded." - self._device_usage = self._max_cpu - idle_cpu + # Only update values if both app and device were captured. + self._max_cpu = max_cpu + self._device_usage = max_cpu - idle_cpu + self._app_usage = float(app_cpu) # Stores app and device usage for later calculations. self._app_values.append(self._app_usage) self._device_values.append(self._device_usage) + return True + def _update_cpu_calculations(self) -> None: """Updates the mean of the application and device CPU usage recorded diff --git a/fps.py b/fps.py index 7827ac1..28e350c 100644 --- a/fps.py +++ b/fps.py @@ -320,29 +320,53 @@ def _get_recent_frames(self): Frame(draw=entries[0], vsync=entries[1], submit=entries[2])) return frames - def print_stats(self, frames: List[Frame]) -> None: - """ - Prints the stats of the list of Frames. - + def get_stats(self, frames: List[Frame], frameless=False) -> dict: + ''' Calculates FPS stats. Args: - frames: List of latest 127 frames. - """ - + frameless: A bool to indicate if frames are provided or not. + Returns: + dict: A dict storing the fps stats calculated. + ''' # It might be possible that 'frames' is almost empty (e.g. SF stats were # recently cleared). When that's the case, don't print anything. # Three is the minimum required to generate two values needed for stdev. + if frameless: + frames = self._get_recent_frames() + if len(frames) < 3: - return + return { + 'avg': 0, + 'total': 0, + 'dt': 0, + 'latencies': [] + } + total = len(frames) - 1 dt = (frames[-1].vsync - frames[0].vsync) / 1000000000 avg = total / dt + return { + 'avg': avg, + 'total': total, + 'dt': dt, + 'latencies': [(f.submit-f.draw) / 1000000 for f in frames] + } + def print_stats(self, frames: List[Frame]) -> None: + """ + Prints the stats of the list of Frames. + + Args: + frames: List of latest 127 frames. + """ + # Line overwrites itself with '\r'. + stats = self.get_stats(frames) + avg = stats['avg'] fps_avg = f'\r- FPS avg={avg:.2f} ' latency_str = '' if self._print_latency: # Store latencies in milliseconds. - latencies = [(f.submit-f.draw) / 1000000 for f in frames] + latencies = stats['latencies'] latency_str = ('/ LATENCY (ms) ' f'mean={statistics.mean(latencies):.2f}, ' f'stdev={statistics.stdev(latencies):.2f}, ' diff --git a/main.py b/main.py index dd687f6..142745b 100644 --- a/main.py +++ b/main.py @@ -20,14 +20,11 @@ def hello(): @app.route('/fps/', methods=['GET', 'POST']) def getfps(package_name): fps = FPS(package_name=package_name, duration=60, dumpfile="text", - print_latency=False, require_full_name=True) - frames = fps._get_recent_frames() + print_latency=False, require_full_name=False) + + stats = fps.get_stats([], True) # Frameless, no frames given, possible error when getting frames if not fps.error: - if len(frames) < 3: - return - total = len(frames) - 1 - dt = (frames[-1].vsync - frames[0].vsync) / 1000000000 - avg = total / dt + avg = stats['avg'] return jsonify(str(avg)) else: return fps.error @@ -36,8 +33,11 @@ def getfps(package_name): @app.route('/cpu/', methods=['GET', 'POST']) def getcpu(package_name): cpu = CPU(package_id=package_name) - output = cpu._web_gather_cpu_usage() - return output + stats = cpu._web_gather_cpu_usage() + if cpu.error: + error = cpu.get_error() + return {'error': error} + return stats @app.route('/mem/', methods=['GET', 'POST'])