blob: 39ea1b1d95324d7e8ec3ed0509cff141f8ad3600 [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_FORENSICS_CRASH_REPORTS_SNAPSHOT_MANAGER_H_
#define SRC_DEVELOPER_FORENSICS_CRASH_REPORTS_SNAPSHOT_MANAGER_H_
#include <fuchsia/feedback/cpp/fidl.h>
#include <lib/async/cpp/task.h>
#include <lib/async/dispatcher.h>
#include <lib/fpromise/promise.h>
#include <lib/sys/cpp/service_directory.h>
#include <lib/zx/time.h>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <vector>
#include "src/developer/forensics/crash_reports/snapshot.h"
#include "src/developer/forensics/feedback/annotations/annotation_manager.h"
#include "src/developer/forensics/feedback/annotations/types.h"
#include "src/developer/forensics/utils/storage_size.h"
#include "src/lib/timekeeper/clock.h"
namespace forensics {
namespace crash_reports {
using SnapshotUuid = std::string;
// Manages the collection, distribution, and lifetime of snapshots.
//
// To limit memory usage, the managed snapshots' annotations/archives cannot exceed
// |max_{annotations,archives}_size_| in size and snapshot manager will return the same Uuid to all
// calls to GetUuid that occur within |shared_request_window_| of a
// fuchsia.feedback.DataProvider/GetSnapshot request.
//
// When space is constrained, SnapshotManager will drop the oldest annotations/archives it
// manages. Additionally, SnapshotManager tracks the number of clients that have received a specific
// Uuid from GetUuid and will automatically delete a snapshot when each client has called Release.
class SnapshotManager {
public:
SnapshotManager(async_dispatcher_t* dispatcher, timekeeper::Clock* clock,
fuchsia::feedback::DataProvider* data_provider,
feedback::AnnotationManager* annotation_manager,
zx::duration shared_request_window,
const std::string& garbage_collected_snapshots_path,
StorageSize max_annotations_size, StorageSize max_archives_size);
// Returns a promise of a snapshot uuid for a snapshot that contains the most up-to-date system
// data (a new snapshot will be created if all existing snapshots contain data that is
// out-of-date). No uuid will be returned if |timeout| expires.
::fpromise::promise<SnapshotUuid> GetSnapshotUuid(zx::duration timeout);
// Returns the snapshot for |uuid|, if one exists. If no snapshot exists for |uuid| a snapshot
// containing annotations indicating the error will be returned.
//
// When a client no longer needs the data contained in a Snapshot, they should call Release to
// inform the SnapshotManager. If all clients call release, the SnapshotManager will voluntarily
// drop the Snapshot, freeing up space for new data.
Snapshot GetSnapshot(const SnapshotUuid& uuid);
// Tell SnapshotManager that a client no longer needs the snapshot for |uuid|. If the difference
// between the number of calls to GetUuid and Release reaches 0, the snapshot for |uuid| will be
// dropped by SnapshotManager.
void Release(const SnapshotUuid& uuid);
// Shuts down the snapshot manager by cancelling any pending FIDL calls and provides waiting
// clients with a UUID for a generic "shutdown" snapshot.
void Shutdown();
// Returns a Uuid a client can use if it doesn't have one, e.g., it was previously stored in a
// file and the file is gone.
static SnapshotUuid UuidForNoSnapshotUuid() { return "no uuid"; }
private:
// State associated with an async call to fuchsia.feedback.DataProvider/GetSnapshot.
struct SnapshotRequest {
// The uuid of the request's snapshot.
SnapshotUuid uuid;
// Whether the request is pending.
bool is_pending;
// Promises that are waiting on the call to complete.
std::vector<::fpromise::suspended_task> blocked_promises;
// The actual request that we delay by |shared_request_window_| after the SnapshotRequest is
// created.
async::TaskClosure delayed_get_snapshot;
};
// State associated with a snapshot.
// * The number of clients with its uuid.
// * The size of its annotations.
// * The size of its archive.
// * The snapshot annotations.
// * The snapshot archive.
// * The annotations that convey any errors affecting the snapshot data.
struct SnapshotData {
size_t num_clients_with_uuid;
StorageSize annotations_size;
StorageSize archive_size;
std::shared_ptr<const feedback::Annotations> annotations;
std::shared_ptr<const ManagedSnapshot::Archive> archive;
std::shared_ptr<feedback::Annotations> presence_annotations;
};
// Determine if the most recent SnapshotRequest's delayed call to
// fuchsia.feedback.DataProvider/GetSnapshopt has executed.
bool UseLatestRequest() const;
// Find the Snapshot{Request,Data} with Uuid |uuid|. If none exists, return nullptr.
SnapshotRequest* FindSnapshotRequest(const SnapshotUuid& uuid);
SnapshotData* FindSnapshotData(const SnapshotUuid& uuid);
// Resume |get_uuid_promise| when |deadline| expires or request |uuid| completes with a snapshot.
void WaitForSnapshot(const SnapshotUuid& uuid, zx::time deadline,
::fpromise::suspended_task get_uuid_promise);
// Make a call to fuchsia.feedback.DataProvider/GetSnapshot, started at |start_time|, and return
// the Uuid of its eventual snapshot.
SnapshotUuid MakeNewSnapshotRequest(zx::time start_time, zx::duration timeout);
// Use |fidl_snapshot| to update all snapshot-related state with Uuid |uuid|.
void CompleteWithSnapshot(const SnapshotUuid& uuid, fuchsia::feedback::Snapshot fidl_snapshot);
// Remove the annotations and archives of the oldest requests, independently of one another, until
// |current_annotations_size_| is less than or equal to |max_annotations_size_| and
// |current_archives_size_| is less than or equal to |max_archives_size_|.
//
// Note: References into |requests_| and |data_| will be invalidated during this process. Be
// cautious using the function!
void EnforceSizeLimits();
// Drop the {annotation,archive} for |uuid| and clean up state associated with them.
void DropAnnotations(SnapshotData* data);
void DropArchive(SnapshotData* data);
void RecordAsGarbageCollected(const SnapshotUuid& uuid);
async_dispatcher_t* dispatcher_;
std::shared_ptr<sys::ServiceDirectory> services_;
timekeeper::Clock* clock_;
fuchsia::feedback::DataProvider* data_provider_;
feedback::AnnotationManager* annotation_manager_;
zx::duration shared_request_window_;
std::string garbage_collected_snapshots_path_;
StorageSize max_annotations_size_;
StorageSize current_annotations_size_;
StorageSize max_archives_size_;
StorageSize current_archives_size_;
std::vector<std::unique_ptr<SnapshotRequest>> requests_;
std::map<SnapshotUuid, SnapshotData> data_;
std::set<SnapshotUuid> garbage_collected_snapshots_;
bool shutdown_{false};
// SnapshotUuid and annotations to return under specific conditions, e.g., garbage collection,
// time outs.
struct SpecialCaseSnapshot {
explicit SpecialCaseSnapshot(SnapshotUuid uuid, feedback::Annotations annotations)
: uuid(std::move(uuid)), annotations(std::move(annotations)) {}
SnapshotUuid uuid;
feedback::Annotations annotations;
};
const SpecialCaseSnapshot garbage_collected_snapshot_;
const SpecialCaseSnapshot not_persisted_snapshot_;
const SpecialCaseSnapshot timed_out_snapshot_;
const SpecialCaseSnapshot shutdown_snapshot_;
const SpecialCaseSnapshot no_uuid_snapshot_;
};
} // namespace crash_reports
} // namespace forensics
#endif // SRC_DEVELOPER_FORENSICS_CRASH_REPORTS_SNAPSHOT_MANAGER_H_