12973Sjlahoda/*-
22973Sjlahoda * Copyright (c) 2005-2007 Joseph Koshy
32973Sjlahoda * Copyright (c) 2007 The FreeBSD Foundation
42973Sjlahoda * All rights reserved.
52973Sjlahoda *
62973Sjlahoda * Portions of this software were developed by A. Joseph Koshy under
72973Sjlahoda * sponsorship from the FreeBSD Foundation and Google, Inc.
82973Sjlahoda *
92973Sjlahoda * Redistribution and use in source and binary forms, with or without
102973Sjlahoda * modification, are permitted provided that the following conditions
112973Sjlahoda * are met:
122973Sjlahoda * 1. Redistributions of source code must retain the above copyright
132973Sjlahoda *    notice, this list of conditions and the following disclaimer.
142973Sjlahoda * 2. Redistributions in binary form must reproduce the above copyright
152973Sjlahoda *    notice, this list of conditions and the following disclaimer in the
162973Sjlahoda *    documentation and/or other materials provided with the distribution.
172973Sjlahoda *
182973Sjlahoda * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
192973Sjlahoda * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
202973Sjlahoda * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
212973Sjlahoda * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
222973Sjlahoda * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
232973Sjlahoda * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
242973Sjlahoda * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
252973Sjlahoda * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
262973Sjlahoda * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
272973Sjlahoda * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
282973Sjlahoda * SUCH DAMAGE.
292973Sjlahoda */
302973Sjlahoda
312973Sjlahoda#include <sys/cdefs.h>
322973Sjlahoda__FBSDID("$FreeBSD: releng/10.3/lib/libpmc/pmclog.c 233628 2012-03-28 20:58:30Z fabient $");
332973Sjlahoda
342973Sjlahoda#include <sys/param.h>
352973Sjlahoda#include <sys/pmc.h>
362973Sjlahoda#include <sys/pmclog.h>
372973Sjlahoda
382973Sjlahoda#include <assert.h>
392973Sjlahoda#include <errno.h>
402973Sjlahoda#include <pmc.h>
412973Sjlahoda#include <pmclog.h>
422973Sjlahoda#include <stddef.h>
432973Sjlahoda#include <stdlib.h>
442973Sjlahoda#include <string.h>
452973Sjlahoda#include <strings.h>
462973Sjlahoda#include <unistd.h>
472973Sjlahoda
482973Sjlahoda#include <machine/pmc_mdep.h>
492973Sjlahoda
502973Sjlahoda#include "libpmcinternal.h"
512973Sjlahoda
522973Sjlahoda#define	PMCLOG_BUFFER_SIZE			4096
532973Sjlahoda
542973Sjlahoda/*
552973Sjlahoda * API NOTES
562973Sjlahoda *
572973Sjlahoda * The pmclog(3) API is oriented towards parsing an event stream in
582973Sjlahoda * "realtime", i.e., from an data source that may or may not preserve
592973Sjlahoda * record boundaries -- for example when the data source is elsewhere
602973Sjlahoda * on a network.  The API allows data to be fed into the parser zero
612973Sjlahoda * or more bytes at a time.
622973Sjlahoda *
632973Sjlahoda * The state for a log file parser is maintained in a 'struct
642973Sjlahoda * pmclog_parse_state'.  Parser invocations are done by calling
652973Sjlahoda * 'pmclog_read()'; this function will inform the caller when a
662973Sjlahoda * complete event is parsed.
672973Sjlahoda *
682973Sjlahoda * The parser first assembles a complete log file event in an internal
692973Sjlahoda * work area (see "ps_saved" below).  Once a complete log file event
702973Sjlahoda * is read, the parser then parses it and converts it to an event
712973Sjlahoda * descriptor usable by the client.  We could possibly avoid this two
722973Sjlahoda * step process by directly parsing the input log to set fields in the
732973Sjlahoda * event record.  However the parser's state machine would get
742973Sjlahoda * insanely complicated, and this code is unlikely to be used in
752973Sjlahoda * performance critical paths.
762973Sjlahoda */
772973Sjlahoda
782973Sjlahodaenum pmclog_parser_state {
792973Sjlahoda	PL_STATE_NEW_RECORD,		/* in-between records */
802973Sjlahoda	PL_STATE_EXPECTING_HEADER,	/* header being read */
812973Sjlahoda	PL_STATE_PARTIAL_RECORD,	/* header present but not the record */
822973Sjlahoda	PL_STATE_ERROR			/* parsing error encountered */
832973Sjlahoda};
842973Sjlahoda
852973Sjlahodastruct pmclog_parse_state {
862973Sjlahoda	enum pmclog_parser_state ps_state;
872973Sjlahoda	enum pmc_cputype	ps_arch;	/* log file architecture */
882973Sjlahoda	uint32_t		ps_version;	/* hwpmc version */
892973Sjlahoda	int			ps_initialized;	/* whether initialized */
902973Sjlahoda	int			ps_count;	/* count of records processed */
912973Sjlahoda	off_t			ps_offset;	/* stream byte offset */
922973Sjlahoda	union pmclog_entry	ps_saved;	/* saved partial log entry */
932973Sjlahoda	int			ps_svcount;	/* #bytes saved */
942973Sjlahoda	int			ps_fd;		/* active fd or -1 */
952973Sjlahoda	char			*ps_buffer;	/* scratch buffer if fd != -1 */
962973Sjlahoda	char			*ps_data;	/* current parse pointer */
972973Sjlahoda	size_t			ps_len;		/* length of buffered data */
982973Sjlahoda};
992973Sjlahoda
1002973Sjlahoda#define	PMCLOG_HEADER_FROM_SAVED_STATE(PS)				\
1012973Sjlahoda	(* ((uint32_t *) &(PS)->ps_saved))
1022973Sjlahoda
1032973Sjlahoda#define	PMCLOG_INITIALIZE_READER(LE,A)	LE = (uint32_t *) &(A)
1042973Sjlahoda#define	PMCLOG_READ32(LE,V) 		do {				\
1052973Sjlahoda		(V)  = *(LE)++;						\
1062973Sjlahoda	} while (0)
1072973Sjlahoda#define	PMCLOG_READ64(LE,V)		do {				\
1082973Sjlahoda		uint64_t _v;						\
1092973Sjlahoda		_v  = (uint64_t) *(LE)++;				\
1102973Sjlahoda		_v |= ((uint64_t) *(LE)++) << 32;			\
1112973Sjlahoda		(V) = _v;						\
1122973Sjlahoda	} while (0)
1132973Sjlahoda
1142973Sjlahoda#define	PMCLOG_READSTRING(LE,DST,LEN)	strlcpy((DST), (char *) (LE), (LEN))
1152973Sjlahoda
1162973Sjlahoda/*
1172973Sjlahoda * Assemble a log record from '*len' octets starting from address '*data'.
1182973Sjlahoda * Update 'data' and 'len' to reflect the number of bytes consumed.
1192973Sjlahoda *
1202973Sjlahoda * '*data' is potentially an unaligned address and '*len' octets may
1212973Sjlahoda * not be enough to complete a event record.
1222973Sjlahoda */
1232973Sjlahoda
1242973Sjlahodastatic enum pmclog_parser_state
1252973Sjlahodapmclog_get_record(struct pmclog_parse_state *ps, char **data, ssize_t *len)
1262973Sjlahoda{
1272973Sjlahoda	int avail, copylen, recordsize, used;
1282973Sjlahoda	uint32_t h;
1292973Sjlahoda	const int HEADERSIZE = sizeof(uint32_t);
1302973Sjlahoda	char *src, *dst;
1312973Sjlahoda
1322973Sjlahoda	if ((avail = *len) <= 0)
1332973Sjlahoda		return (ps->ps_state = PL_STATE_ERROR);
1342973Sjlahoda
1352973Sjlahoda	src = *data;
1362973Sjlahoda	h = used = 0;
1372973Sjlahoda
1382973Sjlahoda	if (ps->ps_state == PL_STATE_NEW_RECORD)
1392973Sjlahoda		ps->ps_svcount = 0;
1402973Sjlahoda
1412973Sjlahoda	dst = (char *) &ps->ps_saved + ps->ps_svcount;
1422973Sjlahoda
1432973Sjlahoda	switch (ps->ps_state) {
1442973Sjlahoda	case PL_STATE_NEW_RECORD:
1452973Sjlahoda
1462973Sjlahoda		/*
1472973Sjlahoda		 * Transitions:
1482973Sjlahoda		 *
1492973Sjlahoda		 * Case A: avail < headersize
1502973Sjlahoda		 *	-> 'expecting header'
1512973Sjlahoda		 *
1522973Sjlahoda		 * Case B: avail >= headersize
1532973Sjlahoda		 *    B.1: avail < recordsize
1542973Sjlahoda		 *	   -> 'partial record'
1552973Sjlahoda		 *    B.2: avail >= recordsize
1562973Sjlahoda		 *         -> 'new record'
1572973Sjlahoda		 */
1582973Sjlahoda
1592973Sjlahoda		copylen = avail < HEADERSIZE ? avail : HEADERSIZE;
1602973Sjlahoda		bcopy(src, dst, copylen);
1612973Sjlahoda		ps->ps_svcount = used = copylen;
1622973Sjlahoda
1632973Sjlahoda		if (copylen < HEADERSIZE) {
1642973Sjlahoda			ps->ps_state = PL_STATE_EXPECTING_HEADER;
1652973Sjlahoda			goto done;
1662973Sjlahoda		}
1672973Sjlahoda
1682973Sjlahoda		src += copylen;
1692973Sjlahoda		dst += copylen;
1702973Sjlahoda
1712973Sjlahoda		h = PMCLOG_HEADER_FROM_SAVED_STATE(ps);
1722973Sjlahoda		recordsize = PMCLOG_HEADER_TO_LENGTH(h);
1732973Sjlahoda
1742973Sjlahoda		if (recordsize <= 0)
1752973Sjlahoda			goto error;
1762973Sjlahoda
1772973Sjlahoda		if (recordsize <= avail) { /* full record available */
1782973Sjlahoda			bcopy(src, dst, recordsize - copylen);
1792973Sjlahoda			ps->ps_svcount = used = recordsize;
1802973Sjlahoda			goto done;
1812973Sjlahoda		}
1822973Sjlahoda
1832973Sjlahoda		/* header + a partial record is available */
1842973Sjlahoda		bcopy(src, dst, avail - copylen);
1852973Sjlahoda		ps->ps_svcount = used = avail;
1862973Sjlahoda		ps->ps_state = PL_STATE_PARTIAL_RECORD;
1872973Sjlahoda
1882973Sjlahoda		break;
1892973Sjlahoda
1902973Sjlahoda	case PL_STATE_EXPECTING_HEADER:
1912973Sjlahoda
1922973Sjlahoda		/*
1932973Sjlahoda		 * Transitions:
1942973Sjlahoda		 *
1952973Sjlahoda		 * Case C: avail+saved < headersize
1962973Sjlahoda		 * 	-> 'expecting header'
1972973Sjlahoda		 *
1982973Sjlahoda		 * Case D: avail+saved >= headersize
1992973Sjlahoda		 *    D.1: avail+saved < recordsize
2002973Sjlahoda		 *    	-> 'partial record'
2012973Sjlahoda		 *    D.2: avail+saved >= recordsize
2022973Sjlahoda		 *    	-> 'new record'
2032973Sjlahoda		 *    (see PARTIAL_RECORD handling below)
2042973Sjlahoda		 */
2052973Sjlahoda
2062973Sjlahoda		if (avail + ps->ps_svcount < HEADERSIZE) {
2072973Sjlahoda			bcopy(src, dst, avail);
2082973Sjlahoda			ps->ps_svcount += avail;
2092973Sjlahoda			used = avail;
2102973Sjlahoda			break;
2112973Sjlahoda		}
2122973Sjlahoda
2132973Sjlahoda		used = copylen = HEADERSIZE - ps->ps_svcount;
2142973Sjlahoda		bcopy(src, dst, copylen);
2152973Sjlahoda		src += copylen;
2162973Sjlahoda		dst += copylen;
2172973Sjlahoda		avail -= copylen;
2182973Sjlahoda		ps->ps_svcount += copylen;
2192973Sjlahoda
2202973Sjlahoda		/*FALLTHROUGH*/
2212973Sjlahoda
2222973Sjlahoda	case PL_STATE_PARTIAL_RECORD:
2232973Sjlahoda
2242973Sjlahoda		/*
2252973Sjlahoda		 * Transitions:
2262973Sjlahoda		 *
2272973Sjlahoda		 * Case E: avail+saved < recordsize
2282973Sjlahoda		 * 	-> 'partial record'
2292973Sjlahoda		 *
2302973Sjlahoda		 * Case F: avail+saved >= recordsize
2312973Sjlahoda		 * 	-> 'new record'
2322973Sjlahoda		 */
2332973Sjlahoda
2342973Sjlahoda		h = PMCLOG_HEADER_FROM_SAVED_STATE(ps);
2352973Sjlahoda		recordsize = PMCLOG_HEADER_TO_LENGTH(h);
2362973Sjlahoda
2372973Sjlahoda		if (recordsize <= 0)
2382973Sjlahoda			goto error;
2392973Sjlahoda
2402973Sjlahoda		if (avail + ps->ps_svcount < recordsize) {
2412973Sjlahoda			copylen = avail;
2422973Sjlahoda			ps->ps_state = PL_STATE_PARTIAL_RECORD;
2432973Sjlahoda		} else {
2442973Sjlahoda			copylen = recordsize - ps->ps_svcount;
2452973Sjlahoda			ps->ps_state = PL_STATE_NEW_RECORD;
2462973Sjlahoda		}
2472973Sjlahoda
2482973Sjlahoda		bcopy(src, dst, copylen);
2492973Sjlahoda		ps->ps_svcount += copylen;
2502973Sjlahoda		used += copylen;
2512973Sjlahoda		break;
2522973Sjlahoda
2532973Sjlahoda	default:
2542973Sjlahoda		goto error;
2552973Sjlahoda	}
2562973Sjlahoda
2572973Sjlahoda done:
2582973Sjlahoda	*data += used;
2592973Sjlahoda	*len  -= used;
2602973Sjlahoda	return ps->ps_state;
2612973Sjlahoda
2622973Sjlahoda error:
2632973Sjlahoda	ps->ps_state = PL_STATE_ERROR;
2642973Sjlahoda	return ps->ps_state;
2652973Sjlahoda}
2662973Sjlahoda
2672973Sjlahoda/*
2682973Sjlahoda * Get an event from the stream pointed to by '*data'.  '*len'
2692973Sjlahoda * indicates the number of bytes available to parse.  Arguments
2702973Sjlahoda * '*data' and '*len' are updated to indicate the number of bytes
2712973Sjlahoda * consumed.
2722973Sjlahoda */
2732973Sjlahoda
2742973Sjlahodastatic int
2752973Sjlahodapmclog_get_event(void *cookie, char **data, ssize_t *len,
2762973Sjlahoda    struct pmclog_ev *ev)
2772973Sjlahoda{
2782973Sjlahoda	int evlen, pathlen;
2792973Sjlahoda	uint32_t h, *le, npc;
2802973Sjlahoda	enum pmclog_parser_state e;
2812973Sjlahoda	struct pmclog_parse_state *ps;
2822973Sjlahoda
2832973Sjlahoda	ps = (struct pmclog_parse_state *) cookie;
2842973Sjlahoda
2852973Sjlahoda	assert(ps->ps_state != PL_STATE_ERROR);
2862973Sjlahoda
2872973Sjlahoda	if ((e = pmclog_get_record(ps,data,len)) == PL_STATE_ERROR) {
2882973Sjlahoda		ev->pl_state = PMCLOG_ERROR;
2892973Sjlahoda		return -1;
2902973Sjlahoda	}
2912973Sjlahoda
2922973Sjlahoda	if (e != PL_STATE_NEW_RECORD) {
2932973Sjlahoda		ev->pl_state = PMCLOG_REQUIRE_DATA;
2942973Sjlahoda		return -1;
2952973Sjlahoda	}
2962973Sjlahoda
2972973Sjlahoda	PMCLOG_INITIALIZE_READER(le, ps->ps_saved);
2982973Sjlahoda
2992973Sjlahoda	PMCLOG_READ32(le,h);
3002973Sjlahoda
3012973Sjlahoda	if (!PMCLOG_HEADER_CHECK_MAGIC(h)) {
3022973Sjlahoda		ps->ps_state = PL_STATE_ERROR;
3032973Sjlahoda		ev->pl_state = PMCLOG_ERROR;
3042973Sjlahoda		return -1;
3052973Sjlahoda	}
3062973Sjlahoda
3072973Sjlahoda	/* copy out the time stamp */
3082973Sjlahoda	PMCLOG_READ32(le,ev->pl_ts.tv_sec);
3092973Sjlahoda	PMCLOG_READ32(le,ev->pl_ts.tv_nsec);
3102973Sjlahoda
3112973Sjlahoda	evlen = PMCLOG_HEADER_TO_LENGTH(h);
3122973Sjlahoda
3132973Sjlahoda#define	PMCLOG_GET_PATHLEN(P,E,TYPE) do {				\
3142973Sjlahoda		(P) = (E) - offsetof(struct TYPE, pl_pathname);		\
3152973Sjlahoda		if ((P) > PATH_MAX || (P) < 0)				\
3162973Sjlahoda			goto error;					\
3172973Sjlahoda	} while (0)
3182973Sjlahoda
3192973Sjlahoda#define	PMCLOG_GET_CALLCHAIN_SIZE(SZ,E) do {				\
3202973Sjlahoda		(SZ) = ((E) - offsetof(struct pmclog_callchain, pl_pc))	\
3212973Sjlahoda			/ sizeof(uintfptr_t);				\
3222973Sjlahoda	} while (0);
3232973Sjlahoda
3242973Sjlahoda	switch (ev->pl_type = PMCLOG_HEADER_TO_TYPE(h)) {
3252973Sjlahoda	case PMCLOG_TYPE_CALLCHAIN:
3262973Sjlahoda		PMCLOG_READ32(le,ev->pl_u.pl_cc.pl_pid);
3272973Sjlahoda		PMCLOG_READ32(le,ev->pl_u.pl_cc.pl_pmcid);
3282973Sjlahoda		PMCLOG_READ32(le,ev->pl_u.pl_cc.pl_cpuflags);
3292973Sjlahoda		PMCLOG_GET_CALLCHAIN_SIZE(ev->pl_u.pl_cc.pl_npc,evlen);
3302973Sjlahoda		for (npc = 0; npc < ev->pl_u.pl_cc.pl_npc; npc++)
3312973Sjlahoda			PMCLOG_READADDR(le,ev->pl_u.pl_cc.pl_pc[npc]);
3322973Sjlahoda		for (;npc < PMC_CALLCHAIN_DEPTH_MAX; npc++)
3332973Sjlahoda			ev->pl_u.pl_cc.pl_pc[npc] = (uintfptr_t) 0;
3342973Sjlahoda		break;
3352973Sjlahoda	case PMCLOG_TYPE_CLOSELOG:
3362973Sjlahoda	case PMCLOG_TYPE_DROPNOTIFY:
3372973Sjlahoda		/* nothing to do */
3382973Sjlahoda		break;
3392973Sjlahoda	case PMCLOG_TYPE_INITIALIZE:
3402973Sjlahoda		PMCLOG_READ32(le,ev->pl_u.pl_i.pl_version);
3412973Sjlahoda		PMCLOG_READ32(le,ev->pl_u.pl_i.pl_arch);
3422973Sjlahoda		ps->ps_version = ev->pl_u.pl_i.pl_version;
3432973Sjlahoda		ps->ps_arch = ev->pl_u.pl_i.pl_arch;
3442973Sjlahoda		ps->ps_initialized = 1;
3452973Sjlahoda		break;
3462973Sjlahoda	case PMCLOG_TYPE_MAP_IN:
3472973Sjlahoda		PMCLOG_GET_PATHLEN(pathlen,evlen,pmclog_map_in);
3482973Sjlahoda		PMCLOG_READ32(le,ev->pl_u.pl_mi.pl_pid);
3492973Sjlahoda		PMCLOG_READADDR(le,ev->pl_u.pl_mi.pl_start);
3502973Sjlahoda		PMCLOG_READSTRING(le, ev->pl_u.pl_mi.pl_pathname, pathlen);
3512973Sjlahoda		break;
3522973Sjlahoda	case PMCLOG_TYPE_MAP_OUT:
3532973Sjlahoda		PMCLOG_READ32(le,ev->pl_u.pl_mo.pl_pid);
3542973Sjlahoda		PMCLOG_READADDR(le,ev->pl_u.pl_mo.pl_start);
3552973Sjlahoda		PMCLOG_READADDR(le,ev->pl_u.pl_mo.pl_end);
3562973Sjlahoda		break;
3572973Sjlahoda	case PMCLOG_TYPE_PCSAMPLE:
3582973Sjlahoda		PMCLOG_READ32(le,ev->pl_u.pl_s.pl_pid);
3592973Sjlahoda		PMCLOG_READADDR(le,ev->pl_u.pl_s.pl_pc);
3602973Sjlahoda		PMCLOG_READ32(le,ev->pl_u.pl_s.pl_pmcid);
3612973Sjlahoda		PMCLOG_READ32(le,ev->pl_u.pl_s.pl_usermode);
3622973Sjlahoda		break;
3632973Sjlahoda	case PMCLOG_TYPE_PMCALLOCATE:
3642973Sjlahoda		PMCLOG_READ32(le,ev->pl_u.pl_a.pl_pmcid);
3652973Sjlahoda		PMCLOG_READ32(le,ev->pl_u.pl_a.pl_event);
3662973Sjlahoda		PMCLOG_READ32(le,ev->pl_u.pl_a.pl_flags);
3672973Sjlahoda		if ((ev->pl_u.pl_a.pl_evname =
3682973Sjlahoda		    _pmc_name_of_event(ev->pl_u.pl_a.pl_event, ps->ps_arch))
3692973Sjlahoda		    == NULL)
3702973Sjlahoda			goto error;
3712973Sjlahoda		break;
3722973Sjlahoda	case PMCLOG_TYPE_PMCALLOCATEDYN:
3732973Sjlahoda		PMCLOG_READ32(le,ev->pl_u.pl_ad.pl_pmcid);
3742973Sjlahoda		PMCLOG_READ32(le,ev->pl_u.pl_ad.pl_event);
3752973Sjlahoda		PMCLOG_READ32(le,ev->pl_u.pl_ad.pl_flags);
3762973Sjlahoda		PMCLOG_READSTRING(le,ev->pl_u.pl_ad.pl_evname,PMC_NAME_MAX);
3772973Sjlahoda		break;
3782973Sjlahoda	case PMCLOG_TYPE_PMCATTACH:
3792973Sjlahoda		PMCLOG_GET_PATHLEN(pathlen,evlen,pmclog_pmcattach);
3802973Sjlahoda		PMCLOG_READ32(le,ev->pl_u.pl_t.pl_pmcid);
3812973Sjlahoda		PMCLOG_READ32(le,ev->pl_u.pl_t.pl_pid);
3822973Sjlahoda		PMCLOG_READSTRING(le,ev->pl_u.pl_t.pl_pathname,pathlen);
3832973Sjlahoda		break;
3842973Sjlahoda	case PMCLOG_TYPE_PMCDETACH:
3852973Sjlahoda		PMCLOG_READ32(le,ev->pl_u.pl_d.pl_pmcid);
3862973Sjlahoda		PMCLOG_READ32(le,ev->pl_u.pl_d.pl_pid);
3872973Sjlahoda		break;
3882973Sjlahoda	case PMCLOG_TYPE_PROCCSW:
3892973Sjlahoda		PMCLOG_READ32(le,ev->pl_u.pl_c.pl_pmcid);
3902973Sjlahoda		PMCLOG_READ64(le,ev->pl_u.pl_c.pl_value);
3912973Sjlahoda		PMCLOG_READ32(le,ev->pl_u.pl_c.pl_pid);
3922973Sjlahoda		break;
3932973Sjlahoda	case PMCLOG_TYPE_PROCEXEC:
3942973Sjlahoda		PMCLOG_GET_PATHLEN(pathlen,evlen,pmclog_procexec);
3952973Sjlahoda		PMCLOG_READ32(le,ev->pl_u.pl_x.pl_pid);
3962973Sjlahoda		PMCLOG_READADDR(le,ev->pl_u.pl_x.pl_entryaddr);
3972973Sjlahoda		PMCLOG_READ32(le,ev->pl_u.pl_x.pl_pmcid);
3982973Sjlahoda		PMCLOG_READSTRING(le,ev->pl_u.pl_x.pl_pathname,pathlen);
3992973Sjlahoda		break;
4002973Sjlahoda	case PMCLOG_TYPE_PROCEXIT:
4012973Sjlahoda		PMCLOG_READ32(le,ev->pl_u.pl_e.pl_pmcid);
4022973Sjlahoda		PMCLOG_READ64(le,ev->pl_u.pl_e.pl_value);
4032973Sjlahoda		PMCLOG_READ32(le,ev->pl_u.pl_e.pl_pid);
4042973Sjlahoda		break;
4052973Sjlahoda	case PMCLOG_TYPE_PROCFORK:
4062973Sjlahoda		PMCLOG_READ32(le,ev->pl_u.pl_f.pl_oldpid);
4072973Sjlahoda		PMCLOG_READ32(le,ev->pl_u.pl_f.pl_newpid);
4082973Sjlahoda		break;
4092973Sjlahoda	case PMCLOG_TYPE_SYSEXIT:
4102973Sjlahoda		PMCLOG_READ32(le,ev->pl_u.pl_se.pl_pid);
4112973Sjlahoda		break;
4122973Sjlahoda	case PMCLOG_TYPE_USERDATA:
4132973Sjlahoda		PMCLOG_READ32(le,ev->pl_u.pl_u.pl_userdata);
4142973Sjlahoda		break;
4152973Sjlahoda	default:	/* unknown record type */
4162973Sjlahoda		ps->ps_state = PL_STATE_ERROR;
4172973Sjlahoda		ev->pl_state = PMCLOG_ERROR;
4182973Sjlahoda		return (-1);
4192973Sjlahoda	}
4202973Sjlahoda
4212973Sjlahoda	ev->pl_offset = (ps->ps_offset += evlen);
4222973Sjlahoda	ev->pl_count  = (ps->ps_count += 1);
4232973Sjlahoda	ev->pl_state = PMCLOG_OK;
4242973Sjlahoda	return 0;
4252973Sjlahoda
4262973Sjlahoda error:
4272973Sjlahoda	ev->pl_state = PMCLOG_ERROR;
4282973Sjlahoda	ps->ps_state = PL_STATE_ERROR;
4292973Sjlahoda	return -1;
4302973Sjlahoda}
4312973Sjlahoda
4322973Sjlahoda/*
4332973Sjlahoda * Extract and return the next event from the byte stream.
4342973Sjlahoda *
4352973Sjlahoda * Returns 0 and sets the event's state to PMCLOG_OK in case an event
4362973Sjlahoda * was successfully parsed.  Otherwise this function returns -1 and
4372973Sjlahoda * sets the event's state to one of PMCLOG_REQUIRE_DATA (if more data
4382973Sjlahoda * is needed) or PMCLOG_EOF (if an EOF was seen) or PMCLOG_ERROR if
4392973Sjlahoda * a parse error was encountered.
4402973Sjlahoda */
4412973Sjlahoda
4422973Sjlahodaint
4432973Sjlahodapmclog_read(void *cookie, struct pmclog_ev *ev)
4442973Sjlahoda{
4452973Sjlahoda	int retval;
4462973Sjlahoda	ssize_t nread;
4472973Sjlahoda	struct pmclog_parse_state *ps;
4482973Sjlahoda
4492973Sjlahoda	ps = (struct pmclog_parse_state *) cookie;
4502973Sjlahoda
4512973Sjlahoda	if (ps->ps_state == PL_STATE_ERROR) {
4522973Sjlahoda		ev->pl_state = PMCLOG_ERROR;
4532973Sjlahoda		return -1;
4542973Sjlahoda	}
4552973Sjlahoda
4562973Sjlahoda	/*
4572973Sjlahoda	 * If there isn't enough data left for a new event try and get
4582973Sjlahoda	 * more data.
4592973Sjlahoda	 */
4602973Sjlahoda	if (ps->ps_len == 0) {
4612973Sjlahoda		ev->pl_state = PMCLOG_REQUIRE_DATA;
4622973Sjlahoda
4632973Sjlahoda		/*
4642973Sjlahoda		 * If we have a valid file descriptor to read from, attempt
4652973Sjlahoda		 * to read from that.  This read may return with an error,
4662973Sjlahoda		 * (which may be EAGAIN or other recoverable error), or
4672973Sjlahoda		 * can return EOF.
4682973Sjlahoda		 */
4692973Sjlahoda		if (ps->ps_fd != PMCLOG_FD_NONE) {
4702973Sjlahoda		refill:
4712973Sjlahoda			nread = read(ps->ps_fd, ps->ps_buffer,
4722973Sjlahoda			    PMCLOG_BUFFER_SIZE);
4732973Sjlahoda
4742973Sjlahoda			if (nread <= 0) {
4752973Sjlahoda				if (nread == 0)
4762973Sjlahoda					ev->pl_state = PMCLOG_EOF;
4772973Sjlahoda				else if (errno != EAGAIN) /* not restartable */
4782973Sjlahoda					ev->pl_state = PMCLOG_ERROR;
4792973Sjlahoda				return -1;
4802973Sjlahoda			}
4812973Sjlahoda
4822973Sjlahoda			ps->ps_len = nread;
4832973Sjlahoda			ps->ps_data = ps->ps_buffer;
4842973Sjlahoda		} else
4852973Sjlahoda			return -1;
4862973Sjlahoda	}
4872973Sjlahoda
4882973Sjlahoda	assert(ps->ps_len > 0);
4892973Sjlahoda
4902973Sjlahoda
4912973Sjlahoda	 /* Retrieve one event from the byte stream. */
4922973Sjlahoda	retval = pmclog_get_event(ps, &ps->ps_data, &ps->ps_len, ev);
4932973Sjlahoda
4942973Sjlahoda	/*
4952973Sjlahoda	 * If we need more data and we have a configured fd, try read
4962973Sjlahoda	 * from it.
4972973Sjlahoda	 */
4982973Sjlahoda	if (retval < 0 && ev->pl_state == PMCLOG_REQUIRE_DATA &&
4992973Sjlahoda	    ps->ps_fd != -1) {
5002973Sjlahoda		assert(ps->ps_len == 0);
5012973Sjlahoda		goto refill;
5022973Sjlahoda	}
5032973Sjlahoda
5042973Sjlahoda	return retval;
5052973Sjlahoda}
5062973Sjlahoda
5072973Sjlahoda/*
5082973Sjlahoda * Feed data to a memory based parser.
5092973Sjlahoda *
5102973Sjlahoda * The memory area pointed to by 'data' needs to be valid till the
5112973Sjlahoda * next error return from pmclog_next_event().
5122973Sjlahoda */
5132973Sjlahoda
5142973Sjlahodaint
5152973Sjlahodapmclog_feed(void *cookie, char *data, int len)
5162973Sjlahoda{
5172973Sjlahoda	struct pmclog_parse_state *ps;
5182973Sjlahoda
5192973Sjlahoda	ps = (struct pmclog_parse_state *) cookie;
5202973Sjlahoda
5212973Sjlahoda	if (len < 0 ||		/* invalid length */
5222973Sjlahoda	    ps->ps_buffer ||	/* called for a file parser */
5232973Sjlahoda	    ps->ps_len != 0)	/* unnecessary call */
5242973Sjlahoda		return -1;
5252973Sjlahoda
5262973Sjlahoda	ps->ps_data = data;
5272973Sjlahoda	ps->ps_len  = len;
5282973Sjlahoda
5292973Sjlahoda	return 0;
5302973Sjlahoda}
5312973Sjlahoda
5322973Sjlahoda/*
5332973Sjlahoda * Allocate and initialize parser state.
5342973Sjlahoda */
5352973Sjlahoda
5362973Sjlahodavoid *
5372973Sjlahodapmclog_open(int fd)
5382973Sjlahoda{
5392973Sjlahoda	struct pmclog_parse_state *ps;
5402973Sjlahoda
5412973Sjlahoda	if ((ps = (struct pmclog_parse_state *) malloc(sizeof(*ps))) == NULL)
5422973Sjlahoda		return NULL;
5432973Sjlahoda
5442973Sjlahoda	ps->ps_state = PL_STATE_NEW_RECORD;
5452973Sjlahoda	ps->ps_arch = -1;
5462973Sjlahoda	ps->ps_initialized = 0;
5472973Sjlahoda	ps->ps_count = 0;
5482973Sjlahoda	ps->ps_offset = (off_t) 0;
5492973Sjlahoda	bzero(&ps->ps_saved, sizeof(ps->ps_saved));
5502973Sjlahoda	ps->ps_svcount = 0;
5512973Sjlahoda	ps->ps_fd    = fd;
5522973Sjlahoda	ps->ps_data  = NULL;
5532973Sjlahoda	ps->ps_buffer = NULL;
5542973Sjlahoda	ps->ps_len   = 0;
5552973Sjlahoda
5562973Sjlahoda	/* allocate space for a work area */
5572973Sjlahoda	if (ps->ps_fd != PMCLOG_FD_NONE) {
5582973Sjlahoda		if ((ps->ps_buffer = malloc(PMCLOG_BUFFER_SIZE)) == NULL) {
5592973Sjlahoda			free(ps);
5602973Sjlahoda			return NULL;
5612973Sjlahoda		}
5622973Sjlahoda	}
5632973Sjlahoda
5642973Sjlahoda	return ps;
5652973Sjlahoda}
5662973Sjlahoda
5672973Sjlahoda
5682973Sjlahoda/*
5692973Sjlahoda * Free up parser state.
5702973Sjlahoda */
5712973Sjlahoda
5722973Sjlahodavoid
5732973Sjlahodapmclog_close(void *cookie)
5742973Sjlahoda{
5752973Sjlahoda	struct pmclog_parse_state *ps;
5762973Sjlahoda
5772973Sjlahoda	ps = (struct pmclog_parse_state *) cookie;
5782973Sjlahoda
5792973Sjlahoda	if (ps->ps_buffer)
5802973Sjlahoda		free(ps->ps_buffer);
5812973Sjlahoda
5822973Sjlahoda	free(ps);
5832973Sjlahoda}
5842973Sjlahoda