/ deps / KittyMemoryEx / KittyMemoryEx.cpp
KittyMemoryEx.cpp
  1  #include "KittyMemoryEx.hpp"
  2  #include "KittyIOFile.hpp"
  3  #include <fstream>
  4  
  5  
  6  namespace KittyMemoryEx
  7  {
  8      std::string getProcessName(pid_t pid)
  9      {
 10          if (pid <= 0)
 11              return "";
 12  
 13          char filePath[256] = {0};
 14          snprintf(filePath, sizeof(filePath), "/proc/%d/cmdline", pid);
 15  
 16          errno = 0;
 17          FILE *fp = fopen(filePath, "r");
 18          if (!fp)
 19          {
 20              KITTY_LOGD("Couldn't open cmdline file %s, error=%s", filePath, strerror(errno));
 21              return "";
 22          }
 23  
 24          char cmdline[128] = {0};
 25          fgets(cmdline, sizeof(cmdline), fp);
 26          fclose(fp);
 27          return cmdline;
 28      }
 29  
 30      std::vector<pid_t> getProcessIDs(const std::string &processName)
 31      {
 32          std::vector<pid_t> pids;
 33  
 34          if (processName.empty())
 35              return pids;
 36  
 37          errno = 0;
 38          DIR *dir = opendir("/proc/");
 39          if (!dir)
 40          {
 41              KITTY_LOGD("Couldn't open /proc/, error=%s", strerror(errno));
 42              return pids;
 43          }
 44  
 45          dirent *entry = nullptr;
 46          while ((entry = readdir(dir)) != nullptr)
 47          {
 48              int entry_pid = atoi(entry->d_name);
 49              if (entry_pid > 0)
 50              {
 51                  if (processName == getProcessName(entry_pid))
 52                  {
 53                      pids.push_back(entry_pid);
 54                  }
 55              }
 56          }
 57          closedir(dir);
 58          return pids;
 59      }
 60  
 61      pid_t getProcessID(const std::string &processName)
 62      {
 63          if (processName.empty())
 64              return 0;
 65  
 66          pid_t pid = 0;
 67  
 68          errno = 0;
 69          DIR *dir = opendir("/proc/");
 70          if (!dir)
 71          {
 72              KITTY_LOGD("Couldn't open /proc/, error=%s", strerror(errno));
 73              return pid;
 74          }
 75  
 76          dirent *entry = nullptr;
 77          while ((entry = readdir(dir)) != nullptr)
 78          {
 79              if (entry->d_name[0] == '.')
 80                  continue;
 81  
 82              int entry_pid = atoi(entry->d_name);
 83              if (entry_pid > 0)
 84              {
 85                  if (processName == getProcessName(entry_pid))
 86                  {
 87                      pid = entry_pid;
 88                      break;
 89                  }
 90              }
 91          }
 92          closedir(dir);
 93          return pid;
 94      }
 95  
 96      std::vector<pid_t> getAllThreads(pid_t pid)
 97      {
 98          std::vector<pid_t> tids;
 99          if (pid <= 0)
100              return tids;
101  
102          char filePath[256] = {0};
103          snprintf(filePath, sizeof(filePath), "/proc/%d/task", pid);
104  
105          errno = 0;
106          DIR *dir = opendir(filePath);
107          if (!dir)
108          {
109              KITTY_LOGD("Couldn't open %s, error=%s", filePath, strerror(errno));
110              return tids;
111          }
112  
113          dirent *entry = nullptr;
114          while ((entry = readdir(dir)) != nullptr)
115          {
116              if (entry->d_name[0] == '.')
117                  continue;
118  
119              int entry_tid = atoi(entry->d_name);
120              if (entry_tid > 0)
121              {
122                  tids.push_back(entry_tid);
123              }
124          }
125          closedir(dir);
126          return tids;
127      }
128  
129      bool ProcStatus::parse(const std::string &path, ProcStatus *out)
130      {
131          if (out)
132              out->data.clear();
133  
134          std::ifstream file(path.c_str());
135          if (!file.is_open())
136              return false;
137  
138          std::string line;
139  
140          while (std::getline(file, line))
141          {
142              std::size_t colon = line.find(':');
143              if (colon == std::string::npos)
144                  continue;
145  
146              std::string key = line.substr(0, colon);
147  
148              while (!key.empty() && std::isspace(static_cast<unsigned char>(key.back())))
149                  key.pop_back();
150  
151              const char *p = line.c_str() + colon + 1;
152  
153              while (*p && std::isspace(static_cast<unsigned char>(*p)))
154                  ++p;
155  
156              std::string value(p);
157  
158              while (!value.empty() && std::isspace(static_cast<unsigned char>(value.back())))
159                  value.pop_back();
160  
161              if (out)
162                  out->data.emplace(std::move(key), std::string(p));
163          }
164  
165          return true;
166      }
167  
168      std::vector<ProcMap> getAllMaps(pid_t pid)
169      {
170          std::vector<ProcMap> retMaps;
171          if (pid <= 0)
172              return retMaps;
173  
174          char filePath[256] = {0};
175          snprintf(filePath, sizeof(filePath), "/proc/%d/maps", pid);
176  
177          errno = 0;
178          FILE *fp = fopen(filePath, "r");
179          if (!fp)
180          {
181              KITTY_LOGD("Couldn't open maps file %s, error=%s", filePath, strerror(errno));
182              return retMaps;
183          }
184  
185          char line[512] = {0};
186          while (fgets(line, sizeof(line), fp))
187          {
188              ProcMap map;
189              map.pid = pid;
190  
191              char perms[5] = {0}, dev[11] = {0}, pathname[256] = {0};
192              // parse a line in maps file
193              // (format) startAddress-endAddress perms offset dev inode pathname
194              sscanf(line,
195                     "%" SCNxPTR "-%" SCNxPTR " %4s %" SCNxPTR " %s %lu %s",
196                     &map.startAddress,
197                     &map.endAddress,
198                     perms,
199                     &map.offset,
200                     dev,
201                     &map.inode,
202                     pathname);
203  
204              map.length = map.endAddress - map.startAddress;
205              map.dev = dev;
206              map.pathname = pathname;
207  
208              if (perms[0] == 'r')
209              {
210                  map.protection |= PROT_READ;
211                  map.readable = true;
212              }
213              if (perms[1] == 'w')
214              {
215                  map.protection |= PROT_WRITE;
216                  map.writeable = true;
217              }
218              if (perms[2] == 'x')
219              {
220                  map.protection |= PROT_EXEC;
221                  map.executable = true;
222              }
223  
224              map.is_private = (perms[3] == 'p');
225              map.is_shared = (perms[3] == 's');
226  
227              map.is_rx = (strncmp(perms, "r-x", 3) == 0);
228              map.is_rw = (strncmp(perms, "rw-", 3) == 0);
229              map.is_ro = (strncmp(perms, "r--", 3) == 0);
230  
231              retMaps.push_back(map);
232          }
233  
234          fclose(fp);
235  
236          if (retMaps.empty())
237          {
238              KITTY_LOGD("getAllMaps err couldn't find any map");
239          }
240          return retMaps;
241      }
242  
243      std::vector<ProcMap> getMaps(pid_t pid,
244                                   EProcMapFilter filter,
245                                   const std::string &name,
246                                   const std::vector<ProcMap> &maps)
247      {
248          std::vector<ProcMap> retMaps;
249          regex_t re{};
250          bool isRegex = (filter == EProcMapFilter::Regex);
251  
252          if (isRegex)
253          {
254              if (regcomp(&re, name.c_str(), REG_EXTENDED | REG_NOSUB) != 0)
255                  return retMaps;
256          }
257  
258          for (auto &it : (maps.empty() ? getAllMaps(pid) : maps))
259          {
260              if (!it.isValid())
261                  continue;
262  
263              bool match = false;
264              switch (filter)
265              {
266              case EProcMapFilter::Equal:
267                  match = (it.pathname == name);
268                  break;
269              case EProcMapFilter::StartWith:
270                  match = KittyUtils::String::startsWith(it.pathname, name);
271                  break;
272              case EProcMapFilter::EndWith:
273                  match = KittyUtils::String::endsWith(it.pathname, name);
274                  break;
275              case EProcMapFilter::Regex:
276                  match = (regexec(&re, it.pathname.c_str(), 0, NULL, 0) == 0);
277                  break;
278              case EProcMapFilter::Contains:
279              default:
280                  match = KittyUtils::String::contains(it.pathname, name);
281                  break;
282              }
283  
284              if (match)
285              {
286                  retMaps.push_back(it);
287              }
288          }
289  
290          if (isRegex)
291              regfree(&re);
292  
293          return retMaps;
294      }
295  
296      std::vector<ProcMap> getMaps(EProcMapFilter filter, const std::string &name, const std::vector<ProcMap> &maps)
297      {
298          std::vector<ProcMap> retMaps;
299          regex_t re{};
300          bool isRegex = (filter == EProcMapFilter::Regex);
301  
302          if (isRegex)
303          {
304              if (regcomp(&re, name.c_str(), REG_EXTENDED | REG_NOSUB) != 0)
305                  return retMaps;
306          }
307  
308          for (const auto &it : maps)
309          {
310              if (!it.isValid())
311                  continue;
312  
313              bool match = false;
314              switch (filter)
315              {
316              case EProcMapFilter::Equal:
317                  match = (it.pathname == name);
318                  break;
319              case EProcMapFilter::StartWith:
320                  match = KittyUtils::String::startsWith(it.pathname, name);
321                  break;
322              case EProcMapFilter::EndWith:
323                  match = KittyUtils::String::endsWith(it.pathname, name);
324                  break;
325              case EProcMapFilter::Regex:
326                  match = (regexec(&re, it.pathname.c_str(), 0, NULL, 0) == 0);
327                  break;
328              case EProcMapFilter::Contains:
329              default:
330                  match = KittyUtils::String::contains(it.pathname, name);
331                  break;
332              }
333  
334              if (match)
335              {
336                  retMaps.push_back(it);
337              }
338          }
339  
340          if (isRegex)
341              regfree(&re);
342  
343          return retMaps;
344      }
345  
346      ProcMap getAddressMap(pid_t pid, uintptr_t address, const std::vector<ProcMap> &maps)
347      {
348          if (!address)
349              return {};
350  
351          address = KittyUtils::untagHeepPtr(address);
352  
353          if (!maps.empty())
354          {
355              auto it = std::lower_bound(maps.begin(), maps.end(), address, [](const ProcMap &m, uintptr_t val) {
356                  return m.endAddress <= val;
357              });
358  
359              if (it != maps.end() && address >= it->startAddress && address < it->endAddress)
360              {
361                  return *it;
362              }
363          }
364          else
365          {
366              auto pmaps = getAllMaps(pid);
367              auto it = std::lower_bound(pmaps.begin(), pmaps.end(), address, [](const ProcMap &m, uintptr_t val) {
368                  return m.endAddress <= val;
369              });
370  
371              if (it != pmaps.end() && address >= it->startAddress && address < it->endAddress)
372              {
373                  return *it;
374              }
375          }
376  
377          return {};
378      }
379  
380  #ifdef __ANDROID__
381      std::string getAppDirectory(const std::string &pkg)
382      {
383          std::string directory = "/data/app/", base_apk = "base.apk", ret;
384          KittyIOFile::listFilesCallback(directory, [&](const std::string &filePath) {
385              if (KittyUtils::Path::fileName(filePath) == base_apk)
386              {
387                  const std::string fileDir = KittyUtils::Path::fileDirectory(filePath);
388                  if (strstr(fileDir.c_str(), pkg.c_str()))
389                  {
390                      ret = fileDir;
391                      return true;
392                  }
393              }
394              return false;
395          });
396          return ret;
397      }
398  #endif
399  
400  } // namespace KittyMemoryEx