blob: 2678275ab2fe8e7c19c7719b57c49c84d197badf [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.
#include "src/developer/forensics/feedback_data/inspect_data_budget.h"
#include <lib/syslog/cpp/macros.h>
#include <stdint.h>
#include <algorithm>
#include <filesystem>
#include "src/developer/forensics/feedback_data/constants.h"
#include "src/developer/forensics/utils/cobalt/metrics.h"
namespace forensics::feedback_data {
namespace {
// We target a 2MB final ZIP file. We give a budget between 4MB and 20MB for Inspect data, starting
// at 20MB.
const size_t kTargetZipSizeInBytes = 2 * 1024 * 1024;
const size_t kMinInspectDataBudgetInBytes = 4 * 1024 * 1024;
const size_t kMaxInspectDataBudgetInBytes = 20 * 1024 * 1024;
const size_t kStartingInspectDataBudgetInBytes = kMaxInspectDataBudgetInBytes;
} // namespace
InspectDataBudget::InspectDataBudget(const bool limit_data, InspectNodeManager* node,
cobalt::Logger* cobalt)
: limit_data_flag_(limit_data), inspect_node_(node), cobalt_(cobalt) {
inspect_budget_enabled_ =
inspect_node_->Get("/inspect_budget")
.CreateString("is_budget_enabled", limit_data_flag_ ? "true" : "false");
if (!limit_data_flag_) {
data_budget_ = std::nullopt;
return;
}
data_budget_ = kStartingInspectDataBudgetInBytes;
inspect_min_budget_ = inspect_node_->Get("/inspect_budget")
.CreateUint("min_input_budget_bytes", kMinInspectDataBudgetInBytes);
inspect_max_budget_ = inspect_node_->Get("/inspect_budget")
.CreateUint("max_input_budget_bytes", kMaxInspectDataBudgetInBytes);
inspect_target_size_ = inspect_node_->Get("/inspect_budget")
.CreateUint("target_snapshot_size_bytes", kTargetZipSizeInBytes);
}
void InspectDataBudget::UpdateBudget(
const std::map<std::string, ArchiveFileStats>& file_size_stats) {
std::string inspect_filename(kAttachmentInspect);
// No-op if data limiting is disabled or the Inspect file doesn't exist in the latest archive.
if (!limit_data_flag_ || file_size_stats.count(inspect_filename) == 0) {
return;
}
size_t previous_zip_size = 0;
for (const auto& [name, size] : file_size_stats) {
previous_zip_size += size.compressed_bytes;
}
// Online algorithm; there is no guarantee the same input will give us the same output. For
// simplicity we use only the last budget and its size output to calculate the new budget.
//
// Note: converges faster when the compressed portion of the inspect file is larger.
// Note: converges when the relationship between budget and snapshot is close to linearity.
const double ratio = (double)kTargetZipSizeInBytes / (double)previous_zip_size;
const size_t new_budget = (size_t)((double)data_budget_.value() * ratio);
data_budget_ = std::clamp(new_budget, kMinInspectDataBudgetInBytes, kMaxInspectDataBudgetInBytes);
// Add new budget data to Inspect.
auto entry =
inspect_node_->Get("/inspect_budget/last_ten_input_budget_previous_snapshot_size_bytes")
.CreateUintArray(std::to_string(next_reading_idx_++), 2);
entry.Set(0, data_budget_.value());
entry.Set(1, previous_zip_size);
inspect_last_ten_readings_.push_back(std::move(entry));
// We only keep the last 10 readings.
if (inspect_last_ten_readings_.size() > 10) {
inspect_last_ten_readings_.pop_front();
}
cobalt_->LogIntegerEvent(cobalt::kInspectBudgetMetricId, data_budget_.value());
}
} // namespace forensics::feedback_data