int trigger_fill_xecute_buffer(gv_trigger_t *trigdsc)
{
	int	src_fetch_status;

	assert(!dollar_tlevel || (tstart_trigger_depth <= gtm_trigger_depth));
	/* We have 3 cases to consider - all of which REQUIRE a TP fence to already be in effect. The reason for this is, if we
	 * detect a restartable condition, we are going to cause this region's triggers to be unloaded which destroys the block
	 * our parameter is pointing to so the restart logic MUST take place outside of this routine.
	 *
	 *   1. We have an active transaction due to an IMPLICIT TSTART done by trigger handling but the trigger level has not yet
	 *	been created. We don't need another TP wrapper in this case but we do need a condition handler to trap the thrown
	 *	retry to again prevent C stack unwind and return to the caller in the same shape that gtm_trigger would return.
	 *   2. We have an active transaction due to an EXPLICIT M-code TSTART command. For this case, the trigger loads proceed
	 *   	as normal with restarts handled in the regular automatic fashion. Note this case also covers the tp restarts done
	 *	by both the update process and mupip recover forward since those functions have their own way of intercepting and
	 *	dealing with restarts. To cover those cases, tp->implicit_tstart can be TRUE but tp_implicit_trigger MUST be
	 *	FALSE.
	 *   3. We have an active transaction due to an IMPLICIT TSTART done by trigger handling and one or more triggers are
	 *	running. This becomes like case 2 since the restart will be handled by gtm_trigger and the proper thing will
	 *	be done.
	 *
	 * An extra note about case 3. Case 3 can be the identified case if in a nested trigger we are in trigger-no-mans-land
	 * with a base frame for the nested trigger (having driven one of a set of parallel nested triggers) but no actual trigger
	 * execution frame yet exists. This is really a case 1 situation with a nested trigger but it turns out that dealing with
	 * like case 3 does the right thing because if/when mdb_condition_handler catches a thrown TPRETRY error, mdb_condition
	 * handler will peal the nested trigger frame off before doing the restart which works for us and avoids issues of
	 * multi-level implicit restarts we would otherwise have to handle.
	 *
	 * Note, this routine is for loading trigger source when the trigger is to be driven. The trigger_source_read_andor_verify()
	 * routine should be used when fetching trigger source for reasons other than driving the triggers. This routine is lighter
	 * weight but has a dependence on the restartability of the trigger-drive logic for getting the triggers reloaded as
	 * necessary.
	 */
	assertpro(0 < dollar_tlevel);
	if (!tp_pointer->implicit_trigger		/* Case 2 */
		|| (tp_pointer->implicit_tstart && tp_pointer->implicit_trigger
		&& (tstart_trigger_depth != gtm_trigger_depth)))	/* Case 3 */
	{	/* Test for Case 3/4 where we get to do very little: */
		DBGTRIGR((stderr, "trigger_fill_xecute_buffer: Case 2/3\n"));
		assert((!tp_pointer->implicit_trigger) || (0 < gtm_trigger_depth));
		trigger_fill_xecute_buffer_read_trigger_source(trigdsc);
	} else
	{	/* Test for Case 1 where we only need a condition handler */
		DBGTRIGR((stderr, "trigger_fill_xecute_buffer: Case 1\n"));
		assert(tp_pointer->implicit_tstart && tp_pointer->implicit_trigger);
		assert(tstart_trigger_depth == gtm_trigger_depth);
		ESTABLISH_RET(trigger_fill_xecute_buffer_ch, SIGNAL);
		trigger_fill_xecute_buffer_read_trigger_source(trigdsc);
		REVERT;
	}
	/* return our bounty to caller */
	trigdsc->xecute_str.mvtype = MV_STR;
	return 0;	/* Could return ERR_TPRETRY if return is via our condition handler */
}
Example #2
0
/* Unwind a trigger level, pop all the associated mv_stents and dispense with the base frame.
 * Note that this unwind restores the condition handler stack pointer and correct gtm_trigger_depth value in
 * order to maintain these values properly in the event of a major unwind. This routine is THE routine to use to unwind
 * trigger base frames in all cases due to the cleanups it takes care of.
 */
