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__FBSDID("$FreeBSD$");
29
30#include <sys/types.h>
31#include <sys/cpuset.h>
32#include <sys/param.h>
33#include <sys/socket.h>
34#include <sys/stat.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 <stdio.h>
47#include <stdlib.h>
48#include <string.h>
49#include <strings.h>
50#include <sysexits.h>
51#include <unistd.h>
52
53#include "libpmcstat.h"
54
55/*
56 * Get PMC record by id, apply merge policy.
57 */
58
59static struct pmcstat_pmcrecord *
60pmcstat_lookup_pmcid(pmc_id_t pmcid, int pmcstat_mergepmc)
61{
62	struct pmcstat_pmcrecord *pr;
63
64	LIST_FOREACH(pr, &pmcstat_pmcs, pr_next) {
65		if (pr->pr_pmcid == pmcid) {
66			if (pmcstat_mergepmc)
67				return pr->pr_merge;
68			return pr;
69		}
70	}
71
72	return NULL;
73}
74
75/*
76 * Add a {pmcid,name} mapping.
77 */
78
79static void
80pmcstat_pmcid_add(pmc_id_t pmcid, pmcstat_interned_string ps,
81    struct pmcstat_args *args, struct pmc_plugins *plugins,
82    int *pmcstat_npmcs)
83{
84	struct pmcstat_pmcrecord *pr, *prm;
85
86	/* Replace an existing name for the PMC. */
87	prm = NULL;
88	LIST_FOREACH(pr, &pmcstat_pmcs, pr_next)
89		if (pr->pr_pmcid == pmcid) {
90			pr->pr_pmcname = ps;
91			return;
92		} else if (pr->pr_pmcname == ps)
93			prm = pr;
94
95	/*
96	 * Otherwise, allocate a new descriptor and call the
97	 * plugins hook.
98	 */
99	if ((pr = malloc(sizeof(*pr))) == NULL)
100		err(EX_OSERR, "ERROR: Cannot allocate pmc record");
101
102	pr->pr_pmcid = pmcid;
103	pr->pr_pmcname = ps;
104	pr->pr_pmcin = (*pmcstat_npmcs)++;
105	pr->pr_samples = 0;
106	pr->pr_dubious_frames = 0;
107	pr->pr_merge = prm == NULL ? pr : prm;
108
109	LIST_INSERT_HEAD(&pmcstat_pmcs, pr, pr_next);
110
111	if (plugins[args->pa_pplugin].pl_newpmc != NULL)
112		plugins[args->pa_pplugin].pl_newpmc(ps, pr);
113	if (plugins[args->pa_plugin].pl_newpmc != NULL)
114		plugins[args->pa_plugin].pl_newpmc(ps, pr);
115}
116
117/*
118 * Unmap images in the range [start..end) associated with process
119 * 'pp'.
120 */
121
122static void
123pmcstat_image_unmap(struct pmcstat_process *pp, uintfptr_t start,
124    uintfptr_t end)
125{
126	struct pmcstat_pcmap *pcm, *pcmtmp, *pcmnew;
127
128	assert(pp != NULL);
129	assert(start < end);
130
131	/*
132	 * Cases:
133	 * - we could have the range completely in the middle of an
134	 *   existing pcmap; in this case we have to split the pcmap
135	 *   structure into two (i.e., generate a 'hole').
136	 * - we could have the range covering multiple pcmaps; these
137	 *   will have to be removed.
138	 * - we could have either 'start' or 'end' falling in the
139	 *   middle of a pcmap; in this case shorten the entry.
140	 */
141	TAILQ_FOREACH_SAFE(pcm, &pp->pp_map, ppm_next, pcmtmp) {
142		assert(pcm->ppm_lowpc < pcm->ppm_highpc);
143		if (pcm->ppm_highpc <= start)
144			continue;
145		if (pcm->ppm_lowpc >= end)
146			return;
147		if (pcm->ppm_lowpc >= start && pcm->ppm_highpc <= end) {
148			/*
149			 * The current pcmap is completely inside the
150			 * unmapped range: remove it entirely.
151			 */
152			TAILQ_REMOVE(&pp->pp_map, pcm, ppm_next);
153			free(pcm);
154		} else if (pcm->ppm_lowpc < start && pcm->ppm_highpc > end) {
155			/*
156			 * Split this pcmap into two; curtail the
157			 * current map to end at [start-1], and start
158			 * the new one at [end].
159			 */
160			if ((pcmnew = malloc(sizeof(*pcmnew))) == NULL)
161				err(EX_OSERR,
162				    "ERROR: Cannot split a map entry");
163
164			pcmnew->ppm_image = pcm->ppm_image;
165
166			pcmnew->ppm_lowpc = end;
167			pcmnew->ppm_highpc = pcm->ppm_highpc;
168
169			pcm->ppm_highpc = start;
170
171			TAILQ_INSERT_AFTER(&pp->pp_map, pcm, pcmnew, ppm_next);
172
173			return;
174		} else if (pcm->ppm_lowpc < start && pcm->ppm_highpc <= end)
175			pcm->ppm_highpc = start;
176		else if (pcm->ppm_lowpc >= start && pcm->ppm_highpc > end)
177			pcm->ppm_lowpc = end;
178		else
179			assert(0);
180	}
181}
182
183/*
184 * Convert a hwpmc(4) log to profile information.  A system-wide
185 * callgraph is generated if FLAG_DO_CALLGRAPHS is set.  gmon.out
186 * files usable by gprof(1) are created if FLAG_DO_GPROF is set.
187 */
188int
189pmcstat_analyze_log(struct pmcstat_args *args,
190    struct pmc_plugins *plugins,
191    struct pmcstat_stats *pmcstat_stats,
192    struct pmcstat_process *pmcstat_kernproc,
193    int pmcstat_mergepmc,
194    int *pmcstat_npmcs,
195    int *ps_samples_period)
196{
197	uint32_t cpu, cpuflags;
198	pid_t pid;
199	struct pmcstat_image *image;
200	struct pmcstat_process *pp, *ppnew;
201	struct pmcstat_pcmap *ppm, *ppmtmp;
202	struct pmclog_ev ev;
203	struct pmcstat_pmcrecord *pmcr;
204	pmcstat_interned_string image_path;
205
206	assert(args->pa_flags & FLAG_DO_ANALYSIS);
207
208	if (elf_version(EV_CURRENT) == EV_NONE)
209		err(EX_UNAVAILABLE, "Elf library initialization failed");
210
211	while (pmclog_read(args->pa_logparser, &ev) == 0) {
212		assert(ev.pl_state == PMCLOG_OK);
213
214		switch (ev.pl_type) {
215		case PMCLOG_TYPE_INITIALIZE:
216			if ((ev.pl_u.pl_i.pl_version & 0xFF000000) !=
217			    PMC_VERSION_MAJOR << 24 && args->pa_verbosity > 0)
218				warnx(
219"WARNING: Log version 0x%x does not match compiled version 0x%x.",
220				    ev.pl_u.pl_i.pl_version, PMC_VERSION_MAJOR);
221			break;
222
223		case PMCLOG_TYPE_MAP_IN:
224			/*
225			 * Introduce an address range mapping for a
226			 * userland process or the kernel (pid == -1).
227			 *
228			 * We always allocate a process descriptor so
229			 * that subsequent samples seen for this
230			 * address range are mapped to the current
231			 * object being mapped in.
232			 */
233			pid = ev.pl_u.pl_mi.pl_pid;
234			if (pid == -1)
235				pp = pmcstat_kernproc;
236			else
237				pp = pmcstat_process_lookup(pid,
238				    PMCSTAT_ALLOCATE);
239
240			assert(pp != NULL);
241
242			image_path = pmcstat_string_intern(ev.pl_u.pl_mi.
243			    pl_pathname);
244			image = pmcstat_image_from_path(image_path, pid == -1,
245			    args, plugins);
246			if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN)
247				pmcstat_image_determine_type(image, args);
248			if (image->pi_type != PMCSTAT_IMAGE_INDETERMINABLE)
249				pmcstat_image_link(pp, image,
250				    ev.pl_u.pl_mi.pl_start);
251			break;
252
253		case PMCLOG_TYPE_MAP_OUT:
254			/*
255			 * Remove an address map.
256			 */
257			pid = ev.pl_u.pl_mo.pl_pid;
258			if (pid == -1)
259				pp = pmcstat_kernproc;
260			else
261				pp = pmcstat_process_lookup(pid, 0);
262
263			if (pp == NULL)	/* unknown process */
264				break;
265
266			pmcstat_image_unmap(pp, ev.pl_u.pl_mo.pl_start,
267			    ev.pl_u.pl_mo.pl_end);
268			break;
269
270		case PMCLOG_TYPE_CALLCHAIN:
271			pmcstat_stats->ps_samples_total++;
272			*ps_samples_period += 1;
273
274			cpuflags = ev.pl_u.pl_cc.pl_cpuflags;
275			cpu = PMC_CALLCHAIN_CPUFLAGS_TO_CPU(cpuflags);
276
277			if ((args->pa_flags & FLAG_FILTER_THREAD_ID) &&
278				args->pa_tid != ev.pl_u.pl_cc.pl_tid) {
279				pmcstat_stats->ps_samples_skipped++;
280				break;
281			}
282			/* Filter on the CPU id. */
283			if (!CPU_ISSET(cpu, &(args->pa_cpumask))) {
284				pmcstat_stats->ps_samples_skipped++;
285				break;
286			}
287
288			pp = pmcstat_process_lookup(ev.pl_u.pl_cc.pl_pid,
289			    PMCSTAT_ALLOCATE);
290
291			/* Get PMC record. */
292			pmcr = pmcstat_lookup_pmcid(ev.pl_u.pl_cc.pl_pmcid, pmcstat_mergepmc);
293			assert(pmcr != NULL);
294			pmcr->pr_samples++;
295
296			/*
297			 * Call the plugins processing
298			 */
299
300			if (plugins[args->pa_pplugin].pl_process != NULL)
301				plugins[args->pa_pplugin].pl_process(
302				    pp, pmcr,
303				    ev.pl_u.pl_cc.pl_npc,
304				    ev.pl_u.pl_cc.pl_pc,
305				    PMC_CALLCHAIN_CPUFLAGS_TO_USERMODE(cpuflags),
306				    cpu);
307			plugins[args->pa_plugin].pl_process(
308			    pp, pmcr,
309			    ev.pl_u.pl_cc.pl_npc,
310			    ev.pl_u.pl_cc.pl_pc,
311			    PMC_CALLCHAIN_CPUFLAGS_TO_USERMODE(cpuflags),
312			    cpu);
313			break;
314
315		case PMCLOG_TYPE_PMCALLOCATE:
316			/*
317			 * Record the association pmc id between this
318			 * PMC and its name.
319			 */
320			pmcstat_pmcid_add(ev.pl_u.pl_a.pl_pmcid,
321			    pmcstat_string_intern(ev.pl_u.pl_a.pl_evname),
322			    args, plugins, pmcstat_npmcs);
323			break;
324
325		case PMCLOG_TYPE_PMCALLOCATEDYN:
326			/*
327			 * Record the association pmc id between this
328			 * PMC and its name.
329			 */
330			pmcstat_pmcid_add(ev.pl_u.pl_ad.pl_pmcid,
331			    pmcstat_string_intern(ev.pl_u.pl_ad.pl_evname),
332			    args, plugins, pmcstat_npmcs);
333			break;
334
335		case PMCLOG_TYPE_PROCEXEC:
336			/*
337			 * Change the executable image associated with
338			 * a process.
339			 */
340			pp = pmcstat_process_lookup(ev.pl_u.pl_x.pl_pid,
341			    PMCSTAT_ALLOCATE);
342
343			/* delete the current process map */
344			TAILQ_FOREACH_SAFE(ppm, &pp->pp_map, ppm_next, ppmtmp) {
345				TAILQ_REMOVE(&pp->pp_map, ppm, ppm_next);
346				free(ppm);
347			}
348
349			/*
350			 * Associate this process image.
351			 */
352			image_path = pmcstat_string_intern(
353				ev.pl_u.pl_x.pl_pathname);
354			assert(image_path != NULL);
355			pmcstat_process_exec(pp, image_path,
356			    ev.pl_u.pl_x.pl_entryaddr, args,
357			    plugins, pmcstat_stats);
358			break;
359
360		case PMCLOG_TYPE_PROCEXIT:
361
362			/*
363			 * Due to the way the log is generated, the
364			 * last few samples corresponding to a process
365			 * may appear in the log after the process
366			 * exit event is recorded.  Thus we keep the
367			 * process' descriptor and associated data
368			 * structures around, but mark the process as
369			 * having exited.
370			 */
371			pp = pmcstat_process_lookup(ev.pl_u.pl_e.pl_pid, 0);
372			if (pp == NULL)
373				break;
374			pp->pp_isactive = 0;	/* mark as a zombie */
375			break;
376
377		case PMCLOG_TYPE_SYSEXIT:
378			pp = pmcstat_process_lookup(ev.pl_u.pl_se.pl_pid, 0);
379			if (pp == NULL)
380				break;
381			pp->pp_isactive = 0;	/* make a zombie */
382			break;
383
384		case PMCLOG_TYPE_PROCFORK:
385
386			/*
387			 * Allocate a process descriptor for the new
388			 * (child) process.
389			 */
390			ppnew =
391			    pmcstat_process_lookup(ev.pl_u.pl_f.pl_newpid,
392				PMCSTAT_ALLOCATE);
393
394			/*
395			 * If we had been tracking the parent, clone
396			 * its address maps.
397			 */
398			pp = pmcstat_process_lookup(ev.pl_u.pl_f.pl_oldpid, 0);
399			if (pp == NULL)
400				break;
401			TAILQ_FOREACH(ppm, &pp->pp_map, ppm_next)
402			    pmcstat_image_link(ppnew, ppm->ppm_image,
403				ppm->ppm_lowpc);
404			break;
405
406		default:	/* other types of entries are not relevant */
407			break;
408		}
409	}
410
411	if (ev.pl_state == PMCLOG_EOF)
412		return (PMCSTAT_FINISHED);
413	else if (ev.pl_state == PMCLOG_REQUIRE_DATA)
414		return (PMCSTAT_RUNNING);
415
416	err(EX_DATAERR,
417	    "ERROR: event parsing failed state: %d type: %d (record %jd, offset 0x%jx)",
418	    ev.pl_state, ev.pl_type, (uintmax_t) ev.pl_count + 1, ev.pl_offset);
419}
420
421/*
422 * Open a log file, for reading or writing.
423 *
424 * The function returns the fd of a successfully opened log or -1 in
425 * case of failure.
426 */
427
428int
429pmcstat_open_log(const char *path, int mode)
430{
431	int error, fd, cfd;
432	size_t hlen;
433	const char *p, *errstr;
434	struct addrinfo hints, *res, *res0;
435	char hostname[MAXHOSTNAMELEN];
436
437	errstr = NULL;
438	fd = -1;
439
440	/*
441	 * If 'path' is "-" then open one of stdin or stdout depending
442	 * on the value of 'mode'.
443	 *
444	 * If 'path' contains a ':' and does not start with a '/' or '.',
445	 * and is being opened for writing, treat it as a "host:port"
446	 * specification and open a network socket.
447	 *
448	 * Otherwise, treat 'path' as a file name and open that.
449	 */
450	if (path[0] == '-' && path[1] == '\0')
451		fd = (mode == PMCSTAT_OPEN_FOR_READ) ? 0 : 1;
452	else if (path[0] != '/' &&
453	    path[0] != '.' && strchr(path, ':') != NULL) {
454
455		p = strrchr(path, ':');
456		hlen = p - path;
457		if (p == path || hlen >= sizeof(hostname)) {
458			errstr = strerror(EINVAL);
459			goto done;
460		}
461
462		assert(hlen < sizeof(hostname));
463		(void) strncpy(hostname, path, hlen);
464		hostname[hlen] = '\0';
465
466		(void) memset(&hints, 0, sizeof(hints));
467		hints.ai_family = AF_UNSPEC;
468		hints.ai_socktype = SOCK_STREAM;
469		if ((error = getaddrinfo(hostname, p+1, &hints, &res0)) != 0) {
470			errstr = gai_strerror(error);
471			goto done;
472		}
473
474		fd = -1;
475		for (res = res0; res; res = res->ai_next) {
476			if ((fd = socket(res->ai_family, res->ai_socktype,
477			    res->ai_protocol)) < 0) {
478				errstr = strerror(errno);
479				continue;
480			}
481			if (mode == PMCSTAT_OPEN_FOR_READ) {
482				if (bind(fd, res->ai_addr, res->ai_addrlen) < 0) {
483					errstr = strerror(errno);
484					(void) close(fd);
485					fd = -1;
486					continue;
487				}
488				listen(fd, 1);
489				cfd = accept(fd, NULL, NULL);
490				(void) close(fd);
491				if (cfd < 0) {
492					errstr = strerror(errno);
493					fd = -1;
494					break;
495				}
496				fd = cfd;
497			} else {
498				if (connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
499					errstr = strerror(errno);
500					(void) close(fd);
501					fd = -1;
502					continue;
503				}
504			}
505			errstr = NULL;
506			break;
507		}
508		freeaddrinfo(res0);
509
510	} else if ((fd = open(path, mode == PMCSTAT_OPEN_FOR_READ ?
511		    O_RDONLY : (O_WRONLY|O_CREAT|O_TRUNC),
512		    S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0)
513			errstr = strerror(errno);
514
515  done:
516	if (errstr)
517		errx(EX_OSERR, "ERROR: Cannot open \"%s\" for %s: %s.", path,
518		    (mode == PMCSTAT_OPEN_FOR_READ ? "reading" : "writing"),
519		    errstr);
520
521	return (fd);
522}
523
524/*
525 * Close a logfile, after first flushing all in-module queued data.
526 */
527
528int
529pmcstat_close_log(struct pmcstat_args *args)
530{
531	/* If a local logfile is configured ask the kernel to stop
532	 * and flush data. Kernel will close the file when data is flushed
533	 * so keep the status to EXITING.
534	 */
535	if (args->pa_logfd != -1) {
536		if (pmc_close_logfile() < 0)
537			err(EX_OSERR, "ERROR: logging failed");
538	}
539
540	return (args->pa_flags & FLAG_HAS_PIPE ? PMCSTAT_EXITING :
541	    PMCSTAT_FINISHED);
542}
543
544/*
545 * Initialize module.
546 */
547
548void
549pmcstat_initialize_logging(struct pmcstat_process **pmcstat_kernproc,
550    struct pmcstat_args *args, struct pmc_plugins *plugins,
551    int *pmcstat_npmcs, int *pmcstat_mergepmc)
552{
553	struct pmcstat_process *pmcstat_kp;
554	int i;
555
556	/* use a convenient format for 'ldd' output */
557	if (setenv("LD_TRACE_LOADED_OBJECTS_FMT1","%o \"%p\" %x\n",1) != 0)
558		err(EX_OSERR, "ERROR: Cannot setenv");
559
560	/* Initialize hash tables */
561	pmcstat_string_initialize();
562	for (i = 0; i < PMCSTAT_NHASH; i++) {
563		LIST_INIT(&pmcstat_image_hash[i]);
564		LIST_INIT(&pmcstat_process_hash[i]);
565	}
566
567	/*
568	 * Create a fake 'process' entry for the kernel with pid -1.
569	 * hwpmc(4) will subsequently inform us about where the kernel
570	 * and any loaded kernel modules are mapped.
571	 */
572	if ((pmcstat_kp = pmcstat_process_lookup((pid_t) -1,
573	    PMCSTAT_ALLOCATE)) == NULL)
574		err(EX_OSERR, "ERROR: Cannot initialize logging");
575
576	*pmcstat_kernproc = pmcstat_kp;
577
578	/* PMC count. */
579	*pmcstat_npmcs = 0;
580
581	/* Merge PMC with same name. */
582	*pmcstat_mergepmc = args->pa_mergepmc;
583
584	/*
585	 * Initialize plugins
586	 */
587
588	if (plugins[args->pa_pplugin].pl_init != NULL)
589		plugins[args->pa_pplugin].pl_init();
590	if (plugins[args->pa_plugin].pl_init != NULL)
591		plugins[args->pa_plugin].pl_init();
592}
593
594/*
595 * Shutdown module.
596 */
597
598void
599pmcstat_shutdown_logging(struct pmcstat_args *args,
600    struct pmc_plugins *plugins,
601    struct pmcstat_stats *pmcstat_stats)
602{
603	struct pmcstat_image *pi, *pitmp;
604	struct pmcstat_process *pp, *pptmp;
605	struct pmcstat_pcmap *ppm, *ppmtmp;
606	FILE *mf;
607	int i;
608
609	/* determine where to send the map file */
610	mf = NULL;
611	if (args->pa_mapfilename != NULL)
612		mf = (strcmp(args->pa_mapfilename, "-") == 0) ?
613		    args->pa_printfile : fopen(args->pa_mapfilename, "w");
614
615	if (mf == NULL && args->pa_flags & FLAG_DO_GPROF &&
616	    args->pa_verbosity >= 2)
617		mf = args->pa_printfile;
618
619	if (mf)
620		(void) fprintf(mf, "MAP:\n");
621
622	/*
623	 * Shutdown the plugins
624	 */
625
626	if (plugins[args->pa_plugin].pl_shutdown != NULL)
627		plugins[args->pa_plugin].pl_shutdown(mf);
628	if (plugins[args->pa_pplugin].pl_shutdown != NULL)
629		plugins[args->pa_pplugin].pl_shutdown(mf);
630
631	for (i = 0; i < PMCSTAT_NHASH; i++) {
632		LIST_FOREACH_SAFE(pi, &pmcstat_image_hash[i], pi_next,
633		    pitmp) {
634			if (plugins[args->pa_plugin].pl_shutdownimage != NULL)
635				plugins[args->pa_plugin].pl_shutdownimage(pi);
636			if (plugins[args->pa_pplugin].pl_shutdownimage != NULL)
637				plugins[args->pa_pplugin].pl_shutdownimage(pi);
638
639			free(pi->pi_symbols);
640			if (pi->pi_addr2line != NULL)
641				pclose(pi->pi_addr2line);
642			LIST_REMOVE(pi, pi_next);
643			free(pi);
644		}
645
646		LIST_FOREACH_SAFE(pp, &pmcstat_process_hash[i], pp_next,
647		    pptmp) {
648			TAILQ_FOREACH_SAFE(ppm, &pp->pp_map, ppm_next, ppmtmp) {
649				TAILQ_REMOVE(&pp->pp_map, ppm, ppm_next);
650				free(ppm);
651			}
652			LIST_REMOVE(pp, pp_next);
653			free(pp);
654		}
655	}
656
657	pmcstat_string_shutdown();
658
659	/*
660	 * Print errors unless -q was specified.  Print all statistics
661	 * if verbosity > 1.
662	 */
663#define	PRINT(N,V) do {							\
664		if (pmcstat_stats->ps_##V || args->pa_verbosity >= 2)	\
665			(void) fprintf(args->pa_printfile, " %-40s %d\n",\
666			    N, pmcstat_stats->ps_##V);			\
667	} while (0)
668
669	if (args->pa_verbosity >= 1 && (args->pa_flags & FLAG_DO_ANALYSIS)) {
670		(void) fprintf(args->pa_printfile, "CONVERSION STATISTICS:\n");
671		PRINT("#exec/a.out", exec_aout);
672		PRINT("#exec/elf", exec_elf);
673		PRINT("#exec/unknown", exec_indeterminable);
674		PRINT("#exec handling errors", exec_errors);
675		PRINT("#samples/total", samples_total);
676		PRINT("#samples/unclaimed", samples_unknown_offset);
677		PRINT("#samples/unknown-object", samples_indeterminable);
678		PRINT("#samples/unknown-function", samples_unknown_function);
679		PRINT("#callchain/dubious-frames", callchain_dubious_frames);
680	}
681
682	if (mf)
683		(void) fclose(mf);
684}
685