1 /* 2 * Copyright (C) 2023 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.tracing.perfetto; 18 19 import static android.internal.perfetto.protos.TestEventOuterClass.TestEvent.PAYLOAD; 20 import static android.internal.perfetto.protos.TestEventOuterClass.TestEvent.TestPayload.SINGLE_INT; 21 import static android.internal.perfetto.protos.TracePacketOuterClass.TracePacket.FOR_TESTING; 22 23 import static java.io.File.createTempFile; 24 import static java.nio.file.Files.createTempDirectory; 25 26 import android.internal.perfetto.protos.DataSourceConfigOuterClass.DataSourceConfig; 27 import android.internal.perfetto.protos.TestConfigOuterClass.TestConfig; 28 import android.tools.ScenarioBuilder; 29 import android.tools.Tag; 30 import android.tools.io.TraceType; 31 import android.tools.traces.TraceConfig; 32 import android.tools.traces.TraceConfigs; 33 import android.tools.traces.io.ResultReader; 34 import android.tools.traces.io.ResultWriter; 35 import android.tools.traces.monitors.PerfettoTraceMonitor; 36 import android.tools.traces.monitors.TraceMonitor; 37 import android.util.proto.ProtoInputStream; 38 import android.util.proto.ProtoOutputStream; 39 40 import androidx.test.ext.junit.runners.AndroidJUnit4; 41 42 import com.google.common.truth.Truth; 43 import com.google.protobuf.InvalidProtocolBufferException; 44 45 import org.junit.Before; 46 import org.junit.BeforeClass; 47 import org.junit.Test; 48 import org.junit.runner.RunWith; 49 50 import perfetto.protos.PerfettoConfig; 51 import perfetto.protos.TracePacketOuterClass; 52 53 import java.io.File; 54 import java.io.IOException; 55 import java.util.ArrayList; 56 import java.util.List; 57 import java.util.concurrent.CountDownLatch; 58 import java.util.concurrent.TimeUnit; 59 import java.util.concurrent.atomic.AtomicBoolean; 60 import java.util.concurrent.atomic.AtomicInteger; 61 import java.util.concurrent.atomic.AtomicReference; 62 63 @RunWith(AndroidJUnit4.class) 64 public class DataSourceTest { 65 private final File mTracingDirectory = createTempDirectory("temp").toFile(); 66 67 private final ResultWriter mWriter = new ResultWriter() 68 .forScenario(new ScenarioBuilder() 69 .forClass(createTempFile("temp", "").getName()).build()) 70 .withOutputDir(mTracingDirectory) 71 .setRunComplete(); 72 73 private final TraceConfigs mTraceConfig = new TraceConfigs( 74 new TraceConfig(false, true, false), 75 new TraceConfig(false, true, false), 76 new TraceConfig(false, true, false), 77 new TraceConfig(false, true, false) 78 ); 79 80 private static TestDataSource sTestDataSource; 81 82 private static TestDataSource.DataSourceInstanceProvider sInstanceProvider; 83 private static TestDataSource.TlsStateProvider sTlsStateProvider; 84 private static TestDataSource.IncrementalStateProvider sIncrementalStateProvider; 85 DataSourceTest()86 public DataSourceTest() throws IOException {} 87 88 @BeforeClass beforeAll()89 public static void beforeAll() { 90 Producer.init(InitArguments.DEFAULTS); 91 setupProviders(); 92 sTestDataSource = new TestDataSource( 93 (ds, idx, configStream) -> sInstanceProvider.provide(ds, idx, configStream), 94 args -> sTlsStateProvider.provide(args), 95 args -> sIncrementalStateProvider.provide(args)); 96 sTestDataSource.register(DataSourceParams.DEFAULTS); 97 } 98 setupProviders()99 private static void setupProviders() { 100 sInstanceProvider = (ds, idx, configStream) -> 101 new TestDataSource.TestDataSourceInstance(ds, idx); 102 sTlsStateProvider = args -> new TestDataSource.TestTlsState(); 103 sIncrementalStateProvider = args -> new TestDataSource.TestIncrementalState(); 104 } 105 106 @Before setup()107 public void setup() { 108 setupProviders(); 109 } 110 111 @Test canTraceData()112 public void canTraceData() throws InvalidProtocolBufferException { 113 final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() 114 .enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder() 115 .setName(sTestDataSource.name).build()).build(); 116 117 try { 118 traceMonitor.start(); 119 120 sTestDataSource.trace((ctx) -> { 121 final ProtoOutputStream protoOutputStream = ctx.newTracePacket(); 122 long forTestingToken = protoOutputStream.start(FOR_TESTING); 123 long payloadToken = protoOutputStream.start(PAYLOAD); 124 protoOutputStream.write(SINGLE_INT, 10); 125 protoOutputStream.end(payloadToken); 126 protoOutputStream.end(forTestingToken); 127 }); 128 } finally { 129 traceMonitor.stop(mWriter); 130 } 131 132 final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); 133 final byte[] rawProtoFromFile = reader.readBytes(TraceType.PERFETTO, Tag.ALL); 134 assert rawProtoFromFile != null; 135 final perfetto.protos.TraceOuterClass.Trace trace = perfetto.protos.TraceOuterClass.Trace 136 .parseFrom(rawProtoFromFile); 137 138 Truth.assertThat(trace.getPacketCount()).isGreaterThan(0); 139 final List<TracePacketOuterClass.TracePacket> tracePackets = trace.getPacketList() 140 .stream().filter(TracePacketOuterClass.TracePacket::hasForTesting).toList(); 141 final List<TracePacketOuterClass.TracePacket> matchingPackets = tracePackets.stream() 142 .filter(it -> it.getForTesting().getPayload().getSingleInt() == 10).toList(); 143 Truth.assertThat(matchingPackets).hasSize(1); 144 } 145 146 @Test canUseTlsStateForCustomState()147 public void canUseTlsStateForCustomState() { 148 final int expectedStateTestValue = 10; 149 final AtomicInteger actualStateTestValue = new AtomicInteger(); 150 151 final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() 152 .enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder() 153 .setName(sTestDataSource.name).build()).build(); 154 155 try { 156 traceMonitor.start(); 157 158 sTestDataSource.trace((ctx) -> { 159 TestDataSource.TestTlsState state = ctx.getCustomTlsState(); 160 state.testStateValue = expectedStateTestValue; 161 }); 162 163 sTestDataSource.trace((ctx) -> { 164 TestDataSource.TestTlsState state = ctx.getCustomTlsState(); 165 actualStateTestValue.set(state.testStateValue); 166 }); 167 } finally { 168 traceMonitor.stop(mWriter); 169 } 170 171 Truth.assertThat(actualStateTestValue.get()).isEqualTo(expectedStateTestValue); 172 } 173 174 @Test eachInstanceHasOwnTlsState()175 public void eachInstanceHasOwnTlsState() { 176 final int[] expectedStateTestValues = new int[] { 1, 2 }; 177 final int[] actualStateTestValues = new int[] { 0, 0 }; 178 179 final TraceMonitor traceMonitor1 = PerfettoTraceMonitor.newBuilder() 180 .enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder() 181 .setName(sTestDataSource.name).build()).build(); 182 final TraceMonitor traceMonitor2 = PerfettoTraceMonitor.newBuilder() 183 .enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder() 184 .setName(sTestDataSource.name).build()).build(); 185 186 try { 187 traceMonitor1.start(); 188 try { 189 traceMonitor2.start(); 190 191 AtomicInteger index = new AtomicInteger(0); 192 sTestDataSource.trace((ctx) -> { 193 TestDataSource.TestTlsState state = ctx.getCustomTlsState(); 194 state.testStateValue = expectedStateTestValues[index.getAndIncrement()]; 195 }); 196 197 index.set(0); 198 sTestDataSource.trace((ctx) -> { 199 TestDataSource.TestTlsState state = ctx.getCustomTlsState(); 200 actualStateTestValues[index.getAndIncrement()] = state.testStateValue; 201 }); 202 } finally { 203 traceMonitor1.stop(mWriter); 204 } 205 } finally { 206 traceMonitor2.stop(mWriter); 207 } 208 209 Truth.assertThat(actualStateTestValues[0]).isEqualTo(expectedStateTestValues[0]); 210 Truth.assertThat(actualStateTestValues[1]).isEqualTo(expectedStateTestValues[1]); 211 } 212 213 @Test eachThreadHasOwnTlsState()214 public void eachThreadHasOwnTlsState() throws InterruptedException { 215 final int thread1ExpectedStateValue = 1; 216 final int thread2ExpectedStateValue = 2; 217 218 final AtomicInteger thread1ActualStateValue = new AtomicInteger(); 219 final AtomicInteger thread2ActualStateValue = new AtomicInteger(); 220 221 final CountDownLatch setUpLatch = new CountDownLatch(2); 222 final CountDownLatch setStateLatch = new CountDownLatch(2); 223 final CountDownLatch setOutStateLatch = new CountDownLatch(2); 224 225 final RunnableCreator createTask = (stateValue, stateOut) -> () -> { 226 Producer.init(InitArguments.DEFAULTS); 227 228 setUpLatch.countDown(); 229 230 try { 231 setUpLatch.await(3, TimeUnit.SECONDS); 232 } catch (InterruptedException e) { 233 throw new RuntimeException(e); 234 } 235 236 sTestDataSource.trace((ctx) -> { 237 TestDataSource.TestTlsState state = ctx.getCustomTlsState(); 238 state.testStateValue = stateValue; 239 setStateLatch.countDown(); 240 }); 241 242 try { 243 setStateLatch.await(3, TimeUnit.SECONDS); 244 } catch (InterruptedException e) { 245 throw new RuntimeException(e); 246 } 247 248 sTestDataSource.trace((ctx) -> { 249 stateOut.set(ctx.getCustomTlsState().testStateValue); 250 setOutStateLatch.countDown(); 251 }); 252 }; 253 254 final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() 255 .enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder() 256 .setName(sTestDataSource.name).build()).build(); 257 258 try { 259 traceMonitor.start(); 260 261 new Thread( 262 createTask.create(thread1ExpectedStateValue, thread1ActualStateValue)).start(); 263 new Thread( 264 createTask.create(thread2ExpectedStateValue, thread2ActualStateValue)).start(); 265 266 setOutStateLatch.await(3, TimeUnit.SECONDS); 267 268 } finally { 269 traceMonitor.stop(mWriter); 270 } 271 272 Truth.assertThat(thread1ActualStateValue.get()).isEqualTo(thread1ExpectedStateValue); 273 Truth.assertThat(thread2ActualStateValue.get()).isEqualTo(thread2ExpectedStateValue); 274 } 275 276 @Test incrementalStateIsReset()277 public void incrementalStateIsReset() throws InterruptedException { 278 279 final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() 280 .enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder() 281 .setName(sTestDataSource.name).build()) 282 .setIncrementalTimeout(10) 283 .build(); 284 285 final AtomicInteger testStateValue = new AtomicInteger(); 286 try { 287 traceMonitor.start(); 288 289 sTestDataSource.trace(ctx -> ctx.getIncrementalState().testStateValue = 1); 290 291 // Timeout to make sure the incremental state is cleared. 292 Thread.sleep(1000); 293 294 sTestDataSource.trace(ctx -> 295 testStateValue.set(ctx.getIncrementalState().testStateValue)); 296 } finally { 297 traceMonitor.stop(mWriter); 298 } 299 300 Truth.assertThat(testStateValue.get()).isNotEqualTo(1); 301 } 302 303 @Test getInstanceConfigOnCreateInstance()304 public void getInstanceConfigOnCreateInstance() throws IOException { 305 final int expectedDummyIntValue = 10; 306 AtomicReference<ProtoInputStream> configStream = new AtomicReference<>(); 307 sInstanceProvider = (ds, idx, config) -> { 308 configStream.set(config); 309 return new TestDataSource.TestDataSourceInstance(ds, idx); 310 }; 311 312 final TraceMonitor monitor = PerfettoTraceMonitor.newBuilder() 313 .enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder() 314 .setName(sTestDataSource.name) 315 .setForTesting(PerfettoConfig.TestConfig.newBuilder().setDummyFields( 316 PerfettoConfig.TestConfig.DummyFields.newBuilder() 317 .setFieldInt32(expectedDummyIntValue) 318 .build()) 319 .build()) 320 .build()) 321 .build(); 322 323 try { 324 monitor.start(); 325 } finally { 326 monitor.stop(mWriter); 327 } 328 329 int configDummyIntValue = 0; 330 while (configStream.get().nextField() != ProtoInputStream.NO_MORE_FIELDS) { 331 if (configStream.get().getFieldNumber() 332 == (int) DataSourceConfig.FOR_TESTING) { 333 final long forTestingToken = configStream.get() 334 .start(DataSourceConfig.FOR_TESTING); 335 while (configStream.get().nextField() != ProtoInputStream.NO_MORE_FIELDS) { 336 if (configStream.get().getFieldNumber() 337 == (int) TestConfig.DUMMY_FIELDS) { 338 final long dummyFieldsToken = configStream.get() 339 .start(TestConfig.DUMMY_FIELDS); 340 while (configStream.get().nextField() != ProtoInputStream.NO_MORE_FIELDS) { 341 if (configStream.get().getFieldNumber() 342 == (int) TestConfig.DummyFields.FIELD_INT32) { 343 int val = configStream.get().readInt( 344 TestConfig.DummyFields.FIELD_INT32); 345 if (val != 0) { 346 configDummyIntValue = val; 347 break; 348 } 349 } 350 } 351 configStream.get().end(dummyFieldsToken); 352 break; 353 } 354 } 355 configStream.get().end(forTestingToken); 356 break; 357 } 358 } 359 360 Truth.assertThat(configDummyIntValue).isEqualTo(expectedDummyIntValue); 361 } 362 363 @Test multipleTraceInstances()364 public void multipleTraceInstances() throws IOException, InterruptedException { 365 final int instanceCount = 3; 366 367 final List<TraceMonitor> monitors = new ArrayList<>(); 368 final List<ResultWriter> writers = new ArrayList<>(); 369 370 for (int i = 0; i < instanceCount; i++) { 371 final ResultWriter writer = new ResultWriter() 372 .forScenario(new ScenarioBuilder() 373 .forClass(createTempFile("temp", "").getName()).build()) 374 .withOutputDir(mTracingDirectory) 375 .setRunComplete(); 376 writers.add(writer); 377 } 378 379 // Start at 1 because 0 is considered null value so payload will be ignored in that case 380 TestDataSource.TestTlsState.lastIndex = 1; 381 382 final AtomicInteger traceCallCount = new AtomicInteger(); 383 final CountDownLatch latch = new CountDownLatch(instanceCount); 384 385 try { 386 // Start instances 387 for (int i = 0; i < instanceCount; i++) { 388 final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() 389 .enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder() 390 .setName(sTestDataSource.name).build()).build(); 391 monitors.add(traceMonitor); 392 traceMonitor.start(); 393 } 394 395 // Trace the stateIndex of the tracing instance. 396 sTestDataSource.trace(ctx -> { 397 final int testIntValue = ctx.getCustomTlsState().stateIndex; 398 traceCallCount.incrementAndGet(); 399 400 final ProtoOutputStream os = ctx.newTracePacket(); 401 long forTestingToken = os.start(FOR_TESTING); 402 long payloadToken = os.start(PAYLOAD); 403 os.write(SINGLE_INT, testIntValue); 404 os.end(payloadToken); 405 os.end(forTestingToken); 406 407 latch.countDown(); 408 }); 409 } finally { 410 // Stop instances 411 for (int i = 0; i < instanceCount; i++) { 412 final TraceMonitor monitor = monitors.get(i); 413 final ResultWriter writer = writers.get(i); 414 monitor.stop(writer); 415 } 416 } 417 418 latch.await(3, TimeUnit.SECONDS); 419 Truth.assertThat(traceCallCount.get()).isEqualTo(instanceCount); 420 421 for (int i = 0; i < instanceCount; i++) { 422 final int expectedTracedValue = i + 1; 423 final ResultWriter writer = writers.get(i); 424 final ResultReader reader = new ResultReader(writer.write(), mTraceConfig); 425 final byte[] rawProtoFromFile = reader.readBytes(TraceType.PERFETTO, Tag.ALL); 426 assert rawProtoFromFile != null; 427 final perfetto.protos.TraceOuterClass.Trace trace = 428 perfetto.protos.TraceOuterClass.Trace.parseFrom(rawProtoFromFile); 429 430 Truth.assertThat(trace.getPacketCount()).isGreaterThan(0); 431 final List<TracePacketOuterClass.TracePacket> tracePackets = trace.getPacketList() 432 .stream().filter(TracePacketOuterClass.TracePacket::hasForTesting).toList(); 433 Truth.assertWithMessage("One packet has for testing data") 434 .that(tracePackets).hasSize(1); 435 436 final List<TracePacketOuterClass.TracePacket> matchingPackets = 437 tracePackets.stream() 438 .filter(it -> it.getForTesting().getPayload() 439 .getSingleInt() == expectedTracedValue).toList(); 440 Truth.assertWithMessage( 441 "One packet has testing data with a payload with the expected value") 442 .that(matchingPackets).hasSize(1); 443 } 444 } 445 446 @Test onStartCallbackTriggered()447 public void onStartCallbackTriggered() throws InterruptedException { 448 final CountDownLatch latch = new CountDownLatch(1); 449 450 final AtomicBoolean callbackCalled = new AtomicBoolean(false); 451 sInstanceProvider = (ds, idx, config) -> new TestDataSource.TestDataSourceInstance( 452 ds, 453 idx, 454 (args) -> { 455 callbackCalled.set(true); 456 latch.countDown(); 457 }, 458 (args) -> {}, 459 (args) -> {} 460 ); 461 462 final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() 463 .enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder() 464 .setName(sTestDataSource.name).build()).build(); 465 466 Truth.assertThat(callbackCalled.get()).isFalse(); 467 try { 468 traceMonitor.start(); 469 latch.await(3, TimeUnit.SECONDS); 470 Truth.assertThat(callbackCalled.get()).isTrue(); 471 } finally { 472 traceMonitor.stop(mWriter); 473 } 474 } 475 476 @Test onFlushCallbackTriggered()477 public void onFlushCallbackTriggered() throws InterruptedException { 478 final CountDownLatch latch = new CountDownLatch(1); 479 final AtomicBoolean callbackCalled = new AtomicBoolean(false); 480 sInstanceProvider = (ds, idx, config) -> 481 new TestDataSource.TestDataSourceInstance( 482 ds, 483 idx, 484 (args) -> {}, 485 (args) -> { 486 callbackCalled.set(true); 487 latch.countDown(); 488 }, 489 (args) -> {} 490 ); 491 492 final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() 493 .enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder() 494 .setName(sTestDataSource.name).build()).build(); 495 496 try { 497 traceMonitor.start(); 498 Truth.assertThat(callbackCalled.get()).isFalse(); 499 sTestDataSource.trace((ctx) -> { 500 final ProtoOutputStream protoOutputStream = ctx.newTracePacket(); 501 long forTestingToken = protoOutputStream.start(FOR_TESTING); 502 long payloadToken = protoOutputStream.start(PAYLOAD); 503 protoOutputStream.write(SINGLE_INT, 10); 504 protoOutputStream.end(payloadToken); 505 protoOutputStream.end(forTestingToken); 506 }); 507 } finally { 508 traceMonitor.stop(mWriter); 509 } 510 511 latch.await(3, TimeUnit.SECONDS); 512 Truth.assertThat(callbackCalled.get()).isTrue(); 513 } 514 515 @Test onStopCallbackTriggered()516 public void onStopCallbackTriggered() throws InterruptedException { 517 final CountDownLatch latch = new CountDownLatch(1); 518 final AtomicBoolean callbackCalled = new AtomicBoolean(false); 519 sInstanceProvider = (ds, idx, config) -> 520 new TestDataSource.TestDataSourceInstance( 521 ds, 522 idx, 523 (args) -> {}, 524 (args) -> {}, 525 (args) -> { 526 callbackCalled.set(true); 527 latch.countDown(); 528 } 529 ); 530 531 final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() 532 .enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder() 533 .setName(sTestDataSource.name).build()).build(); 534 535 try { 536 traceMonitor.start(); 537 Truth.assertThat(callbackCalled.get()).isFalse(); 538 } finally { 539 traceMonitor.stop(mWriter); 540 } 541 542 latch.await(3, TimeUnit.SECONDS); 543 Truth.assertThat(callbackCalled.get()).isTrue(); 544 } 545 546 @Test canUseDataSourceInstanceToCreateTlsState()547 public void canUseDataSourceInstanceToCreateTlsState() throws InvalidProtocolBufferException { 548 final Object testObject = new Object(); 549 550 sInstanceProvider = (ds, idx, configStream) -> { 551 final TestDataSource.TestDataSourceInstance dsInstance = 552 new TestDataSource.TestDataSourceInstance(ds, idx); 553 dsInstance.testObject = testObject; 554 return dsInstance; 555 }; 556 557 sTlsStateProvider = args -> { 558 final TestDataSource.TestTlsState tlsState = new TestDataSource.TestTlsState(); 559 560 try (TestDataSource.TestDataSourceInstance dataSourceInstance = 561 args.getDataSourceInstanceLocked()) { 562 if (dataSourceInstance != null) { 563 tlsState.testStateValue = dataSourceInstance.testObject.hashCode(); 564 } 565 } 566 567 return tlsState; 568 }; 569 570 final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() 571 .enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder() 572 .setName(sTestDataSource.name).build()).build(); 573 574 try { 575 traceMonitor.start(); 576 sTestDataSource.trace((ctx) -> { 577 final ProtoOutputStream protoOutputStream = ctx.newTracePacket(); 578 long forTestingToken = protoOutputStream.start(FOR_TESTING); 579 long payloadToken = protoOutputStream.start(PAYLOAD); 580 protoOutputStream.write(SINGLE_INT, ctx.getCustomTlsState().testStateValue); 581 protoOutputStream.end(payloadToken); 582 protoOutputStream.end(forTestingToken); 583 }); 584 } finally { 585 traceMonitor.stop(mWriter); 586 } 587 588 final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); 589 final byte[] rawProtoFromFile = reader.readBytes(TraceType.PERFETTO, Tag.ALL); 590 assert rawProtoFromFile != null; 591 final perfetto.protos.TraceOuterClass.Trace trace = perfetto.protos.TraceOuterClass.Trace 592 .parseFrom(rawProtoFromFile); 593 594 Truth.assertThat(trace.getPacketCount()).isGreaterThan(0); 595 final List<TracePacketOuterClass.TracePacket> tracePackets = trace.getPacketList() 596 .stream().filter(TracePacketOuterClass.TracePacket::hasForTesting).toList(); 597 final List<TracePacketOuterClass.TracePacket> matchingPackets = tracePackets.stream() 598 .filter(it -> it.getForTesting().getPayload().getSingleInt() 599 == testObject.hashCode()).toList(); 600 Truth.assertThat(matchingPackets).hasSize(1); 601 } 602 603 @Test canUseDataSourceInstanceToCreateIncrementalState()604 public void canUseDataSourceInstanceToCreateIncrementalState() 605 throws InvalidProtocolBufferException { 606 final Object testObject = new Object(); 607 608 sInstanceProvider = (ds, idx, configStream) -> { 609 final TestDataSource.TestDataSourceInstance dsInstance = 610 new TestDataSource.TestDataSourceInstance(ds, idx); 611 dsInstance.testObject = testObject; 612 return dsInstance; 613 }; 614 615 sIncrementalStateProvider = args -> { 616 final TestDataSource.TestIncrementalState incrementalState = 617 new TestDataSource.TestIncrementalState(); 618 619 try (TestDataSource.TestDataSourceInstance dataSourceInstance = 620 args.getDataSourceInstanceLocked()) { 621 if (dataSourceInstance != null) { 622 incrementalState.testStateValue = dataSourceInstance.testObject.hashCode(); 623 } 624 } 625 626 return incrementalState; 627 }; 628 629 final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() 630 .enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder() 631 .setName(sTestDataSource.name).build()).build(); 632 633 try { 634 traceMonitor.start(); 635 sTestDataSource.trace((ctx) -> { 636 final ProtoOutputStream protoOutputStream = ctx.newTracePacket(); 637 long forTestingToken = protoOutputStream.start(FOR_TESTING); 638 long payloadToken = protoOutputStream.start(PAYLOAD); 639 protoOutputStream.write(SINGLE_INT, ctx.getIncrementalState().testStateValue); 640 protoOutputStream.end(payloadToken); 641 protoOutputStream.end(forTestingToken); 642 }); 643 } finally { 644 traceMonitor.stop(mWriter); 645 } 646 647 final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); 648 final byte[] rawProtoFromFile = reader.readBytes(TraceType.PERFETTO, Tag.ALL); 649 assert rawProtoFromFile != null; 650 final perfetto.protos.TraceOuterClass.Trace trace = perfetto.protos.TraceOuterClass.Trace 651 .parseFrom(rawProtoFromFile); 652 653 Truth.assertThat(trace.getPacketCount()).isGreaterThan(0); 654 final List<TracePacketOuterClass.TracePacket> tracePackets = trace.getPacketList() 655 .stream().filter(TracePacketOuterClass.TracePacket::hasForTesting).toList(); 656 final List<TracePacketOuterClass.TracePacket> matchingPackets = tracePackets.stream() 657 .filter(it -> it.getForTesting().getPayload().getSingleInt() 658 == testObject.hashCode()).toList(); 659 Truth.assertThat(matchingPackets).hasSize(1); 660 } 661 662 @Test canTraceOnFlush()663 public void canTraceOnFlush() throws InvalidProtocolBufferException, InterruptedException { 664 final int singleIntValue = 101; 665 sInstanceProvider = (ds, idx, config) -> 666 new TestDataSource.TestDataSourceInstance( 667 ds, 668 idx, 669 (args) -> {}, 670 (args) -> sTestDataSource.trace(ctx -> { 671 final ProtoOutputStream protoOutputStream = ctx.newTracePacket(); 672 long forTestingToken = protoOutputStream.start(FOR_TESTING); 673 long payloadToken = protoOutputStream.start(PAYLOAD); 674 protoOutputStream.write(SINGLE_INT, singleIntValue); 675 protoOutputStream.end(payloadToken); 676 protoOutputStream.end(forTestingToken); 677 }), 678 (args) -> {} 679 ); 680 681 final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() 682 .enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder() 683 .setName(sTestDataSource.name).build()).build(); 684 685 try { 686 traceMonitor.start(); 687 } finally { 688 traceMonitor.stop(mWriter); 689 } 690 691 final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); 692 final byte[] rawProtoFromFile = reader.readBytes(TraceType.PERFETTO, Tag.ALL); 693 assert rawProtoFromFile != null; 694 final perfetto.protos.TraceOuterClass.Trace trace = perfetto.protos.TraceOuterClass.Trace 695 .parseFrom(rawProtoFromFile); 696 697 Truth.assertThat(trace.getPacketCount()).isGreaterThan(0); 698 final List<TracePacketOuterClass.TracePacket> tracePackets = trace.getPacketList() 699 .stream().filter(TracePacketOuterClass.TracePacket::hasForTesting).toList(); 700 final List<TracePacketOuterClass.TracePacket> matchingPackets = tracePackets.stream() 701 .filter(it -> it.getForTesting().getPayload().getSingleInt() 702 == singleIntValue).toList(); 703 Truth.assertThat(matchingPackets).hasSize(1); 704 } 705 706 interface RunnableCreator { create(int state, AtomicInteger stateOut)707 Runnable create(int state, AtomicInteger stateOut); 708 } 709 } 710