180814287SRaphael Isemann //===-- LinuxProcMaps.cpp -------------------------------------------------===//
2026e1bf5SGreg Clayton //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6026e1bf5SGreg Clayton //
7026e1bf5SGreg Clayton //===----------------------------------------------------------------------===//
8026e1bf5SGreg Clayton 
9026e1bf5SGreg Clayton #include "LinuxProcMaps.h"
10026e1bf5SGreg Clayton #include "lldb/Target/MemoryRegionInfo.h"
11026e1bf5SGreg Clayton #include "lldb/Utility/Status.h"
12026e1bf5SGreg Clayton #include "lldb/Utility/StringExtractor.h"
13*32541685SDavid Spickett #include "llvm/ADT/StringRef.h"
14026e1bf5SGreg Clayton 
15026e1bf5SGreg Clayton using namespace lldb_private;
16026e1bf5SGreg Clayton 
17*32541685SDavid Spickett enum class MapsKind { Maps, SMaps };
18026e1bf5SGreg Clayton 
19*32541685SDavid Spickett static llvm::Expected<MemoryRegionInfo> ProcMapError(const char *msg,
20*32541685SDavid Spickett                                                      MapsKind kind) {
21*32541685SDavid Spickett   return llvm::createStringError(llvm::inconvertibleErrorCode(), msg,
22*32541685SDavid Spickett                                  kind == MapsKind::Maps ? "maps" : "smaps");
23*32541685SDavid Spickett }
24*32541685SDavid Spickett 
25*32541685SDavid Spickett static llvm::Expected<MemoryRegionInfo>
26*32541685SDavid Spickett ParseMemoryRegionInfoFromProcMapsLine(llvm::StringRef maps_line,
27*32541685SDavid Spickett                                       MapsKind maps_kind) {
28*32541685SDavid Spickett   MemoryRegionInfo region;
29026e1bf5SGreg Clayton   StringExtractor line_extractor(maps_line);
30026e1bf5SGreg Clayton 
31026e1bf5SGreg Clayton   // Format: {address_start_hex}-{address_end_hex} perms offset  dev   inode
32026e1bf5SGreg Clayton   // pathname perms: rwxp   (letter is present if set, '-' if not, final
33026e1bf5SGreg Clayton   // character is p=private, s=shared).
34026e1bf5SGreg Clayton 
35026e1bf5SGreg Clayton   // Parse out the starting address
36026e1bf5SGreg Clayton   lldb::addr_t start_address = line_extractor.GetHexMaxU64(false, 0);
37026e1bf5SGreg Clayton 
38026e1bf5SGreg Clayton   // Parse out hyphen separating start and end address from range.
39026e1bf5SGreg Clayton   if (!line_extractor.GetBytesLeft() || (line_extractor.GetChar() != '-'))
40*32541685SDavid Spickett     return ProcMapError(
41*32541685SDavid Spickett         "malformed /proc/{pid}/%s entry, missing dash between address range",
42*32541685SDavid Spickett         maps_kind);
43026e1bf5SGreg Clayton 
44026e1bf5SGreg Clayton   // Parse out the ending address
45026e1bf5SGreg Clayton   lldb::addr_t end_address = line_extractor.GetHexMaxU64(false, start_address);
46026e1bf5SGreg Clayton 
47026e1bf5SGreg Clayton   // Parse out the space after the address.
48026e1bf5SGreg Clayton   if (!line_extractor.GetBytesLeft() || (line_extractor.GetChar() != ' '))
49*32541685SDavid Spickett     return ProcMapError(
50*32541685SDavid Spickett         "malformed /proc/{pid}/%s entry, missing space after range", maps_kind);
51026e1bf5SGreg Clayton 
52026e1bf5SGreg Clayton   // Save the range.
53*32541685SDavid Spickett   region.GetRange().SetRangeBase(start_address);
54*32541685SDavid Spickett   region.GetRange().SetRangeEnd(end_address);
55026e1bf5SGreg Clayton 
56*32541685SDavid Spickett   // Any memory region in /proc/{pid}/(maps|smaps) is by definition mapped
57*32541685SDavid Spickett   // into the process.
58*32541685SDavid Spickett   region.SetMapped(MemoryRegionInfo::OptionalBool::eYes);
59026e1bf5SGreg Clayton 
60026e1bf5SGreg Clayton   // Parse out each permission entry.
61026e1bf5SGreg Clayton   if (line_extractor.GetBytesLeft() < 4)
62*32541685SDavid Spickett     return ProcMapError(
63*32541685SDavid Spickett         "malformed /proc/{pid}/%s entry, missing some portion of "
64*32541685SDavid Spickett         "permissions",
65*32541685SDavid Spickett         maps_kind);
66026e1bf5SGreg Clayton 
67026e1bf5SGreg Clayton   // Handle read permission.
68026e1bf5SGreg Clayton   const char read_perm_char = line_extractor.GetChar();
69026e1bf5SGreg Clayton   if (read_perm_char == 'r')
70*32541685SDavid Spickett     region.SetReadable(MemoryRegionInfo::OptionalBool::eYes);
71026e1bf5SGreg Clayton   else if (read_perm_char == '-')
72*32541685SDavid Spickett     region.SetReadable(MemoryRegionInfo::OptionalBool::eNo);
73026e1bf5SGreg Clayton   else
74*32541685SDavid Spickett     return ProcMapError("unexpected /proc/{pid}/%s read permission char",
75*32541685SDavid Spickett                         maps_kind);
76026e1bf5SGreg Clayton 
77026e1bf5SGreg Clayton   // Handle write permission.
78026e1bf5SGreg Clayton   const char write_perm_char = line_extractor.GetChar();
79026e1bf5SGreg Clayton   if (write_perm_char == 'w')
80*32541685SDavid Spickett     region.SetWritable(MemoryRegionInfo::OptionalBool::eYes);
81026e1bf5SGreg Clayton   else if (write_perm_char == '-')
82*32541685SDavid Spickett     region.SetWritable(MemoryRegionInfo::OptionalBool::eNo);
83026e1bf5SGreg Clayton   else
84*32541685SDavid Spickett     return ProcMapError("unexpected /proc/{pid}/%s write permission char",
85*32541685SDavid Spickett                         maps_kind);
86026e1bf5SGreg Clayton 
87026e1bf5SGreg Clayton   // Handle execute permission.
88026e1bf5SGreg Clayton   const char exec_perm_char = line_extractor.GetChar();
89026e1bf5SGreg Clayton   if (exec_perm_char == 'x')
90*32541685SDavid Spickett     region.SetExecutable(MemoryRegionInfo::OptionalBool::eYes);
91026e1bf5SGreg Clayton   else if (exec_perm_char == '-')
92*32541685SDavid Spickett     region.SetExecutable(MemoryRegionInfo::OptionalBool::eNo);
93026e1bf5SGreg Clayton   else
94*32541685SDavid Spickett     return ProcMapError("unexpected /proc/{pid}/%s exec permission char",
95*32541685SDavid Spickett                         maps_kind);
96026e1bf5SGreg Clayton 
97026e1bf5SGreg Clayton   line_extractor.GetChar();              // Read the private bit
98026e1bf5SGreg Clayton   line_extractor.SkipSpaces();           // Skip the separator
99026e1bf5SGreg Clayton   line_extractor.GetHexMaxU64(false, 0); // Read the offset
100026e1bf5SGreg Clayton   line_extractor.GetHexMaxU64(false, 0); // Read the major device number
101026e1bf5SGreg Clayton   line_extractor.GetChar();              // Read the device id separator
102026e1bf5SGreg Clayton   line_extractor.GetHexMaxU64(false, 0); // Read the major device number
103026e1bf5SGreg Clayton   line_extractor.SkipSpaces();           // Skip the separator
104026e1bf5SGreg Clayton   line_extractor.GetU64(0, 10);          // Read the inode number
105026e1bf5SGreg Clayton 
106026e1bf5SGreg Clayton   line_extractor.SkipSpaces();
107026e1bf5SGreg Clayton   const char *name = line_extractor.Peek();
108026e1bf5SGreg Clayton   if (name)
109*32541685SDavid Spickett     region.SetName(name);
110026e1bf5SGreg Clayton 
111*32541685SDavid Spickett   return region;
112026e1bf5SGreg Clayton }
113026e1bf5SGreg Clayton 
114026e1bf5SGreg Clayton void lldb_private::ParseLinuxMapRegions(llvm::StringRef linux_map,
115026e1bf5SGreg Clayton                                         LinuxMapCallback const &callback) {
116026e1bf5SGreg Clayton   llvm::StringRef lines(linux_map);
117026e1bf5SGreg Clayton   llvm::StringRef line;
118026e1bf5SGreg Clayton   while (!lines.empty()) {
119026e1bf5SGreg Clayton     std::tie(line, lines) = lines.split('\n');
120*32541685SDavid Spickett     if (!callback(ParseMemoryRegionInfoFromProcMapsLine(line, MapsKind::Maps)))
121026e1bf5SGreg Clayton       break;
122026e1bf5SGreg Clayton   }
123026e1bf5SGreg Clayton }
124*32541685SDavid Spickett 
125*32541685SDavid Spickett void lldb_private::ParseLinuxSMapRegions(llvm::StringRef linux_smap,
126*32541685SDavid Spickett                                          LinuxMapCallback const &callback) {
127*32541685SDavid Spickett   // Entries in /smaps look like:
128*32541685SDavid Spickett   // 00400000-0048a000 r-xp 00000000 fd:03 960637
129*32541685SDavid Spickett   // Size:                552 kB
130*32541685SDavid Spickett   // Rss:                 460 kB
131*32541685SDavid Spickett   // <...>
132*32541685SDavid Spickett   // VmFlags: rd ex mr mw me dw
133*32541685SDavid Spickett   // 00500000-0058a000 rwxp 00000000 fd:03 960637
134*32541685SDavid Spickett   // <...>
135*32541685SDavid Spickett   //
136*32541685SDavid Spickett   // Where the first line is identical to the /maps format
137*32541685SDavid Spickett   // and VmFlags is only printed for kernels >= 3.8.
138*32541685SDavid Spickett 
139*32541685SDavid Spickett   llvm::StringRef lines(linux_smap);
140*32541685SDavid Spickett   llvm::StringRef line;
141*32541685SDavid Spickett   llvm::Optional<MemoryRegionInfo> region;
142*32541685SDavid Spickett 
143*32541685SDavid Spickett   while (lines.size()) {
144*32541685SDavid Spickett     std::tie(line, lines) = lines.split('\n');
145*32541685SDavid Spickett 
146*32541685SDavid Spickett     // A property line looks like:
147*32541685SDavid Spickett     // <word>: <value>
148*32541685SDavid Spickett     // (no spaces on the left hand side)
149*32541685SDavid Spickett     // A header will have a ':' but the LHS will contain spaces
150*32541685SDavid Spickett     llvm::StringRef name;
151*32541685SDavid Spickett     llvm::StringRef value;
152*32541685SDavid Spickett     std::tie(name, value) = line.split(':');
153*32541685SDavid Spickett 
154*32541685SDavid Spickett     // If this line is a property line
155*32541685SDavid Spickett     if (!name.contains(' ')) {
156*32541685SDavid Spickett       if (region) {
157*32541685SDavid Spickett         if (name == "VmFlags") {
158*32541685SDavid Spickett           if (value.contains("mt"))
159*32541685SDavid Spickett             region->SetMemoryTagged(MemoryRegionInfo::eYes);
160*32541685SDavid Spickett           else
161*32541685SDavid Spickett             region->SetMemoryTagged(MemoryRegionInfo::eNo);
162*32541685SDavid Spickett         }
163*32541685SDavid Spickett         // Ignore anything else
164*32541685SDavid Spickett       } else {
165*32541685SDavid Spickett         // Orphaned settings line
166*32541685SDavid Spickett         callback(ProcMapError(
167*32541685SDavid Spickett             "Found a property line without a corresponding mapping "
168*32541685SDavid Spickett             "in /proc/{pid}/%s",
169*32541685SDavid Spickett             MapsKind::SMaps));
170*32541685SDavid Spickett         return;
171*32541685SDavid Spickett       }
172*32541685SDavid Spickett     } else {
173*32541685SDavid Spickett       // Must be a new region header
174*32541685SDavid Spickett       if (region) {
175*32541685SDavid Spickett         // Save current region
176*32541685SDavid Spickett         callback(*region);
177*32541685SDavid Spickett         region.reset();
178*32541685SDavid Spickett       }
179*32541685SDavid Spickett 
180*32541685SDavid Spickett       // Try to start a new region
181*32541685SDavid Spickett       llvm::Expected<MemoryRegionInfo> new_region =
182*32541685SDavid Spickett           ParseMemoryRegionInfoFromProcMapsLine(line, MapsKind::SMaps);
183*32541685SDavid Spickett       if (new_region) {
184*32541685SDavid Spickett         region = *new_region;
185*32541685SDavid Spickett       } else {
186*32541685SDavid Spickett         // Stop at first invalid region header
187*32541685SDavid Spickett         callback(new_region.takeError());
188*32541685SDavid Spickett         return;
189*32541685SDavid Spickett       }
190*32541685SDavid Spickett     }
191*32541685SDavid Spickett   }
192*32541685SDavid Spickett 
193*32541685SDavid Spickett   // Catch last region
194*32541685SDavid Spickett   if (region)
195*32541685SDavid Spickett     callback(*region);
196*32541685SDavid Spickett }
197