Path: blob/main/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxProcMaps.cpp
39644 views
//===-- LinuxProcMaps.cpp -------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "LinuxProcMaps.h"9#include "lldb/Target/MemoryRegionInfo.h"10#include "lldb/Utility/Status.h"11#include "lldb/Utility/StringExtractor.h"12#include "llvm/ADT/StringRef.h"13#include <optional>1415using namespace lldb_private;1617enum class MapsKind { Maps, SMaps };1819static llvm::Expected<MemoryRegionInfo> ProcMapError(const char *msg,20MapsKind kind) {21return llvm::createStringError(llvm::inconvertibleErrorCode(), msg,22kind == MapsKind::Maps ? "maps" : "smaps");23}2425static llvm::Expected<MemoryRegionInfo>26ParseMemoryRegionInfoFromProcMapsLine(llvm::StringRef maps_line,27MapsKind maps_kind) {28MemoryRegionInfo region;29StringExtractor line_extractor(maps_line);3031// Format: {address_start_hex}-{address_end_hex} perms offset dev inode32// pathname perms: rwxp (letter is present if set, '-' if not, final33// character is p=private, s=shared).3435// Parse out the starting address36lldb::addr_t start_address = line_extractor.GetHexMaxU64(false, 0);3738// Parse out hyphen separating start and end address from range.39if (!line_extractor.GetBytesLeft() || (line_extractor.GetChar() != '-'))40return ProcMapError(41"malformed /proc/{pid}/%s entry, missing dash between address range",42maps_kind);4344// Parse out the ending address45lldb::addr_t end_address = line_extractor.GetHexMaxU64(false, start_address);4647// Parse out the space after the address.48if (!line_extractor.GetBytesLeft() || (line_extractor.GetChar() != ' '))49return ProcMapError(50"malformed /proc/{pid}/%s entry, missing space after range", maps_kind);5152// Save the range.53region.GetRange().SetRangeBase(start_address);54region.GetRange().SetRangeEnd(end_address);5556// Any memory region in /proc/{pid}/(maps|smaps) is by definition mapped57// into the process.58region.SetMapped(MemoryRegionInfo::OptionalBool::eYes);5960// Parse out each permission entry.61if (line_extractor.GetBytesLeft() < 4)62return ProcMapError(63"malformed /proc/{pid}/%s entry, missing some portion of "64"permissions",65maps_kind);6667// Handle read permission.68const char read_perm_char = line_extractor.GetChar();69if (read_perm_char == 'r')70region.SetReadable(MemoryRegionInfo::OptionalBool::eYes);71else if (read_perm_char == '-')72region.SetReadable(MemoryRegionInfo::OptionalBool::eNo);73else74return ProcMapError("unexpected /proc/{pid}/%s read permission char",75maps_kind);7677// Handle write permission.78const char write_perm_char = line_extractor.GetChar();79if (write_perm_char == 'w')80region.SetWritable(MemoryRegionInfo::OptionalBool::eYes);81else if (write_perm_char == '-')82region.SetWritable(MemoryRegionInfo::OptionalBool::eNo);83else84return ProcMapError("unexpected /proc/{pid}/%s write permission char",85maps_kind);8687// Handle execute permission.88const char exec_perm_char = line_extractor.GetChar();89if (exec_perm_char == 'x')90region.SetExecutable(MemoryRegionInfo::OptionalBool::eYes);91else if (exec_perm_char == '-')92region.SetExecutable(MemoryRegionInfo::OptionalBool::eNo);93else94return ProcMapError("unexpected /proc/{pid}/%s exec permission char",95maps_kind);9697// Handle sharing status (private/shared).98const char sharing_char = line_extractor.GetChar();99if (sharing_char == 's')100region.SetShared(MemoryRegionInfo::OptionalBool::eYes);101else if (sharing_char == 'p')102region.SetShared(MemoryRegionInfo::OptionalBool::eNo);103else104region.SetShared(MemoryRegionInfo::OptionalBool::eDontKnow);105106line_extractor.SkipSpaces(); // Skip the separator107line_extractor.GetHexMaxU64(false, 0); // Read the offset108line_extractor.GetHexMaxU64(false, 0); // Read the major device number109line_extractor.GetChar(); // Read the device id separator110line_extractor.GetHexMaxU64(false, 0); // Read the major device number111line_extractor.SkipSpaces(); // Skip the separator112line_extractor.GetU64(0, 10); // Read the inode number113114line_extractor.SkipSpaces();115const char *name = line_extractor.Peek();116if (name)117region.SetName(name);118119return region;120}121122void lldb_private::ParseLinuxMapRegions(llvm::StringRef linux_map,123LinuxMapCallback const &callback) {124llvm::StringRef lines(linux_map);125llvm::StringRef line;126while (!lines.empty()) {127std::tie(line, lines) = lines.split('\n');128if (!callback(ParseMemoryRegionInfoFromProcMapsLine(line, MapsKind::Maps)))129break;130}131}132133void lldb_private::ParseLinuxSMapRegions(llvm::StringRef linux_smap,134LinuxMapCallback const &callback) {135// Entries in /smaps look like:136// 00400000-0048a000 r-xp 00000000 fd:03 960637137// Size: 552 kB138// Rss: 460 kB139// <...>140// VmFlags: rd ex mr mw me dw141// 00500000-0058a000 rwxp 00000000 fd:03 960637142// <...>143//144// Where the first line is identical to the /maps format145// and VmFlags is only printed for kernels >= 3.8.146147llvm::StringRef lines(linux_smap);148llvm::StringRef line;149std::optional<MemoryRegionInfo> region;150151while (lines.size()) {152std::tie(line, lines) = lines.split('\n');153154// A property line looks like:155// <word>: <value>156// (no spaces on the left hand side)157// A header will have a ':' but the LHS will contain spaces158llvm::StringRef name;159llvm::StringRef value;160std::tie(name, value) = line.split(':');161162// If this line is a property line163if (!name.contains(' ')) {164if (region) {165if (name == "VmFlags") {166if (value.contains("mt"))167region->SetMemoryTagged(MemoryRegionInfo::eYes);168else169region->SetMemoryTagged(MemoryRegionInfo::eNo);170}171// Ignore anything else172} else {173// Orphaned settings line174callback(ProcMapError(175"Found a property line without a corresponding mapping "176"in /proc/{pid}/%s",177MapsKind::SMaps));178return;179}180} else {181// Must be a new region header182if (region) {183// Save current region184callback(*region);185region.reset();186}187188// Try to start a new region189llvm::Expected<MemoryRegionInfo> new_region =190ParseMemoryRegionInfoFromProcMapsLine(line, MapsKind::SMaps);191if (new_region) {192region = *new_region;193} else {194// Stop at first invalid region header195callback(new_region.takeError());196return;197}198}199}200201// Catch last region202if (region)203callback(*region);204}205206207