diff --git a/module/common/include/oops/str.h b/module/common/include/oops/str.h index e616f23..de42487 100644 --- a/module/common/include/oops/str.h +++ b/module/common/include/oops/str.h @@ -174,6 +174,7 @@ constexpr std::string_view Strip(std::string_view s, std::string_view chars = SP std::string Elide(std::string_view s, std::size_t n); +// 后续替换成lexcial_cast template ::std::string ToStr(const T &t) { ::std::ostringstream oss; diff --git a/module/profiling/include/oops/system_info.h b/module/profiling/include/oops/system_info.h index 055a88f..23ba21c 100644 --- a/module/profiling/include/oops/system_info.h +++ b/module/profiling/include/oops/system_info.h @@ -15,24 +15,12 @@ namespace oops { namespace proc { namespace status { +// clang-format off enum class Field : uint8_t { - VM_PEAK, - VM_SIZE, - VM_LCK, - VM_PIN, - VM_HWM, - VM_RSS, - RSS_ANON, - RSS_FILE, - RSS_SHMEM, - VM_DATA, - VM_STK, - VM_EXE, - VM_LIB, - VM_PTE, - VM_SWAP, - COUNT + VM_PEAK, VM_SIZE, VM_LCK, VM_PIN, VM_HWM, VM_RSS, RSS_ANON, RSS_FILE, RSS_SHMEM, VM_DATA, + VM_STK, VM_EXE, VM_LIB, VM_PTE, VM_SWAP, COUNT }; +// clang-format on using FieldMask = EnumBitset; using oops::operator|; // 支持Field和FieldMask或运算ADL @@ -61,8 +49,8 @@ struct Info { [[nodiscard]] Info Get(pid_t pid, const FieldMask &field_mask); std::ostream &operator<<(std::ostream &out, const Info &info); } // namespace status - -// vma数据结构,maps/smaps/smaps共用 +namespace maps { +// vma数据结构,maps, smaps, smaps_rollup共用 struct Vma { std::size_t Size() const { return static_cast(address.start - address.end); } std::uint32_t MajorDev() const; @@ -86,37 +74,28 @@ struct Vma { std::string pathname{}; }; +struct Info { + std::vector vma_table; +}; + +[[nodiscard]] Info Get(); +[[nodiscard]] Info Get(pid_t pid); +std::ostream &operator<<(std::ostream &out, const Info &info); +} // namespace maps namespace smaps_rollup { // Since Linux +// clang-format off enum class Field : uint8_t { - VMA, - RSS, - PSS, - PSS_DIRTY, - PSS_ANON, - PSS_FILE, - PSS_SHMEM, - SHARED_CLEAN, - SHARED_DIRTY, - PRIVATE_CLEAN, - PRIVATE_DIRTY, - REFERENCED, - ANONYMOUS, - KSM, - LAZY_FREE, - ANON_HUGE_PAGES, - SHMEM_PMD_MAPPED, - FILE_PMD_MAPPED, - SHARED_HUGETLB, - PRIVATE_HUGETLB, - SWAP, - SWAP_PSS, - LOCKED, - COUNT + VMA, RSS, PSS, PSS_DIRTY, PSS_ANON, PSS_FILE, + PSS_SHMEM, SHARED_CLEAN, SHARED_DIRTY, PRIVATE_CLEAN, PRIVATE_DIRTY, REFERENCED, + ANONYMOUS, KSM, LAZY_FREE, ANON_HUGE_PAGES, SHMEM_PMD_MAPPED, FILE_PMD_MAPPED, + SHARED_HUGETLB, PRIVATE_HUGETLB, SWAP, SWAP_PSS, LOCKED, COUNT }; +// clang-format on using FieldMask = EnumBitset; using oops::operator|; // 支持Field和FieldMask或运算ADL +using Vma = maps::Vma; struct Info { Vma vma; // 核心内存统计 @@ -188,17 +167,12 @@ struct Info { } // namespace proc namespace lscpu { +// clang-format off enum class Field { - ARCHITECTURE, - CPUS, - THREADS_PER_CORE, - CORES_PER_SOCKET, - SOCKETS, - NUMA_NODES, - MODEL_NAME, - CPU_MHZ, - COUNT + ARCHITECTURE, CPUS, THREADS_PER_CORE, CORES_PER_SOCKET, SOCKETS, NUMA_NODES, + MODEL_NAME, CPU_MHZ, COUNT }; +// clang-format on using FieldMask = EnumBitset; using oops::operator|; // 支持Field和FieldMask或运算ADL diff --git a/module/profiling/src/system_info.cpp b/module/profiling/src/system_info.cpp index 62ed50b..4c61697 100644 --- a/module/profiling/src/system_info.cpp +++ b/module/profiling/src/system_info.cpp @@ -43,7 +43,6 @@ std::string GetCmdOutput(const std::string &cmd) { class IStreamBacktraceGuard { public: explicit IStreamBacktraceGuard(std::istream &is) : is_{is}, pos_{is.tellg()} {} - IStreamBacktraceGuard(const IStreamBacktraceGuard &) = delete; IStreamBacktraceGuard &operator=(const IStreamBacktraceGuard &) = delete; @@ -61,23 +60,33 @@ class IStreamBacktraceGuard { std::streampos pos_; }; -::std::string ToStr(const KiBs &t) { return std::to_string(t.Count()); } +template +bool ParseField(std::string_view s, T &t, std::string_view ctx = "{}") { + auto scan_res{scn::scan(s, ctx)}; + if (scan_res) { + t = scan_res->value(); + } + return bool{scan_res}; +} -template <> -KiBs FromStr(const ::std::string_view sv) { - ::std::istringstream iss{::std::string{sv}}; // TODO(resserops): 优化自定义stream - std::size_t t{}; - iss >> t; - return KiBs{t}; +template +bool ParseField(std::string_view s, Storage &storage, std::string_view ctx = "{}") { + R r{}; + auto res{ParseField(s, r, ctx)}; + storage = Storage{r}; + return res; } template -bool FromSv(std::string_view sv, T &value) { - value = FromStr(sv); - return true; +std::string FormatField(const T &t, std::string_view ctx = "{}") { + return fmt::format(ctx, t); +} + +template +std::string FormatField(const Storage &storage, std::string_view ctx = "{}") { + return FormatField(storage.Count(), ctx); } -// 新版KeyValueParser template class KeyValueParser { template @@ -97,8 +106,8 @@ class KeyValueParser { MemberPtrVar member_ptr_var; std::string suffix{}; // 后续如果没有特殊规则的解析,移除注册固化函数,或者使用fmt格式化字符串 - bool (*parse)(std::string_view, MemberPtrVar, Struct &){ParseField}; - std::string (*format)(MemberPtrVar, const Struct &){FormatField}; + bool (*parse)(std::string_view, MemberPtrVar, Struct &){ParseMemberPtrVar}; + std::string (*format)(MemberPtrVar, const Struct &){FormatMemberPtrVar}; }; explicit KeyValueParser(std::initializer_list entries) : field_table_{entries} {} @@ -109,18 +118,18 @@ class KeyValueParser { KeyValueParser(std::initializer_list entries, std::string delim, std::function stop) : field_table_{entries}, delim_{std::move(delim)}, stop_{std::move(stop)} {} - static bool ParseField(std::string_view value, MemberPtrVar member_ptr_var, Struct &obj) { - auto f = [value, &obj](auto member_ptr) { + static bool ParseMemberPtrVar(std::string_view s, MemberPtrVar member_ptr_var, Struct &obj) { + auto f = [s, &obj](auto member_ptr) { auto &member{obj.*member_ptr}; - return FromSv(value, member); + return ParseField(s, member, "{}"); }; return std::visit(f, member_ptr_var); } - static std::string FormatField(MemberPtrVar member_ptr_var, const Struct &obj) { + static std::string FormatMemberPtrVar(MemberPtrVar member_ptr_var, const Struct &obj) { auto f = [&obj](auto member_ptr) -> std::string { auto &member{obj.*member_ptr}; - return ToStr(member); + return FormatField(member, "{}"); }; return std::visit(f, member_ptr_var); } @@ -136,7 +145,7 @@ class KeyValueParser { if (stop_(line)) { break; } - guard.Commit(); // 本行已经确定是KeyValuePair,提交本行 + guard.Commit(); // 本行已经确定是kv-pair,提交本行 if (pos == line.npos) { continue; @@ -196,7 +205,7 @@ class KeyValueParser { namespace proc { namespace status { -KeyValueParser> parser{ +KeyValueParser> kvparser{ {Field::VM_PEAK, "VmPeak", &Info::vm_peak}, {Field::VM_SIZE, "VmSize", &Info::vm_size}, {Field::VM_LCK, "VmLck", &Info::vm_lck}, {Field::VM_PIN, "VmPin", &Info::vm_pin}, {Field::VM_HWM, "VmHWM", &Info::vm_hwm}, {Field::VM_RSS, "VmRSS", &Info::vm_rss}, @@ -208,7 +217,7 @@ KeyValueParser> parser{ Info Get(std::istream &is, const FieldMask &field_mask) { Info info; - info.parsed |= parser.Parse(is, info, field_mask); + info.parsed |= kvparser.Parse(is, info, field_mask); return info; } @@ -221,16 +230,16 @@ Info Get(const FieldMask &field_mask) { } Info Get(pid_t pid, const FieldMask &field_mask) { - std::ifstream ifs(std::string{"/proc/"} + std::to_string(pid) + "/status"); + std::ifstream ifs(fmt::format("/proc/{}/status", pid)); return Get(ifs, field_mask); } -std::ostream &operator<<(std::ostream &out, const Info &info) { - parser.Format(out, info, info.parsed); - return out; +std::ostream &operator<<(std::ostream &os, const Info &info) { + kvparser.Format(os, info, info.parsed); + return os; } } // namespace status - +namespace maps { std::uint32_t Vma::MajorDev() const { return major(dev); } std::uint32_t Vma::MinorDev() const { return minor(dev); } @@ -293,8 +302,33 @@ void FormatVma(std::ostream &os, const Vma &vma) { os << "\n"; } +Info Get(std::istream &is) { + Info info; + while (auto res{maps::ParseVma(is)}) { + info.vma_table.push_back(std::move(res.vma)); + } + return info; +} + +Info Get() { + std::ifstream ifs("/proc/self/maps"); + return Get(ifs); +} + +Info Get(pid_t pid) { + std::ifstream ifs(fmt::format("/proc/{}/maps", pid)); + return Get(ifs); +} + +std::ostream &operator<<(std::ostream &os, const Info &info) { + for (const auto &vma : info.vma_table) { + FormatVma(os, vma); + } + return os; +} +} // namespace maps namespace smaps_rollup { -KeyValueParser>> parser{ +KeyValueParser>> kvparser{ {Field::RSS, "Rss", &Info::rss}, {Field::PSS, "Pss", &Info::pss}, {Field::PSS_DIRTY, "Pss_Dirty", &Info::pss_dirty}, @@ -321,7 +355,7 @@ KeyValueParser>> parser{ Info Get(std::istream &is, const FieldMask &field_mask) { Info info; if (field_mask.Test(Field::VMA)) { - auto res{ParseVma(is)}; + auto res{maps::ParseVma(is)}; if (res) { info.vma = std::move(res.vma); info.parsed.Set(Field::VMA); @@ -330,7 +364,7 @@ Info Get(std::istream &is, const FieldMask &field_mask) { // 跳过VMA行 is.ignore(std::numeric_limits::max(), '\n'); } - info.parsed |= parser.Parse(is, info, field_mask); + info.parsed |= kvparser.Parse(is, info, field_mask); return info; } @@ -343,15 +377,15 @@ Info Get(const FieldMask &field_mask) { } Info Get(pid_t pid, const FieldMask &field_mask) { - std::ifstream ifs(std::string{"/proc/"} + std::to_string(pid) + "/smaps_rollup"); + std::ifstream ifs(fmt::format("/proc/{}/smaps_rollup", pid)); return Get(ifs, field_mask); } std::ostream &operator<<(std::ostream &os, const Info &info) { if (info.parsed.Test(Field::VMA)) { - FormatVma(os, info.vma); + maps::FormatVma(os, info.vma); } - parser.Format(os, info, info.parsed); + kvparser.Format(os, info, info.parsed); return os; } } // namespace smaps_rollup @@ -359,9 +393,8 @@ namespace numa_maps { // TODO(resserops): 补充实现 } // namespace numa_maps } // namespace proc - namespace lscpu { -KeyValueParser> parser{ +KeyValueParser> kvparser{ {Field::ARCHITECTURE, "Architecture", &Info::architecture}, {Field::CPUS, "CPU(s)", &Info::cpus}, {Field::THREADS_PER_CORE, "Thread(s) per core", &Info::threads_per_core}, @@ -375,13 +408,13 @@ Info Get() { return Get(~FieldMask{}); } Info Get(const FieldMask &field_mask) { Info info; std::istringstream iss{GetCmdOutput("lscpu")}; - info.parsed |= parser.Parse(iss, info, field_mask); + info.parsed |= kvparser.Parse(iss, info, field_mask); return info; } -std::ostream &operator<<(std::ostream &out, const Info &info) { - parser.Format(out, info, info.parsed); - return out; +std::ostream &operator<<(std::ostream &os, const Info &info) { + kvparser.Format(os, info, info.parsed); + return os; } } // namespace lscpu } // namespace oops diff --git a/module/profiling/test/test_system_info.cpp b/module/profiling/test/test_system_info.cpp index d1e145e..09a5a65 100644 --- a/module/profiling/test/test_system_info.cpp +++ b/module/profiling/test/test_system_info.cpp @@ -88,6 +88,11 @@ TEST(ProfilingSystemInfo, Lscpu) { std::cout << lscpu::Get(); } +TEST(ProfilingSystemInfo, Smaps) { + using namespace oops; + std::cout << proc::maps::Get(); +} + TEST(ProfilingSystemInfo, SmapsRollup) { using namespace oops; std::cout << proc::smaps_rollup::Get(); diff --git a/module/profiling/tool/monitor/monitor.cpp b/module/profiling/tool/monitor/monitor.cpp index 7f474af..386e0dc 100644 --- a/module/profiling/tool/monitor/monitor.cpp +++ b/module/profiling/tool/monitor/monitor.cpp @@ -521,7 +521,7 @@ void ParseArgs(int argc, char *argv[]) { ENABLED_METRIC_GROUP.Set(MetricGroup::CPU); matched = true; } - if ((token == "all" || token == "memory" || token == "m") && + if ((token == "all" || token == "memory" || token == "mem" || token == "m") && !ENABLED_METRIC_GROUP.Test(MetricGroup::MEMORY)) { ENABLED_METRIC_GROUP.Set(MetricGroup::MEMORY); matched = true;