1dda28197Spatrick //===-- LinuxProcMaps.cpp -------------------------------------------------===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick
9061da546Spatrick #include "LinuxProcMaps.h"
10061da546Spatrick #include "lldb/Target/MemoryRegionInfo.h"
11061da546Spatrick #include "lldb/Utility/Status.h"
12061da546Spatrick #include "lldb/Utility/StringExtractor.h"
13be691f3bSpatrick #include "llvm/ADT/StringRef.h"
14*f6aab3d8Srobert #include <optional>
15061da546Spatrick
16061da546Spatrick using namespace lldb_private;
17061da546Spatrick
18be691f3bSpatrick enum class MapsKind { Maps, SMaps };
19061da546Spatrick
ProcMapError(const char * msg,MapsKind kind)20be691f3bSpatrick static llvm::Expected<MemoryRegionInfo> ProcMapError(const char *msg,
21be691f3bSpatrick MapsKind kind) {
22be691f3bSpatrick return llvm::createStringError(llvm::inconvertibleErrorCode(), msg,
23be691f3bSpatrick kind == MapsKind::Maps ? "maps" : "smaps");
24be691f3bSpatrick }
25be691f3bSpatrick
26be691f3bSpatrick static llvm::Expected<MemoryRegionInfo>
ParseMemoryRegionInfoFromProcMapsLine(llvm::StringRef maps_line,MapsKind maps_kind)27be691f3bSpatrick ParseMemoryRegionInfoFromProcMapsLine(llvm::StringRef maps_line,
28be691f3bSpatrick MapsKind maps_kind) {
29be691f3bSpatrick MemoryRegionInfo region;
30061da546Spatrick StringExtractor line_extractor(maps_line);
31061da546Spatrick
32061da546Spatrick // Format: {address_start_hex}-{address_end_hex} perms offset dev inode
33061da546Spatrick // pathname perms: rwxp (letter is present if set, '-' if not, final
34061da546Spatrick // character is p=private, s=shared).
35061da546Spatrick
36061da546Spatrick // Parse out the starting address
37061da546Spatrick lldb::addr_t start_address = line_extractor.GetHexMaxU64(false, 0);
38061da546Spatrick
39061da546Spatrick // Parse out hyphen separating start and end address from range.
40061da546Spatrick if (!line_extractor.GetBytesLeft() || (line_extractor.GetChar() != '-'))
41be691f3bSpatrick return ProcMapError(
42be691f3bSpatrick "malformed /proc/{pid}/%s entry, missing dash between address range",
43be691f3bSpatrick maps_kind);
44061da546Spatrick
45061da546Spatrick // Parse out the ending address
46061da546Spatrick lldb::addr_t end_address = line_extractor.GetHexMaxU64(false, start_address);
47061da546Spatrick
48061da546Spatrick // Parse out the space after the address.
49061da546Spatrick if (!line_extractor.GetBytesLeft() || (line_extractor.GetChar() != ' '))
50be691f3bSpatrick return ProcMapError(
51be691f3bSpatrick "malformed /proc/{pid}/%s entry, missing space after range", maps_kind);
52061da546Spatrick
53061da546Spatrick // Save the range.
54be691f3bSpatrick region.GetRange().SetRangeBase(start_address);
55be691f3bSpatrick region.GetRange().SetRangeEnd(end_address);
56061da546Spatrick
57be691f3bSpatrick // Any memory region in /proc/{pid}/(maps|smaps) is by definition mapped
58be691f3bSpatrick // into the process.
59be691f3bSpatrick region.SetMapped(MemoryRegionInfo::OptionalBool::eYes);
60061da546Spatrick
61061da546Spatrick // Parse out each permission entry.
62061da546Spatrick if (line_extractor.GetBytesLeft() < 4)
63be691f3bSpatrick return ProcMapError(
64be691f3bSpatrick "malformed /proc/{pid}/%s entry, missing some portion of "
65be691f3bSpatrick "permissions",
66be691f3bSpatrick maps_kind);
67061da546Spatrick
68061da546Spatrick // Handle read permission.
69061da546Spatrick const char read_perm_char = line_extractor.GetChar();
70061da546Spatrick if (read_perm_char == 'r')
71be691f3bSpatrick region.SetReadable(MemoryRegionInfo::OptionalBool::eYes);
72061da546Spatrick else if (read_perm_char == '-')
73be691f3bSpatrick region.SetReadable(MemoryRegionInfo::OptionalBool::eNo);
74061da546Spatrick else
75be691f3bSpatrick return ProcMapError("unexpected /proc/{pid}/%s read permission char",
76be691f3bSpatrick maps_kind);
77061da546Spatrick
78061da546Spatrick // Handle write permission.
79061da546Spatrick const char write_perm_char = line_extractor.GetChar();
80061da546Spatrick if (write_perm_char == 'w')
81be691f3bSpatrick region.SetWritable(MemoryRegionInfo::OptionalBool::eYes);
82061da546Spatrick else if (write_perm_char == '-')
83be691f3bSpatrick region.SetWritable(MemoryRegionInfo::OptionalBool::eNo);
84061da546Spatrick else
85be691f3bSpatrick return ProcMapError("unexpected /proc/{pid}/%s write permission char",
86be691f3bSpatrick maps_kind);
87061da546Spatrick
88061da546Spatrick // Handle execute permission.
89061da546Spatrick const char exec_perm_char = line_extractor.GetChar();
90061da546Spatrick if (exec_perm_char == 'x')
91be691f3bSpatrick region.SetExecutable(MemoryRegionInfo::OptionalBool::eYes);
92061da546Spatrick else if (exec_perm_char == '-')
93be691f3bSpatrick region.SetExecutable(MemoryRegionInfo::OptionalBool::eNo);
94061da546Spatrick else
95be691f3bSpatrick return ProcMapError("unexpected /proc/{pid}/%s exec permission char",
96be691f3bSpatrick maps_kind);
97061da546Spatrick
98*f6aab3d8Srobert // Handle sharing status (private/shared).
99*f6aab3d8Srobert const char sharing_char = line_extractor.GetChar();
100*f6aab3d8Srobert if (sharing_char == 's')
101*f6aab3d8Srobert region.SetShared(MemoryRegionInfo::OptionalBool::eYes);
102*f6aab3d8Srobert else if (sharing_char == 'p')
103*f6aab3d8Srobert region.SetShared(MemoryRegionInfo::OptionalBool::eNo);
104*f6aab3d8Srobert else
105*f6aab3d8Srobert region.SetShared(MemoryRegionInfo::OptionalBool::eDontKnow);
106*f6aab3d8Srobert
107061da546Spatrick line_extractor.SkipSpaces(); // Skip the separator
108061da546Spatrick line_extractor.GetHexMaxU64(false, 0); // Read the offset
109061da546Spatrick line_extractor.GetHexMaxU64(false, 0); // Read the major device number
110061da546Spatrick line_extractor.GetChar(); // Read the device id separator
111061da546Spatrick line_extractor.GetHexMaxU64(false, 0); // Read the major device number
112061da546Spatrick line_extractor.SkipSpaces(); // Skip the separator
113061da546Spatrick line_extractor.GetU64(0, 10); // Read the inode number
114061da546Spatrick
115061da546Spatrick line_extractor.SkipSpaces();
116061da546Spatrick const char *name = line_extractor.Peek();
117061da546Spatrick if (name)
118be691f3bSpatrick region.SetName(name);
119061da546Spatrick
120be691f3bSpatrick return region;
121061da546Spatrick }
122061da546Spatrick
ParseLinuxMapRegions(llvm::StringRef linux_map,LinuxMapCallback const & callback)123061da546Spatrick void lldb_private::ParseLinuxMapRegions(llvm::StringRef linux_map,
124061da546Spatrick LinuxMapCallback const &callback) {
125061da546Spatrick llvm::StringRef lines(linux_map);
126061da546Spatrick llvm::StringRef line;
127061da546Spatrick while (!lines.empty()) {
128061da546Spatrick std::tie(line, lines) = lines.split('\n');
129be691f3bSpatrick if (!callback(ParseMemoryRegionInfoFromProcMapsLine(line, MapsKind::Maps)))
130061da546Spatrick break;
131061da546Spatrick }
132061da546Spatrick }
133be691f3bSpatrick
ParseLinuxSMapRegions(llvm::StringRef linux_smap,LinuxMapCallback const & callback)134be691f3bSpatrick void lldb_private::ParseLinuxSMapRegions(llvm::StringRef linux_smap,
135be691f3bSpatrick LinuxMapCallback const &callback) {
136be691f3bSpatrick // Entries in /smaps look like:
137be691f3bSpatrick // 00400000-0048a000 r-xp 00000000 fd:03 960637
138be691f3bSpatrick // Size: 552 kB
139be691f3bSpatrick // Rss: 460 kB
140be691f3bSpatrick // <...>
141be691f3bSpatrick // VmFlags: rd ex mr mw me dw
142be691f3bSpatrick // 00500000-0058a000 rwxp 00000000 fd:03 960637
143be691f3bSpatrick // <...>
144be691f3bSpatrick //
145be691f3bSpatrick // Where the first line is identical to the /maps format
146be691f3bSpatrick // and VmFlags is only printed for kernels >= 3.8.
147be691f3bSpatrick
148be691f3bSpatrick llvm::StringRef lines(linux_smap);
149be691f3bSpatrick llvm::StringRef line;
150*f6aab3d8Srobert std::optional<MemoryRegionInfo> region;
151be691f3bSpatrick
152be691f3bSpatrick while (lines.size()) {
153be691f3bSpatrick std::tie(line, lines) = lines.split('\n');
154be691f3bSpatrick
155be691f3bSpatrick // A property line looks like:
156be691f3bSpatrick // <word>: <value>
157be691f3bSpatrick // (no spaces on the left hand side)
158be691f3bSpatrick // A header will have a ':' but the LHS will contain spaces
159be691f3bSpatrick llvm::StringRef name;
160be691f3bSpatrick llvm::StringRef value;
161be691f3bSpatrick std::tie(name, value) = line.split(':');
162be691f3bSpatrick
163be691f3bSpatrick // If this line is a property line
164be691f3bSpatrick if (!name.contains(' ')) {
165be691f3bSpatrick if (region) {
166be691f3bSpatrick if (name == "VmFlags") {
167be691f3bSpatrick if (value.contains("mt"))
168be691f3bSpatrick region->SetMemoryTagged(MemoryRegionInfo::eYes);
169be691f3bSpatrick else
170be691f3bSpatrick region->SetMemoryTagged(MemoryRegionInfo::eNo);
171be691f3bSpatrick }
172be691f3bSpatrick // Ignore anything else
173be691f3bSpatrick } else {
174be691f3bSpatrick // Orphaned settings line
175be691f3bSpatrick callback(ProcMapError(
176be691f3bSpatrick "Found a property line without a corresponding mapping "
177be691f3bSpatrick "in /proc/{pid}/%s",
178be691f3bSpatrick MapsKind::SMaps));
179be691f3bSpatrick return;
180be691f3bSpatrick }
181be691f3bSpatrick } else {
182be691f3bSpatrick // Must be a new region header
183be691f3bSpatrick if (region) {
184be691f3bSpatrick // Save current region
185be691f3bSpatrick callback(*region);
186be691f3bSpatrick region.reset();
187be691f3bSpatrick }
188be691f3bSpatrick
189be691f3bSpatrick // Try to start a new region
190be691f3bSpatrick llvm::Expected<MemoryRegionInfo> new_region =
191be691f3bSpatrick ParseMemoryRegionInfoFromProcMapsLine(line, MapsKind::SMaps);
192be691f3bSpatrick if (new_region) {
193be691f3bSpatrick region = *new_region;
194be691f3bSpatrick } else {
195be691f3bSpatrick // Stop at first invalid region header
196be691f3bSpatrick callback(new_region.takeError());
197be691f3bSpatrick return;
198be691f3bSpatrick }
199be691f3bSpatrick }
200be691f3bSpatrick }
201be691f3bSpatrick
202be691f3bSpatrick // Catch last region
203be691f3bSpatrick if (region)
204be691f3bSpatrick callback(*region);
205be691f3bSpatrick }
206