Merge "tp: Implement interval intersect prototype" into main
diff --git a/Android.bp b/Android.bp
index e9d7c45..d9971c4 100644
--- a/Android.bp
+++ b/Android.bp
@@ -11939,6 +11939,7 @@
         "src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_sched_upid.cc",
         "src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_slice_layout.cc",
         "src/trace_processor/perfetto_sql/intrinsics/table_functions/flamegraph_construction_algorithms.cc",
+        "src/trace_processor/perfetto_sql/intrinsics/table_functions/interval_intersect.cc",
         "src/trace_processor/perfetto_sql/intrinsics/table_functions/table_info.cc",
     ],
 }
@@ -12065,6 +12066,7 @@
         "src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/timestamps.sql",
         "src/trace_processor/perfetto_sql/stdlib/graphs/dominator_tree.sql",
         "src/trace_processor/perfetto_sql/stdlib/graphs/search.sql",
+        "src/trace_processor/perfetto_sql/stdlib/intervals/intersect.sql",
         "src/trace_processor/perfetto_sql/stdlib/intervals/overlap.sql",
         "src/trace_processor/perfetto_sql/stdlib/linux/cpu_idle.sql",
         "src/trace_processor/perfetto_sql/stdlib/memory/heap_graph_dominator_tree.sql",
diff --git a/BUILD b/BUILD
index 84baca4..3496afc 100644
--- a/BUILD
+++ b/BUILD
@@ -2314,6 +2314,8 @@
         "src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_slice_layout.h",
         "src/trace_processor/perfetto_sql/intrinsics/table_functions/flamegraph_construction_algorithms.cc",
         "src/trace_processor/perfetto_sql/intrinsics/table_functions/flamegraph_construction_algorithms.h",
+        "src/trace_processor/perfetto_sql/intrinsics/table_functions/interval_intersect.cc",
+        "src/trace_processor/perfetto_sql/intrinsics/table_functions/interval_intersect.h",
         "src/trace_processor/perfetto_sql/intrinsics/table_functions/table_info.cc",
         "src/trace_processor/perfetto_sql/intrinsics/table_functions/table_info.h",
     ],
@@ -2461,6 +2463,7 @@
 perfetto_filegroup(
     name = "src_trace_processor_perfetto_sql_stdlib_intervals_intervals",
     srcs = [
+        "src/trace_processor/perfetto_sql/stdlib/intervals/intersect.sql",
         "src/trace_processor/perfetto_sql/stdlib/intervals/overlap.sql",
     ],
 )
diff --git a/python/generators/sql_processing/utils.py b/python/generators/sql_processing/utils.py
index 539115e..1a76e1f 100644
--- a/python/generators/sql_processing/utils.py
+++ b/python/generators/sql_processing/utils.py
@@ -110,6 +110,7 @@
 ALLOWED_PREFIXES = {
     'counters': 'counter',
     'chrome/util': 'cr',
+    'intervals': 'interval',
     'graphs': 'graph',
     'slices': 'slice'
 }
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/BUILD.gn b/src/trace_processor/perfetto_sql/intrinsics/table_functions/BUILD.gn
index 631942d..6edd50b 100644
--- a/src/trace_processor/perfetto_sql/intrinsics/table_functions/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/BUILD.gn
@@ -43,6 +43,8 @@
     "experimental_slice_layout.h",
     "flamegraph_construction_algorithms.cc",
     "flamegraph_construction_algorithms.h",
