/* * Get the time accounting information for the calling LWP. */ int lwp_info(timestruc_t *tvp) { timestruc_t tv[2]; hrtime_t hrutime, hrstime; klwp_t *lwp = ttolwp(curthread); hrutime = lwp->lwp_mstate.ms_acct[LMS_USER]; hrstime = lwp->lwp_mstate.ms_acct[LMS_SYSTEM] + lwp->lwp_mstate.ms_acct[LMS_TRAP]; scalehrtime(&hrutime); scalehrtime(&hrstime); hrt2ts(hrutime, &tv[0]); hrt2ts(hrstime, &tv[1]); if (get_udatamodel() == DATAMODEL_NATIVE) { if (copyout(tv, tvp, sizeof (tv))) return (set_errno(EFAULT)); } else { timestruc32_t tv32[2]; if (TIMESPEC_OVERFLOW(&tv[0]) || TIMESPEC_OVERFLOW(&tv[1])) return (set_errno(EOVERFLOW)); /* unlikely */ TIMESPEC_TO_TIMESPEC32(&tv32[0], &tv[0]); TIMESPEC_TO_TIMESPEC32(&tv32[1], &tv[1]); if (copyout(tv32, tvp, sizeof (tv32))) return (set_errno(EFAULT)); } return (0); }
/* * Given a source tick, stick, and tod value, set the tick and stick offsets * such that the (current physical register value) + offset == (source value) * and in addition account for some variation between the %tick/%stick on * different CPUs. We account for this variation by adding in double the value * of suspend_tick_stick_max_delta. The following is an explanation of why * suspend_tick_stick_max_delta must be multplied by two and added to * native_stick_offset. * * Consider a guest instance that is yet to be suspended with CPUs p0 and p1 * with physical "source" %stick values s0 and s1 respectively. When the guest * is first resumed, the physical "target" %stick values are t0 and t1 * respectively. The virtual %stick values after the resume are v0 and v1 * respectively. Let x be the maximum difference between any two CPU's %stick * register at a given point in time and let the %stick values be assigned * such that * * s1 = s0 + x and * t1 = t0 - x * * Let us assume that p0 is driving the suspend and resume. Then, we will * calculate the stick offset f and the virtual %stick on p0 after the * resume as follows. * * f = s0 - t0 and * v0 = t0 + f * * We calculate the virtual %stick v1 on p1 after the resume as * * v1 = t1 + f * * Substitution yields * * v1 = t1 + (s0 - t0) * v1 = (t0 - x) + (s0 - t0) * v1 = -x + s0 * v1 = s0 - x * v1 = (s1 - x) - x * v1 = s1 - 2x * * Therefore, in this scenario, without accounting for %stick variation in * the calculation of the native_stick_offset f, the virtual %stick on p1 * is less than the value of the %stick on p1 before the suspend which is * unacceptable. By adding 2x to v1, we guarantee it will be equal to s1 * which means the %stick on p1 after the resume will always be greater * than or equal to the %stick on p1 before the suspend. Since v1 = t1 + f * at any point in time, we can accomplish this by adding 2x to f. This * guarantees any processes bound to CPU P0 or P1 will not see a %stick * decrease across a suspend/resume. Hence, in the code below, we multiply * suspend_tick_stick_max_delta by two in the calculation for * native_stick_offset, native_tick_offset, and target_hrtime. */ static void set_tick_offsets(uint64_t source_tick, uint64_t source_stick, timestruc_t *tsp) { uint64_t target_tick; uint64_t target_stick; hrtime_t source_hrtime; hrtime_t target_hrtime; /* * Temporarily set the offsets to zero so that the following reads * of the registers will yield physical unadjusted counter values. */ native_tick_offset = 0; native_stick_offset = 0; target_tick = gettick_counter(); /* returns %tick */ target_stick = gettick(); /* returns %stick */ /* * Calculate the new offsets. In addition to the delta observed on * this CPU, add an additional value. Multiply the %tick/%stick * frequency by suspend_tick_stick_max_delta (us). Then, multiply by 2 * to account for a delta between CPUs before the suspend and a * delta between CPUs after the resume. */ native_tick_offset = (source_tick - target_tick) + (CPU->cpu_curr_clock * suspend_tick_stick_max_delta * 2 / MICROSEC); native_stick_offset = (source_stick - target_stick) + (sys_tick_freq * suspend_tick_stick_max_delta * 2 / MICROSEC); /* * We've effectively increased %stick and %tick by twice the value * of suspend_tick_stick_max_delta to account for variation across * CPUs. Now adjust the preserved TOD by the same amount. */ source_hrtime = ts2hrt(tsp); target_hrtime = source_hrtime + (suspend_tick_stick_max_delta * 2 * (NANOSEC/MICROSEC)); hrt2ts(target_hrtime, tsp); }
/* * Arrange for the real time profiling signal to be dispatched. */ void realsigprof(int sysnum, int error) { proc_t *p; klwp_t *lwp; if (curthread->t_rprof->rp_anystate == 0) return; p = ttoproc(curthread); lwp = ttolwp(curthread); mutex_enter(&p->p_lock); if (sigismember(&p->p_ignore, SIGPROF) || signal_is_blocked(curthread, SIGPROF)) { mutex_exit(&p->p_lock); return; } lwp->lwp_siginfo.si_signo = SIGPROF; lwp->lwp_siginfo.si_code = PROF_SIG; lwp->lwp_siginfo.si_errno = error; hrt2ts(gethrtime(), &lwp->lwp_siginfo.si_tstamp); lwp->lwp_siginfo.si_syscall = sysnum; lwp->lwp_siginfo.si_nsysarg = (sysnum > 0 && sysnum < NSYSCALL) ? LWP_GETSYSENT(lwp)[sysnum].sy_narg : 0; lwp->lwp_siginfo.si_fault = lwp->lwp_lastfault; lwp->lwp_siginfo.si_faddr = lwp->lwp_lastfaddr; lwp->lwp_lastfault = 0; lwp->lwp_lastfaddr = NULL; sigtoproc(p, curthread, SIGPROF); mutex_exit(&p->p_lock); ASSERT(lwp->lwp_cursig == 0); if (issig(FORREAL)) { psig(); } mutex_enter(&p->p_lock); lwp->lwp_siginfo.si_signo = 0; bzero(curthread->t_rprof, sizeof (*curthread->t_rprof)); mutex_exit(&p->p_lock); }
void exacct_calculate_proc_usage(proc_t *p, proc_usage_t *pu, ulong_t *mask, int flag, int wstat) { timestruc_t ts, ts_run; ASSERT(MUTEX_HELD(&p->p_lock)); /* * Convert CPU and execution times to sec/nsec format. */ if (BT_TEST(mask, AC_PROC_CPU)) { hrt2ts(mstate_aggr_state(p, LMS_USER), &ts); pu->pu_utimesec = (uint64_t)(ulong_t)ts.tv_sec; pu->pu_utimensec = (uint64_t)(ulong_t)ts.tv_nsec; hrt2ts(mstate_aggr_state(p, LMS_SYSTEM), &ts); pu->pu_stimesec = (uint64_t)(ulong_t)ts.tv_sec; pu->pu_stimensec = (uint64_t)(ulong_t)ts.tv_nsec; } if (BT_TEST(mask, AC_PROC_TIME)) { gethrestime(&ts); pu->pu_finishsec = (uint64_t)(ulong_t)ts.tv_sec; pu->pu_finishnsec = (uint64_t)(ulong_t)ts.tv_nsec; hrt2ts(gethrtime() - p->p_mstart, &ts_run); ts.tv_sec -= ts_run.tv_sec; ts.tv_nsec -= ts_run.tv_nsec; if (ts.tv_nsec < 0) { ts.tv_sec--; if ((ts.tv_nsec = ts.tv_nsec + NANOSEC) >= NANOSEC) { ts.tv_sec++; ts.tv_nsec -= NANOSEC; } } pu->pu_startsec = (uint64_t)(ulong_t)ts.tv_sec; pu->pu_startnsec = (uint64_t)(ulong_t)ts.tv_nsec; } pu->pu_pid = p->p_pidp->pid_id; pu->pu_acflag = p->p_user.u_acflag; pu->pu_projid = p->p_task->tk_proj->kpj_id; pu->pu_taskid = p->p_task->tk_tkid; pu->pu_major = getmajor(p->p_sessp->s_dev); pu->pu_minor = getminor(p->p_sessp->s_dev); pu->pu_ancpid = p->p_ancpid; pu->pu_wstat = wstat; /* * Compute average RSS in K. The denominator is the number of * samples: the number of clock ticks plus the initial value. */ pu->pu_mem_rss_avg = (PTOU(p)->u_mem / (p->p_stime + p->p_utime + 1)) * (PAGESIZE / 1024); pu->pu_mem_rss_max = PTOU(p)->u_mem_max * (PAGESIZE / 1024); mutex_enter(&p->p_crlock); pu->pu_ruid = crgetruid(p->p_cred); pu->pu_rgid = crgetrgid(p->p_cred); mutex_exit(&p->p_crlock); bcopy(p->p_user.u_comm, pu->pu_command, strlen(p->p_user.u_comm) + 1); bcopy(p->p_zone->zone_name, pu->pu_zonename, strlen(p->p_zone->zone_name) + 1); bcopy(p->p_zone->zone_nodename, pu->pu_nodename, strlen(p->p_zone->zone_nodename) + 1); /* * Calculate microstate accounting data for a process that is still * running. Presently, we explicitly collect all of the LWP usage into * the proc usage structure here. */ if (flag & EW_PARTIAL) exacct_calculate_proc_mstate(p, pu); if (flag & EW_FINAL) exacct_copy_proc_mstate(p, pu); }
static int exacct_attach_task_item(task_t *tk, task_usage_t *tu, ea_object_t *record, int res) { int attached = 1; switch (res) { case AC_TASK_TASKID: (void) ea_attach_item(record, &tk->tk_tkid, sizeof (uint32_t), EXT_UINT32 | EXD_TASK_TASKID); break; case AC_TASK_PROJID: (void) ea_attach_item(record, &tk->tk_proj->kpj_id, sizeof (uint32_t), EXT_UINT32 | EXD_TASK_PROJID); break; case AC_TASK_CPU: { timestruc_t ts; uint64_t ui; hrt2ts(tu->tu_stime, &ts); ui = ts.tv_sec; (void) ea_attach_item(record, &ui, sizeof (uint64_t), EXT_UINT64 | EXD_TASK_CPU_SYS_SEC); ui = ts.tv_nsec; (void) ea_attach_item(record, &ui, sizeof (uint64_t), EXT_UINT64 | EXD_TASK_CPU_SYS_NSEC); hrt2ts(tu->tu_utime, &ts); ui = ts.tv_sec; (void) ea_attach_item(record, &ui, sizeof (uint64_t), EXT_UINT64 | EXD_TASK_CPU_USER_SEC); ui = ts.tv_nsec; (void) ea_attach_item(record, &ui, sizeof (uint64_t), EXT_UINT64 | EXD_TASK_CPU_USER_NSEC); } break; case AC_TASK_TIME: (void) ea_attach_item(record, &tu->tu_startsec, sizeof (uint64_t), EXT_UINT64 | EXD_TASK_START_SEC); (void) ea_attach_item(record, &tu->tu_startnsec, sizeof (uint64_t), EXT_UINT64 | EXD_TASK_START_NSEC); (void) ea_attach_item(record, &tu->tu_finishsec, sizeof (uint64_t), EXT_UINT64 | EXD_TASK_FINISH_SEC); (void) ea_attach_item(record, &tu->tu_finishnsec, sizeof (uint64_t), EXT_UINT64 | EXD_TASK_FINISH_NSEC); break; case AC_TASK_HOSTNAME: (void) ea_attach_item(record, tk->tk_zone->zone_nodename, strlen(tk->tk_zone->zone_nodename) + 1, EXT_STRING | EXD_TASK_HOSTNAME); break; case AC_TASK_MICROSTATE: (void) ea_attach_item(record, &tu->tu_majflt, sizeof (uint64_t), EXT_UINT64 | EXD_TASK_FAULTS_MAJOR); (void) ea_attach_item(record, &tu->tu_minflt, sizeof (uint64_t), EXT_UINT64 | EXD_TASK_FAULTS_MINOR); (void) ea_attach_item(record, &tu->tu_sndmsg, sizeof (uint64_t), EXT_UINT64 | EXD_TASK_MESSAGES_SND); (void) ea_attach_item(record, &tu->tu_rcvmsg, sizeof (uint64_t), EXT_UINT64 | EXD_TASK_MESSAGES_RCV); (void) ea_attach_item(record, &tu->tu_iblk, sizeof (uint64_t), EXT_UINT64 | EXD_TASK_BLOCKS_IN); (void) ea_attach_item(record, &tu->tu_oblk, sizeof (uint64_t), EXT_UINT64 | EXD_TASK_BLOCKS_OUT); (void) ea_attach_item(record, &tu->tu_ioch, sizeof (uint64_t), EXT_UINT64 | EXD_TASK_CHARS_RDWR); (void) ea_attach_item(record, &tu->tu_vcsw, sizeof (uint64_t), EXT_UINT64 | EXD_TASK_CONTEXT_VOL); (void) ea_attach_item(record, &tu->tu_icsw, sizeof (uint64_t), EXT_UINT64 | EXD_TASK_CONTEXT_INV); (void) ea_attach_item(record, &tu->tu_nsig, sizeof (uint64_t), EXT_UINT64 | EXD_TASK_SIGNALS); (void) ea_attach_item(record, &tu->tu_nswp, sizeof (uint64_t), EXT_UINT64 | EXD_TASK_SWAPS); (void) ea_attach_item(record, &tu->tu_nscl, sizeof (uint64_t), EXT_UINT64 | EXD_TASK_SYSCALLS); break; case AC_TASK_ANCTASKID: (void) ea_attach_item(record, &tu->tu_anctaskid, sizeof (uint32_t), EXT_UINT32 | EXD_TASK_ANCTASKID); break; case AC_TASK_ZONENAME: (void) ea_attach_item(record, tk->tk_zone->zone_name, strlen(tk->tk_zone->zone_name) + 1, EXT_STRING | EXD_TASK_ZONENAME); break; default: attached = 0; } return (attached); }
static int look(pid_t pid) { char pathname[PATH_MAX]; int rval = 0; int fd; psinfo_t psinfo; prusage_t prusage; timestruc_t real, user, sys; hrtime_t hrtime; prusage_t *pup = &prusage; pfirst++; if (proc_get_psinfo(pid, &psinfo) < 0) return (perr("read psinfo")); (void) proc_snprintf(pathname, sizeof (pathname), "/proc/%d/usage", (int)pid); if ((fd = open(pathname, O_RDONLY)) < 0) return (perr("open usage")); if (read(fd, &prusage, sizeof (prusage)) != sizeof (prusage)) rval = perr("read usage"); else { if (pidarg) { hrtime = gethrtime(); hrt2ts(hrtime, &real); } else { real = pup->pr_term; } tssub(&real, &real, &pup->pr_create); user = pup->pr_utime; sys = pup->pr_stime; if (!mflag) tsadd(&sys, &sys, &pup->pr_ttime); if (!pflag || pfirst > 1) (void) fprintf(stderr, "\n"); if (pflag) (void) fprintf(stderr, "%d:\t%.70s\n", (int)psinfo.pr_pid, psinfo.pr_psargs); prtime("real", &real); prtime("user", &user); prtime("sys", &sys); if (mflag) { prtime("trap", &pup->pr_ttime); prtime("tflt", &pup->pr_tftime); prtime("dflt", &pup->pr_dftime); prtime("kflt", &pup->pr_kftime); prtime("lock", &pup->pr_ltime); prtime("slp", &pup->pr_slptime); prtime("lat", &pup->pr_wtime); prtime("stop", &pup->pr_stoptime); } } (void) close(fd); return (rval); }