xref: /aosp_15_r20/system/extras/torq/tests/config_builder_unit_test.py (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
1#
2# Copyright (C) 2024 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#      http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16
17import unittest
18import builtins
19from unittest import mock
20from config_builder import build_default_config, build_custom_config
21from command import ProfilerCommand
22from torq import DEFAULT_DUR_MS
23
24TEST_FAILURE_MSG = "Test failure."
25TEST_DUR_MS = 9000
26INVALID_DUR_MS = "invalid-dur-ms"
27ANDROID_SDK_VERSION_T = 33
28ANDROID_SDK_VERSION_S_V2 = 32
29
30COMMON_DEFAULT_CONFIG_BEGINNING_STRING_1 = f'''\
31<<EOF
32
33buffers: {{
34  size_kb: 4096
35  fill_policy: RING_BUFFER
36}}
37buffers {{
38  size_kb: 4096
39  fill_policy: RING_BUFFER
40}}
41buffers: {{
42  size_kb: 260096
43  fill_policy: RING_BUFFER
44}}
45
46data_sources: {{
47  config {{
48    name: "linux.process_stats"
49    process_stats_config {{
50      scan_all_processes_on_start: true
51    }}
52  }}
53}}
54
55data_sources: {{
56  config {{
57    name: "android.log"
58    android_log_config {{
59    }}
60  }}
61}}
62
63data_sources {{
64  config {{
65    name: "android.packages_list"
66  }}
67}}
68
69data_sources: {{
70  config {{
71    name: "linux.sys_stats"
72    target_buffer: 1
73    sys_stats_config {{
74      stat_period_ms: 500
75      stat_counters: STAT_CPU_TIMES
76      stat_counters: STAT_FORK_COUNT
77      meminfo_period_ms: 1000
78      meminfo_counters: MEMINFO_ACTIVE_ANON
79      meminfo_counters: MEMINFO_ACTIVE_FILE
80      meminfo_counters: MEMINFO_INACTIVE_ANON
81      meminfo_counters: MEMINFO_INACTIVE_FILE
82      meminfo_counters: MEMINFO_KERNEL_STACK
83      meminfo_counters: MEMINFO_MLOCKED
84      meminfo_counters: MEMINFO_SHMEM
85      meminfo_counters: MEMINFO_SLAB
86      meminfo_counters: MEMINFO_SLAB_UNRECLAIMABLE
87      meminfo_counters: MEMINFO_VMALLOC_USED
88      meminfo_counters: MEMINFO_MEM_FREE
89      meminfo_counters: MEMINFO_SWAP_FREE
90      vmstat_period_ms: 1000
91      vmstat_counters: VMSTAT_PGFAULT
92      vmstat_counters: VMSTAT_PGMAJFAULT
93      vmstat_counters: VMSTAT_PGFREE
94      vmstat_counters: VMSTAT_PGPGIN
95      vmstat_counters: VMSTAT_PGPGOUT
96      vmstat_counters: VMSTAT_PSWPIN
97      vmstat_counters: VMSTAT_PSWPOUT
98      vmstat_counters: VMSTAT_PGSCAN_DIRECT
99      vmstat_counters: VMSTAT_PGSTEAL_DIRECT
100      vmstat_counters: VMSTAT_PGSCAN_KSWAPD
101      vmstat_counters: VMSTAT_PGSTEAL_KSWAPD
102      vmstat_counters: VMSTAT_WORKINGSET_REFAULT'''
103
104CPUFREQ_STRING_NEW_ANDROID = f'      cpufreq_period_ms: 500'
105
106COMMON_DEFAULT_CONFIG_BEGINNING_STRING_2 = f'''\
107    }}
108  }}
109}}
110
111data_sources: {{
112  config {{
113    name: "android.surfaceflinger.frametimeline"
114    target_buffer: 2
115  }}
116}}
117
118data_sources: {{
119  config {{
120    name: "linux.ftrace"
121    target_buffer: 2
122    ftrace_config {{'''
123
124COMMON_DEFAULT_CONFIG_MIDDLE_STRING = f'''\
125      atrace_categories: "aidl"
126      atrace_categories: "am"
127      atrace_categories: "dalvik"
128      atrace_categories: "binder_lock"
129      atrace_categories: "binder_driver"
130      atrace_categories: "bionic"
131      atrace_categories: "camera"
132      atrace_categories: "disk"
133      atrace_categories: "freq"
134      atrace_categories: "idle"
135      atrace_categories: "gfx"
136      atrace_categories: "hal"
137      atrace_categories: "input"
138      atrace_categories: "pm"
139      atrace_categories: "power"
140      atrace_categories: "res"
141      atrace_categories: "rro"
142      atrace_categories: "sched"
143      atrace_categories: "sm"
144      atrace_categories: "ss"
145      atrace_categories: "thermal"
146      atrace_categories: "video"
147      atrace_categories: "view"
148      atrace_categories: "wm"
149      atrace_apps: "lmkd"
150      atrace_apps: "system_server"
151      atrace_apps: "com.android.systemui"
152      atrace_apps: "com.google.android.gms"
153      atrace_apps: "com.google.android.gms.persistent"
154      atrace_apps: "android:ui"
155      atrace_apps: "com.google.android.apps.maps"
156      atrace_apps: "*"
157      buffer_size_kb: 16384
158      drain_period_ms: 150
159      symbolize_ksyms: true
160    }}
161  }}
162}}'''
163
164COMMON_CONFIG_ENDING_STRING = f'''\
165write_into_file: true
166file_write_period_ms: 5000
167max_file_size_bytes: 100000000000
168flush_period_ms: 5000
169incremental_state_config {{
170  clear_period_ms: 5000
171}}
172
173'''
174
175COMMON_DEFAULT_FTRACE_EVENTS = f'''\
176      ftrace_events: "dmabuf_heap/dma_heap_stat"
177      ftrace_events: "ftrace/print"
178      ftrace_events: "gpu_mem/gpu_mem_total"
179      ftrace_events: "ion/ion_stat"
180      ftrace_events: "kmem/ion_heap_grow"
181      ftrace_events: "kmem/ion_heap_shrink"
182      ftrace_events: "kmem/rss_stat"
183      ftrace_events: "lowmemorykiller/lowmemory_kill"
184      ftrace_events: "mm_event/mm_event_record"
185      ftrace_events: "oom/mark_victim"
186      ftrace_events: "oom/oom_score_adj_update"
187      ftrace_events: "power/cpu_frequency"
188      ftrace_events: "power/cpu_idle"
189      ftrace_events: "power/gpu_frequency"
190      ftrace_events: "power/suspend_resume"
191      ftrace_events: "power/wakeup_source_activate"
192      ftrace_events: "power/wakeup_source_deactivate"
193      ftrace_events: "sched/sched_blocked_reason"
194      ftrace_events: "sched/sched_process_exit"
195      ftrace_events: "sched/sched_process_free"
196      ftrace_events: "sched/sched_switch"
197      ftrace_events: "sched/sched_wakeup"
198      ftrace_events: "sched/sched_wakeup_new"
199      ftrace_events: "sched/sched_waking"
200      ftrace_events: "task/task_newtask"
201      ftrace_events: "task/task_rename"
202      ftrace_events: "vmscan/*"
203      ftrace_events: "workqueue/*"'''
204
205DEFAULT_CONFIG_9000_DUR_MS = f'''\
206{COMMON_DEFAULT_CONFIG_BEGINNING_STRING_1}
207{CPUFREQ_STRING_NEW_ANDROID}
208{COMMON_DEFAULT_CONFIG_BEGINNING_STRING_2}
209{COMMON_DEFAULT_FTRACE_EVENTS}
210{COMMON_DEFAULT_CONFIG_MIDDLE_STRING}
211duration_ms: {TEST_DUR_MS}
212{COMMON_CONFIG_ENDING_STRING}EOF'''
213
214DEFAULT_CONFIG_EXCLUDED_FTRACE_EVENTS = f'''\
215{COMMON_DEFAULT_CONFIG_BEGINNING_STRING_1}
216{CPUFREQ_STRING_NEW_ANDROID}
217{COMMON_DEFAULT_CONFIG_BEGINNING_STRING_2}
218      ftrace_events: "dmabuf_heap/dma_heap_stat"
219      ftrace_events: "ftrace/print"
220      ftrace_events: "gpu_mem/gpu_mem_total"
221      ftrace_events: "ion/ion_stat"
222      ftrace_events: "kmem/ion_heap_grow"
223      ftrace_events: "kmem/ion_heap_shrink"
224      ftrace_events: "kmem/rss_stat"
225      ftrace_events: "lowmemorykiller/lowmemory_kill"
226      ftrace_events: "oom/mark_victim"
227      ftrace_events: "oom/oom_score_adj_update"
228      ftrace_events: "power/cpu_frequency"
229      ftrace_events: "power/cpu_idle"
230      ftrace_events: "power/gpu_frequency"
231      ftrace_events: "power/wakeup_source_activate"
232      ftrace_events: "power/wakeup_source_deactivate"
233      ftrace_events: "sched/sched_blocked_reason"
234      ftrace_events: "sched/sched_process_exit"
235      ftrace_events: "sched/sched_process_free"
236      ftrace_events: "sched/sched_switch"
237      ftrace_events: "sched/sched_wakeup"
238      ftrace_events: "sched/sched_wakeup_new"
239      ftrace_events: "sched/sched_waking"
240      ftrace_events: "task/task_newtask"
241      ftrace_events: "task/task_rename"
242      ftrace_events: "vmscan/*"
243      ftrace_events: "workqueue/*"
244{COMMON_DEFAULT_CONFIG_MIDDLE_STRING}
245duration_ms: {DEFAULT_DUR_MS}
246{COMMON_CONFIG_ENDING_STRING}EOF'''
247
248DEFAULT_CONFIG_INCLUDED_FTRACE_EVENTS = f'''\
249{COMMON_DEFAULT_CONFIG_BEGINNING_STRING_1}
250{CPUFREQ_STRING_NEW_ANDROID}
251{COMMON_DEFAULT_CONFIG_BEGINNING_STRING_2}
252      ftrace_events: "dmabuf_heap/dma_heap_stat"
253      ftrace_events: "ftrace/print"
254      ftrace_events: "gpu_mem/gpu_mem_total"
255      ftrace_events: "ion/ion_stat"
256      ftrace_events: "kmem/ion_heap_grow"
257      ftrace_events: "kmem/ion_heap_shrink"
258      ftrace_events: "kmem/rss_stat"
259      ftrace_events: "lowmemorykiller/lowmemory_kill"
260      ftrace_events: "mm_event/mm_event_record"
261      ftrace_events: "oom/mark_victim"
262      ftrace_events: "oom/oom_score_adj_update"
263      ftrace_events: "power/cpu_frequency"
264      ftrace_events: "power/cpu_idle"
265      ftrace_events: "power/gpu_frequency"
266      ftrace_events: "power/suspend_resume"
267      ftrace_events: "power/wakeup_source_activate"
268      ftrace_events: "power/wakeup_source_deactivate"
269      ftrace_events: "sched/sched_blocked_reason"
270      ftrace_events: "sched/sched_process_exit"
271      ftrace_events: "sched/sched_process_free"
272      ftrace_events: "sched/sched_switch"
273      ftrace_events: "sched/sched_wakeup"
274      ftrace_events: "sched/sched_wakeup_new"
275      ftrace_events: "sched/sched_waking"
276      ftrace_events: "task/task_newtask"
277      ftrace_events: "task/task_rename"
278      ftrace_events: "vmscan/*"
279      ftrace_events: "workqueue/*"
280      ftrace_events: "mock_ftrace_event1"
281      ftrace_events: "mock_ftrace_event2"
282{COMMON_DEFAULT_CONFIG_MIDDLE_STRING}
283duration_ms: {DEFAULT_DUR_MS}
284{COMMON_CONFIG_ENDING_STRING}EOF'''
285
286DEFAULT_CONFIG_OLD_ANDROID = f'''\
287{COMMON_DEFAULT_CONFIG_BEGINNING_STRING_1}
288
289{COMMON_DEFAULT_CONFIG_BEGINNING_STRING_2}
290{COMMON_DEFAULT_FTRACE_EVENTS}
291{COMMON_DEFAULT_CONFIG_MIDDLE_STRING}
292duration_ms: {DEFAULT_DUR_MS}
293{COMMON_CONFIG_ENDING_STRING}EOF'''
294
295COMMON_CUSTOM_CONFIG_BEGINNING_STRING = f'''\
296
297buffers: {{
298  size_kb: 4096
299  fill_policy: RING_BUFFER
300}}
301
302data_sources: {{
303  config {{
304    name: "linux.ftrace"
305    target_buffer: 2
306    ftrace_config {{
307      ftrace_events: "dmabuf_heap/dma_heap_stat"
308      atrace_categories: "aidl"
309      atrace_apps: "lmkd"
310      buffer_size_kb: 16384
311      drain_period_ms: 150
312      symbolize_ksyms: true
313    }}
314  }}
315}}'''
316
317CUSTOM_CONFIG_9000_DUR_MS = f'''\
318{COMMON_CUSTOM_CONFIG_BEGINNING_STRING}
319duration_ms: {TEST_DUR_MS}
320{COMMON_CONFIG_ENDING_STRING}'''
321
322CUSTOM_CONFIG_9000_DUR_MS_WITH_WHITE_SPACE = f'''\
323{COMMON_CUSTOM_CONFIG_BEGINNING_STRING}
324duration_ms:                                               {TEST_DUR_MS}
325{COMMON_CONFIG_ENDING_STRING}'''
326
327CUSTOM_CONFIG_INVALID_DUR_MS = f'''\
328{COMMON_CUSTOM_CONFIG_BEGINNING_STRING}
329duration_ms: {INVALID_DUR_MS}
330{COMMON_CONFIG_ENDING_STRING}'''
331
332CUSTOM_CONFIG_NO_DUR_MS = f'''\
333{COMMON_CUSTOM_CONFIG_BEGINNING_STRING}
334{COMMON_CONFIG_ENDING_STRING}'''
335
336
337class ConfigBuilderUnitTest(unittest.TestCase):
338
339  def setUp(self):
340    self.command = ProfilerCommand(
341        None, "custom", None, None, DEFAULT_DUR_MS, None, None, "test-path",
342        None, None, None, None, None, None, None)
343
344  def test_build_default_config_setting_valid_dur_ms(self):
345    self.command.dur_ms = TEST_DUR_MS
346
347    config, error = build_default_config(self.command, ANDROID_SDK_VERSION_T)
348
349    self.assertEqual(error, None)
350    self.assertEqual(config, DEFAULT_CONFIG_9000_DUR_MS)
351
352  def test_build_default_config_on_old_android_version(self):
353    config, error = build_default_config(self.command, ANDROID_SDK_VERSION_S_V2)
354
355    self.assertEqual(error, None)
356    self.assertEqual(config, DEFAULT_CONFIG_OLD_ANDROID)
357
358  def test_build_default_config_setting_invalid_dur_ms(self):
359    self.command.dur_ms = None
360
361    with self.assertRaises(ValueError) as e:
362      build_default_config(self.command, ANDROID_SDK_VERSION_T)
363
364    self.assertEqual(str(e.exception), ("Cannot create config because a valid"
365                                        " dur_ms was not set."))
366
367  def test_build_default_config_removing_valid_excluded_ftrace_events(self):
368    self.command.excluded_ftrace_events = ["power/suspend_resume",
369                                           "mm_event/mm_event_record"]
370
371    config, error = build_default_config(self.command, ANDROID_SDK_VERSION_T)
372
373    self.assertEqual(error, None)
374    self.assertEqual(config, DEFAULT_CONFIG_EXCLUDED_FTRACE_EVENTS)
375
376  def test_build_default_config_removing_invalid_excluded_ftrace_events(self):
377    self.command.excluded_ftrace_events = ["invalid_ftrace_event"]
378
379    config, error = build_default_config(self.command, ANDROID_SDK_VERSION_T)
380
381    self.assertEqual(config, None)
382    self.assertNotEqual(error, None)
383    self.assertEqual(error.message, ("Cannot remove ftrace event %s from config"
384                                     " because it is not one of the config's"
385                                     " ftrace events." %
386                                     self.command.excluded_ftrace_events[0]
387                                     ))
388    self.assertEqual(error.suggestion, ("Please specify one of the following"
389                                        " possible ftrace events:\n\t"
390                                        " dmabuf_heap/dma_heap_stat\n\t"
391                                        " ftrace/print\n\t"
392                                        " gpu_mem/gpu_mem_total\n\t"
393                                        " ion/ion_stat\n\t"
394                                        " kmem/ion_heap_grow\n\t"
395                                        " kmem/ion_heap_shrink\n\t"
396                                        " kmem/rss_stat\n\t"
397                                        " lowmemorykiller/lowmemory_kill\n\t"
398                                        " mm_event/mm_event_record\n\t"
399                                        " oom/mark_victim\n\t"
400                                        " oom/oom_score_adj_update\n\t"
401                                        " power/cpu_frequency\n\t"
402                                        " power/cpu_idle\n\t"
403                                        " power/gpu_frequency\n\t"
404                                        " power/suspend_resume\n\t"
405                                        " power/wakeup_source_activate\n\t"
406                                        " power/wakeup_source_deactivate\n\t"
407                                        " sched/sched_blocked_reason\n\t"
408                                        " sched/sched_process_exit\n\t"
409                                        " sched/sched_process_free\n\t"
410                                        " sched/sched_switch\n\t"
411                                        " sched/sched_wakeup\n\t"
412                                        " sched/sched_wakeup_new\n\t"
413                                        " sched/sched_waking\n\t"
414                                        " task/task_newtask\n\t"
415                                        " task/task_rename\n\t"
416                                        " vmscan/*\n\t"
417                                        " workqueue/*"))
418
419  def test_build_default_config_adding_valid_included_ftrace_events(self):
420    self.command.included_ftrace_events = ["mock_ftrace_event1",
421                                           "mock_ftrace_event2"]
422
423    config, error = build_default_config(self.command, ANDROID_SDK_VERSION_T)
424
425    self.assertEqual(error, None)
426    self.assertEqual(config, DEFAULT_CONFIG_INCLUDED_FTRACE_EVENTS)
427
428  def test_build_default_config_adding_invalid_included_ftrace_events(self):
429    self.command.included_ftrace_events = ["power/suspend_resume"]
430
431    config, error = build_default_config(self.command, ANDROID_SDK_VERSION_T)
432
433    self.assertEqual(config, None)
434    self.assertNotEqual(error, None)
435    self.assertEqual(error.message, ("Cannot add ftrace event %s to config"
436                                     " because it is already one of the"
437                                     " config's ftrace events." %
438                                     self.command.included_ftrace_events[0]
439                                     ))
440    self.assertEqual(error.suggestion, ("Please do not specify any of the"
441                                        " following ftrace events that are"
442                                        " already included:\n\t"
443                                        " dmabuf_heap/dma_heap_stat\n\t"
444                                        " ftrace/print\n\t"
445                                        " gpu_mem/gpu_mem_total\n\t"
446                                        " ion/ion_stat\n\t"
447                                        " kmem/ion_heap_grow\n\t"
448                                        " kmem/ion_heap_shrink\n\t"
449                                        " kmem/rss_stat\n\t"
450                                        " lowmemorykiller/lowmemory_kill\n\t"
451                                        " mm_event/mm_event_record\n\t"
452                                        " oom/mark_victim\n\t"
453                                        " oom/oom_score_adj_update\n\t"
454                                        " power/cpu_frequency\n\t"
455                                        " power/cpu_idle\n\t"
456                                        " power/gpu_frequency\n\t"
457                                        " power/suspend_resume\n\t"
458                                        " power/wakeup_source_activate\n\t"
459                                        " power/wakeup_source_deactivate\n\t"
460                                        " sched/sched_blocked_reason\n\t"
461                                        " sched/sched_process_exit\n\t"
462                                        " sched/sched_process_free\n\t"
463                                        " sched/sched_switch\n\t"
464                                        " sched/sched_wakeup\n\t"
465                                        " sched/sched_wakeup_new\n\t"
466                                        " sched/sched_waking\n\t"
467                                        " task/task_newtask\n\t"
468                                        " task/task_rename\n\t"
469                                        " vmscan/*\n\t"
470                                        " workqueue/*"))
471
472  @mock.patch("builtins.open", mock.mock_open(
473      read_data=CUSTOM_CONFIG_9000_DUR_MS))
474  def test_build_custom_config_extracting_valid_dur_ms(self):
475    config, error = build_custom_config(self.command)
476
477    self.assertEqual(error, None)
478    self.assertEqual(config, f"<<EOF\n\n{CUSTOM_CONFIG_9000_DUR_MS}\n\n\nEOF")
479    self.assertEqual(self.command.dur_ms, TEST_DUR_MS)
480
481  @mock.patch("builtins.open", mock.mock_open(
482      read_data=CUSTOM_CONFIG_9000_DUR_MS_WITH_WHITE_SPACE))
483  def test_build_custom_config_extracting_valid_dur_ms_with_white_space(self):
484    config, error = build_custom_config(self.command)
485
486    self.assertEqual(error, None)
487    self.assertEqual(config, (
488        f"<<EOF\n\n{CUSTOM_CONFIG_9000_DUR_MS_WITH_WHITE_SPACE}\n\n\nEOF"))
489    self.assertEqual(self.command.dur_ms, TEST_DUR_MS)
490
491  @mock.patch("builtins.open", mock.mock_open(
492      read_data=CUSTOM_CONFIG_INVALID_DUR_MS))
493  def test_build_custom_config_extracting_invalid_dur_ms_error(self):
494    config, error = build_custom_config(self.command)
495
496    self.assertNotEqual(error, None)
497    self.assertEqual(config, None)
498    self.assertEqual(error.message,
499                     ("Failed to parse custom perfetto-config on local file"
500                      " path: %s. Invalid duration_ms field in config."
501                      % self.command.perfetto_config))
502    self.assertEqual(error.suggestion,
503                     ("Make sure the perfetto config passed via arguments has a"
504                      " valid duration_ms value."))
505
506  @mock.patch("builtins.open", mock.mock_open(
507      read_data=CUSTOM_CONFIG_NO_DUR_MS))
508  def test_build_custom_config_injecting_dur_ms(self):
509    duration_string = "duration_ms: " + str(self.command.dur_ms)
510
511    config, error = build_custom_config(self.command)
512
513    self.assertEqual(error, None)
514    self.assertEqual(config, (
515        f"<<EOF\n\n{CUSTOM_CONFIG_NO_DUR_MS}\n{duration_string}\n\nEOF"))
516
517  @mock.patch.object(builtins, "open")
518  def test_build_custom_config_parsing_error(self, mock_open_file):
519    self.command.dur_ms = None
520    mock_open_file.side_effect = Exception(TEST_FAILURE_MSG)
521
522    config, error = build_custom_config(self.command)
523
524    self.assertNotEqual(error, None)
525    self.assertEqual(config, None)
526    self.assertEqual(error.message, ("Failed to parse custom perfetto-config on"
527                                     " local file path: %s. %s"
528                                     % (self.command.perfetto_config,
529                                        TEST_FAILURE_MSG)))
530    self.assertEqual(error.suggestion, None)
531
532
533if __name__ == '__main__':
534  unittest.main()
535