Merge "Fix crash when recreating producer."
diff --git a/Android.bp b/Android.bp
index c6468df..f780212 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1651,6 +1651,7 @@
   name: "perfetto_protos_perfetto_trace_minimal_lite_gen",
   srcs: [
     "protos/perfetto/trace/clock_snapshot.proto",
+    "protos/perfetto/trace/system_info.proto",
   ],
   tools: [
     "aprotoc",
@@ -1658,6 +1659,7 @@
   cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/clock_snapshot.pb.cc",
+    "external/perfetto/protos/perfetto/trace/system_info.pb.cc",
   ],
 }
 
@@ -1666,6 +1668,7 @@
   name: "perfetto_protos_perfetto_trace_minimal_lite_gen_headers",
   srcs: [
     "protos/perfetto/trace/clock_snapshot.proto",
+    "protos/perfetto/trace/system_info.proto",
   ],
   tools: [
     "aprotoc",
@@ -1673,6 +1676,7 @@
   cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/clock_snapshot.pb.h",
+    "external/perfetto/protos/perfetto/trace/system_info.pb.h",
   ],
   export_include_dirs: [
     "protos",
@@ -2146,6 +2150,7 @@
   name: "perfetto_protos_perfetto_trace_zero_gen",
   srcs: [
     "protos/perfetto/trace/clock_snapshot.proto",
+    "protos/perfetto/trace/system_info.proto",
     "protos/perfetto/trace/test_event.proto",
     "protos/perfetto/trace/trace.proto",
     "protos/perfetto/trace/trace_packet.proto",
@@ -2157,6 +2162,7 @@
   cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/clock_snapshot.pbzero.cc",
+    "external/perfetto/protos/perfetto/trace/system_info.pbzero.cc",
     "external/perfetto/protos/perfetto/trace/test_event.pbzero.cc",
     "external/perfetto/protos/perfetto/trace/trace.pbzero.cc",
     "external/perfetto/protos/perfetto/trace/trace_packet.pbzero.cc",
@@ -2168,6 +2174,7 @@
   name: "perfetto_protos_perfetto_trace_zero_gen_headers",
   srcs: [
     "protos/perfetto/trace/clock_snapshot.proto",
+    "protos/perfetto/trace/system_info.proto",
     "protos/perfetto/trace/test_event.proto",
     "protos/perfetto/trace/trace.proto",
     "protos/perfetto/trace/trace_packet.proto",
@@ -2179,6 +2186,7 @@
   cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/clock_snapshot.pbzero.h",
+    "external/perfetto/protos/perfetto/trace/system_info.pbzero.h",
     "external/perfetto/protos/perfetto/trace/test_event.pbzero.h",
     "external/perfetto/protos/perfetto/trace/trace.pbzero.h",
     "external/perfetto/protos/perfetto/trace/trace_packet.pbzero.h",
@@ -2945,7 +2953,6 @@
     "src/tracing/core/packet_stream_validator_unittest.cc",
     "src/tracing/core/patch_list_unittest.cc",
     "src/tracing/core/process_stats_config.cc",
-    "src/tracing/core/service_impl_unittest.cc",
     "src/tracing/core/shared_memory_abi.cc",
     "src/tracing/core/shared_memory_abi_unittest.cc",
     "src/tracing/core/shared_memory_arbiter_impl.cc",
@@ -2967,6 +2974,7 @@
     "src/tracing/core/trace_writer_impl.cc",
     "src/tracing/core/trace_writer_impl_unittest.cc",
     "src/tracing/core/tracing_service_impl.cc",
+    "src/tracing/core/tracing_service_impl_unittest.cc",
     "src/tracing/core/virtual_destructors.cc",
     "src/tracing/ipc/consumer/consumer_ipc_client_impl.cc",
     "src/tracing/ipc/default_socket.cc",
diff --git a/protos/perfetto/trace/BUILD.gn b/protos/perfetto/trace/BUILD.gn
index 81dcad2..5369d9a 100644
--- a/protos/perfetto/trace/BUILD.gn
+++ b/protos/perfetto/trace/BUILD.gn
@@ -18,7 +18,10 @@
 
 # Common protos used by both the ":minimal_lite" target (for the service) and
 # the generic ":lite" target
-proto_sources_minimal = [ "clock_snapshot.proto" ]
+proto_sources_minimal = [
+  "clock_snapshot.proto",
+  "system_info.proto",
+]
 
 proto_sources_trusted = [ "trusted_packet.proto" ]
 
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index 08242bc..96561d1 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -2403,6 +2403,67 @@
 
 // End of protos/perfetto/trace/power/power_rails.proto
 
+// Begin of protos/perfetto/trace/profiling/profile_packet.proto
+
+message ProfilePacket {
+  // either a function or library name.
+  repeated InternedString strings = 1;
+  message InternedString {
+    optional uint64 id = 1;
+    optional bytes str = 2;
+  }
+
+  repeated Frame frames = 2;
+  message Frame {
+    optional uint64 id = 1;  // Interning key
+    // E.g. "fopen"
+    optional uint64 function_name_id = 2;  // id of string.
+    optional uint64 mapping_id = 3;
+    optional uint64 rel_pc = 4;
+  }
+
+  repeated Callstack callstacks = 3;
+  message Callstack {
+    optional uint64 id = 1;
+    // Frames of this callstack. Bottom frame first.
+    repeated uint64 frame_ids = 2;
+  }
+
+  repeated Mapping mappings = 4;
+  message Mapping {
+    optional uint64 id = 1;        // Interning key.
+    optional uint64 build_id = 2;  // Interning key.
+    optional uint64 offset = 3;
+    optional uint64 start = 4;
+    optional uint64 end = 5;
+    optional uint64 load_bias = 6;
+    // E.g. ["system", "lib64", "libc.so"]
+    repeated uint64 path_string_ids = 7;  // id of string.
+  }
+
+  message HeapSample {
+    optional uint64 callstack_id = 1;
+    // bytes allocated at this frame.
+    optional uint64 self_allocated = 2;
+    // bytes freed at this frame.
+    optional uint64 self_freed = 3;
+    optional uint64 timestamp = 4;  // timestamp [opt]
+    optional uint64 alloc_count = 5;
+    optional uint64 free_count = 6;
+  }
+
+  repeated ProcessHeapSamples process_dumps = 5;
+  message ProcessHeapSamples {
+    optional uint64 pid = 1;
+    repeated HeapSample samples = 2;
+  }
+
+  optional bool continued = 6;
+  optional uint64 index = 7;
+}
+
+// End of protos/perfetto/trace/profiling/profile_packet.proto
+
 // Begin of protos/perfetto/trace/ps/process_stats.proto
 
 // Per-process periodically sampled stats. These samples are wrapped in a
@@ -2532,6 +2593,21 @@
 
 // End of protos/perfetto/trace/sys_stats/sys_stats.proto
 
+// Begin of protos/perfetto/trace/system_info.proto
+
+message Utsname {
+  optional string sysname = 1;
+  optional string version = 2;
+  optional string release = 3;
+  optional string machine = 4;
+}
+
+message SystemInfo {
+  optional Utsname utsname = 1;
+}
+
+// End of protos/perfetto/trace/system_info.proto
+
 // Begin of protos/perfetto/trace/trace.proto
 
 message Trace {
@@ -2552,7 +2628,7 @@
 // TracePacket(s).
 //
 // Next reserved id: 13 (up to 15).
-// Next id: 45.
+// Next id: 46.
 message TracePacket {
   // TODO(primiano): in future we should add a timestamp_clock_domain field to
   // allow mixing timestamps from different clock domains.
@@ -2578,6 +2654,7 @@
     BatteryCounters battery = 38;
     PowerRails power_rails = 40;
     AndroidLogPacket android_log = 39;
+    SystemInfo system_info = 45;
 
     // Only used by TrackEvent.
     ProcessDescriptor process_descriptor = 43;
@@ -2920,67 +2997,6 @@
 
 // End of protos/perfetto/trace/track_event/track_event.proto
 
-// Begin of protos/perfetto/trace/profiling/profile_packet.proto
-
-message ProfilePacket {
-  // either a function or library name.
-  repeated InternedString strings = 1;
-  message InternedString {
-    optional uint64 id = 1;
-    optional bytes str = 2;
-  }
-
-  repeated Frame frames = 2;
-  message Frame {
-    optional uint64 id = 1;  // Interning key
-    // E.g. "fopen"
-    optional uint64 function_name_id = 2;  // id of string.
-    optional uint64 mapping_id = 3;
-    optional uint64 rel_pc = 4;
-  }
-
-  repeated Callstack callstacks = 3;
-  message Callstack {
-    optional uint64 id = 1;
-    // Frames of this callstack. Bottom frame first.
-    repeated uint64 frame_ids = 2;
-  }
-
-  repeated Mapping mappings = 4;
-  message Mapping {
-    optional uint64 id = 1;  // Interning key.
-    optional uint64 build_id = 2;  // Interning key.
-    optional uint64 offset = 3;
-    optional uint64 start = 4;
-    optional uint64 end = 5;
-    optional uint64 load_bias = 6;
-    // E.g. ["system", "lib64", "libc.so"]
-    repeated uint64 path_string_ids = 7;  // id of string.
-  }
-
-  message HeapSample {
-    optional uint64 callstack_id = 1;
-    // bytes allocated at this frame.
-    optional uint64 self_allocated = 2;
-    // bytes freed at this frame.
-    optional uint64 self_freed = 3;
-    optional uint64 timestamp = 4;  // timestamp [opt]
-    optional uint64 alloc_count = 5;
-    optional uint64 free_count = 6;
-  }
-
-  repeated ProcessHeapSamples process_dumps = 5;
-  message ProcessHeapSamples {
-    optional uint64 pid = 1;
-    repeated HeapSample samples = 2;
-  }
-
-  optional bool continued = 6;
-  optional uint64 index = 7;
-}
-
-// End of protos/perfetto/trace/profiling/profile_packet.proto
-
 // Begin of protos/perfetto/config/android/android_log_config.proto
 
 message AndroidLogConfig {
diff --git a/protos/perfetto/trace/profiling/profile_packet.proto b/protos/perfetto/trace/profiling/profile_packet.proto
index 91818b4..9e32fcc 100644
--- a/protos/perfetto/trace/profiling/profile_packet.proto
+++ b/protos/perfetto/trace/profiling/profile_packet.proto
@@ -45,7 +45,7 @@
 
   repeated Mapping mappings = 4;
   message Mapping {
-    optional uint64 id = 1;  // Interning key.
+    optional uint64 id = 1;        // Interning key.
     optional uint64 build_id = 2;  // Interning key.
     optional uint64 offset = 3;
     optional uint64 start = 4;
diff --git a/protos/perfetto/trace/system_info.proto b/protos/perfetto/trace/system_info.proto
new file mode 100644
index 0000000..28f4763
--- /dev/null
+++ b/protos/perfetto/trace/system_info.proto
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 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";
+option optimize_for = LITE_RUNTIME;
+
+package perfetto.protos;
+
+message Utsname {
+  optional string sysname = 1;
+  optional string version = 2;
+  optional string release = 3;
+  optional string machine = 4;
+}
+
+message SystemInfo {
+  optional Utsname utsname = 1;
+}
diff --git a/protos/perfetto/trace/trace_packet.proto b/protos/perfetto/trace/trace_packet.proto
index 44f61ac..846e961 100644
--- a/protos/perfetto/trace/trace_packet.proto
+++ b/protos/perfetto/trace/trace_packet.proto
@@ -22,9 +22,6 @@
 import "perfetto/trace/android/android_log.proto";
 import "perfetto/trace/chrome/chrome_trace_event.proto";
 import "perfetto/trace/clock_snapshot.proto";
-import "perfetto/trace/track_event/process_descriptor.proto";
-import "perfetto/trace/track_event/thread_descriptor.proto";
-import "perfetto/trace/track_event/track_event.proto";
 import "perfetto/trace/filesystem/inode_file_map.proto";
 import "perfetto/trace/ftrace/ftrace_event_bundle.proto";
 import "perfetto/trace/ftrace/ftrace_stats.proto";
@@ -35,7 +32,11 @@
 import "perfetto/trace/ps/process_stats.proto";
 import "perfetto/trace/ps/process_tree.proto";
 import "perfetto/trace/sys_stats/sys_stats.proto";
+import "perfetto/trace/system_info.proto";
 import "perfetto/trace/test_event.proto";
+import "perfetto/trace/track_event/process_descriptor.proto";
+import "perfetto/trace/track_event/thread_descriptor.proto";
+import "perfetto/trace/track_event/track_event.proto";
 
 package perfetto.protos;
 
@@ -43,7 +44,7 @@
 // TracePacket(s).
 //
 // Next reserved id: 13 (up to 15).
-// Next id: 45.
+// Next id: 46.
 message TracePacket {
   // TODO(primiano): in future we should add a timestamp_clock_domain field to
   // allow mixing timestamps from different clock domains.
@@ -69,6 +70,7 @@
     BatteryCounters battery = 38;
     PowerRails power_rails = 40;
     AndroidLogPacket android_log = 39;
+    SystemInfo system_info = 45;
 
     // Only used by TrackEvent.
     ProcessDescriptor process_descriptor = 43;
diff --git a/protos/perfetto/trace/trusted_packet.proto b/protos/perfetto/trace/trusted_packet.proto
index f656b37..0e04c9a 100644
--- a/protos/perfetto/trace/trusted_packet.proto
+++ b/protos/perfetto/trace/trusted_packet.proto
@@ -29,6 +29,7 @@
 import "perfetto/common/trace_stats.proto";
 import "perfetto/config/trace_config.proto";
 import "perfetto/trace/clock_snapshot.proto";
+import "perfetto/trace/system_info.proto";
 
 package perfetto.protos;
 
@@ -56,4 +57,5 @@
   TraceStats trace_stats = 35;
   bytes synchronization_marker = 36;
   uint32 previous_packet_dropped = 42;
+  SystemInfo system_info = 45;
 }
diff --git a/src/trace_processor/args_table.cc b/src/trace_processor/args_table.cc
index 505a907..b628d14 100644
--- a/src/trace_processor/args_table.cc
+++ b/src/trace_processor/args_table.cc
@@ -103,24 +103,27 @@
     case VariadicType::kInt: {
       bool op_is_null = sqlite_utils::IsOpIsNull(op);
       auto predicate = sqlite_utils::CreateNumericPredicate<int64_t>(op, value);
-      index->FilterRows([this, &predicate, op_is_null](uint32_t row) {
-        const auto& arg = storage_->args().arg_values()[row];
-        return arg.type == type_ ? predicate(arg.int_value) : op_is_null;
-      });
+      index->FilterRows(
+          [this, predicate, op_is_null](uint32_t row) PERFETTO_ALWAYS_INLINE {
+            const auto& arg = storage_->args().arg_values()[row];
+            return arg.type == type_ ? predicate(arg.int_value) : op_is_null;
+          });
       break;
     }
     case VariadicType::kReal: {
       bool op_is_null = sqlite_utils::IsOpIsNull(op);
       auto predicate = sqlite_utils::CreateNumericPredicate<double>(op, value);
-      index->FilterRows([this, &predicate, op_is_null](uint32_t row) {
-        const auto& arg = storage_->args().arg_values()[row];
-        return arg.type == type_ ? predicate(arg.real_value) : op_is_null;
-      });
+      index->FilterRows(
+          [this, predicate, op_is_null](uint32_t row) PERFETTO_ALWAYS_INLINE {
+            const auto& arg = storage_->args().arg_values()[row];
+            return arg.type == type_ ? predicate(arg.real_value) : op_is_null;
+          });
       break;
     }
     case VariadicType::kString: {
       auto predicate = sqlite_utils::CreateStringPredicate(op, value);
-      index->FilterRows([this, &predicate](uint32_t row) {
+      index->FilterRows([this,
+                         &predicate](uint32_t row) PERFETTO_ALWAYS_INLINE {
         const auto& arg = storage_->args().arg_values()[row];
         return arg.type == type_
                    ? predicate(storage_->GetString(arg.string_value).c_str())
diff --git a/src/trace_processor/counters_table.h b/src/trace_processor/counters_table.h
index d672350..84ff4d8 100644
--- a/src/trace_processor/counters_table.h
+++ b/src/trace_processor/counters_table.h
@@ -17,11 +17,12 @@
 #ifndef SRC_TRACE_PROCESSOR_COUNTERS_TABLE_H_
 #define SRC_TRACE_PROCESSOR_COUNTERS_TABLE_H_
 
-#include "src/trace_processor/storage_table.h"
-
 #include <deque>
 #include <memory>
 #include <string>
+#include <vector>
+
+#include "src/trace_processor/storage_table.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -67,7 +68,7 @@
   };
 
  private:
-  std::deque<std::string> ref_types_;
+  std::vector<std::string> ref_types_;
   const TraceStorage* const storage_;
 };
 }  // namespace trace_processor
diff --git a/src/trace_processor/filtered_row_index.h b/src/trace_processor/filtered_row_index.h
index 275d83d..d8dcaeb 100644
--- a/src/trace_processor/filtered_row_index.h
+++ b/src/trace_processor/filtered_row_index.h
@@ -54,8 +54,8 @@
 
   // Calls |fn| on each row index which is currently to be returned and retains
   // row index if |fn| returns true or discards the row otherwise.
-  template <typename Predicate>
-  void FilterRows(Predicate fn) {
+  template <typename RowPredicate /* (uint32_t) -> bool */>
+  void FilterRows(RowPredicate fn) {
     PERFETTO_DCHECK(error_.empty());
 
     switch (mode_) {
@@ -99,10 +99,11 @@
   template <typename Predicate>
   void FilterAllRows(Predicate fn) {
     mode_ = Mode::kBitVector;
-    row_filter_.resize(end_row_ - start_row_, true);
+    row_filter_.resize(end_row_ - start_row_, false);
 
-    for (uint32_t i = start_row_; i < end_row_; i++) {
-      row_filter_[i - start_row_] = fn(i);
+    for (auto i = start_row_; i < end_row_; i++) {
+      if (fn(i))
+        row_filter_[i - start_row_] = true;
     }
   }
 
@@ -110,10 +111,12 @@
   void FilterBitVector(Predicate fn) {
     auto b = row_filter_.begin();
     auto e = row_filter_.end();
+
     using std::find;
     for (auto it = find(b, e, true); it != e; it = find(it + 1, e, true)) {
       auto filter_idx = static_cast<uint32_t>(std::distance(b, it));
-      *it = fn(start_row_ + filter_idx);
+      auto value_it = start_row_ + filter_idx;
+      *it = fn(value_it);
     }
   }
 
diff --git a/src/trace_processor/instants_table.h b/src/trace_processor/instants_table.h
index f19f5fc..baee499 100644
--- a/src/trace_processor/instants_table.h
+++ b/src/trace_processor/instants_table.h
@@ -17,6 +17,9 @@
 #ifndef SRC_TRACE_PROCESSOR_INSTANTS_TABLE_H_
 #define SRC_TRACE_PROCESSOR_INSTANTS_TABLE_H_
 
+#include <string>
+#include <vector>
+
 #include "src/trace_processor/storage_table.h"
 #include "src/trace_processor/trace_storage.h"
 
@@ -35,7 +38,7 @@
   int BestIndex(const QueryConstraints&, BestIndexInfo*) override;
 
  private:
-  std::deque<std::string> ref_types_;
+  std::vector<std::string> ref_types_;
   const TraceStorage* const storage_;
 };
 }  // namespace trace_processor
diff --git a/src/trace_processor/process_table.cc b/src/trace_processor/process_table.cc
index 09e42c7..7aac10e 100644
--- a/src/trace_processor/process_table.cc
+++ b/src/trace_processor/process_table.cc
@@ -108,8 +108,7 @@
     case Column::kName: {
       const auto& process = storage_->GetProcess(current);
       const auto& name = storage_->GetString(process.name_id);
-      sqlite3_result_text(context, name.c_str(),
-                          static_cast<int>(name.length()), kSqliteStatic);
+      sqlite3_result_text(context, name.c_str(), -1, kSqliteStatic);
       break;
     }
     case Column::kPid: {
diff --git a/src/trace_processor/process_tracker.cc b/src/trace_processor/process_tracker.cc
index 5fc15cc..33fdfcc 100644
--- a/src/trace_processor/process_tracker.cc
+++ b/src/trace_processor/process_tracker.cc
@@ -123,13 +123,15 @@
                                         base::StringView name) {
   auto proc_name_id = context_->storage->InternString(name);
 
+  base::Optional<UniquePid> pupid;
+  if (ppid.has_value()) {
+    pupid = GetOrCreateProcess(ppid.value(), 0 /* start_ns */).first;
+  }
   UniquePid upid;
   TraceStorage::Process* process;
   std::tie(upid, process) = GetOrCreateProcess(pid, 0 /* start_ns */);
   process->name_id = proc_name_id;
-  if (ppid.has_value()) {
-    process->pupid = GetOrCreateProcess(ppid.value(), 0 /* start_ns */).first;
-  }
+  process->pupid = pupid;
   return upid;
 }
 
diff --git a/src/trace_processor/sqlite_utils.h b/src/trace_processor/sqlite_utils.h
index 9974e05..7f5d732 100644
--- a/src/trace_processor/sqlite_utils.h
+++ b/src/trace_processor/sqlite_utils.h
@@ -90,6 +90,10 @@
   return op == SQLITE_INDEX_CONSTRAINT_ISNULL;
 }
 
+inline bool IsOpIsNotNull(int op) {
+  return op == SQLITE_INDEX_CONSTRAINT_ISNOTNULL;
+}
+
 template <typename T>
 T ExtractSqliteValue(sqlite3_value* value);
 
@@ -140,34 +144,46 @@
   return std::string(extracted);
 }
 
-template <typename T, typename sqlite_utils::is_numeric<T>* = nullptr>
-std::function<bool(T)> CreateNumericPredicate(int op, sqlite3_value* value) {
-  switch (op) {
-    case SQLITE_INDEX_CONSTRAINT_ISNULL:
-      return [](T) { return false; };
-    case SQLITE_INDEX_CONSTRAINT_ISNOTNULL:
-      return [](T) { return true; };
+template <typename T>
+class NumericPredicate {
+ public:
+  NumericPredicate(int op, T constant) : op_(op), constant_(constant) {}
+
+  PERFETTO_ALWAYS_INLINE bool operator()(T other) const {
+    switch (op_) {
+      case SQLITE_INDEX_CONSTRAINT_ISNULL:
+        return false;
+      case SQLITE_INDEX_CONSTRAINT_ISNOTNULL:
+        return true;
+      case SQLITE_INDEX_CONSTRAINT_EQ:
+      case SQLITE_INDEX_CONSTRAINT_IS:
+        return std::equal_to<T>()(other, constant_);
+      case SQLITE_INDEX_CONSTRAINT_NE:
+      case SQLITE_INDEX_CONSTRAINT_ISNOT:
+        return std::not_equal_to<T>()(other, constant_);
+      case SQLITE_INDEX_CONSTRAINT_GE:
+        return std::greater_equal<T>()(other, constant_);
+      case SQLITE_INDEX_CONSTRAINT_GT:
+        return std::greater<T>()(other, constant_);
+      case SQLITE_INDEX_CONSTRAINT_LE:
+        return std::less_equal<T>()(other, constant_);
+      case SQLITE_INDEX_CONSTRAINT_LT:
+        return std::less<T>()(other, constant_);
+      default:
+        PERFETTO_FATAL("For GCC");
+    }
   }
 
-  T val = ExtractSqliteValue<T>(value);
-  switch (op) {
-    case SQLITE_INDEX_CONSTRAINT_EQ:
-    case SQLITE_INDEX_CONSTRAINT_IS:
-      return [val](T f) { return std::equal_to<T>()(f, val); };
-    case SQLITE_INDEX_CONSTRAINT_NE:
-    case SQLITE_INDEX_CONSTRAINT_ISNOT:
-      return [val](T f) { return std::not_equal_to<T>()(f, val); };
-    case SQLITE_INDEX_CONSTRAINT_GE:
-      return [val](T f) { return f >= val; };
-    case SQLITE_INDEX_CONSTRAINT_GT:
-      return [val](T f) { return f > val; };
-    case SQLITE_INDEX_CONSTRAINT_LE:
-      return [val](T f) { return f <= val; };
-    case SQLITE_INDEX_CONSTRAINT_LT:
-      return [val](T f) { return f < val; };
-    default:
-      PERFETTO_FATAL("For GCC");
-  }
+ private:
+  int op_;
+  T constant_;
+};
+
+template <typename T, typename sqlite_utils::is_numeric<T>* = nullptr>
+NumericPredicate<T> CreateNumericPredicate(int op, sqlite3_value* value) {
+  T extracted =
+      IsOpIsNull(op) || IsOpIsNotNull(op) ? 0 : ExtractSqliteValue<T>(value);
+  return NumericPredicate<T>(op, extracted);
 }
 
 inline std::function<bool(const char*)> CreateStringPredicate(
diff --git a/src/trace_processor/storage_columns.cc b/src/trace_processor/storage_columns.cc
index 8dfc381..25cda66 100644
--- a/src/trace_processor/storage_columns.cc
+++ b/src/trace_processor/storage_columns.cc
@@ -46,7 +46,7 @@
                          sqlite3_value* value,
                          FilteredRowIndex* index) const {
   auto predicate = sqlite_utils::CreateNumericPredicate<int64_t>(op, value);
-  index->FilterRows([this, &predicate](uint32_t row) {
+  index->FilterRows([this, predicate](uint32_t row) PERFETTO_ALWAYS_INLINE {
     return predicate((*ts_start_)[row] + (*dur_)[row]);
   });
 }
diff --git a/src/trace_processor/storage_columns.h b/src/trace_processor/storage_columns.h
index 0a3f158..7945bd7 100644
--- a/src/trace_processor/storage_columns.h
+++ b/src/trace_processor/storage_columns.h
@@ -16,8 +16,10 @@
 #define SRC_TRACE_PROCESSOR_STORAGE_COLUMNS_H_
 
 #include <deque>
+#include <limits>
 #include <memory>
 #include <string>
+#include <vector>
 
 #include "src/trace_processor/filtered_row_index.h"
 #include "src/trace_processor/sqlite_utils.h"
@@ -199,9 +201,11 @@
                       sqlite3_value* value,
                       FilteredRowIndex* index) const {
     auto predicate = sqlite_utils::CreateNumericPredicate<C>(op, value);
-    index->FilterRows([this, &predicate](uint32_t row) {
+    auto cast_predicate = [this,
+                           predicate](uint32_t row) PERFETTO_ALWAYS_INLINE {
       return predicate(static_cast<C>((*deque_)[row]));
-    });
+    };
+    index->FilterRows(cast_predicate);
   }
 
   bool is_naturally_ordered_ = false;
@@ -212,7 +216,7 @@
  public:
   StringColumn(std::string col_name,
                const std::deque<Id>* deque,
-               const std::deque<std::string>* string_map,
+               const std::vector<std::string>* string_map,
                bool hidden = false)
       : StorageColumn(col_name, hidden),
         deque_(deque),
@@ -258,7 +262,7 @@
 
  private:
   const std::deque<Id>* deque_ = nullptr;
-  const std::deque<std::string>* string_map_ = nullptr;
+  const std::vector<std::string>* string_map_ = nullptr;
 };
 
 // Column which represents the "ts_end" column present in all time based
@@ -268,7 +272,7 @@
   TsEndColumn(std::string col_name,
               const std::deque<int64_t>* ts_start,
               const std::deque<int64_t>* dur);
-  virtual ~TsEndColumn() override;
+  ~TsEndColumn() override;
 
   void ReportResult(sqlite3_context*, uint32_t) const override;
 
@@ -295,7 +299,7 @@
 class IdColumn final : public StorageColumn {
  public:
   IdColumn(std::string column_name, TableId table_id);
-  virtual ~IdColumn() override;
+  ~IdColumn() override;
 
   void ReportResult(sqlite3_context* ctx, uint32_t row) const override {
     auto id = TraceStorage::CreateRowId(table_id_, row);
@@ -308,7 +312,7 @@
               sqlite3_value* value,
               FilteredRowIndex* index) const override {
     auto predicate = sqlite_utils::CreateNumericPredicate<RowId>(op, value);
-    index->FilterRows([this, &predicate](uint32_t row) {
+    index->FilterRows([this, predicate](uint32_t row) PERFETTO_ALWAYS_INLINE {
       return predicate(TraceStorage::CreateRowId(table_id_, row));
     });
   }
diff --git a/src/trace_processor/storage_schema.h b/src/trace_processor/storage_schema.h
index ae77643..ee7452d 100644
--- a/src/trace_processor/storage_schema.h
+++ b/src/trace_processor/storage_schema.h
@@ -16,6 +16,11 @@
 #define SRC_TRACE_PROCESSOR_STORAGE_SCHEMA_H_
 
 #include <algorithm>
+#include <deque>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
 
 #include "src/trace_processor/filtered_row_index.h"
 #include "src/trace_processor/sqlite_utils.h"
@@ -63,7 +68,7 @@
     template <class Id>
     Builder& AddStringColumn(std::string column_name,
                              const std::deque<Id>* ids,
-                             const std::deque<std::string>* string_map) {
+                             const std::vector<std::string>* string_map) {
       columns_.emplace_back(new StringColumn<Id>(column_name, ids, string_map));
       return *this;
     }
diff --git a/src/trace_processor/thread_table.cc b/src/trace_processor/thread_table.cc
index 0a38faa..a641243 100644
--- a/src/trace_processor/thread_table.cc
+++ b/src/trace_processor/thread_table.cc
@@ -118,8 +118,7 @@
     }
     case Column::kName: {
       const auto& name = storage_->GetString(thread.name_id);
-      sqlite3_result_text(context, name.c_str(),
-                          static_cast<int>(name.length()), kSqliteStatic);
+      sqlite3_result_text(context, name.c_str(), -1, kSqliteStatic);
       break;
     }
     case Column::kTid: {
diff --git a/src/trace_processor/trace_storage.h b/src/trace_processor/trace_storage.h
index 22a3427..ca942db 100644
--- a/src/trace_processor/trace_storage.h
+++ b/src/trace_processor/trace_storage.h
@@ -610,7 +610,7 @@
   const RawEvents& raw_events() const { return raw_events_; }
   RawEvents* mutable_raw_events() { return &raw_events_; }
 
-  const std::deque<std::string>& string_pool() const { return string_pool_; }
+  const std::vector<std::string>& string_pool() const { return string_pool_; }
 
   // |unique_processes_| always contains at least 1 element becuase the 0th ID
   // is reserved to indicate an invalid process.
@@ -644,13 +644,15 @@
   Args args_;
 
   // One entry for each unique string in the trace.
-  std::deque<std::string> string_pool_;
+  std::vector<std::string> string_pool_;
 
   // One entry for each unique string in the trace.
   std::unordered_map<StringHash, StringId> string_index_;
 
   // One entry for each UniquePid, with UniquePid as the index.
-  std::deque<Process> unique_processes_;
+  // Never hold on to pointers to Process, as vector resize will
+  // invalidate them.
+  std::vector<Process> unique_processes_;
 
   // One entry for each UniqueTid, with UniqueTid as the index.
   std::deque<Thread> unique_threads_;
diff --git a/src/tracing/BUILD.gn b/src/tracing/BUILD.gn
index 06969cc..b018f84 100644
--- a/src/tracing/BUILD.gn
+++ b/src/tracing/BUILD.gn
@@ -113,10 +113,10 @@
   # has no Windows implementation.
   if (!is_win) {
     sources += [
-      "core/service_impl_unittest.cc",
       "core/shared_memory_arbiter_impl_unittest.cc",
       "core/startup_trace_writer_unittest.cc",
       "core/trace_writer_impl_unittest.cc",
+      "core/tracing_service_impl_unittest.cc",
       "test/fake_producer_endpoint.h",
       "test/mock_consumer.cc",
       "test/mock_consumer.h",
diff --git a/src/tracing/core/trace_buffer.cc b/src/tracing/core/trace_buffer.cc
index 266a08b..4af9b6d 100644
--- a/src/tracing/core/trace_buffer.cc
+++ b/src/tracing/core/trace_buffer.cc
@@ -663,17 +663,25 @@
 
       if (action == kReadOnePacket) {
         // The easy peasy case B.
-        if (PERFETTO_LIKELY(ReadNextPacketInChunk(chunk_meta, packet))) {
+        ReadPacketResult result = ReadNextPacketInChunk(chunk_meta, packet);
+
+        if (PERFETTO_LIKELY(result == ReadPacketResult::kSucceeded)) {
           *sequence_properties = {trusted_producer_id, trusted_uid, writer_id};
           *previous_packet_on_sequence_dropped = previous_packet_dropped;
           return true;
+        } else if (result == ReadPacketResult::kFailedEmptyPacket) {
+          // We can ignore and skip empty packets.
+          PERFETTO_DCHECK(packet->slices().empty());
+          continue;
         }
 
         // In extremely rare cases (producer bugged / malicious) the chunk might
         // contain an invalid fragment. In such case we don't want to stall the
-        // sequence but just skip the chunk and move on.
-        stats_.set_abi_violations(stats_.abi_violations() + 1);
-        PERFETTO_DCHECK(suppress_sanity_dchecks_for_testing_);
+        // sequence but just skip the chunk and move on. ReadNextPacketInChunk()
+        // marks the chunk as fully read, so we don't attempt to read from it
+        // again in a future call to ReadBuffers(). It also already records an
+        // abi violation for this.
+        PERFETTO_DCHECK(result == ReadPacketResult::kFailedInvalidPacket);
         chunk_meta->set_last_read_packet_skipped(true);
         previous_packet_dropped = true;
         break;
@@ -771,10 +779,11 @@
       PERFETTO_DCHECK(read_iter_.is_valid());
       TRACE_BUFFER_DLOG("    commit chunk %u", read_iter_.chunk_id());
       if (PERFETTO_LIKELY((*read_iter_).num_fragments > 0)) {
-        // In the unlikely case of a corrupted packet, invalidate the all
-        // stitching and move on to the next chunk in the same sequence,
-        // if any.
-        packet_corruption |= !ReadNextPacketInChunk(&*read_iter_, packet);
+        // In the unlikely case of a corrupted packet (corrupted or empty
+        // fragment), invalidate the all stitching and move on to the next chunk
+        // in the same sequence, if any.
+        packet_corruption |= ReadNextPacketInChunk(&*read_iter_, packet) ==
+                             ReadPacketResult::kFailedInvalidPacket;
       }
       if (read_iter_.cur == it.cur)
         break;
@@ -783,8 +792,7 @@
     PERFETTO_DCHECK(read_iter_.cur == it.cur);
 
     if (PERFETTO_UNLIKELY(packet_corruption)) {
-      stats_.set_abi_violations(stats_.abi_violations() + 1);
-      PERFETTO_DCHECK(suppress_sanity_dchecks_for_testing_);
+      // ReadNextPacketInChunk() already records an abi violation for this case.
       *packet = TracePacket();  // clear.
       return ReadAheadResult::kFailedStayOnSameSequence;
     }
@@ -794,8 +802,9 @@
   return ReadAheadResult::kFailedMoveToNextSequence;
 }
 
-bool TraceBuffer::ReadNextPacketInChunk(ChunkMeta* chunk_meta,
-                                        TracePacket* packet) {
+TraceBuffer::ReadPacketResult TraceBuffer::ReadNextPacketInChunk(
+    ChunkMeta* chunk_meta,
+    TracePacket* packet) {
   PERFETTO_DCHECK(chunk_meta->num_fragments_read < chunk_meta->num_fragments);
   PERFETTO_DCHECK(!(chunk_meta->flags & kChunkNeedsPatching));
 
@@ -811,7 +820,14 @@
     // contains more packets beyond its boundaries.
     stats_.set_abi_violations(stats_.abi_violations() + 1);
     PERFETTO_DCHECK(suppress_sanity_dchecks_for_testing_);
-    return false;
+    chunk_meta->cur_fragment_offset = 0;
+    chunk_meta->num_fragments_read = chunk_meta->num_fragments;
+    if (PERFETTO_LIKELY(chunk_meta->is_complete())) {
+      stats_.set_chunks_read(stats_.chunks_read() + 1);
+      stats_.set_bytes_read(stats_.bytes_read() +
+                            chunk_meta->chunk_record->size);
+    }
+    return ReadPacketResult::kFailedInvalidPacket;
   }
 
   // A packet (or a fragment) starts with a varint stating its size, followed
@@ -836,8 +852,9 @@
       stats_.set_bytes_read(stats_.bytes_read() +
                             chunk_meta->chunk_record->size);
     }
-    return false;
+    return ReadPacketResult::kFailedInvalidPacket;
   }
+
   chunk_meta->cur_fragment_offset =
       static_cast<uint16_t>(next_packet - packets_begin);
   chunk_meta->num_fragments_read++;
@@ -849,17 +866,15 @@
     stats_.set_bytes_read(stats_.bytes_read() + chunk_meta->chunk_record->size);
   }
 
-  if (PERFETTO_UNLIKELY(packet_size == 0)) {
-    stats_.set_abi_violations(stats_.abi_violations() + 1);
-    PERFETTO_DCHECK(suppress_sanity_dchecks_for_testing_);
-    return false;
-  }
+  chunk_meta->set_last_read_packet_skipped(false);
+
+  if (PERFETTO_UNLIKELY(packet_size == 0))
+    return ReadPacketResult::kFailedEmptyPacket;
 
   if (PERFETTO_LIKELY(packet))
     packet->AddSlice(packet_data, static_cast<size_t>(packet_size));
 
-  chunk_meta->set_last_read_packet_skipped(false);
-  return true;
+  return ReadPacketResult::kSucceeded;
 }
 
 void TraceBuffer::DiscardWrite() {
diff --git a/src/tracing/core/trace_buffer.h b/src/tracing/core/trace_buffer.h
index cd32fdc..d86037a 100644
--- a/src/tracing/core/trace_buffer.h
+++ b/src/tracing/core/trace_buffer.h
@@ -481,6 +481,12 @@
     kFailedStayOnSameSequence,
   };
 
+  enum class ReadPacketResult {
+    kSucceeded,
+    kFailedInvalidPacket,
+    kFailedEmptyPacket,
+  };
+
   explicit TraceBuffer(OverwritePolicy);
   TraceBuffer(const TraceBuffer&) = delete;
   TraceBuffer& operator=(const TraceBuffer&) = delete;
@@ -537,7 +543,7 @@
   // TracePacket can be nullptr, in which case the read state is still advanced.
   // When TracePacket is not nullptr, ProducerID must also be not null and will
   // be updated with the ProducerID that originally wrote the chunk.
-  bool ReadNextPacketInChunk(ChunkMeta*, TracePacket*);
+  ReadPacketResult ReadNextPacketInChunk(ChunkMeta*, TracePacket*);
 
   void DcheckIsAlignedAndWithinBounds(const uint8_t* ptr) const {
     PERFETTO_DCHECK(ptr >= begin() && ptr <= end() - sizeof(ChunkRecord));
diff --git a/src/tracing/core/trace_buffer_unittest.cc b/src/tracing/core/trace_buffer_unittest.cc
index 07a17b4..b1f60ca 100644
--- a/src/tracing/core/trace_buffer_unittest.cc
+++ b/src/tracing/core/trace_buffer_unittest.cc
@@ -461,6 +461,23 @@
   ASSERT_THAT(ReadPacket(), IsEmpty());
 }
 
+// Verify that empty packets are skipped.
+TEST_F(TraceBufferTest, ReadWrite_EmptyPacket) {
+  ResetBuffer(4096);
+  CreateChunk(ProducerID(1), WriterID(1), 0)
+      .AddPacket(42, 1)
+      .AddPacket(1, 2)
+      .AddPacket(42, 3)
+      .CopyIntoTraceBuffer();
+
+  trace_buffer()->BeginRead();
+  ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(42, 1)));
+  ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(42, 3)));
+  ASSERT_THAT(ReadPacket(), IsEmpty());
+
+  EXPECT_EQ(0u, trace_buffer()->stats().abi_violations());
+}
+
 // --------------------------------------
 // Fragments stitching and skipping logic
 // --------------------------------------
@@ -1071,7 +1088,8 @@
 }
 
 // Like the Malicious_ZeroVarintHeader, but put the chunk in the middle of a
-// sequence that would be otherwise valid.
+// sequence that would be otherwise valid. The zero-sized fragment should be
+// skipped.
 TEST_F(TraceBufferTest, Malicious_ZeroVarintHeaderInSequence) {
   ResetBuffer(4096);
   SuppressSanityDchecksForTesting();
@@ -1094,17 +1112,17 @@
       .CopyIntoTraceBuffer();
 
   trace_buffer()->BeginRead();
+  ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(4, 'a'),
+                                        FakePacketFragment(4, 'c')));
   ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(4, 'd')));
   ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(4, 'e')));
   ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(5, 'f')));
   ASSERT_THAT(ReadPacket(), IsEmpty());
 }
 
-// Similar to Malicious_ZeroVarintHeader, but this time the zero-sized fragment
-// is the last fragment for a chunk and is marked for continuation.
-// One might argue that this case is borderline legit, and in this case we
-// should just read a packet consisting of (4, 'c'). However this is too complex
-// to support and doesn't bring any benefit.
+// Similar to Malicious_ZeroVarintHeaderInSequence, but this time the zero-sized
+// fragment is the last fragment for a chunk and is marked for continuation. The
+// zero-sized fragment should be skipped.
 TEST_F(TraceBufferTest, Malicious_ZeroVarintHeaderAtEndOfChunk) {
   ResetBuffer(4096);
   SuppressSanityDchecksForTesting();
@@ -1126,6 +1144,7 @@
 
   trace_buffer()->BeginRead();
   ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(4, 'a')));
