diff options
Diffstat (limited to 'xpcom/base/MemoryMapping.cpp')
-rw-r--r-- | xpcom/base/MemoryMapping.cpp | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/xpcom/base/MemoryMapping.cpp b/xpcom/base/MemoryMapping.cpp new file mode 100644 index 0000000000..3a91d7ceae --- /dev/null +++ b/xpcom/base/MemoryMapping.cpp @@ -0,0 +1,207 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/MemoryMapping.h" + +#include "mozilla/BinarySearch.h" +#include "mozilla/FileUtils.h" + +#include <fstream> +#include <string> +#include <sstream> + +namespace mozilla { + +namespace { +struct VMFlagString { + const char* mName; + const char* mPrettyName; + VMFlag mFlag; +}; + +static const VMFlagString sVMFlagStrings[] = { + // clang-format off + {"ac", "Accountable", VMFlag::Accountable}, + {"ar", "ArchSpecific", VMFlag::ArchSpecific}, + {"dc", "NoFork", VMFlag::NoFork}, + {"dd", "NoCore", VMFlag::NoCore}, + {"de", "NoExpand", VMFlag::NoExpand}, + {"dw", "DisabledWrite", VMFlag::DisabledWrite}, + {"ex", "Executable", VMFlag::Executable}, + {"gd", "GrowsDown", VMFlag::GrowsDown}, + {"hg", "HugePage", VMFlag::HugePage}, + {"ht", "HugeTLB", VMFlag::HugeTLB}, + {"io", "IO", VMFlag::IO}, + {"lo", "Locked", VMFlag::Locked}, + {"me", "MayExecute", VMFlag::MayExecute}, + {"mg", "Mergeable", VMFlag::Mergeable}, + {"mm", "MixedMap", VMFlag::MixedMap}, + {"mr", "MayRead", VMFlag::MayRead}, + {"ms", "MayShare", VMFlag::MayShare}, + {"mw", "MayWrite", VMFlag::MayWrite}, + {"nh", "NoHugePage", VMFlag::NoHugePage}, + {"nl", "NonLinear", VMFlag::NonLinear}, + {"nr", "NotReserved", VMFlag::NotReserved}, + {"pf", "PurePFN", VMFlag::PurePFN}, + {"rd", "Readable", VMFlag::Readable}, + {"rr", "Random", VMFlag::Random}, + {"sd", "SoftDirty", VMFlag::SoftDirty}, + {"sh", "Shared", VMFlag::Shared}, + {"sr", "Sequential", VMFlag::Sequential}, + {"wr", "Writable", VMFlag::Writable}, + // clang-format on +}; +} // anonymous namespace + +constexpr size_t kVMFlags = size_t(-1); + +// An array of known field names which may be present in an smaps file, and the +// offsets of the corresponding fields in a MemoryMapping class. +const MemoryMapping::Field MemoryMapping::sFields[] = { + // clang-format off + {"AnonHugePages", offsetof(MemoryMapping, mAnonHugePages)}, + {"Anonymous", offsetof(MemoryMapping, mAnonymous)}, + {"KernelPageSize", offsetof(MemoryMapping, mKernelPageSize)}, + {"LazyFree", offsetof(MemoryMapping, mLazyFree)}, + {"Locked", offsetof(MemoryMapping, mLocked)}, + {"MMUPageSize", offsetof(MemoryMapping, mMMUPageSize)}, + {"Private_Clean", offsetof(MemoryMapping, mPrivate_Clean)}, + {"Private_Dirty", offsetof(MemoryMapping, mPrivate_Dirty)}, + {"Private_Hugetlb", offsetof(MemoryMapping, mPrivate_Hugetlb)}, + {"Pss", offsetof(MemoryMapping, mPss)}, + {"Referenced", offsetof(MemoryMapping, mReferenced)}, + {"Rss", offsetof(MemoryMapping, mRss)}, + {"Shared_Clean", offsetof(MemoryMapping, mShared_Clean)}, + {"Shared_Dirty", offsetof(MemoryMapping, mShared_Dirty)}, + {"Shared_Hugetlb", offsetof(MemoryMapping, mShared_Hugetlb)}, + {"ShmemPmdMapped", offsetof(MemoryMapping, mShmemPmdMapped)}, + {"Size", offsetof(MemoryMapping, mSize)}, + {"Swap", offsetof(MemoryMapping, mSwap)}, + {"SwapPss", offsetof(MemoryMapping, mSwapPss)}, + // VmFlags is a special case. It contains an array of flag strings, which + // describe attributes of the mapping, rather than a mapping size. We include + // it in this array to aid in parsing, but give it a separate sentinel value, + // and treat it specially. + {"VmFlags", kVMFlags}, + // clang-format on +}; + +template <typename T, int n> +const T* FindEntry(const char* aName, const T (&aEntries)[n]) { + size_t index; + if (BinarySearchIf( + aEntries, 0, n, + [&](const T& aEntry) { return strcmp(aName, aEntry.mName); }, + &index)) { + return &aEntries[index]; + } + return nullptr; +} + +using Perm = MemoryMapping::Perm; +using PermSet = MemoryMapping::PermSet; + +nsresult GetMemoryMappings(nsTArray<MemoryMapping>& aMappings, pid_t aPid) { + std::ifstream stream; + if (aPid == 0) { + stream.open("/proc/self/smaps"); + } else { + std::ostringstream path; + path << "/proc/" << aPid << "/smaps" << std::ends; + stream.open(path.str()); + } + if (stream.fail()) { + return NS_ERROR_FAILURE; + } + + MemoryMapping* current = nullptr; + std::string line; + while (std::getline(stream, line)) { + size_t start, end, offset; + char flags[4] = "---"; + char name[512]; + + name[0] = 0; + + // clang-format off + // Match the start of an entry. A typical line looks something like: + // + // 1487118a7000-148711a5a000 r-xp 00000000 103:03 54004561 /usr/lib/libc-2.27.so + // clang-format on + if (sscanf(line.c_str(), "%zx-%zx %4c %zx %*u:%*u %*u %511s\n", &start, + &end, flags, &offset, name) >= 4) { + PermSet perms; + if (flags[0] == 'r') { + perms += Perm::Read; + } + if (flags[1] == 'w') { + perms += Perm::Write; + } + if (flags[2] == 'x') { + perms += Perm::Execute; + } + if (flags[3] == 'p') { + perms += Perm::Private; + } else if (flags[3] == 's') { + perms += Perm::Shared; + } + + current = aMappings.AppendElement( + MemoryMapping{start, end, perms, offset, name}); + continue; + } + if (!current) { + continue; + } + + char* savePtr; + char* fieldName = strtok_r(line.data(), ":", &savePtr); + if (!fieldName) { + continue; + } + auto* field = FindEntry(fieldName, MemoryMapping::sFields); + if (!field) { + continue; + } + + if (field->mOffset == kVMFlags) { + while (char* flagName = strtok_r(nullptr, " \n", &savePtr)) { + if (auto* flag = FindEntry(flagName, sVMFlagStrings)) { + current->mFlags += flag->mFlag; + } + } + continue; + } + + const char* rest = strtok_r(nullptr, "\n", &savePtr); + size_t value; + if (sscanf(rest, "%zd kB", &value) > 0) { + current->ValueForField(*field) = value * 1024; + } + } + + return NS_OK; +} + +void MemoryMapping::Dump(nsACString& aOut) const { + aOut.AppendPrintf("%zx-%zx Size: %zu Offset: %zx %s\n", mStart, mEnd, + mEnd - mStart, mOffset, mName.get()); + + for (auto& field : MemoryMapping::sFields) { + if (field.mOffset < sizeof(*this)) { + aOut.AppendPrintf(" %s: %zd\n", field.mName, ValueForField(field)); + } + } + + aOut.AppendPrintf(" Flags: %x\n", mFlags.serialize()); + for (auto& flag : sVMFlagStrings) { + if (mFlags.contains(flag.mFlag)) { + aOut.AppendPrintf(" : %s %s\n", flag.mName, flag.mPrettyName); + } + } +} + +} // namespace mozilla |