diff --git a/lib/Makefile.am b/lib/Makefile.am index d6f6a901..b31ff98b 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -45,6 +45,8 @@ headers_DATA = \ sysprof-version.h \ sp-address.h \ sp-callgraph-profile.h \ + sp-capture-condition.h \ + sp-capture-cursor.h \ sp-capture-reader.h \ sp-capture-writer.h \ sp-capture-types.h \ @@ -71,6 +73,8 @@ libsysprof_@API_VERSION@_la_SOURCES = \ sp-address.c \ sp-callgraph-profile.c \ sp-callgraph-profile-private.h \ + sp-capture-condition.c \ + sp-capture-cursor.c \ sp-capture-reader.c \ sp-capture-writer.c \ sp-clock.c \ diff --git a/lib/sp-capture-condition.c b/lib/sp-capture-condition.c new file mode 100644 index 00000000..d7ff7a0c --- /dev/null +++ b/lib/sp-capture-condition.c @@ -0,0 +1,193 @@ +/* sp-capture-condition.c + * + * Copyright (C) 2016 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 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 . + */ + +#define G_LOG_DOMAIN "sp-capture-condition" + +#include + +#include "sp-capture-condition.h" + +typedef enum +{ + SP_CAPTURE_CONDITION_WHERE_TYPE_IN, + SP_CAPTURE_CONDITION_WHERE_TIME_BETWEEN, + SP_CAPTURE_CONDITION_WHERE_PID_IN, +} SpCaptureConditionType; + +struct _SpCaptureCondition +{ + SpCaptureConditionType type; + union { + GArray *where_type_in; + struct { + gint64 begin; + gint64 end; + } where_time_between; + GArray *where_pid_in; + } u; +}; + +gboolean +sp_capture_condition_match (const SpCaptureCondition *self, + const SpCaptureFrame *frame) +{ + g_assert (self != NULL); + g_assert (frame != NULL); + + switch (self->type) + { + case SP_CAPTURE_CONDITION_WHERE_TYPE_IN: + for (guint i = 0; i < self->u.where_type_in->len; i++) + { + if (frame->type == g_array_index (self->u.where_type_in, SpCaptureFrameType, i)) + return TRUE; + } + return FALSE; + + case SP_CAPTURE_CONDITION_WHERE_TIME_BETWEEN: + return (frame->time >= self->u.where_time_between.begin && frame->time <= self->u.where_time_between.end); + + case SP_CAPTURE_CONDITION_WHERE_PID_IN: + for (guint i = 0; i < self->u.where_pid_in->len; i++) + { + if (frame->pid == g_array_index (self->u.where_pid_in, GPid, i)) + return TRUE; + } + return FALSE; + + default: + break; + } + + g_assert_not_reached (); + + return FALSE; +} + +static SpCaptureCondition * +sp_capture_condition_copy (const SpCaptureCondition *self) +{ + SpCaptureCondition *copy; + + copy = g_slice_new0 (SpCaptureCondition); + copy->type = self->type; + + switch (self->type) + { + case SP_CAPTURE_CONDITION_WHERE_TYPE_IN: + return sp_capture_condition_new_where_type_in ( + self->u.where_type_in->len, (const SpCaptureFrameType *)self->u.where_type_in->data); + + case SP_CAPTURE_CONDITION_WHERE_TIME_BETWEEN: + break; + + case SP_CAPTURE_CONDITION_WHERE_PID_IN: + return sp_capture_condition_new_where_pid_in ( + self->u.where_pid_in->len, (const GPid *)self->u.where_pid_in->data); + + default: + g_assert_not_reached (); + break; + } + + return copy; +} + +static void +sp_capture_condition_free (SpCaptureCondition *self) +{ + switch (self->type) + { + case SP_CAPTURE_CONDITION_WHERE_TYPE_IN: + g_array_free (self->u.where_type_in, TRUE); + break; + + case SP_CAPTURE_CONDITION_WHERE_TIME_BETWEEN: + break; + + case SP_CAPTURE_CONDITION_WHERE_PID_IN: + g_array_free (self->u.where_pid_in, TRUE); + break; + + default: + g_assert_not_reached (); + break; + } + + g_slice_free (SpCaptureCondition, self); +} + +G_DEFINE_BOXED_TYPE (SpCaptureCondition, + sp_capture_condition, + sp_capture_condition_copy, + sp_capture_condition_free) + +SpCaptureCondition * +sp_capture_condition_new_where_type_in (guint n_types, + const SpCaptureFrameType *types) +{ + SpCaptureCondition *self; + + g_return_val_if_fail (types != NULL, NULL); + + self = g_slice_new0 (SpCaptureCondition); + self->type = SP_CAPTURE_CONDITION_WHERE_TYPE_IN; + self->u.where_type_in = g_array_sized_new (FALSE, FALSE, sizeof (SpCaptureFrameType), n_types); + g_array_set_size (self->u.where_type_in, n_types); + memcpy (self->u.where_type_in->data, types, sizeof (SpCaptureFrameType) * n_types); + + return self; +} + +SpCaptureCondition * +sp_capture_condition_new_where_time_between (gint64 begin_time, + gint64 end_time) +{ + SpCaptureCondition *self; + + if G_UNLIKELY (begin_time > end_time) + { + gint64 tmp = begin_time; + begin_time = end_time; + end_time = tmp; + } + + self = g_slice_new0 (SpCaptureCondition); + self->type = SP_CAPTURE_CONDITION_WHERE_TIME_BETWEEN; + self->u.where_time_between.begin = begin_time; + self->u.where_time_between.end = end_time; + + return self; +} + +SpCaptureCondition * +sp_capture_condition_new_where_pid_in (guint n_pids, + const GPid *pids) +{ + SpCaptureCondition *self; + + g_return_val_if_fail (pids != NULL, NULL); + + self = g_slice_new0 (SpCaptureCondition); + self->type = SP_CAPTURE_CONDITION_WHERE_PID_IN; + self->u.where_pid_in = g_array_sized_new (FALSE, FALSE, sizeof (GPid), n_pids); + g_array_set_size (self->u.where_pid_in, n_pids); + memcpy (self->u.where_pid_in->data, pids, sizeof (GPid) * n_pids); + + return self; +} diff --git a/lib/sp-capture-condition.h b/lib/sp-capture-condition.h new file mode 100644 index 00000000..67f70120 --- /dev/null +++ b/lib/sp-capture-condition.h @@ -0,0 +1,40 @@ +/* sp-capture-condition.h + * + * Copyright (C) 2016 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 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 . + */ + +#ifndef SP_CAPTURE_CONDITION_H +#define SP_CAPTURE_CONDITION_H + +#include "sp-capture-types.h" + +G_BEGIN_DECLS + +#define SP_TYPE_CAPTURE_CONDITION (sp_capture_condition_get_type()) + +GType sp_capture_condition_get_type (void); +SpCaptureCondition *sp_capture_condition_new_where_type_in (guint n_types, + const SpCaptureFrameType *types); +SpCaptureCondition *sp_capture_condition_new_where_time_between (gint64 begin_time, + gint64 end_time); +SpCaptureCondition *sp_capture_condition_new_where_pid_in (guint n_pids, + const GPid *pids); +gboolean sp_capture_condition_match (const SpCaptureCondition *self, + const SpCaptureFrame *frame); + +G_END_DECLS + +#endif /* SP_CAPTURE_CONDITION_H */ diff --git a/lib/sp-capture-cursor.c b/lib/sp-capture-cursor.c new file mode 100644 index 00000000..de25a9f6 --- /dev/null +++ b/lib/sp-capture-cursor.c @@ -0,0 +1,204 @@ +/* sp-capture-cursor.c + * + * Copyright (C) 2016 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 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 . + */ + +#define G_LOG_DOMAIN "sp-capture-cursor" + +#include "sp-capture-condition.h" +#include "sp-capture-cursor.h" +#include "sp-capture-reader.h" + +#define READ_DELEGATE(f) ((ReadDelegate)(f)) + +typedef const SpCaptureFrame *(*ReadDelegate) (SpCaptureReader *); + +struct _SpCaptureCursor +{ + GObject parent_instance; + GPtrArray *conditions; + SpCaptureReader *reader; + guint reversed : 1; +}; + +G_DEFINE_TYPE (SpCaptureCursor, sp_capture_cursor, G_TYPE_OBJECT) + +static void +destroy_condition (gpointer data) +{ + g_boxed_free (SP_TYPE_CAPTURE_CONDITION, data); +} + +static void +sp_capture_cursor_finalize (GObject *object) +{ + SpCaptureCursor *self = (SpCaptureCursor *)object; + + g_clear_pointer (&self->conditions, g_ptr_array_unref); + g_clear_pointer (&self->reader, sp_capture_reader_unref); + + G_OBJECT_CLASS (sp_capture_cursor_parent_class)->finalize (object); +} + +static void +sp_capture_cursor_class_init (SpCaptureCursorClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sp_capture_cursor_finalize; +} + +static void +sp_capture_cursor_init (SpCaptureCursor *self) +{ + self->conditions = g_ptr_array_new_with_free_func (destroy_condition); +} + +void +sp_capture_cursor_foreach (SpCaptureCursor *self, + SpCaptureCursorCallback callback, + gpointer user_data) +{ + g_return_if_fail (SP_IS_CAPTURE_CURSOR (self)); + g_return_if_fail (self->reader != NULL); + g_return_if_fail (callback != NULL); + + for (;;) + { + const SpCaptureFrame *frame; + SpCaptureFrameType type = 0; + ReadDelegate delegate; + + if (!sp_capture_reader_peek_type (self->reader, &type)) + return; + + switch (type) + { + case SP_CAPTURE_FRAME_TIMESTAMP: + delegate = READ_DELEGATE (sp_capture_reader_read_timestamp); + break; + + case SP_CAPTURE_FRAME_SAMPLE: + delegate = READ_DELEGATE (sp_capture_reader_read_sample); + break; + + case SP_CAPTURE_FRAME_MAP: + delegate = READ_DELEGATE (sp_capture_reader_read_map); + break; + + case SP_CAPTURE_FRAME_PROCESS: + delegate = READ_DELEGATE (sp_capture_reader_read_process); + break; + + case SP_CAPTURE_FRAME_FORK: + delegate = READ_DELEGATE (sp_capture_reader_read_fork); + break; + + case SP_CAPTURE_FRAME_EXIT: + delegate = READ_DELEGATE (sp_capture_reader_read_exit); + break; + + case SP_CAPTURE_FRAME_JITMAP: + delegate = READ_DELEGATE (sp_capture_reader_read_jitmap); + break; + + case SP_CAPTURE_FRAME_CTRDEF: + delegate = READ_DELEGATE (sp_capture_reader_read_counter_define); + break; + + case SP_CAPTURE_FRAME_CTRSET: + delegate = READ_DELEGATE (sp_capture_reader_read_counter_set); + break; + + default: + if (!sp_capture_reader_skip (self->reader)) + return; + delegate = NULL; + break; + } + + if (delegate != NULL && NULL == (frame = delegate (self->reader))) + return; + + if (self->conditions->len == 0) + { + if (!callback (frame, user_data)) + return; + } + else + { + for (guint i = 0; i < self->conditions->len; i++) + { + SpCaptureCondition *condition = g_ptr_array_index (self->conditions, i); + + if (sp_capture_condition_match (condition, frame)) + { + if (!callback (frame, user_data)) + return; + break; + } + } + } + } +} + +void +sp_capture_cursor_reset (SpCaptureCursor *self) +{ + g_return_if_fail (SP_IS_CAPTURE_CURSOR (self)); + g_return_if_fail (self->reader != NULL); + + sp_capture_reader_reset (self->reader); +} + +void +sp_capture_cursor_reverse (SpCaptureCursor *self) +{ + g_return_if_fail (SP_IS_CAPTURE_CURSOR (self)); + + self->reversed = !self->reversed; +} + +/** + * sp_capture_cursor_add_condition: + * @self: An #SpCaptureCursor + * @condition: (transfer full): An #SpCaptureCondition + * + * Adds the condition to the cursor. This condition must evaluate to + * true or a given #SpCapureFrame will not be matched. + */ +void +sp_capture_cursor_add_condition (SpCaptureCursor *self, + SpCaptureCondition *condition) +{ + g_return_if_fail (SP_IS_CAPTURE_CURSOR (self)); + g_return_if_fail (condition != NULL); + + g_ptr_array_add (self->conditions, condition); +} + +SpCaptureCursor * +sp_capture_cursor_new (SpCaptureReader *reader) +{ + SpCaptureCursor *self; + + g_return_val_if_fail (reader != NULL, NULL); + + self = g_object_new (SP_TYPE_CAPTURE_CURSOR, NULL); + self->reader = sp_capture_reader_copy (reader); + + return self; +} diff --git a/lib/sp-capture-cursor.h b/lib/sp-capture-cursor.h new file mode 100644 index 00000000..e59fab29 --- /dev/null +++ b/lib/sp-capture-cursor.h @@ -0,0 +1,55 @@ +/* sp-capture-cursor.h + * + * Copyright (C) 2016 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 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 . + */ + +#ifndef SP_CAPTURE_CURSOR_H +#define SP_CAPTURE_CURSOR_H + +#include "sp-capture-types.h" + +G_BEGIN_DECLS + +#define SP_TYPE_CAPTURE_CURSOR (sp_capture_cursor_get_type()) + +G_DECLARE_FINAL_TYPE (SpCaptureCursor, sp_capture_cursor, SP, CAPTURE_CURSOR, GObject) + +/** + * SpCaptureCursorCallback: + * + * This is the prototype for callbacks that are called for each frame + * matching the cursor query. + * + * Functions matching this typedef should return %TRUE if they want the + * the caller to stop iteration of cursor. + * + * Returns: %TRUE if iteration should stop, otherwise %FALSE. + */ +typedef gboolean (*SpCaptureCursorCallback) (const SpCaptureFrame *frame, + gpointer user_data); + +SpCaptureCursor *sp_capture_cursor_new (SpCaptureReader *reader); +void sp_capture_cursor_foreach (SpCaptureCursor *self, + SpCaptureCursorCallback callback, + gpointer user_data); +void sp_capture_cursor_reset (SpCaptureCursor *self); +void sp_capture_cursor_reverse (SpCaptureCursor *self); +void sp_capture_cursor_add_condition (SpCaptureCursor *self, + SpCaptureCondition *condition); + +G_END_DECLS + +#endif /* SP_CAPTURE_CURSOR_H */ diff --git a/lib/sp-capture-types.h b/lib/sp-capture-types.h index c0dd2b27..65d6b690 100644 --- a/lib/sp-capture-types.h +++ b/lib/sp-capture-types.h @@ -42,8 +42,10 @@ G_BEGIN_DECLS #define SP_CAPTURE_COUNTER_INT64 0 #define SP_CAPTURE_COUNTER_DOUBLE 1 -typedef struct _SpCaptureReader SpCaptureReader; -typedef struct _SpCaptureWriter SpCaptureWriter; +typedef struct _SpCaptureReader SpCaptureReader; +typedef struct _SpCaptureWriter SpCaptureWriter; +typedef struct _SpCaptureCursor SpCaptureCursor; +typedef struct _SpCaptureCondition SpCaptureCondition; typedef guint64 SpCaptureAddress; diff --git a/lib/sysprof.h b/lib/sysprof.h index 30e17c92..5f3b6964 100644 --- a/lib/sysprof.h +++ b/lib/sysprof.h @@ -26,6 +26,8 @@ G_BEGIN_DECLS #define SYSPROF_INSIDE # include "sp-address.h" # include "sp-callgraph-profile.h" +# include "sp-capture-condition.h" +# include "sp-capture-cursor.h" # include "sp-capture-reader.h" # include "sp-capture-writer.h" # include "sp-clock.h" diff --git a/tests/test-capture-cursor.c b/tests/test-capture-cursor.c new file mode 100644 index 00000000..14e0e76f --- /dev/null +++ b/tests/test-capture-cursor.c @@ -0,0 +1,58 @@ +#include +#include + +static gboolean +increment (const SpCaptureFrame *frame, + gpointer user_data) +{ + gint *count= user_data; + (*count)++; + return TRUE; +} + +static void +test_cursor_basic (void) +{ + SpCaptureReader *reader; + SpCaptureWriter *writer; + SpCaptureCursor *cursor; + GError *error = NULL; + gint64 t = SP_CAPTURE_CURRENT_TIME; + guint i; + gint r; + gint count = 0; + + writer = sp_capture_writer_new ("capture-file", 0); + g_assert (writer != NULL); + + reader = sp_capture_reader_new ("capture-file", &error); + g_assert_no_error (error); + g_assert (reader != NULL); + + for (i = 0; i < 100; i++) + { + r = sp_capture_writer_add_timestamp (writer, t, i, -1); + g_assert_cmpint (r, ==, TRUE); + } + + sp_capture_writer_flush (writer); + + cursor = sp_capture_cursor_new (reader); + sp_capture_cursor_foreach (cursor, increment, &count); + g_assert_cmpint (count, ==, 100); + g_clear_object (&cursor); + + sp_capture_reader_unref (reader); + sp_capture_writer_unref (writer); + + g_unlink ("capture-file"); +} + +int +main (int argc, + char *argv[]) +{ + g_test_init (&argc, &argv, NULL); + g_test_add_func ("/SpCaptureCursor/basic", test_cursor_basic); + return g_test_run (); +}