int gtm_init() { rhdtyp *base_addr; unsigned char *transfer_addr; error_def(ERR_CITPNESTED); error_def(ERR_CIMAXLEVELS); if (!gtm_startup_active) { /* call-in invoked from C as base. GT.M hasn't been started up yet. */ image_type = GTM_IMAGE; gtm_env_init(); /* read in all environment variables */ err_init(stop_image_conditional_core); cli_lex_setup(0, NULL); /* Initialize msp to the maximum so if errors occur during GT.M startup below, * the unwind logic in gtmci_ch() will get rid of the whole stack. */ msp = (unsigned char*)-1; } ESTABLISH_RET(gtmci_ch, mumps_status); if (!gtm_startup_active) { /* GT.M is not active yet. Create GT.M startup environment */ invocation_mode = MUMPS_CALLIN; init_gtm(); gtm_savetraps(); /* nullify default $ZTRAP handling */ assert(gtm_startup_active); assert(frame_pointer->flags & SFF_CI); nested_level = 1; } else if (!(frame_pointer->flags & SFF_CI)) { /* Nested call-in: setup a new CI environment (SFF_CI frame on top of base-frame) */ /* Mark the beginning of the new stack so that initialization errors in * call-in frame do not unwind entries of the previous stack (see gtmci_ch).*/ fgncal_stack = msp; /* Report if condition handlers stack may overrun during this callin level. * Every underlying level can not have more than 2 active condition handlers, * plus extra MAX_HANDLERS are reserved for this level. */ if (chnd_end - ctxt <= MAX_HANDLERS) rts_error(VARLSTCNT(3) ERR_CIMAXLEVELS, 1, nested_level); /* Disallow call-ins within a TP boundary since TP restarts are not supported * currently across nested call-ins. When we implement TP restarts across call-ins, * this error needs be changed to a Warning or Notification */ if (0 < dollar_tlevel) rts_error(VARLSTCNT(1) ERR_CITPNESTED); base_addr = make_cimode(); transfer_addr = PTEXT_ADR(base_addr); gtm_init_env(base_addr, transfer_addr); SET_CI_ENV(ci_ret_code_exit); gtmci_isv_save(); nested_level++; } /* Now that GT.M is initialized. Mark the new stack pointer (msp) so that errors * while executing an M routine do not unwind stack below this mark. It important that * the call-in frames (SFF_CI), that hold nesting information (eg. $ECODE/$STACK data * of the previous stack), are kept from being unwound. */ fgncal_stack = msp; REVERT; return 0; }
void gtm_init_env(rhdtyp *base_addr, unsigned char *transfer_addr) { assert(CURRENT_RHEAD_ADR(base_addr) == base_addr); base_frame(base_addr); #ifdef HAS_LITERAL_SECT new_stack_frame(base_addr, (unsigned char *)LINKAGE_ADR(base_addr), 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(base_addr, PTEXT_ADR(base_addr), transfer_addr); #endif }
void jobchild_init(void) { unsigned int status; job_params_type jparms; /* Transfer data */ unsigned char *transfer_addr; rhdtyp *base_addr; unsigned short i, arg_len; char run_file_name[FILE_NAME_SIZE + 2], *c; gcall_args job_arglist; mval job_args[MAX_ACTUALS]; error_def (ERR_RUNPARAMERR); static char interactive_mode_buf[] = "INTERACTIVE"; static char other_mode_buf[] = "OTHER"; error_def(ERR_TEXT); ESTABLISH(job_init_ch); /* * Check if environment variable ppid - job parent pid * exists. If it does not, we are a regular gtm process, * else, we are a child process of a job command. */ if ((c = GETENV(CHILD_FLAG_ENV)) && strlen(c)) { /* * We are a Jobbed process. * Get Job parameters and set up environment * to run the Job command */ /* Clear the environment variable so that subsequent child * mumps processes can start normal initialization. */ if (PUTENV(CLEAR_CHILD_FLAG_ENV)) { util_out_print("Unable to clear gtmj0 process !UL exiting.", TRUE, process_id); rts_error(VARLSTCNT(1) errno); } /* read parameters into parameter structure */ ojchildparms(&jparms, &job_arglist, job_args); /* Execute the command to be run before executing the actual M routine */ if (jparms.startup.len) SYSTEM(jparms.startup.addr); /* Set up job's input, output and error files. Redirect them, if necessary. */ /* It is needed since the middle process would not have always done this(under jobpid == TRUE cases) */ if (!(status = ojchildioset(&jparms))) rts_error(VARLSTCNT(4) ERR_TEXT, 2, LEN_AND_LIT("Failed to set STDIN/OUT/ERR for the job")); job_addr(&jparms.routine, &jparms.label, jparms.offset, (char **)&base_addr, (char **)&transfer_addr); /* Set process priority */ if (jparms.baspri) nice((int) jparms.baspri); /* Set up $ZMODE to "OTHER" */ dollar_zmode.mvtype = MV_STR; dollar_zmode.str.addr = &other_mode_buf[0]; dollar_zmode.str.len = sizeof(other_mode_buf) -1; } else { /* If we are not a child, setup a dummy mumps routine */ if (MUMPS_RUN == invocation_mode) { mstr routine, label; int offset; arg_len = FILE_NAME_SIZE; if (!cli_get_str("INFILE", run_file_name, &arg_len)) rts_error(VARLSTCNT(1) ERR_RUNPARAMERR); lref_parse((uchar_ptr_t)run_file_name, &routine, &label, &offset); job_addr(&routine, &label, offset, (char **)&base_addr, (char **)&transfer_addr); } else if (MUMPS_CALLIN & invocation_mode) /* call-in mode */ { base_addr = make_cimode(); transfer_addr = PTEXT_ADR(base_addr); } else /* direct mode */ { base_addr = make_dmode(); transfer_addr = PTEXT_ADR(base_addr); } job_arglist.callargs = 0; /* Set up $ZMODE to "INTERACTIVE" */ dollar_zmode.mvtype = MV_STR; dollar_zmode.str.addr = &interactive_mode_buf[0]; dollar_zmode.str.len = sizeof(interactive_mode_buf) -1; } gtm_init_env(base_addr, transfer_addr); if (MUMPS_CALLIN & invocation_mode) { SET_CI_ENV(ci_ret_code_exit); } if (job_arglist.callargs) { callg((int(*)())push_parm, &job_arglist); frame_pointer->type |= SFT_EXTFUN; } REVERT; }
void urx_remove(rhdtyp *rtnhdr) { urx_rtnref *rtn, *rtnprev; urx_labref *lab, *labprev; urx_addr *addr, *addrprev, *savaddr; unsigned char *regstart, *regend; int deletes; DEBUG_ONLY(deletes = 0); #ifdef USHBIN_SUPPORTED /* All unresolved addresses will point into the linkage section */ regstart = (unsigned char *)rtnhdr->linkage_adr; regend = regstart + (SIZEOF(lnk_tabent) * rtnhdr->linkage_len); #else /* All unresolved addresses will point into the code section */ regstart = PTEXT_ADR(rtnhdr); regend = PTEXT_END_ADR(rtnhdr); #endif rtnprev = &urx_anchor; rtn = rtnprev->next; while (rtn) { /* For each unresolved routine.. */ addrprev = NULL; addr = rtn->addr; while (addr) { /* Run list of resolve addrs for this routine */ if (IS_IN_RANGE(regstart, regend, addr->addr)) { /* We will be deleting an element so addrprev will not be changing */ if (NULL == addrprev) rtn->addr = addr->next; /* First element being removed */ else addrprev->next = addr->next; savaddr = addr->next; free(addr); addr = savaddr; DEBUG_ONLY(++deletes); continue; } addrprev = addr; addr = addr->next; } /* Note that the structure of the urx_labref and urx_rtnref is critical here. The urx_rtnref serves as an anchor for the urx_labref chain by virtue of urx_rtnref's "lab" pointer being at the same offset as urx_labref's "next" pointer. */ labprev = (urx_labref *)rtn; lab = rtn->lab; while (lab) { addrprev = NULL; addr = lab->addr; while (addr) { if (IS_IN_RANGE(regstart, regend, addr->addr)) { /* We will be deleting an element so addrprev will not be changing */ if (NULL == addrprev) lab->addr = addr->next; /* First element being removed */ else addrprev->next = addr->next; savaddr = addr->next; free(addr); addr = savaddr; DEBUG_ONLY(++deletes); continue; } addrprev = addr; addr = addr->next; } if (NULL == lab->addr) { /* No references to this label left .. remove from unresolved chain */ labprev->next = lab->next; free(lab); lab = labprev->next; DEBUG_ONLY(++deletes); continue; } labprev = lab; lab = lab->next; } /* Note that it is possible to have a routine on the unresolved chain with no addr chain of unresolves for it yet there are labels unresolved. This would be the case if a routine contained a call to a non-existent label. It is not an error until/unless the call call is executed. The reverse is also true, it is possible to have an unresolved addr chain for the routine with no labels. This occurs when a call using indirection such as DO @LBL^RTN. In this case, there will be no unresolved label but there will be an unresolved routine. */ if (NULL == rtn->addr && NULL == rtn->lab) { /* This node has no reason to keep living */ rtnprev->next = rtn->next; free(rtn); rtn = rtnprev->next; DEBUG_ONLY(++deletes); continue; } rtnprev = rtn; rtn = rtn->next; } #ifdef DEBUG_URX PRINTF("urx_remove: Deleted %d entries\n", deletes); #endif }
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; }