Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 505ee0bf authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Define the metric definition for DmaBuf allocation per process metric." into main

parents eebf06f1 af53c1bb
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ filegroup {
    srcs: [
        "android_bitmap_metric.textproto",
        "android_sf_critical_work_main_thread.textproto",
        "android_dmabuf_per_process_metric.textproto",
    ],
}

@@ -36,6 +37,11 @@ prebuilt_etc {
    src: "android_sf_critical_work_main_thread.textproto",
}

prebuilt_etc {
    name: "android_dmabuf_per_process_metric.textproto",
    src: "android_dmabuf_per_process_metric.textproto",
}

python_library_host {
    name: "metrics-tests-utils",
    srcs: ["tests/utils/*.py"],
+69 −0
Original line number Diff line number Diff line
# Copyright (C) 2025 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.

# We compute the duration of the current value by subtracting the current timestamp
# from the timestamp of the next value.
# The LEAD function is used to get the next timestamp. If there is no next timestamp, it uses the
# end of the trace as the next timestamp.

metric_template_spec: {
  id_prefix: "android_dmabuf_per_process_metric"
  dimensions: "process_name"
  value_columns: "min_val"
  value_columns: "max_val"
  value_columns: "avg_val"
  query: {
    sql: {
      column_names: "process_name"
      column_names: "metric_val"
      column_names: "dur"
      sql: "
        INCLUDE PERFETTO MODULE android.memory.dmabuf;

        SELECT
          process_name,
          value AS metric_val,
          LEAD(ts, 1, (SELECT end_ts FROM trace_bounds))
          OVER(PARTITION BY COALESCE(upid, utid) ORDER BY ts) - ts AS dur
        FROM android_memory_cumulative_dmabuf
        WHERE upid IS NOT NULL
      "
    }
    filters: {
      column_name: "process_name"
      op: EQUAL
      string_rhs: "com.android.systemui"
      string_rhs: "com.google.android.apps.nexuslauncher"
      string_rhs: "system_server"
    }
    group_by: {
      column_names: "process_name"
      aggregates: {
        column_name: "metric_val"
        op: MIN
        result_column_name: "min_val"
      }
      aggregates: {
        column_name: "metric_val"
        op: MAX
        result_column_name: "max_val"
      }
      aggregates: {
        column_name: "metric_val"
        op: DURATION_WEIGHTED_MEAN
        result_column_name: "avg_val"
      }
    }
  }
}
 No newline at end of file
+147 −0
Original line number Diff line number Diff line
metric_bundles {
  row {
    values {
      double_value: 1024.0
    }
    values {
      double_value: 1536.0
    }
    values {
      double_value: 1317.1965811965813
    }
    dimension {
      string_value: "com.android.systemui"
    }
  }
  row {
    values {
      double_value: 0.0
    }
    values {
      double_value: 8192.0
    }
    values {
      double_value: 4055.4455445544554
    }
    dimension {
      string_value: "system_server"
    }
  }
  specs {
    id: "android_dmabuf_per_process_metric_min_val"
    dimensions: "process_name"
    value: "min_val"
    query {
      sql {
        sql: "\n        INCLUDE PERFETTO MODULE android.memory.dmabuf;\n\n        SELECT\n          process_name,\n          value AS metric_val,\n          LEAD(ts, 1, (SELECT end_ts FROM trace_bounds))\n          OVER(PARTITION BY COALESCE(upid, utid) ORDER BY ts) - ts AS dur\n        FROM android_memory_cumulative_dmabuf\n        WHERE upid IS NOT NULL\n      "
        column_names: "process_name"
        column_names: "metric_val"
        column_names: "dur"
      }
      filters {
        column_name: "process_name"
        op: EQUAL
        string_rhs: "com.android.systemui"
        string_rhs: "com.google.android.apps.nexuslauncher"
        string_rhs: "system_server"
      }
      group_by {
        column_names: "process_name"
        aggregates {
          column_name: "metric_val"
          op: MIN
          result_column_name: "min_val"
        }
        aggregates {
          column_name: "metric_val"
          op: MAX
          result_column_name: "max_val"
        }
        aggregates {
          column_name: "metric_val"
          op: DURATION_WEIGHTED_MEAN
          result_column_name: "avg_val"
        }
      }
    }
    bundle_id: "android_dmabuf_per_process_metric"
  }
  specs {
    id: "android_dmabuf_per_process_metric_max_val"
    dimensions: "process_name"
    value: "max_val"
    query {
      sql {
        sql: "\n        INCLUDE PERFETTO MODULE android.memory.dmabuf;\n\n        SELECT\n          process_name,\n          value AS metric_val,\n          LEAD(ts, 1, (SELECT end_ts FROM trace_bounds))\n          OVER(PARTITION BY COALESCE(upid, utid) ORDER BY ts) - ts AS dur\n        FROM android_memory_cumulative_dmabuf\n        WHERE upid IS NOT NULL\n      "
        column_names: "process_name"
        column_names: "metric_val"
        column_names: "dur"
      }
      filters {
        column_name: "process_name"
        op: EQUAL
        string_rhs: "com.android.systemui"
        string_rhs: "com.google.android.apps.nexuslauncher"
        string_rhs: "system_server"
      }
      group_by {
        column_names: "process_name"
        aggregates {
          column_name: "metric_val"
          op: MIN
          result_column_name: "min_val"
        }
        aggregates {
          column_name: "metric_val"
          op: MAX
          result_column_name: "max_val"
        }
        aggregates {
          column_name: "metric_val"
          op: DURATION_WEIGHTED_MEAN
          result_column_name: "avg_val"
        }
      }
    }
    bundle_id: "android_dmabuf_per_process_metric"
  }
  specs {
    id: "android_dmabuf_per_process_metric_avg_val"
    dimensions: "process_name"
    value: "avg_val"
    query {
      sql {
        sql: "\n        INCLUDE PERFETTO MODULE android.memory.dmabuf;\n\n        SELECT\n          process_name,\n          value AS metric_val,\n          LEAD(ts, 1, (SELECT end_ts FROM trace_bounds))\n          OVER(PARTITION BY COALESCE(upid, utid) ORDER BY ts) - ts AS dur\n        FROM android_memory_cumulative_dmabuf\n        WHERE upid IS NOT NULL\n      "
        column_names: "process_name"
        column_names: "metric_val"
        column_names: "dur"
      }
      filters {
        column_name: "process_name"
        op: EQUAL
        string_rhs: "com.android.systemui"
        string_rhs: "com.google.android.apps.nexuslauncher"
        string_rhs: "system_server"
      }
      group_by {
        column_names: "process_name"
        aggregates {
          column_name: "metric_val"
          op: MIN
          result_column_name: "min_val"
        }
        aggregates {
          column_name: "metric_val"
          op: MAX
          result_column_name: "max_val"
        }
        aggregates {
          column_name: "metric_val"
          op: DURATION_WEIGHTED_MEAN
          result_column_name: "avg_val"
        }
      }
    }
    bundle_id: "android_dmabuf_per_process_metric"
  }
}
+13 −0
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@

from metrics_specs.tests.utils import android_bitmap_metric_trace
from metrics_specs.tests.utils import android_sf_critical_work_main_thread_trace
from metrics_specs.tests.utils import android_dmabuf_per_process_metric_trace
from metrics_specs.tests.utils import test_helper
import unittest

@@ -47,5 +48,17 @@ class MetricsV2Test(unittest.TestCase):
            ]
        )

    def test_android_dmabuf_per_process_metric(self):
        self.helper.verify_metric(
            spec_file="android_dmabuf_per_process_metric.textproto",
            trace_proto_bytes = android_dmabuf_per_process_metric_trace.get_proto(),
            expected_output_file = "android_dmabuf_per_process_metric_output.txt",
            metric_ids = [
                "android_dmabuf_per_process_metric_min_val",
                "android_dmabuf_per_process_metric_max_val",
                "android_dmabuf_per_process_metric_avg_val",
            ]
        )

