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.os; 18 19 import static android.os.PerfettoTrace.Category; 20 21 import static com.google.common.truth.Truth.assertThat; 22 23 import static perfetto.protos.ChromeLatencyInfoOuterClass.ChromeLatencyInfo.LatencyComponentType.COMPONENT_INPUT_EVENT_LATENCY_BEGIN_RWH; 24 import static perfetto.protos.ChromeLatencyInfoOuterClass.ChromeLatencyInfo.LatencyComponentType.COMPONENT_INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL; 25 26 import android.platform.test.annotations.DisabledOnRavenwood; 27 import android.platform.test.annotations.RequiresFlagsEnabled; 28 import android.platform.test.flag.junit.CheckFlagsRule; 29 import android.platform.test.flag.junit.DeviceFlagsValueProvider; 30 import android.util.ArraySet; 31 32 import androidx.test.InstrumentationRegistry; 33 import androidx.test.ext.junit.runners.AndroidJUnit4; 34 35 import org.junit.Before; 36 import org.junit.Ignore; 37 import org.junit.Rule; 38 import org.junit.Test; 39 import org.junit.runner.RunWith; 40 41 import perfetto.protos.ChromeLatencyInfoOuterClass.ChromeLatencyInfo; 42 import perfetto.protos.ChromeLatencyInfoOuterClass.ChromeLatencyInfo.ComponentInfo; 43 import perfetto.protos.DataSourceConfigOuterClass.DataSourceConfig; 44 import perfetto.protos.DebugAnnotationOuterClass.DebugAnnotation; 45 import perfetto.protos.DebugAnnotationOuterClass.DebugAnnotationName; 46 import perfetto.protos.InternedDataOuterClass.InternedData; 47 import perfetto.protos.SourceLocationOuterClass.SourceLocation; 48 import perfetto.protos.TraceConfigOuterClass.TraceConfig; 49 import perfetto.protos.TraceConfigOuterClass.TraceConfig.BufferConfig; 50 import perfetto.protos.TraceConfigOuterClass.TraceConfig.DataSource; 51 import perfetto.protos.TraceConfigOuterClass.TraceConfig.TriggerConfig; 52 import perfetto.protos.TraceConfigOuterClass.TraceConfig.TriggerConfig.Trigger; 53 import perfetto.protos.TraceOuterClass.Trace; 54 import perfetto.protos.TracePacketOuterClass.TracePacket; 55 import perfetto.protos.TrackDescriptorOuterClass.TrackDescriptor; 56 import perfetto.protos.TrackEventConfigOuterClass.TrackEventConfig; 57 import perfetto.protos.TrackEventOuterClass.EventCategory; 58 import perfetto.protos.TrackEventOuterClass.EventName; 59 import perfetto.protos.TrackEventOuterClass.TrackEvent; 60 61 import java.util.List; 62 import java.util.Set; 63 import java.util.concurrent.CountDownLatch; 64 import java.util.concurrent.TimeUnit; 65 66 /** 67 * This class is used to test the native tracing support. Run this test 68 * while tracing on the emulator and then run traceview to view the trace. 69 */ 70 @RunWith(AndroidJUnit4.class) 71 @DisabledOnRavenwood(blockedBy = PerfettoTrace.class) 72 public class PerfettoTraceTest { 73 @Rule 74 public final CheckFlagsRule mCheckFlagsRule = 75 DeviceFlagsValueProvider.createCheckFlagsRule( 76 InstrumentationRegistry.getInstrumentation().getUiAutomation()); 77 78 private static final String TAG = "PerfettoTraceTest"; 79 private static final String FOO = "foo"; 80 private static final String BAR = "bar"; 81 private static final String TEXT_ABOVE_4K_SIZE = 82 new String(new char[8192]).replace('\0', 'a'); 83 84 private static final Category FOO_CATEGORY = new Category(FOO); 85 private static final int MESSAGE = 1234567; 86 private static final int MESSAGE_COUNT = 3; 87 88 private final Set<String> mCategoryNames = new ArraySet<>(); 89 private final Set<String> mEventNames = new ArraySet<>(); 90 private final Set<String> mDebugAnnotationNames = new ArraySet<>(); 91 private final Set<String> mTrackNames = new ArraySet<>(); 92 93 @Before setUp()94 public void setUp() { 95 PerfettoTrace.register(true); 96 FOO_CATEGORY.register(); 97 98 mCategoryNames.clear(); 99 mEventNames.clear(); 100 mDebugAnnotationNames.clear(); 101 mTrackNames.clear(); 102 } 103 104 @Test 105 @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2) testDebugAnnotations()106 public void testDebugAnnotations() throws Exception { 107 TraceConfig traceConfig = getTraceConfig(FOO); 108 109 PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray()); 110 111 PerfettoTrace.instant(FOO_CATEGORY, "event") 112 .setFlow(2) 113 .setTerminatingFlow(3) 114 .addArg("long_val", 10000000000L) 115 .addArg("bool_val", true) 116 .addArg("double_val", 3.14) 117 .addArg("string_val", FOO) 118 .emit(); 119 120 byte[] traceBytes = session.close(); 121 122 Trace trace = Trace.parseFrom(traceBytes); 123 124 boolean hasTrackEvent = false; 125 boolean hasDebugAnnotations = false; 126 for (TracePacket packet: trace.getPacketList()) { 127 TrackEvent event; 128 if (packet.hasTrackEvent()) { 129 hasTrackEvent = true; 130 event = packet.getTrackEvent(); 131 132 if (TrackEvent.Type.TYPE_INSTANT.equals(event.getType()) 133 && event.getDebugAnnotationsCount() == 4 && event.getFlowIdsCount() == 1 134 && event.getTerminatingFlowIdsCount() == 1) { 135 hasDebugAnnotations = true; 136 137 List<DebugAnnotation> annotations = event.getDebugAnnotationsList(); 138 139 assertThat(annotations.get(0).getIntValue()).isEqualTo(10000000000L); 140 assertThat(annotations.get(1).getBoolValue()).isTrue(); 141 assertThat(annotations.get(2).getDoubleValue()).isEqualTo(3.14); 142 assertThat(annotations.get(3).getStringValue()).isEqualTo(FOO); 143 } 144 } 145 146 collectInternedData(packet); 147 } 148 149 assertThat(hasTrackEvent).isTrue(); 150 assertThat(hasDebugAnnotations).isTrue(); 151 assertThat(mCategoryNames).contains(FOO); 152 153 assertThat(mDebugAnnotationNames).contains("long_val"); 154 assertThat(mDebugAnnotationNames).contains("bool_val"); 155 assertThat(mDebugAnnotationNames).contains("double_val"); 156 assertThat(mDebugAnnotationNames).contains("string_val"); 157 } 158 159 @Test 160 @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2) testNamedTrack()161 public void testNamedTrack() throws Exception { 162 TraceConfig traceConfig = getTraceConfig(FOO); 163 164 PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray()); 165 166 PerfettoTrace.begin(FOO_CATEGORY, "event") 167 .usingNamedTrack(PerfettoTrace.getProcessTrackUuid(), FOO) 168 .emit(); 169 170 171 PerfettoTrace.end(FOO_CATEGORY) 172 .usingNamedTrack(PerfettoTrace.getThreadTrackUuid(Process.myTid()), "bar") 173 .emit(); 174 175 Trace trace = Trace.parseFrom(session.close()); 176 177 boolean hasTrackEvent = false; 178 boolean hasTrackUuid = false; 179 for (TracePacket packet: trace.getPacketList()) { 180 TrackEvent event; 181 if (packet.hasTrackEvent()) { 182 hasTrackEvent = true; 183 event = packet.getTrackEvent(); 184 185 if (TrackEvent.Type.TYPE_SLICE_BEGIN.equals(event.getType()) 186 && event.hasTrackUuid()) { 187 hasTrackUuid = true; 188 } 189 190 if (TrackEvent.Type.TYPE_SLICE_END.equals(event.getType()) 191 && event.hasTrackUuid()) { 192 hasTrackUuid &= true; 193 } 194 } 195 196 collectInternedData(packet); 197 collectTrackNames(packet); 198 } 199 200 assertThat(hasTrackEvent).isTrue(); 201 assertThat(hasTrackUuid).isTrue(); 202 assertThat(mCategoryNames).contains(FOO); 203 assertThat(mTrackNames).contains(FOO); 204 assertThat(mTrackNames).contains("bar"); 205 } 206 207 @Test 208 @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2) testProcessThreadNamedTrack()209 public void testProcessThreadNamedTrack() throws Exception { 210 TraceConfig traceConfig = getTraceConfig(FOO); 211 212 PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray()); 213 214 PerfettoTrace.begin(FOO_CATEGORY, "event") 215 .usingProcessNamedTrack(FOO) 216 .emit(); 217 218 219 PerfettoTrace.end(FOO_CATEGORY) 220 .usingThreadNamedTrack(Process.myTid(), "bar") 221 .emit(); 222 223 Trace trace = Trace.parseFrom(session.close()); 224 225 boolean hasTrackEvent = false; 226 boolean hasTrackUuid = false; 227 for (TracePacket packet: trace.getPacketList()) { 228 TrackEvent event; 229 if (packet.hasTrackEvent()) { 230 hasTrackEvent = true; 231 event = packet.getTrackEvent(); 232 233 if (TrackEvent.Type.TYPE_SLICE_BEGIN.equals(event.getType()) 234 && event.hasTrackUuid()) { 235 hasTrackUuid = true; 236 } 237 238 if (TrackEvent.Type.TYPE_SLICE_END.equals(event.getType()) 239 && event.hasTrackUuid()) { 240 hasTrackUuid &= true; 241 } 242 } 243 244 collectInternedData(packet); 245 collectTrackNames(packet); 246 } 247 248 assertThat(hasTrackEvent).isTrue(); 249 assertThat(hasTrackUuid).isTrue(); 250 assertThat(mCategoryNames).contains(FOO); 251 assertThat(mTrackNames).contains(FOO); 252 assertThat(mTrackNames).contains("bar"); 253 } 254 255 @Test 256 @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2) testCounterSimple()257 public void testCounterSimple() throws Exception { 258 TraceConfig traceConfig = getTraceConfig(FOO); 259 260 PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray()); 261 262 PerfettoTrace.counter(FOO_CATEGORY, 16, FOO).emit(); 263 264 PerfettoTrace.counter(FOO_CATEGORY, 3.14, "bar").emit(); 265 266 Trace trace = Trace.parseFrom(session.close()); 267 268 boolean hasTrackEvent = false; 269 boolean hasCounterValue = false; 270 boolean hasDoubleCounterValue = false; 271 for (TracePacket packet: trace.getPacketList()) { 272 TrackEvent event; 273 if (packet.hasTrackEvent()) { 274 hasTrackEvent = true; 275 event = packet.getTrackEvent(); 276 277 if (TrackEvent.Type.TYPE_COUNTER.equals(event.getType()) 278 && event.getCounterValue() == 16) { 279 hasCounterValue = true; 280 } 281 282 if (TrackEvent.Type.TYPE_COUNTER.equals(event.getType()) 283 && event.getDoubleCounterValue() == 3.14) { 284 hasDoubleCounterValue = true; 285 } 286 } 287 288 collectTrackNames(packet); 289 } 290 291 assertThat(hasTrackEvent).isTrue(); 292 assertThat(hasCounterValue).isTrue(); 293 assertThat(hasDoubleCounterValue).isTrue(); 294 assertThat(mTrackNames).contains(FOO); 295 assertThat(mTrackNames).contains(BAR); 296 } 297 298 @Test 299 @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2) testCounter()300 public void testCounter() throws Exception { 301 TraceConfig traceConfig = getTraceConfig(FOO); 302 303 PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray()); 304 305 PerfettoTrace.counter(FOO_CATEGORY, 16) 306 .usingCounterTrack(PerfettoTrace.getProcessTrackUuid(), FOO).emit(); 307 308 PerfettoTrace.counter(FOO_CATEGORY, 3.14) 309 .usingCounterTrack(PerfettoTrace.getThreadTrackUuid(Process.myTid()), 310 "bar").emit(); 311 312 Trace trace = Trace.parseFrom(session.close()); 313 314 boolean hasTrackEvent = false; 315 boolean hasCounterValue = false; 316 boolean hasDoubleCounterValue = false; 317 for (TracePacket packet: trace.getPacketList()) { 318 TrackEvent event; 319 if (packet.hasTrackEvent()) { 320 hasTrackEvent = true; 321 event = packet.getTrackEvent(); 322 323 if (TrackEvent.Type.TYPE_COUNTER.equals(event.getType()) 324 && event.getCounterValue() == 16) { 325 hasCounterValue = true; 326 } 327 328 if (TrackEvent.Type.TYPE_COUNTER.equals(event.getType()) 329 && event.getDoubleCounterValue() == 3.14) { 330 hasDoubleCounterValue = true; 331 } 332 } 333 334 collectTrackNames(packet); 335 } 336 337 assertThat(hasTrackEvent).isTrue(); 338 assertThat(hasCounterValue).isTrue(); 339 assertThat(hasDoubleCounterValue).isTrue(); 340 assertThat(mTrackNames).contains(FOO); 341 assertThat(mTrackNames).contains("bar"); 342 } 343 344 @Test 345 @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2) testProcessThreadCounter()346 public void testProcessThreadCounter() throws Exception { 347 TraceConfig traceConfig = getTraceConfig(FOO); 348 349 PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray()); 350 351 PerfettoTrace.counter(FOO_CATEGORY, 16).usingProcessCounterTrack(FOO).emit(); 352 353 PerfettoTrace.counter(FOO_CATEGORY, 3.14) 354 .usingThreadCounterTrack(Process.myTid(), "bar").emit(); 355 356 Trace trace = Trace.parseFrom(session.close()); 357 358 boolean hasTrackEvent = false; 359 boolean hasCounterValue = false; 360 boolean hasDoubleCounterValue = false; 361 for (TracePacket packet: trace.getPacketList()) { 362 TrackEvent event; 363 if (packet.hasTrackEvent()) { 364 hasTrackEvent = true; 365 event = packet.getTrackEvent(); 366 367 if (TrackEvent.Type.TYPE_COUNTER.equals(event.getType()) 368 && event.getCounterValue() == 16) { 369 hasCounterValue = true; 370 } 371 372 if (TrackEvent.Type.TYPE_COUNTER.equals(event.getType()) 373 && event.getDoubleCounterValue() == 3.14) { 374 hasDoubleCounterValue = true; 375 } 376 } 377 378 collectTrackNames(packet); 379 } 380 381 assertThat(hasTrackEvent).isTrue(); 382 assertThat(hasCounterValue).isTrue(); 383 assertThat(hasDoubleCounterValue).isTrue(); 384 assertThat(mTrackNames).contains(FOO); 385 assertThat(mTrackNames).contains("bar"); 386 } 387 388 @Test 389 @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2) testProto()390 public void testProto() throws Exception { 391 TraceConfig traceConfig = getTraceConfig(FOO); 392 393 PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray()); 394 395 PerfettoTrace.instant(FOO_CATEGORY, "event_proto") 396 .beginProto() 397 .beginNested(33L) 398 .addField(4L, 2L) 399 .addField(3, "ActivityManagerService.java:11489") 400 .endNested() 401 .addField(2001, "AIDL::IActivityManager") 402 .endProto() 403 .emit(); 404 405 byte[] traceBytes = session.close(); 406 407 Trace trace = Trace.parseFrom(traceBytes); 408 409 boolean hasTrackEvent = false; 410 boolean hasSourceLocation = false; 411 for (TracePacket packet: trace.getPacketList()) { 412 TrackEvent event; 413 if (packet.hasTrackEvent()) { 414 hasTrackEvent = true; 415 event = packet.getTrackEvent(); 416 417 if (TrackEvent.Type.TYPE_INSTANT.equals(event.getType()) 418 && event.hasSourceLocation()) { 419 SourceLocation loc = event.getSourceLocation(); 420 if ("ActivityManagerService.java:11489".equals(loc.getFunctionName()) 421 && loc.getLineNumber() == 2) { 422 hasSourceLocation = true; 423 } 424 } 425 } 426 427 collectInternedData(packet); 428 } 429 430 assertThat(hasTrackEvent).isTrue(); 431 assertThat(hasSourceLocation).isTrue(); 432 assertThat(mCategoryNames).contains(FOO); 433 } 434 435 @Test 436 @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2) testProtoWithSlowPath()437 public void testProtoWithSlowPath() throws Exception { 438 TraceConfig traceConfig = getTraceConfig(FOO); 439 440 PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray()); 441 442 PerfettoTrace.instant(FOO_CATEGORY, "event_proto") 443 .beginProto() 444 .beginNested(33L) 445 .addField(4L, 2L) 446 .addField(3, TEXT_ABOVE_4K_SIZE) 447 .endNested() 448 .addField(2001, "AIDL::IActivityManager") 449 .endProto() 450 .emit(); 451 452 byte[] traceBytes = session.close(); 453 454 Trace trace = Trace.parseFrom(traceBytes); 455 456 boolean hasTrackEvent = false; 457 boolean hasSourceLocation = false; 458 for (TracePacket packet: trace.getPacketList()) { 459 TrackEvent event; 460 if (packet.hasTrackEvent()) { 461 hasTrackEvent = true; 462 event = packet.getTrackEvent(); 463 464 if (TrackEvent.Type.TYPE_INSTANT.equals(event.getType()) 465 && event.hasSourceLocation()) { 466 SourceLocation loc = event.getSourceLocation(); 467 if (TEXT_ABOVE_4K_SIZE.equals(loc.getFunctionName()) 468 && loc.getLineNumber() == 2) { 469 hasSourceLocation = true; 470 } 471 } 472 } 473 474 collectInternedData(packet); 475 } 476 477 assertThat(hasTrackEvent).isTrue(); 478 assertThat(hasSourceLocation).isTrue(); 479 assertThat(mCategoryNames).contains(FOO); 480 } 481 482 @Test 483 @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2) testProtoNested()484 public void testProtoNested() throws Exception { 485 TraceConfig traceConfig = getTraceConfig(FOO); 486 487 PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray()); 488 489 PerfettoTrace.instant(FOO_CATEGORY, "event_proto_nested") 490 .beginProto() 491 .beginNested(29L) 492 .beginNested(4L) 493 .addField(1L, 2) 494 .addField(2L, 20000) 495 .endNested() 496 .beginNested(4L) 497 .addField(1L, 1) 498 .addField(2L, 40000) 499 .endNested() 500 .endNested() 501 .endProto() 502 .emit(); 503 504 byte[] traceBytes = session.close(); 505 506 Trace trace = Trace.parseFrom(traceBytes); 507 508 boolean hasTrackEvent = false; 509 boolean hasChromeLatencyInfo = false; 510 511 for (TracePacket packet: trace.getPacketList()) { 512 TrackEvent event; 513 if (packet.hasTrackEvent()) { 514 hasTrackEvent = true; 515 event = packet.getTrackEvent(); 516 517 if (TrackEvent.Type.TYPE_INSTANT.equals(event.getType()) 518 && event.hasChromeLatencyInfo()) { 519 ChromeLatencyInfo latencyInfo = event.getChromeLatencyInfo(); 520 if (latencyInfo.getComponentInfoCount() == 2) { 521 hasChromeLatencyInfo = true; 522 ComponentInfo cmpInfo1 = latencyInfo.getComponentInfo(0); 523 assertThat(cmpInfo1.getComponentType()) 524 .isEqualTo(COMPONENT_INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL); 525 assertThat(cmpInfo1.getTimeUs()).isEqualTo(20000); 526 527 ComponentInfo cmpInfo2 = latencyInfo.getComponentInfo(1); 528 assertThat(cmpInfo2.getComponentType()) 529 .isEqualTo(COMPONENT_INPUT_EVENT_LATENCY_BEGIN_RWH); 530 assertThat(cmpInfo2.getTimeUs()).isEqualTo(40000); 531 } 532 } 533 } 534 535 collectInternedData(packet); 536 } 537 538 assertThat(hasTrackEvent).isTrue(); 539 assertThat(hasChromeLatencyInfo).isTrue(); 540 assertThat(mCategoryNames).contains(FOO); 541 } 542 543 @Test 544 @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2) testActivateTrigger()545 public void testActivateTrigger() throws Exception { 546 TraceConfig traceConfig = getTriggerTraceConfig(FOO, FOO); 547 548 PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray()); 549 550 PerfettoTrace.instant(FOO_CATEGORY, "event_trigger").emit(); 551 552 PerfettoTrace.activateTrigger(FOO, 1000); 553 554 byte[] traceBytes = session.close(); 555 556 Trace trace = Trace.parseFrom(traceBytes); 557 558 boolean hasTrackEvent = false; 559 boolean hasChromeLatencyInfo = false; 560 561 for (TracePacket packet: trace.getPacketList()) { 562 TrackEvent event; 563 if (packet.hasTrackEvent()) { 564 hasTrackEvent = true; 565 } 566 567 collectInternedData(packet); 568 } 569 570 assertThat(mCategoryNames).contains(FOO); 571 } 572 573 @Test 574 @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2) testRegister()575 public void testRegister() throws Exception { 576 TraceConfig traceConfig = getTraceConfig(BAR); 577 578 Category barCategory = new Category(BAR); 579 PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray()); 580 581 PerfettoTrace.instant(barCategory, "event") 582 .addArg("before", 1) 583 .emit(); 584 585 barCategory.register(); 586 587 PerfettoTrace.instant(barCategory, "event") 588 .addArg("after", 1) 589 .emit(); 590 591 byte[] traceBytes = session.close(); 592 593 Trace trace = Trace.parseFrom(traceBytes); 594 595 boolean hasTrackEvent = false; 596 for (TracePacket packet: trace.getPacketList()) { 597 TrackEvent event; 598 if (packet.hasTrackEvent()) { 599 hasTrackEvent = true; 600 event = packet.getTrackEvent(); 601 } 602 603 collectInternedData(packet); 604 } 605 606 assertThat(hasTrackEvent).isTrue(); 607 assertThat(mCategoryNames).contains(BAR); 608 609 assertThat(mDebugAnnotationNames).contains("after"); 610 assertThat(mDebugAnnotationNames).doesNotContain("before"); 611 } 612 613 @Test 614 @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2) 615 @Ignore("b/303199244") testMessageQueue()616 public void testMessageQueue() throws Exception { 617 TraceConfig traceConfig = getTraceConfig("mq"); 618 619 PerfettoTrace.MQ_CATEGORY.register(); 620 final HandlerThread thread = new HandlerThread("test"); 621 thread.start(); 622 final Handler handler = thread.getThreadHandler(); 623 final CountDownLatch latch = new CountDownLatch(1); 624 625 PerfettoTrace.Session session = new PerfettoTrace.Session(true, 626 getTraceConfig("mq").toByteArray()); 627 628 handler.sendEmptyMessage(MESSAGE); 629 handler.sendEmptyMessageDelayed(MESSAGE, 10); 630 handler.sendEmptyMessage(MESSAGE); 631 handler.postDelayed(() -> { 632 latch.countDown(); 633 }, 10); 634 assertThat(latch.await(100, TimeUnit.MILLISECONDS)).isTrue(); 635 636 byte[] traceBytes = session.close(); 637 638 Trace trace = Trace.parseFrom(traceBytes); 639 640 boolean hasTrackEvent = false; 641 int instantCount = 0; 642 int counterCount = 0; 643 int beginCount = 0; 644 int endCount = 0; 645 646 Set<Long> flowIds = new ArraySet<>(); 647 for (TracePacket packet: trace.getPacketList()) { 648 TrackEvent event; 649 if (packet.hasTrackEvent()) { 650 hasTrackEvent = true; 651 event = packet.getTrackEvent(); 652 } else { 653 continue; 654 } 655 656 List<DebugAnnotation> annotations = event.getDebugAnnotationsList(); 657 switch (event.getType()) { 658 case TrackEvent.Type.TYPE_INSTANT: 659 if (annotations.get(2).getIntValue() == MESSAGE) { 660 instantCount++; 661 assertThat(annotations.get(0).getStringValue()).isEqualTo("test"); 662 assertThat(event.getFlowIdsCount()).isEqualTo(1); 663 flowIds.addAll(event.getFlowIdsList()); 664 } 665 break; 666 case TrackEvent.Type.TYPE_COUNTER: 667 counterCount++; 668 break; 669 case TrackEvent.Type.TYPE_SLICE_BEGIN: 670 annotations = event.getDebugAnnotationsList(); 671 if (flowIds.containsAll(event.getTerminatingFlowIdsList())) { 672 beginCount++; 673 assertThat(event.getTerminatingFlowIdsCount()).isEqualTo(1); 674 } 675 break; 676 case TrackEvent.Type.TYPE_SLICE_END: 677 endCount++; 678 break; 679 default: 680 break; 681 } 682 collectInternedData(packet); 683 } 684 685 assertThat(hasTrackEvent).isTrue(); 686 assertThat(mCategoryNames).contains("mq"); 687 assertThat(mEventNames).contains("message_queue_send"); 688 assertThat(mEventNames).contains("message_queue_receive"); 689 assertThat(mDebugAnnotationNames).contains("what"); 690 assertThat(mDebugAnnotationNames).contains("delay"); 691 assertThat(instantCount).isEqualTo(MESSAGE_COUNT); 692 assertThat(beginCount).isEqualTo(MESSAGE_COUNT); 693 assertThat(endCount).isAtLeast(MESSAGE_COUNT); 694 assertThat(counterCount).isAtLeast(MESSAGE_COUNT); 695 } 696 getTrackEvent(Trace trace, int idx)697 private TrackEvent getTrackEvent(Trace trace, int idx) { 698 int curIdx = 0; 699 for (TracePacket packet: trace.getPacketList()) { 700 if (packet.hasTrackEvent()) { 701 if (curIdx++ == idx) { 702 return packet.getTrackEvent(); 703 } 704 } 705 } 706 707 return null; 708 } 709 getTraceConfig(String cat)710 private TraceConfig getTraceConfig(String cat) { 711 BufferConfig bufferConfig = BufferConfig.newBuilder().setSizeKb(1024).build(); 712 TrackEventConfig trackEventConfig = TrackEventConfig 713 .newBuilder() 714 .addEnabledCategories(cat) 715 .build(); 716 DataSourceConfig dsConfig = DataSourceConfig 717 .newBuilder() 718 .setName("track_event") 719 .setTargetBuffer(0) 720 .setTrackEventConfig(trackEventConfig) 721 .build(); 722 DataSource ds = DataSource.newBuilder().setConfig(dsConfig).build(); 723 TraceConfig traceConfig = TraceConfig 724 .newBuilder() 725 .addBuffers(bufferConfig) 726 .addDataSources(ds) 727 .build(); 728 return traceConfig; 729 } 730 getTriggerTraceConfig(String cat, String triggerName)731 private TraceConfig getTriggerTraceConfig(String cat, String triggerName) { 732 BufferConfig bufferConfig = BufferConfig.newBuilder().setSizeKb(1024).build(); 733 TrackEventConfig trackEventConfig = TrackEventConfig 734 .newBuilder() 735 .addEnabledCategories(cat) 736 .build(); 737 DataSourceConfig dsConfig = DataSourceConfig 738 .newBuilder() 739 .setName("track_event") 740 .setTargetBuffer(0) 741 .setTrackEventConfig(trackEventConfig) 742 .build(); 743 DataSource ds = DataSource.newBuilder().setConfig(dsConfig).build(); 744 Trigger trigger = Trigger.newBuilder().setName(triggerName).build(); 745 TriggerConfig triggerConfig = TriggerConfig 746 .newBuilder() 747 .setTriggerMode(TriggerConfig.TriggerMode.STOP_TRACING) 748 .setTriggerTimeoutMs(1000) 749 .addTriggers(trigger) 750 .build(); 751 TraceConfig traceConfig = TraceConfig 752 .newBuilder() 753 .addBuffers(bufferConfig) 754 .addDataSources(ds) 755 .setTriggerConfig(triggerConfig) 756 .build(); 757 return traceConfig; 758 } 759 collectInternedData(TracePacket packet)760 private void collectInternedData(TracePacket packet) { 761 if (!packet.hasInternedData()) { 762 return; 763 } 764 765 InternedData data = packet.getInternedData(); 766 767 for (EventCategory cat : data.getEventCategoriesList()) { 768 mCategoryNames.add(cat.getName()); 769 } 770 for (EventName ev : data.getEventNamesList()) { 771 mEventNames.add(ev.getName()); 772 } 773 for (DebugAnnotationName dbg : data.getDebugAnnotationNamesList()) { 774 mDebugAnnotationNames.add(dbg.getName()); 775 } 776 } 777 collectTrackNames(TracePacket packet)778 private void collectTrackNames(TracePacket packet) { 779 if (!packet.hasTrackDescriptor()) { 780 return; 781 } 782 TrackDescriptor desc = packet.getTrackDescriptor(); 783 mTrackNames.add(desc.getName()); 784 } 785 } 786