15ffd83dbSDimitry Andric //===-- LinuxProcMaps.cpp -------------------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric
90b57cec5SDimitry Andric #include "LinuxProcMaps.h"
100b57cec5SDimitry Andric #include "lldb/Target/MemoryRegionInfo.h"
110b57cec5SDimitry Andric #include "lldb/Utility/Status.h"
120b57cec5SDimitry Andric #include "lldb/Utility/StringExtractor.h"
13*af732203SDimitry Andric #include "llvm/ADT/StringRef.h"
140b57cec5SDimitry Andric
150b57cec5SDimitry Andric using namespace lldb_private;
160b57cec5SDimitry Andric
17*af732203SDimitry Andric enum class MapsKind { Maps, SMaps };
180b57cec5SDimitry Andric
ProcMapError(const char * msg,MapsKind kind)19*af732203SDimitry Andric static llvm::Expected<MemoryRegionInfo> ProcMapError(const char *msg,
20*af732203SDimitry Andric MapsKind kind) {
21*af732203SDimitry Andric return llvm::createStringError(llvm::inconvertibleErrorCode(), msg,
22*af732203SDimitry Andric kind == MapsKind::Maps ? "maps" : "smaps");
23*af732203SDimitry Andric }
24*af732203SDimitry Andric
25*af732203SDimitry Andric static llvm::Expected<MemoryRegionInfo>
ParseMemoryRegionInfoFromProcMapsLine(llvm::StringRef maps_line,MapsKind maps_kind)26*af732203SDimitry Andric ParseMemoryRegionInfoFromProcMapsLine(llvm::StringRef maps_line,
27*af732203SDimitry Andric MapsKind maps_kind) {
28*af732203SDimitry Andric MemoryRegionInfo region;
290b57cec5SDimitry Andric StringExtractor line_extractor(maps_line);
300b57cec5SDimitry Andric
310b57cec5SDimitry Andric // Format: {address_start_hex}-{address_end_hex} perms offset dev inode
320b57cec5SDimitry Andric // pathname perms: rwxp (letter is present if set, '-' if not, final
330b57cec5SDimitry Andric // character is p=private, s=shared).
340b57cec5SDimitry Andric
350b57cec5SDimitry Andric // Parse out the starting address
360b57cec5SDimitry Andric lldb::addr_t start_address = line_extractor.GetHexMaxU64(false, 0);
370b57cec5SDimitry Andric
380b57cec5SDimitry Andric // Parse out hyphen separating start and end address from range.
390b57cec5SDimitry Andric if (!line_extractor.GetBytesLeft() || (line_extractor.GetChar() != '-'))
40*af732203SDimitry Andric return ProcMapError(
41*af732203SDimitry Andric "malformed /proc/{pid}/%s entry, missing dash between address range",
42*af732203SDimitry Andric maps_kind);
430b57cec5SDimitry Andric
440b57cec5SDimitry Andric // Parse out the ending address
450b57cec5SDimitry Andric lldb::addr_t end_address = line_extractor.GetHexMaxU64(false, start_address);
460b57cec5SDimitry Andric
470b57cec5SDimitry Andric // Parse out the space after the address.
480b57cec5SDimitry Andric if (!line_extractor.GetBytesLeft() || (line_extractor.GetChar() != ' '))
49*af732203SDimitry Andric return ProcMapError(
50*af732203SDimitry Andric "malformed /proc/{pid}/%s entry, missing space after range", maps_kind);
510b57cec5SDimitry Andric
520b57cec5SDimitry Andric // Save the range.
53*af732203SDimitry Andric region.GetRange().SetRangeBase(start_address);
54*af732203SDimitry Andric region.GetRange().SetRangeEnd(end_address);
550b57cec5SDimitry Andric
56*af732203SDimitry Andric // Any memory region in /proc/{pid}/(maps|smaps) is by definition mapped
57*af732203SDimitry Andric // into the process.
58*af732203SDimitry Andric region.SetMapped(MemoryRegionInfo::OptionalBool::eYes);
590b57cec5SDimitry Andric
600b57cec5SDimitry Andric // Parse out each permission entry.
610b57cec5SDimitry Andric if (line_extractor.GetBytesLeft() < 4)
62*af732203SDimitry Andric return ProcMapError(
63*af732203SDimitry Andric "malformed /proc/{pid}/%s entry, missing some portion of "
64*af732203SDimitry Andric "permissions",
65*af732203SDimitry Andric maps_kind);
660b57cec5SDimitry Andric
670b57cec5SDimitry Andric // Handle read permission.
680b57cec5SDimitry Andric const char read_perm_char = line_extractor.GetChar();
690b57cec5SDimitry Andric if (read_perm_char == 'r')
70*af732203SDimitry Andric region.SetReadable(MemoryRegionInfo::OptionalBool::eYes);
710b57cec5SDimitry Andric else if (read_perm_char == '-')
72*af732203SDimitry Andric region.SetReadable(MemoryRegionInfo::OptionalBool::eNo);
730b57cec5SDimitry Andric else
74*af732203SDimitry Andric return ProcMapError("unexpected /proc/{pid}/%s read permission char",
75*af732203SDimitry Andric maps_kind);
760b57cec5SDimitry Andric
770b57cec5SDimitry Andric // Handle write permission.
780b57cec5SDimitry Andric const char write_perm_char = line_extractor.GetChar();
790b57cec5SDimitry Andric if (write_perm_char == 'w')
80*af732203SDimitry Andric region.SetWritable(MemoryRegionInfo::OptionalBool::eYes);
810b57cec5SDimitry Andric else if (write_perm_char == '-')
82*af732203SDimitry Andric region.SetWritable(MemoryRegionInfo::OptionalBool::eNo);
830b57cec5SDimitry Andric else
84*af732203SDimitry Andric return ProcMapError("unexpected /proc/{pid}/%s write permission char",
85*af732203SDimitry Andric maps_kind);
860b57cec5SDimitry Andric
870b57cec5SDimitry Andric // Handle execute permission.
880b57cec5SDimitry Andric const char exec_perm_char = line_extractor.GetChar();
890b57cec5SDimitry Andric if (exec_perm_char == 'x')
90*af732203SDimitry Andric region.SetExecutable(MemoryRegionInfo::OptionalBool::eYes);
910b57cec5SDimitry Andric else if (exec_perm_char == '-')
92*af732203SDimitry Andric region.SetExecutable(MemoryRegionInfo::OptionalBool::eNo);
930b57cec5SDimitry Andric else
94*af732203SDimitry Andric return ProcMapError("unexpected /proc/{pid}/%s exec permission char",
95*af732203SDimitry Andric maps_kind);
960b57cec5SDimitry Andric
970b57cec5SDimitry Andric line_extractor.GetChar(); // Read the private bit
980b57cec5SDimitry Andric line_extractor.SkipSpaces(); // Skip the separator
990b57cec5SDimitry Andric line_extractor.GetHexMaxU64(false, 0); // Read the offset
1000b57cec5SDimitry Andric line_extractor.GetHexMaxU64(false, 0); // Read the major device number
1010b57cec5SDimitry Andric line_extractor.GetChar(); // Read the device id separator
1020b57cec5SDimitry Andric line_extractor.GetHexMaxU64(false, 0); // Read the major device number
1030b57cec5SDimitry Andric line_extractor.SkipSpaces(); // Skip the separator
1040b57cec5SDimitry Andric line_extractor.GetU64(0, 10); // Read the inode number
1050b57cec5SDimitry Andric
1060b57cec5SDimitry Andric line_extractor.SkipSpaces();
1070b57cec5SDimitry Andric const char *name = line_extractor.Peek();
1080b57cec5SDimitry Andric if (name)
109*af732203SDimitry Andric region.SetName(name);
1100b57cec5SDimitry Andric
111*af732203SDimitry Andric return region;
1120b57cec5SDimitry Andric }
1130b57cec5SDimitry Andric
ParseLinuxMapRegions(llvm::StringRef linux_map,LinuxMapCallback const & callback)1140b57cec5SDimitry Andric void lldb_private::ParseLinuxMapRegions(llvm::StringRef linux_map,
1150b57cec5SDimitry Andric LinuxMapCallback const &callback) {
1160b57cec5SDimitry Andric llvm::StringRef lines(linux_map);
1170b57cec5SDimitry Andric llvm::StringRef line;
1180b57cec5SDimitry Andric while (!lines.empty()) {
1190b57cec5SDimitry Andric std::tie(line, lines) = lines.split('\n');
120*af732203SDimitry Andric if (!callback(ParseMemoryRegionInfoFromProcMapsLine(line, MapsKind::Maps)))
1210b57cec5SDimitry Andric break;
1220b57cec5SDimitry Andric }
1230b57cec5SDimitry Andric }
124*af732203SDimitry Andric
ParseLinuxSMapRegions(llvm::StringRef linux_smap,LinuxMapCallback const & callback)125*af732203SDimitry Andric void lldb_private::ParseLinuxSMapRegions(llvm::StringRef linux_smap,
126*af732203SDimitry Andric LinuxMapCallback const &callback) {
127*af732203SDimitry Andric // Entries in /smaps look like:
128*af732203SDimitry Andric // 00400000-0048a000 r-xp 00000000 fd:03 960637
129*af732203SDimitry Andric // Size: 552 kB
130*af732203SDimitry Andric // Rss: 460 kB
131*af732203SDimitry Andric // <...>
132*af732203SDimitry Andric // VmFlags: rd ex mr mw me dw
133*af732203SDimitry Andric // 00500000-0058a000 rwxp 00000000 fd:03 960637
134*af732203SDimitry Andric // <...>
135*af732203SDimitry Andric //
136*af732203SDimitry Andric // Where the first line is identical to the /maps format
137*af732203SDimitry Andric // and VmFlags is only printed for kernels >= 3.8.
138*af732203SDimitry Andric
139*af732203SDimitry Andric llvm::StringRef lines(linux_smap);
140*af732203SDimitry Andric llvm::StringRef line;
141*af732203SDimitry Andric llvm::Optional<MemoryRegionInfo> region;
142*af732203SDimitry Andric
143*af732203SDimitry Andric while (lines.size()) {
144*af732203SDimitry Andric std::tie(line, lines) = lines.split('\n');
145*af732203SDimitry Andric
146*af732203SDimitry Andric // A property line looks like:
147*af732203SDimitry Andric // <word>: <value>
148*af732203SDimitry Andric // (no spaces on the left hand side)
149*af732203SDimitry Andric // A header will have a ':' but the LHS will contain spaces
150*af732203SDimitry Andric llvm::StringRef name;
151*af732203SDimitry Andric llvm::StringRef value;
152*af732203SDimitry Andric std::tie(name, value) = line.split(':');
153*af732203SDimitry Andric
154*af732203SDimitry Andric // If this line is a property line
155*af732203SDimitry Andric if (!name.contains(' ')) {
156*af732203SDimitry Andric if (region) {
157*af732203SDimitry Andric if (name == "VmFlags") {
158*af732203SDimitry Andric if (value.contains("mt"))
159*af732203SDimitry Andric region->SetMemoryTagged(MemoryRegionInfo::eYes);
160*af732203SDimitry Andric else
161*af732203SDimitry Andric region->SetMemoryTagged(MemoryRegionInfo::eNo);
162*af732203SDimitry Andric }
163*af732203SDimitry Andric // Ignore anything else
164*af732203SDimitry Andric } else {
165*af732203SDimitry Andric // Orphaned settings line
166*af732203SDimitry Andric callback(ProcMapError(
167*af732203SDimitry Andric "Found a property line without a corresponding mapping "
168*af732203SDimitry Andric "in /proc/{pid}/%s",
169*af732203SDimitry Andric MapsKind::SMaps));
170*af732203SDimitry Andric return;
171*af732203SDimitry Andric }
172*af732203SDimitry Andric } else {
173*af732203SDimitry Andric // Must be a new region header
174*af732203SDimitry Andric if (region) {
175*af732203SDimitry Andric // Save current region
176*af732203SDimitry Andric callback(*region);
177*af732203SDimitry Andric region.reset();
178*af732203SDimitry Andric }
179*af732203SDimitry Andric
180*af732203SDimitry Andric // Try to start a new region
181*af732203SDimitry Andric llvm::Expected<MemoryRegionInfo> new_region =
182*af732203SDimitry Andric ParseMemoryRegionInfoFromProcMapsLine(line, MapsKind::SMaps);
183*af732203SDimitry Andric if (new_region) {
184*af732203SDimitry Andric region = *new_region;
185*af732203SDimitry Andric } else {
186*af732203SDimitry Andric // Stop at first invalid region header
187*af732203SDimitry Andric callback(new_region.takeError());
188*af732203SDimitry Andric return;
189*af732203SDimitry Andric }
190*af732203SDimitry Andric }
191*af732203SDimitry Andric }
192*af732203SDimitry Andric
193*af732203SDimitry Andric // Catch last region
194*af732203SDimitry Andric if (region)
195*af732203SDimitry Andric callback(*region);
196*af732203SDimitry Andric }
197