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 17 package android.profiling.cts; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertNotNull; 22 import static org.junit.Assert.assertNull; 23 import static org.junit.Assert.assertTrue; 24 import static org.junit.Assert.fail; 25 import static org.mockito.ArgumentMatchers.any; 26 import static org.mockito.ArgumentMatchers.anyBoolean; 27 import static org.mockito.Mockito.spy; 28 import static org.mockito.Mockito.times; 29 import static org.mockito.Mockito.verify; 30 31 import android.app.Instrumentation; 32 import android.content.Context; 33 import android.os.Bundle; 34 import android.os.CancellationSignal; 35 import android.os.ProfilingManager; 36 import android.os.ProfilingResult; 37 import android.os.profiling.DeviceConfigHelper; 38 import android.os.profiling.Flags; 39 import android.os.profiling.ProfilingService; 40 import android.platform.test.annotations.RequiresFlagsEnabled; 41 import android.platform.test.flag.junit.CheckFlagsRule; 42 import android.platform.test.flag.junit.DeviceFlagsValueProvider; 43 44 import androidx.test.core.app.ApplicationProvider; 45 import androidx.test.filters.LargeTest; 46 import androidx.test.platform.app.InstrumentationRegistry; 47 import androidx.test.runner.AndroidJUnit4; 48 49 import com.android.compatibility.common.util.SystemUtil; 50 51 import com.google.errorprone.annotations.FormatMethod; 52 53 import org.junit.After; 54 import org.junit.Before; 55 import org.junit.Rule; 56 import org.junit.Test; 57 import org.junit.rules.TestName; 58 import org.junit.runner.RunWith; 59 import org.testng.TestException; 60 61 import java.io.File; 62 import java.io.IOException; 63 import java.nio.file.FileSystems; 64 import java.nio.file.Files; 65 import java.nio.file.Path; 66 import java.nio.file.Paths; 67 import java.util.concurrent.atomic.AtomicBoolean; 68 import java.util.function.Consumer; 69 70 /** 71 * 72 * Tests defined in this class are expected to test the API implementation. All tests below require 73 * the android.os.profiling.telemetry_apis flag to be enabled, otherwise you will receive an 74 * assumed failure for any tests has the @RequiresFlagsEnabled annotation. 75 * 76 */ 77 78 @RunWith(AndroidJUnit4.class) 79 public final class ProfilingFrameworkTests { 80 81 // Wait for callback for 5 seconds at a time for up to 60 increments totalling 5 minutes. 82 private static final int CALLBACK_WAIT_TIME_INCREMENT_MS = 5 * 1000; 83 private static final int CALLBACK_WAIT_TIME_INCREMENTS_COUNT = 60; 84 85 // Smaller number of increments for cancel case - wait for callback for 5 seconds at a time for 86 // up to 4 increments totalling 20 seconds. 87 private static final int CALLBACK_CANCEL_WAIT_TIME_INCREMENTS_COUNT = 4; 88 89 // Wait for rate limiter config to update for 250 milliseconds at a time for up to 12 increments 90 // totalling 3 seconds. 91 private static final int RATE_LIMITER_WAIT_TIME_INCREMENT_MS = 250; 92 private static final int RATE_LIMITER_WAIT_TIME_INCREMENTS_COUNT = 12; 93 94 // Wait 2 seconds for profiling to get started before attempting to cancel it. 95 private static final int WAIT_TIME_FOR_PROFILING_START_MS = 2 * 1000; 96 97 // Keep in sync with {@link ProfilingService} because we can't access it. 98 private static final String OUTPUT_FILE_JAVA_HEAP_DUMP_SUFFIX = ".perfetto-java-heap-dump"; 99 private static final String OUTPUT_FILE_HEAP_PROFILE_SUFFIX = ".perfetto-heap-profile"; 100 private static final String OUTPUT_FILE_STACK_SAMPLING_SUFFIX = ".perfetto-stack-sample"; 101 private static final String OUTPUT_FILE_TRACE_SUFFIX = ".perfetto-trace"; 102 103 public static final Path DUMP_PATH = FileSystems.getDefault() 104 .getPath("/sdcard/ProfilesCollected/"); 105 106 private static final String COMMAND_OVERRIDE_DEVICE_CONFIG_INT = "device_config put %s %s %d"; 107 private static final String COMMAND_OVERRIDE_DEVICE_CONFIG_BOOL = "device_config put %s %s %b"; 108 109 private static final int ONE_SECOND_MS = 1 * 1000; 110 private static final int FIVE_SECONDS_MS = 5 * 1000; 111 private static final int TEN_SECONDS_MS = 10 * 1000; 112 private static final int ONE_MINUTE_MS = 60 * 1000; 113 private static final int FIVE_MINUTES_MS = 5 * 60 * 1000; 114 private static final int TEN_MINUTES_MS = 10 * 60 * 1000; 115 116 private ProfilingManager mProfilingManager = null; 117 private Context mContext = null; 118 private Instrumentation mInstrumentation; 119 120 @Rule 121 public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); 122 123 @Rule 124 public final TestName mTestName = new TestName(); 125 126 @Before setup()127 public void setup() { 128 mContext = ApplicationProvider.getApplicationContext(); 129 mProfilingManager = mContext.getSystemService(ProfilingManager.class); 130 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 131 132 // This permission is required for Headless (HSUM) tests, including Auto. 133 mInstrumentation.getUiAutomation().adoptShellPermissionIdentity( 134 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); 135 136 // Disable the rate limiter, we're not testing that in any of these tests. 137 disableRateLimiter(); 138 } 139 140 @SuppressWarnings("GuardedBy") // Suppress warning for mProfilingManager.mProfilingService lock. 141 @After cleanup()142 public void cleanup() { 143 mProfilingManager.mProfilingService = null; 144 } 145 146 /** Check and see if we can get a reference to the ProfilingManager service. */ 147 @Test 148 @RequiresFlagsEnabled(Flags.FLAG_TELEMETRY_APIS) createServiceTest()149 public void createServiceTest() { 150 assertNotNull(mProfilingManager); 151 } 152 153 /** Test that request with invalid profiling type fails with correct error output. */ 154 @Test 155 @RequiresFlagsEnabled(Flags.FLAG_TELEMETRY_APIS) testInvalidProfilingType()156 public void testInvalidProfilingType() { 157 if (mProfilingManager == null) throw new TestException("mProfilingManager can not be null"); 158 159 AppCallback callback = new AppCallback(); 160 161 // This call is passing an invalid profiling request type and should result in an error. 162 mProfilingManager.requestProfiling( 163 -1, 164 null, 165 null, 166 null, 167 new ProfilingTestUtils.ImmediateExecutor(), 168 callback); 169 170 // Wait until callback#onAccept is triggered so we can confirm the result. 171 waitForCallback(callback); 172 173 assertEquals(ProfilingResult.ERROR_FAILED_INVALID_REQUEST, callback.mResult.getErrorCode()); 174 } 175 176 /** Test that request with invalid profiling params fails with correct error output. */ 177 @Test 178 @RequiresFlagsEnabled(Flags.FLAG_TELEMETRY_APIS) testInvalidProfilingParams()179 public void testInvalidProfilingParams() { 180 if (mProfilingManager == null) throw new TestException("mProfilingManager can not be null"); 181 182 AppCallback callback = new AppCallback(); 183 184 Bundle params = new Bundle(); 185 params.putBoolean("bypass_rate_limiter", true); 186 187 // This call is passing a parameters bundle with an invalid parameter and should result in 188 // an error. 189 mProfilingManager.requestProfiling( 190 ProfilingManager.PROFILING_TYPE_STACK_SAMPLING, 191 params, 192 null, 193 null, 194 new ProfilingTestUtils.ImmediateExecutor(), 195 callback); 196 197 // Wait until callback#onAccept is triggered so we can confirm the result. 198 waitForCallback(callback); 199 200 assertEquals(ProfilingResult.ERROR_FAILED_INVALID_REQUEST, callback.mResult.getErrorCode()); 201 } 202 203 /** Test that profiling request for java heap dump succeeds and returns a non-empty file. */ 204 @Test 205 @LargeTest 206 @RequiresFlagsEnabled(Flags.FLAG_TELEMETRY_APIS) testRequestJavaHeapDumpSuccess()207 public void testRequestJavaHeapDumpSuccess() throws Exception { 208 if (mProfilingManager == null) throw new TestException("mProfilingManager can not be null"); 209 210 overrideJavaHeapDumpDeviceConfigValues(false, ONE_SECOND_MS, TEN_SECONDS_MS); 211 212 AppCallback callback = new AppCallback(); 213 214 // Now kick off the request. 215 mProfilingManager.requestProfiling( 216 ProfilingManager.PROFILING_TYPE_JAVA_HEAP_DUMP, 217 null, 218 null, 219 null, 220 new ProfilingTestUtils.ImmediateExecutor(), 221 callback); 222 223 // Wait until callback#onAccept is triggered so we can confirm the result. 224 waitForCallback(callback); 225 226 // Assert that result matches assumptions for success. 227 confirmCollectionSuccess(callback.mResult, OUTPUT_FILE_JAVA_HEAP_DUMP_SUFFIX); 228 dumpTrace(callback.mResult); 229 } 230 231 /** Test that profiling request for heap profile succeeds and returns a non-empty file. */ 232 @Test 233 @RequiresFlagsEnabled(Flags.FLAG_TELEMETRY_APIS) testRequestHeapProfileSuccess()234 public void testRequestHeapProfileSuccess() throws Exception { 235 if (mProfilingManager == null) throw new TestException("mProfilingManager can not be null"); 236 237 overrideHeapProfileDeviceConfigValues(false, ONE_SECOND_MS, ONE_SECOND_MS, FIVE_SECONDS_MS); 238 239 AppCallback callback = new AppCallback(); 240 241 // Add sampling interval param to test because it is currently the only long param. 242 Bundle params = ProfilingTestUtils.getOneSecondDurationParamBundle(); 243 params.putLong(ProfilingManager.KEY_SAMPLING_INTERVAL_BYTES, 4096L); 244 245 // Now kick off the request. 246 mProfilingManager.requestProfiling( 247 ProfilingManager.PROFILING_TYPE_HEAP_PROFILE, 248 params, 249 null, 250 null, 251 new ProfilingTestUtils.ImmediateExecutor(), 252 callback); 253 254 // Wait until callback#onAccept is triggered so we can confirm the result. 255 waitForCallback(callback); 256 257 // Assert that result matches assumptions for success. 258 confirmCollectionSuccess(callback.mResult, OUTPUT_FILE_HEAP_PROFILE_SUFFIX); 259 dumpTrace(callback.mResult); 260 } 261 262 /** Test that profiling request for stack sampling succeeds and returns a non-empty file. */ 263 @Test 264 @RequiresFlagsEnabled(Flags.FLAG_TELEMETRY_APIS) testRequestStackSamplingSuccess()265 public void testRequestStackSamplingSuccess() throws Exception { 266 if (mProfilingManager == null) throw new TestException("mProfilingManager can not be null"); 267 268 overrideStackSamplingDeviceConfigValues(false, ONE_SECOND_MS, ONE_SECOND_MS, 269 FIVE_SECONDS_MS); 270 271 AppCallback callback = new AppCallback(); 272 273 // Now kick off the request. 274 mProfilingManager.requestProfiling( 275 ProfilingManager.PROFILING_TYPE_STACK_SAMPLING, 276 ProfilingTestUtils.getOneSecondDurationParamBundle(), 277 null, 278 null, 279 new ProfilingTestUtils.ImmediateExecutor(), 280 callback); 281 282 BusyLoopThread busy = new BusyLoopThread(); 283 284 // Wait until callback#onAccept is triggered so we can confirm the result. 285 waitForCallback(callback); 286 287 busy.stop(); 288 289 // Assert that result matches assumptions for success. 290 confirmCollectionSuccess(callback.mResult, OUTPUT_FILE_STACK_SAMPLING_SUFFIX); 291 dumpTrace(callback.mResult); 292 } 293 294 /** 295 * Test that profiling request for system trace fails as it's disabled until redaction 296 * is in place. 297 */ 298 @Test 299 @RequiresFlagsEnabled({Flags.FLAG_TELEMETRY_APIS, Flags.FLAG_REDACTION_ENABLED}) testRequestSystemTraceSuccess()300 public void testRequestSystemTraceSuccess() throws Exception { 301 if (mProfilingManager == null) throw new TestException("mProfilingManager can not be null"); 302 303 overrideSystemTraceDeviceConfigValues(false, ONE_SECOND_MS, ONE_SECOND_MS, FIVE_SECONDS_MS); 304 305 AppCallback callback = new AppCallback(); 306 307 // Now kick off the request. 308 mProfilingManager.requestProfiling( 309 ProfilingManager.PROFILING_TYPE_SYSTEM_TRACE, 310 ProfilingTestUtils.getOneSecondDurationParamBundle(), 311 null, 312 null, 313 new ProfilingTestUtils.ImmediateExecutor(), 314 callback); 315 316 // Wait until callback#onAccept is triggered so we can confirm the result. 317 waitForCallback(callback); 318 319 // Assert trace has succeeded. 320 confirmCollectionSuccess(callback.mResult, OUTPUT_FILE_TRACE_SUFFIX); 321 dumpTrace(callback.mResult); 322 } 323 324 /** Test that cancelling java heap dump stops collection and still receives correct result. */ 325 @Test 326 @RequiresFlagsEnabled(Flags.FLAG_TELEMETRY_APIS) testRequestJavaHeapDumpCancel()327 public void testRequestJavaHeapDumpCancel() throws Exception { 328 if (mProfilingManager == null) throw new TestException("mProfilingManager can not be null"); 329 330 // Set override duration and timeout to 10 minutes so we can ensure it finishes early when 331 // canceled. 332 overrideJavaHeapDumpDeviceConfigValues(false, TEN_MINUTES_MS, TEN_MINUTES_MS); 333 334 AppCallback callback = new AppCallback(); 335 CancellationSignal cancellationSignal = new CancellationSignal(); 336 337 // Now kick off the request. 338 mProfilingManager.requestProfiling( 339 ProfilingManager.PROFILING_TYPE_JAVA_HEAP_DUMP, 340 null, // Use default parameters since we will cancel quickly 341 null, 342 cancellationSignal, 343 new ProfilingTestUtils.ImmediateExecutor(), 344 callback); 345 346 // Wait a bit for collection to get started. 347 sleep(WAIT_TIME_FOR_PROFILING_START_MS); 348 349 // Now request cancellation. 350 cancellationSignal.cancel(); 351 352 // Wait until callback#onAccept is triggered so we can confirm the result. 353 waitForCancelCallback(callback); 354 355 // Assert that result matches assumptions for success. 356 confirmCollectionSuccess(callback.mResult, OUTPUT_FILE_JAVA_HEAP_DUMP_SUFFIX); 357 } 358 359 /** Test that cancelling heap profile stops collection and still receives correct result. */ 360 @Test 361 @RequiresFlagsEnabled(Flags.FLAG_TELEMETRY_APIS) testRequestHeapProfileCancel()362 public void testRequestHeapProfileCancel() throws Exception { 363 if (mProfilingManager == null) throw new TestException("mProfilingManager can not be null"); 364 365 // Set override durations to 10 minutes so we can ensure it finishes early when canceled. 366 overrideHeapProfileDeviceConfigValues(false, TEN_MINUTES_MS, TEN_MINUTES_MS, 367 TEN_MINUTES_MS); 368 369 AppCallback callback = new AppCallback(); 370 CancellationSignal cancellationSignal = new CancellationSignal(); 371 372 // Now kick off the request. 373 mProfilingManager.requestProfiling( 374 ProfilingManager.PROFILING_TYPE_HEAP_PROFILE, 375 null, // Use default parameters since we will cancel quickly 376 null, 377 cancellationSignal, 378 new ProfilingTestUtils.ImmediateExecutor(), 379 callback); 380 381 // Wait a bit for collection to get started. 382 sleep(WAIT_TIME_FOR_PROFILING_START_MS); 383 384 // Now request cancellation. 385 cancellationSignal.cancel(); 386 387 // Wait until callback#onAccept is triggered so we can confirm the result. 388 waitForCancelCallback(callback); 389 390 // Assert that result matches assumptions for success. 391 confirmCollectionSuccess(callback.mResult, OUTPUT_FILE_HEAP_PROFILE_SUFFIX); 392 } 393 394 /** Test that cancelling stack sampling stops collection and still receives correct result. */ 395 @Test 396 @RequiresFlagsEnabled(Flags.FLAG_TELEMETRY_APIS) testRequestStackSamplingCancel()397 public void testRequestStackSamplingCancel() throws Exception { 398 if (mProfilingManager == null) throw new TestException("mProfilingManager can not be null"); 399 400 // Set override durations to 10 minutes so we can ensure it finishes early when canceled. 401 overrideStackSamplingDeviceConfigValues(false, TEN_MINUTES_MS, TEN_MINUTES_MS, 402 TEN_MINUTES_MS); 403 404 AppCallback callback = new AppCallback(); 405 CancellationSignal cancellationSignal = new CancellationSignal(); 406 407 // Now kick off the request. 408 mProfilingManager.requestProfiling( 409 ProfilingManager.PROFILING_TYPE_STACK_SAMPLING, 410 null, // Use default parameters since we will cancel quickly 411 null, 412 cancellationSignal, 413 new ProfilingTestUtils.ImmediateExecutor(), 414 callback); 415 416 // Wait a bit for collection to get started. 417 sleep(WAIT_TIME_FOR_PROFILING_START_MS); 418 419 // Now request cancellation. 420 cancellationSignal.cancel(); 421 422 // Wait until callback#onAccept is triggered so we can confirm the result. 423 waitForCancelCallback(callback); 424 425 // Assert that result matches assumptions for success. 426 confirmCollectionSuccess(callback.mResult, OUTPUT_FILE_STACK_SAMPLING_SUFFIX); 427 } 428 429 /** Test that cancelling stack sampling stops collection and still receives correct result. */ 430 @Test 431 @RequiresFlagsEnabled({Flags.FLAG_TELEMETRY_APIS, Flags.FLAG_REDACTION_ENABLED}) testRequestSystemTraceCancel()432 public void testRequestSystemTraceCancel() throws Exception { 433 if (mProfilingManager == null) throw new TestException("mProfilingManager can not be null"); 434 435 // Set override durations to 10 minutes so we can ensure it finishes early when canceled. 436 overrideSystemTraceDeviceConfigValues(false, TEN_MINUTES_MS, TEN_MINUTES_MS, 437 TEN_MINUTES_MS); 438 439 AppCallback callback = new AppCallback(); 440 CancellationSignal cancellationSignal = new CancellationSignal(); 441 442 // Now kick off the request. 443 mProfilingManager.requestProfiling( 444 ProfilingManager.PROFILING_TYPE_SYSTEM_TRACE, 445 null, // Use default parameters since we will cancel quickly 446 null, 447 cancellationSignal, 448 new ProfilingTestUtils.ImmediateExecutor(), 449 callback); 450 451 // Wait a bit for collection to get started. 452 sleep(WAIT_TIME_FOR_PROFILING_START_MS); 453 454 // Now request cancellation. 455 cancellationSignal.cancel(); 456 457 // Wait until callback#onAccept is triggered so we can confirm the result. 458 waitForCancelCallback(callback); 459 460 // Assert that result matches assumptions for success. 461 confirmCollectionSuccess(callback.mResult, OUTPUT_FILE_TRACE_SUFFIX); 462 } 463 464 /** Test that unregistering a global listener works and that listener does not get called. */ 465 @Test 466 @SuppressWarnings("GuardedBy") // Suppress warning for mProfilingManager.mCallbacks lock. 467 @RequiresFlagsEnabled(Flags.FLAG_TELEMETRY_APIS) testUnregisterGeneralListener()468 public void testUnregisterGeneralListener() throws Exception { 469 if (mProfilingManager == null) throw new TestException("mProfilingManager can not be null"); 470 471 overrideStackSamplingDeviceConfigValues(false, ONE_SECOND_MS, ONE_SECOND_MS, 472 FIVE_SECONDS_MS); 473 474 // Clear all existing callbacks. 475 mProfilingManager.mCallbacks.clear(); 476 477 // Create 2 callbacks. 478 AppCallback callbackSpecific = new AppCallback(); 479 AppCallback callbackGeneral = new AppCallback(); 480 481 // Register the general callback. 482 mProfilingManager.registerForAllProfilingResults( 483 new ProfilingTestUtils.ImmediateExecutor(), callbackGeneral); 484 485 // Confirm callback is properly registered by checking for size of 1. 486 assertTrue(mProfilingManager.mCallbacks.size() == 1); 487 488 // Now unregister the general callback. 489 mProfilingManager.unregisterForAllProfilingResults(callbackGeneral); 490 491 // Now kick off the request. 492 mProfilingManager.requestProfiling( 493 ProfilingManager.PROFILING_TYPE_STACK_SAMPLING, 494 ProfilingTestUtils.getOneSecondDurationParamBundle(), 495 null, 496 null, 497 new ProfilingTestUtils.ImmediateExecutor(), 498 callbackSpecific); 499 500 // Wait until callback#onAccept is triggered so we can confirm the result. 501 waitForCallback(callbackSpecific); 502 503 // Assert that the unregistered callback was not triggered. 504 assertNull(callbackGeneral.mResult); 505 506 } 507 508 /** Test that a globally registered listener is triggered along with the specific one. */ 509 @Test 510 @RequiresFlagsEnabled(Flags.FLAG_TELEMETRY_APIS) testTriggerAllListeners()511 public void testTriggerAllListeners() throws Exception { 512 if (mProfilingManager == null) throw new TestException("mProfilingManager can not be null"); 513 514 overrideStackSamplingDeviceConfigValues(false, ONE_SECOND_MS, ONE_SECOND_MS, 515 FIVE_SECONDS_MS); 516 517 // Create 3 callbacks. 518 AppCallback callbackSpecific = new AppCallback(); 519 AppCallback callbackGeneral1 = new AppCallback(); 520 AppCallback callbackGeneral2 = new AppCallback(); 521 522 // Register the first general callback before kicking off request. 523 mProfilingManager.registerForAllProfilingResults( 524 new ProfilingTestUtils.ImmediateExecutor(), callbackGeneral1); 525 526 // Now kick off the request. 527 mProfilingManager.requestProfiling( 528 ProfilingManager.PROFILING_TYPE_STACK_SAMPLING, 529 ProfilingTestUtils.getOneSecondDurationParamBundle(), 530 null, 531 null, 532 new ProfilingTestUtils.ImmediateExecutor(), 533 callbackSpecific); 534 535 // Register the 2nd general callback after kicking off request, but before result is ready. 536 mProfilingManager.registerForAllProfilingResults( 537 new ProfilingTestUtils.ImmediateExecutor(), callbackGeneral2); 538 539 // Wait until callback#onAccept is triggered so we can confirm the result. 540 waitForCallback(callbackSpecific); 541 542 // Assert that result matches assumptions for success in all callbacks. 543 confirmCollectionSuccess(callbackSpecific.mResult, OUTPUT_FILE_STACK_SAMPLING_SUFFIX); 544 confirmCollectionSuccess(callbackGeneral1.mResult, OUTPUT_FILE_STACK_SAMPLING_SUFFIX); 545 confirmCollectionSuccess(callbackGeneral2.mResult, OUTPUT_FILE_STACK_SAMPLING_SUFFIX); 546 } 547 548 /** Test that listeners registered to the same UID from different contexts are all triggered. */ 549 @Test 550 @RequiresFlagsEnabled(Flags.FLAG_TELEMETRY_APIS) testTriggerAllListenersDifferentContexts()551 public void testTriggerAllListenersDifferentContexts() throws Exception { 552 if (mProfilingManager == null) throw new TestException("mProfilingManager can not be null"); 553 554 overrideStackSamplingDeviceConfigValues(false, ONE_SECOND_MS, ONE_SECOND_MS, 555 FIVE_SECONDS_MS); 556 557 // Obtain another ProfilingManager instance from a different context. 558 Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); 559 // Confirm the 2 contexts are of a different class. This check is broader than is a strictly 560 // required, but guarantees these contexts cannot be the same. 561 assertFalse(mContext.getClass().equals(context.getClass())); 562 ProfilingManager profilingManager = context.getSystemService(ProfilingManager.class); 563 564 // Create 3 callbacks. 565 AppCallback callbackSpecific = new AppCallback(); 566 AppCallback callbackGeneral1 = new AppCallback(); 567 AppCallback callbackGeneral2 = new AppCallback(); 568 569 // Register the general callbacks, one to each context. 570 profilingManager.registerForAllProfilingResults( 571 new ProfilingTestUtils.ImmediateExecutor(), callbackGeneral1); 572 mProfilingManager.registerForAllProfilingResults( 573 new ProfilingTestUtils.ImmediateExecutor(), callbackGeneral2); 574 575 // Now kick off the request. 576 mProfilingManager.requestProfiling( 577 ProfilingManager.PROFILING_TYPE_STACK_SAMPLING, 578 ProfilingTestUtils.getOneSecondDurationParamBundle(), 579 null, 580 null, 581 new ProfilingTestUtils.ImmediateExecutor(), 582 callbackSpecific); 583 584 585 // Wait until callback#onAccept is triggered so we can confirm the result. 586 waitForCallback(callbackSpecific); 587 588 // Assert that result matches assumptions for success in all callbacks. 589 confirmCollectionSuccess(callbackSpecific.mResult, OUTPUT_FILE_STACK_SAMPLING_SUFFIX); 590 confirmCollectionSuccess(callbackGeneral1.mResult, OUTPUT_FILE_STACK_SAMPLING_SUFFIX); 591 confirmCollectionSuccess(callbackGeneral2.mResult, OUTPUT_FILE_STACK_SAMPLING_SUFFIX); 592 } 593 594 /** Test that profiling request result file name contains the correct tag. */ 595 @Test 596 @RequiresFlagsEnabled(Flags.FLAG_TELEMETRY_APIS) testRequestTagInFilename()597 public void testRequestTagInFilename() throws Exception { 598 if (mProfilingManager == null) throw new TestException("mProfilingManager can not be null"); 599 600 overrideStackSamplingDeviceConfigValues(false, ONE_SECOND_MS, ONE_SECOND_MS, 601 FIVE_SECONDS_MS); 602 603 AppCallback callback = new AppCallback(); 604 605 // Setup tag to use with invalid chars and length, and expected cleaned up version. 606 String fullTag = "TestTag-_-_-12345678901234567890\\\"&:|<>"; 607 String tagForFilename = "testtag---1234567890"; 608 609 // Now kick off the request. 610 mProfilingManager.requestProfiling( 611 ProfilingManager.PROFILING_TYPE_STACK_SAMPLING, 612 ProfilingTestUtils.getOneSecondDurationParamBundle(), 613 fullTag, 614 null, 615 new ProfilingTestUtils.ImmediateExecutor(), 616 callback); 617 618 // Wait until callback#onAccept is triggered so we can confirm the result. 619 waitForCallback(callback); 620 621 // Assert that used tag matches returned tag. 622 assertTrue(fullTag.equals(callback.mResult.getTag())); 623 624 // Split the path to obtain the filename. 625 String[] pathArray = callback.mResult.getResultFilePath().split("/"); 626 // Then split the filename to obtain the tag section. 627 String[] nameArray = pathArray[pathArray.length - 1].split("_"); 628 629 // Assert that the file name section containing the tag matches the expected filename tag. 630 assertTrue(nameArray[1].equals(tagForFilename)); 631 } 632 633 /** Test that java heap dump killswitch disables collection. */ 634 @Test 635 @RequiresFlagsEnabled(Flags.FLAG_TELEMETRY_APIS) testJavaHeapDumpKillswitchEnabled()636 public void testJavaHeapDumpKillswitchEnabled() throws Exception { 637 if (mProfilingManager == null) throw new TestException("mProfilingManager can not be null"); 638 639 overrideJavaHeapDumpDeviceConfigValues(true, ONE_SECOND_MS, TEN_SECONDS_MS); 640 641 AppCallback callback = new AppCallback(); 642 643 // Now kick off the request. 644 mProfilingManager.requestProfiling( 645 ProfilingManager.PROFILING_TYPE_JAVA_HEAP_DUMP, 646 null, 647 null, 648 null, 649 new ProfilingTestUtils.ImmediateExecutor(), 650 callback); 651 652 // Wait until callback#onAccept is triggered so we can confirm the result. 653 waitForCallback(callback); 654 655 // Assert that request failed with correct error code. 656 assertEquals(ProfilingResult.ERROR_FAILED_INVALID_REQUEST, callback.mResult.getErrorCode()); 657 } 658 659 /** Test that heap profile killswitch disables collection. */ 660 @Test 661 @RequiresFlagsEnabled(Flags.FLAG_TELEMETRY_APIS) testHeapProfileKillswitchEnabled()662 public void testHeapProfileKillswitchEnabled() throws Exception { 663 if (mProfilingManager == null) throw new TestException("mProfilingManager can not be null"); 664 665 overrideHeapProfileDeviceConfigValues(true, ONE_SECOND_MS, FIVE_SECONDS_MS, TEN_SECONDS_MS); 666 667 AppCallback callback = new AppCallback(); 668 669 // Now kick off the request. 670 mProfilingManager.requestProfiling( 671 ProfilingManager.PROFILING_TYPE_HEAP_PROFILE, 672 null, 673 null, 674 null, 675 new ProfilingTestUtils.ImmediateExecutor(), 676 callback); 677 678 // Wait until callback#onAccept is triggered so we can confirm the result. 679 waitForCallback(callback); 680 681 // Assert that request failed with correct error code. 682 assertEquals(ProfilingResult.ERROR_FAILED_INVALID_REQUEST, callback.mResult.getErrorCode()); 683 } 684 685 /** Test that stack sampling killswitch disables collection. */ 686 @Test 687 @RequiresFlagsEnabled(Flags.FLAG_TELEMETRY_APIS) testStackSamplingKillswitchEnabled()688 public void testStackSamplingKillswitchEnabled() throws Exception { 689 if (mProfilingManager == null) throw new TestException("mProfilingManager can not be null"); 690 691 overrideStackSamplingDeviceConfigValues(true, ONE_SECOND_MS, FIVE_SECONDS_MS, 692 TEN_SECONDS_MS); 693 694 AppCallback callback = new AppCallback(); 695 696 // Now kick off the request. 697 mProfilingManager.requestProfiling( 698 ProfilingManager.PROFILING_TYPE_STACK_SAMPLING, 699 null, 700 null, 701 null, 702 new ProfilingTestUtils.ImmediateExecutor(), 703 callback); 704 705 // Wait until callback#onAccept is triggered so we can confirm the result. 706 waitForCallback(callback); 707 708 // Assert that request failed with correct error code. 709 assertEquals(ProfilingResult.ERROR_FAILED_INVALID_REQUEST, callback.mResult.getErrorCode()); 710 } 711 712 /** Test that system trace killswitch disables collection. */ 713 @Test 714 @RequiresFlagsEnabled({Flags.FLAG_TELEMETRY_APIS, Flags.FLAG_REDACTION_ENABLED}) testSystemTraceKillswitchEnabled()715 public void testSystemTraceKillswitchEnabled() throws Exception { 716 if (mProfilingManager == null) throw new TestException("mProfilingManager can not be null"); 717 718 overrideSystemTraceDeviceConfigValues(true, ONE_SECOND_MS, FIVE_SECONDS_MS, TEN_SECONDS_MS); 719 720 AppCallback callback = new AppCallback(); 721 722 // Now kick off the request. 723 mProfilingManager.requestProfiling( 724 ProfilingManager.PROFILING_TYPE_SYSTEM_TRACE, 725 null, 726 null, 727 null, 728 new ProfilingTestUtils.ImmediateExecutor(), 729 callback); 730 731 // Wait until callback#onAccept is triggered so we can confirm the result. 732 waitForCallback(callback); 733 734 // Assert that request failed with correct error code. 735 assertEquals(ProfilingResult.ERROR_FAILED_INVALID_REQUEST, callback.mResult.getErrorCode()); 736 } 737 738 /** 739 * Test that adding a new general listener when no listeners have been added to that instance 740 * works correctly, that is: that mProfilingService has been initialized. 741 * 742 * The flow should result in registerResultsCallback being triggered with isGeneralListener true 743 * and generalListenerAdded not being triggered, but we cannot confirm this specifically here. 744 */ 745 @SuppressWarnings("GuardedBy") // Suppress warning for mProfilingManager lock. 746 @Test 747 @RequiresFlagsEnabled({Flags.FLAG_TELEMETRY_APIS}) testAddGeneralListenerNoCurrentListeners()748 public void testAddGeneralListenerNoCurrentListeners() throws Exception { 749 if (mProfilingManager == null) throw new TestException("mProfilingManager can not be null"); 750 751 // Setup for no current listener - mProfilingService should be null and mCallbacks empty. 752 mProfilingManager.mProfilingService = null; 753 mProfilingManager.mCallbacks.clear(); 754 755 AppCallback callback = new AppCallback(); 756 757 // Register the general callback. 758 mProfilingManager.registerForAllProfilingResults(new ProfilingTestUtils.ImmediateExecutor(), 759 callback); 760 761 // Confirm that mProfilingService has been initialized. 762 assertNotNull(mProfilingManager.mProfilingService); 763 } 764 765 /** 766 * Test that adding a new profiling instance specific listener when no listeners have been 767 * added to that instance works correctly, that is: that mProfilingService has been initialized. 768 * 769 * The flow should result in registerResultsCallback being triggered with isGeneralListener 770 * false and generalListenerAdded not being triggered, but we cannot confirm this specifically 771 * here. 772 */ 773 @SuppressWarnings("GuardedBy") // Suppress warning for mProfilingManager lock. 774 @Test 775 @RequiresFlagsEnabled({Flags.FLAG_TELEMETRY_APIS}) testAddSpecificListenerNoCurrentListeners()776 public void testAddSpecificListenerNoCurrentListeners() throws Exception { 777 if (mProfilingManager == null) throw new TestException("mProfilingManager can not be null"); 778 779 overrideStackSamplingDeviceConfigValues(false, ONE_SECOND_MS, ONE_SECOND_MS, 780 FIVE_SECONDS_MS); 781 782 // Setup for no current listener - mProfilingService should be null and mCallbacks empty. 783 mProfilingManager.mProfilingService = null; 784 mProfilingManager.mCallbacks.clear(); 785 786 AppCallback callback = new AppCallback(); 787 788 mProfilingManager.requestProfiling( 789 ProfilingManager.PROFILING_TYPE_STACK_SAMPLING, 790 null, 791 null, 792 null, 793 new ProfilingTestUtils.ImmediateExecutor(), 794 callback); 795 796 // Confirm that mProfilingService has been initialized. 797 assertNotNull(mProfilingManager.mProfilingService); 798 } 799 800 /** 801 * Test that adding a new general listener when a listener has already been added to that 802 * instance works correctly, that is: generalListenerAdded is triggered, but 803 * registerResultsCallback is not. 804 */ 805 @SuppressWarnings("GuardedBy") // Suppress warning for mProfilingManager lock. 806 @Test 807 @RequiresFlagsEnabled({Flags.FLAG_TELEMETRY_APIS}) testAddGeneralListenerWithCurrentListener()808 public void testAddGeneralListenerWithCurrentListener() throws Exception { 809 if (mProfilingManager == null) throw new TestException("mProfilingManager can not be null"); 810 811 mProfilingManager.mProfilingService = spy(new ProfilingService(mContext)); 812 813 AppCallback callback = new AppCallback(); 814 815 // Register the general callback. 816 mProfilingManager.registerForAllProfilingResults(new ProfilingTestUtils.ImmediateExecutor(), 817 callback); 818 819 // Confirm that generalListenerAdded was triggered and registerResultsCallback was not. 820 verify(mProfilingManager.mProfilingService, times(0)).registerResultsCallback(anyBoolean(), 821 any()); 822 verify(mProfilingManager.mProfilingService, times(1)).generalListenerAdded(); 823 } 824 825 /** 826 * Test that adding a new profiling instance specific listener when a listener has already been 827 * added to that instance works correctly, that is: neither registerResultsCallback nor 828 * generalListenerAdded are triggered. 829 */ 830 @SuppressWarnings("GuardedBy") // Suppress warning for mProfilingManager lock. 831 @Test 832 @RequiresFlagsEnabled({Flags.FLAG_TELEMETRY_APIS}) testAddSpecificListenerWithCurrentListener()833 public void testAddSpecificListenerWithCurrentListener() throws Exception { 834 if (mProfilingManager == null) throw new TestException("mProfilingManager can not be null"); 835 836 overrideStackSamplingDeviceConfigValues(false, ONE_SECOND_MS, ONE_SECOND_MS, 837 FIVE_SECONDS_MS); 838 839 mProfilingManager.mProfilingService = spy(new ProfilingService(mContext)); 840 841 AppCallback callback = new AppCallback(); 842 843 mProfilingManager.requestProfiling( 844 ProfilingManager.PROFILING_TYPE_STACK_SAMPLING, 845 null, 846 null, 847 null, 848 new ProfilingTestUtils.ImmediateExecutor(), 849 callback); 850 851 // Confirm that neither generalListenerAdded nor registerResultsCallback were triggered. 852 verify(mProfilingManager.mProfilingService, times(0)).registerResultsCallback(anyBoolean(), 853 any()); 854 verify(mProfilingManager.mProfilingService, times(0)).generalListenerAdded(); 855 } 856 857 /** Disable the rate limiter and wait long enough for the update to be picked up. */ disableRateLimiter()858 private void disableRateLimiter() { 859 SystemUtil.runShellCommand( 860 "device_config put profiling_testing rate_limiter.disabled true"); 861 for (int i = 0; i < RATE_LIMITER_WAIT_TIME_INCREMENTS_COUNT; i++) { 862 sleep(RATE_LIMITER_WAIT_TIME_INCREMENT_MS); 863 String output = SystemUtil.runShellCommand( 864 "device_config get profiling_testing rate_limiter.disabled"); 865 if (Boolean.parseBoolean(output.trim())) { 866 return; 867 } 868 869 } 870 } 871 872 /** Wait for callback to be triggered. Waits for up to 5 minutes, checking every 5 seconds. */ waitForCallback(AppCallback callback)873 private void waitForCallback(AppCallback callback) { 874 waitForCallback(callback, CALLBACK_WAIT_TIME_INCREMENT_MS, 875 CALLBACK_WAIT_TIME_INCREMENTS_COUNT); 876 } 877 878 /** 879 * Wait for callback to be triggered after cancellation. Waits for up to 20 seconds, checking 880 * every 5 seconds. 881 */ waitForCancelCallback(AppCallback callback)882 private void waitForCancelCallback(AppCallback callback) { 883 waitForCallback(callback, CALLBACK_WAIT_TIME_INCREMENT_MS, 884 CALLBACK_CANCEL_WAIT_TIME_INCREMENTS_COUNT); 885 } 886 887 /** 888 * Wait for callback to be triggered. Waits up to incrementMs * count, checking every 889 * incrementMs milliseconds. 890 */ waitForCallback(AppCallback callback, int incrementMs, int count)891 private void waitForCallback(AppCallback callback, int incrementMs, int count) { 892 for (int i = 0; i < count; i++) { 893 sleep(incrementMs); 894 if (callback.mResult != null) { 895 return; 896 } 897 } 898 fail("Test timed out waiting for callback"); 899 } 900 901 /** Assert that result matches a success case, specifically: contains a path and no errors. */ confirmCollectionSuccess(ProfilingResult result, String suffix)902 private void confirmCollectionSuccess(ProfilingResult result, String suffix) { 903 assertNotNull(result); 904 assertEquals(ProfilingResult.ERROR_NONE, result.getErrorCode()); 905 assertNotNull(result.getResultFilePath()); 906 assertTrue(result.getResultFilePath().contains(suffix)); 907 assertNull(result.getErrorMessage()); 908 909 // Confirm output file exists and is not empty. 910 File file = new File(result.getResultFilePath()); 911 assertTrue(file.exists()); 912 assertFalse(file.length() == 0); 913 } 914 915 /** Copies the trace to an /sdcard directory that will be collected by the test runner. */ dumpTrace(ProfilingResult result)916 private void dumpTrace(ProfilingResult result) { 917 assertNotNull(result); 918 assertEquals(ProfilingResult.ERROR_NONE, result.getErrorCode()); 919 assertNotNull(result.getResultFilePath()); 920 assertNull(result.getErrorMessage()); 921 922 // Copy to dump directory 923 Path path = Paths.get(result.getResultFilePath()); 924 try { 925 Files.createDirectories(DUMP_PATH); 926 String filename = mTestName.getMethodName() + "_" + path.getFileName() 927 + ".perfetto-trace"; 928 Files.copy(path, Paths.get(DUMP_PATH.toString(), filename)); 929 } catch (IOException e) { 930 throw new AssertionError("Failed to copy to DUMP_PATH", e); 931 } 932 } 933 overrideJavaHeapDumpDeviceConfigValues(boolean killswitchEnabled, int durationMs, int dataSourceTimeoutMs)934 private void overrideJavaHeapDumpDeviceConfigValues(boolean killswitchEnabled, int durationMs, 935 int dataSourceTimeoutMs) throws Exception { 936 executeShellCmd(COMMAND_OVERRIDE_DEVICE_CONFIG_BOOL, DeviceConfigHelper.NAMESPACE, 937 DeviceConfigHelper.KILLSWITCH_JAVA_HEAP_DUMP, killswitchEnabled); 938 executeShellCmd(COMMAND_OVERRIDE_DEVICE_CONFIG_INT, DeviceConfigHelper.NAMESPACE, 939 DeviceConfigHelper.JAVA_HEAP_DUMP_DURATION_MS_DEFAULT, durationMs); 940 executeShellCmd(COMMAND_OVERRIDE_DEVICE_CONFIG_INT, DeviceConfigHelper.NAMESPACE, 941 DeviceConfigHelper.JAVA_HEAP_DUMP_DATA_SOURCE_STOP_TIMEOUT_MS_DEFAULT, 942 dataSourceTimeoutMs); 943 } 944 overrideHeapProfileDeviceConfigValues(boolean killswitchEnabled, int durationDefaultMs, int durationMinMs, int durationMaxMs)945 private void overrideHeapProfileDeviceConfigValues(boolean killswitchEnabled, 946 int durationDefaultMs, int durationMinMs, int durationMaxMs) throws Exception { 947 executeShellCmd(COMMAND_OVERRIDE_DEVICE_CONFIG_BOOL, DeviceConfigHelper.NAMESPACE, 948 DeviceConfigHelper.KILLSWITCH_HEAP_PROFILE, killswitchEnabled); 949 executeShellCmd(COMMAND_OVERRIDE_DEVICE_CONFIG_INT, DeviceConfigHelper.NAMESPACE, 950 DeviceConfigHelper.HEAP_PROFILE_DURATION_MS_DEFAULT, durationDefaultMs); 951 executeShellCmd(COMMAND_OVERRIDE_DEVICE_CONFIG_INT, DeviceConfigHelper.NAMESPACE, 952 DeviceConfigHelper.HEAP_PROFILE_DURATION_MS_MIN, durationMinMs); 953 executeShellCmd(COMMAND_OVERRIDE_DEVICE_CONFIG_INT, DeviceConfigHelper.NAMESPACE, 954 DeviceConfigHelper.HEAP_PROFILE_DURATION_MS_MAX, durationMaxMs); 955 } 956 overrideStackSamplingDeviceConfigValues(boolean killswitchEnabled, int durationDefaultMs, int durationMinMs, int durationMaxMs)957 private void overrideStackSamplingDeviceConfigValues(boolean killswitchEnabled, 958 int durationDefaultMs, int durationMinMs, int durationMaxMs) throws Exception { 959 executeShellCmd(COMMAND_OVERRIDE_DEVICE_CONFIG_BOOL, DeviceConfigHelper.NAMESPACE, 960 DeviceConfigHelper.KILLSWITCH_STACK_SAMPLING, killswitchEnabled); 961 executeShellCmd(COMMAND_OVERRIDE_DEVICE_CONFIG_INT, DeviceConfigHelper.NAMESPACE, 962 DeviceConfigHelper.STACK_SAMPLING_DURATION_MS_DEFAULT, durationDefaultMs); 963 executeShellCmd(COMMAND_OVERRIDE_DEVICE_CONFIG_INT, DeviceConfigHelper.NAMESPACE, 964 DeviceConfigHelper.STACK_SAMPLING_DURATION_MS_MIN, durationMinMs); 965 executeShellCmd(COMMAND_OVERRIDE_DEVICE_CONFIG_INT, DeviceConfigHelper.NAMESPACE, 966 DeviceConfigHelper.STACK_SAMPLING_DURATION_MS_MAX, durationMaxMs); 967 } 968 overrideSystemTraceDeviceConfigValues(boolean killswitchEnabled, int durationDefaultMs, int durationMinMs, int durationMaxMs)969 private void overrideSystemTraceDeviceConfigValues(boolean killswitchEnabled, 970 int durationDefaultMs, int durationMinMs, int durationMaxMs) throws Exception { 971 executeShellCmd(COMMAND_OVERRIDE_DEVICE_CONFIG_BOOL, DeviceConfigHelper.NAMESPACE, 972 DeviceConfigHelper.KILLSWITCH_SYSTEM_TRACE, killswitchEnabled); 973 executeShellCmd(COMMAND_OVERRIDE_DEVICE_CONFIG_INT, DeviceConfigHelper.NAMESPACE, 974 DeviceConfigHelper.SYSTEM_TRACE_DURATION_MS_DEFAULT, durationDefaultMs); 975 executeShellCmd(COMMAND_OVERRIDE_DEVICE_CONFIG_INT, DeviceConfigHelper.NAMESPACE, 976 DeviceConfigHelper.SYSTEM_TRACE_DURATION_MS_MIN, durationMinMs); 977 executeShellCmd(COMMAND_OVERRIDE_DEVICE_CONFIG_INT, DeviceConfigHelper.NAMESPACE, 978 DeviceConfigHelper.SYSTEM_TRACE_DURATION_MS_MAX, durationMaxMs); 979 } 980 981 @FormatMethod executeShellCmd(String cmdFormat, Object... args)982 private String executeShellCmd(String cmdFormat, Object... args) throws Exception { 983 String cmd = String.format(cmdFormat, args); 984 return SystemUtil.runShellCommand(mInstrumentation, cmd); 985 } 986 sleep(long ms)987 private static void sleep(long ms) { 988 try { 989 Thread.sleep(ms); 990 } catch (InterruptedException e) { 991 // Do nothing. 992 } 993 } 994 995 public static class AppCallback implements Consumer<ProfilingResult> { 996 997 public ProfilingResult mResult; 998 999 @Override accept(ProfilingResult result)1000 public void accept(ProfilingResult result) { 1001 mResult = result; 1002 } 1003 } 1004 1005 // Starts a thread that keeps a CPU busy. 1006 private static class BusyLoopThread { 1007 private Thread thread; 1008 private AtomicBoolean done = new AtomicBoolean(false); 1009 BusyLoopThread()1010 public BusyLoopThread() { 1011 done.set(false); 1012 thread = new Thread(() -> { 1013 while (!done.get()) { 1014 } 1015 }); 1016 thread.start(); 1017 } 1018 stop()1019 public void stop() { 1020 done.set(true); 1021 try { 1022 thread.join(); 1023 } catch (InterruptedException e) { 1024 throw new AssertionError("InterruptedException", e); 1025 } 1026 } 1027 } 1028 } 1029