benchmarking/platforms/ios/ios_platform.py (127 lines of code) (raw):
#!/usr/bin/env python
##############################################################################
# Copyright 2017-present, Facebook, Inc.
# All rights reserved.
#
# This source code is licensed under the license found in the
# LICENSE file in the root directory of this source tree.
##############################################################################
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import json
import os
import shlex
import time
from platforms.platform_base import PlatformBase
from profilers.profilers import getProfilerByUsage
from utils.custom_logger import getLogger
from utils.subprocess_with_logger import processRun
from utils.utilities import getRunStatus, setRunStatus
class IOSPlatform(PlatformBase):
def __init__(self, tempdir, idb, args, platform_meta, usb_controller=None):
super(IOSPlatform, self).__init__(
tempdir,
args.ios_dir,
idb,
args.hash_platform_mapping,
args.device_name_mapping,
)
self.platform_os_version = platform_meta.get("os_version")
self.platform_model = platform_meta.get("model")
self.platform_abi = platform_meta.get("abi")
self.setPlatformHash(idb.device)
self.usb_controller = usb_controller
self.type = "ios"
self.app = None
def getKind(self):
if self.platform_model and self.platform_os_version:
return "{}-{}".format(self.platform_model, self.platform_os_version)
return self.platform
def getOS(self):
if self.platform_os_version:
return "iOS {}".format(self.platform_os_version)
return "iOS"
def preprocess(self, *args, **kwargs):
assert "programs" in kwargs, "Must have programs specified"
programs = kwargs["programs"]
# find the first zipped app file
assert "program" in programs, "program is not specified"
program = programs["program"]
assert program.endswith(".ipa"), "IOS program must be an ipa file"
processRun(["unzip", "-o", "-d", self.tempdir, program])
# get the app name
app_dir = os.path.join(self.tempdir, "Payload")
dirs = [
f for f in os.listdir(app_dir) if os.path.isdir(os.path.join(app_dir, f))
]
assert len(dirs) == 1, "Only one app in the Payload directory"
app_name = dirs[0]
self.app = os.path.join(app_dir, app_name)
del programs["program"]
bundle_id, _ = processRun(["osascript", "-e", 'id of app "' + self.app + '"'])
assert len(bundle_id) > 0, "bundle id cannot be found"
self.util.setBundleId(bundle_id[0].strip())
# We know this command will fail. Avoid propogating this
# failure to the upstream
success = getRunStatus()
self.util.run(["--bundle", self.app, "--uninstall", "--justlaunch"])
setRunStatus(success, overwrite=True)
def postprocess(self, *args, **kwargs):
success = getRunStatus()
self.util.run(["--bundle", self.app, "--uninstall_only"])
setRunStatus(success, overwrite=True)
def runBenchmark(self, cmd, *args, **kwargs):
if not isinstance(cmd, list):
cmd = shlex.split(cmd)
assert self.util.bundle_id is not None, "Bundle id is not specified"
arguments = self.getPairedArguments(cmd)
argument_filename = os.path.join(self.tempdir, "benchmark.json")
arguments_json = json.dumps(arguments, indent=2, sort_keys=True)
with open(argument_filename, "w") as f:
f.write(arguments_json)
tgt_argument_filename = os.path.join(self.tgt_dir, "benchmark.json")
self.util.push(argument_filename, tgt_argument_filename)
run_cmd = [
"--bundle",
self.app,
"--noninteractive",
"--noinstall",
"--unbuffered",
]
platform_args = {}
if "platform_args" in kwargs:
platform_args = kwargs["platform_args"]
if "power" in platform_args and platform_args["power"]:
platform_args["timeout"] = 10
run_cmd += ["--justlaunch"]
if platform_args.get("enable_profiling", False):
# attempt to run with profiling, else fallback to standard run
try:
args = " ".join(["--" + x + " " + arguments[x] for x in arguments])
xctrace = getProfilerByUsage(
"ios",
None,
platform=self,
model_name=platform_args.get("model_name", None),
args=args,
)
if xctrace:
f = xctrace.start()
output, meta = f.result()
if not output or not meta:
raise RuntimeError("No data returned from XCTrace profiler.")
return output, meta
except Exception:
getLogger().critical(
f"An error occurred when running XCTrace profiler on device {self.platform} {self.platform_hash}.",
exc_info=True,
)
# meta is used to store any data about the benchmark run
# that is not the output of the command
meta = {}
if arguments:
run_cmd += [
"--args",
" ".join(["--" + x + " " + arguments[x] for x in arguments]),
]
# the command may fail, but the err_output is what we need
log_screen = self.util.run(run_cmd, **platform_args)
return log_screen, meta
def rebootDevice(self):
success = self.util.reboot()
if success:
time.sleep(180)
def killProgram(self, program):
# TODO Implement or find workaround for hardware power measurement
pass
def currentPower(self):
result = self.util.batteryLevel()
return result
@property
def powerInfo(self):
return {"unit": "percentage", "metric": "batteryLevel"}