?? RIGHT := 110 ??
MODULE osm$keypoint_support;
?? PUSH (LISTEXT := ON) ??
*copyc amt$file_identifier
*copyc osc$keypoint_buffer_pva_offset
*copyc osc$multiprocessor_constants
*copyc osc$status_parameter_delimiter
*copyc osd$default_pragmats
*copyc ose$keypoint_conditions
*copyc oss$job_fixed
*copyc oss$task_shared
*copyc ost$caller_identifier
*copyc ost$execution_control_block
*copyc ost$wait

*copyc jmv$ijl_p
*copyc jmv$jcb
*copyc mtv$scb
*copyc osv$keypoint_control
*copyc osv$page_size
*copyc osv$task_shared_heap
*copyc syv$perf_keypoints_enabled
*copyc tmv$ptl_p

*copyc clp$log_comment
*copyc clp$put_job_command_response
*copyc i#call_monitor
*copyc i$real_memory_address
*copyc jmp$get_ijle_p
*copyc mmp$free_pages
*copyc mmp$set_segment_length
*copyc mmp$verify_access
*copyc ofp$display_status_message
*copyc osp$append_status_parameter
*copyc osp$clear_signature_lock
*copyc osp$collection_file_info_r1
*copyc osp$disestablish_cond_handler
*copyc osp$establish_condition_handler
*copyc osp$fetch_keypoint_lock
*copyc osp$init_keypoints_for_proc
*copyc osp$processor_selections_r1
*copyc osp$release_keypoint_r1
*copyc osp$reserve_keypoint_r1
*copyc osp$reset_processor_r1
*copyc osp$set_signature_lock
*copyc osp$set_status_from_condition
*copyc osp$set_status_abnormal
*copyc osp$verify_system_privilege
*copyc pmp$continue_to_cause
*copyc pmp$deselect_processor
*copyc pmp$disestab_end_hndlr_in_ring
*copyc pmp$establish_end_hndlr_in_ring
*copyc pmp$find_executing_task_xcb
*copyc pmp$get_processor_descriptions
*copyc pmp$log_ascii
*copyc pmp$long_term_wait
*copyc pmp$select_processor
*copyc pmp$get_compact_date_time
*copyc syp$pop_inhibit_job_recovery
*copyc syp$push_inhibit_job_recovery
?? POP ??

