summaryrefslogtreecommitdiffstats
path: root/xpcom/base/MemoryMapping.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xpcom/base/MemoryMapping.cpp')
-rw-r--r--xpcom/base/MemoryMapping.cpp208
1 files changed, 208 insertions, 0 deletions
diff --git a/xpcom/base/MemoryMapping.cpp b/xpcom/base/MemoryMapping.cpp
new file mode 100644
index 0000000000..1ec1cf1f61
--- /dev/null
+++ b/xpcom/base/MemoryMapping.cpp
@@ -0,0 +1,208 @@
+/* -*- 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 buffer;
+ while (std::getline(stream, buffer)) {
+ 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(buffer.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;
+ }
+
+ nsAutoCStringN<128> line(buffer.c_str());
+ char* savePtr;
+ char* fieldName = strtok_r(line.BeginWriting(), ":", &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