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

Side by Side Diff: src/client/linux/minidump_writer/linux_dumper.cc

Issue 2161713002: Recover memory mappings before writing dump on ChromeOS (Closed) Base URL: https://chromium.googlesource.com/breakpad/breakpad.git@master
Patch Set: Created 4 years, 5 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
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2010, Google Inc. 1 // Copyright (c) 2010, Google Inc.
2 // All rights reserved. 2 // All rights reserved.
3 // 3 //
4 // Redistribution and use in source and binary forms, with or without 4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are 5 // modification, are permitted provided that the following conditions are
6 // met: 6 // met:
7 // 7 //
8 // * Redistributions of source code must retain the above copyright 8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer. 9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above 10 // * Redistributions in binary form must reproduce the above
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
77 // because the semantics of the open may be driver-specific so we'd risk 77 // because the semantics of the open may be driver-specific so we'd risk
78 // hanging the crash dumper. And a file in /dev/ almost certainly has no 78 // hanging the crash dumper. And a file in /dev/ almost certainly has no
79 // ELF file identifier anyways. 79 // ELF file identifier anyways.
80 return my_strncmp(mapping.name, 80 return my_strncmp(mapping.name,
81 kMappedFileUnsafePrefix, 81 kMappedFileUnsafePrefix,
82 sizeof(kMappedFileUnsafePrefix) - 1) == 0; 82 sizeof(kMappedFileUnsafePrefix) - 1) == 0;
83 } 83 }
84 84
85 namespace google_breakpad { 85 namespace google_breakpad {
86 86
87 #if defined(__CHROMEOS__)
88
89 namespace {
90
91 // Recover memory mappings before writing dump on ChromeOS
92 //
93 // On Linux, breakpad relies on /proc/[pid]/maps to associate symbols from
94 // addresses. ChromeOS' hugepage implementation replaces some segments with
95 // anonymous private pages, which is a restriction of current implementation
96 // in Linux kernel at the time of writing. Thus, breakpad can no longer
97 // symbolize addresses from those text segments replaced with hugepages.
98 //
99 // This postprocess tries to recover the mappings. Because hugepages are always
100 // inserted in between some .text sections, it tries to infer the names and
101 // offsets of the segments, by looking at segments immediately precede and
102 // succeed them.
103 //
104 // For example, a text segment before hugepage optimization
105 // 02001000-03002000 r-xp /opt/google/chrome/chrome
106 //
107 // can be broken into
108 // 02001000-02200000 r-xp /opt/google/chrome/chrome
109 // 02200000-03000000 r-xp
110 // 03000000-03002000 r-xp /opt/google/chrome/chrome
111 //
112 // For more details, see:
113 // crbug.com/628040 ChromeOS' use of hugepages confuses crash symbolization
Wez 2016/07/18 22:28:15 Do/will we have a bug filed to remove this hack if
114
115 // Copied from CrOS' hugepage implementation, which is unlikely to change.
116 // The hugepage size is 2M.
117 const unsigned int kHpageShift = 21;
118 const size_t kHpageSize = (1 << kHpageShift);
119 const size_t kHpageMask = (~(kHpageSize - 1));
120
121 // Find and merge anonymous r-xp segments with surrounding named segments.
122 // There are two cases:
123
124 // Case 1: curr, next
Wez 2016/07/18 22:28:15 What do these "cases" mean? Do we sometimes see Ch
125 // curr is anonymous
126 // curr is r-xp
127 // curr.size >= 2M
128 // curr.size is a multiple of 2M.
Wez 2016/07/18 22:28:15 Do we need to be this specific about the sizes? Ho
129 // next is backed by some file.
130 // curr and next are contiguous.
131 // offset(next) == sizeof(curr)
132 void TryRecoverMappings(MappingInfo *curr, MappingInfo *next) {
Wez 2016/07/18 22:28:15 Looks like this function actually checks whether |
133 // Merged segments are marked with size = 0.
Wez 2016/07/18 22:28:15 It's not clear from the context that it's possible
134 if (curr->size == 0 || next->size == 0)
135 return;
136
137 if (curr->size >= kHpageSize &&
138 curr->exec &&
139 (curr->size & kHpageMask) == curr->size &&
140 (curr->start_addr & kHpageMask) == curr->start_addr &&
141 curr->name[0] == '\0' &&
142 next->name[0] != '\0' &&
143 curr->start_addr + curr->size == next->start_addr &&
144 curr->size == next->offset) {
Wez 2016/07/18 22:28:15 Strongly suggest turning this into a set of early-
145
146 // matched
Wez 2016/07/18 22:28:15 nit: This comment doesn't add any information :P
147 my_strlcpy(curr->name, next->name, NAME_MAX);
148 if (next->exec) {
149 // (curr, next)
Wez 2016/07/18 22:28:14 nit: Nor does this one ;)
150 curr->size += next->size;
151 next->size = 0;
152 }
153 }
154 }
155
156 // Case 2: prev, curr, next
157 // curr is anonymous
158 // curr is r-xp
159 // curr.size >= 2M
160 // curr.size is a multiple of 2M.
161 // next and prev are backed by the same file.
162 // prev, curr and next are contiguous.
163 // offset(next) == offset(prev) + sizeof(prev) + sizeof(curr)
164 void TryRecoverMappings(MappingInfo *prev, MappingInfo *curr,
165 MappingInfo *next) {
166 // Merged segments are marked with size = 0.
167 if (prev->size == 0 || curr->size == 0 || next->size == 0)
168 return;
169
170 if (curr->size >= kHpageSize &&
171 curr->exec &&
172 (curr->size & kHpageMask) == curr->size &&
173 (curr->start_addr & kHpageMask) == curr->start_addr &&
174 curr->name[0] == '\0' &&
175 next->name[0] != '\0' &&
176 curr->start_addr + curr->size == next->start_addr &&
177 prev->start_addr + prev->size == curr->start_addr &&
178 my_strncmp(prev->name, next->name, NAME_MAX) == 0 &&
179 next->offset == prev->offset + prev->size + curr->size) {
180
181 // matched
182 my_strlcpy(curr->name, prev->name, NAME_MAX);
183 if (prev->exec) {
184 curr->offset = prev->offset;
185 curr->start_addr = prev->start_addr;
186 if (next->exec) {
187 // (prev, curr, next)
188 curr->size += prev->size + next->size;
189 prev->size = 0;
190 next->size = 0;
191 } else {
192 // (prev, curr), next
193 curr->size += prev->size;
194 prev->size = 0;
195 }
196 } else {
197 curr->offset = prev->offset + prev->size;
198 if (next->exec) {
199 // prev, (curr, next)
200 curr->size += next->size;
201 next->size = 0;
202 } else {
203 // prev, curr, next
204 }
205 }
206 }
207 }
208
209 // mappings_ is sorted excepted for the first entry.
210 // This function tries to merge segemnts into the first entry,
Wez 2016/07/18 22:28:15 typo: segments
211 // then check for other sorted entries.
212 // See LinuxDumper::EnumerateMappings().
Wez 2016/07/18 22:28:15 nit: EnumerateMappings() doesn't have any document
213 void CrOSPostProcessMappings(wasteful_vector<MappingInfo*>& mappings) {
214 // Find the candidate "next" to first segment, which is the only one that
215 // could be out-of-order.
216 size_t l = 1;
217 size_t r = mappings.size();
Wez 2016/07/18 22:28:15 nit: Please use descriptive variable names, for re
218 size_t next = mappings.size();
219 while (l < r) {
220 int m = (l + r) / 2;
221 if (mappings[m]->start_addr > mappings[0]->start_addr)
222 r = next = m;
223 else
224 l = m + 1;
Wez 2016/07/18 22:28:15 nit: I'd recommend using {} around the two cases h
225 }
226
227 // Try to merge segments into the first.
Wez 2016/07/18 22:28:15 It's not clear to me what this is actually trying
228 if (next < mappings.size()) {
229 TryRecoverMappings(mappings[0], mappings[next]);
230 if (next - 1 > 0)
231 TryRecoverMappings(mappings[next - 1], mappings[0], mappings[next]);
232 }
233
234 // Iterate through normal, sorted cases.
235 // Normal case 1.
236 for (size_t i = 1; i < mappings.size() - 1; i++)
237 TryRecoverMappings(mappings[i], mappings[i + 1]);
238
239 // Normal case 2.
240 for (size_t i = 1; i < mappings.size() - 2; i++)
241 TryRecoverMappings(mappings[i], mappings[i + 1], mappings[i + 2]);
Wez 2016/07/18 22:30:48 If we already ran through trying to merge (anon, m
242
243 // Collect merged (size == 0) segments.
244 size_t f, e;
245 for (f = e = 0; e < mappings.size(); e++)
246 if (mappings[e]->size > 0)
247 mappings[f++] = mappings[e];
248 mappings.resize(f);
249 }
250
251 } // namespace
252 #endif // __CHROMEOS__
253
87 // All interesting auvx entry types are below AT_SYSINFO_EHDR 254 // All interesting auvx entry types are below AT_SYSINFO_EHDR
88 #define AT_MAX AT_SYSINFO_EHDR 255 #define AT_MAX AT_SYSINFO_EHDR
89 256
90 LinuxDumper::LinuxDumper(pid_t pid, const char* root_prefix) 257 LinuxDumper::LinuxDumper(pid_t pid, const char* root_prefix)
91 : pid_(pid), 258 : pid_(pid),
92 root_prefix_(root_prefix), 259 root_prefix_(root_prefix),
93 crash_address_(0), 260 crash_address_(0),
94 crash_signal_(0), 261 crash_signal_(0),
95 crash_thread_(pid), 262 crash_thread_(pid),
96 threads_(&allocator_, 8), 263 threads_(&allocator_, 8),
97 mappings_(&allocator_), 264 mappings_(&allocator_),
98 auxv_(&allocator_, AT_MAX + 1) { 265 auxv_(&allocator_, AT_MAX + 1) {
99 assert(root_prefix_ && my_strlen(root_prefix_) < PATH_MAX); 266 assert(root_prefix_ && my_strlen(root_prefix_) < PATH_MAX);
100 // The passed-in size to the constructor (above) is only a hint. 267 // The passed-in size to the constructor (above) is only a hint.
101 // Must call .resize() to do actual initialization of the elements. 268 // Must call .resize() to do actual initialization of the elements.
102 auxv_.resize(AT_MAX + 1); 269 auxv_.resize(AT_MAX + 1);
103 } 270 }
104 271
105 LinuxDumper::~LinuxDumper() { 272 LinuxDumper::~LinuxDumper() {
106 } 273 }
107 274
108 bool LinuxDumper::Init() { 275 bool LinuxDumper::Init() {
109 return ReadAuxv() && EnumerateThreads() && EnumerateMappings(); 276 return ReadAuxv() && EnumerateThreads() && EnumerateMappings();
110 } 277 }
111 278
112 bool LinuxDumper::LateInit() { 279 bool LinuxDumper::LateInit() {
113 #if defined(__ANDROID__) 280 #if defined(__ANDROID__)
114 LatePostprocessMappings(); 281 LatePostprocessMappings();
115 #endif 282 #endif
283
284 #if defined(__CHROMEOS__)
285 CrOSPostProcessMappings(mappings_);
286 #endif
287
116 return true; 288 return true;
117 } 289 }
118 290
119 bool 291 bool
120 LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping, 292 LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping,
121 bool member, 293 bool member,
122 unsigned int mapping_id, 294 unsigned int mapping_id,
123 wasteful_vector<uint8_t>& identifier) { 295 wasteful_vector<uint8_t>& identifier) {
124 assert(!member || mapping_id < mappings_.size()); 296 assert(!member || mapping_id < mappings_.size());
125 if (IsMappedFileOpenUnsafe(mapping)) 297 if (IsMappedFileOpenUnsafe(mapping))
(...skipping 210 matching lines...) Expand 10 before | Expand all | Expand 10 after
336 if (*i3 == ' ') { 508 if (*i3 == ' ') {
337 const char* name = NULL; 509 const char* name = NULL;
338 // Only copy name if the name is a valid path name, or if 510 // Only copy name if the name is a valid path name, or if
339 // it's the VDSO image. 511 // it's the VDSO image.
340 if (((name = my_strchr(line, '/')) == NULL) && 512 if (((name = my_strchr(line, '/')) == NULL) &&
341 linux_gate_loc && 513 linux_gate_loc &&
342 reinterpret_cast<void*>(start_addr) == linux_gate_loc) { 514 reinterpret_cast<void*>(start_addr) == linux_gate_loc) {
343 name = kLinuxGateLibraryName; 515 name = kLinuxGateLibraryName;
344 offset = 0; 516 offset = 0;
345 } 517 }
346 // Merge adjacent mappings with the same name into one module, 518 // Merge adjacent mappings with the same name into one module,
Mark Mentovai 2016/07/18 22:03:29 Would it have been easier to work the change in he
347 // assuming they're a single library mapped by the dynamic linker 519 // assuming they're a single library mapped by the dynamic linker
348 if (name && !mappings_.empty()) { 520 if (name && !mappings_.empty()) {
349 MappingInfo* module = mappings_.back(); 521 MappingInfo* module = mappings_.back();
350 if ((start_addr == module->start_addr + module->size) && 522 if ((start_addr == module->start_addr + module->size) &&
351 (my_strlen(name) == my_strlen(module->name)) && 523 (my_strlen(name) == my_strlen(module->name)) &&
352 (my_strncmp(name, module->name, my_strlen(name)) == 0) && 524 (my_strncmp(name, module->name, my_strlen(name)) == 0) &&
353 (exec == module->exec)) { 525 (exec == module->exec)) {
354 module->size = end_addr - module->start_addr; 526 module->size = end_addr - module->start_addr;
355 line_reader->PopLine(line_len); 527 line_reader->PopLine(line_len);
356 continue; 528 continue;
(...skipping 221 matching lines...) Expand 10 before | Expand all | Expand 10 after
578 exe_stat.st_dev == new_path_stat.st_dev && 750 exe_stat.st_dev == new_path_stat.st_dev &&
579 exe_stat.st_ino == new_path_stat.st_ino) { 751 exe_stat.st_ino == new_path_stat.st_ino) {
580 return false; 752 return false;
581 } 753 }
582 754
583 my_memcpy(path, exe_link, NAME_MAX); 755 my_memcpy(path, exe_link, NAME_MAX);
584 return true; 756 return true;
585 } 757 }
586 758
587 } // namespace google_breakpad 759 } // namespace google_breakpad
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698