void gtm_trigger_fini(boolean_t forced_unwind, boolean_t fromzgoto)
{
	if (0 == (frame_pointer->type & SFT_TRIGR))
		GTMASSERT;		/* Would normally be an assert but potential frame stack damage so severe
					   and resulting debug difficulty that we GTMASSERT instead. */
	/* Unwind the trigger base frame */
	op_unwind();
	/* restore frame_pointer stored at msp (see base_frame.c) */
	frame_pointer = *(stack_frame**)msp;
	msp += SIZEOF(stack_frame *);		/* Remove frame save pointer from stack */
	if (!forced_unwind)
	{	/* Remove the "do not return to me" flag only on non-error unwinds. Note this flag may have already been
		 * turned off by an earlier tp_restart if this is not an implicit_tstart situation.
		 */
		assert(!tp_pointer->implicit_tstart || (SFF_TRIGR_CALLD & frame_pointer->flags));
		frame_pointer->flags &= SFF_TRIGR_CALLD_OFF;
		DBGTRIGR((stderr, "gtm_trigger_fini: turning off SFF_TRIGR_CALLD (2) in frame 0x"lvaddr"\n", frame_pointer));
	} else
	{	/* Error unwind, make sure certain cleanups are done */
#		ifdef DEBUG
		assert(!dollar_tlevel || (tstart_trigger_depth <= gtm_trigger_depth));
		if (tstart_trigger_depth == gtm_trigger_depth) /* Unwinding gvcst_put() so get rid of flag it potentially set */
			donot_INVOKE_MUMTSTART = FALSE;
#		endif
		if (tp_pointer)
		{	/* This TP transaction can never be allowed to commit if this is the first trigger
			 * (see comment in tp_frame.h against "cannot_commit" field for details).
			 */
			if ((0 == gtm_trigger_depth) && !fromzgoto)
			{
				DBGTRIGR((stderr, "gtm_trigger: cannot_commit flag set to TRUE\n"))
				tp_pointer->cannot_commit = TRUE;
			}
			if ((tp_pointer->fp == frame_pointer) && tp_pointer->implicit_tstart)
				OP_TROLLBACK(-1); /* We just unrolled the implicitly started TSTART so unroll what it did */
		}
	}
	DBGTRIGR((stderr, "gtm_trigger: POP: frame_pointer 0x%016lx  ctxt value: 0x%016lx\n", frame_pointer, ctxt));
	/* Re-allow interruptions now that our base frame is gone */
	if (forced_unwind)
	{	/* Since we are being force-unwound, we don't know the state of things except that it it should be either
		 * the state we set it to or the ok-to-interrupt state. Assert that and if we are changing the state,
		 * be sure to run the deferred handler.
		 */
		assert((INTRPT_IN_TRIGGER_NOMANS_LAND == intrpt_ok_state) || (INTRPT_OK_TO_INTERRUPT == intrpt_ok_state));
		ENABLE_INTERRUPTS(intrpt_ok_state);
	} else
	{	/* Normal unwind should be ok with this macro */
		ENABLE_INTERRUPTS(INTRPT_IN_TRIGGER_NOMANS_LAND);
	}
}
Example #3
0
STATICFNDEF int gtm_trigger_invoke(void)
{	/* Invoke trigger M routine. Separate so error returns to gtm_trigger with proper retcode */
	int		rc;

	ESTABLISH_RET(gtm_trigger_ch, mumps_status);
	gtm_trigger_depth++;
	DBGTRIGR((stderr, "gtm_trigger: Dispatching trigger at depth %d\n", gtm_trigger_depth));
	assert(0 < gtm_trigger_depth);
	assert(GTM_TRIGGER_DEPTH_MAX >= gtm_trigger_depth);
	/* Allow interrupts to occur while the trigger is running */
	ENABLE_INTERRUPTS(INTRPT_IN_TRIGGER_NOMANS_LAND);
	rc = dm_start();
	/* Now that we no longer have a trigger stack frame, we are back in trigger no-mans-land */
	DEFER_INTERRUPTS(INTRPT_IN_TRIGGER_NOMANS_LAND);
	gtm_trigger_depth--;
	DBGTRIGR((stderr, "gtm_trigger: Trigger returns with rc %d\n", rc));
	REVERT;
	assert(frame_pointer->type & SFT_TRIGR);
	assert(0 <= gtm_trigger_depth);
	return rc;
}
Example #4
0
void t_retry(enum cdb_sc failure)
{
	tp_frame		*tf;
	unsigned char		*end, buff[MAX_ZWR_KEY_SZ];
	short			tl;
	sgmnt_addrs		*csa;
	sgmnt_data_ptr_t	csd;
	node_local_ptr_t	cnl;
#	ifdef DEBUG
	unsigned int		tries;
#	endif
	boolean_t		skip_invoke_restart;
	boolean_t		redo_root_search_done = FALSE;
	unsigned int		local_t_tries;
	mstr			gvname_mstr, reg_mstr;
	gd_region		*restart_reg;
	mval			t_restart_entryref;
	DCL_THREADGBL_ACCESS;

	SETUP_THREADGBL_ACCESS;
#	ifdef GTM_TRIGGER
	skip_invoke_restart = skip_INVOKE_RESTART;	/* note down global value in local variable */
	GTMTRIG_ONLY(DBGTRIGR((stderr, "t_retry: entered\n")));
#	else
	skip_invoke_restart = FALSE;	/* no triggers so set local variable to default state */
#	endif
	/* We expect t_retry to be invoked with an abnormal failure code. mupip reorg is the only exception and can pass
	 * cdb_sc_normal failure code in case it finds a global variable existed at start of reorg, but not when it came
	 * into mu_reorg and did a gvcst_search. It cannot confirm this unless it holds crit for which it has to wait
	 * until the final retry which is why we accept this way of invoking t_retry. Assert accordingly.
	 */
	assert((cdb_sc_normal != failure) || mu_reorg_process);
	t_fail_hist[t_tries] = (unsigned char)failure;
	if (mu_reorg_process)
		CWS_RESET;
	DEBUG_ONLY(TREF(donot_commit) = FALSE;)
Example #5
0
/* Restore given local variable from supplied TP restore entry into given symval. Note lvscan_anchor will only be non-NULL
 * for the final level we are restoring (but not unwinding). We don't need to restore counters for any vars except the
 * very last level.
 *
 * The return code is only used when unrolling the M stack runs into a trigger base frame which must be unrolled
 * by gtm_trigger. A non-zero return code signals to tp_unwind() that it needs to rethrow the tprestart error.
 */
int tp_unwind_restlv(lv_val *curr_lv, lv_val *save_lv, tp_var *restore_ent, lvscan_blk **lvscan_anchor, int *tprestart_rc)
{
	ht_ent_mname	*tabent;
	lv_val		*inuse_lv;
	int		elemindx;
	mv_stent	*mvc;
	lvscan_blk	*lvscan, *newlvscan;
	lvTree		*lvt_child;
	boolean_t	var_cloned;

	assert(curr_lv);
	assert(LV_IS_BASE_VAR(curr_lv));
	assert(curr_lv->tp_var);
	DBGRFCT((stderr, "\ntp_unwind_restlv: Entered for varname: '%.*s' curr_lv: 0x"lvaddr"  save_lv: 0x"lvaddr"\n",
		 restore_ent->key.var_name.len, restore_ent->key.var_name.addr, curr_lv, save_lv));
	DBGRFCT((stderr, "tp_unwind_restlv: tp_pointer/current: fp: 0x"lvaddr"/0x"lvaddr" mvc: 0x"lvaddr"/0x"lvaddr
		 " symval: 0x"lvaddr"/0x"lvaddr"\n",
		 tp_pointer->fp, frame_pointer, tp_pointer->mvc, mv_chain, tp_pointer->sym, curr_symval));

	/* First get the stack in the position where we can actually process this entry. Need to make sure we are processing
	 * the symbol table we need to be processing so unwind enough stuff to get there.
	 */
	if (curr_symval != tp_pointer->sym)
	{	/* Unwind as many stackframes as are necessary up to the max */
		while((curr_symval != tp_pointer->sym) && (frame_pointer < tp_pointer->fp))
		{
#			ifdef GTM_TRIGGER
			if (SFT_TRIGR & frame_pointer->type)
			{	/* We have encountered a trigger base frame. We cannot unroll it because there are C frames
				 * associated with it so we must interrupt this tp_restart and return to gtm_trigger() so
				 * it can unroll the base frame and rethrow the error to properly unroll the C stack.
				 */
				*tprestart_rc = ERR_TPRETRY;
				tprestart_state = TPRESTART_STATE_TPUNW;
				DBGTRIGR((stderr, "tp_unwind: Encountered trigger base frame during M-stack unwind - "
					  "rethrowing\n"));
				return -1;
			}
#			endif
			op_unwind();
		}
		if (curr_symval != tp_pointer->sym)
		{	/* Unwind as many mv_stents as are necessary up to the max */
			mvc = mv_chain;
			while((curr_symval != tp_pointer->sym) && (mvc < tp_pointer->mvc))
			{
				unw_mv_ent(mvc);
				mvc = (mv_stent *)(mvc->mv_st_next + (char *)mvc);
			}
			mv_chain = mvc;
			/* Final check */
			assertpro(curr_symval == tp_pointer->sym);
		}
	}
	var_cloned = curr_lv->tp_var->var_cloned;
	if (var_cloned)
	{	/* Var/tree has been copied (and modified) -- see about restoring it */
		DBGRFCT((stderr, "\ntp_unwind_restlv: curr_lv was modified and cloned -- needs restoration\n"));
		if (NULL != restore_ent->key.var_name.addr)
		{	/* Restore data into a named variable (hash table entry)
			 * Step 1 -- find its hash table address to see what lv_val is there now.
			 */
			tabent = lookup_hashtab_mname(&((tp_pointer->sym)->h_symtab), &restore_ent->key);
			assert(tabent);
			/* Step 2 -- If lv_val is NOT the same as it was, then we must replace the lv_val
			 * currently in use. Decrement its use count (which will delete it and the tree if
			 * it is no longer used) and replace with desired previous lv_val whose use count
			 * was incremented when it was saved.
			 */
			if (curr_lv != (inuse_lv = (lv_val *)tabent->value))	/* Note assignment */
			{
				if (inuse_lv)
					DECR_BASE_REF_RQ(tabent, inuse_lv, FALSE);
				DBGRFCT((stderr, "tp_unwind: hte 0x"lvaddr" being reset from 0x"lvaddr" to 0x"lvaddr"\n",
					 tabent, tabent->value, curr_lv));
				tabent->value = (void *)curr_lv;
				INCR_TREFCNT(curr_lv);			/* Back in the hash table, bump its reference */
			}
		} /* Else, if restoring orphaned data, just prune the old var and copy in the saved tree (if one existed) */
		/* Step 3 -- We have the correct lv_val in the hash table now but it has the wrong value.
		 * Get rid of its current tree if any.
		 */
		if (lvt_child = LV_GET_CHILD(curr_lv))	/* Note assignment */
		{
			DBGRFCT((stderr, "\ntp_unwind_restlv: Killing children of curr_lv 0x"lvaddr"\n", curr_lv));
			assert((lvTreeNode *)curr_lv == LVT_PARENT(lvt_child));
			LV_CHILD(curr_lv) = NULL;	/* prevent recursion due to alias containers */
			lv_killarray(lvt_child, FALSE);
		}
		/* Step 4:  Copy in the needed fields from the saved flavor lv_val back to curr_lv.
		 * Preserve the ref counts of the current var since the copy's ref counts have not been kept up to date.
		 */
		DBGRFCT((stderr, "\ntp_unwind_restlv: Restoring value of lv 0x"lvaddr" back into lv 0x"lvaddr"\n",
			save_lv, curr_lv));
		/* The following is optimized to do the initialization of just the needed structure members. For that it assumes a
		 * particular "lv_val" structure layout. The assumed layout is asserted so any changes to the layout will
		 * automatically show an issue here and cause the below initialization to be accordingly reworked.
		 */
		assert(0 == OFFSETOF(lv_val, v));
		assert(OFFSETOF(lv_val, v) + SIZEOF(curr_lv->v) == OFFSETOF(lv_val, ptrs));
		assert(OFFSETOF(lv_val, ptrs) + SIZEOF(curr_lv->ptrs) == OFFSETOF(lv_val, stats));
		assert(OFFSETOF(lv_val, stats) + SIZEOF(curr_lv->stats) == OFFSETOF(lv_val, has_aliascont));
		assert(OFFSETOF(lv_val, has_aliascont) + SIZEOF(curr_lv->has_aliascont) == OFFSETOF(lv_val, lvmon_mark));
		assert(OFFSETOF(lv_val, lvmon_mark) + SIZEOF(curr_lv->lvmon_mark) == OFFSETOF(lv_val, tp_var));
		assert(OFFSETOF(lv_val, tp_var) + SIZEOF(curr_lv->tp_var) == SIZEOF(lv_val));
		/* save_lv -> curr_lv Copy begin */
		curr_lv->v = save_lv->v;
		curr_lv->ptrs = save_lv->ptrs;
		assert(0 < curr_lv->stats.trefcnt);	/* No need to copy "stats" as curr_lv is more uptodate */
		assert(0 < curr_lv->stats.crefcnt);
		assert(8 == (OFFSETOF(lv_val, tp_var) - OFFSETOF(lv_val, has_aliascont)));
		curr_lv->has_aliascont = save_lv->has_aliascont;
		DBGALS_ONLY(curr_lv->lvmon_mark = save_lv->has_aliascont);
		assert(save_lv->tp_var == curr_lv->tp_var);	/* no need to copy this field */
		/* save_lv -> curr_lv Copy done */
		/* Some fixup may need to be done if the variable was cloned (and thus moved around) */
		curr_lv->tp_var->var_cloned = FALSE;
		if (lvt_child = LV_GET_CHILD(curr_lv))
		{	/* Some pointer fix up needs to be done since the owner of the restored tree changed */
			assert(LVT_PARENT(lvt_child) == ((lvTreeNode *)curr_lv->tp_var->save_value));
			LV_CHILD(save_lv) = NULL;	/* now that curr_lv->tp_var->var_cloned has been reset */
			LVT_PARENT(lvt_child) = (lvTreeNode *)curr_lv;
			if (curr_lv->has_aliascont && (NULL != lvscan_anchor))
			{	/* Some ref counts need to be restored for arrays this tree points to -- but only if the
				 * array contains pointers (alias containers).
				 */
				DBGRFCT((stderr, "\ntp_unwind_restlv: Putting lv 0x:"lvaddr" on the lvscan list\n", curr_lv));
				/* This array needs to have container pointer target reference counts reestablished. Record
				 * the lv so this can happen after all vars are restored.
				 */
				lvscan = *lvscan_anchor;
				elemindx = lvscan->elemcnt++;	/* Note post increment so elemindx has minus-one value */
				if (ARY_SCNCNTNR_MAX < elemindx)
				{	/* Present block is full so allocate a new one and chain it on */
					lvscan->elemcnt--;		/* New element ended up not being in that block.. */
					newlvscan = (lvscan_blk *)malloc(SIZEOF(lvscan_blk));
					newlvscan->next = lvscan;
					newlvscan->elemcnt = 1;		/* Going to use first one *now* */
					elemindx = 0;
					*lvscan_anchor = newlvscan;
					lvscan = newlvscan;
				}
				assert((ARY_SCNCNTNR_MAX >= elemindx) && (0 <= elemindx));
				lvscan->ary_scncntnr[elemindx] = curr_lv;
			}
		}
	} else
	{
		DBGRFCT((stderr, "\ntp_unwind_restlv: curr_lv was NOT modified or cloned\n"));
		assert(NULL == LV_CHILD(save_lv));
		assert(!save_lv->tp_var->var_cloned);
		/* We know that the subscript array underneath curr_lv did not change since saving it into save_lv. But the
		 * unsubscripted lv could have changed (have no way of checking if that is the case) so restore it unconditionally.
		 */
		curr_lv->v = save_lv->v;
		/* No need to copy "save_lv->ptrs" as "ptrs" contains 2 fields both of which are already correct in "curr_lv" */
		assert(save_lv->ptrs.val_ent.parent.sym == curr_lv->ptrs.val_ent.parent.sym);
		assert(NULL == save_lv->ptrs.val_ent.children);
		/* No need to copy "save_lv->stats" as "curr_lv->stats" is more uptodate */
		assert(save_lv->has_aliascont == curr_lv->has_aliascont);	/* no need to copy this field */
		assert(save_lv->lvmon_mark == curr_lv->lvmon_mark);	/* no need to copy this field */
		assert(save_lv->tp_var == curr_lv->tp_var);	/* no need to copy this field */
	}
	if (NULL == lvscan_anchor)
		/* Means this is completely unwinding a nested level so we need to reset the tstartcycle in this
		 * lvval so it gets handled correctly when this lv is encountered again after the restart completes.
		 */
		curr_lv->stats.tstartcycle = 0;
	return 0;
}
Example #6
0
    gd_region		*r_top, *r_local;
    gd_addr			*addr_ptr;
    unsigned char		tp_bat[TP_BATCH_LEN];
    mname_entry		tpvent;
    ht_ent_mname		*tabent, *curent, *topent;
    sgmnt_addrs		*csa;
    int4			shift_size;
    boolean_t		tphold_noshift = FALSE, implicit_tstart;
    GTMTRIG_ONLY(boolean_t	implicit_trigger;)
    DCL_THREADGBL_ACCESS;

    SETUP_THREADGBL_ACCESS;
    implicit_tstart = 0 != (implicit_flag & IMPLICIT_TSTART);
    GTMTRIG_ONLY(implicit_trigger = 0 != (implicit_flag & IMPLICIT_TRIGGER_TSTART));
    GTMTRIG_ONLY(assert(!implicit_trigger || (implicit_trigger && implicit_tstart)));
    GTMTRIG_ONLY(DBGTRIGR((stderr, "op_tstart: Entered - dollar_tlevel: %d, implicit_flag: %d\n",
                           dollar_tlevel, implicit_flag)));
    if (implicit_tstart)
        /* An implicit op_tstart is being done. In this case, even if we are in direct mode, we want to do
         * regular TPHOLD processing (no setting of tphold in the parent frame and shifting of all mv_stents).
         * This is ok because the life of the TP transaction will be done before the implicit operation()s are done
         * done so it will not persist across M lines like it normally would in direct mode.
         */
        tphold_noshift = TRUE;
    /* If we haven't done any TP until now, turn the flag on to tell gvcst_init to
       initialize it in any regions it opens from now on and initialize it in any
       regions that are already open.
    */
    if (!tp_in_use)
    {
        tp_in_use = TRUE;
        for (addr_ptr = get_next_gdr(NULL); addr_ptr; addr_ptr = get_next_gdr(addr_ptr))
Example #7
0
void flush_jmp (rhdtyp *rtn_base, unsigned char *context, unsigned char *transfer_addr)
{
	mv_stent	*mv_st_ent, *mv_st_prev;
	char		*top;
	unsigned char	*msp_save;
	int4		shift, size, mv_st_type;
	rhdtyp		*old_rtnhdr;

	unwind_nocounts();
	/* We are going to mutate the current frame from the program it was running to the program we want it to run.
	 * If the current frame is marked for indr cache cleanup, do that cleanup now and unmark the frame.
	 */
	IF_INDR_FRAME_CLEANUP_CACHE_ENTRY_AND_UNMARK(frame_pointer);

	DBGEHND((stderr, "flush_jmp: Retargetting stack frame 0x"lvaddr" for transfer address 0x"lvaddr"\n", frame_pointer,
		 transfer_addr));
	/* Also unmark the SFF_ETRAP_ERR bit in case it is set. This way we ensure control gets transferred to
	 * the mpc below instead of "error_return" (which is what getframe will do in case the bit is set).
	 * It is ok to clear this bit because the global variable "error_frame" will still be set to point to
	 * this frame so whenever we unwind out of this, we will rethrow the error at the parent frame.
	 */
	assert(!(frame_pointer->flags & SFF_ETRAP_ERR) || (NULL == error_frame) || (error_frame == frame_pointer));
	assert(!(SFT_TRIGR & frame_pointer->type));
	frame_pointer->flags &= SFF_ETRAP_ERR_OFF;	  /* clear SFF_ETRAP_ERR bit */
	frame_pointer->flags &= SFF_IMPLTSTART_CALLD_OFF; /* clear SFF_IMPLTSTART_CALLD bit since this frame is being rewritten */
	GTMTRIG_ONLY(DBGTRIGR((stderr, "flush_jmp: Turrning off SFF_IMPLTSTART_CALLD_OFF in frame 0x"lvaddr"\n", frame_pointer)));
	old_rtnhdr = frame_pointer->rvector;
	frame_pointer->rvector = rtn_base;
	/* Now that fp->rvector has been overwritten to new routine, check if the older routine had a "rtn_relinked" flag set
	 * and if so that cleanup can be performed now.
	 */
	USHBIN_ONLY(CLEANUP_COPIED_RECURSIVE_RTN(old_rtnhdr));	/* cleanup if needed */
	frame_pointer->vartab_ptr = (char *)VARTAB_ADR(rtn_base);
	frame_pointer->vartab_len = frame_pointer->rvector->vartab_len;
	frame_pointer->mpc = transfer_addr;
	frame_pointer->ctxt = context;
#ifdef HAS_LITERAL_SECT
	frame_pointer->literal_ptr = (int4 *)LITERAL_ADR(rtn_base);
#endif
	frame_pointer->temp_mvals = frame_pointer->rvector->temp_mvals;
	size = rtn_base->temp_size;
	frame_pointer->temps_ptr = (unsigned char *)frame_pointer - size;
	size += rtn_base->vartab_len * SIZEOF(ht_ent_mname *);
	frame_pointer->l_symtab = (ht_ent_mname **)((char *)frame_pointer - size);
	assert(frame_pointer->type & SFT_COUNT);
	assert((unsigned char *)mv_chain > stacktop && (unsigned char *)mv_chain <= stackbase);
	while (((char *)mv_chain < (char *)frame_pointer) && !mvs_save[mv_chain->mv_st_type])
	{
		assert(MVST_TRIGR != mv_chain->mv_st_type);	/* Should never unwind a trigger frame here */
		msp = (unsigned char *)mv_chain;
		op_oldvar();
	}
	if ((char *)mv_chain > (char *)frame_pointer)
	{
		msp_save = msp;
		msp = (unsigned char *)frame_pointer->l_symtab;
	   	if (msp <= stackwarn)
	   	{
			if (msp <= stacktop)
			{
				msp = msp_save;
				rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_STACKOFLOW);
	   		} else
				rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_STACKCRIT);
	   	}
		memset(msp, 0, size);
		DBGEHND((stderr, "flush_jmp: Old msp: 0x"lvaddr"  New msp: 0x"lvaddr"\n", msp_save, msp));
		return;
	}
	/* We kept one or more mv_stents for this frame. We may need to shift the stack to get room to create an l_symtab
	 * for this re-purposed frame. In the above loop, we stopped searching the mv_stent chain at the first mv_stent we
	 * knew we had to keep. Since we are moving things around anyway, see if there are any mv_stents associated
	 * with this frame which don't need to be kept and can reclaim.
	 */
	mv_st_ent = mv_chain;
	mv_st_prev = (mv_stent *)((char *)mv_st_ent + mv_st_ent->mv_st_next);
	top = (char *)mv_st_ent + mvs_size[mv_st_ent->mv_st_type];
	while ((char *)mv_st_prev < (char *)frame_pointer)
	{
		mv_st_type = mv_st_prev->mv_st_type;
		assert(MVST_TRIGR != mv_st_type);	/* Should never unwind a trigger frame here */
		if (!mvs_save[mv_st_type])
		{	/* Don't need to keep this mv_stent. Remove it from the chain */
			DBGEHND((stderr, "flush_jmp: Removing no-save mv_stent addr 0x"lvaddr" and type %d\n",
				 mv_st_prev, mv_st_type));
			unw_mv_ent(mv_st_prev);
			mv_st_ent->mv_st_next += mv_st_prev->mv_st_next;
			mv_st_prev = (mv_stent *)((char *)mv_st_prev + mv_st_prev->mv_st_next);
			continue;
		}
		/* We found a previous mv_stent we need to keep. If we had an interveening mv_stent we don't need to
		 * keep, migrate the new keeper mv_stent adjacent to the previous keeper. */
		if (mv_st_prev != (mv_stent *)top)
		{
			DBGEHND((stderr, "flush_jmp: Migrating keeper mv_stent from 0x"lvaddr" to 0x"lvaddr" type %d\n",
				 mv_st_prev, top, mv_st_type));
			if (MVST_TPHOLD == mv_st_type)
			{	/* If we are moving an MVST_TPHOLD mv_stent, find it in the tpstack and fix its
				 * address there too. Else we won't unwind to the correct place on a restart. */
				fix_tphold_mvc(top, (char *)mv_st_prev, ((char *)mv_st_prev + mvs_size[MVST_TPHOLD]));
			}
			memmove(top, mv_st_prev, mvs_size[mv_st_type]);
		}
		DBGEHND((stderr, "flush_jmp: Updating offsets for mv_stent at addr 0x"lvaddr" type %d\n",
			 mv_st_ent, mv_st_ent->mv_st_type));
		mv_st_ent->mv_st_next = mvs_size[mv_st_ent->mv_st_type];
		mv_st_ent = (mv_stent *)top;
		mv_st_ent->mv_st_next += (unsigned int)((char *)mv_st_prev - top);
		top += mvs_size[mv_st_ent->mv_st_type];
		mv_st_prev = (mv_stent *)((char *)mv_st_ent + mv_st_ent->mv_st_next);
	}
	shift = (int4)((char *)frame_pointer - top - size);
	DBGEHND_ONLY(msp_save = msp);
	if (shift)
	{
   		if ((unsigned char *)mv_chain + shift <= stackwarn)
   		{
			if ((unsigned char *)mv_chain + shift <= stacktop)
   				rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_STACKOFLOW);
   			else
   				rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_STACKCRIT);
      		}
		DBGEHND((stderr, "flush_jmp: Shifting %d bytes of stack from 0x"lvaddr" to 0x"lvaddr" by %d bytes\n",
			 INTCAST(top - (char *)mv_chain), mv_chain, (mv_chain + shift), shift));
		/* Since we are moving one or more mv_stents, it is no more difficult to check the range against the
		 * tp_frame chain than it is to loop through the mv_stents checking each one since the tp_frame stack
		 * is usually no more than 1-3 deep.
		 */
		fix_tphold_mvc(((char *)mv_chain + shift), (char *)mv_chain, ((char *)mv_chain + (top - (char *)mv_chain)));
		memmove((char *)mv_chain + shift, mv_chain, top - (char *)mv_chain);
		mv_chain = (mv_stent *)((char *)mv_chain + shift);
		mv_st_ent = (mv_stent *)((char *)mv_st_ent + shift);
		mv_st_ent->mv_st_next -= shift;
		msp = (unsigned char *)mv_chain;
	}
	memset(frame_pointer->l_symtab, 0, size);
	DBGEHND((stderr, "flush_jmp: Old msp: 0x"lvaddr"  New msp: 0x"lvaddr"\n", msp_save, msp));
	return;
}
Example #8
0
void	goframes(int4 frames)
#endif
{
        mval            *ret_targ;

	GTMTRIG_ONLY(goframes_unwound_trigger = FALSE);
        for (ret_targ = NULL; frames--; )
        {
		while (tp_pointer && tp_pointer->fp <= frame_pointer)
		{
               	        OP_TROLLBACK(-1);
		}
		if (0 == frames)
		{
			ret_targ = (mval *)get_ret_targ(NULL);
			/* If alias_retarg is non-NULL, *ret_targ would have been already initialized so no need to set it.
			 * Setting it to literal_null in that case would cause reference counts to not be decremented later
			 * in op_unwind/mdb_condition_handler so it is actually necessary to skip it in that case.
			 */
	       		if ((NULL != ret_targ) && (NULL == alias_retarg))
	       		{
	       		        *ret_targ = literal_null;
	       		        ret_targ->mvtype |= MV_RETARG;
	       		}
		}
		skip_error_ret = TRUE;
#		ifdef GTM_TRIGGER
		if (!(SFT_TRIGR & frame_pointer->type))
		{	/* Normal frame unwind */
			DBGTRIGR((stderr, "goframes: unwinding regular frame at %016lx\n", frame_pointer));
			op_unwind();
			DBGTRIGR((stderr, "goframes: after regular frame unwind: frame_pointer 0x%016lx  ctxt value: 0x%016lx\n",
				  frame_pointer, ctxt));
		} else
		{	/* Trigger base frame unwind (special case) */
			DBGTRIGR((stderr, "goframes: unwinding trigger base frame at %016lx\n", frame_pointer));
			gtm_trigger_fini(TRUE, fromzgoto);
			goframes_unwound_trigger = TRUE;
		}
#		else
		/* If triggers are not enabled, just a normal unwind */
		DBGEHND((stderr, "goframes: unwinding regular frame at %016lx\n", frame_pointer));
		op_unwind();
#		endif
		assert(FALSE == skip_error_ret);	/* op_unwind() should have read and reset this */
		skip_error_ret = FALSE;			/* be safe in PRO versions */
	}
#	ifdef GTM_TRIGGER
	if (unwtrigrframe && (SFT_TRIGR & frame_pointer->type))
	{	/* If we landed on a trigger base frame after unwinding everything, we are in the same boat as if we had run into
		 * one while we were unwinding. We cannot return this frame to (for example) zgoto which is going to morph it into
		 * something else (unwtrigrframe only set when ZGOTO with entryref specified). So if the flag says we should never
		 * land on a trigger frame, go ahead and unwind that one too.
		 */
		DBGTRIGR((stderr, "goframes: unwinding trailing trigger base frame at %016lx\n", frame_pointer));
		gtm_trigger_fini(TRUE, fromzgoto);
		goframes_unwound_trigger = TRUE;
	}
#	endif

	return;
}
void trans_code_cleanup(void)
{
	stack_frame	*fp, *fpprev;
	uint4		errmsg;
	DCL_THREADGBL_ACCESS;

	SETUP_THREADGBL_ACCESS;
	assert(!(SFT_ZINTR & proc_act_type));
	/* With no extra ztrap frame being pushed onto stack, we may miss error(s)
	 * during trans_code if we don't check proc_act_type in addition to
	 * frame_pointer->type below.
	 */
	if (SFT_ZTRAP == proc_act_type)
	{
		if (0 < dollar_ztrap.str.len)
			errmsg = ERR_ERRWZTRAP;
		else
			errmsg = ERR_ERRWETRAP;
	} else if (SFT_DEV_ACT == proc_act_type)
		errmsg = ERR_ERRWIOEXC;
	else
		errmsg = 0;
	proc_act_type = 0;
	if (TREF(compile_time))
	{
		TREF(compile_time) = FALSE;
		if (stringpool.base != rts_stringpool.base)
			stringpool = rts_stringpool;
	}
	for (fp = frame_pointer; fp; fp = fpprev)
	{
		fpprev = fp->old_frame_pointer;
#		ifdef GTM_TRIGGER
		if (SFT_TRIGR & fpprev->type)
			fpprev = *(stack_frame **)(fpprev + 1);
#		endif
		if (fp->type & SFT_DM)
			break;
		if (fp->type & SFT_COUNT)
		{
			if ((ERR_ERRWZTRAP == errmsg) || (ERR_ERRWETRAP == errmsg))
			{	/* Whether ETRAP or ZTRAP we want to rethrow the error at one level down */
				SET_ERROR_FRAME(fp);	/* reset error_frame to point to the closest counted frame */
				assert(fp->flags & SFF_ETRAP_ERR);
				/* Turn off any device exception related flags now that we are going to handle errors using
				 * $ETRAP or $ZTRAP AT THE PARENT LEVEL only (no more device exceptions).
				 */
				dollar_ztrap.str.len = 0;
				ztrap_explicit_null = FALSE;
				fp->flags &= SFF_DEV_ACT_ERR_OFF;
				fp->flags &= SFF_ZTRAP_ERR_OFF;
				err_act = &dollar_etrap.str;
				break;
			} else if (ERR_ERRWIOEXC == errmsg)
			{	/* Error while compiling device exception. Set SFF_ETRAP_ERR bit so control is transferred to
				 * error_return() which in turn will rethrow the error AT THE SAME LEVEL in order to try and
				 * use $ZTRAP or $ETRAP whichever is active. Also set the SFF_DEV_ACT_ERR bit to signify this
				 * is a device exception that is rethrown instead of a ztrap/etrap error. Also assert that
				 * the rethrow will not use IO exception again (thereby ensuring error processing will
				 * eventually terminate instead of indefinitely recursing).
				 */
				fp->flags |= (SFF_DEV_ACT_ERR | SFF_ETRAP_ERR);
				assert(NULL == active_device);	/* mdb_condition_handler should have reset it */
				break;
			} else if ((ERR_ERRWZBRK == errmsg) || (ERR_ERRWEXC == errmsg))
			{	/* For typical exceptions in ZBREAK and ZSTEP, get back to direct mode */
				dm_setup();
				break;
			} else
			{	/* The only known way to be here is if the command is a command given in direct mode as
				 * mdb_condition_handler won't drive an error handler in that case which would be caught in
				 * one of the above conditions. Not breaking out of the loop here means the frame will just
				 * unwind and we'll break on the direct mode frame which will be redriven. If the parent frame
				 * is not a direct mode frame, we'll assert in debug or break in pro and just continue.
				 * to direct mode.
				 */
				assert(fp->flags && (SFF_INDCE));
				if (!fp->old_frame_pointer || !(fp->old_frame_pointer->type & SFT_DM))
				{
					assert(FALSE);
					break;
				}
			}
		}
		if (fp->type)
		{
			SET_ERR_CODE(fp, errmsg);
		}
		/* If this frame is indicated for cache cleanup, do that cleanup
		 * now before we get rid of the pointers used by that cleanup.
		 */
		IF_INDR_FRAME_CLEANUP_CACHE_ENTRY_AND_UNMARK(fp);
		fp->mpc = CODE_ADDRESS(pseudo_ret);
		fp->ctxt = GTM_CONTEXT(pseudo_ret);
		fp->flags &= SFF_IMPLTSTART_CALLD_OFF;	/* Frame enterable now with mpc reset */
		GTMTRIG_ONLY(DBGTRIGR((stderr, "trans_code_cleanup: turning off SFF_IMPLTSTART_CALLD in frame 0x"lvaddr"\n",
				       frame_pointer)));
	}
	TREF(transform) = TRUE;
	if (0 != errmsg)
		dec_err(VARLSTCNT(1) errmsg);
}
Example #10
0
/* Restore given local variable from supplied TP restore entry into given symval. Note lvscan_anchor will only be non-NULL
 * for the final level we are restoring (but not unwinding). We don't need to restore counters for any vars except the
 * very last level.
 *
 * The return code is only used when unrolling the M stack runs into a trigger base frame which must be unrolled
 * by gtm_trigger. A non-zero return code signals to tp_unwind() that it needs to rethrow the tprestart error.
 */
