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