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