blob: dbe2006938a37c68731e9694fe2d19897a89d04b [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/feedback/crashpad_agent/info/inspect_manager.h"
#include <lib/inspect/cpp/hierarchy.h>
#include <lib/inspect/cpp/inspect.h>
#include <lib/inspect/cpp/reader.h>
#include <lib/timekeeper/test_clock.h>
#include <lib/zx/time.h>
#include <cstdint>
#include <memory>
#include "sdk/lib/inspect/testing/cpp/inspect.h"
#include "src/developer/feedback/crashpad_agent/config.h"
#include "src/developer/feedback/crashpad_agent/constants.h"
#include "src/developer/feedback/crashpad_agent/settings.h"
#include "src/lib/fxl/logging.h"
#include "src/lib/fxl/strings/string_printf.h"
#include "third_party/googletest/googlemock/include/gmock/gmock.h"
#include "third_party/googletest/googletest/include/gtest/gtest.h"
namespace feedback {
namespace {
using inspect::testing::ChildrenMatch;
using inspect::testing::NameMatches;
using inspect::testing::NodeMatches;
using inspect::testing::PropertyList;
using inspect::testing::StringIs;
using inspect::testing::UintIs;
using testing::Contains;
using testing::ElementsAre;
using testing::IsEmpty;
using testing::Not;
using testing::UnorderedElementsAreArray;
constexpr zx::time_utc kTime1(0);
constexpr zx::time_utc kTime2((zx::hour(7) + zx::min(14) + zx::sec(52)).get());
constexpr zx::time_utc kTime3((zx::hour(3) * 24 + zx::hour(15) + zx::min(33) + zx::sec(17)).get());
constexpr char kTime1Str[] = "1970-01-01 00:00:00 GMT";
constexpr char kTime2Str[] = "1970-01-01 07:14:52 GMT";
constexpr char kTime3Str[] = "1970-01-04 15:33:17 GMT";
constexpr CrashServerConfig::UploadPolicy kConfigDisabled =
CrashServerConfig::UploadPolicy::DISABLED;
constexpr CrashServerConfig::UploadPolicy kConfigEnabled = CrashServerConfig::UploadPolicy::ENABLED;
constexpr CrashServerConfig::UploadPolicy kConfigReadFromPrivacySettings =
CrashServerConfig::UploadPolicy::READ_FROM_PRIVACY_SETTINGS;
constexpr Settings::UploadPolicy kSettingsDisabled = Settings::UploadPolicy::DISABLED;
constexpr Settings::UploadPolicy kSettingsEnabled = Settings::UploadPolicy::ENABLED;
constexpr Settings::UploadPolicy kSettingsLimbo = Settings::UploadPolicy::LIMBO;
class InspectManagerTest : public testing::Test {
public:
void SetUp() override {
inspector_ = std::make_unique<inspect::Inspector>();
inspect_manager_ = std::make_unique<InspectManager>(&inspector_->GetRoot(), clock_);
}
protected:
inspect::Hierarchy InspectTree() {
auto result = inspect::ReadFromVmo(inspector_->DuplicateVmo());
FXL_CHECK(result.is_ok());
return result.take_value();
}
timekeeper::TestClock clock_;
std::unique_ptr<InspectManager> inspect_manager_;
private:
std::unique_ptr<inspect::Inspector> inspector_;
};
TEST_F(InspectManagerTest, InitialInspectTree) {
EXPECT_THAT(InspectTree(), ChildrenMatch(UnorderedElementsAreArray({
NodeMatches(NameMatches("config")),
NodeMatches(NameMatches("database")),
NodeMatches(NameMatches("reports")),
NodeMatches(NameMatches("settings")),
NodeMatches(NameMatches("queue")),
})));
}
TEST_F(InspectManagerTest, Succeed_AddReport_UniqueReports) {
clock_.Set(kTime1);
EXPECT_TRUE(inspect_manager_->AddReport("program_1", "local_report_id_1"));
EXPECT_THAT(
InspectTree(),
ChildrenMatch(Contains(
AllOf(NodeMatches(NameMatches("reports")),
ChildrenMatch(ElementsAre(AllOf(
NodeMatches(NameMatches("program_1")),
ChildrenMatch(ElementsAre(NodeMatches(AllOf(
NameMatches("local_report_id_1"),
PropertyList(ElementsAre(StringIs("creation_time", kTime1Str))))))))))))));
clock_.Set(kTime2);
EXPECT_TRUE(inspect_manager_->AddReport("program_1", "local_report_id_2"));
EXPECT_THAT(
InspectTree(),
ChildrenMatch(Contains(AllOf(
NodeMatches(NameMatches("reports")),
ChildrenMatch(ElementsAre(AllOf(NodeMatches(NameMatches("program_1")),
ChildrenMatch(UnorderedElementsAreArray({
NodeMatches(AllOf(NameMatches("local_report_id_1"),
PropertyList(ElementsAre(StringIs(
"creation_time", kTime1Str))))),
NodeMatches(AllOf(NameMatches("local_report_id_2"),
PropertyList(ElementsAre(StringIs(
"creation_time", kTime2Str))))),
})))))))));
clock_.Set(kTime3);
EXPECT_TRUE(inspect_manager_->AddReport("program_2", "local_report_id_3"));
EXPECT_TRUE(inspect_manager_->AddReport("program_2", "local_report_id_4"));
EXPECT_TRUE(inspect_manager_->AddReport("program_2", "local_report_id_5"));
EXPECT_THAT(
InspectTree(),
ChildrenMatch(Contains(AllOf(
NodeMatches(NameMatches("reports")),
ChildrenMatch(UnorderedElementsAreArray({
AllOf(NodeMatches(NameMatches("program_1")),
ChildrenMatch(UnorderedElementsAreArray({
NodeMatches(
AllOf(NameMatches("local_report_id_1"),
PropertyList(ElementsAre(StringIs("creation_time", kTime1Str))))),
NodeMatches(
AllOf(NameMatches("local_report_id_2"),
PropertyList(ElementsAre(StringIs("creation_time", kTime2Str))))),
}))),
AllOf(NodeMatches(NameMatches("program_2")),
ChildrenMatch(UnorderedElementsAreArray({
NodeMatches(
AllOf(NameMatches("local_report_id_3"),
PropertyList(ElementsAre(StringIs("creation_time", kTime3Str))))),
NodeMatches(
AllOf(NameMatches("local_report_id_4"),
PropertyList(ElementsAre(StringIs("creation_time", kTime3Str))))),
NodeMatches(
AllOf(NameMatches("local_report_id_5"),
PropertyList(ElementsAre(StringIs("creation_time", kTime3Str))))),
}))),
}))))));
}
TEST_F(InspectManagerTest, Succeed_AddReport_ProgramNameHasBackslashes) {
const std::string program_name = "fuchsia-pkg://fuchsia.com/foo_bar.cmx";
clock_.Set(kTime1);
EXPECT_TRUE(inspect_manager_->AddReport(program_name, "local_report_id_1"));
EXPECT_THAT(
InspectTree(),
ChildrenMatch(Contains(
AllOf(NodeMatches(NameMatches("reports")),
ChildrenMatch(ElementsAre(AllOf(
NodeMatches(NameMatches(program_name)),
ChildrenMatch(ElementsAre(NodeMatches(AllOf(
NameMatches("local_report_id_1"),
PropertyList(ElementsAre(StringIs("creation_time", kTime1Str))))))))))))));
}
TEST_F(InspectManagerTest, Fail_AddReport_DuplicateReport) {
clock_.Set(kTime2);
EXPECT_TRUE(inspect_manager_->AddReport("program", "local_report_id"));
clock_.Set(kTime3);
EXPECT_FALSE(inspect_manager_->AddReport("program", "local_report_id"));
EXPECT_THAT(
InspectTree(),
ChildrenMatch(Contains(
AllOf(NodeMatches(NameMatches("reports")),
ChildrenMatch(ElementsAre(AllOf(
NodeMatches(NameMatches("program")),
ChildrenMatch(ElementsAre(NodeMatches(AllOf(
NameMatches("local_report_id"),
PropertyList(ElementsAre(StringIs("creation_time", kTime2Str))))))))))))));
}
TEST_F(InspectManagerTest, Succeed_SetUploadAttempt) {
clock_.Set(kTime2);
EXPECT_TRUE(inspect_manager_->AddReport("program", "local_report_id"));
EXPECT_TRUE(inspect_manager_->SetUploadAttempt("local_report_id", 1u));
EXPECT_THAT(InspectTree(),
ChildrenMatch(Contains(AllOf(
NodeMatches(NameMatches("reports")),
ChildrenMatch(ElementsAre(AllOf(
NodeMatches(NameMatches("program")),
ChildrenMatch(ElementsAre(AllOf(NodeMatches(AllOf(
NameMatches("local_report_id"), PropertyList(UnorderedElementsAreArray({
StringIs("creation_time", kTime2Str),
UintIs("upload_attempts", 1u),
}))))))))))))));
}
TEST_F(InspectManagerTest, Succeed_MarkReportAsUploaded) {
clock_.Set(kTime2);
EXPECT_TRUE(inspect_manager_->AddReport("program", "local_report_id"));
clock_.Set(kTime3);
EXPECT_TRUE(inspect_manager_->SetUploadAttempt("local_report_id", 1u));
EXPECT_TRUE(inspect_manager_->MarkReportAsUploaded("local_report_id", "server_report_id"));
EXPECT_THAT(InspectTree(),
ChildrenMatch(Contains(AllOf(
NodeMatches(NameMatches("reports")),
ChildrenMatch(ElementsAre(AllOf(
NodeMatches(NameMatches("program")),
ChildrenMatch(ElementsAre(AllOf(
NodeMatches(AllOf(NameMatches("local_report_id"),
PropertyList(UnorderedElementsAreArray({
StringIs("creation_time", kTime2Str),
StringIs("final_state", "uploaded"),
UintIs("upload_attempts", 1u),
})))),
ChildrenMatch(ElementsAre(NodeMatches(AllOf(
NameMatches("crash_server"), PropertyList(UnorderedElementsAreArray({
StringIs("creation_time", kTime3Str),
StringIs("id", "server_report_id"),
}))))))))))))))));
}
TEST_F(InspectManagerTest, Succeed_MarkReportAsArchived) {
clock_.Set(kTime2);
EXPECT_TRUE(inspect_manager_->AddReport("program", "local_report_id"));
EXPECT_TRUE(inspect_manager_->MarkReportAsArchived("local_report_id"));
EXPECT_THAT(InspectTree(),
ChildrenMatch(Contains(AllOf(
NodeMatches(NameMatches("reports")),
ChildrenMatch(ElementsAre(AllOf(
NodeMatches(NameMatches("program")),
ChildrenMatch(ElementsAre(AllOf(NodeMatches(AllOf(
NameMatches("local_report_id"), PropertyList(UnorderedElementsAreArray({
StringIs("creation_time", kTime2Str),
StringIs("final_state", "archived"),
}))))))))))))));
}
TEST_F(InspectManagerTest, Succeed_MarkReportAsGarbageCollected) {
clock_.Set(kTime2);
EXPECT_TRUE(inspect_manager_->AddReport("program", "local_report_id"));
EXPECT_TRUE(inspect_manager_->MarkReportAsGarbageCollected("local_report_id"));
EXPECT_THAT(
InspectTree(),
ChildrenMatch(Contains(AllOf(
NodeMatches(NameMatches("reports")),
ChildrenMatch(ElementsAre(AllOf(
NodeMatches(NameMatches("program")),
ChildrenMatch(ElementsAre(AllOf(NodeMatches(AllOf(
NameMatches("local_report_id"), PropertyList(UnorderedElementsAreArray({
StringIs("creation_time", kTime2Str),
StringIs("final_state", "garbage_collected"),
}))))))))))))));
}
TEST_F(InspectManagerTest, Fail_SetUploadAttempt_UnknownReport) {
EXPECT_FALSE(inspect_manager_->SetUploadAttempt("unknown_report", 1u));
EXPECT_THAT(InspectTree(), ChildrenMatch(Contains(AllOf(NodeMatches(NameMatches("reports")),
ChildrenMatch(IsEmpty())))));
}
TEST_F(InspectManagerTest, Fail_MarkReportAsUploaded_UnknownReport) {
EXPECT_FALSE(inspect_manager_->MarkReportAsUploaded("unknown_report", "server_report_id"));
EXPECT_THAT(InspectTree(), ChildrenMatch(Contains(AllOf(NodeMatches(NameMatches("reports")),
ChildrenMatch(IsEmpty())))));
}
TEST_F(InspectManagerTest, Fail_MarkReportAsArchived_UnknownReport) {
EXPECT_FALSE(inspect_manager_->MarkReportAsArchived("unknown_report"));
EXPECT_THAT(InspectTree(), ChildrenMatch(Contains(AllOf(NodeMatches(NameMatches("reports")),
ChildrenMatch(IsEmpty())))));
}
TEST_F(InspectManagerTest, Fail_MarkReportAsGarbageCollected_UnknownReport) {
EXPECT_FALSE(inspect_manager_->MarkReportAsGarbageCollected("unknown_report"));
EXPECT_THAT(InspectTree(), ChildrenMatch(Contains(AllOf(NodeMatches(NameMatches("reports")),
ChildrenMatch(IsEmpty())))));
}
TEST_F(InspectManagerTest, ExposeConfig_UploadEnabled) {
inspect_manager_->ExposeConfig(
Config{/*crash_server=*/
{
/*upload_policy=*/kConfigEnabled,
/*url=*/std::make_unique<std::string>("http://localhost:1234"),
}});
EXPECT_THAT(InspectTree(),
ChildrenMatch(Contains(
AllOf(NodeMatches(NameMatches("config")),
ChildrenMatch(ElementsAre(NodeMatches(AllOf(
NameMatches(kCrashServerKey),
PropertyList(UnorderedElementsAreArray({
StringIs(kCrashServerUploadPolicyKey, ToString(kConfigEnabled)),
StringIs(kCrashServerUrlKey, "http://localhost:1234"),
}))))))))));
}
TEST_F(InspectManagerTest, ExposeConfig_UploadDisabled) {
inspect_manager_->ExposeConfig(Config{/*crashpad_database=*/
/*crash_server=*/
{
/*upload_policy=*/kConfigDisabled,
/*url=*/nullptr,
}});
EXPECT_THAT(InspectTree(),
ChildrenMatch(Contains(
AllOf(NodeMatches(NameMatches("config")),
ChildrenMatch(ElementsAre(NodeMatches(AllOf(
NameMatches(kCrashServerKey),
PropertyList(ElementsAre(StringIs(kCrashServerUploadPolicyKey,
ToString(kConfigDisabled))))))))))));
}
TEST_F(InspectManagerTest, ExposeConfig_UploadReadFromPrivacySettings) {
inspect_manager_->ExposeConfig(Config{/*crash_server=*/
{
/*upload_policy=*/kConfigReadFromPrivacySettings,
/*url=*/nullptr,
}});
EXPECT_THAT(
InspectTree(),
ChildrenMatch(Contains(AllOf(
NodeMatches(NameMatches("config")),
ChildrenMatch(ElementsAre(NodeMatches(AllOf(
NameMatches(kCrashServerKey),
PropertyList(ElementsAre(StringIs(kCrashServerUploadPolicyKey,
ToString(kConfigReadFromPrivacySettings))))))))))));
}
TEST_F(InspectManagerTest, ExposeSettings_TrackUploadPolicyChanges) {
Settings settings;
settings.set_upload_policy(kSettingsEnabled);
inspect_manager_->ExposeSettings(&settings);
EXPECT_THAT(
InspectTree(),
ChildrenMatch(Contains(NodeMatches(AllOf(
NameMatches("settings"),
PropertyList(ElementsAre(StringIs("upload_policy", ToString(kSettingsEnabled)))))))));
settings.set_upload_policy(kSettingsDisabled);
EXPECT_THAT(
InspectTree(),
ChildrenMatch(Contains(NodeMatches(AllOf(
NameMatches("settings"),
PropertyList(ElementsAre(StringIs("upload_policy", ToString(kSettingsDisabled)))))))));
settings.set_upload_policy(kSettingsLimbo);
EXPECT_THAT(
InspectTree(),
ChildrenMatch(Contains(NodeMatches(
AllOf(NameMatches("settings"),
PropertyList(ElementsAre(StringIs("upload_policy", ToString(kSettingsLimbo)))))))));
settings.set_upload_policy(kSettingsEnabled);
EXPECT_THAT(
InspectTree(),
ChildrenMatch(Contains(NodeMatches(AllOf(
NameMatches("settings"),
PropertyList(ElementsAre(StringIs("upload_policy", ToString(kSettingsEnabled)))))))));
}
TEST_F(InspectManagerTest, IncreaseReportsCleanedBy) {
const uint64_t kNumReportsCleaned = 10;
for (size_t i = 1; i < 5; ++i) {
inspect_manager_->IncreaseReportsCleanedBy(kNumReportsCleaned);
EXPECT_THAT(
InspectTree(),
ChildrenMatch(Contains(NodeMatches(AllOf(
NameMatches("database"),
PropertyList(ElementsAre(UintIs("num_reports_cleaned", i * kNumReportsCleaned))))))));
}
}
TEST_F(InspectManagerTest, IncreaseReportsPrunedBy) {
const uint64_t kNumReportsPruned = 10;
for (size_t i = 1; i < 5; ++i) {
inspect_manager_->IncreaseReportsPrunedBy(kNumReportsPruned);
EXPECT_THAT(
InspectTree(),
ChildrenMatch(Contains(NodeMatches(AllOf(
NameMatches("database"),
PropertyList(ElementsAre(UintIs("num_reports_pruned", i * kNumReportsPruned))))))));
}
}
TEST_F(InspectManagerTest, SetQueueSize) {
const uint64_t kQueueSize = 10u;
inspect_manager_->SetQueueSize(kQueueSize);
EXPECT_THAT(InspectTree(),
ChildrenMatch(Contains(NodeMatches(AllOf(
NameMatches("queue"), PropertyList(ElementsAre(UintIs("size", kQueueSize))))))));
}
TEST_F(InspectManagerTest, Check_CanAccessMultipleReportsForTheSameProgram) {
// A use-after-free bug was occurring when there were multiple reports for the same crashing
// program and we would try to mark one of them as uploaded.
// Add enough reports to force the underlying vector to resize.
const size_t kNumReports = 150u;
for (size_t i = 0; i < kNumReports; ++i) {
EXPECT_TRUE(
inspect_manager_->AddReport("program", fxl::StringPrintf("local_report_id_%zu", i)));
}
for (size_t i = 0; i < kNumReports; ++i) {
EXPECT_TRUE(inspect_manager_->MarkReportAsUploaded(fxl::StringPrintf("local_report_id_%zu", i),
"server_report_id"));
}
}
} // namespace
} // namespace feedback