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