1// Copyright (c) 2014, Google Inc. 2// All rights reserved. 3// 4// Redistribution and use in source and binary forms, with or without 5// modification, are permitted provided that the following conditions are 6// met: 7// 8// * Redistributions of source code must retain the above copyright 9// notice, this list of conditions and the following disclaimer. 10// * Redistributions in binary form must reproduce the above 11// copyright notice, this list of conditions and the following disclaimer 12// in the documentation and/or other materials provided with the 13// distribution. 14// * Neither the name of Google Inc. nor the names of its 15// contributors may be used to endorse or promote products derived from 16// this software without specific prior written permission. 17// 18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 30// This translation unit generates microdumps into the console (logcat on 31// Android). See crbug.com/410294 for more info and design docs. 32 33#include "client/linux/microdump_writer/microdump_writer.h" 34 35#include <sys/utsname.h> 36 37#include "client/linux/dump_writer_common/seccomp_unwinder.h" 38#include "client/linux/dump_writer_common/thread_info.h" 39#include "client/linux/dump_writer_common/ucontext_reader.h" 40#include "client/linux/handler/exception_handler.h" 41#include "client/linux/log/log.h" 42#include "client/linux/minidump_writer/linux_ptrace_dumper.h" 43#include "common/linux/linux_libc_support.h" 44 45namespace { 46 47using google_breakpad::ExceptionHandler; 48using google_breakpad::LinuxDumper; 49using google_breakpad::LinuxPtraceDumper; 50using google_breakpad::MappingInfo; 51using google_breakpad::MappingList; 52using google_breakpad::RawContextCPU; 53using google_breakpad::SeccompUnwinder; 54using google_breakpad::ThreadInfo; 55using google_breakpad::UContextReader; 56 57const size_t kLineBufferSize = 2048; 58 59class MicrodumpWriter { 60 public: 61 MicrodumpWriter(const ExceptionHandler::CrashContext* context, 62 const MappingList& mappings, 63 LinuxDumper* dumper) 64 : ucontext_(context ? &context->context : NULL), 65#if !defined(__ARM_EABI__) && !defined(__mips__) 66 float_state_(context ? &context->float_state : NULL), 67#endif 68 dumper_(dumper), 69 mapping_list_(mappings), 70 log_line_(NULL) { 71 log_line_ = reinterpret_cast<char*>(Alloc(kLineBufferSize)); 72 if (log_line_) 73 log_line_[0] = '\0'; // Clear out the log line buffer. 74 } 75 76 ~MicrodumpWriter() { dumper_->ThreadsResume(); } 77 78 bool Init() { 79 // In the exceptional case where the system was out of memory and there 80 // wasn't even room to allocate the line buffer, bail out. There is nothing 81 // useful we can possibly achieve without the ability to Log. At least let's 82 // try to not crash. 83 if (!dumper_->Init() || !log_line_) 84 return false; 85 return dumper_->ThreadsSuspend(); 86 } 87 88 bool Dump() { 89 bool success; 90 LogLine("-----BEGIN BREAKPAD MICRODUMP-----"); 91 success = DumpOSInformation(); 92 if (success) 93 success = DumpCrashingThread(); 94 if (success) 95 success = DumpMappings(); 96 LogLine("-----END BREAKPAD MICRODUMP-----"); 97 dumper_->ThreadsResume(); 98 return success; 99 } 100 101 private: 102 // Writes one line to the system log. 103 void LogLine(const char* msg) { 104 logger::write(msg, my_strlen(msg)); 105#if !defined(__ANDROID__) 106 logger::write("\n", 1); // Android logger appends the \n. Linux's doesn't. 107#endif 108 } 109 110 // Stages the given string in the current line buffer. 111 void LogAppend(const char* str) { 112 my_strlcat(log_line_, str, kLineBufferSize); 113 } 114 115 // As above (required to take precedence over template specialization below). 116 void LogAppend(char* str) { 117 LogAppend(const_cast<const char*>(str)); 118 } 119 120 // Stages the hex repr. of the given int type in the current line buffer. 121 template<typename T> 122 void LogAppend(T value) { 123 // Make enough room to hex encode the largest int type + NUL. 124 static const char HEX[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 125 'A', 'B', 'C', 'D', 'E', 'F'}; 126 char hexstr[sizeof(T) * 2 + 1]; 127 for (int i = sizeof(T) * 2 - 1; i >= 0; --i, value >>= 4) 128 hexstr[i] = HEX[static_cast<uint8_t>(value) & 0x0F]; 129 hexstr[sizeof(T) * 2] = '\0'; 130 LogAppend(hexstr); 131 } 132 133 // Stages the buffer content hex-encoded in the current line buffer. 134 void LogAppend(const void* buf, size_t length) { 135 const uint8_t* ptr = reinterpret_cast<const uint8_t*>(buf); 136 for (size_t i = 0; i < length; ++i, ++ptr) 137 LogAppend(*ptr); 138 } 139 140 // Writes out the current line buffer on the system log. 141 void LogCommitLine() { 142 LogLine(log_line_); 143 my_strlcpy(log_line_, "", kLineBufferSize); 144 } 145 146 bool DumpOSInformation() { 147 struct utsname uts; 148 if (uname(&uts)) 149 return false; 150 const uint8_t n_cpus = static_cast<uint8_t>(sysconf(_SC_NPROCESSORS_CONF)); 151 152#if defined(__ANDROID__) 153 const char kOSId[] = "A"; 154#else 155 const char kOSId[] = "L"; 156#endif 157 158// We cannot depend on uts.machine. On multiarch devices it always returns the 159// primary arch, not the one that match the executable being run. 160#if defined(__aarch64__) 161 const char kArch[] = "arm64"; 162#elif defined(__ARMEL__) 163 const char kArch[] = "arm"; 164#elif defined(__x86_64__) 165 const char kArch[] = "x86_64"; 166#elif defined(__i386__) 167 const char kArch[] = "x86"; 168#elif defined(__mips__) 169 const char kArch[] = "mips"; 170#else 171#error "This code has not been ported to your platform yet" 172#endif 173 174 LogAppend("O "); 175 LogAppend(kOSId); 176 LogAppend(" "); 177 LogAppend(kArch); 178 LogAppend(" "); 179 LogAppend(n_cpus); 180 LogAppend(" "); 181 LogAppend(uts.machine); 182 LogAppend(" "); 183 LogAppend(uts.release); 184 LogAppend(" "); 185 LogAppend(uts.version); 186 LogCommitLine(); 187 return true; 188 } 189 190 bool DumpThreadStack(uint32_t thread_id, 191 uintptr_t stack_pointer, 192 int max_stack_len, 193 uint8_t** stack_copy) { 194 *stack_copy = NULL; 195 const void* stack; 196 size_t stack_len; 197 198 if (!dumper_->GetStackInfo(&stack, &stack_len, stack_pointer)) { 199 // The stack pointer might not be available. In this case we don't hard 200 // fail, just produce a (almost useless) microdump w/o a stack section. 201 return true; 202 } 203 204 LogAppend("S 0 "); 205 LogAppend(stack_pointer); 206 LogAppend(" "); 207 LogAppend(reinterpret_cast<uintptr_t>(stack)); 208 LogAppend(" "); 209 LogAppend(stack_len); 210 LogCommitLine(); 211 212 if (max_stack_len >= 0 && 213 stack_len > static_cast<unsigned int>(max_stack_len)) { 214 stack_len = max_stack_len; 215 } 216 217 *stack_copy = reinterpret_cast<uint8_t*>(Alloc(stack_len)); 218 dumper_->CopyFromProcess(*stack_copy, thread_id, stack, stack_len); 219 220 // Dump the content of the stack, splicing it into chunks which size is 221 // compatible with the max logcat line size (see LOGGER_ENTRY_MAX_PAYLOAD). 222 const size_t STACK_DUMP_CHUNK_SIZE = 384; 223 for (size_t stack_off = 0; stack_off < stack_len; 224 stack_off += STACK_DUMP_CHUNK_SIZE) { 225 LogAppend("S "); 226 LogAppend(reinterpret_cast<uintptr_t>(stack) + stack_off); 227 LogAppend(" "); 228 LogAppend(*stack_copy + stack_off, 229 std::min(STACK_DUMP_CHUNK_SIZE, stack_len - stack_off)); 230 LogCommitLine(); 231 } 232 return true; 233 } 234 235 // Write information about the crashing thread. 236 bool DumpCrashingThread() { 237 const unsigned num_threads = dumper_->threads().size(); 238 239 for (unsigned i = 0; i < num_threads; ++i) { 240 MDRawThread thread; 241 my_memset(&thread, 0, sizeof(thread)); 242 thread.thread_id = dumper_->threads()[i]; 243 244 // Dump only the crashing thread. 245 if (static_cast<pid_t>(thread.thread_id) != dumper_->crash_thread()) 246 continue; 247 248 assert(ucontext_); 249 assert(!dumper_->IsPostMortem()); 250 251 uint8_t* stack_copy; 252 const uintptr_t stack_ptr = UContextReader::GetStackPointer(ucontext_); 253 if (!DumpThreadStack(thread.thread_id, stack_ptr, -1, &stack_copy)) 254 return false; 255 256 RawContextCPU cpu; 257 my_memset(&cpu, 0, sizeof(RawContextCPU)); 258#if !defined(__ARM_EABI__) && !defined(__mips__) 259 UContextReader::FillCPUContext(&cpu, ucontext_, float_state_); 260#else 261 UContextReader::FillCPUContext(&cpu, ucontext_); 262#endif 263 if (stack_copy) 264 SeccompUnwinder::PopSeccompStackFrame(&cpu, thread, stack_copy); 265 DumpCPUState(&cpu); 266 } 267 return true; 268 } 269 270 void DumpCPUState(RawContextCPU* cpu) { 271 LogAppend("C "); 272 LogAppend(cpu, sizeof(*cpu)); 273 LogCommitLine(); 274 } 275 276 // If there is caller-provided information about this mapping 277 // in the mapping_list_ list, return true. Otherwise, return false. 278 bool HaveMappingInfo(const MappingInfo& mapping) { 279 for (MappingList::const_iterator iter = mapping_list_.begin(); 280 iter != mapping_list_.end(); 281 ++iter) { 282 // Ignore any mappings that are wholly contained within 283 // mappings in the mapping_info_ list. 284 if (mapping.start_addr >= iter->first.start_addr && 285 (mapping.start_addr + mapping.size) <= 286 (iter->first.start_addr + iter->first.size)) { 287 return true; 288 } 289 } 290 return false; 291 } 292 293 // Dump information about the provided |mapping|. If |identifier| is non-NULL, 294 // use it instead of calculating a file ID from the mapping. 295 void DumpModule(const MappingInfo& mapping, 296 bool member, 297 unsigned int mapping_id, 298 const uint8_t* identifier) { 299 MDGUID module_identifier; 300 if (identifier) { 301 // GUID was provided by caller. 302 my_memcpy(&module_identifier, identifier, sizeof(MDGUID)); 303 } else { 304 dumper_->ElfFileIdentifierForMapping( 305 mapping, 306 member, 307 mapping_id, 308 reinterpret_cast<uint8_t*>(&module_identifier)); 309 } 310 311 char file_name[NAME_MAX]; 312 char file_path[NAME_MAX]; 313 LinuxDumper::GetMappingEffectiveNameAndPath( 314 mapping, file_path, sizeof(file_path), file_name, sizeof(file_name)); 315 316 LogAppend("M "); 317 LogAppend(static_cast<uintptr_t>(mapping.start_addr)); 318 LogAppend(" "); 319 LogAppend(mapping.offset); 320 LogAppend(" "); 321 LogAppend(mapping.size); 322 LogAppend(" "); 323 LogAppend(module_identifier.data1); 324 LogAppend(module_identifier.data2); 325 LogAppend(module_identifier.data3); 326 LogAppend(module_identifier.data4[0]); 327 LogAppend(module_identifier.data4[1]); 328 LogAppend(module_identifier.data4[2]); 329 LogAppend(module_identifier.data4[3]); 330 LogAppend(module_identifier.data4[4]); 331 LogAppend(module_identifier.data4[5]); 332 LogAppend(module_identifier.data4[6]); 333 LogAppend(module_identifier.data4[7]); 334 LogAppend("0 "); // Age is always 0 on Linux. 335 LogAppend(file_name); 336 LogCommitLine(); 337 } 338 339 // Write information about the mappings in effect. 340 bool DumpMappings() { 341 // First write all the mappings from the dumper 342 for (unsigned i = 0; i < dumper_->mappings().size(); ++i) { 343 const MappingInfo& mapping = *dumper_->mappings()[i]; 344 if (mapping.name[0] == 0 || // only want modules with filenames. 345 !mapping.exec || // only want executable mappings. 346 mapping.size < 4096 || // too small to get a signature for. 347 HaveMappingInfo(mapping)) { 348 continue; 349 } 350 351 DumpModule(mapping, true, i, NULL); 352 } 353 // Next write all the mappings provided by the caller 354 for (MappingList::const_iterator iter = mapping_list_.begin(); 355 iter != mapping_list_.end(); 356 ++iter) { 357 DumpModule(iter->first, false, 0, iter->second); 358 } 359 return true; 360 } 361 362 void* Alloc(unsigned bytes) { return dumper_->allocator()->Alloc(bytes); } 363 364 const struct ucontext* const ucontext_; 365#if !defined(__ARM_EABI__) && !defined(__mips__) 366 const google_breakpad::fpstate_t* const float_state_; 367#endif 368 LinuxDumper* dumper_; 369 const MappingList& mapping_list_; 370 char* log_line_; 371}; 372} // namespace 373 374namespace google_breakpad { 375 376bool WriteMicrodump(pid_t crashing_process, 377 const void* blob, 378 size_t blob_size, 379 const MappingList& mappings) { 380 LinuxPtraceDumper dumper(crashing_process); 381 const ExceptionHandler::CrashContext* context = NULL; 382 if (blob) { 383 if (blob_size != sizeof(ExceptionHandler::CrashContext)) 384 return false; 385 context = reinterpret_cast<const ExceptionHandler::CrashContext*>(blob); 386 dumper.set_crash_address( 387 reinterpret_cast<uintptr_t>(context->siginfo.si_addr)); 388 dumper.set_crash_signal(context->siginfo.si_signo); 389 dumper.set_crash_thread(context->tid); 390 } 391 MicrodumpWriter writer(context, mappings, &dumper); 392 if (!writer.Init()) 393 return false; 394 return writer.Dump(); 395} 396 397} // namespace google_breakpad 398