blob: b948c7aeb92a28702de02355c0e415c0b8a03b35 [file] [log] [blame]
Daniel Chenged0471b2019-05-10 11:43:361// Copyright 2019 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef BASE_IMMEDIATE_CRASH_H_
6#define BASE_IMMEDIATE_CRASH_H_
7
8#include "build/build_config.h"
9
10// Crashes in the fastest possible way with no attempt at logging.
11// There are different constraints to satisfy here, see http://crbug.com/664209
12// for more context:
13// - The trap instructions, and hence the PC value at crash time, have to be
14// distinct and not get folded into the same opcode by the compiler.
15// On Linux/Android this is tricky because GCC still folds identical
16// asm volatile blocks. The workaround is generating distinct opcodes for
17// each CHECK using the __COUNTER__ macro.
18// - The debug info for the trap instruction has to be attributed to the source
19// line that has the CHECK(), to make crash reports actionable. This rules
20// out the ability of using a inline function, at least as long as clang
21// doesn't support attribute(artificial).
22// - Failed CHECKs should produce a signal that is distinguishable from an
23// invalid memory access, to improve the actionability of crash reports.
24// - The compiler should treat the CHECK as no-return instructions, so that the
25// trap code can be efficiently packed in the prologue of the function and
26// doesn't interfere with the main execution flow.
27// - When debugging, developers shouldn't be able to accidentally step over a
28// CHECK. This is achieved by putting opcodes that will cause a non
29// continuable exception after the actual trap instruction.
30// - Don't cause too much binary bloat.
31#if defined(COMPILER_GCC)
32
33#if defined(ARCH_CPU_X86_FAMILY) && !defined(OS_NACL)
34// int 3 will generate a SIGTRAP.
35#define TRAP_SEQUENCE() \
36 asm volatile( \
37 "int3; ud2; push %0;" ::"i"(static_cast<unsigned char>(__COUNTER__)))
38
39#elif defined(ARCH_CPU_ARMEL) && !defined(OS_NACL)
40// bkpt will generate a SIGBUS when running on armv7 and a SIGTRAP when running
41// as a 32 bit userspace app on arm64. There doesn't seem to be any way to
42// cause a SIGTRAP from userspace without using a syscall (which would be a
43// problem for sandboxing).
44#define TRAP_SEQUENCE() \
45 asm volatile("bkpt #0; udf %0;" ::"i"(__COUNTER__ % 256))
46
47#elif defined(ARCH_CPU_ARM64) && !defined(OS_NACL)
48// This will always generate a SIGTRAP on arm64.
49#define TRAP_SEQUENCE() \
50 asm volatile("brk #0; hlt %0;" ::"i"(__COUNTER__ % 65536))
51
52#else
53// Crash report accuracy will not be guaranteed on other architectures, but at
54// least this will crash as expected.
55#define TRAP_SEQUENCE() __builtin_trap()
56#endif // ARCH_CPU_*
57
58#elif defined(COMPILER_MSVC)
59
60// Clang is cleverer about coalescing int3s, so we need to add a unique-ish
61// instruction following the __debugbreak() to have it emit distinct locations
62// for CHECKs rather than collapsing them all together. It would be nice to use
63// a short intrinsic to do this (and perhaps have only one implementation for
64// both clang and MSVC), however clang-cl currently does not support intrinsics.
65// On the flip side, MSVC x64 doesn't support inline asm. So, we have to have
66// two implementations. Normally clang-cl's version will be 5 bytes (1 for
67// `int3`, 2 for `ud2`, 2 for `push byte imm`, however, TODO(scottmg):
68// https://crbug.com/694670 clang-cl doesn't currently support %'ing
69// __COUNTER__, so eventually it will emit the dword form of push.
70// TODO(scottmg): Reinvestigate a short sequence that will work on both
71// compilers once clang supports more intrinsics. See https://crbug.com/693713.
72#if !defined(__clang__)
73#define TRAP_SEQUENCE() __debugbreak()
74#elif defined(ARCH_CPU_ARM64)
75#define TRAP_SEQUENCE() \
76 __asm volatile("brk #0\n hlt %0\n" ::"i"(__COUNTER__ % 65536));
77#else
78#define TRAP_SEQUENCE() ({ {__asm int 3 __asm ud2 __asm push __COUNTER__}; })
79#endif // __clang__
80
81#else
82#error Port
83#endif // COMPILER_GCC
84
85// CHECK() and the trap sequence can be invoked from a constexpr function.
86// This could make compilation fail on GCC, as it forbids directly using inline
87// asm inside a constexpr function. However, it allows calling a lambda
88// expression including the same asm.
89// The side effect is that the top of the stacktrace will not point to the
90// calling function, but to this anonymous lambda. This is still useful as the
91// full name of the lambda will typically include the name of the function that
92// calls CHECK() and the debugger will still break at the right line of code.
93#if !defined(COMPILER_GCC)
94#define WRAPPED_TRAP_SEQUENCE() TRAP_SEQUENCE()
95#else
96#define WRAPPED_TRAP_SEQUENCE() \
97 do { \
98 [] { TRAP_SEQUENCE(); }(); \
99 } while (false)
100#endif
101
102#if defined(__clang__) || defined(COMPILER_GCC)
103#define IMMEDIATE_CRASH() \
104 ({ \
105 WRAPPED_TRAP_SEQUENCE(); \
106 __builtin_unreachable(); \
107 })
108#else
109// This is supporting non-chromium user of logging.h to build with MSVC, like
110// pdfium. On MSVC there is no __builtin_unreachable().
111#define IMMEDIATE_CRASH() WRAPPED_TRAP_SEQUENCE()
112#endif
113
114#endif // BASE_IMMEDIATE_CRASH_H_