+    "interval_intersect.cc",
+    "interval_intersect.h",
     "table_info.cc",
     "table_info.h",
   ]
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/interval_intersect.cc b/src/trace_processor/perfetto_sql/intrinsics/table_functions/interval_intersect.cc
new file mode 100644
index 0000000..db83694
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/interval_intersect.cc
@@ -0,0 +1,218 @@
+/*
+ * 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.
+ */
+
+#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/interval_intersect.h"
+
+#include <algorithm>
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <optional>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "perfetto/base/compiler.h"
+#include "perfetto/base/logging.h"
+#include "perfetto/base/status.h"
+#include "perfetto/ext/base/status_or.h"
+#include "perfetto/protozero/proto_decoder.h"
+#include "perfetto/protozero/proto_utils.h"
+#include "perfetto/trace_processor/basic_types.h"
+#include "perfetto/trace_processor/status.h"
+#include "protos/perfetto/trace_processor/metrics_impl.pbzero.h"
+#include "src/trace_processor/containers/string_pool.h"
+#include "src/trace_processor/db/column.h"
+#include "src/trace_processor/db/table.h"
+#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/tables_py.h"
+#include "src/trace_processor/util/status_macros.h"
+
+namespace perfetto::trace_processor {
+namespace tables {
+IntervalIntersectTable::~IntervalIntersectTable() = default;
+}  // namespace tables
+
+namespace {
+
+using RepeatedDecoder = protos::pbzero::RepeatedBuilderResult::Decoder;
+using RepeatedIter = ::protozero::PackedRepeatedFieldIterator<
+    ::protozero::proto_utils::ProtoWireType::kFixed64,
+    int64_t>;
+
+base::StatusOr<RepeatedIter> DecodeArgument(const SqlValue& raw_arg,
+                                            const char* debug_name,
+                                            bool& parse_error) {
+  if (raw_arg.type != SqlValue::kBytes) {
+    return base::ErrStatus(
+        "interval_intersect: '%s' should be a repeated field", debug_name);
+  }
+  protos::pbzero::ProtoBuilderResult::Decoder proto_arg(
+      static_cast<const uint8_t*>(raw_arg.AsBytes()), raw_arg.bytes_count);
+  if (!proto_arg.is_repeated()) {
+    return base::ErrStatus(
+        "interval_intersect: '%s' is not generated by RepeatedField "
+        "function",
+        debug_name);
+  }
+
+  auto iter =
+      protos::pbzero::RepeatedBuilderResult::Decoder(proto_arg.repeated())
+          .int_values(&parse_error);
+  if (parse_error) {
+    return base::ErrStatus(
+        "interval_intersect: error when parsing '%s' values.", debug_name);
+  }
+
+  return iter;
+}
+
+struct Interval {
+  int64_t id;
+  int64_t ts;
+  int64_t dur;
+
+  int64_t end() { return ts + dur; }
+};
+
+struct IntervalsIterator {
+  RepeatedIter ids;
+  RepeatedIter tses;
+  RepeatedIter durs;
+
+  static base::StatusOr<IntervalsIterator> Create(
+      const SqlValue& raw_ids,
+      const SqlValue& raw_timestamps,
+      const SqlValue& raw_durs,
+      bool& parse_error) {
+    ASSIGN_OR_RETURN(RepeatedIter ids,
+                     DecodeArgument(raw_ids, "ids", parse_error));
+    ASSIGN_OR_RETURN(RepeatedIter tses,
+                     DecodeArgument(raw_timestamps, "timestamps", parse_error));
+    ASSIGN_OR_RETURN(RepeatedIter durs,
+                     DecodeArgument(raw_durs, "durations", parse_error));
+
+    return IntervalsIterator{ids, tses, durs};
+  }
+
+  void operator++() {
+    PERFETTO_DCHECK(ids && tses && durs);
+    ids++;
+    tses++;
+    durs++;
+  }
+
+  Interval operator*() const { return Interval{*ids, *tses, *durs}; }
+
+  explicit operator bool() const { return bool(ids); }
+};
+
+}  // namespace
+IntervalIntersect::IntervalIntersect(StringPool* pool) : pool_(pool) {}
+IntervalIntersect::~IntervalIntersect() = default;
+
+Table::Schema IntervalIntersect::CreateSchema() {
+  return tables::IntervalIntersectTable::ComputeStaticSchema();
+}
+
+std::string IntervalIntersect::TableName() {
+  return tables::IntervalIntersectTable::Name();
+}
+
+uint32_t IntervalIntersect::EstimateRowCount() {
+  // TODO(mayzner): Give proper estimate.
+  return 1024;
+}
+
+base::StatusOr<std::unique_ptr<Table>> IntervalIntersect::ComputeTable(
+    const std::vector<SqlValue>& arguments) {
+  PERFETTO_DCHECK(arguments.size() == 6);
+
+  bool parse_error = false;
+  ASSIGN_OR_RETURN(IntervalsIterator l_it,
+                   IntervalsIterator::Create(arguments[0], arguments[1],
+                                             arguments[2], parse_error));
+  ASSIGN_OR_RETURN(IntervalsIterator r_it,
+                   IntervalsIterator::Create(arguments[3], arguments[4],
+                                             arguments[5], parse_error));
+
+  // If there are no intervals in one of the tables then there are no intervals
+  // returned.
+  if (!l_it || !r_it) {
+    return std::unique_ptr<Table>(
+        std::make_unique<tables::IntervalIntersectTable>(pool_));
+  }
+
+  // We copy |l_it| and |r_it| for the second for loop.
+  IntervalsIterator l_it_2 = l_it;
+  IntervalsIterator r_it_2 = r_it;
+
+  auto table = std::make_unique<tables::IntervalIntersectTable>(pool_);
+
+  // Find all intersections where interval from right table started duringan
+  // interval from left table.
+  for (Interval l_i = *l_it; l_it && r_it && !parse_error;
+       ++l_it, l_i = *l_it) {
+    // If the next |r_i| starts after |l_i| ends, that means that we need to
+    // go the the next |l_i|, so we need to exit the loop.
+    for (Interval r_i = *r_it; r_it && r_i.ts < l_i.end() && !parse_error;
+         ++r_it, r_i = *r_it) {
+      // We already know (because we are in the loop) that |r_i| started before
+      // |l_i| ended, we should not intersect only if |r_i| started before
+      // |l_i|.
+      if (r_i.ts < l_i.ts) {
+        continue;
+      }
+
+      tables::IntervalIntersectTable::Row row;
+      row.ts = static_cast<uint32_t>(std::max(r_i.ts, l_i.ts));
+      row.dur = static_cast<uint32_t>(std::min(r_i.end(), l_i.end())) - row.ts;
+      row.left_id = static_cast<uint32_t>(l_i.id);
+      row.right_id = static_cast<uint32_t>(r_i.id);
+      table->Insert(row);
+    }
+  }
+
+  // Find all intersections where interval from the left table started during an
+  // interval from right table.
+  for (Interval r_i = *r_it_2; r_it_2 && l_it_2 && !parse_error;
+       ++r_it_2, r_i = *r_it_2) {
+    for (Interval l_i = *l_it_2; l_it_2 && l_i.ts < r_i.end() && !parse_error;
+         ++l_it_2, l_i = *l_it_2) {
+      // The only difference between this and above algorithm is not
+      // intersecting if the intervals started at the same time. We do this to
+      // prevent double counting intervals.
+      if (l_i.ts <= r_i.ts) {
+        continue;
+      }
+
+      tables::IntervalIntersectTable::Row row;
+      row.ts = static_cast<uint32_t>(std::max(r_i.ts, l_i.ts));
+      row.dur = static_cast<uint32_t>(std::min(r_i.end(), l_i.end())) - row.ts;
+      row.left_id = static_cast<uint32_t>(l_i.id);
+      row.right_id = static_cast<uint32_t>(r_i.id);
+      table->Insert(row);
+    }
+  }
+
+  if (parse_error) {
+    return base::ErrStatus(
+        "interval_intersect: Error in parsing of one of the arguments.");
+  }
+
+  return std::unique_ptr<Table>(std::move(table));
+}
+
+}  // namespace perfetto::trace_processor
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/interval_intersect.h b/src/trace_processor/perfetto_sql/intrinsics/table_functions/interval_intersect.h
new file mode 100644
index 0000000..ec8ccdf
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/interval_intersect.h
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_TABLE_FUNCTIONS_INTERVAL_INTERSECT_H_
+#define SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_TABLE_FUNCTIONS_INTERVAL_INTERSECT_H_
+
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "perfetto/ext/base/status_or.h"
+#include "perfetto/trace_processor/basic_types.h"
+#include "src/trace_processor/containers/string_pool.h"
+#include "src/trace_processor/db/table.h"
+#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/static_table_function.h"
+
+namespace perfetto::trace_processor {
+
+// An SQL table-function which computes the intersection of intervals from two
+// tables.
+//
+// Given two sets of sorted non-overlapping intervals (with id, timestamp and
+// duration) returns intervals that are intersections between those two sets,
+// with ids to state what intervals are intersected.
+//
+// LEFT         . - - - - - - .
+// RIGHT        - - . - - . - -
+// Intersection . - . - - . - .
+//
+// Arguments are RepeatedBuilderResult protos containing a column of
+// numerics values:
+// 1) |in_left_ids|(uint32_t): Ids from the left table.
+// 2) |in_left_tses|(uint64_t): Timestamps (starts) of intervals from
+// the left table.
+// 3) |in_left_durs|(uint64_t): Durations of intervals
+// from the left table.
+// 4) |in_right_ids|(uint32_t): Ids from the right table.
+// 5) |in_right_tses|(uint64_t): Timestamps (starts) of intervals
+// from the right table.
+// 6) |in_right_durs|(uint64_t): Durations of intervals from the right table.
+//
+// NOTES:
+// - The first 3 arguments have to have the same number of values.
+// - Timestamps in left and right columns have to be sorted.
+//
+// Returns:
+// 1) |ts|: Start of the intersection.
+// 2) |dur|: Duration of the intersection.
+// 3) |left_id|: Id of the slice that was intersected in the first table.
+// 4) |right_id|: Id of the slice that was intersected in the second table.
+class IntervalIntersect : public StaticTableFunction {
+ public:
+  explicit IntervalIntersect(StringPool*);
+  virtual ~IntervalIntersect() override;
+
+  // StaticTableFunction implementation.
+  Table::Schema CreateSchema() override;
+  std::string TableName() override;
+  uint32_t EstimateRowCount() override;
+  base::StatusOr<std::unique_ptr<Table>> ComputeTable(
+      const std::vector<SqlValue>& arguments) override;
+
+ private:
+  StringPool* pool_;
+};
+
+}  // namespace perfetto::trace_processor
+
+#endif  // SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_TABLE_FUNCTIONS_INTERVAL_INTERSECT_H_
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/tables.py b/src/trace_processor/perfetto_sql/intrinsics/table_functions/tables.py
index f71ab9c..3c86e50 100644
--- a/src/trace_processor/perfetto_sql/intrinsics/table_functions/tables.py
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/tables.py
@@ -170,6 +170,23 @@
           flags=ColumnFlag.HIDDEN),
     ])
 
