| // 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.gz"; |
| |
| // 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. |