Merge "ui: Add command & hotkey (Q) to control drawer visibility" into main
diff --git a/Android.bp b/Android.bp
index dec60eb..698fe51 100644
--- a/Android.bp
+++ b/Android.bp
@@ -10718,6 +10718,8 @@
         "src/protozero/test/example_proto/extensions.proto",
         "src/protozero/test/example_proto/library.proto",
         "src/protozero/test/example_proto/library_internals/galaxies.proto",
+        "src/protozero/test/example_proto/other_package/test_messages.proto",
+        "src/protozero/test/example_proto/subpackage/test_messages.proto",
         "src/protozero/test/example_proto/test_messages.proto",
         "src/protozero/test/example_proto/upper_import.proto",
     ],
@@ -10737,6 +10739,8 @@
         "src/protozero/test/example_proto/extensions.proto",
         "src/protozero/test/example_proto/library.proto",
         "src/protozero/test/example_proto/library_internals/galaxies.proto",
+        "src/protozero/test/example_proto/other_package/test_messages.proto",
+        "src/protozero/test/example_proto/subpackage/test_messages.proto",
         "src/protozero/test/example_proto/test_messages.proto",
         "src/protozero/test/example_proto/upper_import.proto",
     ],
@@ -10757,6 +10761,8 @@
         "external/perfetto/src/protozero/test/example_proto/extensions.gen.cc",
         "external/perfetto/src/protozero/test/example_proto/library.gen.cc",
         "external/perfetto/src/protozero/test/example_proto/library_internals/galaxies.gen.cc",
+        "external/perfetto/src/protozero/test/example_proto/other_package/test_messages.gen.cc",
+        "external/perfetto/src/protozero/test/example_proto/subpackage/test_messages.gen.cc",
         "external/perfetto/src/protozero/test/example_proto/test_messages.gen.cc",
         "external/perfetto/src/protozero/test/example_proto/upper_import.gen.cc",
     ],
@@ -10777,6 +10783,8 @@
         "external/perfetto/src/protozero/test/example_proto/extensions.gen.h",
         "external/perfetto/src/protozero/test/example_proto/library.gen.h",
         "external/perfetto/src/protozero/test/example_proto/library_internals/galaxies.gen.h",
+        "external/perfetto/src/protozero/test/example_proto/other_package/test_messages.gen.h",
+        "external/perfetto/src/protozero/test/example_proto/subpackage/test_messages.gen.h",
         "external/perfetto/src/protozero/test/example_proto/test_messages.gen.h",
         "external/perfetto/src/protozero/test/example_proto/upper_import.gen.h",
     ],
@@ -10793,6 +10801,8 @@
         "src/protozero/test/example_proto/extensions.proto",
         "src/protozero/test/example_proto/library.proto",
         "src/protozero/test/example_proto/library_internals/galaxies.proto",
+        "src/protozero/test/example_proto/other_package/test_messages.proto",
+        "src/protozero/test/example_proto/subpackage/test_messages.proto",
         "src/protozero/test/example_proto/test_messages.proto",
         "src/protozero/test/example_proto/upper_import.proto",
     ],
@@ -10812,6 +10822,8 @@
         "external/perfetto/src/protozero/test/example_proto/extensions.pb.cc",
         "external/perfetto/src/protozero/test/example_proto/library.pb.cc",
         "external/perfetto/src/protozero/test/example_proto/library_internals/galaxies.pb.cc",
+        "external/perfetto/src/protozero/test/example_proto/other_package/test_messages.pb.cc",
+        "external/perfetto/src/protozero/test/example_proto/subpackage/test_messages.pb.cc",
         "external/perfetto/src/protozero/test/example_proto/test_messages.pb.cc",
         "external/perfetto/src/protozero/test/example_proto/upper_import.pb.cc",
     ],
@@ -10831,6 +10843,8 @@
         "external/perfetto/src/protozero/test/example_proto/extensions.pb.h",
         "external/perfetto/src/protozero/test/example_proto/library.pb.h",
         "external/perfetto/src/protozero/test/example_proto/library_internals/galaxies.pb.h",
+        "external/perfetto/src/protozero/test/example_proto/other_package/test_messages.pb.h",
+        "external/perfetto/src/protozero/test/example_proto/subpackage/test_messages.pb.h",
         "external/perfetto/src/protozero/test/example_proto/test_messages.pb.h",
         "external/perfetto/src/protozero/test/example_proto/upper_import.pb.h",
     ],
@@ -10847,6 +10861,8 @@
         "src/protozero/test/example_proto/extensions.proto",
         "src/protozero/test/example_proto/library.proto",
         "src/protozero/test/example_proto/library_internals/galaxies.proto",
+        "src/protozero/test/example_proto/other_package/test_messages.proto",
+        "src/protozero/test/example_proto/subpackage/test_messages.proto",
         "src/protozero/test/example_proto/test_messages.proto",
         "src/protozero/test/example_proto/upper_import.proto",
     ],
@@ -10867,6 +10883,8 @@
         "external/perfetto/src/protozero/test/example_proto/extensions.pbzero.cc",
         "external/perfetto/src/protozero/test/example_proto/library.pbzero.cc",
         "external/perfetto/src/protozero/test/example_proto/library_internals/galaxies.pbzero.cc",
+        "external/perfetto/src/protozero/test/example_proto/other_package/test_messages.pbzero.cc",
+        "external/perfetto/src/protozero/test/example_proto/subpackage/test_messages.pbzero.cc",
         "external/perfetto/src/protozero/test/example_proto/test_messages.pbzero.cc",
         "external/perfetto/src/protozero/test/example_proto/upper_import.pbzero.cc",
     ],
@@ -10887,6 +10905,8 @@
         "external/perfetto/src/protozero/test/example_proto/extensions.pbzero.h",
         "external/perfetto/src/protozero/test/example_proto/library.pbzero.h",
         "external/perfetto/src/protozero/test/example_proto/library_internals/galaxies.pbzero.h",
+        "external/perfetto/src/protozero/test/example_proto/other_package/test_messages.pbzero.h",
+        "external/perfetto/src/protozero/test/example_proto/subpackage/test_messages.pbzero.h",
         "external/perfetto/src/protozero/test/example_proto/test_messages.pbzero.h",
         "external/perfetto/src/protozero/test/example_proto/upper_import.pbzero.h",
     ],
