• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
45 namespace {
46 
47 using google_breakpad::ExceptionHandler;
48 using google_breakpad::LinuxDumper;
49 using google_breakpad::LinuxPtraceDumper;
50 using google_breakpad::MappingInfo;
51 using google_breakpad::MappingList;
52 using google_breakpad::RawContextCPU;
53 using google_breakpad::SeccompUnwinder;
54 using google_breakpad::ThreadInfo;
55 using google_breakpad::UContextReader;
56 
57 const size_t kLineBufferSize = 2048;
58 
59 class MicrodumpWriter {
60  public:
MicrodumpWriter(const ExceptionHandler::CrashContext * context,const MappingList & mappings,LinuxDumper * dumper)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 
~MicrodumpWriter()76   ~MicrodumpWriter() { dumper_->ThreadsResume(); }
77 
Init()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 
Dump()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.
LogLine(const char * msg)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.
LogAppend(const char * str)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).
LogAppend(char * str)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>
LogAppend(T value)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.
LogAppend(const void * buf,size_t length)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.
LogCommitLine()141   void LogCommitLine() {
142     LogLine(log_line_);
143     my_strlcpy(log_line_, "", kLineBufferSize);
144   }
145 
DumpOSInformation()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 
DumpThreadStack(uint32_t thread_id,uintptr_t stack_pointer,int max_stack_len,uint8_t ** stack_copy)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.
DumpCrashingThread()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 
DumpCPUState(RawContextCPU * cpu)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.
HaveMappingInfo(const MappingInfo & mapping)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.
DumpModule(const MappingInfo & mapping,bool member,unsigned int mapping_id,const uint8_t * identifier)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.
DumpMappings()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 
Alloc(unsigned bytes)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 
374 namespace google_breakpad {
375 
WriteMicrodump(pid_t crashing_process,const void * blob,size_t blob_size,const MappingList & mappings)376 bool 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