[stdlib]: Add an android.oom_adjuster module

android_oom_adj_states contains a row per oom_adj transition in all
processes. The transition is augmented with metadata about the last
oom_adj update in the system_server. Furthermore, we also expose
a generic oom_adjuster translation function.

Test: tools/diff_test_trace_processor.py out/android/trace_processor_shell --name-filter '.*oom_adjuster.*'
Change-Id: I338e40ba2feaae2de8981e774db82a7a4cabe3ba
diff --git a/Android.bp b/Android.bp
index 73487c9..58e5e2d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -11986,6 +11986,7 @@
         "src/trace_processor/perfetto_sql/stdlib/android/job_scheduler.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/monitor_contention.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/network_packets.sql",
+        "src/trace_processor/perfetto_sql/stdlib/android/oom_adjuster.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/process_metadata.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/screenshots.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/services.sql",
diff --git a/BUILD b/BUILD
index a60811a..50532d6 100644
--- a/BUILD
+++ b/BUILD
@@ -2374,6 +2374,7 @@
         "src/trace_processor/perfetto_sql/stdlib/android/job_scheduler.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/monitor_contention.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/network_packets.sql",
+        "src/trace_processor/perfetto_sql/stdlib/android/oom_adjuster.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/process_metadata.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/screenshots.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/services.sql",
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/android/BUILD.gn
index be50773..29c4b89 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/stdlib/android/BUILD.gn
@@ -31,6 +31,7 @@
     "job_scheduler.sql",
     "monitor_contention.sql",
     "network_packets.sql",
