Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(59)

Side by Side Diff: components/tracing/process_metrics_memory_dump_provider.cc

Issue 1617263002: Revert of [tracing] Dump child processes' memory metrics in browser (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@web_cache2_base
Patch Set: Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/tracing/process_metrics_memory_dump_provider.h"
6
7 #include <fcntl.h>
8 #include <stdint.h>
9
10 #include <map>
11
12 #include "base/files/file_util.h"
13 #include "base/files/scoped_file.h"
14 #include "base/format_macros.h"
15 #include "base/lazy_instance.h"
16 #include "base/logging.h"
17 #include "base/process/process_metrics.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/string_util.h"
20 #include "base/trace_event/memory_dump_manager.h"
21 #include "base/trace_event/process_memory_dump.h"
22 #include "base/trace_event/process_memory_maps.h"
23 #include "base/trace_event/process_memory_totals.h"
24 #include "build/build_config.h"
25
26 namespace tracing {
27
28 namespace {
29
30 base::LazyInstance<
31 std::map<base::ProcessId, scoped_ptr<ProcessMetricsMemoryDumpProvider>>>::
32 Leaky g_dump_providers_map = LAZY_INSTANCE_INITIALIZER;
33
34 #if defined(OS_LINUX) || defined(OS_ANDROID)
35 const char kClearPeakRssCommand[] = "5";
36
37 const uint32_t kMaxLineSize = 4096;
38
39 bool ParseSmapsHeader(const char* header_line,
40 base::trace_event::ProcessMemoryMaps::VMRegion* region) {
41 // e.g., "00400000-00421000 r-xp 00000000 fc:01 1234 /foo.so\n"
42 bool res = true; // Whether this region should be appended or skipped.
43 uint64_t end_addr = 0;
44 char protection_flags[5] = {0};
45 char mapped_file[kMaxLineSize];
46
47 if (sscanf(header_line, "%" SCNx64 "-%" SCNx64 " %4c %*s %*s %*s%4095[^\n]\n",
48 &region->start_address, &end_addr, protection_flags,
49 mapped_file) != 4)
50 return false;
51
52 if (end_addr > region->start_address) {
53 region->size_in_bytes = end_addr - region->start_address;
54 } else {
55 // This is not just paranoia, it can actually happen (See crbug.com/461237).
56 region->size_in_bytes = 0;
57 res = false;
58 }
59
60 region->protection_flags = 0;
61 if (protection_flags[0] == 'r') {
62 region->protection_flags |=
63 base::trace_event::ProcessMemoryMaps::VMRegion::kProtectionFlagsRead;
64 }
65 if (protection_flags[1] == 'w') {
66 region->protection_flags |=
67 base::trace_event::ProcessMemoryMaps::VMRegion::kProtectionFlagsWrite;
68 }
69 if (protection_flags[2] == 'x') {
70 region->protection_flags |=
71 base::trace_event::ProcessMemoryMaps::VMRegion::kProtectionFlagsExec;
72 }
73
74 region->mapped_file = mapped_file;
75 base::TrimWhitespaceASCII(region->mapped_file, base::TRIM_ALL,
76 &region->mapped_file);
77
78 return res;
79 }
80
81 uint64_t ReadCounterBytes(char* counter_line) {
82 uint64_t counter_value = 0;
83 int res = sscanf(counter_line, "%*s %" SCNu64 " kB", &counter_value);
84 return res == 1 ? counter_value * 1024 : 0;
85 }
86
87 uint32_t ParseSmapsCounter(
88 char* counter_line,
89 base::trace_event::ProcessMemoryMaps::VMRegion* region) {
90 // A smaps counter lines looks as follows: "RSS: 0 Kb\n"
91 uint32_t res = 1;
92 char counter_name[20];
93 int did_read = sscanf(counter_line, "%19[^\n ]", counter_name);
94 if (did_read != 1)
95 return 0;
96
97 if (strcmp(counter_name, "Pss:") == 0) {
98 region->byte_stats_proportional_resident = ReadCounterBytes(counter_line);
99 } else if (strcmp(counter_name, "Private_Dirty:") == 0) {
100 region->byte_stats_private_dirty_resident = ReadCounterBytes(counter_line);
101 } else if (strcmp(counter_name, "Private_Clean:") == 0) {
102 region->byte_stats_private_clean_resident = ReadCounterBytes(counter_line);
103 } else if (strcmp(counter_name, "Shared_Dirty:") == 0) {
104 region->byte_stats_shared_dirty_resident = ReadCounterBytes(counter_line);
105 } else if (strcmp(counter_name, "Shared_Clean:") == 0) {
106 region->byte_stats_shared_clean_resident = ReadCounterBytes(counter_line);
107 } else if (strcmp(counter_name, "Swap:") == 0) {
108 region->byte_stats_swapped = ReadCounterBytes(counter_line);
109 } else {
110 res = 0;
111 }
112
113 return res;
114 }
115
116 uint32_t ReadLinuxProcSmapsFile(FILE* smaps_file,
117 base::trace_event::ProcessMemoryMaps* pmm) {
118 if (!smaps_file)
119 return 0;
120
121 fseek(smaps_file, 0, SEEK_SET);
122
123 char line[kMaxLineSize];
124 const uint32_t kNumExpectedCountersPerRegion = 6;
125 uint32_t counters_parsed_for_current_region = 0;
126 uint32_t num_valid_regions = 0;
127 base::trace_event::ProcessMemoryMaps::VMRegion region;
128 bool should_add_current_region = false;
129 for (;;) {
130 line[0] = '\0';
131 if (fgets(line, kMaxLineSize, smaps_file) == nullptr || !strlen(line))
132 break;
133 if (isxdigit(line[0]) && !isupper(line[0])) {
134 region = base::trace_event::ProcessMemoryMaps::VMRegion();
135 counters_parsed_for_current_region = 0;
136 should_add_current_region = ParseSmapsHeader(line, &region);
137 } else {
138 counters_parsed_for_current_region += ParseSmapsCounter(line, &region);
139 DCHECK_LE(counters_parsed_for_current_region,
140 kNumExpectedCountersPerRegion);
141 if (counters_parsed_for_current_region == kNumExpectedCountersPerRegion) {
142 if (should_add_current_region) {
143 pmm->AddVMRegion(region);
144 ++num_valid_regions;
145 should_add_current_region = false;
146 }
147 }
148 }
149 }
150 return num_valid_regions;
151 }
152 #endif // defined(OS_LINUX) || defined(OS_ANDROID)
153
154 scoped_ptr<base::ProcessMetrics> CreateProcessMetrics(base::ProcessId process) {
155 if (process == base::kNullProcessId)
156 return make_scoped_ptr(base::ProcessMetrics::CreateCurrentProcessMetrics());
157 #if defined(OS_LINUX) || defined(OS_ANDROID)
158 // Just pass ProcessId instead of handle since they are the same in linux and
159 // android.
160 return make_scoped_ptr(base::ProcessMetrics::CreateProcessMetrics(process));
161 #else
162 // Creating process metrics for child processes in mac or windows requires
163 // additional information like ProcessHandle or port provider. This is a non
164 // needed use case.
165 NOTREACHED();
166 return scoped_ptr<base::ProcessMetrics>();
167 #endif // defined(OS_LINUX) || defined(OS_ANDROID)
168 }
169
170 } // namespace
171
172 // static
173 uint64_t ProcessMetricsMemoryDumpProvider::rss_bytes_for_testing = 0;
174
175 #if defined(OS_LINUX) || defined(OS_ANDROID)
176
177 // static
178 FILE* ProcessMetricsMemoryDumpProvider::proc_smaps_for_testing = nullptr;
179
180 bool ProcessMetricsMemoryDumpProvider::DumpProcessMemoryMaps(
181 const base::trace_event::MemoryDumpArgs& args,
182 base::trace_event::ProcessMemoryDump* pmd) {
183 uint32_t res = 0;
184 if (proc_smaps_for_testing) {
185 res = ReadLinuxProcSmapsFile(proc_smaps_for_testing, pmd->process_mmaps());
186 } else {
187 std::string file_name = "/proc/" + (process_ == base::kNullProcessId
188 ? "self"
189 : base::IntToString(process_)) +
190 "/smaps";
191 base::ScopedFILE smaps_file(fopen(file_name.c_str(), "r"));
192 res = ReadLinuxProcSmapsFile(smaps_file.get(), pmd->process_mmaps());
193 }
194
195 if (res)
196 pmd->set_has_process_mmaps();
197 return res;
198 }
199 #endif // defined(OS_LINUX) || defined(OS_ANDROID)
200
201 // static
202 void ProcessMetricsMemoryDumpProvider::RegisterForProcess(
203 base::ProcessId process) {
204 scoped_ptr<ProcessMetricsMemoryDumpProvider> metrics_provider(
205 new ProcessMetricsMemoryDumpProvider(process));
206 base::trace_event::MemoryDumpProvider::Options options(process);
207 base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
208 metrics_provider.get(), "ProcessMemoryMetrics", nullptr, options);
209 bool did_insert =
210 g_dump_providers_map.Get()
211 .insert(std::make_pair(process, std::move(metrics_provider)))
212 .second;
213 if (!did_insert) {
214 DLOG(ERROR) << "ProcessMetricsMemoryDumpProvider already registered for "
215 << (process == base::kNullProcessId
216 ? "current process"
217 : "process id " + base::IntToString(process));
218 }
219 }
220
221 // static
222 void ProcessMetricsMemoryDumpProvider::UnregisterForProcess(
223 base::ProcessId process) {
224 auto iter = g_dump_providers_map.Get().find(process);
225 if (iter == g_dump_providers_map.Get().end()) {
226 return;
227 }
228 base::trace_event::MemoryDumpManager::GetInstance()
229 ->UnregisterAndDeleteDumpProviderSoon(std::move(iter->second));
230 g_dump_providers_map.Get().erase(iter);
231 }
232
233 ProcessMetricsMemoryDumpProvider::ProcessMetricsMemoryDumpProvider(
234 base::ProcessId process)
235 : process_(process),
236 process_metrics_(CreateProcessMetrics(process)),
237 is_rss_peak_resettable_(true) {}
238
239 ProcessMetricsMemoryDumpProvider::~ProcessMetricsMemoryDumpProvider() {}
240
241 // Called at trace dump point time. Creates a snapshot of the memory maps for
242 // the current process.
243 bool ProcessMetricsMemoryDumpProvider::OnMemoryDump(
244 const base::trace_event::MemoryDumpArgs& args,
245 base::trace_event::ProcessMemoryDump* pmd) {
246 bool res = DumpProcessTotals(args, pmd);
247
248 #if defined(OS_LINUX) || defined(OS_ANDROID)
249 if (args.level_of_detail ==
250 base::trace_event::MemoryDumpLevelOfDetail::DETAILED)
251 res &= DumpProcessMemoryMaps(args, pmd);
252 #endif
253 return res;
254 }
255
256 bool ProcessMetricsMemoryDumpProvider::DumpProcessTotals(
257 const base::trace_event::MemoryDumpArgs& args,
258 base::trace_event::ProcessMemoryDump* pmd) {
259 const uint64_t rss_bytes = rss_bytes_for_testing
260 ? rss_bytes_for_testing
261 : process_metrics_->GetWorkingSetSize();
262
263 // rss_bytes will be 0 if the process ended while dumping.
264 if (!rss_bytes)
265 return false;
266
267 uint64_t peak_rss_bytes = 0;
268
269 #if !defined(OS_IOS)
270 peak_rss_bytes = process_metrics_->GetPeakWorkingSetSize();
271 #if defined(OS_LINUX) || defined(OS_ANDROID)
272 if (is_rss_peak_resettable_) {
273 std::string clear_refs_file =
274 "/proc/" +
275 (process_ == base::kNullProcessId ? "self"
276 : base::IntToString(process_)) +
277 "/clear_refs";
278 int clear_refs_fd = open(clear_refs_file.c_str(), O_WRONLY);
279 if (clear_refs_fd > 0 &&
280 base::WriteFileDescriptor(clear_refs_fd, kClearPeakRssCommand,
281 sizeof(kClearPeakRssCommand))) {
282 pmd->process_totals()->set_is_peak_rss_resetable(true);
283 } else {
284 is_rss_peak_resettable_ = false;
285 }
286 close(clear_refs_fd);
287 }
288 #elif defined(MACOSX)
289 size_t private_bytes;
290 bool res = process_metrics_->GetMemoryBytes(&private_bytes,
291 nullptr /* shared_bytes */);
292 if (res)
293 pmd->process_totals()->SetExtraFieldInBytes("private_bytes", private_bytes);
294 #endif // defined(OS_LINUX) || defined(OS_ANDROID)
295 #endif // !defined(OS_IOS)
296
297 pmd->process_totals()->set_resident_set_bytes(rss_bytes);
298 pmd->set_has_process_totals();
299 pmd->process_totals()->set_peak_resident_set_bytes(peak_rss_bytes);
300
301 // Returns true even if other metrics failed, since rss is reported.
302 return true;
303 }
304
305 } // namespace tracing
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698