/src/mozilla-central/xpcom/base/MemoryMapping.cpp
Line | Count | Source (jump to first uncovered line) |
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/. */ |
6 | | |
7 | | #include "mozilla/MemoryMapping.h" |
8 | | |
9 | | #include "mozilla/BinarySearch.h" |
10 | | #include "mozilla/FileUtils.h" |
11 | | |
12 | | #include <fstream> |
13 | | #include <string> |
14 | | |
15 | | namespace mozilla { |
16 | | |
17 | | namespace { |
18 | | struct VMFlagString |
19 | | { |
20 | | const char* mName; |
21 | | const char* mPrettyName; |
22 | | VMFlag mFlag; |
23 | | }; |
24 | | |
25 | | static const VMFlagString sVMFlagStrings[] = { |
26 | | {"ac", "Accountable", VMFlag::Accountable}, |
27 | | {"ar", "ArchSpecific", VMFlag::ArchSpecific}, |
28 | | {"dc", "NoFork", VMFlag::NoFork}, |
29 | | {"dd", "NoCore", VMFlag::NoCore}, |
30 | | {"de", "NoExpand", VMFlag::NoExpand}, |
31 | | {"dw", "DisabledWrite", VMFlag::DisabledWrite}, |
32 | | {"ex", "Executable", VMFlag::Executable}, |
33 | | {"gd", "GrowsDown", VMFlag::GrowsDown}, |
34 | | {"hg", "HugePage", VMFlag::HugePage}, |
35 | | {"ht", "HugeTLB", VMFlag::HugeTLB}, |
36 | | {"io", "IO", VMFlag::IO}, |
37 | | {"lo", "Locked", VMFlag::Locked}, |
38 | | {"me", "MayExecute", VMFlag::MayExecute}, |
39 | | {"mg", "Mergeable", VMFlag::Mergeable}, |
40 | | {"mm", "MixedMap", VMFlag::MixedMap}, |
41 | | {"mr", "MayRead", VMFlag::MayRead}, |
42 | | {"ms", "MayShare", VMFlag::MayShare}, |
43 | | {"mw", "MayWrite", VMFlag::MayWrite}, |
44 | | {"nh", "NoHugePage", VMFlag::NoHugePage}, |
45 | | {"nl", "NonLinear", VMFlag::NonLinear}, |
46 | | {"nr", "NotReserved", VMFlag::NotReserved}, |
47 | | {"pf", "PurePFN", VMFlag::PurePFN}, |
48 | | {"rd", "Readable", VMFlag::Readable}, |
49 | | {"rr", "Random", VMFlag::Random}, |
50 | | {"sd", "SoftDirty", VMFlag::SoftDirty}, |
51 | | {"sh", "Shared", VMFlag::Shared}, |
52 | | {"sr", "Sequential", VMFlag::Sequential}, |
53 | | {"wr", "Writable", VMFlag::Writable}, |
54 | | }; |
55 | | } // anonymous namespace |
56 | | |
57 | | constexpr size_t kVMFlags = size_t(-1); |
58 | | |
59 | | // An array of known field names which may be present in an smaps file, and the |
60 | | // offsets of the corresponding fields in a MemoryMapping class. |
61 | | const MemoryMapping::Field MemoryMapping::sFields[] = { |
62 | | {"AnonHugePages", offsetof(MemoryMapping, mAnonHugePages)}, |
63 | | {"Anonymous", offsetof(MemoryMapping, mAnonymous)}, |
64 | | {"KernelPageSize", offsetof(MemoryMapping, mKernelPageSize)}, |
65 | | {"LazyFree", offsetof(MemoryMapping, mLazyFree)}, |
66 | | {"Locked", offsetof(MemoryMapping, mLocked)}, |
67 | | {"MMUPageSize", offsetof(MemoryMapping, mMMUPageSize)}, |
68 | | {"Private_Clean", offsetof(MemoryMapping, mPrivate_Clean)}, |
69 | | {"Private_Dirty", offsetof(MemoryMapping, mPrivate_Dirty)}, |
70 | | {"Private_Hugetlb", offsetof(MemoryMapping, mPrivate_Hugetlb)}, |
71 | | {"Pss", offsetof(MemoryMapping, mPss)}, |
72 | | {"Referenced", offsetof(MemoryMapping, mReferenced)}, |
73 | | {"Rss", offsetof(MemoryMapping, mRss)}, |
74 | | {"Shared_Clean", offsetof(MemoryMapping, mShared_Clean)}, |
75 | | {"Shared_Dirty", offsetof(MemoryMapping, mShared_Dirty)}, |
76 | | {"Shared_Hugetlb", offsetof(MemoryMapping, mShared_Hugetlb)}, |
77 | | {"ShmemPmdMapped", offsetof(MemoryMapping, mShmemPmdMapped)}, |
78 | | {"Size", offsetof(MemoryMapping, mSize)}, |
79 | | {"Swap", offsetof(MemoryMapping, mSwap)}, |
80 | | {"SwapPss", offsetof(MemoryMapping, mSwapPss)}, |
81 | | // VmFlags is a special case. It contains an array of flag strings, which |
82 | | // describe attributes of the mapping, rather than a mapping size. We include |
83 | | // it in this array to aid in parsing, but give it a separate sentinel value, |
84 | | // and treat it specially. |
85 | | {"VmFlags", kVMFlags}, |
86 | | }; |
87 | | |
88 | | template <typename T, int n> |
89 | | const T* |
90 | | FindEntry(const char* aName, const T (&aEntries)[n]) |
91 | 0 | { |
92 | 0 | size_t index; |
93 | 0 | if (BinarySearchIf(aEntries, 0, n, |
94 | 0 | [&] (const T& aEntry) { |
95 | 0 | return strcmp(aName, aEntry.mName); |
96 | 0 | }, Unexecuted instantiation: mozilla::MemoryMapping::Field const* mozilla::FindEntry<mozilla::MemoryMapping::Field, 20>(char const*, mozilla::MemoryMapping::Field const (&) [20])::{lambda(mozilla::MemoryMapping::Field const&)#1}::operator()(mozilla::MemoryMapping::Field const&) const Unexecuted instantiation: Unified_cpp_xpcom_base0.cpp:mozilla::(anonymous namespace)::VMFlagString const* mozilla::FindEntry<mozilla::(anonymous namespace)::VMFlagString, 28>(char const*, mozilla::(anonymous namespace)::VMFlagString const (&) [28])::{lambda(mozilla::(anonymous namespace)::VMFlagString const&)#1}::operator()(mozilla::(anonymous namespace)::VMFlagString const&) const |
97 | 0 | &index)) { |
98 | 0 | return &aEntries[index]; |
99 | 0 | } |
100 | 0 | return nullptr; |
101 | 0 | } Unexecuted instantiation: mozilla::MemoryMapping::Field const* mozilla::FindEntry<mozilla::MemoryMapping::Field, 20>(char const*, mozilla::MemoryMapping::Field const (&) [20]) Unexecuted instantiation: Unified_cpp_xpcom_base0.cpp:mozilla::(anonymous namespace)::VMFlagString const* mozilla::FindEntry<mozilla::(anonymous namespace)::VMFlagString, 28>(char const*, mozilla::(anonymous namespace)::VMFlagString const (&) [28]) |
102 | | |
103 | | using Perm = MemoryMapping::Perm; |
104 | | using PermSet = MemoryMapping::PermSet; |
105 | | |
106 | | nsresult |
107 | | GetMemoryMappings(nsTArray<MemoryMapping>& aMappings) |
108 | 0 | { |
109 | 0 | std::ifstream stream("/proc/self/smaps"); |
110 | 0 | if (stream.fail()) { |
111 | 0 | return NS_ERROR_FAILURE; |
112 | 0 | } |
113 | 0 | |
114 | 0 | MemoryMapping* current = nullptr; |
115 | 0 | std::string buffer; |
116 | 0 | while (std::getline(stream, buffer)) { |
117 | 0 | size_t start, end, offset; |
118 | 0 | char flags[4] = "---"; |
119 | 0 | char name[512]; |
120 | 0 |
|
121 | 0 | name[0] = 0; |
122 | 0 |
|
123 | 0 | // Match the start of an entry. A typical line looks something like: |
124 | 0 | // |
125 | 0 | // 1487118a7000-148711a5a000 r-xp 00000000 103:03 54004561 /usr/lib/libc-2.27.so |
126 | 0 | if (sscanf(buffer.c_str(), "%zx-%zx %4c %zx %*u:%*u %*u %511s\n", |
127 | 0 | &start, &end, flags, &offset, name) >= 4) { |
128 | 0 | PermSet perms; |
129 | 0 | if (flags[0] == 'r') { |
130 | 0 | perms += Perm::Read; |
131 | 0 | } |
132 | 0 | if (flags[1] == 'w') { |
133 | 0 | perms += Perm::Write; |
134 | 0 | } |
135 | 0 | if (flags[2] == 'x') { |
136 | 0 | perms += Perm::Execute; |
137 | 0 | } |
138 | 0 | if (flags[3] == 'p') { |
139 | 0 | perms += Perm::Private; |
140 | 0 | } else if (flags[3] == 's') { |
141 | 0 | perms += Perm::Shared; |
142 | 0 | } |
143 | 0 |
|
144 | 0 | current = aMappings.AppendElement(MemoryMapping{start, end, perms, offset, name}); |
145 | 0 | continue; |
146 | 0 | } |
147 | 0 | if (!current) { |
148 | 0 | continue; |
149 | 0 | } |
150 | 0 | |
151 | 0 | nsAutoCStringN<128> line(buffer.c_str()); |
152 | 0 | char* savePtr; |
153 | 0 | char* fieldName = strtok_r(line.BeginWriting(), ":", &savePtr); |
154 | 0 | if (!fieldName) { |
155 | 0 | continue; |
156 | 0 | } |
157 | 0 | auto* field = FindEntry(fieldName, MemoryMapping::sFields); |
158 | 0 | if (!field) { |
159 | 0 | continue; |
160 | 0 | } |
161 | 0 | |
162 | 0 | if (field->mOffset == kVMFlags) { |
163 | 0 | while (char* flagName = strtok_r(nullptr, " \n", &savePtr)) { |
164 | 0 | if (auto* flag = FindEntry(flagName, sVMFlagStrings)) { |
165 | 0 | current->mFlags += flag->mFlag; |
166 | 0 | } |
167 | 0 | } |
168 | 0 | continue; |
169 | 0 | } |
170 | 0 |
|
171 | 0 | const char* rest = strtok_r(nullptr, "\n", &savePtr); |
172 | 0 | size_t value; |
173 | 0 | if (sscanf(rest, "%zd kB", &value) > 0) { |
174 | 0 | current->ValueForField(*field) = value * 1024; |
175 | 0 | } |
176 | 0 | } |
177 | 0 |
|
178 | 0 | return NS_OK; |
179 | 0 | } |
180 | | |
181 | | void |
182 | | MemoryMapping::Dump(nsACString& aOut) const |
183 | 0 | { |
184 | 0 | aOut.AppendPrintf("%zx-%zx Size: %zu Offset: %zx %s\n", |
185 | 0 | mStart, mEnd, |
186 | 0 | mEnd - mStart, |
187 | 0 | mOffset, mName.get()); |
188 | 0 |
|
189 | 0 | for (auto& field : MemoryMapping::sFields) { |
190 | 0 | if (field.mOffset < sizeof(*this)) { |
191 | 0 | aOut.AppendPrintf(" %s: %zd\n", field.mName, ValueForField(field)); |
192 | 0 | } |
193 | 0 | } |
194 | 0 |
|
195 | 0 | aOut.AppendPrintf(" Flags: %x\n", mFlags.serialize()); |
196 | 0 | for (auto& flag : sVMFlagStrings) { |
197 | 0 | if (mFlags.contains(flag.mFlag)) { |
198 | 0 | aOut.AppendPrintf(" : %s %s\n", flag.mName, flag.mPrettyName); |
199 | 0 | } |
200 | 0 | } |
201 | 0 | } |
202 | | |
203 | | } // namespace mozilla |