brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.9 KiB · 7214a2b Raw
193 lines · cpp
1//===-- sanitizer_procmaps_common.cpp -------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// Information about the process mappings (common parts).10//===----------------------------------------------------------------------===//11 12#include "sanitizer_platform.h"13 14#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD ||                \15    SANITIZER_SOLARIS16 17#include "sanitizer_common.h"18#include "sanitizer_placement_new.h"19#include "sanitizer_procmaps.h"20 21namespace __sanitizer {22 23static ProcSelfMapsBuff cached_proc_self_maps;24static StaticSpinMutex cache_lock;25 26static int TranslateDigit(char c) {27  if (c >= '0' && c <= '9')28    return c - '0';29  if (c >= 'a' && c <= 'f')30    return c - 'a' + 10;31  if (c >= 'A' && c <= 'F')32    return c - 'A' + 10;33  return -1;34}35 36// Parse a number and promote 'p' up to the first non-digit character.37static uptr ParseNumber(const char **p, int base) {38  uptr n = 0;39  int d;40  CHECK(base >= 2 && base <= 16);41  while ((d = TranslateDigit(**p)) >= 0 && d < base) {42    n = n * base + d;43    (*p)++;44  }45  return n;46}47 48bool IsDecimal(char c) {49  int d = TranslateDigit(c);50  return d >= 0 && d < 10;51}52 53uptr ParseDecimal(const char **p) {54  return ParseNumber(p, 10);55}56 57bool IsHex(char c) {58  int d = TranslateDigit(c);59  return d >= 0 && d < 16;60}61 62uptr ParseHex(const char **p) {63  return ParseNumber(p, 16);64}65 66void MemoryMappedSegment::AddAddressRanges(LoadedModule *module) {67  // data_ should be unused on this platform68  CHECK(!data_);69  module->addAddressRange(start, end, IsExecutable(), IsWritable());70}71 72MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) {73  // FIXME: in the future we may want to cache the mappings on demand only.74  if (cache_enabled)75    CacheMemoryMappings();76 77  // Read maps after the cache update to capture the maps/unmaps happening in78  // the process of updating.79  ReadProcMaps(&data_.proc_self_maps);80  if (cache_enabled && data_.proc_self_maps.mmaped_size == 0)81    LoadFromCache();82 83  Reset();84}85 86bool MemoryMappingLayout::Error() const {87  return data_.current == nullptr;88}89 90MemoryMappingLayout::~MemoryMappingLayout() {91  // Only unmap the buffer if it is different from the cached one. Otherwise92  // it will be unmapped when the cache is refreshed.93  if (data_.proc_self_maps.data != cached_proc_self_maps.data)94    UnmapOrDie(data_.proc_self_maps.data, data_.proc_self_maps.mmaped_size);95}96 97void MemoryMappingLayout::Reset() {98  data_.current = data_.proc_self_maps.data;99}100 101// static102void MemoryMappingLayout::CacheMemoryMappings() {103  ProcSelfMapsBuff new_proc_self_maps;104  ReadProcMaps(&new_proc_self_maps);105  // Don't invalidate the cache if the mappings are unavailable.106  if (new_proc_self_maps.mmaped_size == 0)107    return;108  SpinMutexLock l(&cache_lock);109  if (cached_proc_self_maps.mmaped_size)110    UnmapOrDie(cached_proc_self_maps.data, cached_proc_self_maps.mmaped_size);111  cached_proc_self_maps = new_proc_self_maps;112}113 114void MemoryMappingLayout::LoadFromCache() {115  SpinMutexLock l(&cache_lock);116  if (cached_proc_self_maps.data)117    data_.proc_self_maps = cached_proc_self_maps;118}119 120void MemoryMappingLayout::DumpListOfModules(121    InternalMmapVectorNoCtor<LoadedModule> *modules) {122  Reset();123  InternalMmapVector<char> module_name(kMaxPathLength);124  MemoryMappedSegment segment(module_name.data(), module_name.size());125  for (uptr i = 0; Next(&segment); i++) {126    const char *cur_name = segment.filename;127    if (cur_name[0] == '\0')128      continue;129    // Don't subtract 'cur_beg' from the first entry:130    // * If a binary is compiled w/o -pie, then the first entry in131    //   process maps is likely the binary itself (all dynamic libs132    //   are mapped higher in address space). For such a binary,133    //   instruction offset in binary coincides with the actual134    //   instruction address in virtual memory (as code section135    //   is mapped to a fixed memory range).136    // * If a binary is compiled with -pie, all the modules are137    //   mapped high at address space (in particular, higher than138    //   shadow memory of the tool), so the module can't be the139    //   first entry.140    uptr base_address = (i ? segment.start : 0) - segment.offset;141    LoadedModule cur_module;142    cur_module.set(cur_name, base_address);143    segment.AddAddressRanges(&cur_module);144    modules->push_back(cur_module);145  }146}147 148#if SANITIZER_LINUX || SANITIZER_ANDROID || SANITIZER_SOLARIS149void GetMemoryProfile(fill_profile_f cb, uptr *stats) {150  char *smaps = nullptr;151  uptr smaps_cap = 0;152  uptr smaps_len = 0;153  if (!ReadFileToBuffer("/proc/self/smaps", &smaps, &smaps_cap, &smaps_len))154    return;155  ParseUnixMemoryProfile(cb, stats, smaps, smaps_len);156  UnmapOrDie(smaps, smaps_cap);157}158 159void ParseUnixMemoryProfile(fill_profile_f cb, uptr *stats, char *smaps,160                            uptr smaps_len) {161  uptr start = 0;162  bool file = false;163  const char *pos = smaps;164  char *end = smaps + smaps_len;165  if (smaps_len < 2)166    return;167  // The following parsing can crash on almost every line168  // in the case of malformed/truncated input.169  // Fixing that is hard b/c e.g. ParseDecimal does not170  // even accept end of the buffer and assumes well-formed input.171  // So instead we patch end of the input a bit,172  // it does not affect well-formed complete inputs.173  *--end = 0;174  *--end = '\n';175  while (pos < end) {176    if (IsHex(pos[0])) {177      start = ParseHex(&pos);178      for (; *pos != '/' && *pos > '\n'; pos++) {}179      file = *pos == '/';180    } else if (internal_strncmp(pos, "Rss:", 4) == 0) {181      while (pos < end && !IsDecimal(*pos)) pos++;182      uptr rss = ParseDecimal(&pos) * 1024;183      cb(start, rss, file, stats);184    }185    while (*pos++ != '\n') {}186  }187}188#endif189 190} // namespace __sanitizer191 192#endif193