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

Commit af53c1bb authored by archisha's avatar archisha Committed by Archisha Baranwal
Browse files

Define the metric definition for DmaBuf allocation per process metric.

Add a new metric to track the per-process dmabuf allocs Perfetto Counter
Track which tracks the per process DMA buffer allocations/deallocations.

Bug: 435136511
Test: atest SysUiMetricsV2Test
Flag: EXEMPT added new perfetto metric for sysui
Change-Id: I4693234be0c53cea34dd87202b282d21c1a32d85
parent 5411a44e
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