int tp_unwind_restlv(lv_val *curr_lv, lv_val *save_lv, tp_var *restore_ent, lvscan_blk **lvscan_anchor, int *tprestart_rc)
{
	ht_ent_mname	*tabent;
	lv_val		*inuse_lv;
	int		elemindx;
	mv_stent	*mvc;
	lvscan_blk	*lvscan, *newlvscan;
	lvTree		*lvt_child;
	boolean_t	var_cloned;

	assert(curr_lv);
	assert(LV_IS_BASE_VAR(curr_lv));
	assert(curr_lv->tp_var);
	DBGRFCT((stderr, "\ntp_unwind_restlv: Entered for varname: '%.*s' curr_lv: 0x"lvaddr"  save_lv: 0x"lvaddr"\n",
		 restore_ent->key.var_name.len, restore_ent->key.var_name.addr, curr_lv, save_lv));
	DBGRFCT((stderr, "tp_unwind_restlv: tp_pointer/current: fp: 0x"lvaddr"/0x"lvaddr" mvc: 0x"lvaddr"/0x"lvaddr
		 " symval: 0x"lvaddr"/0x"lvaddr"\n",
		 tp_pointer->fp, frame_pointer, tp_pointer->mvc, mv_chain, tp_pointer->sym, curr_symval));

	/* First get the stack in the position where we can actually process this entry. Need to make sure we are processing
	 * the symbol table we need to be processing so unwind enough stuff to get there.
	 */
	if (curr_symval != tp_pointer->sym)
	{	/* Unwind as many stackframes as are necessary up to the max */
		while(curr_symval != tp_pointer->sym && frame_pointer < tp_pointer->fp)
		{
#			ifdef GTM_TRIGGER
			if (SFT_TRIGR & frame_pointer->type)
			{	/* We have encountered a trigger base frame. We cannot unroll it because there are C frames
				 * associated with it so we must interrupt this tp_restart and return to gtm_trigger() so
				 * it can unroll the base frame and rethrow the error to properly unroll the C stack.
				 */
				*tprestart_rc = ERR_TPRETRY;
				tprestart_state = TPRESTART_STATE_TPUNW;
				DBGTRIGR((stderr, "tp_unwind: Encountered trigger base frame during M-stack unwind - "
					  "rethrowing\n"));
				return -1;
			}
#			endif
			op_unwind();
		}
		if (curr_symval != tp_pointer->sym)
		{	/* Unwind as many mv_stents as are necessary up to the max */
			mvc = mv_chain;
			while(curr_symval != tp_pointer->sym && mvc < tp_pointer->mvc)
			{
				unw_mv_ent(mvc);
				mvc = (mv_stent *)(mvc->mv_st_next + (char *)mvc);
			}
			mv_chain = mvc;
			/* Final check */
			if (curr_symval != tp_pointer->sym)
				GTMASSERT;
		}
	}
	var_cloned = curr_lv->tp_var->var_cloned;
	if (var_cloned)
	{	/* Var/tree has been copied (and modified) -- see about restoring it */
		DBGRFCT((stderr, "\ntp_unwind_restlv: curr_lv was modified and cloned -- needs restoration\n"));
		if (NULL != restore_ent->key.var_name.addr)
		{	/* Restore data into a named variable (hash table entry)
			 * Step 1 -- find its hash table address to see what lv_val is there now.
			 */
			tabent = lookup_hashtab_mname(&((tp_pointer->sym)->h_symtab), &restore_ent->key);
			assert(tabent);
			/* Step 2 -- If lv_val is NOT the same as it was, then we must replace the lv_val
			 * currently in use. Decrement its use count (which will delete it and the tree if
			 * it is no longer used) and replace with desired previous lv_val whose use count
			 * was incremented when it was saved.
			 */
			if (curr_lv != (inuse_lv = (lv_val *)tabent->value))
			{
				if (inuse_lv)
					DECR_BASE_REF_RQ(tabent, inuse_lv, FALSE);
				DBGRFCT((stderr, "tp_unwind: hte 0x"lvaddr" being reset from 0x"lvaddr" to 0x"lvaddr"\n",
					 tabent, tabent->value, curr_lv));
				tabent->value = (void *)curr_lv;
				INCR_TREFCNT(curr_lv);			/* Back in the hash table, bump its reference */
			}
		} /* Else, if restoring orphaned data, just prune the old var and copy in the saved tree (if one existed) */
		/* Step 3 -- We have the correct lv_val in the hash table now but it has the wrong value.
		 * Get rid of its current tree if any.
		 */
		if (lvt_child = LV_GET_CHILD(curr_lv))	/* Note assignment */
		{
			DBGRFCT((stderr, "\ntp_unwind_restlv: Killing children of curr_lv 0x"lvaddr"\n", curr_lv));
			assert((lvTreeNode *)curr_lv == LVT_PARENT(lvt_child));
			LV_CHILD(curr_lv) = NULL;	/* prevent recursion due to alias containers */
			lv_killarray(lvt_child, FALSE);
		}
		/* Step 4:  Copy in the needed fields from the saved flavor lv_val back to curr_lv.
		 * Preserve the ref counts of the current var since the copy's ref counts have not been kept up to date.
		 */
		DBGRFCT((stderr, "\ntp_unwind_restlv: Restoring value of lv 0x"lvaddr" back into lv 0x"lvaddr"\n",
			save_lv, curr_lv));
		/* The following is optimized to do the initialization of just the needed structure members. For that it assumes a
		 * particular "lv_val" structure layout. The assumed layout is asserted so any changes to the layout will
		 * automatically show an issue here and cause the below initialization to be accordingly reworked.
		 */
		assert(0 == OFFSETOF(lv_val, v));
		assert(OFFSETOF(lv_val, v) + SIZEOF(curr_lv->v) == OFFSETOF(lv_val, ptrs));
		assert(OFFSETOF(lv_val, ptrs) + SIZEOF(curr_lv->ptrs) == OFFSETOF(lv_val, stats));
		assert(OFFSETOF(lv_val, stats) + SIZEOF(curr_lv->stats) == OFFSETOF(lv_val, has_aliascont));
		assert(OFFSETOF(lv_val, has_aliascont) + SIZEOF(curr_lv->has_aliascont) == OFFSETOF(lv_val, lvmon_mark));
		assert(OFFSETOF(lv_val, lvmon_mark) + SIZEOF(curr_lv->lvmon_mark) == OFFSETOF(lv_val, tp_var));
		assert(OFFSETOF(lv_val, tp_var) + SIZEOF(curr_lv->tp_var) == SIZEOF(lv_val));
		/* save_lv -> curr_lv Copy begin */
		curr_lv->v = save_lv->v;
		curr_lv->ptrs = save_lv->ptrs;
		assert(0 < curr_lv->stats.trefcnt);	/* No need to copy "stats" as curr_lv is more uptodate */
		assert(0 < curr_lv->stats.crefcnt);
		assert(8 == (OFFSETOF(lv_val, tp_var) - OFFSETOF(lv_val, has_aliascont)));
		/* lv_val->has_aliascont/lvmon_mark all initialized in one shot.
		 * Note: We use "qw_num *" instead of "uint8 *" below because the former works on 32-bit platforms too.
		 * On 64-bit platforms, this is 1 8-byte move instead of 2 4-byte moves (the latter means more instructions).
		 * Even though lvmon_mark is maintained only if DEBUG_ALIAS is #defined, we do the copy here unconditionally
		 * because on 64-bit platforms, a 4-byte copy (of just has_aliascont) is the same as an 8-byte copy
		 * (of has_aliascont and lvmon_mark). On 32-bit platforms, it saves us one copy but we dont worry about it much.
		 */
		GTM64_ONLY(assert(IS_PTR_8BYTE_ALIGNED(&curr_lv->has_aliascont));)
		NON_GTM64_ONLY(assert(IS_PTR_4BYTE_ALIGNED(&curr_lv->has_aliascont));)
Example #11
0
int gtm_trigger(gv_trigger_t *trigdsc, gtm_trigger_parms *trigprm)
{
	mval		*lvvalue;
	lnr_tabent	*lbl_offset_p;
	uchar_ptr_t	transfer_addr;
	lv_val		*lvval;
	mname_entry	*mne_p;
	uint4		*indx_p;
	ht_ent_mname	*tabent;
	boolean_t	added;
	int		clrlen, rc, i, unwinds;
	mval		**lvvalarray;
	mv_stent	*mv_st_ent;
	symval		*new_symval;
	uint4		dollar_tlevel_start;
	stack_frame	*fp, *fpprev;
	DCL_THREADGBL_ACCESS;

	SETUP_THREADGBL_ACCESS;
	assert(!skip_dbtriggers);	/* should not come here if triggers are not supposed to be invoked */
	assert(trigdsc);
	assert(trigprm);
	assert((NULL != trigdsc->rtn_desc.rt_adr) || ((MV_STR & trigdsc->xecute_str.mvtype)
						      && (0 != trigdsc->xecute_str.str.len)
						      && (NULL != trigdsc->xecute_str.str.addr)));
	assert(dollar_tlevel);
	/* Determine if trigger needs to be compiled */
	if (NULL == trigdsc->rtn_desc.rt_adr)
	{	/* No routine hdr addr exists. Need to do compile */
		if (0 != gtm_trigger_complink(trigdsc, TRUE))
		{
			PRN_ERROR;	/* Leave record of what error caused the compilation failure if any */
			rts_error(VARLSTCNT(4) ERR_TRIGCOMPFAIL, 2, trigdsc->rtn_desc.rt_name.len, trigdsc->rtn_desc.rt_name.addr);
		}
	}
	assert(trigdsc->rtn_desc.rt_adr);
	assert(trigdsc->rtn_desc.rt_adr == CURRENT_RHEAD_ADR(trigdsc->rtn_desc.rt_adr));
	/* Setup trigger environment stack frame(s) for execution */
	if (!(frame_pointer->type & SFT_TRIGR))
	{	/* Create new trigger base frame first that back-stops stack unrolling and return to us */
		if (GTM_TRIGGER_DEPTH_MAX < (gtm_trigger_depth + 1))	/* Verify we won't nest too deep */
			rts_error(VARLSTCNT(3) ERR_MAXTRIGNEST, 1, GTM_TRIGGER_DEPTH_MAX);
		DBGTRIGR((stderr, "gtm_trigger: PUSH: frame_pointer 0x%016lx  ctxt value: 0x%016lx\n", frame_pointer, ctxt));
		/* Protect against interrupts while we have only a trigger base frame on the stack */
		DEFER_INTERRUPTS(INTRPT_IN_TRIGGER_NOMANS_LAND);
		/* The current frame invoked a trigger. We cannot return to it for a TP restart or other reason unless
		 * either the total operation (including trigger) succeeds and we unwind normally or unless the mpc is reset
		 * (like what happens in various error or restart conditions) because right now it returns to where a database
		 * command (KILL, SET or ZTRIGGER) was entered. Set flag in the frame to prevent MUM_TSTART unless the frame gets
		 * reset.
		 */
		frame_pointer->flags |= SFF_TRIGR_CALLD;	/* Do not return to this frame via MUM_TSTART */
		DBGTRIGR((stderr, "gtm_trigger: Setting SFF_TRIGR_CALLD in frame 0x"lvaddr"\n", frame_pointer));
		base_frame(trigdsc->rtn_desc.rt_adr);
		/* Finish base frame initialization - reset mpc/context to return to us without unwinding base frame */
		frame_pointer->type |= SFT_TRIGR;
#		if defined(__hpux) && defined(__hppa)
		/* For HPUX-HPPA (PA-RISC), we use longjmp() to return to gtm_trigger() to avoid some some space register
		 * corruption issues. Use call-ins already existing mechanism for doing this. Although we no longer support
		 * HPUX-HPPA for triggers due to some unlocated space register error, this code (effectively always ifdef'd
		 * out) left in in case it gets resurrected in the future (01/2010 SE).
		 */
		frame_pointer->mpc = CODE_ADDRESS(ci_ret_code);
		frame_pointer->ctxt = GTM_CONTEXT(ci_ret_code);
#		else
		frame_pointer->mpc = CODE_ADDRESS(gtm_levl_ret_code);
		frame_pointer->ctxt = GTM_CONTEXT(gtm_levl_ret_code);
#		endif
		/* This base stack frame is also where we save environmental info for all triggers invoked at this stack level.
		 * Subsequent triggers fired at this level in this trigger invocation need only reinitialize a few things but
		 * can avoid "the big save".
		 */
		if (NULL == trigr_symval_list)
		{	/* No available symvals for use with this trigger, create one */
			symbinit();	/* Initialize a symbol table the trigger will use */
			curr_symval->trigr_symval = TRUE;	/* Mark as trigger symval so will be saved not decommissioned */
		} else
		{	/* Trigger symval is available for reuse */
			new_symval = trigr_symval_list;
			assert(new_symval->trigr_symval);
			trigr_symval_list = new_symval->last_tab;		/* dequeue new curr_symval from list */
			REINIT_SYMVAL_BLK(new_symval, curr_symval);
			curr_symval = new_symval;
			PUSH_MV_STENT(MVST_STAB);
			mv_chain->mv_st_cont.mvs_stab = new_symval;		/* So unw_mv_ent() can requeue it for later use */
		}
		/* Push our trigger environment save mv_stent onto the chain */
		PUSH_MV_STENT(MVST_TRIGR);
		mv_st_ent = mv_chain;
		/* Initialize the mv_stent elements processed by stp_gcol which can be called by either op_gvsavtarg() or
		 * by the extnam saving code below. This initialization keeps stp_gcol - should it be called - from attempting
		 * to process unset fields filled with garbage in them as valid mstr address/length pairs.
		 */
		mv_st_ent->mv_st_cont.mvs_trigr.savtarg.str.len = 0;
		mv_st_ent->mv_st_cont.mvs_trigr.savextref.len = 0;
		mv_st_ent->mv_st_cont.mvs_trigr.dollar_etrap_save.str.len = 0;
		mv_st_ent->mv_st_cont.mvs_trigr.dollar_ztrap_save.str.len = 0;
		mv_st_ent->mv_st_cont.mvs_trigr.saved_dollar_truth = dollar_truth;
		op_gvsavtarg(&mv_st_ent->mv_st_cont.mvs_trigr.savtarg);
		if (extnam_str.len)
		{
			ENSURE_STP_FREE_SPACE(extnam_str.len);
			mv_st_ent->mv_st_cont.mvs_trigr.savextref.addr = (char *)stringpool.free;
			memcpy(mv_st_ent->mv_st_cont.mvs_trigr.savextref.addr, extnam_str.addr, extnam_str.len);
			stringpool.free += extnam_str.len;
			assert(stringpool.free <= stringpool.top);
		}
		mv_st_ent->mv_st_cont.mvs_trigr.savextref.len = extnam_str.len;
		mv_st_ent->mv_st_cont.mvs_trigr.ztname_save = dollar_ztname;
		mv_st_ent->mv_st_cont.mvs_trigr.ztdata_save = dollar_ztdata;
		mv_st_ent->mv_st_cont.mvs_trigr.ztoldval_save = dollar_ztoldval;
		mv_st_ent->mv_st_cont.mvs_trigr.ztriggerop_save = dollar_ztriggerop;
		mv_st_ent->mv_st_cont.mvs_trigr.ztupdate_save = dollar_ztupdate;
		mv_st_ent->mv_st_cont.mvs_trigr.ztvalue_save = dollar_ztvalue;
		mv_st_ent->mv_st_cont.mvs_trigr.ztvalue_changed_ptr = ztvalue_changed_ptr;
#		ifdef DEBUG
		/* In a debug process, these fields give clues of what trigger we are working on */
		mv_st_ent->mv_st_cont.mvs_trigr.gtm_trigdsc_last_save = trigdsc;
		mv_st_ent->mv_st_cont.mvs_trigr.gtm_trigprm_last_save = trigprm;
#		endif
		assert(((0 == gtm_trigger_depth) && (ch_at_trigger_init == ctxt->ch))
		       || ((0 < gtm_trigger_depth) && (&mdb_condition_handler == ctxt->ch)));
		mv_st_ent->mv_st_cont.mvs_trigr.ctxt_save = ctxt;
		mv_st_ent->mv_st_cont.mvs_trigr.gtm_trigger_depth_save = gtm_trigger_depth;
		if (0 == gtm_trigger_depth)
		{	/* Only back up $*trap settings when initiating the first trigger level */
			mv_st_ent->mv_st_cont.mvs_trigr.dollar_etrap_save = dollar_etrap;
			mv_st_ent->mv_st_cont.mvs_trigr.dollar_ztrap_save = dollar_ztrap;
			mv_st_ent->mv_st_cont.mvs_trigr.ztrap_explicit_null_save = ztrap_explicit_null;
			dollar_ztrap.str.len = 0;
			ztrap_explicit_null = FALSE;
			if (NULL != gtm_trigger_etrap.str.addr)
				/* An etrap was defined for the trigger environment - Else existing $etrap persists */
				dollar_etrap = gtm_trigger_etrap;
		}
		mv_st_ent->mv_st_cont.mvs_trigr.mumps_status_save = mumps_status;
		mv_st_ent->mv_st_cont.mvs_trigr.run_time_save = run_time;
		/* See if a MERGE launched the trigger. If yes, save some state so ZWRITE, ZSHOW and/or MERGE can be
		 * run in the trigger we dispatch. */
		if ((0 != merge_args) || TREF(in_zwrite))
			PUSH_MVST_MRGZWRSV;
		mumps_status = 0;
		run_time = TRUE;	/* Previous value saved just above restored when frame pops */
	} else
	{	/* Trigger base frame exists so reinitialize the symbol table for new trigger invocation */
		REINIT_SYMVAL_BLK(curr_symval, curr_symval->last_tab);
		/* Locate the MVST_TRIGR mv_stent containing the backed up values. Some of those values need
		 * to be restored so the 2nd trigger has the same environment as the previous trigger at this level
		 */
		for (mv_st_ent = mv_chain;
		     (NULL != mv_st_ent) && (MVST_TRIGR != mv_st_ent->mv_st_type);
		     mv_st_ent = (mv_stent *)(mv_st_ent->mv_st_next + (char *)mv_st_ent))
			;
		assert(NULL != mv_st_ent);
		assert((char *)mv_st_ent < (char *)frame_pointer); /* Ensure mv_stent associated this trigger frame */
		/* Reinit backed up values from the trigger environment backup */
		dollar_truth = mv_st_ent->mv_st_cont.mvs_trigr.saved_dollar_truth;
		op_gvrectarg(&mv_st_ent->mv_st_cont.mvs_trigr.savtarg);
		extnam_str.len = mv_st_ent->mv_st_cont.mvs_trigr.savextref.len;
		if (extnam_str.len)
			memcpy(extnam_str.addr, mv_st_ent->mv_st_cont.mvs_trigr.savextref.addr, extnam_str.len);
		mumps_status = 0;
		assert(run_time);
		/* Note we do not reset the handlers for parallel triggers - set one time only when enter first level
		 * trigger. After that, whatever happens in trigger world, stays in trigger world.
		 */
	}
	assert(frame_pointer->type & SFT_TRIGR);
#	ifdef DEBUG
	gtm_trigdsc_last = trigdsc;
	gtm_trigprm_last = trigprm;
#	endif
	/* Set new value of trigger ISVs. Previous values already saved in trigger base frame */
	dollar_ztname = &trigdsc->rtn_desc.rt_name;
	dollar_ztdata = (mval *)trigprm->ztdata_new;
	dollar_ztoldval = trigprm->ztoldval_new;
	dollar_ztriggerop = (mval *)trigprm->ztriggerop_new;
	dollar_ztupdate = trigprm->ztupdate_new;
	dollar_ztvalue = trigprm->ztvalue_new;
	ztvalue_changed_ptr = &trigprm->ztvalue_changed;
	/* Set values associated with trigger into symbol table */
	lvvalarray = trigprm->lvvalarray;
	for (i = 0, mne_p = trigdsc->lvnamearray, indx_p = trigdsc->lvindexarray;
	     i < trigdsc->numlvsubs; ++i, ++mne_p, ++indx_p)
	{	/* Once thru for each subscript we are to set */
		lvvalue = lvvalarray[*indx_p];			/* Locate mval that contains value */
		assert(NULL != lvvalue);
		assert(MV_DEFINED(lvvalue));			/* No sense in defining the undefined */
		lvval = lv_getslot(curr_symval);		/* Allocate an lvval to put into symbol table */
		LVVAL_INIT(lvval, curr_symval);
		lvval->v = *lvvalue;				/* Copy mval into lvval */
		added = add_hashtab_mname_symval(&curr_symval->h_symtab, mne_p, lvval, &tabent);
		assert(added);
		assert(NULL != tabent);
	}
	/* While the routine header is available in trigdsc, we also need the <null> label address associated with
	 *  the first (and only) line of code.
	 */
	lbl_offset_p = LNRTAB_ADR(trigdsc->rtn_desc.rt_adr);
	transfer_addr = (uchar_ptr_t)LINE_NUMBER_ADDR(trigdsc->rtn_desc.rt_adr, lbl_offset_p);
	/* Create new stack frame for invoked trigger in same fashion as gtm_init_env() creates its 2ndary frame */
#	ifdef HAS_LITERAL_SECT
	new_stack_frame(trigdsc->rtn_desc.rt_adr, (unsigned char *)LINKAGE_ADR(trigdsc->rtn_desc.rt_adr), transfer_addr);
#	else
	/* Any platform that does not follow pv-based linkage model either
	 *	(1) uses the following calculation to determine the context pointer value, or
	 *	(2) doesn't need a context pointer
	 */
	new_stack_frame(trigdsc->rtn_desc.rt_adr, PTEXT_ADR(trigdsc->rtn_desc.rt_adr), transfer_addr);
#	endif
	dollar_tlevel_start = dollar_tlevel;
	assert(gv_target->gd_csa == cs_addrs);
	gv_target->trig_local_tn = local_tn;			/* Record trigger being driven for this global */
	/* Invoke trigger generated code */
	rc = gtm_trigger_invoke();
	if (1 == rc)
	{	/* Normal return code (from dm_start). Check if TP has been unwound or not */
		assert(dollar_tlevel <= dollar_tlevel_start);	/* Bigger would be quite the surprise */
		if (dollar_tlevel < dollar_tlevel_start)
		{	/* Our TP level was unwound during the trigger so throw an error */
			DBGTRIGR((stderr, "gtm_trigger: $TLEVEL less than at start - throwing TRIGTLVLCHNG\n"));
			gtm_trigger_fini(TRUE, FALSE);	/* dump this trigger level */
			rts_error(VARLSTCNT(4) ERR_TRIGTLVLCHNG, 2, trigdsc->rtn_desc.rt_name.len,
				  trigdsc->rtn_desc.rt_name.addr);
		}
		rc = 0;			/* Be polite and return 0 for the (hopefully common) success case */
	} else if (ERR_TPRETRY == rc)
	{	/* We are restarting the entire transaction. There are two possibilities here:
		 * 1) This is a nested trigger level in which case we need to unwind further or
		 *    the outer trigger level was created by M code. If either is true, just
		 *    rethrow the TPRETRY error.
		 * 2) This is the outer trigger and the call to op_tstart() was done by our caller.
		 *    In this case, we just return to our caller with a code signifying they need
		 *    to restart the implied transaction.
		 */
		assert(dollar_tlevel && (tstart_trigger_depth <= gtm_trigger_depth));
		if ((tstart_trigger_depth < gtm_trigger_depth) || !tp_pointer->implicit_tstart || !tp_pointer->implicit_trigger)
		{	/* Unwind a trigger level to restart level or to next trigger boundary */
			gtm_trigger_fini(FALSE, FALSE);	/* Get rid of this trigger level - we won't be returning */
			DBGTRIGR((stderr, "gtm_trigger: dm_start returned rethrow code - rethrowing ERR_TPRETRY\n"));
			INVOKE_RESTART;
		} else
		{	/* It is possible we are restarting a transaction that never got around to creating a base
			 * frame yet the implicit TStart was done. So if there is no trigger base frame, do not
			 * run gtm_trigger_fini() but instead do the one piece of cleanup it does that we still need.
			 */
			assert(donot_INVOKE_MUMTSTART);
			if (SFT_TRIGR & frame_pointer->type)
			{	/* Normal case when TP restart unwinding back to implicit beginning */
				gtm_trigger_fini(FALSE, FALSE);
				DBGTRIGR((stderr, "gtm_trigger: dm_start returned rethrow code - returning to gvcst_<caller>\n"));
			} else
			{       /* Unusual case of trigger that died in no-mans-land before trigger base frame established.
				 * Remove the "do not return to me" flag only on non-error unwinds */
				assert(tp_pointer->implicit_tstart);
				assert(SFF_TRIGR_CALLD & frame_pointer->flags);
				frame_pointer->flags &= SFF_TRIGR_CALLD_OFF;
				DBGTRIGR((stderr, "gtm_trigger: turning off SFF_TRIGR_CALLD (1) in frame 0x"lvaddr"\n",
					  frame_pointer));
				DBGTRIGR((stderr, "gtm_trigger: unwinding no-base-frame trigger for TP restart\n"));
			}
		}
		/* Fall out and return ERR_TPRETRY to caller */
	} else if (0 == rc)
		/* We should never get a return code of 0. This would be out-of-design and a signal that something
		 * is quite broken. We cannot "rethrow" outside the trigger because it was not initially an error so
		 * mdb_condition_handler would have no record of it (rethrown errors must have originally occurred in
		 * or to be RE-thrown) and assert fail at best.
		 */
		GTMASSERT;
	else
	{	/* We have an unexpected return code due to some error during execution of the trigger that tripped
		 * gtm_trigger's safety handler (i.e. an error occurred in mdb_condition_handler() established by
		 * dm_start(). Since we are going to unwind the trigger frame and rethrow the error, we also have
		 * to unwind all the stack frames on top of the trigger frame. Figure out how many frames that is,
		 * unwind them all plus the trigger base frame before rethrowing the error.
		 */
		for (unwinds = 0, fp = frame_pointer; (NULL != fp) && !(SFT_TRIGR & fp->type); fp = fp->old_frame_pointer)
			unwinds++;
		assert((NULL != fp) && (SFT_TRIGR & fp->type));
		GOFRAMES(unwinds, TRUE, FALSE);
		assert((NULL != frame_pointer) && !(SFT_TRIGR & frame_pointer->type));
		DBGTRIGR((stderr, "gtm_trigger: Unsupported return code (%d) - unwound %d frames and now rethrowing error\n",
			  rc, unwinds));
		rts_error(VARLSTCNT(1) ERR_REPEATERROR);
	}
	return rc;
}
Example #12
0
int gtm_trigger_complink(gv_trigger_t *trigdsc, boolean_t dolink)
{
	char		rtnname[GTM_PATH_MAX + 1], rtnname_template[GTM_PATH_MAX + 1];
	char		objname[GTM_PATH_MAX + 1];
	char		zcomp_parms[(GTM_PATH_MAX * 2) + SIZEOF(mident_fixed) + SIZEOF(OBJECT_PARM) + SIZEOF(NAMEOFRTN_PARM)];
	mstr		save_zsource;
	int		rtnfd, rc, lenrtnname, lenobjname, len, alphnum_len, retry, save_errno;
	char		*mident_suffix_p1, *mident_suffix_p2, *mident_suffix_top, *namesub1, *namesub2, *zcomp_parms_ptr;
	mval		zlfile, zcompprm;
	DCL_THREADGBL_ACCESS;

	SETUP_THREADGBL_ACCESS;
	DBGTRIGR_ONLY(memcpy(rtnname, trigdsc->rtn_desc.rt_name.addr, trigdsc->rtn_desc.rt_name.len));
	DBGTRIGR_ONLY(rtnname[trigdsc->rtn_desc.rt_name.len] = 0);
	DBGTRIGR((stderr, "gtm_trigger_complink: (Re)compiling trigger %s\n", rtnname));
	ESTABLISH_RET(gtm_trigger_complink_ch, ((0 == error_condition) ? TREF(dollar_zcstatus) : error_condition ));
	 /* Verify there are 2 available chars for uniqueness */
	assert((MAX_MIDENT_LEN - TRIGGER_NAME_RESERVED_SPACE) >= (trigdsc->rtn_desc.rt_name.len));
	assert(NULL == trigdsc->rtn_desc.rt_adr);
	gtm_trigger_comp_prev_run_time = run_time;
	run_time = TRUE;	/* Required by compiler */
	/* Verify the routine name set by MUPIP TRIGGER and read by gvtr_db_read_hasht() is not in use */
	if (NULL != find_rtn_hdr(&trigdsc->rtn_desc.rt_name))
	{	/* Ooops .. need name to be more unique.. */
		/* Though variable definitions are conventionally done at the function entry, the reason alphanumeric_table
		 * definition is done here is to minimize the time taken to initialize the below table in the most common case
		 * (i.e. no trigger name collisions).
		 */
		char 		alphanumeric_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
							'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
							'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
							't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
							'8', '9', '\0'};
		alphnum_len = STR_LIT_LEN(alphanumeric_table);
		namesub1 = trigdsc->rtn_desc.rt_name.addr + trigdsc->rtn_desc.rt_name.len++;
		/* If WBTEST_HELPOUT_TRIGNAMEUNIQ is defined, set alphnum_len to 1. This way, we make the maximum
		 * possible combinations for the uniqe trigger names to be 3 which is significantly lesser than
		 * the actual number of combinations (62x62 = 3844). For eg., if ^a is a global having triggers defined
		 * in 4 global directories, then the possible unique trigger names are a#1# ; a#1#A ; a#1#AA.
		 */
		GTM_WHITE_BOX_TEST(WBTEST_HELPOUT_TRIGNAMEUNIQ, alphnum_len, 1);
		mident_suffix_top = (char *)alphanumeric_table + alphnum_len;
		/* Phase 1. See if any single character can add uniqueness */
		for (mident_suffix_p1 = (char *)alphanumeric_table; mident_suffix_p1 < mident_suffix_top; mident_suffix_p1++)
		{
			*namesub1 = *mident_suffix_p1;
			if (NULL == find_rtn_hdr(&trigdsc->rtn_desc.rt_name))
				break;
		}
		if (mident_suffix_p1 == mident_suffix_top)
		{	/* Phase 2. Phase 1 could not find uniqueness .. Find it with 2 char variations */
			namesub2 = trigdsc->rtn_desc.rt_name.addr + trigdsc->rtn_desc.rt_name.len++;
			for (mident_suffix_p1 = (char *)alphanumeric_table; mident_suffix_p1 < mident_suffix_top;
			     mident_suffix_p1++)
			{	/* First char loop */
				for (mident_suffix_p2 = (char *)alphanumeric_table; mident_suffix_p2 < mident_suffix_top;
				     mident_suffix_p2++)
				{	/* 2nd char loop */
					*namesub1 = *mident_suffix_p1;
					*namesub2 = *mident_suffix_p2;
					if (NULL == find_rtn_hdr(&trigdsc->rtn_desc.rt_name))
					{
						mident_suffix_p1 = mident_suffix_top + 1;	/* Break out of both loops */
						break;
					}
				}
			}
			if (mident_suffix_p1 == mident_suffix_top)
			{	/* Phase 3: Punt */
				assert(WBTEST_HELPOUT_TRIGNAMEUNIQ == gtm_white_box_test_case_number);
				rts_error(VARLSTCNT(5) ERR_TRIGNAMEUNIQ, 3, trigdsc->rtn_desc.rt_name.len - 2,
					  trigdsc->rtn_desc.rt_name.addr, alphnum_len * alphnum_len);
			}
		}
	}
	/* Write trigger execute string out to temporary file and compile it */
	assert(MAX_XECUTE_LEN >= trigdsc->xecute_str.str.len);
	rc = SNPRINTF(rtnname_template, GTM_PATH_MAX, "%s/trgtmpXXXXXX", DEFAULT_GTM_TMP);
	assert(0 < rc);					/* Note rc is return code aka length - we expect a non-zero length */
	assert(GTM_PATH_MAX >= rc);
	/* The mkstemp() routine is known to bogus-fail for no apparent reason at all especially on AIX 6.1. In the event
	 * this shortcoming plagues other platforms as well, we add a low-cost retry wrapper.
	 */
	retry = MAX_MKSTEMP_RETRIES;
	do
	{
		strcpy(rtnname, rtnname_template);
		rtnfd = mkstemp(rtnname);
	} while ((-1 == rtnfd) && (EEXIST == errno) && (0 < --retry));
	if (-1 == rtnfd)
	{
		save_errno = errno;
		assert(FALSE);
		rts_error(VARLSTCNT(12) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("mkstemp()"), CALLFROM,
			  ERR_TEXT, 2, RTS_ERROR_TEXT(rtnname), save_errno);
	}
	assert(0 < rtnfd);	/* Verify file descriptor */
	rc = 0;
#	ifdef GEN_TRIGCOMPFAIL_ERROR
	{	/* Used ONLY to generate an error in a trigger compile by adding some junk in a previous line */
		DOWRITERC(rtnfd, ERROR_CAUSING_JUNK, strlen(ERROR_CAUSING_JUNK), rc); /* BYPASSOK */
		if (0 != rc)
		{
			UNLINK(rtnname);
			rts_error(VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("write()"), CALLFROM, rc);
		}
	}
#	endif
	DOWRITERC(rtnfd, trigdsc->xecute_str.str.addr, trigdsc->xecute_str.str.len, rc);
	if (0 != rc)
	{
		UNLINK(rtnname);
		rts_error(VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("write()"), CALLFROM, rc);
	}
	if (NULL == memchr(trigdsc->xecute_str.str.addr, '\n', trigdsc->xecute_str.str.len))
	{
		DOWRITERC(rtnfd, NEWLINE, strlen(NEWLINE), rc);			/* BYPASSOK */
		if (0 != rc)
		{
			UNLINK(rtnname);
			rts_error(VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("write()"), CALLFROM, rc);
		}
	}
	CLOSEFILE(rtnfd, rc);
	if (0 != rc)
	{
		UNLINK(rtnname);
		rts_error(VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("close()"), CALLFROM, rc);
	}
	assert(MAX_MIDENT_LEN > trigdsc->rtn_desc.rt_name.len);
	zcomp_parms_ptr = zcomp_parms;
	lenrtnname = STRLEN(rtnname);
	MEMCPY_LIT(zcomp_parms_ptr, NAMEOFRTN_PARM);
	zcomp_parms_ptr += STRLEN(NAMEOFRTN_PARM);
	memcpy(zcomp_parms_ptr, trigdsc->rtn_desc.rt_name.addr, trigdsc->rtn_desc.rt_name.len);
	zcomp_parms_ptr += trigdsc->rtn_desc.rt_name.len;
	MEMCPY_LIT(zcomp_parms_ptr, OBJECT_PARM);
	zcomp_parms_ptr += STRLEN(OBJECT_PARM);
	strcpy(objname, rtnname);		/* Make copy of rtnname to become object name */
	strcat(objname, OBJECT_FTYPE);		/* Turn into object file reference */
	lenobjname = lenrtnname + STRLEN(OBJECT_FTYPE);
	memcpy(zcomp_parms_ptr, objname, lenobjname);
	zcomp_parms_ptr += lenobjname;
	*zcomp_parms_ptr++ = ' ';
	memcpy(zcomp_parms_ptr, rtnname, lenrtnname);
	zcomp_parms_ptr += lenrtnname;
	*zcomp_parms_ptr = '\0';		/* Null tail */
	len = INTCAST(zcomp_parms_ptr - zcomp_parms);
	assert((SIZEOF(zcomp_parms) - 1) > len);	/* Verify no overflow */
	zcompprm.mvtype = MV_STR;
	zcompprm.str.addr = zcomp_parms;
	zcompprm.str.len = len;
	/* Backup dollar_zsource so trigger doesn't show */
	PUSH_MV_STENT(MVST_MSAV);
	mv_chain->mv_st_cont.mvs_msav.v = dollar_zsource;
	mv_chain->mv_st_cont.mvs_msav.addr = &dollar_zsource;
	TREF(trigger_compile) = TRUE;		/* Set flag so compiler knows this is a special trigger compile */
	op_zcompile(&zcompprm, FALSE);	/* Compile but don't require a .m file extension */
	TREF(trigger_compile) = FALSE;	/* compile_source_file() establishes handler so always returns */
	if (0 != TREF(dollar_zcstatus))
	{	/* Someone err'd.. */
		run_time = gtm_trigger_comp_prev_run_time;
		REVERT;
		UNLINK(objname);	/* Remove files before return error */
		UNLINK(rtnname);
		return ERR_TRIGCOMPFAIL;
	}
	if (dolink)
	{	/* Link is optional as MUPIP TRIGGER doesn't need link */
		zlfile.mvtype = MV_STR;
		zlfile.str.addr = objname;
		zlfile.str.len = lenobjname;
		/* Specifying literal_null for a second arg (as opposed to NULL or 0) allows us to specify
		 * linking the object file (no compilation or looking for source). The 2nd arg is parms for
		 * recompilation and is non-null in an explicit zlink which we need to emulate.
		 */
#		ifdef GEN_TRIGLINKFAIL_ERROR
		UNLINK(objname);				/* delete object before it can be used */
#		endif
		op_zlink(&zlfile, (mval *)&literal_null);	/* need cast due to "extern const" attributes */
		/* No return here if link fails for some reason */
		trigdsc->rtn_desc.rt_adr = find_rtn_hdr(&trigdsc->rtn_desc.rt_name);
		if (NULL == trigdsc->rtn_desc.rt_adr)
			GTMASSERT;	/* Can't find routine we just put there? Catastrophic if happens */
		/* Replace the randomly generated source name with the constant "GTM Trigger" */
		trigdsc->rtn_desc.rt_adr->src_full_name.addr = GTM_TRIGGER_SOURCE_NAME;
		trigdsc->rtn_desc.rt_adr->src_full_name.len = STRLEN(GTM_TRIGGER_SOURCE_NAME);
		trigdsc->rtn_desc.rt_adr->trigr_handle = trigdsc;       /* Back pointer to trig def */
	}
	if (MVST_MSAV == mv_chain->mv_st_type && &dollar_zsource == mv_chain->mv_st_cont.mvs_msav.addr)
	{       /* Top mv_stent is one we pushed on there - restore dollar_zsource and get rid of it */
		dollar_zsource = mv_chain->mv_st_cont.mvs_msav.v;
		POP_MV_STENT();
	} else
		assert(FALSE); 	/* This mv_stent should be the one we just pushed */
	/* Remove temporary files created */
	UNLINK(objname);	/* Delete the object file first since rtnname is the unique key */
	UNLINK(rtnname);	/* Delete the source file */
	run_time = gtm_trigger_comp_prev_run_time;
	REVERT;
	return 0;
}
/* Workhorse of fetching source for given trigger.
 */
STATICFNDEF void trigger_fill_xecute_buffer_read_trigger_source(gv_trigger_t *trigdsc)
{
	enum cdb_sc		cdb_status;
	int4			index;
	mstr			gbl, xecute_buff;
	mval			trig_index;
	sgmnt_addrs		*csa;
	sgmnt_data_ptr_t	csd;
	gvt_trigger_t		*gvt_trigger;
	gv_namehead		*gvt;
	gv_namehead		*save_gv_target;
	gd_region		*save_gv_cur_region;
	sgm_info		*save_sgm_info_ptr;
	gv_key			save_currkey[DBKEYALLOC(MAX_KEY_SZ)];

	assert(0 < dollar_tlevel);
	assert(NULL != trigdsc);
	SAVE_TRIGGER_REGION_INFO(save_currkey);

	gvt_trigger = trigdsc->gvt_trigger;			/* We now know our base block now */
	index = trigdsc - gvt_trigger->gv_trig_array + 1;	/* We now know our trigger index value */
	i2mval(&trig_index, index);
	DBGTRIGR((stderr, "trigger_fill_xecute_buffer_read_trigger_source: entry $tlevel:%d\tindex:%d of %d\n",
				dollar_tlevel, index, gvt_trigger->num_gv_triggers));
	gvt = gv_target = gvt_trigger->gv_target;		/* gv_target contains global name */
	gbl.addr = gvt->gvname.var_name.addr;
	gbl.len = gvt->gvname.var_name.len;
	/* Our situation is that while our desired gv_target has csa information, we don't know specifically
	 * which global directory was in use so we can't run gv_bind_name() lest we find the given global
	 * name in the wrong global directory thus running the wrong triggers. But we know this target is
	 * properly formed since it had to be when it was recorded when the triggers were loaded. Because of
	 * that, we can get the correct csa and gv_target and csa-region will point us to a region that will
	 * work even if it isn't exactly the one we used to get to this trigger.
	 */
	TP_CHANGE_REG_IF_NEEDED(gvt->gd_csa->region);
	csa = cs_addrs;
	csd = csa->hdr;
	assert(csd == cs_data);
	tp_set_sgm();
	/* See if we need to reload our triggers */
	if ((csa->db_trigger_cycle != gvt->db_trigger_cycle)
	    || (csa->db_dztrigger_cycle && (gvt->db_dztrigger_cycle != csa->db_dztrigger_cycle)))
	{       /* The process' view of the triggers could be potentially stale. Restart to be safe.
		 * Triggers can be invoked only by GT.M and Update process. Out of these, we expect only
		 * GT.M to see restarts due to concurrent trigger changes. Update process is the only
		 * updater on the secondary so we dont expect it to see any concurrent trigger changes
		 * Assert accordingly.
		 */
		DBGTRIGR((stderr, "trigger_fill_xecute_buffer_read_trigger_source: stale trigger view\n"));
		assert(CDB_STAGNATE > t_tries);
		assert(IS_GTM_IMAGE);
		t_retry(cdb_sc_triggermod);
	}
	SET_GVTARGET_TO_HASHT_GBL(csa);
	INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED;
	assert(0 == trigdsc->xecute_str.str.len);	/* Make sure not replacing/losing a buffer */
	xecute_buff.addr = trigger_gbl_fill_xecute_buffer(gbl.addr, gbl.len, &trig_index, NULL, (int4 *)&xecute_buff.len);
	trigdsc->xecute_str.str = xecute_buff;
	/* Restore gv_target/gv_currkey which need to be kept in sync */
	RESTORE_TRIGGER_REGION_INFO(save_currkey);
	return;
}