blob: 67589fbf753bed1ce4f45c22eeabdbd5deddadd4 [file] [log] [blame]
// Copyright (c) 2012 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 "crash-reporter/udev_collector.h"
#include <base/files/file_enumerator.h>
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/strings/stringprintf.h>
#include <brillo/syslog_logging.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "crash-reporter/paths.h"
#include "crash-reporter/test_util.h"
using base::FilePath;
namespace {
// Dummy log config file name.
const char kLogConfigFileName[] = "log_config_file";
// Dummy directory for storing device coredumps.
const char kDevCoredumpDirectory[] = "devcoredump";
// A bunch of random rules to put into the dummy log config file.
const char kLogConfigFileContents[] =
"crash_reporter-udev-collection-change-card0-drm=echo change card0 drm\n"
"crash_reporter-udev-collection-add-state0-cpu=echo change state0 cpu\n"
"crash_reporter-udev-collection-devcoredump-iwlwifi=echo devcoredump\n"
"cros_installer=echo not for udev";
const char kCrashLogFilePattern[] = "*.log.gz";
const char kDevCoredumpFilePattern[] = "*.devcore";
// Dummy content for device coredump data file.
const char kDevCoredumpDataContents[] = "coredump";
// Driver name for a coredump that should not be collected:
const char kNoCollectDriverName[] = "iwlwifi";
// Driver name for a coredump that should be collected:
const char kCollectedDriverName[] = "msm";
// Returns the number of files found in the given path that matches the
// specified file name pattern.
int GetNumFiles(const FilePath& path, const std::string& file_pattern) {
base::FileEnumerator enumerator(path, false, base::FileEnumerator::FILES,
file_pattern);
int num_files = 0;
for (FilePath file_path = enumerator.Next(); !file_path.value().empty();
file_path = enumerator.Next()) {
num_files++;
}
return num_files;
}
} // namespace
class UdevCollectorMock : public UdevCollector {
public:
MOCK_METHOD(void, SetUpDBus, (), (override));
};
class UdevCollectorTest : public ::testing::Test {
protected:
base::ScopedTempDir temp_dir_generator_;
void HandleCrash(const std::string& udev_event) {
collector_.HandleCrash(udev_event);
}
void GenerateDevCoredump(const std::string& device_name,
const std::string& driver_name) {
// Generate coredump data file.
ASSERT_TRUE(CreateDirectory(FilePath(
base::StringPrintf("%s/%s", collector_.dev_coredump_directory_.c_str(),
device_name.c_str()))));
FilePath data_path = FilePath(base::StringPrintf(
"%s/%s/data", collector_.dev_coredump_directory_.c_str(),
device_name.c_str()));
ASSERT_TRUE(test_util::CreateFile(data_path, kDevCoredumpDataContents));
// Generate uevent file for failing device.
ASSERT_TRUE(CreateDirectory(FilePath(base::StringPrintf(
"%s/%s/failing_device", collector_.dev_coredump_directory_.c_str(),
device_name.c_str()))));
FilePath uevent_path = FilePath(base::StringPrintf(
"%s/%s/failing_device/uevent",
collector_.dev_coredump_directory_.c_str(), device_name.c_str()));
ASSERT_TRUE(
test_util::CreateFile(uevent_path, "DRIVER=" + driver_name + "\n"));
}
void SetUpCollector(UdevCollectorMock* collector) {
EXPECT_CALL(*collector, SetUpDBus()).WillRepeatedly(testing::Return());
collector->Initialize(false);
collector->log_config_path_ = log_config_path_;
collector->set_crash_directory_for_test(temp_dir_generator_.GetPath());
FilePath dev_coredump_path =
temp_dir_generator_.GetPath().Append(kDevCoredumpDirectory);
collector->dev_coredump_directory_ = dev_coredump_path.value();
}
private:
void SetUp() override {
ASSERT_TRUE(temp_dir_generator_.CreateUniqueTempDir());
log_config_path_ = temp_dir_generator_.GetPath().Append(kLogConfigFileName);
SetUpCollector(&collector_);
// Write to a dummy log config file.
ASSERT_TRUE(
test_util::CreateFile(log_config_path_, kLogConfigFileContents));
brillo::ClearLog();
}
FilePath log_config_path_;
UdevCollectorMock collector_;
};
TEST_F(UdevCollectorTest, TestNoMatch) {
// No rule should match this.
HandleCrash("ACTION=change:KERNEL=foo:SUBSYSTEM=bar");
EXPECT_EQ(0,
GetNumFiles(temp_dir_generator_.GetPath(), kCrashLogFilePattern));
}
TEST_F(UdevCollectorTest, TestMatches) {
// Try multiple udev events in sequence. The number of log files generated
// should increase.
HandleCrash("ACTION=change:KERNEL=card0:SUBSYSTEM=drm");
EXPECT_EQ(1,
GetNumFiles(temp_dir_generator_.GetPath(), kCrashLogFilePattern));
// Each collector is only allowed to handle one crash, so create a second
// collector for the second crash.
UdevCollectorMock second_collector;
SetUpCollector(&second_collector);
second_collector.HandleCrash("ACTION=add:KERNEL=state0:SUBSYSTEM=cpu");
EXPECT_EQ(2,
GetNumFiles(temp_dir_generator_.GetPath(), kCrashLogFilePattern));
}
TEST_F(UdevCollectorTest, TestDevCoredump) {
GenerateDevCoredump("devcd0", kNoCollectDriverName);
HandleCrash("ACTION=add:KERNEL_NUMBER=0:SUBSYSTEM=devcoredump");
// IsDeveloperImage() returns false while running this test so devcoredumps
// will not be added to the crash directory.
EXPECT_EQ(
0, GetNumFiles(temp_dir_generator_.GetPath(), kDevCoredumpFilePattern));
GenerateDevCoredump("devcd1", kNoCollectDriverName);
// Each collector is only allowed to handle one crash, so create a second
// collector for the second crash.
UdevCollectorMock second_collector;
SetUpCollector(&second_collector);
second_collector.HandleCrash(
"ACTION=add:KERNEL_NUMBER=1:SUBSYSTEM=devcoredump");
EXPECT_EQ(
0, GetNumFiles(temp_dir_generator_.GetPath(), kDevCoredumpFilePattern));
}
TEST_F(UdevCollectorTest, TestCollectedDevCoredump) {
// One more test, this time for the case of a devcoredump that should be
// collected in all builds:
GenerateDevCoredump("devcd2", kCollectedDriverName);
UdevCollectorMock third_collector;
SetUpCollector(&third_collector);
third_collector.HandleCrash(
"ACTION=add:KERNEL_NUMBER=2:SUBSYSTEM=devcoredump");
EXPECT_EQ(
1, GetNumFiles(temp_dir_generator_.GetPath(), kDevCoredumpFilePattern));
// Check for the expected crash signature:
base::FilePath meta_path;
std::string meta_pattern = "devcoredump_";
meta_pattern += kCollectedDriverName;
meta_pattern += ".*.meta";
EXPECT_TRUE(test_util::DirectoryHasFileWithPattern(
temp_dir_generator_.GetPath(), meta_pattern, &meta_path));
std::string meta_contents;
EXPECT_TRUE(base::ReadFileToString(meta_path, &meta_contents));
std::string expected_sig = "sig=crash_reporter-udev-collection-devcoredump-";
expected_sig += kCollectedDriverName;
EXPECT_THAT(meta_contents, testing::HasSubstr(expected_sig));
}
// TODO(sque, crosbug.com/32238) - test wildcard cases, multiple identical udev
// events.