You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by bm...@apache.org on 2015/09/03 00:02:20 UTC
mesos git commit: Add support for parsing different versions of perf
sample output.
Repository: mesos
Updated Branches:
refs/heads/master ca208f283 -> f4a40c1fe
Add support for parsing different versions of perf sample output.
Review: https://reviews.apache.org/r/37462
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/f4a40c1f
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/f4a40c1f
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/f4a40c1f
Branch: refs/heads/master
Commit: f4a40c1fef6fd489691264438b6b26737af07482
Parents: ca208f2
Author: Paul Brett <pa...@twopensource.com>
Authored: Wed Sep 2 14:03:43 2015 -0700
Committer: Benjamin Mahler <be...@gmail.com>
Committed: Wed Sep 2 14:49:22 2015 -0700
----------------------------------------------------------------------
src/linux/perf.cpp | 136 ++++++++++++++++++----------
src/linux/perf.hpp | 6 +-
src/tests/containerizer/perf_tests.cpp | 12 ++-
3 files changed, 97 insertions(+), 57 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mesos/blob/f4a40c1f/src/linux/perf.cpp
----------------------------------------------------------------------
diff --git a/src/linux/perf.cpp b/src/linux/perf.cpp
index bb8b591..0011482 100644
--- a/src/linux/perf.cpp
+++ b/src/linux/perf.cpp
@@ -93,7 +93,7 @@ public:
virtual ~Perf() {}
- Future<string> future()
+ Future<string> output()
{
return promise.future();
}
@@ -264,11 +264,11 @@ private:
Future<Version> version()
{
internal::Perf* perf = new internal::Perf({"--version"});
- Future<string> future = perf->future();
+ Future<string> output = perf->output();
spawn(perf, true);
- return future
- .then([=](const string& output) -> Future<Version> {
+ return output
+ .then([](const string& output) -> Future<Version> {
// Trim off the leading 'perf version ' text to convert.
return Version::parse(strings::remove(
output, "perf version ", strings::PREFIX));
@@ -276,15 +276,40 @@ Future<Version> version()
};
+bool supported(const Version& version)
+{
+ // Require perf version >= 2.6.39 to support cgroups and formatting.
+ return version >= Version(2, 6, 39);
+}
+
+
+bool supported()
+{
+ Future<Version> version = perf::version();
+
+ // If perf does not respond in a reasonable time, mark as unsupported.
+ version.await(Seconds(5));
+
+ if (!version.isReady()) {
+ if (version.isFailed()) {
+ LOG(ERROR) << "Failed to get perf version: " << version.failure();
+ } else {
+ LOG(ERROR) << "Failed to get perf version: timeout of 5secs exceeded";
+ }
+
+ version.discard();
+ return false;
+ }
+
+ return supported(version.get());
+}
+
+
Future<hashmap<string, mesos::PerfStatistics>> sample(
const set<string>& events,
const set<string>& cgroups,
const Duration& duration)
{
- if (!supported()) {
- return Failure("Perf is not supported");
- }
-
vector<string> argv = {
"stat",
@@ -317,26 +342,38 @@ Future<hashmap<string, mesos::PerfStatistics>> sample(
Time start = Clock::now();
internal::Perf* perf = new internal::Perf(argv);
- Future<string> future = perf->future();
+ Future<string> output = perf->output();
spawn(perf, true);
- auto parse = [start, duration](const string& output) ->
+ auto parse = [start, duration](
+ const tuple<Version, string> values) ->
Future<hashmap<string, mesos::PerfStatistics>> {
- Try<hashmap<string, mesos::PerfStatistics>> parse = perf::parse(output);
+ const Version& version = std::get<0>(values);
+ const string& output = std::get<1>(values);
+
+ // Check that the version is supported.
+ if (!supported(version)) {
+ return Failure("Perf " + stringify(version) + " is not supported");
+ }
+
+ Try<hashmap<string, mesos::PerfStatistics>> result =
+ perf::parse(output, version);
- if (parse.isError()) {
- return Failure("Failed to parse perf sample: " + parse.error());
+ if (result.isError()) {
+ return Failure("Failed to parse perf sample: " + result.error());
}
- foreachvalue (mesos::PerfStatistics& statistics, parse.get()) {
+ foreachvalue (mesos::PerfStatistics& statistics, result.get()) {
statistics.set_timestamp(start.secs());
statistics.set_duration(duration.secs());
}
- return parse.get();
+ return result.get();
};
- return future.then(parse);
+ // TODO(pbrett): Don't wait for these forever!
+ return process::collect(perf::version(), output)
+ .then(parse);
}
@@ -355,29 +392,6 @@ bool valid(const set<string>& events)
}
-bool supported()
-{
- // Require perf version >= 2.6.39 to support cgroups and formatting.
- Future<Version> version = perf::version();
-
- // If perf does not respond in a reasonable time, mark as unsupported.
- version.await(Seconds(5));
-
- if (!version.isReady()) {
- if (version.isFailed()) {
- LOG(ERROR) << "Failed to get perf version: " << version.failure();
- } else {
- LOG(ERROR) << "Failed to get perf version: timeout of 5secs exceeded";
- }
-
- version.discard();
- return false;
- }
-
- return version.get() >= Version(2, 6, 39);
-}
-
-
struct Sample
{
const string value;
@@ -386,30 +400,51 @@ struct Sample
// Convert a single line of perf output in CSV format (using
// PERF_DELIMITER as a separator) to a sample.
- static Try<Sample> parse(const string& line)
+ static Try<Sample> parse(const string& line, const Version& version)
{
- vector<string> tokens = strings::tokenize(line, PERF_DELIMITER);
-
- // Expected format for an output line is 'value,event,cgroup'.
- if (tokens.size() == 3) {
- return Sample({tokens[0], internal::normalize(tokens[1]), tokens[2]});
+ // We use strings::split to separate the tokens
+ // because the unit field can be empty.
+ vector<string> tokens = strings::split(line, PERF_DELIMITER);
+
+ if (version >= Version(4, 0, 0)) {
+ // Optional running time and ratio were introduced in Linux v4.0,
+ // which make the format either:
+ // value,unit,event,cgroup
+ // value,unit,event,cgroup,running,ratio
+ if ((tokens.size() == 4) || (tokens.size() == 6)) {
+ return Sample({tokens[0], internal::normalize(tokens[2]), tokens[3]});
+ }
+ } else if (version >= Version(3, 13, 0)) {
+ // Unit was added in Linux v3.13, making the format:
+ // value,unit,event,cgroup
+ if (tokens.size() == 4) {
+ return Sample({tokens[0], internal::normalize(tokens[2]), tokens[3]});
+ }
} else {
- return Error("Unexpected number of fields");
+ // Expected format for Linux kernel <= 3.12 is:
+ // value,event,cgroup
+ if (tokens.size() == 3) {
+ return Sample({tokens[0], internal::normalize(tokens[1]), tokens[2]});
+ }
}
+
+ return Error("Unexpected number of fields");
}
};
-Try<hashmap<string, mesos::PerfStatistics>> parse(const string& output)
+Try<hashmap<string, mesos::PerfStatistics>> parse(
+ const string& output,
+ const Version& version)
{
hashmap<string, mesos::PerfStatistics> statistics;
foreach (const string& line, strings::tokenize(output, "\n")) {
- Try<Sample> sample = Sample::parse(line);
+ Try<Sample> sample = Sample::parse(line, version);
if (sample.isError()) {
return Error("Failed to parse perf sample line '" + line + "': " +
- sample.error() );
+ sample.error());
}
if (!statistics.contains(sample->cgroup)) {
@@ -423,7 +458,8 @@ Try<hashmap<string, mesos::PerfStatistics>> parse(const string& output)
sample->event);
if (field == NULL) {
- return Error("Unexpected perf output at line: " + line);
+ return Error("Unexpected event '" + sample->event + "'"
+ " in perf output at line: " + line);
}
if (sample->value == "<not supported>") {
http://git-wip-us.apache.org/repos/asf/mesos/blob/f4a40c1f/src/linux/perf.hpp
----------------------------------------------------------------------
diff --git a/src/linux/perf.hpp b/src/linux/perf.hpp
index c444456..d10968c 100644
--- a/src/linux/perf.hpp
+++ b/src/linux/perf.hpp
@@ -53,9 +53,11 @@ bool valid(const std::set<std::string>& events);
bool supported();
-// Note: Exposed for testing purposes.
+// Note: The parse function is exposed to allow testing of the
+// multiple supported perf stat output formats.
Try<hashmap<std::string, mesos::PerfStatistics>> parse(
- const std::string& output);
+ const std::string& output,
+ const Version& version);
} // namespace perf {
http://git-wip-us.apache.org/repos/asf/mesos/blob/f4a40c1f/src/tests/containerizer/perf_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/perf_tests.cpp b/src/tests/containerizer/perf_tests.cpp
index 8c29ca3..bef475e 100644
--- a/src/tests/containerizer/perf_tests.cpp
+++ b/src/tests/containerizer/perf_tests.cpp
@@ -64,7 +64,8 @@ TEST_F(PerfTest, Parse)
perf::parse("123,cycles,cgroup1\n"
"456,cycles,cgroup2\n"
"0.456,task-clock,cgroup2\n"
- "0.123,task-clock,cgroup1");
+ "0.123,task-clock,cgroup1",
+ Version(3, 12, 0));
ASSERT_SOME(parse);
EXPECT_EQ(2u, parse->size());
@@ -86,7 +87,7 @@ TEST_F(PerfTest, Parse)
EXPECT_EQ(0.456, statistics.task_clock());
// Statistics reporting <not supported> should not appear.
- parse = perf::parse("<not supported>,cycles,cgroup1");
+ parse = perf::parse("<not supported>,cycles,cgroup1", Version(3, 12, 0));
ASSERT_SOME(parse);
ASSERT_TRUE(parse->contains("cgroup1"));
@@ -95,7 +96,8 @@ TEST_F(PerfTest, Parse)
// Statistics reporting <not counted> should be zero.
parse = perf::parse("<not counted>,cycles,cgroup1\n"
- "<not counted>,task-clock,cgroup1");
+ "<not counted>,task-clock,cgroup1",
+ Version(3, 12, 0));
ASSERT_SOME(parse);
ASSERT_TRUE(parse->contains("cgroup1"));
@@ -107,10 +109,10 @@ TEST_F(PerfTest, Parse)
EXPECT_EQ(0.0, statistics.task_clock());
// Check parsing fails.
- parse = perf::parse("1,cycles\ngarbage");
+ parse = perf::parse("1,cycles\ngarbage", Version(3, 12, 0));
EXPECT_ERROR(parse);
- parse = perf::parse("1,unknown-field");
+ parse = perf::parse("1,unknown-field", Version(3, 12, 0));
EXPECT_ERROR(parse);
}