+INTERVAL_INTERSECT_TABLE = Table(
+    python_module=__file__,
+    class_name="IntervalIntersectTable",
+    sql_name="__intrinsic_interval_intersect",
+    columns=[
+        C("ts", CppInt64()),
+        C("dur", CppInt64()),
+        C("left_id", CppUint32()),
+        C("right_id", CppUint32()),
+        C("in_left_ids", CppOptional(CppString()), flags=ColumnFlag.HIDDEN),
+        C("in_left_tses", CppOptional(CppString()), flags=ColumnFlag.HIDDEN),
+        C("in_left_durs", CppOptional(CppString()), flags=ColumnFlag.HIDDEN),
+        C("in_right_ids", CppOptional(CppString()), flags=ColumnFlag.HIDDEN),
+        C("in_right_tses", CppOptional(CppString()), flags=ColumnFlag.HIDDEN),
+        C("in_right_durs", CppOptional(CppString()), flags=ColumnFlag.HIDDEN),
+    ])
+
 # Keep this list sorted.
 ALL_TABLES = [
     ANCESTOR_SLICE_BY_STACK_TABLE,
@@ -184,5 +201,6 @@
     EXPERIMENTAL_COUNTER_DUR_TABLE,
     EXPERIMENTAL_SCHED_UPID_TABLE,
     EXPERIMENTAL_SLICE_LAYOUT_TABLE,
+    INTERVAL_INTERSECT_TABLE,
     TABLE_INFO_TABLE,
 ]
