#!/usr/bin/sh
 #
 # cputimes - print CPU time consumed by Kernel/Idle/Processes.
 #            Written using DTrace (Solaris 10 3/05).
 #
 # $Id: cputimes 3 2007-08-01 10:50:08Z brendan $
 #
 # This program accurately measures time consumed by the kernel, but in
 # doing so creates extra kernel load of it's own. The extra kernel
 # activity can be measured by running one cputimes and then another, and
 # comparing the difference in kernel consumed time. This method can be
 # used to estimate the load created by other DTrace scripts.
 #
 # USAGE:	cputimes [-ahTV] [-t top] [interval [count]]
 #
 #		-a                # print all processes
 #		-T                # print totals
 #		-V                # don't print timestamps
 #		-t num            # print top num lines only
 #  eg,
 #		cputimes 1        # print every 1 second
 #		cputimes -a 10    # print all processes every 10 secs
 #		cputimes -at 8 5  # print top 8 lines every 5 secs
 #
 #
 # FIELDS: 
 #		THREADS         The following or the process name,
 #		IDLE            Idle time - CPU running idle thread
 #		KERNEL          Kernel time - Kernel servicing interrupts, ...
 #		PROCESS         Process time - PIDs running on the system
 #		TIME (ns)       Sum of the CPU time, ns (nanoseconds)
 #
 # NOTES:
 # * This takes into account multiple CPU servers, the total 
 # seconds consumed will be a multiple of the CPU count and interval.
 #
 # SEE ALSO: cpudists
 #           Heisenberg's uncertainty principle.
 #
 # COPYRIGHT: Copyright (c) 2005 Brendan Gregg.
 #
 # CDDL HEADER START
 #
 #  The contents of this file are subject to the terms of the
 #  Common Development and Distribution License, Version 1.0 only
 #  (the "License").  You may not use this file except in compliance
 #  with the License.
 #
 #  You can obtain a copy of the license at Docs/cddl1.txt
 #  or http://www.opensolaris.org/os/licensing.
 #  See the License for the specific language governing permissions
 #  and limitations under the License.
 #
 # CDDL HEADER END
 #
 # Author: Brendan Gregg  [Sydney, Australia]
 #
 # 27-Apr-2005   Brendan Gregg   Created this.
 # 22-Sep-2005      "      "	Fixed a key corruption bug.
 # 22-Sep-2005      "      "	Last update.
 #
 
 
 ##############################
 # --- Process Arguments ---
 #
 opt_all=0; opt_time=1; opt_top=0; opt_totals=0
 top=0; interval=1; count=1
 
 while getopts aht:TV name
 do
         case $name in
         a)      opt_all=1 ;;
         T)      opt_totals=1 ;;
         V)      opt_time=0 ;;
         t)      opt_top=1; top=$OPTARG ;;
         h|?)    cat <<-END >&2
 		USAGE: cputimes [-ahTV] [-t top] [interval [count]]
 		       cputimes                  # default output
 		               -a                # print all processes
 		               -T                # print totals
 		               -V                # don't print times
 		               -t num            # print top num lines only
 		          eg,
 		               cputimes 1        # print every 1 second
 		               cputimes -a 10    # all processes per 10 sec
 		               cputimes -at 8 5  # top 8 lines every 5 secs
 		END
 		exit 1
         esac
 done
 shift `expr $OPTIND - 1`
 
 if [ "$1" -gt 0 ]; then
         interval=$1; count=-1; shift
 fi
 if [ "$1" -gt 0 ]; then
 	count=$1; shift
 fi
 
 
 #################################
 # --- Main Program, DTrace ---
 #
 /usr/sbin/dtrace -n '
  #pragma D option quiet
 
  /*
   * Command line arguments
   */
  inline int OPT_all    = '$opt_all';
  inline int OPT_time   = '$opt_time';
  inline int OPT_totals = '$opt_totals';
  inline int OPT_top    = '$opt_top';
  inline int TOP        = '$top';
  inline int INTERVAL   = '$interval';
  inline int COUNTER    = '$count';
 
  /* Initialise variables */
  dtrace:::BEGIN
  {
 	cpustart[cpu] = 0;
 	counts = COUNTER;
 	secs = INTERVAL;
  }
 
  /* Flag this thread as idle */
  sysinfo:unix:idle_enter:idlethread
  {
 	idle[cpu] = 1;
  }
 
  /* Save kernel time between running threads */
  sched:::on-cpu 
  /cpustart[cpu]/
  {
 	this->elapsed = timestamp - cpustart[cpu];
 	@Procs["KERNEL"] = sum(this->elapsed);
  }
 
  /* Save the elapsed time of a thread */
  sched:::off-cpu,
  sched:::remain-cpu,
  profile:::profile-1sec
  /cpustart[cpu]/
  {
 	/* determine the name for this thread */
 	program[cpu] = pid == 0 ? idle[cpu] ? "IDLE" : "KERNEL" :
 	    OPT_all ? execname : "PROCESS";
 
 	/* save elapsed */
 	this->elapsed = timestamp - cpustart[cpu];
 	@Procs[program[cpu]] = sum(this->elapsed);
 	cpustart[cpu] = timestamp;
  }
 
  /* Record the start time of a thread */
  sched:::on-cpu,
  sched:::remain-cpu
  {
 	idle[cpu] = 0;
 	cpustart[cpu] = timestamp;
  }
 
 
  profile:::tick-1sec
  {
 	secs--;
  }
 
  /* Print time */
  profile:::tick-1sec 
  /secs == 0/
  { 
 	OPT_time ? printf("%Y,\n", walltimestamp) : 1;
 	printf("%16s %16s\n", "THREADS", "TIME (ns)");
  }
 
  /* Print report */
  profile:::tick-1sec 
  /secs == 0/ 
  { 
 	OPT_top ? trunc(@Procs, TOP) : 1;
 	printa("%16s %@16d\n", @Procs);
 	trunc(@Procs);
 	secs = INTERVAL;
 	counts--;
  }
 
  /* End of program */
  profile:::tick-1sec 
  /counts == 0/ 
  {
 	exit(0);
  }
 
  /* cleanup for Ctrl-C */
  dtrace:::END
  {
 	trunc(@Procs);
  }
 '