diff --git a/docs/concepts/concurrent-tracing-sessions.md b/docs/concepts/concurrent-tracing-sessions.md
new file mode 100644
index 0000000..63f6dd4
--- /dev/null
+++ b/docs/concepts/concurrent-tracing-sessions.md
@@ -0,0 +1,76 @@
+# Concurrent tracing sessions
+
+Perfetto supports multiple concurrent tracing sessions.
+Sessions are isolated from each other each and each session can choose a different mix of producers and data sources in its [config](config.md) and, in general, it will only receive events specified by that config.
+This is a powerful mechanism which allows great flexibility when collecting traces from the lab or field.
+However there are a few caveats to bear in mind with concurrent tracing sessions:
+1. [Some data sources do not support concurrent sessions](#some-data-sources-do-not-support-concurrent-sessions)
+2. [Some settings are per session while others are per producer](#some-settings-are-per-session-while-others-are-per-producer)
+3. Due to the [way atrace works works](#atrace) if a session requests *any* atrace category or app it receives *all* atrace events enabled on the device
+4. [Various limits](#various-limits) apply
+
+## Some data sources do not support concurrent sessions
+
+Whilst most data sources implemented with the Perfetto SDK as well as most data sources provided by the Perfetto team, do support concurrent tracing sessions some do not.
+This can be due to:
+
+- Hardware or driver constraints
+- Difficulty of implementing the config muxing
+- Perfetto SDK: users may [opt-out of multiple session support](https://cs.android.com/android/platform/superproject/main/+/main:external/perfetto/include/perfetto/tracing/data_source.h;l=266;drc=f988c792c18f93841b14ffa71019fdedf7ab2f03)
+
+### Known to work
+- `traced_probes` data sources ([linux.ftrace](/docs/reference/trace-config-proto.autogen#FtraceConfig), [linux.process_stats](/docs/reference/trace-config-proto.autogen#ProcessStatsConfig), [linux.sys_stats](/docs/reference/trace-config-proto.autogen#SysStatsConfig), [linux.system_info](https://perfetto.dev/docs/reference/trace-config-proto.autogen#SystemInfoConfig), etc)
+
+### Known to work with caveats
+- `heapprofd` supports multiple sessions but each process can only be in one session.
+- `traced_perf` in general supports multiple sessions but the kernel has a limit on counters so may reject a config.
+
+### Known not to work
+- `traced metatracing`
+
+## Some settings are per session while others are per producer
+
+Most buffer sizes and timings specified in the config are per session.
+For example the buffer [sizes](https://cs.android.com/android/platform/superproject/main/+/main:external/perfetto/protos/perfetto/config/trace_config.proto;l=32?q=f:perfetto%20f:trace_config&ss=android%2Fplatform%2Fsuperproject%2Fmain).
+
+However some parameters configure per-producer settings: for example the [size and layout](https://cs.android.com/android/platform/superproject/main/+/main:external/perfetto/protos/perfetto/config/trace_config.proto;l=182;drc=488df1649781de42b72e981c5e79ad922508d1e5) of the shmem buffer between the producer and traced.
+While that is general data source setting the same can apply to data source specific settings.
+For example the ftrace [kernel buffer size and drain period](https://cs.android.com/android/platform/superproject/main/+/main:external/perfetto/protos/perfetto/config/ftrace/ftrace_config.proto;l=32;drc=6a3d3540e68f3d5949b5d86ca736bfd7f811deff) are settings that have to be shared between all users of `traced_probes`.
+
+Bear in mind that
+- Some resources like the shmem buffers are shared by all sessions
+- As suggested by the comments in linked code above some settings are best treated as 'hints' since another config may have already set them before you get a chance to.
+
+## Atrace
+
+Atrace is an Android specific mechanism for doing userland instrumentation and the only available tracing method prior to the introduction of the Perfetto SDK into Android.
+It still powers [os.Trace](https://developer.android.com/reference/android/os/Trace) (as used by platform and application Java code) and [ATRACE_*](https://cs.android.com/android/platform/superproject/main/+/main:system/core/libcutils/include/cutils/trace.h;l=188;drc=0c44d8d68d56c7aecb828d8d87fba7dcb114f3d9) (as used by platform C++).
+
+
+Atrace (both prior to Perfetto and via Perfetto) works as follows:
+- Configuration:
+  - Users choose zero or more 'categories' from a hardcoded list
+  - Users choose zero or more package names including globs
+- This sets:
+  - Some kernel ftrace events
+  - A [system property bitmask](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/native/cmds/atrace/atrace.cpp;l=306;drc=c8af4d3407f3d6be46fafdfc044ace55944fb4b7) (for the atrace categories)
+  - A [system property](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/native/cmds/atrace/atrace.cpp;l=306;bpv=1;bpt=1) for each package.
+- When the Java or C++ tracing APIs are called we examine the system props.
+- If the relevant category or package is enabled we write the event to `trace_marker`
+
+As mentioned, each category may enable a number of kernel ftrace events.
+For example the 'sched' atrace category enables the `sched/sched_switch` ftrace event.
+Kernel ftrace events do not suffer from the current session issues so will not be described further.
+
+For the userland instrumentation:
+- Perfetto ensures the union of all atrace packages categories are installed
+- However since:
+  - the atrace system properties are global
+  - we cannot tell which event comes from which category/package
+Every session that requests *any* atrace event gets *all* enabled atrace events.
+
+## Various limits
+- Perfetto SDK: Max 8 datasource instances per datasource type per producer
+- `traced`: Limit of [15 concurrent sessions](https://cs.android.com/android/platform/superproject/main/+/main:external/perfetto/src/tracing/service/tracing_service_impl.cc;l=114?q=kMaxConcurrentTracingSessions%20)
+- `traced`: Limit of [5 (10 for statsd) concurrent sessions per UID](https://cs.android.com/android/platform/superproject/main/+/main:external/perfetto/src/tracing/service/tracing_service_impl.cc;l=115;drc=17d5806d458e214bdb829deeeb08b098c2b5254d)
+
diff --git a/docs/toc.md b/docs/toc.md
index 2ac2751..f828874 100644
--- a/docs/toc.md
+++ b/docs/toc.md
@@ -66,6 +66,7 @@
   * [Service model](concepts/service-model.md)
   * [Clock synchronization](concepts/clock-sync.md)
   * [Detached mode](concepts/detached-mode.md)
+  * [Concurrent tracing sessions](concepts/concurrent-tracing-sessions.md)
 
 * [Reference](#)
   * [Trace Config proto](reference/trace-config-proto.autogen)
diff --git a/include/perfetto/public/protos/common/data_source_descriptor.pzc.h b/include/perfetto/public/protos/common/data_source_descriptor.pzc.h
index 21e62fb..e2cd133 100644
--- a/include/perfetto/public/protos/common/data_source_descriptor.pzc.h
+++ b/include/perfetto/public/protos/common/data_source_descriptor.pzc.h
@@ -56,6 +56,11 @@
                   handles_incremental_state_clear,
                   4);
 PERFETTO_PB_FIELD(perfetto_protos_DataSourceDescriptor,
+                  VARINT,
+                  bool,
+                  no_flush,
+                  9);
+PERFETTO_PB_FIELD(perfetto_protos_DataSourceDescriptor,
                   MSG,
                   perfetto_protos_GpuCounterDescriptor,
                   gpu_counter_descriptor,
diff --git a/include/perfetto/public/protos/config/data_source_config.pzc.h b/include/perfetto/public/protos/config/data_source_config.pzc.h
index 718a3bc..ed7632b 100644
--- a/include/perfetto/public/protos/config/data_source_config.pzc.h
+++ b/include/perfetto/public/protos/config/data_source_config.pzc.h
@@ -26,12 +26,14 @@
 #include "perfetto/public/pb_macros.h"
 
 PERFETTO_PB_MSG_DECL(perfetto_protos_AndroidGameInterventionListConfig);
+PERFETTO_PB_MSG_DECL(perfetto_protos_AndroidInputEventConfig);
 PERFETTO_PB_MSG_DECL(perfetto_protos_AndroidLogConfig);
 PERFETTO_PB_MSG_DECL(perfetto_protos_AndroidPolledStateConfig);
 PERFETTO_PB_MSG_DECL(perfetto_protos_AndroidPowerConfig);
 PERFETTO_PB_MSG_DECL(perfetto_protos_AndroidSdkSyspropGuardConfig);
 PERFETTO_PB_MSG_DECL(perfetto_protos_AndroidSystemPropertyConfig);
 PERFETTO_PB_MSG_DECL(perfetto_protos_ChromeConfig);
+PERFETTO_PB_MSG_DECL(perfetto_protos_EtwConfig);
 PERFETTO_PB_MSG_DECL(perfetto_protos_FtraceConfig);
 PERFETTO_PB_MSG_DECL(perfetto_protos_GpuCounterConfig);
 PERFETTO_PB_MSG_DECL(perfetto_protos_HeapprofdConfig);
@@ -42,6 +44,7 @@
 PERFETTO_PB_MSG_DECL(perfetto_protos_PackagesListConfig);
 PERFETTO_PB_MSG_DECL(perfetto_protos_PerfEventConfig);
 PERFETTO_PB_MSG_DECL(perfetto_protos_ProcessStatsConfig);
+PERFETTO_PB_MSG_DECL(perfetto_protos_ProtoLogConfig);
 PERFETTO_PB_MSG_DECL(perfetto_protos_StatsdTracingConfig);
 PERFETTO_PB_MSG_DECL(perfetto_protos_SurfaceFlingerLayersConfig);
 PERFETTO_PB_MSG_DECL(perfetto_protos_SurfaceFlingerTransactionsConfig);
@@ -49,6 +52,7 @@
 PERFETTO_PB_MSG_DECL(perfetto_protos_SystemInfoConfig);
 PERFETTO_PB_MSG_DECL(perfetto_protos_TestConfig);
 PERFETTO_PB_MSG_DECL(perfetto_protos_TrackEventConfig);
+PERFETTO_PB_MSG_DECL(perfetto_protos_V8Config);
 PERFETTO_PB_MSG_DECL(perfetto_protos_VulkanMemoryConfig);
 
 PERFETTO_PB_ENUM_IN_MSG(perfetto_protos_DataSourceConfig, SessionInitiator){
@@ -196,6 +200,11 @@
                   101);
 PERFETTO_PB_FIELD(perfetto_protos_DataSourceConfig,
                   MSG,
+                  perfetto_protos_V8Config,
+                  v8_config,
+                  127);
+PERFETTO_PB_FIELD(perfetto_protos_DataSourceConfig,
+                  MSG,
                   perfetto_protos_InterceptorConfig,
                   interceptor_config,
                   115);
@@ -220,6 +229,21 @@
                   android_sdk_sysprop_guard_config,
                   124);
 PERFETTO_PB_FIELD(perfetto_protos_DataSourceConfig,
+                  MSG,
+                  perfetto_protos_EtwConfig,
+                  etw_config,
+                  125);
+PERFETTO_PB_FIELD(perfetto_protos_DataSourceConfig,
+                  MSG,
+                  perfetto_protos_ProtoLogConfig,
+                  protolog_config,
+                  126);
+PERFETTO_PB_FIELD(perfetto_protos_DataSourceConfig,
+                  MSG,
+                  perfetto_protos_AndroidInputEventConfig,
+                  android_input_event_config,
+                  128);
+PERFETTO_PB_FIELD(perfetto_protos_DataSourceConfig,
                   STRING,
                   const char*,
                   legacy_config,
diff --git a/include/perfetto/public/protos/config/trace_config.pzc.h b/include/perfetto/public/protos/config/trace_config.pzc.h
index a2b8432..76492e3 100644
--- a/include/perfetto/public/protos/config/trace_config.pzc.h
+++ b/include/perfetto/public/protos/config/trace_config.pzc.h
@@ -80,6 +80,8 @@
                                   SFP_MATCH_BREAK) = 3,
     PERFETTO_PB_ENUM_IN_MSG_ENTRY(perfetto_protos_TraceConfig_TraceFilter,
                                   SFP_ATRACE_MATCH_BREAK) = 4,
+    PERFETTO_PB_ENUM_IN_MSG_ENTRY(perfetto_protos_TraceConfig_TraceFilter,
+                                  SFP_ATRACE_REPEATED_SEARCH_REDACT_GROUPS) = 5,
 };
 
 PERFETTO_PB_ENUM_IN_MSG(perfetto_protos_TraceConfig_TriggerConfig, TriggerMode){
@@ -204,6 +206,11 @@
                   bugreport_score,
                   30);
 PERFETTO_PB_FIELD(perfetto_protos_TraceConfig,
+                  STRING,
+                  const char*,
+                  bugreport_filename,
+                  38);
+PERFETTO_PB_FIELD(perfetto_protos_TraceConfig,
                   MSG,
                   perfetto_protos_TraceConfig_TriggerConfig,
                   trigger_config,
@@ -234,11 +241,6 @@
                   compression_type,
                   24);
 PERFETTO_PB_FIELD(perfetto_protos_TraceConfig,
-                  VARINT,
-                  bool,
-                  compress_from_cli,
-                  37);
-PERFETTO_PB_FIELD(perfetto_protos_TraceConfig,
                   MSG,
                   perfetto_protos_TraceConfig_IncidentReportConfig,
                   incident_report_config,
diff --git a/include/perfetto/public/protos/trace/interned_data/interned_data.pzc.h b/include/perfetto/public/protos/trace/interned_data/interned_data.pzc.h
index 3c8f88a..71e26fa 100644
--- a/include/perfetto/public/protos/trace/interned_data/interned_data.pzc.h
+++ b/include/perfetto/public/protos/trace/interned_data/interned_data.pzc.h
@@ -35,6 +35,11 @@
 PERFETTO_PB_MSG_DECL(perfetto_protos_InternedGpuRenderStageSpecification);
 PERFETTO_PB_MSG_DECL(perfetto_protos_InternedGraphicsContext);
 PERFETTO_PB_MSG_DECL(perfetto_protos_InternedString);
+PERFETTO_PB_MSG_DECL(perfetto_protos_InternedV8Isolate);
+PERFETTO_PB_MSG_DECL(perfetto_protos_InternedV8JsFunction);
+PERFETTO_PB_MSG_DECL(perfetto_protos_InternedV8JsScript);
+PERFETTO_PB_MSG_DECL(perfetto_protos_InternedV8String);
+PERFETTO_PB_MSG_DECL(perfetto_protos_InternedV8WasmScript);
 PERFETTO_PB_MSG_DECL(perfetto_protos_LogMessageBody);
 PERFETTO_PB_MSG_DECL(perfetto_protos_Mapping);
 PERFETTO_PB_MSG_DECL(perfetto_protos_NetworkPacketContext);
@@ -153,5 +158,40 @@
                   perfetto_protos_NetworkPacketContext,
                   packet_context,
                   30);
+PERFETTO_PB_FIELD(perfetto_protos_InternedData,
+                  MSG,
+                  perfetto_protos_InternedV8String,
+                  v8_js_function_name,
+                  31);
+PERFETTO_PB_FIELD(perfetto_protos_InternedData,
+                  MSG,
+                  perfetto_protos_InternedV8JsFunction,
+                  v8_js_function,
+                  32);
+PERFETTO_PB_FIELD(perfetto_protos_InternedData,
+                  MSG,
+                  perfetto_protos_InternedV8JsScript,
+                  v8_js_script,
+                  33);
+PERFETTO_PB_FIELD(perfetto_protos_InternedData,
+                  MSG,
+                  perfetto_protos_InternedV8WasmScript,
+                  v8_wasm_script,
+                  34);
+PERFETTO_PB_FIELD(perfetto_protos_InternedData,
+                  MSG,
+                  perfetto_protos_InternedV8Isolate,
+                  v8_isolate,
+                  35);
+PERFETTO_PB_FIELD(perfetto_protos_InternedData,
+                  MSG,
+                  perfetto_protos_InternedString,
+                  protolog_string_args,
+                  36);
+PERFETTO_PB_FIELD(perfetto_protos_InternedData,
+                  MSG,
+                  perfetto_protos_InternedString,
+                  protolog_stacktrace,
+                  37);
 
 #endif  // INCLUDE_PERFETTO_PUBLIC_PROTOS_TRACE_INTERNED_DATA_INTERNED_DATA_PZC_H_
diff --git a/include/perfetto/public/protos/trace/trace_packet.pzc.h b/include/perfetto/public/protos/trace/trace_packet.pzc.h
index 52df003..d83ab18 100644
--- a/include/perfetto/public/protos/trace/trace_packet.pzc.h
+++ b/include/perfetto/public/protos/trace/trace_packet.pzc.h
@@ -29,6 +29,7 @@
 PERFETTO_PB_MSG_DECL(perfetto_protos_AndroidCameraSessionStats);
 PERFETTO_PB_MSG_DECL(perfetto_protos_AndroidEnergyEstimationBreakdown);
 PERFETTO_PB_MSG_DECL(perfetto_protos_AndroidGameInterventionList);
+PERFETTO_PB_MSG_DECL(perfetto_protos_AndroidInputEvent);
 PERFETTO_PB_MSG_DECL(perfetto_protos_AndroidLogPacket);
 PERFETTO_PB_MSG_DECL(perfetto_protos_AndroidSystemProperty);
 PERFETTO_PB_MSG_DECL(perfetto_protos_BatteryCounters);
@@ -39,6 +40,7 @@
 PERFETTO_PB_MSG_DECL(perfetto_protos_CpuInfo);
 PERFETTO_PB_MSG_DECL(perfetto_protos_DeobfuscationMapping);
 PERFETTO_PB_MSG_DECL(perfetto_protos_EntityStateResidency);
+PERFETTO_PB_MSG_DECL(perfetto_protos_EtwTraceEventBundle);
 PERFETTO_PB_MSG_DECL(perfetto_protos_ExtensionDescriptor);
 PERFETTO_PB_MSG_DECL(perfetto_protos_FrameTimelineEvent);
 PERFETTO_PB_MSG_DECL(perfetto_protos_FtraceEventBundle);
@@ -66,6 +68,11 @@
 PERFETTO_PB_MSG_DECL(perfetto_protos_ProcessTree);
 PERFETTO_PB_MSG_DECL(perfetto_protos_ProfilePacket);
 PERFETTO_PB_MSG_DECL(perfetto_protos_ProfiledFrameSymbols);
+PERFETTO_PB_MSG_DECL(perfetto_protos_ProtoLogMessage);
+PERFETTO_PB_MSG_DECL(perfetto_protos_ProtoLogViewerConfig);
+PERFETTO_PB_MSG_DECL(perfetto_protos_RemoteClockSync);
+PERFETTO_PB_MSG_DECL(perfetto_protos_ShellHandlerMappings);
+PERFETTO_PB_MSG_DECL(perfetto_protos_ShellTransition);
 PERFETTO_PB_MSG_DECL(perfetto_protos_SmapsPacket);
 PERFETTO_PB_MSG_DECL(perfetto_protos_StatsdAtom);
 PERFETTO_PB_MSG_DECL(perfetto_protos_StreamingAllocation);
@@ -87,6 +94,11 @@
 PERFETTO_PB_MSG_DECL(perfetto_protos_TranslationTable);
 PERFETTO_PB_MSG_DECL(perfetto_protos_Trigger);
 PERFETTO_PB_MSG_DECL(perfetto_protos_UiState);
+PERFETTO_PB_MSG_DECL(perfetto_protos_V8CodeMove);
+PERFETTO_PB_MSG_DECL(perfetto_protos_V8InternalCode);
+PERFETTO_PB_MSG_DECL(perfetto_protos_V8JsCode);
+PERFETTO_PB_MSG_DECL(perfetto_protos_V8RegExpCode);
+PERFETTO_PB_MSG_DECL(perfetto_protos_V8WasmCode);
 PERFETTO_PB_MSG_DECL(perfetto_protos_VulkanApiEvent);
 PERFETTO_PB_MSG_DECL(perfetto_protos_VulkanMemoryEvent);
 
@@ -423,6 +435,66 @@
                   94);
 PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
                   MSG,
+                  perfetto_protos_ShellTransition,
+                  shell_transition,
+                  96);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_ShellHandlerMappings,
+                  shell_handler_mappings,
+                  97);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_ProtoLogMessage,
+                  protolog_message,
+                  104);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_ProtoLogViewerConfig,
+                  protolog_viewer_config,
+                  105);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_EtwTraceEventBundle,
+                  etw_events,
+                  95);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_V8JsCode,
+                  v8_js_code,
+                  99);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_V8InternalCode,
+                  v8_internal_code,
+                  100);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_V8WasmCode,
+                  v8_wasm_code,
+                  101);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_V8RegExpCode,
+                  v8_reg_exp_code,
+                  102);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_V8CodeMove,
+                  v8_code_move,
+                  103);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_AndroidInputEvent,
+                  android_input_event,
+                  106);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_RemoteClockSync,
+                  remote_clock_sync,
+                  107);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
                   perfetto_protos_TestEvent,
                   for_testing,
                   900);
