You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by ji...@apache.org on 2016/09/05 19:04:20 UTC

mesos git commit: Implemented `PerfEventSubsystem`.

Repository: mesos
Updated Branches:
  refs/heads/master a835a1945 -> 1e9e82152


Implemented `PerfEventSubsystem`.

Review: https://reviews.apache.org/r/49853/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/1e9e8215
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/1e9e8215
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/1e9e8215

Branch: refs/heads/master
Commit: 1e9e82152f55ff1f854765d2c99a567623b7709e
Parents: a835a19
Author: haosdent huang <ha...@gmail.com>
Authored: Mon Sep 5 11:16:05 2016 -0700
Committer: Jie Yu <yu...@gmail.com>
Committed: Mon Sep 5 12:03:06 2016 -0700

----------------------------------------------------------------------
 src/CMakeLists.txt                              |   1 +
 src/Makefile.am                                 |   2 +
 .../mesos/isolators/cgroups/cgroups.cpp         |   1 +
 .../mesos/isolators/cgroups/constants.hpp       |   1 +
 .../mesos/isolators/cgroups/subsystem.cpp       |   2 +
 .../isolators/cgroups/subsystems/perf_event.cpp | 215 +++++++++++++++++++
 .../isolators/cgroups/subsystems/perf_event.hpp | 108 ++++++++++
 7 files changed, 330 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/1e9e8215/src/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 751b8e9..01ef494 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -170,6 +170,7 @@ set(LINUX_SRC
   slave/containerizer/mesos/isolators/cgroups/subsystems/devices.cpp
   slave/containerizer/mesos/isolators/cgroups/subsystems/memory.cpp
   slave/containerizer/mesos/isolators/cgroups/subsystems/net_cls.cpp
+  slave/containerizer/mesos/isolators/cgroups/subsystems/perf_event.cpp
   slave/containerizer/mesos/isolators/docker/runtime.cpp
   slave/containerizer/mesos/isolators/docker/volume/driver.cpp
   slave/containerizer/mesos/isolators/docker/volume/isolator.cpp

http://git-wip-us.apache.org/repos/asf/mesos/blob/1e9e8215/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index 16fafd1..15b9a63 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1036,6 +1036,7 @@ MESOS_LINUX_FILES =									\
   slave/containerizer/mesos/isolators/cgroups/subsystems/devices.cpp			\
   slave/containerizer/mesos/isolators/cgroups/subsystems/memory.cpp			\
   slave/containerizer/mesos/isolators/cgroups/subsystems/net_cls.cpp			\
+  slave/containerizer/mesos/isolators/cgroups/subsystems/perf_event.cpp			\
   slave/containerizer/mesos/isolators/docker/runtime.cpp				\
   slave/containerizer/mesos/isolators/docker/volume/isolator.cpp			\
   slave/containerizer/mesos/isolators/filesystem/linux.cpp				\
@@ -1076,6 +1077,7 @@ MESOS_LINUX_FILES +=									\
   slave/containerizer/mesos/isolators/cgroups/subsystems/devices.hpp			\
   slave/containerizer/mesos/isolators/cgroups/subsystems/memory.hpp			\
   slave/containerizer/mesos/isolators/cgroups/subsystems/net_cls.hpp			\
+  slave/containerizer/mesos/isolators/cgroups/subsystems/perf_event.hpp			\
   slave/containerizer/mesos/isolators/docker/runtime.hpp				\
   slave/containerizer/mesos/isolators/docker/volume/isolator.hpp			\
   slave/containerizer/mesos/isolators/filesystem/linux.hpp				\

http://git-wip-us.apache.org/repos/asf/mesos/blob/1e9e8215/src/slave/containerizer/mesos/isolators/cgroups/cgroups.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/isolators/cgroups/cgroups.cpp b/src/slave/containerizer/mesos/isolators/cgroups/cgroups.cpp
index 11547b6..8b6dfde 100644
--- a/src/slave/containerizer/mesos/isolators/cgroups/cgroups.cpp
+++ b/src/slave/containerizer/mesos/isolators/cgroups/cgroups.cpp
@@ -80,6 +80,7 @@ Try<Isolator*> CgroupsIsolatorProcess::create(const Flags& flags)
     {"devices", CGROUP_SUBSYSTEM_DEVICES_NAME},
     {"mem", CGROUP_SUBSYSTEM_MEMORY_NAME},
     {"net_cls", CGROUP_SUBSYSTEM_NET_CLS_NAME},
+    {"perf_event", CGROUP_SUBSYSTEM_PERF_EVENT_NAME},
   };
 
   foreach (string isolator, strings::tokenize(flags.isolation, ",")) {

http://git-wip-us.apache.org/repos/asf/mesos/blob/1e9e8215/src/slave/containerizer/mesos/isolators/cgroups/constants.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/isolators/cgroups/constants.hpp b/src/slave/containerizer/mesos/isolators/cgroups/constants.hpp
index 4af8f7f..8c73efd 100644
--- a/src/slave/containerizer/mesos/isolators/cgroups/constants.hpp
+++ b/src/slave/containerizer/mesos/isolators/cgroups/constants.hpp
@@ -44,6 +44,7 @@ const std::string CGROUP_SUBSYSTEM_CPUACCT_NAME = "cpuacct";
 const std::string CGROUP_SUBSYSTEM_DEVICES_NAME = "devices";
 const std::string CGROUP_SUBSYSTEM_MEMORY_NAME = "memory";
 const std::string CGROUP_SUBSYSTEM_NET_CLS_NAME = "net_cls";
+const std::string CGROUP_SUBSYSTEM_PERF_EVENT_NAME = "perf_event";
 
 } // namespace slave {
 } // namespace internal {

