1 // 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 15 #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 28 namespace metric_blob 29 { 30 31 using phosphor::logging::log; 32 using level = phosphor::logging::level; 33 34 char controlCharsToSpace(char c) 35 { 36 if (c < 32) 37 { 38 c = ' '; 39 } 40 return c; 41 } 42 43 long getTicksPerSec() 44 { 45 return sysconf(_SC_CLK_TCK); 46 } 47 48 std::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 63 bool 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. 86 std::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 99 std::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. 118 TcommUtimeStime 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 180 TcommUtimeStime 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 191 bool 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 209 bool 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