@@ -467,5 +539,10 @@
                   bool,
                   first_packet_on_sequence,
                   87);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  VARINT,
+                  uint32_t,
+                  machine_id,
+                  98);
 
 #endif  // INCLUDE_PERFETTO_PUBLIC_PROTOS_TRACE_TRACE_PACKET_PZC_H_
diff --git a/include/perfetto/public/protos/trace/track_event/track_event.pzc.h b/include/perfetto/public/protos/trace/track_event/track_event.pzc.h
index 5553376..2ee81f1 100644
--- a/include/perfetto/public/protos/trace/track_event/track_event.pzc.h
+++ b/include/perfetto/public/protos/trace/track_event/track_event.pzc.h
@@ -41,6 +41,7 @@
 PERFETTO_PB_MSG_DECL(perfetto_protos_ChromeWindowHandleEventInfo);
 PERFETTO_PB_MSG_DECL(perfetto_protos_DebugAnnotation);
 PERFETTO_PB_MSG_DECL(perfetto_protos_LogMessage);
+PERFETTO_PB_MSG_DECL(perfetto_protos_Screenshot);
 PERFETTO_PB_MSG_DECL(perfetto_protos_SourceLocation);
 PERFETTO_PB_MSG_DECL(perfetto_protos_TaskExecution);
 PERFETTO_PB_MSG_DECL(perfetto_protos_TrackEvent_LegacyEvent);
@@ -246,6 +247,11 @@
                   49);
 PERFETTO_PB_FIELD(perfetto_protos_TrackEvent,
                   MSG,
+                  perfetto_protos_Screenshot,
+                  screenshot,
+                  50);
+PERFETTO_PB_FIELD(perfetto_protos_TrackEvent,
+                  MSG,
                   perfetto_protos_SourceLocation,
                   source_location,
                   33);
diff --git a/protos/perfetto/trace/ftrace/ftrace_event.proto b/protos/perfetto/trace/ftrace/ftrace_event.proto
index def2a72..5c0cc3e 100644
--- a/protos/perfetto/trace/ftrace/ftrace_event.proto
+++ b/protos/perfetto/trace/ftrace/ftrace_event.proto
@@ -606,5 +606,6 @@
     SchedSwitchWithCtrsFtraceEvent sched_switch_with_ctrs = 487;
     GpuWorkPeriodFtraceEvent gpu_work_period = 488;
     RpmStatusFtraceEvent rpm_status = 489;
