blob: cb14d8a2983bd8c4bba89ff5caf0d1c667a5c601 [file] [log] [blame]
// Copyright 2020 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.
#ifndef SRC_DEVELOPER_SYSTEM_MONITOR_BIN_HARVESTER_GATHER_VMOS_H_
#define SRC_DEVELOPER_SYSTEM_MONITOR_BIN_HARVESTER_GATHER_VMOS_H_
#include <array>
#include <deque>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include "gather_category.h"
#include "os.h"
#include "union_find.h"
namespace harvester {
class SampleBundle;
const size_t kNumRootedVmos = 4;
template <typename T>
using VmoMap = std::array<T, kNumRootedVmos>;
using MemType = int64_t;
using ProcessMap = std::unordered_map<zx_koid_t, VmoMap<MemType>>;
// Gather detailed VMO information from the kernel.
class GatherVmos : public GatherCategory {
public:
// Public-facing Vmo structure (instead of zx_info_vmo_t).
struct Vmo {
Vmo() = default;
explicit Vmo(const zx_info_vmo_t& raw_vmo)
: parent_koid(raw_vmo.parent_koid),
committed_bytes(raw_vmo.committed_bytes),
allocated_bytes(raw_vmo.size_bytes) {
memcpy(name, raw_vmo.name, sizeof(name));
}
zx_koid_t parent_koid;
// fx mem uses uint64_t here resulting in underflow.
MemType committed_bytes;
MemType allocated_bytes;
char name[ZX_MAX_NAME_LEN];
};
GatherVmos(zx_handle_t info_resource,
harvester::DockyardProxy* dockyard_proxy,
harvester::TaskTree& task_tree, OS* os)
: GatherCategory(info_resource, dockyard_proxy),
task_tree_(task_tree),
os_(os) {}
// GatherCategory.
void GatherDeviceProperties() override;
void Gather() override;
private:
GatherVmos() = delete;
// TaskTree used for VMO gathering.
TaskTree& task_tree_;
// Mockable proxy class for OS calls like zx_object_get_info().
OS* os_;
// Data kept between Gather() invocations, and updated by
// DoSparseVmoUpdate().
// Map of all vmos in the system. |zx_info_vmo_t| contains the koid and
// parent_koid. Fetching VMO handles is not possible by design.
std::unordered_map<zx_koid_t, zx_info_vmo_t> koids_to_vmos_;
// Necessary to detect new processes (for immediate VMO scanning).
std::unordered_set<zx_koid_t> last_seen_processes_;
// Queue of processes we will scan sparsely.
std::deque<TaskTree::Task> process_scan_queue_;
// Map of process koids to their VMOs.
std::unordered_map<zx_koid_t, std::unordered_set<zx_koid_t>> process_to_vmos_;
// List of processes that should be scanned on every gather.
std::unordered_set<zx_koid_t> processes_with_rooted_vmos_;
// Set of VMOs that are related to rooted VMOs. Derived from |vmo_forest_|,
// which is not fast enough for the number of queries we need to run.
std::unordered_set<zx_koid_t> rooted_vmo_descendants_;
// Map of rooted VMOs. This can't be a VmoMap because some VMOs occur
// multiple times (e.g. Sysmem-external-heap).
std::unordered_set<zx_koid_t> rooted_vmos_;
// Disjoint sets of VMO koids.
UnionFind<zx_koid_t> vmo_forest_;
// Builds rooted_vmo_descendants_ set.
void BuildRootedVmoDescendants(const std::vector<zx_koid_t>& new_vmos);
// Builds the processes_with_rooted_vmos_ set.
void BuildProcessesWithRootedVmos();
// Builds the final rooted VMO data for dockyard.
//
// Returns a map of VMO koids to Vmo objects. This differs subtly from the
// data in |koids_to_vmos_| to reflect a common usage pattern for rooted VMOs:
// large allocations are often granted to services/processes that in turn
// grant smaller slices to the processes that actually use the rooted memory.
// BuildVmoData pushes those apparent allocations down from the services to
// the children to more accurately reflect memory usage:
//
// Sysmem-contig-core
// |
// v
// <service-allocating-contig-mem>
// | |
// v v
// <process-1> <process-2>
//
// In this case, our accounting of VMO memory usage will not count the
// intermediate service as "owning" rooted memory that it has already
// allocated to process 1 or process 2.
std::map<zx_koid_t, Vmo> BuildVmoData();
// Removes dead processes from process_to_vmos_.
void CleanProcessToVmos(
const std::unordered_set<zx_koid_t>& live_process_koids);
// Updates *some of* the VMO lists in koids_to_vmos_ and process_to_vmos_.
//
// The criteria for whether or not to update is described in detail in the
// function.
//
// Returns:
// * A vector of previously unseen VMO koids for more efficient updates.
// * An unordered set of the current live process koids.
void DoSparseVmoUpdate(const TaskTree& task_tree,
std::vector<zx_koid_t>& new_vmos,
std::unordered_set<zx_koid_t>& live_process_koids);
// Returns true if the VMO is descended from a rooted VMO.
bool VmoHasRootedAncestor(zx_koid_t vmo_koid);
// Returns the koid of the rooted ancestor VMO if one exists, zero if not.
zx_koid_t GetRootedAncestorKoid(zx_koid_t child_vmo_koid);
// Creates and stores handles to all VMOs belonging to |parent_process|.
//
// New VMOs are added to the |new_vmos| vector.
void GatherVmosForProcess(zx_handle_t process, zx_koid_t process_koid,
std::vector<zx_koid_t>& new_vmos);
// Uploads samples to dockyard.
void UploadSamples(const std::unordered_set<zx_koid_t>& live_process_koids,
std::map<zx_koid_t, Vmo>& vmo_data);
};
} // namespace harvester
#endif // SRC_DEVELOPER_SYSTEM_MONITOR_BIN_HARVESTER_GATHER_VMOS_H_