Files
sysprof/src/libsysprof-capture/sysprof-capture-reader.c
Christian Hergert 14139232d5 capture: rename PidRoot to Overlay and add src/dst
Really what we want to deal with here is tracking an overlay that we may
need to be able to decode after the fact (in case processes exit or we
need to do post-processing symbol resolution).

For the podman case, that is $some_path mapped to root (/), generally
speaking. For flatpak though, that would have two mappings, one for
/app and another for /usr (possibly more).
2021-02-25 13:43:09 -08:00

1638 lines
41 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* sysprof-capture-reader.c
*
* Copyright 2016-2019 Christian Hergert <chergert@redhat.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* Subject to the terms and conditions of this license, each copyright holder
* and contributor hereby grants to those receiving rights under this license
* a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
* irrevocable (except for failure to satisfy the conditions of this license)
* patent license to make, have made, use, offer to sell, sell, import, and
* otherwise transfer this software, where such license applies only to those
* patent claims, already acquired or hereafter acquired, licensable by such
* copyright holder or contributor that are necessarily infringed by:
*
* (a) their Contribution(s) (the licensed copyrights of copyright holders
* and non-copyrightable additions of contributors, in source or binary
* form) alone; or
*
* (b) combination of their Contribution(s) with the work of authorship to
* which such Contribution(s) was added by such copyright holder or
* contributor, if, at the time the Contribution is added, such addition
* causes such combination to be necessarily infringed. The patent license
* shall not apply to any other combinations which include the
* Contribution.
*
* Except as expressly stated above, no rights or licenses from any copyright
* holder or contributor is granted under this license, whether expressly, by
* implication, estoppel or otherwise.
*
* DISCLAIMER
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* SPDX-License-Identifier: BSD-2-Clause-Patent
*/
#include "config.h"
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "sysprof-capture-reader.h"
#include "sysprof-capture-util-private.h"
#include "sysprof-capture-writer.h"
#include "sysprof-macros-internal.h"
struct _SysprofCaptureReader
{
volatile int ref_count;
char *filename;
uint8_t *buf;
size_t bufsz;
size_t len;
size_t pos;
size_t fd_off;
int fd;
int endian;
SysprofCaptureFileHeader header;
int64_t end_time;
SysprofCaptureStat st_buf;
unsigned int st_buf_set : 1;
char **list_files;
size_t n_list_files;
};
/* Sets @errno on failure. Sets @errno to EBADMSG if the file magic doesnt
* match, and otherwise can return any error as for read(). */
static bool
sysprof_capture_reader_read_file_header (SysprofCaptureReader *self,
SysprofCaptureFileHeader *header)
{
assert (self != NULL);
assert (header != NULL);
if (sizeof *header != _sysprof_pread (self->fd, header, sizeof *header, 0L))
{
/* errno is propagated */
return false;
}
if (header->magic != SYSPROF_CAPTURE_MAGIC)
{
errno = EBADMSG;
return false;
}
header->capture_time[sizeof header->capture_time - 1] = '\0';
return true;
}
static void
sysprof_capture_reader_finalize (SysprofCaptureReader *self)
{
if (self != NULL)
{
for (size_t i = 0; i < self->n_list_files; i++)
free (self->list_files[i]);
free (self->list_files);
close (self->fd);
free (self->buf);
free (self->filename);
free (self);
}
}
const char *
sysprof_capture_reader_get_time (SysprofCaptureReader *self)
{
assert (self != NULL);
return self->header.capture_time;
}
const char *
sysprof_capture_reader_get_filename (SysprofCaptureReader *self)
{
assert (self != NULL);
return self->filename;
}
static void
sysprof_capture_reader_discover_end_time (SysprofCaptureReader *self)
{
SysprofCaptureFrame frame;
assert (self != NULL);
while (sysprof_capture_reader_peek_frame (self, &frame))
{
int64_t end_time = frame.time;
switch (frame.type)
{
case SYSPROF_CAPTURE_FRAME_MARK: {
const SysprofCaptureMark *mark = NULL;
if ((mark = sysprof_capture_reader_read_mark (self)))
end_time = frame.time + ((mark->duration > 0) ? mark->duration : 0);
}
break;
case SYSPROF_CAPTURE_FRAME_ALLOCATION:
case SYSPROF_CAPTURE_FRAME_CTRSET:
case SYSPROF_CAPTURE_FRAME_EXIT:
case SYSPROF_CAPTURE_FRAME_FORK:
case SYSPROF_CAPTURE_FRAME_LOG:
case SYSPROF_CAPTURE_FRAME_PROCESS:
case SYSPROF_CAPTURE_FRAME_SAMPLE:
case SYSPROF_CAPTURE_FRAME_TIMESTAMP:
if (end_time > self->end_time)
self->end_time = end_time;
break;
case SYSPROF_CAPTURE_FRAME_MAP:
case SYSPROF_CAPTURE_FRAME_JITMAP:
case SYSPROF_CAPTURE_FRAME_CTRDEF:
case SYSPROF_CAPTURE_FRAME_METADATA:
case SYSPROF_CAPTURE_FRAME_FILE_CHUNK:
default:
break;
}
if (!sysprof_capture_reader_skip (self))
break;
}
sysprof_capture_reader_reset (self);
}
/**
* sysprof_capture_reader_new_from_fd:
* @fd: an fd to take ownership from
*
* Creates a new reader using the file-descriptor.
*
* This is useful if you don't necessarily have access to the filename itself.
*
* If this function fails, `errno` is set.
*
* Returns: (transfer full): an #SysprofCaptureReader or %NULL upon failure.
*/
SysprofCaptureReader *
sysprof_capture_reader_new_from_fd (int fd)
{
SysprofCaptureReader *self;
assert (fd > -1);
self = sysprof_malloc0 (sizeof (SysprofCaptureReader));
if (self == NULL)
{
errno = ENOMEM;
return NULL;
}
self->ref_count = 1;
self->bufsz = USHRT_MAX * 2;
self->buf = sysprof_malloc0 (self->bufsz);
if (self->buf == NULL)
{
free (self);
errno = ENOMEM;
return NULL;
}
self->len = 0;
self->pos = 0;
self->fd = fd;
self->fd_off = sizeof (SysprofCaptureFileHeader);
if (!sysprof_capture_reader_read_file_header (self, &self->header))
{
int errsv = errno;
sysprof_capture_reader_finalize (self);
errno = errsv;
return NULL;
}
if (self->header.little_endian)
self->endian = __LITTLE_ENDIAN;
else
self->endian = __BIG_ENDIAN;
/* If we detect a capture file that did not get an end time, or an erroneous
* end time, then we need to take a performance hit here and scan the file
* and discover the end time with frame timings.
*/
if (self->header.end_time < self->header.time)
sysprof_capture_reader_discover_end_time (self);
return self;
}
SysprofCaptureReader *
sysprof_capture_reader_new (const char *filename)
{
SysprofCaptureReader *self;
int fd;
assert (filename != NULL);
if (-1 == (fd = open (filename, O_RDONLY, 0)))
{
/* errno is propagated */
return NULL;
}
if (NULL == (self = sysprof_capture_reader_new_from_fd (fd)))
{
int errsv = errno;
close (fd);
errno = errsv;
return NULL;
}
self->filename = sysprof_strdup (filename);
return self;
}
static inline void
sysprof_capture_reader_bswap_frame (SysprofCaptureReader *self,
SysprofCaptureFrame *frame)
{
assert (self != NULL);
assert (frame!= NULL);
if (SYSPROF_UNLIKELY (self->endian != __BYTE_ORDER))
{
frame->len = bswap_16 (frame->len);
frame->cpu = bswap_16 (frame->cpu);
frame->pid = bswap_32 (frame->pid);
frame->time = bswap_64 (frame->time);
}
}
static inline void
sysprof_capture_reader_bswap_file_chunk (SysprofCaptureReader *self,
SysprofCaptureFileChunk *file_chunk)
{
assert (self != NULL);
assert (file_chunk != NULL);
if (SYSPROF_UNLIKELY (self->endian != __BYTE_ORDER))
file_chunk->len = bswap_16 (file_chunk->len);
}
static inline void
sysprof_capture_reader_bswap_log (SysprofCaptureReader *self,
SysprofCaptureLog *log)
{
assert (self != NULL);
assert (log != NULL);
if (SYSPROF_UNLIKELY (self->endian != __BYTE_ORDER))
log->severity = bswap_16 (log->severity);
}
static inline void
sysprof_capture_reader_bswap_map (SysprofCaptureReader *self,
SysprofCaptureMap *map)
{
assert (self != NULL);
assert (map != NULL);
if (SYSPROF_UNLIKELY (self->endian != __BYTE_ORDER))
{
map->start = bswap_64 (map->start);
map->end = bswap_64 (map->end);
map->offset = bswap_64 (map->offset);
map->inode = bswap_64 (map->inode);
}
}
static inline void
sysprof_capture_reader_bswap_mark (SysprofCaptureReader *self,
SysprofCaptureMark *mark)
{
assert (self != NULL);
assert (mark != NULL);
if (SYSPROF_UNLIKELY (self->endian != __BYTE_ORDER))
mark->duration = bswap_64 (mark->duration);
}
static inline void
sysprof_capture_reader_bswap_overlay (SysprofCaptureReader *self,
SysprofCaptureOverlay *pr)
{
assert (self != NULL);
assert (pr != NULL);
if (SYSPROF_UNLIKELY (self->endian != __BYTE_ORDER))
{
pr->layer = bswap_32 (pr->layer);
pr->src_len = bswap_32 (pr->src_len);
pr->dst_len = bswap_32 (pr->dst_len);
}
}
static inline void
sysprof_capture_reader_bswap_jitmap (SysprofCaptureReader *self,
SysprofCaptureJitmap *jitmap)
{
assert (self != NULL);
assert (jitmap != NULL);
if (SYSPROF_UNLIKELY (self->endian != __BYTE_ORDER))
jitmap->n_jitmaps = bswap_64 (jitmap->n_jitmaps);
}
static bool
sysprof_capture_reader_ensure_space_for (SysprofCaptureReader *self,
size_t len)
{
assert (self != NULL);
assert (self->pos <= self->len);
assert (len > 0);
/* Ensure alignment of length to read */
len = (len + SYSPROF_CAPTURE_ALIGN - 1) & ~(SYSPROF_CAPTURE_ALIGN - 1);
if ((self->len - self->pos) < len)
{
ssize_t r;
if (self->len > self->pos)
memmove (self->buf, &self->buf[self->pos], self->len - self->pos);
self->len -= self->pos;
self->pos = 0;
while (self->len < len)
{
assert ((self->pos + self->len) < self->bufsz);
assert (self->len < self->bufsz);
/* Read into our buffer after our current read position */
r = _sysprof_pread (self->fd,
&self->buf[self->len],
self->bufsz - self->len,
self->fd_off);
if (r <= 0)
break;
self->fd_off += r;
self->len += r;
}
}
return (self->len - self->pos) >= len;
}
bool
sysprof_capture_reader_skip (SysprofCaptureReader *self)
{
SysprofCaptureFrame *frame;
assert (self != NULL);
assert ((self->pos % SYSPROF_CAPTURE_ALIGN) == 0);
if (!sysprof_capture_reader_ensure_space_for (self, sizeof (SysprofCaptureFrame)))
return false;
frame = (SysprofCaptureFrame *)(void *)&self->buf[self->pos];
sysprof_capture_reader_bswap_frame (self, frame);
if (frame->len < sizeof (SysprofCaptureFrame))
return false;
if (!sysprof_capture_reader_ensure_space_for (self, frame->len))
return false;
frame = (SysprofCaptureFrame *)(void *)&self->buf[self->pos];
self->pos += frame->len;
if ((self->pos % SYSPROF_CAPTURE_ALIGN) != 0)
return false;
return true;
}
bool
sysprof_capture_reader_peek_frame (SysprofCaptureReader *self,
SysprofCaptureFrame *frame)
{
SysprofCaptureFrame *real_frame;
assert (self != NULL);
assert ((self->pos % SYSPROF_CAPTURE_ALIGN) == 0);
assert (self->pos <= self->len);
assert (self->pos <= self->bufsz);
if (!sysprof_capture_reader_ensure_space_for (self, sizeof *real_frame))
return false;
assert ((self->pos % SYSPROF_CAPTURE_ALIGN) == 0);
real_frame = (SysprofCaptureFrame *)(void *)&self->buf[self->pos];
*frame = *real_frame;
sysprof_capture_reader_bswap_frame (self, frame);
/* In case the capture did not update the end_time during normal usage,
* we can update our cached known end_time based on the greatest frame
* we come across.
*/
if (frame->time > self->end_time)
self->end_time = frame->time;
return frame->type > 0 && frame->type < SYSPROF_CAPTURE_FRAME_LAST;
}
bool
sysprof_capture_reader_peek_type (SysprofCaptureReader *self,
SysprofCaptureFrameType *type)
{
SysprofCaptureFrame frame;
assert (self != NULL);
assert (type != NULL);
if (!sysprof_capture_reader_peek_frame (self, &frame))
return false;
*type = frame.type;
return frame.type > 0 && frame.type < SYSPROF_CAPTURE_FRAME_LAST;
}
static const SysprofCaptureFrame *
sysprof_capture_reader_read_basic (SysprofCaptureReader *self,
SysprofCaptureFrameType type,
size_t extra)
{
SysprofCaptureFrame *frame;
size_t len = sizeof *frame + extra;
assert (self != NULL);
assert ((self->pos % SYSPROF_CAPTURE_ALIGN) == 0);
assert (self->pos <= self->bufsz);
if (!sysprof_capture_reader_ensure_space_for (self, len))
return NULL;
frame = (SysprofCaptureFrame *)(void *)&self->buf[self->pos];
sysprof_capture_reader_bswap_frame (self, frame);
if (frame->len < len)
return NULL;
if (frame->type != type)
return NULL;
if (frame->len > (self->len - self->pos))
return NULL;
self->pos += frame->len;
return frame;
}
const SysprofCaptureTimestamp *
sysprof_capture_reader_read_timestamp (SysprofCaptureReader *self)
{
return (SysprofCaptureTimestamp *)
sysprof_capture_reader_read_basic (self, SYSPROF_CAPTURE_FRAME_TIMESTAMP, 0);
}
const SysprofCaptureExit *
sysprof_capture_reader_read_exit (SysprofCaptureReader *self)
{
return (SysprofCaptureExit *)
sysprof_capture_reader_read_basic (self, SYSPROF_CAPTURE_FRAME_EXIT, 0);
}
const SysprofCaptureFork *
sysprof_capture_reader_read_fork (SysprofCaptureReader *self)
{
SysprofCaptureFork *fk;
assert (self != NULL);
fk = (SysprofCaptureFork *)
sysprof_capture_reader_read_basic (self, SYSPROF_CAPTURE_FRAME_FORK, sizeof (uint32_t));
if (fk != NULL)
{
if (SYSPROF_UNLIKELY (self->endian != __BYTE_ORDER))
fk->child_pid = bswap_32 (fk->child_pid);
}
return fk;
}
const SysprofCaptureMap *
sysprof_capture_reader_read_map (SysprofCaptureReader *self)
{
SysprofCaptureMap *map;
assert (self != NULL);
assert ((self->pos % SYSPROF_CAPTURE_ALIGN) == 0);
assert (self->pos <= self->bufsz);
if (!sysprof_capture_reader_ensure_space_for (self, sizeof *map))
return NULL;
map = (SysprofCaptureMap *)(void *)&self->buf[self->pos];
sysprof_capture_reader_bswap_frame (self, &map->frame);
if (map->frame.type != SYSPROF_CAPTURE_FRAME_MAP)
return NULL;
if (map->frame.len < (sizeof *map + 1))
return NULL;
if (!sysprof_capture_reader_ensure_space_for (self, map->frame.len))
return NULL;
map = (SysprofCaptureMap *)(void *)&self->buf[self->pos];
if (self->buf[self->pos + map->frame.len - 1] != '\0')
return NULL;
sysprof_capture_reader_bswap_map (self, map);
self->pos += map->frame.len;
if ((self->pos % SYSPROF_CAPTURE_ALIGN) != 0)
return NULL;
return map;
}
const SysprofCaptureLog *
sysprof_capture_reader_read_log (SysprofCaptureReader *self)
{
SysprofCaptureLog *log;
assert (self != NULL);
assert ((self->pos % SYSPROF_CAPTURE_ALIGN) == 0);
assert (self->pos <= self->bufsz);
if (!sysprof_capture_reader_ensure_space_for (self, sizeof *log))
return NULL;
log = (SysprofCaptureLog *)(void *)&self->buf[self->pos];
sysprof_capture_reader_bswap_frame (self, &log->frame);
if (log->frame.type != SYSPROF_CAPTURE_FRAME_LOG)
return NULL;
if (log->frame.len < (sizeof *log + 1))
return NULL;
if (!sysprof_capture_reader_ensure_space_for (self, log->frame.len))
return NULL;
log = (SysprofCaptureLog *)(void *)&self->buf[self->pos];
sysprof_capture_reader_bswap_log (self, log);
self->pos += log->frame.len;
if ((self->pos % SYSPROF_CAPTURE_ALIGN) != 0)
return NULL;
/* Ensure trailing \0 in domain and message */
log->domain[sizeof log->domain - 1] = 0;
if (log->frame.len > sizeof *log)
((char *)log)[log->frame.len - 1] = 0;
return log;
}
const SysprofCaptureMark *
sysprof_capture_reader_read_mark (SysprofCaptureReader *self)
{
SysprofCaptureMark *mark;
assert (self != NULL);
assert ((self->pos % SYSPROF_CAPTURE_ALIGN) == 0);
assert (self->pos <= self->bufsz);
if (!sysprof_capture_reader_ensure_space_for (self, sizeof *mark))
return NULL;
mark = (SysprofCaptureMark *)(void *)&self->buf[self->pos];
sysprof_capture_reader_bswap_frame (self, &mark->frame);
if (mark->frame.type != SYSPROF_CAPTURE_FRAME_MARK)
return NULL;
if (mark->frame.len < (sizeof *mark + 1))
return NULL;
if (!sysprof_capture_reader_ensure_space_for (self, mark->frame.len))
return NULL;
mark = (SysprofCaptureMark *)(void *)&self->buf[self->pos];
sysprof_capture_reader_bswap_mark (self, mark);
self->pos += mark->frame.len;
if ((self->pos % SYSPROF_CAPTURE_ALIGN) != 0)
return NULL;
/* Ensure trailing \0 in name and message */
mark->name[sizeof mark->name - 1] = 0;
if (mark->frame.len > sizeof *mark)
((char *)mark)[mark->frame.len - 1] = 0;
/* Maybe update end-time */
if SYSPROF_UNLIKELY ((mark->frame.time + mark->duration) > self->end_time)
self->end_time = mark->frame.time + mark->duration;
return mark;
}
const SysprofCaptureOverlay *
sysprof_capture_reader_read_overlay (SysprofCaptureReader *self)
{
SysprofCaptureOverlay *pr;
assert (self != NULL);
assert ((self->pos % SYSPROF_CAPTURE_ALIGN) == 0);
assert (self->pos <= self->bufsz);
if (!sysprof_capture_reader_ensure_space_for (self, sizeof *pr + 1))
return NULL;
pr = (SysprofCaptureOverlay *)(void *)&self->buf[self->pos];
sysprof_capture_reader_bswap_frame (self, &pr->frame);
if (pr->frame.type != SYSPROF_CAPTURE_FRAME_OVERLAY)
return NULL;
if (pr->frame.len < (sizeof *pr + 2))
return NULL;
if (!sysprof_capture_reader_ensure_space_for (self, pr->frame.len))
return NULL;
pr = (SysprofCaptureOverlay *)(void *)&self->buf[self->pos];
sysprof_capture_reader_bswap_overlay (self, pr);
/* Make sure there is enough space for paths and trailing \0 */
if (((size_t)pr->src_len + (size_t)pr->dst_len) > (pr->frame.len - sizeof *pr - 2))
return NULL;
/* Enforce trailing \0 */
pr->data[pr->src_len] = 0;
pr->data[pr->src_len+1+pr->dst_len] = 0;
self->pos += pr->frame.len;
if ((self->pos % SYSPROF_CAPTURE_ALIGN) != 0)
return NULL;
/* Ensure trailing \0 in name and message */
((char *)pr)[pr->frame.len-1] = 0;
return pr;
}
const SysprofCaptureMetadata *
sysprof_capture_reader_read_metadata (SysprofCaptureReader *self)
{
SysprofCaptureMetadata *metadata;
assert (self != NULL);
assert ((self->pos % SYSPROF_CAPTURE_ALIGN) == 0);
assert (self->pos <= self->bufsz);
if (!sysprof_capture_reader_ensure_space_for (self, sizeof *metadata))
return NULL;
metadata = (SysprofCaptureMetadata *)(void *)&self->buf[self->pos];
sysprof_capture_reader_bswap_frame (self, &metadata->frame);
if (metadata->frame.type != SYSPROF_CAPTURE_FRAME_METADATA)
return NULL;
if (metadata->frame.len < (sizeof *metadata + 1))
return NULL;
if (!sysprof_capture_reader_ensure_space_for (self, metadata->frame.len))
return NULL;
metadata = (SysprofCaptureMetadata *)(void *)&self->buf[self->pos];
self->pos += metadata->frame.len;
if ((self->pos % SYSPROF_CAPTURE_ALIGN) != 0)
return NULL;
/* Ensure trailing \0 in .id and .metadata */
metadata->id[sizeof metadata->id - 1] = 0;
if (metadata->frame.len > sizeof *metadata)
((char *)metadata)[metadata->frame.len - 1] = 0;
return metadata;
}
const SysprofCaptureProcess *
sysprof_capture_reader_read_process (SysprofCaptureReader *self)
{
SysprofCaptureProcess *process;
assert (self != NULL);
assert ((self->pos % SYSPROF_CAPTURE_ALIGN) == 0);
assert (self->pos <= self->bufsz);
if (!sysprof_capture_reader_ensure_space_for (self, sizeof *process))
return NULL;
process = (SysprofCaptureProcess *)(void *)&self->buf[self->pos];
sysprof_capture_reader_bswap_frame (self, &process->frame);
if (process->frame.type != SYSPROF_CAPTURE_FRAME_PROCESS)
return NULL;
if (process->frame.len < (sizeof *process + 1))
return NULL;
if (!sysprof_capture_reader_ensure_space_for (self, process->frame.len))
return NULL;
process = (SysprofCaptureProcess *)(void *)&self->buf[self->pos];
if (self->buf[self->pos + process->frame.len - 1] != '\0')
return NULL;
self->pos += process->frame.len;
if ((self->pos % SYSPROF_CAPTURE_ALIGN) != 0)
return NULL;
return process;
}
const SysprofCaptureJitmap *
sysprof_capture_reader_read_jitmap (SysprofCaptureReader *self)
{
SysprofCaptureJitmap *jitmap;
uint8_t *buf;
uint8_t *endptr;
unsigned int i;
assert (self != NULL);
assert ((self->pos % SYSPROF_CAPTURE_ALIGN) == 0);
assert (self->pos <= self->bufsz);
if (!sysprof_capture_reader_ensure_space_for (self, sizeof *jitmap))
return NULL;
jitmap = (SysprofCaptureJitmap *)(void *)&self->buf[self->pos];
sysprof_capture_reader_bswap_frame (self, &jitmap->frame);
if (jitmap->frame.type != SYSPROF_CAPTURE_FRAME_JITMAP)
return NULL;
if (jitmap->frame.len < sizeof *jitmap)
return NULL;
if (!sysprof_capture_reader_ensure_space_for (self, jitmap->frame.len))
return NULL;
jitmap = (SysprofCaptureJitmap *)(void *)&self->buf[self->pos];
buf = jitmap->data;
endptr = &self->buf[self->pos + jitmap->frame.len];
/* Check the strings are all nul-terminated. */
for (i = 0; i < jitmap->n_jitmaps; i++)
{
SysprofCaptureAddress addr;
if (buf + sizeof addr >= endptr)
return NULL;
memcpy (&addr, buf, sizeof addr);
buf += sizeof addr;
buf = memchr (buf, '\0', (endptr - buf));
if (buf == NULL)
return NULL;
buf++;
}
sysprof_capture_reader_bswap_jitmap (self, jitmap);
self->pos += jitmap->frame.len;
return jitmap;
}
const SysprofCaptureSample *
sysprof_capture_reader_read_sample (SysprofCaptureReader *self)
{
SysprofCaptureSample *sample;
assert (self != NULL);
assert ((self->pos % SYSPROF_CAPTURE_ALIGN) == 0);
assert (self->pos <= self->bufsz);
if (!sysprof_capture_reader_ensure_space_for (self, sizeof *sample))
return NULL;
sample = (SysprofCaptureSample *)(void *)&self->buf[self->pos];
sysprof_capture_reader_bswap_frame (self, &sample->frame);
if (sample->frame.type != SYSPROF_CAPTURE_FRAME_SAMPLE)
return NULL;
if (sample->frame.len < sizeof *sample)
return NULL;
if (self->endian != __BYTE_ORDER)
sample->n_addrs = bswap_16 (sample->n_addrs);
if (sample->frame.len < (sizeof *sample + (sizeof(SysprofCaptureAddress) * sample->n_addrs)))
return NULL;
if (!sysprof_capture_reader_ensure_space_for (self, sample->frame.len))
return NULL;
sample = (SysprofCaptureSample *)(void *)&self->buf[self->pos];
if (SYSPROF_UNLIKELY (self->endian != __BYTE_ORDER))
{
unsigned int i;
for (i = 0; i < sample->n_addrs; i++)
sample->addrs[i] = bswap_64 (sample->addrs[i]);
}
self->pos += sample->frame.len;
return sample;
}
const SysprofCaptureCounterDefine *
sysprof_capture_reader_read_counter_define (SysprofCaptureReader *self)
{
SysprofCaptureCounterDefine *def;
assert (self != NULL);
assert ((self->pos % SYSPROF_CAPTURE_ALIGN) == 0);
assert (self->pos <= self->bufsz);
if (!sysprof_capture_reader_ensure_space_for (self, sizeof *def))
return NULL;
def = (SysprofCaptureCounterDefine *)(void *)&self->buf[self->pos];
if (def->frame.type != SYSPROF_CAPTURE_FRAME_CTRDEF)
return NULL;
if (def->frame.len < sizeof *def)
return NULL;
if (SYSPROF_UNLIKELY (self->endian != __BYTE_ORDER))
def->n_counters = bswap_16 (def->n_counters);
if (def->frame.len < (sizeof *def + (sizeof (SysprofCaptureCounterDefine) * def->n_counters)))
return NULL;
if (!sysprof_capture_reader_ensure_space_for (self, def->frame.len))
return NULL;
def = (SysprofCaptureCounterDefine *)(void *)&self->buf[self->pos];
if (SYSPROF_UNLIKELY (self->endian != __BYTE_ORDER))
{
unsigned int i;
for (i = 0; i < def->n_counters; i++)
{
def->counters[i].id = bswap_32 (def->counters[i].id);
def->counters[i].value.v64 = bswap_64 (def->counters[i].value.v64);
}
}
self->pos += def->frame.len;
return def;
}
const SysprofCaptureCounterSet *
sysprof_capture_reader_read_counter_set (SysprofCaptureReader *self)
{
SysprofCaptureCounterSet *set;
assert (self != NULL);
assert ((self->pos % SYSPROF_CAPTURE_ALIGN) == 0);
assert (self->pos <= self->bufsz);
if (!sysprof_capture_reader_ensure_space_for (self, sizeof *set))
return NULL;
set = (SysprofCaptureCounterSet *)(void *)&self->buf[self->pos];
if (set->frame.type != SYSPROF_CAPTURE_FRAME_CTRSET)
return NULL;
if (set->frame.len < sizeof *set)
return NULL;
if (self->endian != __BYTE_ORDER)
set->n_values = bswap_16 (set->n_values);
if (set->frame.len < (sizeof *set + (sizeof (SysprofCaptureCounterValues) * set->n_values)))
return NULL;
if (!sysprof_capture_reader_ensure_space_for (self, set->frame.len))
return NULL;
set = (SysprofCaptureCounterSet *)(void *)&self->buf[self->pos];
if (SYSPROF_UNLIKELY (self->endian != __BYTE_ORDER))
{
unsigned int i;
for (i = 0; i < set->n_values; i++)
{
unsigned int j;
for (j = 0; j < SYSPROF_N_ELEMENTS (set->values[0].values); j++)
{
set->values[i].ids[j] = bswap_32 (set->values[i].ids[j]);
set->values[i].values[j].v64 = bswap_64 (set->values[i].values[j].v64);
}
}
}
self->pos += set->frame.len;
return set;
}
bool
sysprof_capture_reader_reset (SysprofCaptureReader *self)
{
assert (self != NULL);
self->fd_off = sizeof (SysprofCaptureFileHeader);
self->pos = 0;
self->len = 0;
return true;
}
SysprofCaptureReader *
sysprof_capture_reader_ref (SysprofCaptureReader *self)
{
assert (self != NULL);
assert (self->ref_count > 0);
__atomic_fetch_add (&self->ref_count, 1, __ATOMIC_SEQ_CST);
return self;
}
void
sysprof_capture_reader_unref (SysprofCaptureReader *self)
{
assert (self != NULL);
assert (self->ref_count > 0);
if (__atomic_fetch_sub (&self->ref_count, 1, __ATOMIC_SEQ_CST) == 1)
sysprof_capture_reader_finalize (self);
}
bool
sysprof_capture_reader_splice (SysprofCaptureReader *self,
SysprofCaptureWriter *dest)
{
assert (self != NULL);
assert (self->fd != -1);
assert (dest != NULL);
/* Flush before writing anything to ensure consistency */
if (!sysprof_capture_writer_flush (dest))
{
/* errno is propagated */
return false;
}
/*
* We don't need to track position because writer will
* track the current position to avoid reseting it.
*/
/* Perform the splice; errno is propagated on failure */
return _sysprof_capture_writer_splice_from_fd (dest, self->fd);
}
/**
* sysprof_capture_reader_save_as:
* @self: An #SysprofCaptureReader
* @filename: the file to save the capture as
*
* This is a convenience function for copying a capture file for which
* you may have already discarded the writer for.
*
* `errno` is set on failure. It may be any of the errors returned by
* `open()`, `fstat()`, `ftruncate()`, `lseek()` or `sendfile()`.
*
* Returns: %TRUE on success; otherwise %FALSE.
*/
bool
sysprof_capture_reader_save_as (SysprofCaptureReader *self,
const char *filename)
{
struct stat stbuf;
off_t in_off;
size_t to_write;
int fd = -1;
int errsv;
assert (self != NULL);
assert (filename != NULL);
if (-1 == (fd = open (filename, O_CREAT | O_WRONLY, 0640)))
goto handle_errno;
if (-1 == fstat (self->fd, &stbuf))
goto handle_errno;
if (-1 == ftruncate (fd, stbuf.st_size))
goto handle_errno;
if ((off_t)-1 == lseek (fd, 0L, SEEK_SET))
goto handle_errno;
in_off = 0;
to_write = stbuf.st_size;
while (to_write > 0)
{
ssize_t written;
written = _sysprof_sendfile (fd, self->fd, &in_off, to_write);
if (written < 0)
goto handle_errno;
if (written == 0 && errno != EAGAIN)
goto handle_errno;
assert (written <= (ssize_t)to_write);
to_write -= written;
}
if (self->filename == NULL)
self->filename = sysprof_strdup (filename);
close (fd);
return true;
handle_errno:
errsv = errno;
if (fd != -1)
close (fd);
errno = errsv;
return false;
}
int64_t
sysprof_capture_reader_get_start_time (SysprofCaptureReader *self)
{
assert (self != NULL);
if (self->endian != __BYTE_ORDER)
return bswap_64 (self->header.time);
return self->header.time;
}
/**
* sysprof_capture_reader_get_end_time:
*
* This function will return the end time for the capture, which is the
* same as the last event added to the capture.
*
* If the end time has been stored in the capture header, that will be used.
* Otherwise, the time is discovered from the last capture frame and therefore
* the caller must have read all frames to ensure this value is accurate.
*
* Captures created by sysprof versions containing this API will have the
* end time set if the output capture file-descriptor supports seeking.
*
* Since: 3.22.1
*/
int64_t
sysprof_capture_reader_get_end_time (SysprofCaptureReader *self)
{
int64_t end_time = 0;
assert (self != NULL);
if (self->header.end_time != 0)
{
if (self->endian != __BYTE_ORDER)
end_time = bswap_64 (self->header.end_time);
else
end_time = self->header.end_time;
}
return (self->end_time > end_time) ? self->end_time : end_time;
}
/**
* sysprof_capture_reader_copy:
*
* This function makes a copy of the reader. Since readers use
* positioned reads with pread(), this allows you to have multiple
* readers with the shared file descriptor. This uses dup() to create
* another file descriptor.
*
* Returns: (transfer full): A copy of @self with a new file-descriptor.
*/
SysprofCaptureReader *
sysprof_capture_reader_copy (SysprofCaptureReader *self)
{
SysprofCaptureReader *copy;
int fd;
assert (self != NULL);
if (-1 == (fd = dup (self->fd)))
return NULL;
copy = sysprof_malloc0 (sizeof (SysprofCaptureReader));
if (copy == NULL)
{
close (fd);
return NULL;
}
*copy = *self;
copy->ref_count = 1;
copy->filename = sysprof_strdup (self->filename);
copy->fd = fd;
copy->end_time = self->end_time;
copy->st_buf = self->st_buf;
copy->st_buf_set = self->st_buf_set;
copy->buf = malloc (self->bufsz);
if (copy->buf == NULL)
{
close (fd);
free (copy->filename);
free (copy);
return NULL;
}
memcpy (copy->buf, self->buf, self->bufsz);
return copy;
}
void
sysprof_capture_reader_set_stat (SysprofCaptureReader *self,
const SysprofCaptureStat *st_buf)
{
assert (self != NULL);
if (st_buf != NULL)
{
self->st_buf = *st_buf;
self->st_buf_set = true;
}
else
{
memset (&self->st_buf, 0, sizeof (self->st_buf));
self->st_buf_set = false;
}
}
bool
sysprof_capture_reader_get_stat (SysprofCaptureReader *self,
SysprofCaptureStat *st_buf)
{
assert (self != NULL);
if (st_buf != NULL)
*st_buf = self->st_buf;
return self->st_buf_set;
}
const SysprofCaptureFileChunk *
sysprof_capture_reader_read_file (SysprofCaptureReader *self)
{
SysprofCaptureFileChunk *file_chunk;
assert (self != NULL);
assert ((self->pos % SYSPROF_CAPTURE_ALIGN) == 0);
assert (self->pos <= self->bufsz);
if (!sysprof_capture_reader_ensure_space_for (self, sizeof *file_chunk))
return NULL;
file_chunk = (SysprofCaptureFileChunk *)(void *)&self->buf[self->pos];
sysprof_capture_reader_bswap_frame (self, &file_chunk->frame);
if (file_chunk->frame.type != SYSPROF_CAPTURE_FRAME_FILE_CHUNK)
return NULL;
if (file_chunk->frame.len < sizeof *file_chunk)
return NULL;
if (!sysprof_capture_reader_ensure_space_for (self, file_chunk->frame.len))
return NULL;
file_chunk = (SysprofCaptureFileChunk *)(void *)&self->buf[self->pos];
sysprof_capture_reader_bswap_file_chunk (self, file_chunk);
self->pos += file_chunk->frame.len;
if ((self->pos % SYSPROF_CAPTURE_ALIGN) != 0)
return NULL;
/* Make sure len is < the extra frame data */
if (file_chunk->len > (file_chunk->frame.len - sizeof *file_chunk))
return NULL;
/* Ensure trailing \0 in .path */
file_chunk->path[sizeof file_chunk->path - 1] = 0;
return file_chunk;
}
static bool
array_append (const char ***files,
size_t *n_files,
size_t *n_files_allocated,
const char *new_element)
{
if (*n_files == *n_files_allocated)
{
const char **new_files;
*n_files_allocated = (*n_files_allocated > 0) ? 2 * *n_files_allocated : 4;
new_files = _sysprof_reallocarray (*files, *n_files_allocated, sizeof (**files));
if (new_files == NULL)
return false;
*files = new_files;
}
(*files)[*n_files] = new_element ? strdup (new_element) : NULL;
*n_files = *n_files + 1;
assert (*n_files <= *n_files_allocated);
return true;
}
static void
array_deduplicate (const char **files,
size_t *n_files)
{
size_t last_written, next_to_read;
if (*n_files == 0)
return;
for (last_written = 0, next_to_read = 1; last_written <= next_to_read && next_to_read < *n_files;)
{
if (strcmp (files[next_to_read], files[last_written]) == 0)
next_to_read++;
else
files[++last_written] = files[next_to_read++];
}
assert (last_written + 1 <= *n_files);
*n_files = last_written + 1;
}
static int
compare_strings (const void *a,
const void *b)
{
const char * const *astr = a;
const char * const *bstr = b;
return strcmp (*astr, *bstr);
}
const char **
sysprof_capture_reader_list_files (SysprofCaptureReader *self)
{
const char **files = NULL;
size_t n_files = 0, n_files_allocated = 0;
SysprofCaptureFrameType type;
assert (self != NULL);
/* Only generate the list of files once */
if (self->list_files == NULL)
{
while (sysprof_capture_reader_peek_type (self, &type))
{
const SysprofCaptureFileChunk *file;
if (type != SYSPROF_CAPTURE_FRAME_FILE_CHUNK)
{
sysprof_capture_reader_skip (self);
continue;
}
if (!(file = sysprof_capture_reader_read_file (self)))
break;
if (!array_append (&files, &n_files, &n_files_allocated, file->path))
{
free (files);
errno = ENOMEM;
return NULL;
}
}
/* Sort and deduplicate the files array. */
qsort (files, n_files, sizeof (*files), compare_strings);
array_deduplicate (files, &n_files);
/* Add a null terminator */
if (!array_append (&files, &n_files, &n_files_allocated, NULL))
{
free (files);
errno = ENOMEM;
return NULL;
}
self->list_files = (char **)sysprof_steal_pointer (&files);
self->n_list_files = n_files; /* including NULL */
}
/* Now copy the list but not the strings */
files = malloc (sizeof (char *) * self->n_list_files);
memcpy (files, self->list_files, sizeof (char *) * self->n_list_files);
return sysprof_steal_pointer (&files);
}
bool
sysprof_capture_reader_read_file_fd (SysprofCaptureReader *self,
const char *path,
int fd)
{
assert (self != NULL);
assert (path != NULL);
assert (fd > -1);
for (;;)
{
SysprofCaptureFrameType type;
const SysprofCaptureFileChunk *file;
const uint8_t *buf;
size_t to_write;
if (!sysprof_capture_reader_peek_type (self, &type))
return false;
if (type != SYSPROF_CAPTURE_FRAME_FILE_CHUNK)
goto skip;
if (!(file = sysprof_capture_reader_read_file (self)))
return false;
if (strcmp (path, file->path) != 0)
goto skip;
buf = file->data;
to_write = file->len;
while (to_write > 0)
{
ssize_t written;
written = _sysprof_write (fd, buf, to_write);
if (written < 0)
return false;
if (written == 0 && errno != EAGAIN)
return false;
assert (written <= (ssize_t)to_write);
buf += written;
to_write -= written;
}
if (!file->is_last)
continue;
return true;
skip:
if (!sysprof_capture_reader_skip (self))
return false;
}
sysprof_assert_not_reached ();
}
int
sysprof_capture_reader_get_byte_order (SysprofCaptureReader *self)
{
assert (self != NULL);
return self->endian;
}
const SysprofCaptureFileChunk *
sysprof_capture_reader_find_file (SysprofCaptureReader *self,
const char *path)
{
SysprofCaptureFrameType type;
assert (self != NULL);
assert (path != NULL);
while (sysprof_capture_reader_peek_type (self, &type))
{
if (type == SYSPROF_CAPTURE_FRAME_FILE_CHUNK)
{
const SysprofCaptureFileChunk *fc;
if (!(fc = sysprof_capture_reader_read_file (self)))
break;
if (strcmp (path, fc->path) == 0)
return fc;
continue;
}
if (!sysprof_capture_reader_skip (self))
break;
}
return NULL;
}
const SysprofCaptureAllocation *
sysprof_capture_reader_read_allocation (SysprofCaptureReader *self)
{
SysprofCaptureAllocation *ma;
assert (self != NULL);
assert ((self->pos % SYSPROF_CAPTURE_ALIGN) == 0);
assert (self->pos <= self->bufsz);
if (!sysprof_capture_reader_ensure_space_for (self, sizeof *ma))
return NULL;
ma = (SysprofCaptureAllocation *)(void *)&self->buf[self->pos];
sysprof_capture_reader_bswap_frame (self, &ma->frame);
if (ma->frame.type != SYSPROF_CAPTURE_FRAME_ALLOCATION)
return NULL;
if (ma->frame.len < sizeof *ma)
return NULL;
if (self->endian != __BYTE_ORDER)
{
ma->n_addrs = bswap_16 (ma->n_addrs);
ma->alloc_size = bswap_64 (ma->alloc_size);
ma->alloc_addr = bswap_64 (ma->alloc_addr);
ma->tid = bswap_32 (ma->tid);
}
if (ma->frame.len < (sizeof *ma + (sizeof(SysprofCaptureAddress) * ma->n_addrs)))
return NULL;
if (!sysprof_capture_reader_ensure_space_for (self, ma->frame.len))
return NULL;
ma = (SysprofCaptureAllocation *)(void *)&self->buf[self->pos];
if (SYSPROF_UNLIKELY (self->endian != __BYTE_ORDER))
{
for (unsigned int i = 0; i < ma->n_addrs; i++)
ma->addrs[i] = bswap_64 (ma->addrs[i]);
}
self->pos += ma->frame.len;
return ma;
}
typedef struct {
const SysprofCaptureJitmap *jitmap;
const uint8_t *buf;
unsigned int i;
void *padding1;
void *padding2;
} RealSysprofCaptureJitmapIter;
void
sysprof_capture_jitmap_iter_init (SysprofCaptureJitmapIter *iter,
const SysprofCaptureJitmap *jitmap)
{
RealSysprofCaptureJitmapIter *real_iter = (RealSysprofCaptureJitmapIter *) iter;
assert (iter != NULL);
assert (jitmap != NULL);
real_iter->jitmap = jitmap;
real_iter->buf = jitmap->data;
real_iter->i = 0;
}
bool
sysprof_capture_jitmap_iter_next (SysprofCaptureJitmapIter *iter,
SysprofCaptureAddress *addr,
const char **name)
{
RealSysprofCaptureJitmapIter *real_iter = (RealSysprofCaptureJitmapIter *) iter;
const char *_name;
assert (iter != NULL);
if (real_iter->i >= real_iter->jitmap->n_jitmaps)
return false;
if (addr != NULL)
memcpy (addr, real_iter->buf, sizeof (*addr));
real_iter->buf += sizeof (*addr);
_name = (const char *) real_iter->buf;
if (name != NULL)
*name = _name;
real_iter->buf += strlen (_name) + 1;
real_iter->i++;
return true;
}