tp: Add thread_slice_cpu_time to stdlib

Change-Id: Ib8bbda2f7c43b5bc4ea0f25df125add26e84f787
diff --git a/Android.bp b/Android.bp
index 40a53fd..ccc41a7 100644
--- a/Android.bp
+++ b/Android.bp
@@ -12062,6 +12062,7 @@
         "src/trace_processor/perfetto_sql/stdlib/sched/utilization/process.sql",
         "src/trace_processor/perfetto_sql/stdlib/sched/utilization/system.sql",
         "src/trace_processor/perfetto_sql/stdlib/sched/utilization/thread.sql",
+        "src/trace_processor/perfetto_sql/stdlib/slices/cpu_time.sql",
         "src/trace_processor/perfetto_sql/stdlib/slices/flat_slices.sql",
         "src/trace_processor/perfetto_sql/stdlib/slices/slices.sql",
         "src/trace_processor/perfetto_sql/stdlib/slices/with_context.sql",
diff --git a/BUILD b/BUILD
index 14dc7b2..c161054 100644
--- a/BUILD
+++ b/BUILD
@@ -2517,6 +2517,7 @@
 perfetto_filegroup(
     name = "src_trace_processor_perfetto_sql_stdlib_slices_slices",
     srcs = [
+        "src/trace_processor/perfetto_sql/stdlib/slices/cpu_time.sql",
         "src/trace_processor/perfetto_sql/stdlib/slices/flat_slices.sql",
         "src/trace_processor/perfetto_sql/stdlib/slices/slices.sql",
         "src/trace_processor/perfetto_sql/stdlib/slices/with_context.sql",
diff --git a/python/generators/sql_processing/utils.py b/python/generators/sql_processing/utils.py
index ba14110..539115e 100644
--- a/python/generators/sql_processing/utils.py
+++ b/python/generators/sql_processing/utils.py
@@ -116,7 +116,8 @@
 
 # Allows for nonstandard object names.
 OBJECT_NAME_ALLOWLIST = {
-    'slices/with_context.sql': ['process_slice', 'thread_slice']
+    'slices/with_context.sql': ['process_slice', 'thread_slice'],
+    'slices/cpu_time.sql': ['thread_slice_cpu_time']
 }
 
 # Given a regex pattern and a string to match against, returns all the
diff --git a/src/trace_processor/perfetto_sql/stdlib/slices/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/slices/BUILD.gn
index 2e5f02f..f69eafd 100644
--- a/src/trace_processor/perfetto_sql/stdlib/slices/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/stdlib/slices/BUILD.gn
@@ -16,6 +16,7 @@
 
 perfetto_sql_source_set("slices") {
   sources = [
+    "cpu_time.sql",
     "flat_slices.sql",
     "slices.sql",
     "with_context.sql",
diff --git a/src/trace_processor/perfetto_sql/stdlib/slices/cpu_time.sql b/src/trace_processor/perfetto_sql/stdlib/slices/cpu_time.sql
new file mode 100644
index 0000000..4a36d9c
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/slices/cpu_time.sql
@@ -0,0 +1,72 @@
+--
+-- 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.
+
+-- TODO(mayzner): Replace with good implementation of interval intersect.
+CREATE PERFETTO MACRO _interval_intersect_partition_utid(
+  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 AND A.utid = B.utid)
+  ), 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 AND A.utid = B.utid)
+  )
+  SELECT * FROM on_left
+  UNION ALL
+  SELECT * FROM on_right
+);
+
+-- Time each thread slice spent running on CPU.
+-- Requires scheduling data to be available in the trace.
+CREATE PERFETTO TABLE thread_slice_cpu_time(
+    -- Slice id.
+    id INT,
+    -- Duration of the time the slice was running.
+    cpu_time INT) AS
+WITH slice_with_utid AS (
+  SELECT
+      slice.id,
+      slice.ts,
+      slice.dur,
+      utid
+  FROM slice
+  JOIN thread_track ON slice.track_id = thread_track.id
+  JOIN thread USING (utid)
+  WHERE utid != 0)
+SELECT left_id AS id, SUM(dur) AS cpu_time
+FROM _interval_intersect_partition_utid!(slice_with_utid, sched)
+GROUP BY 1
+ORDER BY 1;
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/stdlib/slices/tests.py b/test/trace_processor/diff_tests/stdlib/slices/tests.py
index e3db459..053dabf 100644
--- a/test/trace_processor/diff_tests/stdlib/slices/tests.py
+++ b/test/trace_processor/diff_tests/stdlib/slices/tests.py
@@ -91,4 +91,28 @@
         "ThreadControllerImpl::RunTask",174796099970797,186000,0
         "Looper.dispatch: jy3(null)",174800056530797,1368000,0
         "ThreadControllerImpl::RunTask",174800107962797,132000,0
-      """))
\ No newline at end of file
+      """))
+
+  def test_thread_slice_cpu_time(self):
+    return DiffTestBlueprint(
+        trace=DataPath('example_android_trace_30s.pb'),
+        query="""
+        INCLUDE PERFETTO MODULE slices.cpu_time;
+
+        SELECT *
+        FROM thread_slice_cpu_time
+        LIMIT 10;
+        """,
+        out=Csv("""
+        "id","cpu_time"
+        0,178646
+        1,119740
+        2,58073
+        3,98698
+        4,121979
+        5,45000
+        6,35104
+        7,33333
+        8,46926
+        9,17865
+        """))
\ No newline at end of file