OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |