From c9b3f44be48067abf777d318ed2327c72dc6c267 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=A9mie=20Galarneau?= Date: Tue, 5 Jan 2016 18:07:05 -0500 Subject: [PATCH] Tests: Add CTF-IR reference counting test MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérémie Galarneau --- tests/lib/Makefile.am | 8 +- tests/lib/test_ctf_ir_ref.c | 487 ++++++++++++++++++++++++++++++++++++ tests/tests | 1 + 3 files changed, 495 insertions(+), 1 deletion(-) create mode 100644 tests/lib/test_ctf_ir_ref.c diff --git a/tests/lib/Makefile.am b/tests/lib/Makefile.am index 5a3c1a17..50976b9b 100644 --- a/tests/lib/Makefile.am +++ b/tests/lib/Makefile.am @@ -23,12 +23,18 @@ test_ctf_writer_LDADD = $(LIBTAP) \ test_bt_values_LDADD = $(LIBTAP) \ $(top_builddir)/lib/libbabeltrace.la -noinst_PROGRAMS = test_seek test_bitfield test_ctf_writer test_bt_values +test_ctf_ir_ref_LDADD = $(LIBTAP) \ + $(top_builddir)/lib/libbabeltrace.la \ + $(top_builddir)/formats/ctf/libbabeltrace-ctf.la + +noinst_PROGRAMS = test_seek test_bitfield test_ctf_writer test_bt_values \ + test_ctf_ir_ref test_seek_SOURCES = test_seek.c test_bitfield_SOURCES = test_bitfield.c test_ctf_writer_SOURCES = test_ctf_writer.c test_bt_values_SOURCES = test_bt_values.c +test_ctf_ir_ref_SOURCES = test_ctf_ir_ref.c SCRIPT_LIST = test_seek_big_trace \ test_seek_empty_packet \ diff --git a/tests/lib/test_ctf_ir_ref.c b/tests/lib/test_ctf_ir_ref.c new file mode 100644 index 00000000..856715cb --- /dev/null +++ b/tests/lib/test_ctf_ir_ref.c @@ -0,0 +1,487 @@ +/* + * test_ctf_ir_ref.c + * + * CTF IR Reference Count test + * + * Copyright 2016 - Jérémie Galarneau + * + * 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; under version 2 of the License. + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "tap/tap.h" +#include +#include +#include +#include +#include +#include + +struct user { + struct bt_ctf_trace *tc; + struct bt_ctf_stream_class *sc; + struct bt_ctf_event_class *ec; +}; + +/** + * Returns a structure containing the following fields: + * - uint8_t payload_8; + * - uint16_t payload_16; + * - uint32_t payload_32; + */ +static struct bt_ctf_field_type *create_integer_struct(void) +{ + int ret; + struct bt_ctf_field_type *structure = NULL; + struct bt_ctf_field_type *ui8 = NULL, *ui16 = NULL, *ui32 = NULL; + + structure = bt_ctf_field_type_structure_create(); + if (!structure) { + goto error; + } + + ui8 = bt_ctf_field_type_integer_create(8); + if (!ui8) { + diag("Failed to create uint8_t type"); + goto error; + } + ret = bt_ctf_field_type_structure_add_field(structure, ui8, + "payload_8"); + if (ret) { + diag("Failed to add uint8_t to structure"); + goto error; + } + ui16 = bt_ctf_field_type_integer_create(16); + if (!ui16) { + diag("Failed to create uint16_t type"); + goto error; + } + ret = bt_ctf_field_type_structure_add_field(structure, ui16, + "payload_16"); + if (ret) { + diag("Failed to add uint16_t to structure"); + goto error; + } + ui32 = bt_ctf_field_type_integer_create(32); + if (!ui32) { + diag("Failed to create uint32_t type"); + goto error; + } + ret = bt_ctf_field_type_structure_add_field(structure, ui32, + "payload_32"); + if (ret) { + diag("Failed to add uint32_t to structure"); + goto error; + } +end: + BT_PUT(ui8); + BT_PUT(ui16); + BT_PUT(ui32); + return structure; +error: + BT_PUT(structure); + goto end; +} + +/** + * A simple event has the following payload: + * - uint8_t payload_8; + * - uint16_t payload_16; + * - uint32_t payload_32; + */ +static struct bt_ctf_event_class *create_simple_event(const char *name) +{ + int ret; + struct bt_ctf_event_class *event = NULL; + struct bt_ctf_field_type *payload = NULL; + + assert(name); + event = bt_ctf_event_class_create(name); + if (!event) { + diag("Failed to create simple event"); + goto error; + } + + payload = create_integer_struct(); + if (!payload) { + diag("Failed to initialize integer structure"); + goto error; + } + + ret = bt_ctf_event_class_set_payload_type(event, payload); + if (ret) { + diag("Failed to set simple event payload"); + goto error; + } +end: + BT_PUT(payload); + return event; +error: + BT_PUT(event); + goto end;; +} + +/** + * A complex event has the following payload: + * - uint8_t payload_8; + * - uint16_t payload_16; + * - uint32_t payload_32; + * - struct payload_struct: + * - uint8_t payload_8; + * - uint16_t payload_16; + * - uint32_t payload_32; + */ +static struct bt_ctf_event_class *create_complex_event(const char *name) +{ + int ret; + struct bt_ctf_event_class *event = NULL; + struct bt_ctf_field_type *inner = NULL, *outer = NULL; + + assert(name); + event = bt_ctf_event_class_create(name); + if (!event) { + diag("Failed to create complex event"); + goto error; + } + + outer = create_integer_struct(); + if (!outer) { + diag("Failed to initialize integer structure"); + goto error; + } + + inner = create_integer_struct(); + if (!inner) { + diag("Failed to initialize integer structure"); + goto error; + } + + ret = bt_ctf_field_type_structure_add_field(outer, inner, + "payload_struct"); + if (ret) { + diag("Failed to add inner structure to outer structure"); + goto error; + } + + ret = bt_ctf_event_class_set_payload_type(event, outer); + if (ret) { + diag("Failed to set complex event payload"); + goto error; + } +end: + BT_PUT(inner); + BT_PUT(outer); + return event; +error: + BT_PUT(event); + goto end;; +} + +static struct bt_ctf_stream_class *create_sc1(void) +{ + int ret; + struct bt_ctf_event_class *ec1 = NULL, *ec2 = NULL; + struct bt_ctf_stream_class *sc1 = NULL, *ret_stream = NULL; + + sc1 = bt_ctf_stream_class_create("sc1"); + if (!sc1) { + diag("Failed to create Stream Class"); + goto error; + } + + ec1 = create_complex_event("ec1"); + if (!ec1) { + diag("Failed to create complex event EC1"); + goto error; + } + ret = bt_ctf_stream_class_add_event_class(sc1, ec1); + if (ret) { + diag("Failed to add EC1 to SC1"); + goto error; + } + + ec2 = create_simple_event("ec2"); + if (!ec2) { + diag("Failed to create simple event EC2"); + goto error; + } + ret = bt_ctf_stream_class_add_event_class(sc1, ec2); + if (ret) { + diag("Failed to add EC1 to SC1"); + goto error; + } + + ret_stream = bt_ctf_event_class_get_stream_class(ec1); + ok(ret_stream == sc1, "Get parent stream SC1 from EC1"); + BT_PUT(ret_stream); + + ret_stream = bt_ctf_event_class_get_stream_class(ec2); + ok(ret_stream == sc1, "Get parent stream SC1 from EC2"); +end: + BT_PUT(ret_stream); + BT_PUT(ec1); + BT_PUT(ec2); + return sc1; +error: + BT_PUT(sc1); + goto end; +} + +static struct bt_ctf_stream_class *create_sc2(void) +{ + int ret; + struct bt_ctf_event_class *ec3 = NULL; + struct bt_ctf_stream_class *sc2 = NULL, *ret_stream = NULL; + + sc2 = bt_ctf_stream_class_create("sc2"); + if (!sc2) { + diag("Failed to create Stream Class"); + goto error; + } + + ec3 = create_simple_event("ec3"); + if (!ec3) { + diag("Failed to create simple event EC3"); + goto error; + } + ret = bt_ctf_stream_class_add_event_class(sc2, ec3); + if (ret) { + diag("Failed to add EC3 to SC2"); + goto error; + } + + ret_stream = bt_ctf_event_class_get_stream_class(ec3); + ok(ret_stream == sc2, "Get parent stream SC2 from EC3"); +end: + BT_PUT(ret_stream); + BT_PUT(ec3); + return sc2; +error: + BT_PUT(sc2); + goto end; +} + +static struct bt_ctf_trace *create_tc1(void) +{ + int ret; + struct bt_ctf_trace *tc1 = NULL; + struct bt_ctf_stream_class *sc1 = NULL, *sc2 = NULL; + + tc1 = bt_ctf_trace_create(); + if (!tc1) { + diag("bt_ctf_trace_create returned NULL"); + goto error; + } + + sc1 = create_sc1(); + ok(sc1, "Create SC1"); + if (!sc1) { + goto error; + } + ret = bt_ctf_trace_add_stream_class(tc1, sc1); + ok(!ret, "Add SC1 to TC1"); + if (ret) { + goto error; + } + + sc2 = create_sc2(); + ok(sc2, "Create SC2"); + if (!sc2) { + goto error; + } + ret = bt_ctf_trace_add_stream_class(tc1, sc2); + ok(!ret, "Add SC2 to TC1"); + if (ret) { + goto error; + } +end: + BT_PUT(sc1); + BT_PUT(sc2); + return tc1; +error: + BT_PUT(tc1); + goto end; +} + +static void init_weak_refs(struct bt_ctf_trace *tc, + struct bt_ctf_trace **tc1, + struct bt_ctf_stream_class **sc1, + struct bt_ctf_stream_class **sc2, + struct bt_ctf_event_class **ec1, + struct bt_ctf_event_class **ec2, + struct bt_ctf_event_class **ec3) +{ + *tc1 = tc; + *sc1 = bt_ctf_trace_get_stream_class(tc, 0); + *sc2 = bt_ctf_trace_get_stream_class(tc, 1); + *ec1 = bt_ctf_stream_class_get_event_class(*sc1, 0); + *ec2 = bt_ctf_stream_class_get_event_class(*sc1, 1); + *ec3 = bt_ctf_stream_class_get_event_class(*sc2, 0); + bt_put(*sc1); + bt_put(*sc2); + bt_put(*ec1); + bt_put(*ec2); + bt_put(*ec3); +} + +/** + * The objective of this test is to implement and expand upon the scenario + * described in the reference counting documentation and ensure that any node of + * the Trace, Stream Class, Event Class, Stream and Event hiearchy keeps all + * other "alive" and reachable. + * + * External tools (e.g. valgrind) should be used to confirm that this + * known-good test does not leak memory. + */ +int main(int argc, char **argv) +{ + /** + * Weak pointers to CTF-IR objects are to be used very carefully. + * This is NOT a good practice and is strongly discouraged; this + * is only done to facilitate the validation of expected reference + * counts without affecting them by taking "real" references to the + * objects. + */ + struct bt_ctf_trace *tc1 = NULL, *weak_tc1 = NULL; + struct bt_ctf_stream_class *weak_sc1 = NULL, *weak_sc2 = NULL; + struct bt_ctf_event_class *weak_ec1 = NULL, *weak_ec2 = NULL, + *weak_ec3 = NULL; + struct user user_a = { 0 }, user_b = { 0 }, user_c = { 0 }; + + /* The only reference which exists at this point is on TC1. */ + tc1 = create_tc1(); + ok(tc1, "Initialize trace"); + if (!tc1) { + goto end; + } + + init_weak_refs(tc1, &weak_tc1, &weak_sc1, &weak_sc2, &weak_ec1, + &weak_ec2, &weak_ec3); + plan_no_plan(); + + ok(bt_object_get_ref_count(weak_sc1) == 0, + "Initial SC1 reference count is 0"); + ok(bt_object_get_ref_count(weak_sc2) == 0, + "Initial SC2 reference count is 0"); + ok(bt_object_get_ref_count(weak_ec1) == 0, + "Initial EC1 reference count is 0"); + ok(bt_object_get_ref_count(weak_ec2) == 0, + "Initial EC2 reference count is 0"); + ok(bt_object_get_ref_count(weak_ec3) == 0, + "Initial EC3 reference count is 0"); + + /* User A has ownership of the trace. */ + BT_MOVE(user_a.tc, tc1); + ok(bt_object_get_ref_count(user_a.tc) == 1, + "TC1 reference count is 1"); + + /* User A acquires a reference to SC2 from TC1. */ + user_a.sc = bt_ctf_trace_get_stream_class(user_a.tc, 1); + ok(user_a.sc, "User A acquires SC2 from TC1"); + ok(bt_object_get_ref_count(weak_tc1) == 2, + "TC1 reference count is 2"); + ok(bt_object_get_ref_count(weak_sc2) == 1, + "SC2 reference count is 1"); + + /* User A acquires a reference to EC3 from SC2. */ + user_a.ec = bt_ctf_stream_class_get_event_class(user_a.sc, 0); + ok(user_a.ec, "User A acquires EC3 from SC2"); + ok(bt_object_get_ref_count(weak_tc1) == 2, + "TC1 reference count is 2"); + ok(bt_object_get_ref_count(weak_sc2) == 2, + "SC2 reference count is 2"); + ok(bt_object_get_ref_count(weak_ec3) == 1, + "EC3 reference count is 1"); + + /* User A releases its reference to SC2. */ + diag("User A releases SC2"); + BT_PUT(user_a.sc); + /* + * We keep the pointer to SC2 around to validate its reference + * count. + */ + ok(bt_object_get_ref_count(weak_tc1) == 2, + "TC1 reference count is 2"); + ok(bt_object_get_ref_count(weak_sc2) == 1, + "SC2 reference count is 1"); + ok(bt_object_get_ref_count(weak_ec3) == 1, + "EC3 reference count is 1"); + + /* User A releases its reference to TC1. */ + diag("User A releases TC1"); + BT_PUT(user_a.tc); + /* + * We keep the pointer to TC1 around to validate its reference + * count. + */ + ok(bt_object_get_ref_count(weak_tc1) == 1, + "TC1 reference count is 1"); + ok(bt_object_get_ref_count(weak_sc2) == 1, + "SC2 reference count is 1"); + ok(bt_object_get_ref_count(weak_ec3) == 1, + "EC3 reference count is 1"); + + /* User B acquires a reference to SC1. */ + diag("User B acquires a reference to SC1"); + user_b.sc = bt_get(weak_sc1); + ok(bt_object_get_ref_count(weak_tc1) == 2, + "TC1 reference count is 2"); + ok(bt_object_get_ref_count(weak_sc1) == 1, + "SC1 reference count is 1"); + + /* User C acquires a reference to EC1. */ + diag("User C acquires a reference to EC1"); + user_c.ec = bt_ctf_stream_class_get_event_class(user_b.sc, 0); + ok(bt_object_get_ref_count(weak_ec1) == 1, + "EC1 reference count is 1"); + ok(bt_object_get_ref_count(weak_sc1) == 2, + "SC1 reference count is 2"); + + /* User A releases its reference on EC3. */ + diag("User A releases its reference on EC3"); + BT_PUT(user_a.ec); + ok(bt_object_get_ref_count(weak_ec3) == 0, + "EC3 reference count is 1"); + ok(bt_object_get_ref_count(weak_sc2) == 0, + "SC2 reference count is 0"); + ok(bt_object_get_ref_count(weak_tc1) == 1, + "TC1 reference count is 1"); + + /* User B releases its reference on SC1. */ + diag("User B releases its reference on SC1"); + BT_PUT(user_b.sc); + ok(bt_object_get_ref_count(weak_sc1) == 1, + "SC1 reference count is 1"); + + /* + * User C is the sole owner of an object and is keeping the whole + * trace hierarchy "alive" by holding a reference to EC1. + */ + ok(bt_object_get_ref_count(weak_tc1) == 1, + "TC1 reference count is 1"); + ok(bt_object_get_ref_count(weak_sc1) == 1, + "SC1 reference count is 1"); + ok(bt_object_get_ref_count(weak_sc2) == 0, + "SC2 reference count is 0"); + ok(bt_object_get_ref_count(weak_ec1) == 1, + "EC1 reference count is 1"); + ok(bt_object_get_ref_count(weak_ec2) == 0, + "EC2 reference count is 0"); + ok(bt_object_get_ref_count(weak_ec3) == 0, + "EC3 reference count is 0"); + + /* Reclaim last reference held by User C. */ + BT_PUT(user_c.ec); +end: + return exit_status(); +} diff --git a/tests/tests b/tests/tests index 7d6ba4d4..7b4f8d36 100644 --- a/tests/tests +++ b/tests/tests @@ -4,3 +4,4 @@ lib/test_seek_empty_packet lib/test_seek_big_trace lib/test_ctf_writer_complete lib/test_bt_values +lib/test_ctf_ir_ref -- 2.34.1