+  ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(4, 'c')));
   ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(4, 'd')));
   ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(4, 'e')));
   ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(4, 'f')));
diff --git a/src/tracing/core/tracing_service_impl.cc b/src/tracing/core/tracing_service_impl.cc
index 9c00ec9..4091ead 100644
--- a/src/tracing/core/tracing_service_impl.cc
+++ b/src/tracing/core/tracing_service_impl.cc
@@ -25,6 +25,7 @@
 
 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
 #include <sys/uio.h>
+#include <sys/utsname.h>
 #include <unistd.h>
 #endif
 
@@ -50,6 +51,7 @@
 #include "src/tracing/core/trace_buffer.h"
 
 #include "perfetto/trace/clock_snapshot.pb.h"
+#include "perfetto/trace/system_info.pb.h"
 #include "perfetto/trace/trusted_packet.pb.h"
 
 // General note: this class must assume that Producers are malicious and will
@@ -1093,6 +1095,7 @@
       SnapshotClocks(&packets);
   }
   MaybeEmitTraceConfig(tracing_session, &packets);
+  MaybeEmitSystemInfo(tracing_session, &packets);
 
   size_t packets_bytes = 0;  // SUM(slice.size() for each slice in |packets|).
   size_t total_slices = 0;   // SUM(#slices in |packets|).
