Skip to content

Commit

Permalink
Add xop_dump field and integrate into op_dump()
Browse files Browse the repository at this point in the history
When working with nontrivially-shaped custom ops, such as ones based on
UNOP_AUX with interesting op_aux arrays, it is often useful to be able
to peek into the contents of these op structures with `op_dump()`.
Perl's core dumper cannot know the contents of these aux arrays, but by
defining a helper function in the module that provides the custom op,
help can be achieved.

A helper function, `opdump_printf` is also provided that acts as a
printf()-alike function for outputting lines of content. The various
internal arguments to it (level, bar, file) are bundled up into an
opaque structure, so as to achieve a modicum of abstraction away from
the specific internals on how dump.c happens to work.
  • Loading branch information
leonerd committed Sep 9, 2024
1 parent 2f720f7 commit 1eab73a
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 2 deletions.
86 changes: 86 additions & 0 deletions dump.c
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,76 @@ S_opdump_indent(pTHX_ const OP *o, I32 level, UV bar, PerlIO *file,
va_end(args);
}

struct Perl_OpDumpContext {
I32 level;
UV bar;
PerlIO *file;
bool indent_needed;
};

static void
S_opdump_print(pTHX_ struct Perl_OpDumpContext *ctx, SV *msg)
{
STRLEN msglen;
const char *msgpv = SvPV(msg, msglen);

while(msglen) {
if(ctx->indent_needed) {
PerlIO_puts(ctx->file, " ");

for (I32 i = ctx->level-1; i >= 0; i--)
PerlIO_puts(ctx->file,
(ctx->bar & (1 << i)) ? "| " : " ");
}

const char *eol_at = strchr(msgpv, '\n');
if(eol_at) {
STRLEN partlen = eol_at - msgpv + 1;
PerlIO_write(ctx->file, msgpv, partlen);

ctx->indent_needed = true;
msgpv += partlen;
msglen -= partlen;
}
else {
PerlIO_write(ctx->file, msgpv, msglen);

ctx->indent_needed = false;
msglen = 0;
}
}
}

/*
=for apidoc_section $debugging
=for apidoc opdump_printf
Prints formatted output to C<STDERR> according to the pattern and subsequent
arguments, in the style of C<printf()> et.al. This should only be called by
a function invoked by the C<xop_dump> field of a custom operator, where the
C<ctx> opaque structure pointer should be passed in from the argument given
to the C<xop_dump> callback.
This function handles indentation after linefeeds, so message strings passed
in should not account for it themselves. Multiple lines may be passed to this
function at once, or a single line may be split across multiple calls.
=cut
*/

void
Perl_opdump_printf(pTHX_ struct Perl_OpDumpContext *ctx, const char *pat, ...)
{
va_list args;

PERL_ARGS_ASSERT_OPDUMP_PRINTF;

va_start(args, pat);
SV *msg_sv = sv_2mortal(vnewSVpvf(pat, &args));
S_opdump_print(aTHX_ ctx, msg_sv);
va_end(args);
}


