1 // Copyright 2004-present Facebook. All Rights Reserved.
2 
3 #include "SystemMetrics.h"
4 
5 #if defined(IS_BSD) && defined(__APPLE__)
6 #include <mach/mach_init.h>
7 #include <mach/task.h>
8 #elif defined(IS_BSD)
9 #include <sys/resource.h>
10 #include <sys/time.h>
11 #include <sys/types.h>
12 #endif
13 
14 namespace fbzmq {
15 
16 /* Return RSS memory the process currently used from /proc/[pid]/status.
17  / The /proc is a pseudo-filesystem providing an API to kernel data
18  / structures.
19 */
20 folly::Optional<size_t>
getRSSMemBytes()21 SystemMetrics::getRSSMemBytes() {
22 #if !defined(IS_BSD)
23   folly::Optional<size_t> rss{folly::none};
24   // match the line like: "VmRSS:      9028 kB"
25   std::regex rssRegex("VmRSS:\\s+(\\d+)\\s+(\\w+)");
26   std::smatch rssMatched;
27   std::string line;
28   std::ifstream input;
29   try {
30     // "/proc/self/" allows a process to look at itself without knowing the PID.
31     std::ifstream input("/proc/self/status");
32     if (input.is_open()) {
33       while (std::getline(input, line)) {
34         if (std::regex_search(line, rssMatched, rssRegex) &&
35             rssMatched[2] == "kB") {
36           rss = std::stoull(rssMatched[1]) * 1024;
37           break;
38         }
39       }
40     }
41   } catch (const std::exception& ex) {
42     LOG(ERROR)
43         << "Fail to read the \"/proc/self/status\" of current process to get the memory usage: "
44         << ex.what();
45   }
46   return rss;
47 #elif !defined(__APPLE__)
48   struct rusage rusage;
49   getrusage(RUSAGE_SELF, &rusage);
50   return (size_t)(rusage.ru_maxrss * 1024);
51 #else
52   struct task_basic_info t_info;
53   mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
54   task_info(
55       current_task(), TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count);
56   return t_info.resident_size;
57 #endif
58 }
59 
60 /* Return CPU% the process used
61  / This need to be called twice to get the time difference
62  / and calculate the CPU%.
63  /
64  / It will return folly::none when:
65  /    1. first time query
66  /    2. get invalid time:
67  /        - previous timestamp > current timestamp
68  /        - preivous total used time > current total used time
69 */
70 folly::Optional<double>
getCPUpercentage()71 SystemMetrics::getCPUpercentage() {
72   struct rusage usage;
73   getrusage(RUSAGE_SELF, &usage);
74 
75   ProcCpuTime nowCpuTime(usage);
76   folly::Optional<double> cpuPct{folly::none};
77 
78   // calculate the CPU% = (process time diff) / (time elapsed) * 100
79   if (prevCpuTime.timestamp != 0 && // has cached before
80       nowCpuTime.timestamp > prevCpuTime.timestamp &&
81       nowCpuTime.totalTime > prevCpuTime.totalTime) {
82     uint64_t timestampDiff = nowCpuTime.timestamp - prevCpuTime.timestamp;
83     uint64_t procTimeDiff = nowCpuTime.totalTime - prevCpuTime.totalTime;
84     cpuPct = ((double)procTimeDiff / (double)timestampDiff) * 100;
85   }
86 
87   // update the cache for next CPU% update
88   prevCpuTime = nowCpuTime;
89 
90   return cpuPct;
91 }
92 
93 // get current timestamp
94 uint64_t
getCurrentNanoTime()95 SystemMetrics::getCurrentNanoTime() {
96   return std::chrono::duration_cast<std::chrono::nanoseconds>(
97              std::chrono::steady_clock::now().time_since_epoch())
98       .count();
99 }
100 
101 } // namespace fbzmq
102