@@ -1813,6 +1816,32 @@
   packets->back().AddSlice(std::move(slice));
 }
 
+void TracingServiceImpl::MaybeEmitSystemInfo(
+    TracingSession* tracing_session,
+    std::vector<TracePacket>* packets) {
+  if (tracing_session->did_emit_system_info)
+    return;
+  tracing_session->did_emit_system_info = true;
+  protos::TrustedPacket packet;
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  protos::SystemInfo* info = packet.mutable_system_info();
+  struct utsname uname_info;
+  if (uname(&uname_info) == 0) {
+    protos::Utsname* utsname_info = info->mutable_utsname();
+    utsname_info->set_sysname(uname_info.sysname);
+    utsname_info->set_version(uname_info.version);
+    utsname_info->set_machine(uname_info.machine);
+    utsname_info->set_release(uname_info.release);
+  }
+#endif
+  packet.set_trusted_uid(static_cast<int32_t>(uid_));
+  packet.set_trusted_packet_sequence_id(kServicePacketSequenceID);
+  Slice slice = Slice::Allocate(static_cast<size_t>(packet.ByteSize()));
+  PERFETTO_CHECK(packet.SerializeWithCachedSizesToArray(slice.own_data()));
+  packets->emplace_back();
+  packets->back().AddSlice(std::move(slice));
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // TracingServiceImpl::ConsumerEndpointImpl implementation
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/src/tracing/core/tracing_service_impl.h b/src/tracing/core/tracing_service_impl.h
index d3e33ca..3731336 100644
--- a/src/tracing/core/tracing_service_impl.h
+++ b/src/tracing/core/tracing_service_impl.h
@@ -366,6 +366,9 @@
     // Whether we mirrored the trace config back to the trace output yet.
     bool did_emit_config = false;
 
+    // Whether we put the system info into the trace output yet.
+    bool did_emit_system_info = false;
+
     State state = DISABLED;
 
     // If the consumer detached the session, this variable defines the key used
@@ -410,6 +413,7 @@
   void SnapshotStats(TracingSession*, std::vector<TracePacket>*);
   TraceStats GetTraceStats(TracingSession* tracing_session);
   void MaybeEmitTraceConfig(TracingSession*, std::vector<TracePacket>*);
+  void MaybeEmitSystemInfo(TracingSession*, std::vector<TracePacket>*);
   void OnFlushTimeout(TracingSessionID, FlushRequestID);
   void OnDisableTracingTimeout(TracingSessionID);
   void DisableTracingNotifyConsumerAndFlushFile(TracingSession*);
diff --git a/src/tracing/core/service_impl_unittest.cc b/src/tracing/core/tracing_service_impl_unittest.cc
similarity index 99%
rename from src/tracing/core/service_impl_unittest.cc
rename to src/tracing/core/tracing_service_impl_unittest.cc
index 81f4634..eca7d6d 100644
--- a/src/tracing/core/service_impl_unittest.cc
+++ b/src/tracing/core/tracing_service_impl_unittest.cc
@@ -430,7 +430,11 @@
   producer->WaitForDataSourceSetup("data_source");
   producer->WaitForDataSourceStart("data_source");
 
-  static const int kNumPreamblePackets = 4;
+  // The preamble packets are:
+  // Config
+  // SystemInfo
+  // 3x unknown
+  static const int kNumPreamblePackets = 5;
   static const int kNumTestPackets = 10;
   static const char kPayload[] = "1234567890abcdef-";
 
diff --git a/src/tracing/test/fake_packet.cc b/src/tracing/test/fake_packet.cc
index 3486ca9..210f5fa 100644
--- a/src/tracing/test/fake_packet.cc
+++ b/src/tracing/test/fake_packet.cc
@@ -29,9 +29,9 @@
 namespace perfetto {
 
 FakePacketFragment::FakePacketFragment(size_t size, char prefix) {
-  // |size| has to be at least == 2, because one byte will be taken just by the
+  // |size| has to be at least == 1, because one byte will be taken just by the
   // varint header.
-  PERFETTO_CHECK(size > 1);
+  PERFETTO_CHECK(size >= 1);
 
   // Finding the |payload_size| from |size| is quite tricky:
   // A packet with 127 bytes of payload requires:
diff --git a/src/tracing/test/tracing_integration_test.cc b/src/tracing/test/tracing_integration_test.cc
index fdac291..d536ee3 100644
--- a/src/tracing/test/tracing_integration_test.cc
+++ b/src/tracing/test/tracing_integration_test.cc
@@ -380,6 +380,8 @@
   protos::Trace tmp_trace;
   ASSERT_TRUE(tmp_trace.ParseFromArray(tmp_buf, static_cast<int>(rsize)));
   size_t num_test_packet = 0;
+  size_t num_clock_snapshot_packet = 0;
+  size_t num_system_info_packet = 0;
   bool saw_trace_stats = false;
   for (int i = 0; i < tmp_trace.packet_size(); i++) {
     const protos::TracePacket& packet = tmp_trace.packet(i);
@@ -389,9 +391,15 @@
     } else if (packet.has_trace_stats()) {
       saw_trace_stats = true;
       CheckTraceStats(packet);
+    } else if (packet.has_clock_snapshot()) {
+      num_clock_snapshot_packet++;
+    } else if (packet.has_system_info()) {
+      num_system_info_packet++;
     }
   }
   ASSERT_TRUE(saw_trace_stats);
+  ASSERT_GT(num_clock_snapshot_packet, 0u);
+  ASSERT_GT(num_system_info_packet, 0u);
 }
 
 // TODO(primiano): add tests to cover:
diff --git a/test/end_to_end_integrationtest.cc b/test/end_to_end_integrationtest.cc
index 5b43527..e1017d5 100644
--- a/test/end_to_end_integrationtest.cc
+++ b/test/end_to_end_integrationtest.cc
@@ -361,7 +361,7 @@
   ds_config->set_name("android.perfetto.FakeProducer");
   ds_config->set_target_buffer(0);
 
-  static constexpr size_t kNumPackets = 10;
+  static constexpr size_t kNumPackets = 11;
   static constexpr uint32_t kRandomSeed = 42;
   static constexpr uint32_t kMsgSize = 1024;
   ds_config->mutable_for_testing()->set_seed(kRandomSeed);
@@ -402,7 +402,7 @@
   ds_config->set_name("android.perfetto.FakeProducer");
   ds_config->set_target_buffer(0);
 
-  static constexpr size_t kNumPackets = 5;
+  static constexpr size_t kNumPackets = 7;
   static constexpr uint32_t kRandomSeed = 42;
   static constexpr uint32_t kMsgSize = 1024 * 1024 - 42;
   ds_config->mutable_for_testing()->set_seed(kRandomSeed);
@@ -438,7 +438,7 @@
   trace_config.set_duration_ms(10000);  // Max timeout, session is ended before.
   auto* ds_config = trace_config.add_data_sources()->mutable_config();
   ds_config->set_name("android.perfetto.FakeProducer");
-  static constexpr size_t kNumPackets = 10;
+  static constexpr size_t kNumPackets = 11;
   ds_config->mutable_for_testing()->set_message_count(kNumPackets);
   ds_config->mutable_for_testing()->set_message_size(32);
 
diff --git a/test/test_helper.cc b/test/test_helper.cc
index e019a5f..2111c77 100644
--- a/test/test_helper.cc
+++ b/test/test_helper.cc
@@ -64,7 +64,8 @@
     protos::TracePacket packet;
     ASSERT_TRUE(encoded_packet.Decode(&packet));
     if (packet.has_clock_snapshot() || packet.has_trace_config() ||
-        packet.has_trace_stats() || !packet.synchronization_marker().empty()) {
+        packet.has_trace_stats() || !packet.synchronization_marker().empty() ||
+        packet.has_system_info()) {
       continue;
     }
     ASSERT_EQ(protos::TracePacket::kTrustedUid,
diff --git a/tools/gen_merged_protos b/tools/gen_merged_protos
index 8c4ec83..ee12f10 100755
--- a/tools/gen_merged_protos
+++ b/tools/gen_merged_protos
@@ -68,9 +68,11 @@
   'protos/perfetto/trace/interned_data/interned_data.proto',
   'protos/perfetto/trace/power/battery_counters.proto',
   'protos/perfetto/trace/power/power_rails.proto',
+  'protos/perfetto/trace/profiling/profile_packet.proto',
   'protos/perfetto/trace/ps/process_stats.proto',
   'protos/perfetto/trace/ps/process_tree.proto',
   'protos/perfetto/trace/sys_stats/sys_stats.proto',
+  'protos/perfetto/trace/system_info.proto',
   'protos/perfetto/trace/trace.proto',
   'protos/perfetto/trace/trace_packet.proto',
   'protos/perfetto/trace/track_event/debug_annotation.proto',
@@ -78,7 +80,6 @@
   'protos/perfetto/trace/track_event/task_execution.proto',
   'protos/perfetto/trace/track_event/thread_descriptor.proto',
   'protos/perfetto/trace/track_event/track_event.proto',
-  'protos/perfetto/trace/profiling/profile_packet.proto',
 )
 
 MERGED_TRACE_PROTO = 'protos/perfetto/trace/perfetto_trace.proto'