blob: 13fb52ce83b4d026d1501ee1fb26924445bde294 [file] [log] [blame]
// Copyright 2021 The Chromium OS 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 "debugd/src/drm_trace_tool.h"
#include <limits.h>
#include <memory>
#include <optional>
#include <base/files/file_enumerator.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/strings/stringprintf.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "debugd/src/log_provider.h"
using ::testing::Return;
namespace debugd {
class MockLogProvider : public LogProvider {
public:
MOCK_METHOD1(GetLog, std::optional<std::string>(const std::string&));
};
struct SnapshotTestParam {
SnapshotTestParam(DRMSnapshotType type, const std::string& name)
: snapshot_type(type), log_name(name) {}
DRMSnapshotType snapshot_type;
std::string log_name;
};
class DRMTraceToolTest : public testing::Test {
protected:
base::ScopedTempDir temp_dir_;
MockLogProvider log_provider_;
std::unique_ptr<DRMTraceTool> drm_trace_tool_;
void SetUp() override {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
ASSERT_TRUE(base::SetPosixFilePermissions(temp_dir_.GetPath(), 0750));
// Create files that we expect to interact with in DRMTraceTool.
ASSERT_TRUE(base::CreateDirectory(
temp_dir_.GetPath().Append("sys/module/drm/parameters")));
ASSERT_EQ(0, base::WriteFile(temp_dir_.GetPath().Append(
"sys/module/drm/parameters/trace"),
"", 0));
ASSERT_TRUE(base::CreateDirectory(
temp_dir_.GetPath().Append("sys/kernel/debug/tracing/instances/drm")));
ASSERT_EQ(0,
base::WriteFile(
temp_dir_.GetPath().Append(
"sys/kernel/debug/tracing/instances/drm/buffer_size_kb"),
"", 0));
ASSERT_EQ(0, base::WriteFile(
temp_dir_.GetPath().Append(
"sys/kernel/debug/tracing/instances/drm/trace_marker"),
"", 0));
ASSERT_EQ(
0, base::WriteFile(temp_dir_.GetPath().Append(
"sys/kernel/debug/tracing/instances/drm/trace"),
"", 0));
ASSERT_TRUE(base::CreateDirectory(
temp_dir_.GetPath().Append("var/log/display_debug")));
ASSERT_TRUE(base::SetPosixFilePermissions(
temp_dir_.GetPath().Append("var/log/display_debug"), 0750));
// Initialize DRMTraceTool with a fake root path for testing.
drm_trace_tool_ = std::unique_ptr<DRMTraceTool>(
new DRMTraceTool(temp_dir_.GetPath(), &log_provider_));
}
};
class DRMTraceToolSnapshotTest
: public DRMTraceToolTest,
public testing::WithParamInterface<SnapshotTestParam> {};
TEST_F(DRMTraceToolTest, SetCategories) {
brillo::ErrorPtr error;
EXPECT_TRUE(drm_trace_tool_->SetCategories(&error, 0));
EXPECT_EQ(error, nullptr);
uint32_t all_flags = DRMTraceCategory_CORE | DRMTraceCategory_DRIVER |
DRMTraceCategory_KMS | DRMTraceCategory_PRIME |
DRMTraceCategory_ATOMIC | DRMTraceCategory_VBL |
DRMTraceCategory_STATE | DRMTraceCategory_LEASE |
DRMTraceCategory_DP | DRMTraceCategory_DRMRES;
EXPECT_TRUE(drm_trace_tool_->SetCategories(&error, all_flags));
EXPECT_EQ(error, nullptr);
uint32_t too_large_flag = DRMTraceCategory_DRMRES << 1;
EXPECT_FALSE(drm_trace_tool_->SetCategories(&error, too_large_flag));
EXPECT_NE(error, nullptr);
}
TEST_F(DRMTraceToolTest, SetSize) {
brillo::ErrorPtr error;
EXPECT_TRUE(drm_trace_tool_->SetSize(&error, DRMTraceSize_DEFAULT));
EXPECT_EQ(error, nullptr);
EXPECT_TRUE(drm_trace_tool_->SetSize(&error, DRMTraceSize_DEBUG));
EXPECT_EQ(error, nullptr);
uint32_t invalid_enum = DRMTraceSize_DEBUG + 1;
EXPECT_FALSE(drm_trace_tool_->SetSize(&error, invalid_enum));
EXPECT_NE(error, nullptr);
}
TEST_F(DRMTraceToolTest, AnnotateLog) {
brillo::ErrorPtr error;
EXPECT_TRUE(drm_trace_tool_->AnnotateLog(&error, "elephant"));
EXPECT_EQ(error, nullptr);
std::string contents;
ASSERT_TRUE(base::ReadFileToString(
temp_dir_.GetPath().Append(
"sys/kernel/debug/tracing/instances/drm/trace_marker"),
&contents));
EXPECT_EQ(contents, "elephant");
}
TEST_F(DRMTraceToolTest, AnnotateLogInvalidCharacter) {
brillo::ErrorPtr error;
EXPECT_TRUE(drm_trace_tool_->AnnotateLog(&error, "bell\a"));
EXPECT_EQ(error, nullptr);
std::string contents;
ASSERT_TRUE(base::ReadFileToString(
temp_dir_.GetPath().Append(
"sys/kernel/debug/tracing/instances/drm/trace_marker"),
&contents));
EXPECT_EQ(contents, "bell_");
}
TEST_F(DRMTraceToolTest, AnnotateLogTooLarge) {
brillo::ErrorPtr error;
// Large buffer filled with 'c's.
std::string large_log(1024 * 1024, 'c');
EXPECT_FALSE(drm_trace_tool_->AnnotateLog(&error, large_log));
EXPECT_NE(error, nullptr);
}
TEST_F(DRMTraceToolTest, SnapshotInvalid) {
brillo::ErrorPtr error;
// If new enum values are added this should be updated.
const uint32_t kInvalidType = 2;
EXPECT_FALSE(drm_trace_tool_->Snapshot(&error, kInvalidType));
EXPECT_NE(error, nullptr);
}
TEST_P(DRMTraceToolSnapshotTest, SnapshotSuccess) {
brillo::ErrorPtr error;
std::string trace_contents = "lorem ipsum";
EXPECT_CALL(log_provider_, GetLog(GetParam().log_name))
.WillOnce(Return(std::make_optional(trace_contents)));
EXPECT_TRUE(drm_trace_tool_->Snapshot(&error, GetParam().snapshot_type));
EXPECT_EQ(error, nullptr);
// Expect one file to have been created in /var/log/display_debug
std::string glob = GetParam().log_name + ".*";
base::FileEnumerator enumerator(
temp_dir_.GetPath().Append("var/log/display_debug"), false,
base::FileEnumerator::FileType::FILES, glob);
base::FilePath snapshot_file_path = enumerator.Next();
EXPECT_EQ(enumerator.GetError(), base::File::FILE_OK);
ASSERT_FALSE(snapshot_file_path.empty());
// Verify that the snapshot has the same contents as the trace file.
std::string snapshot_contents;
ASSERT_TRUE(base::ReadFileToString(snapshot_file_path, &snapshot_contents));
EXPECT_EQ(trace_contents, snapshot_contents);
}
INSTANTIATE_TEST_SUITE_P(
SnapshotTypes,
DRMTraceToolSnapshotTest,
::testing::Values(SnapshotTestParam(DRMSnapshotType_TRACE, "drm_trace"),
SnapshotTestParam(DRMSnapshotType_MODETEST, "modetest")));
TEST_F(DRMTraceToolTest, WriteToNonExistentFile) {
brillo::ErrorPtr error;
EXPECT_FALSE(DRMTraceTool::WriteToFile(
&error, base::FilePath("/probably/not/a/real/file"), "content"));
EXPECT_NE(error, nullptr);
}
TEST_F(DRMTraceToolTest, WriteToReadOnlyFile) {
brillo::ErrorPtr error;
// Create a new file, and make it read-only
base::FilePath path = temp_dir_.GetPath().Append("readonly-file");
ASSERT_TRUE(base::WriteFile(path, "data"));
base::SetPosixFilePermissions(
path, base::FilePermissionBits::FILE_PERMISSION_READ_BY_USER);
EXPECT_FALSE(DRMTraceTool::WriteToFile(&error, path, "content"));
EXPECT_NE(error, nullptr);
}
TEST_F(DRMTraceToolTest, WriteToNonWritableFile) {
brillo::ErrorPtr error;
// Attempt to write to a directory.
base::FilePath path = temp_dir_.GetPath().Append("directory");
ASSERT_TRUE(base::CreateDirectory(path));
EXPECT_FALSE(DRMTraceTool::WriteToFile(&error, path, "content"));
EXPECT_NE(error, nullptr);
}
} // namespace debugd