http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/memory_test_helper.cpp ---------------------------------------------------------------------- diff --git a/src/tests/memory_test_helper.cpp b/src/tests/memory_test_helper.cpp deleted file mode 100644 index 8093e66..0000000 --- a/src/tests/memory_test_helper.cpp +++ /dev/null @@ -1,320 +0,0 @@ -/** - * 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.n - */ - -#include <signal.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include <sys/mman.h> -#include <sys/types.h> -#include <sys/wait.h> - -#include <string> -#include <vector> - -#include <stout/bytes.hpp> -#include <stout/error.hpp> -#include <stout/hashmap.hpp> -#include <stout/lambda.hpp> -#include <stout/os.hpp> -#include <stout/stringify.hpp> -#include <stout/strings.hpp> -#include <stout/try.hpp> - -#include "tests/flags.hpp" -#include "tests/memory_test_helper.hpp" - -using process::Subprocess; - -using std::cerr; -using std::cin; -using std::cout; -using std::endl; -using std::flush; -using std::getline; -using std::string; -using std::vector; - -namespace mesos { -namespace internal { -namespace tests { - -// Constants used to sync MemoryTestHelper and its subprocess. - -// Used by the subprocess to inform that it has started. -const char STARTED = 'S'; - -// Used by the subprocess to inform that the work requested is done. -const char DONE = 'D'; - -// Used to signal an increaseRSS request. -const char INCREASE_RSS[] = "INCREASE_RSS"; - -// Used to signal an increasePageCache request. -const char INCREASE_PAGE_CACHE[] = "INCREASE_PAGE_CACHE"; - - -// This helper allocates and locks specified anonymous memory (RSS). -// It uses mlock and memset to make sure allocated memory is mapped. -static Try<void*> allocateRSS(const Bytes& size, bool lock = true) -{ - void* rss = NULL; - - if (posix_memalign(&rss, getpagesize(), size.bytes()) != 0) { - return ErrnoError("Failed to increase RSS memory, posix_memalign"); - } - - // Use memset to actually page in the memory in the kernel. - memset(rss, 1, size.bytes()); - - // Locking a page makes it unevictable in the kernel. - if (lock && mlock(rss, size.bytes()) != 0) { - return ErrnoError("Failed to lock memory, mlock"); - } - - return rss; -} - - -static Try<Nothing> increaseRSS(const vector<string>& tokens) -{ - if (tokens.size() < 2) { - return Error("Expect at least one argument"); - } - - Try<Bytes> size = Bytes::parse(tokens[1]); - if (size.isError()) { - return Error("The first argument '" + tokens[1] + "' is not a byte size"); - } - - Try<void*> memory = allocateRSS(size.get()); - if (memory.isError()) { - return Error("Failed to allocate RSS memory: " + memory.error()); - } - - return Nothing(); -} - - -static Try<Nothing> increasePageCache(const vector<string>& tokens) -{ - const Bytes UNIT = Megabytes(1); - - if (tokens.size() < 2) { - return Error("Expect at least one argument"); - } - - Try<Bytes> size = Bytes::parse(tokens[1]); - if (size.isError()) { - return Error("The first argument '" + tokens[1] + "' is not a byte size"); - } - - // TODO(chzhcn): Currently, we assume the current working directory - // is a temporary directory and will be cleaned up when the test - // finishes. Since the child process will inherit the current - // working directory from the parent process, that means the test - // that uses this helper probably needs to inherit from - // TemporaryDirectoryTest. Consider relaxing this constraint. - Try<string> path = os::mktemp(path::join(os::getcwd(), "XXXXXX")); - if (path.isError()) { - return Error("Failed to create a temporary file: " + path.error()); - } - - Try<int> fd = os::open(path.get(), O_WRONLY); - if (fd.isError()) { - return Error("Failed to open file: " + fd.error()); - } - - // NOTE: We are doing round-down here to calculate the number of - // writes to do. - for (uint64_t i = 0; i < size.get().bytes() / UNIT.bytes(); i++) { - // Write UNIT size to disk at a time. The content isn't important. - Try<Nothing> write = os::write(fd.get(), string(UNIT.bytes(), 'a')); - if (write.isError()) { - os::close(fd.get()); - return Error("Failed to write file: " + write.error()); - } - - // Use fsync to make sure data is written to disk. - if (fsync(fd.get()) == -1) { - // Save the error message because os::close below might - // overwrite the errno. - const string message = strerror(errno); - - os::close(fd.get()); - return Error("Failed to fsync: " + message); - } - } - - os::close(fd.get()); - return Nothing(); -} - - -MemoryTestHelper::~MemoryTestHelper() -{ - cleanup(); -} - - -Try<Nothing> MemoryTestHelper::spawn() -{ - if (s.isSome()) { - return Error("A subprocess has been spawned already"); - } - - vector<string> argv; - argv.push_back("memory-test-helper"); - argv.push_back(MemoryTestHelperMain::NAME); - - Try<Subprocess> process = subprocess( - path::join(flags.build_dir, - "src", - "memory-test-helper"), - argv, - Subprocess::PIPE(), - Subprocess::PIPE(), - Subprocess::FD(STDERR_FILENO)); - - if (process.isError()) { - return Error("Failed to spawn a subprocess: " + process.error()); - } - - s = process.get(); - - // Wait for the child to inform it has started before returning. - // Otherwise, the user might set the memory limit too earlier, and - // cause the child oom-killed because 'ld' could use a lot of - // memory. - Result<string> read = os::read(s.get().out().get(), sizeof(STARTED)); - if (!read.isSome() || read.get() != string(sizeof(STARTED), STARTED)) { - cleanup(); - return Error("Failed to sync with the subprocess"); - } - - return Nothing(); -} - - -void MemoryTestHelper::cleanup() -{ - if (s.isSome()) { - // We just want to make sure the subprocess is terminated in case - // it's stuck, but we don't care about its status. Any error - // should have been logged in the subprocess directly. - ::kill(s.get().pid(), SIGKILL); - ::waitpid(s.get().pid(), NULL, 0); - s = None(); - } -} - - -Try<pid_t> MemoryTestHelper::pid() -{ - if (s.isNone()) { - return Error("The subprocess has not been spawned yet"); - } - - return s.get().pid(); -} - - -// Send a request to the subprocess and wait for its signal that the -// work has been done. -Try<Nothing> MemoryTestHelper::requestAndWait(const string& request) -{ - if (s.isNone()) { - return Error("The subprocess has not been spawned yet"); - } - - Try<Nothing> write = os::write(s.get().in().get(), request + "\n"); - if (write.isError()) { - cleanup(); - return Error("Fail to sync with the subprocess: " + write.error()); - } - - Result<string> read = os::read(s.get().out().get(), sizeof(DONE)); - if (!read.isSome() || read.get() != string(sizeof(DONE), DONE)) { - cleanup(); - return Error("Failed to sync with the subprocess"); - } - - return Nothing(); -} - - -Try<Nothing> MemoryTestHelper::increaseRSS(const Bytes& size) -{ - return requestAndWait(string(INCREASE_RSS) + " " + stringify(size)); -} - - -Try<Nothing> MemoryTestHelper::increasePageCache(const Bytes& size) -{ - return requestAndWait(string(INCREASE_PAGE_CACHE) + " " + stringify(size)); -} - - -const char MemoryTestHelperMain::NAME[] = "MemoryTestHelperMain"; - - -int MemoryTestHelperMain::execute() -{ - hashmap<string, Try<Nothing>(*)(const vector<string>&)> commands; - commands[INCREASE_RSS] = &increaseRSS; - commands[INCREASE_PAGE_CACHE] = &increasePageCache; - - // Tell the parent that child has started. - cout << STARTED << flush; - - string line; - while(cin.good()) { - getline(cin, line); - vector<string> tokens = strings::tokenize(line, " "); - - if (tokens.empty()) { - cerr << "No command from the parent" << endl; - return 1; - } - - if (!commands.contains(tokens[0])) { - cerr << "Unknown command from the parent '" << tokens[0] << "'" << endl; - return 1; - } - - Try<Nothing> result = commands[tokens[0]](tokens); - if (result.isError()) { - cerr << result.error(); - return 1; - } - - cout << DONE << flush; - } - - if (!cin) { - cerr << "Failed to sync with the parent" << endl; - return 1; - } - - return 0; -} - -} // namespace tests { -} // namespace internal { -} // namespace mesos {
http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/memory_test_helper.hpp ---------------------------------------------------------------------- diff --git a/src/tests/memory_test_helper.hpp b/src/tests/memory_test_helper.hpp deleted file mode 100644 index 11712d7..0000000 --- a/src/tests/memory_test_helper.hpp +++ /dev/null @@ -1,89 +0,0 @@ -/** - * 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. - */ - -#ifndef __MEMORY_TEST_HELPER_HPP__ -#define __MEMORY_TEST_HELPER_HPP__ - -#include <process/subprocess.hpp> - -#include <stout/bytes.hpp> -#include <stout/option.hpp> -#include <stout/subcommand.hpp> -#include <stout/try.hpp> - -namespace mesos { -namespace internal { -namespace tests { - -// The abstraction for controlling the memory usage of a subprocess. -// TODO(chzhcn): Currently, this helper is only supposed to be used by -// one thread. Consider making it thread safe. -class MemoryTestHelper -{ -public: - MemoryTestHelper() {}; - ~MemoryTestHelper(); - - // Spawns a subprocess. - // TODO(chzhcn): Consider returning a future instead of blocking. - Try<Nothing> spawn(); - - // Kill and reap the subprocess if exists. - // TODO(chzhcn): Consider returning a future instead of blocking. - void cleanup(); - - // Returns the pid of the subprocess. - Try<pid_t> pid(); - - // Allocate and lock specified page-aligned anonymous memory (RSS) - // in the subprocess. It uses mlock and memset to make sure - // allocated memory is mapped. - // TODO(chzhcn): Consider returning a future instead of blocking. - Try<Nothing> increaseRSS(const Bytes& size); - - // This function attempts to generate requested size of page cache - // in the subprocess by using a small buffer and writing it to disk - // multiple times. - // TODO(chzhcn): Consider returning a future instead of blocking. - Try<Nothing> increasePageCache(const Bytes& size = Megabytes(1)); - -private: - Try<Nothing> requestAndWait(const std::string& request); - - Option<process::Subprocess> s; -}; - - -// The actual subprocess behind MemoryTestHelper. It runs in a loop -// and executes commands passed from stdin. -class MemoryTestHelperMain : public Subcommand -{ -public: - static const char NAME[]; - - MemoryTestHelperMain() : Subcommand(NAME) {}; - -protected: - virtual int execute(); -}; - -} // namespace tests { -} // namespace internal { -} // namespace mesos { - -#endif // __MEMORY_TEST_HELPER_HPP__ http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/memory_test_helper_main.cpp ---------------------------------------------------------------------- diff --git a/src/tests/memory_test_helper_main.cpp b/src/tests/memory_test_helper_main.cpp deleted file mode 100644 index 362535f..0000000 --- a/src/tests/memory_test_helper_main.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/** - * 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 <stout/subcommand.hpp> - -#include "tests/memory_test_helper.hpp" - -using mesos::internal::tests::MemoryTestHelperMain; - -int main(int argc, char** argv) -{ - return Subcommand::dispatch( - None(), - argc, - argv, - new MemoryTestHelperMain()); -} http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/ns_tests.cpp ---------------------------------------------------------------------- diff --git a/src/tests/ns_tests.cpp b/src/tests/ns_tests.cpp deleted file mode 100644 index bcd0e12..0000000 --- a/src/tests/ns_tests.cpp +++ /dev/null @@ -1,301 +0,0 @@ -/** - * 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 <sys/wait.h> - -#include <iostream> - -#include <pthread.h> -#include <unistd.h> - -#include <list> -#include <set> -#include <vector> - -#include <gtest/gtest.h> - -#include <stout/gtest.hpp> -#include <stout/lambda.hpp> -#include <stout/os.hpp> - -#include <process/gtest.hpp> -#include <process/subprocess.hpp> - -#include "linux/ns.hpp" - -#include "tests/flags.hpp" -#include "tests/setns_test_helper.hpp" - -using namespace process; - -using std::list; -using std::set; -using std::string; -using std::vector; - -namespace mesos { -namespace internal { -namespace tests { - - -// Helper for cloneChild() which expects an int(void*). -static int cloneChildHelper(void* _func) -{ - const lambda::function<int()>* func = - static_cast<const lambda::function<int()>*> (_func); - - return (*func)(); -} - - -static pid_t cloneChild( - int flags, - const lambda::function<int()>& func) - -{ - // 8 MiB stack for child. - static unsigned long long stack[(8*1024*1024)/sizeof(unsigned long long)]; - - return ::clone( - cloneChildHelper, - &stack[sizeof(stack)/sizeof(stack[0]) - 1], // Stack grows down. - flags | SIGCHLD, - (void*) &func); -} - - -// Test that a child in different namespace(s) can setns back to the -// root namespace. We must fork a child to test this because setns -// doesn't support multi-threaded processes (which gtest is). -TEST(NsTest, ROOT_setns) -{ - // Clone then exec the setns-test-helper into a new namespace for - // each available namespace. - set<string> namespaces = ns::namespaces(); - ASSERT_FALSE(namespaces.empty()); - - int flags = 0; - - foreach (const string& ns, namespaces) { - // Skip 'user' namespace because it causes 'clone' to change us - // from being user 'root' to user 'nobody', but these tests - // require root. See MESOS-3083. - if (ns == "user") { - continue; - } - - Try<int> nstype = ns::nstype(ns); - ASSERT_SOME(nstype); - - flags |= nstype.get(); - } - - vector<string> argv; - argv.push_back("setns-test-helper"); - argv.push_back(SetnsTestHelper::NAME); - - Try<Subprocess> s = subprocess( - path::join(tests::flags.build_dir, "src", "setns-test-helper"), - argv, - Subprocess::FD(STDIN_FILENO), - Subprocess::FD(STDOUT_FILENO), - Subprocess::FD(STDERR_FILENO), - None(), - None(), - None(), - lambda::bind(&cloneChild, flags, lambda::_1)); - - // Continue in parent. - ASSERT_SOME(s); - - // The child should exit 0. - Future<Option<int>> status = s.get().status(); - AWAIT_READY(status); - - ASSERT_SOME(status.get()); - EXPECT_TRUE(WIFEXITED(status.get().get())); - EXPECT_EQ(0, status.get().get()); -} - - -static void* childThread(void* arg) -{ - // Newly created threads have PTHREAD_CANCEL_ENABLE and - // PTHREAD_CANCEL_DEFERRED so they can be cancelled. - while (true) { os::sleep(Seconds(1)); } - - return NULL; -} - - -// Test that setns correctly refuses to re-associate to a namespace if -// the caller is multi-threaded. -TEST(NsTest, ROOT_setnsMultipleThreads) -{ - set<string> namespaces = ns::namespaces(); - EXPECT_LT(0u, namespaces.size()); - - // Do not allow multi-threaded environment. - pthread_t pthread; - ASSERT_EQ(0, pthread_create(&pthread, NULL, childThread, NULL)); - - foreach (const string& ns, namespaces) { - EXPECT_ERROR(ns::setns(::getpid(), ns)); - } - - // Terminate the threads. - EXPECT_EQ(0, pthread_cancel(pthread)); - EXPECT_EQ(0, pthread_join(pthread, NULL)); -} - - -// Use a different child function for clone because it requires -// int(*)(void*). -static int childGetns(void* arg) -{ - // Sleep until killed. - while (true) { sleep(1); } - - ABORT("Error, child should be killed before reaching here"); -} - - -// Test that we can get the namespace inodes for a forked child. -TEST(NsTest, ROOT_getns) -{ - set<string> namespaces = ns::namespaces(); - - // ns::setns() does not support "pid". - namespaces.erase("pid"); - - // Use the first other namespace available. - ASSERT_FALSE(namespaces.empty()); - string ns = *(namespaces.begin()); - - ASSERT_SOME(ns::getns(::getpid(), ns)); - - Try<int> nstype = ns::nstype(ns); - ASSERT_SOME(nstype); - - // 8 MiB stack for child. - static unsigned long long stack[(8*1024*1024)/sizeof(unsigned long long)]; - - pid_t pid = clone( - childGetns, - &stack[sizeof(stack)/sizeof(stack[0]) - 1], // Stack grows down. - SIGCHLD | nstype.get(), - NULL); - - ASSERT_NE(-1, pid); - - // Continue in parent. - Try<ino_t> nsParent = ns::getns(::getpid(), ns); - ASSERT_SOME(nsParent); - - Try<ino_t> nsChild = ns::getns(pid, ns); - ASSERT_SOME(nsChild); - - // Child should be in a different namespace. - EXPECT_NE(nsParent.get(), nsChild.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)); -} - - -static int childDestroy(void* arg) -{ - // Fork a bunch of children. - ::fork(); - ::fork(); - ::fork(); - - // Parent and all children sleep. - while (true) { sleep(1); } - - ABORT("Error, child should be killed before reaching here"); -} - - -// Test we can destroy a pid namespace, i.e., kill all processes. -TEST(NsTest, ROOT_destroy) -{ - set<string> namespaces = ns::namespaces(); - - if (namespaces.count("pid") == 0) { - // Pid namespace is not available. - return; - } - - Try<int> nstype = ns::nstype("pid"); - ASSERT_SOME(nstype); - - // 8 MiB stack for child. - static unsigned long long stack[(8*1024*1024)/sizeof(unsigned long long)]; - - pid_t pid = clone( - childDestroy, - &stack[sizeof(stack)/sizeof(stack[0]) - 1], // Stack grows down. - SIGCHLD | nstype.get(), - NULL); - - ASSERT_NE(-1, pid); - - Future<Option<int>> status = process::reap(pid); - - // Ensure the child is in a different pid namespace. - Try<ino_t> childNs = ns::getns(pid, "pid"); - ASSERT_SOME(childNs); - - Try<ino_t> ourNs = ns::getns(::getpid(), "pid"); - ASSERT_SOME(ourNs); - - ASSERT_NE(ourNs.get(), childNs.get()); - - // Kill the child. - AWAIT_READY(ns::pid::destroy(childNs.get())); - - AWAIT_READY(status); - ASSERT_SOME(status.get()); - ASSERT_TRUE(WIFSIGNALED(status.get().get())); - EXPECT_EQ(SIGKILL, WTERMSIG(status.get().get())); - - // Finally, verify that no processes are in the child's pid - // namespace, i.e., destroy() also killed all descendants. - Try<set<pid_t>> pids = os::pids(); - ASSERT_SOME(pids); - - foreach (pid_t pid, pids.get()) { - Try<ino_t> otherNs = ns::getns(pid, "pid"); - // pid may have exited since getting the snapshot of pids so - // ignore any error. - if (otherNs.isSome()) { - ASSERT_SOME_NE(childNs.get(), otherNs); - } - } -} - -} // namespace tests { -} // namespace internal { -} // namespace mesos { http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/perf_tests.cpp ---------------------------------------------------------------------- diff --git a/src/tests/perf_tests.cpp b/src/tests/perf_tests.cpp deleted file mode 100644 index 6b3d70f..0000000 --- a/src/tests/perf_tests.cpp +++ /dev/null @@ -1,183 +0,0 @@ -/** - * 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 <sys/prctl.h> - -#include <set> - -#include <gmock/gmock.h> - -#include <process/clock.hpp> -#include <process/gtest.hpp> -#include <process/reap.hpp> - -#include <stout/gtest.hpp> -#include <stout/stringify.hpp> - -#include "linux/perf.hpp" - -using std::set; -using std::string; - -using namespace process; - -namespace mesos { -namespace internal { -namespace tests { - -class PerfTest : public ::testing::Test {}; - - -TEST_F(PerfTest, ROOT_Events) -{ - set<string> events; - // Valid events. - events.insert("cycles"); - events.insert("task-clock"); - EXPECT_TRUE(perf::valid(events)); - - // Add an invalid event. - events.insert("this-is-an-invalid-event"); - EXPECT_FALSE(perf::valid(events)); -} - - -TEST_F(PerfTest, Parse) -{ - // uint64 and floats should be parsed. - Try<hashmap<string, mesos::PerfStatistics> > parse = - perf::parse("123,cycles\n0.123,task-clock"); - CHECK_SOME(parse); - - ASSERT_TRUE(parse.get().contains("")); - mesos::PerfStatistics statistics = parse.get().get("").get(); - - ASSERT_TRUE(statistics.has_cycles()); - EXPECT_EQ(123u, statistics.cycles()); - ASSERT_TRUE(statistics.has_task_clock()); - EXPECT_EQ(0.123, statistics.task_clock()); - - // Parse multiple cgroups. - parse = perf::parse("123,cycles,cgroup1\n" - "456,cycles,cgroup2\n" - "0.456,task-clock,cgroup2\n" - "0.123,task-clock,cgroup1"); - CHECK_SOME(parse); - EXPECT_FALSE(parse.get().contains("")); - - ASSERT_TRUE(parse.get().contains("cgroup1")); - statistics = parse.get().get("cgroup1").get(); - - ASSERT_TRUE(statistics.has_cycles()); - EXPECT_EQ(123u, statistics.cycles()); - ASSERT_TRUE(statistics.has_task_clock()); - EXPECT_EQ(0.123, statistics.task_clock()); - - ASSERT_TRUE(parse.get().contains("cgroup2")); - statistics = parse.get().get("cgroup2").get(); - - ASSERT_TRUE(statistics.has_cycles()); - EXPECT_EQ(456u, statistics.cycles()); - EXPECT_TRUE(statistics.has_task_clock()); - EXPECT_EQ(0.456, statistics.task_clock()); - - // Statistics reporting <not supported> should not appear. - parse = perf::parse("<not supported>,cycles"); - CHECK_SOME(parse); - - ASSERT_TRUE(parse.get().contains("")); - statistics = parse.get().get("").get(); - EXPECT_FALSE(statistics.has_cycles()); - - // Statistics reporting <not counted> should be zero. - parse = perf::parse("<not counted>,cycles\n<not counted>,task-clock"); - CHECK_SOME(parse); - - ASSERT_TRUE(parse.get().contains("")); - statistics = parse.get().get("").get(); - - EXPECT_TRUE(statistics.has_cycles()); - EXPECT_EQ(0u, statistics.cycles()); - EXPECT_TRUE(statistics.has_task_clock()); - EXPECT_EQ(0.0, statistics.task_clock()); - - // Check parsing fails. - parse = perf::parse("1,cycles\ngarbage"); - EXPECT_ERROR(parse); - - parse = perf::parse("1,unknown-field"); - EXPECT_ERROR(parse); -} - - -TEST_F(PerfTest, ROOT_SamplePid) -{ - // TODO(idownes): Replace this with a Subprocess when it supports - // DEATHSIG. - // Fork a child which we'll run perf against. - pid_t pid = fork(); - ASSERT_GE(pid, 0); - - if (pid == 0) { - // Kill ourself if the parent dies to prevent leaking the child. - prctl(PR_SET_PDEATHSIG, SIGKILL); - - // Spin child to consume cpu cycles. - while (true); - } - - // Continue in parent. - set<string> events; - // Hardware event. - events.insert("cycles"); - // Software event. - events.insert("task-clock"); - - // Sample the child. - Duration duration = Milliseconds(100); - Future<mesos::PerfStatistics> statistics = - perf::sample(events, pid, duration); - AWAIT_READY(statistics); - - // Kill the child and reap it. - Future<Option<int>> status = reap(pid); - kill(pid, SIGKILL); - AWAIT_READY(status); - - // Check the sample timestamp is within the last 5 seconds. This is generous - // because there's the process reap delay in addition to the sampling - // duration. - ASSERT_TRUE(statistics.get().has_timestamp()); - EXPECT_GT( - Seconds(5).secs(), Clock::now().secs() - statistics.get().timestamp()); - EXPECT_EQ(duration.secs(), statistics.get().duration()); - - ASSERT_TRUE(statistics.get().has_cycles()); - - // TODO(benh): Some Linux distributions (Ubuntu 14.04) fail to - // properly sample 'cycles' with 'perf', so we don't explicitly - // check the value here. See MESOS-3082. - // EXPECT_LT(0u, statistics.get().cycles()); - - ASSERT_TRUE(statistics.get().has_task_clock()); - EXPECT_LT(0.0, statistics.get().task_clock()); -} - -} // namespace tests { -} // namespace internal { -} // namespace mesos {
