sanitizer_procmaps_common.cpp 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  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-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. //
  9. // Information about the process mappings (common parts).
  10. //===----------------------------------------------------------------------===//
  11. #include "sanitizer_platform.h"
  12. #if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \
  13. SANITIZER_SOLARIS
  14. #include "sanitizer_common.h"
  15. #include "sanitizer_placement_new.h"
  16. #include "sanitizer_procmaps.h"
  17. namespace __sanitizer {
  18. static ProcSelfMapsBuff cached_proc_self_maps;
  19. static StaticSpinMutex cache_lock;
  20. static int TranslateDigit(char c) {
  21. if (c >= '0' && c <= '9')
  22. return c - '0';
  23. if (c >= 'a' && c <= 'f')
  24. return c - 'a' + 10;
  25. if (c >= 'A' && c <= 'F')
  26. return c - 'A' + 10;
  27. return -1;
  28. }
  29. // Parse a number and promote 'p' up to the first non-digit character.
  30. static uptr ParseNumber(const char **p, int base) {
  31. uptr n = 0;
  32. int d;
  33. CHECK(base >= 2 && base <= 16);
  34. while ((d = TranslateDigit(**p)) >= 0 && d < base) {
  35. n = n * base + d;
  36. (*p)++;
  37. }
  38. return n;
  39. }
  40. bool IsDecimal(char c) {
  41. int d = TranslateDigit(c);
  42. return d >= 0 && d < 10;
  43. }
  44. uptr ParseDecimal(const char **p) {
  45. return ParseNumber(p, 10);
  46. }
  47. bool IsHex(char c) {
  48. int d = TranslateDigit(c);
  49. return d >= 0 && d < 16;
  50. }
  51. uptr ParseHex(const char **p) {
  52. return ParseNumber(p, 16);
  53. }
  54. void MemoryMappedSegment::AddAddressRanges(LoadedModule *module) {
  55. // data_ should be unused on this platform
  56. CHECK(!data_);
  57. module->addAddressRange(start, end, IsExecutable(), IsWritable());
  58. }
  59. MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) {
  60. // FIXME: in the future we may want to cache the mappings on demand only.
  61. if (cache_enabled)
  62. CacheMemoryMappings();
  63. // Read maps after the cache update to capture the maps/unmaps happening in
  64. // the process of updating.
  65. ReadProcMaps(&data_.proc_self_maps);
  66. if (cache_enabled && data_.proc_self_maps.mmaped_size == 0)
  67. LoadFromCache();
  68. Reset();
  69. }
  70. bool MemoryMappingLayout::Error() const {
  71. return data_.current == nullptr;
  72. }
  73. MemoryMappingLayout::~MemoryMappingLayout() {
  74. // Only unmap the buffer if it is different from the cached one. Otherwise
  75. // it will be unmapped when the cache is refreshed.
  76. if (data_.proc_self_maps.data != cached_proc_self_maps.data)
  77. UnmapOrDie(data_.proc_self_maps.data, data_.proc_self_maps.mmaped_size);
  78. }
  79. void MemoryMappingLayout::Reset() {
  80. data_.current = data_.proc_self_maps.data;
  81. }
  82. // static
  83. void MemoryMappingLayout::CacheMemoryMappings() {
  84. ProcSelfMapsBuff new_proc_self_maps;
  85. ReadProcMaps(&new_proc_self_maps);
  86. // Don't invalidate the cache if the mappings are unavailable.
  87. if (new_proc_self_maps.mmaped_size == 0)
  88. return;
  89. SpinMutexLock l(&cache_lock);
  90. if (cached_proc_self_maps.mmaped_size)
  91. UnmapOrDie(cached_proc_self_maps.data, cached_proc_self_maps.mmaped_size);
  92. cached_proc_self_maps = new_proc_self_maps;
  93. }
  94. void MemoryMappingLayout::LoadFromCache() {
  95. SpinMutexLock l(&cache_lock);
  96. if (cached_proc_self_maps.data)
  97. data_.proc_self_maps = cached_proc_self_maps;
  98. }
  99. void MemoryMappingLayout::DumpListOfModules(
  100. InternalMmapVectorNoCtor<LoadedModule> *modules) {
  101. Reset();
  102. InternalMmapVector<char> module_name(kMaxPathLength);
  103. MemoryMappedSegment segment(module_name.data(), module_name.size());
  104. for (uptr i = 0; Next(&segment); i++) {
  105. const char *cur_name = segment.filename;
  106. if (cur_name[0] == '\0')
  107. continue;
  108. // Don't subtract 'cur_beg' from the first entry:
  109. // * If a binary is compiled w/o -pie, then the first entry in
  110. // process maps is likely the binary itself (all dynamic libs
  111. // are mapped higher in address space). For such a binary,
  112. // instruction offset in binary coincides with the actual
  113. // instruction address in virtual memory (as code section
  114. // is mapped to a fixed memory range).
  115. // * If a binary is compiled with -pie, all the modules are
  116. // mapped high at address space (in particular, higher than
  117. // shadow memory of the tool), so the module can't be the
  118. // first entry.
  119. uptr base_address = (i ? segment.start : 0) - segment.offset;
  120. LoadedModule cur_module;
  121. cur_module.set(cur_name, base_address);
  122. segment.AddAddressRanges(&cur_module);
  123. modules->push_back(cur_module);
  124. }
  125. }
  126. void GetMemoryProfile(fill_profile_f cb, uptr *stats) {
  127. char *smaps = nullptr;
  128. uptr smaps_cap = 0;
  129. uptr smaps_len = 0;
  130. if (!ReadFileToBuffer("/proc/self/smaps", &smaps, &smaps_cap, &smaps_len))
  131. return;
  132. ParseUnixMemoryProfile(cb, stats, smaps, smaps_len);
  133. UnmapOrDie(smaps, smaps_cap);
  134. }
  135. void ParseUnixMemoryProfile(fill_profile_f cb, uptr *stats, char *smaps,
  136. uptr smaps_len) {
  137. uptr start = 0;
  138. bool file = false;
  139. const char *pos = smaps;
  140. char *end = smaps + smaps_len;
  141. if (smaps_len < 2)
  142. return;
  143. // The following parsing can crash on almost every line
  144. // in the case of malformed/truncated input.
  145. // Fixing that is hard b/c e.g. ParseDecimal does not
  146. // even accept end of the buffer and assumes well-formed input.
  147. // So instead we patch end of the input a bit,
  148. // it does not affect well-formed complete inputs.
  149. *--end = 0;
  150. *--end = '\n';
  151. while (pos < end) {
  152. if (IsHex(pos[0])) {
  153. start = ParseHex(&pos);
  154. for (; *pos != '/' && *pos > '\n'; pos++) {}
  155. file = *pos == '/';
  156. } else if (internal_strncmp(pos, "Rss:", 4) == 0) {
  157. while (pos < end && !IsDecimal(*pos)) pos++;
  158. uptr rss = ParseDecimal(&pos) * 1024;
  159. cb(start, rss, file, stats);
  160. }
  161. while (*pos++ != '\n') {}
  162. }
  163. }
  164. } // namespace __sanitizer
  165. #endif