Black Hill Software

  • Home
  • Products
    • EasySMF
      • Release Notes and Latest Version
      • Online Manual
      • License Agreement
    • EasySMF:JE
      • EasySMF:JE Java Quickstart
      • Release Notes and Latest Version
      • Javadoc
      • EasySMF JSON Javadoc
      • License Agreement
    • 30 Day Trial
  • Purchase
    • How to Buy
    • Purchase a License
  • Support
    • EasySMF Support
    • Get the latest version of EasySMF
    • EasySMF:JE Support
    • Get the latest version of EasySMF:JE
  • News
  • Contact
    • Support
    • Sales

Java SMF Sample 2 : Prime Shift Top CPU Consumers

November 4, 2014 by Andrew

This second sample illustrates the use of java.time functions to report based on time and the day of the week. It reports the top 10 CPU consuming jobs by jobname between 8:30 am and 5:30 pm for each weekday.

Opening the file and reading the records work the same way as the previous sample. However in this case the processing is slightly more complicated.

Program Setup

We use a HashMap<String, JobData> to map job names to the cumulative job information. We want to report the days of the week separately, so we create a List containing a Map for each day of the week. The days of the week are numbered 1-7. We create a list of 8 entries so we can use indexes 1-7, leaving entry 0 unused.

List<HashMap<String, JobData>> jobsByDay = 
        new ArrayList<HashMap<String, JobData>>();
        
for (int i=0; i <= 7; i++) 
{ 
      jobsByDay.add(new HashMap<String, JobData>());
}

Java Time LocalTime variables are used to define the times of day we are interested in:

LocalTime primeStart = new LocalTime(8, 30);
LocalTime primeEnd = new LocalTime(17, 30);

Processing the records

Firstly we check whether the record is one we are interested in: type 30 subtype 2 and 3, written during the prime shift we defined.

if (record.recordType() == 30 
    && (record.subType() == 2 || record.subType() == 3)) 
{               
    DayOfWeek recordDayOfWeek = record.smfDate().getDayOfWeek();
    LocalTime recordTime = record.smfTime();
                
    if (recordDayOfWeek.getValue() >= DayOfWeek.MONDAY.getValue()
        && recordDayOfWeek.getValue() <= DayOfWeek.FRIDAY.getValue()
        && recordTime.isAfter(primeStart) 
        && recordTime.isBefore(primeEnd)) 
    {
        // process this record...
    }
}

For each record that matches we need to:

  1. Create a Smf30Record from the SmfRecord, to give access to the type 30 information.
  2. Get the collection of jobs for this day of the week.
  3. Extract the job name from the Identification Section, and look for existing statistics for the job name.
  4. Update or add job statistics with information from the Processor Accounting Section.

Each type 30 record has an Identification Section, so the r30.identificationSection() returns that section directly. However, records may or may not have a Processor Accounting Section. The method r30.processorAccountingSections() returns a List which contains 0 or 1 entry. This means that we can iterate over the list and it will happily process 0 sections from records without a processor accounting section. An explicit check for the number of sections isn’t required, and the method of processing can be consistent for all section types where the number of sections is variable.

Smf30Record r30 = new Smf30Record(record);
              
for (ProcessorAccountingSection proc : r30.processorAccountingSections())
{
    Map<String, JobData> day = jobsByDay.get(recordDayOfWeek.getValue());
                  
    String jobname = r30.identificationSection().smf30jbn();
    JobData jobentry = day.get(jobname);
    if (jobentry == null)
    {
        day.put(jobname, new JobData(proc));
    }
    else
    {
        jobentry.add(proc);
    }
}

Collecting the CPU times

We collect each of CP, zIIP and zAAP time from the processor accounting section.

private static class JobData {
    JobData(ProcessorAccountingSection proc)
    {
        add(proc);
    }
        
    public void add(ProcessorAccountingSection proc)
    {
        cpTime = cpTime.plus(proc.smf30cpt())
            .plus(proc.smf30cps())
            .minus(proc.smf30TimeOnIfa()) 
            .minus(proc.smf30TimeOnZiip());
        zaapTime = zaapTime.plus(proc.smf30TimeOnIfa());
        ziipTime ziipTime.plus(proc.smf30TimeOnZiip());
    }
        