http://git-wip-us.apache.org/repos/asf/mesos/blob/1e9e8215/src/slave/containerizer/mesos/isolators/cgroups/subsystem.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/isolators/cgroups/subsystem.cpp b/src/slave/containerizer/mesos/isolators/cgroups/subsystem.cpp
index c207948..05cb8b8 100644
--- a/src/slave/containerizer/mesos/isolators/cgroups/subsystem.cpp
+++ b/src/slave/containerizer/mesos/isolators/cgroups/subsystem.cpp
@@ -25,6 +25,7 @@
 #include "slave/containerizer/mesos/isolators/cgroups/subsystems/devices.hpp"
 #include "slave/containerizer/mesos/isolators/cgroups/subsystems/memory.hpp"
 #include "slave/containerizer/mesos/isolators/cgroups/subsystems/net_cls.hpp"
+#include "slave/containerizer/mesos/isolators/cgroups/subsystems/perf_event.hpp"
 
 using mesos::slave::ContainerLimitation;
 
@@ -49,6 +50,7 @@ Try<Owned<Subsystem>> Subsystem::create(
     {CGROUP_SUBSYSTEM_DEVICES_NAME, &DevicesSubsystem::create},
     {CGROUP_SUBSYSTEM_MEMORY_NAME, &MemorySubsystem::create},
     {CGROUP_SUBSYSTEM_NET_CLS_NAME, &NetClsSubsystem::create},
+    {CGROUP_SUBSYSTEM_PERF_EVENT_NAME, &PerfEventSubsystem::create},
   };
 
   if (!creators.contains(name)) {

http://git-wip-us.apache.org/repos/asf/mesos/blob/1e9e8215/src/slave/containerizer/mesos/isolators/cgroups/subsystems/perf_event.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/isolators/cgroups/subsystems/perf_event.cpp b/src/slave/containerizer/mesos/isolators/cgroups/subsystems/perf_event.cpp
new file mode 100644
index 0000000..cc39f28
--- /dev/null
+++ b/src/slave/containerizer/mesos/isolators/cgroups/subsystems/perf_event.cpp
@@ -0,0 +1,215 @@
+// 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 <process/defer.hpp>
+#include <process/delay.hpp>
+#include <process/id.hpp>
+#include <process/reap.hpp>
+
+#include <stout/duration.hpp>
+#include <stout/error.hpp>
+
+#include "linux/perf.hpp"
+
+#include "slave/containerizer/mesos/isolators/cgroups/subsystems/perf_event.hpp"
+
+using process::Clock;
+using process::Failure;
+using process::Future;
+using process::Owned;
+using process::PID;
+using process::Time;
+
+using std::set;
+using std::string;
+
+namespace mesos {
+namespace internal {
+namespace slave {
+
+Try<Owned<Subsystem>> PerfEventSubsystem::create(
+    const Flags& flags,
+    const string& hierarchy)
+{
+  if (!perf::supported()) {
+    return Error("Perf is not supported");
+  }
+
+  if (flags.perf_duration > flags.perf_interval) {
+    return Error(
+        "Sampling perf for duration (" + stringify(flags.perf_duration) + ") > "
+        "interval (" + stringify(flags.perf_interval) + ") is not supported.");
+  }
+
+  if (!flags.perf_events.isSome()) {
+    return Error("No perf events specified");
+  }
+
+  set<string> events;
+  foreach (const string& event,
+           strings::tokenize(flags.perf_events.get(), ",")) {
+    events.insert(event);
+  }
+
+  if (!perf::valid(events)) {
+    return Error("Invalid perf events: " + stringify(events));
+  }
+
+  LOG(INFO) << "perf_event subsystem will profile for "
+            << "'" << flags.perf_duration << "' "
+            << "every '" << flags.perf_interval << "' "
+            << "for events: " << stringify(events);
+
+  return Owned<Subsystem>(new PerfEventSubsystem(flags, hierarchy, events));
+}
+
+
+PerfEventSubsystem::PerfEventSubsystem(
+    const Flags& _flags,
+    const string& _hierarchy,
+    const set<string>& _events)
+  : ProcessBase(process::ID::generate("cgroups-perf-event-subsystem")),
+    Subsystem(_flags, _hierarchy),
+    events(_events) {}
+
+
+void PerfEventSubsystem::initialize()
+{
+  // Start sampling.
+  sample();
+}
+
+
+Future<Nothing> PerfEventSubsystem::recover(const ContainerID& containerId)
+{
+  if (infos.contains(containerId)) {
+    return Failure("The subsystem '" + name() + "' has already been recovered");
+  }
+
+  infos.put(containerId, Owned<Info>(new Info));
+
+  return Nothing();
+}
+
+
+Future<Nothing> PerfEventSubsystem::prepare(const ContainerID& containerId)
+{
+  if (infos.contains(containerId)) {
+    return Failure("The subsystem '" + name() + "' has already been prepared");
+  }
+
+  infos.put(containerId, Owned<Info>(new Info));
+
+  return Nothing();
+}
+
+
+Future<ResourceStatistics> PerfEventSubsystem::usage(
+    const ContainerID& containerId)
+{
+  if (!infos.contains(containerId)) {
+    return Failure(
+        "Failed to get the usage of subsystem '" + name() + "'"
+        ": Unknown container");
+  }
+
+  ResourceStatistics statistics;
+  statistics.mutable_perf()->CopyFrom(infos[containerId]->statistics);
+
+  return statistics;
+}
+
+
+Future<Nothing> PerfEventSubsystem::cleanup(const ContainerID& containerId)
+{
+  if (!infos.contains(containerId)) {
+    VLOG(1) << "Ignoring cleanup subsystem '" << name() << "' "
+            << "request for unknown container " << containerId;
+
+    return Nothing();
+  }
+
+  infos.erase(containerId);
+
+  return Nothing();
+}
+
+
+void PerfEventSubsystem::sample()
+{
+  // Collect a perf sample for all cgroups that are not being
+  // destroyed. Since destroyal is asynchronous, 'perf stat' may
+  // fail if the cgroup is destroyed before running perf.
+  set<string> cgroups;
+
+  foreachkey (const ContainerID& containerId, infos) {
+    cgroups.insert(path::join(flags.cgroups_root, containerId.value()));
+  }
+
+  // The discard timeout includes an allowance of twice the
+  // reaper interval to ensure we see the perf process exit.
+  Duration timeout = flags.perf_duration + process::MAX_REAP_INTERVAL() * 2;
+  Duration duration = flags.perf_duration;
+
+  perf::sample(events, cgroups, flags.perf_duration)
+    .after(timeout, [=](Future<hashmap<string, PerfStatistics>> future) {
+      LOG(ERROR) << "Perf sample of " << stringify(duration)
+                 << " failed to complete within " << stringify(timeout)
+                 << "; sampling will be halted";
+
+      future.discard();
+
+      return future;
+    })
+    .onAny(defer(PID<PerfEventSubsystem>(this),
+                 &PerfEventSubsystem::_sample,
+                 Clock::now() + flags.perf_interval,
+                 lambda::_1));
+}
+
+
+void PerfEventSubsystem::_sample(
+    const Time& next,
+    const Future<hashmap<string, PerfStatistics>>& statistics)
+{
+  if (!statistics.isReady()) {
+    // In case the failure is transient or this is due to a timeout,
+    // we continue sampling. Note that since sampling is done on an
+    // interval, it should be ok if this is a non-transient failure.
+    LOG(ERROR) << "Failed to get the perf sample: "
+               << (statistics.isFailed() ? statistics.failure() : "timeout");
+  } else {
+    // Store the latest statistics, note that cgroups added in the
+    // interim will be picked up by the next sample.
+    foreachpair (const ContainerID& containerId,
+                 const Owned<Info>& info,
+                 infos) {
+      string cgroup = path::join(flags.cgroups_root, containerId.value());
+      if (statistics->contains(cgroup)) {
+        info->statistics = statistics->get(cgroup).get();
+      }
+    }
+  }
+
+  // Schedule sample for the next time.
+  delay(next - Clock::now(),
+        PID<PerfEventSubsystem>(this),
+        &PerfEventSubsystem::sample);
+}
+
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/1e9e8215/src/slave/containerizer/mesos/isolators/cgroups/subsystems/perf_event.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/isolators/cgroups/subsystems/perf_event.hpp b/src/slave/containerizer/mesos/isolators/cgroups/subsystems/perf_event.hpp
new file mode 100644
index 0000000..5a0adfd
--- /dev/null
+++ b/src/slave/containerizer/mesos/isolators/cgroups/subsystems/perf_event.hpp
@@ -0,0 +1,108 @@
+// 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 __CGROUPS_ISOLATOR_SUBSYSTEMS_PERF_EVENT_HPP__
+#define __CGROUPS_ISOLATOR_SUBSYSTEMS_PERF_EVENT_HPP__
+
+#include <set>
+#include <string>
+
+#include <mesos/resources.hpp>
+
+#include <process/clock.hpp>
+#include <process/future.hpp>
+#include <process/owned.hpp>
+#include <process/time.hpp>
+
+#include <stout/hashmap.hpp>
+
+#include "slave/flags.hpp"
+
+#include "slave/containerizer/mesos/isolators/cgroups/constants.hpp"
+#include "slave/containerizer/mesos/isolators/cgroups/subsystem.hpp"
+
+namespace mesos {
+namespace internal {
+namespace slave {
+
+/**
+ * Represent cgroups perf_event subsystem.
+ */
+class PerfEventSubsystem: public Subsystem
+{
+public:
+  static Try<process::Owned<Subsystem>> create(
+      const Flags& flags,
+      const std::string& hierarchy);
+
+  virtual ~PerfEventSubsystem() {}
+
+  virtual std::string name() const
+  {
+    return CGROUP_SUBSYSTEM_PERF_EVENT_NAME;
+  }
+
+  virtual process::Future<Nothing> prepare(const ContainerID& containerId);
+
+  virtual process::Future<Nothing> recover(const ContainerID& containerId);
+
+  virtual process::Future<ResourceStatistics> usage(
+      const ContainerID& containerId);
+
+  virtual process::Future<Nothing> cleanup(const ContainerID& containerId);
+
+protected:
+  virtual void initialize();
+
+private:
+  PerfEventSubsystem(
+      const Flags& flags,
+      const std::string& hierarchy,
+      const std::set<std::string>& events);
+
+  struct Info
+  {
+    Info()
+    {
+      // Ensure the initial statistics include the required fields.
+      // Note the duration is set to zero to indicate no sampling has
+      // taken place. This empty sample will be returned from usage()
+      // until the first true sample is obtained.
+      statistics.set_timestamp(process::Clock::now().secs());
+      statistics.set_duration(Seconds(0).secs());
+    }
+
+    PerfStatistics statistics;
+  };
+
+  void sample();
+
+  void _sample(
+      const process::Time& next,
+      const process::Future<hashmap<std::string, PerfStatistics>>& statistics);
+
+  // Set of events to sample.
+  std::set<std::string> events;
+
+  // Stores cgroups associated information for container.
+  hashmap<ContainerID, process::Owned<Info>> infos;
+};
+
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {
+
+#endif // __CGROUPS_ISOLATOR_SUBSYSTEMS_PERF_EVENT_HPP__