xref: /aosp_15_r20/external/cronet/base/debug/debugger_posix.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/debug/debugger.h"
6 
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <stddef.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <sys/param.h>
13 #include <sys/stat.h>
14 #include <sys/types.h>
15 #include <unistd.h>
16 
17 #include <memory>
18 #include <string_view>
19 
20 #include "base/check_op.h"
21 #include "base/notimplemented.h"
22 #include "base/strings/string_util.h"
23 #include "base/threading/platform_thread.h"
24 #include "base/time/time.h"
25 #include "build/build_config.h"
26 
27 #if defined(__GLIBCXX__)
28 #include <cxxabi.h>
29 #endif
30 
31 #if BUILDFLAG(IS_APPLE)
32 #include <AvailabilityMacros.h>
33 #endif
34 
35 #if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_BSD)
36 #include <sys/sysctl.h>
37 #endif
38 
39 #if BUILDFLAG(IS_FREEBSD)
40 #include <sys/user.h>
41 #endif
42 
43 #include <ostream>
44 
45 #include "base/check.h"
46 #include "base/debug/alias.h"
47 #include "base/debug/debugging_buildflags.h"
48 #include "base/environment.h"
49 #include "base/files/file_util.h"
50 #include "base/posix/eintr_wrapper.h"
51 #include "base/process/process.h"
52 #include "base/strings/string_number_conversions.h"
53 
54 #if defined(USE_SYMBOLIZE)
55 #include "base/third_party/symbolize/symbolize.h"  // nogncheck
56 #endif
57 
58 namespace base {
59 namespace debug {
60 
61 #if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_BSD)
62 
63 // Based on Apple's recommended method as described in
64 // http://developer.apple.com/qa/qa2004/qa1361.html
BeingDebugged()65 bool BeingDebugged() {
66   // NOTE: This code MUST be async-signal safe (it's used by in-process
67   // stack dumping signal handler). NO malloc or stdio is allowed here.
68   //
69   // While some code used below may be async-signal unsafe, note how
70   // the result is cached (see |is_set| and |being_debugged| static variables
71   // right below). If this code is properly warmed-up early
72   // in the start-up process, it should be safe to use later.
73 
74   // If the process is sandboxed then we can't use the sysctl, so cache the
75   // value.
76   static bool is_set = false;
77   static bool being_debugged = false;
78 
79   if (is_set)
80     return being_debugged;
81 
82   // Initialize mib, which tells sysctl what info we want.  In this case,
83   // we're looking for information about a specific process ID.
84   int mib[] = {
85     CTL_KERN,
86     KERN_PROC,
87     KERN_PROC_PID,
88     getpid()
89 #if BUILDFLAG(IS_OPENBSD)
90         ,
91     sizeof(struct kinfo_proc),
92     0
93 #endif
94   };
95 
96   // Caution: struct kinfo_proc is marked __APPLE_API_UNSTABLE.  The source and
97   // binary interfaces may change.
98   struct kinfo_proc info;
99   size_t info_size = sizeof(info);
100 
101 #if BUILDFLAG(IS_OPENBSD)
102   if (sysctl(mib, std::size(mib), NULL, &info_size, NULL, 0) < 0)
103     return -1;
104 
105   mib[5] = (info_size / sizeof(struct kinfo_proc));
106 #endif
107 
108   int sysctl_result = sysctl(mib, std::size(mib), &info, &info_size, NULL, 0);
109   DCHECK_EQ(sysctl_result, 0);
110   if (sysctl_result != 0) {
111     is_set = true;
112     being_debugged = false;
113     return being_debugged;
114   }
115 
116   // This process is being debugged if the P_TRACED flag is set.
117   is_set = true;
118 #if BUILDFLAG(IS_FREEBSD)
119   being_debugged = (info.ki_flag & P_TRACED) != 0;
120 #elif BUILDFLAG(IS_BSD)
121   being_debugged = (info.p_flag & P_TRACED) != 0;
122 #else
123   being_debugged = (info.kp_proc.p_flag & P_TRACED) != 0;
124 #endif
125   return being_debugged;
126 }
127 
VerifyDebugger()128 void VerifyDebugger() {
129 #if BUILDFLAG(ENABLE_LLDBINIT_WARNING)
130   if (Environment::Create()->HasVar("CHROMIUM_LLDBINIT_SOURCED"))
131     return;
132   if (!BeingDebugged())
133     return;
134   DCHECK(false)
135       << "Detected lldb without sourcing //tools/lldb/lldbinit.py. lldb may "
136          "not be able to find debug symbols. Please see debug instructions for "
137          "using //tools/lldb/lldbinit.py:\n"
138          "https://chromium.googlesource.com/chromium/src/+/main/docs/"
139          "lldbinit.md\n"
140          "To continue anyway, type 'continue' in lldb. To always skip this "
141          "check, define an environment variable CHROMIUM_LLDBINIT_SOURCED=1";
142 #endif
143 }
144 
145 #elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
146     BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_AIX)
147 
148 // We can look in /proc/self/status for TracerPid.  We are likely used in crash
149 // handling, so we are careful not to use the heap or have side effects.
150 // Another option that is common is to try to ptrace yourself, but then we
151 // can't detach without forking(), and that's not so great.
152 // static
153 Process GetDebuggerProcess() {
154   // NOTE: This code MUST be async-signal safe (it's used by in-process
155   // stack dumping signal handler). NO malloc or stdio is allowed here.
156 
157   int status_fd = open("/proc/self/status", O_RDONLY);
158   if (status_fd == -1)
159     return Process();
160 
161   // We assume our line will be in the first 1024 characters and that we can
162   // read this much all at once.  In practice this will generally be true.
163   // This simplifies and speeds up things considerably.
164   char buf[1024];
165 
166   ssize_t num_read = HANDLE_EINTR(read(status_fd, buf, sizeof(buf)));
167   if (IGNORE_EINTR(close(status_fd)) < 0)
168     return Process();
169 
170   if (num_read <= 0)
171     return Process();
172 
173   std::string_view status(buf, static_cast<size_t>(num_read));
174   std::string_view tracer("TracerPid:\t");
175 
176   StringPiece::size_type pid_index = status.find(tracer);
177   if (pid_index == StringPiece::npos)
178     return Process();
179   pid_index += tracer.size();
180   StringPiece::size_type pid_end_index = status.find('\n', pid_index);
181   if (pid_end_index == StringPiece::npos)
182     return Process();
183 
184   std::string_view pid_str(buf + pid_index, pid_end_index - pid_index);
185   int pid = 0;
186   if (!StringToInt(pid_str, &pid))
187     return Process();
188 
189   return Process(pid);
190 }
191 
192 bool BeingDebugged() {
193   return GetDebuggerProcess().IsValid();
194 }
195 
196 void VerifyDebugger() {
197 #if BUILDFLAG(ENABLE_GDBINIT_WARNING)
198   // Quick check before potentially slower GetDebuggerProcess().
199   if (Environment::Create()->HasVar("CHROMIUM_GDBINIT_SOURCED"))
200     return;
201 
202   Process proc = GetDebuggerProcess();
203   if (!proc.IsValid())
204     return;
205 
206   FilePath cmdline_file =
207       FilePath("/proc").Append(NumberToString(proc.Handle())).Append("cmdline");
208   std::string cmdline;
209   if (!ReadFileToString(cmdline_file, &cmdline))
210     return;
211 
212   // /proc/*/cmdline separates arguments with null bytes, but we only care about
213   // the executable name, so interpret |cmdline| as a null-terminated C string
214   // to extract the exe portion.
215   std::string_view exe(cmdline.c_str());
216 
217   DCHECK(ToLowerASCII(exe).find("gdb") == std::string::npos)
218       << "Detected gdb without sourcing //tools/gdb/gdbinit.  gdb may not be "
219          "able to find debug symbols, and pretty-printing of STL types may not "
220          "work.  Please see debug instructions for using //tools/gdb/gdbinit:\n"
221          "https://chromium.googlesource.com/chromium/src/+/main/docs/"
222          "gdbinit.md\n"
223          "To continue anyway, type 'continue' in gdb.  To always skip this "
224          "check, define an environment variable CHROMIUM_GDBINIT_SOURCED=1";
225 #endif
226 }
227 
228 #else
229 
230 bool BeingDebugged() {
231   NOTIMPLEMENTED();
232   return false;
233 }
234 
235 void VerifyDebugger() {}
236 
237 #endif
238 
239 // We want to break into the debugger in Debug mode, and cause a crash dump in
240 // Release mode. Breakpad behaves as follows:
241 //
242 // +-------+-----------------+-----------------+
243 // | OS    | Dump on SIGTRAP | Dump on SIGABRT |
244 // +-------+-----------------+-----------------+
245 // | Linux |       N         |        Y        |
246 // | Mac   |       Y         |        N        |
247 // +-------+-----------------+-----------------+
248 //
249 // Thus we do the following:
250 // Linux: Debug mode if a debugger is attached, send SIGTRAP; otherwise send
251 //        SIGABRT
252 // Mac: Always send SIGTRAP.
253 
254 #if defined(ARCH_CPU_ARMEL)
255 #define DEBUG_BREAK_ASM() asm("bkpt 0")
256 #elif defined(ARCH_CPU_ARM64)
257 #define DEBUG_BREAK_ASM() asm("brk 0")
258 #elif defined(ARCH_CPU_MIPS_FAMILY)
259 #define DEBUG_BREAK_ASM() asm("break 2")
260 #elif defined(ARCH_CPU_X86_FAMILY)
261 #define DEBUG_BREAK_ASM() asm("int3")
262 #endif
263 
264 #if defined(NDEBUG) && !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_ANDROID)
265 #define DEBUG_BREAK() abort()
266 #elif BUILDFLAG(IS_NACL)
267 // The NaCl verifier doesn't let use use int3.  For now, we call abort().  We
268 // should ask for advice from some NaCl experts about the optimum thing here.
269 // http://code.google.com/p/nativeclient/issues/detail?id=645
270 #define DEBUG_BREAK() abort()
271 #elif !BUILDFLAG(IS_APPLE)
272 // Though Android has a "helpful" process called debuggerd to catch native
273 // signals on the general assumption that they are fatal errors. If no debugger
274 // is attached, we call abort since Breakpad needs SIGABRT to create a dump.
275 // When debugger is attached, for ARM platform the bkpt instruction appears
276 // to cause SIGBUS which is trapped by debuggerd, and we've had great
277 // difficulty continuing in a debugger once we stop from SIG triggered by native
278 // code, use GDB to set |go| to 1 to resume execution; for X86 platform, use
279 // "int3" to setup breakpiont and raise SIGTRAP.
280 //
281 // On other POSIX architectures, except Mac OS X, we use the same logic to
282 // ensure that breakpad creates a dump on crashes while it is still possible to
283 // use a debugger.
284 namespace {
DebugBreak()285 void DebugBreak() {
286   if (!BeingDebugged()) {
287     abort();
288   } else {
289 #if defined(DEBUG_BREAK_ASM)
290     DEBUG_BREAK_ASM();
291 #else
292     volatile int go = 0;
293     while (!go)
294       PlatformThread::Sleep(Milliseconds(100));
295 #endif
296   }
297 }
298 }  // namespace
299 #define DEBUG_BREAK() DebugBreak()
300 #elif defined(DEBUG_BREAK_ASM)
301 #define DEBUG_BREAK() DEBUG_BREAK_ASM()
302 #else
303 #error "Don't know how to debug break on this architecture/OS"
304 #endif
305 
BreakDebuggerAsyncSafe()306 void BreakDebuggerAsyncSafe() {
307   // NOTE: This code MUST be async-signal safe (it's used by in-process
308   // stack dumping signal handler). NO malloc or stdio is allowed here.
309 
310   // Linker's ICF feature may merge this function with other functions with the
311   // same definition (e.g. any function whose sole job is to call abort()) and
312   // it may confuse the crash report processing system. http://crbug.com/508489
313   static int static_variable_to_make_this_function_unique = 0;
314   Alias(&static_variable_to_make_this_function_unique);
315 
316   DEBUG_BREAK();
317 #if BUILDFLAG(IS_ANDROID) && !defined(OFFICIAL_BUILD)
318   // For Android development we always build release (debug builds are
319   // unmanageably large), so the unofficial build is used for debugging. It is
320   // helpful to be able to insert BreakDebugger() statements in the source,
321   // attach the debugger, inspect the state of the program and then resume it by
322   // setting the 'go' variable above.
323 #elif defined(NDEBUG)
324   // Terminate the program after signaling the debug break.
325   // When DEBUG_BREAK() expands to abort(), this is unreachable code. Rather
326   // than carefully tracking in which cases DEBUG_BREAK()s is noreturn, just
327   // disable the unreachable code warning here.
328 #pragma GCC diagnostic push
329 #pragma GCC diagnostic ignored "-Wunreachable-code"
330   _exit(1);
331 #pragma GCC diagnostic pop
332 #endif
333 }
334 
335 }  // namespace debug
336 }  // namespace base
337