+    "oom_adjuster.sql",
     "process_metadata.sql",
     "screenshots.sql",
     "services.sql",
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/oom_adjuster.sql b/src/trace_processor/perfetto_sql/stdlib/android/oom_adjuster.sql
new file mode 100644
index 0000000..9c184e5
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/android/oom_adjuster.sql
@@ -0,0 +1,129 @@
+--
+-- 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.
+--
+
+INCLUDE PERFETTO MODULE common.slices;
+INCLUDE PERFETTO MODULE counters.intervals;
+
+-- Converts an oom_adj score Integer to String bucket name.
+CREATE PERFETTO FUNCTION android_oom_adj_score_to_bucket_name(
+  -- oom_adj score.
+  value INT,
+  -- android_app id of the process.
+  android_appid INT)
+-- Returns the oom_adj bucket.
+RETURNS STRING
+AS
+SELECT
+  CASE
+    WHEN $value = -1000 THEN 'native'
+    WHEN $value = -900 THEN 'system'
+    WHEN $value = -800 THEN 'persistent'
+    WHEN $value = -700 THEN 'persistent'
+    WHEN $value = -600 THEN 'logcat'
+    WHEN $value = 0 THEN 'foreground_app'
+    WHEN $value = 50 THEN 'perceptible_foreground_app'
+    WHEN $value BETWEEN 100 AND 199 THEN 'visible_app'
+    WHEN $value BETWEEN 200 AND 224 THEN 'perceptible_app'
+    WHEN $value BETWEEN 225 AND 249 THEN 'perceptible_medium_app'
+    WHEN $value BETWEEN 250 AND 299 THEN 'perceptible_low_app'
+    WHEN $value BETWEEN 300 AND 399 THEN 'backup'
+    WHEN $value BETWEEN 400 AND 499 THEN 'heavy_weight_app'
+    WHEN $value BETWEEN 500 AND 599 THEN 'service'
+    WHEN $value BETWEEN 600 AND 699 THEN 'home_app'
+    WHEN $value BETWEEN 700 AND 799 THEN 'previous_app'
+    WHEN $value BETWEEN 800 AND 899 THEN 'service_b'
+    WHEN $value BETWEEN 900 AND 949 THEN 'cached_app'
+    WHEN $value >= 950 THEN 'cached_app_lmk_first'
+    WHEN $android_appid IS NULL THEN 'unknown'
+    WHEN $android_appid < 10000 THEN 'unknown_native'
+    ELSE 'unknown_app'
+  END;
+
+-- All oom adj state intervals across all processes along with the reason for the state update.
+CREATE PERFETTO TABLE android_oom_adj_intervals (
+  -- Timestamp the oom_adj score of the process changed
+  ts INT,
+  -- Duration until the next oom_adj score change of the process.
+  dur INT,
+  -- oom_adj score of the process.
+  score INT,
+  -- oom_adj bucket of the process.
+  bucket STRING,
+  -- Upid of the process having an oom_adj update.
+  upid INT,
+  -- Name of the process having an oom_adj update.
+  process_name STRING,
+  -- Slice id of the latest oom_adj update in the system_server.
+  oom_adj_id INT,
+  -- Timestamp of the latest oom_adj update in the system_server.
+  oom_adj_ts INT,
+  -- Duration of the latest oom_adj update in the system_server.
+  oom_adj_dur INT,
+  -- Track id of the latest oom_adj update in the system_server
+  oom_adj_track_id INT,
+  -- Thread name of the latest oom_adj update in the system_server.
+  oom_adj_thread_name STRING,
+  -- Reason for the latest oom_adj update in the system_server.
+  oom_adj_reason STRING,
+  -- Trigger for the latest oom_adj update in the system_server.
+  oom_adj_trigger STRING
+  ) AS
+WITH
+  reason AS (
+    SELECT
+      thread_slice.id AS oom_adj_id,
+      thread_slice.ts AS oom_adj_ts,
+      thread_slice.dur AS oom_adj_dur,
+      thread_slice.track_id AS oom_adj_track_id,
+      thread_name AS oom_adj_thread_name,
+      str_split(thread_slice.name, '_', 1) AS oom_adj_reason,
+      slice.name AS oom_adj_trigger,
+      LEAD(thread_slice.ts) OVER (ORDER BY thread_slice.ts) AS oom_adj_next_ts
+    FROM thread_slice
+    JOIN slice ON slice.id = thread_slice.parent_id
+    WHERE thread_slice.name GLOB 'updateOomAdj_*' AND process_name = 'system_server'
+  )
+SELECT
+  ts,
+  dur,
+  value AS score,
+  android_oom_adj_score_to_bucket_name(value, android_appid) AS bucket,
+  process.upid,
+  process.name AS process_name,
+  reason.oom_adj_id,
+  reason.oom_adj_ts,
+  reason.oom_adj_dur,
+  reason.oom_adj_track_id,
+  reason.oom_adj_thread_name,
+  reason.oom_adj_reason,
+  reason.oom_adj_trigger
+FROM
+  counter_leading_intervals
+    !(
+      (
+        SELECT counter.*
+        FROM counter
+        JOIN counter_track track
+          ON track.id = counter.track_id AND track.name = 'oom_score_adj'
+      ))
+      counter
+JOIN process_counter_track track
+  ON counter.track_id = track.id
+JOIN process
+  USING (upid)
+LEFT JOIN reason
+  ON counter.ts BETWEEN oom_adj_ts AND COALESCE(oom_adj_next_ts, trace_end())
+WHERE track.name = 'oom_score_adj';
diff --git a/test/trace_processor/diff_tests/stdlib/android/tests.py b/test/trace_processor/diff_tests/stdlib/android/tests.py
index ad4a5ed..927394f 100644
--- a/test/trace_processor/diff_tests/stdlib/android/tests.py
+++ b/test/trace_processor/diff_tests/stdlib/android/tests.py
@@ -1213,3 +1213,38 @@
         0,"com.android.bluetooth","droid.bluetooth",7639,7639,571248973750,9874358,-700,"com.android.bluetooth","binder:7639_2",7672,7639,571871169647,6460322,"android.os.BinderProxy@7482132","android.bluetooth.IBluetooth","com.android.bluetooth/.btservice.AdapterService","[NULL]",4
         -700,"com.android.bluetooth","droid.bluetooth",7639,7639,572342110044,4874276,-700,"com.android.bluetooth","binder:7639_2",7672,7639,572466393291,1404185,"android.os.BinderProxy@ce5a6fc","android.media.browse.MediaBrowserService","com.android.bluetooth/.avrcpcontroller.BluetoothMediaBrowserService","[NULL]",10
       """))
+
+  def test_oom_adjuster_transitions(self):
+    return DiffTestBlueprint(
+        trace=DataPath('sched_wakeup_trace.atr'),
+        query="""
+        INCLUDE PERFETTO MODULE android.oom_adjuster;
+        SELECT
+        ts,
+        dur,
+        score,
+        bucket,
+        process_name,
+        oom_adj_ts,
+        oom_adj_dur,
+        oom_adj_thread_name,
+        oom_adj_reason,
+        oom_adj_trigger
+        FROM android_oom_adj_intervals
+        WHERE oom_adj_reason IS NOT NULL
+        ORDER BY ts
+        LIMIT 10
+      """,
+        out=Csv("""
+        "ts","dur","score","bucket","process_name","oom_adj_ts","oom_adj_dur","oom_adj_thread_name","oom_adj_reason","oom_adj_trigger"
+        1737065264829,701108081,925,"cached_app","com.android.providers.calendar",1737064421516,29484835,"binder:642_1","processEnd","IActivityManager#1598246212"
+        1737066678827,3470211742,935,"cached_app","com.android.imsserviceentitlement",1737064421516,29484835,"binder:642_1","processEnd","IActivityManager#1598246212"
+        1737066873002,3470017567,945,"cached_app","com.android.carrierconfig",1737064421516,29484835,"binder:642_1","processEnd","IActivityManager#1598246212"
+        1737067058812,3469831757,955,"cached_app_lmk_first","com.android.messaging",1737064421516,29484835,"binder:642_1","processEnd","IActivityManager#1598246212"
+        1737067246975,699224817,955,"cached_app_lmk_first","android.process.acore",1737064421516,29484835,"binder:642_1","processEnd","IActivityManager#1598246212"
+        1737068421919,3468468650,965,"cached_app_lmk_first","com.android.shell",1737064421516,29484835,"binder:642_1","processEnd","IActivityManager#1598246212"
+        1737068599673,697908135,965,"cached_app_lmk_first","android.process.media",1737064421516,29484835,"binder:642_1","processEnd","IActivityManager#1598246212"
+        1737068933602,3467956967,975,"cached_app_lmk_first","com.android.gallery3d",1737064421516,29484835,"binder:642_1","processEnd","IActivityManager#1598246212"
+        1737069091010,3467799559,975,"cached_app_lmk_first","com.android.packageinstaller",1737064421516,29484835,"binder:642_1","processEnd","IActivityManager#1598246212"
+        1737069240534,3467650035,985,"cached_app_lmk_first","com.android.managedprovisioning",1737064421516,29484835,"binder:642_1","processEnd","IActivityManager#1598246212"
+      """))