#include <mach/mach.h>
#include <err.h>
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <libproc.h>
#include <mach/mach_time.h>
static void usage(void);
static void do_print(void);
static void do_difftime(bool usepercent, uint64_t *olduser, uint64_t *oldsystem, uint64_t *oldidle);
static void do_piddifftime(bool userpercent, int pid, uint64_t *old_pid_user, uint64_t *old_pid_system, uint64_t *old_pid_time);
static kern_return_t get_processor_time(uint64_t *user, uint64_t *sys, uint64_t *idle);
static kern_return_t get_processor_count(int *ncpu);
static mach_timebase_info_data_t timebase_info;
int
main(int argc, char *argv[])
{
int ch;
const char *optu = NULL;
const char *opts = NULL;
const char *opti = NULL;
const char *tpid = NULL;
const char *opt_sleep_time = NULL;
int sleep_time = 1;
int target_pid;
bool systemwide_time = false;
int pid;
int status;
uint64_t olduser, oldsystem, oldidle;
uint64_t old_pid_time;
uint64_t old_pid_user, old_pid_system;
kern_return_t kret;
bool usepercent = false;
bool recurring = false;
while ((ch = getopt(argc, argv, "PrT:t:pu:s:i:")) != -1) {
switch (ch) {
case 'P':
usepercent = true;
break;
case 'r':
recurring = true;
break;
case 't':
opt_sleep_time = optarg;
break;
case 'T':
tpid = optarg;
break;
case 'p':
systemwide_time = true;
break;
case 'u':
optu = optarg;
break;
case 's':
opts = optarg;
break;
case 'i':
opti = optarg;
break;
case '?':
default:
usage();
}
}
mach_timebase_info(&timebase_info);
if (opt_sleep_time) {
char *endstr;
sleep_time = (int)strtoul(opt_sleep_time, &endstr, 0);
if (opt_sleep_time[0] == '\0' || endstr[0] != '\0')
usage();
}
if (systemwide_time) {
bool first_pass = true;
olduser = oldsystem = oldidle = 0;
if (recurring != true) {
do_print();
exit(0);
}
do {
if (first_pass) {
do_difftime(false, &olduser, &oldsystem, &oldidle);
first_pass = false;
} else {
do_difftime(usepercent, &olduser, &oldsystem, &oldidle);
}
sleep(sleep_time);
} while (recurring);
exit(0);
}
if (tpid) {
char *endstr;
bool first_pass = true;
target_pid = (int)strtoul(tpid, &endstr, 0);
if (tpid[0] == '\0' || endstr[0] != '\0')
usage();
olduser = oldsystem = oldidle = 0;
old_pid_user = old_pid_system = old_pid_time = 0;
do {
if (first_pass) {
do_difftime(false, &olduser, &oldsystem, &oldidle);
do_piddifftime(false, target_pid, &old_pid_user, &old_pid_system, &old_pid_time);
first_pass = false;
} else {
do_difftime(usepercent, &olduser, &oldsystem, &oldidle);
do_piddifftime(usepercent, target_pid, &old_pid_user, &old_pid_system, &old_pid_time);
}
sleep(sleep_time);
} while (recurring);
exit(0);
}
if (optu || opts || opti) {
char *endstr;
if (!optu)
usage();
olduser = strtoull(optu, &endstr, 0);
if (optu[0] == '\0' || endstr[0] != '\0')
usage();
if (!opts)
usage();
oldsystem = strtoull(opts, &endstr, 0);
if (opts[0] == '\0' || endstr[0] != '\0')
usage();
if (!opti)
usage();
oldidle = strtoull(opti, &endstr, 0);
if (opti[0] == '\0' || endstr[0] != '\0')
usage();
do_difftime(usepercent, &olduser, &oldsystem, &oldidle);
exit(0);
}
argc -= optind;
argv += optind;
if (argc == 0)
usage();
do {
kret = get_processor_time(&olduser, &oldsystem, &oldidle);
if (kret)
errx(1, "Error getting processor time: %s (%d)", mach_error_string(kret), kret);
switch(pid = vfork()) {
case -1:
perror("time");
exit(1);
case 0:
execvp(*argv, argv);
perror(*argv);
_exit((errno == ENOENT) ? 127 : 126);
}
(void)signal(SIGINT, SIG_IGN);
(void)signal(SIGQUIT, SIG_IGN);
while (wait(&status) != pid);
(void)signal(SIGINT, SIG_DFL);
(void)signal(SIGQUIT, SIG_DFL);
do_difftime(usepercent, &olduser, &oldsystem, &oldidle);
sleep(sleep_time);
} while (recurring);
exit (WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE);
return 0;
}
static void
usage(void)
{
fprintf(stderr, "usage: systime [-P] [-r] [-t sleep_time] utility [argument ...]\n"
" systime -p [-r] [-t sleep_time]\n"
" systime [-P] -u user -s sys -i idle\n"
" systime [-P] [-r] [-t sleep_time] -T target_pid\n");
exit(1);
}
static void
do_print(void)
{
uint64_t user, system, idle;
kern_return_t kret;
kret = get_processor_time(&user, &system, &idle);
if (kret)
errx(1, "Error getting processor time: %s (%d)", mach_error_string(kret), kret);
printf("systime_user=%llu\n", user);
printf("systime_sys=%llu\n", system);
printf("systime_idle=%llu\n", idle);
}
static void
do_difftime(bool usepercent, uint64_t *olduser, uint64_t *oldsystem, uint64_t *oldidle)
{
uint64_t user, system, idle;
uint64_t userelapsed, systemelapsed, idleelapsed, totalelapsed;
kern_return_t kret;
kret = get_processor_time(&user, &system, &idle);
if (kret)
errx(1, "Error getting processor time: %s (%d)", mach_error_string(kret), kret);
userelapsed = user - *olduser;
systemelapsed = system - *oldsystem;
idleelapsed = idle - *oldidle;
totalelapsed = userelapsed + systemelapsed + idleelapsed;
if (usepercent) {
fprintf(stderr, "%1.02f%% user %1.02f%% sys %1.02f%% idle\n",
((double)userelapsed * 100)/totalelapsed,
((double)systemelapsed * 100)/totalelapsed,
((double)idleelapsed * 100)/totalelapsed);
} else {
int ncpu;
kret = get_processor_count(&ncpu);
if (kret)
errx(1, "Error getting processor count: %s (%d)", mach_error_string(kret), kret);
fprintf(stderr, "%1.02f real %1.02f user %1.02f sys\n",
((double)totalelapsed) / 1000 / ncpu,
((double)userelapsed) / 1000,
((double)systemelapsed) / 1000);
}
*olduser = user;
*oldsystem = system;
*oldidle = idle;
}
static void
do_piddifftime(bool usepercent, int pid, uint64_t *old_pid_user, uint64_t *old_pid_system, uint64_t *old_pid_time)
{
uint64_t pid_user, pid_system, pid_time;
uint64_t userelapsed, systemelapsed, totalelapsed;
struct proc_taskinfo info;
int size;
size = proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &info, sizeof(info));
if (size == PROC_PIDTASKINFO_SIZE) {
pid_user = info.pti_total_user;
pid_system = info.pti_total_system;
} else {
fprintf(stderr, "Error in proc_pidinfo(): %s",
strerror(errno));
exit(1);
}
pid_time = mach_absolute_time();
userelapsed = pid_user - *old_pid_user;
systemelapsed = pid_system - *old_pid_system;
totalelapsed = pid_time - *old_pid_time;
if (usepercent) {
fprintf(stderr, "Pid %d: %1.02f%% user %1.02f%% sys\n",
pid,
((double)userelapsed * 100)/totalelapsed,
((double)systemelapsed * 100)/totalelapsed);
} else {
fprintf(stderr, "Pid %d: %1.02f user %1.02f sys\n",
pid,
(((double)userelapsed) * timebase_info.numer / timebase_info.denom) / 1000000000,
(((double)systemelapsed) * timebase_info.numer / timebase_info.denom) / 1000000000);
}
*old_pid_user = pid_user;
*old_pid_system = pid_system;
*old_pid_time = pid_time;
}
static kern_return_t
get_processor_time(uint64_t *user, uint64_t *sys, uint64_t *idle)
{
host_name_port_t host;
kern_return_t kret;
host_cpu_load_info_data_t host_load;
mach_msg_type_number_t count;
host = mach_host_self();
count = HOST_CPU_LOAD_INFO_COUNT;
kret = host_statistics(host, HOST_CPU_LOAD_INFO, (host_info_t)&host_load, &count);
if (kret)
return kret;
*user = ((uint64_t)host_load.cpu_ticks[CPU_STATE_USER]) * 10 ;
*sys = ((uint64_t)host_load.cpu_ticks[CPU_STATE_SYSTEM]) * 10;
*idle = ((uint64_t)host_load.cpu_ticks[CPU_STATE_IDLE]) * 10;
return KERN_SUCCESS;
}
static kern_return_t
get_processor_count(int *ncpu)
{
host_name_port_t host;
kern_return_t kret;
host_basic_info_data_t hi;
mach_msg_type_number_t count;
host = mach_host_self();
count = HOST_BASIC_INFO_COUNT;
kret = host_info(host, HOST_BASIC_INFO, (host_info_t)&hi, &count);
if (kret)
return kret;
*ncpu = hi.avail_cpus;
return KERN_SUCCESS;
}