diff --git a/src/trace_processor/perfetto_sql/stdlib/intervals/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/intervals/BUILD.gn
index dcac571..e9e22e9 100644
--- a/src/trace_processor/perfetto_sql/stdlib/intervals/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/stdlib/intervals/BUILD.gn
@@ -15,5 +15,8 @@
 import("../../../../../gn/perfetto_sql.gni")
 
 perfetto_sql_source_set("intervals") {
-  sources = [ "overlap.sql" ]
+  sources = [
+    "intersect.sql",
+    "overlap.sql",
+  ]
 }
diff --git a/src/trace_processor/perfetto_sql/stdlib/intervals/intersect.sql b/src/trace_processor/perfetto_sql/stdlib/intervals/intersect.sql
new file mode 100644
index 0000000..b48aec6
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/intervals/intersect.sql
@@ -0,0 +1,34 @@
+--
+-- Copyright 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
+--
+--     https://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.
+
+CREATE PERFETTO MACRO _interval_intersect(
+  left_table TableOrSubquery,
+  right_table TableOrSubquery
+)
+RETURNS TableOrSubquery AS
+(
+  WITH 
+    __temp_left_table AS (SELECT * FROM $left_table ORDER BY ts),
+    __temp_right_table AS (SELECT * FROM $right_table ORDER BY ts)
+  SELECT ii.ts, ii.dur, ii.left_id, ii.right_id
+  FROM __intrinsic_interval_intersect(
+    (SELECT RepeatedField(id) FROM __temp_left_table),
+    (SELECT RepeatedField(ts) FROM __temp_left_table),
+    (SELECT RepeatedField(dur) FROM __temp_left_table),
+    (SELECT RepeatedField(id) FROM __temp_right_table),
+    (SELECT RepeatedField(ts) FROM __temp_right_table),
+    (SELECT RepeatedField(dur) FROM __temp_right_table)
+  ) ii
+);
diff --git a/src/trace_processor/perfetto_sql/stdlib/prelude/slices.sql b/src/trace_processor/perfetto_sql/stdlib/prelude/slices.sql
index 24f470a..6c58c60 100644
--- a/src/trace_processor/perfetto_sql/stdlib/prelude/slices.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/prelude/slices.sql
@@ -29,39 +29,4 @@
 FROM slice ancestor
 JOIN slice descendant
 WHERE ancestor.id = $ancestor_id
