xref: /src/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxProcMaps.cpp (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
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