blob: 6096d51c57a0ea9b2b966ebea7498c2a0aa14920 [file] [log] [blame]
Sui Chen03eba282021-02-11 11:35:56 -08001#include "util.hpp"
2
3#include <unistd.h>
4
5#include <phosphor-logging/log.hpp>
6
7#include <cmath>
8#include <cstdlib>
9#include <fstream>
10#include <sstream>
11#include <string>
12#include <string_view>
13
14namespace metric_blob
15{
16
17using phosphor::logging::log;
18using level = phosphor::logging::level;
19
20char controlCharsToSpace(char c)
21{
22 if (c < 32)
23 {
24 c = ' ';
25 }
26 return c;
27}
28
29long getTicksPerSec()
30{
31 return sysconf(_SC_CLK_TCK);
32}
33
34std::string readFileIntoString(const std::string_view fileName)
35{
36 std::stringstream ss;
37 std::ifstream ifs(fileName.data());
38 while (ifs.good())
39 {
40 std::string line;
41 std::getline(ifs, line);
42 ss << line;
43 if (ifs.good())
44 ss << std::endl;
45 }
46 return ss.str();
47}
48
49bool isNumericPath(const std::string_view path, int& value)
50{
51 size_t p = path.rfind('/');
52 if (p == std::string::npos)
53 {
54 return false;
55 }
56 int id = 0;
57 for (size_t i = p + 1; i < path.size(); ++i)
58 {
59 const char ch = path[i];
60 if (ch < '0' || ch > '9')
61 return false;
62 else
63 {
64 id = id * 10 + (ch - '0');
65 }
66 }
67 value = id;
68 return true;
69}
70
71// Trims all control characters at the end of a string.
72std::string trimStringRight(std::string_view s)
73{
74 std::string ret(s.data());
75 while (!ret.empty())
76 {
77 if (ret.back() <= 32)
78 ret.pop_back();
79 else
80 break;
81 }
82 return ret;
83}
84
85std::string getCmdLine(const int pid)
86{
87 const std::string& cmdlinePath =
88 "/proc/" + std::to_string(pid) + "/cmdline";
89
90 std::string cmdline = readFileIntoString(cmdlinePath);
91 for (size_t i = 0; i < cmdline.size(); ++i)
92 {
93 cmdline[i] = controlCharsToSpace(cmdline[i]);
94 }
95
96 // Trim empty strings
97 cmdline = trimStringRight(cmdline);
98
99 return cmdline;
100}
101
102// strtok is used in this function in order to avoid usage of <sstream>.
103// However, that would require us to create a temporary std::string.
104TcommUtimeStime parseTcommUtimeStimeString(std::string_view content,
105 const long ticksPerSec)
106{
107 TcommUtimeStime ret;
108 ret.tcomm = "";
109 ret.utime = ret.stime = 0;
110
111 const float invTicksPerSec = 1.0f / static_cast<float>(ticksPerSec);
112
113 // pCol now points to the first part in content after content is split by
114 // space.
115 // This is not ideal,
116 std::string temp(content);
117 char* pCol = strtok(temp.data(), " ");
118
119 if (pCol != nullptr)
120 {
121 const int fields[] = {1, 13, 14}; // tcomm, utime, stime
122 int fieldIdx = 0;
123 for (int colIdx = 0; colIdx < 15; ++colIdx)
124 {
125 if (fieldIdx < 3 && colIdx == fields[fieldIdx])
126 {
127 switch (fieldIdx)
128 {
129 case 0:
130 {
131 ret.tcomm = std::string(pCol);
132 break;
133 }
134 case 1:
135 [[fallthrough]];
136 case 2:
137 {
138 int ticks = std::atoi(pCol);
139 float t = static_cast<float>(ticks) * invTicksPerSec;
140
141 if (fieldIdx == 1)
142 {
143 ret.utime = t;
144 }
145 else if (fieldIdx == 2)
146 {
147 ret.stime = t;
148 }
149 break;
150 }
151 }
152 ++fieldIdx;
153 }
154 pCol = strtok(nullptr, " ");
155 }
156 }
157
158 if (ticksPerSec <= 0)
159 {
160 log<level::ERR>("ticksPerSec is equal or less than zero");
161 }
162
163 return ret;
164}
165
166TcommUtimeStime getTcommUtimeStime(const int pid, const long ticksPerSec)
167{
168 const std::string& statPath = "/proc/" + std::to_string(pid) + "/stat";
169 return parseTcommUtimeStimeString(readFileIntoString(statPath),
170 ticksPerSec);
171}
172
173// Returns true if successfully parsed and false otherwise. If parsing was
174// successful, value is set accordingly.
175// Input: "MemAvailable: 1234 kB"
176// Returns true, value set to 1234
177bool parseMeminfoValue(const std::string_view content,
178 const std::string_view keyword, int& value)
179{
180 size_t p = content.find(keyword);
181 if (p != std::string::npos)
182 {
183 std::string_view v = content.substr(p + keyword.size());
184 p = v.find("kB");
185 if (p != std::string::npos)
186 {
187 v = v.substr(0, p);
188 value = std::atoi(v.data());
189 return true;
190 }
191 }
192 return false;
193}
194
195bool parseProcUptime(const std::string_view content, double& uptime,
196 double& idleProcessTime)
197{
198 double t0, t1; // Attempts to parse uptime and idleProcessTime
199 int ret = sscanf(content.data(), "%lf %lf", &t0, &t1);
200 if (ret == 2 && std::isfinite(t0) && std::isfinite(t1))
201 {
202 uptime = t0;
203 idleProcessTime = t1;
204 return true;
205 }
206 return false;
207}
208
209} // namespace metric_blob