|
- // g++ -o main main.cpp
- #include <unistd.h>
- #include <dirent.h>
- #include <sys/stat.h>
- #include <signal.h>
-
- #include <cstdio>
- #include <cstdint>
- #include <iostream>
- #include <fstream>
- #include <algorithm>
- #include <string>
- #include <cstring>
- #include <chrono>
- #include <thread>
-
- static bool should_exit = false;
-
- static void SignalHandler(int sig)
- {
- if (sig == SIGINT)
- {
- std::cerr << "KeyboardInterrupt" << std::endl;
- should_exit = true;
- }
- }
-
- class Sampler
- {
- public:
- inline Sampler(double frequency)
- : expected_cycle_time_(static_cast<int32_t>((1.0 / frequency) * 1000000))
- {
- signal(SIGINT, SignalHandler);
- }
-
- template <typename Fn, typename... Args>
- inline void Run(Fn&& fx, Args&& ... args)
- {
- auto func = std::bind(std::forward<Fn>(fx), std::forward<Args>(args)...);
-
- start_ = std::chrono::steady_clock::now();
- while (!should_exit)
- {
- func();
- Sleep();
- }
- }
-
- private:
- inline bool Sleep()
- {
- std::chrono::steady_clock::time_point expected_end = start_ + expected_cycle_time_;
- std::chrono::steady_clock::time_point actual_end = std::chrono::steady_clock::now();
-
- // detect backward jumps in time
- if (actual_end < start_)
- {
- expected_end = actual_end + expected_cycle_time_;
- }
-
- // calculate the time we'll sleep for
- std::chrono::duration<int32_t, std::micro> sleep_time = std::chrono::duration_cast<std::chrono::microseconds>(expected_end - actual_end);
- // make sure to reset our start time
- start_ = expected_end;
-
- // if we've taken too much time we won't sleep
- if (sleep_time <= std::chrono::duration<int32_t, std::micro>(0))
- {
- // if we've jumped forward in time, or the loop has taken more than
- a full extra
- // cycle, reset our cycle
- if (actual_end > expected_end + expected_cycle_time_)
- {
- start_ = actual_end;
- }
-
- // return false to show that the desired rate was not met
- return false;
- }
-
- std::this_thread::sleep_for(sleep_time);
- return true;
- }
-
- private:
- std::chrono::steady_clock::time_point start_;
- std::chrono::duration<int32_t, std::micro> expected_cycle_time_;
- };
-
- inline uint64_t NowTimestampMs()
- {
- return static_cast<uint64_t>(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
- }
-
- inline bool IsNumber(std::string &s)
- {
- return std::all_of(s.begin(), s.end(), [](char c){ return std::isdigit(c) != 0; });
- }
-
- inline bool IsNumber(const char *s)
- {
- return std::all_of(s, s + std::strlen(s), [](char c){ return std::isdigit(c) != 0; });
- }
-
- int64_t CollectMemoryInfo()
- {
- static const std::string ProcPath = "/proc";
-
- DIR *dir {nullptr};
- struct dirent *ptr {nullptr};
- std::string stmt_path{};
- char line_buffer[256];
-
- int64_t all = 0;
-
- if ((dir = opendir(ProcPath.c_str())) == nullptr)
- {
- std::cerr << "Failed to open " << ProcPath << std::endl;
- exit(1);
- }
-
- while ((ptr = readdir(dir)) != nullptr)
- {
- struct stat buff{};
-
- if (ptr->d_type == DT_DIR && IsNumber(ptr->d_name))
- {
- stmt_path.clear();
-
- stmt_path.append(ProcPath).append("/").append(ptr->d_name).append("/").append("smaps");
-
- if (access(stmt_path.c_str(), R_OK) == 0)
- {
- std::ifstream ifs(stmt_path);
-
- while (ifs.getline(line_buffer, sizeof(line_buffer)))
- {
- if (std::sscanf(line_buffer, "Pss: %d", &pss) == 1)
- {
- all += pss;
- }
- }
- }
- }
- }
- closedir(dir);
-
- return all;
- }
-
- int main ()
- {
- Sampler sampler(50);
-
- sampler.Run([]{
- auto memory_usage_in_kb = CollectMemoryInfo();
- auto now_timestamp_ms = NowTimestampMs();
-
- std::cout << now_timestamp_ms << "," << memory_usage_in_kb << "\n";
- });
-
- return 0;
- }
|