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"
21 const char* mPrettyName
;
25 static const VMFlagString sVMFlagStrings
[] = {
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
},
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
[] = {
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
},
92 template <typename T
, int n
>
93 const T
* FindEntry(const char* aName
, const T (&aEntries
)[n
]) {
97 [&](const T
& aEntry
) { return strcmp(aName
, aEntry
.mName
); },
99 return &aEntries
[index
];
104 using Perm
= MemoryMapping::Perm
;
105 using PermSet
= MemoryMapping::PermSet
;
107 nsresult
GetMemoryMappings(nsTArray
<MemoryMapping
>& aMappings
, pid_t aPid
) {
108 std::ifstream stream
;
110 stream
.open("/proc/self/smaps");
112 std::ostringstream path
;
113 path
<< "/proc/" << aPid
<< "/smaps" << std::ends
;
114 stream
.open(path
.str());
117 return NS_ERROR_FAILURE
;
120 MemoryMapping
* current
= nullptr;
122 while (std::getline(stream
, line
)) {
123 size_t start
, end
, offset
;
124 char flags
[4] = "---";
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
134 if (sscanf(line
.c_str(), "%zx-%zx %4c %zx %*u:%*u %*u %511s\n", &start
,
135 &end
, flags
, &offset
, name
) >= 4) {
137 if (flags
[0] == 'r') {
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
});
161 char* fieldName
= strtok_r(line
.data(), ":", &savePtr
);
165 auto* field
= FindEntry(fieldName
, MemoryMapping::sFields
);
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
;
179 const char* rest
= strtok_r(nullptr, "\n", &savePtr
);
181 if (sscanf(rest
, "%zd kB", &value
) > 0) {
182 current
->ValueForField(*field
) = value
* 1024;
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