1 #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 
14 namespace metric_blob
15 {
16 
17 using phosphor::logging::log;
18 using level = phosphor::logging::level;
19 
20 char controlCharsToSpace(char c)
21 {
22     if (c < 32)
23     {
24         c = ' ';
25     }
26     return c;
27 }
28 
29 long getTicksPerSec()
30 {
31     return sysconf(_SC_CLK_TCK);
32 }
33 
34 std::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 
49 bool 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.
72 std::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 
85 std::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.
104 TcommUtimeStime 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 
166 TcommUtimeStime 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
177 bool 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 
195 bool 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