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 
41 typedef struct {
42 	float		d_time;
43 	int		d_count;
44 	const char	*d_symname;
45 } d_entry;
46 
47 typedef struct list {
48 	d_entry		*l_dep;
49 	struct list	*l_next;
50 } List;
51 
52 
53 static Elist	    *bindto_list = NULL;
54 static Elist	    *bindfrom_list = NULL;
55 
56 static int  initialized;
57 extern long long gethrvtime();
58 
59 static const char *progname;
60 static long long starts[1000];
61 static long long accounted[1000];		/* time accounted for */
62 static int  counter = 0;
63 
64 static float	total_time = 0.0;
65 static List	*list_head = NULL;
66 
67 static hash	*tbl;
68 
69 static sigset_t		iset;
70 
71 static void
list_insert(d_entry * dep)72 list_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 
106 uint_t
la_version(uint_t version)107 la_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 */
141 uint_t
la_objopen(Link_map * lmp,Lmid_t lmid,uintptr_t * cookie)142 la_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)
170 uintptr_t
la_sparcv9_pltenter(Elf64_Sym * symp,uint_t symndx,uintptr_t * refcookie,uintptr_t * defcookie,La_sparcv9_regs * regset,uint_t * sb_flags,const char * sym_name)171 la_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)
175 uintptr_t
176 la_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)
179 uintptr_t
180 la_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)
184 uintptr_t
185 la_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 */
200 uintptr_t
la_pltexit64(Elf64_Sym * symp,uint_t symndx,uintptr_t * refcookie,uintptr_t * defcookie,uintptr_t retval,const char * sym_name)201 la_pltexit64(Elf64_Sym *symp, uint_t symndx, uintptr_t *refcookie,
202 	uintptr_t *defcookie, uintptr_t retval, const char *sym_name)
203 #else
204 uintptr_t
205 la_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 */
243 static void
scanlist(d_entry * dep,void * food,char * name)244 scanlist(d_entry *dep, void *food, char *name)
245 {
246 	total_time += dep->d_time;
247 	list_insert(dep);
248 }
249 
250 #pragma fini(cleanup)
251 static void
cleanup()252 cleanup()
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