Skip to content

Commit fdf470f

Browse files
committed
WIP#4 implement C_BP macro for throwing a C debugger breakpoint
-short macro name, less to type -cross platform -makes it easier to work on Perl core or XS CPAN -emits debug info to console for CI/smoke/unattended machine -writes to STDOUT and STDERR, incase one of the 2 FDs are redirected to a disk file or piped to another process, or that disk file is temp flagged, and OS instantly deletes it -breaking TAP testing is good -C_BP; is less to type vs DebugBreak(); or __debugbreak(); on Win32
1 parent 82c4939 commit fdf470f

File tree

10 files changed

+341
-3
lines changed

10 files changed

+341
-3
lines changed

embed.fnc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,8 @@ CRTp |I32 |cast_i32 |NV f
802802
CRTp |IV |cast_iv |NV f
803803
CRTp |U32 |cast_ulong |NV f
804804
CRTp |UV |cast_uv |NV f
805+
FTXdp |void |c_bp |NN const char *file_metadata \
806+
|...
805807
p |bool |check_utf8_print \
806808
|NN const U8 *s \
807809
|const STRLEN len

embed.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -917,6 +917,7 @@
917917
# define boot_core_builtin() Perl_boot_core_builtin(aTHX)
918918
# define boot_core_mro() Perl_boot_core_mro(aTHX)
919919
# define build_infix_plugin(a,b,c) Perl_build_infix_plugin(aTHX_ a,b,c)
920+
# define c_bp Perl_c_bp
920921
# define cando(a,b,c) Perl_cando(aTHX_ a,b,c)
921922
# define check_utf8_print(a,b) Perl_check_utf8_print(aTHX_ a,b)
922923
# define closest_cop(a,b,c,d) Perl_closest_cop(aTHX_ a,b,c,d)

ext/XS-APItest/APItest.pm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use strict;
44
use warnings;
55
use Carp;
66

7-
our $VERSION = '1.38';
7+
our $VERSION = '1.39';
88

99
require XSLoader;
1010

ext/XS-APItest/APItest.xs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,19 @@
55

66
/* Do *not* define PERL_NO_GET_CONTEXT. This is the one place where we get
77
to test implicit Perl_get_context(). */
8+
/* for GetErrorMode */
9+
#ifdef WIN32
10+
# define _WIN32_WINNT 0x0601
11+
#endif
812

913
#include "EXTERN.h"
1014
#include "perl.h"
1115
#include "XSUB.h"
1216

17+
#ifdef WIN32
18+
# include <windows.h>
19+
#endif
20+
1321
/* PERL_VERSION_xx sanity checks */
1422
#if !PERL_VERSION_EQ(PERL_VERSION_MAJOR, PERL_VERSION_MINOR, PERL_VERSION_PATCH)
1523
# error PERL_VERSION_EQ(major, minor, patch) is false; expected true
@@ -3152,6 +3160,20 @@ my_cxt_setsv(sv)
31523160
my_cxt_setsv_p(sv _aMY_CXT);
31533161
SvREFCNT_inc(sv);
31543162

3163+
void
3164+
test_C_BP_breakpoint()
3165+
CODE:
3166+
{
3167+
#ifdef WIN32
3168+
UINT em = GetErrorMode();
3169+
SetErrorMode( SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX );
3170+
#endif
3171+
C_BP;
3172+
#ifdef WIN32
3173+
SetErrorMode(em);
3174+
#endif
3175+
}
3176+
31553177
bool
31563178
sv_setsv_cow_hashkey_core()
31573179

handy.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,9 @@ required, but is kept for backwards compatibility.
155155
/* Try to figure out __func__ or __FUNCTION__ equivalent, if any.
156156
* XXX Should really be a Configure probe, with HAS__FUNCTION__
157157
* and FUNCTION__ as results.
158-
* XXX Similarly, a Configure probe for __FILE__ and __LINE__ is needed. */
158+
* XXX Similarly, a Configure probe for __FILE__ and __LINE__ is needed.
159+
* Remember to also update CBPFUNCTION__ in util.h
160+
*/
159161
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || (defined(__SUNPRO_C)) /* C99 or close enough. */
160162
# define FUNCTION__ __func__
161163
# define SAFE_FUNCTION__ __func__

pod/perldelta.pod

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,18 @@ well.
346346

347347
=over 4
348348

349+
=item * C_BP XS macro added for C debugger breakpoints
350+
351+
A cross platform macro C<C_BP> was added, that triggers a C breakpoint in the
352+
appropriate OS/platform specific C debugger, if the C debugger is already
353+
started. If a C debugger is not available, the C<C_BP> will immedialty kill
354+
the perl process similar to a SEGV. This new macro makes Perl core hacking
355+
and XS development easier. The macro is never intended to be shipped in stable
356+
or production code or even alpha beta code, and is strictly development
357+
helper tool for local use. It is similar to C<assert()> but launches or triggers
358+
a breakpoint in the C debugger, and you can resume execution use the step
359+
controls in the C debugger.
360+
349361
=item *
350362

