From c0fc0a1cadf6cb8c5083232384aea5c2db4e93d5 Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Tue, 8 Oct 2019 17:01:48 -0700 Subject: [PATCH] ircd::prof: Add interface to pressure stall information on linux platforms. --- include/ircd/prof/prof.h | 1 + include/ircd/prof/psi.h | 52 ++++++++++++++++ ircd/prof_linux.cc | 125 +++++++++++++++++++++++++++++++++++++++ modules/console.cc | 58 ++++++++++++++++++ 4 files changed, 236 insertions(+) create mode 100644 include/ircd/prof/psi.h diff --git a/include/ircd/prof/prof.h b/include/ircd/prof/prof.h index a1c577460..477cea507 100644 --- a/include/ircd/prof/prof.h +++ b/include/ircd/prof/prof.h @@ -41,6 +41,7 @@ namespace ircd::prof #include "resource.h" #include "times.h" #include "system.h" +#include "psi.h" // Exports to ircd:: namespace ircd diff --git a/include/ircd/prof/psi.h b/include/ircd/prof/psi.h new file mode 100644 index 000000000..5325f22a1 --- /dev/null +++ b/include/ircd/prof/psi.h @@ -0,0 +1,52 @@ +// Matrix Construct +// +// Copyright (C) Matrix Construct Developers, Authors & Contributors +// Copyright (C) 2016-2019 Jason Volk +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice is present in all copies. The +// full license for this software is available in the LICENSE file. + +#pragma once +#define HAVE_IRCD_PROF_PSI_H + +namespace ircd::prof::psi +{ + struct file; + struct metric; + struct refresh; + + bool refresh(file &) noexcept; + + extern const bool supported; + extern file cpu, mem, io; +} + +struct ircd::prof::psi::metric +{ + struct avg + { + seconds window; + float pct; + }; + + std::array avg; + microseconds stall; +}; + +struct ircd::prof::psi::file +{ + string_view name; + system_point sampled; + metric some, full; +}; + +#ifndef __linux__ +inline bool +ircd::prof::psi::refresh(metric &) +noexcept +{ + return false; // unsupported platform +} +#endif diff --git a/ircd/prof_linux.cc b/ircd/prof_linux.cc index 05d193e0c..d66c889d5 100644 --- a/ircd/prof_linux.cc +++ b/ircd/prof_linux.cc @@ -154,6 +154,131 @@ catch(const std::exception &e) return nullptr; } +/////////////////////////////////////////////////////////////////////////////// +// +// prof/psi.h +// + +decltype(ircd::prof::psi::supported) +ircd::prof::psi::supported +{ + info::kernel_version[0] > 4 || + (info::kernel_version[0] >= 4 && info::kernel_version[1] >= 20) +}; + +decltype(ircd::prof::psi::cpu) +ircd::prof::psi::cpu +{ + "cpu" +}; + +decltype(ircd::prof::psi::mem) +ircd::prof::psi::mem +{ + "memory" +}; + +decltype(ircd::prof::psi::io) +ircd::prof::psi::io +{ + "io" +}; + +// +// prof::psi::metric::refresh +// + +bool +ircd::prof::psi::refresh(file &file) +noexcept try +{ + if(!supported) + return false; + + if(unlikely(!file.name)) + return false; + + thread_local unique_mutable_buffer path_buf + { + fs::PATH_MAX_LEN + }; + + const auto &path + { + fs::path(path_buf, vector_view + { + "/proc/pressure"_sv, file.name + }) + }; + + // Copy value into userspace + char buf[256]; + fs::read_opts opts; + opts.aio = false; // can't read /proc through AIO + opts.all = false; // don't need posix read-loop; make one read(2) only. + const auto &result + { + fs::read(path, buf, opts) + }; + + tokens(result, '\n', [&file] // Read each line + (const string_view &line) + { + const auto &[type, vals] + { + split(line, ' ') + }; + + // The first token tells us what the metric is; we have allocated + // results for the following + if(type != "full" && type != "some") + return; + + auto &metric + { + type == "full"? + file.full: + file.some + }; + + size_t i(0); + tokens(vals, ' ', [&metric, &i] // Read each key=value pair + (const string_view &key_val) + { + const auto &[key, val] + { + split(key_val, '=') + }; + + if(key == "total") + { + metric.stall = lex_cast(val); + return; + } + else if(startswith(key, "avg") && i < metric.avg.size()) + { + metric.avg.at(i).window = lex_cast(lstrip(key, "avg")); + metric.avg.at(i).pct = lex_cast(val); + ++i; + } + }); + }); + + file.sampled = ircd::now(); + return true; +} +catch(const std::exception &e) +{ + log::error + { + "Failed to refresh pressure stall information '%s' :%s", + file.name, + e.what(), + }; + + return false; +} + /////////////////////////////////////////////////////////////////////////////// // // prof/instructions.h diff --git a/modules/console.cc b/modules/console.cc index 8e7777289..b0d81af65 100644 --- a/modules/console.cc +++ b/modules/console.cc @@ -1194,6 +1194,64 @@ console_cmd__vg(opt &out, const string_view &line) // prof // +bool +console_cmd__prof__psi(opt &out, const string_view &line) +{ + const auto show_file{[&out] + (const string_view &name, prof::psi::file &file) + { + if(!refresh(file)) + return; + + char pbuf[48]; + out + << std::left << name + << " some stall " + << pretty(pbuf, file.some.stall) + << " (" + << file.some.stall.count() + << ')' + << std::endl + ; + for(size_t i(0); i < file.some.avg.size(); i++) + out + << std::left << name + << " some " + << std::right << std::setw(3) << file.some.avg.at(i).window.count() + << "s " + << std::right << std::setw(4) << file.some.avg.at(i).pct << '%' + << std::endl + ; + + out + << std::endl + << std::left << name + << " full stall " + << pretty(pbuf, file.full.stall) + << " (" + << file.full.stall.count() + << ')' + << std::endl + ; + for(size_t i(0); i < file.full.avg.size(); i++) + out + << std::left << name + << " full " + << std::right << std::setw(3) << file.full.avg.at(i).window.count() + << "s " + << std::right << std::setw(4) << file.full.avg.at(i).pct << '%' + << std::endl + ; + + out << std::endl; + }}; + + show_file("cpu", prof::psi::cpu); + show_file("mem", prof::psi::mem); + show_file("io ", prof::psi::io); + return true; +} + bool console_cmd__prof__vg__start(opt &out, const string_view &line) {