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

Side by Side Diff: base/trace_event/process_memory_maps_dump_provider.cc

Issue 1332943002: [tracing] Smaps provider uses fscanf instead of istream (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 3 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
1 // Copyright 2015 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/trace_event/process_memory_maps_dump_provider.h" 5 #include "base/trace_event/process_memory_maps_dump_provider.h"
6 6
7 #include <cctype> 7 #include <fcntl.h>
8 #include <fstream> 8 #include <inttypes.h>
Primiano Tucci (use gerrit) 2015/09/10 16:52:36 inttypes is not well supported on windows. use bas
ssid 2015/09/21 11:25:43 Done.
9 9
10 #include "base/files/file_util.h"
10 #include "base/logging.h" 11 #include "base/logging.h"
11 #include "base/process/process_metrics.h" 12 #include "base/process/process_metrics.h"
12 #include "base/trace_event/process_memory_dump.h" 13 #include "base/trace_event/process_memory_dump.h"
13 #include "base/trace_event/process_memory_maps.h" 14 #include "base/trace_event/process_memory_maps.h"
14 15
15 namespace base { 16 namespace base {
16 namespace trace_event { 17 namespace trace_event {
17 18
18 #if defined(OS_LINUX) || defined(OS_ANDROID) 19 #if defined(OS_LINUX) || defined(OS_ANDROID)
19 // static 20 // static
20 std::istream* ProcessMemoryMapsDumpProvider::proc_smaps_for_testing = nullptr; 21 ScopedFD* ProcessMemoryMapsDumpProvider::proc_smaps_for_testing = nullptr;
21 22
22 namespace { 23 namespace {
23 24
24 const uint32 kMaxLineSize = 4096; 25 const uint32 kMaxLineSize = 4096;
26 const uint32 kMaxCounterNameSize = 20;
25 27
26 bool ParseSmapsHeader(std::istream* smaps, 28 bool ParseSmapsHeader(FILE* smaps_file, ProcessMemoryMaps::VMRegion* region) {
27 ProcessMemoryMaps::VMRegion* region) {
28 // e.g., "00400000-00421000 r-xp 00000000 fc:01 1234 /foo.so\n" 29 // e.g., "00400000-00421000 r-xp 00000000 fc:01 1234 /foo.so\n"
29 bool res = true; // Whether this region should be appended or skipped. 30 bool res = true; // Whether this region should be appended or skipped.
30 uint64 end_addr; 31 uint64 end_addr = 0;
31 std::string protection_flags; 32 if (fscanf(smaps_file, "%" SCNx64 "-%" SCNx64, &region->start_address,
32 std::string ignored; 33 &end_addr) != 2)
33 *smaps >> std::hex >> region->start_address; 34 return false;
34 smaps->ignore(1); 35
35 *smaps >> std::hex >> end_addr;
36 if (end_addr > region->start_address) { 36 if (end_addr > region->start_address) {
37 region->size_in_bytes = end_addr - region->start_address; 37 region->size_in_bytes = end_addr - region->start_address;
38 } else { 38 } else {
39 // This is not just paranoia, it can actually happen (See crbug.com/461237). 39 // This is not just paranoia, it can actually happen (See crbug.com/461237).
40 region->size_in_bytes = 0; 40 region->size_in_bytes = 0;
41 res = false; 41 res = false;
42 } 42 }
43 43
44 char protection_flags[4];
Primiano Tucci (use gerrit) 2015/09/10 16:52:36 char protection_flags[5] = {0}; so you get also a
ssid 2015/09/21 11:25:43 Done.
45 CHECK_EQ(1, fscanf(smaps_file, "%4s", protection_flags));
Primiano Tucci (use gerrit) 2015/09/10 16:52:36 use %4c, is safer
ssid 2015/09/21 11:25:43 Actually %4c does not add a '\0' at the end as far
46
44 region->protection_flags = 0; 47 region->protection_flags = 0;
45 *smaps >> protection_flags;
46 CHECK_EQ(4UL, protection_flags.size());
47 if (protection_flags[0] == 'r') { 48 if (protection_flags[0] == 'r') {
48 region->protection_flags |= 49 region->protection_flags |=
49 ProcessMemoryMaps::VMRegion::kProtectionFlagsRead; 50 ProcessMemoryMaps::VMRegion::kProtectionFlagsRead;
50 } 51 }
51 if (protection_flags[1] == 'w') { 52 if (protection_flags[1] == 'w') {
52 region->protection_flags |= 53 region->protection_flags |=
53 ProcessMemoryMaps::VMRegion::kProtectionFlagsWrite; 54 ProcessMemoryMaps::VMRegion::kProtectionFlagsWrite;
54 } 55 }
55 if (protection_flags[2] == 'x') { 56 if (protection_flags[2] == 'x') {
56 region->protection_flags |= 57 region->protection_flags |=
57 ProcessMemoryMaps::VMRegion::kProtectionFlagsExec; 58 ProcessMemoryMaps::VMRegion::kProtectionFlagsExec;
58 } 59 }
59 *smaps >> ignored; // Ignore mapped file offset.
60 *smaps >> ignored; // Ignore device maj-min (fc:01 in the example above).
61 *smaps >> ignored; // Ignore inode number (1234 in the example above).
62 60
63 while (smaps->peek() == ' ') 61 // Ignore mapped file offset, device maj-min (fc:01 in the example above) and
64 smaps->ignore(1); 62 // inode number (1234 in the example above).
63 const int kMaxAddressLength = 20;
64 char ignore[kMaxAddressLength];
65 if (fscanf(smaps_file, "%s %s %s", ignore, ignore, ignore) != 3)
Primiano Tucci (use gerrit) 2015/09/10 16:52:36 you don't need the ignore vars, nor kMaxAddressLen
ssid 2015/09/21 11:25:43 Moved everything to single line fscanf.
66 return false;
67
65 char mapped_file[kMaxLineSize]; 68 char mapped_file[kMaxLineSize];
66 smaps->getline(mapped_file, sizeof(mapped_file)); 69 char next;
Primiano Tucci (use gerrit) 2015/09/10 16:52:36 you don't need all this as scanf handles multiple
ssid 2015/09/21 11:25:43 Hm, it was tricky case if file name does not exist
67 region->mapped_file = mapped_file; 70 do {
71 next = fgetc(smaps_file);
72 } while (next == ' ');
73 ungetc(next, smaps_file);
74 if (fgets(mapped_file, sizeof(mapped_file), smaps_file)) {
75 mapped_file[strlen(mapped_file) - 1] = '\0';
76 region->mapped_file = mapped_file;
77 }
68 78
69 return res; 79 return res;
70 } 80 }
71 81
72 uint64 ReadCounterBytes(std::istream* smaps) { 82 uint64 ReadCounterBytes(FILE* smaps_file) {
73 uint64 counter_value = 0; 83 uint64 counter_value = 0;
74 *smaps >> std::dec >> counter_value; 84 CHECK_EQ(1, fscanf(smaps_file, "%" SCNu64, &counter_value));
75 return counter_value * 1024; 85 return counter_value * 1024;
76 } 86 }
77 87
78 uint32 ParseSmapsCounter(std::istream* smaps, 88 uint32 ParseSmapsCounter(FILE* smaps_file,
79 ProcessMemoryMaps::VMRegion* region) { 89 ProcessMemoryMaps::VMRegion* region) {
80 // A smaps counter lines looks as follows: "RSS: 0 Kb\n" 90 // A smaps counter lines looks as follows: "RSS: 0 Kb\n"
81 uint32 res = 1; 91 uint32 res = 1;
82 std::string counter_name; 92 char counter_name[kMaxCounterNameSize];
83 *smaps >> counter_name; 93 if (fscanf(smaps_file, "%s", counter_name) != 1)
94 return 0;
84 95
85 // TODO(primiano): "Swap" should also be accounted as resident. Check 96 // TODO(primiano): "Swap" should also be accounted as resident. Check
86 // whether Rss isn't already counting swapped and fix below if that is 97 // whether Rss isn't already counting swapped and fix below if that is
87 // the case. 98 // the case.
88 if (counter_name == "Pss:") { 99 if (strcmp(counter_name, "Pss:") == 0) {
89 region->byte_stats_proportional_resident = ReadCounterBytes(smaps); 100 region->byte_stats_proportional_resident = ReadCounterBytes(smaps_file);
90 } else if (counter_name == "Private_Dirty:") { 101 } else if (strcmp(counter_name, "Private_Dirty:") == 0) {
91 region->byte_stats_private_dirty_resident = ReadCounterBytes(smaps); 102 region->byte_stats_private_dirty_resident = ReadCounterBytes(smaps_file);
92 } else if (counter_name == "Private_Clean:") { 103 } else if (strcmp(counter_name, "Private_Clean:") == 0) {
93 region->byte_stats_private_clean_resident = ReadCounterBytes(smaps); 104 region->byte_stats_private_clean_resident = ReadCounterBytes(smaps_file);
94 } else if (counter_name == "Shared_Dirty:") { 105 } else if (strcmp(counter_name, "Shared_Dirty:") == 0) {
95 region->byte_stats_shared_dirty_resident = ReadCounterBytes(smaps); 106 region->byte_stats_shared_dirty_resident = ReadCounterBytes(smaps_file);
96 } else if (counter_name == "Shared_Clean:") { 107 } else if (strcmp(counter_name, "Shared_Clean:") == 0) {
97 region->byte_stats_shared_clean_resident = ReadCounterBytes(smaps); 108 region->byte_stats_shared_clean_resident = ReadCounterBytes(smaps_file);
98 } else if (counter_name == "Swap:") { 109 } else if (strcmp(counter_name, "Swap:") == 0) {
99 region->byte_stats_swapped = ReadCounterBytes(smaps); 110 region->byte_stats_swapped = ReadCounterBytes(smaps_file);
100 } else { 111 } else {
101 res = 0; 112 res = 0;
102 } 113 }
103 114
104 #ifndef NDEBUG 115 #ifndef NDEBUG
105 // Paranoid check against changes of the Kernel /proc interface. 116 // Paranoid check against changes of the Kernel /proc interface.
106 if (res) { 117 if (res) {
107 std::string unit; 118 std::string unit(2, 0);
Primiano Tucci (use gerrit) 2015/09/10 16:52:36 I think you can just do a single scanf like scanf(
ssid 2015/09/21 11:25:43 Done.
108 *smaps >> unit; 119 fscanf(smaps_file, "%s", &unit[0]);
109 DCHECK_EQ("kB", unit); 120 DCHECK_EQ("kB", unit);
110 } 121 }
111 #endif 122 #endif
112 123
113 smaps->ignore(kMaxLineSize, '\n'); 124 // Ignore the rest of the line.
125 char ignore[kMaxLineSize];
126 fgets(ignore, sizeof(ignore), smaps_file);
114 127
115 return res; 128 return res;
116 } 129 }
117 130
118 uint32 ReadLinuxProcSmapsFile(std::istream* smaps, ProcessMemoryMaps* pmm) { 131 uint32 ReadLinuxProcSmapsFile(ScopedFD& smaps_fd, ProcessMemoryMaps* pmm) {
119 if (!smaps->good()) 132 if (!smaps_fd.is_valid())
120 return 0; 133 return 0;
121 134
135 FILE* smaps_file = fdopen(smaps_fd.get(), "r");
Primiano Tucci (use gerrit) 2015/09/10 16:52:36 Hmm I think you should cache also the FILE* struct
ssid 2015/09/21 11:25:43 Yes, Made necessary changes.
136 fseek(smaps_file, 0, SEEK_SET);
137
122 const uint32 kNumExpectedCountersPerRegion = 6; 138 const uint32 kNumExpectedCountersPerRegion = 6;
123 uint32 counters_parsed_for_current_region = 0; 139 uint32 counters_parsed_for_current_region = 0;
124 uint32 num_valid_regions = 0; 140 uint32 num_valid_regions = 0;
125 ProcessMemoryMaps::VMRegion region; 141 ProcessMemoryMaps::VMRegion region;
126 bool should_add_current_region = false; 142 bool should_add_current_region = false;
127 for (;;) { 143 for (;;) {
128 int next = smaps->peek(); 144 char next = 0;
129 if (next == std::ifstream::traits_type::eof() || next == '\n') 145 // Peek into the next integer and move pointer back.
146 next = fgetc(smaps_file);
Primiano Tucci (use gerrit) 2015/09/10 16:52:36 not sure what all this is for? aren't you trying t
ssid 2015/09/21 11:25:43 hm, the next is also used to check if it is a head
147 if (next == EOF)
130 break; 148 break;
149 fseek(smaps_file, -1, SEEK_CUR);
131 if (isxdigit(next) && !isupper(next)) { 150 if (isxdigit(next) && !isupper(next)) {
132 region = ProcessMemoryMaps::VMRegion(); 151 region = ProcessMemoryMaps::VMRegion();
133 counters_parsed_for_current_region = 0; 152 counters_parsed_for_current_region = 0;
134 should_add_current_region = ParseSmapsHeader(smaps, &region); 153 should_add_current_region = ParseSmapsHeader(smaps_file, &region);
135 } else { 154 } else {
136 counters_parsed_for_current_region += ParseSmapsCounter(smaps, &region); 155 counters_parsed_for_current_region +=
156 ParseSmapsCounter(smaps_file, &region);
137 DCHECK_LE(counters_parsed_for_current_region, 157 DCHECK_LE(counters_parsed_for_current_region,
138 kNumExpectedCountersPerRegion); 158 kNumExpectedCountersPerRegion);
139 if (counters_parsed_for_current_region == kNumExpectedCountersPerRegion) { 159 if (counters_parsed_for_current_region == kNumExpectedCountersPerRegion) {
140 if (should_add_current_region) { 160 if (should_add_current_region) {
141 pmm->AddVMRegion(region); 161 pmm->AddVMRegion(region);
142 ++num_valid_regions; 162 ++num_valid_regions;
143 should_add_current_region = false; 163 should_add_current_region = false;
144 } 164 }
145 } 165 }
146 } 166 }
(...skipping 21 matching lines...) Expand all
168 bool ProcessMemoryMapsDumpProvider::OnMemoryDump(const MemoryDumpArgs& args, 188 bool ProcessMemoryMapsDumpProvider::OnMemoryDump(const MemoryDumpArgs& args,
169 ProcessMemoryDump* pmd) { 189 ProcessMemoryDump* pmd) {
170 // Snapshot of memory maps is not taken for light dump requests. 190 // Snapshot of memory maps is not taken for light dump requests.
171 if (args.level_of_detail == MemoryDumpArgs::LevelOfDetail::LOW) 191 if (args.level_of_detail == MemoryDumpArgs::LevelOfDetail::LOW)
172 return true; 192 return true;
173 193
174 uint32 res = 0; 194 uint32 res = 0;
175 195
176 #if defined(OS_LINUX) || defined(OS_ANDROID) 196 #if defined(OS_LINUX) || defined(OS_ANDROID)
177 if (UNLIKELY(proc_smaps_for_testing)) { 197 if (UNLIKELY(proc_smaps_for_testing)) {
178 res = ReadLinuxProcSmapsFile(proc_smaps_for_testing, pmd->process_mmaps()); 198 res = ReadLinuxProcSmapsFile(*proc_smaps_for_testing, pmd->process_mmaps());
179 } else { 199 } else {
180 std::ifstream proc_self_smaps("/proc/self/smaps"); 200 ScopedFD smaps_fd(HANDLE_EINTR(open("/proc/self/smaps", O_RDONLY)));
181 res = ReadLinuxProcSmapsFile(&proc_self_smaps, pmd->process_mmaps()); 201 res = ReadLinuxProcSmapsFile(smaps_fd, pmd->process_mmaps());
182 } 202 }
183 #else 203 #else
184 LOG(ERROR) << "ProcessMemoryMaps dump provider is supported only on Linux"; 204 LOG(ERROR) << "ProcessMemoryMaps dump provider is supported only on Linux";
185 #endif 205 #endif
186 206
187 if (res > 0) { 207 if (res > 0) {
188 pmd->set_has_process_mmaps(); 208 pmd->set_has_process_mmaps();
189 return true; 209 return true;
190 } 210 }
191 211
192 return false; 212 return false;
193 } 213 }
194 214
195 } // namespace trace_event 215 } // namespace trace_event
196 } // namespace base 216 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698