1cfca06d7SDimitry Andric //===-- LinuxProcMaps.cpp -------------------------------------------------===//
294994d37SDimitry Andric //
35f29bb8aSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45f29bb8aSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
55f29bb8aSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
694994d37SDimitry Andric //
794994d37SDimitry Andric //===----------------------------------------------------------------------===//
894994d37SDimitry Andric
994994d37SDimitry Andric #include "LinuxProcMaps.h"
1094994d37SDimitry Andric #include "lldb/Target/MemoryRegionInfo.h"
1194994d37SDimitry Andric #include "lldb/Utility/Status.h"
1294994d37SDimitry Andric #include "lldb/Utility/StringExtractor.h"
13b60736ecSDimitry Andric #include "llvm/ADT/StringRef.h"
14e3b55780SDimitry Andric #include <optional>
1594994d37SDimitry Andric
1694994d37SDimitry Andric using namespace lldb_private;
1794994d37SDimitry Andric
18b60736ecSDimitry Andric enum class MapsKind { Maps, SMaps };
1994994d37SDimitry Andric
ProcMapError(const char * msg,MapsKind kind)20b60736ecSDimitry Andric static llvm::Expected<MemoryRegionInfo> ProcMapError(const char *msg,
21b60736ecSDimitry Andric MapsKind kind) {
22b60736ecSDimitry Andric return llvm::createStringError(llvm::inconvertibleErrorCode(), msg,
23b60736ecSDimitry Andric kind == MapsKind::Maps ? "maps" : "smaps");
24b60736ecSDimitry Andric }
25b60736ecSDimitry Andric
26b60736ecSDimitry Andric static llvm::Expected<MemoryRegionInfo>
ParseMemoryRegionInfoFromProcMapsLine(llvm::StringRef maps_line,MapsKind maps_kind)27b60736ecSDimitry Andric ParseMemoryRegionInfoFromProcMapsLine(llvm::StringRef maps_line,
28b60736ecSDimitry Andric MapsKind maps_kind) {
29b60736ecSDimitry Andric MemoryRegionInfo region;
3094994d37SDimitry Andric StringExtractor line_extractor(maps_line);
3194994d37SDimitry Andric
3294994d37SDimitry Andric // Format: {address_start_hex}-{address_end_hex} perms offset dev inode
3394994d37SDimitry Andric // pathname perms: rwxp (letter is present if set, '-' if not, final
3494994d37SDimitry Andric // character is p=private, s=shared).
3594994d37SDimitry Andric
3694994d37SDimitry Andric // Parse out the starting address
3794994d37SDimitry Andric lldb::addr_t start_address = line_extractor.GetHexMaxU64(false, 0);
3894994d37SDimitry Andric
3994994d37SDimitry Andric // Parse out hyphen separating start and end address from range.
4094994d37SDimitry Andric if (!line_extractor.GetBytesLeft() || (line_extractor.GetChar() != '-'))
41b60736ecSDimitry Andric return ProcMapError(
42b60736ecSDimitry Andric "malformed /proc/{pid}/%s entry, missing dash between address range",
43b60736ecSDimitry Andric maps_kind);
4494994d37SDimitry Andric
4594994d37SDimitry Andric // Parse out the ending address
4694994d37SDimitry Andric lldb::addr_t end_address = line_extractor.GetHexMaxU64(false, start_address);
4794994d37SDimitry Andric
4894994d37SDimitry Andric // Parse out the space after the address.
4994994d37SDimitry Andric if (!line_extractor.GetBytesLeft() || (line_extractor.GetChar() != ' '))
50b60736ecSDimitry Andric return ProcMapError(
51b60736ecSDimitry Andric "malformed /proc/{pid}/%s entry, missing space after range", maps_kind);
5294994d37SDimitry Andric
5394994d37SDimitry Andric // Save the range.
54b60736ecSDimitry Andric region.GetRange().SetRangeBase(start_address);
55b60736ecSDimitry Andric region.GetRange().SetRangeEnd(end_address);
5694994d37SDimitry Andric
57b60736ecSDimitry Andric // Any memory region in /proc/{pid}/(maps|smaps) is by definition mapped
58b60736ecSDimitry Andric // into the process.
59b60736ecSDimitry Andric region.SetMapped(MemoryRegionInfo::OptionalBool::eYes);
6094994d37SDimitry Andric
6194994d37SDimitry Andric // Parse out each permission entry.
6294994d37SDimitry Andric if (line_extractor.GetBytesLeft() < 4)
63b60736ecSDimitry Andric return ProcMapError(
64b60736ecSDimitry Andric "malformed /proc/{pid}/%s entry, missing some portion of "
65b60736ecSDimitry Andric "permissions",
66b60736ecSDimitry Andric maps_kind);
6794994d37SDimitry Andric
6894994d37SDimitry Andric // Handle read permission.
6994994d37SDimitry Andric const char read_perm_char = line_extractor.GetChar();
7094994d37SDimitry Andric if (read_perm_char == 'r')
71b60736ecSDimitry Andric region.SetReadable(MemoryRegionInfo::OptionalBool::eYes);
7294994d37SDimitry Andric else if (read_perm_char == '-')
73b60736ecSDimitry Andric region.SetReadable(MemoryRegionInfo::OptionalBool::eNo);
7494994d37SDimitry Andric else
75b60736ecSDimitry Andric return ProcMapError("unexpected /proc/{pid}/%s read permission char",
76b60736ecSDimitry Andric maps_kind);
7794994d37SDimitry Andric
7894994d37SDimitry Andric // Handle write permission.
7994994d37SDimitry Andric const char write_perm_char = line_extractor.GetChar();
8094994d37SDimitry Andric if (write_perm_char == 'w')
81b60736ecSDimitry Andric region.SetWritable(MemoryRegionInfo::OptionalBool::eYes);
8294994d37SDimitry Andric else if (write_perm_char == '-')
83b60736ecSDimitry Andric region.SetWritable(MemoryRegionInfo::OptionalBool::eNo);
8494994d37SDimitry Andric else
85b60736ecSDimitry Andric return ProcMapError("unexpected /proc/{pid}/%s write permission char",
86b60736ecSDimitry Andric maps_kind);
8794994d37SDimitry Andric
8894994d37SDimitry Andric // Handle execute permission.
8994994d37SDimitry Andric const char exec_perm_char = line_extractor.GetChar();
9094994d37SDimitry Andric if (exec_perm_char == 'x')
91b60736ecSDimitry Andric region.SetExecutable(MemoryRegionInfo::OptionalBool::eYes);
9294994d37SDimitry Andric else if (exec_perm_char == '-')
93b60736ecSDimitry Andric region.SetExecutable(MemoryRegionInfo::OptionalBool::eNo);
9494994d37SDimitry Andric else
95b60736ecSDimitry Andric return ProcMapError("unexpected /proc/{pid}/%s exec permission char",
96b60736ecSDimitry Andric maps_kind);
9794994d37SDimitry Andric
98145449b1SDimitry Andric // Handle sharing status (private/shared).
99145449b1SDimitry Andric const char sharing_char = line_extractor.GetChar();
100145449b1SDimitry Andric if (sharing_char == 's')
101145449b1SDimitry Andric region.SetShared(MemoryRegionInfo::OptionalBool::eYes);
102145449b1SDimitry Andric else if (sharing_char == 'p')
103145449b1SDimitry Andric region.SetShared(MemoryRegionInfo::OptionalBool::eNo);
104145449b1SDimitry Andric else
105145449b1SDimitry Andric region.SetShared(MemoryRegionInfo::OptionalBool::eDontKnow);
106145449b1SDimitry Andric
10794994d37SDimitry Andric line_extractor.SkipSpaces(); // Skip the separator
10894994d37SDimitry Andric line_extractor.GetHexMaxU64(false, 0); // Read the offset
10994994d37SDimitry Andric line_extractor.GetHexMaxU64(false, 0); // Read the major device number
11094994d37SDimitry Andric line_extractor.GetChar(); // Read the device id separator
11194994d37SDimitry Andric line_extractor.GetHexMaxU64(false, 0); // Read the major device number
11294994d37SDimitry Andric line_extractor.SkipSpaces(); // Skip the separator
11394994d37SDimitry Andric line_extractor.GetU64(0, 10); // Read the inode number
11494994d37SDimitry Andric
11594994d37SDimitry Andric line_extractor.SkipSpaces();
11694994d37SDimitry Andric const char *name = line_extractor.Peek();
11794994d37SDimitry Andric if (name)
118b60736ecSDimitry Andric region.SetName(name);
11994994d37SDimitry Andric
120b60736ecSDimitry Andric return region;
12194994d37SDimitry Andric }
12294994d37SDimitry Andric
ParseLinuxMapRegions(llvm::StringRef linux_map,LinuxMapCallback const & callback)12394994d37SDimitry Andric void lldb_private::ParseLinuxMapRegions(llvm::StringRef linux_map,
12494994d37SDimitry Andric LinuxMapCallback const &callback) {
12594994d37SDimitry Andric llvm::StringRef lines(linux_map);
12694994d37SDimitry Andric llvm::StringRef line;
12794994d37SDimitry Andric while (!lines.empty()) {
12894994d37SDimitry Andric std::tie(line, lines) = lines.split('\n');
129b60736ecSDimitry Andric if (!callback(ParseMemoryRegionInfoFromProcMapsLine(line, MapsKind::Maps)))
13094994d37SDimitry Andric break;
13194994d37SDimitry Andric }
13294994d37SDimitry Andric }
133b60736ecSDimitry Andric
ParseLinuxSMapRegions(llvm::StringRef linux_smap,LinuxMapCallback const & callback)134b60736ecSDimitry Andric void lldb_private::ParseLinuxSMapRegions(llvm::StringRef linux_smap,
135b60736ecSDimitry Andric LinuxMapCallback const &callback) {
136b60736ecSDimitry Andric // Entries in /smaps look like:
137b60736ecSDimitry Andric // 00400000-0048a000 r-xp 00000000 fd:03 960637
138b60736ecSDimitry Andric // Size: 552 kB
139b60736ecSDimitry Andric // Rss: 460 kB
140b60736ecSDimitry Andric // <...>
141b60736ecSDimitry Andric // VmFlags: rd ex mr mw me dw
142b60736ecSDimitry Andric // 00500000-0058a000 rwxp 00000000 fd:03 960637
143b60736ecSDimitry Andric // <...>
144b60736ecSDimitry Andric //
145b60736ecSDimitry Andric // Where the first line is identical to the /maps format
146b60736ecSDimitry Andric // and VmFlags is only printed for kernels >= 3.8.
147b60736ecSDimitry Andric
148b60736ecSDimitry Andric llvm::StringRef lines(linux_smap);
149b60736ecSDimitry Andric llvm::StringRef line;
150e3b55780SDimitry Andric std::optional<MemoryRegionInfo> region;
151b60736ecSDimitry Andric
152b60736ecSDimitry Andric while (lines.size()) {
153b60736ecSDimitry Andric std::tie(line, lines) = lines.split('\n');
154b60736ecSDimitry Andric
155b60736ecSDimitry Andric // A property line looks like:
156b60736ecSDimitry Andric // <word>: <value>
157b60736ecSDimitry Andric // (no spaces on the left hand side)
158b60736ecSDimitry Andric // A header will have a ':' but the LHS will contain spaces
159b60736ecSDimitry Andric llvm::StringRef name;
160b60736ecSDimitry Andric llvm::StringRef value;
161b60736ecSDimitry Andric std::tie(name, value) = line.split(':');
162b60736ecSDimitry Andric
163b60736ecSDimitry Andric // If this line is a property line
164b60736ecSDimitry Andric if (!name.contains(' ')) {
165b60736ecSDimitry Andric if (region) {
166b60736ecSDimitry Andric if (name == "VmFlags") {
167b60736ecSDimitry Andric if (value.contains("mt"))
168b60736ecSDimitry Andric region->SetMemoryTagged(MemoryRegionInfo::eYes);
169b60736ecSDimitry Andric else
170b60736ecSDimitry Andric region->SetMemoryTagged(MemoryRegionInfo::eNo);
171b60736ecSDimitry Andric }
172b60736ecSDimitry Andric // Ignore anything else
173b60736ecSDimitry Andric } else {
174b60736ecSDimitry Andric // Orphaned settings line
175b60736ecSDimitry Andric callback(ProcMapError(
176b60736ecSDimitry Andric "Found a property line without a corresponding mapping "
177b60736ecSDimitry Andric "in /proc/{pid}/%s",
178b60736ecSDimitry Andric MapsKind::SMaps));
179b60736ecSDimitry Andric return;
180b60736ecSDimitry Andric }
181b60736ecSDimitry Andric } else {
182b60736ecSDimitry Andric // Must be a new region header
183b60736ecSDimitry Andric if (region) {
184b60736ecSDimitry Andric // Save current region
185b60736ecSDimitry Andric callback(*region);
186b60736ecSDimitry Andric region.reset();
187b60736ecSDimitry Andric }
188b60736ecSDimitry Andric
189b60736ecSDimitry Andric // Try to start a new region
190b60736ecSDimitry Andric llvm::Expected<MemoryRegionInfo> new_region =
191b60736ecSDimitry Andric ParseMemoryRegionInfoFromProcMapsLine(line, MapsKind::SMaps);
192b60736ecSDimitry Andric if (new_region) {
193b60736ecSDimitry Andric region = *new_region;
194b60736ecSDimitry Andric } else {
195b60736ecSDimitry Andric // Stop at first invalid region header
196b60736ecSDimitry Andric callback(new_region.takeError());
197b60736ecSDimitry Andric return;
198b60736ecSDimitry Andric }
199b60736ecSDimitry Andric }
200b60736ecSDimitry Andric }
201b60736ecSDimitry Andric
202b60736ecSDimitry Andric // Catch last region
203b60736ecSDimitry Andric if (region)
204b60736ecSDimitry Andric callback(*region);
205b60736ecSDimitry Andric }
206