-  AND descendant.id = $descendant_id;
-
-CREATE PERFETTO MACRO _interval_intersect(
-  left_table TableOrSubquery,
-  right_table TableOrSubquery
-)
-RETURNS TableOrSubquery AS
-(
-  WITH on_left AS (
-    SELECT
-      B.ts,
-      IIF(
-        A.ts + A.dur <= B.ts + B.dur,
-        A.ts + A.dur - B.ts, B.dur) AS dur,
-      A.id AS left_id,
-      B.id as right_id
-    FROM $left_table A
-    JOIN $right_table B ON (A.ts <= B.ts AND A.ts + A.dur > B.ts)
-  ), on_right AS (
-    SELECT
-      B.ts,
-      IIF(
-        A.ts + A.dur <= B.ts + B.dur,
-        A.ts + A.dur - B.ts, B.dur) AS dur,
-      B.id as left_id,
-      A.id AS right_id
-    FROM $right_table A
-    -- The difference between this table and on_left is the lack of equality on
-    -- A.ts <= B.ts. This is to remove the issue of double accounting
-    -- timestamps that start at the same time.
-    JOIN $left_table B ON (A.ts < B.ts AND A.ts + A.dur > B.ts)
-  )
-  SELECT * FROM on_left
-  UNION ALL
-  SELECT * FROM on_right
-);
\ No newline at end of file
+  AND descendant.id = $descendant_id;
\ No newline at end of file
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index 02bc3ae..08ec0d3 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -78,6 +78,7 @@
 #include "src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_flat_slice.h"
 #include "src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_sched_upid.h"
 #include "src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_slice_layout.h"
+#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/interval_intersect.h"
 #include "src/trace_processor/perfetto_sql/intrinsics/table_functions/table_info.h"
 #include "src/trace_processor/perfetto_sql/prelude/tables_views.h"
 #include "src/trace_processor/perfetto_sql/stdlib/stdlib.h"
@@ -883,6 +884,8 @@
       new ExperimentalFlatSlice(&context_)));
   engine_->RegisterStaticTableFunction(
       std::make_unique<DominatorTree>(context_.storage->mutable_string_pool()));
