perfcnt.c revision 12927:a27c46eb192b
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25#include <errno.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <unistd.h>
29#include <stropts.h>
30#include <link.h>
31#include <sys/types.h>
32#include <sys/regset.h>
33#include <sys/frame.h>
34#include <sys/procfs.h>
35#include <fcntl.h>
36#include <signal.h>
37#include "env.h"
38#include "hash.h"
39
40
41typedef struct {
42	float		d_time;
43	int		d_count;
44	const char	*d_symname;
45} d_entry;
46
47typedef struct list {
48	d_entry		*l_dep;
49	struct list	*l_next;
50} List;
51
52
53static Elist	    *bindto_list = NULL;
54static Elist	    *bindfrom_list = NULL;
55
56static int  initialized;
57extern long long gethrvtime();
58
59static const char *progname;
60static long long starts[1000];
61static long long accounted[1000];		/* time accounted for */
62static int  counter = 0;
63
64static float	total_time = 0.0;
65static List	*list_head = NULL;
66
67static hash	*tbl;
68
69static sigset_t		iset;
70
71static void
72list_insert(d_entry *dep)
73{
74	List *new_list;
75	List *cur;
76	List *prev;
77
78	if ((new_list = malloc(sizeof (List))) == NULL) {
79		(void) printf("libperfcnt.so: malloc failed - "
80		    "can't print summary\n");
81		exit(1);
82	}
83	new_list->l_dep = dep;
84
85	if (list_head == NULL) {
86		list_head = new_list;
87		new_list->l_next = NULL;
88		return;
89	}
90	for (cur = list_head, prev = NULL;
91	    (cur && (cur->l_dep->d_time < dep->d_time));
92	    prev = cur, cur = cur->l_next)
93		;
94	/*
95	 * insert at head of list
96	 */
97	if (prev == NULL) {
98		new_list->l_next = list_head;
99		list_head = new_list;
100		return;
101	}
102	prev->l_next = new_list;
103	new_list->l_next = cur;
104}
105
106uint_t
107la_version(uint_t version)
108{
109	int fd;
110	char buffer[100];
111
112	if (version > LAV_CURRENT)
113		(void) fprintf(stderr, "perfcnt.so.1: unexpected version: %d\n",
114		    version);
115
116	(void) sprintf(buffer, "/proc/%d", (int)getpid());
117	if ((fd = open(buffer, O_RDWR)) >= 0) {
118		long state = PR_MSACCT;
119		if (ioctl(fd, PIOCSET, &state) == -1)
120			perror("PIOCSET");
121		(void) close(fd);
122	}
123
124	initialized++;
125	tbl = make_hash(213);
126
127	build_env_list(&bindto_list, (const char *)"PERFCNT_BINDTO");
128	build_env_list(&bindto_list, (const char *)"PERFCNT_BINDFROM");
129
130	/*
131	 * Initalize iset to the full set of signals to be masked durring
132	 * pltenter/pltexit
133	 */
134	(void) sigfillset(&iset);
135
136	return (LAV_CURRENT);
137}
138
139
140/* ARGSUSED1 */
141uint_t
142la_objopen(Link_map *lmp, Lmid_t lmid, uintptr_t *cookie)
143{
144	static int	first = 1;
145	uint_t	flags = 0;
146
147	if (first) {
148		progname = lmp->l_name;
149		first = 0;
150	}
151
152	if (bindto_list == NULL)
153		flags = LA_FLG_BINDTO;
154	else {
155		if (check_list(bindto_list, lmp->l_name))
156			flags = LA_FLG_BINDTO;
157	}
158	if (bindfrom_list == NULL)
159		flags |= LA_FLG_BINDFROM;
160	else {
161		if (check_list(bindfrom_list, lmp->l_name))
162			flags |= LA_FLG_BINDFROM;
163	}
164
165	return (flags);
166}
167
168/* ARGSUSED1 */
169#if	defined(__sparcv9)
170uintptr_t
171la_sparcv9_pltenter(Elf64_Sym *symp, uint_t symndx, uintptr_t *refcookie,
172	uintptr_t *defcookie, La_sparcv9_regs *regset, uint_t *sb_flags,
173	const char *sym_name)
174#elif	defined(__sparc)
175uintptr_t
176la_sparcv8_pltenter(Elf32_Sym *symp, uint_t symndx, uintptr_t *refcookie,
177	uintptr_t *defcookie, La_sparcv8_regs *regset, uint_t *sb_flags)
178#elif	defined(__amd64)
179uintptr_t
180la_amd64_pltenter(Elf64_Sym *symp, uint_t symndx, uintptr_t *refcookie,
181	uintptr_t *defcookie, La_amd64_regs *regset, uint_t *sb_flags,
182	const char *sym_name)
183#elif	defined(__i386)
184uintptr_t
185la_i86_pltenter(Elf32_Sym *symp, uint_t symndx, uintptr_t *refcooke,
186	uintptr_t *defcook, La_i86_regs *regset, uint_t *sb_flags)
187#endif
188{
189	accounted[counter] = 0;
190	starts[counter] = gethrvtime();
191	counter++;
192	return (symp->st_value);
193}
194
195
196
197/* ARGSUSED1 */
198#if	defined(_LP64)
199/* ARGSUSED */
200uintptr_t
201la_pltexit64(Elf64_Sym *symp, uint_t symndx, uintptr_t *refcookie,
202	uintptr_t *defcookie, uintptr_t retval, const char *sym_name)
203#else
204uintptr_t
205la_pltexit(Elf32_Sym *symp, uint_t symndx, uintptr_t *refcookie,
206	uintptr_t *defcookie, uintptr_t retval)
207#endif
208{
209	d_entry		**dep;
210	long long	time_used;
211	sigset_t	oset;
212#if	!defined(_LP64)
213	const char	*sym_name = (const char *)symp->st_name;
214#endif
215
216	(void) sigprocmask(SIG_BLOCK, &iset, &oset);
217
218	counter--;
219	time_used = gethrvtime() - starts[counter];
220
221	dep = (d_entry **)get_hash(tbl, (char *)sym_name);
222	if (*dep == NULL) {
223		char *ptr = malloc(sizeof (d_entry));
224		/* LINTED */
225		(*dep) = (d_entry *)ptr;
226		(*dep)->d_count = 0;
227		(*dep)->d_time = 0.0;
228		(*dep)->d_symname = sym_name;
229	}
230
231	if (counter)
232		accounted[counter - 1] += time_used;
233
234	((*dep)->d_count)++;
235	(*dep)->d_time += (double)((time_used - accounted[counter]) / 1.0e9);
236
237	(void) sigprocmask(SIG_SETMASK, &oset, NULL);
238
239	return (retval);
240}
241
242/* ARGSUSED1 */
243static void
244scanlist(d_entry *dep, void *food, char *name)
245{
246	total_time += dep->d_time;
247	list_insert(dep);
248}
249
250#pragma fini(cleanup)
251static void
252cleanup()
253{
254	List	*cur;
255	(void) operate_hash(tbl, scanlist, NULL);
256	(void) printf("\n\nPerf Counts for: %s\n\n", progname);
257	(void) printf("%20s\tc_count\t    tim\t\tavg. tim\ttot. %%\n",
258	    "SYMBOL");
259	(void) printf("--------------------------------------------------"
260	    "-------------------\n");
261	for (cur = list_head; cur; cur = cur->l_next) {
262		d_entry		*dep = cur->l_dep;
263		float		tim = dep->d_time * 1000000;
264
265		(void) printf("%20s\t%d\t%8.2f\t%8.2f\t%2.2f%%\n",
266		    dep->d_symname, dep->d_count, tim, tim / dep->d_count,
267		    ((dep->d_time / total_time) * 100.0));
268	}
269	(void) printf("--------------------------------------------------"
270	    "-------------------\n");
271	(void) printf("\t\t\t\t\t\tTotal Time: %8.2f\n",
272	    total_time * 1000000);
273}
274