llvm-project/lldb/unittests/Process/Utility/LinuxProcMapsTest.cpp
David Spickett 2e51fdaa77
[lldb] Replace OptionalBool with LazyBool (#189652)
The only difference between them is that OptionalBool's third state
is "unknown" and LazyBool's is "calculate". We don't need to tell
the difference in a single context, so I've made a new eLazyBoolDontKnow
which is an alias of eLazyBoolCalculate.
2026-04-01 09:26:02 +01:00

279 lines
13 KiB
C++

//===-- LinuxProcMapsTest.cpp ---------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "Plugins/Process/Utility/LinuxProcMaps.h"
#include "lldb/Target/MemoryRegionInfo.h"
#include "lldb/Utility/Status.h"
#include <tuple>
using namespace lldb_private;
typedef std::tuple<const char *, MemoryRegionInfos, const char *>
LinuxProcMapsTestParams;
// Wrapper for convenience because Range is usually begin, size
static MemoryRegionInfo::RangeType make_range(lldb::addr_t begin,
lldb::addr_t end) {
MemoryRegionInfo::RangeType range(begin, 0);
range.SetRangeEnd(end);
return range;
}
class LinuxProcMapsTestFixture
: public ::testing::TestWithParam<LinuxProcMapsTestParams> {
protected:
Status error;
std::string err_str;
MemoryRegionInfos regions;
LinuxMapCallback callback;
void SetUp() override {
callback = [this](llvm::Expected<MemoryRegionInfo> Info) {
if (Info) {
err_str.clear();
regions.push_back(*Info);
return true;
}
err_str = toString(Info.takeError());
return false;
};
}
void check_regions(LinuxProcMapsTestParams params) {
EXPECT_THAT(std::get<1>(params), testing::ContainerEq(regions));
ASSERT_EQ(std::get<2>(params), err_str);
}
};
TEST_P(LinuxProcMapsTestFixture, ParseMapRegions) {
auto params = GetParam();
ParseLinuxMapRegions(std::get<0>(params), callback);
check_regions(params);
}
// Note: ConstString("") != ConstString(nullptr)
// When a region has no name, it will have the latter in the MemoryRegionInfo
INSTANTIATE_TEST_SUITE_P(
ProcMapTests, LinuxProcMapsTestFixture,
::testing::Values(
// Nothing in nothing out
std::make_tuple("", MemoryRegionInfos{}, ""),
// Various formatting error conditions
std::make_tuple("55a4512f7000/55a451b68000 rw-p 00000000 00:00 0",
MemoryRegionInfos{},
"malformed /proc/{pid}/maps entry, missing dash "
"between address range"),
std::make_tuple("0-0 rw", MemoryRegionInfos{},
"malformed /proc/{pid}/maps entry, missing some "
"portion of permissions"),
std::make_tuple("0-0 z--p 00000000 00:00 0", MemoryRegionInfos{},
"unexpected /proc/{pid}/maps read permission char"),
std::make_tuple("0-0 rz-p 00000000 00:00 0", MemoryRegionInfos{},
"unexpected /proc/{pid}/maps write permission char"),
std::make_tuple("0-0 rwzp 00000000 00:00 0", MemoryRegionInfos{},
"unexpected /proc/{pid}/maps exec permission char"),
// Stops at first parsing error
std::make_tuple("0-1 rw-p 00000000 00:00 0 [abc]\n"
"0-0 rwzp 00000000 00:00 0\n"
"2-3 r-xp 00000000 00:00 0 [def]\n",
MemoryRegionInfos{
MemoryRegionInfo(make_range(0, 1), eLazyBoolYes,
eLazyBoolYes, eLazyBoolNo,
eLazyBoolNo, eLazyBoolYes,
ConstString("[abc]")),
},
"unexpected /proc/{pid}/maps exec permission char"),
// Single entry
std::make_tuple(
"55a4512f7000-55a451b68000 rw-p 00000000 00:00 0 [heap]",
MemoryRegionInfos{
MemoryRegionInfo(make_range(0x55a4512f7000, 0x55a451b68000),
eLazyBoolYes, eLazyBoolYes, eLazyBoolNo,
eLazyBoolNo, eLazyBoolYes,
ConstString("[heap]")),
},
""),
// Multiple entries
std::make_tuple(
"7fc090021000-7fc094000000 ---p 00000000 00:00 0\n"
"7fc094000000-7fc094a00000 ---s 00000000 00:00 0\n"
"ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 "
"[vsyscall]",
MemoryRegionInfos{
MemoryRegionInfo(make_range(0x7fc090021000, 0x7fc094000000),
eLazyBoolNo, eLazyBoolNo, eLazyBoolNo,
eLazyBoolNo, eLazyBoolYes,
ConstString(nullptr)),
MemoryRegionInfo(make_range(0x7fc094000000, 0x7fc094a00000),
eLazyBoolNo, eLazyBoolNo, eLazyBoolNo,
eLazyBoolYes, eLazyBoolYes,
ConstString(nullptr)),
MemoryRegionInfo(
make_range(0xffffffffff600000, 0xffffffffff601000),
eLazyBoolYes, eLazyBoolNo, eLazyBoolYes, eLazyBoolNo,
eLazyBoolYes, ConstString("[vsyscall]")),
},
"")));
class LinuxProcSMapsTestFixture : public LinuxProcMapsTestFixture {};
INSTANTIATE_TEST_SUITE_P(
ProcSMapTests, LinuxProcSMapsTestFixture,
::testing::Values(
// Nothing in nothing out
std::make_tuple("", MemoryRegionInfos{}, ""),
// Uses the same parsing for first line, so same errors but referring to
// smaps
std::make_tuple("0/0 rw-p 00000000 00:00 0", MemoryRegionInfos{},
"malformed /proc/{pid}/smaps entry, missing dash "
"between address range"),
// Stop parsing at first error
std::make_tuple(
"1111-2222 rw-p 00000000 00:00 0 [foo]\n"
"0/0 rw-p 00000000 00:00 0",
MemoryRegionInfos{
MemoryRegionInfo(make_range(0x1111, 0x2222), eLazyBoolYes,
eLazyBoolYes, eLazyBoolNo, eLazyBoolNo,
eLazyBoolYes, ConstString("[foo]")),
},
"malformed /proc/{pid}/smaps entry, missing dash between address "
"range"),
// Property line without a region is an error
std::make_tuple("Referenced: 2188 kB\n"
"1111-2222 rw-p 00000000 00:00 0 [foo]\n"
"3333-4444 rw-p 00000000 00:00 0 [bar]\n",
MemoryRegionInfos{},
"Found a property line without a corresponding mapping "
"in /proc/{pid}/smaps"),
// Single region parses, has no flags
std::make_tuple(
"1111-2222 rw-p 00000000 00:00 0 [foo]",
MemoryRegionInfos{
MemoryRegionInfo(make_range(0x1111, 0x2222), eLazyBoolYes,
eLazyBoolYes, eLazyBoolNo, eLazyBoolNo,
eLazyBoolYes, ConstString("[foo]")),
},
""),
// Single shared region parses, has no flags
std::make_tuple(
"1111-2222 rw-s 00000000 00:00 0 [foo]",
MemoryRegionInfos{
MemoryRegionInfo(make_range(0x1111, 0x2222), eLazyBoolYes,
eLazyBoolYes, eLazyBoolNo, eLazyBoolYes,
eLazyBoolYes, ConstString("[foo]")),
},
""),
// Single region with flags, other lines ignored
std::make_tuple("1111-2222 rw-p 00000000 00:00 0 [foo]\n"
"Referenced: 2188 kB\n"
"AnonHugePages: 0 kB\n"
"VmFlags: mt",
MemoryRegionInfos{
MemoryRegionInfo(make_range(0x1111, 0x2222),
eLazyBoolYes, eLazyBoolYes,
eLazyBoolNo, eLazyBoolNo,
eLazyBoolYes, ConstString("[foo]"))
.SetIsShadowStack(eLazyBoolNo)
.SetMemoryTagged(eLazyBoolYes),
},
""),
// Whitespace ignored
std::make_tuple("0-0 rw-p 00000000 00:00 0\n"
"VmFlags: mt ",
MemoryRegionInfos{
MemoryRegionInfo(make_range(0, 0), eLazyBoolYes,
eLazyBoolYes, eLazyBoolNo,
eLazyBoolNo, eLazyBoolYes,
ConstString(nullptr))
.SetIsShadowStack(eLazyBoolNo)
.SetMemoryTagged(eLazyBoolYes),
},
""),
// VmFlags line means it has flag info, but nothing is set
std::make_tuple("0-0 rw-p 00000000 00:00 0\n"
"VmFlags: ",
MemoryRegionInfos{
MemoryRegionInfo(make_range(0, 0), eLazyBoolYes,
eLazyBoolYes, eLazyBoolNo,
eLazyBoolNo, eLazyBoolYes,
ConstString(nullptr))
.SetIsShadowStack(eLazyBoolNo)
.SetMemoryTagged(eLazyBoolNo),
},
""),
// Handle some pages not having a flags line
std::make_tuple(
"1111-2222 rw-p 00000000 00:00 0 [foo]\n"
"Referenced: 2188 kB\n"
"AnonHugePages: 0 kB\n"
"3333-4444 r-xp 00000000 00:00 0 [bar]\n"
"VmFlags: mt",
MemoryRegionInfos{
MemoryRegionInfo(make_range(0x1111, 0x2222), eLazyBoolYes,
eLazyBoolYes, eLazyBoolNo, eLazyBoolNo,
eLazyBoolYes, ConstString("[foo]")),
MemoryRegionInfo(make_range(0x3333, 0x4444), eLazyBoolYes,
eLazyBoolNo, eLazyBoolYes, eLazyBoolNo,
eLazyBoolYes, ConstString("[bar]"))
.SetIsShadowStack(eLazyBoolNo)
.SetMemoryTagged(eLazyBoolYes),
},
""),
// Handle no pages having a flags line (older kernels)
std::make_tuple(
"1111-2222 rw-p 00000000 00:00 0\n"
"Referenced: 2188 kB\n"
"AnonHugePages: 0 kB\n"
"3333-4444 r-xp 00000000 00:00 0\n"
"KernelPageSize: 4 kB\n"
"MMUPageSize: 4 kB\n",
MemoryRegionInfos{
MemoryRegionInfo(make_range(0x1111, 0x2222), eLazyBoolYes,
eLazyBoolYes, eLazyBoolNo, eLazyBoolNo,
eLazyBoolYes, ConstString(nullptr)),
MemoryRegionInfo(make_range(0x3333, 0x4444), eLazyBoolYes,
eLazyBoolNo, eLazyBoolYes, eLazyBoolNo,
eLazyBoolYes, ConstString(nullptr)),
},
""),
// We must look for exact flag strings, ignoring substrings of longer
// flag names.
std::make_tuple("0-0 rw-p 00000000 00:00 0\n"
"VmFlags: amt mtb amtb",
MemoryRegionInfos{
MemoryRegionInfo(make_range(0, 0), eLazyBoolYes,
eLazyBoolYes, eLazyBoolNo,
eLazyBoolNo, eLazyBoolYes,
ConstString(nullptr))
.SetIsShadowStack(eLazyBoolNo)
.SetMemoryTagged(eLazyBoolNo),
},
""),
// "ss" means shadow stack.
std::make_tuple("0-0 rw-p 00000000 00:00 0\n"
"VmFlags: ss",
MemoryRegionInfos{
MemoryRegionInfo(make_range(0, 0), eLazyBoolYes,
eLazyBoolYes, eLazyBoolNo,
eLazyBoolNo, eLazyBoolYes,
ConstString(nullptr))
.SetIsShadowStack(eLazyBoolYes)
.SetMemoryTagged(eLazyBoolNo),
},
"")));
TEST_P(LinuxProcSMapsTestFixture, ParseSMapRegions) {
auto params = GetParam();
ParseLinuxSMapRegions(std::get<0>(params), callback);
check_regions(params);
}