+    PanelWriteGenericFtraceEvent panel_write_generic = 490;
   }
 }
diff --git a/protos/perfetto/trace/ftrace/panel.proto b/protos/perfetto/trace/ftrace/panel.proto
index 5b84b3b..fde72d9 100644
--- a/protos/perfetto/trace/ftrace/panel.proto
+++ b/protos/perfetto/trace/ftrace/panel.proto
@@ -18,3 +18,11 @@
   optional uint32 tx_buf = 2;
   optional uint32 type = 3;
 }
+message PanelWriteGenericFtraceEvent {
+  optional int32 pid = 1;
+  optional string trace_name = 2;
+  optional uint32 trace_begin = 3;
+  optional string name = 4;
+  optional uint32 type = 5;
+  optional int32 value = 6;
+}
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index b657572..97962a7 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -9256,6 +9256,14 @@
   optional uint32 tx_buf = 2;
   optional uint32 type = 3;
 }
+message PanelWriteGenericFtraceEvent {
+  optional int32 pid = 1;
+  optional string trace_name = 2;
+  optional uint32 trace_begin = 3;
+  optional string name = 4;
+  optional uint32 type = 5;
+  optional int32 value = 6;
+}
 
 // End of protos/perfetto/trace/ftrace/panel.proto
 
@@ -10584,6 +10592,7 @@
     SchedSwitchWithCtrsFtraceEvent sched_switch_with_ctrs = 487;
     GpuWorkPeriodFtraceEvent gpu_work_period = 488;
     RpmStatusFtraceEvent rpm_status = 489;
+    PanelWriteGenericFtraceEvent panel_write_generic = 490;
   }
 }
 
diff --git a/src/protozero/BUILD.gn b/src/protozero/BUILD.gn
index 4d940bc..796a125 100644
--- a/src/protozero/BUILD.gn
+++ b/src/protozero/BUILD.gn
@@ -93,6 +93,8 @@
     "test/example_proto/library_internals/galaxies.proto",
     "test/example_proto/test_messages.proto",
     "test/example_proto/upper_import.proto",
+    "test/example_proto/other_package/test_messages.proto",
+    "test/example_proto/subpackage/test_messages.proto",
   ]
   proto_path = perfetto_root_path
 }
diff --git a/src/protozero/protoc_plugin/cppgen_plugin.cc b/src/protozero/protoc_plugin/cppgen_plugin.cc
index f134caa..4257613 100644
--- a/src/protozero/protoc_plugin/cppgen_plugin.cc
+++ b/src/protozero/protoc_plugin/cppgen_plugin.cc
@@ -95,7 +95,13 @@
     return full_type;
   }
 
+  template <class T>
+  bool HasSamePackage(const T* descriptor) const {
+    return descriptor->file()->package() == package_;
+  }
+
   mutable std::string wrapper_namespace_;
+  mutable std::string package_;
 };
 
 CppObjGenerator::CppObjGenerator() = default;
@@ -116,6 +122,8 @@
     }
   }
 
+  package_ = file->package();
+
   auto get_file_name = [](const FileDescriptor* proto) {
     return StripSuffix(proto->name(), ".proto") + ".gen";
   };
@@ -372,10 +380,16 @@
       return constref ? "const std::string&" : "std::string";
     case FieldDescriptor::TYPE_MESSAGE:
       assert(!field->options().lazy());
-      return constref ? "const " + GetFullName(field->message_type()) + "&"
-                      : GetFullName(field->message_type());
+      return constref
+                 ? "const " +
+                       GetFullName(field->message_type(),
+                                   !HasSamePackage(field->message_type())) +
+                       "&"
+                 : GetFullName(field->message_type(),
+                               !HasSamePackage(field->message_type()));
     case FieldDescriptor::TYPE_ENUM:
-      return GetFullName(field->enum_type());
+      return GetFullName(field->enum_type(),
+                         !HasSamePackage(field->enum_type()));
     case FieldDescriptor::TYPE_GROUP:
       abort();
   }
diff --git a/src/protozero/protoc_plugin/protozero_c_plugin.cc b/src/protozero/protoc_plugin/protozero_c_plugin.cc
index 27fe24f..5bd05dd 100644
--- a/src/protozero/protoc_plugin/protozero_c_plugin.cc
+++ b/src/protozero/protoc_plugin/protozero_c_plugin.cc
@@ -127,24 +127,12 @@
       error_ = reason;
   }
 
-  // Get full name (including outer descriptors) of proto descriptor.
-  template <class T>
-  inline std::string GetDescriptorName(const T* descriptor) {
-    if (!package_.empty()) {
-      return StripPrefix(descriptor->full_name(), package_ + ".");
-    }
-    return descriptor->full_name();
-  }
-
   // Get C++ class name corresponding to proto descriptor.
   // Nested names are splitted by underscores. Underscores in type names aren't
   // prohibited but not recommended in order to avoid name collisions.
   template <class T>