if __name__ == '__main__':
    unittest.main()
+117 −0
Original line number Diff line number Diff line
#!/usr/bin/env python3
# Copyright (C) 2025 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.

from metrics_specs.tests.utils import trace_proto_builder
from perfetto.protos.perfetto.trace.perfetto_trace_pb2 import Trace
from typing import Optional

def add_dmabuf_alloc_event(builder, ts: int, buf_size: int, tid: int, inode: int, total_allocated: int):
    """Adds a dma_heap_stat event as an ftrace_event."""
    pid = tid
    ftrace = builder.add_ftrace_event(ts, tid)
    dma_heap_stat = ftrace.dma_heap_stat
    dma_heap_stat.inode = inode
    dma_heap_stat.len = buf_size
    dma_heap_stat.total_allocated = total_allocated

def add_binder_transaction_events(builder, ts_start: int, duration: int, client_tid: int, server_tid: int, flow_id: int):
    reply_ts_start = ts_start + int(duration/2)
    reply_ts_end = ts_start + duration
    transaction_id = flow_id
    reply_id = flow_id + 1000
    builder.add_binder_transaction(transaction_id, ts_start, ts_start + duration, client_tid, client_tid, reply_id, reply_ts_start, reply_ts_end, server_tid, server_tid)

def add_process(builder, package_name: str, uid: int, pid: int):
  builder.add_package_list(ts=0, name=package_name, uid=uid, version_code=1)
  builder.add_process(pid=pid, ppid=pid, cmdline=package_name, uid=uid)
  builder.add_thread(tid=pid, tgid=pid, cmdline="MainThread", name="MainThread")

