Add memory limit enforcement to aos_starter

This gives us a hammer to figure out who is running us out of RAM by
allocating it.  That'll let us kill the culprit rather than letting the
OOM killer pick for us.

Change-Id: Id270b878e908f0bf296ed5fc176e327e9b6c2d5a
Signed-off-by: Austin Schuh <austin.schuh@bluerivertech.com>
diff --git a/aos/starter/subprocess.h b/aos/starter/subprocess.h
index a4d7cbb..bacc574 100644
--- a/aos/starter/subprocess.h
+++ b/aos/starter/subprocess.h
@@ -35,6 +35,22 @@
   DISALLOW_COPY_AND_ASSIGN(SignalListener);
 };
 
+// Class to use the V1 cgroup API to limit memory usage.
+class MemoryCGroup {
+ public:
+  MemoryCGroup(std::string_view name);
+  ~MemoryCGroup();
+
+  // Adds a thread ID to be managed by the cgroup.
+  void AddTid(pid_t pid = 0);
+
+  // Sets the provided limit to the provided value.
+  void SetLimit(std::string_view limit_name, uint64_t limit_value);
+
+ private:
+  std::string cgroup_;
+};
+
 // Manages a running process, allowing starting and stopping, and restarting
 // automatically.
 class Application {
@@ -80,6 +96,14 @@
   const std::string &GetStderr();
   std::optional<int> exit_code() const { return exit_code_; }
 
+  // Sets the memory limit for the application to the provided limit.
+  void SetMemoryLimit(size_t limit) {
+    if (!memory_cgroup_) {
+      memory_cgroup_ = std::make_unique<MemoryCGroup>(name_);
+    }
+    memory_cgroup_->SetLimit("memory.limit_in_bytes", limit);
+  }
+
  private:
   typedef aos::util::ScopedPipe::PipePair PipePair;
   void set_args(
@@ -144,6 +168,8 @@
 
   std::function<void()> on_change_;
 
+  std::unique_ptr<MemoryCGroup> memory_cgroup_;
+
   DISALLOW_COPY_AND_ASSIGN(Application);
 };