зеркало из https://github.com/mozilla/gecko-dev.git
209 строки
6.7 KiB
C++
209 строки
6.7 KiB
C++
/* -*- 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
|