|
4 | 4 |
|
5 | 5 | from __future__ import annotations |
6 | 6 |
|
| 7 | +import datetime as dt |
7 | 8 | import functools |
8 | 9 | import logging |
9 | 10 | import math |
|
25 | 26 | from crossbench.plt.posix import RemotePosixPlatform |
26 | 27 | from crossbench.plt.process_meminfo import ProcessMeminfo |
27 | 28 |
|
| 29 | +from android_protoc import activitymanagerservice_pb2 |
| 30 | + |
28 | 31 | # Defines the Android permissions to be granted. |
29 | 32 | # TODO(381985595): make this configurable. |
30 | 33 | ANDROID_PERMISSIONS = ["POST_NOTIFICATIONS", "CAMERA", "RECORD_AUDIO"] |
@@ -755,9 +758,13 @@ def processes(self, |
755 | 758 | return res |
756 | 759 |
|
757 | 760 | @override |
758 | | - def meminfo(self, process_name: str) -> dict[str, ProcessMeminfo]: |
759 | | - dumpsys_output = self.sh_stdout("dumpsys", "meminfo", "--package", |
760 | | - process_name) |
| 761 | + def meminfo( |
| 762 | + self, process_name: str, timeout: dt.timedelta = dt.timedelta(seconds=10) |
| 763 | + ) -> dict[str, ProcessMeminfo]: |
| 764 | + timeout_ms = int(timeout / dt.timedelta(milliseconds=1)) |
| 765 | + dumpsys_output = self.sh_stdout_bytes("dumpsys", "-T", str(timeout_ms), |
| 766 | + "meminfo", "--proto", "--package", |
| 767 | + process_name) |
761 | 768 | return self._parse_dumpsys_meminfo(dumpsys_output) |
762 | 769 |
|
763 | 770 | @functools.lru_cache(maxsize=1) |
@@ -837,37 +844,21 @@ def display_resolution(self) -> tuple[int, int]: |
837 | 844 | def user_id(self) -> int: |
838 | 845 | return NumberParser.any_int(self.sh_stdout("am", "get-current-user")) |
839 | 846 |
|
840 | | - def _parse_dumpsys_meminfo(self, |
841 | | - meminfo_output: str) -> dict[str, ProcessMeminfo]: |
842 | | - pid_sections = re.split(r"\*\* MEMINFO in pid (\d+) \[(.*?)] \*\*", |
843 | | - meminfo_output)[1:] |
| 847 | + _DUMPSYS_TIMEOUT_RE = re.compile( |
| 848 | + rb"\*\*\* SERVICE '[^']+' DUMP TIMEOUT \(\d+ms\) EXPIRED \*\*\*") |
844 | 849 |
|
| 850 | + def _parse_dumpsys_meminfo( |
| 851 | + self, meminfo_output: bytes) -> dict[str, ProcessMeminfo]: |
| 852 | + if self._DUMPSYS_TIMEOUT_RE.search(meminfo_output): |
| 853 | + raise TimeoutError("dumpsys meminfo timed out") |
| 854 | + proto_dump = activitymanagerservice_pb2.MemInfoDumpProto() |
| 855 | + proto_dump.ParseFromString(meminfo_output) |
845 | 856 | meminfos = {} |
846 | | - |
847 | | - for i in range(0, len(pid_sections), 3): |
848 | | - pid = pid_sections[i] |
849 | | - process_name = pid_sections[i + 1].strip() |
850 | | - raw_process_info = pid_sections[i + 2] |
851 | | - |
852 | | - pss_rss_total_v1 = re.search( |
853 | | - r"TOTAL PSS:\s+(?P<pss_total>\d+)\s+TOTAL RSS:\s+(?P<rss_total>\d+)" |
854 | | - r"\s+TOTAL SWAP \(KB\):\s+(?P<swap_total>\d+)", raw_process_info) |
855 | | - |
856 | | - # TOTAL PSS: 91273 TOTAL RSS: 259028 TOTAL SWAP PSS: 209 |
857 | | - pss_rss_total_v2 = re.search( |
858 | | - r"TOTAL PSS:\s+(?P<pss_total>\d+)\s+TOTAL RSS:\s+(?P<rss_total>\d+)" |
859 | | - r"\s+TOTAL SWAP PSS:\s+(?P<swap_total>\d+)", raw_process_info) |
860 | | - |
861 | | - if pss_rss_total_v1: |
862 | | - pss_rss_total = pss_rss_total_v1 |
863 | | - elif pss_rss_total_v2: |
864 | | - pss_rss_total = pss_rss_total_v2 |
865 | | - else: |
866 | | - raise ValueError("Failed to parse meminfo.") |
867 | | - |
868 | | - meminfos[process_name] = ProcessMeminfo( |
869 | | - pid=int(pid), |
870 | | - pss_total=int(pss_rss_total["pss_total"]), |
871 | | - rss_total=int(pss_rss_total["rss_total"]), |
872 | | - swap_total=int(pss_rss_total["swap_total"])) |
| 857 | + for app_process in proto_dump.app_processes: |
| 858 | + mem_info = app_process.process_memory.total_heap.mem_info |
| 859 | + meminfos[app_process.process_memory.process_name] = ProcessMeminfo( |
| 860 | + pid=app_process.process_memory.pid, |
| 861 | + pss_total=mem_info.total_pss_kb, |
| 862 | + rss_total=mem_info.total_rss_kb, |
| 863 | + swap_total=mem_info.dirty_swap_pss_kb or mem_info.dirty_swap_kb) |
873 | 864 | return meminfos |
0 commit comments