1181880Sjhb/*-
2181880Sjhb * Copyright (c) 2008 Yahoo!, Inc.
3181880Sjhb * All rights reserved.
4181880Sjhb * Written by: John Baldwin <jhb@FreeBSD.org>
5181880Sjhb *
6181880Sjhb * Redistribution and use in source and binary forms, with or without
7181880Sjhb * modification, are permitted provided that the following conditions
8181880Sjhb * are met:
9181880Sjhb * 1. Redistributions of source code must retain the above copyright
10181880Sjhb *    notice, this list of conditions and the following disclaimer.
11181880Sjhb * 2. Redistributions in binary form must reproduce the above copyright
12181880Sjhb *    notice, this list of conditions and the following disclaimer in the
13181880Sjhb *    documentation and/or other materials provided with the distribution.
14181880Sjhb * 3. Neither the name of the author nor the names of any co-contributors
15181880Sjhb *    may be used to endorse or promote products derived from this software
16181880Sjhb *    without specific prior written permission.
17181880Sjhb *
18181880Sjhb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19181880Sjhb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20181880Sjhb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21181880Sjhb * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22181880Sjhb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23181880Sjhb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24181880Sjhb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25181880Sjhb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26181880Sjhb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27181880Sjhb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28181880Sjhb * SUCH DAMAGE.
29181880Sjhb */
30181880Sjhb
31181880Sjhb#include <sys/cdefs.h>
32181880Sjhb__FBSDID("$FreeBSD$");
33181880Sjhb
34181880Sjhb#include <sys/param.h>
35181880Sjhb#include <sys/pcpu.h>
36181880Sjhb#include <sys/resource.h>
37181880Sjhb#include <sys/sysctl.h>
38181880Sjhb#include <errno.h>
39181880Sjhb#include <kvm.h>
40181880Sjhb#include <limits.h>
41181880Sjhb#include <stdlib.h>
42194186Sed#include <string.h>
43181880Sjhb
44181880Sjhb#include "kvm_private.h"
45181880Sjhb
46181880Sjhbstatic struct nlist kvm_cp_time_nl[] = {
47217744Suqs	{ .n_name = "_cp_time" },		/* (deprecated) */
48217744Suqs	{ .n_name = NULL },
49181880Sjhb};
50181880Sjhb
51181880Sjhb#define	NL_CP_TIME		0
52181880Sjhb
53181880Sjhbstatic int kvm_cp_time_cached;
54181880Sjhb
55181880Sjhbstatic int
56181880Sjhb_kvm_cp_time_init(kvm_t *kd)
57181880Sjhb{
58181880Sjhb
59181880Sjhb	if (kvm_nlist(kd, kvm_cp_time_nl) < 0)
60181880Sjhb		return (-1);
61181880Sjhb	kvm_cp_time_cached = 1;
62217744Suqs	return (0);
63181880Sjhb}
64181880Sjhb
65181880Sjhbstatic int
66181880Sjhbgetsysctl(kvm_t *kd, const char *name, void *buf, size_t len)
67181880Sjhb{
68181880Sjhb	size_t nlen;
69181880Sjhb
70181880Sjhb	nlen = len;
71181880Sjhb	if (sysctlbyname(name, buf, &nlen, NULL, 0) < 0) {
72181880Sjhb		_kvm_err(kd, kd->program, "cannot read sysctl %s:%s", name,
73181880Sjhb		    strerror(errno));
74181880Sjhb		return (-1);
75181880Sjhb	}
76181880Sjhb	if (nlen != len) {
77181880Sjhb		_kvm_err(kd, kd->program, "sysctl %s has unexpected size",
78181880Sjhb		    name);
79181880Sjhb		return (-1);
80181880Sjhb	}
81181880Sjhb	return (0);
82181880Sjhb}
83181880Sjhb
84181880Sjhbint
85181880Sjhbkvm_getcptime(kvm_t *kd, long *cp_time)
86181880Sjhb{
87181880Sjhb	struct pcpu *pc;
88181880Sjhb	int i, j, maxcpu;
89181880Sjhb
90181880Sjhb	if (kd == NULL) {
91181880Sjhb		kvm_cp_time_cached = 0;
92181880Sjhb		return (0);
93181880Sjhb	}
94181880Sjhb
95181880Sjhb	if (ISALIVE(kd))
96181880Sjhb		return (getsysctl(kd, "kern.cp_time", cp_time, sizeof(long) *
97181880Sjhb		    CPUSTATES));
98181880Sjhb
99181880Sjhb	if (kvm_cp_time_cached == 0) {
100181880Sjhb		if (_kvm_cp_time_init(kd) < 0)
101181880Sjhb			return (-1);
102181880Sjhb	}
103181880Sjhb
104181880Sjhb	/* If this kernel has a "cp_time[]" symbol, then just read that. */
105181880Sjhb	if (kvm_cp_time_nl[NL_CP_TIME].n_value != 0) {
106181880Sjhb		if (kvm_read(kd, kvm_cp_time_nl[NL_CP_TIME].n_value, cp_time,
107181880Sjhb		    sizeof(long) * CPUSTATES) != sizeof(long) * CPUSTATES) {
108181880Sjhb			_kvm_err(kd, kd->program, "cannot read cp_time array");
109181880Sjhb			return (-1);
110181880Sjhb		}
111181880Sjhb		return (0);
112181880Sjhb	}
113181880Sjhb
114181880Sjhb	/*
115181880Sjhb	 * If we don't have that symbol, then we have to simulate
116181880Sjhb	 * "cp_time[]" by adding up the individual times for each CPU.
117181880Sjhb	 */
118181880Sjhb	maxcpu = kvm_getmaxcpu(kd);
119181880Sjhb	if (maxcpu < 0)
120181880Sjhb		return (-1);
121181880Sjhb	for (i = 0; i < CPUSTATES; i++)
122181880Sjhb		cp_time[i] = 0;
123181880Sjhb	for (i = 0; i < maxcpu; i++) {
124181880Sjhb		pc = kvm_getpcpu(kd, i);
125181880Sjhb		if (pc == NULL)
126181880Sjhb			continue;
127181880Sjhb		if (pc == (void *)-1)
128181880Sjhb			return (-1);
129181880Sjhb		for (j = 0; j < CPUSTATES; j++)
130181880Sjhb			cp_time[j] += pc->pc_cp_time[j];
131181880Sjhb		free(pc);
132181880Sjhb	}
133181880Sjhb	return (0);
134181880Sjhb}
135