| #!/usr/bin/env python |
| # SPDX-License-Identifier: Apache-2.0 |
| # |
| # Copyright (C) 2017, ARM Limited, Google, and contributors. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); you may |
| # not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| # |
| |
| from time import sleep |
| import os |
| import re |
| import argparse |
| import pandas as pd |
| import matplotlib.pyplot as plt |
| from android import System |
| from env import TestEnv |
| |
| # Setup target configuration |
| conf = { |
| # Target platform and board |
| "platform" : 'android', |
| # Useful for reading names of little/big cluster |
| # and energy model info, its device specific and use |
| # only if needed for analysis |
| # "board" : 'pixel', |
| # Device |
| # By default the device connected is detected, but if more than 1 |
| # device, override the following to get a specific device. |
| # "device" : "HT66N0300080", |
| # Folder where all the results will be collected |
| "results_dir" : "BinderTransactionTracing", |
| # Define devlib modules to load |
| "modules" : [ |
| 'cpufreq', # enable CPUFreq support |
| 'cpuidle', # enable cpuidle support |
| # 'cgroups' # Enable for cgroup support |
| ], |
| "emeter" : { |
| 'instrument': 'monsoon', |
| 'conf': { } |
| }, |
| "systrace": { |
| 'extra_categories': ['binder_driver'], |
| "extra_events": ["binder_transaction_alloc_buf"], |
| }, |
| # Tools required by the experiments |
| "tools" : [ 'taskset'], |
| |
| "skip_nrg_model" : True, |
| } |
| |
| te = TestEnv(conf, wipe=False) |
| target = te.target |
| |
| def run_page_stats(duration, frequency): |
| procs = {} |
| for i in range(int(duration/frequency)): |
| ss = target.execute("cat /d/binder/stats") |
| proc_dump = re.split("\nproc ", ss)[1:] |
| |
| for proc in proc_dump[1:]: |
| lines = proc.split("\n ") |
| proc_id = lines[0] |
| page = re.search("pages: (\d+:\d+:\d+)", proc) |
| active, lru, free = map(int, page.group(1).split(":")) |
| if proc_id not in procs: |
| procs[proc_id] = {"alloc": [], "lru": []} |
| procs[proc_id]["alloc"].append(active + lru) |
| procs[proc_id]["lru"].append(lru) |
| |
| sleep(frequency) |
| for proc in procs: |
| df = pd.DataFrame(data={"alloc": procs[proc]["alloc"], |
| "lru": procs[proc]["lru"]}) |
| df.plot(title="proc " + proc) |
| plt.show() |
| |
| def experiment(duration_s, cmd, frequency): |
| """ |
| Starts systrace and run a command on target if specified. If |
| no command is given, collect the trace for duration_s seconds. |
| |
| :param duration_s: duration to collect systrace |
| :type duration_s: int |
| |
| :param cmd: command to execute on the target |
| :type cmd: string |
| |
| :param frequency: sampling frequency for page stats |
| :type frequency: float |
| """ |
| systrace_output = System.systrace_start( |
| te, os.path.join(te.res_dir, 'trace.html'), conf=conf) |
| systrace_output.expect("Starting tracing") |
| |
| if frequency: |
| run_page_stats(duration_s, frequency) |
| elif cmd: |
| target.execute(cmd) |
| else: |
| sleep(duration_s) |
| |
| systrace_output.sendline("") |
| System.systrace_wait(te, systrace_output) |
| te.platform_dump(te.res_dir) |
| |
| parser = argparse.ArgumentParser( |
| description="Collect systrace for binder events while executing" |
| "a command on the target or wait for duration_s seconds") |
| |
| parser.add_argument("--duration", "-d", type=int, default=0, |
| help="How long to collect the trace in seconds.") |
| parser.add_argument("--command", "-c", type=str, default="", |
| help="Command to execute on the target.") |
| parser.add_argument("--pagestats", "-p", nargs="?", type=float, default=None, |
| const=0.1, help="Run binder page stats analysis." |
| "Optional argument for sample interval in seconds.") |
| |
| if __name__ == "__main__": |
| args = parser.parse_args() |
| experiment(args.duration, args.command, args.pagestats) |