aboutsummaryrefslogtreecommitdiff
path: root/libstdc++-v3/src/c++26/debugging.cc
blob: c6262db73f442e3e775300bc3019c4c39f0f2b35 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
// Implementation of <debugging> -*- C++ -*-

// Copyright The GNU Toolchain Authors.
//
// This file is part of the GNU ISO C++ Library.  This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3.

// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.

// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
// <http://www.gnu.org/licenses/>.

#include <debugging>

#if __cpp_lib_debugging

#include <csignal> // std::raise

#if _GLIBCXX_USE_PROC_SELF_STATUS
# include <fstream>
# include <string>
#endif

#if _GLIBCXX_HAVE_SYS_PTRACE_H
# include <sys/types.h> // for darwin ptrace
# include <sys/ptrace.h>
# include <errno.h>
#endif

#if _GLIBCXX_HAVE_DEBUGAPI_H
# include <debugapi.h>
#endif

#ifdef _GLIBCXX_HAVE_SYS_SDT_H
# include <sys/sdt.h>
/* We only want to use stap probes starting with v3.  Earlier versions
   added too much startup cost.  */
# if defined (STAP_PROBE) && _SDT_NOTE_TYPE >= 3
#  define PROBE(name) STAP_PROBE(libstdcxx, name)
# endif
#endif

#ifndef PROBE
# define PROBE(name)
#endif

namespace __gnu_cxx
{
  // This should be changed to non-zero by debuggers when they attach
  // and back to zero when they detach.
  // If the value is positive, std::breakpoint() will use it as the argument
  // to std::raise, so it should be a valid signal number, e.g. SIGABRT or
  // SIGTRAP.
  // If the value is negative, std::breakpoint() will use a target-specific
  // trap, e.g. asm("int3") or __builtin_trap().
  volatile int debugger_signal_for_breakpoint = 0;
}

_GLIBCXX_WEAK_DEFINITION
bool
std::is_debugger_present() noexcept
{
  PROBE(std::is_debugger_present);

  if (__gnu_cxx::debugger_signal_for_breakpoint != 0)
    return true;

#if _GLIBCXX_HOSTED
# if _GLIBCXX_USE_PROC_SELF_STATUS
  const string_view prefix = "TracerPid:\t"; // populated since Linux 2.6.0
  ifstream in("/proc/self/status");
  string line;
  while (std::getline(in, line))
    {
      if (!line.starts_with(prefix))
	continue;

      string_view tracer = line;
      tracer.remove_prefix(prefix.size());
      if (tracer.size() == 1 && tracer[0] == '0') [[likely]]
	return false; // Not being traced.

      in.close();
      string_view cmd;
      string proc_dir = "/proc/" + string(tracer) + '/';
      in.open(proc_dir + "comm"); // since Linux 2.6.33
      if (std::getline(in, line)) [[likely]]
	cmd = line;
      else
	{
	  in.close();
	  in.open(proc_dir + "cmdline");
	  if (std::getline(in, line))
	    cmd = line.c_str(); // Only up to first '\0'
	  else
	    return false;
	}

      for (auto i : {"gdb", "gdbserver", "lldb-server"}) // known debuggers
	if (cmd.ends_with(i))
	  return true;

      // We found the TracerPid line, no need to do any more work.
      return false;
    }
# elif _GLIBCXX_USE_PTRACE
  if (::ptrace(PTRACE_TRACEME, 0, 1, 0) == -1)
    return errno == EPERM;
# endif
# if _GLIBCXX_HAVE_DEBUGAPI_H && defined(_WIN32) && !defined(__CYGWIN__)
  return IsDebuggerPresent();
# endif
#endif // HOSTED
  return false;
}

void
std::breakpoint() noexcept
{
  PROBE(std::breakpoint);

  if (__gnu_cxx::debugger_signal_for_breakpoint > 0)
    std::raise(__gnu_cxx::debugger_signal_for_breakpoint);

#if _GLIBCXX_HAVE_DEBUGAPI_H && defined(_WIN32) && !defined(__CYGWIN__)
  DebugBreak();
#elif __has_builtin(__builtin_debugtrap)
  __builtin_debugtrap(); // Clang
#elif defined(__i386__) || defined(__x86_64__)
  // nop is for GDB, see https://sourceware.org/bugzilla/show_bug.cgi?id=31194
  __asm__ volatile ("int $0x3\n\tnop");
#elifdef __thumb__
  __asm__ volatile (".inst 0xde01");
#elifdef __aarch64__
  __asm__ volatile (".inst 0xd4200000");
#elifdef __arm__
  __asm__ volatile (".inst 0xe7f001f0");
#elifdef __riscv
  /* section 2.8 in the RISC-V unprivileged ISA manual says for semi-hosted
   * environments we want the sequence:
   * slli x0, x0, 0x1f     # Entry NOP
   * ebreak         # Break to debugger
   * srai x0, x0, 7    # NOP encoding the semihosting call number 7
   */
  __asm__ volatile (".4byte 0x00100073");
#elif defined __powerpc__ && ! defined _AIX
  __asm__ volatile(".4byte 0x7d821008");
#else
  __builtin_trap();
#endif
} // If the debugger stops here, std::breakpoint() was called.

// This is intentionally not defined inline. A non-inline definition allows
// debuggers to insert a breakpoint on calls to the function, avoiding the
// overhead of calling `is_debugger_present()`.
void
std::breakpoint_if_debugging() noexcept
{
  PROBE(std::breakpoint_if_debugging);

  if (std::is_debugger_present()) [[unlikely]]
    std::breakpoint();
}

#endif // __cpp_lib_debugging