chromium/services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

module memory_instrumentation.mojom;

import "mojo/public/mojom/base/big_string.mojom";
import "mojo/public/mojom/base/process_id.mojom";
import "mojo/public/mojom/base/time.mojom";

// Common structs:

enum DumpType {
  PERIODIC_INTERVAL,
  EXPLICITLY_TRIGGERED,
  SUMMARY_ONLY
};

enum LevelOfDetail {
  BACKGROUND,
  LIGHT,
  DETAILED
};

// Tells the MemoryDumpProvider(s) if they should try to make the result
// more deterministic by forcing garbage collection.
enum Determinism {
  NONE,
  FORCE_GC
};

enum ProcessType {
  OTHER,
  BROWSER,
  RENDERER,
  GPU,
  UTILITY,
  PLUGIN,
  ARC
};

enum MemoryMapOption {
  // Do not fetch any information about mapped regions in the virtual address
  // space.
  NONE,

  // Only fetch information on code modules, e.g. Chrome Binary, system
  // libraries.
  MODULES,

  // Fetch information for every single mapped region.
  FULL
};

// These structs are internal only (only for the communication between
// the service and the ClientProcess library).
// See corresponding types in //base/trace_event for comments.

struct RequestArgs {
  uint64 dump_guid;
  DumpType dump_type;
  LevelOfDetail level_of_detail;
  Determinism determinism;
};

struct RawAllocatorDumpEdge {
  uint64 source_id;
  uint64 target_id;
  int32 importance;
  bool overridable;
};

union RawAllocatorDumpEntryValue {
  uint64 value_uint64;
  string value_string;
};

struct RawAllocatorDumpEntry {
  string name;
  string units;
  RawAllocatorDumpEntryValue value;
};

struct RawAllocatorDump {
  uint64 id;
  string absolute_name;
  bool weak;
  LevelOfDetail level_of_detail;
  array<RawAllocatorDumpEntry> entries;
};

struct RawProcessMemoryDump {
  LevelOfDetail level_of_detail;
  array<RawAllocatorDumpEdge> allocator_dump_edges;
  array<RawAllocatorDump> allocator_dumps;
};

struct VmRegion {
  const uint32 kProtectionFlagsRead = 4;
  const uint32 kProtectionFlagsWrite = 2;
  const uint32 kProtectionFlagsExec = 1;
  const uint32 kProtectionFlagsMayshare = 128;

  uint64 start_address;
  uint64 size_in_bytes;
  uint64 module_timestamp;
  // Unique module debug identifier to retrieve debug information.
  string module_debugid;
  string module_debug_path;
  uint32 protection_flags;

  string mapped_file;

  // private_dirty_resident + private_clean_resident + shared_dirty_resident +
  // shared_clean_resident = resident set size.
  uint64 byte_stats_private_dirty_resident;
  uint64 byte_stats_private_clean_resident;
  uint64 byte_stats_shared_dirty_resident;
  uint64 byte_stats_shared_clean_resident;

  uint64 byte_stats_swapped;
  uint64 byte_locked;

  // For multiprocess accounting.
  uint64 byte_stats_proportional_resident;
};

// Platform-specific data that will be used to compute the
// PrivateMemoryFootprint.
struct PlatformPrivateFootprint {
  uint64 phys_footprint_bytes = 0;
  // macOS 10.12+
  uint64 internal_bytes = 0;

  // macOS [all versions]
  uint64 compressed_bytes = 0;
  uint64 rss_anon_bytes = 0;

  // Linux, Android, ChromeOS
  uint64 vm_swap_bytes = 0;

  // On Windows,
  uint64 private_bytes = 0;

  // On iOS,
  //   TBD: https://crbug.com/714961
};

struct RawOSMemDump {
  uint32 resident_set_kb = 0;
  // peak_resident_set_kb is an experimental field. Please do not use it until
  // crbug.com/957978 reaches to a conclusion.
  uint32 peak_resident_set_kb = 0;
  bool is_peak_rss_resettable = false;
  PlatformPrivateFootprint platform_private_footprint;
  array<VmRegion> memory_maps;

  // Each bit corresponds to a page. The bit is set if the page
  // is mapped and resident in memory.
  array<uint8> native_library_pages_bitmap;
};

// These structs are public:

struct OSMemDump {
  uint32 resident_set_kb = 0;

  // The peak resident set size as observed by the kernel. This is currently
  // reported on Linux and Android only. Also, this is an experimental field.
  // Please do not use it until crbug.com/957978 reaches to a conclusion.
  uint32 peak_resident_set_kb = 0;

  // Indicates whether the above peak_resident_set_kb is the peak from the start
  // of the process or from the previous memory dump.
  bool is_peak_rss_resettable = false;

  // This is roughly private, anonymous, non-discardable, resident or swapped
  // memory in kilobytes. For more details, see https://goo.gl/3kPb9S.
  uint32 private_footprint_kb = 0;

  // This is roughly non-private, anonymous, non-discardable, resident memory
  // in kilobytes. For more details, see https://goo.gl/3kPb9S.
  uint32 shared_footprint_kb = 0;

  // This is private swapped memory in kilobytes reported on Linux and Android
  // only.
  [EnableIf=private_swap_info]
  uint32 private_footprint_swap_kb = 0;
};

// This struct contains information about a particular allocator dump
// (e.g. cc/resource_memory).
struct AllocatorMemDump {
  // The entries for the allocator which are of scalar types (i.e. not strings).
  // Examples include: effective_size, size, object_count.
  map<string, uint64> numeric_entries;

  // If details were requested for this allocator to include descendants then
  // |children| describes the immediate child nodes.
  map<string, AllocatorMemDump> children;
};

// This struct is used for the public-facing API
// Coordinator::RequestGlobalMemoryDump().
struct ProcessMemoryDump {
  ProcessType process_type;
  OSMemDump os_dump;

  // Details for each of the Chrome allocators specified by the input args.
  // See the |RequestGlobalMemoryDump()| argument |allocator_dump_names|.
  map<string, AllocatorMemDump> chrome_allocator_dumps;

  // |pid| is necessary to correlate a ProcessMemoryDump with the URLs for the
  // process, which is obtained from the ResourceCoordinator service. In a
  // future world where both ResourceCoordinator and MemoryInstrumentation are
  // less in flux, they will probably be merged into a single service, and this
  // parameter can be removed.
  mojo_base.mojom.ProcessId pid;

  // The name of the primary service running in the process, if any.
  string? service_name;
};

// Metrics aggregated across all processes.
struct AggregatedMetrics {
  // These values are not always available, -1 when invalid.
  int32 native_library_resident_kb = 0;
  int32 native_library_resident_not_ordered_kb = 0;
  int32 native_library_not_resident_ordered_kb = 0;
};

// This struct is returned by the public-facing API
// Coordinator::RequestGlobalMemoryDump().
struct GlobalMemoryDump {
  // This time stamp precedes any of the data captured in |process_dumps|. Since
  // dumps are queued, and since at most one dump is captured at a time, the
  // start time of a global dump may lag the request time substantially.
  mojo_base.mojom.TimeTicks start_time;
  array<ProcessMemoryDump> process_dumps;
  AggregatedMetrics aggregated_metrics;
};

// This is the interface implemented by the per-process client library. This
// allows a process to contribute to memory-infra dumps. There should be at
// most one instance of this per hosting process.
interface ClientProcess {
  // When |success| == true the dump is appended in the process-local trace
  // buffer of the target process and an ACK. A summary of the dump is also
  // returned in case of success.
  RequestChromeMemoryDump(RequestArgs args) =>
      (bool success, uint64 dump_id, RawProcessMemoryDump? raw_process_memory_dump);

  // Requests an OSMemDump for each pid in |pids|.
  // The Memory-infra service deals with two kinds of information:
  // 1) Chrome's view of its own memory use (recorded as
  //    |chrome_allocator_dumps|)
  // 2) The OS's view of Chrome's memory use (recorded as OSMemDumps)
  // Both of these are collected per process. On most platforms each process
  // can make the system calls to collect its own OSMemDump however on some
  // platforms *cough* Linux *cough* due to sandboxing only the browser
  // process can collect OSMemDumps. This API allows for these two cases.
  // On most platforms we send this message to each ClientProcess with
  // with one pid - kNullProcessId - meaning return just your own OSMemDump.
  // On Linux we call this once on the browser process ClientProcess passing
  // the pids for all processes.
  // See crbug.com/461788
  RequestOSMemoryDump(MemoryMapOption option, array<mojo_base.mojom.ProcessId> pids) =>
      (bool success, map<mojo_base.mojom.ProcessId, RawOSMemDump> dumps);
};