/* display a link field (e.g. op_next) in the format
* ====> sequence_number [opname 0x123456]
Expand Down Expand Up @@ -1493,6 +1563,22 @@ S_do_op_dump_bar(pTHX_ I32 level, UV bar, PerlIO *file, const OP *o)
break;
}

case OP_CUSTOM:
{
void (*custom_dumper)(pTHX_ const OP *o, struct Perl_OpDumpContext *ctx) =
XopENTRYCUSTOM(o, xop_dump);

if(custom_dumper) {
struct Perl_OpDumpContext ctx = {
.level = level,
.bar = bar,
.file = file,
.indent_needed = true,
};
(*custom_dumper)(aTHX_ o, &ctx);
}
break;
}

default:
break;
Expand Down
3 changes: 3 additions & 0 deletions embed.fnc
Original file line number Diff line number Diff line change
Expand Up @@ -2376,6 +2376,9 @@ ARdp |OP * |op_convert_list|I32 optype \
|I32 flags \
|NULLOK OP *o
Adp |void |op_dump |NN const OP *o
Adfp |void |opdump_printf |NN struct Perl_OpDumpContext *ctx \
|NN const char *pat \
|...
; Used in op.c and class.c
Adp |OP * |op_force_list |NULLOK OP *o
Adp |void |op_free |NULLOK OP *arg
Expand Down
1 change: 1 addition & 0 deletions embed.h
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,7 @@
# define op_scope(a) Perl_op_scope(aTHX_ a)
# define op_sibling_splice Perl_op_sibling_splice
# define op_wrap_finally(a,b) Perl_op_wrap_finally(aTHX_ a,b)
# define opdump_printf(a,...) Perl_opdump_printf(aTHX_ a,__VA_ARGS__)
# define packlist(a,b,c,d,e) Perl_packlist(aTHX_ a,b,c,d,e)
# define pad_add_anon(a,b) Perl_pad_add_anon(aTHX_ a,b)
# define pad_add_name_pv(a,b,c,d) Perl_pad_add_name_pv(aTHX_ a,b,c,d)
Expand Down
8 changes: 7 additions & 1 deletion op.c
Original file line number Diff line number Diff line change
Expand Up @@ -15402,7 +15402,7 @@ Perl_custom_op_get_field(pTHX_ const OP *o, const xop_flags_enum field)
HE *he = NULL;
XOP *xop;

static const XOP xop_null = { 0, 0, 0, 0, 0 };
static const XOP xop_null = { 0, 0, 0, 0, 0, 0 };

PERL_ARGS_ASSERT_CUSTOM_OP_GET_FIELD;
assert(o->op_type == OP_CUSTOM);
Expand Down Expand Up @@ -15476,6 +15476,9 @@ Perl_custom_op_get_field(pTHX_ const OP *o, const xop_flags_enum field)
case XOPe_xop_peep:
any.xop_peep = xop->xop_peep;
break;
case XOPe_xop_dump:
any.xop_dump = xop->xop_dump;
break;
default:
field_panic:
Perl_croak(aTHX_
Expand All @@ -15497,6 +15500,9 @@ Perl_custom_op_get_field(pTHX_ const OP *o, const xop_flags_enum field)
case XOPe_xop_peep:
any.xop_peep = XOPd_xop_peep;
break;
case XOPe_xop_dump:
any.xop_dump = XOPd_xop_dump;
break;
default:
goto field_panic;
break;
Expand Down
7 changes: 6 additions & 1 deletion op.h
Original file line number Diff line number Diff line change
Expand Up @@ -923,6 +923,7 @@ struct custom_op {
const char *xop_desc;
U32 xop_class;
void (*xop_peep)(pTHX_ OP *o, OP *oldop);
void (*xop_dump)(pTHX_ const OP *o, struct Perl_OpDumpContext *ctx);
};

/* return value of Perl_custom_op_get_field, similar to void * then casting but
Expand All @@ -933,6 +934,7 @@ typedef union {
const char *xop_desc;
U32 xop_class;
void (*xop_peep)(pTHX_ OP *o, OP *oldop);
void (*xop_dump)(pTHX_ const OP *o, struct Perl_OpDumpContext *ctx);
XOP *xop_ptr;
} XOPRETANY;

Expand All @@ -942,20 +944,23 @@ typedef union {
#define XOPf_xop_desc 0x02
#define XOPf_xop_class 0x04
#define XOPf_xop_peep 0x08
#define XOPf_xop_dump 0x10

/* used by Perl_custom_op_get_field for option checking */
typedef enum {
XOPe_xop_ptr = 0, /* just get the XOP *, don't look inside it */
XOPe_xop_name = XOPf_xop_name,
XOPe_xop_desc = XOPf_xop_desc,
XOPe_xop_class = XOPf_xop_class,
XOPe_xop_peep = XOPf_xop_peep
XOPe_xop_peep = XOPf_xop_peep,
XOPe_xop_dump = XOPf_xop_dump,
} xop_flags_enum;

#define XOPd_xop_name PL_op_name[OP_CUSTOM]
#define XOPd_xop_desc PL_op_desc[OP_CUSTOM]
#define XOPd_xop_class OA_BASEOP
#define XOPd_xop_peep ((Perl_cpeep_t)0)
#define XOPd_xop_dump NULL

#define XopENTRY_set(xop, which, to) \
STMT_START { \
Expand Down
2 changes: 2 additions & 0 deletions perl.h
Original file line number Diff line number Diff line change
Expand Up @@ -4505,6 +4505,8 @@ typedef struct crypt_data { /* straight from /usr/include/crypt.h */

#include "perly.h"

/* opaque struct type used to communicate between xop_dump and opdump_printf */
struct Perl_OpDumpContext;

/* macros to define bit-fields in structs. */
#ifndef PERL_BITFIELD8
Expand Down
11 changes: 11 additions & 0 deletions pod/perlguts.pod
Original file line number Diff line number Diff line change
Expand Up @@ -3848,6 +3848,17 @@ will be called from C<Perl_rpeep> when ops of this type are encountered
by the peephole optimizer. I<o> is the OP that needs optimizing;
I<oldop> is the previous OP optimized, whose C<op_next> points to I<o>.

=item xop_dump

This member is a pointer to a function of type
C<void (pTHX_ OP *, struct OpDumpContext *)>. If set, this function is called
by C<op_dump()> when dumping a custom operator of this type, after the op's
basic fields have been printed. This function may make use of
C<opdump_printf()> to emit additional output that may be useful for debugging.

The opaque structure pointer passed in as its final argument should be passed
directly into C<opdump_printf()>.

=for apidoc_section $optree_manipulation
=for apidoc Ayh||Perl_cpeep_t

Expand Down
6 changes: 6 additions & 0 deletions proto.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 1eab73a

Please sign in to comment.