mirror of
https://github.com/varun-r-mallya/sysprof.git
synced 2025-12-31 20:36:25 +00:00
They work exactly the same way as the GLib functions. Signed-off-by: Philip Withnall <withnall@endlessm.com> Helps: #40
532 lines
18 KiB
C
532 lines
18 KiB
C
/* sysprof-capture-writer-cat.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
|
|
*/
|
|
|
|
#define G_LOG_DOMAIN "sysprof-cat"
|
|
|
|
#include "config.h"
|
|
|
|
#include <glib/gstdio.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <sysprof-capture.h>
|
|
#include <unistd.h>
|
|
|
|
#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;
|
|
}
|