// g++ -o main main.cpp #include #include #include #include #include #include #include #include #include #include #include #include #include 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((1.0 / frequency) * 1000000)) { signal(SIGINT, SignalHandler); } template inline void Run(Fn&& fx, Args&& ... args) { auto func = std::bind(std::forward(fx), std::forward(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 sleep_time = std::chrono::duration_cast(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(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 expected_cycle_time_; }; inline uint64_t NowTimestampMs() { return static_cast(std::chrono::duration_cast(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; }