def get_proto():
    SYSUI_PID = 1000
    SYSUI_PROCESS_NAME = "com.android.systemui"

    SYSTEM_SERVER_PID = 2000
    SYSTEM_SERVER_PROCESS_NAME = "system_server"

    GRALLOC_PID = 4000
    GRALLOC_PROCESS_NAME = "/vendor/bin/hw/android.hardware.graphics.allocator"

    RANDOM_PROCESS_PID = 5000
    RANDOM_PROCESS_NAME = "random_process"

    INODE = 1111

    trace = Trace()
    builder = trace_proto_builder.TraceProtoBuilder(trace)

    # Add a generic packet, this is needed to add the processes and threads
    builder.add_packet()

    # Add processes
    add_process(builder, package_name=SYSUI_PROCESS_NAME, uid=10001, pid=SYSUI_PID)
    add_process(builder, package_name=SYSTEM_SERVER_PROCESS_NAME, uid=20001, pid=SYSTEM_SERVER_PID)
    add_process(builder, package_name=GRALLOC_PROCESS_NAME, uid=40001, pid=GRALLOC_PID)
    add_process(builder, package_name=RANDOM_PROCESS_NAME, uid=50001, pid=RANDOM_PROCESS_PID)

    # Add ftrace packet
    builder.add_ftrace_packet(cpu=0)

    current_ts = 0
    flow_id_counter = 1
    total_allocated = 0

    # Simulate DMABuf events, some with binder attribution
    # Process 1: Multiple allocations
    total_allocated += 1024
    add_dmabuf_alloc_event(builder, current_ts, 1024, SYSUI_PID, INODE, total_allocated)
    current_ts += 1000

    # Gralloc Allocation, with binder attribution
    total_allocated += 2048
    add_dmabuf_alloc_event(builder, current_ts, 2048, GRALLOC_PID, INODE, total_allocated)
    add_binder_transaction_events(builder, current_ts, 50, SYSUI_PID, GRALLOC_PID, flow_id_counter)
    flow_id_counter += 1
    current_ts += 1500

    total_allocated -= 1024
    add_dmabuf_alloc_event(builder, current_ts, -1024, SYSUI_PID, INODE, total_allocated)
    current_ts += 500

    total_allocated += 512
    add_dmabuf_alloc_event(builder, current_ts, 512, SYSUI_PID, INODE, total_allocated)
    current_ts += 2000

    # Process 2: Allocation and free
    total_allocated += 8192
    add_dmabuf_alloc_event(builder, current_ts, 8192, SYSTEM_SERVER_PID, INODE, total_allocated)
    current_ts += 1000

    total_allocated -= 8192
    add_dmabuf_alloc_event(builder, current_ts, -8192, SYSTEM_SERVER_PID, INODE, total_allocated)
    current_ts += 500

    # Random process allocation, should not be tracked
    total_allocated += 256
    add_dmabuf_alloc_event(builder, current_ts, 256, RANDOM_PROCESS_PID, INODE, total_allocated)
    current_ts += 500

    # Gralloc release, with binder attribution
    total_allocated -= 2048
    add_dmabuf_alloc_event(builder, current_ts, -2048, GRALLOC_PID, INODE, total_allocated)
    add_binder_transaction_events(builder, current_ts, 20, SYSUI_PID, GRALLOC_PID, flow_id_counter)
    flow_id_counter += 1
    current_ts += 500

    return builder.trace.SerializeToString()
 No newline at end of file
Loading