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(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
>> std::hex
>> region
->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 uint32
ParseSmapsCounter(std::istream
* smaps
,
77 ProcessMemoryMaps::VMRegion
* region
) {
78 // e.g., "RSS: 0 Kb\n"
80 std::string counter_name
;
81 *smaps
>> counter_name
;
83 // TODO(primiano): "Swap" should also be accounted as resident. Check
84 // whether Rss isn't already counting swapped and fix below if that is
86 if (counter_name
== "Rss:") {
87 *smaps
>> std::dec
>> region
->byte_stats_resident
;
88 region
->byte_stats_resident
*= 1024;
90 } else if (counter_name
== "Anonymous:") {
91 *smaps
>> std::dec
>> region
->byte_stats_anonymous
;
92 region
->byte_stats_anonymous
*= 1024;
97 // Paranoid check against changes of the Kernel /proc interface.
101 DCHECK_EQ("kB", unit
);
105 smaps
->ignore(kMaxLineSize
, '\n');
110 uint32
ReadLinuxProcSmapsFile(std::istream
* smaps
, ProcessMemoryMaps
* pmm
) {
114 const uint32 kNumExpectedCountersPerRegion
= 2;
115 uint32 counters_parsed_for_current_region
= 0;
116 uint32 num_valid_regions
= 0;
117 ProcessMemoryMaps::VMRegion region
;
118 bool should_add_current_region
= false;
120 int next
= smaps
->peek();
121 if (next
== std::ifstream::traits_type::eof() || next
== '\n')
123 if (isxdigit(next
) && !isupper(next
)) {
125 counters_parsed_for_current_region
= 0;
126 should_add_current_region
= ParseSmapsHeader(smaps
, ®ion
);
128 counters_parsed_for_current_region
+= ParseSmapsCounter(smaps
, ®ion
);
129 DCHECK_LE(counters_parsed_for_current_region
,
130 kNumExpectedCountersPerRegion
);
131 if (counters_parsed_for_current_region
== kNumExpectedCountersPerRegion
) {
132 if (should_add_current_region
) {
133 pmm
->AddVMRegion(region
);
135 should_add_current_region
= false;
140 return num_valid_regions
;
144 #endif // defined(OS_LINUX) || defined(OS_ANDROID)
147 ProcessMemoryMapsDumpProvider
* ProcessMemoryMapsDumpProvider::GetInstance() {
148 return Singleton
<ProcessMemoryMapsDumpProvider
,
149 LeakySingletonTraits
<ProcessMemoryMapsDumpProvider
>>::get();
152 ProcessMemoryMapsDumpProvider::ProcessMemoryMapsDumpProvider() {
155 ProcessMemoryMapsDumpProvider::~ProcessMemoryMapsDumpProvider() {
158 // Called at trace dump point time. Creates a snapshot the memory maps for the
160 bool ProcessMemoryMapsDumpProvider::DumpInto(ProcessMemoryDump
* pmd
) {
163 #if defined(OS_LINUX) || defined(OS_ANDROID)
164 if (UNLIKELY(proc_smaps_for_testing
)) {
165 res
= ReadLinuxProcSmapsFile(proc_smaps_for_testing
, pmd
->process_mmaps());
167 std::ifstream
proc_self_smaps("/proc/self/smaps");
168 res
= ReadLinuxProcSmapsFile(&proc_self_smaps
, pmd
->process_mmaps());
171 LOG(ERROR
) << "ProcessMemoryMaps dump provider is supported only on Linux";
175 pmd
->set_has_process_mmaps();
182 const char* ProcessMemoryMapsDumpProvider::GetFriendlyName() const {
183 return kDumperFriendlyName
;
186 } // namespace trace_event