1 //===-- LinuxProcMaps.cpp -------------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "LinuxProcMaps.h"
10 #include "lldb/Target/MemoryRegionInfo.h"
11 #include "lldb/Utility/Status.h"
12 #include "lldb/Utility/StringExtractor.h"
13 #include "llvm/ADT/StringRef.h"
16 using namespace lldb_private
;
18 enum class MapsKind
{ Maps
, SMaps
};
20 static llvm::Expected
<MemoryRegionInfo
> ProcMapError(const char *msg
,
22 return llvm::createStringError(llvm::inconvertibleErrorCode(), msg
,
23 kind
== MapsKind::Maps
? "maps" : "smaps");
26 static llvm::Expected
<MemoryRegionInfo
>
27 ParseMemoryRegionInfoFromProcMapsLine(llvm::StringRef maps_line
,
29 MemoryRegionInfo region
;
30 StringExtractor
line_extractor(maps_line
);
32 // Format: {address_start_hex}-{address_end_hex} perms offset dev inode
33 // pathname perms: rwxp (letter is present if set, '-' if not, final
34 // character is p=private, s=shared).
36 // Parse out the starting address
37 lldb::addr_t start_address
= line_extractor
.GetHexMaxU64(false, 0);
39 // Parse out hyphen separating start and end address from range.
40 if (!line_extractor
.GetBytesLeft() || (line_extractor
.GetChar() != '-'))
42 "malformed /proc/{pid}/%s entry, missing dash between address range",
45 // Parse out the ending address
46 lldb::addr_t end_address
= line_extractor
.GetHexMaxU64(false, start_address
);
48 // Parse out the space after the address.
49 if (!line_extractor
.GetBytesLeft() || (line_extractor
.GetChar() != ' '))
51 "malformed /proc/{pid}/%s entry, missing space after range", maps_kind
);
54 region
.GetRange().SetRangeBase(start_address
);
55 region
.GetRange().SetRangeEnd(end_address
);
57 // Any memory region in /proc/{pid}/(maps|smaps) is by definition mapped
59 region
.SetMapped(MemoryRegionInfo::OptionalBool::eYes
);
61 // Parse out each permission entry.
62 if (line_extractor
.GetBytesLeft() < 4)
64 "malformed /proc/{pid}/%s entry, missing some portion of "
68 // Handle read permission.
69 const char read_perm_char
= line_extractor
.GetChar();
70 if (read_perm_char
== 'r')
71 region
.SetReadable(MemoryRegionInfo::OptionalBool::eYes
);
72 else if (read_perm_char
== '-')
73 region
.SetReadable(MemoryRegionInfo::OptionalBool::eNo
);
75 return ProcMapError("unexpected /proc/{pid}/%s read permission char",
78 // Handle write permission.
79 const char write_perm_char
= line_extractor
.GetChar();
80 if (write_perm_char
== 'w')
81 region
.SetWritable(MemoryRegionInfo::OptionalBool::eYes
);
82 else if (write_perm_char
== '-')
83 region
.SetWritable(MemoryRegionInfo::OptionalBool::eNo
);
85 return ProcMapError("unexpected /proc/{pid}/%s write permission char",
88 // Handle execute permission.
89 const char exec_perm_char
= line_extractor
.GetChar();
90 if (exec_perm_char
== 'x')
91 region
.SetExecutable(MemoryRegionInfo::OptionalBool::eYes
);
92 else if (exec_perm_char
== '-')
93 region
.SetExecutable(MemoryRegionInfo::OptionalBool::eNo
);
95 return ProcMapError("unexpected /proc/{pid}/%s exec permission char",
98 // Handle sharing status (private/shared).
99 const char sharing_char
= line_extractor
.GetChar();
100 if (sharing_char
== 's')
101 region
.SetShared(MemoryRegionInfo::OptionalBool::eYes
);
102 else if (sharing_char
== 'p')
103 region
.SetShared(MemoryRegionInfo::OptionalBool::eNo
);
105 region
.SetShared(MemoryRegionInfo::OptionalBool::eDontKnow
);
107 line_extractor
.SkipSpaces(); // Skip the separator
108 line_extractor
.GetHexMaxU64(false, 0); // Read the offset
109 line_extractor
.GetHexMaxU64(false, 0); // Read the major device number
110 line_extractor
.GetChar(); // Read the device id separator
111 line_extractor
.GetHexMaxU64(false, 0); // Read the major device number
112 line_extractor
.SkipSpaces(); // Skip the separator
113 line_extractor
.GetU64(0, 10); // Read the inode number
115 line_extractor
.SkipSpaces();
116 const char *name
= line_extractor
.Peek();
118 region
.SetName(name
);
123 void lldb_private::ParseLinuxMapRegions(llvm::StringRef linux_map
,
124 LinuxMapCallback
const &callback
) {
125 llvm::StringRef
lines(linux_map
);
126 llvm::StringRef line
;
127 while (!lines
.empty()) {
128 std::tie(line
, lines
) = lines
.split('\n');
129 if (!callback(ParseMemoryRegionInfoFromProcMapsLine(line
, MapsKind::Maps
)))
134 void lldb_private::ParseLinuxSMapRegions(llvm::StringRef linux_smap
,
135 LinuxMapCallback
const &callback
) {
136 // Entries in /smaps look like:
137 // 00400000-0048a000 r-xp 00000000 fd:03 960637
141 // VmFlags: rd ex mr mw me dw
142 // 00500000-0058a000 rwxp 00000000 fd:03 960637
145 // Where the first line is identical to the /maps format
146 // and VmFlags is only printed for kernels >= 3.8.
148 llvm::StringRef
lines(linux_smap
);
149 llvm::StringRef line
;
150 std::optional
<MemoryRegionInfo
> region
;
152 while (lines
.size()) {
153 std::tie(line
, lines
) = lines
.split('\n');
155 // A property line looks like:
157 // (no spaces on the left hand side)
158 // A header will have a ':' but the LHS will contain spaces
159 llvm::StringRef name
;
160 llvm::StringRef value
;
161 std::tie(name
, value
) = line
.split(':');
163 // If this line is a property line
164 if (!name
.contains(' ')) {
166 if (name
== "VmFlags") {
167 if (value
.contains("mt"))
168 region
->SetMemoryTagged(MemoryRegionInfo::eYes
);
170 region
->SetMemoryTagged(MemoryRegionInfo::eNo
);
172 // Ignore anything else
174 // Orphaned settings line
175 callback(ProcMapError(
176 "Found a property line without a corresponding mapping "
182 // Must be a new region header
184 // Save current region
189 // Try to start a new region
190 llvm::Expected
<MemoryRegionInfo
> new_region
=
191 ParseMemoryRegionInfoFromProcMapsLine(line
, MapsKind::SMaps
);
193 region
= *new_region
;
195 // Stop at first invalid region header
196 callback(new_region
.takeError());