~siborgium/prelockdpp

4d2f50ab4634021e977658342bf918e60fba203c — Sergey Smirnykh 2 years ago 3dde0a0
get_current_set v2 -- get_uniq_id
1 files changed, 110 insertions(+), 89 deletions(-)

M main.cpp
M main.cpp => main.cpp +110 -89
@@ 4,8 4,8 @@
#include <charconv>
#include <string>
#include <string_view>
#include <filesystem>
#include <vector>
#include <unordered_set>

#include <cassert>
#include <cstring>


@@ 33,11 33,14 @@ template <typename F> defer_impl(F&&) -> defer_impl<F>;
#define CONCAT(x, y) x##y
#define DEFER        defer_impl CONCAT(_defer_, __COUNTER__) = [&]()

namespace fs = std::filesystem;

constexpr std::size_t MiB = 1024 * 1024;
constexpr std::size_t TIMEOUT_MSEC = 60'000;

constexpr std::string_view proc_path{ "/proc/" };
constexpr std::string_view stat_path{ "/stat" };
constexpr std::string_view cgroup_path{ "/cgroup" };
constexpr std::string_view maps_path{ "/maps" };

enum class SourceId: std::uint32_t {
    Timer,
    Signal


@@ 68,9 71,12 @@ struct process_lookup_error: public std::runtime_error {
};

struct config {
#if 0 // unused for now
    fs::path dump_path{ "/var/lib/prelockdpp/dump.json" };
#endif
    time_t   interval_sec{ 5 };
    bool     lock_only_critical{ 0 };
    bool     lock_only_critical{ true };
    bool     check_cgroup{ true };
};

struct sc_info {


@@ 130,32 136,24 @@ struct timer_fd: public base_fd {
};

struct context {
    std::chrono::steady_clock::time_point start_time;
    int                                   self_pid;
    int                                   cgroup_v2_index{ 0 };
    sc_info                               info;
    config&                               cfg;
    timespec start_time;
    int      self_pid;
    int      cgroup_v2_index{ 0 };
    sc_info  info;
    config&  cfg;

    std::vector<int>                      pids;
    std::string                           stat_buffer;

    context(config& cfg): cfg{ cfg } {
        start_time = std::chrono::steady_clock::now();
        self_pid = getpid();
    constexpr static auto path_buffer_size
        = proc_path.size()
        + std::numeric_limits<int>::digits10
        + std::max({ stat_path.size(), cgroup_path.size(), maps_path.size() })
        + 2;
    char path_buf[path_buffer_size];
    char* const pid_begin;
    char*       pid_end;

        // get cgroup v2 index
        {
            std::ifstream file{ "/proc/self/cgroup" };
            std::string buf;
            while (std::getline(file, buf)) {
                std::string_view line{ buf };
                if (line.starts_with("0::")) {
                    break;
                }
                ++cgroup_v2_index;
            }
        }
    }
    std::string                     stat_buffer;
    std::string                     cgroup_buffer;
    std::unordered_set<std::string> name_set;

