diff --git a/module/profiling/include/oops/system_info.h b/module/profiling/include/oops/system_info.h index 23ba21c..f57ccbb 100644 --- a/module/profiling/include/oops/system_info.h +++ b/module/profiling/include/oops/system_info.h @@ -17,15 +17,15 @@ 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_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 -struct Info { - std::size_t vm_peak{}; // 当前只解析内存部分 +struct Info { // 仅支持部分字段 + std::size_t vm_peak{}; std::size_t vm_size{}; std::size_t vm_lck{}; std::size_t vm_pin{}; @@ -40,6 +40,7 @@ struct Info { std::size_t vm_lib{}; std::size_t vm_pte{}; std::size_t vm_swap{}; + FieldMask parsed; }; @@ -47,7 +48,7 @@ struct Info { [[nodiscard]] Info Get(pid_t pid); [[nodiscard]] Info Get(const FieldMask &field_mask); [[nodiscard]] Info Get(pid_t pid, const FieldMask &field_mask); -std::ostream &operator<<(std::ostream &out, const Info &info); +std::ostream &operator<<(std::ostream &os, const Info &info); } // namespace status namespace maps { // vma数据结构,maps, smaps, smaps_rollup共用 @@ -56,12 +57,12 @@ struct Vma { std::uint32_t MajorDev() const; std::uint32_t MinorDev() const; - struct { + struct Address { std::uintptr_t start{}; std::uintptr_t end{}; } address{}; - struct { + struct Perms { bool r : 1; bool w : 1; bool x : 1; @@ -80,15 +81,113 @@ struct Info { [[nodiscard]] Info Get(); [[nodiscard]] Info Get(pid_t pid); -std::ostream &operator<<(std::ostream &out, const Info &info); +std::ostream &operator<<(std::ostream &os, const Info &info); } // namespace maps +namespace smaps { +// clang-format off +enum class Field : uint8_t { + VMA, SIZE, KERNEL_PAGE_SIZE, MMU_PAGE_SIZE, RSS, PSS, + PSS_DIRTY, 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, THP_ELIGIBLE, + VM_FLAGS, COUNT +}; +// clang-format on +using FieldMask = EnumBitset; +using oops::operator|; + +using Vma = maps::Vma; +struct VmaExt { + Vma vma; + // 核心内存统计 + KiBs size{}; + KiBs kernel_page_size{}; + KiBs mmu_page_size{}; + KiBs rss{}; + KiBs pss{}; + KiBs pss_dirty{}; + + // 页面共享状态统计 + KiBs shared_clean{}; + KiBs shared_dirty{}; + KiBs private_clean{}; + KiBs private_dirty{}; + + // 引用与内核统计 + KiBs referenced{}; + KiBs anonymous{}; + KiBs ksm{}; + KiBs lazy_free{}; + + // 大页统计 + KiBs anon_huge_pages{}; + KiBs shmem_pmd_mapped{}; + KiBs file_pmd_mapped{}; + KiBs shared_hugetlb{}; + KiBs private_hugetlb{}; + + // 交换区与锁定统计 + KiBs swap{}; + KiBs swap_pss{}; + KiBs locked{}; + + // 标志统计 + bool thp_eligible{}; + struct VmFlags { + bool rd : 1; + bool wr : 1; + bool ex : 1; + bool sh : 1; + bool mr : 1; + bool mw : 1; + bool me : 1; + bool ms : 1; + bool gd : 1; + bool pf : 1; + bool dw : 1; + bool lo : 1; + bool io : 1; + bool sr : 1; + bool rr : 1; + bool dc : 1; + bool de : 1; + bool ac : 1; + bool nr : 1; + bool ht : 1; + bool sf : 1; + bool nl : 1; + bool ar : 1; + bool wf : 1; + bool dd : 1; + bool sd : 1; + bool mm : 1; + bool hg : 1; + bool nh : 1; + bool mg : 1; + bool um : 1; + bool uw : 1; + } vm_flags{}; + + FieldMask parsed; +}; + +struct Info { + std::vector vma_table; +}; + +[[nodiscard]] Info Get(); +[[nodiscard]] Info Get(pid_t pid); +[[nodiscard]] Info Get(const FieldMask &field_mask); +[[nodiscard]] Info Get(pid_t pid, const FieldMask &field_mask); +std::ostream &operator<<(std::ostream &os, const Info &info); +} // namespace smaps 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, + 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 @@ -137,7 +236,7 @@ struct Info { [[nodiscard]] Info Get(pid_t pid); [[nodiscard]] Info Get(const FieldMask &field_mask); [[nodiscard]] Info Get(pid_t pid, const FieldMask &field_mask); -std::ostream &operator<<(std::ostream &out, const Info &info); +std::ostream &operator<<(std::ostream &os, const Info &info); } // namespace smaps_rollup namespace numa_maps { // Since Linux 2.6.14 @@ -169,14 +268,14 @@ struct Info { namespace lscpu { // clang-format off enum class Field { - ARCHITECTURE, CPUS, THREADS_PER_CORE, CORES_PER_SOCKET, SOCKETS, NUMA_NODES, + 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 +using oops::operator|; -struct Info { +struct Info { // 仅支持部分字段 std::string architecture{}; std::size_t cpus{}; std::size_t threads_per_core{}; @@ -190,6 +289,6 @@ struct Info { [[nodiscard]] Info Get(); [[nodiscard]] Info Get(const FieldMask &field_mask); -::std::ostream &operator<<(::std::ostream &out, const Info &info); +::std::ostream &operator<<(::std::ostream &os, const Info &info); } // namespace lscpu } // namespace oops diff --git a/module/profiling/src/system_info.cpp b/module/profiling/src/system_info.cpp index 4c61697..213c851 100644 --- a/module/profiling/src/system_info.cpp +++ b/module/profiling/src/system_info.cpp @@ -25,7 +25,7 @@ namespace oops { using namespace meta; -// 获取命令输出 +// 获取命令行输出 std::string GetCmdOutput(const std::string &cmd) { std::unique_ptr p{popen(cmd.c_str(), "r"), &pclose}; std::string output; @@ -40,26 +40,7 @@ std::string GetCmdOutput(const std::string &cmd) { return output; } -class IStreamBacktraceGuard { -public: - explicit IStreamBacktraceGuard(std::istream &is) : is_{is}, pos_{is.tellg()} {} - IStreamBacktraceGuard(const IStreamBacktraceGuard &) = delete; - IStreamBacktraceGuard &operator=(const IStreamBacktraceGuard &) = delete; - - void Commit() { pos_ = is_.tellg(); } - - ~IStreamBacktraceGuard() { - if (pos_ != std::streampos{-1}) { - is_.clear(); - is_.seekg(pos_); - } - } - -private: - std::istream &is_; - std::streampos pos_; -}; - +// 解析和格式化字段的特化注册 template bool ParseField(std::string_view s, T &t, std::string_view ctx = "{}") { auto scan_res{scn::scan(s, ctx)}; @@ -69,6 +50,12 @@ bool ParseField(std::string_view s, T &t, std::string_view ctx = "{}") { return bool{scan_res}; } +template +std::string FormatField(const T &t, std::string_view ctx = "{}") { + return fmt::format(ctx, t); +} + +// Storage特化 template bool ParseField(std::string_view s, Storage &storage, std::string_view ctx = "{}") { R r{}; @@ -77,16 +64,61 @@ bool ParseField(std::string_view s, Storage &storage, std::string_view ctx return res; } -template -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); } +// proc::smaps::VmaExt::VmFlags特化 +struct VmFlagsEntry { + std::string_view key; + void (*set)(proc::smaps::VmaExt::VmFlags &); + bool (*get)(const proc::smaps::VmaExt::VmFlags &); +}; + +#define ENTRY(flag) \ + { \ + #flag, [](proc::smaps::VmaExt::VmFlags &f) { f.flag = true; }, \ + [](const proc::smaps::VmaExt::VmFlags &f) { return f.flag; } \ + } +constexpr VmFlagsEntry VM_FLAGS_TABLE[]{ENTRY(rd), ENTRY(wr), ENTRY(ex), ENTRY(sh), ENTRY(mr), ENTRY(mw), ENTRY(me), + ENTRY(ms), ENTRY(gd), ENTRY(pf), ENTRY(dw), ENTRY(lo), ENTRY(io), ENTRY(sr), + ENTRY(rr), ENTRY(dc), ENTRY(de), ENTRY(ac), ENTRY(nr), ENTRY(ht), ENTRY(sf), + ENTRY(nl), ENTRY(ar), ENTRY(wf), ENTRY(dd), ENTRY(sd), ENTRY(mm), ENTRY(hg), + ENTRY(nh), ENTRY(mg), ENTRY(um), ENTRY(uw)}; +#undef ENTRY + +template <> +bool ParseField(std::string_view s, proc::smaps::VmaExt::VmFlags &vm_flags, std::string_view) { + bool failed{false}; + for (auto token : Split>(s)) { + auto it{std::find_if( + std::begin(VM_FLAGS_TABLE), std::end(VM_FLAGS_TABLE), [token](auto &entry) { return entry.key == token; })}; + if (it != std::end(VM_FLAGS_TABLE)) { + it->set(vm_flags); + } else { + failed = true; + } + } + return !failed; +} + +template <> +std::string FormatField(const proc::smaps::VmaExt::VmFlags &vm_flags, std::string_view) { + std::string s; + s.reserve(64); + for (const auto &item : VM_FLAGS_TABLE) { + if (item.get(vm_flags)) { + s += item.key; + s += ' '; + } + } + if (!s.empty()) { + s.pop_back(); + } + return s; +} + template class KeyValueParser { template @@ -105,18 +137,15 @@ class KeyValueParser { std::string key; MemberPtrVar member_ptr_var; std::string suffix{}; - // 后续如果没有特殊规则的解析,移除注册固化函数,或者使用fmt格式化字符串 - bool (*parse)(std::string_view, MemberPtrVar, Struct &){ParseMemberPtrVar}; - std::string (*format)(MemberPtrVar, const Struct &){FormatMemberPtrVar}; }; explicit KeyValueParser(std::initializer_list entries) : field_table_{entries} {} KeyValueParser(std::initializer_list entries, std::string delim) : field_table_{entries}, delim_{std::move(delim)} {} - KeyValueParser(std::initializer_list entries, std::function stop) - : field_table_{entries}, stop_{std::move(stop)} {} - KeyValueParser(std::initializer_list entries, std::string delim, std::function stop) - : field_table_{entries}, delim_{std::move(delim)}, stop_{std::move(stop)} {} + KeyValueParser(std::initializer_list entries, bool (*stop)(std::string_view)) + : field_table_{entries}, stop_{stop} {} + KeyValueParser(std::initializer_list entries, std::string delim, bool (*stop)(std::string_view)) + : field_table_{entries}, delim_{std::move(delim)}, stop_{stop} {} static bool ParseMemberPtrVar(std::string_view s, MemberPtrVar member_ptr_var, Struct &obj) { auto f = [s, &obj](auto member_ptr) { @@ -136,20 +165,19 @@ class KeyValueParser { // 根据注册表向object中各字段填值,更适合引用输出 EnumBitset Parse(std::istream &is, Struct &object, const EnumBitset &field_mask) { - IStreamBacktraceGuard guard(is); EnumBitset parsed; + std::streampos checkpoint{is.tellg()}; std::string line_buf; while (std::getline(is, line_buf)) { std::string_view line{line_buf}; - std::size_t pos{line.find(delim_)}; - if (stop_(line)) { + if (stop_ && stop_(line)) { + is.seekg(checkpoint); break; } - guard.Commit(); // 本行已经确定是kv-pair,提交本行 - + checkpoint = is.tellg(); + std::size_t pos{line.find(delim_)}; if (pos == line.npos) { continue; - ; } std::string_view key{Strip(line.substr(0, pos))}; @@ -174,13 +202,21 @@ class KeyValueParser { continue; } - it->parse(value, it->member_ptr_var, object); + ParseMemberPtrVar(value, it->member_ptr_var, object); parsed |= it->field; - guard.Commit(); if (parsed == field_mask) { break; // 已解析全量 } } + if (stop_) { + while (std::getline(is, line_buf)) { + if (stop_(line_buf)) { + is.seekg(checkpoint); + break; + } + checkpoint = is.tellg(); + } + } return parsed; } @@ -188,7 +224,7 @@ class KeyValueParser { FTable ftable; for (const auto &entry : field_table_) { if (field_mask.Test(entry.field)) { - std::string value{entry.format(entry.member_ptr_var, object)}; + std::string value{FormatMemberPtrVar(entry.member_ptr_var, object)}; if (!value.empty()) { ftable.AppendRow(std::string{entry.key} + delim_, value, entry.suffix); } @@ -200,20 +236,27 @@ class KeyValueParser { private: std::vector field_table_; std::string delim_{":"}; - std::function stop_{[](std::string_view) { return false; }}; + bool (*stop_)(std::string_view){nullptr}; }; namespace proc { namespace status { 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}, - {Field::RSS_ANON, "RssAnon", &Info::rss_anon}, {Field::RSS_FILE, "RssFile", &Info::rss_file}, - {Field::RSS_SHMEM, "RssShmem", &Info::rss_shmem}, {Field::VM_DATA, "VmData", &Info::vm_data}, - {Field::VM_STK, "VmStk", &Info::vm_stk}, {Field::VM_EXE, "VmExe", &Info::vm_exe}, - {Field::VM_LIB, "VmLib", &Info::vm_lib}, {Field::VM_PTE, "VmPTE", &Info::vm_pte}, - {Field::VM_SWAP, "VmSwap", &Info::vm_swap}}; + {{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}, + {Field::RSS_ANON, "RssAnon", &Info::rss_anon}, + {Field::RSS_FILE, "RssFile", &Info::rss_file}, + {Field::RSS_SHMEM, "RssShmem", &Info::rss_shmem}, + {Field::VM_DATA, "VmData", &Info::vm_data}, + {Field::VM_STK, "VmStk", &Info::vm_stk}, + {Field::VM_EXE, "VmExe", &Info::vm_exe}, + {Field::VM_LIB, "VmLib", &Info::vm_lib}, + {Field::VM_PTE, "VmPTE", &Info::vm_pte}, + {Field::VM_SWAP, "VmSwap", &Info::vm_swap}}}; Info Get(std::istream &is, const FieldMask &field_mask) { Info info; @@ -244,13 +287,13 @@ std::uint32_t Vma::MajorDev() const { return major(dev); } std::uint32_t Vma::MinorDev() const { return minor(dev); } auto ParseVma(std::istream &is) { - IStreamBacktraceGuard guard(is); struct { explicit operator bool() const noexcept { return !failed; } Vma vma; bool failed{}; } res; + std::streampos checkpoint{is.tellg()}; std::string buf; if (!std::getline(is, buf)) { res.failed = true; @@ -260,10 +303,11 @@ auto ParseVma(std::istream &is) { auto scan_res{scn::scan( buf, "{:x}-{:x} {}{}{}{} {:x} {:x}:{:x} {}")}; if (!scan_res) { + checkpoint = is.tellg(); res.failed = true; return res; } - guard.Commit(); + checkpoint = is.tellg(); const auto &values{scan_res->values()}; res.vma.address.start = std::get<0>(values); @@ -327,30 +371,101 @@ std::ostream &operator<<(std::ostream &os, const Info &info) { return os; } } // namespace maps +namespace smaps { +KeyValueParser, bool, decltype(VmaExt::vm_flags)>> kvparser{ + {{Field::SIZE, "Size", &VmaExt::size}, + {Field::KERNEL_PAGE_SIZE, "KernelPageSize", &VmaExt::kernel_page_size}, + {Field::MMU_PAGE_SIZE, "MMUPageSize", &VmaExt::mmu_page_size}, + {Field::RSS, "Rss", &VmaExt::rss}, + {Field::PSS, "Pss", &VmaExt::pss}, + {Field::PSS_DIRTY, "Pss_Dirty", &VmaExt::pss_dirty}, + {Field::SHARED_CLEAN, "Shared_Clean", &VmaExt::shared_clean}, + {Field::SHARED_DIRTY, "Shared_Dirty", &VmaExt::shared_dirty}, + {Field::PRIVATE_CLEAN, "Private_Clean", &VmaExt::private_clean}, + {Field::PRIVATE_DIRTY, "Private_Dirty", &VmaExt::private_dirty}, + {Field::REFERENCED, "Referenced", &VmaExt::referenced}, + {Field::ANONYMOUS, "Anonymous", &VmaExt::anonymous}, + {Field::KSM, "KSM", &VmaExt::ksm}, + {Field::LAZY_FREE, "LazyFree", &VmaExt::lazy_free}, + {Field::ANON_HUGE_PAGES, "AnonHugePages", &VmaExt::anon_huge_pages}, + {Field::SHMEM_PMD_MAPPED, "ShmemPmdMapped", &VmaExt::shmem_pmd_mapped}, + {Field::FILE_PMD_MAPPED, "FilePmdMapped", &VmaExt::file_pmd_mapped}, + {Field::SHARED_HUGETLB, "Shared_Hugetlb", &VmaExt::shared_hugetlb}, + {Field::PRIVATE_HUGETLB, "Private_Hugetlb", &VmaExt::private_hugetlb}, + {Field::SWAP, "Swap", &VmaExt::swap}, + {Field::SWAP_PSS, "SwapPss", &VmaExt::swap_pss}, + {Field::LOCKED, "Locked", &VmaExt::locked}, + {Field::THP_ELIGIBLE, "THPeligible", &VmaExt::thp_eligible}, + {Field::VM_FLAGS, "VmFlags", &VmaExt::vm_flags}}, + ":", + [](std::string_view s) { return s.find('-') != std::string_view::npos; }}; + +Info Get(std::istream &is, const FieldMask &field_mask) { + Info info; + while (!is.eof()) { + VmaExt vma_ext; + if (field_mask.Test(Field::VMA)) { + auto res{maps::ParseVma(is)}; + if (res) { + vma_ext.vma = std::move(res.vma); + vma_ext.parsed.Set(Field::VMA); + } + } else { + is.ignore(std::numeric_limits::max(), '\n'); + } + vma_ext.parsed |= kvparser.Parse(is, vma_ext, field_mask); + info.vma_table.push_back(std::move(vma_ext)); + } + return info; +} + +Info Get() { return Get(~FieldMask{}); } +Info Get(pid_t pid) { return Get(pid, ~FieldMask{}); } + +Info Get(const FieldMask &field_mask) { + std::ifstream ifs("/proc/self/smaps"); + return Get(ifs, field_mask); +} + +Info Get(pid_t pid, const FieldMask &field_mask) { + std::ifstream ifs(fmt::format("/proc/{}/smaps", pid)); + return Get(ifs, field_mask); +} + +std::ostream &operator<<(std::ostream &os, const Info &info) { + for (const auto &vma_ext : info.vma_table) { + if (vma_ext.parsed.Test(Field::VMA)) { + maps::FormatVma(os, vma_ext.vma); + } + kvparser.Format(os, vma_ext, vma_ext.parsed); + } + return os; +} +} // namespace smaps namespace smaps_rollup { KeyValueParser>> kvparser{ - {Field::RSS, "Rss", &Info::rss}, - {Field::PSS, "Pss", &Info::pss}, - {Field::PSS_DIRTY, "Pss_Dirty", &Info::pss_dirty}, - {Field::PSS_ANON, "Pss_Anon", &Info::pss_anon}, - {Field::PSS_FILE, "Pss_File", &Info::pss_file}, - {Field::PSS_SHMEM, "Pss_Shmem", &Info::pss_shmem}, - {Field::SHARED_CLEAN, "Shared_Clean", &Info::shared_clean}, - {Field::SHARED_DIRTY, "Shared_Dirty", &Info::shared_dirty}, - {Field::PRIVATE_CLEAN, "Private_Clean", &Info::private_clean}, - {Field::PRIVATE_DIRTY, "Private_Dirty", &Info::private_dirty}, - {Field::REFERENCED, "Referenced", &Info::referenced}, - {Field::ANONYMOUS, "Anonymous", &Info::anonymous}, - {Field::KSM, "KSM", &Info::ksm}, - {Field::LAZY_FREE, "LazyFree", &Info::lazy_free}, - {Field::ANON_HUGE_PAGES, "AnonHugePages", &Info::anon_huge_pages}, - {Field::SHMEM_PMD_MAPPED, "ShmemPmdMapped", &Info::shmem_pmd_mapped}, - {Field::FILE_PMD_MAPPED, "FilePmdMapped", &Info::file_pmd_mapped}, - {Field::SHARED_HUGETLB, "Shared_Hugetlb", &Info::shared_hugetlb}, - {Field::PRIVATE_HUGETLB, "Private_Hugetlb", &Info::private_hugetlb}, - {Field::SWAP, "Swap", &Info::swap}, - {Field::SWAP_PSS, "SwapPss", &Info::swap_pss}, - {Field::LOCKED, "Locked", &Info::locked}}; + {{Field::RSS, "Rss", &Info::rss}, + {Field::PSS, "Pss", &Info::pss}, + {Field::PSS_DIRTY, "Pss_Dirty", &Info::pss_dirty}, + {Field::PSS_ANON, "Pss_Anon", &Info::pss_anon}, + {Field::PSS_FILE, "Pss_File", &Info::pss_file}, + {Field::PSS_SHMEM, "Pss_Shmem", &Info::pss_shmem}, + {Field::SHARED_CLEAN, "Shared_Clean", &Info::shared_clean}, + {Field::SHARED_DIRTY, "Shared_Dirty", &Info::shared_dirty}, + {Field::PRIVATE_CLEAN, "Private_Clean", &Info::private_clean}, + {Field::PRIVATE_DIRTY, "Private_Dirty", &Info::private_dirty}, + {Field::REFERENCED, "Referenced", &Info::referenced}, + {Field::ANONYMOUS, "Anonymous", &Info::anonymous}, + {Field::KSM, "KSM", &Info::ksm}, + {Field::LAZY_FREE, "LazyFree", &Info::lazy_free}, + {Field::ANON_HUGE_PAGES, "AnonHugePages", &Info::anon_huge_pages}, + {Field::SHMEM_PMD_MAPPED, "ShmemPmdMapped", &Info::shmem_pmd_mapped}, + {Field::FILE_PMD_MAPPED, "FilePmdMapped", &Info::file_pmd_mapped}, + {Field::SHARED_HUGETLB, "Shared_Hugetlb", &Info::shared_hugetlb}, + {Field::PRIVATE_HUGETLB, "Private_Hugetlb", &Info::private_hugetlb}, + {Field::SWAP, "Swap", &Info::swap}, + {Field::SWAP_PSS, "SwapPss", &Info::swap_pss}, + {Field::LOCKED, "Locked", &Info::locked}}}; Info Get(std::istream &is, const FieldMask &field_mask) { Info info; @@ -395,14 +510,14 @@ namespace numa_maps { } // namespace proc namespace lscpu { 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}, - {Field::CORES_PER_SOCKET, "Core(s) per socket", &Info::cores_per_socket}, - {Field::SOCKETS, "Socket(s)", &Info::sockets}, - {Field::NUMA_NODES, "NUMA node(s)", &Info::numa_nodes}, - {Field::MODEL_NAME, "Model name", &Info::model_name}, - {Field::CPU_MHZ, "CPU MHz", &Info::cpu_mhz}}; + {{Field::ARCHITECTURE, "Architecture", &Info::architecture}, + {Field::CPUS, "CPU(s)", &Info::cpus}, + {Field::THREADS_PER_CORE, "Thread(s) per core", &Info::threads_per_core}, + {Field::CORES_PER_SOCKET, "Core(s) per socket", &Info::cores_per_socket}, + {Field::SOCKETS, "Socket(s)", &Info::sockets}, + {Field::NUMA_NODES, "NUMA node(s)", &Info::numa_nodes}, + {Field::MODEL_NAME, "Model name", &Info::model_name}, + {Field::CPU_MHZ, "CPU MHz", &Info::cpu_mhz}}}; Info Get() { return Get(~FieldMask{}); } Info Get(const FieldMask &field_mask) { diff --git a/module/profiling/test/test_system_info.cpp b/module/profiling/test/test_system_info.cpp index 09a5a65..2ef12fd 100644 --- a/module/profiling/test/test_system_info.cpp +++ b/module/profiling/test/test_system_info.cpp @@ -33,66 +33,23 @@ TEST(ProfilingSystemInfo, Status3) { std::cout << k << std::endl; } -struct CpuTicks { - std::uintmax_t TotalTicks() const { return user_ticks + kernel_ticks; }; - std::uintmax_t user_ticks; - std::uintmax_t kernel_ticks; -}; - -inline CpuTicks GetCpuTimePoint2() { - // 文件快照(/proc/pid/stat)获取cpu时间,Ticks级精度(约10ms) - // 解析效率要求高,使用栈缓冲 - char buf[512]; - std::FILE *f{std::fopen("/proc/self/stat", "r")}; - if (!f) { - return {}; // 无法打开文件,返回空类型 - } - - auto res{std::fgets(buf, sizeof(buf), f)}; - std::fclose(f); - if (res == nullptr) { - return {}; // 读取失败,返回空类型 - } - - // 查找第2个字段的右括号 - char *p{std::strrchr(buf, ')')}; - assert(p != nullptr); - p += 2; - - // 根据空格数量匹配第14个字段user_ticks - int rem{11}; - while (rem-- > 0) { - p = std::strchr(p + 1, ' '); - assert(p != nullptr); - } - - CpuTicks cpu_ticks; - cpu_ticks.user_ticks = std::strtoull(p + 1, &p, 10); - - // 匹配第15个字段kernel_ticks - cpu_ticks.kernel_ticks = std::strtoull(p + 1, nullptr, 10); - return cpu_ticks; -} - -TEST(ProfilingSystemInfo, Parse) { - using namespace oops::proc; - auto t1 = std::chrono::steady_clock::now(); - auto k = GetCpuTimePoint2(); - auto t2 = std::chrono::steady_clock::now(); - std::cout << "Time: " << std::chrono::duration{t2 - t1}.count() << std::endl; - std::cout << k.TotalTicks() << std::endl; -} - TEST(ProfilingSystemInfo, Lscpu) { using namespace oops; std::cout << lscpu::Get(); } -TEST(ProfilingSystemInfo, Smaps) { +TEST(ProfilingSystemInfo, Maps) { using namespace oops; std::cout << proc::maps::Get(); } +TEST(ProfilingSystemInfo, Smaps) { + using namespace oops; + std::cout << proc::smaps::Get(proc::smaps::Field::VMA); + std::cout << proc::smaps::Get(proc::smaps::Field::RSS); + std::cout << proc::smaps::Get(proc::smaps::Field::RSS | proc::smaps::Field::VMA); +} + TEST(ProfilingSystemInfo, SmapsRollup) { using namespace oops; std::cout << proc::smaps_rollup::Get(); diff --git a/third_party/cmake/pybind11.cmake b/third_party/cmake/pybind11.cmake index f81ba35..4ead0d9 100644 --- a/third_party/cmake/pybind11.cmake +++ b/third_party/cmake/pybind11.cmake @@ -4,4 +4,5 @@ if(NOT EXISTS "${lib_dir}/CMakeLists.txt") execute_process(COMMAND git submodule update --init --recursive ${lib_dir}) endif() +set(PYBIND11_FINDPYTHON NEW) # 设置pybind11使用现代查找模式 add_subdirectory(${lib_dir})