/* This file is included twice to generate both versions of the
 * runops loop.
 */

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "plxsdtrace.h"
#include "runops.h"

#undef PROBE_ENTRY
#undef PROBE_RETURN
#undef RUNOPS_DTRACE
#undef RUNOPS_SUB_EXIT

#ifdef RUNOPS_FAKE

#define PROBE_ENTRY(func, file, line)               \
    if ( func && file ) {                           \
        printf( "ENTRY(%s, %s, %d)\n", func, file, line ); \
    }

#define PROBE_RETURN(func, file, line)              \
    if ( func && file ) {                           \
        printf( "RETURN(%s, %s, %d)\n", func, file, line ); \
    }

#define RUNOPS_DTRACE   _runops_dtrace_fake
#define RUNOPS_SUB_EXIT _runops_sub_exit_fake

#else

#define PROBE_ENTRY(func, file, line)                       \
if ( PERLXS_SUB_ENTRY_ENABLED(  ) && func && file ) {       \
    PERLXS_SUB_ENTRY( func, file, line );                   \
}

#define PROBE_RETURN(func, file, line)                      \
    if ( PERLXS_SUB_RETURN_ENABLED(  ) && func && file ) {  \
        PERLXS_SUB_RETURN( func, file, line );              \
    }

#define RUNOPS_DTRACE   _runops_dtrace
#define RUNOPS_SUB_EXIT _runops_sub_exit

#endif

#define IS_ENTERSUB(op) \
    ((op->op_type) == OP_ENTERSUB)

STATIC void
RUNOPS_SUB_EXIT( pTHX_ void *sub_name ) {
    PROBE_RETURN( ( char * ) sub_name,
                  CopFILE( PL_curcop ), CopLINE( PL_curcop ) );
}

STATIC int
RUNOPS_DTRACE( pTHX ) {
    const OP *last_op = NULL;
    const OP *next_op = NULL;
    I32 last_cxstack_ix = 0;
    const char *last_func = NULL;
    I32 eval_depth = 0;

    /* TODO: Use save_destructor(_x) to install a hook that's called on
     * scope exit so we match RETURNS with ENTRIES.
     * TODO: Remove last_op, last_cxstack_ix, last_func and do the test
     * /before/ calling the op.
     */

    while ( PL_op ) {
        last_op = PL_op;
        next_op = PL_op->op_next;

        if ( PL_op = CALL_FPTR( PL_op->op_ppaddr ) ( aTHX ), PL_op ) {
            PERL_ASYNC_CHECK(  );
        }

        if ( IS_ENTERSUB( last_op ) ) {
            /* If we just called XS we'll now be at the next op. If we
             * called a Perl subroutine we'll be executing its first op
             * instead. 
             */
            if ( PL_op != next_op ) {
                char *sub_name = ( char * ) _sub_name( aTHX );
                PROBE_ENTRY( sub_name, CopFILE( PL_curcop ),
                             CopLINE( PL_curcop ) );
                save_destructor_x( RUNOPS_SUB_EXIT, sub_name );
            }
        }
    }

    TAINT_NOT;
    return 0;
}
