1 // Copyright 2013 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef BASE_DEBUG_PROC_MAPS_LINUX_H_ 6 #define BASE_DEBUG_PROC_MAPS_LINUX_H_ 7 8 #include <stdint.h> 9 10 #include <optional> 11 #include <string> 12 #include <vector> 13 14 #include "base/base_export.h" 15 16 namespace base::debug { 17 18 // Describes a region of mapped memory and the path of the file mapped. 19 struct BASE_EXPORT MappedMemoryRegion { 20 enum Permission { 21 READ = 1 << 0, 22 WRITE = 1 << 1, 23 EXECUTE = 1 << 2, 24 PRIVATE = 1 << 3, // If set, region is private, otherwise it is shared. 25 }; 26 27 MappedMemoryRegion(); 28 MappedMemoryRegion(const MappedMemoryRegion&); 29 MappedMemoryRegion(MappedMemoryRegion&&) noexcept; 30 31 // The address range [start,end) of mapped memory. 32 uintptr_t start; 33 uintptr_t end; 34 35 // Byte offset into |path| of the range mapped into memory. 36 unsigned long long offset; 37 38 // Image base, if this mapping corresponds to an ELF image. 39 uintptr_t base; 40 41 // Bitmask of read/write/execute/private/shared permissions. 42 uint8_t permissions; 43 44 // Major and minor device numbers for the region. 45 uint8_t dev_major; 46 uint8_t dev_minor; 47 48 // Inode for the region. 49 long inode; 50 51 // Name of the file mapped into memory. 52 // 53 // NOTE: path names aren't guaranteed to point at valid files. For example, 54 // "[heap]" and "[stack]" are used to represent the location of the process' 55 // heap and stack, respectively. 56 std::string path; 57 }; 58 59 // Reads the data from /proc/self/maps and stores the result in |proc_maps|. 60 // Returns true if successful, false otherwise. 61 // 62 // There is *NO* guarantee that the resulting contents will be free of 63 // duplicates or even contain valid entries by time the method returns. 64 // 65 // 66 // THE GORY DETAILS 67 // 68 // Did you know it's next-to-impossible to atomically read the whole contents 69 // of /proc/<pid>/maps? You would think that if we passed in a large-enough 70 // buffer to read() that It Should Just Work(tm), but sadly that's not the case. 71 // 72 // Linux's procfs uses seq_file [1] for handling iteration, text formatting, 73 // and dealing with resulting data that is larger than the size of a page. That 74 // last bit is especially important because it means that seq_file will never 75 // return more than the size of a page in a single call to read(). 76 // 77 // Unfortunately for a program like Chrome the size of /proc/self/maps is 78 // larger than the size of page so we're forced to call read() multiple times. 79 // If the virtual memory table changed in any way between calls to read() (e.g., 80 // a different thread calling mprotect()), it can make seq_file generate 81 // duplicate entries or skip entries. 82 // 83 // Even if seq_file was changed to keep flushing the contents of its page-sized 84 // buffer to the usermode buffer inside a single call to read(), it has to 85 // release its lock on the virtual memory table to handle page faults while 86 // copying data to usermode. This puts us in the same situation where the table 87 // can change while we're copying data. 88 // 89 // Alternatives such as fork()-and-suspend-the-parent-while-child-reads were 90 // attempted, but they present more subtle problems than it's worth. Depending 91 // on your use case your best bet may be to read /proc/<pid>/maps prior to 92 // starting other threads. 93 // 94 // [1] http://kernelnewbies.org/Documents/SeqFileHowTo 95 BASE_EXPORT bool ReadProcMaps(std::string* proc_maps); 96 97 // Parses /proc/<pid>/maps input data and stores in |regions|. Returns true 98 // and updates |regions| if and only if all of |input| was successfully parsed. 99 BASE_EXPORT bool ParseProcMaps(const std::string& input, 100 std::vector<MappedMemoryRegion>* regions); 101 102 struct SmapsRollup { 103 size_t rss = 0; 104 size_t pss = 0; 105 size_t pss_anon = 0; 106 size_t pss_file = 0; 107 size_t pss_shmem = 0; 108 size_t private_dirty = 0; 109 size_t swap = 0; 110 size_t swap_pss = 0; 111 }; 112 113 // Attempts to read /proc/self/smaps_rollup. Returns nullopt on error. 114 BASE_EXPORT std::optional<SmapsRollup> ReadAndParseSmapsRollup(); 115 116 // |smaps_rollup| should be the result of reading /proc/*/smaps_rollup. 117 BASE_EXPORT std::optional<SmapsRollup> ParseSmapsRollupForTesting( 118 const std::string& smaps_rollup); 119 120 } // namespace base::debug 121 122 #endif // BASE_DEBUG_PROC_MAPS_LINUX_H_ 123