Tests: Add CTF-IR reference counting test
[babeltrace.git] / tests / lib / test_ctf_ir_ref.c
1 /*
2 * test_ctf_ir_ref.c
3 *
4 * CTF IR Reference Count test
5 *
6 * Copyright 2016 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; under version 2 of the License.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22 #include "tap/tap.h"
23 #include <babeltrace/ctf-ir/trace.h>
24 #include <babeltrace/ctf-ir/stream-class.h>
25 #include <babeltrace/ctf-ir/stream.h>
26 #include <babeltrace/ctf-ir/event.h>
27 #include <babeltrace/object-internal.h>
28 #include <assert.h>
29
30 struct user {
31 struct bt_ctf_trace *tc;
32 struct bt_ctf_stream_class *sc;
33 struct bt_ctf_event_class *ec;
34 };
35
36 /**
37 * Returns a structure containing the following fields:
38 * - uint8_t payload_8;
39 * - uint16_t payload_16;
40 * - uint32_t payload_32;
41 */
42 static struct bt_ctf_field_type *create_integer_struct(void)
43 {
44 int ret;
45 struct bt_ctf_field_type *structure = NULL;
46 struct bt_ctf_field_type *ui8 = NULL, *ui16 = NULL, *ui32 = NULL;
47
48 structure = bt_ctf_field_type_structure_create();
49 if (!structure) {
50 goto error;
51 }
52
53 ui8 = bt_ctf_field_type_integer_create(8);
54 if (!ui8) {
55 diag("Failed to create uint8_t type");
56 goto error;
57 }
58 ret = bt_ctf_field_type_structure_add_field(structure, ui8,
59 "payload_8");
60 if (ret) {
61 diag("Failed to add uint8_t to structure");
62 goto error;
63 }
64 ui16 = bt_ctf_field_type_integer_create(16);
65 if (!ui16) {
66 diag("Failed to create uint16_t type");
67 goto error;
68 }
69 ret = bt_ctf_field_type_structure_add_field(structure, ui16,
70 "payload_16");
71 if (ret) {
72 diag("Failed to add uint16_t to structure");
73 goto error;
74 }
75 ui32 = bt_ctf_field_type_integer_create(32);
76 if (!ui32) {
77 diag("Failed to create uint32_t type");
78 goto error;
79 }
80 ret = bt_ctf_field_type_structure_add_field(structure, ui32,
81 "payload_32");
82 if (ret) {
83 diag("Failed to add uint32_t to structure");
84 goto error;
85 }
86 end:
87 BT_PUT(ui8);
88 BT_PUT(ui16);
89 BT_PUT(ui32);
90 return structure;
91 error:
92 BT_PUT(structure);
93 goto end;
94 }
95
96 /**
97 * A simple event has the following payload:
98 * - uint8_t payload_8;
99 * - uint16_t payload_16;
100 * - uint32_t payload_32;
101 */
102 static struct bt_ctf_event_class *create_simple_event(const char *name)
103 {
104 int ret;
105 struct bt_ctf_event_class *event = NULL;
106 struct bt_ctf_field_type *payload = NULL;
107
108 assert(name);
109 event = bt_ctf_event_class_create(name);
110 if (!event) {
111 diag("Failed to create simple event");
112 goto error;
113 }
114
115 payload = create_integer_struct();
116 if (!payload) {
117 diag("Failed to initialize integer structure");
118 goto error;
119 }
120
121 ret = bt_ctf_event_class_set_payload_type(event, payload);
122 if (ret) {
123 diag("Failed to set simple event payload");
124 goto error;
125 }
126 end:
127 BT_PUT(payload);
128 return event;
129 error:
130 BT_PUT(event);
131 goto end;;
132 }
133
134 /**
135 * A complex event has the following payload:
136 * - uint8_t payload_8;
137 * - uint16_t payload_16;
138 * - uint32_t payload_32;
139 * - struct payload_struct:
140 * - uint8_t payload_8;
141 * - uint16_t payload_16;
142 * - uint32_t payload_32;
143 */
144 static struct bt_ctf_event_class *create_complex_event(const char *name)
145 {
146 int ret;
147 struct bt_ctf_event_class *event = NULL;
148 struct bt_ctf_field_type *inner = NULL, *outer = NULL;
149
150 assert(name);
151 event = bt_ctf_event_class_create(name);
152 if (!event) {
153 diag("Failed to create complex event");
154 goto error;
155 }
156
157 outer = create_integer_struct();
158 if (!outer) {
159 diag("Failed to initialize integer structure");
160 goto error;
161 }
162
163 inner = create_integer_struct();
164 if (!inner) {
165 diag("Failed to initialize integer structure");
166 goto error;
167 }
168
169 ret = bt_ctf_field_type_structure_add_field(outer, inner,
170 "payload_struct");
171 if (ret) {
172 diag("Failed to add inner structure to outer structure");
173 goto error;
174 }
175
176 ret = bt_ctf_event_class_set_payload_type(event, outer);
177 if (ret) {
178 diag("Failed to set complex event payload");
179 goto error;
180 }
181 end:
182 BT_PUT(inner);
183 BT_PUT(outer);
184 return event;
185 error:
186 BT_PUT(event);
187 goto end;;
188 }
189
190 static struct bt_ctf_stream_class *create_sc1(void)
191 {
192 int ret;
193 struct bt_ctf_event_class *ec1 = NULL, *ec2 = NULL;
194 struct bt_ctf_stream_class *sc1 = NULL, *ret_stream = NULL;
195
196 sc1 = bt_ctf_stream_class_create("sc1");
197 if (!sc1) {
198 diag("Failed to create Stream Class");
199 goto error;
200 }
201
202 ec1 = create_complex_event("ec1");
203 if (!ec1) {
204 diag("Failed to create complex event EC1");
205 goto error;
206 }
207 ret = bt_ctf_stream_class_add_event_class(sc1, ec1);
208 if (ret) {
209 diag("Failed to add EC1 to SC1");
210 goto error;
211 }
212
213 ec2 = create_simple_event("ec2");
214 if (!ec2) {
215 diag("Failed to create simple event EC2");
216 goto error;
217 }
218 ret = bt_ctf_stream_class_add_event_class(sc1, ec2);
219 if (ret) {
220 diag("Failed to add EC1 to SC1");
221 goto error;
222 }
223
224 ret_stream = bt_ctf_event_class_get_stream_class(ec1);
225 ok(ret_stream == sc1, "Get parent stream SC1 from EC1");
226 BT_PUT(ret_stream);
227
228 ret_stream = bt_ctf_event_class_get_stream_class(ec2);
229 ok(ret_stream == sc1, "Get parent stream SC1 from EC2");
230 end:
231 BT_PUT(ret_stream);
232 BT_PUT(ec1);
233 BT_PUT(ec2);
234 return sc1;
235 error:
236 BT_PUT(sc1);
237 goto end;
238 }
239
240 static struct bt_ctf_stream_class *create_sc2(void)
241 {
242 int ret;
243 struct bt_ctf_event_class *ec3 = NULL;
244 struct bt_ctf_stream_class *sc2 = NULL, *ret_stream = NULL;
245
246 sc2 = bt_ctf_stream_class_create("sc2");
247 if (!sc2) {
248 diag("Failed to create Stream Class");
249 goto error;
250 }
251
252 ec3 = create_simple_event("ec3");
253 if (!ec3) {
254 diag("Failed to create simple event EC3");
255 goto error;
256 }
257 ret = bt_ctf_stream_class_add_event_class(sc2, ec3);
258 if (ret) {
259 diag("Failed to add EC3 to SC2");
260 goto error;
261 }
262
263 ret_stream = bt_ctf_event_class_get_stream_class(ec3);
264 ok(ret_stream == sc2, "Get parent stream SC2 from EC3");
265 end:
266 BT_PUT(ret_stream);
267 BT_PUT(ec3);
268 return sc2;
269 error:
270 BT_PUT(sc2);
271 goto end;
272 }
273
274 static struct bt_ctf_trace *create_tc1(void)
275 {
276 int ret;
277 struct bt_ctf_trace *tc1 = NULL;
278 struct bt_ctf_stream_class *sc1 = NULL, *sc2 = NULL;
279
280 tc1 = bt_ctf_trace_create();
281 if (!tc1) {
282 diag("bt_ctf_trace_create returned NULL");
283 goto error;
284 }
285
286 sc1 = create_sc1();
287 ok(sc1, "Create SC1");
288 if (!sc1) {
289 goto error;
290 }
291 ret = bt_ctf_trace_add_stream_class(tc1, sc1);
292 ok(!ret, "Add SC1 to TC1");
293 if (ret) {
294 goto error;
295 }
296
297 sc2 = create_sc2();
298 ok(sc2, "Create SC2");
299 if (!sc2) {
300 goto error;
301 }
302 ret = bt_ctf_trace_add_stream_class(tc1, sc2);
303 ok(!ret, "Add SC2 to TC1");
304 if (ret) {
305 goto error;
306 }
307 end:
308 BT_PUT(sc1);
309 BT_PUT(sc2);
310 return tc1;
311 error:
312 BT_PUT(tc1);
313 goto end;
314 }
315
316 static void init_weak_refs(struct bt_ctf_trace *tc,
317 struct bt_ctf_trace **tc1,
318 struct bt_ctf_stream_class **sc1,
319 struct bt_ctf_stream_class **sc2,
320 struct bt_ctf_event_class **ec1,
321 struct bt_ctf_event_class **ec2,
322 struct bt_ctf_event_class **ec3)
323 {
324 *tc1 = tc;
325 *sc1 = bt_ctf_trace_get_stream_class(tc, 0);
326 *sc2 = bt_ctf_trace_get_stream_class(tc, 1);
327 *ec1 = bt_ctf_stream_class_get_event_class(*sc1, 0);
328 *ec2 = bt_ctf_stream_class_get_event_class(*sc1, 1);
329 *ec3 = bt_ctf_stream_class_get_event_class(*sc2, 0);
330 bt_put(*sc1);
331 bt_put(*sc2);
332 bt_put(*ec1);
333 bt_put(*ec2);
334 bt_put(*ec3);
335 }
336
337 /**
338 * The objective of this test is to implement and expand upon the scenario
339 * described in the reference counting documentation and ensure that any node of
340 * the Trace, Stream Class, Event Class, Stream and Event hiearchy keeps all
341 * other "alive" and reachable.
342 *
343 * External tools (e.g. valgrind) should be used to confirm that this
344 * known-good test does not leak memory.
345 */
346 int main(int argc, char **argv)
347 {
348 /**
349 * Weak pointers to CTF-IR objects are to be used very carefully.
350 * This is NOT a good practice and is strongly discouraged; this
351 * is only done to facilitate the validation of expected reference
352 * counts without affecting them by taking "real" references to the
353 * objects.
354 */
355 struct bt_ctf_trace *tc1 = NULL, *weak_tc1 = NULL;
356 struct bt_ctf_stream_class *weak_sc1 = NULL, *weak_sc2 = NULL;
357 struct bt_ctf_event_class *weak_ec1 = NULL, *weak_ec2 = NULL,
358 *weak_ec3 = NULL;
359 struct user user_a = { 0 }, user_b = { 0 }, user_c = { 0 };
360
361 /* The only reference which exists at this point is on TC1. */
362 tc1 = create_tc1();
363 ok(tc1, "Initialize trace");
364 if (!tc1) {
365 goto end;
366 }
367
368 init_weak_refs(tc1, &weak_tc1, &weak_sc1, &weak_sc2, &weak_ec1,
369 &weak_ec2, &weak_ec3);
370 plan_no_plan();
371
372 ok(bt_object_get_ref_count(weak_sc1) == 0,
373 "Initial SC1 reference count is 0");
374 ok(bt_object_get_ref_count(weak_sc2) == 0,
375 "Initial SC2 reference count is 0");
376 ok(bt_object_get_ref_count(weak_ec1) == 0,
377 "Initial EC1 reference count is 0");
378 ok(bt_object_get_ref_count(weak_ec2) == 0,
379 "Initial EC2 reference count is 0");
380 ok(bt_object_get_ref_count(weak_ec3) == 0,
381 "Initial EC3 reference count is 0");
382
383 /* User A has ownership of the trace. */
384 BT_MOVE(user_a.tc, tc1);
385 ok(bt_object_get_ref_count(user_a.tc) == 1,
386 "TC1 reference count is 1");
387
388 /* User A acquires a reference to SC2 from TC1. */
389 user_a.sc = bt_ctf_trace_get_stream_class(user_a.tc, 1);
390 ok(user_a.sc, "User A acquires SC2 from TC1");
391 ok(bt_object_get_ref_count(weak_tc1) == 2,
392 "TC1 reference count is 2");
393 ok(bt_object_get_ref_count(weak_sc2) == 1,
394 "SC2 reference count is 1");
395
396 /* User A acquires a reference to EC3 from SC2. */
397 user_a.ec = bt_ctf_stream_class_get_event_class(user_a.sc, 0);
398 ok(user_a.ec, "User A acquires EC3 from SC2");
399 ok(bt_object_get_ref_count(weak_tc1) == 2,
400 "TC1 reference count is 2");
401 ok(bt_object_get_ref_count(weak_sc2) == 2,
402 "SC2 reference count is 2");
403 ok(bt_object_get_ref_count(weak_ec3) == 1,
404 "EC3 reference count is 1");
405
406 /* User A releases its reference to SC2. */
407 diag("User A releases SC2");
408 BT_PUT(user_a.sc);
409 /*
410 * We keep the pointer to SC2 around to validate its reference
411 * count.
412 */
413 ok(bt_object_get_ref_count(weak_tc1) == 2,
414 "TC1 reference count is 2");
415 ok(bt_object_get_ref_count(weak_sc2) == 1,
416 "SC2 reference count is 1");
417 ok(bt_object_get_ref_count(weak_ec3) == 1,
418 "EC3 reference count is 1");
419
420 /* User A releases its reference to TC1. */
421 diag("User A releases TC1");
422 BT_PUT(user_a.tc);
423 /*
424 * We keep the pointer to TC1 around to validate its reference
425 * count.
426 */
427 ok(bt_object_get_ref_count(weak_tc1) == 1,
428 "TC1 reference count is 1");
429 ok(bt_object_get_ref_count(weak_sc2) == 1,
430 "SC2 reference count is 1");
431 ok(bt_object_get_ref_count(weak_ec3) == 1,
432 "EC3 reference count is 1");
433
434 /* User B acquires a reference to SC1. */
435 diag("User B acquires a reference to SC1");
436 user_b.sc = bt_get(weak_sc1);
437 ok(bt_object_get_ref_count(weak_tc1) == 2,
438 "TC1 reference count is 2");
439 ok(bt_object_get_ref_count(weak_sc1) == 1,
440 "SC1 reference count is 1");
441
442 /* User C acquires a reference to EC1. */
443 diag("User C acquires a reference to EC1");
444 user_c.ec = bt_ctf_stream_class_get_event_class(user_b.sc, 0);
445 ok(bt_object_get_ref_count(weak_ec1) == 1,
446 "EC1 reference count is 1");
447 ok(bt_object_get_ref_count(weak_sc1) == 2,
448 "SC1 reference count is 2");
449
450 /* User A releases its reference on EC3. */
451 diag("User A releases its reference on EC3");
452 BT_PUT(user_a.ec);
453 ok(bt_object_get_ref_count(weak_ec3) == 0,
454 "EC3 reference count is 1");
455 ok(bt_object_get_ref_count(weak_sc2) == 0,
456 "SC2 reference count is 0");
457 ok(bt_object_get_ref_count(weak_tc1) == 1,
458 "TC1 reference count is 1");
459
460 /* User B releases its reference on SC1. */
461 diag("User B releases its reference on SC1");
462 BT_PUT(user_b.sc);
463 ok(bt_object_get_ref_count(weak_sc1) == 1,
464 "SC1 reference count is 1");
465
466 /*
467 * User C is the sole owner of an object and is keeping the whole
468 * trace hierarchy "alive" by holding a reference to EC1.
469 */
470 ok(bt_object_get_ref_count(weak_tc1) == 1,
471 "TC1 reference count is 1");
472 ok(bt_object_get_ref_count(weak_sc1) == 1,
473 "SC1 reference count is 1");
474 ok(bt_object_get_ref_count(weak_sc2) == 0,
475 "SC2 reference count is 0");
476 ok(bt_object_get_ref_count(weak_ec1) == 1,
477 "EC1 reference count is 1");
478 ok(bt_object_get_ref_count(weak_ec2) == 0,
479 "EC2 reference count is 0");
480 ok(bt_object_get_ref_count(weak_ec3) == 0,
481 "EC3 reference count is 0");
482
483 /* Reclaim last reference held by User C. */
484 BT_PUT(user_c.ec);
485 end:
486 return exit_status();
487 }
This page took 0.039937 seconds and 4 git commands to generate.