mirror of
https://github.com/varun-r-mallya/sysprof.git
synced 2025-12-31 20:36:25 +00:00
This is like sample but has an "enter/exit" flag with it. This can be useful when you want to provide tracing instead of sampling. We use a different frame type so that we can denote that this isn't traditional sampling, and the flag can be used to find the next exit for the current enter for calculating durations. The entire stack trace is provided to make things easier on tools which may want to deal with indirect functions that were not instrumented but can be unwound. That may allow for tooling to give the user some insight that it's not *just* this function entering, but some functions before it were entered too. This also adds a SysprofTracer instrument which will preload a libsysprof-tracer-6.so into the process providing the __cyg_profile_func_enter() and __cyg_profile_func_leave() hooks.
612 lines
21 KiB
C
612 lines
21 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
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
#include "sysprof-capture.h"
|
|
#include "sysprof-capture-util-private.h"
|
|
#include "sysprof-macros-internal.h"
|
|
|
|
typedef struct
|
|
{
|
|
uint64_t src;
|
|
uint64_t dst;
|
|
} TranslateItem;
|
|
|
|
typedef struct
|
|
{
|
|
TranslateItem *items;
|
|
size_t n_items;
|
|
size_t n_items_allocated;
|
|
} TranslateTable;
|
|
|
|
enum {
|
|
TRANSLATE_ADDR,
|
|
TRANSLATE_CTR,
|
|
N_TRANSLATE
|
|
};
|
|
|
|
static void
|
|
translate_table_clear (TranslateTable *tables,
|
|
unsigned int table)
|
|
{
|
|
TranslateTable *table_ptr = &tables[table];
|
|
|
|
sysprof_clear_pointer (&table_ptr->items, free);
|
|
table_ptr->n_items = 0;
|
|
table_ptr->n_items_allocated = 0;
|
|
}
|
|
|
|
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 (TranslateTable *tables,
|
|
unsigned int table)
|
|
{
|
|
TranslateTable *table_ptr = &tables[table];
|
|
|
|
if (table_ptr->items)
|
|
qsort (table_ptr->items, table_ptr->n_items, sizeof (*table_ptr->items), compare_by_src);
|
|
}
|
|
|
|
static void
|
|
translate_table_add (TranslateTable *tables,
|
|
unsigned int table,
|
|
uint64_t src,
|
|
uint64_t dst)
|
|
{
|
|
TranslateTable *table_ptr = &tables[table];
|
|
const TranslateItem item = { src, dst };
|
|
|
|
if (table_ptr->n_items == table_ptr->n_items_allocated)
|
|
{
|
|
table_ptr->n_items_allocated = (table_ptr->n_items_allocated > 0) ? table_ptr->n_items_allocated * 2 : 4;
|
|
table_ptr->items = _sysprof_reallocarray (table_ptr->items, table_ptr->n_items_allocated, sizeof (*table_ptr->items));
|
|
assert (table_ptr->items != NULL);
|
|
}
|
|
|
|
table_ptr->items[table_ptr->n_items++] = item;
|
|
assert (table_ptr->n_items <= table_ptr->n_items_allocated);
|
|
}
|
|
|
|
static uint64_t
|
|
translate_table_translate (TranslateTable *tables,
|
|
unsigned int table,
|
|
uint64_t src)
|
|
{
|
|
TranslateTable *table_ptr = &tables[table];
|
|
const TranslateItem *item;
|
|
TranslateItem key = { src, 0 };
|
|
|
|
if (table == TRANSLATE_ADDR)
|
|
{
|
|
if ((src & SYSPROF_CAPTURE_JITMAP_MARK) == 0)
|
|
return src;
|
|
}
|
|
|
|
if (table_ptr->items == NULL)
|
|
return src;
|
|
|
|
item = bsearch (&key,
|
|
table_ptr->items,
|
|
table_ptr->n_items,
|
|
sizeof (*table_ptr->items),
|
|
compare_by_src);
|
|
|
|
return item != NULL ? item->dst : src;
|
|
}
|
|
|
|
bool
|
|
sysprof_capture_writer_cat (SysprofCaptureWriter *self,
|
|
SysprofCaptureReader *reader)
|
|
{
|
|
TranslateTable tables[N_TRANSLATE] = { 0, };
|
|
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))
|
|
{
|
|
const SysprofCaptureJitmap *jitmap;
|
|
SysprofCaptureJitmapIter iter;
|
|
SysprofCaptureAddress addr;
|
|
const char *name;
|
|
|
|
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;
|
|
|
|
sysprof_capture_jitmap_iter_init (&iter, jitmap);
|
|
while (sysprof_capture_jitmap_iter_next (&iter, &addr, &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 - offsetof (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_TRACE:
|
|
{
|
|
const SysprofCaptureTrace *frame;
|
|
|
|
if (!(frame = sysprof_capture_reader_read_trace (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_trace (self,
|
|
frame->frame.time,
|
|
frame->frame.cpu,
|
|
frame->frame.pid,
|
|
frame->tid,
|
|
addrs,
|
|
frame->n_addrs,
|
|
frame->entering);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case SYSPROF_CAPTURE_FRAME_CTRDEF:
|
|
{
|
|
const SysprofCaptureCounterDefine *frame;
|
|
|
|
if (!(frame = sysprof_capture_reader_read_counter_define (reader)))
|
|
goto panic;
|
|
|
|
{
|
|
SysprofCaptureCounter *counters = calloc (frame->n_counters, sizeof (*counters));
|
|
size_t n_counters = 0;
|
|
if (counters == NULL)
|
|
goto panic;
|
|
|
|
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);
|
|
|
|
counters[n_counters++] = c;
|
|
}
|
|
|
|
sysprof_capture_writer_define_counters (self,
|
|
frame->frame.time,
|
|
frame->frame.cpu,
|
|
frame->frame.pid,
|
|
counters,
|
|
n_counters);
|
|
|
|
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;
|
|
|
|
{
|
|
unsigned int *ids = NULL;
|
|
SysprofCaptureCounterValue *values = NULL;
|
|
size_t n_elements = 0;
|
|
size_t n_elements_allocated = 0;
|
|
|
|
for (unsigned int z = 0; z < frame->n_values; z++)
|
|
{
|
|
const SysprofCaptureCounterValues *v = &frame->values[z];
|
|
|
|
for (unsigned int y = 0; y < SYSPROF_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];
|
|
|
|
if (n_elements == n_elements_allocated)
|
|
{
|
|
n_elements_allocated = (n_elements_allocated > 0) ? n_elements_allocated * 2 : 4;
|
|
ids = _sysprof_reallocarray (ids, n_elements_allocated, sizeof (*ids));
|
|
values = _sysprof_reallocarray (values, n_elements_allocated, sizeof (*values));
|
|
if (ids == NULL || values == NULL)
|
|
goto panic;
|
|
}
|
|
|
|
ids[n_elements] = dst;
|
|
values[n_elements] = value;
|
|
n_elements++;
|
|
assert (n_elements <= n_elements_allocated);
|
|
}
|
|
}
|
|
}
|
|
|
|
sysprof_capture_writer_set_counters (self,
|
|
frame->frame.time,
|
|
frame->frame.cpu,
|
|
frame->frame.pid,
|
|
ids,
|
|
values,
|
|
n_elements);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
case SYSPROF_CAPTURE_FRAME_OVERLAY:
|
|
{
|
|
const SysprofCaptureOverlay *frame;
|
|
const char *src;
|
|
const char *dst;
|
|
|
|
if (!(frame = sysprof_capture_reader_read_overlay (reader)))
|
|
goto panic;
|
|
|
|
/* This should have been verified alrady when decoding */
|
|
assert (frame->frame.len >= (sizeof *frame + frame->src_len + 1 + frame->dst_len + 1));
|
|
|
|
src = &frame->data[0];
|
|
dst = &frame->data[frame->src_len+1];
|
|
|
|
sysprof_capture_writer_add_overlay (self,
|
|
frame->frame.time,
|
|
frame->frame.cpu,
|
|
frame->frame.pid,
|
|
frame->layer,
|
|
src,
|
|
dst);
|
|
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:
|
|
translate_table_clear (tables, TRANSLATE_ADDR);
|
|
translate_table_clear (tables, TRANSLATE_CTR);
|
|
|
|
errno = EIO;
|
|
return false;
|
|
}
|