blob: 42e9f4eb2acb5d5c5a92be25bb6d380da054bb94 [file] [log] [blame]
#
# Copyright (C) 2024 The Android Open Source Project
#
# 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.
#
import unittest
import builtins
from unittest import mock
from config_builder import build_default_config, build_custom_config
from command import ProfilerCommand
from torq import DEFAULT_DUR_MS
TEST_FAILURE_MSG = "Test failure."
TEST_DUR_MS = 9000
INVALID_DUR_MS = "invalid-dur-ms"
ANDROID_SDK_VERSION_T = 33
ANDROID_SDK_VERSION_S_V2 = 32
COMMON_DEFAULT_CONFIG_BEGINNING_STRING_1 = f'''\
<<EOF
buffers: {{
size_kb: 4096
fill_policy: RING_BUFFER
}}
buffers {{
size_kb: 4096
fill_policy: RING_BUFFER
}}
buffers: {{
size_kb: 260096
fill_policy: RING_BUFFER
}}
data_sources: {{
config {{
name: "linux.process_stats"
process_stats_config {{
scan_all_processes_on_start: true
}}
}}
}}
data_sources: {{
config {{
name: "android.log"
android_log_config {{
}}
}}
}}
data_sources {{
config {{
name: "android.packages_list"
}}
}}
data_sources: {{
config {{
name: "linux.sys_stats"
target_buffer: 1
sys_stats_config {{
stat_period_ms: 500
stat_counters: STAT_CPU_TIMES
stat_counters: STAT_FORK_COUNT
meminfo_period_ms: 1000
meminfo_counters: MEMINFO_ACTIVE_ANON
meminfo_counters: MEMINFO_ACTIVE_FILE
meminfo_counters: MEMINFO_INACTIVE_ANON
meminfo_counters: MEMINFO_INACTIVE_FILE
meminfo_counters: MEMINFO_KERNEL_STACK
meminfo_counters: MEMINFO_MLOCKED
meminfo_counters: MEMINFO_SHMEM
meminfo_counters: MEMINFO_SLAB
meminfo_counters: MEMINFO_SLAB_UNRECLAIMABLE
meminfo_counters: MEMINFO_VMALLOC_USED
meminfo_counters: MEMINFO_MEM_FREE
meminfo_counters: MEMINFO_SWAP_FREE
vmstat_period_ms: 1000
vmstat_counters: VMSTAT_PGFAULT
vmstat_counters: VMSTAT_PGMAJFAULT
vmstat_counters: VMSTAT_PGFREE
vmstat_counters: VMSTAT_PGPGIN
vmstat_counters: VMSTAT_PGPGOUT
vmstat_counters: VMSTAT_PSWPIN
vmstat_counters: VMSTAT_PSWPOUT
vmstat_counters: VMSTAT_PGSCAN_DIRECT
vmstat_counters: VMSTAT_PGSTEAL_DIRECT
vmstat_counters: VMSTAT_PGSCAN_KSWAPD
vmstat_counters: VMSTAT_PGSTEAL_KSWAPD
vmstat_counters: VMSTAT_WORKINGSET_REFAULT'''
CPUFREQ_STRING_NEW_ANDROID = f' cpufreq_period_ms: 500'
COMMON_DEFAULT_CONFIG_BEGINNING_STRING_2 = f'''\
}}
}}
}}
data_sources: {{
config {{
name: "android.surfaceflinger.frametimeline"
target_buffer: 2
}}
}}
data_sources: {{
config {{
name: "linux.ftrace"
target_buffer: 2
ftrace_config {{'''
COMMON_DEFAULT_CONFIG_MIDDLE_STRING = f'''\
atrace_categories: "aidl"
atrace_categories: "am"
atrace_categories: "dalvik"
atrace_categories: "binder_lock"
atrace_categories: "binder_driver"
atrace_categories: "bionic"
atrace_categories: "camera"
atrace_categories: "disk"
atrace_categories: "freq"
atrace_categories: "idle"
atrace_categories: "gfx"
atrace_categories: "hal"
atrace_categories: "input"
atrace_categories: "pm"
atrace_categories: "power"
atrace_categories: "res"
atrace_categories: "rro"
atrace_categories: "sched"
atrace_categories: "sm"
atrace_categories: "ss"
atrace_categories: "thermal"
atrace_categories: "video"
atrace_categories: "view"
atrace_categories: "wm"
atrace_apps: "lmkd"
atrace_apps: "system_server"
atrace_apps: "com.android.systemui"
atrace_apps: "com.google.android.gms"
atrace_apps: "com.google.android.gms.persistent"
atrace_apps: "android:ui"
atrace_apps: "com.google.android.apps.maps"
atrace_apps: "*"
buffer_size_kb: 16384
drain_period_ms: 150
symbolize_ksyms: true
}}
}}
}}'''
COMMON_CONFIG_ENDING_STRING = f'''\
write_into_file: true
file_write_period_ms: 5000
max_file_size_bytes: 100000000000
flush_period_ms: 5000
incremental_state_config {{
clear_period_ms: 5000
}}
'''
COMMON_DEFAULT_FTRACE_EVENTS = f'''\
ftrace_events: "dmabuf_heap/dma_heap_stat"
ftrace_events: "ftrace/print"
ftrace_events: "gpu_mem/gpu_mem_total"
ftrace_events: "ion/ion_stat"
ftrace_events: "kmem/ion_heap_grow"
ftrace_events: "kmem/ion_heap_shrink"
ftrace_events: "kmem/rss_stat"
ftrace_events: "lowmemorykiller/lowmemory_kill"
ftrace_events: "mm_event/mm_event_record"
ftrace_events: "oom/mark_victim"
ftrace_events: "oom/oom_score_adj_update"
ftrace_events: "power/cpu_frequency"
ftrace_events: "power/cpu_idle"
ftrace_events: "power/gpu_frequency"
ftrace_events: "power/suspend_resume"
ftrace_events: "power/wakeup_source_activate"
ftrace_events: "power/wakeup_source_deactivate"
ftrace_events: "sched/sched_blocked_reason"
ftrace_events: "sched/sched_process_exit"
ftrace_events: "sched/sched_process_free"
ftrace_events: "sched/sched_switch"
ftrace_events: "sched/sched_wakeup"
ftrace_events: "sched/sched_wakeup_new"
ftrace_events: "sched/sched_waking"
ftrace_events: "task/task_newtask"
ftrace_events: "task/task_rename"
ftrace_events: "vmscan/*"
ftrace_events: "workqueue/*"'''
DEFAULT_CONFIG_9000_DUR_MS = f'''\
{COMMON_DEFAULT_CONFIG_BEGINNING_STRING_1}
{CPUFREQ_STRING_NEW_ANDROID}
{COMMON_DEFAULT_CONFIG_BEGINNING_STRING_2}
{COMMON_DEFAULT_FTRACE_EVENTS}
{COMMON_DEFAULT_CONFIG_MIDDLE_STRING}
duration_ms: {TEST_DUR_MS}
{COMMON_CONFIG_ENDING_STRING}EOF'''
DEFAULT_CONFIG_EXCLUDED_FTRACE_EVENTS = f'''\
{COMMON_DEFAULT_CONFIG_BEGINNING_STRING_1}
{CPUFREQ_STRING_NEW_ANDROID}
{COMMON_DEFAULT_CONFIG_BEGINNING_STRING_2}
ftrace_events: "dmabuf_heap/dma_heap_stat"
ftrace_events: "ftrace/print"
ftrace_events: "gpu_mem/gpu_mem_total"
ftrace_events: "ion/ion_stat"
ftrace_events: "kmem/ion_heap_grow"
ftrace_events: "kmem/ion_heap_shrink"
ftrace_events: "kmem/rss_stat"
ftrace_events: "lowmemorykiller/lowmemory_kill"
ftrace_events: "oom/mark_victim"
ftrace_events: "oom/oom_score_adj_update"
ftrace_events: "power/cpu_frequency"
ftrace_events: "power/cpu_idle"
ftrace_events: "power/gpu_frequency"
ftrace_events: "power/wakeup_source_activate"
ftrace_events: "power/wakeup_source_deactivate"
ftrace_events: "sched/sched_blocked_reason"
ftrace_events: "sched/sched_process_exit"
ftrace_events: "sched/sched_process_free"
ftrace_events: "sched/sched_switch"
ftrace_events: "sched/sched_wakeup"
ftrace_events: "sched/sched_wakeup_new"
ftrace_events: "sched/sched_waking"
ftrace_events: "task/task_newtask"
ftrace_events: "task/task_rename"
ftrace_events: "vmscan/*"
ftrace_events: "workqueue/*"
{COMMON_DEFAULT_CONFIG_MIDDLE_STRING}
duration_ms: {DEFAULT_DUR_MS}
{COMMON_CONFIG_ENDING_STRING}EOF'''
DEFAULT_CONFIG_INCLUDED_FTRACE_EVENTS = f'''\
{COMMON_DEFAULT_CONFIG_BEGINNING_STRING_1}
{CPUFREQ_STRING_NEW_ANDROID}
{COMMON_DEFAULT_CONFIG_BEGINNING_STRING_2}
ftrace_events: "dmabuf_heap/dma_heap_stat"
ftrace_events: "ftrace/print"
ftrace_events: "gpu_mem/gpu_mem_total"
ftrace_events: "ion/ion_stat"
ftrace_events: "kmem/ion_heap_grow"
ftrace_events: "kmem/ion_heap_shrink"
ftrace_events: "kmem/rss_stat"
ftrace_events: "lowmemorykiller/lowmemory_kill"
ftrace_events: "mm_event/mm_event_record"
ftrace_events: "oom/mark_victim"
ftrace_events: "oom/oom_score_adj_update"
ftrace_events: "power/cpu_frequency"
ftrace_events: "power/cpu_idle"
ftrace_events: "power/gpu_frequency"
ftrace_events: "power/suspend_resume"
ftrace_events: "power/wakeup_source_activate"
ftrace_events: "power/wakeup_source_deactivate"
ftrace_events: "sched/sched_blocked_reason"
ftrace_events: "sched/sched_process_exit"
ftrace_events: "sched/sched_process_free"
ftrace_events: "sched/sched_switch"
ftrace_events: "sched/sched_wakeup"
ftrace_events: "sched/sched_wakeup_new"
ftrace_events: "sched/sched_waking"
ftrace_events: "task/task_newtask"
ftrace_events: "task/task_rename"
ftrace_events: "vmscan/*"
ftrace_events: "workqueue/*"
ftrace_events: "mock_ftrace_event1"
ftrace_events: "mock_ftrace_event2"
{COMMON_DEFAULT_CONFIG_MIDDLE_STRING}
duration_ms: {DEFAULT_DUR_MS}
{COMMON_CONFIG_ENDING_STRING}EOF'''
DEFAULT_CONFIG_OLD_ANDROID = f'''\
{COMMON_DEFAULT_CONFIG_BEGINNING_STRING_1}
{COMMON_DEFAULT_CONFIG_BEGINNING_STRING_2}
{COMMON_DEFAULT_FTRACE_EVENTS}
{COMMON_DEFAULT_CONFIG_MIDDLE_STRING}
duration_ms: {DEFAULT_DUR_MS}
{COMMON_CONFIG_ENDING_STRING}EOF'''
COMMON_CUSTOM_CONFIG_BEGINNING_STRING = f'''\
buffers: {{
size_kb: 4096
fill_policy: RING_BUFFER
}}
data_sources: {{
config {{
name: "linux.ftrace"
target_buffer: 2
ftrace_config {{
ftrace_events: "dmabuf_heap/dma_heap_stat"
atrace_categories: "aidl"
atrace_apps: "lmkd"
buffer_size_kb: 16384
drain_period_ms: 150
symbolize_ksyms: true
}}
}}
}}'''
CUSTOM_CONFIG_9000_DUR_MS = f'''\
{COMMON_CUSTOM_CONFIG_BEGINNING_STRING}
duration_ms: {TEST_DUR_MS}
{COMMON_CONFIG_ENDING_STRING}'''
CUSTOM_CONFIG_9000_DUR_MS_WITH_WHITE_SPACE = f'''\
{COMMON_CUSTOM_CONFIG_BEGINNING_STRING}
duration_ms: {TEST_DUR_MS}
{COMMON_CONFIG_ENDING_STRING}'''
CUSTOM_CONFIG_INVALID_DUR_MS = f'''\
{COMMON_CUSTOM_CONFIG_BEGINNING_STRING}
duration_ms: {INVALID_DUR_MS}
{COMMON_CONFIG_ENDING_STRING}'''
CUSTOM_CONFIG_NO_DUR_MS = f'''\
{COMMON_CUSTOM_CONFIG_BEGINNING_STRING}
{COMMON_CONFIG_ENDING_STRING}'''
class ConfigBuilderUnitTest(unittest.TestCase):
def setUp(self):
self.command = ProfilerCommand(
None, "custom", None, None, DEFAULT_DUR_MS, None, None, "test-path",
None, None, None, None, None, None, None)
def test_build_default_config_setting_valid_dur_ms(self):
self.command.dur_ms = TEST_DUR_MS
config, error = build_default_config(self.command, ANDROID_SDK_VERSION_T)
self.assertEqual(error, None)
self.assertEqual(config, DEFAULT_CONFIG_9000_DUR_MS)
def test_build_default_config_on_old_android_version(self):
config, error = build_default_config(self.command, ANDROID_SDK_VERSION_S_V2)
self.assertEqual(error, None)
self.assertEqual(config, DEFAULT_CONFIG_OLD_ANDROID)
def test_build_default_config_setting_invalid_dur_ms(self):
self.command.dur_ms = None
with self.assertRaises(ValueError) as e:
build_default_config(self.command, ANDROID_SDK_VERSION_T)
self.assertEqual(str(e.exception), ("Cannot create config because a valid"
" dur_ms was not set."))
def test_build_default_config_removing_valid_excluded_ftrace_events(self):
self.command.excluded_ftrace_events = ["power/suspend_resume",
"mm_event/mm_event_record"]
config, error = build_default_config(self.command, ANDROID_SDK_VERSION_T)
self.assertEqual(error, None)
self.assertEqual(config, DEFAULT_CONFIG_EXCLUDED_FTRACE_EVENTS)
def test_build_default_config_removing_invalid_excluded_ftrace_events(self):
self.command.excluded_ftrace_events = ["invalid_ftrace_event"]
config, error = build_default_config(self.command, ANDROID_SDK_VERSION_T)
self.assertEqual(config, None)
self.assertNotEqual(error, None)
self.assertEqual(error.message, ("Cannot remove ftrace event %s from config"
" because it is not one of the config's"
" ftrace events." %
self.command.excluded_ftrace_events[0]
))
self.assertEqual(error.suggestion, ("Please specify one of the following"
" possible ftrace events:\n\t"
" dmabuf_heap/dma_heap_stat\n\t"
" ftrace/print\n\t"
" gpu_mem/gpu_mem_total\n\t"
" ion/ion_stat\n\t"
" kmem/ion_heap_grow\n\t"
" kmem/ion_heap_shrink\n\t"
" kmem/rss_stat\n\t"
" lowmemorykiller/lowmemory_kill\n\t"
" mm_event/mm_event_record\n\t"
" oom/mark_victim\n\t"
" oom/oom_score_adj_update\n\t"
" power/cpu_frequency\n\t"
" power/cpu_idle\n\t"
" power/gpu_frequency\n\t"
" power/suspend_resume\n\t"
" power/wakeup_source_activate\n\t"
" power/wakeup_source_deactivate\n\t"
" sched/sched_blocked_reason\n\t"
" sched/sched_process_exit\n\t"
" sched/sched_process_free\n\t"
" sched/sched_switch\n\t"
" sched/sched_wakeup\n\t"
" sched/sched_wakeup_new\n\t"
" sched/sched_waking\n\t"
" task/task_newtask\n\t"
" task/task_rename\n\t"
" vmscan/*\n\t"
" workqueue/*"))
def test_build_default_config_adding_valid_included_ftrace_events(self):
self.command.included_ftrace_events = ["mock_ftrace_event1",
"mock_ftrace_event2"]
config, error = build_default_config(self.command, ANDROID_SDK_VERSION_T)
self.assertEqual(error, None)
self.assertEqual(config, DEFAULT_CONFIG_INCLUDED_FTRACE_EVENTS)
def test_build_default_config_adding_invalid_included_ftrace_events(self):
self.command.included_ftrace_events = ["power/suspend_resume"]
config, error = build_default_config(self.command, ANDROID_SDK_VERSION_T)
self.assertEqual(config, None)
self.assertNotEqual(error, None)
self.assertEqual(error.message, ("Cannot add ftrace event %s to config"
" because it is already one of the"
" config's ftrace events." %
self.command.included_ftrace_events[0]
))
self.assertEqual(error.suggestion, ("Please do not specify any of the"
" following ftrace events that are"
" already included:\n\t"
" dmabuf_heap/dma_heap_stat\n\t"
" ftrace/print\n\t"
" gpu_mem/gpu_mem_total\n\t"
" ion/ion_stat\n\t"
" kmem/ion_heap_grow\n\t"
" kmem/ion_heap_shrink\n\t"
" kmem/rss_stat\n\t"
" lowmemorykiller/lowmemory_kill\n\t"
" mm_event/mm_event_record\n\t"
" oom/mark_victim\n\t"
" oom/oom_score_adj_update\n\t"
" power/cpu_frequency\n\t"
" power/cpu_idle\n\t"
" power/gpu_frequency\n\t"
" power/suspend_resume\n\t"
" power/wakeup_source_activate\n\t"
" power/wakeup_source_deactivate\n\t"
" sched/sched_blocked_reason\n\t"
" sched/sched_process_exit\n\t"
" sched/sched_process_free\n\t"
" sched/sched_switch\n\t"
" sched/sched_wakeup\n\t"
" sched/sched_wakeup_new\n\t"
" sched/sched_waking\n\t"
" task/task_newtask\n\t"
" task/task_rename\n\t"
" vmscan/*\n\t"
" workqueue/*"))
@mock.patch("builtins.open", mock.mock_open(
read_data=CUSTOM_CONFIG_9000_DUR_MS))
def test_build_custom_config_extracting_valid_dur_ms(self):
config, error = build_custom_config(self.command)
self.assertEqual(error, None)
self.assertEqual(config, f"<<EOF\n\n{CUSTOM_CONFIG_9000_DUR_MS}\n\n\nEOF")
self.assertEqual(self.command.dur_ms, TEST_DUR_MS)
@mock.patch("builtins.open", mock.mock_open(
read_data=CUSTOM_CONFIG_9000_DUR_MS_WITH_WHITE_SPACE))
def test_build_custom_config_extracting_valid_dur_ms_with_white_space(self):
config, error = build_custom_config(self.command)
self.assertEqual(error, None)
self.assertEqual(config, (
f"<<EOF\n\n{CUSTOM_CONFIG_9000_DUR_MS_WITH_WHITE_SPACE}\n\n\nEOF"))
self.assertEqual(self.command.dur_ms, TEST_DUR_MS)
@mock.patch("builtins.open", mock.mock_open(
read_data=CUSTOM_CONFIG_INVALID_DUR_MS))
def test_build_custom_config_extracting_invalid_dur_ms_error(self):
config, error = build_custom_config(self.command)
self.assertNotEqual(error, None)
self.assertEqual(config, None)
self.assertEqual(error.message,
("Failed to parse custom perfetto-config on local file"
" path: %s. Invalid duration_ms field in config."
% self.command.perfetto_config))
self.assertEqual(error.suggestion,
("Make sure the perfetto config passed via arguments has a"
" valid duration_ms value."))
@mock.patch("builtins.open", mock.mock_open(
read_data=CUSTOM_CONFIG_NO_DUR_MS))
def test_build_custom_config_injecting_dur_ms(self):
duration_string = "duration_ms: " + str(self.command.dur_ms)
config, error = build_custom_config(self.command)
self.assertEqual(error, None)
self.assertEqual(config, (
f"<<EOF\n\n{CUSTOM_CONFIG_NO_DUR_MS}\n{duration_string}\n\nEOF"))
@mock.patch.object(builtins, "open")
def test_build_custom_config_parsing_error(self, mock_open_file):
self.command.dur_ms = None
mock_open_file.side_effect = Exception(TEST_FAILURE_MSG)
config, error = build_custom_config(self.command)
self.assertNotEqual(error, None)
self.assertEqual(config, None)
self.assertEqual(error.message, ("Failed to parse custom perfetto-config on"
" local file path: %s. %s"
% (self.command.perfetto_config,
TEST_FAILURE_MSG)))
self.assertEqual(error.suggestion, None)
if __name__ == '__main__':
unittest.main()