Commit | Line | Data |
---|---|---|
47e5a032 JG |
1 | /* |
2 | * iterator.c | |
3 | * | |
4 | * Babeltrace Notification Iterator | |
5 | * | |
6 | * Copyright 2015 Jérémie Galarneau <jeremie.galarneau@efficios.com> | |
3230ee6b | 7 | * Copyright 2017 Philippe Proulx <pproulx@efficios.com> |
47e5a032 JG |
8 | * |
9 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
10 | * of this software and associated documentation files (the "Software"), to deal | |
11 | * in the Software without restriction, including without limitation the rights | |
12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
13 | * copies of the Software, and to permit persons to whom the Software is | |
14 | * furnished to do so, subject to the following conditions: | |
15 | * | |
16 | * The above copyright notice and this permission notice shall be included in | |
17 | * all copies or substantial portions of the Software. | |
18 | * | |
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
25 | * SOFTWARE. | |
26 | */ | |
27 | ||
5af447e5 PP |
28 | #define BT_LOG_TAG "NOTIF-ITER" |
29 | #include <babeltrace/lib-logging-internal.h> | |
30 | ||
3d9990ac | 31 | #include <babeltrace/compiler-internal.h> |
b8a06801 | 32 | #include <babeltrace/ref.h> |
2ec84d26 PP |
33 | #include <babeltrace/ctf-ir/fields.h> |
34 | #include <babeltrace/ctf-ir/field-types.h> | |
35 | #include <babeltrace/ctf-ir/field-types-internal.h> | |
3230ee6b PP |
36 | #include <babeltrace/ctf-ir/event-internal.h> |
37 | #include <babeltrace/ctf-ir/packet-internal.h> | |
38 | #include <babeltrace/ctf-ir/stream-internal.h> | |
73d5c1ad | 39 | #include <babeltrace/graph/connection.h> |
bd14d768 | 40 | #include <babeltrace/graph/connection-internal.h> |
b2e0c907 PP |
41 | #include <babeltrace/graph/component.h> |
42 | #include <babeltrace/graph/component-source-internal.h> | |
43 | #include <babeltrace/graph/component-class-internal.h> | |
fa054faf | 44 | #include <babeltrace/graph/notification.h> |
b2e0c907 PP |
45 | #include <babeltrace/graph/notification-iterator.h> |
46 | #include <babeltrace/graph/notification-iterator-internal.h> | |
e7fa96c3 | 47 | #include <babeltrace/graph/notification-internal.h> |
3230ee6b PP |
48 | #include <babeltrace/graph/notification-event.h> |
49 | #include <babeltrace/graph/notification-event-internal.h> | |
50 | #include <babeltrace/graph/notification-packet.h> | |
51 | #include <babeltrace/graph/notification-packet-internal.h> | |
52 | #include <babeltrace/graph/notification-stream.h> | |
53 | #include <babeltrace/graph/notification-stream-internal.h> | |
2ec84d26 | 54 | #include <babeltrace/graph/notification-discarded-elements-internal.h> |
3230ee6b | 55 | #include <babeltrace/graph/port.h> |
c55a9f58 | 56 | #include <babeltrace/types.h> |
fa054faf | 57 | #include <stdint.h> |
2ec84d26 | 58 | #include <inttypes.h> |
0fbb9a9f | 59 | #include <stdlib.h> |
3230ee6b | 60 | |
2ec84d26 PP |
61 | struct discarded_elements_state { |
62 | struct bt_ctf_clock_value *cur_begin; | |
63 | uint64_t cur_count; | |
64 | }; | |
65 | ||
3230ee6b PP |
66 | struct stream_state { |
67 | struct bt_ctf_stream *stream; /* owned by this */ | |
68 | struct bt_ctf_packet *cur_packet; /* owned by this */ | |
2ec84d26 PP |
69 | struct discarded_elements_state discarded_packets_state; |
70 | struct discarded_elements_state discarded_events_state; | |
c55a9f58 | 71 | bt_bool is_ended; |
3230ee6b PP |
72 | }; |
73 | ||
74 | enum action_type { | |
75 | ACTION_TYPE_PUSH_NOTIF, | |
76 | ACTION_TYPE_MAP_PORT_TO_COMP_IN_STREAM, | |
77 | ACTION_TYPE_ADD_STREAM_STATE, | |
78 | ACTION_TYPE_SET_STREAM_STATE_IS_ENDED, | |
79 | ACTION_TYPE_SET_STREAM_STATE_CUR_PACKET, | |
2ec84d26 PP |
80 | ACTION_TYPE_UPDATE_STREAM_STATE_DISCARDED_PACKETS, |
81 | ACTION_TYPE_UPDATE_STREAM_STATE_DISCARDED_EVENTS, | |
3230ee6b PP |
82 | }; |
83 | ||
84 | struct action { | |
85 | enum action_type type; | |
86 | union { | |
87 | /* ACTION_TYPE_PUSH_NOTIF */ | |
88 | struct { | |
89 | struct bt_notification *notif; /* owned by this */ | |
90 | } push_notif; | |
91 | ||
92 | /* ACTION_TYPE_MAP_PORT_TO_COMP_IN_STREAM */ | |
93 | struct { | |
94 | struct bt_ctf_stream *stream; /* owned by this */ | |
95 | struct bt_component *component; /* owned by this */ | |
96 | struct bt_port *port; /* owned by this */ | |
97 | } map_port_to_comp_in_stream; | |
98 | ||
99 | /* ACTION_TYPE_ADD_STREAM_STATE */ | |
100 | struct { | |
101 | struct bt_ctf_stream *stream; /* owned by this */ | |
102 | struct stream_state *stream_state; /* owned by this */ | |
103 | } add_stream_state; | |
104 | ||
105 | /* ACTION_TYPE_SET_STREAM_STATE_IS_ENDED */ | |
106 | struct { | |
107 | struct stream_state *stream_state; /* weak */ | |
108 | } set_stream_state_is_ended; | |
109 | ||
110 | /* ACTION_TYPE_SET_STREAM_STATE_CUR_PACKET */ | |
111 | struct { | |
112 | struct stream_state *stream_state; /* weak */ | |
113 | struct bt_ctf_packet *packet; /* owned by this */ | |
114 | } set_stream_state_cur_packet; | |
2ec84d26 PP |
115 | |
116 | /* ACTION_TYPE_UPDATE_STREAM_STATE_DISCARDED_PACKETS */ | |
117 | /* ACTION_TYPE_UPDATE_STREAM_STATE_DISCARDED_EVENTS */ | |
118 | struct { | |
119 | struct stream_state *stream_state; /* weak */ | |
120 | struct bt_ctf_clock_value *cur_begin; /* owned by this */ | |
121 | uint64_t cur_count; | |
122 | } update_stream_state_discarded_elements; | |
3230ee6b PP |
123 | } payload; |
124 | }; | |
125 | ||
126 | static | |
127 | void stream_destroy_listener(struct bt_ctf_stream *stream, void *data) | |
128 | { | |
129 | struct bt_notification_iterator *iterator = data; | |
130 | ||
131 | /* Remove associated stream state */ | |
132 | g_hash_table_remove(iterator->stream_states, stream); | |
133 | } | |
134 | ||
135 | static | |
136 | void destroy_stream_state(struct stream_state *stream_state) | |
137 | { | |
138 | if (!stream_state) { | |
139 | return; | |
140 | } | |
141 | ||
5af447e5 PP |
142 | BT_LOGV("Destroying stream state: stream-state-addr=%p", stream_state); |
143 | BT_LOGV_STR("Putting stream state's current packet."); | |
3230ee6b | 144 | bt_put(stream_state->cur_packet); |
5af447e5 | 145 | BT_LOGV_STR("Putting stream state's stream."); |
3230ee6b | 146 | bt_put(stream_state->stream); |
2ec84d26 PP |
147 | bt_put(stream_state->discarded_packets_state.cur_begin); |
148 | bt_put(stream_state->discarded_events_state.cur_begin); | |
3230ee6b PP |
149 | g_free(stream_state); |
150 | } | |
151 | ||
152 | static | |
153 | void destroy_action(struct action *action) | |
154 | { | |
155 | assert(action); | |
156 | ||
157 | switch (action->type) { | |
158 | case ACTION_TYPE_PUSH_NOTIF: | |
159 | BT_PUT(action->payload.push_notif.notif); | |
160 | break; | |
161 | case ACTION_TYPE_MAP_PORT_TO_COMP_IN_STREAM: | |
162 | BT_PUT(action->payload.map_port_to_comp_in_stream.stream); | |
163 | BT_PUT(action->payload.map_port_to_comp_in_stream.component); | |
164 | BT_PUT(action->payload.map_port_to_comp_in_stream.port); | |
165 | break; | |
166 | case ACTION_TYPE_ADD_STREAM_STATE: | |
167 | BT_PUT(action->payload.add_stream_state.stream); | |
168 | destroy_stream_state( | |
169 | action->payload.add_stream_state.stream_state); | |
170 | action->payload.add_stream_state.stream_state = NULL; | |
171 | break; | |
172 | case ACTION_TYPE_SET_STREAM_STATE_CUR_PACKET: | |
173 | BT_PUT(action->payload.set_stream_state_cur_packet.packet); | |
174 | break; | |
175 | case ACTION_TYPE_SET_STREAM_STATE_IS_ENDED: | |
176 | break; | |
2ec84d26 PP |
177 | case ACTION_TYPE_UPDATE_STREAM_STATE_DISCARDED_PACKETS: |
178 | case ACTION_TYPE_UPDATE_STREAM_STATE_DISCARDED_EVENTS: | |
179 | BT_PUT(action->payload.update_stream_state_discarded_elements.cur_begin); | |
180 | break; | |
3230ee6b | 181 | default: |
2ec84d26 | 182 | BT_LOGF("Unexpected action's type: type=%d", action->type); |
0fbb9a9f | 183 | abort(); |
3230ee6b PP |
184 | } |
185 | } | |
186 | ||
187 | static | |
188 | void add_action(struct bt_notification_iterator *iterator, | |
189 | struct action *action) | |
190 | { | |
191 | g_array_append_val(iterator->actions, *action); | |
192 | } | |
193 | ||
194 | static | |
195 | void clear_actions(struct bt_notification_iterator *iterator) | |
196 | { | |
197 | size_t i; | |
198 | ||
199 | for (i = 0; i < iterator->actions->len; i++) { | |
200 | struct action *action = &g_array_index(iterator->actions, | |
201 | struct action, i); | |
202 | ||
203 | destroy_action(action); | |
204 | } | |
205 | ||
206 | g_array_set_size(iterator->actions, 0); | |
207 | } | |
208 | ||
5af447e5 PP |
209 | static inline |
210 | const char *action_type_string(enum action_type type) | |
211 | { | |
212 | switch (type) { | |
213 | case ACTION_TYPE_PUSH_NOTIF: | |
214 | return "ACTION_TYPE_PUSH_NOTIF"; | |
215 | case ACTION_TYPE_MAP_PORT_TO_COMP_IN_STREAM: | |
216 | return "ACTION_TYPE_MAP_PORT_TO_COMP_IN_STREAM"; | |
217 | case ACTION_TYPE_ADD_STREAM_STATE: | |
218 | return "ACTION_TYPE_ADD_STREAM_STATE"; | |
219 | case ACTION_TYPE_SET_STREAM_STATE_IS_ENDED: | |
220 | return "ACTION_TYPE_SET_STREAM_STATE_IS_ENDED"; | |
221 | case ACTION_TYPE_SET_STREAM_STATE_CUR_PACKET: | |
222 | return "ACTION_TYPE_SET_STREAM_STATE_CUR_PACKET"; | |
2ec84d26 PP |
223 | case ACTION_TYPE_UPDATE_STREAM_STATE_DISCARDED_PACKETS: |
224 | return "ACTION_TYPE_UPDATE_STREAM_STATE_DISCARDED_PACKETS"; | |
225 | case ACTION_TYPE_UPDATE_STREAM_STATE_DISCARDED_EVENTS: | |
226 | return "ACTION_TYPE_UPDATE_STREAM_STATE_DISCARDED_EVENTS"; | |
5af447e5 PP |
227 | default: |
228 | return "(unknown)"; | |
229 | } | |
230 | } | |
231 | ||
3230ee6b PP |
232 | static |
233 | void apply_actions(struct bt_notification_iterator *iterator) | |
234 | { | |
235 | size_t i; | |
236 | ||
5af447e5 PP |
237 | BT_LOGV("Applying notification's iterator current actions: " |
238 | "count=%u", iterator->actions->len); | |
239 | ||
3230ee6b PP |
240 | for (i = 0; i < iterator->actions->len; i++) { |
241 | struct action *action = &g_array_index(iterator->actions, | |
242 | struct action, i); | |
243 | ||
5af447e5 PP |
244 | BT_LOGV("Applying action: index=%zu, type=%s", |
245 | i, action_type_string(action->type)); | |
246 | ||
3230ee6b PP |
247 | switch (action->type) { |
248 | case ACTION_TYPE_PUSH_NOTIF: | |
249 | /* Move notification to queue */ | |
250 | g_queue_push_head(iterator->queue, | |
251 | action->payload.push_notif.notif); | |
252 | bt_notification_freeze( | |
253 | action->payload.push_notif.notif); | |
254 | action->payload.push_notif.notif = NULL; | |
255 | break; | |
256 | case ACTION_TYPE_MAP_PORT_TO_COMP_IN_STREAM: | |
257 | bt_ctf_stream_map_component_to_port( | |
258 | action->payload.map_port_to_comp_in_stream.stream, | |
259 | action->payload.map_port_to_comp_in_stream.component, | |
260 | action->payload.map_port_to_comp_in_stream.port); | |
261 | break; | |
262 | case ACTION_TYPE_ADD_STREAM_STATE: | |
263 | /* Move stream state to hash table */ | |
264 | g_hash_table_insert(iterator->stream_states, | |
265 | action->payload.add_stream_state.stream, | |
266 | action->payload.add_stream_state.stream_state); | |
267 | ||
268 | action->payload.add_stream_state.stream_state = NULL; | |
269 | break; | |
270 | case ACTION_TYPE_SET_STREAM_STATE_IS_ENDED: | |
271 | /* | |
272 | * We know that this stream is ended. We need to | |
273 | * remember this as long as the stream exists to | |
274 | * enforce that the same stream does not end | |
275 | * twice. | |
276 | * | |
277 | * Here we add a destroy listener to the stream | |
278 | * which we put after (becomes weak as the hash | |
279 | * table key). If we were the last object to own | |
280 | * this stream, the destroy listener is called | |
281 | * when we call bt_put() which removes this | |
282 | * stream state completely. This is important | |
283 | * because the memory used by this stream object | |
284 | * could be reused for another stream, and they | |
285 | * must have different states. | |
286 | */ | |
287 | bt_ctf_stream_add_destroy_listener( | |
288 | action->payload.set_stream_state_is_ended.stream_state->stream, | |
289 | stream_destroy_listener, iterator); | |
c55a9f58 | 290 | action->payload.set_stream_state_is_ended.stream_state->is_ended = BT_TRUE; |
3230ee6b PP |
291 | BT_PUT(action->payload.set_stream_state_is_ended.stream_state->stream); |
292 | break; | |
293 | case ACTION_TYPE_SET_STREAM_STATE_CUR_PACKET: | |
294 | /* Move packet to stream state's current packet */ | |
295 | BT_MOVE(action->payload.set_stream_state_cur_packet.stream_state->cur_packet, | |
296 | action->payload.set_stream_state_cur_packet.packet); | |
297 | break; | |
2ec84d26 PP |
298 | case ACTION_TYPE_UPDATE_STREAM_STATE_DISCARDED_PACKETS: |
299 | case ACTION_TYPE_UPDATE_STREAM_STATE_DISCARDED_EVENTS: | |
300 | { | |
301 | struct discarded_elements_state *state; | |
302 | ||
303 | if (action->type == ACTION_TYPE_UPDATE_STREAM_STATE_DISCARDED_PACKETS) { | |
304 | state = &action->payload.update_stream_state_discarded_elements.stream_state->discarded_packets_state; | |
305 | } else { | |
306 | state = &action->payload.update_stream_state_discarded_elements.stream_state->discarded_events_state; | |
307 | } | |
308 | ||
309 | BT_MOVE(state->cur_begin, | |
310 | action->payload.update_stream_state_discarded_elements.cur_begin); | |
311 | state->cur_count = action->payload.update_stream_state_discarded_elements.cur_count; | |
312 | break; | |
313 | } | |
3230ee6b | 314 | default: |
2ec84d26 PP |
315 | BT_LOGF("Unexpected action's type: type=%d", |
316 | action->type); | |
0fbb9a9f | 317 | abort(); |
3230ee6b PP |
318 | } |
319 | } | |
320 | ||
321 | clear_actions(iterator); | |
322 | } | |
323 | ||
324 | static | |
325 | struct stream_state *create_stream_state(struct bt_ctf_stream *stream) | |
326 | { | |
327 | struct stream_state *stream_state = g_new0(struct stream_state, 1); | |
328 | ||
329 | if (!stream_state) { | |
5af447e5 | 330 | BT_LOGE_STR("Failed to allocate one stream state."); |
3230ee6b PP |
331 | goto end; |
332 | } | |
333 | ||
126215b4 MD |
334 | /* |
335 | * The packet index is a monotonic counter which may not start | |
336 | * at 0 at the beginning of the stream. We therefore need to | |
337 | * have an internal object initial state of -1ULL to distinguish | |
338 | * between initial state and having seen a packet with | |
339 | * seqnum = 0. | |
340 | */ | |
341 | stream_state->discarded_packets_state.cur_count = -1ULL; | |
342 | ||
3230ee6b PP |
343 | /* |
344 | * We keep a reference to the stream until we know it's ended | |
345 | * because we need to be able to create an automatic "stream | |
346 | * end" notification when the user's "next" method returns | |
347 | * BT_NOTIFICATION_ITERATOR_STATUS_END. | |
348 | * | |
349 | * We put this reference when the stream is marked as ended. | |
350 | */ | |
351 | stream_state->stream = bt_get(stream); | |
5af447e5 PP |
352 | BT_LOGV("Created stream state: stream-addr=%p, stream-name=\"%s\", " |
353 | "stream-state-addr=%p", | |
354 | stream, bt_ctf_stream_get_name(stream), stream_state); | |
3230ee6b PP |
355 | |
356 | end: | |
357 | return stream_state; | |
358 | } | |
47e5a032 JG |
359 | |
360 | static | |
b8a06801 | 361 | void bt_notification_iterator_destroy(struct bt_object *obj) |
47e5a032 | 362 | { |
8738a040 JG |
363 | struct bt_notification_iterator *iterator; |
364 | ||
b8a06801 | 365 | assert(obj); |
d3eb6e8f | 366 | |
bd14d768 PP |
367 | /* |
368 | * The notification iterator's reference count is 0 if we're | |
369 | * here. Increment it to avoid a double-destroy (possibly | |
370 | * infinitely recursive). This could happen for example if the | |
371 | * notification iterator's finalization function does bt_get() | |
372 | * (or anything that causes bt_get() to be called) on itself | |
373 | * (ref. count goes from 0 to 1), and then bt_put(): the | |
374 | * reference count would go from 1 to 0 again and this function | |
375 | * would be called again. | |
376 | */ | |
377 | obj->ref_count.count++; | |
378 | iterator = container_of(obj, struct bt_notification_iterator, base); | |
5af447e5 PP |
379 | BT_LOGD("Destroying notification iterator object: addr=%p", |
380 | iterator); | |
bd14d768 | 381 | bt_notification_iterator_finalize(iterator); |
d3eb6e8f | 382 | |
3230ee6b PP |
383 | if (iterator->queue) { |
384 | struct bt_notification *notif; | |
385 | ||
5af447e5 PP |
386 | BT_LOGD("Putting notifications in queue."); |
387 | ||
3230ee6b PP |
388 | while ((notif = g_queue_pop_tail(iterator->queue))) { |
389 | bt_put(notif); | |
390 | } | |
391 | ||
392 | g_queue_free(iterator->queue); | |
393 | } | |
394 | ||
395 | if (iterator->stream_states) { | |
396 | /* | |
397 | * Remove our destroy listener from each stream which | |
398 | * has a state in this iterator. Otherwise the destroy | |
399 | * listener would be called with an invalid/other | |
400 | * notification iterator object. | |
401 | */ | |
402 | GHashTableIter ht_iter; | |
403 | gpointer stream_gptr, stream_state_gptr; | |
404 | ||
405 | g_hash_table_iter_init(&ht_iter, iterator->stream_states); | |
406 | ||
407 | while (g_hash_table_iter_next(&ht_iter, &stream_gptr, &stream_state_gptr)) { | |
408 | assert(stream_gptr); | |
5af447e5 PP |
409 | |
410 | BT_LOGD_STR("Removing stream's destroy listener for notification iterator."); | |
3230ee6b PP |
411 | bt_ctf_stream_remove_destroy_listener( |
412 | (void *) stream_gptr, stream_destroy_listener, | |
413 | iterator); | |
414 | } | |
415 | ||
416 | g_hash_table_destroy(iterator->stream_states); | |
417 | } | |
418 | ||
419 | if (iterator->actions) { | |
420 | g_array_free(iterator->actions, TRUE); | |
421 | } | |
422 | ||
bd14d768 PP |
423 | if (iterator->connection) { |
424 | /* | |
425 | * Remove ourself from the originating connection so | |
426 | * that it does not try to finalize a dangling pointer | |
427 | * later. | |
428 | */ | |
429 | bt_connection_remove_iterator(iterator->connection, iterator); | |
430 | } | |
431 | ||
5af447e5 | 432 | BT_LOGD_STR("Putting current notification."); |
3230ee6b | 433 | bt_put(iterator->current_notification); |
8738a040 | 434 | g_free(iterator); |
47e5a032 JG |
435 | } |
436 | ||
bd14d768 PP |
437 | BT_HIDDEN |
438 | void bt_notification_iterator_finalize( | |
439 | struct bt_notification_iterator *iterator) | |
440 | { | |
441 | struct bt_component_class *comp_class = NULL; | |
442 | bt_component_class_notification_iterator_finalize_method | |
443 | finalize_method = NULL; | |
444 | ||
445 | assert(iterator); | |
446 | ||
447 | switch (iterator->state) { | |
448 | case BT_NOTIFICATION_ITERATOR_STATE_FINALIZED: | |
449 | case BT_NOTIFICATION_ITERATOR_STATE_FINALIZED_AND_ENDED: | |
450 | /* Already finalized */ | |
5af447e5 PP |
451 | BT_LOGD("Not finalizing notification iterator: already finalized: " |
452 | "addr=%p", iterator); | |
bd14d768 PP |
453 | return; |
454 | default: | |
455 | break; | |
456 | } | |
457 | ||
5af447e5 PP |
458 | BT_LOGD("Finalizing notification iterator: addr=%p", iterator); |
459 | ||
df14f8af | 460 | if (iterator->state == BT_NOTIFICATION_ITERATOR_STATE_ENDED) { |
5af447e5 PP |
461 | BT_LOGD("Updating notification iterator's state: " |
462 | "new-state=BT_NOTIFICATION_ITERATOR_STATE_FINALIZED_AND_ENDED"); | |
df14f8af MD |
463 | iterator->state = BT_NOTIFICATION_ITERATOR_STATE_FINALIZED_AND_ENDED; |
464 | } else { | |
5af447e5 PP |
465 | BT_LOGD("Updating notification iterator's state: " |
466 | "new-state=BT_NOTIFICATION_ITERATOR_STATE_FINALIZED"); | |
df14f8af MD |
467 | iterator->state = BT_NOTIFICATION_ITERATOR_STATE_FINALIZED; |
468 | } | |
469 | ||
bd14d768 PP |
470 | assert(iterator->upstream_component); |
471 | comp_class = iterator->upstream_component->class; | |
472 | ||
473 | /* Call user-defined destroy method */ | |
474 | switch (comp_class->type) { | |
475 | case BT_COMPONENT_CLASS_TYPE_SOURCE: | |
476 | { | |
477 | struct bt_component_class_source *source_class; | |
478 | ||
479 | source_class = container_of(comp_class, struct bt_component_class_source, parent); | |
480 | finalize_method = source_class->methods.iterator.finalize; | |
481 | break; | |
482 | } | |
483 | case BT_COMPONENT_CLASS_TYPE_FILTER: | |
484 | { | |
485 | struct bt_component_class_filter *filter_class; | |
486 | ||
487 | filter_class = container_of(comp_class, struct bt_component_class_filter, parent); | |
488 | finalize_method = filter_class->methods.iterator.finalize; | |
489 | break; | |
490 | } | |
491 | default: | |
492 | /* Unreachable */ | |
0fbb9a9f | 493 | abort(); |
bd14d768 PP |
494 | } |
495 | ||
496 | if (finalize_method) { | |
5af447e5 PP |
497 | BT_LOGD("Calling user's finalization method: addr=%p", |
498 | iterator); | |
bd14d768 PP |
499 | finalize_method( |
500 | bt_private_notification_iterator_from_notification_iterator(iterator)); | |
501 | } | |
502 | ||
bd14d768 PP |
503 | iterator->upstream_component = NULL; |
504 | iterator->upstream_port = NULL; | |
5af447e5 | 505 | BT_LOGD("Finalized notification iterator: addr=%p", iterator); |
bd14d768 PP |
506 | } |
507 | ||
508 | BT_HIDDEN | |
509 | void bt_notification_iterator_set_connection( | |
510 | struct bt_notification_iterator *iterator, | |
511 | struct bt_connection *connection) | |
512 | { | |
513 | assert(iterator); | |
514 | iterator->connection = connection; | |
5af447e5 PP |
515 | BT_LOGV("Set notification iterator's connection: " |
516 | "iter-addr=%p, conn-addr=%p", iterator, connection); | |
bd14d768 PP |
517 | } |
518 | ||
fa054faf PP |
519 | static |
520 | int create_subscription_mask_from_notification_types( | |
521 | struct bt_notification_iterator *iterator, | |
522 | const enum bt_notification_type *notif_types) | |
523 | { | |
524 | const enum bt_notification_type *notif_type; | |
525 | int ret = 0; | |
526 | ||
527 | assert(notif_types); | |
528 | iterator->subscription_mask = 0; | |
529 | ||
530 | for (notif_type = notif_types; | |
531 | *notif_type != BT_NOTIFICATION_TYPE_SENTINEL; | |
532 | notif_type++) { | |
533 | switch (*notif_type) { | |
534 | case BT_NOTIFICATION_TYPE_ALL: | |
535 | iterator->subscription_mask |= | |
536 | BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_EVENT | | |
537 | BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_INACTIVITY | | |
538 | BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_STREAM_BEGIN | | |
539 | BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_STREAM_END | | |
540 | BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_PACKET_BEGIN | | |
2ec84d26 PP |
541 | BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_PACKET_END | |
542 | BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_DISCARDED_EVENTS | | |
543 | BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_DISCARDED_PACKETS; | |
fa054faf PP |
544 | break; |
545 | case BT_NOTIFICATION_TYPE_EVENT: | |
546 | iterator->subscription_mask |= BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_EVENT; | |
547 | break; | |
548 | case BT_NOTIFICATION_TYPE_INACTIVITY: | |
549 | iterator->subscription_mask |= BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_INACTIVITY; | |
550 | break; | |
551 | case BT_NOTIFICATION_TYPE_STREAM_BEGIN: | |
552 | iterator->subscription_mask |= BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_STREAM_BEGIN; | |
553 | break; | |
554 | case BT_NOTIFICATION_TYPE_STREAM_END: | |
555 | iterator->subscription_mask |= BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_STREAM_END; | |
556 | break; | |
557 | case BT_NOTIFICATION_TYPE_PACKET_BEGIN: | |
558 | iterator->subscription_mask |= BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_PACKET_BEGIN; | |
559 | break; | |
560 | case BT_NOTIFICATION_TYPE_PACKET_END: | |
561 | iterator->subscription_mask |= BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_PACKET_END; | |
562 | break; | |
2ec84d26 PP |
563 | case BT_NOTIFICATION_TYPE_DISCARDED_EVENTS: |
564 | iterator->subscription_mask |= BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_DISCARDED_EVENTS; | |
565 | break; | |
566 | case BT_NOTIFICATION_TYPE_DISCARDED_PACKETS: | |
567 | iterator->subscription_mask |= BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_DISCARDED_PACKETS; | |
568 | break; | |
fa054faf PP |
569 | default: |
570 | ret = -1; | |
571 | goto end; | |
572 | } | |
5af447e5 PP |
573 | |
574 | BT_LOGV("Added notification type to subscription mask: " | |
575 | "type=%s, mask=%x", | |
576 | bt_notification_type_string(*notif_type), | |
577 | iterator->subscription_mask); | |
fa054faf PP |
578 | } |
579 | ||
580 | end: | |
581 | return ret; | |
582 | } | |
583 | ||
47e5a032 | 584 | BT_HIDDEN |
73d5c1ad | 585 | enum bt_connection_status bt_notification_iterator_create( |
3230ee6b | 586 | struct bt_component *upstream_comp, |
fa054faf | 587 | struct bt_port *upstream_port, |
bd14d768 | 588 | const enum bt_notification_type *notification_types, |
73d5c1ad PP |
589 | struct bt_connection *connection, |
590 | struct bt_notification_iterator **user_iterator) | |
47e5a032 | 591 | { |
73d5c1ad | 592 | enum bt_connection_status status = BT_CONNECTION_STATUS_OK; |
d3e4dcd8 | 593 | enum bt_component_class_type type; |
47e5a032 JG |
594 | struct bt_notification_iterator *iterator = NULL; |
595 | ||
3230ee6b PP |
596 | assert(upstream_comp); |
597 | assert(upstream_port); | |
fa054faf | 598 | assert(notification_types); |
3230ee6b | 599 | assert(bt_port_is_connected(upstream_port)); |
73d5c1ad | 600 | assert(user_iterator); |
5af447e5 PP |
601 | BT_LOGD("Creating notification iterator: " |
602 | "upstream-comp-addr=%p, upstream-comp-name=\"%s\", " | |
603 | "upstream-port-addr=%p, upstream-port-name=\"%s\", " | |
604 | "conn-addr=%p", | |
605 | upstream_comp, bt_component_get_name(upstream_comp), | |
606 | upstream_port, bt_port_get_name(upstream_port), | |
607 | connection); | |
3230ee6b | 608 | type = bt_component_get_class_type(upstream_comp); |
ef2f7566 PP |
609 | assert(type == BT_COMPONENT_CLASS_TYPE_SOURCE || |
610 | type == BT_COMPONENT_CLASS_TYPE_FILTER); | |
47e5a032 JG |
611 | iterator = g_new0(struct bt_notification_iterator, 1); |
612 | if (!iterator) { | |
5af447e5 | 613 | BT_LOGE_STR("Failed to allocate one notification iterator."); |
73d5c1ad PP |
614 | status = BT_CONNECTION_STATUS_NOMEM; |
615 | goto end; | |
47e5a032 JG |
616 | } |
617 | ||
b8a06801 | 618 | bt_object_init(iterator, bt_notification_iterator_destroy); |
3230ee6b | 619 | |
fa054faf PP |
620 | if (create_subscription_mask_from_notification_types(iterator, |
621 | notification_types)) { | |
5af447e5 | 622 | BT_LOGW_STR("Cannot create subscription mask from notification types."); |
73d5c1ad PP |
623 | status = BT_CONNECTION_STATUS_INVALID; |
624 | goto end; | |
fa054faf PP |
625 | } |
626 | ||
3230ee6b PP |
627 | iterator->stream_states = g_hash_table_new_full(g_direct_hash, |
628 | g_direct_equal, NULL, (GDestroyNotify) destroy_stream_state); | |
629 | if (!iterator->stream_states) { | |
5af447e5 | 630 | BT_LOGE_STR("Failed to allocate a GHashTable."); |
73d5c1ad PP |
631 | status = BT_CONNECTION_STATUS_NOMEM; |
632 | goto end; | |
3230ee6b PP |
633 | } |
634 | ||
635 | iterator->queue = g_queue_new(); | |
636 | if (!iterator->queue) { | |
5af447e5 | 637 | BT_LOGE_STR("Failed to allocate a GQueue."); |
73d5c1ad PP |
638 | status = BT_CONNECTION_STATUS_NOMEM; |
639 | goto end; | |
3230ee6b PP |
640 | } |
641 | ||
642 | iterator->actions = g_array_new(FALSE, FALSE, sizeof(struct action)); | |
643 | if (!iterator->actions) { | |
5af447e5 | 644 | BT_LOGE_STR("Failed to allocate a GArray."); |
73d5c1ad PP |
645 | status = BT_CONNECTION_STATUS_NOMEM; |
646 | goto end; | |
3230ee6b PP |
647 | } |
648 | ||
bd14d768 PP |
649 | iterator->upstream_component = upstream_comp; |
650 | iterator->upstream_port = upstream_port; | |
651 | iterator->connection = connection; | |
652 | iterator->state = BT_NOTIFICATION_ITERATOR_STATE_ACTIVE; | |
5af447e5 PP |
653 | BT_LOGD("Created notification iterator: " |
654 | "upstream-comp-addr=%p, upstream-comp-name=\"%s\", " | |
655 | "upstream-port-addr=%p, upstream-port-name=\"%s\", " | |
656 | "conn-addr=%p, iter-addr=%p", | |
657 | upstream_comp, bt_component_get_name(upstream_comp), | |
658 | upstream_port, bt_port_get_name(upstream_port), | |
659 | connection, iterator); | |
1a6a376a PP |
660 | |
661 | /* Move reference to user */ | |
662 | *user_iterator = iterator; | |
663 | iterator = NULL; | |
3230ee6b | 664 | |
47e5a032 | 665 | end: |
73d5c1ad PP |
666 | bt_put(iterator); |
667 | return status; | |
47e5a032 JG |
668 | } |
669 | ||
890882ef PP |
670 | void *bt_private_notification_iterator_get_user_data( |
671 | struct bt_private_notification_iterator *private_iterator) | |
ea8d3e58 | 672 | { |
890882ef PP |
673 | struct bt_notification_iterator *iterator = |
674 | bt_notification_iterator_from_private(private_iterator); | |
675 | ||
ea8d3e58 JG |
676 | return iterator ? iterator->user_data : NULL; |
677 | } | |
678 | ||
679 | enum bt_notification_iterator_status | |
890882ef PP |
680 | bt_private_notification_iterator_set_user_data( |
681 | struct bt_private_notification_iterator *private_iterator, | |
682 | void *data) | |
ea8d3e58 JG |
683 | { |
684 | enum bt_notification_iterator_status ret = | |
685 | BT_NOTIFICATION_ITERATOR_STATUS_OK; | |
890882ef PP |
686 | struct bt_notification_iterator *iterator = |
687 | bt_notification_iterator_from_private(private_iterator); | |
ea8d3e58 | 688 | |
d3eb6e8f | 689 | if (!iterator) { |
5af447e5 | 690 | BT_LOGW_STR("Invalid parameter: notification iterator is NULL."); |
fe8ad2b6 | 691 | ret = BT_NOTIFICATION_ITERATOR_STATUS_INVALID; |
ea8d3e58 JG |
692 | goto end; |
693 | } | |
694 | ||
695 | iterator->user_data = data; | |
5af447e5 PP |
696 | BT_LOGV("Set notification iterator's user data: " |
697 | "iter-addr=%p, user-data-addr=%p", iterator, data); | |
698 | ||
8738a040 JG |
699 | end: |
700 | return ret; | |
701 | } | |
413bc2c4 | 702 | |
53d45b87 JG |
703 | struct bt_notification *bt_notification_iterator_get_notification( |
704 | struct bt_notification_iterator *iterator) | |
705 | { | |
41a2b7ae | 706 | struct bt_notification *notification = NULL; |
d3eb6e8f | 707 | |
41a2b7ae | 708 | if (!iterator) { |
5af447e5 | 709 | BT_LOGW_STR("Invalid parameter: notification iterator is NULL."); |
41a2b7ae | 710 | goto end; |
d3eb6e8f | 711 | } |
d3eb6e8f | 712 | |
41a2b7ae | 713 | notification = bt_get(iterator->current_notification); |
d3eb6e8f | 714 | |
41a2b7ae PP |
715 | end: |
716 | return notification; | |
53d45b87 JG |
717 | } |
718 | ||
fa054faf PP |
719 | static |
720 | enum bt_notification_iterator_notif_type | |
721 | bt_notification_iterator_notif_type_from_notif_type( | |
722 | enum bt_notification_type notif_type) | |
723 | { | |
724 | enum bt_notification_iterator_notif_type iter_notif_type; | |
725 | ||
726 | switch (notif_type) { | |
727 | case BT_NOTIFICATION_TYPE_EVENT: | |
728 | iter_notif_type = BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_EVENT; | |
729 | break; | |
730 | case BT_NOTIFICATION_TYPE_INACTIVITY: | |
731 | iter_notif_type = BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_INACTIVITY; | |
732 | break; | |
733 | case BT_NOTIFICATION_TYPE_STREAM_BEGIN: | |
734 | iter_notif_type = BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_STREAM_BEGIN; | |
735 | break; | |
736 | case BT_NOTIFICATION_TYPE_STREAM_END: | |
737 | iter_notif_type = BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_STREAM_END; | |
738 | break; | |
739 | case BT_NOTIFICATION_TYPE_PACKET_BEGIN: | |
740 | iter_notif_type = BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_PACKET_BEGIN; | |
741 | break; | |
742 | case BT_NOTIFICATION_TYPE_PACKET_END: | |
743 | iter_notif_type = BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_PACKET_END; | |
744 | break; | |
2ec84d26 PP |
745 | case BT_NOTIFICATION_TYPE_DISCARDED_EVENTS: |
746 | iter_notif_type = BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_DISCARDED_EVENTS; | |
747 | break; | |
748 | case BT_NOTIFICATION_TYPE_DISCARDED_PACKETS: | |
749 | iter_notif_type = BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_DISCARDED_PACKETS; | |
750 | break; | |
fa054faf | 751 | default: |
0fbb9a9f | 752 | abort(); |
fa054faf PP |
753 | } |
754 | ||
755 | return iter_notif_type; | |
756 | } | |
757 | ||
3230ee6b | 758 | static |
c55a9f58 | 759 | bt_bool validate_notification(struct bt_notification_iterator *iterator, |
3230ee6b PP |
760 | struct bt_notification *notif, |
761 | struct bt_ctf_stream *notif_stream, | |
762 | struct bt_ctf_packet *notif_packet) | |
763 | { | |
c55a9f58 | 764 | bt_bool is_valid = BT_TRUE; |
3230ee6b PP |
765 | struct stream_state *stream_state; |
766 | struct bt_port *stream_comp_cur_port; | |
767 | ||
768 | assert(notif_stream); | |
769 | stream_comp_cur_port = | |
770 | bt_ctf_stream_port_for_component(notif_stream, | |
771 | iterator->upstream_component); | |
772 | if (!stream_comp_cur_port) { | |
773 | /* | |
774 | * This is the first time this notification iterator | |
775 | * bumps into this stream. Add an action to map the | |
776 | * iterator's upstream component to the iterator's | |
777 | * upstream port in this stream. | |
778 | */ | |
779 | struct action action = { | |
780 | .type = ACTION_TYPE_MAP_PORT_TO_COMP_IN_STREAM, | |
781 | .payload.map_port_to_comp_in_stream = { | |
782 | .stream = bt_get(notif_stream), | |
783 | .component = bt_get(iterator->upstream_component), | |
784 | .port = bt_get(iterator->upstream_port), | |
785 | }, | |
786 | }; | |
787 | ||
788 | add_action(iterator, &action); | |
789 | } else { | |
790 | if (stream_comp_cur_port != iterator->upstream_port) { | |
791 | /* | |
792 | * It looks like two different ports of the same | |
793 | * component are emitting notifications which | |
794 | * have references to the same stream. This is | |
795 | * bad: the API guarantees that it can never | |
796 | * happen. | |
797 | */ | |
5af447e5 PP |
798 | BT_LOGW("Two different ports of the same component are emitting notifications which refer to the same stream: " |
799 | "stream-addr=%p, stream-name=\"%s\", " | |
800 | "stream-comp-cur-port-addr=%p, " | |
801 | "stream-comp-cur-port-name=%p, " | |
802 | "iter-upstream-port-addr=%p, " | |
803 | "iter-upstream-port-name=%s", | |
804 | notif_stream, | |
805 | bt_ctf_stream_get_name(notif_stream), | |
806 | stream_comp_cur_port, | |
807 | bt_port_get_name(stream_comp_cur_port), | |
808 | iterator->upstream_port, | |
809 | bt_port_get_name(iterator->upstream_port)); | |
c55a9f58 | 810 | is_valid = BT_FALSE; |
3230ee6b PP |
811 | goto end; |
812 | } | |
813 | ||
814 | } | |
815 | ||
816 | stream_state = g_hash_table_lookup(iterator->stream_states, | |
817 | notif_stream); | |
818 | if (stream_state) { | |
5af447e5 PP |
819 | BT_LOGV("Stream state already exists: " |
820 | "stream-addr=%p, stream-name=\"%s\", " | |
821 | "stream-state-addr=%p", | |
822 | notif_stream, | |
823 | bt_ctf_stream_get_name(notif_stream), stream_state); | |
824 | ||
3230ee6b PP |
825 | if (stream_state->is_ended) { |
826 | /* | |
827 | * There's a new notification which has a | |
828 | * reference to a stream which, from this | |
829 | * iterator's point of view, is ended ("end of | |
830 | * stream" notification was returned). This is | |
831 | * bad: the API guarantees that it can never | |
832 | * happen. | |
833 | */ | |
5af447e5 PP |
834 | BT_LOGW("Stream is already ended: " |
835 | "stream-addr=%p, stream-name=\"%s\"", | |
836 | notif_stream, | |
837 | bt_ctf_stream_get_name(notif_stream)); | |
c55a9f58 | 838 | is_valid = BT_FALSE; |
3230ee6b PP |
839 | goto end; |
840 | } | |
841 | ||
842 | switch (notif->type) { | |
843 | case BT_NOTIFICATION_TYPE_STREAM_BEGIN: | |
844 | /* | |
845 | * We already have a stream state, which means | |
846 | * we already returned a "stream begin" | |
847 | * notification: this is an invalid duplicate. | |
848 | */ | |
5af447e5 PP |
849 | BT_LOGW("Duplicate stream beginning notification: " |
850 | "stream-addr=%p, stream-name=\"%s\"", | |
851 | notif_stream, | |
852 | bt_ctf_stream_get_name(notif_stream)); | |
c55a9f58 | 853 | is_valid = BT_FALSE; |
3230ee6b PP |
854 | goto end; |
855 | case BT_NOTIFICATION_TYPE_PACKET_BEGIN: | |
856 | if (notif_packet == stream_state->cur_packet) { | |
857 | /* Duplicate "packet begin" notification */ | |
5af447e5 PP |
858 | BT_LOGW("Duplicate stream beginning notification: " |
859 | "stream-addr=%p, stream-name=\"%s\", " | |
860 | "packet-addr=%p", | |
861 | notif_stream, | |
862 | bt_ctf_stream_get_name(notif_stream), | |
863 | notif_packet); | |
c55a9f58 | 864 | is_valid = BT_FALSE; |
3230ee6b PP |
865 | goto end; |
866 | } | |
867 | break; | |
868 | default: | |
869 | break; | |
870 | } | |
871 | } | |
872 | ||
873 | end: | |
874 | return is_valid; | |
875 | } | |
876 | ||
fa054faf | 877 | static |
c55a9f58 | 878 | bt_bool is_subscribed_to_notification_type(struct bt_notification_iterator *iterator, |
fa054faf PP |
879 | enum bt_notification_type notif_type) |
880 | { | |
881 | uint32_t iter_notif_type = | |
882 | (uint32_t) bt_notification_iterator_notif_type_from_notif_type( | |
883 | notif_type); | |
884 | ||
c55a9f58 | 885 | return (iter_notif_type & iterator->subscription_mask) ? BT_TRUE : BT_FALSE; |
fa054faf PP |
886 | } |
887 | ||
3230ee6b PP |
888 | static |
889 | void add_action_push_notif(struct bt_notification_iterator *iterator, | |
890 | struct bt_notification *notif) | |
891 | { | |
892 | struct action action = { | |
893 | .type = ACTION_TYPE_PUSH_NOTIF, | |
3230ee6b PP |
894 | }; |
895 | ||
896 | assert(notif); | |
fa054faf PP |
897 | |
898 | if (!is_subscribed_to_notification_type(iterator, notif->type)) { | |
899 | return; | |
900 | } | |
901 | ||
902 | action.payload.push_notif.notif = bt_get(notif); | |
3230ee6b | 903 | add_action(iterator, &action); |
5af447e5 | 904 | BT_LOGV("Added \"push notification\" action: notif-addr=%p", notif); |
3230ee6b PP |
905 | } |
906 | ||
907 | static | |
908 | int add_action_push_notif_stream_begin( | |
909 | struct bt_notification_iterator *iterator, | |
910 | struct bt_ctf_stream *stream) | |
911 | { | |
912 | int ret = 0; | |
913 | struct bt_notification *stream_begin_notif = NULL; | |
914 | ||
fa054faf PP |
915 | if (!is_subscribed_to_notification_type(iterator, |
916 | BT_NOTIFICATION_TYPE_STREAM_BEGIN)) { | |
5af447e5 PP |
917 | BT_LOGV("Not adding \"push stream beginning notification\" action: " |
918 | "notification iterator is not subscribed: addr=%p", | |
919 | iterator); | |
fa054faf PP |
920 | goto end; |
921 | } | |
922 | ||
3230ee6b PP |
923 | assert(stream); |
924 | stream_begin_notif = bt_notification_stream_begin_create(stream); | |
925 | if (!stream_begin_notif) { | |
5af447e5 | 926 | BT_LOGE_STR("Cannot create stream beginning notification."); |
3230ee6b PP |
927 | goto error; |
928 | } | |
929 | ||
930 | add_action_push_notif(iterator, stream_begin_notif); | |
5af447e5 PP |
931 | BT_LOGV("Added \"push stream beginning notification\" action: " |
932 | "stream-addr=%p, stream-name=\"%s\"", | |
933 | stream, bt_ctf_stream_get_name(stream)); | |
3230ee6b PP |
934 | goto end; |
935 | ||
936 | error: | |
937 | ret = -1; | |
938 | ||
939 | end: | |
940 | bt_put(stream_begin_notif); | |
941 | return ret; | |
942 | } | |
943 | ||
944 | static | |
945 | int add_action_push_notif_stream_end( | |
946 | struct bt_notification_iterator *iterator, | |
947 | struct bt_ctf_stream *stream) | |
948 | { | |
949 | int ret = 0; | |
950 | struct bt_notification *stream_end_notif = NULL; | |
951 | ||
fa054faf PP |
952 | if (!is_subscribed_to_notification_type(iterator, |
953 | BT_NOTIFICATION_TYPE_STREAM_END)) { | |
5af447e5 PP |
954 | BT_LOGV("Not adding \"push stream end notification\" action: " |
955 | "notification iterator is not subscribed: addr=%p", | |
956 | iterator); | |
fa054faf PP |
957 | goto end; |
958 | } | |
959 | ||
3230ee6b PP |
960 | assert(stream); |
961 | stream_end_notif = bt_notification_stream_end_create(stream); | |
962 | if (!stream_end_notif) { | |
5af447e5 | 963 | BT_LOGE_STR("Cannot create stream end notification."); |
3230ee6b PP |
964 | goto error; |
965 | } | |
966 | ||
967 | add_action_push_notif(iterator, stream_end_notif); | |
5af447e5 PP |
968 | BT_LOGV("Added \"push stream end notification\" action: " |
969 | "stream-addr=%p, stream-name=\"%s\"", | |
970 | stream, bt_ctf_stream_get_name(stream)); | |
3230ee6b PP |
971 | goto end; |
972 | ||
973 | error: | |
974 | ret = -1; | |
975 | ||
976 | end: | |
977 | bt_put(stream_end_notif); | |
978 | return ret; | |
979 | } | |
980 | ||
981 | static | |
982 | int add_action_push_notif_packet_begin( | |
983 | struct bt_notification_iterator *iterator, | |
984 | struct bt_ctf_packet *packet) | |
985 | { | |
986 | int ret = 0; | |
987 | struct bt_notification *packet_begin_notif = NULL; | |
988 | ||
fa054faf PP |
989 | if (!is_subscribed_to_notification_type(iterator, |
990 | BT_NOTIFICATION_TYPE_PACKET_BEGIN)) { | |
5af447e5 PP |
991 | BT_LOGV("Not adding \"push packet beginning notification\" action: " |
992 | "notification iterator is not subscribed: addr=%p", | |
993 | iterator); | |
fa054faf PP |
994 | goto end; |
995 | } | |
996 | ||
3230ee6b PP |
997 | assert(packet); |
998 | packet_begin_notif = bt_notification_packet_begin_create(packet); | |
999 | if (!packet_begin_notif) { | |
5af447e5 | 1000 | BT_LOGE_STR("Cannot create packet beginning notification."); |
3230ee6b PP |
1001 | goto error; |
1002 | } | |
1003 | ||
1004 | add_action_push_notif(iterator, packet_begin_notif); | |
5af447e5 PP |
1005 | BT_LOGV("Added \"push packet beginning notification\" action: " |
1006 | "packet-addr=%p", packet); | |
3230ee6b PP |
1007 | goto end; |
1008 | ||
1009 | error: | |
1010 | ret = -1; | |
1011 | ||
1012 | end: | |
1013 | bt_put(packet_begin_notif); | |
1014 | return ret; | |
1015 | } | |
1016 | ||
1017 | static | |
1018 | int add_action_push_notif_packet_end( | |
1019 | struct bt_notification_iterator *iterator, | |
1020 | struct bt_ctf_packet *packet) | |
1021 | { | |
1022 | int ret = 0; | |
1023 | struct bt_notification *packet_end_notif = NULL; | |
1024 | ||
fa054faf PP |
1025 | if (!is_subscribed_to_notification_type(iterator, |
1026 | BT_NOTIFICATION_TYPE_PACKET_END)) { | |
5af447e5 PP |
1027 | BT_LOGV("Not adding \"push packet end notification\" action: " |
1028 | "notification iterator is not subscribed: addr=%p", | |
1029 | iterator); | |
fa054faf PP |
1030 | goto end; |
1031 | } | |
1032 | ||
3230ee6b PP |
1033 | assert(packet); |
1034 | packet_end_notif = bt_notification_packet_end_create(packet); | |
1035 | if (!packet_end_notif) { | |
5af447e5 | 1036 | BT_LOGE_STR("Cannot create packet end notification."); |
3230ee6b PP |
1037 | goto error; |
1038 | } | |
1039 | ||
1040 | add_action_push_notif(iterator, packet_end_notif); | |
5af447e5 PP |
1041 | BT_LOGV("Added \"push packet end notification\" action: " |
1042 | "packet-addr=%p", packet); | |
3230ee6b PP |
1043 | goto end; |
1044 | ||
1045 | error: | |
1046 | ret = -1; | |
1047 | ||
1048 | end: | |
1049 | bt_put(packet_end_notif); | |
1050 | return ret; | |
1051 | } | |
1052 | ||
1053 | static | |
1054 | void add_action_set_stream_state_is_ended( | |
1055 | struct bt_notification_iterator *iterator, | |
1056 | struct stream_state *stream_state) | |
1057 | { | |
1058 | struct action action = { | |
1059 | .type = ACTION_TYPE_SET_STREAM_STATE_IS_ENDED, | |
1060 | .payload.set_stream_state_is_ended = { | |
1061 | .stream_state = stream_state, | |
1062 | }, | |
1063 | }; | |
1064 | ||
1065 | assert(stream_state); | |
1066 | add_action(iterator, &action); | |
5af447e5 PP |
1067 | BT_LOGV("Added \"set stream state's ended\" action: " |
1068 | "stream-state-addr=%p", stream_state); | |
3230ee6b PP |
1069 | } |
1070 | ||
1071 | static | |
1072 | void add_action_set_stream_state_cur_packet( | |
1073 | struct bt_notification_iterator *iterator, | |
1074 | struct stream_state *stream_state, | |
1075 | struct bt_ctf_packet *packet) | |
1076 | { | |
1077 | struct action action = { | |
1078 | .type = ACTION_TYPE_SET_STREAM_STATE_CUR_PACKET, | |
1079 | .payload.set_stream_state_cur_packet = { | |
1080 | .stream_state = stream_state, | |
1081 | .packet = bt_get(packet), | |
1082 | }, | |
1083 | }; | |
1084 | ||
1085 | assert(stream_state); | |
1086 | add_action(iterator, &action); | |
5af447e5 PP |
1087 | BT_LOGV("Added \"set stream state's current packet\" action: " |
1088 | "stream-state-addr=%p, packet-addr=%p", | |
1089 | stream_state, packet); | |
3230ee6b PP |
1090 | } |
1091 | ||
2ec84d26 PP |
1092 | static |
1093 | void add_action_update_stream_state_discarded_elements( | |
1094 | struct bt_notification_iterator *iterator, | |
1095 | enum action_type type, | |
1096 | struct stream_state *stream_state, | |
1097 | struct bt_ctf_clock_value *cur_begin, | |
1098 | uint64_t cur_count) | |
1099 | { | |
1100 | struct action action = { | |
1101 | .type = type, | |
1102 | .payload.update_stream_state_discarded_elements = { | |
1103 | .stream_state = stream_state, | |
1104 | .cur_begin = bt_get(cur_begin), | |
1105 | .cur_count = cur_count, | |
1106 | }, | |
1107 | }; | |
1108 | ||
1109 | assert(stream_state); | |
1110 | assert(type == ACTION_TYPE_UPDATE_STREAM_STATE_DISCARDED_PACKETS || | |
1111 | type == ACTION_TYPE_UPDATE_STREAM_STATE_DISCARDED_EVENTS); | |
1112 | add_action(iterator, &action); | |
1113 | if (type == ACTION_TYPE_UPDATE_STREAM_STATE_DISCARDED_PACKETS) { | |
1114 | BT_LOGV("Added \"update stream state's discarded packets\" action: " | |
1115 | "stream-state-addr=%p, cur-begin-addr=%p, cur-count=%" PRIu64, | |
1116 | stream_state, cur_begin, cur_count); | |
1117 | } else if (type == ACTION_TYPE_UPDATE_STREAM_STATE_DISCARDED_EVENTS) { | |
1118 | BT_LOGV("Added \"update stream state's discarded events\" action: " | |
1119 | "stream-state-addr=%p, cur-begin-addr=%p, cur-count=%" PRIu64, | |
1120 | stream_state, cur_begin, cur_count); | |
1121 | } | |
1122 | } | |
1123 | ||
3230ee6b PP |
1124 | static |
1125 | int ensure_stream_state_exists(struct bt_notification_iterator *iterator, | |
1126 | struct bt_notification *stream_begin_notif, | |
1127 | struct bt_ctf_stream *notif_stream, | |
2c212c05 | 1128 | struct stream_state **_stream_state) |
3230ee6b PP |
1129 | { |
1130 | int ret = 0; | |
2c212c05 | 1131 | struct stream_state *stream_state = NULL; |
3230ee6b PP |
1132 | |
1133 | if (!notif_stream) { | |
1134 | /* | |
1135 | * The notification does not reference any stream: no | |
1136 | * need to get or create a stream state. | |
1137 | */ | |
1138 | goto end; | |
1139 | } | |
1140 | ||
2c212c05 | 1141 | stream_state = g_hash_table_lookup(iterator->stream_states, |
3230ee6b | 1142 | notif_stream); |
2c212c05 | 1143 | if (!stream_state) { |
3230ee6b PP |
1144 | /* |
1145 | * This iterator did not bump into this stream yet: | |
1146 | * create a stream state and a "stream begin" | |
1147 | * notification. | |
1148 | */ | |
1149 | struct action action = { | |
1150 | .type = ACTION_TYPE_ADD_STREAM_STATE, | |
1151 | .payload.add_stream_state = { | |
1152 | .stream = bt_get(notif_stream), | |
1153 | .stream_state = NULL, | |
1154 | }, | |
1155 | }; | |
1156 | ||
2c212c05 | 1157 | stream_state = create_stream_state(notif_stream); |
3230ee6b | 1158 | if (!stream_state) { |
5af447e5 | 1159 | BT_LOGE_STR("Cannot create stream state."); |
3230ee6b PP |
1160 | goto error; |
1161 | } | |
1162 | ||
2c212c05 | 1163 | action.payload.add_stream_state.stream_state = stream_state; |
3230ee6b PP |
1164 | add_action(iterator, &action); |
1165 | ||
1166 | if (stream_begin_notif) { | |
1167 | add_action_push_notif(iterator, stream_begin_notif); | |
1168 | } else { | |
1169 | ret = add_action_push_notif_stream_begin(iterator, | |
1170 | notif_stream); | |
1171 | if (ret) { | |
5af447e5 | 1172 | BT_LOGE_STR("Cannot add \"push stream beginning notification\" action."); |
3230ee6b PP |
1173 | goto error; |
1174 | } | |
1175 | } | |
1176 | } | |
3230ee6b PP |
1177 | goto end; |
1178 | ||
1179 | error: | |
2c212c05 MD |
1180 | destroy_stream_state(stream_state); |
1181 | stream_state = NULL; | |
3230ee6b PP |
1182 | ret = -1; |
1183 | ||
1184 | end: | |
2c212c05 | 1185 | *_stream_state = stream_state; |
3230ee6b PP |
1186 | return ret; |
1187 | } | |
1188 | ||
2ec84d26 PP |
1189 | static |
1190 | struct bt_ctf_field *get_struct_field_uint(struct bt_ctf_field *struct_field, | |
1191 | const char *field_name) | |
1192 | { | |
1193 | struct bt_ctf_field *field = NULL; | |
1194 | struct bt_ctf_field_type *ft = NULL; | |
1195 | ||
1196 | field = bt_ctf_field_structure_get_field_by_name(struct_field, | |
1197 | field_name); | |
1198 | if (!field) { | |
1199 | BT_LOGV_STR("`%s` field does not exist."); | |
1200 | goto end; | |
1201 | } | |
1202 | ||
1203 | if (!bt_ctf_field_is_integer(field)) { | |
1204 | BT_LOGV("Skipping `%s` field because its type is not an integer field type: " | |
1205 | "field-addr=%p, ft-addr=%p, ft-id=%s", field_name, | |
1206 | field, ft, bt_ctf_field_type_id_string( | |
1207 | bt_ctf_field_type_get_type_id(ft))); | |
1208 | BT_PUT(field); | |
1209 | goto end; | |
1210 | } | |
1211 | ||
1212 | ft = bt_ctf_field_get_type(field); | |
1213 | assert(ft); | |
1214 | ||
1215 | if (bt_ctf_field_type_integer_is_signed(ft)) { | |
1216 | BT_LOGV("Skipping `%s` integer field because its type is signed: " | |
1217 | "field-addr=%p, ft-addr=%p", field_name, field, ft); | |
1218 | BT_PUT(field); | |
1219 | goto end; | |
1220 | } | |
1221 | ||
1222 | end: | |
1223 | bt_put(ft); | |
1224 | return field; | |
1225 | } | |
1226 | ||
1227 | static | |
1228 | uint64_t get_packet_context_events_discarded(struct bt_ctf_packet *packet) | |
1229 | { | |
1230 | struct bt_ctf_field *packet_context = NULL; | |
1231 | struct bt_ctf_field *field = NULL; | |
1232 | uint64_t retval = -1ULL; | |
1233 | int ret; | |
1234 | ||
1235 | packet_context = bt_ctf_packet_get_context(packet); | |
1236 | if (!packet_context) { | |
1237 | goto end; | |
1238 | } | |
1239 | ||
1240 | field = get_struct_field_uint(packet_context, "events_discarded"); | |
1241 | if (!field) { | |
1242 | BT_LOGV("`events_discarded` field does not exist in packet's context field: " | |
1243 | "packet-addr=%p, packet-context-field-addr=%p", | |
1244 | packet, packet_context); | |
1245 | goto end; | |
1246 | } | |
1247 | ||
1248 | assert(bt_ctf_field_is_integer(field)); | |
1249 | ret = bt_ctf_field_unsigned_integer_get_value(field, &retval); | |
1250 | if (ret) { | |
1251 | BT_LOGV("Cannot get raw value of packet's context field's `events_discarded` integer field: " | |
1252 | "packet-addr=%p, field-addr=%p", | |
1253 | packet, field); | |
1254 | retval = -1ULL; | |
1255 | goto end; | |
1256 | } | |
1257 | ||
1258 | end: | |
1259 | bt_put(packet_context); | |
1260 | bt_put(field); | |
1261 | return retval; | |
1262 | } | |
1263 | ||
1264 | static | |
126215b4 | 1265 | uint64_t get_packet_context_packet_seq_num(struct bt_ctf_packet *packet) |
2ec84d26 | 1266 | { |
126215b4 | 1267 | struct bt_ctf_field *packet_context = NULL; |
2ec84d26 PP |
1268 | struct bt_ctf_field *field = NULL; |
1269 | uint64_t retval = -1ULL; | |
1270 | int ret; | |
1271 | ||
126215b4 MD |
1272 | packet_context = bt_ctf_packet_get_context(packet); |
1273 | if (!packet_context) { | |
2ec84d26 PP |
1274 | goto end; |
1275 | } | |
1276 | ||
126215b4 | 1277 | field = get_struct_field_uint(packet_context, "packet_seq_num"); |
2ec84d26 | 1278 | if (!field) { |
126215b4 MD |
1279 | BT_LOGV("`packet_seq_num` field does not exist in packet's context field: " |
1280 | "packet-addr=%p, packet-context-field-addr=%p", | |
1281 | packet, packet_context); | |
2ec84d26 PP |
1282 | goto end; |
1283 | } | |
1284 | ||
1285 | assert(bt_ctf_field_is_integer(field)); | |
1286 | ret = bt_ctf_field_unsigned_integer_get_value(field, &retval); | |
1287 | if (ret) { | |
126215b4 | 1288 | BT_LOGV("Cannot get raw value of packet's context field's `packet_seq_num` integer field: " |
2ec84d26 PP |
1289 | "packet-addr=%p, field-addr=%p", |
1290 | packet, field); | |
1291 | retval = -1ULL; | |
1292 | goto end; | |
1293 | } | |
1294 | ||
1295 | end: | |
126215b4 | 1296 | bt_put(packet_context); |
2ec84d26 PP |
1297 | bt_put(field); |
1298 | return retval; | |
1299 | } | |
1300 | ||
1301 | static | |
1302 | int handle_discarded_packets(struct bt_notification_iterator *iterator, | |
1303 | struct bt_ctf_packet *packet, | |
1304 | struct bt_ctf_clock_value *ts_begin, | |
1305 | struct bt_ctf_clock_value *ts_end, | |
1306 | struct stream_state *stream_state) | |
1307 | { | |
1308 | struct bt_notification *notif = NULL; | |
1309 | uint64_t diff; | |
126215b4 | 1310 | uint64_t prev_count, next_count; |
2ec84d26 PP |
1311 | int ret = 0; |
1312 | ||
126215b4 | 1313 | next_count = get_packet_context_packet_seq_num(packet); |
2ec84d26 | 1314 | if (next_count == -1ULL) { |
126215b4 MD |
1315 | /* |
1316 | * Stream does not have seqnum field, skip discarded | |
1317 | * packets feature. | |
1318 | */ | |
2ec84d26 PP |
1319 | goto end; |
1320 | } | |
126215b4 | 1321 | prev_count = stream_state->discarded_packets_state.cur_count; |
2ec84d26 | 1322 | |
126215b4 MD |
1323 | if (prev_count != -1ULL) { |
1324 | if (next_count < prev_count) { | |
1325 | BT_LOGW("Current value of packet's context field's `packet_seq_num` field is lesser than the previous value for the same stream: " | |
1326 | "not updating the stream state's current value: " | |
1327 | "packet-addr=%p, prev-count=%" PRIu64 ", " | |
1328 | "cur-count=%" PRIu64, | |
1329 | packet, prev_count, next_count); | |
1330 | goto end; | |
1331 | } | |
1332 | if (next_count == prev_count) { | |
1333 | BT_LOGW("Current value of packet's context field's `packet_seq_num` field is equal to the previous value for the same stream: " | |
1334 | "not updating the stream state's current value: " | |
1335 | "packet-addr=%p, prev-count=%" PRIu64 ", " | |
1336 | "cur-count=%" PRIu64, | |
1337 | packet, prev_count, next_count); | |
2ec84d26 PP |
1338 | goto end; |
1339 | } | |
1340 | ||
126215b4 MD |
1341 | diff = next_count - prev_count; |
1342 | if (diff > 1) { | |
1343 | /* | |
1344 | * Add a discarded packets notification. The packets | |
1345 | * are considered to be lost between the state's last time | |
1346 | * and the current packet's beginning time. | |
1347 | * The counter is expected to monotonically increase of | |
1348 | * 1 for each packet. Therefore, the number of missing | |
1349 | * packets is 'diff - 1'. | |
1350 | */ | |
1351 | notif = bt_notification_discarded_elements_create( | |
1352 | BT_NOTIFICATION_TYPE_DISCARDED_PACKETS, | |
1353 | stream_state->stream, | |
1354 | stream_state->discarded_packets_state.cur_begin, | |
1355 | ts_begin, diff - 1); | |
1356 | if (!notif) { | |
1357 | BT_LOGE_STR("Cannot create discarded packets notification."); | |
1358 | ret = -1; | |
1359 | goto end; | |
1360 | } | |
1361 | ||
1362 | add_action_push_notif(iterator, notif); | |
1363 | } | |
2ec84d26 PP |
1364 | } |
1365 | ||
2ec84d26 PP |
1366 | add_action_update_stream_state_discarded_elements(iterator, |
1367 | ACTION_TYPE_UPDATE_STREAM_STATE_DISCARDED_PACKETS, | |
1368 | stream_state, ts_end, next_count); | |
1369 | ||
1370 | end: | |
1371 | bt_put(notif); | |
1372 | return ret; | |
1373 | } | |
1374 | ||
1375 | static | |
1376 | int handle_discarded_events(struct bt_notification_iterator *iterator, | |
1377 | struct bt_ctf_packet *packet, | |
1378 | struct bt_ctf_clock_value *ts_begin, | |
1379 | struct bt_ctf_clock_value *ts_end, | |
1380 | struct stream_state *stream_state) | |
1381 | { | |
1382 | struct bt_notification *notif = NULL; | |
1383 | uint64_t diff; | |
1384 | uint64_t next_count; | |
1385 | int ret = 0; | |
1386 | ||
1387 | next_count = get_packet_context_events_discarded(packet); | |
1388 | if (next_count == -1ULL) { | |
1389 | next_count = stream_state->discarded_events_state.cur_count; | |
1390 | goto update_state; | |
1391 | } | |
1392 | ||
1393 | if (next_count < stream_state->discarded_events_state.cur_count) { | |
1394 | BT_LOGW("Current value of packet's context field's `events_discarded` field is lesser than the previous value for the same stream: " | |
1395 | "not updating the stream state's current value: " | |
1396 | "packet-addr=%p, prev-count=%" PRIu64 ", " | |
1397 | "cur-count=%" PRIu64, | |
1398 | packet, stream_state->discarded_events_state.cur_count, | |
1399 | next_count); | |
1400 | goto end; | |
1401 | } | |
1402 | ||
1403 | diff = next_count - stream_state->discarded_events_state.cur_count; | |
1404 | if (diff > 0) { | |
1405 | /* | |
1406 | * Add a discarded events notification. The events are | |
1407 | * considered to be lost betweem the state's last time | |
1408 | * and the current packet's end time. | |
1409 | */ | |
1410 | notif = bt_notification_discarded_elements_create( | |
1411 | BT_NOTIFICATION_TYPE_DISCARDED_EVENTS, | |
1412 | stream_state->stream, | |
1413 | stream_state->discarded_events_state.cur_begin, | |
1414 | ts_end, diff); | |
1415 | if (!notif) { | |
1416 | BT_LOGE_STR("Cannot create discarded events notification."); | |
1417 | ret = -1; | |
1418 | goto end; | |
1419 | } | |
1420 | ||
1421 | add_action_push_notif(iterator, notif); | |
1422 | } | |
1423 | ||
1424 | update_state: | |
1425 | add_action_update_stream_state_discarded_elements(iterator, | |
1426 | ACTION_TYPE_UPDATE_STREAM_STATE_DISCARDED_EVENTS, | |
1427 | stream_state, ts_end, next_count); | |
1428 | ||
1429 | end: | |
1430 | bt_put(notif); | |
1431 | return ret; | |
1432 | } | |
1433 | ||
1434 | static | |
1435 | int get_field_clock_value(struct bt_ctf_field *root_field, | |
1436 | const char *field_name, | |
1437 | struct bt_ctf_clock_value **user_clock_val) | |
1438 | { | |
1439 | struct bt_ctf_field *field; | |
1440 | struct bt_ctf_field_type *ft = NULL; | |
1441 | struct bt_ctf_clock_class *clock_class = NULL; | |
1442 | struct bt_ctf_clock_value *clock_value = NULL; | |
1443 | uint64_t val; | |
1444 | int ret = 0; | |
1445 | ||
1446 | field = get_struct_field_uint(root_field, field_name); | |
1447 | if (!field) { | |
1448 | /* Not an error: skip this */ | |
1449 | goto end; | |
1450 | } | |
1451 | ||
1452 | ft = bt_ctf_field_get_type(field); | |
1453 | assert(ft); | |
1454 | clock_class = bt_ctf_field_type_integer_get_mapped_clock_class(ft); | |
1455 | if (!clock_class) { | |
1456 | BT_LOGW("Integer field type has no mapped clock class but it's expected to have one: " | |
1457 | "ft-addr=%p", ft); | |
1458 | ret = -1; | |
1459 | goto end; | |
1460 | } | |
1461 | ||
1462 | ret = bt_ctf_field_unsigned_integer_get_value(field, &val); | |
1463 | if (ret) { | |
1464 | BT_LOGW("Cannot get integer field's raw value: " | |
1465 | "field-addr=%p", field); | |
1466 | ret = -1; | |
1467 | goto end; | |
1468 | } | |
1469 | ||
1470 | clock_value = bt_ctf_clock_value_create(clock_class, val); | |
1471 | if (!clock_value) { | |
1472 | BT_LOGE_STR("Cannot create clock value from clock class."); | |
1473 | ret = -1; | |
1474 | goto end; | |
1475 | } | |
1476 | ||
1477 | /* Move clock value to user */ | |
1478 | *user_clock_val = clock_value; | |
1479 | clock_value = NULL; | |
1480 | ||
1481 | end: | |
1482 | bt_put(field); | |
1483 | bt_put(ft); | |
1484 | bt_put(clock_class); | |
1485 | bt_put(clock_value); | |
1486 | return ret; | |
1487 | } | |
1488 | ||
1489 | static | |
1490 | int get_ts_begin_ts_end_from_packet(struct bt_ctf_packet *packet, | |
1491 | struct bt_ctf_clock_value **user_ts_begin, | |
1492 | struct bt_ctf_clock_value **user_ts_end) | |
1493 | { | |
1494 | struct bt_ctf_field *packet_context = NULL; | |
1495 | struct bt_ctf_clock_value *ts_begin = NULL; | |
1496 | struct bt_ctf_clock_value *ts_end = NULL; | |
1497 | int ret = 0; | |
1498 | ||
1499 | packet_context = bt_ctf_packet_get_context(packet); | |
1500 | if (!packet_context) { | |
1501 | goto end; | |
1502 | } | |
1503 | ||
1504 | ret = get_field_clock_value(packet_context, "timestamp_begin", | |
1505 | &ts_begin); | |
1506 | if (ret) { | |
1507 | BT_LOGW("Cannot create clock value for packet context's `timestamp_begin` field: " | |
1508 | "packet-addr=%p, packet-context-field-addr=%p", | |
1509 | packet, packet_context); | |
1510 | goto end; | |
1511 | } | |
1512 | ||
1513 | ret = get_field_clock_value(packet_context, "timestamp_end", | |
1514 | &ts_end); | |
1515 | if (ret) { | |
1516 | BT_LOGW("Cannot create clock value for packet context's `timestamp_begin` field: " | |
1517 | "packet-addr=%p, packet-context-field-addr=%p", | |
1518 | packet, packet_context); | |
1519 | goto end; | |
1520 | } | |
1521 | ||
1522 | /* Move clock values to user */ | |
1523 | *user_ts_begin = ts_begin; | |
1524 | ts_begin = NULL; | |
1525 | *user_ts_end = ts_end; | |
1526 | ts_end = NULL; | |
1527 | ||
1528 | end: | |
1529 | bt_put(packet_context); | |
1530 | bt_put(ts_begin); | |
1531 | bt_put(ts_end); | |
1532 | return ret; | |
1533 | } | |
1534 | ||
1535 | static | |
1536 | int handle_discarded_elements(struct bt_notification_iterator *iterator, | |
1537 | struct bt_ctf_packet *packet, struct stream_state *stream_state) | |
1538 | { | |
1539 | struct bt_ctf_clock_value *ts_begin = NULL; | |
1540 | struct bt_ctf_clock_value *ts_end = NULL; | |
1541 | int ret; | |
1542 | ||
1543 | ret = get_ts_begin_ts_end_from_packet(packet, &ts_begin, &ts_end); | |
1544 | if (ret) { | |
1545 | BT_LOGW("Cannot get packet's beginning or end clock values: " | |
1546 | "packet-addr=%p, ret=%d", packet, ret); | |
1547 | ret = -1; | |
1548 | goto end; | |
1549 | } | |
1550 | ||
1551 | ret = handle_discarded_packets(iterator, packet, ts_begin, ts_end, | |
1552 | stream_state); | |
1553 | if (ret) { | |
1554 | BT_LOGW("Cannot handle discarded packets for packet: " | |
1555 | "packet-addr=%p, ret=%d", packet, ret); | |
1556 | ret = -1; | |
1557 | goto end; | |
1558 | } | |
1559 | ||
1560 | ret = handle_discarded_events(iterator, packet, ts_begin, ts_end, | |
1561 | stream_state); | |
1562 | if (ret) { | |
1563 | BT_LOGW("Cannot handle discarded events for packet: " | |
1564 | "packet-addr=%p, ret=%d", packet, ret); | |
1565 | ret = -1; | |
1566 | goto end; | |
1567 | } | |
1568 | ||
1569 | end: | |
1570 | bt_put(ts_begin); | |
1571 | bt_put(ts_end); | |
1572 | return ret; | |
1573 | } | |
1574 | ||
3230ee6b PP |
1575 | static |
1576 | int handle_packet_switch(struct bt_notification_iterator *iterator, | |
1577 | struct bt_notification *packet_begin_notif, | |
1578 | struct bt_ctf_packet *new_packet, | |
1579 | struct stream_state *stream_state) | |
1580 | { | |
1581 | int ret = 0; | |
1582 | ||
1583 | if (stream_state->cur_packet == new_packet) { | |
1584 | goto end; | |
1585 | } | |
1586 | ||
5af447e5 PP |
1587 | BT_LOGV("Handling packet switch: " |
1588 | "cur-packet-addr=%p, new-packet-addr=%p", | |
1589 | stream_state->cur_packet, new_packet); | |
1590 | ||
3230ee6b PP |
1591 | if (stream_state->cur_packet) { |
1592 | /* End of the current packet */ | |
1593 | ret = add_action_push_notif_packet_end(iterator, | |
1594 | stream_state->cur_packet); | |
1595 | if (ret) { | |
5af447e5 | 1596 | BT_LOGE_STR("Cannot add \"push packet end notification\" action."); |
3230ee6b PP |
1597 | goto error; |
1598 | } | |
1599 | } | |
1600 | ||
2ec84d26 | 1601 | /* |
126215b4 MD |
1602 | * Check the new packet's context fields for discarded packets |
1603 | * and events to emit those automatic notifications. | |
2ec84d26 PP |
1604 | */ |
1605 | ret = handle_discarded_elements(iterator, new_packet, stream_state); | |
1606 | if (ret) { | |
1607 | BT_LOGE_STR("Cannot handle discarded elements for new packet."); | |
1608 | goto error; | |
1609 | } | |
1610 | ||
3230ee6b PP |
1611 | /* Beginning of the new packet */ |
1612 | if (packet_begin_notif) { | |
1613 | add_action_push_notif(iterator, packet_begin_notif); | |
1614 | } else if (new_packet) { | |
1615 | ret = add_action_push_notif_packet_begin(iterator, | |
1616 | new_packet); | |
1617 | if (ret) { | |
5af447e5 | 1618 | BT_LOGE_STR("Cannot add \"push packet beginning notification\" action."); |
3230ee6b PP |
1619 | goto error; |
1620 | } | |
1621 | } | |
1622 | ||
1623 | add_action_set_stream_state_cur_packet(iterator, stream_state, | |
1624 | new_packet); | |
1625 | goto end; | |
1626 | ||
1627 | error: | |
1628 | ret = -1; | |
1629 | ||
1630 | end: | |
1631 | return ret; | |
1632 | } | |
1633 | ||
1634 | static | |
1635 | int handle_notif_stream_begin( | |
1636 | struct bt_notification_iterator *iterator, | |
1637 | struct bt_notification *notif, | |
1638 | struct bt_ctf_stream *notif_stream) | |
1639 | { | |
1640 | int ret = 0; | |
1641 | struct stream_state *stream_state; | |
1642 | ||
1643 | assert(notif->type == BT_NOTIFICATION_TYPE_STREAM_BEGIN); | |
1644 | assert(notif_stream); | |
1645 | ret = ensure_stream_state_exists(iterator, notif, notif_stream, | |
1646 | &stream_state); | |
1647 | if (ret) { | |
5af447e5 | 1648 | BT_LOGE_STR("Cannot ensure that stream state exists."); |
3230ee6b PP |
1649 | goto error; |
1650 | } | |
1651 | ||
1652 | goto end; | |
1653 | ||
1654 | error: | |
1655 | ret = -1; | |
1656 | ||
1657 | end: | |
1658 | return ret; | |
1659 | } | |
1660 | ||
1661 | static | |
1662 | int handle_notif_stream_end( | |
1663 | struct bt_notification_iterator *iterator, | |
1664 | struct bt_notification *notif, | |
1665 | struct bt_ctf_stream *notif_stream) | |
1666 | { | |
1667 | int ret = 0; | |
1668 | struct stream_state *stream_state; | |
1669 | ||
1670 | assert(notif->type == BT_NOTIFICATION_TYPE_STREAM_END); | |
1671 | assert(notif_stream); | |
1672 | ret = ensure_stream_state_exists(iterator, NULL, notif_stream, | |
1673 | &stream_state); | |
1674 | if (ret) { | |
5af447e5 | 1675 | BT_LOGE_STR("Cannot ensure that stream state exists."); |
3230ee6b PP |
1676 | goto error; |
1677 | } | |
1678 | ||
1679 | ret = handle_packet_switch(iterator, NULL, NULL, stream_state); | |
1680 | if (ret) { | |
5af447e5 | 1681 | BT_LOGE_STR("Cannot handle packet switch."); |
3230ee6b PP |
1682 | goto error; |
1683 | } | |
1684 | ||
1685 | add_action_push_notif(iterator, notif); | |
1686 | add_action_set_stream_state_is_ended(iterator, stream_state); | |
1687 | goto end; | |
1688 | ||
1689 | error: | |
1690 | ret = -1; | |
1691 | ||
1692 | end: | |
1693 | return ret; | |
1694 | } | |
1695 | ||
2ec84d26 PP |
1696 | static |
1697 | int handle_notif_discarded_elements( | |
1698 | struct bt_notification_iterator *iterator, | |
1699 | struct bt_notification *notif, | |
1700 | struct bt_ctf_stream *notif_stream) | |
1701 | { | |
1702 | int ret = 0; | |
1703 | struct stream_state *stream_state; | |
1704 | ||
1705 | assert(notif->type == BT_NOTIFICATION_TYPE_DISCARDED_EVENTS || | |
1706 | notif->type == BT_NOTIFICATION_TYPE_DISCARDED_PACKETS); | |
1707 | assert(notif_stream); | |
1708 | ret = ensure_stream_state_exists(iterator, NULL, notif_stream, | |
1709 | &stream_state); | |
1710 | if (ret) { | |
1711 | BT_LOGE_STR("Cannot ensure that stream state exists."); | |
1712 | goto error; | |
1713 | } | |
1714 | ||
1715 | add_action_push_notif(iterator, notif); | |
1716 | goto end; | |
1717 | ||
1718 | error: | |
1719 | ret = -1; | |
1720 | ||
1721 | end: | |
1722 | return ret; | |
1723 | } | |
1724 | ||
3230ee6b PP |
1725 | static |
1726 | int handle_notif_packet_begin( | |
1727 | struct bt_notification_iterator *iterator, | |
1728 | struct bt_notification *notif, | |
1729 | struct bt_ctf_stream *notif_stream, | |
1730 | struct bt_ctf_packet *notif_packet) | |
1731 | { | |
1732 | int ret = 0; | |
1733 | struct stream_state *stream_state; | |
1734 | ||
1735 | assert(notif->type == BT_NOTIFICATION_TYPE_PACKET_BEGIN); | |
1736 | assert(notif_packet); | |
1737 | ret = ensure_stream_state_exists(iterator, NULL, notif_stream, | |
1738 | &stream_state); | |
1739 | if (ret) { | |
5af447e5 | 1740 | BT_LOGE_STR("Cannot ensure that stream state exists."); |
3230ee6b PP |
1741 | goto error; |
1742 | } | |
1743 | ||
1744 | ret = handle_packet_switch(iterator, notif, notif_packet, stream_state); | |
1745 | if (ret) { | |
5af447e5 | 1746 | BT_LOGE_STR("Cannot handle packet switch."); |
3230ee6b PP |
1747 | goto error; |
1748 | } | |
1749 | ||
1750 | goto end; | |
1751 | ||
1752 | error: | |
1753 | ret = -1; | |
1754 | ||
1755 | end: | |
1756 | return ret; | |
1757 | } | |
1758 | ||
1759 | static | |
1760 | int handle_notif_packet_end( | |
1761 | struct bt_notification_iterator *iterator, | |
1762 | struct bt_notification *notif, | |
1763 | struct bt_ctf_stream *notif_stream, | |
1764 | struct bt_ctf_packet *notif_packet) | |
1765 | { | |
1766 | int ret = 0; | |
1767 | struct stream_state *stream_state; | |
1768 | ||
1769 | assert(notif->type == BT_NOTIFICATION_TYPE_PACKET_END); | |
1770 | assert(notif_packet); | |
1771 | ret = ensure_stream_state_exists(iterator, NULL, notif_stream, | |
1772 | &stream_state); | |
1773 | if (ret) { | |
5af447e5 | 1774 | BT_LOGE_STR("Cannot ensure that stream state exists."); |
3230ee6b PP |
1775 | goto error; |
1776 | } | |
1777 | ||
1778 | ret = handle_packet_switch(iterator, NULL, notif_packet, stream_state); | |
1779 | if (ret) { | |
5af447e5 | 1780 | BT_LOGE_STR("Cannot handle packet switch."); |
3230ee6b PP |
1781 | goto error; |
1782 | } | |
1783 | ||
1784 | /* End of the current packet */ | |
1785 | add_action_push_notif(iterator, notif); | |
1786 | add_action_set_stream_state_cur_packet(iterator, stream_state, NULL); | |
1787 | goto end; | |
1788 | ||
1789 | error: | |
1790 | ret = -1; | |
1791 | ||
1792 | end: | |
1793 | return ret; | |
1794 | } | |
1795 | ||
1796 | static | |
1797 | int handle_notif_event( | |
1798 | struct bt_notification_iterator *iterator, | |
1799 | struct bt_notification *notif, | |
1800 | struct bt_ctf_stream *notif_stream, | |
1801 | struct bt_ctf_packet *notif_packet) | |
1802 | { | |
1803 | int ret = 0; | |
1804 | struct stream_state *stream_state; | |
1805 | ||
1806 | assert(notif->type == BT_NOTIFICATION_TYPE_EVENT); | |
1807 | assert(notif_packet); | |
1808 | ret = ensure_stream_state_exists(iterator, NULL, notif_stream, | |
1809 | &stream_state); | |
1810 | if (ret) { | |
5af447e5 | 1811 | BT_LOGE_STR("Cannot ensure that stream state exists."); |
3230ee6b PP |
1812 | goto error; |
1813 | } | |
1814 | ||
1815 | ret = handle_packet_switch(iterator, NULL, notif_packet, stream_state); | |
1816 | if (ret) { | |
5af447e5 | 1817 | BT_LOGE_STR("Cannot handle packet switch."); |
3230ee6b PP |
1818 | goto error; |
1819 | } | |
1820 | ||
1821 | add_action_push_notif(iterator, notif); | |
1822 | goto end; | |
1823 | ||
1824 | error: | |
1825 | ret = -1; | |
1826 | ||
1827 | end: | |
1828 | return ret; | |
1829 | } | |
1830 | ||
1831 | static | |
1832 | int enqueue_notification_and_automatic( | |
1833 | struct bt_notification_iterator *iterator, | |
1834 | struct bt_notification *notif) | |
1835 | { | |
1836 | int ret = 0; | |
1837 | struct bt_ctf_event *notif_event = NULL; | |
1838 | struct bt_ctf_stream *notif_stream = NULL; | |
1839 | struct bt_ctf_packet *notif_packet = NULL; | |
1840 | ||
1841 | assert(notif); | |
1842 | ||
5af447e5 PP |
1843 | BT_LOGV("Enqueuing user notification and automatic notifications: " |
1844 | "iter-addr=%p, notif-addr=%p", iterator, notif); | |
1845 | ||
fa054faf PP |
1846 | // TODO: Skip most of this if the iterator is only subscribed |
1847 | // to event/inactivity notifications. | |
1848 | ||
3230ee6b PP |
1849 | /* Get the stream and packet referred by the notification */ |
1850 | switch (notif->type) { | |
1851 | case BT_NOTIFICATION_TYPE_EVENT: | |
1852 | notif_event = bt_notification_event_borrow_event(notif); | |
1853 | assert(notif_event); | |
1854 | notif_packet = bt_ctf_event_borrow_packet(notif_event); | |
1855 | assert(notif_packet); | |
1856 | break; | |
1857 | case BT_NOTIFICATION_TYPE_STREAM_BEGIN: | |
1858 | notif_stream = | |
1859 | bt_notification_stream_begin_borrow_stream(notif); | |
1860 | assert(notif_stream); | |
1861 | break; | |
1862 | case BT_NOTIFICATION_TYPE_STREAM_END: | |
1863 | notif_stream = bt_notification_stream_end_borrow_stream(notif); | |
1864 | assert(notif_stream); | |
1865 | break; | |
1866 | case BT_NOTIFICATION_TYPE_PACKET_BEGIN: | |
1867 | notif_packet = | |
1868 | bt_notification_packet_begin_borrow_packet(notif); | |
1869 | assert(notif_packet); | |
1870 | break; | |
1871 | case BT_NOTIFICATION_TYPE_PACKET_END: | |
1872 | notif_packet = bt_notification_packet_end_borrow_packet(notif); | |
1873 | assert(notif_packet); | |
1874 | break; | |
1875 | case BT_NOTIFICATION_TYPE_INACTIVITY: | |
1876 | /* Always valid */ | |
7cdc2bab | 1877 | goto handle_notif; |
3230ee6b PP |
1878 | default: |
1879 | /* | |
1880 | * Invalid type of notification. Only the notification | |
1881 | * types above are allowed to be returned by a user | |
1882 | * component. | |
1883 | */ | |
2ec84d26 PP |
1884 | BT_LOGF("Unexpected notification type at this point: " |
1885 | "notif-addr=%p, notif-type=%s", notif, | |
1886 | bt_notification_type_string(notif->type)); | |
1887 | abort(); | |
3230ee6b PP |
1888 | } |
1889 | ||
1890 | if (notif_packet) { | |
1891 | notif_stream = bt_ctf_packet_borrow_stream(notif_packet); | |
1892 | assert(notif_stream); | |
1893 | } | |
1894 | ||
1895 | if (!notif_stream) { | |
1896 | /* | |
1897 | * The notification has no reference to a stream: it | |
1898 | * cannot cause the creation of automatic notifications. | |
1899 | */ | |
5af447e5 | 1900 | BT_LOGV_STR("Notification has no reference to any stream: skipping automatic notification generation."); |
3230ee6b PP |
1901 | goto end; |
1902 | } | |
1903 | ||
1904 | if (!validate_notification(iterator, notif, notif_stream, | |
1905 | notif_packet)) { | |
5af447e5 | 1906 | BT_LOGW_STR("Invalid notification."); |
3230ee6b PP |
1907 | goto error; |
1908 | } | |
1909 | ||
7cdc2bab | 1910 | handle_notif: |
3230ee6b PP |
1911 | switch (notif->type) { |
1912 | case BT_NOTIFICATION_TYPE_EVENT: | |
1913 | ret = handle_notif_event(iterator, notif, notif_stream, | |
1914 | notif_packet); | |
1915 | break; | |
1916 | case BT_NOTIFICATION_TYPE_STREAM_BEGIN: | |
1917 | ret = handle_notif_stream_begin(iterator, notif, notif_stream); | |
1918 | break; | |
1919 | case BT_NOTIFICATION_TYPE_STREAM_END: | |
1920 | ret = handle_notif_stream_end(iterator, notif, notif_stream); | |
1921 | break; | |
1922 | case BT_NOTIFICATION_TYPE_PACKET_BEGIN: | |
1923 | ret = handle_notif_packet_begin(iterator, notif, notif_stream, | |
1924 | notif_packet); | |
1925 | break; | |
1926 | case BT_NOTIFICATION_TYPE_PACKET_END: | |
1927 | ret = handle_notif_packet_end(iterator, notif, notif_stream, | |
1928 | notif_packet); | |
1929 | break; | |
2ec84d26 PP |
1930 | case BT_NOTIFICATION_TYPE_DISCARDED_EVENTS: |
1931 | case BT_NOTIFICATION_TYPE_DISCARDED_PACKETS: | |
1932 | ret = handle_notif_discarded_elements(iterator, notif, | |
1933 | notif_stream); | |
1934 | break; | |
7cdc2bab MD |
1935 | case BT_NOTIFICATION_TYPE_INACTIVITY: |
1936 | add_action_push_notif(iterator, notif); | |
1937 | break; | |
3230ee6b PP |
1938 | default: |
1939 | break; | |
1940 | } | |
1941 | ||
1942 | if (ret) { | |
5af447e5 | 1943 | BT_LOGW_STR("Failed to handle notification for automatic notification generation."); |
3230ee6b PP |
1944 | goto error; |
1945 | } | |
1946 | ||
1947 | apply_actions(iterator); | |
5af447e5 PP |
1948 | BT_LOGV("Enqueued user notification and automatic notifications: " |
1949 | "iter-addr=%p, notif-addr=%p", iterator, notif); | |
3230ee6b PP |
1950 | goto end; |
1951 | ||
1952 | error: | |
1953 | ret = -1; | |
1954 | ||
1955 | end: | |
1956 | return ret; | |
1957 | } | |
1958 | ||
1959 | static | |
1960 | int handle_end(struct bt_notification_iterator *iterator) | |
1961 | { | |
1962 | GHashTableIter stream_state_iter; | |
1963 | gpointer stream_gptr, stream_state_gptr; | |
1964 | int ret = 0; | |
1965 | ||
5af447e5 PP |
1966 | BT_LOGV("Handling end of iteration: addr=%p", iterator); |
1967 | ||
3230ee6b PP |
1968 | /* |
1969 | * Emit a "stream end" notification for each non-ended stream | |
1970 | * known by this iterator and mark them as ended. | |
1971 | */ | |
1972 | g_hash_table_iter_init(&stream_state_iter, iterator->stream_states); | |
1973 | ||
1974 | while (g_hash_table_iter_next(&stream_state_iter, &stream_gptr, | |
1975 | &stream_state_gptr)) { | |
1976 | struct stream_state *stream_state = stream_state_gptr; | |
1977 | ||
1978 | assert(stream_state_gptr); | |
1979 | ||
1980 | if (stream_state->is_ended) { | |
1981 | continue; | |
1982 | } | |
1983 | ||
1984 | ret = handle_packet_switch(iterator, NULL, NULL, stream_state); | |
1985 | if (ret) { | |
5af447e5 | 1986 | BT_LOGE_STR("Cannot handle packet switch."); |
3230ee6b PP |
1987 | goto error; |
1988 | } | |
1989 | ||
1990 | ret = add_action_push_notif_stream_end(iterator, stream_gptr); | |
1991 | if (ret) { | |
5af447e5 | 1992 | BT_LOGE_STR("Cannot add \"push stream end notification\" action."); |
3230ee6b PP |
1993 | goto error; |
1994 | } | |
1995 | ||
1996 | add_action_set_stream_state_is_ended(iterator, stream_state); | |
1997 | } | |
1998 | ||
1999 | apply_actions(iterator); | |
5af447e5 | 2000 | BT_LOGV("Handled end of iteration: addr=%p", iterator); |
3230ee6b PP |
2001 | goto end; |
2002 | ||
2003 | error: | |
2004 | ret = -1; | |
2005 | ||
2006 | end: | |
2007 | return ret; | |
2008 | } | |
2009 | ||
2010 | static | |
2011 | enum bt_notification_iterator_status ensure_queue_has_notifications( | |
2012 | struct bt_notification_iterator *iterator) | |
53d45b87 | 2013 | { |
890882ef PP |
2014 | struct bt_private_notification_iterator *priv_iterator = |
2015 | bt_private_notification_iterator_from_notification_iterator(iterator); | |
d3eb6e8f | 2016 | bt_component_class_notification_iterator_next_method next_method = NULL; |
fe8ad2b6 PP |
2017 | struct bt_notification_iterator_next_return next_return = { |
2018 | .status = BT_NOTIFICATION_ITERATOR_STATUS_OK, | |
2019 | .notification = NULL, | |
2020 | }; | |
3230ee6b PP |
2021 | enum bt_notification_iterator_status status = |
2022 | BT_NOTIFICATION_ITERATOR_STATUS_OK; | |
2023 | int ret; | |
41a2b7ae | 2024 | |
3230ee6b | 2025 | assert(iterator); |
5af447e5 | 2026 | BT_LOGD("Ensuring that notification iterator's queue has at least one notification: " |
8f9d7550 PP |
2027 | "iter-addr=%p, queue-size=%u, iter-state=%s", |
2028 | iterator, iterator->queue->length, | |
2029 | bt_notification_iterator_state_string(iterator->state)); | |
3230ee6b PP |
2030 | |
2031 | if (iterator->queue->length > 0) { | |
8f9d7550 PP |
2032 | /* |
2033 | * We already have enough. Even if this notification | |
2034 | * iterator is finalized, its user can still flush its | |
2035 | * current queue's content by calling its "next" method | |
2036 | * since this content is local and has no impact on what | |
2037 | * used to be the iterator's upstream component. | |
2038 | */ | |
5af447e5 | 2039 | BT_LOGD_STR("Queue already has at least one notification."); |
3230ee6b PP |
2040 | goto end; |
2041 | } | |
2042 | ||
bd14d768 PP |
2043 | switch (iterator->state) { |
2044 | case BT_NOTIFICATION_ITERATOR_STATE_FINALIZED_AND_ENDED: | |
8f9d7550 | 2045 | case BT_NOTIFICATION_ITERATOR_STATE_FINALIZED: |
5af447e5 | 2046 | BT_LOGD_STR("Notification iterator's \"next\" called, but it is finalized."); |
bd14d768 PP |
2047 | status = BT_NOTIFICATION_ITERATOR_STATUS_CANCELED; |
2048 | goto end; | |
2049 | case BT_NOTIFICATION_ITERATOR_STATE_ENDED: | |
5af447e5 | 2050 | BT_LOGD_STR("Notification iterator is ended."); |
3230ee6b | 2051 | status = BT_NOTIFICATION_ITERATOR_STATUS_END; |
41a2b7ae | 2052 | goto end; |
bd14d768 PP |
2053 | default: |
2054 | break; | |
41a2b7ae | 2055 | } |
d3eb6e8f | 2056 | |
3230ee6b PP |
2057 | assert(iterator->upstream_component); |
2058 | assert(iterator->upstream_component->class); | |
d3eb6e8f | 2059 | |
3230ee6b PP |
2060 | /* Pick the appropriate "next" method */ |
2061 | switch (iterator->upstream_component->class->type) { | |
d3eb6e8f PP |
2062 | case BT_COMPONENT_CLASS_TYPE_SOURCE: |
2063 | { | |
2064 | struct bt_component_class_source *source_class = | |
3230ee6b | 2065 | container_of(iterator->upstream_component->class, |
d3eb6e8f PP |
2066 | struct bt_component_class_source, parent); |
2067 | ||
2068 | assert(source_class->methods.iterator.next); | |
2069 | next_method = source_class->methods.iterator.next; | |
2070 | break; | |
2071 | } | |
2072 | case BT_COMPONENT_CLASS_TYPE_FILTER: | |
2073 | { | |
2074 | struct bt_component_class_filter *filter_class = | |
3230ee6b | 2075 | container_of(iterator->upstream_component->class, |
d3eb6e8f PP |
2076 | struct bt_component_class_filter, parent); |
2077 | ||
2078 | assert(filter_class->methods.iterator.next); | |
2079 | next_method = filter_class->methods.iterator.next; | |
2080 | break; | |
2081 | } | |
2082 | default: | |
0fbb9a9f | 2083 | abort(); |
d3eb6e8f PP |
2084 | } |
2085 | ||
3230ee6b PP |
2086 | /* |
2087 | * Call the user's "next" method to get the next notification | |
fa054faf | 2088 | * and status. |
3230ee6b | 2089 | */ |
d3eb6e8f | 2090 | assert(next_method); |
3230ee6b | 2091 | |
fa054faf | 2092 | while (iterator->queue->length == 0) { |
5af447e5 | 2093 | BT_LOGD_STR("Calling user's \"next\" method."); |
fa054faf | 2094 | next_return = next_method(priv_iterator); |
5af447e5 PP |
2095 | BT_LOGD("User method returned: status=%s", |
2096 | bt_notification_iterator_status_string(next_return.status)); | |
fa054faf | 2097 | if (next_return.status < 0) { |
5af447e5 | 2098 | BT_LOGW_STR("User method failed."); |
fa054faf | 2099 | status = next_return.status; |
3230ee6b PP |
2100 | goto end; |
2101 | } | |
2102 | ||
8cf27cc5 PP |
2103 | if (iterator->state == BT_NOTIFICATION_ITERATOR_STATE_FINALIZED || |
2104 | iterator->state == BT_NOTIFICATION_ITERATOR_STATE_FINALIZED_AND_ENDED) { | |
2105 | /* | |
2106 | * The user's "next" method, somehow, cancelled | |
2107 | * its own notification iterator. This can | |
2108 | * happen, for example, when the user's method | |
2109 | * removes the port on which there's the | |
2110 | * connection from which the iterator was | |
2111 | * created. In this case, said connection is | |
2112 | * ended, and all its notification iterators are | |
2113 | * finalized. | |
2114 | * | |
2115 | * Only bt_put() the returned notification if | |
2116 | * the status is | |
2117 | * BT_NOTIFICATION_ITERATOR_STATUS_OK because | |
2118 | * otherwise this field could be garbage. | |
2119 | */ | |
2120 | if (next_return.status == | |
2121 | BT_NOTIFICATION_ITERATOR_STATUS_OK) { | |
2122 | bt_put(next_return.notification); | |
2123 | } | |
2124 | ||
2125 | status = BT_NOTIFICATION_ITERATOR_STATUS_CANCELED; | |
2126 | goto end; | |
2127 | } | |
2128 | ||
fa054faf PP |
2129 | switch (next_return.status) { |
2130 | case BT_NOTIFICATION_ITERATOR_STATUS_END: | |
2131 | ret = handle_end(iterator); | |
2132 | if (ret) { | |
5af447e5 | 2133 | BT_LOGW_STR("Cannot handle end of iteration."); |
fa054faf PP |
2134 | status = BT_NOTIFICATION_ITERATOR_STATUS_ERROR; |
2135 | goto end; | |
2136 | } | |
3230ee6b | 2137 | |
8f9d7550 PP |
2138 | assert(iterator->state == |
2139 | BT_NOTIFICATION_ITERATOR_STATE_ACTIVE); | |
2140 | iterator->state = BT_NOTIFICATION_ITERATOR_STATE_ENDED; | |
bd14d768 | 2141 | |
8f9d7550 PP |
2142 | if (iterator->queue->length == 0) { |
2143 | status = BT_NOTIFICATION_ITERATOR_STATUS_END; | |
bd14d768 | 2144 | } |
5af447e5 PP |
2145 | |
2146 | BT_LOGD("Set new status: status=%s", | |
2147 | bt_notification_iterator_status_string(status)); | |
3230ee6b | 2148 | goto end; |
fa054faf PP |
2149 | case BT_NOTIFICATION_ITERATOR_STATUS_AGAIN: |
2150 | status = BT_NOTIFICATION_ITERATOR_STATUS_AGAIN; | |
2151 | goto end; | |
2152 | case BT_NOTIFICATION_ITERATOR_STATUS_OK: | |
2153 | if (!next_return.notification) { | |
5af447e5 | 2154 | BT_LOGW_STR("User method returned BT_NOTIFICATION_ITERATOR_STATUS_OK, but notification is NULL."); |
fa054faf PP |
2155 | status = BT_NOTIFICATION_ITERATOR_STATUS_ERROR; |
2156 | goto end; | |
2157 | } | |
2158 | ||
2ec84d26 PP |
2159 | /* |
2160 | * Ignore some notifications which are always | |
2161 | * automatically generated by the notification | |
2162 | * iterator to make sure they have valid values. | |
2163 | */ | |
2164 | switch (next_return.notification->type) { | |
2165 | case BT_NOTIFICATION_TYPE_DISCARDED_PACKETS: | |
2166 | case BT_NOTIFICATION_TYPE_DISCARDED_EVENTS: | |
2167 | BT_LOGV("Ignoring discarded elements notification returned by notification iterator's \"next\" method: " | |
2168 | "notif-type=%s", | |
2169 | bt_notification_type_string(next_return.notification->type)); | |
2170 | BT_PUT(next_return.notification); | |
2171 | continue; | |
2172 | default: | |
2173 | break; | |
2174 | } | |
2175 | ||
fa054faf PP |
2176 | /* |
2177 | * We know the notification is valid. Before we | |
2178 | * push it to the head of the queue, push the | |
2179 | * appropriate automatic notifications if any. | |
2180 | */ | |
2181 | ret = enqueue_notification_and_automatic(iterator, | |
2182 | next_return.notification); | |
2183 | BT_PUT(next_return.notification); | |
2184 | if (ret) { | |
5af447e5 | 2185 | BT_LOGW("Cannot enqueue notification and automatic notifications."); |
fa054faf PP |
2186 | status = BT_NOTIFICATION_ITERATOR_STATUS_ERROR; |
2187 | goto end; | |
2188 | } | |
2189 | break; | |
2190 | default: | |
2191 | /* Unknown non-error status */ | |
0fbb9a9f | 2192 | abort(); |
3230ee6b | 2193 | } |
41a2b7ae PP |
2194 | } |
2195 | ||
2196 | end: | |
3230ee6b PP |
2197 | return status; |
2198 | } | |
2199 | ||
2200 | enum bt_notification_iterator_status | |
2201 | bt_notification_iterator_next(struct bt_notification_iterator *iterator) | |
2202 | { | |
2203 | enum bt_notification_iterator_status status; | |
2204 | ||
2205 | if (!iterator) { | |
5af447e5 | 2206 | BT_LOGW_STR("Invalid parameter: notification iterator is NULL."); |
3230ee6b PP |
2207 | status = BT_NOTIFICATION_ITERATOR_STATUS_INVALID; |
2208 | goto end; | |
2209 | } | |
2210 | ||
5af447e5 PP |
2211 | BT_LOGD("Notification iterator's \"next\": iter-addr=%p", iterator); |
2212 | ||
3230ee6b PP |
2213 | /* |
2214 | * Make sure that the iterator's queue contains at least one | |
2215 | * notification. | |
2216 | */ | |
2217 | status = ensure_queue_has_notifications(iterator); | |
2218 | if (status != BT_NOTIFICATION_ITERATOR_STATUS_OK) { | |
5af447e5 | 2219 | /* Not an error */ |
3230ee6b PP |
2220 | goto end; |
2221 | } | |
2222 | ||
2223 | /* | |
2224 | * Move the notification at the tail of the queue to the | |
2225 | * iterator's current notification. | |
2226 | */ | |
2227 | assert(iterator->queue->length > 0); | |
2228 | bt_put(iterator->current_notification); | |
2229 | iterator->current_notification = g_queue_pop_tail(iterator->queue); | |
2230 | assert(iterator->current_notification); | |
2231 | ||
2232 | end: | |
2233 | return status; | |
53d45b87 JG |
2234 | } |
2235 | ||
413bc2c4 JG |
2236 | struct bt_component *bt_notification_iterator_get_component( |
2237 | struct bt_notification_iterator *iterator) | |
2238 | { | |
3230ee6b | 2239 | return bt_get(iterator->upstream_component); |
413bc2c4 JG |
2240 | } |
2241 | ||
91457551 PP |
2242 | struct bt_private_component * |
2243 | bt_private_notification_iterator_get_private_component( | |
2244 | struct bt_private_notification_iterator *private_iterator) | |
2245 | { | |
2246 | return bt_private_component_from_component( | |
2247 | bt_notification_iterator_get_component( | |
2248 | bt_notification_iterator_from_private(private_iterator))); | |
2249 | } | |
2250 | ||
9531634f JG |
2251 | enum bt_notification_iterator_status bt_notification_iterator_seek_time( |
2252 | struct bt_notification_iterator *iterator, | |
2253 | enum bt_notification_iterator_seek_origin seek_origin, | |
2254 | int64_t time) | |
2255 | { | |
b7726e32 MD |
2256 | enum bt_notification_iterator_status ret = |
2257 | BT_NOTIFICATION_ITERATOR_STATUS_UNSUPPORTED; | |
9531634f JG |
2258 | return ret; |
2259 | } |