Chromium Code Reviews| 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 |