/* sysprof-capture-writer-cat.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 */ #define G_LOG_DOMAIN "sysprof-cat" #include "config.h" #include #include #include #include #include #include #include "sysprof-macros-internal.h" typedef struct { uint64_t src; uint64_t dst; } TranslateItem; enum { TRANSLATE_ADDR, TRANSLATE_CTR, N_TRANSLATE }; static void translate_table_clear (GArray **tables, unsigned int table) { sysprof_clear_pointer (&tables[table], g_array_unref); } static int compare_by_src (const void *a, const void *b) { const TranslateItem *itema = a; const TranslateItem *itemb = b; if (itema->src < itemb->src) return -1; else if (itema->src > itemb->src) return 1; else return 0; } static void translate_table_sort (GArray **tables, unsigned int table) { if (tables[table]) g_array_sort (tables[table], compare_by_src); } static void translate_table_add (GArray **tables, unsigned int table, uint64_t src, uint64_t dst) { const TranslateItem item = { src, dst }; if (tables[table] == NULL) tables[table] = g_array_new (FALSE, FALSE, sizeof (TranslateItem)); g_array_append_val (tables[table], item); } static uint64_t translate_table_translate (GArray **tables, unsigned int table, uint64_t src) { const TranslateItem *item; TranslateItem key = { src, 0 }; if (table == TRANSLATE_ADDR) { if ((src & SYSPROF_CAPTURE_JITMAP_MARK) == 0) return src; } if (tables[table] == NULL) return src; item = bsearch (&key, tables[table]->data, tables[table]->len, sizeof (TranslateItem), compare_by_src); return item != NULL ? item->dst : src; } bool sysprof_capture_writer_cat (SysprofCaptureWriter *self, SysprofCaptureReader *reader, GError **error) { GArray *tables[N_TRANSLATE] = { NULL }; SysprofCaptureFrameType type; int64_t start_time; int64_t first_start_time = INT64_MAX; int64_t end_time = -1; assert (self != NULL); assert (reader != NULL); sysprof_capture_reader_reset (reader); translate_table_clear (tables, TRANSLATE_CTR); translate_table_clear (tables, TRANSLATE_ADDR); start_time = sysprof_capture_reader_get_start_time (reader); if (start_time < first_start_time) first_start_time = start_time; /* First we need to find all the JIT maps so that we can translate * addresses later on and have the correct value. */ while (sysprof_capture_reader_peek_type (reader, &type)) { g_autoptr(GHashTable) jitmap = NULL; GHashTableIter iter; const char *name; uint64_t addr; if (type != SYSPROF_CAPTURE_FRAME_JITMAP) { if (!sysprof_capture_reader_skip (reader)) goto panic; continue; } if (!(jitmap = sysprof_capture_reader_read_jitmap (reader))) goto panic; g_hash_table_iter_init (&iter, jitmap); while (g_hash_table_iter_next (&iter, (gpointer *)&addr, (gpointer *)&name)) { uint64_t replace = sysprof_capture_writer_add_jitmap (self, name); /* We need to keep a table of replacement addresses so that * we can translate the samples into the destination address * space that we synthesized for the address identifier. */ translate_table_add (tables, TRANSLATE_ADDR, addr, replace); } } translate_table_sort (tables, TRANSLATE_ADDR); sysprof_capture_reader_reset (reader); while (sysprof_capture_reader_peek_type (reader, &type)) { SysprofCaptureFrame fr; if (sysprof_capture_reader_peek_frame (reader, &fr)) { if (fr.time > end_time) end_time = fr.time; } switch (type) { case SYSPROF_CAPTURE_FRAME_TIMESTAMP: { const SysprofCaptureTimestamp *frame; if (!(frame = sysprof_capture_reader_read_timestamp (reader))) goto panic; sysprof_capture_writer_add_timestamp (self, frame->frame.time, frame->frame.cpu, frame->frame.pid); break; } case SYSPROF_CAPTURE_FRAME_FILE_CHUNK: { const SysprofCaptureFileChunk *frame; if (!(frame = sysprof_capture_reader_read_file (reader))) goto panic; sysprof_capture_writer_add_file (self, frame->frame.time, frame->frame.cpu, frame->frame.pid, frame->path, frame->is_last, frame->data, frame->len); break; } case SYSPROF_CAPTURE_FRAME_LOG: { const SysprofCaptureLog *frame; if (!(frame = sysprof_capture_reader_read_log (reader))) goto panic; sysprof_capture_writer_add_log (self, frame->frame.time, frame->frame.cpu, frame->frame.pid, frame->severity, frame->domain, frame->message); break; } case SYSPROF_CAPTURE_FRAME_MAP: { const SysprofCaptureMap *frame; if (!(frame = sysprof_capture_reader_read_map (reader))) goto panic; sysprof_capture_writer_add_map (self, frame->frame.time, frame->frame.cpu, frame->frame.pid, frame->start, frame->end, frame->offset, frame->inode, frame->filename); break; } case SYSPROF_CAPTURE_FRAME_MARK: { const SysprofCaptureMark *frame; if (!(frame = sysprof_capture_reader_read_mark (reader))) goto panic; sysprof_capture_writer_add_mark (self, frame->frame.time, frame->frame.cpu, frame->frame.pid, frame->duration, frame->group, frame->name, frame->message); if (frame->frame.time + frame->duration > end_time) end_time = frame->frame.time + frame->duration; break; } case SYSPROF_CAPTURE_FRAME_PROCESS: { const SysprofCaptureProcess *frame; if (!(frame = sysprof_capture_reader_read_process (reader))) goto panic; sysprof_capture_writer_add_process (self, frame->frame.time, frame->frame.cpu, frame->frame.pid, frame->cmdline); break; } case SYSPROF_CAPTURE_FRAME_FORK: { const SysprofCaptureFork *frame; if (!(frame = sysprof_capture_reader_read_fork (reader))) goto panic; sysprof_capture_writer_add_fork (self, frame->frame.time, frame->frame.cpu, frame->frame.pid, frame->child_pid); break; } case SYSPROF_CAPTURE_FRAME_EXIT: { const SysprofCaptureExit *frame; if (!(frame = sysprof_capture_reader_read_exit (reader))) goto panic; sysprof_capture_writer_add_exit (self, frame->frame.time, frame->frame.cpu, frame->frame.pid); break; } case SYSPROF_CAPTURE_FRAME_METADATA: { const SysprofCaptureMetadata *frame; if (!(frame = sysprof_capture_reader_read_metadata (reader))) goto panic; sysprof_capture_writer_add_metadata (self, frame->frame.time, frame->frame.cpu, frame->frame.pid, frame->id, frame->metadata, frame->frame.len - G_STRUCT_OFFSET (SysprofCaptureMetadata, metadata)); break; } case SYSPROF_CAPTURE_FRAME_SAMPLE: { const SysprofCaptureSample *frame; if (!(frame = sysprof_capture_reader_read_sample (reader))) goto panic; { SysprofCaptureAddress addrs[frame->n_addrs]; for (unsigned int z = 0; z < frame->n_addrs; z++) addrs[z] = translate_table_translate (tables, TRANSLATE_ADDR, frame->addrs[z]); sysprof_capture_writer_add_sample (self, frame->frame.time, frame->frame.cpu, frame->frame.pid, frame->tid, addrs, frame->n_addrs); } break; } case SYSPROF_CAPTURE_FRAME_CTRDEF: { const SysprofCaptureCounterDefine *frame; if (!(frame = sysprof_capture_reader_read_counter_define (reader))) goto panic; { g_autoptr(GArray) counter = g_array_new (FALSE, FALSE, sizeof (SysprofCaptureCounter)); for (unsigned int z = 0; z < frame->n_counters; z++) { SysprofCaptureCounter c = frame->counters[z]; unsigned int src = c.id; c.id = sysprof_capture_writer_request_counter (self, 1); if (c.id != src) translate_table_add (tables, TRANSLATE_CTR, src, c.id); g_array_append_val (counter, c); } sysprof_capture_writer_define_counters (self, frame->frame.time, frame->frame.cpu, frame->frame.pid, (gpointer)counter->data, counter->len); translate_table_sort (tables, TRANSLATE_CTR); } break; } case SYSPROF_CAPTURE_FRAME_CTRSET: { const SysprofCaptureCounterSet *frame; if (!(frame = sysprof_capture_reader_read_counter_set (reader))) goto panic; { g_autoptr(GArray) ids = g_array_new (FALSE, FALSE, sizeof (guint)); g_autoptr(GArray) values = g_array_new (FALSE, FALSE, sizeof (SysprofCaptureCounterValue)); for (unsigned int z = 0; z < frame->n_values; z++) { const SysprofCaptureCounterValues *v = &frame->values[z]; for (unsigned int y = 0; y < G_N_ELEMENTS (v->ids); y++) { if (v->ids[y]) { unsigned int dst = translate_table_translate (tables, TRANSLATE_CTR, v->ids[y]); SysprofCaptureCounterValue value = v->values[y]; g_array_append_val (ids, dst); g_array_append_val (values, value); } } } assert (ids->len == values->len); sysprof_capture_writer_set_counters (self, frame->frame.time, frame->frame.cpu, frame->frame.pid, (const unsigned int *)(void *)ids->data, (const SysprofCaptureCounterValue *)(void *)values->data, ids->len); } break; } case SYSPROF_CAPTURE_FRAME_JITMAP: /* We already did this */ if (!sysprof_capture_reader_skip (reader)) goto panic; break; case SYSPROF_CAPTURE_FRAME_ALLOCATION: { const SysprofCaptureAllocation *frame; if (!(frame = sysprof_capture_reader_read_allocation (reader))) goto panic; sysprof_capture_writer_add_allocation_copy (self, frame->frame.time, frame->frame.cpu, frame->frame.pid, frame->tid, frame->alloc_addr, frame->alloc_size, frame->addrs, frame->n_addrs); break; } default: /* Silently drop, which is better than looping. We could potentially * copy this over using the raw bytes at some point. */ sysprof_capture_reader_skip (reader); break; } } sysprof_capture_writer_flush (self); /* do this after flushing as it uses pwrite() to replace data */ _sysprof_capture_writer_set_time_range (self, first_start_time, end_time); translate_table_clear (tables, TRANSLATE_ADDR); translate_table_clear (tables, TRANSLATE_CTR); return true; panic: g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, "Failed to write data"); translate_table_clear (tables, TRANSLATE_ADDR); translate_table_clear (tables, TRANSLATE_CTR); return false; }