[gnome-system-monitor] Cache cgroup expansive parsing by file and line which makes it even faster than the original C code.



commit 0337fe894ddac54e0dd814cb548a8159163fd895
Author: Benoît Dejean <bdejean gmail com>
Date:   Fri Oct 28 10:08:57 2016 +0200

    Cache cgroup expansive parsing by file and line which makes it even faster than the
    original C code.
    Parse all cgroups because previously systemd cgroups were ignored.
    Condensed output for cgroups with multiple categories.

 src/cgroups.cpp |  115 ++++++++++++++++++++++++++++++++++++++++++-------------
 1 files changed, 88 insertions(+), 27 deletions(-)
---
diff --git a/src/cgroups.cpp b/src/cgroups.cpp
index b75b9fb..42d1eef 100644
--- a/src/cgroups.cpp
+++ b/src/cgroups.cpp
@@ -2,6 +2,10 @@
 #include "cgroups.h"
 #include "util.h"
 
+#include <map>
+#include <unordered_map>
+#include <utility>
+
 bool
 cgroups_enabled(void)
 {
@@ -16,43 +20,100 @@ cgroups_enabled(void)
     return has_cgroups;
 }
 
-static std::pair<Glib::ustring, Glib::ustring>
-parse_cgroup_line(const Glib::ustring& line) {
-    auto split = std::vector<Glib::ustring>(Glib::Regex::split_simple(":", line));
-    if (split.size() < 3) { return std::make_pair("", ""); }
-    auto cgroups = split.at(2);
-    if (cgroups.empty() || cgroups == "/") { return std::make_pair("", ""); }
-    auto category = split.at(1);
-    if (category.find("name=") != category.npos) { return std::make_pair("", ""); }
 
-    return std::make_pair(category, cgroups);
+
+static const std::pair<std::string, std::string> &
+parse_cgroup_line(const std::string& line) {
+
+    static std::unordered_map<std::string, std::pair<std::string, std::string>> line_cache;
+
+    auto it = line_cache.insert({line, {"", ""} });
+    if (it.second) { // inserted new
+        std::string::size_type cat_start, name_start;
+
+        if ((cat_start = line.find(':')) != std::string::npos
+            && (name_start = line.find(':', cat_start + 1)) != std::string::npos) {
+
+            // printf("%s %lu %lu\n", line.c_str(), cat_start, name_start);
+            auto cat = line.substr(cat_start + 1, name_start - cat_start - 1);
+            auto name = line.substr(name_start + 1);
+
+            // strip the name= prefix
+            if (cat.find("name=") == 0) {
+                cat.erase(0, 5);
+            }
+
+            if (!name.empty() && name != "/") {
+                it.first->second = {name, cat};
+            }
+        }
+    }
+
+    return it.first->second;
 }
 
-static Glib::ustring
-get_process_cgroup_string(unsigned int pid) {
-    if (not cgroups_enabled())
-        return "";
+
+static const std::string&
+get_process_cgroup_string(pid_t pid) {
+
+    static std::unordered_map<std::string, std::string> file_cache{ {"", ""} };
 
     /* read out of /proc/pid/cgroup */
-    auto path = Glib::ustring::compose("/proc/%1/cgroup", pid);
-    Glib::ustring text;
-    try { text = Glib::file_get_contents(path); } catch (...) { return ""; }
-    auto lines = std::vector<Glib::ustring>(Glib::Regex::split_simple("\n", text));
-
-    Glib::ustring cgroup_string;
-    for (auto& line : lines) {
-        auto data = parse_cgroup_line(line);
-        if (data.first.empty() || data.second.empty()) { continue; }
-        if (!cgroup_string.empty()) { cgroup_string += ", "; }
-        cgroup_string += Glib::ustring::compose("%1 (%2)", data.second, data.first);
+    auto path = "/proc/" + std::to_string(pid) + "/cgroup";
+    std::string text;
+    try { text = Glib::file_get_contents(path); } catch (...) { return file_cache[""]; }
+
+    auto it = file_cache.insert({ text, "" });
+
+    if (it.second) { // inserted new
+
+        // name -> [cat...], sorted by name;
+        std::map<std::string, std::vector<std::string>> names;
+
+        std::string::size_type last = 0, eol;
+
+        // for each line in the file
+        while ((eol = text.find('\n', last)) != std::string::npos) {
+            auto line = text.substr(last, eol - last);
+            last = eol + 1;
+
+            const auto& p = parse_cgroup_line(line);
+            if (!p.first.empty()) {
+                names[p.first].push_back(p.second);
+            }
+        }
+
+
+        // name (cat1, cat2), ...
+        // sorted by name, categories
+        std::string groups;
+
+        for (auto& i : names) {
+            std::string cats;
+            std::sort(begin(i.second), end(i.second));
+
+            for (const auto & cat : i.second) {
+                if (!cats.empty()) { cats += ", "; }
+                cats += cat;
+            }
+
+            if (!groups.empty()) { groups += ", "; }
+            groups += i.first + " (" + cats + ')';
+        }
+
+        it.first->second = std::move(groups);
     }
 
-    return cgroup_string;
+    return it.first->second;
 }
 
+
 void get_process_cgroup_info(ProcInfo& info) {
-    auto cgroup_string = get_process_cgroup_string(info.pid);
+    if (not cgroups_enabled())
+        return;
 
+    const auto& cgroup_string = get_process_cgroup_string(info.pid);
     g_free(info.cgroup_name);
-    info.cgroup_name = cgroup_string.empty() ? nullptr : g_strdup(cgroup_string.c_str());
+    info.cgroup_name = g_strdup(cgroup_string.c_str());
 }
+


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]