xref: /freebsd/lib/libpmcstat/libpmcstat_process.c (revision 1d386b48a555f61cb7325543adbbb5c3f3407a66)
1 /*-
2  * Copyright (c) 2003-2008 Joseph Koshy
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 #include <sys/types.h>
29 #include <sys/cpuset.h>
30 #include <sys/event.h>
31 #include <sys/param.h>
32 #include <sys/socket.h>
33 #include <sys/stat.h>
34 #include <sys/module.h>
35 #include <sys/pmc.h>
36 
37 #include <assert.h>
38 #include <ctype.h>
39 #include <err.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <limits.h>
43 #include <netdb.h>
44 #include <pmc.h>
45 #include <pmclog.h>
46 #include <signal.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <strings.h>
51 #include <sysexits.h>
52 #include <unistd.h>
53 
54 #include "libpmcstat.h"
55 
56 /*
57  * Associate an AOUT image with a process.
58  */
59 
60 void
61 pmcstat_process_aout_exec(struct pmcstat_process *pp,
62     struct pmcstat_image *image, uintptr_t baseaddr)
63 {
64 	(void) pp;
65 	(void) image;
66 	(void) baseaddr;
67 	/* TODO Implement a.out handling */
68 }
69 
70 /*
71  * Associate an ELF image with a process.
72  */
73 
74 void
75 pmcstat_process_elf_exec(struct pmcstat_process *pp,
76     struct pmcstat_image *image, uintptr_t baseaddr, uintptr_t dynaddr,
77     struct pmcstat_args *args, struct pmc_plugins *plugins,
78     struct pmcstat_stats *pmcstat_stats)
79 {
80 	struct pmcstat_image *rtldimage;
81 
82 	assert(image->pi_type == PMCSTAT_IMAGE_ELF32 ||
83 	    image->pi_type == PMCSTAT_IMAGE_ELF64);
84 
85 	/*
86 	 * The exact address where the executable gets mapped in will vary for
87 	 * PIEs.  The dynamic address recorded at process exec time corresponds
88 	 * to the address where the executable's file object had been mapped to.
89 	 */
90 	pmcstat_image_link(pp, image, image->pi_vaddr + dynaddr);
91 
92 	/*
93 	 * For dynamically linked executables we need to determine
94 	 * where the dynamic linker was mapped to for this process,
95 	 * Subsequent executable objects that are mapped in by the
96 	 * dynamic linker will be tracked by log events of type
97 	 * PMCLOG_TYPE_MAP_IN.
98 	 */
99 
100 	if (image->pi_isdynamic) {
101 
102 		/*
103 		 * The runtime loader gets loaded just after the maximum
104 		 * possible heap address.  Like so:
105 		 *
106 		 * [  TEXT DATA BSS HEAP -->*RTLD  SHLIBS   <--STACK]
107 		 * ^					            ^
108 		 * 0				   VM_MAXUSER_ADDRESS
109 		 *
110 		 * The exact address where the loader gets mapped in
111 		 * will vary according to the size of the executable
112 		 * and the limits on the size of the process'es data
113 		 * segment at the time of exec().  The base address
114 		 * recorded at process exec time corresponds to the
115 		 * address where the runtime loader's file object had
116 		 * been mapped to.
117 		 */
118 		rtldimage = pmcstat_image_from_path(image->pi_dynlinkerpath,
119 		    0, args, plugins);
120 		if (rtldimage == NULL) {
121 			warnx("WARNING: Cannot find image for \"%s\".",
122 			    pmcstat_string_unintern(image->pi_dynlinkerpath));
123 			pmcstat_stats->ps_exec_errors++;
124 			return;
125 		}
126 
127 		if (rtldimage->pi_type == PMCSTAT_IMAGE_UNKNOWN)
128 			pmcstat_image_get_elf_params(rtldimage, args);
129 
130 		if (rtldimage->pi_type != PMCSTAT_IMAGE_ELF32 &&
131 		    rtldimage->pi_type != PMCSTAT_IMAGE_ELF64) {
132 			warnx("WARNING: rtld not an ELF object \"%s\".",
133 			    pmcstat_string_unintern(image->pi_dynlinkerpath));
134 			return;
135 		}
136 
137 		pmcstat_image_link(pp, rtldimage, baseaddr);
138 	}
139 }
140 
141 /*
142  * Associate an image and a process.
143  */
144 
145 void
146 pmcstat_process_exec(struct pmcstat_process *pp,
147     pmcstat_interned_string path, uintptr_t baseaddr, uintptr_t dynaddr,
148     struct pmcstat_args *args, struct pmc_plugins *plugins,
149     struct pmcstat_stats *pmcstat_stats)
150 {
151 	struct pmcstat_image *image;
152 
153 	if ((image = pmcstat_image_from_path(path, 0,
154 	    args, plugins)) == NULL) {
155 		pmcstat_stats->ps_exec_errors++;
156 		return;
157 	}
158 
159 	if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN)
160 		pmcstat_image_determine_type(image, args);
161 
162 	assert(image->pi_type != PMCSTAT_IMAGE_UNKNOWN);
163 
164 	switch (image->pi_type) {
165 	case PMCSTAT_IMAGE_ELF32:
166 	case PMCSTAT_IMAGE_ELF64:
167 		pmcstat_stats->ps_exec_elf++;
168 		pmcstat_process_elf_exec(pp, image, baseaddr, dynaddr,
169 		    args, plugins, pmcstat_stats);
170 		break;
171 
172 	case PMCSTAT_IMAGE_AOUT:
173 		pmcstat_stats->ps_exec_aout++;
174 		pmcstat_process_aout_exec(pp, image, baseaddr);
175 		break;
176 
177 	case PMCSTAT_IMAGE_INDETERMINABLE:
178 		pmcstat_stats->ps_exec_indeterminable++;
179 		break;
180 
181 	default:
182 		err(EX_SOFTWARE,
183 		    "ERROR: Unsupported executable type for \"%s\"",
184 		    pmcstat_string_unintern(path));
185 	}
186 }
187 
188 /*
189  * Find the map entry associated with process 'p' at PC value 'pc'.
190  */
191 
192 struct pmcstat_pcmap *
193 pmcstat_process_find_map(struct pmcstat_process *p, uintfptr_t pc)
194 {
195 	struct pmcstat_pcmap *ppm;
196 
197 	TAILQ_FOREACH(ppm, &p->pp_map, ppm_next) {
198 		if (pc >= ppm->ppm_lowpc && pc < ppm->ppm_highpc)
199 			return (ppm);
200 		if (pc < ppm->ppm_lowpc)
201 			return (NULL);
202 	}
203 
204 	return (NULL);
205 }
206 
207 /*
208  * Find the process descriptor corresponding to a PID.  If 'allocate'
209  * is zero, we return a NULL if a pid descriptor could not be found or
210  * a process descriptor process.  If 'allocate' is non-zero, then we
211  * will attempt to allocate a fresh process descriptor.  Zombie
212  * process descriptors are only removed if a fresh allocation for the
213  * same PID is requested.
214  */
215 
216 struct pmcstat_process *
217 pmcstat_process_lookup(pid_t pid, int allocate)
218 {
219 	uint32_t hash;
220 	struct pmcstat_pcmap *ppm, *ppmtmp;
221 	struct pmcstat_process *pp, *pptmp;
222 
223 	hash = (uint32_t) pid & PMCSTAT_HASH_MASK;	/* simplicity wins */
224 
225 	LIST_FOREACH_SAFE(pp, &pmcstat_process_hash[hash], pp_next, pptmp)
226 		if (pp->pp_pid == pid) {
227 			/* Found a descriptor, check and process zombies */
228 			if (allocate && pp->pp_isactive == 0) {
229 				/* remove maps */
230 				TAILQ_FOREACH_SAFE(ppm, &pp->pp_map, ppm_next,
231 				    ppmtmp) {
232 					TAILQ_REMOVE(&pp->pp_map, ppm,
233 					    ppm_next);
234 					free(ppm);
235 				}
236 				/* remove process entry */
237 				LIST_REMOVE(pp, pp_next);
238 				free(pp);
239 				break;
240 			}
241 			return (pp);
242 		}
243 
244 	if (!allocate)
245 		return (NULL);
246 
247 	if ((pp = malloc(sizeof(*pp))) == NULL)
248 		err(EX_OSERR, "ERROR: Cannot allocate pid descriptor");
249 
250 	pp->pp_pid = pid;
251 	pp->pp_isactive = 1;
252 
253 	TAILQ_INIT(&pp->pp_map);
254 
255 	LIST_INSERT_HEAD(&pmcstat_process_hash[hash], pp, pp_next);
256 	return (pp);
257 }
258 
259 void
260 pmcstat_create_process(int *pmcstat_sockpair, struct pmcstat_args *args,
261     int pmcstat_kq)
262 {
263 	char token;
264 	pid_t pid;
265 	struct kevent kev;
266 	struct pmcstat_target *pt;
267 
268 	if (socketpair(AF_UNIX, SOCK_STREAM, 0, pmcstat_sockpair) < 0)
269 		err(EX_OSERR, "ERROR: cannot create socket pair");
270 
271 	switch (pid = fork()) {
272 	case -1:
273 		err(EX_OSERR, "ERROR: cannot fork");
274 		/*NOTREACHED*/
275 
276 	case 0:		/* child */
277 		(void) close(pmcstat_sockpair[PARENTSOCKET]);
278 
279 		/* Write a token to tell our parent we've started executing. */
280 		if (write(pmcstat_sockpair[CHILDSOCKET], "+", 1) != 1)
281 			err(EX_OSERR, "ERROR (child): cannot write token");
282 
283 		/* Wait for our parent to signal us to start. */
284 		if (read(pmcstat_sockpair[CHILDSOCKET], &token, 1) < 0)
285 			err(EX_OSERR, "ERROR (child): cannot read token");
286 		(void) close(pmcstat_sockpair[CHILDSOCKET]);
287 
288 		/* exec() the program requested */
289 		execvp(*args->pa_argv, args->pa_argv);
290 		/* and if that fails, notify the parent */
291 		kill(getppid(), SIGCHLD);
292 		err(EX_OSERR, "ERROR: execvp \"%s\" failed", *args->pa_argv);
293 		/*NOTREACHED*/
294 
295 	default:	/* parent */
296 		(void) close(pmcstat_sockpair[CHILDSOCKET]);
297 		break;
298 	}
299 
300 	/* Ask to be notified via a kevent when the target process exits. */
301 	EV_SET(&kev, pid, EVFILT_PROC, EV_ADD | EV_ONESHOT, NOTE_EXIT, 0,
302 	    NULL);
303 	if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0)
304 		err(EX_OSERR, "ERROR: cannot monitor child process %d", pid);
305 
306 	if ((pt = malloc(sizeof(*pt))) == NULL)
307 		errx(EX_SOFTWARE, "ERROR: Out of memory.");
308 
309 	pt->pt_pid = pid;
310 	SLIST_INSERT_HEAD(&args->pa_targets, pt, pt_next);
311 
312 	/* Wait for the child to signal that its ready to go. */
313 	if (read(pmcstat_sockpair[PARENTSOCKET], &token, 1) < 0)
314 		err(EX_OSERR, "ERROR (parent): cannot read token");
315 
316 	return;
317 }
318 
319 /*
320  * Do process profiling
321  *
322  * If a pid was specified, attach each allocated PMC to the target
323  * process.  Otherwise, fork a child and attach the PMCs to the child,
324  * and have the child exec() the target program.
325  */
326 
327 void
328 pmcstat_start_process(int *pmcstat_sockpair)
329 {
330 	/* Signal the child to proceed. */
331 	if (write(pmcstat_sockpair[PARENTSOCKET], "!", 1) != 1)
332 		err(EX_OSERR, "ERROR (parent): write of token failed");
333 
334 	(void) close(pmcstat_sockpair[PARENTSOCKET]);
335 }
336 
337 void
338 pmcstat_attach_pmcs(struct pmcstat_args *args)
339 {
340 	struct pmcstat_ev *ev;
341 	struct pmcstat_target *pt;
342 	int count;
343 
344 	/* Attach all process PMCs to target processes. */
345 	count = 0;
346 	STAILQ_FOREACH(ev, &args->pa_events, ev_next) {
347 		if (PMC_IS_SYSTEM_MODE(ev->ev_mode))
348 			continue;
349 		SLIST_FOREACH(pt, &args->pa_targets, pt_next) {
350 			if (pmc_attach(ev->ev_pmcid, pt->pt_pid) == 0)
351 				count++;
352 			else if (errno != ESRCH)
353 				err(EX_OSERR,
354 "ERROR: cannot attach pmc \"%s\" to process %d",
355 				    ev->ev_name, (int)pt->pt_pid);
356 		}
357 	}
358 
359 	if (count == 0)
360 		errx(EX_DATAERR, "ERROR: No processes were attached to.");
361 }
362