    Duration cpTime = Duration.ZERO;
    Duration ziipTime = Duration.ZERO;
    Duration zaapTime = Duration.ZERO;
}

Producing the Output

The writeReport() method produces the output. We produce a report for each day of the week where we collected job information. For each day we sort the jobs by CP time using the same method as Sample 1, then output the top 10 jobs.

Accumulated CPU time is written in the format hhh:mm:ss. We create a helper method to format the java.time.duration as hhmmss.

private static String hhhmmss(Duration dur)
{
    long hours = dur.toHours();
    long minutes = dur.minus(Duration.ofHours(hours)).toMinutes();
    long seconds = dur.minus(Duration.ofHours(hours))
       .minus(Duration.ofMinutes(minutes)).toMillis() / 1000;
    return String.format("%d:%02d:%02d", hours, minutes, seconds);
}

EasySMF:JE provides a helper method to divide one duration by another, e.g. when calculating CPU time as a percentage of elapsed time or one job CPU time as a percentage of total CPU time.
Utils.divideDurations(jobinfo.cpTime, totalCp) * 100
Output looks like:

Tuesday 
Name             CPU  CPU%        zIIP        zAAP 
CICSP        1:31:32    5%       00:08       00:00 
DB2PDIST     1:30:34    5%     4:55:55       00:00 
DB2PDBM1     1:02:36    4%       00:00       00:00 
...

The Complete Java Program

package com.blackhillsoftware.samples;

import java.io.FileInputStream;
import java.io.IOException;
import java.time.*;
import java.util.*;
import java.util.Map.*;

import com.blackhillsoftware.smf.SmfRecord;
import com.blackhillsoftware.smf.SmfRecordReader;
import com.blackhillsoftware.smf.Utils;
import com.blackhillsoftware.smf.smf30.ProcessorAccountingSection;
import com.blackhillsoftware.smf.smf30.Smf30Record;

public class PrimeShiftTopJobs
{
   public static void main(String[] args) throws IOException
   {

      LocalTime primeStart = LocalTime.of(8, 30);
      LocalTime primeEnd = LocalTime.of(17, 30);

      // We need a Jobname->JobData map for each day of the week.
      // So we will create a List (array) of HashMap<String, JobData>.

      List<HashMap<String, JobData>> jobsByDay = 
                new ArrayList<HashMap<String, JobData>>();

      // Populate the list for each day of the week
      // We need entries for days 1-7. Entry 0 is unused
      for (int i = 0; i <= 7; i++)
      {
         jobsByDay.add(new HashMap<String, JobData>());
      }

      // Read and process the data

      try (SmfRecordReader reader = 
         args.length == 0 ?
         SmfRecordReader.fromDD("INPUT") :
         SmfRecordReader.fromStream(new FileInputStream(args[0]))) 
      { 

          for (SmfRecord record : reader)
          {
             // We are only interested in type 30 subtype 2 or 3
             if (record.recordType() == 30
                && (record.subType() == 2 || record.subType() == 3))
             {

                 DayOfWeek recordDayOfWeek = record.smfDate().getDayOfWeek();
                 LocalTime recordTime = record.smfTime();

                 // check if SMF record was created during prime shift
                 // Monday-Friday
                 if (recordDayOfWeek.getValue() >= DayOfWeek.MONDAY.getValue()
                     && recordDayOfWeek.getValue() <= DayOfWeek.FRIDAY.getValue()
                     && recordTime.isAfter(primeStart)
                     && recordTime.isBefore(primeEnd))
                 {

                     // Create a type 30 record from the original record
                     Smf30Record r30 = new Smf30Record(record);

                     // Iterate over the processor accounting sections
                     // Since there is either 0 or 1 section in a record, this is
                     // just a convenient way to skip records without the section
                     for (ProcessorAccountingSection proc : r30
                              .processorAccountingSections())
                     {
                        // get collection of jobs for this day
                        Map<String, JobData> day = jobsByDay
                           .get(recordDayOfWeek.getValue());

                        String jobname = r30.identificationSection().smf30jbn();
                        JobData jobentry = day.get(jobname);
                        if (jobentry == null) // nothing for this jobname,
                        // create new
                        {
                           day.put(jobname, new JobData(proc));
                        }
                        else
                        {
                           jobentry.add(proc);
                        }
                    }
                 }
              }
          };
       }

       writeReport(jobsByDay);
    }

