/* *********************************************************** * * * Copyright, (C) Honeywell Information Systems Inc., 1982 * * * * Copyright (c) 1972 by Massachusetts Institute of * * Technology and Honeywell Information Systems, Inc. * * * *********************************************************** */ db_break: proc; /* This procedure is part of the debug package. All of the mechanism required to * handle break points is contained in this procedure. This procedure alone, * maintains the user's break segment found in his home directory. This * procedure, along with db_break_map, maintains the break map needed in each * segment which has breaks. Note, what this procedure does NOT know about * is the semantics of the debug language. * Rewritten Dec 72 for the 6180 by Bill Silver. * * db_break has the following entry points: * * check_break: Returns info about a break, especially whether or not a * condition has been met. * * global: Performs a specified action on all the breaks known to the * user in all segments. * * print_bseg: Prints the number of breaks in all the segments which the user * has breaks. Cleans up this break segment. * * print_default: Prints the path name of the current default segment. * * restart: Restarts a break - executes the instruction replaced by * the mme2. * * set_break: Sets up a break. * * set_default: Establishes a segment as the default segment. * * set_skips: Sets a number of skips in a specified break. * * single: Performs a specified action on ONE specified break. * * sub_global: Performs a specified action on all the breaks in the * default segment. */ /* PARAMETER DATA */ dcl arg_action_code fixed bin, /* (INPUT) Implies action to perform on break. * A_C_T_I_O_N_ C_O_D_E_ * list 1 * reset 2 * disable 3 * enable 4 * set command 5 * set condition 6 */ arg_break_num fixed bin, /* (INPUT) The number of a break in the * default segment. */ arg_break_ptr ptr, /* (INPUT) A pointer to the word where * the break will be set. */ arg_cond_flag fixed bin, /* (OUTPUT) A flag indicating whether or not the * condition of a conditional break has been met. * 0 => No condition or condition not met. * 1 => Condition not met - skip break. */ arg_comd_len fixed bin, /* (OUTPUT) The length of the command line * found in a break. 0 => no command. */ arg_comd_ptr ptr, /* (OUTPUT) A pointer to the command line * found in a break. */ arg_line char (236), /* (INPUT) A string that can be either a * command line or condition data. */ arg_line_len fixed bin, /* (INPUT) The length of the above string. */ arg_line_no fixed bin, /* (OUTPUT) Source line number. */ arg_num_skips fixed bin, /* (INPUT/OUTPUT) The number of skips * ( to set/that are set ) in a break. */ arg_print_mode fixed bin, /* (INPUT) 0 => SHORT, 1 => LONG. */ arg_scu_ptr ptr, /* (INPUT) Points to the SCU data * generated by a mme2 fault. */ arg_seg_ptr ptr, /* (INPUT) Pointer to a segment that is * to become the default segment. */ arg_snt_ptr ptr, /* (INPUT) Pointer to debug's arg_snt data. */ arg_type fixed bin; /* (INPUT) The type of break. * 0 => regular, * 1 => temporary, * 2 => disabled. */ /* INTERNAL STATIC DATA */ /* Note, since the following variables must be preserved from one call to another * they are static. Any procedures which need this information therefore must be * part of db_break or called by db_break. */ /* Pointer to the user's break segment. */ dcl break_seg_ptr ptr internal static init (null); /* The following variables are used to define the default segment. * def_seg is the number of the break segment array entry which corresponds * to the default segment. def_break_map_ptr points to the break map in * the default segment. */ dcl def_segx fixed bin internal static init (0), def_break_map_ptr ptr internal static; /* Below is the mme2 instruction that is put into a break word. */ dcl 1 mme2 aligned internal static, 2 break_num fixed bin (17) unaligned, 2 op_code bit (36) unaligned init ("000000100000000000"b); /* An array used to print the type of a break. */ dcl break_type_name (0:3) char (24) internal static aligned init ("Break ", "Temporary break", "Disabled break", "Temporary disabled break"); %include db_ext_stat_; /* AUTOMATIC DATA */ dcl action_code fixed bin, /* These variables are used to copy arguments. */ break_num fixed bin, break_ptr ptr, line_len fixed bin, print_mode fixed bin, snt_ptr ptr, type fixed bin; dcl break_word_ptr ptr, /* Pointer to the word where the break is. */ break_offset fixed bin (18); /* Word offset of the break word. */ dcl dir_name char (168), /* Directory name of a segment. */ ent_name char (32); /* Entry name of a segment. */ /* This is the array of data returned by the calls to hcs_$status_long. */ dcl 1 branch aligned, (2 type bit (2), 2 nnames bit (16), 2 nrp bit (18), 2 dtm bit (36), 2 dtu bit (36), 2 mode bit (5), 2 padding bit (13), 2 records bit (18), 2 dtd bit (36), 2 dtem bit (36), 2 acct bit (36), 2 curlen bit (12), 2 bitcnt bit (24), 2 did bit (4), 2 mdid bit (4), 2 copysw bit (1), 2 pad2 bit (9), 2 rbs (0:2) bit (6), 2 uid bit (36)) unaligned; dcl action_flag bit (1), /* Used to denote if an action has been * performed for any breaks. */ cond_flag fixed bin, /* Used to determine condition state. */ delete_seg_entry_flag bit (1), /* Denotes whether a segment entry in the break * segment array has been deleted. */ inst_ptr ptr, /* Pointer to instruction after break. */ new_bnum fixed bin, /* Temporary break number. */ seg_ptr ptr, /* Temporary segment pointer. */ segx fixed bin; /* Index into break segment array. */ dcl print_num_breaks char (6), /* A word string for printing, */ source_string char (72) var; /* Used to get info back from other procs. */ dcl bit_count fixed bin (24), /* Returned from hcs_$initiate count. */ code fixed bin (35), /* Error return code. */ (i, j) fixed bin; /* Work variables. */ /* BASED DATA */ /* This is a map of the user's break segment. */ dcl 1 bseg based (break_seg_ptr) aligned, 2 num_segs fixed bin, /* Number of segments in seg array. */ 2 seg (1), /* Array of segment entries. Each entry * corresponds to one segment which should * have a break map. */ 3 uid bit (36), /* UID of segment. This field remains * constant for the life of the entry. */ 3 dir_name char (168) unal, /* Directory name of segment. */ 3 ent_name char (32) unal; /* Entry name of the segment. Note, these * two fields may change since the segment * may be moved, renamed, or referenced * via a link. */ /* This is an overlay of the break segment used to move whole entries around. */ dcl 1 bseg_map based (break_seg_ptr) aligned, 2 num_segs fixed bin, 2 array (1), 3 entry char (204); /* This is a map of the mme2 word that is moved into the break word. */ dcl 1 mme2_map based aligned, 2 break_num fixed bin (17) unaligned, 2 op_code bit (18) unaligned; /* This is used to reference one word. */ dcl based_word bit (36) based aligned; /* EXTERNAL ENTRIES */ dcl (addr, addrel, divide, fixed, index, max, null, ptr, rel, size, substr) builtin; dcl com_err_ external entry options (variable), db_break_map$check external entry (ptr, fixed bin (24), ptr), db_break_map$delete external entry (ptr), db_break_map$get_slots external entry (ptr), db_break_map$init external entry (ptr, fixed bin (24), ptr), db_line_no external entry (ptr, fixed bin (18), fixed bin, fixed bin, fixed bin), db_parse_condition$check external entry (ptr, ptr, ptr, fixed bin), db_parse_condition$print_line external entry (ptr, char (72) var), hcs_$fs_get_path_name external entry (ptr, char (*), fixed bin, char (*), fixed bin (35)), hcs_$initiate_count external entry (char (*), char (*), char (*), fixed bin (24), fixed bin (2), ptr, fixed bin (35)), hcs_$make_seg external entry (char (*), char (*), char (*), fixed bin (5), ptr, fixed bin (35)), hcs_$set_bc_seg external entry (ptr, fixed bin (24), fixed bin (35)), hcs_$status_long external entry (char (*), char (*), fixed bin (1), ptr, ptr, fixed bin (35)), hcs_$truncate_seg external entry (ptr, fixed bin (18), fixed bin (35)), ioa_$ioa_stream external entry options (variable), ioa_$rsnnl external entry options (variable), user_info_ external entry (char (*)), print_text_$real_offset external entry (ptr, char (*) var, fixed bin (18)), user_info_$homedir external entry (char (*)); /* */ %include db_break_map_map; /* */ dcl 1 op_mnemonic_$op_mnemonic (0:1023) ext static aligned, 2 opcode char (6) unal, 2 dtype fixed bin (2) unal, 2 num_desc fixed bin (5) unal, 2 num_words fixed bin (8) unal; /* */ %include db_inst; /* */ %include mc; /* */ print_bseg: entry (arg_print_mode); /* This entry is called to print the contents of the users break segment. It will * print the number of breaks that are set for each segment referenced in the break * segment. It will also clean up the break segment. This involves deleting any * entries which are no longer valid or whose segments have no breaks set. */ print_mode = arg_print_mode; /* Copy argument. */ if break_seg_ptr = null () /* Make sure we have a break segment. */ then call INIT_BREAK_SEG; do segx = 1 to bseg.num_segs; /* Process each entry in break segment. */ call CHECK_SEGMENT; /* This will return the segment's break * map pointer if the segment entry was OK * and wasn't deleted. */ /* Was the segment entry deleted. If so, segx now refers to the last segment entry. * It just replaced the one we were working on. Thus segx must be decremented * so it can reference this same entry again on the next iteration. However, * if this is the last entry in the array, we must not decrement segx because we want * to get out of the loop since bseg.num_segs was also decremented when the * segment entry was deleted. */ if delete_seg_entry_flag /* Was segment entry deleted? */ then do; /* YES, thus there were no breaks. */ if segx <= bseg.num_segs /* But is it last entry. */ then segx = segx -1; /* NO, decrement segment index. */ else; /* YES, lets get out of the loop. */ end; else do; /* No, there are breaks. */ if bmap.num_set = 1 then print_num_breaks = "break "; else print_num_breaks = "breaks"; call ioa_$ioa_stream (debug_output, "^d ^a set in ^a>^a", bmap.num_set, print_num_breaks, bseg.seg (segx).dir_name, bseg.seg (segx).ent_name); end; end; /* We have just processed one segment entry. */ /* The only segment entries left in the break segment array will be those of segments * which have breaks set. If there are no entries left, then there were no breaks set. */ if bseg.num_segs = 0 then call ioa_$ioa_stream (debug_output, "No breaks set."); return; /* */ set_default: entry (arg_seg_ptr); /* This entry will establish the specified segment as the default segment. */ break_ptr = arg_seg_ptr; /* Not really a break pointer. */ call SET_DEFAULTS; return; print_default: entry; /* This entry is called to print the name of the default segment. */ break_num = 1; /* Set dummy break number. */ call CHECK_DEFAULT; call ioa_$ioa_stream (debug_output, "Default segment is ^a>^a", bseg.seg (segx).dir_name, bseg.seg (segx).ent_name); return; /* */ check_break: entry (arg_break_ptr, arg_break_num, arg_snt_ptr, arg_cond_flag, arg_num_skips, arg_comd_len, arg_comd_ptr, arg_line_no); /* This entry returns data about the specified break. Especially important is the * condition flag which tells whether or not a conditional break has been satisfied * 0 => either this is not a conditional break or condition has been satisfied, * 1 => conditional break and condition has not been satisfied. */ break_ptr = arg_break_ptr; /* Copy arguments. */ break_num = arg_break_num; snt_ptr = arg_snt_ptr; call SET_DEFAULTS; /* Establish this segment as the default segment. */ call CHECK_BREAK_NUM; /* Validate the break number. */ /* Now set the condition flag. If the length of the condition data is zero, then we * know that this is not a conditional break and we can set the condition flag to * zero. If this is a conditional break, then we must call out to a procedure that * understands the condition data semantics so it can determaine if the condition * has been met. */ arg_num_skips = break_slot.skip_count; /* no. of times to skip this break */ if arg_num_skips > 0 then do; cond_flag = 0; /* Ignore condition because break will be skiped */ break_slot.skip_count = break_slot.skip_count - 1; end; else if break_slot.cond_len = 0 /* Is this a conditional break? */ then cond_flag = 0; /* NO. */ else call db_parse_condition$check (break_map_ptr, addr (break_slot.cond_data), snt_ptr, cond_flag); arg_cond_flag = cond_flag; /* Return info about break. */ arg_comd_len = break_slot.comd_len; arg_comd_ptr = addr (break_slot.comd_line); arg_line_no = break_slot.line_no; return; /* The end of the check_break entry. */ /* */ set_break: entry (arg_break_ptr, arg_type, arg_snt_ptr, arg_print_mode); /* This entry is called to set a break. Unless it is a disabled break, the word * referenced by break_ptr will be set up to take a fault when executed. */ break_ptr = arg_break_ptr; /* Copy arguments. */ type = arg_type; snt_ptr = arg_snt_ptr; print_mode = arg_print_mode; call SET_DEFAULTS; /* Set up this segment as the default segment. */ /* Now we know that we have pointers to the break map of the default segment and * to the user's break segment. Next, we will look for a free slot in the break map * where we can put this new break. If no slots are available, we will try to allocate * some. */ break_offset = fixed (rel (break_ptr)); /* Get word offset of where the break is to go. * This is used to identify the break. */ new_bnum = -1; /* Initialize the new break number to indicate that * we don't have a slot for the break yet. */ do break_num = 1 to bmap.num_slots; /* Search through all the break slots in * this break map. */ call GET_BREAK_SLOT_PTR; /* Get pointer to this slot. */ if break_slot.type = -1 /* Is this break slot free? */ then if new_bnum = -1 /* YES, but is it the first free slot? */ then new_bnum = break_num; /* YES, this is the slot we will use. */ else; /* NO, this slot is not free. There is a break set in it. See if it is the break * we are trying to set now. If it is a temporary break, we still want to set it. */ else if break_slot.offset = break_offset then do; /* This is the break we are trying to set. */ if (break_slot.type ^= 1) & (break_slot.type ^= 3) then do; /* This is not a temporary break. */ call ioa_$ioa_stream (debug_output, "Break ^d already set at ^p", break_num, break_ptr); return; end; new_bnum = break_num; /* Temp break. Use this slot. Restore inst. */ break_ptr -> based_word = break_slot.old_word; end; end; /* We have just checked one break slot in the map. */ if new_bnum = -1 /* Did we find a free break slot? */ then do; /* No, try to allocate more slots. */ call db_break_map$get_slots (break_map_ptr); if break_map_ptr = null () /* Did we get more slots? */ then do; /* NO. */ call ioa_$ioa_stream (debug_output, "Unable to allocate more break slots in ^a>^a", bseg.seg (segx).dir_name, bseg.seg (segx).ent_name); return; end; new_bnum = bmap.num_set + 1; /* Get number of first new slot. */ end; /* Now we have an index to the break slot we will use for this new break. We * must increment the count of breaks that are set and we must initialize the * break itself. */ break_num = new_bnum; /* Use new break number. */ bmap.num_set = bmap.num_set + 1; /* One more break is being set. */ call GET_BREAK_SLOT_PTR; /* Get pointer to new break slot. */ break_slot.type = type; /* Now fill in the break slot. */ break_slot.offset = break_offset; break_slot.old_word = break_ptr -> based_word; /* Get the line number associated with the location (if possible). */ call db_line_no (snt_ptr, fixed (rel (break_ptr), 18), i, j, break_slot.line_no); /* Get the number of words used by this instruction. If there are more * than one, this is an EIS instruction. */ break_slot.num_words = op_mnemonic_$op_mnemonic (fixed (break_ptr -> instr.opcode, 17)).num_words; /* Initially there are no skips set, there is no command to execute, and there is * no condition set. */ break_slot.reserved (*), break_slot.skip_count, break_slot.comd_len, break_slot.cond_len = 0; call LIST; /* Tell user that break is set. */ if type < 2 /* Is break a disabled type? */ then do; /* No, set break. */ mme2.break_num = break_num; /* Connect mme2 to break slot via break index. */ break_ptr -> mme2_map = mme2; /* Move mme2 into break location. */ end; return; /* This is the end of the set_break entry. */ /* */ set_skips: entry (arg_break_num, arg_num_skips); /* This entry is called to set the skip count in the specified break of the * default segment. */ break_num = arg_break_num; /* Copy argument. */ call CHECK_DEFAULT; /* Make sure default segment set up * and break number valid. */ break_slot.skip_count = arg_num_skips; return; /* */ restart: entry (arg_break_ptr, arg_break_num, arg_num_skips, arg_scu_ptr, arg_print_mode); /* This entry is called to restart a break. To do this we must modify the SCU * data in the stack frame of the debug break handler. When the break handler * returns, the instruction that was replaced by the mme2 will be executed. */ break_ptr = arg_break_ptr; /* Copy some arguments. */ break_num = arg_break_num; scup = arg_scu_ptr; print_mode = arg_print_mode; if break_ptr -> mme2_map.op_code ^= mme2.op_code /* Break has alredy been reset */ then scup -> scu.even_inst = break_ptr -> based_word; else do; /* Break is still set. */ call SET_DEFAULTS; call CHECK_BREAK_NUM; /* Make sure it is still valid. */ scup -> scu.even_inst = break_slot.old_word; if break_slot.type = 1 /* Is this a temporary break? */ then call RESET; /* Temporary break */ else do; /* Regular break */ if arg_num_skips >= 0 then break_slot.skip_count = arg_num_skips - 1; if break_slot.num_words > 1 /* EIS instruction */ & bmap.version = "ver3" then do; inst_ptr = addrel (break_ptr, break_slot.num_words); bmap.eis.mme2_indw = rel (break_ptr); bmap.eis.inst_indw = rel (inst_ptr); bmap.eis.mme2 = break_ptr -> based_word; bmap.eis.inst = inst_ptr -> based_word; break_ptr -> based_word = break_slot.old_word; inst_ptr -> based_word = bmap.eis.tra; end; end; end; scup -> scu.apu.xsf = "0"b; /* These fields must be zero. */ addr (scup -> scu.word3) -> based_word = "0"b; scup -> scu.cu.its = "0"b; /* If a break has just been set in the following word (.ct request or by the < request), the odd instruction will have to be set since it has already been fetched. */ break_ptr = addrel (break_ptr, 1); if break_ptr -> mme2_map.op_code = mme2.op_code then scup -> scu.odd_inst = break_ptr -> based_word; return; /* This is the end of the restart entry. */ /* */ single: entry (arg_break_num, arg_action_code, arg_line_len, arg_line, arg_print_mode); /* This entry is called to perform an action on ONE break that is assumed to * be in the default segment. */ break_num = arg_break_num; /* Copy arguments. */ action_code = arg_action_code; line_len = arg_line_len; print_mode = arg_print_mode; call CHECK_DEFAULT; /* Make sure everything is OK. */ if break_slot.type ^= -1 /* Is break really set? */ then call DO_ACTION; /* YES, go perform action on this break. */ else call ioa_$ioa_stream (debug_output, "Break ^d not set.", break_num); return; /* */ sub_global: entry (arg_action_code, arg_line_len, arg_line, arg_print_mode); /* This entry is called to perform an action on all of the breaks set in the * DEFAULT SEGMENT. */ action_flag = "0"b; /* Initialize flag. */ action_code = arg_action_code; /* Copy arguments. */ line_len = arg_line_len; print_mode = arg_print_mode; break_num = 1; /* Set up dummy break number. */ call CHECK_DEFAULT; /* If there are no breaks set in this segment, we will delete its break map. * This will also delete this segment's entry in the break segment array. */ if bmap.num_set = 0 /* Any breaks set? */ then do; /* NO. */ call DELETE_BMAP; call ioa_$ioa_stream (debug_output, "No breaks set."); return; end; /* There are breaks set in the default segment. We have to look at all of the slots * in this segments break map. We will perform the specified action on all of the * breaks that are found. */ do break_num = 1 to bmap.num_slots while (break_map_ptr ^= null ()); call GET_BREAK_SLOT_PTR; if break_slot.type ^= -1 /* Is there a break in this slot? */ then call DO_ACTION; /* YES. */ end; if ^action_flag then call ioa_$ioa_stream (debug_output, "No breaks set."); return; /* End of sub_global entry. */ /* */ global: entry (arg_action_code, arg_line_len, arg_line, arg_print_mode); /* This entry is called to perform a specified action an all of the breaks the user * has in ALL of the segments referenced by his break segment. */ action_flag = "0"b; /* Initialize flag. */ action_code = arg_action_code; /* Copy arguments. */ line_len = arg_line_len; print_mode = arg_print_mode; if break_seg_ptr = null () /* Make sure we have a break segment. */ then call INIT_BREAK_SEG; /* We don't care about any default segment. */ if bseg.num_segs > 0 then do; /* Breaks are set */ segx = 1; do while (segx <= bseg.num_segs); /* Process all segments in user's break segment. * If not, the segment entry will be deleted * and delete_seg_entry_flag will be ON. */ call CHECK_SEGMENT; if ^ delete_seg_entry_flag then do; /* Segment not deleted; at least one break */ do break_num = 1 to bmap.num_slots while (break_map_ptr ^= null ()); call GET_BREAK_SLOT_PTR; if break_slot.type ^= -1 then call DO_ACTION; end; if ^delete_seg_entry_flag then segx = segx + 1; end; end; end; if ^action_flag then call ioa_$ioa_stream (debug_output, "No breaks set."); return; /* The end of the global entry. */ /* */ SET_DEFAULTS: procedure; /* This procedure establishes the segment with the break as the default segment. * It also makes sure that we have a break segment to work with - it sets up the * pointer to the break segment. The default segment index references this segment's * entry in the user's break segment and the default break map pointer references the * break map in this segment. The entry for this segment in the user's break segment * is initialized. If none exists, one will be created. */ if break_seg_ptr = null () /* Has the break segment been set up? */ then call INIT_BREAK_SEG; /* No, do it now. */ /* Get the name of the new default segment. */ call hcs_$fs_get_path_name (break_ptr, dir_name, (0), ent_name, code); if code ^= 0 then goto SET_DEF_ERR; /* Get the bit count and the uid of this segment. Note, if the path name is for * a link, then we will chase the link, and get the status of the branch itself. */ call hcs_$status_long (dir_name, ent_name, 1b, addr (branch), null (), code); if code ^= 0 then goto SET_DEF_ERR; /* Now get the pointer to the break map for this segment. If no break map exists, it * will be created. */ call db_break_map$init (break_ptr, fixed (branch.bitcnt, 24), break_map_ptr); if break_map_ptr = null () | break_ptr = null then do; arg_break_ptr = null; goto RETURN_FROM_DB_BREAK; end; /* Now we must find the entry in the break segment which corresponds to the new * default segment. If there is no entry for this segment, then one will be created. * Note, the search through the break segment entries is done for uid's and not * for path names since they may have been changed. */ do segx = 1 to bseg.num_segs; if bseg.seg (segx).uid = branch.uid then goto SET_DEF_SEG_FOUND; end; /* This break segment doesn't contain an entry for the dafault segment. Thus we * will create one. */ call set_break_seg_bc (segx, "0"b); bseg.num_segs = segx; /* Up count of segments. */ bseg.seg (segx).uid = branch.uid; /* This relates the entry to the segment. */ /* Now that we know the index of the break segment entry for the default segment we * can fill in the names of the segment. These must be reset each time the segment * is established as the default segment. */ SET_DEF_SEG_FOUND: bseg.seg (segx).dir_name = dir_name; bseg.seg (segx).ent_name = ent_name; def_break_map_ptr = break_map_ptr; /* Set up default break map pointer. */ def_segx = segx; /* Set up default segment index. */ return; SET_DEF_ERR: call com_err_ (code, "debug", "Cannot make ^p the default break segment.", break_ptr); goto RETURN_FROM_DB_BREAK; /* Transfer out of this internal procedure and * return directly to the caller of db_break. */ end SET_DEFAULTS; /* */ CHECK_DEFAULT: procedure; /* This internal procedure checks to see if there is a default segment established. * If not, there is an error. It will also check to see if the break number passed * as an argument is valid for this segment. If everything is OK, it will copy the * default variables, which are in internal static, and the break number, which is an * argument, into automatic variables. */ if def_segx = 0 /* Has default segment been established? */ then do; /* NO, error. */ call ioa_$ioa_stream (debug_output, "No default break segment."); goto RETURN_FROM_DB_BREAK; end; break_map_ptr = def_break_map_ptr; /* Copy default variables. */ segx = def_segx; call VALIDATE_BREAK_NUM; /* Check boonds of break number. */ call GET_BREAK_SLOT_PTR; /* Get pointer to its break slot. */ end CHECK_DEFAULT; /* */ CHECK_BREAK_NUM: procedure; /* This internal procedure is called to perform special validation on the break number. * It checks to see if the break number is within valid bounds and it also checks to * see that the specified breakis enabled. If the break number is valid, * it sets up the break slot pointer to point to the break slot associated * with this break number. */ call VALIDATE_BREAK_NUM; /* Check bounds of break number. */ call GET_BREAK_SLOT_PTR; /* Get pointer to this break's slot. */ if (break_slot.type = -1) | /* Is break not set? */ (break_slot.type > 1) /* Or is break disabled? */ then do; /* Yes, break not enabled. */ call ioa_$ioa_stream (debug_output, "Break ^d should be enabled but isn't. Segment must be recompiled.", break_num); arg_break_ptr = null; goto RETURN_FROM_DB_BREAK; end; end CHECK_BREAK_NUM; /* */ VALIDATE_BREAK_NUM: procedure; /* This procedure is called to check that the current break number is * within valid bounds for the current default segment. */ if (break_num <= 0) | /* Is break number within bounds. */ (break_num > bmap.num_slots) then do; /* No, outside bounds of break map. */ call ioa_$ioa_stream (debug_output, "Illegal break number ^d for segment ^a>^a", break_num, bseg.seg (segx).dir_name, bseg.seg (segx).ent_name); goto RETURN_FROM_DB_BREAK; end; end VALIDATE_BREAK_NUM; /* */ CHECK_SEGMENT: procedure; /* This procedure is called to get the break map of a segment and to validate the * fact that the segment has breaks set. The first thing that we must do is to * initiate this segment so we can get a pointer to it and get its bit count. * If any of the following four conditions occur, we will delete this entry in * the break segment: * 1. The segment does not exist. * 2. The uid of the segment doesn't match the uid in the break segment entry. * This implies that the name of the segment was changed and a new * segment was created with its old name. * 3. There is no break map for this segment; => it has no breaks. * 4. There is a break map, but there are no breaks set. * If the segment entry is deleted and there is a break map, the break map will * also be deleted. If for any of the above reasons the segment entry is deleted * the delete_seg_entry_flag will be turned ON. */ delete_seg_entry_flag = "0"b; /* Assume seg entry OK. */ call hcs_$initiate_count (bseg.seg (segx).dir_name, bseg.seg (segx).ent_name, "", bit_count, 0, seg_ptr, code); if seg_ptr = null () /* Does segment exist? */ then do; /* NO. */ call DELETE_SEG_ENTRY; return; end; call hcs_$status_long (bseg.seg (segx).dir_name, bseg.seg (segx).ent_name, 1b, addr (branch), null (), code); if bseg.seg (segx).uid ^= branch.uid /* Is it realy the same segment? */ then do; /* NO. */ call ioa_$ioa_stream (debug_output, "Path name ^a>^a now references new segment.", bseg.seg (segx).dir_name, bseg.seg (segx).ent_name); call DELETE_SEG_ENTRY; return; end; call db_break_map$check (seg_ptr, bit_count, break_map_ptr); if seg_ptr = null then return; /* error return for illegal break format */ if break_map_ptr = null () /* Does it have a break map? */ then do; /* NO. */ if print_mode = 1 /* Only print this message in LONG mode. */ then call ioa_$ioa_stream (debug_output, "^a>^a has no break map.", bseg.seg (segx).dir_name, bseg.seg (segx).ent_name); call DELETE_SEG_ENTRY; return; end; /* This segment has a break map. Does it have any breaks set? If not, we will * delete its break map. This will also result in deleteing its segment entry. */ if bmap.num_set = 0 /* Does it have any breaks set? */ then do; /* NO. */ call DELETE_BMAP; return; end; end CHECK_SEGMENT; /* */ GET_BREAK_SLOT_PTR: procedure; /* This internal procedure is called to get a pointer to the break slot * referenced by "break_num". Temporarily, there are two versionb of * the break_map_header. This procedure must decide which version is being used. */ break_slot_ptr = addr (bmap.breaks (break_num)); end GET_BREAK_SLOT_PTR; /* */ INIT_BREAK_SEG: procedure; /* This procedure is called to get a pointer to the user's break segment in his * home directory. The pointer is set in "break_seg_ptr" which is in internal * static. If no break segment exists for this user, then one will be created. */ call user_info_$homedir (dir_name); /* Get the name of the user's * home directory. */ call user_info_ (ent_name); /* Get the user's login name. * Note, it is a max of 24 chars. */ i = index (ent_name, " "); /* Get index of first blank. */ substr (ent_name, i, 7) = ".breaks"; /* Add debug suffix. */ /* The following call will get a pointer to the segment. If none exists, it will * create the segment. In any case, there is an error only if we don't get a * valid pointer back. We don't use a reference name and we want RWA access to * the segment. */ call hcs_$make_seg (dir_name, ent_name, "", 01011b, break_seg_ptr, code); if break_seg_ptr = null () then do; call com_err_ (code, "debug", "^a>^a", dir_name, ent_name); goto RETURN_FROM_DB_BREAK; end; end INIT_BREAK_SEG; /* */ DO_ACTION: procedure; /* This internal procedure acts as a transfer vector. It calls the db_break * procedure which will perform the specified action. The action code * indicates the type of action. The action flag is turned on in order to * indicate that the action has been performed for at least one break. */ action_flag = "1"b; goto ACTION_LABEL (action_code); /* Go to specified call. */ ACTION_LABEL (1): /* LIST a break. */ call LIST; return; ACTION_LABEL (2): /* RESET a break. */ call RESET; return; ACTION_LABEL (3): /* DISABLE a break. */ call DISABLE; return; ACTION_LABEL (4): /* ENABLE a break. */ call ENABLE; return; ACTION_LABEL (5): /* Set a COMMAND in a break. */ call SET_COMMAND; return; ACTION_LABEL (6): /* Set a CONDITION in a break. */ call SET_CONDITION; end DO_ACTION; /* */ LIST: procedure; /* This procedure is called to print the contents of one break. The print_mode flag * determines the mode of printing. There are two modes: * SHORT (print_mode = 0) Print as little as possible. * LONG (print_mode = 1) Print as much as possible. */ dcl line_info char (14) aligned; /* Char representation of line num. (if available) */ if break_slot.line_no > 0 then call ioa_$rsnnl (" (line ^d)", line_info, j, break_slot.line_no); else line_info = ""; /* Print short mode information first. */ call ioa_$ioa_stream (debug_output, "^a ^d set at ^a|^o^a", break_type_name (break_slot.type), break_num, bseg.seg (segx).ent_name, break_slot.offset, line_info); if print_mode = 0 /* If short mode, that's all. */ then return; /* Now we must print the long mode data. First print the instruction where the * break is. */ call print_text_$real_offset (addr (break_slot.old_word), source_string, break_slot.offset); call ioa_$ioa_stream (debug_output, "^-At instruction: ^a", source_string); /* Now print the command line and the condition line if they exist. */ if break_slot.comd_len ^= 0 then call ioa_$ioa_stream (debug_output, "Command = ^a", substr (break_slot.comd_line, 1, break_slot.comd_len)); if break_slot.cond_len ^= 0 /* Is there a condition line? */ then do; /* Yes, call routine that knows condition format. */ call db_parse_condition$print_line (addr (break_slot.cond_data), source_string); call ioa_$ioa_stream (debug_output, "Condition = ^a", source_string); end; end LIST; /* */ RESET: procedure; /* This procedure is called to reset a break. Resetting a break involves * removing the mme2 instruction from the break word and freeing the break slot * used by the break. When the last break in a segment is reset, its * break map will be deleted. Also, its entry in the break segment array will * be deleted. */ call DISABLE; /* First disable the mme2. */ break_slot.type = -1; /* Now free the break slot. */ bmap.num_set = bmap.num_set -1; /* Segment has one less break set. */ if print_mode ^= 0 then call ioa_$ioa_stream (debug_output, "Break ^d at ^a|^o reset.", break_num, bseg.seg (segx).ent_name, break_slot.offset); /* Now that the break has been reset, we must check to see if there are any breaks left * in the segment. If not, we will delete its break map. */ if bmap.num_set = 0 /* Any breaks left in segment? */ then call DELETE_BMAP; /* NO. */ end RESET; /* */ DISABLE: procedure; /* This procedure is called to disable a break. Disabling a break involves * putting the word that was originally in the break word back into the break word. * This overlays the mme2 that is there while the break is enabled. */ if break_slot.type > 1 /* Is break already disabled? */ then return; /* YES. */ /* Get a pointer to the break and set up our own mme2 word to look like what should * be in the break word now. */ break_word_ptr = ptr (break_map_ptr, break_slot.offset); mme2.break_num = break_num; /* Now check to see that this is a valid break. The break word should contain a * mme2 instruction with an address equal to this break number. */ if break_word_ptr -> based_word ^= addr (mme2) -> based_word then do; call ioa_$ioa_stream (debug_output, "Break ^d at ^a|^o is invalid.", break_num, bseg.seg (segx).ent_name, break_slot.offset); return; end; /* It is a valid break so we disable it now. */ break_word_ptr -> based_word = break_slot.old_word; /* Now set the type to indicate that this is a disabled break. Regular breaks * are now type 2 and temporary breaks are now type 3. */ break_slot.type = break_slot.type + 2; end DISABLE; /* */ ENABLE: procedure; /* This procedure is called to enable a break. Enabling a break involves simply * putting a mme2 instruction the the break word. */ if break_slot.type < 2 /* Is break already enabled */ then return; /* YES, don't bother. */ mme2.break_num = break_num; /* Addr field of mme2 = break num. */ ptr (break_map_ptr, break_slot.offset) -> mme2_map = mme2; /* Now set the type to indicate that the break is enabled. If it is a regular * break it will go from 2 -> 0 and if it is a temporary break if will go from * 3 -> 1. */ break_slot.type = break_slot.type - 2; end ENABLE; /* */ SET_COMMAND: procedure; /* This procedure sets up a command in the specified break. */ break_slot.comd_len = line_len; if line_len ^= 0 /* Is there really a command line? */ then substr (break_slot.comd_line, 1, line_len) = substr (arg_line, 1, line_len); end SET_COMMAND; SET_CONDITION: procedure; /* This procedure is called to set or to reset a condition in a break. The type of * break is not considered and is not changed. Only the condition information * in the break slot is changed. We must determine if we are to set the condition * or reset the condition. If the condition length is zero, we must reset. */ if line_len = 0 /* Are we to SET or RESET? */ then break_slot.cond_len = 0; /* RESET - this implies no condition. */ else do; /* SET condition, there is data. */ break_slot.cond_len = line_len; substr (break_slot.cond_data, 1, line_len) = substr (arg_line, 1, line_len); end; end SET_CONDITION; /* */ DELETE_BMAP: procedure; /* This procedure is called to delete a beak map from a segment. This is done * whenever a segment is referenced which has a break map but does not have any * breaks set. This will also result in deleting the segment's entry in * the break segment array. */ call db_break_map$delete (break_map_ptr); break_map_ptr = null; /* Make sure no attempt is made * look at this break map again */ call DELETE_SEG_ENTRY; /* If this segment is the default segment, we must reset the default segment * variables to indicate that there is no default segment. */ if def_segx = segx /* Is this the default segment? */ then def_segx = 0; /* YES, but no longer. */ end DELETE_BMAP; /* */ DELETE_SEG_ENTRY: procedure; /* This procedure is called to delete an entry in the user's break segment. * We will move the last entry in the break segment array into the entry * that is being deleted. Then we will clear the last entry and decrement * the count of entries. */ /* Move last entry into the one being deleted. */ break_seg_ptr -> bseg_map.array (segx).entry = break_seg_ptr -> bseg_map.array (bseg.num_segs).entry; bseg.num_segs = bseg.num_segs - 1; /* Decrement the count of entries. */ delete_seg_entry_flag = "1"b; call set_break_seg_bc (bseg.num_segs, "1"b); end DELETE_SEG_ENTRY; /* */ /* This procedure sets the bit count on the user's break segment. It is called when a new segment slot is added or deleted. */ set_break_seg_bc: proc (num_slots, truncate); dcl num_slots fixed bin; /* size the break map will become */ dcl truncate bit (1) unal; /* truncate the break segment */ dcl 1 break_slot aligned like bseg.seg based; dcl word_count fixed bin (18); dcl size builtin; word_count = size (break_slot) * num_slots + 1; call hcs_$truncate_seg (break_seg_ptr, word_count, code); if code = 0 then call hcs_$set_bc_seg (break_seg_ptr, 36 * word_count, code); if code ^= 0 then call com_err_ (code, "break segment"); return; end set_break_seg_bc; /* */ /* This statement is part of the main db_break block. It is provided so that * internal procedures may return directly to the caller of db_break. Thus the * db_break entry which called the internal procedure doesn't have to check for * error conditions. */ RETURN_FROM_DB_BREAK: return; end db_break;