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.
5 #include "base/trace_event/process_memory_maps_dump_provider.h"
10 #include "base/logging.h"
11 #include "base/process/process_metrics.h"
12 #include "base/trace_event/process_memory_dump.h"
13 #include "base/trace_event/process_memory_maps.h"
16 namespace trace_event
{
19 const char kDumperFriendlyName
[] = "ProcessMemoryMaps";
22 #if defined(OS_LINUX) || defined(OS_ANDROID)
24 std::istream
* ProcessMemoryMapsDumpProvider::proc_smaps_for_testing
= nullptr;
28 const uint32 kMaxLineSize
= 4096;
30 bool ParseSmapsHeader(std::istream
* smaps
,
31 ProcessMemoryMaps::VMRegion
* region
) {
32 // e.g., "00400000-00421000 r-xp 00000000 fc:01 1234 /foo.so\n"
33 bool res
= true; // Whether this region should be appended or skipped.
35 std::string protection_flags
;
37 *smaps
>> std::hex
>> region
->start_address
;
39 *smaps
>> std::hex
>> end_addr
;
40 if (end_addr
> region
->start_address
) {
41 region
->size_in_bytes
= end_addr
- region
->start_address
;
43 // This is not just paranoia, it can actually happen (See crbug.com/461237).
44 region
->size_in_bytes
= 0;
48 region
->protection_flags
= 0;
49 *smaps
>> protection_flags
;
50 CHECK_EQ(4UL, protection_flags
.size());
51 if (protection_flags
[0] == 'r') {
52 region
->protection_flags
|=
53 ProcessMemoryMaps::VMRegion::kProtectionFlagsRead
;
55 if (protection_flags
[1] == 'w') {
56 region
->protection_flags
|=
57 ProcessMemoryMaps::VMRegion::kProtectionFlagsWrite
;
59 if (protection_flags
[2] == 'x') {
60 region
->protection_flags
|=
61 ProcessMemoryMaps::VMRegion::kProtectionFlagsExec
;
63 *smaps
>> ignored
; // Ignore mapped file offset.
64 *smaps
>> ignored
; // Ignore device maj-min (fc:01 in the example above).
65 *smaps
>> ignored
; // Ignore inode number (1234 in the example above).
67 while (smaps
->peek() == ' ')
69 char mapped_file
[kMaxLineSize
];
70 smaps
->getline(mapped_file
, sizeof(mapped_file
));
71 region
->mapped_file
= mapped_file
;
76 uint64
ReadCounterBytes(std::istream
* smaps
) {
77 uint64 counter_value
= 0;
78 *smaps
>> std::dec
>> counter_value
;
79 return counter_value
* 1024;
82 uint32
ParseSmapsCounter(std::istream
* smaps
,
83 ProcessMemoryMaps::VMRegion
* region
) {
84 // A smaps counter lines looks as follows: "RSS: 0 Kb\n"
86 std::string counter_name
;
87 *smaps
>> counter_name
;
89 // TODO(primiano): "Swap" should also be accounted as resident. Check
90 // whether Rss isn't already counting swapped and fix below if that is
92 if (counter_name
== "Pss:") {
93 region
->byte_stats_proportional_resident
= ReadCounterBytes(smaps
);
95 } else if (counter_name
== "Private_Dirty:" ||
96 counter_name
== "Private_Clean:") {
97 // For Private and Shared counters keep the sum of the dirty + clean stats.
98 region
->byte_stats_private_resident
+= ReadCounterBytes(smaps
);
100 } else if (counter_name
== "Shared_Dirty:" ||
101 counter_name
== "Shared_Clean:") {
102 region
->byte_stats_shared_resident
+= ReadCounterBytes(smaps
);
107 // Paranoid check against changes of the Kernel /proc interface.
111 DCHECK_EQ("kB", unit
);
115 smaps
->ignore(kMaxLineSize
, '\n');
120 uint32
ReadLinuxProcSmapsFile(std::istream
* smaps
, ProcessMemoryMaps
* pmm
) {
124 const uint32 kNumExpectedCountersPerRegion
= 5;
125 uint32 counters_parsed_for_current_region
= 0;
126 uint32 num_valid_regions
= 0;
127 ProcessMemoryMaps::VMRegion region
;
128 bool should_add_current_region
= false;
130 int next
= smaps
->peek();
131 if (next
== std::ifstream::traits_type::eof() || next
== '\n')
133 if (isxdigit(next
) && !isupper(next
)) {
135 counters_parsed_for_current_region
= 0;
136 should_add_current_region
= ParseSmapsHeader(smaps
, ®ion
);
138 counters_parsed_for_current_region
+= ParseSmapsCounter(smaps
, ®ion
);
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
);
145 should_add_current_region
= false;
150 return num_valid_regions
;
154 #endif // defined(OS_LINUX) || defined(OS_ANDROID)
157 ProcessMemoryMapsDumpProvider
* ProcessMemoryMapsDumpProvider::GetInstance() {
158 return Singleton
<ProcessMemoryMapsDumpProvider
,
159 LeakySingletonTraits
<ProcessMemoryMapsDumpProvider
>>::get();
162 ProcessMemoryMapsDumpProvider::ProcessMemoryMapsDumpProvider() {
165 ProcessMemoryMapsDumpProvider::~ProcessMemoryMapsDumpProvider() {
168 // Called at trace dump point time. Creates a snapshot the memory maps for the
170 bool ProcessMemoryMapsDumpProvider::DumpInto(ProcessMemoryDump
* pmd
) {
173 #if defined(OS_LINUX) || defined(OS_ANDROID)
174 if (UNLIKELY(proc_smaps_for_testing
)) {
175 res
= ReadLinuxProcSmapsFile(proc_smaps_for_testing
, pmd
->process_mmaps());
177 std::ifstream
proc_self_smaps("/proc/self/smaps");
178 res
= ReadLinuxProcSmapsFile(&proc_self_smaps
, pmd
->process_mmaps());
181 LOG(ERROR
) << "ProcessMemoryMaps dump provider is supported only on Linux";
185 pmd
->set_has_process_mmaps();
192 const char* ProcessMemoryMapsDumpProvider::GetFriendlyName() const {
193 return kDumperFriendlyName
;
196 } // namespace trace_event