-  inline std::string GetCppClassName(const T* descriptor, bool full = false) {
-    std::string name = StripChars(GetDescriptorName(descriptor), ".", '_');
-    if (full)
-      name = full_namespace_prefix_ + "_" + name;
-    return name;
+  inline std::string GetCppClassName(const T* descriptor) {
+    return StripChars(descriptor->full_name(), ".", '_');
   }
 
   const char* FieldTypeToPackedBufferType(FieldDescriptor::Type type) {
@@ -213,7 +201,7 @@
       case FieldDescriptor::TYPE_DOUBLE:
         return "double";
       case FieldDescriptor::TYPE_ENUM:
-        return "enum " + GetCppClassName(field->enum_type(), true);
+        return "enum " + GetCppClassName(field->enum_type());
       case FieldDescriptor::TYPE_STRING:
       case FieldDescriptor::TYPE_BYTES:
         return "const char*";
@@ -305,10 +293,6 @@
     while (!stack.empty()) {
       const FileDescriptor* imp = stack.back();
       stack.pop_back();
-      // Having imports under different packages leads to unnecessary
-      // complexity with namespaces.
-      if (imp->package() != package_)
-        Abort("Imported proto must be in the same package.");
 
       for (int i = 0; i < imp->public_dependency_count(); ++i) {
         stack.push_back(imp->public_dependency(i));
@@ -427,7 +411,7 @@
     // Print forward declarations.
     for (const Descriptor* message : referenced_messages_) {
       stub_h_->Print("PERFETTO_PB_MSG_DECL($class$);\n", "class",
-                     GetCppClassName(message, true));
+                     GetCppClassName(message));
     }
 
     stub_h_->Print("\n");
@@ -436,11 +420,11 @@
   void GenerateEnumDescriptor(const EnumDescriptor* enumeration) {
     if (enumeration->containing_type()) {
       stub_h_->Print("PERFETTO_PB_ENUM_IN_MSG($msg$, $class$){\n", "msg",
-                     GetCppClassName(enumeration->containing_type(), true),
-                     "class", enumeration->name());
+                     GetCppClassName(enumeration->containing_type()), "class",
+                     enumeration->name());
     } else {
       stub_h_->Print("PERFETTO_PB_ENUM($class$){\n", "class",
-                     GetCppClassName(enumeration, true));
+                     GetCppClassName(enumeration));
     }
     stub_h_->Indent();
 
@@ -451,8 +435,8 @@
       if (enumeration->containing_type()) {
         stub_h_->Print(
             "PERFETTO_PB_ENUM_IN_MSG_ENTRY($msg$, $val$) = $number$,\n", "msg",
-            GetCppClassName(enumeration->containing_type(), true), "val",
-            value_name, "number", std::to_string(value->number()));
+            GetCppClassName(enumeration->containing_type()), "val", value_name,
+            "number", std::to_string(value->number()));
       } else {
         stub_h_->Print("PERFETTO_PB_ENUM_ENTRY($val$) = $number$, \n", "val",
                        full_namespace_prefix_ + "_" + value_name, "number",
@@ -532,8 +516,7 @@
 
   void GenerateNestedMessageFieldDescriptor(const std::string& message_cpp_type,
                                             const FieldDescriptor* field) {
-    std::string inner_class =
-        full_namespace_prefix_ + "_" + GetCppClassName(field->message_type());
+    std::string inner_class = GetCppClassName(field->message_type());
     stub_h_->Print(
         "PERFETTO_PB_FIELD($class$, MSG, $inner_class$, $name$, $id$);\n",
         "class", message_cpp_type, "id", std::to_string(field->number()),
@@ -542,12 +525,11 @@
 
   void GenerateMessageDescriptor(const Descriptor* message) {
     stub_h_->Print("PERFETTO_PB_MSG($name$);\n", "name",
-                   GetCppClassName(message, true));
+                   GetCppClassName(message));
 
     // Field descriptors.
     for (int i = 0; i < message->field_count(); ++i) {
-      GenerateFieldDescriptor(GetCppClassName(message, true),
-                              message->field(i));
+      GenerateFieldDescriptor(GetCppClassName(message), message->field(i));
     }
     stub_h_->Print("\n");
   }
diff --git a/src/protozero/protoc_plugin/protozero_plugin.cc b/src/protozero/protoc_plugin/protozero_plugin.cc
index 7d46a50..5b08292 100644
--- a/src/protozero/protoc_plugin/protozero_plugin.cc
+++ b/src/protozero/protoc_plugin/protozero_plugin.cc
@@ -42,6 +42,7 @@
 using google::protobuf::compiler::GeneratorContext;
 using google::protobuf::io::Printer;
 using google::protobuf::io::ZeroCopyOutputStream;
+using perfetto::base::ReplaceAll;
 using perfetto::base::SplitString;
 using perfetto::base::StripChars;
 using perfetto::base::StripPrefix;
@@ -126,14 +127,9 @@
       error_ = reason;
   }
 
-  // Get full name (including outer descriptors) of proto descriptor.
   template <class T>
-  inline std::string GetDescriptorName(const T* descriptor) {
-    if (!package_.empty()) {
-      return StripPrefix(descriptor->full_name(), package_ + ".");
-    } else {
-      return descriptor->full_name();
-    }
+  bool HasSamePackage(const T* descriptor) const {
+    return descriptor->file()->package() == package_;
   }
 
   // Get C++ class name corresponding to proto descriptor.
@@ -141,9 +137,28 @@
   // prohibited but not recommended in order to avoid name collisions.
   template <class T>
   inline std::string GetCppClassName(const T* descriptor, bool full = false) {
-    std::string name = StripChars(GetDescriptorName(descriptor), ".", '_');
-    if (full)
-      name = full_namespace_prefix_ + name;
+    std::string package = descriptor->file()->package();
+    std::string name = StripPrefix(descriptor->full_name(), package + ".");
+    name = StripChars(name, ".", '_');
+
+    if (full && !package.empty()) {
+      auto get_full_namespace = [&]() {
+        std::vector<std::string> namespaces = SplitString(package, ".");
+        if (!wrapper_namespace_.empty())
+          namespaces.push_back(wrapper_namespace_);
+
+        std::string result = "";
+        for (const std::string& ns : namespaces) {
+          result += "::";
+          result += ns;
+        }
+        return result;
+      };
+
+      std::string namespaces = ReplaceAll(package, ".", "::");
+      name = get_full_namespace() + "::" + name;
+    }
+
     return name;
   }
 
@@ -305,12 +320,14 @@
       case FieldDescriptor::TYPE_DOUBLE:
         return "double";
       case FieldDescriptor::TYPE_ENUM:
-        return GetCppClassName(field->enum_type(), true);
+        return GetCppClassName(field->enum_type(),
+                               !HasSamePackage(field->enum_type()));
       case FieldDescriptor::TYPE_STRING:
       case FieldDescriptor::TYPE_BYTES:
         return "std::string";
       case FieldDescriptor::TYPE_MESSAGE:
-        return GetCppClassName(field->message_type());
+        return GetCppClassName(field->message_type(),
+                               !HasSamePackage(field->message_type()));
       case FieldDescriptor::TYPE_GROUP:
         Abort("Groups not supported.");
         return "";
@@ -405,11 +422,6 @@
     while (!stack.empty()) {
       const FileDescriptor* import = stack.back();
       stack.pop_back();
-      // Having imports under different packages leads to unnecessary
-      // complexity with namespaces.
-      if (import->package() != package_)
-        Abort("Imported proto must be in the same package.");
-
       for (int i = 0; i < import->public_dependency_count(); ++i) {
         stack.push_back(import->public_dependency(i));
       }
@@ -504,32 +516,70 @@
     }
     stub_h_->Print("\n");
 
+    PrintForwardDeclarations();
+
     // Print namespaces.
     for (const std::string& ns : namespaces_) {
       stub_h_->Print("namespace $ns$ {\n", "ns", ns);
     }
     stub_h_->Print("\n");
+  }
 
-    // Print forward declarations.
+  void PrintForwardDeclarations() {
+    struct Descriptors {
+      std::vector<const Descriptor*> messages_;
+      std::vector<const EnumDescriptor*> enums_;
+    };
+    std::map<std::string, Descriptors> package_to_descriptors;
+
     for (const Descriptor* message : referenced_messages_) {
-      stub_h_->Print("class $class$;\n", "class", GetCppClassName(message));
+      package_to_descriptors[message->file()->package()].messages_.push_back(
+          message);
     }
-    for (const EnumDescriptor* enumeration : referenced_enums_) {
-      if (enumeration->containing_type()) {
-        stub_h_->Print("namespace $namespace_name$ {\n", "namespace_name",
-                       GetNamespaceNameForInnerEnum(enumeration));
-      }
-      stub_h_->Print("enum $class$ : int32_t;\n", "class", enumeration->name());
 
-      if (enumeration->containing_type()) {
-        stub_h_->Print("}  // namespace $namespace_name$\n", "namespace_name",
-                       GetNamespaceNameForInnerEnum(enumeration));
-        stub_h_->Print("using $alias$ = $namespace_name$::$short_name$;\n",
-                       "alias", GetCppClassName(enumeration), "namespace_name",
-                       GetNamespaceNameForInnerEnum(enumeration), "short_name",
+    for (const EnumDescriptor* enumeration : referenced_enums_) {
+      package_to_descriptors[enumeration->file()->package()].enums_.push_back(
+          enumeration);
+    }
+
+    for (const auto& [package, descriptors] : package_to_descriptors) {
+      std::vector<std::string> namespaces = SplitString(package, ".");
+      namespaces.push_back(wrapper_namespace_);
+
+      // open namespaces
+      for (const auto& ns : namespaces) {
+        stub_h_->Print("namespace $ns$ {\n", "ns", ns);
+      }
+
+      for (const Descriptor* message : descriptors.messages_) {
+        stub_h_->Print("class $class$;\n", "class", GetCppClassName(message));
+      }
+
+      for (const EnumDescriptor* enumeration : descriptors.enums_) {
+        if (enumeration->containing_type()) {
+          stub_h_->Print("namespace $namespace_name$ {\n", "namespace_name",
+                         GetNamespaceNameForInnerEnum(enumeration));
+        }
+        stub_h_->Print("enum $class$ : int32_t;\n", "class",
                        enumeration->name());
+
+        if (enumeration->containing_type()) {
+          stub_h_->Print("}  // namespace $namespace_name$\n", "namespace_name",
+                         GetNamespaceNameForInnerEnum(enumeration));
+          stub_h_->Print("using $alias$ = $namespace_name$::$short_name$;\n",
+                         "alias", GetCppClassName(enumeration),
+                         "namespace_name",
+                         GetNamespaceNameForInnerEnum(enumeration),
+                         "short_name", enumeration->name());
+        }
+      }
+
+      // close namespaces
+      for (auto it = namespaces.crbegin(); it != namespaces.crend(); ++it) {
+        stub_h_->Print("} // Namespace $ns$.\n", "ns", *it);
       }
     }
+
     stub_h_->Print("\n");
   }
 
@@ -675,7 +725,8 @@
 
   void GenerateNestedMessageFieldDescriptor(const FieldDescriptor* field) {
     std::string action = field->is_repeated() ? "add" : "set";
-    std::string inner_class = GetCppClassName(field->message_type());
+    std::string inner_class = GetCppClassName(
+        field->message_type(), !HasSamePackage(field->message_type()));
     stub_h_->Print(
         "template <typename T = $inner_class$> T* $action$_$name$() {\n"
         "  return BeginNestedMessage<T>($id$);\n"
diff --git a/src/protozero/test/cppgen_conformance_unittest.cc b/src/protozero/test/cppgen_conformance_unittest.cc
index 5ac43aa..d8d284e 100644
--- a/src/protozero/test/cppgen_conformance_unittest.cc
+++ b/src/protozero/test/cppgen_conformance_unittest.cc
@@ -24,14 +24,20 @@
 
 // Autogenerated headers in out/*/gen/
 #include "src/protozero/test/example_proto/library.gen.h"
+#include "src/protozero/test/example_proto/other_package/test_messages.gen.h"
+#include "src/protozero/test/example_proto/subpackage/test_messages.gen.h"
 #include "src/protozero/test/example_proto/test_messages.gen.h"
 #include "src/protozero/test/example_proto/test_messages.pb.h"
 
 // Generated by the cppgen compiler.
 namespace pbtest = protozero::test::protos::gen;
+namespace pbtest_subpackage = protozero::test::protos::subpackage::gen;
+namespace pbtest_otherpackage = other_package::gen;
 
 // Generated by the official protobuf compiler.
 namespace pbgold = protozero::test::protos;
+namespace pbgold_subpackage = protozero::test::protos::subpackage;
+namespace pbgold_other_package = other_package;
 
 namespace protozero {
 namespace {
@@ -339,5 +345,57 @@
     ASSERT_THAT(msg.field_sfixed64(), ElementsAreArray(exp_sfixed64));
   }
 }
+
+TEST(ProtoCppConformanceTest, DifferentPackages) {
+  pbtest::DifferentPackages msg;
+
+  // Pupulate fields defined in "protozero.test.protos.subpackage"
+  pbtest_subpackage::Message* msgSubpackage = msg.mutable_subpackage_message();
+  msgSubpackage->set_field_int32(1);
+  msgSubpackage->set_field_enum(pbtest_subpackage::Enum::A);
+  msgSubpackage->set_field_nested_enum(pbtest_subpackage::Message_NestedEnum_C);
+  msg.mutable_subpackage_nested_message()->set_field_int32(2);
+  msg.set_subpackage_enum(pbtest_subpackage::Enum::B);
+  msg.set_subpackage_nested_enum(pbtest_subpackage::Message_NestedEnum_D);
+
+  // Pupulate fields defined in "other_package"
+  pbtest_otherpackage::Message* msgOtherPackage =
+      msg.mutable_otherpackage_message();
+  msgOtherPackage->set_field_int32(11);
+  msgOtherPackage->set_field_enum(pbtest_otherpackage::Enum::A);
+  msgOtherPackage->set_field_nested_enum(
+      pbtest_otherpackage::Message_NestedEnum_C);
+  msg.mutable_otherpackage_nested_message()->set_field_int32(12);
+  msg.set_otherpackage_enum(pbtest_otherpackage::Enum::B);
+  msg.set_otherpackage_nested_enum(pbtest_otherpackage::Message_NestedEnum_D);
+
+  // Deserialize into golden proto
+  std::string serialized = msg.SerializeAsString();
+  pbgold::DifferentPackages gold_msg;
+  gold_msg.ParseFromString(serialized);
+  EXPECT_EQ(serialized.size(), static_cast<size_t>(gold_msg.ByteSizeLong()));
+
+  // Check fields defined in "protozero.test.protos.subpackage"
+  EXPECT_EQ(1, gold_msg.subpackage_message().field_int32());
+  EXPECT_EQ(pbgold_subpackage::Enum::A,
+            gold_msg.subpackage_message().field_enum());
+  EXPECT_EQ(pbgold_subpackage::Message_NestedEnum_C,
+            gold_msg.subpackage_message().field_nested_enum());
+  EXPECT_EQ(2, gold_msg.subpackage_nested_message().field_int32());
+  EXPECT_EQ(pbgold_subpackage::Enum::B, gold_msg.subpackage_enum());
+  EXPECT_EQ(pbgold_subpackage::Message_NestedEnum_D,
+            gold_msg.subpackage_nested_enum());
+
+  // Check fields defined in "other_package"
+  EXPECT_EQ(11, gold_msg.otherpackage_message().field_int32());
+  EXPECT_EQ(pbgold_other_package::Enum::A,
+            gold_msg.otherpackage_message().field_enum());
+  EXPECT_EQ(pbgold_other_package::Message_NestedEnum_C,
+            gold_msg.otherpackage_message().field_nested_enum());
+  EXPECT_EQ(12, gold_msg.otherpackage_nested_message().field_int32());
+  EXPECT_EQ(pbgold_other_package::Enum::B, gold_msg.otherpackage_enum());
+  EXPECT_EQ(pbgold_other_package::Message_NestedEnum_D,
+            gold_msg.otherpackage_nested_enum());
+}
 }  // namespace
 }  // namespace protozero
diff --git a/src/protozero/test/example_proto/other_package/test_messages.proto b/src/protozero/test/example_proto/other_package/test_messages.proto
new file mode 100644
index 0000000..d299082
--- /dev/null
+++ b/src/protozero/test/example_proto/other_package/test_messages.proto
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package other_package;
+
+enum Enum {
+  A = 10;
+  B = 11;
+}
+
+message Message {
+  message NestedMessage {
+    optional int32 field_int32 = 1;
+  }
+
+  enum NestedEnum {
+    C = 12;
+    D = 13;
+  }
+
+  optional int32 field_int32 = 1;
+  optional Enum field_enum = 2;
+  optional NestedEnum field_nested_enum = 3;
+  optional NestedMessage field_nested_message = 4;
+}
diff --git a/src/protozero/test/example_proto/subpackage/test_messages.proto b/src/protozero/test/example_proto/subpackage/test_messages.proto
new file mode 100644
index 0000000..6f7c0cc
--- /dev/null
+++ b/src/protozero/test/example_proto/subpackage/test_messages.proto
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package protozero.test.protos.subpackage;
+
+enum Enum {
+  A = 1;
+  B = 2;
+}
+
+message Message {
+  message NestedMessage {
+    optional int32 field_int32 = 1;
+  }
+
+  enum NestedEnum {
+    C = 3;
+    D = 4;
+  }
+
+  optional int32 field_int32 = 1;
+  optional Enum field_enum = 2;
+  optional NestedEnum field_nested_enum = 3;
+  optional NestedMessage field_nested_message = 4;
+}
diff --git a/src/protozero/test/example_proto/test_messages.proto b/src/protozero/test/example_proto/test_messages.proto
index e6ece33..b54d883 100644
--- a/src/protozero/test/example_proto/test_messages.proto
+++ b/src/protozero/test/example_proto/test_messages.proto
@@ -19,6 +19,8 @@
 package protozero.test.protos;
 
 import "src/protozero/test/example_proto/library.proto";
+import "src/protozero/test/example_proto/other_package/test_messages.proto";
+import "src/protozero/test/example_proto/subpackage/test_messages.proto";
 
 // This file contains comprehensive set of supported message structures and
 // data types. Unit tests depends on the plugin-processed version of this file.
@@ -179,3 +181,15 @@
   repeated Sub2_V2 sub2_rep = 12;
   optional Sub2_V2 sub2_lazy = 13 [lazy = true];
 }
+
+message DifferentPackages {
+  optional subpackage.Message subpackage_message = 1;
+  optional subpackage.Message.NestedMessage subpackage_nested_message = 2;
+  optional subpackage.Enum subpackage_enum = 3;
+  optional subpackage.Message.NestedEnum subpackage_nested_enum = 4;
+
+  optional .other_package.Message otherpackage_message = 5;
+  optional .other_package.Message.NestedMessage otherpackage_nested_message = 6;
+  optional .other_package.Enum otherpackage_enum = 7;
+  optional .other_package.Message.NestedEnum otherpackage_nested_enum = 8;
+}
diff --git a/src/protozero/test/protozero_conformance_unittest.cc b/src/protozero/test/protozero_conformance_unittest.cc
index 74e2204..3a4b7cf 100644
--- a/src/protozero/test/protozero_conformance_unittest.cc
+++ b/src/protozero/test/protozero_conformance_unittest.cc
@@ -27,14 +27,20 @@
 #include "src/protozero/test/example_proto/extensions.pb.h"
 #include "src/protozero/test/example_proto/extensions.pbzero.h"
 #include "src/protozero/test/example_proto/library.pbzero.h"
+#include "src/protozero/test/example_proto/other_package/test_messages.pbzero.h"
+#include "src/protozero/test/example_proto/subpackage/test_messages.pbzero.h"
 #include "src/protozero/test/example_proto/test_messages.pb.h"
 #include "src/protozero/test/example_proto/test_messages.pbzero.h"
 
 // Generated by the protozero plugin.
 namespace pbtest = protozero::test::protos::pbzero;
+namespace pbtest_subpackage = protozero::test::protos::subpackage::pbzero;
+namespace pbtest_otherpackage = other_package::pbzero;
 
 // Generated by the official protobuf compiler.
 namespace pbgold = protozero::test::protos;
+namespace pbgold_subpackage = protozero::test::protos::subpackage;
+namespace pbgold_other_package = other_package;
 
 namespace protozero {
 namespace {
@@ -310,5 +316,58 @@
                "PING");
 }
 
+TEST(ProtoZeroConformanceTest, DifferentPackages) {
+  HeapBuffered<pbtest::DifferentPackages> msg{kChunkSize, kChunkSize};
+
+  // Pupulate fields defined in "protozero.test.protos.subpackage"
+  pbtest_subpackage::Message* msgSubpackage = msg->set_subpackage_message();
+  msgSubpackage->set_field_int32(1);
+  msgSubpackage->set_field_enum(pbtest_subpackage::Enum::A);
+  msgSubpackage->set_field_nested_enum(
+      pbtest_subpackage::Message::NestedEnum::C);
+  msg->set_subpackage_nested_message()->set_field_int32(2);
+  msg->set_subpackage_enum(pbtest_subpackage::Enum::B);
+  msg->set_subpackage_nested_enum(pbtest_subpackage::Message_NestedEnum::D);
+
+  // Pupulate fields defined in "other_package"
+  pbtest_otherpackage::Message* msgOtherPackage =
+      msg->set_otherpackage_message();
+  msgOtherPackage->set_field_int32(11);
+  msgOtherPackage->set_field_enum(pbtest_otherpackage::Enum::A);
+  msgOtherPackage->set_field_nested_enum(
+      pbtest_otherpackage::Message::NestedEnum::C);
+  msg->set_otherpackage_nested_message()->set_field_int32(12);
+  msg->set_otherpackage_enum(pbtest_otherpackage::Enum::B);
+  msg->set_otherpackage_nested_enum(pbtest_otherpackage::Message_NestedEnum::D);
+
+  // Deserialize into golden proto
+  std::string serialized = msg.SerializeAsString();
+  pbgold::DifferentPackages gold_msg;
+  gold_msg.ParseFromString(serialized);
+  EXPECT_EQ(serialized.size(), static_cast<size_t>(gold_msg.ByteSizeLong()));
+
+  // Check fields defined in "protozero.test.protos.subpackage"
+  EXPECT_EQ(1, gold_msg.subpackage_message().field_int32());
+  EXPECT_EQ(pbgold_subpackage::Enum::A,
+            gold_msg.subpackage_message().field_enum());
+  EXPECT_EQ(pbgold_subpackage::Message_NestedEnum_C,
+            gold_msg.subpackage_message().field_nested_enum());
+  EXPECT_EQ(2, gold_msg.subpackage_nested_message().field_int32());
+  EXPECT_EQ(pbgold_subpackage::Enum::B, gold_msg.subpackage_enum());
+  EXPECT_EQ(pbgold_subpackage::Message_NestedEnum_D,
+            gold_msg.subpackage_nested_enum());
+
+  // Check fields defined in "other_package"
+  EXPECT_EQ(11, gold_msg.otherpackage_message().field_int32());
+  EXPECT_EQ(pbgold_other_package::Enum::A,
+            gold_msg.otherpackage_message().field_enum());
+  EXPECT_EQ(pbgold_other_package::Message_NestedEnum_C,
+            gold_msg.otherpackage_message().field_nested_enum());
+  EXPECT_EQ(12, gold_msg.otherpackage_nested_message().field_int32());
+  EXPECT_EQ(pbgold_other_package::Enum::B, gold_msg.otherpackage_enum());
+  EXPECT_EQ(pbgold_other_package::Message_NestedEnum_D,
+            gold_msg.otherpackage_nested_enum());
+}
+
 }  // namespace
 }  // namespace protozero
diff --git a/src/shared_lib/test/protos/other_package/test_messages.pzc.h b/src/shared_lib/test/protos/other_package/test_messages.pzc.h
new file mode 100644
index 0000000..f6a8260
--- /dev/null
+++ b/src/shared_lib/test/protos/other_package/test_messages.pzc.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Autogenerated by the ProtoZero C compiler plugin.
+// Invoked by tools/gen_c_protos
+// DO NOT EDIT.
+#ifndef SRC_SHARED_LIB_TEST_PROTOS_OTHER_PACKAGE_TEST_MESSAGES_PZC_H_
+#define SRC_SHARED_LIB_TEST_PROTOS_OTHER_PACKAGE_TEST_MESSAGES_PZC_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "perfetto/public/pb_macros.h"
+
+PERFETTO_PB_MSG_DECL(other_package_Message_NestedMessage);
+
+PERFETTO_PB_ENUM(other_package_Enum){
+    PERFETTO_PB_ENUM_ENTRY(other_package_A) = 10,
+    PERFETTO_PB_ENUM_ENTRY(other_package_B) = 11,
+};
+
+PERFETTO_PB_ENUM_IN_MSG(other_package_Message, NestedEnum){
+    PERFETTO_PB_ENUM_IN_MSG_ENTRY(other_package_Message, C) = 12,
+    PERFETTO_PB_ENUM_IN_MSG_ENTRY(other_package_Message, D) = 13,
+};
+
+PERFETTO_PB_MSG(other_package_Message);
+PERFETTO_PB_FIELD(other_package_Message, VARINT, int32_t, field_int32, 1);
+PERFETTO_PB_FIELD(other_package_Message,
+                  VARINT,
+                  enum other_package_Enum,
+                  field_enum,
+                  2);
+PERFETTO_PB_FIELD(other_package_Message,
+                  VARINT,
+                  enum other_package_Message_NestedEnum,
+                  field_nested_enum,
+                  3);
+PERFETTO_PB_FIELD(other_package_Message,
+                  MSG,
+                  other_package_Message_NestedMessage,
+                  field_nested_message,
+                  4);
+
+PERFETTO_PB_MSG(other_package_Message_NestedMessage);
+PERFETTO_PB_FIELD(other_package_Message_NestedMessage,
+                  VARINT,
+                  int32_t,
+                  field_int32,
+                  1);
+
+#endif  // SRC_SHARED_LIB_TEST_PROTOS_OTHER_PACKAGE_TEST_MESSAGES_PZC_H_
diff --git a/src/shared_lib/test/protos/subpackage/test_messages.pzc.h b/src/shared_lib/test/protos/subpackage/test_messages.pzc.h
new file mode 100644
index 0000000..24f963a
--- /dev/null
+++ b/src/shared_lib/test/protos/subpackage/test_messages.pzc.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Autogenerated by the ProtoZero C compiler plugin.
+// Invoked by tools/gen_c_protos
+// DO NOT EDIT.
+#ifndef SRC_SHARED_LIB_TEST_PROTOS_SUBPACKAGE_TEST_MESSAGES_PZC_H_
+#define SRC_SHARED_LIB_TEST_PROTOS_SUBPACKAGE_TEST_MESSAGES_PZC_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "perfetto/public/pb_macros.h"
+
+PERFETTO_PB_MSG_DECL(protozero_test_protos_subpackage_Message_NestedMessage);
+
+PERFETTO_PB_ENUM(protozero_test_protos_subpackage_Enum){
+    PERFETTO_PB_ENUM_ENTRY(protozero_test_protos_subpackage_A) = 1,
+    PERFETTO_PB_ENUM_ENTRY(protozero_test_protos_subpackage_B) = 2,
+};
+
+PERFETTO_PB_ENUM_IN_MSG(protozero_test_protos_subpackage_Message, NestedEnum){
+    PERFETTO_PB_ENUM_IN_MSG_ENTRY(protozero_test_protos_subpackage_Message,
+                                  C) = 3,
+    PERFETTO_PB_ENUM_IN_MSG_ENTRY(protozero_test_protos_subpackage_Message,
+                                  D) = 4,
+};
+
+PERFETTO_PB_MSG(protozero_test_protos_subpackage_Message);
+PERFETTO_PB_FIELD(protozero_test_protos_subpackage_Message,
+                  VARINT,
+                  int32_t,
+                  field_int32,
+                  1);
+PERFETTO_PB_FIELD(protozero_test_protos_subpackage_Message,
+                  VARINT,
+                  enum protozero_test_protos_subpackage_Enum,
+                  field_enum,
+                  2);
+PERFETTO_PB_FIELD(protozero_test_protos_subpackage_Message,
+                  VARINT,
+                  enum protozero_test_protos_subpackage_Message_NestedEnum,
+                  field_nested_enum,
+                  3);
+PERFETTO_PB_FIELD(protozero_test_protos_subpackage_Message,
+                  MSG,
+                  protozero_test_protos_subpackage_Message_NestedMessage,
+                  field_nested_message,
+                  4);
+
+PERFETTO_PB_MSG(protozero_test_protos_subpackage_Message_NestedMessage);
+PERFETTO_PB_FIELD(protozero_test_protos_subpackage_Message_NestedMessage,
+                  VARINT,
+                  int32_t,
+                  field_int32,
+                  1);
+
+#endif  // SRC_SHARED_LIB_TEST_PROTOS_SUBPACKAGE_TEST_MESSAGES_PZC_H_
diff --git a/src/shared_lib/test/protos/test_messages.pzc.h b/src/shared_lib/test/protos/test_messages.pzc.h
index 5198ee7..e566576 100644
--- a/src/shared_lib/test/protos/test_messages.pzc.h
+++ b/src/shared_lib/test/protos/test_messages.pzc.h
@@ -25,7 +25,11 @@
 
 #include "perfetto/public/pb_macros.h"
 #include "src/shared_lib/test/protos/library.pzc.h"
+#include "src/shared_lib/test/protos/other_package/test_messages.pzc.h"
+#include "src/shared_lib/test/protos/subpackage/test_messages.pzc.h"
 
+PERFETTO_PB_MSG_DECL(other_package_Message);
+PERFETTO_PB_MSG_DECL(other_package_Message_NestedMessage);
 PERFETTO_PB_MSG_DECL(protozero_test_protos_EveryField);
 PERFETTO_PB_MSG_DECL(protozero_test_protos_NestedA_NestedB);
 PERFETTO_PB_MSG_DECL(protozero_test_protos_NestedA_NestedB_NestedC);
@@ -33,6 +37,8 @@
 PERFETTO_PB_MSG_DECL(protozero_test_protos_TestVersioning_V2_Sub1_V2);
 PERFETTO_PB_MSG_DECL(protozero_test_protos_TestVersioning_V2_Sub2_V2);
 PERFETTO_PB_MSG_DECL(protozero_test_protos_TransgalacticMessage);
+PERFETTO_PB_MSG_DECL(protozero_test_protos_subpackage_Message);
+PERFETTO_PB_MSG_DECL(protozero_test_protos_subpackage_Message_NestedMessage);
 
 PERFETTO_PB_ENUM(protozero_test_protos_SmallEnum){
     PERFETTO_PB_ENUM_ENTRY(protozero_test_protos_TO_BE) = 1,
@@ -71,6 +77,48 @@
     PERFETTO_PB_ENUM_IN_MSG_ENTRY(protozero_test_protos_EveryField, PONG) = 2,
 };
 
+PERFETTO_PB_MSG(protozero_test_protos_DifferentPackages);
+PERFETTO_PB_FIELD(protozero_test_protos_DifferentPackages,
+                  MSG,
+                  protozero_test_protos_subpackage_Message,
+                  subpackage_message,
+                  1);
+PERFETTO_PB_FIELD(protozero_test_protos_DifferentPackages,
+                  MSG,
+                  protozero_test_protos_subpackage_Message_NestedMessage,
+                  subpackage_nested_message,
+                  2);
+PERFETTO_PB_FIELD(protozero_test_protos_DifferentPackages,
+                  VARINT,
+                  enum protozero_test_protos_subpackage_Enum,
+                  subpackage_enum,
+                  3);
+PERFETTO_PB_FIELD(protozero_test_protos_DifferentPackages,
+                  VARINT,
+                  enum protozero_test_protos_subpackage_Message_NestedEnum,
+                  subpackage_nested_enum,
+                  4);
+PERFETTO_PB_FIELD(protozero_test_protos_DifferentPackages,
+                  MSG,
+                  other_package_Message,
+                  otherpackage_message,
+                  5);
+PERFETTO_PB_FIELD(protozero_test_protos_DifferentPackages,
+                  MSG,
+                  other_package_Message_NestedMessage,
+                  otherpackage_nested_message,
+                  6);
+PERFETTO_PB_FIELD(protozero_test_protos_DifferentPackages,
+                  VARINT,
+                  enum other_package_Enum,
+                  otherpackage_enum,
+                  7);
+PERFETTO_PB_FIELD(protozero_test_protos_DifferentPackages,
+                  VARINT,
+                  enum other_package_Message_NestedEnum,
+                  otherpackage_nested_enum,
+                  8);
+
 PERFETTO_PB_MSG(protozero_test_protos_TestVersioning_V2);
 PERFETTO_PB_FIELD(protozero_test_protos_TestVersioning_V2,
                   VARINT,
diff --git a/src/tools/ftrace_proto_gen/event_list b/src/tools/ftrace_proto_gen/event_list
index b4c80bc..ccb0eee 100644
--- a/src/tools/ftrace_proto_gen/event_list
+++ b/src/tools/ftrace_proto_gen/event_list
@@ -484,3 +484,4 @@
 perf_trace_counters/sched_switch_with_ctrs
 power/gpu_work_period
 rpm/rpm_status
+panel/panel_write_generic
\ No newline at end of file
diff --git a/src/trace_processor/importers/ftrace/ftrace_descriptors.cc b/src/trace_processor/importers/ftrace/ftrace_descriptors.cc
index a88cc26..21435f2 100644
--- a/src/trace_processor/importers/ftrace/ftrace_descriptors.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_descriptors.cc
@@ -24,7 +24,7 @@
 namespace trace_processor {
 namespace {
 
-std::array<FtraceMessageDescriptor, 490> descriptors{{
+std::array<FtraceMessageDescriptor, 491> descriptors{{
     {nullptr, 0, {}},
     {nullptr, 0, {}},
     {nullptr, 0, {}},
@@ -5396,6 +5396,19 @@
             {"status", ProtoSchemaType::kInt32},
         },
     },
+    {
+        "panel_write_generic",
+        6,
+        {
+            {},
+            {"pid", ProtoSchemaType::kInt32},
+            {"trace_name", ProtoSchemaType::kString},
+            {"trace_begin", ProtoSchemaType::kUint32},
+            {"name", ProtoSchemaType::kString},
+            {"type", ProtoSchemaType::kUint32},
+            {"value", ProtoSchemaType::kInt32},
+        },
+    },
 }};
 
 }  // namespace
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.cc b/src/trace_processor/importers/ftrace/ftrace_parser.cc
index 95cf0d8..6e1e1bb 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.cc
@@ -1319,7 +1319,7 @@
     StringId name_id = message_strings.field_name_ids[field_id];
 
     // Check if this field represents a kernel function.
-    const auto* it = std::find_if(
+    const auto it = std::find_if(
         kKernelFunctionFields.begin(), kKernelFunctionFields.end(),
         [ftrace_id, field_id](const FtraceEventAndFieldId& ev) {
           return ev.event_id == ftrace_id && ev.field_id == field_id;
diff --git a/src/traced/probes/ftrace/event_info.cc b/src/traced/probes/ftrace/event_info.cc
index 46b7d24..6db79ff 100644
--- a/src/traced/probes/ftrace/event_info.cc
+++ b/src/traced/probes/ftrace/event_info.cc
@@ -7361,6 +7361,31 @@
        kUnsetFtraceId,
        430,
        kUnsetSize},
+      {"panel_write_generic",
+       "panel",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "pid", 1, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "trace_name", 2, ProtoSchemaType::kString,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "trace_begin", 3, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "name", 4, ProtoSchemaType::kString,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "type", 5, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "value", 6, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       490,
+       kUnsetSize},
       {"sched_switch_with_ctrs",
        "perf_trace_counters",
        {
diff --git a/src/traced/probes/ftrace/ftrace_config_muxer.cc b/src/traced/probes/ftrace/ftrace_config_muxer.cc
index 7fcfef1..97969be 100644
--- a/src/traced/probes/ftrace/ftrace_config_muxer.cc
+++ b/src/traced/probes/ftrace/ftrace_config_muxer.cc
@@ -203,6 +203,9 @@
         AddEventGroup(table, "g2d", &events);
         InsertEvent("g2d", "tracing_mark_write", &events);
         InsertEvent("g2d", "g2d_perf_update_qos", &events);
+
+        AddEventGroup(table, "panel", &events);
+        InsertEvent("panel", "panel_write_generic", &events);
         continue;
       }
 
diff --git a/src/traced/probes/ftrace/test/data/synthetic/available_events b/src/traced/probes/ftrace/test/data/synthetic/available_events
index c57e8f9..299c4ac 100644
--- a/src/traced/probes/ftrace/test/data/synthetic/available_events
+++ b/src/traced/probes/ftrace/test/data/synthetic/available_events
@@ -13,3 +13,4 @@
 power:suspend_resume
 cpuhp:cpuhp_pause
 lwis:tracing_mark_write
+panel:panel_write_generic
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/panel/panel_write_generic/format b/src/traced/probes/ftrace/test/data/synthetic/events/panel/panel_write_generic/format
new file mode 100644
index 0000000..550e6ef
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/panel/panel_write_generic/format
@@ -0,0 +1,14 @@
+name: panel_write_generic
+ID: 1122
+format:
+	field:unsigned short common_type;	offset:0;	size:2;	signed:0;
+	field:unsigned char common_flags;	offset:2;	size:1;	signed:0;
+	field:unsigned char common_preempt_count;	offset:3;	size:1;	signed:0;
+	field:int common_pid;	offset:4;	size:4;	signed:1;
+
+	field:char type;	offset:8;	size:1;	signed:0;
+	field:int pid;	offset:12;	size:4;	signed:1;
+	field:__data_loc char[] name;	offset:16;	size:4;	signed:0;
+	field:int value;	offset:20;	size:4;	signed:1;
+
+print fmt: "%c|%d|%s|%d", REC->type, REC->pid, __get_str(name), REC->value
diff --git a/tools/gen_c_protos b/tools/gen_c_protos
index 38a7491..370e59a 100755
--- a/tools/gen_c_protos
+++ b/tools/gen_c_protos
@@ -49,6 +49,8 @@
     'files': [
       'src/protozero/test/example_proto/library.proto',
       'src/protozero/test/example_proto/library_internals/galaxies.proto',
+      'src/protozero/test/example_proto/other_package/test_messages.proto',
+      'src/protozero/test/example_proto/subpackage/test_messages.proto',
       'src/protozero/test/example_proto/test_messages.proto',
       'src/protozero/test/example_proto/upper_import.proto',
     ],
diff --git a/ui/src/frontend/slice_details.ts b/ui/src/frontend/slice_details.ts
index 08fd411..21fb39d 100644
--- a/ui/src/frontend/slice_details.ts
+++ b/ui/src/frontend/slice_details.ts
@@ -16,7 +16,6 @@
 
 import {BigintMath} from '../base/bigint_math';
 import {sqliteString} from '../base/string_utils';
-import {duration, time} from '../base/time';
 import {exists} from '../base/utils';
 import {Anchor} from '../widgets/anchor';
 import {MenuItem, PopupMenu2} from '../widgets/menu';
@@ -24,7 +23,6 @@
 import {SqlRef} from '../widgets/sql_ref';
 import {Tree, TreeNode} from '../widgets/tree';
 
-import {globals} from './globals';
 import {SliceDetails} from './sql/slice';
 import {
   BreakdownByThreadState,
@@ -36,15 +34,6 @@
 import {DurationWidget} from './widgets/duration';
 import {Timestamp} from './widgets/timestamp';
 
-function computeDuration(ts: time, dur: duration): m.Children {
-  if (dur === -1n) {
-    const minDuration = globals.state.traceTime.end - ts;
-    return [m(DurationWidget, {dur: minDuration}), ' (Did not end)'];
-  } else {
-    return m(DurationWidget, {dur});
-  }
-}
-
 // Renders a widget storing all of the generic details for a slice from the
 // slice table.
 export function renderDetails(
@@ -92,7 +81,7 @@
         TreeNode,
         {
           left: 'Duration',
-          right: computeDuration(slice.ts, slice.dur),
+          right: m(DurationWidget, {dur: slice.dur}),
         },
         exists(durationBreakdown) &&
           slice.dur > 0 &&
@@ -148,7 +137,7 @@
     return m(TreeNode, {
       left: 'Thread duration',
       right: [
-        computeDuration(sliceInfo.threadTs, sliceInfo.threadDur),
+        m(DurationWidget, {dur: sliceInfo.threadDur}),
         threadDurFractionSuffix,
       ],
     });
diff --git a/ui/src/frontend/slice_details_panel.ts b/ui/src/frontend/slice_details_panel.ts
index e9658c2..fb5d3a2 100644
--- a/ui/src/frontend/slice_details_panel.ts
+++ b/ui/src/frontend/slice_details_panel.ts
@@ -126,7 +126,7 @@
       return m(TreeNode, {
         icon: 'timer',
         left: 'Thread Duration',
-        right: this.computeDuration(sliceInfo.threadTs, sliceInfo.threadDur),
+        right: m(DurationWidget, {dur: sliceInfo.threadDur}),
       });
     } else {
       return null;
@@ -189,7 +189,7 @@
         }),
         m(TreeNode, {
           left: 'Duration',
-          right: this.computeDuration(sliceInfo.ts, sliceInfo.dur),
+          right: m(DurationWidget, {dur: sliceInfo.dur}),
         }),
         this.renderThreadDuration(sliceInfo),
         m(TreeNode, {
diff --git a/ui/src/frontend/slice_panel.ts b/ui/src/frontend/slice_panel.ts
index c087e19..aef97ca 100644
--- a/ui/src/frontend/slice_panel.ts
+++ b/ui/src/frontend/slice_panel.ts
@@ -14,11 +14,9 @@
 
 import m from 'mithril';
 
-import {duration, time} from '../base/time';
 import {exists} from '../base/utils';
 
-import {globals, SliceDetails} from './globals';
-import {DurationWidget} from './widgets/duration';
+import {SliceDetails} from './globals';
 
 // To display process or thread, we want to concatenate their name with ID, but
 // either can be undefined and all the cases need to be considered carefully to
@@ -39,15 +37,6 @@
 }
 
 export abstract class SlicePanel implements m.ClassComponent {
-  protected computeDuration(ts: time, dur: duration): m.Children {
-    if (dur === -1n) {
-      const minDuration = globals.state.traceTime.end - ts;
-      return [m(DurationWidget, {dur: minDuration}), ' (Did not end)'];
-    } else {
-      return m(DurationWidget, {dur});
-    }
-  }
-
   protected getProcessThreadDetails(sliceInfo: SliceDetails) {
     return new Map<string, string | undefined>([
       ['Thread', getDisplayName(sliceInfo.threadName, sliceInfo.tid)],
diff --git a/ui/src/frontend/widgets/duration.ts b/ui/src/frontend/widgets/duration.ts
index c0357fb..6a05ab4 100644
--- a/ui/src/frontend/widgets/duration.ts
+++ b/ui/src/frontend/widgets/duration.ts
@@ -38,6 +38,9 @@
 export class DurationWidget implements m.ClassComponent<DurationWidgetAttrs> {
   view({attrs}: m.Vnode<DurationWidgetAttrs>) {
     const {dur} = attrs;
+    if (dur === -1n) {
+      return '(Did not end)';
+    }
     return m(
       PopupMenu2,
       {