2 * Copyright (c) 2005-2007 Joseph Koshy
3 * Copyright (c) 2007 The FreeBSD Foundation
6 * Portions of this software were developed by A. Joseph Koshy under
7 * sponsorship from the FreeBSD Foundation and Google, Inc.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
34 #include <sys/param.h>
36 #include <sys/pmclog.h>
48 #include <machine/pmc_mdep.h>
50 #include "libpmcinternal.h"
52 #define PMCLOG_BUFFER_SIZE 4096
57 * The pmclog(3) API is oriented towards parsing an event stream in
58 * "realtime", i.e., from an data source that may or may not preserve
59 * record boundaries -- for example when the data source is elsewhere
60 * on a network. The API allows data to be fed into the parser zero
61 * or more bytes at a time.
63 * The state for a log file parser is maintained in a 'struct
64 * pmclog_parse_state'. Parser invocations are done by calling
65 * 'pmclog_read()'; this function will inform the caller when a
66 * complete event is parsed.
68 * The parser first assembles a complete log file event in an internal
69 * work area (see "ps_saved" below). Once a complete log file event
70 * is read, the parser then parses it and converts it to an event
71 * descriptor usable by the client. We could possibly avoid this two
72 * step process by directly parsing the input log to set fields in the
73 * event record. However the parser's state machine would get
74 * insanely complicated, and this code is unlikely to be used in
75 * performance critical paths.
78 enum pmclog_parser_state
{
79 PL_STATE_NEW_RECORD
, /* in-between records */
80 PL_STATE_EXPECTING_HEADER
, /* header being read */
81 PL_STATE_PARTIAL_RECORD
, /* header present but not the record */
82 PL_STATE_ERROR
/* parsing error encountered */
85 struct pmclog_parse_state
{
86 enum pmclog_parser_state ps_state
;
87 enum pmc_cputype ps_arch
; /* log file architecture */
88 uint32_t ps_version
; /* hwpmc version */
89 int ps_initialized
; /* whether initialized */
90 int ps_count
; /* count of records processed */
91 off_t ps_offset
; /* stream byte offset */
92 union pmclog_entry ps_saved
; /* saved partial log entry */
93 int ps_svcount
; /* #bytes saved */
94 int ps_fd
; /* active fd or -1 */
95 char *ps_buffer
; /* scratch buffer if fd != -1 */
96 char *ps_data
; /* current parse pointer */
97 size_t ps_len
; /* length of buffered data */
100 #define PMCLOG_HEADER_FROM_SAVED_STATE(PS) \
101 (* ((uint32_t *) &(PS)->ps_saved))
103 #define PMCLOG_INITIALIZE_READER(LE,A) LE = (uint32_t *) &(A)
104 #define PMCLOG_READ32(LE,V) do { \
107 #define PMCLOG_READ64(LE,V) do { \
109 _v = (uint64_t) *(LE)++; \
110 _v |= ((uint64_t) *(LE)++) << 32; \
114 #define PMCLOG_READSTRING(LE,DST,LEN) strlcpy((DST), (char *) (LE), (LEN))
117 * Assemble a log record from '*len' octets starting from address '*data'.
118 * Update 'data' and 'len' to reflect the number of bytes consumed.
120 * '*data' is potentially an unaligned address and '*len' octets may
121 * not be enough to complete a event record.
124 static enum pmclog_parser_state
125 pmclog_get_record(struct pmclog_parse_state
*ps
, char **data
, ssize_t
*len
)
127 int avail
, copylen
, recordsize
, used
;
129 const int HEADERSIZE
= sizeof(uint32_t);
132 if ((avail
= *len
) <= 0)
133 return (ps
->ps_state
= PL_STATE_ERROR
);
138 if (ps
->ps_state
== PL_STATE_NEW_RECORD
)
141 dst
= (char *) &ps
->ps_saved
+ ps
->ps_svcount
;
143 switch (ps
->ps_state
) {
144 case PL_STATE_NEW_RECORD
:
149 * Case A: avail < headersize
150 * -> 'expecting header'
152 * Case B: avail >= headersize
153 * B.1: avail < recordsize
154 * -> 'partial record'
155 * B.2: avail >= recordsize
159 copylen
= avail
< HEADERSIZE
? avail
: HEADERSIZE
;
160 bcopy(src
, dst
, copylen
);
161 ps
->ps_svcount
= used
= copylen
;
163 if (copylen
< HEADERSIZE
) {
164 ps
->ps_state
= PL_STATE_EXPECTING_HEADER
;
171 h
= PMCLOG_HEADER_FROM_SAVED_STATE(ps
);
172 recordsize
= PMCLOG_HEADER_TO_LENGTH(h
);
177 if (recordsize
<= avail
) { /* full record available */
178 bcopy(src
, dst
, recordsize
- copylen
);
179 ps
->ps_svcount
= used
= recordsize
;
183 /* header + a partial record is available */
184 bcopy(src
, dst
, avail
- copylen
);
185 ps
->ps_svcount
= used
= avail
;
186 ps
->ps_state
= PL_STATE_PARTIAL_RECORD
;
190 case PL_STATE_EXPECTING_HEADER
:
195 * Case C: avail+saved < headersize
196 * -> 'expecting header'
198 * Case D: avail+saved >= headersize
199 * D.1: avail+saved < recordsize
200 * -> 'partial record'
201 * D.2: avail+saved >= recordsize
203 * (see PARTIAL_RECORD handling below)
206 if (avail
+ ps
->ps_svcount
< HEADERSIZE
) {
207 bcopy(src
, dst
, avail
);
208 ps
->ps_svcount
+= avail
;
213 used
= copylen
= HEADERSIZE
- ps
->ps_svcount
;
214 bcopy(src
, dst
, copylen
);
218 ps
->ps_svcount
+= copylen
;
222 case PL_STATE_PARTIAL_RECORD
:
227 * Case E: avail+saved < recordsize
228 * -> 'partial record'
230 * Case F: avail+saved >= recordsize
234 h
= PMCLOG_HEADER_FROM_SAVED_STATE(ps
);
235 recordsize
= PMCLOG_HEADER_TO_LENGTH(h
);
240 if (avail
+ ps
->ps_svcount
< recordsize
) {
242 ps
->ps_state
= PL_STATE_PARTIAL_RECORD
;
244 copylen
= recordsize
- ps
->ps_svcount
;
245 ps
->ps_state
= PL_STATE_NEW_RECORD
;
248 bcopy(src
, dst
, copylen
);
249 ps
->ps_svcount
+= copylen
;
263 ps
->ps_state
= PL_STATE_ERROR
;
268 * Get an event from the stream pointed to by '*data'. '*len'
269 * indicates the number of bytes available to parse. Arguments
270 * '*data' and '*len' are updated to indicate the number of bytes
275 pmclog_get_event(void *cookie
, char **data
, ssize_t
*len
,
276 struct pmclog_ev
*ev
)
279 uint32_t h
, *le
, npc
;
280 enum pmclog_parser_state e
;
281 struct pmclog_parse_state
*ps
;
283 ps
= (struct pmclog_parse_state
*) cookie
;
285 assert(ps
->ps_state
!= PL_STATE_ERROR
);
287 if ((e
= pmclog_get_record(ps
,data
,len
)) == PL_STATE_ERROR
) {
288 ev
->pl_state
= PMCLOG_ERROR
;
292 if (e
!= PL_STATE_NEW_RECORD
) {
293 ev
->pl_state
= PMCLOG_REQUIRE_DATA
;
297 PMCLOG_INITIALIZE_READER(le
, ps
->ps_saved
);
301 if (!PMCLOG_HEADER_CHECK_MAGIC(h
)) {
302 ps
->ps_state
= PL_STATE_ERROR
;
303 ev
->pl_state
= PMCLOG_ERROR
;
307 /* copy out the time stamp */
308 PMCLOG_READ32(le
,ev
->pl_ts
.tv_sec
);
309 PMCLOG_READ32(le
,ev
->pl_ts
.tv_nsec
);
311 evlen
= PMCLOG_HEADER_TO_LENGTH(h
);
313 #define PMCLOG_GET_PATHLEN(P,E,TYPE) do { \
314 (P) = (E) - offsetof(struct TYPE, pl_pathname); \
315 if ((P) > PATH_MAX || (P) < 0) \
319 #define PMCLOG_GET_CALLCHAIN_SIZE(SZ,E) do { \
320 (SZ) = ((E) - offsetof(struct pmclog_callchain, pl_pc)) \
321 / sizeof(uintfptr_t); \
324 switch (ev
->pl_type
= PMCLOG_HEADER_TO_TYPE(h
)) {
325 case PMCLOG_TYPE_CALLCHAIN
:
326 PMCLOG_READ32(le
,ev
->pl_u
.pl_cc
.pl_pid
);
327 PMCLOG_READ32(le
,ev
->pl_u
.pl_cc
.pl_pmcid
);
328 PMCLOG_READ32(le
,ev
->pl_u
.pl_cc
.pl_cpuflags
);
329 PMCLOG_GET_CALLCHAIN_SIZE(ev
->pl_u
.pl_cc
.pl_npc
,evlen
);
330 for (npc
= 0; npc
< ev
->pl_u
.pl_cc
.pl_npc
; npc
++)
331 PMCLOG_READADDR(le
,ev
->pl_u
.pl_cc
.pl_pc
[npc
]);
332 for (;npc
< PMC_CALLCHAIN_DEPTH_MAX
; npc
++)
333 ev
->pl_u
.pl_cc
.pl_pc
[npc
] = (uintfptr_t
) 0;
335 case PMCLOG_TYPE_CLOSELOG
:
336 case PMCLOG_TYPE_DROPNOTIFY
:
339 case PMCLOG_TYPE_INITIALIZE
:
340 PMCLOG_READ32(le
,ev
->pl_u
.pl_i
.pl_version
);
341 PMCLOG_READ32(le
,ev
->pl_u
.pl_i
.pl_arch
);
342 ps
->ps_version
= ev
->pl_u
.pl_i
.pl_version
;
343 ps
->ps_arch
= ev
->pl_u
.pl_i
.pl_arch
;
344 ps
->ps_initialized
= 1;
346 case PMCLOG_TYPE_MAP_IN
:
347 PMCLOG_GET_PATHLEN(pathlen
,evlen
,pmclog_map_in
);
348 PMCLOG_READ32(le
,ev
->pl_u
.pl_mi
.pl_pid
);
349 PMCLOG_READADDR(le
,ev
->pl_u
.pl_mi
.pl_start
);
350 PMCLOG_READSTRING(le
, ev
->pl_u
.pl_mi
.pl_pathname
, pathlen
);
352 case PMCLOG_TYPE_MAP_OUT
:
353 PMCLOG_READ32(le
,ev
->pl_u
.pl_mo
.pl_pid
);
354 PMCLOG_READADDR(le
,ev
->pl_u
.pl_mo
.pl_start
);
355 PMCLOG_READADDR(le
,ev
->pl_u
.pl_mo
.pl_end
);
357 case PMCLOG_TYPE_PCSAMPLE
:
358 PMCLOG_READ32(le
,ev
->pl_u
.pl_s
.pl_pid
);
359 PMCLOG_READADDR(le
,ev
->pl_u
.pl_s
.pl_pc
);
360 PMCLOG_READ32(le
,ev
->pl_u
.pl_s
.pl_pmcid
);
361 PMCLOG_READ32(le
,ev
->pl_u
.pl_s
.pl_usermode
);
363 case PMCLOG_TYPE_PMCALLOCATE
:
364 PMCLOG_READ32(le
,ev
->pl_u
.pl_a
.pl_pmcid
);
365 PMCLOG_READ32(le
,ev
->pl_u
.pl_a
.pl_event
);
366 PMCLOG_READ32(le
,ev
->pl_u
.pl_a
.pl_flags
);
367 if ((ev
->pl_u
.pl_a
.pl_evname
=
368 _pmc_name_of_event(ev
->pl_u
.pl_a
.pl_event
, ps
->ps_arch
))
372 case PMCLOG_TYPE_PMCALLOCATEDYN
:
373 PMCLOG_READ32(le
,ev
->pl_u
.pl_ad
.pl_pmcid
);
374 PMCLOG_READ32(le
,ev
->pl_u
.pl_ad
.pl_event
);
375 PMCLOG_READ32(le
,ev
->pl_u
.pl_ad
.pl_flags
);
376 PMCLOG_READSTRING(le
,ev
->pl_u
.pl_ad
.pl_evname
,PMC_NAME_MAX
);
378 case PMCLOG_TYPE_PMCATTACH
:
379 PMCLOG_GET_PATHLEN(pathlen
,evlen
,pmclog_pmcattach
);
380 PMCLOG_READ32(le
,ev
->pl_u
.pl_t
.pl_pmcid
);
381 PMCLOG_READ32(le
,ev
->pl_u
.pl_t
.pl_pid
);
382 PMCLOG_READSTRING(le
,ev
->pl_u
.pl_t
.pl_pathname
,pathlen
);
384 case PMCLOG_TYPE_PMCDETACH
:
385 PMCLOG_READ32(le
,ev
->pl_u
.pl_d
.pl_pmcid
);
386 PMCLOG_READ32(le
,ev
->pl_u
.pl_d
.pl_pid
);
388 case PMCLOG_TYPE_PROCCSW
:
389 PMCLOG_READ32(le
,ev
->pl_u
.pl_c
.pl_pmcid
);
390 PMCLOG_READ64(le
,ev
->pl_u
.pl_c
.pl_value
);
391 PMCLOG_READ32(le
,ev
->pl_u
.pl_c
.pl_pid
);
393 case PMCLOG_TYPE_PROCEXEC
:
394 PMCLOG_GET_PATHLEN(pathlen
,evlen
,pmclog_procexec
);
395 PMCLOG_READ32(le
,ev
->pl_u
.pl_x
.pl_pid
);
396 PMCLOG_READADDR(le
,ev
->pl_u
.pl_x
.pl_entryaddr
);
397 PMCLOG_READ32(le
,ev
->pl_u
.pl_x
.pl_pmcid
);
398 PMCLOG_READSTRING(le
,ev
->pl_u
.pl_x
.pl_pathname
,pathlen
);
400 case PMCLOG_TYPE_PROCEXIT
:
401 PMCLOG_READ32(le
,ev
->pl_u
.pl_e
.pl_pmcid
);
402 PMCLOG_READ64(le
,ev
->pl_u
.pl_e
.pl_value
);
403 PMCLOG_READ32(le
,ev
->pl_u
.pl_e
.pl_pid
);
405 case PMCLOG_TYPE_PROCFORK
:
406 PMCLOG_READ32(le
,ev
->pl_u
.pl_f
.pl_oldpid
);
407 PMCLOG_READ32(le
,ev
->pl_u
.pl_f
.pl_newpid
);
409 case PMCLOG_TYPE_SYSEXIT
:
410 PMCLOG_READ32(le
,ev
->pl_u
.pl_se
.pl_pid
);
412 case PMCLOG_TYPE_USERDATA
:
413 PMCLOG_READ32(le
,ev
->pl_u
.pl_u
.pl_userdata
);
415 default: /* unknown record type */
416 ps
->ps_state
= PL_STATE_ERROR
;
417 ev
->pl_state
= PMCLOG_ERROR
;
421 ev
->pl_offset
= (ps
->ps_offset
+= evlen
);
422 ev
->pl_count
= (ps
->ps_count
+= 1);
423 ev
->pl_state
= PMCLOG_OK
;
427 ev
->pl_state
= PMCLOG_ERROR
;
428 ps
->ps_state
= PL_STATE_ERROR
;
433 * Extract and return the next event from the byte stream.
435 * Returns 0 and sets the event's state to PMCLOG_OK in case an event
436 * was successfully parsed. Otherwise this function returns -1 and
437 * sets the event's state to one of PMCLOG_REQUIRE_DATA (if more data
438 * is needed) or PMCLOG_EOF (if an EOF was seen) or PMCLOG_ERROR if
439 * a parse error was encountered.
443 pmclog_read(void *cookie
, struct pmclog_ev
*ev
)
447 struct pmclog_parse_state
*ps
;
449 ps
= (struct pmclog_parse_state
*) cookie
;
451 if (ps
->ps_state
== PL_STATE_ERROR
) {
452 ev
->pl_state
= PMCLOG_ERROR
;
457 * If there isn't enough data left for a new event try and get
460 if (ps
->ps_len
== 0) {
461 ev
->pl_state
= PMCLOG_REQUIRE_DATA
;
464 * If we have a valid file descriptor to read from, attempt
465 * to read from that. This read may return with an error,
466 * (which may be EAGAIN or other recoverable error), or
469 if (ps
->ps_fd
!= PMCLOG_FD_NONE
) {
471 nread
= read(ps
->ps_fd
, ps
->ps_buffer
,
476 ev
->pl_state
= PMCLOG_EOF
;
477 else if (errno
!= EAGAIN
) /* not restartable */
478 ev
->pl_state
= PMCLOG_ERROR
;
483 ps
->ps_data
= ps
->ps_buffer
;
488 assert(ps
->ps_len
> 0);
491 /* Retrieve one event from the byte stream. */
492 retval
= pmclog_get_event(ps
, &ps
->ps_data
, &ps
->ps_len
, ev
);
495 * If we need more data and we have a configured fd, try read
498 if (retval
< 0 && ev
->pl_state
== PMCLOG_REQUIRE_DATA
&&
500 assert(ps
->ps_len
== 0);
508 * Feed data to a memory based parser.
510 * The memory area pointed to by 'data' needs to be valid till the
511 * next error return from pmclog_next_event().
515 pmclog_feed(void *cookie
, char *data
, int len
)
517 struct pmclog_parse_state
*ps
;
519 ps
= (struct pmclog_parse_state
*) cookie
;
521 if (len
< 0 || /* invalid length */
522 ps
->ps_buffer
|| /* called for a file parser */
523 ps
->ps_len
!= 0) /* unnecessary call */
533 * Allocate and initialize parser state.
539 struct pmclog_parse_state
*ps
;
541 if ((ps
= (struct pmclog_parse_state
*) malloc(sizeof(*ps
))) == NULL
)
544 ps
->ps_state
= PL_STATE_NEW_RECORD
;
546 ps
->ps_initialized
= 0;
548 ps
->ps_offset
= (off_t
) 0;
549 bzero(&ps
->ps_saved
, sizeof(ps
->ps_saved
));
553 ps
->ps_buffer
= NULL
;
556 /* allocate space for a work area */
557 if (ps
->ps_fd
!= PMCLOG_FD_NONE
) {
558 if ((ps
->ps_buffer
= malloc(PMCLOG_BUFFER_SIZE
)) == NULL
) {
569 * Free up parser state.
573 pmclog_close(void *cookie
)
575 struct pmclog_parse_state
*ps
;
577 ps
= (struct pmclog_parse_state
*) cookie
;