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