blob: 700db87a5a23129d4986d0b86252bb8a09b3d430 [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2013 The Chromium Authors
[email protected]959a8bf2013-07-03 02:02:232// 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
aviebe805c2015-12-24 08:20:288#include <stdint.h>
9
Thiabaud Engelbrecht309fb1ea2024-10-29 15:35:1910#include <optional>
[email protected]959a8bf2013-07-03 02:02:2311#include <string>
12#include <vector>
13
14#include "base/base_export.h"
[email protected]959a8bf2013-07-03 02:02:2315
Thiabaud Engelbrecht309fb1ea2024-10-29 15:35:1916namespace base::debug {
[email protected]959a8bf2013-07-03 02:02:2317
18// Describes a region of mapped memory and the path of the file mapped.
Thiabaud Engelbrecht50b1fd72024-10-18 19:53:5319struct BASE_EXPORT MappedMemoryRegion {
[email protected]959a8bf2013-07-03 02:02:2320 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
Thiabaud Engelbrechta383afc2024-10-10 20:59:4927 MappedMemoryRegion();
28 MappedMemoryRegion(const MappedMemoryRegion&);
Thiabaud Engelbrechtf815dc4c2024-10-31 02:17:0929 MappedMemoryRegion(MappedMemoryRegion&&) noexcept;
Thiabaud Engelbrecht7e160bc2024-12-09 17:57:4830 MappedMemoryRegion& operator=(MappedMemoryRegion&);
31 MappedMemoryRegion& operator=(MappedMemoryRegion&&) noexcept;
Thiabaud Engelbrechta383afc2024-10-10 20:59:4932
[email protected]959a8bf2013-07-03 02:02:2333 // The address range [start,end) of mapped memory.
34 uintptr_t start;
35 uintptr_t end;
36
37 // Byte offset into |path| of the range mapped into memory.
38 unsigned long long offset;
39
Peter Collingbourne1a3ed312017-11-04 02:37:4740 // Image base, if this mapping corresponds to an ELF image.
41 uintptr_t base;
42
[email protected]959a8bf2013-07-03 02:02:2343 // Bitmask of read/write/execute/private/shared permissions.
aviebe805c2015-12-24 08:20:2844 uint8_t permissions;
[email protected]959a8bf2013-07-03 02:02:2345
Thiabaud Engelbrechtf815dc4c2024-10-31 02:17:0946 // Major and minor device numbers for the region.
Thiabaud Engelbrechta383afc2024-10-10 20:59:4947 uint8_t dev_major;
48 uint8_t dev_minor;
Thiabaud Engelbrechtf815dc4c2024-10-31 02:17:0949
50 // Inode for the region.
Thiabaud Engelbrechta383afc2024-10-10 20:59:4951 long inode;
52
[email protected]959a8bf2013-07-03 02:02:2353 // Name of the file mapped into memory.
54 //
55 // NOTE: path names aren't guaranteed to point at valid files. For example,
56 // "[heap]" and "[stack]" are used to represent the location of the process'
57 // heap and stack, respectively.
58 std::string path;
59};
60
61// Reads the data from /proc/self/maps and stores the result in |proc_maps|.
62// Returns true if successful, false otherwise.
[email protected]35c80832013-09-06 05:07:5063//
64// There is *NO* guarantee that the resulting contents will be free of
65// duplicates or even contain valid entries by time the method returns.
66//
67//
68// THE GORY DETAILS
69//
70// Did you know it's next-to-impossible to atomically read the whole contents
71// of /proc/<pid>/maps? You would think that if we passed in a large-enough
72// buffer to read() that It Should Just Work(tm), but sadly that's not the case.
73//
74// Linux's procfs uses seq_file [1] for handling iteration, text formatting,
75// and dealing with resulting data that is larger than the size of a page. That
76// last bit is especially important because it means that seq_file will never
77// return more than the size of a page in a single call to read().
78//
79// Unfortunately for a program like Chrome the size of /proc/self/maps is
80// larger than the size of page so we're forced to call read() multiple times.
81// If the virtual memory table changed in any way between calls to read() (e.g.,
82// a different thread calling mprotect()), it can make seq_file generate
83// duplicate entries or skip entries.
84//
85// Even if seq_file was changed to keep flushing the contents of its page-sized
86// buffer to the usermode buffer inside a single call to read(), it has to
87// release its lock on the virtual memory table to handle page faults while
88// copying data to usermode. This puts us in the same situation where the table
89// can change while we're copying data.
90//
91// Alternatives such as fork()-and-suspend-the-parent-while-child-reads were
92// attempted, but they present more subtle problems than it's worth. Depending
93// on your use case your best bet may be to read /proc/<pid>/maps prior to
94// starting other threads.
95//
96// [1] http://kernelnewbies.org/Documents/SeqFileHowTo
[email protected]959a8bf2013-07-03 02:02:2397BASE_EXPORT bool ReadProcMaps(std::string* proc_maps);
98
99// Parses /proc/<pid>/maps input data and stores in |regions|. Returns true
100// and updates |regions| if and only if all of |input| was successfully parsed.
101BASE_EXPORT bool ParseProcMaps(const std::string& input,
102 std::vector<MappedMemoryRegion>* regions);
103
Thiabaud Engelbrecht309fb1ea2024-10-29 15:35:19104struct SmapsRollup {
105 size_t rss = 0;
106 size_t pss = 0;
107 size_t pss_anon = 0;
108 size_t pss_file = 0;
109 size_t pss_shmem = 0;
110 size_t private_dirty = 0;
111 size_t swap = 0;
112 size_t swap_pss = 0;
113};
114
115// Attempts to read /proc/self/smaps_rollup. Returns nullopt on error.
116BASE_EXPORT std::optional<SmapsRollup> ReadAndParseSmapsRollup();
117
118// |smaps_rollup| should be the result of reading /proc/*/smaps_rollup.
119BASE_EXPORT std::optional<SmapsRollup> ParseSmapsRollupForTesting(
120 const std::string& smaps_rollup);
121
122} // namespace base::debug
[email protected]959a8bf2013-07-03 02:02:23123
124#endif // BASE_DEBUG_PROC_MAPS_LINUX_H_