gtk2/gdk/capture/sp-capture-writer.c
Matthias Clasen 032bb45ce3 gdk: Add a profiler
This is writing data in the capture format of sysprof,
using the SpCaptureWriter. For now, this is using a
vendored copy of libsysprof. Eventually, we want to
use the static library that sysprof provides.
2019-05-07 09:37:59 -07:00

1124 lines
27 KiB
C

/* sp-capture-writer.c
*
* Copyright © 2016 Christian Hergert <chergert@redhat.com>
*
* This file is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This file is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif
#include <errno.h>
#include <fcntl.h>
#include <glib/gstdio.h>
#include <string.h>
#include <sys/sendfile.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "capture/sp-capture-writer.h"
#define DEFAULT_BUFFER_SIZE (getpagesize() * 64L)
#define INVALID_ADDRESS (G_GUINT64_CONSTANT(0))
typedef struct
{
/* A pinter into the string buffer */
const gchar *str;
/* The unique address for the string */
guint64 addr;
} SpCaptureJitmapBucket;
struct _SpCaptureWriter
{
/*
* This is our buffer location for incoming strings. This is used
* similarly to GStringChunk except there is only one-page, and after
* it fills, we flush to disk.
*
* This is paired with a closed hash table for deduplication.
*/
gchar addr_buf[4096*4];
/* Our hashtable for deduplication. */
SpCaptureJitmapBucket addr_hash[512];
/* We keep the large fields above so that our allocation will be page
* alinged for the write buffer. This improves the performance of large
* writes to the target file-descriptor.
*/
volatile gint ref_count;
/*
* Our address sequence counter. The value that comes from
* monotonically increasing this is OR'd with JITMAP_MARK to denote
* the address name should come from the JIT map.
*/
gsize addr_seq;
/* Our position in addr_buf. */
gsize addr_buf_pos;
/*
* The number of hash table items in @addr_hash. This is an
* optimization so that we can avoid calculating the number of strings
* when flushing out the jitmap.
*/
guint addr_hash_size;
/* Capture file handle */
int fd;
/* Our write buffer for fd */
guint8 *buf;
gsize pos;
gsize len;
/* counter id sequence */
gint next_counter_id;
/* Statistics while recording */
SpCaptureStat stat;
};
#ifndef SP_DISABLE_GOBJECT
G_DEFINE_BOXED_TYPE (SpCaptureWriter, sp_capture_writer,
sp_capture_writer_ref, sp_capture_writer_unref)
#endif
static inline void
sp_capture_writer_frame_init (SpCaptureFrame *frame_,
gint len,
gint cpu,
GPid pid,
gint64 time_,
SpCaptureFrameType type)
{
g_assert (frame_ != NULL);
frame_->len = len;
frame_->cpu = cpu;
frame_->pid = pid;
frame_->time = time_;
frame_->type = type;
frame_->padding = 0;
}
static void
sp_capture_writer_finalize (SpCaptureWriter *self)
{
if (self != NULL)
{
sp_capture_writer_flush (self);
close (self->fd);
g_free (self->buf);
g_free (self);
}
}
SpCaptureWriter *
sp_capture_writer_ref (SpCaptureWriter *self)
{
g_assert (self != NULL);
g_assert (self->ref_count > 0);
g_atomic_int_inc (&self->ref_count);
return self;
}
void
sp_capture_writer_unref (SpCaptureWriter *self)
{
g_assert (self != NULL);
g_assert (self->ref_count > 0);
if (g_atomic_int_dec_and_test (&self->ref_count))
sp_capture_writer_finalize (self);
}
static gboolean
sp_capture_writer_flush_data (SpCaptureWriter *self)
{
const guint8 *buf;
gssize written;
gsize to_write;
g_assert (self != NULL);
g_assert (self->pos <= self->len);
g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0);
buf = self->buf;
to_write = self->pos;
while (to_write > 0)
{
written = write (self->fd, buf, to_write);
if (written < 0)
return FALSE;
if (written == 0 && errno != EAGAIN)
return FALSE;
g_assert (written <= (gssize)to_write);
buf += written;
to_write -= written;
}
self->pos = 0;
return TRUE;
}
static inline void
sp_capture_writer_realign (gsize *pos)
{
*pos = (*pos + SP_CAPTURE_ALIGN - 1) & ~(SP_CAPTURE_ALIGN - 1);
}
static inline gboolean
sp_capture_writer_ensure_space_for (SpCaptureWriter *self,
gsize len)
{
/* Check for max frame size */
if (len > G_MAXUSHORT)
return FALSE;
if ((self->len - self->pos) < len)
{
if (!sp_capture_writer_flush_data (self))
return FALSE;
}
return TRUE;
}
static inline gpointer
sp_capture_writer_allocate (SpCaptureWriter *self,
gsize *len)
{
gpointer p;
g_assert (self != NULL);
g_assert (len != NULL);
g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0);
sp_capture_writer_realign (len);
if (!sp_capture_writer_ensure_space_for (self, *len))
return NULL;
p = (gpointer)&self->buf[self->pos];
self->pos += *len;
g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0);
return p;
}
static gboolean
sp_capture_writer_flush_jitmap (SpCaptureWriter *self)
{
SpCaptureJitmap jitmap;
gssize r;
gsize len;
g_assert (self != NULL);
if (self->addr_hash_size == 0)
return TRUE;
g_assert (self->addr_buf_pos > 0);
len = sizeof jitmap + self->addr_buf_pos;
sp_capture_writer_realign (&len);
sp_capture_writer_frame_init (&jitmap.frame,
len,
-1,
getpid (),
SP_CAPTURE_CURRENT_TIME,
SP_CAPTURE_FRAME_JITMAP);
jitmap.n_jitmaps = self->addr_hash_size;
if (sizeof jitmap != write (self->fd, &jitmap, sizeof jitmap))
return FALSE;
r = write (self->fd, self->addr_buf, len - sizeof jitmap);
if (r < 0 || (gsize)r != (len - sizeof jitmap))
return FALSE;
self->addr_buf_pos = 0;
self->addr_hash_size = 0;
memset (self->addr_hash, 0, sizeof self->addr_hash);
self->stat.frame_count[SP_CAPTURE_FRAME_JITMAP]++;
return TRUE;
}
static gboolean
sp_capture_writer_lookup_jitmap (SpCaptureWriter *self,
const gchar *name,
SpCaptureAddress *addr)
{
guint hash;
guint i;
g_assert (self != NULL);
g_assert (name != NULL);
g_assert (addr != NULL);
hash = g_str_hash (name) % G_N_ELEMENTS (self->addr_hash);
for (i = hash; i < G_N_ELEMENTS (self->addr_hash); i++)
{
SpCaptureJitmapBucket *bucket = &self->addr_hash[i];
if (bucket->str == NULL)
return FALSE;
if (strcmp (bucket->str, name) == 0)
{
*addr = bucket->addr;
return TRUE;
}
}
for (i = 0; i < hash; i++)
{
SpCaptureJitmapBucket *bucket = &self->addr_hash[i];
if (bucket->str == NULL)
return FALSE;
if (strcmp (bucket->str, name) == 0)
{
*addr = bucket->addr;
return TRUE;
}
}
return FALSE;
}
static SpCaptureAddress
sp_capture_writer_insert_jitmap (SpCaptureWriter *self,
const gchar *str)
{
SpCaptureAddress addr;
gchar *dst;
gsize len;
guint hash;
guint i;
g_assert (self != NULL);
g_assert (str != NULL);
g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0);
len = sizeof addr + strlen (str) + 1;
if ((self->addr_hash_size == G_N_ELEMENTS (self->addr_hash)) ||
((sizeof self->addr_buf - self->addr_buf_pos) < len))
{
if (!sp_capture_writer_flush_jitmap (self))
return INVALID_ADDRESS;
g_assert (self->addr_hash_size == 0);
g_assert (self->addr_buf_pos == 0);
}
g_assert (self->addr_hash_size < G_N_ELEMENTS (self->addr_hash));
g_assert (len > sizeof addr);
/* Allocate the next unique address */
addr = SP_CAPTURE_JITMAP_MARK | ++self->addr_seq;
/* Copy the address into the buffer */
dst = (gchar *)&self->addr_buf[self->addr_buf_pos];
memcpy (dst, &addr, sizeof addr);
/*
* Copy the string into the buffer, keeping dst around for
* when we insert into the hashtable.
*/
dst += sizeof addr;
memcpy (dst, str, len - sizeof addr);
/* Advance our string cache position */
self->addr_buf_pos += len;
g_assert (self->addr_buf_pos <= sizeof self->addr_buf);
/* Now place the address into the hashtable */
hash = g_str_hash (str) % G_N_ELEMENTS (self->addr_hash);
/* Start from the current hash bucket and go forward */
for (i = hash; i < G_N_ELEMENTS (self->addr_hash); i++)
{
SpCaptureJitmapBucket *bucket = &self->addr_hash[i];
if (G_LIKELY (bucket->str == NULL))
{
bucket->str = dst;
bucket->addr = addr;
self->addr_hash_size++;
return addr;
}
}
/* Wrap around to the beginning */
for (i = 0; i < hash; i++)
{
SpCaptureJitmapBucket *bucket = &self->addr_hash[i];
if (G_LIKELY (bucket->str == NULL))
{
bucket->str = dst;
bucket->addr = addr;
self->addr_hash_size++;
return addr;
}
}
g_assert_not_reached ();
return INVALID_ADDRESS;
}
SpCaptureWriter *
sp_capture_writer_new_from_fd (int fd,
gsize buffer_size)
{
g_autofree gchar *nowstr = NULL;
SpCaptureWriter *self;
SpCaptureFileHeader *header;
GTimeVal tv;
gsize header_len = sizeof(*header);
if (buffer_size == 0)
buffer_size = DEFAULT_BUFFER_SIZE;
g_assert (fd != -1);
g_assert (buffer_size % getpagesize() == 0);
if (ftruncate (fd, 0) != 0)
return NULL;
self = g_new0 (SpCaptureWriter, 1);
self->ref_count = 1;
self->fd = fd;
self->buf = (guint8 *)g_malloc0 (buffer_size);
self->len = buffer_size;
self->next_counter_id = 1;
g_get_current_time (&tv);
nowstr = g_time_val_to_iso8601 (&tv);
header = sp_capture_writer_allocate (self, &header_len);
if (header == NULL)
{
sp_capture_writer_finalize (self);
return NULL;
}
header->magic = SP_CAPTURE_MAGIC;
header->version = 1;
#ifdef G_LITTLE_ENDIAN
header->little_endian = TRUE;
#else
header->little_endian = FALSE;
#endif
header->padding = 0;
g_strlcpy (header->capture_time, nowstr, sizeof header->capture_time);
header->time = SP_CAPTURE_CURRENT_TIME;
header->end_time = 0;
memset (header->suffix, 0, sizeof header->suffix);
if (!sp_capture_writer_flush_data (self))
{
sp_capture_writer_finalize (self);
return NULL;
}
g_assert (self->pos == 0);
g_assert (self->len > 0);
g_assert (self->len % getpagesize() == 0);
g_assert (self->buf != NULL);
g_assert (self->addr_hash_size == 0);
g_assert (self->fd != -1);
return self;
}
SpCaptureWriter *
sp_capture_writer_new (const gchar *filename,
gsize buffer_size)
{
SpCaptureWriter *self;
int fd;
g_assert (filename != NULL);
g_assert (buffer_size % getpagesize() == 0);
if ((-1 == (fd = open (filename, O_CREAT | O_RDWR, 0640))) ||
(-1 == ftruncate (fd, 0L)))
return NULL;
self = sp_capture_writer_new_from_fd (fd, buffer_size);
if (self == NULL)
close (fd);
return self;
}
gboolean
sp_capture_writer_add_map (SpCaptureWriter *self,
gint64 time,
gint cpu,
GPid pid,
guint64 start,
guint64 end,
guint64 offset,
guint64 inode,
const gchar *filename)
{
SpCaptureMap *ev;
gsize len;
if (filename == NULL)
filename = "";
g_assert (self != NULL);
g_assert (filename != NULL);
len = sizeof *ev + strlen (filename) + 1;
ev = (SpCaptureMap *)sp_capture_writer_allocate (self, &len);
if (!ev)
return FALSE;
sp_capture_writer_frame_init (&ev->frame,
len,
cpu,
pid,
time,
SP_CAPTURE_FRAME_MAP);
ev->start = start;
ev->end = end;
ev->offset = offset;
ev->inode = inode;
g_strlcpy (ev->filename, filename, len - sizeof *ev);
ev->filename[len - sizeof *ev - 1] = '\0';
self->stat.frame_count[SP_CAPTURE_FRAME_MAP]++;
return TRUE;
}
gboolean
sp_capture_writer_add_mark (SpCaptureWriter *self,
gint64 time,
gint cpu,
GPid pid,
guint64 duration,
const gchar *group,
const gchar *name,
const gchar *message)
{
SpCaptureMark *ev;
gsize message_len;
gsize len;
g_assert (self != NULL);
g_assert (name != NULL);
g_assert (group != NULL);
if (message == NULL)
message = "";
message_len = strlen (message) + 1;
len = sizeof *ev + message_len;
ev = (SpCaptureMark *)sp_capture_writer_allocate (self, &len);
if (!ev)
return FALSE;
sp_capture_writer_frame_init (&ev->frame,
len,
cpu,
pid,
time,
SP_CAPTURE_FRAME_MARK);
ev->duration = duration;
g_strlcpy (ev->group, group, sizeof ev->group);
g_strlcpy (ev->name, name, sizeof ev->name);
memcpy (ev->message, message, message_len);
self->stat.frame_count[SP_CAPTURE_FRAME_MARK]++;
return TRUE;
}
SpCaptureAddress
sp_capture_writer_add_jitmap (SpCaptureWriter *self,
const gchar *name)
{
SpCaptureAddress addr = INVALID_ADDRESS;
if (name == NULL)
name = "";
g_assert (self != NULL);
g_assert (name != NULL);
if (!sp_capture_writer_lookup_jitmap (self, name, &addr))
addr = sp_capture_writer_insert_jitmap (self, name);
return addr;
}
gboolean
sp_capture_writer_add_process (SpCaptureWriter *self,
gint64 time,
gint cpu,
GPid pid,
const gchar *cmdline)
{
SpCaptureProcess *ev;
gsize len;
if (cmdline == NULL)
cmdline = "";
g_assert (self != NULL);
g_assert (cmdline != NULL);
len = sizeof *ev + strlen (cmdline) + 1;
ev = (SpCaptureProcess *)sp_capture_writer_allocate (self, &len);
if (!ev)
return FALSE;
sp_capture_writer_frame_init (&ev->frame,
len,
cpu,
pid,
time,
SP_CAPTURE_FRAME_PROCESS);
g_strlcpy (ev->cmdline, cmdline, len - sizeof *ev);
ev->cmdline[len - sizeof *ev - 1] = '\0';
self->stat.frame_count[SP_CAPTURE_FRAME_PROCESS]++;
return TRUE;
}
gboolean
sp_capture_writer_add_sample (SpCaptureWriter *self,
gint64 time,
gint cpu,
GPid pid,
const SpCaptureAddress *addrs,
guint n_addrs)
{
SpCaptureSample *ev;
gsize len;
g_assert (self != NULL);
len = sizeof *ev + (n_addrs * sizeof (SpCaptureAddress));
ev = (SpCaptureSample *)sp_capture_writer_allocate (self, &len);
if (!ev)
return FALSE;
sp_capture_writer_frame_init (&ev->frame,
len,
cpu,
pid,
time,
SP_CAPTURE_FRAME_SAMPLE);
ev->n_addrs = n_addrs;
memcpy (ev->addrs, addrs, (n_addrs * sizeof (SpCaptureAddress)));
self->stat.frame_count[SP_CAPTURE_FRAME_SAMPLE]++;
return TRUE;
}
gboolean
sp_capture_writer_add_fork (SpCaptureWriter *self,
gint64 time,
gint cpu,
GPid pid,
GPid child_pid)
{
SpCaptureFork *ev;
gsize len = sizeof *ev;
g_assert (self != NULL);
ev = (SpCaptureFork *)sp_capture_writer_allocate (self, &len);
if (!ev)
return FALSE;
sp_capture_writer_frame_init (&ev->frame,
len,
cpu,
pid,
time,
SP_CAPTURE_FRAME_FORK);
ev->child_pid = child_pid;
self->stat.frame_count[SP_CAPTURE_FRAME_FORK]++;
return TRUE;
}
gboolean
sp_capture_writer_add_exit (SpCaptureWriter *self,
gint64 time,
gint cpu,
GPid pid)
{
SpCaptureExit *ev;
gsize len = sizeof *ev;
g_assert (self != NULL);
ev = (SpCaptureExit *)sp_capture_writer_allocate (self, &len);
if (!ev)
return FALSE;
sp_capture_writer_frame_init (&ev->frame,
len,
cpu,
pid,
time,
SP_CAPTURE_FRAME_EXIT);
self->stat.frame_count[SP_CAPTURE_FRAME_EXIT]++;
return TRUE;
}
gboolean
sp_capture_writer_add_timestamp (SpCaptureWriter *self,
gint64 time,
gint cpu,
GPid pid)
{
SpCaptureTimestamp *ev;
gsize len = sizeof *ev;
g_assert (self != NULL);
ev = (SpCaptureTimestamp *)sp_capture_writer_allocate (self, &len);
if (!ev)
return FALSE;
sp_capture_writer_frame_init (&ev->frame,
len,
cpu,
pid,
time,
SP_CAPTURE_FRAME_TIMESTAMP);
self->stat.frame_count[SP_CAPTURE_FRAME_TIMESTAMP]++;
return TRUE;
}
static gboolean
sp_capture_writer_flush_end_time (SpCaptureWriter *self)
{
gint64 end_time = SP_CAPTURE_CURRENT_TIME;
ssize_t ret;
g_assert (self != NULL);
/* This field is opportunistic, so a failure is okay. */
again:
ret = pwrite (self->fd,
&end_time,
sizeof (end_time),
G_STRUCT_OFFSET (SpCaptureFileHeader, end_time));
if (ret < 0 && errno == EAGAIN)
goto again;
return TRUE;
}
gboolean
sp_capture_writer_flush (SpCaptureWriter *self)
{
g_assert (self != NULL);
return (sp_capture_writer_flush_jitmap (self) &&
sp_capture_writer_flush_data (self) &&
sp_capture_writer_flush_end_time (self));
}
/**
* sp_capture_writer_save_as:
* @self: A #SpCaptureWriter
* @filename: the file to save the capture as
* @error: a location for a #GError or %NULL.
*
* Saves the captured data as the file @filename.
*
* This is primarily useful if the writer was created with a memory-backed
* file-descriptor such as a memfd or tmpfs file on Linux.
*
* Returns: %TRUE if successful, otherwise %FALSE and @error is set.
*/
gboolean
sp_capture_writer_save_as (SpCaptureWriter *self,
const gchar *filename,
GError **error)
{
gsize to_write;
off_t in_off;
off_t pos;
int fd = -1;
g_assert (self != NULL);
g_assert (self->fd != -1);
g_assert (filename != NULL);
if (-1 == (fd = open (filename, O_CREAT | O_RDWR, 0640)))
goto handle_errno;
if (!sp_capture_writer_flush (self))
goto handle_errno;
if (-1 == (pos = lseek (self->fd, 0L, SEEK_CUR)))
goto handle_errno;
to_write = pos;
in_off = 0;
while (to_write > 0)
{
gssize written;
written = sendfile (fd, self->fd, &in_off, pos);
if (written < 0)
goto handle_errno;
if (written == 0 && errno != EAGAIN)
goto handle_errno;
g_assert (written <= (gssize)to_write);
to_write -= written;
}
close (fd);
return TRUE;
handle_errno:
g_set_error (error,
G_FILE_ERROR,
g_file_error_from_errno (errno),
"%s", g_strerror (errno));
if (fd != -1)
{
close (fd);
g_unlink (filename);
}
return FALSE;
}
/**
* _sp_capture_writer_splice_from_fd:
* @self: An #SpCaptureWriter
* @fd: the fd to read from.
* @error: A location for a #GError, or %NULL.
*
* This is internal API for SpCaptureWriter and SpCaptureReader to
* communicate when splicing a reader into a writer.
*
* This should not be used outside of #SpCaptureReader or
* #SpCaptureWriter.
*
* This will not advance the position of @fd.
*
* Returns: %TRUE if successful; otherwise %FALSE and @error is set.
*/
gboolean
_sp_capture_writer_splice_from_fd (SpCaptureWriter *self,
int fd,
GError **error)
{
struct stat stbuf;
off_t in_off;
gsize to_write;
g_assert (self != NULL);
g_assert (self->fd != -1);
if (-1 == fstat (fd, &stbuf))
goto handle_errno;
if (stbuf.st_size < 256)
{
g_set_error (error,
G_FILE_ERROR,
G_FILE_ERROR_INVAL,
"Cannot splice, possibly corrupt file.");
return FALSE;
}
in_off = 256;
to_write = stbuf.st_size - in_off;
while (to_write > 0)
{
gssize written;
written = sendfile (self->fd, fd, &in_off, to_write);
if (written < 0)
goto handle_errno;
if (written == 0 && errno != EAGAIN)
goto handle_errno;
g_assert (written <= (gssize)to_write);
to_write -= written;
}
return TRUE;
handle_errno:
g_set_error (error,
G_FILE_ERROR,
g_file_error_from_errno (errno),
"%s", g_strerror (errno));
return FALSE;
}
/**
* sp_capture_writer_splice:
* @self: An #SpCaptureWriter
* @dest: An #SpCaptureWriter
* @error: A location for a #GError, or %NULL.
*
* This function will copy the capture @self into the capture @dest. This
* tries to be semi-efficient by using sendfile() to copy the contents between
* the captures. @self and @dest will be flushed before the contents are copied
* into the @dest file-descriptor.
*
* Returns: %TRUE if successful, otherwise %FALSE and and @error is set.
*/
gboolean
sp_capture_writer_splice (SpCaptureWriter *self,
SpCaptureWriter *dest,
GError **error)
{
gboolean ret;
off_t pos;
g_assert (self != NULL);
g_assert (self->fd != -1);
g_assert (dest != NULL);
g_assert (dest->fd != -1);
/* Flush before writing anything to ensure consistency */
if (!sp_capture_writer_flush (self) || !sp_capture_writer_flush (dest))
goto handle_errno;
/* Track our current position so we can reset */
if ((off_t)-1 == (pos = lseek (self->fd, 0L, SEEK_CUR)))
goto handle_errno;
/* Perform the splice */
ret = _sp_capture_writer_splice_from_fd (dest, self->fd, error);
/* Now reset or file-descriptor position (it should be the same */
if (pos != lseek (self->fd, pos, SEEK_SET))
{
ret = FALSE;
goto handle_errno;
}
return ret;
handle_errno:
g_set_error (error,
G_FILE_ERROR,
g_file_error_from_errno (errno),
"%s", g_strerror (errno));
return FALSE;
}
/**
* sp_capture_writer_stat:
* @self: A #SpCaptureWriter
* @stat: (out): A location for an #SpCaptureStat
*
* This function will fill @stat with statistics generated while capturing
* the profiler session.
*/
void
sp_capture_writer_stat (SpCaptureWriter *self,
SpCaptureStat *stat)
{
g_return_if_fail (self != NULL);
g_return_if_fail (stat != NULL);
*stat = self->stat;
}
gboolean
sp_capture_writer_define_counters (SpCaptureWriter *self,
gint64 time,
gint cpu,
GPid pid,
const SpCaptureCounter *counters,
guint n_counters)
{
SpCaptureFrameCounterDefine *def;
gsize len;
guint i;
g_assert (self != NULL);
g_assert (counters != NULL);
if (n_counters == 0)
return TRUE;
len = sizeof *def + (sizeof *counters * n_counters);
def = (SpCaptureFrameCounterDefine *)sp_capture_writer_allocate (self, &len);
if (!def)
return FALSE;
sp_capture_writer_frame_init (&def->frame,
len,
cpu,
pid,
time,
SP_CAPTURE_FRAME_CTRDEF);
def->padding = 0;
def->n_counters = n_counters;
for (i = 0; i < n_counters; i++)
def->counters[i] = counters[i];
self->stat.frame_count[SP_CAPTURE_FRAME_CTRDEF]++;
return TRUE;
}
gboolean
sp_capture_writer_set_counters (SpCaptureWriter *self,
gint64 time,
gint cpu,
GPid pid,
const guint *counters_ids,
const SpCaptureCounterValue *values,
guint n_counters)
{
SpCaptureFrameCounterSet *set;
gsize len;
guint n_groups;
guint group;
guint field;
guint i;
g_assert (self != NULL);
g_assert (counters_ids != NULL);
g_assert (values != NULL || !n_counters);
if (n_counters == 0)
return TRUE;
/* Determine how many value groups we need */
n_groups = n_counters / G_N_ELEMENTS (set->values[0].values);
if ((n_groups * G_N_ELEMENTS (set->values[0].values)) != n_counters)
n_groups++;
len = sizeof *set + (n_groups * sizeof (SpCaptureCounterValues));
set = (SpCaptureFrameCounterSet *)sp_capture_writer_allocate (self, &len);
if (!set)
return FALSE;
memset (set, 0, len);
sp_capture_writer_frame_init (&set->frame,
len,
cpu,
pid,
time,
SP_CAPTURE_FRAME_CTRSET);
set->padding = 0;
set->n_values = n_groups;
for (i = 0, group = 0, field = 0; i < n_counters; i++)
{
set->values[group].ids[field] = counters_ids[i];
set->values[group].values[field] = values[i];
field++;
if (field == G_N_ELEMENTS (set->values[0].values))
{
field = 0;
group++;
}
}
self->stat.frame_count[SP_CAPTURE_FRAME_CTRSET]++;
return TRUE;
}
gint
sp_capture_writer_request_counter (SpCaptureWriter *self,
guint n_counters)
{
gint ret;
g_assert (self != NULL);
ret = self->next_counter_id;
self->next_counter_id += n_counters;
return ret;
}