1// Copyright (c) 2011 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#include "base/debug/debugger.h"
6#include "build/build_config.h"
7
8#include <errno.h>
9#include <execinfo.h>
10#include <fcntl.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <sys/param.h>
14#include <sys/stat.h>
15#if !defined(OS_NACL)
16#include <sys/sysctl.h>
17#endif
18#include <sys/types.h>
19#include <unistd.h>
20
21#include <string>
22#include <vector>
23
24#if defined(__GLIBCXX__)
25#include <cxxabi.h>
26#endif
27
28#if defined(OS_MACOSX)
29#include <AvailabilityMacros.h>
30#endif
31
32#include <iostream>
33
34#include "base/basictypes.h"
35#include "base/eintr_wrapper.h"
36#include "base/logging.h"
37#include "base/memory/scoped_ptr.h"
38#include "base/safe_strerror_posix.h"
39#include "base/string_piece.h"
40#include "base/stringprintf.h"
41
42#if defined(USE_SYMBOLIZE)
43#include "base/third_party/symbolize/symbolize.h"
44#endif
45
46namespace base {
47namespace debug {
48
49bool SpawnDebuggerOnProcess(unsigned /* process_id */) {
50  NOTIMPLEMENTED();
51  return false;
52}
53
54#if defined(OS_MACOSX)
55
56// Based on Apple's recommended method as described in
57// http://developer.apple.com/qa/qa2004/qa1361.html
58bool BeingDebugged() {
59  // If the process is sandboxed then we can't use the sysctl, so cache the
60  // value.
61  static bool is_set = false;
62  static bool being_debugged = false;
63
64  if (is_set) {
65    return being_debugged;
66  }
67
68  // Initialize mib, which tells sysctl what info we want.  In this case,
69  // we're looking for information about a specific process ID.
70  int mib[] = {
71    CTL_KERN,
72    KERN_PROC,
73    KERN_PROC_PID,
74    getpid()
75  };
76
77  // Caution: struct kinfo_proc is marked __APPLE_API_UNSTABLE.  The source and
78  // binary interfaces may change.
79  struct kinfo_proc info;
80  size_t info_size = sizeof(info);
81
82  int sysctl_result = sysctl(mib, arraysize(mib), &info, &info_size, NULL, 0);
83  DCHECK_EQ(sysctl_result, 0);
84  if (sysctl_result != 0) {
85    is_set = true;
86    being_debugged = false;
87    return being_debugged;
88  }
89
90  // This process is being debugged if the P_TRACED flag is set.
91  is_set = true;
92  being_debugged = (info.kp_proc.p_flag & P_TRACED) != 0;
93  return being_debugged;
94}
95
96#elif defined(OS_LINUX)
97
98// We can look in /proc/self/status for TracerPid.  We are likely used in crash
99// handling, so we are careful not to use the heap or have side effects.
100// Another option that is common is to try to ptrace yourself, but then we
101// can't detach without forking(), and that's not so great.
102// static
103bool BeingDebugged() {
104  int status_fd = open("/proc/self/status", O_RDONLY);
105  if (status_fd == -1)
106    return false;
107
108  // We assume our line will be in the first 1024 characters and that we can
109  // read this much all at once.  In practice this will generally be true.
110  // This simplifies and speeds up things considerably.
111  char buf[1024];
112
113  ssize_t num_read = HANDLE_EINTR(read(status_fd, buf, sizeof(buf)));
114  if (HANDLE_EINTR(close(status_fd)) < 0)
115    return false;
116
117  if (num_read <= 0)
118    return false;
119
120  StringPiece status(buf, num_read);
121  StringPiece tracer("TracerPid:\t");
122
123  StringPiece::size_type pid_index = status.find(tracer);
124  if (pid_index == StringPiece::npos)
125    return false;
126
127  // Our pid is 0 without a debugger, assume this for any pid starting with 0.
128  pid_index += tracer.size();
129  return pid_index < status.size() && status[pid_index] != '0';
130}
131
132#elif defined(OS_NACL)
133
134bool BeingDebugged() {
135  NOTIMPLEMENTED();
136  return false;
137}
138
139#elif defined(OS_FREEBSD)
140
141bool BeingDebugged() {
142  // TODO(benl): can we determine this under FreeBSD?
143  NOTIMPLEMENTED();
144  return false;
145}
146
147#endif  // defined(OS_FREEBSD)
148
149// We want to break into the debugger in Debug mode, and cause a crash dump in
150// Release mode. Breakpad behaves as follows:
151//
152// +-------+-----------------+-----------------+
153// | OS    | Dump on SIGTRAP | Dump on SIGABRT |
154// +-------+-----------------+-----------------+
155// | Linux |       N         |        Y        |
156// | Mac   |       Y         |        N        |
157// +-------+-----------------+-----------------+
158//
159// Thus we do the following:
160// Linux: Debug mode, send SIGTRAP; Release mode, send SIGABRT.
161// Mac: Always send SIGTRAP.
162
163#if defined(NDEBUG) && !defined(OS_MACOSX)
164#define DEBUG_BREAK() abort()
165#elif defined(OS_NACL)
166// The NaCl verifier doesn't let use use int3.  For now, we call abort().  We
167// should ask for advice from some NaCl experts about the optimum thing here.
168// http://code.google.com/p/nativeclient/issues/detail?id=645
169#define DEBUG_BREAK() abort()
170#elif defined(ARCH_CPU_ARM_FAMILY)
171#define DEBUG_BREAK() asm("bkpt 0")
172#elif defined(ARCH_CPU_MIPS_FAMILY)
173#define DEBUG_BREAK() asm("break 2")
174#else
175#define DEBUG_BREAK() asm("int3")
176#endif
177
178void BreakDebugger() {
179  DEBUG_BREAK();
180#if defined(NDEBUG)
181  _exit(1);
182#endif
183}
184
185}  // namespace debug
186}  // namespace base
187