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