Commit | Line | Data |
---|---|---|
11b0cdc8 | 1 | /* |
3f043b05 | 2 | * stream-class.c |
11b0cdc8 | 3 | * |
d2dc44b6 | 4 | * Babeltrace CTF IR - Stream Class |
11b0cdc8 | 5 | * |
de9dd397 | 6 | * Copyright 2013, 2014 Jérémie Galarneau <jeremie.galarneau@efficios.com> |
11b0cdc8 JG |
7 | * |
8 | * Author: Jérémie Galarneau <jeremie.galarneau@efficios.com> | |
9 | * | |
10 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
11 | * of this software and associated documentation files (the "Software"), to deal | |
12 | * in the Software without restriction, including without limitation the rights | |
13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
14 | * copies of the Software, and to permit persons to whom the Software is | |
15 | * furnished to do so, subject to the following conditions: | |
16 | * | |
17 | * The above copyright notice and this permission notice shall be included in | |
18 | * all copies or substantial portions of the Software. | |
19 | * | |
20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
26 | * SOFTWARE. | |
27 | */ | |
28 | ||
d2f71f12 PP |
29 | #define BT_LOG_TAG "STREAM-CLASS" |
30 | #include <babeltrace/lib-logging-internal.h> | |
31 | ||
11b0cdc8 | 32 | #include <babeltrace/ctf-writer/clock.h> |
ac0c6bdd PP |
33 | #include <babeltrace/ctf-writer/clock-internal.h> |
34 | #include <babeltrace/ctf-ir/clock-class-internal.h> | |
11b0cdc8 | 35 | #include <babeltrace/ctf-writer/event.h> |
272df73e | 36 | #include <babeltrace/ctf-ir/event-class-internal.h> |
11b0cdc8 | 37 | #include <babeltrace/ctf-ir/event-internal.h> |
2e33ac5a PP |
38 | #include <babeltrace/ctf-ir/field-types-internal.h> |
39 | #include <babeltrace/ctf-ir/fields-internal.h> | |
11b0cdc8 JG |
40 | #include <babeltrace/ctf-writer/stream.h> |
41 | #include <babeltrace/ctf-ir/stream-class-internal.h> | |
09840de5 | 42 | #include <babeltrace/ctf-ir/validation-internal.h> |
8bf65fbd | 43 | #include <babeltrace/ctf-ir/visitor-internal.h> |
11b0cdc8 | 44 | #include <babeltrace/ctf-writer/functor-internal.h> |
654c1444 | 45 | #include <babeltrace/ctf-ir/utils.h> |
2a3ced3c | 46 | #include <babeltrace/ctf-ir/utils-internal.h> |
83509119 | 47 | #include <babeltrace/ref.h> |
3d9990ac PP |
48 | #include <babeltrace/compiler-internal.h> |
49 | #include <babeltrace/align-internal.h> | |
50 | #include <babeltrace/endian-internal.h> | |
dc3fffef | 51 | #include <inttypes.h> |
544d0515 | 52 | #include <stdint.h> |
e011d2c1 | 53 | #include <stdbool.h> |
11b0cdc8 JG |
54 | |
55 | static | |
50842bdc | 56 | void bt_stream_class_destroy(struct bt_object *obj); |
11b0cdc8 | 57 | static |
50842bdc | 58 | int init_event_header(struct bt_stream_class *stream_class); |
11b0cdc8 | 59 | static |
50842bdc | 60 | int init_packet_context(struct bt_stream_class *stream_class); |
11b0cdc8 | 61 | |
50842bdc | 62 | struct bt_stream_class *bt_stream_class_create(const char *name) |
11b0cdc8 | 63 | { |
50842bdc | 64 | struct bt_stream_class *stream_class; |
12c8a1a3 | 65 | int ret; |
e0e2946b | 66 | |
d2f71f12 | 67 | BT_LOGD("Creating default stream class object: name=\"%s\"", name); |
50842bdc | 68 | stream_class = bt_stream_class_create_empty(name); |
e0e2946b | 69 | if (!stream_class) { |
d2f71f12 | 70 | BT_LOGD_STR("Cannot create empty stream class."); |
e0e2946b PP |
71 | goto error; |
72 | } | |
73 | ||
74 | ret = init_event_header(stream_class); | |
75 | if (ret) { | |
d2f71f12 | 76 | BT_LOGE_STR("Cannot initialize stream class's event header field type."); |
e0e2946b PP |
77 | goto error; |
78 | } | |
79 | ||
80 | ret = init_packet_context(stream_class); | |
81 | if (ret) { | |
d2f71f12 | 82 | BT_LOGE_STR("Cannot initialize stream class's packet context field type."); |
e0e2946b PP |
83 | goto error; |
84 | } | |
85 | ||
d2f71f12 PP |
86 | BT_LOGD("Created default stream class object: addr=%p, name=\"%s\"", |
87 | stream_class, name); | |
e0e2946b PP |
88 | return stream_class; |
89 | ||
90 | error: | |
91 | BT_PUT(stream_class); | |
92 | return stream_class; | |
93 | } | |
94 | ||
50842bdc | 95 | struct bt_stream_class *bt_stream_class_create_empty(const char *name) |
e0e2946b | 96 | { |
50842bdc | 97 | struct bt_stream_class *stream_class = NULL; |
11b0cdc8 | 98 | |
d2f71f12 PP |
99 | BT_LOGD("Creating empty stream class object: name=\"%s\"", name); |
100 | ||
50842bdc | 101 | stream_class = g_new0(struct bt_stream_class, 1); |
11b0cdc8 | 102 | if (!stream_class) { |
d2f71f12 | 103 | BT_LOGE_STR("Failed to allocate one stream class."); |
11b0cdc8 JG |
104 | goto error; |
105 | } | |
106 | ||
107 | stream_class->name = g_string_new(name); | |
108 | stream_class->event_classes = g_ptr_array_new_with_free_func( | |
e6a8e8e4 | 109 | (GDestroyNotify) bt_object_release); |
11b0cdc8 | 110 | if (!stream_class->event_classes) { |
d2f71f12 | 111 | BT_LOGE_STR("Failed to allocate a GPtrArray."); |
83509119 | 112 | goto error; |
11b0cdc8 JG |
113 | } |
114 | ||
0b9ce69f JG |
115 | stream_class->event_classes_ht = g_hash_table_new_full(g_int64_hash, |
116 | g_int64_equal, g_free, NULL); | |
d2f71f12 PP |
117 | if (!stream_class->event_classes_ht) { |
118 | BT_LOGE_STR("Failed to allocate a GHashTable."); | |
119 | goto error; | |
120 | } | |
0b9ce69f | 121 | |
50842bdc | 122 | bt_object_init(stream_class, bt_stream_class_destroy); |
d2f71f12 PP |
123 | BT_LOGD("Created empty stream class object: addr=%p, name=\"%s\"", |
124 | stream_class, name); | |
11b0cdc8 JG |
125 | return stream_class; |
126 | ||
11b0cdc8 | 127 | error: |
e0e2946b | 128 | BT_PUT(stream_class); |
11b0cdc8 JG |
129 | return stream_class; |
130 | } | |
131 | ||
50842bdc PP |
132 | struct bt_trace *bt_stream_class_get_trace( |
133 | struct bt_stream_class *stream_class) | |
142c5610 | 134 | { |
dc3fffef | 135 | return stream_class ? |
50842bdc | 136 | bt_get(bt_stream_class_borrow_trace(stream_class)) : |
dc3fffef | 137 | NULL; |
142c5610 JG |
138 | } |
139 | ||
50842bdc PP |
140 | const char *bt_stream_class_get_name( |
141 | struct bt_stream_class *stream_class) | |
69dc4535 JG |
142 | { |
143 | const char *name = NULL; | |
144 | ||
145 | if (!stream_class) { | |
d2f71f12 | 146 | BT_LOGW_STR("Invalid parameter: stream class is NULL."); |
69dc4535 JG |
147 | goto end; |
148 | } | |
149 | ||
03be3bcd | 150 | name = stream_class->name->len > 0 ? stream_class->name->str : NULL; |
69dc4535 JG |
151 | end: |
152 | return name; | |
153 | } | |
154 | ||
50842bdc | 155 | int bt_stream_class_set_name(struct bt_stream_class *stream_class, |
3ea33115 JG |
156 | const char *name) |
157 | { | |
158 | int ret = 0; | |
159 | ||
d2f71f12 PP |
160 | if (!stream_class) { |
161 | BT_LOGW_STR("Invalid parameter: stream class is NULL."); | |
162 | ret = -1; | |
163 | goto end; | |
164 | } | |
165 | ||
166 | if (stream_class->frozen) { | |
167 | BT_LOGW("Invalid parameter: stream class is frozen: " | |
168 | "addr=%p, name=\"%s\", id=%" PRId64, | |
50842bdc PP |
169 | stream_class, bt_stream_class_get_name(stream_class), |
170 | bt_stream_class_get_id(stream_class)); | |
3ea33115 JG |
171 | ret = -1; |
172 | goto end; | |
173 | } | |
174 | ||
03be3bcd PP |
175 | if (!name) { |
176 | g_string_assign(stream_class->name, ""); | |
177 | } else { | |
178 | if (strlen(name) == 0) { | |
179 | BT_LOGW("Invalid parameter: name is empty."); | |
180 | ret = -1; | |
181 | goto end; | |
182 | } | |
183 | ||
184 | g_string_assign(stream_class->name, name); | |
185 | } | |
186 | ||
d2f71f12 PP |
187 | BT_LOGV("Set stream class's name: " |
188 | "addr=%p, name=\"%s\", id=%" PRId64, | |
50842bdc PP |
189 | stream_class, bt_stream_class_get_name(stream_class), |
190 | bt_stream_class_get_id(stream_class)); | |
3ea33115 JG |
191 | end: |
192 | return ret; | |
193 | } | |
194 | ||
50842bdc PP |
195 | struct bt_ctf_clock *bt_stream_class_get_clock( |
196 | struct bt_stream_class *stream_class) | |
2f100782 JG |
197 | { |
198 | struct bt_ctf_clock *clock = NULL; | |
199 | ||
d2f71f12 PP |
200 | if (!stream_class) { |
201 | BT_LOGW_STR("Invalid parameter: stream class is NULL."); | |
202 | goto end; | |
203 | } | |
204 | ||
205 | if (!stream_class->clock) { | |
206 | BT_LOGV("Stream class has no clock: " | |
207 | "addr=%p, name=\"%s\", id=%" PRId64, | |
50842bdc PP |
208 | stream_class, bt_stream_class_get_name(stream_class), |
209 | bt_stream_class_get_id(stream_class)); | |
2f100782 JG |
210 | goto end; |
211 | } | |
212 | ||
ac0c6bdd | 213 | clock = bt_get(stream_class->clock); |
2f100782 JG |
214 | end: |
215 | return clock; | |
216 | } | |
217 | ||
50842bdc | 218 | int bt_stream_class_set_clock(struct bt_stream_class *stream_class, |
11b0cdc8 JG |
219 | struct bt_ctf_clock *clock) |
220 | { | |
221 | int ret = 0; | |
50842bdc | 222 | struct bt_field_type *timestamp_field = NULL; |
11b0cdc8 | 223 | |
d2f71f12 PP |
224 | if (!stream_class || !clock) { |
225 | BT_LOGW("Invalid parameter: stream class or clock is NULL: " | |
226 | "stream-class-addr=%p, clock-addr=%p", | |
227 | stream_class, clock); | |
228 | ret = -1; | |
229 | goto end; | |
230 | } | |
231 | ||
232 | if (stream_class->frozen) { | |
233 | BT_LOGW("Invalid parameter: stream class is frozen: " | |
234 | "addr=%p, name=\"%s\", id=%" PRId64, | |
50842bdc PP |
235 | stream_class, bt_stream_class_get_name(stream_class), |
236 | bt_stream_class_get_id(stream_class)); | |
11b0cdc8 JG |
237 | ret = -1; |
238 | goto end; | |
239 | } | |
240 | ||
ac0c6bdd PP |
241 | /* Replace the current clock of this stream class. */ |
242 | bt_put(stream_class->clock); | |
243 | stream_class->clock = bt_get(clock); | |
d2f71f12 PP |
244 | BT_LOGV("Set stream class's clock: " |
245 | "addr=%p, name=\"%s\", id=%" PRId64 ", " | |
246 | "clock-addr=%p, clock-name=\"%s\"", | |
50842bdc PP |
247 | stream_class, bt_stream_class_get_name(stream_class), |
248 | bt_stream_class_get_id(stream_class), | |
d2f71f12 PP |
249 | stream_class->clock, |
250 | bt_ctf_clock_get_name(stream_class->clock)); | |
11b0cdc8 | 251 | |
11b0cdc8 | 252 | end: |
ac0c6bdd | 253 | bt_put(timestamp_field); |
11b0cdc8 JG |
254 | return ret; |
255 | } | |
256 | ||
50842bdc | 257 | int64_t bt_stream_class_get_id(struct bt_stream_class *stream_class) |
2f100782 JG |
258 | { |
259 | int64_t ret; | |
260 | ||
d2f71f12 PP |
261 | if (!stream_class) { |
262 | BT_LOGW_STR("Invalid parameter: stream class is NULL."); | |
263 | ret = (int64_t) -1; | |
264 | goto end; | |
265 | } | |
266 | ||
267 | if (!stream_class->id_set) { | |
268 | BT_LOGV("Stream class's ID is not set: addr=%p, name=\"%s\"", | |
269 | stream_class, | |
50842bdc | 270 | bt_stream_class_get_name(stream_class)); |
9ac68eb1 | 271 | ret = (int64_t) -1; |
2f100782 JG |
272 | goto end; |
273 | } | |
274 | ||
9ac68eb1 | 275 | ret = stream_class->id; |
2f100782 JG |
276 | end: |
277 | return ret; | |
278 | } | |
279 | ||
5ca83563 | 280 | BT_HIDDEN |
50842bdc PP |
281 | void _bt_stream_class_set_id( |
282 | struct bt_stream_class *stream_class, int64_t id) | |
5ca83563 | 283 | { |
d2f71f12 | 284 | assert(stream_class); |
5ca83563 JG |
285 | stream_class->id = id; |
286 | stream_class->id_set = 1; | |
d2f71f12 PP |
287 | BT_LOGV("Set stream class's ID (internal): " |
288 | "addr=%p, name=\"%s\", id=%" PRId64, | |
50842bdc PP |
289 | stream_class, bt_stream_class_get_name(stream_class), |
290 | bt_stream_class_get_id(stream_class)); | |
5ca83563 JG |
291 | } |
292 | ||
9ac68eb1 PP |
293 | struct event_class_set_stream_class_id_data { |
294 | int64_t stream_class_id; | |
29664b2a PP |
295 | int ret; |
296 | }; | |
297 | ||
29664b2a | 298 | BT_HIDDEN |
50842bdc PP |
299 | int bt_stream_class_set_id_no_check( |
300 | struct bt_stream_class *stream_class, int64_t id) | |
2f100782 | 301 | { |
50842bdc | 302 | _bt_stream_class_set_id(stream_class, id); |
cf76ce92 | 303 | return 0; |
2f100782 JG |
304 | } |
305 | ||
50842bdc | 306 | int bt_stream_class_set_id(struct bt_stream_class *stream_class, |
9ac68eb1 | 307 | uint64_t id_param) |
29664b2a PP |
308 | { |
309 | int ret = 0; | |
9ac68eb1 | 310 | int64_t id = (int64_t) id_param; |
29664b2a | 311 | |
d2f71f12 PP |
312 | if (!stream_class) { |
313 | BT_LOGW_STR("Invalid parameter: stream class is NULL."); | |
314 | ret = -1; | |
315 | goto end; | |
316 | } | |
317 | ||
318 | if (stream_class->frozen) { | |
319 | BT_LOGW("Invalid parameter: stream class is frozen: " | |
320 | "addr=%p, name=\"%s\", id=%" PRId64, | |
50842bdc PP |
321 | stream_class, bt_stream_class_get_name(stream_class), |
322 | bt_stream_class_get_id(stream_class)); | |
d2f71f12 PP |
323 | ret = -1; |
324 | goto end; | |
325 | } | |
326 | ||
327 | if (id < 0) { | |
328 | BT_LOGW("Invalid parameter: invalid stream class's ID: " | |
329 | "stream-class-addr=%p, stream-class-name=\"%s\", " | |
330 | "stream-class-id=%" PRId64 ", id=%" PRIu64, | |
50842bdc PP |
331 | stream_class, bt_stream_class_get_name(stream_class), |
332 | bt_stream_class_get_id(stream_class), | |
d2f71f12 | 333 | id_param); |
29664b2a PP |
334 | ret = -1; |
335 | goto end; | |
336 | } | |
337 | ||
50842bdc | 338 | ret = bt_stream_class_set_id_no_check(stream_class, id); |
d2f71f12 PP |
339 | if (ret == 0) { |
340 | BT_LOGV("Set stream class's ID: " | |
341 | "addr=%p, name=\"%s\", id=%" PRId64, | |
50842bdc PP |
342 | stream_class, bt_stream_class_get_name(stream_class), |
343 | bt_stream_class_get_id(stream_class)); | |
d2f71f12 | 344 | } |
29664b2a PP |
345 | end: |
346 | return ret; | |
347 | } | |
348 | ||
0d23acbe PP |
349 | static |
350 | void event_class_exists(gpointer element, gpointer query) | |
351 | { | |
50842bdc | 352 | struct bt_event_class *event_class_a = element; |
0d23acbe | 353 | struct search_query *search_query = query; |
50842bdc | 354 | struct bt_event_class *event_class_b = search_query->value; |
0d23acbe PP |
355 | int64_t id_a, id_b; |
356 | ||
357 | if (search_query->value == element) { | |
358 | search_query->found = 1; | |
359 | goto end; | |
360 | } | |
361 | ||
0d23acbe PP |
362 | /* |
363 | * Two event classes cannot share the same ID in a given | |
364 | * stream class. | |
365 | */ | |
50842bdc PP |
366 | id_a = bt_event_class_get_id(event_class_a); |
367 | id_b = bt_event_class_get_id(event_class_b); | |
0d23acbe PP |
368 | |
369 | if (id_a < 0 || id_b < 0) { | |
370 | /* at least one ID is not set: will be automatically set later */ | |
371 | goto end; | |
372 | } | |
373 | ||
374 | if (id_a == id_b) { | |
66871d36 | 375 | BT_LOGW("Event class with this ID already exists in the stream class: " |
d2f71f12 | 376 | "id=%" PRId64 ", name=\"%s\"", |
50842bdc | 377 | id_a, bt_event_class_get_name(event_class_a)); |
0d23acbe PP |
378 | search_query->found = 1; |
379 | goto end; | |
380 | } | |
381 | ||
382 | end: | |
383 | return; | |
384 | } | |
385 | ||
50842bdc PP |
386 | int bt_stream_class_add_event_class( |
387 | struct bt_stream_class *stream_class, | |
388 | struct bt_event_class *event_class) | |
11b0cdc8 JG |
389 | { |
390 | int ret = 0; | |
0b9ce69f | 391 | int64_t *event_id = NULL; |
50842bdc PP |
392 | struct bt_trace *trace = NULL; |
393 | struct bt_stream_class *old_stream_class = NULL; | |
394 | struct bt_validation_output validation_output = { 0 }; | |
395 | struct bt_field_type *packet_header_type = NULL; | |
396 | struct bt_field_type *packet_context_type = NULL; | |
397 | struct bt_field_type *event_header_type = NULL; | |
398 | struct bt_field_type *stream_event_ctx_type = NULL; | |
399 | struct bt_field_type *event_context_type = NULL; | |
400 | struct bt_field_type *event_payload_type = NULL; | |
401 | const enum bt_validation_flag validation_flags = | |
402 | BT_VALIDATION_FLAG_EVENT; | |
2a3ced3c | 403 | struct bt_clock_class *expected_clock_class = NULL; |
11b0cdc8 | 404 | |
e011d2c1 PP |
405 | if (!stream_class || !event_class) { |
406 | BT_LOGW("Invalid parameter: stream class or event class is NULL: " | |
407 | "stream-class-addr=%p, event-class-addr=%p", | |
408 | stream_class, event_class); | |
409 | ret = -1; | |
410 | goto end; | |
411 | } | |
412 | ||
d2f71f12 PP |
413 | BT_LOGD("Adding event class to stream class: " |
414 | "stream-class-addr=%p, stream-class-name=\"%s\", " | |
415 | "stream-class-id=%" PRId64 ", event-class-addr=%p, " | |
416 | "event-class-name=\"%s\", event-class-id=%" PRId64, | |
50842bdc PP |
417 | stream_class, bt_stream_class_get_name(stream_class), |
418 | bt_stream_class_get_id(stream_class), | |
d2f71f12 | 419 | event_class, |
50842bdc PP |
420 | bt_event_class_get_name(event_class), |
421 | bt_event_class_get_id(event_class)); | |
d2f71f12 | 422 | |
50842bdc | 423 | trace = bt_stream_class_get_trace(stream_class); |
5acf2ae6 | 424 | if (trace && trace->is_static) { |
2a3ced3c PP |
425 | BT_LOGW("Invalid parameter: stream class's trace is static: " |
426 | "trace-addr=%p, trace-name=\"%s\"", | |
427 | trace, bt_trace_get_name(trace)); | |
5acf2ae6 PP |
428 | ret = -1; |
429 | goto end; | |
430 | } | |
431 | ||
2a3ced3c PP |
432 | if (stream_class->frozen) { |
433 | /* | |
434 | * We only check that the event class to be added has a | |
435 | * single class which matches the stream class's | |
436 | * expected clock class if the stream class is frozen. | |
437 | * If it's not, then this event class is added "as is" | |
438 | * and the validation will be performed when calling | |
439 | * either bt_trace_add_stream_class() or | |
440 | * bt_event_create(). This is because the stream class's | |
441 | * field types (packet context, event header, event | |
442 | * context) could change before the next call to one of | |
443 | * those two functions. | |
444 | */ | |
445 | expected_clock_class = bt_get(stream_class->clock_class); | |
446 | ||
447 | /* | |
448 | * At this point, `expected_clock_class` can be NULL, | |
449 | * and bt_event_class_validate_single_clock_class() | |
450 | * below can set it. | |
451 | */ | |
452 | ret = bt_event_class_validate_single_clock_class( | |
453 | event_class, &expected_clock_class); | |
454 | if (ret) { | |
455 | BT_LOGW("Event class contains a field type which is not " | |
456 | "recursively mapped to its stream class's " | |
457 | "expected clock class: " | |
458 | "stream-class-addr=%p, " | |
459 | "stream-class-id=%" PRId64 ", " | |
460 | "stream-class-name=\"%s\", " | |
461 | "expected-clock-class-addr=%p, " | |
462 | "expected-clock-class-name=\"%s\"", | |
463 | stream_class, | |
464 | bt_stream_class_get_id(stream_class), | |
465 | bt_stream_class_get_name(stream_class), | |
466 | expected_clock_class, | |
467 | expected_clock_class ? | |
468 | bt_clock_class_get_name(expected_clock_class) : | |
469 | NULL); | |
470 | goto end; | |
471 | } | |
472 | } | |
473 | ||
0b9ce69f JG |
474 | event_id = g_new(int64_t, 1); |
475 | if (!event_id) { | |
d2f71f12 | 476 | BT_LOGE_STR("Failed to allocate one int64_t."); |
0b9ce69f JG |
477 | ret = -1; |
478 | goto end; | |
479 | } | |
480 | ||
11b0cdc8 JG |
481 | /* Check for duplicate event classes */ |
482 | struct search_query query = { .value = event_class, .found = 0 }; | |
0d23acbe PP |
483 | g_ptr_array_foreach(stream_class->event_classes, event_class_exists, |
484 | &query); | |
11b0cdc8 | 485 | if (query.found) { |
d2f71f12 | 486 | BT_LOGW_STR("Another event class part of this stream class has the same ID."); |
11b0cdc8 JG |
487 | ret = -1; |
488 | goto end; | |
489 | } | |
490 | ||
50842bdc | 491 | old_stream_class = bt_event_class_get_stream_class(event_class); |
e6a8e8e4 JG |
492 | if (old_stream_class) { |
493 | /* Event class is already associated to a stream class. */ | |
d2f71f12 PP |
494 | BT_LOGW("Event class is already part of another stream class: " |
495 | "event-class-stream-class-addr=%p, " | |
496 | "event-class-stream-class-name=\"%s\", " | |
497 | "event-class-stream-class-id=%" PRId64, | |
498 | old_stream_class, | |
50842bdc PP |
499 | bt_stream_class_get_name(old_stream_class), |
500 | bt_stream_class_get_id(old_stream_class)); | |
e6a8e8e4 JG |
501 | ret = -1; |
502 | goto end; | |
503 | } | |
504 | ||
e6a8e8e4 | 505 | if (trace) { |
09840de5 PP |
506 | /* |
507 | * If the stream class is associated with a trace, then | |
508 | * both those objects are frozen. Also, this event class | |
509 | * is about to be frozen. | |
510 | * | |
511 | * Therefore the event class must be validated here. | |
512 | * The trace and stream class should be valid at this | |
513 | * point. | |
514 | */ | |
515 | assert(trace->valid); | |
516 | assert(stream_class->valid); | |
517 | packet_header_type = | |
50842bdc | 518 | bt_trace_get_packet_header_type(trace); |
09840de5 | 519 | packet_context_type = |
50842bdc | 520 | bt_stream_class_get_packet_context_type( |
09840de5 PP |
521 | stream_class); |
522 | event_header_type = | |
50842bdc | 523 | bt_stream_class_get_event_header_type(stream_class); |
09840de5 | 524 | stream_event_ctx_type = |
50842bdc | 525 | bt_stream_class_get_event_context_type( |
09840de5 PP |
526 | stream_class); |
527 | event_context_type = | |
50842bdc | 528 | bt_event_class_get_context_type(event_class); |
09840de5 | 529 | event_payload_type = |
50842bdc PP |
530 | bt_event_class_get_payload_type(event_class); |
531 | ret = bt_validate_class_types( | |
09840de5 PP |
532 | trace->environment, packet_header_type, |
533 | packet_context_type, event_header_type, | |
534 | stream_event_ctx_type, event_context_type, | |
535 | event_payload_type, trace->valid, | |
536 | stream_class->valid, event_class->valid, | |
537 | &validation_output, validation_flags); | |
538 | BT_PUT(packet_header_type); | |
539 | BT_PUT(packet_context_type); | |
540 | BT_PUT(event_header_type); | |
541 | BT_PUT(stream_event_ctx_type); | |
542 | BT_PUT(event_context_type); | |
543 | BT_PUT(event_payload_type); | |
544 | ||
26079216 | 545 | if (ret) { |
09840de5 PP |
546 | /* |
547 | * This means something went wrong during the | |
548 | * validation process, not that the objects are | |
549 | * invalid. | |
550 | */ | |
d2f71f12 | 551 | BT_LOGE("Failed to validate event class: ret=%d", ret); |
09840de5 PP |
552 | goto end; |
553 | } | |
554 | ||
555 | if ((validation_output.valid_flags & validation_flags) != | |
556 | validation_flags) { | |
557 | /* Invalid event class */ | |
66871d36 | 558 | BT_LOGW("Invalid trace, stream class, or event class: " |
d2f71f12 PP |
559 | "valid-flags=0x%x", |
560 | validation_output.valid_flags); | |
09840de5 | 561 | ret = -1; |
26079216 JG |
562 | goto end; |
563 | } | |
564 | } | |
565 | ||
09840de5 | 566 | /* Only set an event ID if none was explicitly set before */ |
50842bdc | 567 | *event_id = bt_event_class_get_id(event_class); |
24626e8b | 568 | if (*event_id < 0) { |
d2f71f12 PP |
569 | BT_LOGV("Event class has no ID: automatically setting it: " |
570 | "id=%" PRId64, stream_class->next_event_id); | |
571 | ||
50842bdc | 572 | if (bt_event_class_set_id(event_class, |
d2f71f12 PP |
573 | stream_class->next_event_id)) { |
574 | BT_LOGE("Cannot set event class's ID: id=%" PRId64, | |
575 | stream_class->next_event_id); | |
2f100782 JG |
576 | ret = -1; |
577 | goto end; | |
578 | } | |
d2f71f12 | 579 | stream_class->next_event_id++; |
0b9ce69f | 580 | *event_id = stream_class->next_event_id; |
2f100782 JG |
581 | } |
582 | ||
e6a8e8e4 | 583 | bt_object_set_parent(event_class, stream_class); |
09840de5 PP |
584 | |
585 | if (trace) { | |
586 | /* | |
587 | * At this point we know that the function will be | |
588 | * successful. Therefore we can replace the event | |
589 | * class's field types with what's in the validation | |
590 | * output structure and mark this event class as valid. | |
591 | */ | |
50842bdc | 592 | bt_validation_replace_types(NULL, NULL, event_class, |
09840de5 PP |
593 | &validation_output, validation_flags); |
594 | event_class->valid = 1; | |
595 | ||
596 | /* | |
597 | * Put what was not moved in | |
50842bdc | 598 | * bt_validation_replace_types(). |
09840de5 | 599 | */ |
50842bdc | 600 | bt_validation_output_put_types(&validation_output); |
09840de5 PP |
601 | } |
602 | ||
603 | /* Add to the event classes of the stream class */ | |
11b0cdc8 | 604 | g_ptr_array_add(stream_class->event_classes, event_class); |
0b9ce69f JG |
605 | g_hash_table_insert(stream_class->event_classes_ht, event_id, |
606 | event_class); | |
607 | event_id = NULL; | |
09840de5 PP |
608 | |
609 | /* Freeze the event class */ | |
50842bdc | 610 | bt_event_class_freeze(event_class); |
5ca83563 | 611 | |
2a3ced3c PP |
612 | /* |
613 | * It is safe to set the stream class's unique clock class | |
614 | * now if the stream class is frozen. | |
615 | */ | |
616 | if (stream_class->frozen && expected_clock_class) { | |
617 | assert(!stream_class->clock_class || | |
618 | stream_class->clock_class == expected_clock_class); | |
619 | BT_MOVE(stream_class->clock_class, expected_clock_class); | |
620 | } | |
621 | ||
9b888ff3 JG |
622 | /* Notifiy listeners of the trace's schema modification. */ |
623 | if (trace) { | |
50842bdc PP |
624 | struct bt_visitor_object obj = { .object = event_class, |
625 | .type = BT_VISITOR_OBJECT_TYPE_EVENT_CLASS }; | |
9b888ff3 | 626 | |
50842bdc | 627 | (void) bt_trace_object_modification(&obj, trace); |
9b888ff3 | 628 | } |
d2f71f12 PP |
629 | |
630 | BT_LOGD("Added event class to stream class: " | |
631 | "stream-class-addr=%p, stream-class-name=\"%s\", " | |
632 | "stream-class-id=%" PRId64 ", event-class-addr=%p, " | |
633 | "event-class-name=\"%s\", event-class-id=%" PRId64, | |
50842bdc PP |
634 | stream_class, bt_stream_class_get_name(stream_class), |
635 | bt_stream_class_get_id(stream_class), | |
d2f71f12 | 636 | event_class, |
50842bdc PP |
637 | bt_event_class_get_name(event_class), |
638 | bt_event_class_get_id(event_class)); | |
d2f71f12 | 639 | |
11b0cdc8 | 640 | end: |
e6a8e8e4 JG |
641 | BT_PUT(trace); |
642 | BT_PUT(old_stream_class); | |
50842bdc | 643 | bt_validation_output_put_types(&validation_output); |
2a3ced3c | 644 | bt_put(expected_clock_class); |
09840de5 PP |
645 | assert(!packet_header_type); |
646 | assert(!packet_context_type); | |
647 | assert(!event_header_type); | |
648 | assert(!stream_event_ctx_type); | |
649 | assert(!event_context_type); | |
650 | assert(!event_payload_type); | |
0b9ce69f | 651 | g_free(event_id); |
09840de5 | 652 | |
11b0cdc8 JG |
653 | return ret; |
654 | } | |
655 | ||
50842bdc PP |
656 | int64_t bt_stream_class_get_event_class_count( |
657 | struct bt_stream_class *stream_class) | |
69dc4535 | 658 | { |
544d0515 | 659 | int64_t ret; |
69dc4535 JG |
660 | |
661 | if (!stream_class) { | |
d2f71f12 | 662 | BT_LOGW_STR("Invalid parameter: stream class is NULL."); |
9ac68eb1 | 663 | ret = (int64_t) -1; |
69dc4535 JG |
664 | goto end; |
665 | } | |
666 | ||
9ac68eb1 | 667 | ret = (int64_t) stream_class->event_classes->len; |
69dc4535 JG |
668 | end: |
669 | return ret; | |
670 | } | |
671 | ||
50842bdc PP |
672 | struct bt_event_class *bt_stream_class_get_event_class_by_index( |
673 | struct bt_stream_class *stream_class, uint64_t index) | |
69dc4535 | 674 | { |
50842bdc | 675 | struct bt_event_class *event_class = NULL; |
69dc4535 | 676 | |
d2f71f12 PP |
677 | if (!stream_class) { |
678 | BT_LOGW_STR("Invalid parameter: stream class is NULL."); | |
679 | goto end; | |
680 | } | |
681 | ||
682 | if (index >= stream_class->event_classes->len) { | |
683 | BT_LOGW("Invalid parameter: index is out of bounds: " | |
684 | "addr=%p, name=\"%s\", id=%" PRId64 ", " | |
685 | "index=%" PRIu64 ", count=%u", | |
50842bdc PP |
686 | stream_class, bt_stream_class_get_name(stream_class), |
687 | bt_stream_class_get_id(stream_class), | |
d2f71f12 | 688 | index, stream_class->event_classes->len); |
69dc4535 JG |
689 | goto end; |
690 | } | |
691 | ||
692 | event_class = g_ptr_array_index(stream_class->event_classes, index); | |
83509119 | 693 | bt_get(event_class); |
69dc4535 JG |
694 | end: |
695 | return event_class; | |
696 | } | |
697 | ||
50842bdc PP |
698 | struct bt_event_class *bt_stream_class_get_event_class_by_id( |
699 | struct bt_stream_class *stream_class, uint64_t id) | |
0863f950 | 700 | { |
9ac68eb1 | 701 | int64_t id_key = (int64_t) id; |
50842bdc | 702 | struct bt_event_class *event_class = NULL; |
0863f950 | 703 | |
d2f71f12 PP |
704 | if (!stream_class) { |
705 | BT_LOGW_STR("Invalid parameter: stream class is NULL."); | |
706 | goto end; | |
707 | } | |
708 | ||
709 | if (id_key < 0) { | |
710 | BT_LOGW("Invalid parameter: invalid event class's ID: " | |
711 | "stream-class-addr=%p, stream-class-name=\"%s\", " | |
712 | "stream-class-id=%" PRId64 ", event-class-id=%" PRIu64, | |
713 | stream_class, | |
50842bdc PP |
714 | bt_stream_class_get_name(stream_class), |
715 | bt_stream_class_get_id(stream_class), id); | |
0863f950 PP |
716 | goto end; |
717 | } | |
718 | ||
0b9ce69f JG |
719 | event_class = g_hash_table_lookup(stream_class->event_classes_ht, |
720 | &id_key); | |
721 | bt_get(event_class); | |
0863f950 PP |
722 | end: |
723 | return event_class; | |
724 | } | |
725 | ||
50842bdc PP |
726 | struct bt_field_type *bt_stream_class_get_packet_context_type( |
727 | struct bt_stream_class *stream_class) | |
12c8a1a3 | 728 | { |
50842bdc | 729 | struct bt_field_type *ret = NULL; |
12c8a1a3 JG |
730 | |
731 | if (!stream_class) { | |
d2f71f12 | 732 | BT_LOGW_STR("Invalid parameter: stream class is NULL."); |
12c8a1a3 JG |
733 | goto end; |
734 | } | |
735 | ||
83509119 | 736 | bt_get(stream_class->packet_context_type); |
12c8a1a3 JG |
737 | ret = stream_class->packet_context_type; |
738 | end: | |
739 | return ret; | |
740 | } | |
741 | ||
50842bdc PP |
742 | int bt_stream_class_set_packet_context_type( |
743 | struct bt_stream_class *stream_class, | |
744 | struct bt_field_type *packet_context_type) | |
12c8a1a3 JG |
745 | { |
746 | int ret = 0; | |
747 | ||
d2f71f12 PP |
748 | if (!stream_class) { |
749 | BT_LOGW_STR("Invalid parameter: stream class is NULL."); | |
750 | ret = -1; | |
751 | goto end; | |
752 | } | |
753 | ||
754 | if (stream_class->frozen) { | |
755 | BT_LOGW("Invalid parameter: stream class is frozen: " | |
756 | "addr=%p, name=\"%s\", id=%" PRId64, | |
50842bdc PP |
757 | stream_class, bt_stream_class_get_name(stream_class), |
758 | bt_stream_class_get_id(stream_class)); | |
12c8a1a3 JG |
759 | ret = -1; |
760 | goto end; | |
761 | } | |
762 | ||
835b2d10 | 763 | if (packet_context_type && |
50842bdc PP |
764 | bt_field_type_get_type_id(packet_context_type) != |
765 | BT_FIELD_TYPE_ID_STRUCT) { | |
835b2d10 | 766 | /* A packet context must be a structure. */ |
d2f71f12 PP |
767 | BT_LOGW("Invalid parameter: stream class's packet context field type must be a structure: " |
768 | "addr=%p, name=\"%s\", id=%" PRId64 ", " | |
769 | "packet-context-ft-addr=%p, packet-context-ft-id=%s", | |
50842bdc PP |
770 | stream_class, bt_stream_class_get_name(stream_class), |
771 | bt_stream_class_get_id(stream_class), | |
d2f71f12 | 772 | packet_context_type, |
50842bdc PP |
773 | bt_field_type_id_string( |
774 | bt_field_type_get_type_id(packet_context_type))); | |
12c8a1a3 JG |
775 | ret = -1; |
776 | goto end; | |
777 | } | |
778 | ||
83509119 JG |
779 | bt_put(stream_class->packet_context_type); |
780 | bt_get(packet_context_type); | |
12c8a1a3 | 781 | stream_class->packet_context_type = packet_context_type; |
d2f71f12 PP |
782 | BT_LOGV("Set stream class's packet context field type: " |
783 | "addr=%p, name=\"%s\", id=%" PRId64 ", " | |
784 | "packet-context-ft-addr=%p", | |
50842bdc PP |
785 | stream_class, bt_stream_class_get_name(stream_class), |
786 | bt_stream_class_get_id(stream_class), | |
d2f71f12 PP |
787 | packet_context_type); |
788 | ||
12c8a1a3 JG |
789 | end: |
790 | return ret; | |
791 | } | |
792 | ||
50842bdc PP |
793 | struct bt_field_type *bt_stream_class_get_event_header_type( |
794 | struct bt_stream_class *stream_class) | |
662e778c | 795 | { |
50842bdc | 796 | struct bt_field_type *ret = NULL; |
662e778c | 797 | |
d2f71f12 PP |
798 | if (!stream_class) { |
799 | BT_LOGW_STR("Invalid parameter: stream class is NULL."); | |
800 | goto end; | |
801 | } | |
802 | ||
803 | if (!stream_class->event_header_type) { | |
804 | BT_LOGV("Stream class has no event header field type: " | |
805 | "addr=%p, name=\"%s\", id=%" PRId64, | |
50842bdc PP |
806 | stream_class, bt_stream_class_get_name(stream_class), |
807 | bt_stream_class_get_id(stream_class)); | |
662e778c JG |
808 | goto end; |
809 | } | |
810 | ||
83509119 | 811 | bt_get(stream_class->event_header_type); |
662e778c JG |
812 | ret = stream_class->event_header_type; |
813 | end: | |
814 | return ret; | |
815 | } | |
816 | ||
50842bdc PP |
817 | int bt_stream_class_set_event_header_type( |
818 | struct bt_stream_class *stream_class, | |
819 | struct bt_field_type *event_header_type) | |
662e778c JG |
820 | { |
821 | int ret = 0; | |
822 | ||
d2f71f12 PP |
823 | if (!stream_class) { |
824 | BT_LOGW_STR("Invalid parameter: stream class is NULL."); | |
825 | ret = -1; | |
826 | goto end; | |
827 | } | |
828 | ||
829 | if (stream_class->frozen) { | |
830 | BT_LOGW("Invalid parameter: stream class is frozen: " | |
831 | "addr=%p, name=\"%s\", id=%" PRId64, | |
50842bdc PP |
832 | stream_class, bt_stream_class_get_name(stream_class), |
833 | bt_stream_class_get_id(stream_class)); | |
662e778c JG |
834 | ret = -1; |
835 | goto end; | |
836 | } | |
837 | ||
835b2d10 | 838 | if (event_header_type && |
50842bdc PP |
839 | bt_field_type_get_type_id(event_header_type) != |
840 | BT_FIELD_TYPE_ID_STRUCT) { | |
835b2d10 | 841 | /* An event header must be a structure. */ |
d2f71f12 PP |
842 | BT_LOGW("Invalid parameter: stream class's event header field type must be a structure: " |
843 | "addr=%p, name=\"%s\", id=%" PRId64 ", " | |
844 | "event-header-ft-addr=%p, event-header-ft-id=%s", | |
50842bdc PP |
845 | stream_class, bt_stream_class_get_name(stream_class), |
846 | bt_stream_class_get_id(stream_class), | |
d2f71f12 | 847 | event_header_type, |
50842bdc PP |
848 | bt_field_type_id_string( |
849 | bt_field_type_get_type_id(event_header_type))); | |
662e778c JG |
850 | ret = -1; |
851 | goto end; | |
852 | } | |
853 | ||
83509119 | 854 | bt_put(stream_class->event_header_type); |
835b2d10 | 855 | stream_class->event_header_type = bt_get(event_header_type); |
d2f71f12 PP |
856 | BT_LOGV("Set stream class's event header field type: " |
857 | "addr=%p, name=\"%s\", id=%" PRId64 ", " | |
858 | "event-header-ft-addr=%p", | |
50842bdc PP |
859 | stream_class, bt_stream_class_get_name(stream_class), |
860 | bt_stream_class_get_id(stream_class), | |
d2f71f12 | 861 | event_header_type); |
662e778c JG |
862 | end: |
863 | return ret; | |
864 | } | |
865 | ||
50842bdc PP |
866 | struct bt_field_type *bt_stream_class_get_event_context_type( |
867 | struct bt_stream_class *stream_class) | |
af181248 | 868 | { |
50842bdc | 869 | struct bt_field_type *ret = NULL; |
af181248 | 870 | |
d2f71f12 PP |
871 | if (!stream_class) { |
872 | BT_LOGW_STR("Invalid parameter: stream class is NULL."); | |
873 | goto end; | |
874 | } | |
875 | ||
876 | if (!stream_class->event_context_type) { | |
af181248 JG |
877 | goto end; |
878 | } | |
879 | ||
83509119 | 880 | bt_get(stream_class->event_context_type); |
af181248 JG |
881 | ret = stream_class->event_context_type; |
882 | end: | |
883 | return ret; | |
884 | } | |
885 | ||
50842bdc PP |
886 | int bt_stream_class_set_event_context_type( |
887 | struct bt_stream_class *stream_class, | |
888 | struct bt_field_type *event_context_type) | |
af181248 JG |
889 | { |
890 | int ret = 0; | |
891 | ||
d2f71f12 PP |
892 | if (!stream_class) { |
893 | BT_LOGW_STR("Invalid parameter: stream class is NULL."); | |
894 | ret = -1; | |
895 | goto end; | |
896 | } | |
897 | ||
898 | if (stream_class->frozen) { | |
899 | BT_LOGW("Invalid parameter: stream class is frozen: " | |
900 | "addr=%p, name=\"%s\", id=%" PRId64, | |
50842bdc PP |
901 | stream_class, bt_stream_class_get_name(stream_class), |
902 | bt_stream_class_get_id(stream_class)); | |
af181248 JG |
903 | ret = -1; |
904 | goto end; | |
905 | } | |
906 | ||
835b2d10 | 907 | if (event_context_type && |
50842bdc PP |
908 | bt_field_type_get_type_id(event_context_type) != |
909 | BT_FIELD_TYPE_ID_STRUCT) { | |
835b2d10 | 910 | /* A packet context must be a structure. */ |
d2f71f12 PP |
911 | BT_LOGW("Invalid parameter: stream class's event context field type must be a structure: " |
912 | "addr=%p, name=\"%s\", id=%" PRId64 ", " | |
913 | "event-context-ft-addr=%p, event-context-ft-id=%s", | |
50842bdc PP |
914 | stream_class, bt_stream_class_get_name(stream_class), |
915 | bt_stream_class_get_id(stream_class), | |
d2f71f12 | 916 | event_context_type, |
50842bdc PP |
917 | bt_field_type_id_string( |
918 | bt_field_type_get_type_id(event_context_type))); | |
af181248 JG |
919 | ret = -1; |
920 | goto end; | |
921 | } | |
922 | ||
83509119 | 923 | bt_put(stream_class->event_context_type); |
835b2d10 | 924 | stream_class->event_context_type = bt_get(event_context_type); |
d2f71f12 PP |
925 | BT_LOGV("Set stream class's event context field type: " |
926 | "addr=%p, name=\"%s\", id=%" PRId64 ", " | |
927 | "event-context-ft-addr=%p", | |
50842bdc PP |
928 | stream_class, bt_stream_class_get_name(stream_class), |
929 | bt_stream_class_get_id(stream_class), | |
d2f71f12 | 930 | event_context_type); |
af181248 JG |
931 | end: |
932 | return ret; | |
933 | } | |
934 | ||
d2f71f12 | 935 | /* Pre-2.0 CTF writer backward compatibility */ |
50842bdc | 936 | void bt_ctf_stream_class_get(struct bt_stream_class *stream_class) |
11b0cdc8 | 937 | { |
83509119 | 938 | bt_get(stream_class); |
11b0cdc8 JG |
939 | } |
940 | ||
d2f71f12 | 941 | /* Pre-2.0 CTF writer backward compatibility */ |
50842bdc | 942 | void bt_ctf_stream_class_put(struct bt_stream_class *stream_class) |
11b0cdc8 | 943 | { |
83509119 | 944 | bt_put(stream_class); |
11b0cdc8 JG |
945 | } |
946 | ||
8bf65fbd | 947 | static |
544d0515 | 948 | int64_t get_event_class_count(void *element) |
8bf65fbd | 949 | { |
50842bdc PP |
950 | return bt_stream_class_get_event_class_count( |
951 | (struct bt_stream_class *) element); | |
8bf65fbd JG |
952 | } |
953 | ||
954 | static | |
955 | void *get_event_class(void *element, int i) | |
956 | { | |
50842bdc PP |
957 | return bt_stream_class_get_event_class_by_index( |
958 | (struct bt_stream_class *) element, i); | |
8bf65fbd JG |
959 | } |
960 | ||
961 | static | |
50842bdc | 962 | int visit_event_class(void *object, bt_visitor visitor,void *data) |
8bf65fbd | 963 | { |
50842bdc | 964 | struct bt_visitor_object obj = |
d9a13d86 | 965 | { .object = object, |
50842bdc | 966 | .type = BT_VISITOR_OBJECT_TYPE_EVENT_CLASS }; |
8bf65fbd | 967 | |
d9a13d86 | 968 | return visitor(&obj, data); |
8bf65fbd JG |
969 | } |
970 | ||
50842bdc PP |
971 | int bt_stream_class_visit(struct bt_stream_class *stream_class, |
972 | bt_visitor visitor, void *data) | |
8bf65fbd JG |
973 | { |
974 | int ret; | |
50842bdc | 975 | struct bt_visitor_object obj = |
d9a13d86 | 976 | { .object = stream_class, |
50842bdc | 977 | .type = BT_VISITOR_OBJECT_TYPE_STREAM_CLASS }; |
8bf65fbd JG |
978 | |
979 | if (!stream_class || !visitor) { | |
d2f71f12 PP |
980 | BT_LOGW("Invalid parameter: stream class or visitor is NULL: " |
981 | "stream-class-addr=%p, visitor=%p", | |
982 | stream_class, visitor); | |
8bf65fbd JG |
983 | ret = -1; |
984 | goto end; | |
985 | } | |
986 | ||
d9a13d86 | 987 | ret = visitor_helper(&obj, get_event_class_count, |
8bf65fbd JG |
988 | get_event_class, |
989 | visit_event_class, visitor, data); | |
d2f71f12 | 990 | BT_LOGV("visitor_helper() returned: ret=%d", ret); |
8bf65fbd JG |
991 | end: |
992 | return ret; | |
993 | } | |
994 | ||
11b0cdc8 | 995 | BT_HIDDEN |
50842bdc | 996 | void bt_stream_class_freeze(struct bt_stream_class *stream_class) |
11b0cdc8 | 997 | { |
d2f71f12 | 998 | if (!stream_class || stream_class->frozen) { |
11b0cdc8 JG |
999 | return; |
1000 | } | |
1001 | ||
d2f71f12 | 1002 | BT_LOGD("Freezing stream class: addr=%p, name=\"%s\", id=%" PRId64, |
50842bdc PP |
1003 | stream_class, bt_stream_class_get_name(stream_class), |
1004 | bt_stream_class_get_id(stream_class)); | |
11b0cdc8 | 1005 | stream_class->frozen = 1; |
50842bdc PP |
1006 | bt_field_type_freeze(stream_class->event_header_type); |
1007 | bt_field_type_freeze(stream_class->packet_context_type); | |
1008 | bt_field_type_freeze(stream_class->event_context_type); | |
ac0c6bdd PP |
1009 | |
1010 | if (stream_class->clock) { | |
50842bdc | 1011 | bt_clock_class_freeze(stream_class->clock->clock_class); |
ac0c6bdd | 1012 | } |
11b0cdc8 JG |
1013 | } |
1014 | ||
11b0cdc8 | 1015 | BT_HIDDEN |
50842bdc | 1016 | int bt_stream_class_serialize(struct bt_stream_class *stream_class, |
11b0cdc8 JG |
1017 | struct metadata_context *context) |
1018 | { | |
9ac68eb1 | 1019 | int ret = 0; |
11b0cdc8 | 1020 | size_t i; |
50842bdc PP |
1021 | struct bt_trace *trace; |
1022 | struct bt_field_type *packet_header_type = NULL; | |
11b0cdc8 | 1023 | |
d2f71f12 PP |
1024 | BT_LOGD("Serializing stream class's metadata: " |
1025 | "stream-class-addr=%p, stream-class-name=\"%s\", " | |
1026 | "stream-class-id=%" PRId64 ", metadata-context-addr=%p", | |
50842bdc PP |
1027 | stream_class, bt_stream_class_get_name(stream_class), |
1028 | bt_stream_class_get_id(stream_class), context); | |
11b0cdc8 JG |
1029 | g_string_assign(context->field_name, ""); |
1030 | context->current_indentation_level = 1; | |
1031 | if (!stream_class->id_set) { | |
d2f71f12 | 1032 | BT_LOGW_STR("Stream class's ID is not set."); |
11b0cdc8 JG |
1033 | ret = -1; |
1034 | goto end; | |
1035 | } | |
1036 | ||
6a5c98a5 JG |
1037 | g_string_append(context->string, "stream {\n"); |
1038 | ||
1039 | /* | |
1040 | * The reference to the trace is only borrowed since the | |
1041 | * serialization of the stream class might have been triggered | |
1042 | * by the trace's destruction. In such a case, the trace's | |
1043 | * reference count would, unexepectedly, go through the sequence | |
1044 | * 1 -> 0 -> 1 -> 0 -> ..., provoking an endless loop of destruction | |
1045 | * and serialization. | |
1046 | */ | |
50842bdc | 1047 | trace = bt_stream_class_borrow_trace(stream_class); |
6a5c98a5 | 1048 | assert(trace); |
50842bdc | 1049 | packet_header_type = bt_trace_get_packet_header_type(trace); |
6a5c98a5 JG |
1050 | trace = NULL; |
1051 | if (packet_header_type) { | |
50842bdc | 1052 | struct bt_field_type *stream_id_type; |
6a5c98a5 JG |
1053 | |
1054 | stream_id_type = | |
50842bdc | 1055 | bt_field_type_structure_get_field_type_by_name( |
6a5c98a5 JG |
1056 | packet_header_type, "stream_id"); |
1057 | if (stream_id_type) { | |
1058 | /* | |
1059 | * Only set the stream's id if the trace's packet header | |
1060 | * contains a stream_id field. This field is only | |
1061 | * needed if the trace contains only one stream | |
1062 | * class. | |
1063 | */ | |
1064 | g_string_append_printf(context->string, | |
1065 | "\tid = %" PRId64 ";\n", stream_class->id); | |
1066 | } | |
1067 | bt_put(stream_id_type); | |
1068 | } | |
e011d2c1 PP |
1069 | if (stream_class->event_header_type) { |
1070 | BT_LOGD_STR("Serializing stream class's event header field type's metadata."); | |
1071 | g_string_append(context->string, "\tevent.header := "); | |
50842bdc | 1072 | ret = bt_field_type_serialize(stream_class->event_header_type, |
e011d2c1 PP |
1073 | context); |
1074 | if (ret) { | |
1075 | BT_LOGW("Cannot serialize stream class's event header field type's metadata: " | |
1076 | "ret=%d", ret); | |
1077 | goto end; | |
1078 | } | |
1079 | g_string_append(context->string, ";"); | |
11b0cdc8 JG |
1080 | } |
1081 | ||
e011d2c1 | 1082 | |
98edd02c | 1083 | if (stream_class->packet_context_type) { |
e011d2c1 PP |
1084 | BT_LOGD_STR("Serializing stream class's packet context field type's metadata."); |
1085 | g_string_append(context->string, "\n\n\tpacket.context := "); | |
50842bdc | 1086 | ret = bt_field_type_serialize(stream_class->packet_context_type, |
98edd02c JG |
1087 | context); |
1088 | if (ret) { | |
66871d36 | 1089 | BT_LOGW("Cannot serialize stream class's packet context field type's metadata: " |
d2f71f12 | 1090 | "ret=%d", ret); |
98edd02c JG |
1091 | goto end; |
1092 | } | |
e011d2c1 | 1093 | g_string_append(context->string, ";"); |
11b0cdc8 JG |
1094 | } |
1095 | ||
1096 | if (stream_class->event_context_type) { | |
e011d2c1 PP |
1097 | BT_LOGD_STR("Serializing stream class's event context field type's metadata."); |
1098 | g_string_append(context->string, "\n\n\tevent.context := "); | |
50842bdc | 1099 | ret = bt_field_type_serialize( |
11b0cdc8 JG |
1100 | stream_class->event_context_type, context); |
1101 | if (ret) { | |
66871d36 | 1102 | BT_LOGW("Cannot serialize stream class's event context field type's metadata: " |
d2f71f12 | 1103 | "ret=%d", ret); |
11b0cdc8 JG |
1104 | goto end; |
1105 | } | |
e011d2c1 | 1106 | g_string_append(context->string, ";"); |
11b0cdc8 JG |
1107 | } |
1108 | ||
e011d2c1 PP |
1109 | g_string_append(context->string, "\n};\n\n"); |
1110 | ||
11b0cdc8 | 1111 | for (i = 0; i < stream_class->event_classes->len; i++) { |
50842bdc | 1112 | struct bt_event_class *event_class = |
11b0cdc8 JG |
1113 | stream_class->event_classes->pdata[i]; |
1114 | ||
50842bdc | 1115 | ret = bt_event_class_serialize(event_class, context); |
11b0cdc8 | 1116 | if (ret) { |
66871d36 | 1117 | BT_LOGW("Cannot serialize event class's metadata: " |
d2f71f12 PP |
1118 | "event-class-addr=%p, event-class-name=\"%s\", " |
1119 | "event-class-id=%" PRId64, | |
1120 | event_class, | |
50842bdc PP |
1121 | bt_event_class_get_name(event_class), |
1122 | bt_event_class_get_id(event_class)); | |
11b0cdc8 JG |
1123 | goto end; |
1124 | } | |
1125 | } | |
1126 | end: | |
6a5c98a5 | 1127 | bt_put(packet_header_type); |
11b0cdc8 JG |
1128 | context->current_indentation_level = 0; |
1129 | return ret; | |
1130 | } | |
1131 | ||
1132 | static | |
50842bdc | 1133 | void bt_stream_class_destroy(struct bt_object *obj) |
11b0cdc8 | 1134 | { |
50842bdc | 1135 | struct bt_stream_class *stream_class; |
11b0cdc8 | 1136 | |
50842bdc | 1137 | stream_class = container_of(obj, struct bt_stream_class, base); |
d2f71f12 | 1138 | BT_LOGD("Destroying stream class: addr=%p, name=\"%s\", id=%" PRId64, |
50842bdc PP |
1139 | stream_class, bt_stream_class_get_name(stream_class), |
1140 | bt_stream_class_get_id(stream_class)); | |
83509119 | 1141 | bt_put(stream_class->clock); |
2a3ced3c | 1142 | bt_put(stream_class->clock_class); |
11b0cdc8 | 1143 | |
0b9ce69f JG |
1144 | if (stream_class->event_classes_ht) { |
1145 | g_hash_table_destroy(stream_class->event_classes_ht); | |
1146 | } | |
11b0cdc8 | 1147 | if (stream_class->event_classes) { |
8c8cb0f2 | 1148 | BT_LOGD_STR("Destroying event classes."); |
11b0cdc8 JG |
1149 | g_ptr_array_free(stream_class->event_classes, TRUE); |
1150 | } | |
1151 | ||
1152 | if (stream_class->name) { | |
1153 | g_string_free(stream_class->name, TRUE); | |
1154 | } | |
1155 | ||
8c8cb0f2 | 1156 | BT_LOGD_STR("Putting event header field type."); |
83509119 | 1157 | bt_put(stream_class->event_header_type); |
8c8cb0f2 | 1158 | BT_LOGD_STR("Putting packet context field type."); |
83509119 | 1159 | bt_put(stream_class->packet_context_type); |
8c8cb0f2 | 1160 | BT_LOGD_STR("Putting event context field type."); |
83509119 | 1161 | bt_put(stream_class->event_context_type); |
11b0cdc8 JG |
1162 | g_free(stream_class); |
1163 | } | |
1164 | ||
1165 | static | |
50842bdc | 1166 | int init_event_header(struct bt_stream_class *stream_class) |
11b0cdc8 JG |
1167 | { |
1168 | int ret = 0; | |
50842bdc PP |
1169 | struct bt_field_type *event_header_type = |
1170 | bt_field_type_structure_create(); | |
1171 | struct bt_field_type *_uint32_t = | |
11b0cdc8 | 1172 | get_field_type(FIELD_TYPE_ALIAS_UINT32_T); |
50842bdc | 1173 | struct bt_field_type *_uint64_t = |
11b0cdc8 JG |
1174 | get_field_type(FIELD_TYPE_ALIAS_UINT64_T); |
1175 | ||
1176 | if (!event_header_type) { | |
d2f71f12 | 1177 | BT_LOGE_STR("Cannot create empty structure field type."); |
11b0cdc8 JG |
1178 | ret = -1; |
1179 | goto end; | |
1180 | } | |
1181 | ||
50842bdc | 1182 | ret = bt_field_type_structure_add_field(event_header_type, |
11b0cdc8 JG |
1183 | _uint32_t, "id"); |
1184 | if (ret) { | |
d2f71f12 | 1185 | BT_LOGE_STR("Cannot add `id` field to event header field type."); |
11b0cdc8 JG |
1186 | goto end; |
1187 | } | |
1188 | ||
50842bdc | 1189 | ret = bt_field_type_structure_add_field(event_header_type, |
11b0cdc8 JG |
1190 | _uint64_t, "timestamp"); |
1191 | if (ret) { | |
d2f71f12 | 1192 | BT_LOGE_STR("Cannot add `timestamp` field to event header field type."); |
11b0cdc8 JG |
1193 | goto end; |
1194 | } | |
1195 | ||
e0e2946b | 1196 | BT_MOVE(stream_class->event_header_type, event_header_type); |
11b0cdc8 JG |
1197 | end: |
1198 | if (ret) { | |
83509119 | 1199 | bt_put(event_header_type); |
11b0cdc8 JG |
1200 | } |
1201 | ||
83509119 JG |
1202 | bt_put(_uint32_t); |
1203 | bt_put(_uint64_t); | |
11b0cdc8 JG |
1204 | return ret; |
1205 | } | |
1206 | ||
1207 | static | |
50842bdc | 1208 | int init_packet_context(struct bt_stream_class *stream_class) |
11b0cdc8 JG |
1209 | { |
1210 | int ret = 0; | |
50842bdc PP |
1211 | struct bt_field_type *packet_context_type = |
1212 | bt_field_type_structure_create(); | |
1213 | struct bt_field_type *_uint64_t = | |
11b0cdc8 | 1214 | get_field_type(FIELD_TYPE_ALIAS_UINT64_T); |
50842bdc | 1215 | struct bt_field_type *ts_begin_end_uint64_t; |
11b0cdc8 JG |
1216 | |
1217 | if (!packet_context_type) { | |
d2f71f12 | 1218 | BT_LOGE_STR("Cannot create empty structure field type."); |
11b0cdc8 JG |
1219 | ret = -1; |
1220 | goto end; | |
1221 | } | |
1222 | ||
50842bdc | 1223 | ts_begin_end_uint64_t = bt_field_type_copy(_uint64_t); |
e011d2c1 PP |
1224 | if (!ts_begin_end_uint64_t) { |
1225 | BT_LOGE_STR("Cannot copy integer field type for `timestamp_begin` and `timestamp_end` fields."); | |
1226 | ret = -1; | |
1227 | goto end; | |
1228 | } | |
1229 | ||
11b0cdc8 JG |
1230 | /* |
1231 | * We create a stream packet context as proposed in the CTF | |
1232 | * specification. | |
1233 | */ | |
50842bdc | 1234 | ret = bt_field_type_structure_add_field(packet_context_type, |
e011d2c1 | 1235 | ts_begin_end_uint64_t, "timestamp_begin"); |
11b0cdc8 | 1236 | if (ret) { |
d2f71f12 | 1237 | BT_LOGE_STR("Cannot add `timestamp_begin` field to event header field type."); |
11b0cdc8 JG |
1238 | goto end; |
1239 | } | |
1240 | ||
50842bdc | 1241 | ret = bt_field_type_structure_add_field(packet_context_type, |
e011d2c1 | 1242 | ts_begin_end_uint64_t, "timestamp_end"); |
11b0cdc8 | 1243 | if (ret) { |
d2f71f12 | 1244 | BT_LOGE_STR("Cannot add `timestamp_end` field to event header field type."); |
11b0cdc8 JG |
1245 | goto end; |
1246 | } | |
1247 | ||
50842bdc | 1248 | ret = bt_field_type_structure_add_field(packet_context_type, |
11b0cdc8 JG |
1249 | _uint64_t, "content_size"); |
1250 | if (ret) { | |
d2f71f12 | 1251 | BT_LOGE_STR("Cannot add `content_size` field to event header field type."); |
11b0cdc8 JG |
1252 | goto end; |
1253 | } | |
1254 | ||
50842bdc | 1255 | ret = bt_field_type_structure_add_field(packet_context_type, |
11b0cdc8 JG |
1256 | _uint64_t, "packet_size"); |
1257 | if (ret) { | |
d2f71f12 | 1258 | BT_LOGE_STR("Cannot add `packet_size` field to event header field type."); |
11b0cdc8 JG |
1259 | goto end; |
1260 | } | |
1261 | ||
50842bdc | 1262 | ret = bt_field_type_structure_add_field(packet_context_type, |
11b0cdc8 JG |
1263 | _uint64_t, "events_discarded"); |
1264 | if (ret) { | |
d2f71f12 | 1265 | BT_LOGE_STR("Cannot add `events_discarded` field to event header field type."); |
11b0cdc8 JG |
1266 | goto end; |
1267 | } | |
1268 | ||
e0e2946b | 1269 | BT_MOVE(stream_class->packet_context_type, packet_context_type); |
11b0cdc8 JG |
1270 | end: |
1271 | if (ret) { | |
83509119 | 1272 | bt_put(packet_context_type); |
11b0cdc8 JG |
1273 | goto end; |
1274 | } | |
1275 | ||
83509119 | 1276 | bt_put(_uint64_t); |
e011d2c1 PP |
1277 | bt_put(ts_begin_end_uint64_t); |
1278 | return ret; | |
1279 | } | |
1280 | ||
1281 | static | |
50842bdc | 1282 | int try_map_clock_class(struct bt_stream_class *stream_class, |
8cd07f2c | 1283 | struct bt_field_type *parent_ft, const char *field_name) |
e011d2c1 | 1284 | { |
50842bdc | 1285 | struct bt_clock_class *mapped_clock_class = NULL; |
e011d2c1 | 1286 | int ret = 0; |
8cd07f2c PP |
1287 | struct bt_field_type *ft = |
1288 | bt_field_type_structure_get_field_type_by_name(parent_ft, | |
1289 | field_name); | |
1290 | ||
1291 | assert(stream_class->clock); | |
e011d2c1 PP |
1292 | |
1293 | if (!ft) { | |
1294 | /* Field does not exist: not an error */ | |
1295 | goto end; | |
1296 | } | |
1297 | ||
50842bdc | 1298 | assert(bt_field_type_is_integer(ft)); |
e011d2c1 | 1299 | mapped_clock_class = |
50842bdc | 1300 | bt_field_type_integer_get_mapped_clock_class(ft); |
e011d2c1 | 1301 | if (!mapped_clock_class) { |
8cd07f2c PP |
1302 | struct bt_field_type *ft_copy; |
1303 | ||
e011d2c1 PP |
1304 | if (!stream_class->clock) { |
1305 | BT_LOGW("Cannot automatically set field's type mapped clock class: stream class's clock is not set: " | |
1306 | "stream-class-addr=%p, stream-class-name=\"%s\", " | |
1307 | "stream-class-id=%" PRId64 ", ft-addr=%p", | |
50842bdc PP |
1308 | stream_class, bt_stream_class_get_name(stream_class), |
1309 | bt_stream_class_get_id(stream_class), ft); | |
e011d2c1 PP |
1310 | ret = -1; |
1311 | goto end; | |
1312 | } | |
1313 | ||
8cd07f2c PP |
1314 | ft_copy = bt_field_type_copy(ft); |
1315 | if (!ft_copy) { | |
1316 | BT_LOGE("Failed to copy integer field type: ft-addr=%p", | |
1317 | ft); | |
e011d2c1 PP |
1318 | } |
1319 | ||
8cd07f2c PP |
1320 | ret = bt_field_type_integer_set_mapped_clock_class_no_check( |
1321 | ft_copy, stream_class->clock->clock_class); | |
1322 | assert(ret == 0); | |
1323 | ret = bt_field_type_structure_replace_field(parent_ft, | |
1324 | field_name, ft_copy); | |
1325 | bt_put(ft_copy); | |
e011d2c1 PP |
1326 | BT_LOGV("Automatically mapped field type to stream class's clock class: " |
1327 | "stream-class-addr=%p, stream-class-name=\"%s\", " | |
8cd07f2c PP |
1328 | "stream-class-id=%" PRId64 ", ft-addr=%p, " |
1329 | "ft-copy-addr=%p", | |
50842bdc | 1330 | stream_class, bt_stream_class_get_name(stream_class), |
8cd07f2c | 1331 | bt_stream_class_get_id(stream_class), ft, ft_copy); |
e011d2c1 PP |
1332 | } |
1333 | ||
1334 | end: | |
8cd07f2c | 1335 | bt_put(ft); |
e011d2c1 PP |
1336 | bt_put(mapped_clock_class); |
1337 | return ret; | |
1338 | } | |
1339 | ||
1340 | BT_HIDDEN | |
50842bdc PP |
1341 | int bt_stream_class_map_clock_class( |
1342 | struct bt_stream_class *stream_class, | |
1343 | struct bt_field_type *packet_context_type, | |
1344 | struct bt_field_type *event_header_type) | |
e011d2c1 | 1345 | { |
e011d2c1 PP |
1346 | int ret = 0; |
1347 | ||
1348 | assert(stream_class); | |
1349 | ||
8cd07f2c PP |
1350 | if (!stream_class->clock) { |
1351 | /* No clock class to map to */ | |
1352 | goto end; | |
1353 | } | |
1354 | ||
e011d2c1 | 1355 | if (packet_context_type) { |
8cd07f2c PP |
1356 | if (try_map_clock_class(stream_class, packet_context_type, |
1357 | "timestamp_begin")) { | |
e011d2c1 PP |
1358 | BT_LOGE_STR("Cannot automatically set stream class's packet context field type's `timestamp_begin` field's mapped clock class."); |
1359 | ret = -1; | |
1360 | goto end; | |
1361 | } | |
1362 | ||
8cd07f2c PP |
1363 | if (try_map_clock_class(stream_class, packet_context_type, |
1364 | "timestamp_end")) { | |
e011d2c1 PP |
1365 | BT_LOGE_STR("Cannot automatically set stream class's packet context field type's `timestamp_end` field's mapped clock class."); |
1366 | ret = -1; | |
1367 | goto end; | |
1368 | } | |
e011d2c1 PP |
1369 | } |
1370 | ||
1371 | if (event_header_type) { | |
8cd07f2c PP |
1372 | if (try_map_clock_class(stream_class, event_header_type, |
1373 | "timestamp")) { | |
e011d2c1 PP |
1374 | BT_LOGE_STR("Cannot automatically set stream class's event header field type's `timestamp` field's mapped clock class."); |
1375 | ret = -1; | |
1376 | goto end; | |
1377 | } | |
e011d2c1 PP |
1378 | } |
1379 | ||
1380 | end: | |
11b0cdc8 JG |
1381 | return ret; |
1382 | } | |
2a3ced3c PP |
1383 | |
1384 | BT_HIDDEN | |
1385 | int bt_stream_class_validate_single_clock_class( | |
1386 | struct bt_stream_class *stream_class, | |
1387 | struct bt_clock_class **expected_clock_class) | |
1388 | { | |
1389 | int ret; | |
1390 | uint64_t i; | |
1391 | ||
1392 | assert(stream_class); | |
1393 | assert(expected_clock_class); | |
1394 | ret = bt_validate_single_clock_class(stream_class->packet_context_type, | |
1395 | expected_clock_class); | |
1396 | if (ret) { | |
1397 | BT_LOGW("Stream class's packet context field type " | |
1398 | "is not recursively mapped to the " | |
1399 | "expected clock class: " | |
1400 | "stream-class-addr=%p, " | |
1401 | "stream-class-name=\"%s\", " | |
1402 | "stream-class-id=%" PRId64 ", " | |
1403 | "ft-addr=%p", | |
1404 | stream_class, | |
1405 | bt_stream_class_get_name(stream_class), | |
1406 | stream_class->id, | |
1407 | stream_class->packet_context_type); | |
1408 | goto end; | |
1409 | } | |
1410 | ||
1411 | ret = bt_validate_single_clock_class(stream_class->event_header_type, | |
1412 | expected_clock_class); | |
1413 | if (ret) { | |
1414 | BT_LOGW("Stream class's event header field type " | |
1415 | "is not recursively mapped to the " | |
1416 | "expected clock class: " | |
1417 | "stream-class-addr=%p, " | |
1418 | "stream-class-name=\"%s\", " | |
1419 | "stream-class-id=%" PRId64 ", " | |
1420 | "ft-addr=%p", | |
1421 | stream_class, | |
1422 | bt_stream_class_get_name(stream_class), | |
1423 | stream_class->id, | |
1424 | stream_class->event_header_type); | |
1425 | goto end; | |
1426 | } | |
1427 | ||
1428 | ret = bt_validate_single_clock_class(stream_class->event_context_type, | |
1429 | expected_clock_class); | |
1430 | if (ret) { | |
1431 | BT_LOGW("Stream class's event context field type " | |
1432 | "is not recursively mapped to the " | |
1433 | "expected clock class: " | |
1434 | "stream-class-addr=%p, " | |
1435 | "stream-class-name=\"%s\", " | |
1436 | "stream-class-id=%" PRId64 ", " | |
1437 | "ft-addr=%p", | |
1438 | stream_class, | |
1439 | bt_stream_class_get_name(stream_class), | |
1440 | stream_class->id, | |
1441 | stream_class->event_context_type); | |
1442 | goto end; | |
1443 | } | |
1444 | ||
1445 | for (i = 0; i < stream_class->event_classes->len; i++) { | |
1446 | struct bt_event_class *event_class = | |
1447 | g_ptr_array_index(stream_class->event_classes, i); | |
1448 | ||
1449 | assert(event_class); | |
1450 | ret = bt_event_class_validate_single_clock_class(event_class, | |
1451 | expected_clock_class); | |
1452 | if (ret) { | |
1453 | BT_LOGW("Stream class's event class contains a " | |
1454 | "field type which is not recursively mapped to " | |
1455 | "the expected clock class: " | |
1456 | "stream-class-addr=%p, " | |
1457 | "stream-class-name=\"%s\", " | |
1458 | "stream-class-id=%" PRId64, | |
1459 | stream_class, | |
1460 | bt_stream_class_get_name(stream_class), | |
1461 | stream_class->id); | |
1462 | goto end; | |
1463 | } | |
1464 | } | |
1465 | ||
1466 | end: | |
1467 | return ret; | |
1468 | } |