?? TITLE := 'osp$reserve_keypoint_env', EJECT ??

  PROCEDURE [XDCL, #GATE] osp$reserve_keypoint_env
    (   env:ost$keypoint_environment;
        mm,
        jm: ost$keypoint_class_mask;
        pva_array: ^array [ * ] OF ^cell;
        wait: ost$wait;
        mpo: ost$keypoint_multipro_option;
        kc: integer;
        kbs: integer;
        str: string (32);
        performance_keypoints: syt$perf_keypoints_enabled;
    VAR status: ost$status);

*copyc osh$reserve_keypoint_env
?? EJECT ??

    PROCEDURE ch
      (   cond: pmt$condition;
          ci: ^pmt$condition_information;
          sa: ^ost$stack_frame_save_area;
      VAR hs: ost$status);

      IF cond.selector = ifc$interactive_condition THEN
        IF cond.interactive_condition = ifc$terminate_break THEN
          osp$set_status_from_condition ('OS', cond, sa, status, hs);
          EXIT osp$reserve_keypoint_env;
        IFEND;
      ELSE
        pmp$continue_to_cause (pmc$execute_standard_procedure, hs);
      IFEND;
      hs.normal := TRUE;
    PROCEND ch;
?? EJECT ??

    VAR
      cid: ost$caller_identifier,
      cpu_selections: ost$processor_id_set,
      fap: integer,
      i: integer,
      ijle_p: ^jmt$initiated_job_list_entry,
      j: integer,
      job_xcb_list: [XREF, oss$job_fixed] record
        head: ^ost$execution_control_block,
        lock: ost$signature_lock,
      recend,
      jtc: integer,
      k: integer,
      lstatus: ost$status,
      max_buffer_size: integer,
      mes: string (10),
      mtr_req: ost$rb_keypoint_request,
      nap: integer,
      offset: integer,
      osv$keypoint_enable: [XREF] integer,
      osv$max_kpt_pages: [XREF] integer,
      pd: pmt$processor_descriptions,
      perf_keys: syt$perf_keypoints_enabled,
      pptu: integer,
      stl: integer,
      xcb: ^ost$execution_control_block,
      xjm: ost$keypoint_class_mask,
      xcb_p: ^ost$execution_control_block,
      xkc: integer,
      xmm: ost$keypoint_class_mask;

    mes := '  ';
    #caller_id (cid);
    lstatus := status;
    status := lstatus;
    pptu := 16384 DIV osv$page_size; {!!!!!!!!!!!}
    max_buffer_size := osv$max_kpt_pages DIV pptu;

    i := #read_register (osc$pr_keypoint_buffer_ptr);
    IF i = 0 THEN
      osp$set_status_abnormal ('OS', ose$kpt_wrong_hardware, '', status);
      RETURN;
    IFEND;

    IF osv$keypoint_enable = osc$kpt_disabled THEN
      osp$set_status_abnormal ('OS', ose$kpt_illegal_request, '', status);
      RETURN;
    IFEND;

    IF (kbs * pptu) > osv$max_kpt_pages THEN
      STRINGREP (mes, stl, kbs);
      osp$set_status_abnormal ('OS', ose$kpt_kbs_too_large, mes, status);
      STRINGREP (mes, stl, max_buffer_size);
      osp$append_status_parameter (osc$status_parameter_delimiter, mes, status);
      RETURN;
    IFEND;

    pmp$get_processor_descriptions (pd, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    xcb := job_xcb_list.head;
    jtc := 0;
    WHILE xcb <> NIL DO
      jtc := jtc + 1;
      xcb := xcb^.link;
    WHILEND;

    IF ((env = osc$job_keypoints) AND (jmv$jcb.ijle_p^.multiprocessing_allowed)) AND
       NOT (mpo = osc$keypoints_multi_processor) THEN
      osp$set_status_abnormal ('OS', ose$job_kpt_invalid_mp_job, '', status);
      RETURN;
    IFEND;

    IF (mpo = osc$keypoints_multi_processor) OR (env = osc$system_keypoints) OR
       (env = osc$system_sample_keypoints) THEN
      nap := 0;
      FOR i := 0 TO pd.count - 1 DO
        IF pd.processor [i].state = cmc$on THEN
          nap := nap + 1;
        IFEND;
      FOREND;
    ELSE
      nap := 1;
    IFEND;

    IF ((env = osc$system_keypoints) OR (env = osc$system_sample_keypoints)) AND
       (nap > 1) THEN
      IF mpo <> osc$keypoints_multi_processor THEN
        osp$set_status_abnormal ('OS', ose$kpt_mp_required, '', status);
        RETURN;
      IFEND;
    IFEND;
    nap := nap - 1;

    pmp$find_executing_task_xcb (xcb_p);
    jmp$get_ijle_p (tmv$ptl_p^ [xcb_p^.global_task_id.index].ijl_ordinal, ijle_p);
    IF ijle_p^.job_scheduler_data.job_class = jmc$maintenance_job_class THEN
      cpu_selections := mtv$scb.cpus.logically_on;
    ELSE
      cpu_selections := mtv$scb.cpus.available_for_use;
    IFEND;
    IF (xcb_p^.processor_selections <> cpu_selections) AND ((env = osc$job_keypoints) OR
          (env = osc$job_sample_keypoints)) AND (mpo = osc$keypoints_multi_processor) THEN
      osp$set_status_abnormal ('OS', ose$not_enough_procs_for_kpt, '', status);
      RETURN;
    IFEND;

    IF UPPERBOUND (pva_array^) - LOWERBOUND (pva_array^) < nap THEN
      osp$set_status_abnormal ('OS', ose$kpt_not_enough_files_for_mp, '',
            status);
      RETURN;
    IFEND;

    FOR i := LOWERBOUND (pva_array^) TO LOWERBOUND (pva_array^) + nap DO
      IF NOT mmp$verify_access (#LOC (pva_array^ [i]), mmc$va_write) THEN
        osp$set_status_abnormal ('OS', ose$kpt_invalid_collection_file, '',
              status);
        RETURN;
      IFEND;

      { free pages associated with file ...
      { this prevents confusion between file pages in jws/aq/amq and kpt io
      {directly to file
      { kpt io goes directly to file WITHOUT using the FILE'S sva (awk!).

      mmp$free_pages (pva_array^ [i], UPPERVALUE (ost$byte_count), osc$wait,
            status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;
    FOREND;

    i := (kbs * pptu * osv$page_size) DIV 8;
    xkc := kc + (i - (kc MOD i));
    xmm := mm - $ost$keypoint_class_mask [15];
    xjm := jm - $ost$keypoint_class_mask [15];
    fap := 0;
    perf_keys := performance_keypoints;
    osp$establish_condition_handler (^ch, FALSE);

    REPEAT
      syp$push_inhibit_job_recovery;
      osp$reserve_keypoint_r1 (env, xmm, xjm, mpo, xkc, kbs * pptu, fap,
            perf_keys, status);
      IF NOT status.normal THEN
        syp$pop_inhibit_job_recovery;
        IF status.condition = ose$kpt_environment_not_avail THEN
          IF wait = osc$wait THEN
            ofp$display_status_message ('Waiting for keypoint environment', status);
            status.normal := FALSE;
            pmp$long_term_wait (30000, 30000);
          ELSE
            RETURN;
          IFEND;
        ELSE
          RETURN;
        IFEND;
      IFEND;
    UNTIL status.normal;

    osp$disestablish_cond_handler;

    mtr_req.reqcode := syc$rc_keypoint;
    mtr_req.sub_request := osc$kpt_mr_init;
    pmp$get_compact_date_time (mtr_req.kpt.date_time, status);
    mtr_req.kpt.microsecond_clock := #free_running_clock (0);
    mtr_req.kpt.user_data := str;
    mtr_req.kpt.keypoint.clock := mtr_req.kpt.microsecond_clock MOD
          10000000(16);
    mtr_req.kpt.keypoint.keypoint_class := 15;
    mtr_req.kpt.keypoint.keypoint_code := osc$keypoint_cl15_reserve;

    offset := osc$keypoint_buffer_pva_offset;

/processor_selections/
    FOR i := LOWERBOUND (pva_array^) TO LOWERBOUND (pva_array^) + nap DO
    /job/
      BEGIN
      IF (osv$keypoint_control.environment = osc$job_keypoints) OR
        (osv$keypoint_control.environment = osc$job_sample_keypoints) THEN
        IF osv$keypoint_control.processor_select_flag THEN
          FOR j := 0 TO pd.count - 1 DO
            IF (pd.processor [j].id IN osv$keypoint_control.processor_selections) AND
                (pd.processor [j].state = cmc$on) THEN
              pmp$select_processor (pd.processor [j].id, status);
              osp$init_keypoints_for_proc (pva_array^ [i], offset, mtr_req, pd.
                 processor [j].id, status);
              IF NOT status.normal THEN
                syp$pop_inhibit_job_recovery;
                reset_processor_selections (pd);
                RETURN;
              IFEND;
              offset := offset + osc$kpt_pva_increment;
            IFEND;
          FOREND;
          EXIT /processor_selections/;
        ELSE
          IF mpo = osc$keypoints_multi_processor THEN
            EXIT /job/;
          IFEND;
          IF (pd.count > 1) AND (pd.processor [1].state = cmc$on) THEN
            pmp$select_processor (pd.processor [1].id, status);
            osp$init_keypoints_for_proc (pva_array^ [i], offset, mtr_req, pd.
               processor [1].id, status);
          ELSE
            pmp$select_processor (pd.processor [0].id, status);
            osp$init_keypoints_for_proc (pva_array^ [i], offset, mtr_req, pd.
               processor [0].id, status);
          IFEND;
          IF NOT status.normal THEN
            syp$pop_inhibit_job_recovery;
            osp$release_keypoint_r1 (lstatus);
            RETURN;
          IFEND;
          EXIT /processor_selections/;
        IFEND;
      IFEND;
      END /job/;

      pmp$select_processor (pd.processor [fap].id, status);
      IF NOT status.normal THEN
        syp$pop_inhibit_job_recovery;
        osp$release_keypoint_r1 (lstatus);
        RETURN;
      IFEND;

      osp$init_keypoints_for_proc (pva_array^ [i], offset, mtr_req, pd.
            processor [fap].id, status);
      IF NOT status.normal THEN
        syp$pop_inhibit_job_recovery;
        reset_processor_selections (pd);
        RETURN;
      IFEND;
      offset := offset + osc$kpt_pva_increment;
      fap := fap + 1;

    FOREND /processor_selections/;
    IF mpo = osc$keypoints_multi_processor THEN
      { enable multi-processor collection
      pmp$deselect_processor (status);
    IFEND;

    pmp$establish_end_hndlr_in_ring (^end_handler, cid.ring, status);
    syp$pop_inhibit_job_recovery;

  PROCEND osp$reserve_keypoint_env;
?? TITLE := 'end_handler', EJECT ??

  PROCEDURE end_handler
    (    termination_status: ost$status;
     VAR status: ost$status);

    VAR
      file_id_array: array [1 .. osc$max_number_of_processors] of amt$file_identifier,
      number_of_files: 0 .. 0ff(16),
      pa: array [1 .. osc$max_number_of_processors] of ^cell;

    pmp$log_ascii ('KEYPOINT TERMINATION ABORT', $pmt$ascii_logset [pmc$system_log,
          pmc$job_log], pmc$msg_origin_system, status);
    osp$fetch_collection_file_info (pa, file_id_array, number_of_files);
    osp$release_keypoint_env (pa, 'TERMINATION ABORT !!!!!!!!!!!   ', status);

  PROCEND end_handler;
?? TITLE := 'reset_processor_selections', EJECT ??

  PROCEDURE reset_processor_selections
    (pd: pmt$processor_descriptions);

    VAR
      k: integer,
      status: ost$status;

    IF osv$keypoint_control.processor_select_flag THEN
      FOR k := 0 TO pd.count - 1 DO
        IF (pd.processor [k].id IN  osv$keypoint_control.processor_selections) THEN
          pmp$select_processor (pd.processor [k].id, status);
        IFEND;
      FOREND;
    ELSE
      osp$reset_processor_r1;
    IFEND;
    osp$release_keypoint_r1 (status);

  PROCEND reset_processor_selections;
?? TITLE := 'osp$release_keypoint_env', EJECT ??

  PROCEDURE [XDCL, #GATE] osp$release_keypoint_env
    (VAR pva_array: array [1 .. osc$max_number_of_processors] of ^cell;
         str: string (32);
     VAR status: ost$status);
*copyc osh$release_keypoint_env

    PROCEDURE ch
      (   condition: pmt$condition;
          condition_info: ^pmt$condition_information;
          save_area: ^ost$stack_frame_save_area;
      VAR handler_status: ost$status);

      IF handler_invoked THEN
        RETURN;
      IFEND;
      IF condition.selector = ifc$interactive_condition THEN
        IF condition.interactive_condition = ifc$pause_break THEN
          pmp$continue_to_cause (pmc$execute_standard_procedure, handler_status);
          RETURN;
        IFEND;
      IFEND;
      handler_invoked := TRUE;
      pmp$deselect_processor (status);
      reset_processor_selections (pd);
      pmp$disestab_end_hndlr_in_ring (^end_handler, cid.ring, status);
      osp$set_status_from_condition ('OS', condition, save_area, status, handler_status);
      IF NOT status.normal THEN
        handler_status := status;
      IFEND;
      EXIT osp$release_keypoint_env;
    PROCEND ch;

    VAR
      cid: ost$caller_identifier,
      fap: -2 .. 31,
      handler_invoked: boolean,
      i: integer,
      idx: integer,
      j: integer,
      lap: -2 .. 31,
      lstatus: ost$status,
      mtr_req: ost$rb_keypoint_request,
      off: integer,
      off_save_array: array [0 .. 7] of integer,
      pd: pmt$processor_descriptions,
      xcb_p: ^ost$execution_control_block;

    #caller_id (cid);
    lstatus := status;
    status := lstatus;

    IF (osv$keypoint_control.environment = osc$job_keypoints) OR
      (osv$keypoint_control.environment = osc$job_sample_keypoints) THEN
      pmp$find_executing_task_xcb (xcb_p);
      IF (NOT xcb_p^.keypoint_enable) THEN
        osp$set_status_abnormal ('OS', ose$kpt_not_valid_in_task, '', status);
        RETURN;
      IFEND;
    IFEND;

    osp$fetch_keypoint_lock (i);
    IF (i = 0) OR (osv$keypoint_control.jsn <> jmv$jcb.system_name) THEN
      osp$set_status_abnormal ('OS', ose$kpt_illegal_request, '', status);
      RETURN;
    IFEND;

    IF (UPPERBOUND (pva_array) - LOWERBOUND (pva_array) + 1) <
          (osv$keypoint_control.last_active_processor - osv$keypoint_control.
          first_active_processor + 1) THEN
      osp$set_status_abnormal ('OS', ose$kpt_not_enough_files_for_mp, '',
            status);
      RETURN;
    IFEND;

    pmp$get_processor_descriptions (pd, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    handler_invoked := FALSE;
    osp$establish_condition_handler(^ch, TRUE);
    osp$stop_keypoint_collection (str, status);

    mtr_req.reqcode := syc$rc_keypoint;
    mtr_req.sub_request := osc$kpt_mr_term;
    pmp$get_compact_date_time (mtr_req.kpt.date_time, status);
    mtr_req.kpt.microsecond_clock := #free_running_clock (0);
    mtr_req.kpt.user_data := str;
    mtr_req.kpt.keypoint.clock := mtr_req.kpt.microsecond_clock MOD
          10000000(16);
    mtr_req.kpt.keypoint.keypoint_class := 15;
    mtr_req.kpt.keypoint.keypoint_code := osc$keypoint_cl15_release;
    status.normal := TRUE;
    idx := LOWERBOUND (pva_array);
    FOR i := osv$keypoint_control.first_active_processor TO
          osv$keypoint_control.last_active_processor DO
      pmp$select_processor (osv$keypoint_control.cpus [i].pid, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;
      REPEAT
        i#call_monitor (#LOC (mtr_req), #SIZE (mtr_req));
      UNTIL mtr_req.status.normal;
      IF (osv$keypoint_control.environment = osc$job_keypoints) OR
            (osv$keypoint_control.environment = osc$system_keypoints) THEN
        mmp$set_segment_length (pva_array [idx], 1, osv$keypoint_control.cpus [i].offset,
          status);
      ELSE
        mmp$set_segment_length (pva_array [idx], 1, 0, status);
      IFEND;
      idx := idx + 1;
    FOREND;

    reset_processor_selections (pd);

    pmp$disestab_end_hndlr_in_ring (^end_handler, cid.ring, lstatus);
    osp$disestablish_cond_handler;

  PROCEND osp$release_keypoint_env;
?? TITLE := 'osp$start_keypoint_collection', EJECT ??

  PROCEDURE [XDCL, #GATE] osp$start_keypoint_collection
    (   str: string (32);
     VAR status: ost$status);
*copyc osh$start_keypoint_collection

    VAR
      i: integer,
      j: integer,
      mtr_req: ost$rb_keypoint_request,
      xcb_p: ^ost$execution_control_block;

    status.normal := TRUE;
    IF (osv$keypoint_control.environment = osc$job_keypoints) OR
      (osv$keypoint_control.environment = osc$job_sample_keypoints) THEN
      pmp$find_executing_task_xcb (xcb_p);
      IF (NOT xcb_p^.keypoint_enable) THEN
        osp$set_status_abnormal ('OS', ose$kpt_not_valid_in_task, '', status);
        RETURN;
      IFEND;
    IFEND;

    osp$fetch_keypoint_lock (i);
    IF (i = 0) OR (osv$keypoint_control.jsn <> jmv$jcb.system_name) THEN
      osp$set_status_abnormal ('OS', ose$kpt_illegal_request, '', status);
      RETURN;
    IFEND;
    IF osv$keypoint_control.active THEN
      osp$set_status_abnormal ('OS', ose$kpt_collect_already_started, '',
            status);
      RETURN;
    IFEND;
    IF osv$keypoint_control.termination_status <> osc$kp_term_not_stopped THEN
      osp$set_status_abnormal ('OS', osv$keypoint_control.termination_status,
            '', status);
      RETURN;
    IFEND;

    mtr_req.reqcode := syc$rc_keypoint;
    mtr_req.sub_request := osc$kpt_mr_start;
    pmp$get_compact_date_time (mtr_req.kpt.date_time, status);
    mtr_req.kpt.microsecond_clock := #free_running_clock (0);
    mtr_req.kpt.user_data := str;
    mtr_req.kpt.keypoint.clock := mtr_req.kpt.microsecond_clock MOD
          10000000(16);
    mtr_req.kpt.keypoint.keypoint_class := 15;
    mtr_req.kpt.keypoint.keypoint_code := osc$keypoint_cl15_start;
    status.normal := TRUE;

 { The following two FOR loops should not be combined. The monitor
 { activities must be done synchronously.

    FOR i := osv$keypoint_control.first_active_processor TO
          osv$keypoint_control.last_active_processor DO
      pmp$select_processor (i, status);
      i#call_monitor (#LOC (mtr_req), #SIZE (mtr_req));
      IF NOT mtr_req.status.normal THEN
        osp$set_status_abnormal ('OS', mtr_req.status.condition, '', status);
        RETURN;
      IFEND;
    FOREND;

    FOR i := osv$keypoint_control.first_active_processor TO
          osv$keypoint_control.last_active_processor DO
      pmp$select_processor (i, status);
      mtr_req.sub_request := osc$kpt_mr_go;
      i#call_monitor (#LOC (mtr_req), #SIZE (mtr_req));
      IF NOT mtr_req.status.normal THEN
        osp$set_status_abnormal ('OS', mtr_req.status.condition, '', status);
        RETURN;
      IFEND;
    FOREND;

    IF (osv$keypoint_control.mpo = osc$keypoints_multi_processor) THEN
      pmp$deselect_processor (status);
    IFEND;
  PROCEND osp$start_keypoint_collection;
?? TITLE := 'osp$stop_keypoint_collection', EJECT ??

  PROCEDURE [XDCL, #GATE] osp$stop_keypoint_collection
    (   str: string (32);
     VAR status: ost$status);
*copyc osh$stop_keypoint_collection

    VAR
      i: integer,
      mtr_req: ost$rb_keypoint_request,
      xcb_p: ^ost$execution_control_block;

    status.normal := TRUE;
    IF (osv$keypoint_control.environment = osc$job_keypoints) OR
      (osv$keypoint_control.environment = osc$job_sample_keypoints) THEN
      pmp$find_executing_task_xcb (xcb_p);
      IF (NOT xcb_p^.keypoint_enable) THEN
        osp$set_status_abnormal ('OS', ose$kpt_not_valid_in_task, '', status);
        RETURN;
      IFEND;
    IFEND;

    osp$fetch_keypoint_lock (i);
    IF (i = 0) OR (osv$keypoint_control.jsn <> jmv$jcb.system_name) THEN
      osp$set_status_abnormal ('OS', ose$kpt_illegal_request, '', status);
      RETURN;
    IFEND;
    IF NOT osv$keypoint_control.active THEN
      IF osv$keypoint_control.termination_status <> osc$kp_term_not_stopped
            THEN
        osp$set_status_abnormal ('OS', osv$keypoint_control.termination_status,
              '', status);
      ELSE
        osp$set_status_abnormal ('OS', ose$kpt_collect_already_stopped, '',
              status);
      IFEND;
      RETURN;
    IFEND;

    mtr_req.reqcode := syc$rc_keypoint;
    mtr_req.sub_request := osc$kpt_mr_stop;
    pmp$get_compact_date_time (mtr_req.kpt.date_time, status);
    mtr_req.kpt.microsecond_clock := #free_running_clock (0);
    mtr_req.kpt.user_data := str;
    mtr_req.kpt.keypoint.clock := mtr_req.kpt.microsecond_clock MOD
          10000000(16);
    mtr_req.kpt.keypoint.keypoint_class := 15;
    mtr_req.kpt.keypoint.keypoint_code := osc$keypoint_cl15_stop;
    status.normal := TRUE;
    FOR i := osv$keypoint_control.first_active_processor TO
          osv$keypoint_control.last_active_processor DO
      pmp$select_processor (i, status);
      i#call_monitor (#LOC (mtr_req), #SIZE (mtr_req));
      IF NOT mtr_req.status.normal THEN
        osp$set_status_abnormal ('OS', mtr_req.status.condition, '', status);
      IFEND;
    FOREND;
    IF (osv$keypoint_control.mpo = osc$keypoints_multi_processor) THEN
      pmp$deselect_processor (status);
    IFEND;
  PROCEND osp$stop_keypoint_collection;
?? TITLE := 'osp$issue_keypoint_collection', EJECT ??

  PROCEDURE [XDCL, #GATE] osp$issue_string_as_keypoint
    (   str: string (32);
     VAR status: ost$status);
*copyc osh$issue_string_as_keypoint

    VAR
      i: integer,
      j: integer,
      mtr_req: ost$rb_keypoint_request,
      xcb_p: ^ost$execution_control_block;

    status.normal := TRUE;
    IF (osv$keypoint_control.environment = osc$job_keypoints) OR
      (osv$keypoint_control.environment = osc$job_sample_keypoints) THEN
      pmp$find_executing_task_xcb (xcb_p);
      IF (NOT xcb_p^.keypoint_enable) THEN
        osp$set_status_abnormal ('OS', ose$kpt_not_valid_in_task, '', status);
        RETURN;
      IFEND;
    IFEND;

    osp$fetch_keypoint_lock (i);
    IF (i = 0) OR (osv$keypoint_control.jsn <> jmv$jcb.system_name) THEN
      osp$set_status_abnormal ('OS', ose$kpt_illegal_request, '', status);
      RETURN;
    IFEND;

    mtr_req.reqcode := syc$rc_keypoint;
    mtr_req.sub_request := osc$kpt_mr_issue;
    pmp$get_compact_date_time (mtr_req.kpt.date_time, status);
    mtr_req.kpt.microsecond_clock := #free_running_clock (0);
    mtr_req.kpt.user_data := str;
    mtr_req.kpt.keypoint.clock := mtr_req.kpt.microsecond_clock MOD
          10000000(16);
    mtr_req.kpt.keypoint.keypoint_class := 15;
    mtr_req.kpt.keypoint.keypoint_code := osc$keypoint_cl15_issue;
    status.normal := TRUE;
    FOR i := osv$keypoint_control.first_active_processor TO
          osv$keypoint_control.last_active_processor DO
      i#call_monitor (#LOC (mtr_req), #SIZE (mtr_req));
      IF NOT mtr_req.status.normal THEN
        osp$set_status_abnormal ('OS', mtr_req.status.condition, '', status);
      IFEND;
    FOREND;
  PROCEND osp$issue_string_as_keypoint;
?? TITLE := 'ost$display_keypoint_status', EJECT ??

  PROCEDURE [XDCL, #GATE] osp$display_keypoint_status
    (VAR kc:ost$keypoint_control;
     VAR perf_keypoints: syt$perf_keypoints_enabled;
     VAR status: ost$status);

    VAR
      i: integer;

    status.normal := TRUE;
    osp$fetch_keypoint_lock (i);
    IF (i = 0) OR (osv$keypoint_control.jsn <> jmv$jcb.system_name) THEN
      osp$set_status_abnormal ('OS', ose$kpt_illegal_request, '', status);
      RETURN;
    IFEND;
    perf_keypoints := syv$perf_keypoints_enabled;
    kc := osv$keypoint_control;
    status.normal := TRUE;
  PROCEND osp$display_keypoint_status;
?? TITLE := 'osp$fetch_keypoint_buffer_rmas', EJECT ??

  PROCEDURE osp$fetch_keypoint_buffer_rmas
    (VAR rmas: array [ * ] OF integer;
     VAR number_of_pages: integer;
     VAR page_size: integer;
     VAR status: ost$status);

    VAR
      i: integer,
      idx: integer,
      pc: ^cell;

    number_of_pages := 0;
    osp$fetch_keypoint_lock (i);
    IF (i = 0) OR (osv$keypoint_control.jsn <> jmv$jcb.system_name) THEN
      osp$set_status_abnormal ('OS', ose$kpt_illegal_request, '', status);
      RETURN;
    IFEND;

    IF (osv$keypoint_control.environment <> osc$job_sample_keypoints) AND
          (osv$keypoint_control.environment <> osc$system_sample_keypoints)
          THEN
      osp$set_status_abnormal ('OS', ose$kpt_illegal_request,
        'must be trace mode', status);
      RETURN;
    IFEND;

    IF (UPPERBOUND (rmas) - LOWERBOUND (rmas) + 1) < osv$keypoint_control.
          max_pages THEN
      osp$set_status_abnormal ('OS', ose$kpt_illegal_request, '', status);
      RETURN;
    IFEND;

    idx := LOWERBOUND (rmas);
    pc := osv$keypoint_control.cpus [#read_register (osc$pr_processor_id)].
          collector_pva;

    FOR i := 1 TO osv$keypoint_control.max_pages DO
      i#real_memory_address (pc, rmas [idx]);
      pc := #address (#ring (pc), #segment (pc), #offset (pc) + osv$page_size);
      idx := idx + 1;
    FOREND;

    page_size := osv$page_size;
    number_of_pages := osv$keypoint_control.max_pages;
    status.normal := TRUE;
  PROCEND osp$fetch_keypoint_buffer_rmas;
?? TITLE := 'osp$collection_file_info', EJECT ??

  PROCEDURE [XDCL, #GATE] osp$collection_file_info
    (   number_of_files: 0 .. 0ff(16);
        file_id_array: array [1 .. osc$max_number_of_processors] of amt$file_identifier;
        pva_array: array [1 .. osc$max_number_of_processors] of ^cell;
    VAR status: ost$status);

    VAR
       local_file_id_array: array [1 .. osc$max_number_of_processors] of amt$file_identifier,
       local_pva_array: array [1 .. osc$max_number_of_processors] of ^cell,
       local_number_of_files: 0 .. 0ff(16);

    status.normal := TRUE;
    local_pva_array := pva_array;
    local_file_id_array := file_id_array;
    local_number_of_files := number_of_files;
    osp$collection_file_info_r1 (local_pva_array, local_file_id_array, local_number_of_files);

  PROCEND osp$collection_file_info;

?? TITLE := 'osp$collection_file_info_r1', EJECT ??

  PROCEDURE [XDCL, #GATE] osp$fetch_collection_file_info
    (VAR pva_array: array [1 ..    osc$max_number_of_processors] of ^cell;
     VAR file_id_array: array [1 .. osc$max_number_of_processors] of amt$file_identifier;
     VAR num_of_files: 0 .. 0ff(16));

    VAR
      i: integer;

    num_of_files := osv$keypoint_control.number_of_keypoint_files;
    FOR i := 1 TO num_of_files DO
      pva_array [i] := osv$keypoint_control.keypoint_pva_array [i];
      file_id_array [i] := osv$keypoint_control.keypoint_file_array [i];
    FOREND;

 PROCEND osp$fetch_collection_file_info;

?? TITLE := 'osp$handle_keyp_environ_change', EJECT ??

{ Purpose:
{   This procedure is called in response to a system flag being set as a result of a CPU state change while
{   keypoints are being run.  The job which has the keypoint environment has had the environment taken away
{   from under it, and must be informed using this procedure.

  PROCEDURE [XDCL, #GATE] osp$handle_keyp_environ_change
    (    flag_id: ost$system_flag);

    VAR
      status: ost$status,
      log_name_selections: array [1 .. 2] of ost$name;

    osp$verify_system_privilege;

    log_name_selections [1] := 'JOB                            ';
    log_name_selections [2] := 'SYSTEM                         ';
    clp$log_comment (' *** KEYPOINT ENVIRONMENT RELEASED DURING CPU STATE CHANGE ***', log_name_selections,
          status);
    clp$put_job_command_response ('-- WARNING -- Keypoint environment released due to CPU state change',
          status);

  PROCEND osp$handle_keyp_environ_change;
MODEND osm$keypoint_support;