    void mlockall() {
        if (auto err = ::mlockall(MCL_FUTURE)) {


@@ 216,35 214,17 @@ struct context {
        }
        throw std::logic_error{ "read(/proc/uptime) was empty" };
    }
    auto const& get_pid_list() {
        pids.clear();

        DIR* dir = opendir("/proc");
        if (!dir) {
            throw errno_exception("opendir(/proc) failed", errno);
        }
        DEFER{ closedir(dir); };
        errno = 0;
        auto* pid = &pids.emplace_back(self_pid);
        while (dirent* entry = readdir(dir)) {
            auto end = entry->d_name + strlen(entry->d_name);
            auto [_, ec] = std::from_chars(entry->d_name, end, *pid);
            if (ec == std::errc() && _ == end && *pid != self_pid) {
                pid = &pids.emplace_back(self_pid);
            }
        }
        if (*pid == self_pid) {
            pids.pop_back();
        }
        if (errno) {
            throw errno_exception("readdir(proc) failed", errno);
        }
        return pids;
    constexpr auto fmt_proc_pid_file(std::string_view file) {
        auto end = std::copy(file.begin(), file.end(), pid_end);
        assert(end < std::end(path_buf));
        *end = '\0';
        return end;
    }
    void format_path_buf(auto & buf) {

    auto match_cgroup(std::string_view cgroup) {
        std::cout << "match_cgroup(" << cgroup << "): unimplemented\n";
        return true;
    }
    auto get_uniq_id(const char* stat_path) {
    auto with_uniq_id(const char* stat_path, auto && callback) {
        using namespace std::string_view_literals;
        try {
            // TODO: can we do reading stat cheaper?


@@ 254,19 234,56 @@ struct context {
            std::string_view stat_view{ stat_buffer };
            if (stat_view.ends_with(" 0 0 0")) {
                // ignore kthreads
                return 0;
                return;
            }
            auto lparen_idx = stat_view.find('(');
            auto rparen_idx = stat_view.rfind(')');
            assert(lparen_idx != stat_view.npos && rparen_idx != stat_view.npos);
            auto comm = stat_view.substr(lparen_idx + 1, rparen_idx - lparen_idx - 1);
            std::cout << comm;

            return 0;
        } catch (const file_not_found_error&) {
            return -1;
        } catch (const process_lookup_error&) {
            return -1;
            if (cfg.lock_only_critical) {
                // FIXME: fsck unordered_set
                //        imagine not being able to lookup strings by string_view key
                //        in 2022
                bool lock_ok = name_set.contains(std::string(comm));
                if (!lock_ok && cfg.check_cgroup) {
                    fmt_proc_pid_file(cgroup_path);

                    std::ifstream stream{ path_buf };
                    std::size_t index{ 0 };
                    while (std::getline(stream, cgroup_buffer)) {
                        if (index == cgroup_v2_index) {
                            std::string_view cgroup{
                                cgroup_buffer.c_str() + 3,
                                cgroup_buffer.c_str() + cgroup_buffer.size()
                            };
                            std::cout << '\n';
                            lock_ok = match_cgroup(cgroup);
                            break;
                        }
                        ++index;
                    }
                }
                if (!lock_ok) {
                    return;
                }
            }

            auto start_time = [=]() {
                auto begin = stat_view.find(' ', rparen_idx) + 1;
                for (std::size_t i = 0; i < 19; ++i) {
                    begin = stat_view.find(' ', begin) + 1;
                }
                auto end = stat_view.find(' ', begin);
                return stat_view.substr(begin, end - begin);
            }();

            callback(start_time, stat_view.substr(0, rparen_idx));
        } catch (const std::system_error& e) {
            auto code = e.code();
            if (code.value() != ENOENT) {
                throw;
            }
        }
    }
    auto get_current_set() {


@@ 276,29 293,12 @@ struct context {
        auto p0 = get_clock_time(CLOCK_PROCESS_CPUTIME_ID);
        auto uptime = get_uptime();

        // FIXME: move to separate function
        constexpr auto proc_path = "/proc/"sv;
        constexpr auto stat_path = "/stat"sv;

        constexpr auto buf_size
        = proc_path.size()
        + std::numeric_limits<int>::digits10
        + stat_path.size()
        + 2;

        char path_buf[buf_size];
        auto const pid_begin = std::copy(
            proc_path.begin(),
            proc_path.end(),
            std::begin(path_buf)
        );


        DIR* proc_dir = opendir("/proc");
        if (!proc_dir) {
            throw errno_exception("opendir(/proc) failed", errno);
        }
        DEFER{ closedir(proc_dir); };

        errno = 0;
        while (dirent* entry = readdir(proc_dir)) {
            DEFER{ errno = 0; };


@@ 313,22 313,43 @@ struct context {
                continue;
            }

            auto const pid_end = std::copy(entry->d_name, name_end, pid_begin);
            auto end = std::copy(stat_path.begin(), stat_path.end(), pid_end);
            assert(end < std::end(path_buf));
            *end = '\0';
            ++end;
            pid_end = std::copy(entry->d_name, name_end, pid_begin);
            fmt_proc_pid_file(stat_path);

            std::cout << pid << ' ';
            get_uniq_id(path_buf);
            std::cout << '\n';
            std::cout << pid << '\n';
            with_uniq_id(path_buf, [&](auto start_time, auto prefix){
                std::cout << "start_time: " << start_time << '\n';
                std::cout << "prefix:     " << prefix << '\n';
            });
        }
        if (errno) {
            throw errno_exception("readdir(proc) failed", errno);
        }

        std::cout << "Uptime: " << uptime.tv_sec << '.' << uptime.tv_nsec << '\n';
        std::cout << "Uptime:         " << uptime.tv_sec << '.' << uptime.tv_nsec << '\n';
        std::cout << "Monotonic time: " << m0.tv_sec << '.' << m0.tv_nsec << '\n';
        std::cout << "Proc CPU  time: " << p0.tv_sec << '.' << p0.tv_nsec << '\n';
    }

    context(config& cfg):
        cfg{ cfg },
        pid_begin{ std::copy(proc_path.begin(), proc_path.end(), path_buf) }
    {
        start_time = get_clock_time(CLOCK_MONOTONIC);
        self_pid = getpid();

        // get cgroup v2 index
        {
            std::ifstream file{ "/proc/self/cgroup" };
            std::string buf;
            while (std::getline(file, buf)) {
                std::string_view line{ buf };
                if (line.starts_with("0::")) {
                    break;
                }
                ++cgroup_v2_index;
            }
        }
    }
};