351363
XXX

proto.h

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

t/uni/caller.t

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ BEGIN {
77
set_up_inc('../lib');
88
}
99

10+
use Config;
1011
use utf8;
1112
use open qw( :utf8 :std );
1213

13-
plan( tests => 18 );
14+
plan( tests => 19 );
1415

1516
package main;
1617

@@ -74,3 +75,32 @@ $^P = 16;
7475
$^P = $saved_perldb;
7576

7677
::is( eval 'pb()', 'main::pb', 'actually return the right function name even if $^P had been on at some point' );
78+
79+
# Skip the OS signal/exception from this faux-SEGV
80+
# code is from cpan/Test-Harness/t/harness.t
81+
SKIP: {
82+
::skip "No SIGSEGV on $^O", 1
83+
if $^O ne 'MSWin32' && $Config::Config{'sig_name'} !~ m/SEGV/;
84+
#line below not in cpan/Test-Harness/t/harness.t
85+
::skip "No SIGTRAP on $^O", 1
86+
if $^O ne 'MSWin32' && $Config::Config{'sig_name'} !~ m/TRAP/;
87+
88+
# some people -Dcc="somecc -fsanitize=..." or -Doptimize="-fsanitize=..."
89+
::skip "ASAN doesn't passthrough SEGV", 1
90+
if "$Config{cc} $Config{ccflags} $Config{optimize}" =~ /-fsanitize\b/;
91+
92+
my $out_str = ::fresh_perl('BEGIN { chdir \'t\' if -d \'t\';'
93+
.'require \'./test.pl\';set_up_inc(\'../lib\',\'../../lib\');}'
94+
.'use XS::APItest;XS::APItest::test_C_BP_breakpoint();');
95+
96+
# On machines where 'ulimit -c' does not return '0', a perl.core
97+
# file is created here. We don't need to examine it, and it's
98+
# annoying to have it subsequently show up as an untracked file in
99+
# `git status`, so simply get rid of it per suggestion by Karen
100+
# Etheridge.
101+
END { unlink 'perl.core' }
102+
103+
104+
::like($out_str, qr/panic: C breakpoint hit file/,
105+
'C_BP macro and C breakpoint works');
106+
}

util.c

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2003,6 +2003,168 @@ Perl_croak_popstack(void)
20032003
my_exit(1);
20042004
}
20052005

