Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor _setup_perfetto_config #510

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
183 changes: 25 additions & 158 deletions benchmarking/profilers/perfetto/perfetto.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,9 @@
from enum import Enum
from pathlib import Path
from tempfile import NamedTemporaryFile
from typing import Optional

# from platforms.android.android_platform import AndroidPlatform
from profilers.perfetto.perfetto_config import (
ANDROID_LOG_CONFIG,
CPU_FTRACE_CONFIG,
CPU_SCHEDULING_DETAILS_FTRACE_CONFIG,
CPU_SYS_STATS_CONFIG,
CPU_SYSCALLS_FTRACE_CONFIG,
GPU_FTRACE_CONFIG,
GPU_MEM_TOTAL_FTRACE_CONFIG,
GPU_MEMORY_CONFIG,
HEAPPROFD_CONFIG,
LINUX_FTRACE_CONFIG,
LINUX_PROCESS_STATS_CONFIG,
PERFETTO_CONFIG_TEMPLATE,
POWER_CONFIG,
POWER_FTRACE_CONFIG,
POWER_SUSPEND_RESUME_CONFIG,
)
from profilers.perfetto.perfetto_config import PerfettoConfig
from profilers.profiler_base import ProfilerBase
from profilers.utilities import generate_perf_filename, upload_profiling_reports
from utils.custom_logger import getLogger
Expand Down Expand Up @@ -106,11 +89,10 @@ def __init__(
self.valid = False
self.restoreState = False
self.perfetto_pid = None
self.all_heaps_config = (
" all_heaps: true\n"
if self.android_version >= 12 and self.options.get("all_heaps", False)
else ""
)

if self.android_version < 12 and self.options.get("all_heaps", False):
self.options.all_heaps = False
self.perfetto_config = PerfettoConfig(self.types, self.options)
self.basename = generate_perf_filename(model_name, self.platform.platform_hash)
self.trace_file_name = f"{self.basename}.perfetto-trace"
self.trace_file_device = f"{self.DEVICE_TRACE_DIRECTORY}/{self.trace_file_name}"
Expand Down Expand Up @@ -195,7 +177,7 @@ def _start(self):

# Generate and upload custom config file
getLogger().info(f"Perfetto profile type(s) = {', '.join(self.types)}.")
self._setup_perfetto_config()
self._setupPerfettoConfig()

# call Perfetto
output = self._perfetto()
Expand Down Expand Up @@ -227,7 +209,7 @@ def _finish(self):
getLogger().info(
f"Looking for Perfetto data on device {self.platform.device_label}."
)
self._copyPerfDataToHost()
self._copyPerfettoDataToHost()
self._generateReport()
self.meta.update(self._uploadResults())
else:
Expand Down Expand Up @@ -276,7 +258,7 @@ def _setBatteryState(self, state: BatteryState):
else:
getLogger().exception(error)

def _upload_config(self, config_file):
def _uploadConfig(self, config_file):
self.meta = upload_profiling_reports(
{
"perfetto_config": config_file,
Expand Down Expand Up @@ -396,146 +378,31 @@ def _setStateForPerfetto(self):
timeout=self.DEFAULT_TIMEOUT,
)

def _setup_perfetto_config(
def _setupPerfettoConfig(
self,
*,
app_name: str = "program",
config_file_host: Optional[str] = None,
):
config_str = self.perfetto_config.GeneratePerfettoConfig()
with NamedTemporaryFile() as f:
if config_file_host is None:
# Write custom perfetto config
config_file_host = f.name
android_log_config = ""
cpu_scheduling_details_ftrace_config = ""
cpu_ftrace_config = ""
cpu_sys_stats_config = ""
cpu_syscalls_ftrace_config = ""
gpu_ftrace_config = ""
gpu_mem_total_frace_config = ""
gpu_memory_config = ""
heapprofd_config = ""
linux_ftrace_config = ""
linux_process_stats_config = ""
power_config = ""
power_ftrace_config = ""
power_suspend_resume_config = ""
track_event_config = ""

buffer_size_kb = self.options.get(
"buffer_size_kb", self.BUFFER_SIZE_KB_DEFAULT
)
buffer_size2_kb = self.options.get(
"buffer_size2_kb", self.BUFFER_SIZE2_KB_DEFAULT
)
max_file_size_bytes = self.options.get(
"max_file_size_bytes", self.MAX_FILE_SIZE_BYTES_DEFAULT
)
if "memory" in self.types:
android_log_config = ANDROID_LOG_CONFIG
shmem_size_bytes = self.options.get(
"shmem_size_bytes", self.SHMEM_SIZE_BYTES_DEFAULT
)
sampling_interval_bytes = self.options.get(
"sampling_interval_bytes", self.SAMPLING_INTERVAL_BYTES_DEFAULT
)
dump_interval_ms = self.options.get(
"dump_interval_ms", self.DUMP_INTERVAL_MS_DEFAULT
)
dump_phase_ms = self.options.get("dump_phase_ms", dump_interval_ms)
heapprofd_config = HEAPPROFD_CONFIG.format(
all_heaps_config=self.all_heaps_config,
shmem_size_bytes=shmem_size_bytes,
sampling_interval_bytes=sampling_interval_bytes,
dump_interval_ms=dump_interval_ms,
dump_phase_ms=dump_phase_ms,
app_name=app_name,
)
if "battery" in self.types:
battery_poll_ms = self.options.get(
"battery_poll_ms", self.BATTERY_POLL_MS_DEFAULT
)
power_config = POWER_CONFIG.format(
battery_poll_ms=battery_poll_ms,
)
power_ftrace_config = POWER_FTRACE_CONFIG
power_suspend_resume_config = POWER_SUSPEND_RESUME_CONFIG
# Write custom perfetto config
f.write(config_str.encode("utf-8"))
f.flush()

if "gpu" in self.types:
getLogger().info(
"Applying GPU profiling with perfetto.",
)
gpu_mem_total_frace_config = GPU_MEM_TOTAL_FTRACE_CONFIG
gpu_memory_config = GPU_MEMORY_CONFIG
gpu_ftrace_config = GPU_FTRACE_CONFIG.format(
gpu_mem_total_frace_config=gpu_mem_total_frace_config,
)

if "cpu" in self.types:
cpu_poll_ms = max(
self.options.get("cpu_poll_ms", self.CPU_POLL_MS_DEFAULT), 100
) # minimum is 100ms or error
log_cpu_scheduling_details = self.options.get(
"log_cpu_scheduling_details", True
)
if self.options.get("log_coarse_cpu_usage", False):
cpu_sys_stats_config = CPU_SYS_STATS_CONFIG.format(
cpu_poll_ms=cpu_poll_ms,
)
if self.options.get("log_cpu_sys_calls", False):
cpu_syscalls_ftrace_config = CPU_SYSCALLS_FTRACE_CONFIG
if log_cpu_scheduling_details:
cpu_scheduling_details_ftrace_config = (
CPU_SCHEDULING_DETAILS_FTRACE_CONFIG
)
linux_process_stats_config = LINUX_PROCESS_STATS_CONFIG.format(
cpu_poll_ms=cpu_poll_ms,
)
cpu_ftrace_config = CPU_FTRACE_CONFIG
power_suspend_resume_config = POWER_SUSPEND_RESUME_CONFIG

if {"battery", "gpu", "cpu"}.intersection(self.types):
linux_ftrace_config = LINUX_FTRACE_CONFIG.format(
app_name=app_name,
cpu_ftrace_config=cpu_ftrace_config,
cpu_scheduling_details_ftrace_config=cpu_scheduling_details_ftrace_config,
cpu_syscalls_ftrace_config=cpu_syscalls_ftrace_config,
gpu_ftrace_config=gpu_ftrace_config,
power_ftrace_config=power_ftrace_config,
power_suspend_resume_config=power_suspend_resume_config,
)

# Generate config file
config_str = PERFETTO_CONFIG_TEMPLATE.format(
max_file_size_bytes=max_file_size_bytes,
buffer_size_kb=buffer_size_kb,
buffer_size2_kb=buffer_size2_kb,
android_log_config=android_log_config,
cpu_sys_stats_config=cpu_sys_stats_config,
gpu_memory_config=gpu_memory_config,
heapprofd_config=heapprofd_config,
linux_ftrace_config=linux_ftrace_config,
linux_process_stats_config=linux_process_stats_config,
power_config=power_config,
track_event_config=track_event_config,
)
f.write(config_str.encode("utf-8"))
f.flush()

# Save away the config file for reference
# Save away the config file
self.host_output_dir = tempfile.mkdtemp()
self.config_file_host = os.path.join(self.host_output_dir, self.config_file)
shutil.copy(config_file_host, self.config_file_host)
self._upload_config(self.config_file_host)
shutil.copy(f.name, self.config_file_host)
self._uploadConfig(self.config_file_host)

# Push perfetto config to device
getLogger().info(
f"Host config file = {self.config_file_host},\nDevice config file = {self.config_file_device}."
)
self.adb.push(self.config_file_host, self.config_file_device)
# Push perfetto config to device
getLogger().info(
f"Host config file = {self.config_file_host},\nDevice config file = {self.config_file_device}."
)
self.adb.push(self.config_file_host, self.config_file_device)

# Setup permissions for it, to avoid perfetto call failure
self.adb.shell(["chmod", "777", self.config_file_device])
# Setup permissions for it, to avoid perfetto call failure
self.adb.shell(["chmod", "777", self.config_file_device])

def _perfetto(self):
"""Run perfetto on platform with benchmark process id."""
Expand All @@ -546,11 +413,11 @@ def _perfetto(self):
getLogger().info(f"Perfetto returned: {output}.")

# longer delay if all_heaps is specified
startup_time: float = 2.0 if self.all_heaps_config != "" else 0.2
startup_time: float = 2.0 if self.options.get("all_heaps", False) else 0.2
time.sleep(startup_time) # give perfetto time to spin up
return output

def _copyPerfDataToHost(self):
def _copyPerfettoDataToHost(self):
self.platform.moveFilesFromPlatform(
os.path.join(self.trace_file_device),
os.path.join(self.host_output_dir),
Expand Down
142 changes: 142 additions & 0 deletions benchmarking/profilers/perfetto/perfetto_config.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,148 @@
#!/usr/bin/env python3
# Copyright 2004-present Facebook. All Rights Reserved.

from typing import Any, Dict, List, Optional


class PerfettoConfig:
BUFFER_SIZE_KB_DEFAULT = 256 * 1024 # 256 megabytes
BUFFER_SIZE2_KB_DEFAULT = 2 * 1024 # 2 megabytes
SHMEM_SIZE_BYTES_DEFAULT = (
8192 * 4096
) # Shared memory buffer must be a large multiple of 4096
SAMPLING_INTERVAL_BYTES_DEFAULT = 4096
DUMP_INTERVAL_MS_DEFAULT = 1000
BATTERY_POLL_MS_DEFAULT = 1000
CPU_POLL_MS_DEFAULT = 1000
MAX_FILE_SIZE_BYTES_DEFAULT = 100000000

def __init__(
self,
types: List[str],
options: Dict[str, Any],
*,
app_name: Optional[str] = "program",
):
self.types = types
self.options = options
self.app_name = app_name

def GeneratePerfettoConfig(self) -> str:
# Write custom perfetto config
android_log_config = ""
cpu_scheduling_details_ftrace_config = ""
cpu_ftrace_config = ""
cpu_sys_stats_config = ""
cpu_syscalls_ftrace_config = ""
gpu_ftrace_config = ""
gpu_mem_total_frace_config = ""
gpu_memory_config = ""
heapprofd_config = ""
linux_ftrace_config = ""
linux_process_stats_config = ""
power_config = ""
power_ftrace_config = ""
power_suspend_resume_config = ""
track_event_config = ""
all_heaps_config = (
" all_heaps: true\n"
if self.options.get("all_heaps", False)
else ""
)
buffer_size_kb = self.options.get("buffer_size_kb", self.BUFFER_SIZE_KB_DEFAULT)
buffer_size2_kb = self.options.get(
"buffer_size2_kb", self.BUFFER_SIZE2_KB_DEFAULT
)
max_file_size_bytes = self.options.get(
"max_file_size_bytes", self.MAX_FILE_SIZE_BYTES_DEFAULT
)
if self.options.get("include_android_log", False):
android_log_config = ANDROID_LOG_CONFIG
if "memory" in self.types:
shmem_size_bytes = self.options.get(
"shmem_size_bytes", self.SHMEM_SIZE_BYTES_DEFAULT
)
sampling_interval_bytes = self.options.get(
"sampling_interval_bytes", self.SAMPLING_INTERVAL_BYTES_DEFAULT
)
dump_interval_ms = self.options.get(
"dump_interval_ms", self.DUMP_INTERVAL_MS_DEFAULT
)
dump_phase_ms = self.options.get("dump_phase_ms", dump_interval_ms)
heapprofd_config = HEAPPROFD_CONFIG.format(
all_heaps_config=all_heaps_config,
shmem_size_bytes=shmem_size_bytes,
sampling_interval_bytes=sampling_interval_bytes,
dump_interval_ms=dump_interval_ms,
dump_phase_ms=dump_phase_ms,
app_name=self.app_name,
)
if "battery" in self.types:
battery_poll_ms = self.options.get(
"battery_poll_ms", self.BATTERY_POLL_MS_DEFAULT
)
power_config = POWER_CONFIG.format(
battery_poll_ms=battery_poll_ms,
)
power_ftrace_config = POWER_FTRACE_CONFIG
power_suspend_resume_config = POWER_SUSPEND_RESUME_CONFIG

if "gpu" in self.types:
gpu_mem_total_frace_config = GPU_MEM_TOTAL_FTRACE_CONFIG
gpu_memory_config = GPU_MEMORY_CONFIG
gpu_ftrace_config = GPU_FTRACE_CONFIG.format(
gpu_mem_total_frace_config=gpu_mem_total_frace_config,
)

if "cpu" in self.types:
cpu_poll_ms = max(
self.options.get("cpu_poll_ms", self.CPU_POLL_MS_DEFAULT), 100
) # minimum is 100ms or error
log_cpu_scheduling_details = self.options.get(
"log_cpu_scheduling_details", True
)
if self.options.get("log_coarse_cpu_usage", False):
cpu_sys_stats_config = CPU_SYS_STATS_CONFIG.format(
cpu_poll_ms=cpu_poll_ms,
)
if self.options.get("log_cpu_sys_calls", False):
cpu_syscalls_ftrace_config = CPU_SYSCALLS_FTRACE_CONFIG
if log_cpu_scheduling_details:
cpu_scheduling_details_ftrace_config = (
CPU_SCHEDULING_DETAILS_FTRACE_CONFIG
)
linux_process_stats_config = LINUX_PROCESS_STATS_CONFIG.format(
cpu_poll_ms=cpu_poll_ms,
)
cpu_ftrace_config = CPU_FTRACE_CONFIG
power_suspend_resume_config = POWER_SUSPEND_RESUME_CONFIG

if {"battery", "gpu", "cpu"}.intersection(self.types):
linux_ftrace_config = LINUX_FTRACE_CONFIG.format(
app_name=self.app_name,
cpu_ftrace_config=cpu_ftrace_config,
cpu_scheduling_details_ftrace_config=cpu_scheduling_details_ftrace_config,
cpu_syscalls_ftrace_config=cpu_syscalls_ftrace_config,
gpu_ftrace_config=gpu_ftrace_config,
power_ftrace_config=power_ftrace_config,
power_suspend_resume_config=power_suspend_resume_config,
)

# Generate config file
return PERFETTO_CONFIG_TEMPLATE.format(
max_file_size_bytes=max_file_size_bytes,
buffer_size_kb=buffer_size_kb,
buffer_size2_kb=buffer_size2_kb,
android_log_config=android_log_config,
cpu_sys_stats_config=cpu_sys_stats_config,
gpu_memory_config=gpu_memory_config,
heapprofd_config=heapprofd_config,
linux_ftrace_config=linux_ftrace_config,
linux_process_stats_config=linux_process_stats_config,
power_config=power_config,
track_event_config=track_event_config,
)


# duration_ms: {duration_ms}
# max_file_size_bytes: 10000000000
Expand Down