/* sysprof-capture-reader.c * * Copyright 2016-2019 Christian Hergert * * 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 #include #include #include #include #include #include #include #include #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 doesn’t * 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; }