+  engine_->RegisterStaticTableFunction(std::make_unique<IntervalIntersect>(
+      context_.storage->mutable_string_pool()));
   engine_->RegisterStaticTableFunction(
       std::make_unique<Dfs>(context_.storage->mutable_string_pool()));
 
diff --git a/test/trace_processor/diff_tests/include_index.py b/test/trace_processor/diff_tests/include_index.py
index a5347cf..7da78d7 100644
--- a/test/trace_processor/diff_tests/include_index.py
+++ b/test/trace_processor/diff_tests/include_index.py
@@ -99,6 +99,7 @@
 from diff_tests.stdlib.counters.tests import StdlibCounterIntervals
 from diff_tests.stdlib.dynamic_tables.tests import DynamicTables
 from diff_tests.stdlib.intervals.tests import StdlibIntervals
+from diff_tests.stdlib.intervals.intersect_tests import IntervalsIntersect
 from diff_tests.stdlib.graphs.dominator_tree_tests import DominatorTree
 from diff_tests.stdlib.graphs.search_tests import GraphSearchTests
 from diff_tests.stdlib.linux.tests import LinuxStdlib
@@ -274,7 +275,9 @@
       *SpanJoinSmoke(index_path, 'stdlib/span_join', 'SpanJoinSmoke').fetch(),
       *StdlibCommon(index_path, 'stdlib/common', 'StdlibCommon').fetch(),
       *StdlibIntervals(index_path, 'stdlib/intervals',
-                       'StdlibIntervals').fetch(),
+                       'StdlibIntervalsIntersect').fetch(),
+      *IntervalsIntersect(index_path, 'stdlib/intervals',
+                          'StdlibIntervals').fetch(),
       *Timestamps(index_path, 'stdlib/timestamps', 'Timestamps').fetch(),
   ] + chrome_stdlib_tests
 
