Bug 1919083 - [ci] Enable os-integration variant for more suites, r=jmaher
[gecko.git] / xpcom / base / MemoryMapping.cpp
blob3a91d7ceaeec7f67ce272060cf2c1f444d8a5ffe
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/MemoryMapping.h"
9 #include "mozilla/BinarySearch.h"
10 #include "mozilla/FileUtils.h"
12 #include <fstream>
13 #include <string>
14 #include <sstream>
16 namespace mozilla {
18 namespace {
19 struct VMFlagString {
20 const char* mName;
21 const char* mPrettyName;
22 VMFlag mFlag;
25 static const VMFlagString sVMFlagStrings[] = {
26 // clang-format off
27 {"ac", "Accountable", VMFlag::Accountable},
28 {"ar", "ArchSpecific", VMFlag::ArchSpecific},
29 {"dc", "NoFork", VMFlag::NoFork},
30 {"dd", "NoCore", VMFlag::NoCore},
31 {"de", "NoExpand", VMFlag::NoExpand},
32 {"dw", "DisabledWrite", VMFlag::DisabledWrite},
33 {"ex", "Executable", VMFlag::Executable},
34 {"gd", "GrowsDown", VMFlag::GrowsDown},
35 {"hg", "HugePage", VMFlag::HugePage},
36 {"ht", "HugeTLB", VMFlag::HugeTLB},
37 {"io", "IO", VMFlag::IO},
38 {"lo", "Locked", VMFlag::Locked},
39 {"me", "MayExecute", VMFlag::MayExecute},
40 {"mg", "Mergeable", VMFlag::Mergeable},
41 {"mm", "MixedMap", VMFlag::MixedMap},
42 {"mr", "MayRead", VMFlag::MayRead},
43 {"ms", "MayShare", VMFlag::MayShare},
44 {"mw", "MayWrite", VMFlag::MayWrite},
45 {"nh", "NoHugePage", VMFlag::NoHugePage},
46 {"nl", "NonLinear", VMFlag::NonLinear},
47 {"nr", "NotReserved", VMFlag::NotReserved},
48 {"pf", "PurePFN", VMFlag::PurePFN},
49 {"rd", "Readable", VMFlag::Readable},
50 {"rr", "Random", VMFlag::Random},
51 {"sd", "SoftDirty", VMFlag::SoftDirty},
52 {"sh", "Shared", VMFlag::Shared},
53 {"sr", "Sequential", VMFlag::Sequential},
54 {"wr", "Writable", VMFlag::Writable},
55 // clang-format on
57 } // anonymous namespace
59 constexpr size_t kVMFlags = size_t(-1);
61 // An array of known field names which may be present in an smaps file, and the
62 // offsets of the corresponding fields in a MemoryMapping class.
63 const MemoryMapping::Field MemoryMapping::sFields[] = {
64 // clang-format off
65 {"AnonHugePages", offsetof(MemoryMapping, mAnonHugePages)},
66 {"Anonymous", offsetof(MemoryMapping, mAnonymous)},
67 {"KernelPageSize", offsetof(MemoryMapping, mKernelPageSize)},
68 {"LazyFree", offsetof(MemoryMapping, mLazyFree)},
69 {"Locked", offsetof(MemoryMapping, mLocked)},
70 {"MMUPageSize", offsetof(MemoryMapping, mMMUPageSize)},
71 {"Private_Clean", offsetof(MemoryMapping, mPrivate_Clean)},
72 {"Private_Dirty", offsetof(MemoryMapping, mPrivate_Dirty)},
73 {"Private_Hugetlb", offsetof(MemoryMapping, mPrivate_Hugetlb)},
74 {"Pss", offsetof(MemoryMapping, mPss)},
75 {"Referenced", offsetof(MemoryMapping, mReferenced)},
76 {"Rss", offsetof(MemoryMapping, mRss)},
77 {"Shared_Clean", offsetof(MemoryMapping, mShared_Clean)},
78 {"Shared_Dirty", offsetof(MemoryMapping, mShared_Dirty)},
79 {"Shared_Hugetlb", offsetof(MemoryMapping, mShared_Hugetlb)},
80 {"ShmemPmdMapped", offsetof(MemoryMapping, mShmemPmdMapped)},
81 {"Size", offsetof(MemoryMapping, mSize)},
82 {"Swap", offsetof(MemoryMapping, mSwap)},
83 {"SwapPss", offsetof(MemoryMapping, mSwapPss)},
84 // VmFlags is a special case. It contains an array of flag strings, which
85 // describe attributes of the mapping, rather than a mapping size. We include
86 // it in this array to aid in parsing, but give it a separate sentinel value,
87 // and treat it specially.
88 {"VmFlags", kVMFlags},
89 // clang-format on
92 template <typename T, int n>
93 const T* FindEntry(const char* aName, const T (&aEntries)[n]) {
94 size_t index;
95 if (BinarySearchIf(
96 aEntries, 0, n,
97 [&](const T& aEntry) { return strcmp(aName, aEntry.mName); },
98 &index)) {
99 return &aEntries[index];
101 return nullptr;
104 using Perm = MemoryMapping::Perm;
105 using PermSet = MemoryMapping::PermSet;
107 nsresult GetMemoryMappings(nsTArray<MemoryMapping>& aMappings, pid_t aPid) {
108 std::ifstream stream;
109 if (aPid == 0) {
110 stream.open("/proc/self/smaps");
111 } else {
112 std::ostringstream path;
113 path << "/proc/" << aPid << "/smaps" << std::ends;
114 stream.open(path.str());
116 if (stream.fail()) {
117 return NS_ERROR_FAILURE;
120 MemoryMapping* current = nullptr;
121 std::string line;
122 while (std::getline(stream, line)) {
123 size_t start, end, offset;
124 char flags[4] = "---";
125 char name[512];
127 name[0] = 0;
129 // clang-format off
130 // Match the start of an entry. A typical line looks something like:
132 // 1487118a7000-148711a5a000 r-xp 00000000 103:03 54004561 /usr/lib/libc-2.27.so
133 // clang-format on
134 if (sscanf(line.c_str(), "%zx-%zx %4c %zx %*u:%*u %*u %511s\n", &start,
135 &end, flags, &offset, name) >= 4) {
136 PermSet perms;
137 if (flags[0] == 'r') {
138 perms += Perm::Read;
140 if (flags[1] == 'w') {
141 perms += Perm::Write;
143 if (flags[2] == 'x') {
144 perms += Perm::Execute;
146 if (flags[3] == 'p') {
147 perms += Perm::Private;
148 } else if (flags[3] == 's') {
149 perms += Perm::Shared;
152 current = aMappings.AppendElement(
153 MemoryMapping{start, end, perms, offset, name});
154 continue;
156 if (!current) {
157 continue;
160 char* savePtr;
161 char* fieldName = strtok_r(line.data(), ":", &savePtr);
162 if (!fieldName) {
163 continue;
165 auto* field = FindEntry(fieldName, MemoryMapping::sFields);
166 if (!field) {
167 continue;
170 if (field->mOffset == kVMFlags) {
171 while (char* flagName = strtok_r(nullptr, " \n", &savePtr)) {
172 if (auto* flag = FindEntry(flagName, sVMFlagStrings)) {
173 current->mFlags += flag->mFlag;
176 continue;
179 const char* rest = strtok_r(nullptr, "\n", &savePtr);
180 size_t value;
181 if (sscanf(rest, "%zd kB", &value) > 0) {
182 current->ValueForField(*field) = value * 1024;
186 return NS_OK;
189 void MemoryMapping::Dump(nsACString& aOut) const {
190 aOut.AppendPrintf("%zx-%zx Size: %zu Offset: %zx %s\n", mStart, mEnd,
191 mEnd - mStart, mOffset, mName.get());
193 for (auto& field : MemoryMapping::sFields) {
194 if (field.mOffset < sizeof(*this)) {
195 aOut.AppendPrintf(" %s: %zd\n", field.mName, ValueForField(field));
199 aOut.AppendPrintf(" Flags: %x\n", mFlags.serialize());
200 for (auto& flag : sVMFlagStrings) {
201 if (mFlags.contains(flag.mFlag)) {
202 aOut.AppendPrintf(" : %s %s\n", flag.mName, flag.mPrettyName);
207 } // namespace mozilla