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