| # Copyright 2025 The Fuchsia Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import argparse |
| import json |
| import os |
| import os.path |
| import sqlite3 |
| import sys |
| from typing import Any, Dict |
| |
| # This scripts creates a SQLite database with the following schema (expressed here in DBML): |
| # |
| # // Kernel system-level memory statistics. |
| # Table kernel_stats { |
| # name TEXT // Name of the statistic |
| # value INTEGER // Value of the statistic |
| # } |
| # |
| # // Principals on the system. |
| # Table principals { |
| # // ID of the principal. |
| # id INTEGER [PRIMARY KEY] |
| # // Human-readable name of the principal. |
| # name TEXT |
| # // Kind of the principal (e.g. Component or Part) |
| # principal_kind TEXT |
| # // Type of the principal (e.g. Runnable or Part) |
| # principal_type TEXT |
| # // Optional: parent (attributor) of the principal. |
| # parent INTEGER [ref: > principals.id] |
| # } |
| # |
| # // Names of resources |
| # Table resource_names { |
| # // Identifier of the resource name. |
| # id INTEGER [PRIMARY KEY] |
| # // Name of the resource |
| # name TEXT |
| # } |
| # |
| # // Memory resources |
| # Table resources { |
| # // Kernel object identifier of the resource |
| # koid INTEGER [PRIMARY KEY] |
| # // ID of the name of this resource |
| # name_id INTEGER [ref: > resource_names.id] |
| # // Type of this resource (e.g. job, process or vmo) |
| # type TEXT |
| # } |
| # |
| # // Virtual Memory Object (a type of resource) |
| # Table vmos { |
| # // Kernel object identifier of the VMO. Has a one-to-(zero or one) link with resources.koid. |
| # koid INTEGER [PRIMARY KEY] |
| # // Optional: parent of this VMO |
| # parent INTEGER [ref: > vmos.koid] |
| # // Memory usage of this VMO |
| # private_committed_bytes INTEGER |
| # private_populated_bytes INTEGER |
| # scaled_committed_bytes INTEGER |
| # scaled_populated_bytes INTEGER |
| # total_committed_bytes INTEGER |
| # total_populated_bytes INTEGER |
| # // Flags (bitfield) of this VMO |
| # flags INTEGER |
| # } |
| # |
| # // Associative table to join principals and resources. |
| # Table principals_resources { |
| # principal_id INTEGER [primary key, ref: > principals.id] |
| # resource_id INTEGER [ref: > resources.koid] |
| # } |
| # |
| # ref: vmos.koid - resources.koid [delete: cascade] |
| |
| |
| def main() -> int: |
| parser = argparse.ArgumentParser( |
| "components_sql", |
| description="This script converts a memory snapshot generated using `ffx --machine json profile memory components --detailed` into a SQLite database", |
| ) |
| parser.add_argument( |
| "input", |
| help="Input memory profile: detailed machine JSON output of ffx profile memory components. Use '-' for stdin", |
| ) |
| parser.add_argument("output", help="Output file") |
| |
| args = parser.parse_args() |
| if args.input == "-": |
| json_input = json.load(sys.stdin) |
| else: |
| with open(args.input) as f: |
| json_input = json.load(f) |
| process_json_input(json_input, args.output) |
| return 0 |
| |
| |
| def process_json_input(json_input: Dict[str, Any], output_path: str) -> None: |
| if "Detailed" not in json_input: |
| print( |
| "Error: The input JSON does not contain a 'Detailed' key. " |
| "Please ensure the input is generated using " |
| "`ffx --machine json profile memory components --detailed`.", |
| file=sys.stderr, |
| ) |
| sys.exit(1) |
| else: |
| data = json_input["Detailed"] |
| |
| if os.path.exists(output_path): |
| os.remove(output_path) |
| |
| con = sqlite3.connect(output_path) |
| |
| con.execute( |
| """CREATE TABLE kernel_stats ( |
| name TEXT PRIMARY KEY, |
| value INTEGER |
| )""" |
| ) |
| |
| con.executemany( |
| """INSERT INTO kernel_stats VALUES (?, ?)""", |
| data["kernel"]["memory_statistics"].items(), |
| ) |
| con.executemany( |
| """INSERT INTO kernel_stats VALUES (?, ?)""", |
| [ |
| (k, v) |
| for k, v in data["kernel"]["compression_statistics"].items() |
| if isinstance(v, int) |
| ], |
| ) |
| con.executemany( |
| """INSERT INTO kernel_stats VALUES (?, ?)""", |
| data["performance"].items(), |
| ) |
| |
| con.execute( |
| """CREATE TABLE principals ( |
| id INTEGER PRIMARY KEY, |
| name TEXT, |
| principal_kind TEXT, |
| principal_type TEXT, |
| parent INTEGER, |
| FOREIGN KEY(parent) REFERENCES principals(id) |
| )""" |
| ) |
| |
| con.execute( |
| """CREATE TABLE resource_names ( |
| id INTEGER PRIMARY KEY, |
| name TEXT |
| )""" |
| ) |
| |
| con.executemany( |
| "INSERT INTO resource_names VALUES (?, ?)", |
| enumerate(data["resource_names"]), |
| ) |
| |
| con.execute( |
| """CREATE TABLE resources ( |
| koid INTEGER PRIMARY KEY, |
| name_id INTEGER, |
| type TEXT, |
| FOREIGN KEY(name_id) REFERENCES resource_names(id) |
| )""" |
| ) |
| |
| con.execute( |
| """CREATE TABLE vmos ( |
| koid INTEGER PRIMARY KEY, |
| parent INTEGER, |
| private_committed_bytes INTEGER, |
| private_populated_bytes INTEGER, |
| scaled_committed_bytes INTEGER, |
| scaled_populated_bytes INTEGER, |
| total_committed_bytes INTEGER, |
| total_populated_bytes INTEGER, |
| flags INTEGER, |
| FOREIGN KEY(parent) REFERENCES resources(koid), |
| FOREIGN KEY(koid) REFERENCES resources(koid) ON DELETE CASCADE |
| )""" |
| ) |
| |
| data_resources = data["resources"] |
| for data_resource in data_resources: |
| res = data_resource["resource"] |
| koid = res["koid"] |
| name_id = res["name_index"] |
| [(resource_type_name, resource_type_value)] = list( |
| res["resource_type"].items() |
| ) |
| |
| con.execute( |
| "INSERT INTO resources VALUES (?, ?, ?)", |
| (koid, name_id, resource_type_name), |
| ) |
| |
| if resource_type_name == "Vmo": |
| vmo = resource_type_value |
| con.execute( |
| "INSERT INTO vmos VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", |
| ( |
| koid, |
| vmo["parent"], |
| vmo["private_committed_bytes"], |
| vmo["private_populated_bytes"], |
| vmo["scaled_committed_bytes"], |
| vmo["scaled_populated_bytes"], |
| vmo["total_committed_bytes"], |
| vmo["total_populated_bytes"], |
| vmo["flags"], |
| ), |
| ) |
| |
| con.execute( |
| """CREATE TABLE principals_resources ( |
| principal_id INTEGER, |
| resource_id INTEGER, |
| FOREIGN KEY(principal_id) REFERENCES principals(id), |
| FOREIGN KEY(resource_id) REFERENCES resources(koid) |
| )""" |
| ) |
| |
| principals = data["principals"] |
| for data_principal in principals: |
| principal = data_principal["principal"] |
| id = principal["identifier"] |
| [(kind, name)] = list(principal["description"].items()) |
| |
| principal_type = principal["principal_type"] |
| |
| con.execute( |
| "INSERT INTO principals VALUES (?, ?, ?, ?, ?)", |
| ( |
| id, |
| name, |
| kind, |
| principal_type, |
| principal["parent"], |
| ), |
| ) |
| |
| for r in data_principal["resources"]: |
| con.execute( |
| "INSERT INTO principals_resources VALUES (?, ?)", (id, r) |
| ) |
| |
| con.commit() |
| con.close() |
| |
| |
| if __name__ == "__main__": |
| main() |