2006+
/*
2007+
=for apidoc c_bp
2008+
2009+
Internal helper for C<C_BP>. Not to be called directly.
2010+
2011+
Prints file name, C function name, line number, and CPU the instruction
2012+
pointer. Instruction pointer intended to be copied to a C debugger tool or
2013+
disassembler or used with core dumps. It is a faux-function pointer to
2014+
somewhere in the middle of the caller's C function, this address can never
2015+
be casted from I<void *> to a function pointer, then called, a SEGV will
2016+
occur.
2017+
2018+
=cut
2019+
*/
2020+
2021+
void
2022+
Perl_c_bp(const char * file_metadata, ...)
2023+
{
2024+
/* file_metadata is a string in the format of "XS_my_func*XSModule.c*6789"
2025+
The 3 arguments are catted together by CPP, so in the caller,
2026+
when using a C debugger, you press "Step One" key 2 times less, when
2027+
using step by disassembly view. On compilers like GCC where __FUNCTION__
2028+
is not a string litteral but a const char * global
2029+
variable/linker symbol, file_metadata is only the
2030+
function name, and does not have a "*" in it, so then read the 2nd
2031+
optional argument which const char * string litteral that has
2032+
the file name and line number catted together with "*";
2033+
2034+
C_BP macro should never appear in
2035+
public Stable/Gold releases of Perl core or any CPAN module. Using
2036+
C_BP even in a alpha release, is questionable. Smokers/CI greatly
2037+
dislike SEGVs which someone require human intervention to unfreeze
2038+
the console or unattended CI tool.
2039+
*/
2040+
2041+
/* XXX improvements, identify which .so/.dll on disk this address is from.
2042+
Ajust value to a 0-indexed value to remove ASLR randomizing between
2043+
process runs. Better integration with USE_C_BACKTRACE if
2044+
USE_C_BACKTRACE enabled on a particular platform. */
2045+
#if defined(__has_builtin) && __has_builtin(__builtin_return_address)
2046+
void * ip = __builtin_return_address(0); /* GCC family */
2047+
#elif _MSC_VER
2048+
void * ip = _ReturnAddress();
2049+
#else
2050+
/* last resort, seems to work on all CPU archs, guaranteed to work
2051+
on all x86/x64 OSes, all CCs, exceptions to last resort, rumor says
2052+
Solaris SPARC, call/ret instructions pop and push function pointers
2053+
to an array of function pointers, far far away from the C stack as
2054+
a security measure so on SPARC this would be the contents of a random
2055+
C auto var in the caller.
2056+
2057+
IA64, with hardware assistence by the IA64, supposedly appropriate
2058+
portions of the C stack are automatically shifted into kernel space on
2059+
each function call so no callee can read or write any C auto var in its
2060+
caller. Only exception is "other_func(&some_var_this_func);" The shift
2061+
factor now excludes some_var_this_func. So the line below would SEGV.
2062+
2063+
If any bug reports come in from these old CPUs, implement the correct
2064+
platform specific way to get debugging info, or uncomment the fallback */
2065+
void * ip = *(((void **)&file_metadata)-1);
2066+
/* fallback
2067+
# if PTRSIZE == 4
2068+
void * ip = (void *)0x12345678;
2069+
# else
2070+
void * ip = (void *)0x123456789ABCDEF0;
2071+
# endif
2072+
*/
2073+
#endif
2074+
char buf [sizeof("panic: C breakpoint hit file \"%.*s\", function \"%.*s\" line %.*s CPU IP 0x%p\n")
2075+
+ (U8_MAX*3) + (PTRSIZE*2) + 1];
2076+
va_list args;
2077+
int out_len;
2078+
U32 f_len;
2079+
const char * file_metadata_end;
2080+
const char * p;
2081+
2082+
const char * fnc_st;
2083+
const char * fnc_end;
2084+
U8 fnc_len;
2085+
2086+
const char * fn_st;
2087+
const char * fn_end;
2088+
U8 fn_len;
2089+
2090+
const char * ln_st;
2091+
const char * ln_end;
2092+
U8 ln_len;
2093+
2094+
PERL_ARGS_ASSERT_C_BP;
2095+
2096+
va_start(args, file_metadata);
2097+
f_len = (U32)strlen(file_metadata);
2098+
file_metadata_end = file_metadata + f_len;
2099+
p = file_metadata;
2100+
2101+
fnc_st = p;
2102+
fnc_end = (const char*)memchr( (const void *)fnc_st,
2103+
'*', file_metadata_end - fnc_st);
2104+
if(!fnc_end) {
2105+
if(f_len) {
2106+
fnc_end = fnc_st + f_len;
2107+
file_metadata = va_arg(args, const char *);
2108+
f_len = strlen(file_metadata);
2109+
file_metadata_end = file_metadata + f_len;
2110+
p = file_metadata;
2111+
}
2112+
else {
2113+
fnc_st = "unknown";
2114+
fnc_end = fnc_st + STRLENs("unknown");
2115+
p = file_metadata_end;
2116+
}
2117+
}
2118+
else {
2119+
p = fnc_end + 1;
2120+
}
2121+
fnc_len = (U8)(fnc_end - fnc_st);
2122+
2123+
fn_st = p;
2124+
fn_end = (const char*)memchr( (const void *)fn_st,
2125+
'*', file_metadata_end - fn_st);
2126+
if(!fn_end) {
2127+
fn_st = "unknown";
2128+
fn_end = fn_st + STRLENs("unknown");
2129+
p = file_metadata_end;
2130+
}
2131+
else {
2132+
p = fn_end + 1;
2133+
}
2134+
fn_len = (U8)(fn_end-fn_st);
2135+
2136+
ln_st = p;
2137+
ln_end = file_metadata_end;
2138+
ln_len = (U8)(ln_end - p);
2139+
if(!ln_len) {
2140+
ln_st = "unknown";
2141+
ln_len = STRLENs("unknown");
2142+
}
2143+
out_len = my_snprintf((char*)buf, sizeof(buf)-2,
2144+
"panic: C breakpoint hit file \"%.*s\", "
2145+
"function \"%.*s\" line %.*s CPU IP 0x%p",
2146+
(int)fn_len, fn_st, (int)fnc_len, fnc_st,
2147+
(int)ln_len, ln_st, ip);
2148+
if(out_len > 0 && out_len >= (int)(sizeof(buf)-2)) {
2149+
out_len = (int)(sizeof(buf)-2);
2150+
}
2151+
buf[out_len] = '\0'; /* MSVCRT bug don't ask, paranoia */
2152+
2153+
STMT_START {
2154+
dTHX; /* stderr+stdout, force user to see it */
2155+
Perl_warn(aTHX_ "%s", (char *)buf); /* no "\n" for max diag info */
2156+
PerlIO_flush(PerlIO_stderr());
2157+
PerlIO * out = PerlIO_stdout();
2158+
buf[out_len] = '\n'; /* force shell/terminal to print it, paranoia */
2159+
out_len++;
2160+
buf[out_len] = '\0';
2161+
PerlIO_write(out, (char *)buf, (Size_t)out_len);
2162+
PerlIO_flush(out); /* force shell/terminal to print it */
2163+
} STMT_END;
2164+
va_end(args);
2165+
return;
2166+
}
2167+
20062168
/*
20072169
=for apidoc warn_sv
20082170

0 commit comments

Comments
 (0)