blob: a81b9f0b7a3acd3cbea3682ca440c18480adafb2 [file] [log] [blame]
// Copyright 2019 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.
#include "src/developer/memory/metrics/digest.h"
#include <lib/trace/event.h>
#include <regex>
namespace memory {
bool BlobfsIsActiveVmo(const Vmo& vmo) {
if (std::regex_match(vmo.name, std::regex("blob-[0-9a-f]+"))) {
// Data VMOs are only active when they have clients
return vmo.num_children > 0;
}
// Any other VMO is considered active
return true;
}
// VMOs are bucketed into the first matching entry.
const std::vector<const BucketMatch> Digester::kDefaultBucketMatches = {
{"ZBI Buffer", ".*", "uncompressed-bootfs"},
// Memory used with the GPU or display hardware.
{"Graphics", ".*", "magma_create_buffer|Mali .*|Magma.*|ImagePipe2Surface.*"},
// Unused protected pool memory.
{"ProtectedPool", "driver_host:.*", "SysmemAmlogicProtectedPool"},
// Unused contiguous pool memory.
{"ContiguousPool", "driver_host:.*", "SysmemContiguousPool"},
{"Fshost", "fshost.cm", ".*"},
{"Minfs", ".*minfs", ".*"},
{"Blobfs", ".*blobfs", ".*", [](const Vmo& vmo) { return BlobfsIsActiveVmo(vmo); }},
{"BlobfsInactive", ".*blobfs", ".*", [](const Vmo& vmo) { return !BlobfsIsActiveVmo(vmo); }},
{"FlutterApps", "io\\.flutter\\..*", "dart.*"},
{"Flutter", "io\\.flutter\\..*", ".*"},
{"Web", "web_engine_exe:.*", ".*"},
{"Kronk", "kronk.cmx|kronk_for_testing.cmx", ".*"},
{"Scenic", "scenic.cmx", ".*"},
{"Amlogic", "driver_host:pdev:05:00:f", ".*"},
{"Netstack", "netstack.cmx", ".*"},
{"Pkgfs", "pkgfs", ".*"},
{"Cast", "cast_agent.cmx", ".*"},
{"Archivist", "archivist.cm", ".*"},
{"Cobalt", "cobalt.cmx", ".*"},
{"Audio", "audio_core.cmx", ".*"},
{"Context", "context_provider.cmx", ".*"},
};
BucketMatch::BucketMatch(const std::string& name, const std::string& process,
const std::string& vmo)
: name_(name), process_(process), vmo_(vmo) {}
BucketMatch::BucketMatch(const std::string& name, const std::string& process,
const std::string& vmo, VmoMatcher vmo_matcher)
: name_(name), process_(process), vmo_(vmo), vmo_matcher_(vmo_matcher) {}
bool BucketMatch::ProcessMatch(const std::string& process) {
const auto& pi = process_match_.find(process);
if (pi != process_match_.end()) {
return pi->second;
}
bool match = std::regex_match(process, process_);
process_match_.emplace(process, match);
return match;
}
bool BucketMatch::VmoMatch(const Vmo& vmo) {
const auto& vi = vmo_match_.find(vmo.name);
if (vi != vmo_match_.end()) {
return vi->second;
}
bool match = std::regex_match(vmo.name, vmo_);
if (vmo_matcher_) {
match &= (*vmo_matcher_)(vmo);
}
vmo_match_.emplace(vmo.name, match);
return match;
}
Digest::Digest(const Capture& capture, Digester* digester) { digester->Digest(capture, this); }
Digester::Digester(const std::vector<const BucketMatch>& bucket_matches) {
for (const auto& bucket_match : bucket_matches) {
bucket_matches_.emplace_back(bucket_match);
}
}
void Digester::Digest(const Capture& capture, class Digest* digest) {
TRACE_DURATION("memory_metrics", "Digester::Digest");
digest->time_ = capture.time();
digest->undigested_vmos_.reserve(capture.koid_to_vmo().size());
for (const auto& [koid, vmo] : capture.koid_to_vmo()) {
digest->undigested_vmos_.emplace(koid);
}
digest->buckets_.reserve(bucket_matches_.size());
for (auto& bucket_match : bucket_matches_) {
auto& bucket = digest->buckets_.emplace_back(bucket_match.name(), 0);
for (const auto& [koid, process] : capture.koid_to_process()) {
if (!bucket_match.ProcessMatch(process.name)) {
continue;
}
for (const auto& v : process.vmos) {
if (digest->undigested_vmos_.count(v) == 0) {
continue;
}
const auto& vmo = capture.vmo_for_koid(v);
if (!bucket_match.VmoMatch(vmo)) {
continue;
}
bucket.size_ += vmo.committed_bytes;
digest->undigested_vmos_.erase(v);
}
}
}
std::sort(digest->buckets_.begin(), digest->buckets_.end(),
[](const Bucket& a, const Bucket& b) { return a.size() > b.size(); });
uint64_t undigested_size = 0;
for (auto v : digest->undigested_vmos_) {
undigested_size += capture.vmo_for_koid(v).committed_bytes;
}
if (undigested_size > 0) {
digest->buckets_.emplace_back("Undigested", undigested_size);
}
const auto& kmem = capture.kmem();
if (kmem.total_bytes > 0) {
uint64_t vmo_size = 0;
for (const auto& bucket : digest->buckets_) {
vmo_size += bucket.size_;
}
if (vmo_size < kmem.vmo_bytes) {
digest->buckets_.emplace_back("Orphaned", kmem.vmo_bytes - vmo_size);
}
digest->buckets_.emplace_back("Kernel", kmem.wired_bytes + kmem.total_heap_bytes +
kmem.mmu_overhead_bytes + kmem.ipc_bytes +
kmem.other_bytes);
digest->buckets_.emplace_back("Free", kmem.free_bytes);
const auto& kmem_ext = capture.kmem_extended();
if (kmem_ext.vmo_pager_total_bytes > 0) {
digest->buckets_.emplace_back("[Addl]PagerTotal", kmem_ext.vmo_pager_total_bytes);
digest->buckets_.emplace_back("[Addl]PagerNewest", kmem_ext.vmo_pager_newest_bytes);
digest->buckets_.emplace_back("[Addl]PagerOldest", kmem_ext.vmo_pager_oldest_bytes);
}
if (kmem_ext.vmo_discardable_locked_bytes > 0 || kmem_ext.vmo_discardable_unlocked_bytes > 0) {
digest->buckets_.emplace_back("[Addl]DiscardableLocked",
kmem_ext.vmo_discardable_locked_bytes);
digest->buckets_.emplace_back("[Addl]DiscardableUnlocked",
kmem_ext.vmo_discardable_unlocked_bytes);
}
}
}
} // namespace memory