struct HeapProfileResult {
  mojo_base.mojom.ProcessId pid;
  mojo_base.mojom.BigString json;
};

// HeapProfilers expose a single interface to memory_instrumentation, allowing
// the latter to query for heap dumps when necessary.
//
// This interface is NOT implemented in resource_coordinator but by the
// profiling service in chrome/profiler. The profiling service registers itself
// with the Coordinator (see RegisterHeapProfiler) and is invoked when a memory
// dump is requested (via Coordinator::RequestGlobalMemoryDump).
interface HeapProfiler {
  // Dumps the memory log of all profiled processes into BigString's.
  // The contents of each BigString is a JSON string compatible with
  // TRACE_EVENT* macros. Processes that fail to dump will be omitted from
  // |result|. When |strip_path_from_mapped_files| is true, only the base name
  // of mapped files is emitted. This prevents usernames from sneaking into the
  // trace.
  // |strip_path_from_mapped_files| should only be true for traces that will be
  // uploaded to the crash servers - this strips potential PII, but prevents
  // symbolization of local builds.
  // |write_proto| will additionally write proto heap profiles to traces.
  DumpProcessesForTracing(bool strip_path_from_mapped_files,
                          bool write_proto) =>
      (array<HeapProfileResult> results);
};

// Implemented by resource_coordinator to provide additional information needed
// by the HeapProfiler.
interface HeapProfilerHelper {
  // Broadcasts a RequestOSMemoryDump-only request for all registered client
  // processes and retrieves only their memory maps.
  GetVmRegionsForHeapProfiler(array<mojo_base.mojom.ProcessId> pids) =>
      (map<mojo_base.mojom.ProcessId, array<VmRegion>> vm_regions);
};

// The memory-infra service implements this interface. There is one instance for
// the whole system. The coordinator maintains a list of registered client
// processes and polls them whenever a global dump is required.
interface Coordinator {
  // Broadcasts a dump request to all registered client processes and returns a
  // global dump with details.
  // If |dump_type| is SUMMARY_ONLY then details will additionally be returned
  // for allocators listed in |allocator_dump_names|. If an entry has a trailing
  // "/*" then details will be returned for the named node, and all descendents.
  // e.g. { "malloc", "v8/*" } will include details for "malloc", and might
  // also include "v8", "v8/main/heap", etc.
  // Allocator dumps provide a set of numeric values such as "size",
  // "effective_size", etc.
  RequestGlobalMemoryDump(DumpType dump_type,
                          LevelOfDetail level_of_detail,
                          Determinism determinism,
                          array<string> allocator_dump_names) =>
      (bool success, GlobalMemoryDump? global_memory_dump);

  // Sends a dump request to the client process given by the specified pid and
  // returns a summarized dump back or null if there was a failure.
  RequestGlobalMemoryDumpForPid(mojo_base.mojom.ProcessId pid,
                                array<string> allocator_dump_names) =>
      (bool success, GlobalMemoryDump? global_memory_dump);

  // Requesting a null pid is the same as requesting all pids.
  // The returned dump only contains OS metrics. |chrome_allocator_dumps| will
  // be empty.
  RequestPrivateMemoryFootprint(mojo_base.mojom.ProcessId pid) =>
      (bool success, GlobalMemoryDump? global_memory_dump);

  // Broadcasts a dump request to all registered client processes and injects the
  // dump in the trace buffer (if tracing is enabled).
  RequestGlobalMemoryDumpAndAppendToTrace(DumpType dump_type,
                                          LevelOfDetail level_of_detail,
                                          Determinism determinism) =>
      (bool success, uint64 dump_id);
};

// An interface used by client processes to register themselves with a
// memory_instrumentation::Registry. The implementation must already know the
// calling process. It adds authoritative information about the calling process'
// identity when forwarding the registration to a
// memory_instrumentation::Registry.
interface CoordinatorConnector {
  RegisterCoordinatorClient(pending_receiver<Coordinator> receiver,
                            pending_remote<ClientProcess> client_process);
};