| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <assert.h> |
| #include <errno.h> |
| #include <signal.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| #include <set> |
| #include <string> |
| #include <vector> |
| |
| #include <sys/mman.h> |
| #include <sys/types.h> |
| #include <sys/wait.h> |
| |
| #include <gmock/gmock.h> |
| |
| #include <process/gtest.hpp> |
| |
| #include <stout/gtest.hpp> |
| #include <stout/hashmap.hpp> |
| #include <stout/option.hpp> |
| #include <stout/os.hpp> |
| #include <stout/path.hpp> |
| #include <stout/stringify.hpp> |
| #include <stout/strings.hpp> |
| |
| #include "linux/cgroups.hpp" |
| |
| #include "tests/mesos.hpp" // For TEST_CGROUPS_(HIERARCHY|ROOT). |
| |
| using namespace mesos::internal::tests; |
| |
| using namespace process; |
| |
| |
| class CgroupsTest : public ::testing::Test |
| { |
| public: |
| static void SetUpTestCase() |
| { |
| // Clean up the testing hierarchy, in case it wasn't cleaned up |
| // properly from previous tests. |
| AWAIT_READY(cgroups::cleanup(TEST_CGROUPS_HIERARCHY)); |
| } |
| |
| static void TearDownTestCase() |
| { |
| AWAIT_READY(cgroups::cleanup(TEST_CGROUPS_HIERARCHY)); |
| } |
| }; |
| |
| |
| // A fixture which is used to name tests that expect NO hierarchy to |
| // exist in order to test the ability to create a hierarchy (since |
| // most likely existing hierarchies will have all or most subsystems |
| // attached rendering our ability to create a hierarchy fruitless). |
| class DISABLED_CgroupsNoHierarchyTest : public CgroupsTest |
| { |
| public: |
| static void SetUpTestCase() |
| { |
| CgroupsTest::SetUpTestCase(); |
| |
| Try<std::set<std::string> > hierarchies = cgroups::hierarchies(); |
| ASSERT_SOME(hierarchies); |
| ASSERT_TRUE(hierarchies.get().empty()) |
| << "-------------------------------------------------------------\n" |
| << "We cannot run any cgroups tests that require mounting\n" |
| << "hierarchies because you have the following hierarchies mounted:\n" |
| << strings::trim(stringify(hierarchies.get()), " {},") << "\n" |
| << "You can either unmount those hierarchies, or disable\n" |
| << "this test case (i.e., --gtest_filter=-CgroupsNoHierarchyTest.*).\n" |
| << "-------------------------------------------------------------"; |
| } |
| }; |
| |
| |
| // A fixture that assumes ANY hierarchy is acceptable for use provided |
| // it has the subsystems attached that were specified in the |
| // constructor. If no hierarchy could be found that has all the |
| // required subsystems then we attempt to create a new hierarchy. |
| class DISABLED_CgroupsAnyHierarchyTest : public CgroupsTest |
| { |
| public: |
| DISABLED_CgroupsAnyHierarchyTest(const std::string& _subsystems = "cpu") |
| : subsystems(_subsystems) {} |
| |
| protected: |
| virtual void SetUp() |
| { |
| Result<std::string> hierarchy_ = cgroups::hierarchy(subsystems); |
| ASSERT_FALSE(hierarchy_.isError()); |
| if (hierarchy_.isNone()) { |
| // Try to mount a hierarchy for testing. |
| ASSERT_SOME(cgroups::mount(TEST_CGROUPS_HIERARCHY, subsystems)) |
| << "-------------------------------------------------------------\n" |
| << "We cannot run any cgroups tests that require\n" |
| << "a hierarchy with subsystems '" << subsystems << "'\n" |
| << "because we failed to find an existing hierarchy\n" |
| << "or create a new one. You can either remove all existing\n" |
| << "hierarchies, or disable this test case\n" |
| << "(i.e., --gtest_filter=-" |
| << ::testing::UnitTest::GetInstance() |
| ->current_test_info() |
| ->test_case_name() << ".*).\n" |
| << "-------------------------------------------------------------"; |
| |
| hierarchy = TEST_CGROUPS_HIERARCHY; |
| } else { |
| hierarchy = hierarchy_.get(); |
| } |
| |
| // Create a cgroup (removing first if necessary) for the tests to use. |
| Try<bool> exists = cgroups::exists(hierarchy, TEST_CGROUPS_ROOT); |
| ASSERT_SOME(exists); |
| if (exists.get()) { |
| AWAIT_READY(cgroups::destroy(hierarchy, TEST_CGROUPS_ROOT)); |
| } |
| ASSERT_SOME(cgroups::create(hierarchy, TEST_CGROUPS_ROOT)); |
| } |
| |
| virtual void TearDown() |
| { |
| // Remove all *our* cgroups. |
| Try<bool> exists = cgroups::exists(hierarchy, TEST_CGROUPS_ROOT); |
| ASSERT_SOME(exists); |
| if (exists.get()) { |
| AWAIT_READY(cgroups::destroy(hierarchy, TEST_CGROUPS_ROOT)); |
| } |
| |
| // And cleanup TEST_CGROUPS_HIERARCHY in the event it is needed |
| // to be created. |
| AWAIT_READY(cgroups::cleanup(TEST_CGROUPS_HIERARCHY)); |
| } |
| |
| const std::string subsystems; // Subsystems required to run tests. |
| std::string hierarchy; // Path to the hierarchy being used. |
| }; |
| |
| |
| class DISABLED_CgroupsAnyHierarchyWithCpuMemoryTest |
| : public DISABLED_CgroupsAnyHierarchyTest |
| { |
| public: |
| DISABLED_CgroupsAnyHierarchyWithCpuMemoryTest() |
| : DISABLED_CgroupsAnyHierarchyTest("cpu,memory") {} |
| }; |
| |
| |
| class DISABLED_CgroupsAnyHierarchyWithCpuMemoryFreezerTest |
| : public DISABLED_CgroupsAnyHierarchyTest |
| { |
| public: |
| DISABLED_CgroupsAnyHierarchyWithCpuMemoryFreezerTest() |
| : DISABLED_CgroupsAnyHierarchyTest("cpu,memory,freezer") {} |
| }; |
| |
| |
| TEST_F(DISABLED_CgroupsAnyHierarchyTest, ROOT_CGROUPS_Enabled) |
| { |
| EXPECT_SOME_TRUE(cgroups::enabled("")); |
| EXPECT_SOME_TRUE(cgroups::enabled(",")); |
| EXPECT_SOME_TRUE(cgroups::enabled("cpu")); |
| EXPECT_SOME_TRUE(cgroups::enabled(",cpu")); |
| EXPECT_SOME_TRUE(cgroups::enabled("cpu,memory")); |
| EXPECT_SOME_TRUE(cgroups::enabled("cpu,memory,")); |
| EXPECT_ERROR(cgroups::enabled("invalid")); |
| EXPECT_ERROR(cgroups::enabled("cpu,invalid")); |
| } |
| |
| |
| TEST_F(DISABLED_CgroupsAnyHierarchyWithCpuMemoryTest, ROOT_CGROUPS_Busy) |
| { |
| EXPECT_SOME_FALSE(cgroups::busy("")); |
| EXPECT_SOME_FALSE(cgroups::busy(",")); |
| EXPECT_SOME_TRUE(cgroups::busy("cpu")); |
| EXPECT_SOME_TRUE(cgroups::busy(",cpu")); |
| EXPECT_SOME_TRUE(cgroups::busy("cpu,memory")); |
| EXPECT_SOME_TRUE(cgroups::busy("cpu,memory,")); |
| EXPECT_ERROR(cgroups::busy("invalid")); |
| EXPECT_ERROR(cgroups::busy("cpu,invalid")); |
| } |
| |
| |
| TEST_F(DISABLED_CgroupsAnyHierarchyTest, ROOT_CGROUPS_Subsystems) |
| { |
| Try<std::set<std::string> > names = cgroups::subsystems(); |
| ASSERT_SOME(names); |
| |
| Option<std::string> cpu; |
| Option<std::string> memory; |
| foreach (const std::string& name, names.get()) { |
| if (name == "cpu") { |
| cpu = name; |
| } else if (name == "memory") { |
| memory = name; |
| } |
| } |
| |
| EXPECT_SOME(cpu); |
| EXPECT_SOME(memory); |
| } |
| |
| |
| TEST_F(DISABLED_CgroupsAnyHierarchyWithCpuMemoryTest, ROOT_CGROUPS_SubsystemsHierarchy) |
| { |
| Try<std::set<std::string> > names = cgroups::subsystems(hierarchy); |
| ASSERT_SOME(names); |
| |
| Option<std::string> cpu; |
| Option<std::string> memory; |
| foreach (const std::string& name, names.get()) { |
| if (name == "cpu") { |
| cpu = name; |
| } else if (name == "memory") { |
| memory = name; |
| } |
| } |
| |
| EXPECT_SOME(cpu); |
| EXPECT_SOME(memory); |
| } |
| |
| |
| TEST_F(DISABLED_CgroupsNoHierarchyTest, ROOT_CGROUPS_NOHIERARCHY_MountUnmountHierarchy) |
| { |
| EXPECT_ERROR(cgroups::mount("/tmp", "cpu")); |
| EXPECT_ERROR(cgroups::mount(TEST_CGROUPS_HIERARCHY, "invalid")); |
| ASSERT_SOME(cgroups::mount(TEST_CGROUPS_HIERARCHY, "cpu,memory")); |
| EXPECT_ERROR(cgroups::mount(TEST_CGROUPS_HIERARCHY, "cpuset")); |
| EXPECT_ERROR(cgroups::unmount("/tmp")); |
| ASSERT_SOME(cgroups::unmount(TEST_CGROUPS_HIERARCHY)); |
| } |
| |
| |
| TEST_F(DISABLED_CgroupsAnyHierarchyTest, ROOT_CGROUPS_Mounted) |
| { |
| EXPECT_SOME_FALSE(cgroups::mounted("/tmp-nonexist")); |
| EXPECT_SOME_FALSE(cgroups::mounted("/tmp")); |
| EXPECT_SOME_FALSE(cgroups::mounted(hierarchy + "/not_expected")); |
| EXPECT_SOME_TRUE(cgroups::mounted(hierarchy)); |
| EXPECT_SOME_TRUE(cgroups::mounted(hierarchy + "/")); |
| } |
| |
| |
| TEST_F(DISABLED_CgroupsAnyHierarchyWithCpuMemoryTest, ROOT_CGROUPS_MountedSubsystems) |
| { |
| EXPECT_SOME_FALSE(cgroups::mounted("/tmp-nonexist", "cpu")); |
| EXPECT_SOME_FALSE(cgroups::mounted("/tmp", "cpu,memory")); |
| EXPECT_SOME_FALSE(cgroups::mounted("/tmp", "cpu")); |
| EXPECT_SOME_FALSE(cgroups::mounted("/tmp", "invalid")); |
| EXPECT_SOME_TRUE(cgroups::mounted(hierarchy, "cpu,memory")); |
| EXPECT_SOME_TRUE(cgroups::mounted(hierarchy, "memory")); |
| EXPECT_SOME_FALSE(cgroups::mounted(hierarchy, "invalid")); |
| EXPECT_SOME_FALSE(cgroups::mounted(hierarchy + "/not_expected", "cpu")); |
| } |
| |
| |
| TEST_F(DISABLED_CgroupsAnyHierarchyWithCpuMemoryTest, ROOT_CGROUPS_CreateRemove) |
| { |
| EXPECT_ERROR(cgroups::create("/tmp", "test")); |
| EXPECT_ERROR(cgroups::create(hierarchy, "mesos_test_missing/1")); |
| ASSERT_SOME(cgroups::create(hierarchy, "mesos_test_missing")); |
| EXPECT_ERROR(cgroups::remove(hierarchy, "invalid")); |
| ASSERT_SOME(cgroups::remove(hierarchy, "mesos_test_missing")); |
| } |
| |
| |
| TEST_F(DISABLED_CgroupsAnyHierarchyTest, ROOT_CGROUPS_Get) |
| { |
| ASSERT_SOME(cgroups::create(hierarchy, "mesos_test1")); |
| ASSERT_SOME(cgroups::create(hierarchy, "mesos_test2")); |
| |
| Try<std::vector<std::string> > cgroups = cgroups::get(hierarchy); |
| ASSERT_SOME(cgroups); |
| |
| EXPECT_EQ(cgroups.get()[0], "mesos_test2"); |
| EXPECT_EQ(cgroups.get()[1], "mesos_test1"); |
| EXPECT_EQ(cgroups.get()[2], TEST_CGROUPS_ROOT); |
| |
| ASSERT_SOME(cgroups::remove(hierarchy, "mesos_test1")); |
| ASSERT_SOME(cgroups::remove(hierarchy, "mesos_test2")); |
| } |
| |
| |
| TEST_F(DISABLED_CgroupsAnyHierarchyTest, ROOT_CGROUPS_NestedCgroups) |
| { |
| ASSERT_SOME(cgroups::create(hierarchy, path::join(TEST_CGROUPS_ROOT, "1"))) |
| << "-------------------------------------------------------------\n" |
| << "We cannot run this test because it appears you do not have\n" |
| << "a modern enough version of the Linux kernel. You won't be\n" |
| << "able to use the cgroups isolator, but feel free to disable\n" |
| << "this test.\n" |
| << "-------------------------------------------------------------"; |
| |
| ASSERT_SOME(cgroups::create(hierarchy, path::join(TEST_CGROUPS_ROOT, "2"))); |
| |
| Try<std::vector<std::string> > cgroups = |
| cgroups::get(hierarchy, TEST_CGROUPS_ROOT); |
| |
| ASSERT_SOME(cgroups); |
| ASSERT_EQ(2u, cgroups.get().size()); |
| |
| EXPECT_EQ(cgroups.get()[0], path::join(TEST_CGROUPS_ROOT, "2")); |
| EXPECT_EQ(cgroups.get()[1], path::join(TEST_CGROUPS_ROOT, "1")); |
| |
| ASSERT_SOME(cgroups::remove(hierarchy, path::join(TEST_CGROUPS_ROOT, "1"))); |
| ASSERT_SOME(cgroups::remove(hierarchy, path::join(TEST_CGROUPS_ROOT, "2"))); |
| } |
| |
| |
| TEST_F(DISABLED_CgroupsAnyHierarchyTest, ROOT_CGROUPS_Tasks) |
| { |
| Try<std::set<pid_t> > pids = cgroups::tasks(hierarchy, "/"); |
| ASSERT_SOME(pids); |
| EXPECT_NE(0u, pids.get().count(1)); |
| EXPECT_NE(0u, pids.get().count(::getpid())); |
| } |
| |
| |
| TEST_F(DISABLED_CgroupsAnyHierarchyTest, ROOT_CGROUPS_Read) |
| { |
| EXPECT_ERROR(cgroups::read(hierarchy, TEST_CGROUPS_ROOT, "invalid")); |
| |
| std::string pid = stringify(::getpid()); |
| |
| Try<std::string> result = cgroups::read(hierarchy, "/", "tasks"); |
| ASSERT_SOME(result); |
| EXPECT_TRUE(strings::contains(result.get(), pid)); |
| } |
| |
| |
| TEST_F(DISABLED_CgroupsAnyHierarchyTest, ROOT_CGROUPS_Write) |
| { |
| EXPECT_ERROR( |
| cgroups::write(hierarchy, TEST_CGROUPS_ROOT, "invalid", "invalid")); |
| |
| pid_t pid = ::fork(); |
| ASSERT_NE(-1, pid); |
| |
| if (pid > 0) { |
| // In parent process. |
| ASSERT_SOME( |
| cgroups::write(hierarchy, TEST_CGROUPS_ROOT, "tasks", stringify(pid))); |
| |
| Try<std::set<pid_t> > pids = cgroups::tasks(hierarchy, TEST_CGROUPS_ROOT); |
| ASSERT_SOME(pids); |
| |
| EXPECT_NE(0u, pids.get().count(pid)); |
| |
| // Kill the child process. |
| ASSERT_NE(-1, ::kill(pid, SIGKILL)); |
| |
| // Wait for the child process. |
| int status; |
| EXPECT_NE(-1, ::waitpid((pid_t) -1, &status, 0)); |
| ASSERT_TRUE(WIFSIGNALED(status)); |
| EXPECT_EQ(SIGKILL, WTERMSIG(status)); |
| } else { |
| // In child process, wait for kill signal. |
| while (true); |
| |
| // Should not reach here. |
| std::cerr << "Reach an unreachable statement!" << std::endl; |
| abort(); |
| } |
| } |
| |
| |
| class CgroupsAnyHierarchyWithCpuAcctMemoryTest |
| : public DISABLED_CgroupsAnyHierarchyTest |
| { |
| public: |
| CgroupsAnyHierarchyWithCpuAcctMemoryTest() |
| : DISABLED_CgroupsAnyHierarchyTest("cpuacct,memory") {} |
| }; |
| |
| |
| TEST_F(CgroupsAnyHierarchyWithCpuAcctMemoryTest, ROOT_CGROUPS_Stat) |
| { |
| EXPECT_ERROR(cgroups::stat(hierarchy, TEST_CGROUPS_ROOT, "invalid")); |
| |
| Try<hashmap<std::string, uint64_t> > result = |
| cgroups::stat(hierarchy, "/", "cpuacct.stat"); |
| ASSERT_SOME(result); |
| EXPECT_TRUE(result.get().contains("user")); |
| EXPECT_TRUE(result.get().contains("system")); |
| EXPECT_GT(result.get()["user"], 0llu); |
| EXPECT_GT(result.get()["system"], 0llu); |
| |
| result = cgroups::stat(hierarchy, "/", "memory.stat"); |
| ASSERT_SOME(result); |
| EXPECT_TRUE(result.get().contains("rss")); |
| EXPECT_GT(result.get()["rss"], 0llu); |
| } |
| |
| |
| TEST_F(DISABLED_CgroupsAnyHierarchyWithCpuMemoryTest, ROOT_CGROUPS_Listen) |
| { |
| ASSERT_SOME( |
| cgroups::exists(hierarchy, TEST_CGROUPS_ROOT, "memory.oom_control")) |
| << "-------------------------------------------------------------\n" |
| << "We cannot run this test because it appears you do not have\n" |
| << "a modern enough version of the Linux kernel. You won't be\n" |
| << "able to use the cgroups isolator, but feel free to disable\n" |
| << "this test.\n" |
| << "-------------------------------------------------------------"; |
| |
| // Disable oom killer. |
| ASSERT_SOME( |
| cgroups::write(hierarchy, TEST_CGROUPS_ROOT, "memory.oom_control", "1")); |
| |
| // Limit the memory usage of the test cgroup to 64MB. |
| size_t limit = 1024 * 1024 * 64; |
| ASSERT_SOME(cgroups::write( |
| hierarchy, |
| TEST_CGROUPS_ROOT, |
| "memory.limit_in_bytes", |
| stringify(limit))); |
| |
| // Listen on oom events for test cgroup. |
| Future<uint64_t> future = cgroups::listen( |
| hierarchy, TEST_CGROUPS_ROOT, "memory.oom_control"); |
| ASSERT_FALSE(future.isFailed()); |
| |
| // Test the cancellation. |
| future.discard(); |
| |
| // Test the normal operation below. |
| future = cgroups::listen(hierarchy, TEST_CGROUPS_ROOT, "memory.oom_control"); |
| ASSERT_FALSE(future.isFailed()); |
| |
| pid_t pid = ::fork(); |
| ASSERT_NE(-1, pid); |
| |
| if (pid > 0) { |
| // In parent process. |
| future.await(Seconds(5)); |
| |
| EXPECT_TRUE(future.isReady()); |
| |
| // Kill the child process. |
| EXPECT_NE(-1, ::kill(pid, SIGKILL)); |
| |
| // Wait for the child process. |
| int status; |
| EXPECT_NE(-1, ::waitpid((pid_t) -1, &status, 0)); |
| ASSERT_TRUE(WIFSIGNALED(status)); |
| EXPECT_EQ(SIGKILL, WTERMSIG(status)); |
| } else { |
| // In child process. We try to trigger an oom here. |
| // Put self into the test cgroup. |
| Try<Nothing> assign = |
| cgroups::assign(hierarchy, TEST_CGROUPS_ROOT, ::getpid()); |
| |
| if (assign.isError()) { |
| std::cerr << "Failed to assign cgroup: " << assign.error() << std::endl; |
| abort(); |
| } |
| |
| // Blow up the memory. |
| size_t limit = 1024 * 1024 * 512; |
| void* buffer = NULL; |
| |
| if (posix_memalign(&buffer, getpagesize(), limit) != 0) { |
| perror("Failed to allocate page-aligned memory, posix_memalign"); |
| abort(); |
| } |
| |
| // We use mlock and memset here to make sure that the memory |
| // actually gets paged in and thus accounted for. |
| if (mlock(buffer, limit) != 0) { |
| perror("Failed to lock memory, mlock"); |
| abort(); |
| } |
| |
| if (memset(buffer, 1, limit) != buffer) { |
| perror("Failed to fill memory, memset"); |
| abort(); |
| } |
| |
| // Should not reach here. |
| std::cerr << "OOM does not happen!" << std::endl; |
| abort(); |
| } |
| } |
| |
| |
| TEST_F(DISABLED_CgroupsAnyHierarchyWithCpuMemoryFreezerTest, ROOT_CGROUPS_Freeze) |
| { |
| int pipes[2]; |
| int dummy; |
| ASSERT_NE(-1, ::pipe(pipes)); |
| |
| pid_t pid = ::fork(); |
| ASSERT_NE(-1, pid); |
| |
| if (pid > 0) { |
| // In parent process. |
| ::close(pipes[1]); |
| |
| // Wait until child has assigned the cgroup. |
| ASSERT_LT(0, ::read(pipes[0], &dummy, sizeof(dummy))); |
| ::close(pipes[0]); |
| |
| // Freeze the test cgroup. |
| Future<bool> freeze = cgroups::freeze(hierarchy, TEST_CGROUPS_ROOT); |
| freeze.await(Seconds(5)); |
| ASSERT_TRUE(freeze.isReady()); |
| EXPECT_EQ(true, freeze.get()); |
| |
| // Thaw the test cgroup. |
| Future<bool> thaw = cgroups::thaw(hierarchy, TEST_CGROUPS_ROOT); |
| thaw.await(Seconds(5)); |
| ASSERT_TRUE(thaw.isReady()); |
| EXPECT_EQ(true, thaw.get()); |
| |
| // Kill the child process. |
| ASSERT_NE(-1, ::kill(pid, SIGKILL)); |
| |
| // Wait for the child process. |
| int status; |
| EXPECT_NE(-1, ::waitpid((pid_t) -1, &status, 0)); |
| ASSERT_TRUE(WIFSIGNALED(status)); |
| EXPECT_EQ(SIGKILL, WTERMSIG(status)); |
| } else { |
| // In child process. |
| ::close(pipes[0]); |
| |
| // Put self into the test cgroup. |
| Try<Nothing> assign = |
| cgroups::assign(hierarchy, TEST_CGROUPS_ROOT, ::getpid()); |
| |
| if (assign.isError()) { |
| std::cerr << "Failed to assign cgroup: " << assign.error() << std::endl; |
| abort(); |
| } |
| |
| // Notify the parent. |
| if (::write(pipes[1], &dummy, sizeof(dummy)) != sizeof(dummy)) { |
| perror("Failed to notify the parent"); |
| abort(); |
| } |
| ::close(pipes[1]); |
| |
| // Infinite loop here. |
| while (true); |
| |
| // Should not reach here. |
| std::cerr << "Reach an unreachable statement!" << std::endl; |
| abort(); |
| } |
| } |
| |
| |
| TEST_F(DISABLED_CgroupsAnyHierarchyWithCpuMemoryFreezerTest, ROOT_CGROUPS_Kill) |
| { |
| int pipes[2]; |
| int dummy; |
| ASSERT_NE(-1, ::pipe(pipes)); |
| |
| pid_t pid = ::fork(); |
| ASSERT_NE(-1, pid); |
| |
| if (pid > 0) { |
| // In parent process. |
| ::close(pipes[1]); |
| |
| // Wait until all children have assigned the cgroup. |
| ASSERT_LT(0, ::read(pipes[0], &dummy, sizeof(dummy))); |
| ASSERT_LT(0, ::read(pipes[0], &dummy, sizeof(dummy))); |
| ASSERT_LT(0, ::read(pipes[0], &dummy, sizeof(dummy))); |
| ASSERT_LT(0, ::read(pipes[0], &dummy, sizeof(dummy))); |
| ::close(pipes[0]); |
| |
| Try<Nothing> kill = cgroups::kill(hierarchy, TEST_CGROUPS_ROOT, SIGKILL); |
| EXPECT_SOME(kill); |
| |
| int status; |
| EXPECT_NE(-1, ::waitpid((pid_t) -1, &status, 0)); |
| ASSERT_TRUE(WIFSIGNALED(status)); |
| EXPECT_EQ(SIGKILL, WTERMSIG(status)); |
| } else { |
| // In child process. |
| |
| // We create 4 child processes here using two forks to test the case in |
| // which there are multiple active processes in the given cgroup. |
| ::fork(); |
| ::fork(); |
| |
| // Put self into the test cgroup. |
| Try<Nothing> assign = |
| cgroups::assign(hierarchy, TEST_CGROUPS_ROOT, ::getpid()); |
| |
| if (assign.isError()) { |
| std::cerr << "Failed to assign cgroup: " << assign.error() << std::endl; |
| abort(); |
| } |
| |
| // Notify the parent. |
| ::close(pipes[0]); // TODO(benh): Close after first fork? |
| if (::write(pipes[1], &dummy, sizeof(dummy)) != sizeof(dummy)) { |
| perror("Failed to notify the parent"); |
| abort(); |
| } |
| ::close(pipes[1]); |
| |
| // Wait kill signal from parent. |
| while (true); |
| |
| // Should not reach here. |
| std::cerr << "Reach an unreachable statement!" << std::endl; |
| abort(); |
| } |
| } |
| |
| |
| // TODO(benh): Write a version of this test with nested cgroups. |
| TEST_F(DISABLED_CgroupsAnyHierarchyWithCpuMemoryFreezerTest, ROOT_CGROUPS_Destroy) |
| { |
| int pipes[2]; |
| int dummy; |
| ASSERT_NE(-1, ::pipe(pipes)); |
| |
| pid_t pid = ::fork(); |
| ASSERT_NE(-1, pid); |
| |
| if (pid > 0) { |
| // In parent process. |
| ::close(pipes[1]); |
| |
| // Wait until all children have assigned the cgroup. |
| ASSERT_LT(0, ::read(pipes[0], &dummy, sizeof(dummy))); |
| ASSERT_LT(0, ::read(pipes[0], &dummy, sizeof(dummy))); |
| ASSERT_LT(0, ::read(pipes[0], &dummy, sizeof(dummy))); |
| ASSERT_LT(0, ::read(pipes[0], &dummy, sizeof(dummy))); |
| ::close(pipes[0]); |
| |
| Future<bool> future = cgroups::destroy(hierarchy, TEST_CGROUPS_ROOT); |
| future.await(Seconds(5)); |
| ASSERT_TRUE(future.isReady()); |
| EXPECT_TRUE(future.get()); |
| |
| int status; |
| EXPECT_NE(-1, ::waitpid((pid_t) -1, &status, 0)); |
| ASSERT_TRUE(WIFSIGNALED(status)); |
| EXPECT_EQ(SIGKILL, WTERMSIG(status)); |
| } else { |
| // In child process. |
| |
| // We create 4 child processes here using two forks to test the case in |
| // which there are multiple active processes in the given cgroup. |
| ::fork(); |
| ::fork(); |
| |
| // Put self into the test cgroup. |
| Try<Nothing> assign = |
| cgroups::assign(hierarchy, TEST_CGROUPS_ROOT, ::getpid()); |
| |
| if (assign.isError()) { |
| std::cerr << "Failed to assign cgroup: " << assign.error() << std::endl; |
| abort(); |
| } |
| |
| // Notify the parent. |
| ::close(pipes[0]); // TODO(benh): Close after first fork? |
| if (::write(pipes[1], &dummy, sizeof(dummy)) != sizeof(dummy)) { |
| perror("Failed to notify the parent"); |
| abort(); |
| } |
| ::close(pipes[1]); |
| |
| // Wait kill signal from parent. |
| while (true) ; |
| |
| // Should not reach here. |
| std::cerr << "Reach an unreachable statement!" << std::endl; |
| abort(); |
| } |
| } |