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