   private static void writeReport(List<HashMap<String, JobData>> dailyJobs)
   {

      for (DayOfWeek day : DayOfWeek.values())
      {
         // get the jobs for the day as a List so we can sort them
         List<Entry<String, JobData>> jobs = new ArrayList<Entry<String, JobData>>(
            dailyJobs.get(day.getValue()).entrySet());

         if (jobs.size() > 0) // if we have data for this day
         {
            // sort list by CPU time descending
            Collections.sort(jobs, new Comparator<Entry<String, JobData>>()
            {
               public int compare(Entry<String, JobData> s1,
                  Entry<String, JobData> s2)
               {
                  return s2.getValue().cpTime.compareTo(s1.getValue().cpTime);
               }
            });

            // calculate total CPU for the day
            Duration totalCp = Duration.ZERO;
            for (Entry<String, JobData> entry : jobs)
            {
               totalCp = totalCp.plus(entry.getValue().cpTime);
            }

            // write output for top 10 job s

            // Headings
            System.out.format("%n%s%n", day.toString());
            System.out.format("%-8s %11s %5s %11s %11s %n", "Name", "CPU",
               "CPU%", "zIIP", "zAAP");

            int count = 0;
            for (Entry<String, JobData> entry : jobs)
            {
               JobData jobinfo = entry.getValue();

               // write detail line

               System.out.format(
                  "%-8s %11s %4.0f%% %11s %11s %n",
                  entry.getKey(), // jobname
                  hhhmmss(jobinfo.cpTime),
                  Utils.divideDurations(jobinfo.cpTime, totalCp) * 100,
                  hhhmmss(jobinfo.ziipTime),
                  hhhmmss(jobinfo.zaapTime));

               // limit to top 10 jobs
               if (++count >= 10)
                  break;
            }
         }
      }
   }

   private static String hhhmmss(Duration dur)
   {
      long hours = dur.toHours();
      long minutes = dur.minus(Duration.ofHours(hours)).toMinutes();
      long seconds = dur.minus(Duration.ofHours(hours))
         .minus(Duration.ofMinutes(minutes)).toMillis() / 1000;
      return String.format("%d:%02d:%02d", hours, minutes, seconds);
   }

    private static class JobData
    {
       JobData(ProcessorAccountingSection proc)
       {
          add(proc);
       }

       public void add(ProcessorAccountingSection proc)
       {
          cpTime = cpTime.plus(proc.smf30cpt()).plus(proc.smf30cps())
             .minus(proc.smf30TimeOnIfa()).minus(proc.smf30TimeOnZiip());
          zaapTime = zaapTime.plus(proc.smf30TimeOnIfa());
          ziipTime = ziipTime.plus(proc.smf30TimeOnZiip());
       }

       Duration cpTime = Duration.ZERO;
       Duration ziipTime = Duration.ZERO;
       Duration zaapTime = Duration.ZERO;
    }

}

Filed Under: Java SMF

30 Day Trial

EasySMF and EasySMF:JE are available for a free 30 day trial. Download now and start using them immediately.
30 Day Trial

Information

EasySMF:JE Java API for SMF Quickstart

EasySMF:JE Sample 1 : SMF Records by type and subtype

Systems Programmer Friendly Java

Sending Email from z/OS using Java

Sign up for EasySMF News

Stay up to date. Enter your email address to receive updates about EasySMF.
unsubscribe from list

Black Hill Software

Suite 10b, 28 University Drive, Mt Helen, VIC 3350, Australia
PO Box 2214, Bakery Hill, VIC 3354, Australia
+61 3 5331 8201
+1 (310) 634 9882
info@blackhillsoftware.com

News

  • Finding UID 0 work on z/OS using SMF Data
  • Apache Log4j CVE-2021-44228 Information
  • Java vs C++ : Drag Racing on z/OS

Twitter

My Tweets

Copyright © 2023 · Enterprise Pro Theme on Genesis Framework · WordPress · Log in