blob: 2fb43c7b24438b3e5707ae39b18e26e463bfa264 [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 Engelbrechta383afc2024-10-10 20:59:4930
[email protected]959a8bf2013-07-03 02:02:2331 // 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
Peter Collingbourne1a3ed312017-11-04 02:37:4738 // Image base, if this mapping corresponds to an ELF image.
39 uintptr_t base;
40
[email protected]959a8bf2013-07-03 02:02:2341 // Bitmask of read/write/execute/private/shared permissions.
aviebe805c2015-12-24 08:20:2842 uint8_t permissions;
[email protected]959a8bf2013-07-03 02:02:2343
Thiabaud Engelbrechtf815dc4c2024-10-31 02:17:0944 // Major and minor device numbers for the region.
Thiabaud Engelbrechta383afc2024-10-10 20:59:4945 uint8_t dev_major;
46 uint8_t dev_minor;
Thiabaud Engelbrechtf815dc4c2024-10-31 02:17:0947
48 // Inode for the region.
Thiabaud Engelbrechta383afc2024-10-10 20:59:4949 long inode;
50
[email protected]959a8bf2013-07-03 02:02:2351 // 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.
[email protected]35c80832013-09-06 05:07:5061//
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
[email protected]959a8bf2013-07-03 02:02:2395BASE_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.
99BASE_EXPORT bool ParseProcMaps(const std::string& input,
100 std::vector<MappedMemoryRegion>* regions);
101
Thiabaud Engelbrecht309fb1ea2024-10-29 15:35:19102struct 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.
114BASE_EXPORT std::optional<SmapsRollup> ReadAndParseSmapsRollup();
115
116// |smaps_rollup| should be the result of reading /proc/*/smaps_rollup.
117BASE_EXPORT std::optional<SmapsRollup> ParseSmapsRollupForTesting(
118 const std::string& smaps_rollup);
119
120} // namespace base::debug
[email protected]959a8bf2013-07-03 02:02:23121
122#endif // BASE_DEBUG_PROC_MAPS_LINUX_H_