diff --git a/test/trace_processor/diff_tests/stdlib/intervals/intersect_tests.py b/test/trace_processor/diff_tests/stdlib/intervals/intersect_tests.py
new file mode 100644
index 0000000..b6e1500
--- /dev/null
+++ b/test/trace_processor/diff_tests/stdlib/intervals/intersect_tests.py
@@ -0,0 +1,220 @@
+#!/usr/bin/env python3
+# 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 a
+#
+#      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.
+
+from python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class IntervalsIntersect(TestSuite):
+
+  def test_simple_inteval_intersect(self):
+    return DiffTestBlueprint(
+        trace=TextProto(""),
+        #      0 1 2 3 4 5 6 7
+        # A:   _ - - - - - - _
+        # B:   - - _ - - _ - -
+        # res: _ - _ - - _ - _
+        query="""
+        INCLUDE PERFETTO MODULE intervals.intersect;
+
+        CREATE PERFETTO TABLE A AS
+          WITH data(id, ts, dur) AS (
+            VALUES
+            (0, 1, 6)
+          )
+          SELECT * FROM data;
+
+        CREATE PERFETTO TABLE B AS
+          WITH data(id, ts, dur) AS (
+            VALUES
+            (0, 0, 2),
+            (1, 3, 2),
+            (2, 6, 2)
+          )
+          SELECT * FROM data;
+
+        SELECT ts, dur, left_id, right_id
+        FROM _interval_intersect!(A, B)
+        ORDER BY ts;
+        """,
+        out=Csv("""
+        "ts","dur","left_id","right_id"
+        1,1,0,0
+        3,2,0,1
+        6,1,0,2
+        """))
+
+  def test_simple_inteval_intersect_rev(self):
+    return DiffTestBlueprint(
+        trace=TextProto(""),
+        #      0 1 2 3 4 5 6 7
+        # A:   _ - - - - - - _
+        # B:   - - _ - - _ - -
+        # res: _ - _ - - _ - _
+        query="""
+        INCLUDE PERFETTO MODULE intervals.intersect;
+
+        CREATE PERFETTO TABLE A AS
+          WITH data(id, ts, dur) AS (
+            VALUES
+            (0, 1, 6)
+          )
+          SELECT * FROM data;
+
+        CREATE PERFETTO TABLE B AS
+          WITH data(id, ts, dur) AS (
+            VALUES
+            (0, 0, 2),
+            (1, 3, 2),
+            (2, 6, 2)
+          )
+          SELECT * FROM data;
+
+        SELECT ts, dur, left_id, right_id
+        FROM _interval_intersect!(B, A)
+        ORDER BY ts;
+        """,
+        out=Csv("""
+        "ts","dur","left_id","right_id"
+        1,1,0,0
+        3,2,1,0
+        6,1,2,0
+        """))
+
+  def test_no_overlap(self):
+    return DiffTestBlueprint(
+        trace=TextProto(""),
+        # A:   __-
+        # B:   -__
+        # res: ___
+        query="""
+        INCLUDE PERFETTO MODULE intervals.intersect;
+
+        CREATE PERFETTO TABLE A AS
+          WITH data(id, ts, dur) AS (
+            VALUES
+            (0, 2, 1)
+          )
+          SELECT * FROM data;
+
+        CREATE PERFETTO TABLE B AS
+          WITH data(id, ts, dur) AS (
+            VALUES
+            (0, 0, 1)
+          )
+          SELECT * FROM data;
+
+        SELECT ts, dur, left_id, right_id
+        FROM _interval_intersect!(A, B)
+        ORDER BY ts;
+        """,
+        out=Csv("""
+        "ts","dur","left_id","right_id"
+        """))
+
+  def test_no_overlap_rev(self):
+    return DiffTestBlueprint(
+        trace=TextProto(""),
+        # A:   __-
+        # B:   -__
+        # res: ___
+        query="""
+        INCLUDE PERFETTO MODULE intervals.intersect;
+
+        CREATE PERFETTO TABLE A AS
+          WITH data(id, ts, dur) AS (
+            VALUES
+            (0, 2, 1)
+          )
+          SELECT * FROM data;
+
+        CREATE PERFETTO TABLE B AS
+          WITH data(id, ts, dur) AS (
+            VALUES
+            (0, 0, 1)
+          )
+          SELECT * FROM data;
+
+        SELECT ts, dur, left_id, right_id
+        FROM _interval_intersect!(B, A)
+        ORDER BY ts;
+        """,
+        out=Csv("""
+        "ts","dur","left_id","right_id"
+        """))
+
+  def test_single_point_overlap(self):
+    return DiffTestBlueprint(
+        trace=TextProto(""),
+        # A:   _-
+        # B:   -_
+        # res: __
+        query="""
+        INCLUDE PERFETTO MODULE intervals.intersect;
+
+        CREATE PERFETTO TABLE A AS
+          WITH data(id, ts, dur) AS (
+            VALUES
+            (0, 1, 1)
+          )
+          SELECT * FROM data;
+
+        CREATE PERFETTO TABLE B AS
+          WITH data(id, ts, dur) AS (
+            VALUES
+            (0, 0, 1)
+          )
+          SELECT * FROM data;
+
+        SELECT ts, dur, left_id, right_id
+        FROM _interval_intersect!(A, B)
+        ORDER BY ts;
+        """,
+        out=Csv("""
+        "ts","dur","left_id","right_id"
+        """))
+
+  def test_single_point_overlap_rev(self):
+    return DiffTestBlueprint(
+        trace=TextProto(""),
+        # A:   _-
+        # B:   -_
+        # res: __
+        query="""
+        INCLUDE PERFETTO MODULE intervals.intersect;
+
+        CREATE PERFETTO TABLE A AS
+          WITH data(id, ts, dur) AS (
+            VALUES
+            (0, 1, 1)
+          )
+          SELECT * FROM data;
+
+        CREATE PERFETTO TABLE B AS
+          WITH data(id, ts, dur) AS (
+            VALUES
+            (0, 0, 1)
+          )
+          SELECT * FROM data;
+
+        SELECT ts, dur, left_id, right_id
+        FROM _interval_intersect!(B, A)
+        ORDER BY ts;
+        """,
+        out=Csv("""
+        "ts","dur","left_id","right_id"
+        """))
\ No newline at end of file