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

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

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

Powered by Google App Engine
This is Rietveld 408576698