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> |
b04a393b 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> | |
b04a393b | 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> |
b04a393b | 58 | #include <inttypes.h> |
0fbb9a9f | 59 | #include <stdlib.h> |
3230ee6b | 60 | |
b04a393b 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 */ | |
b04a393b 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, | |
b04a393b 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; | |
b04a393b 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); |
b04a393b 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; | |
b04a393b 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: |
b04a393b | 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"; | |
b04a393b 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; | |
b04a393b 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: |
b04a393b 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 | ||
5cde4876 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) { | |
dba1e5d8 PP |
448 | case BT_NOTIFICATION_ITERATOR_STATE_NON_INITIALIZED: |
449 | /* Skip user finalization if user initialization failed */ | |
450 | BT_LOGD("Not finalizing non-initialized notification iterator: " | |
451 | "addr=%p", iterator); | |
452 | return; | |
bd14d768 PP |
453 | case BT_NOTIFICATION_ITERATOR_STATE_FINALIZED: |
454 | case BT_NOTIFICATION_ITERATOR_STATE_FINALIZED_AND_ENDED: | |
455 | /* Already finalized */ | |
5af447e5 PP |
456 | BT_LOGD("Not finalizing notification iterator: already finalized: " |
457 | "addr=%p", iterator); | |
bd14d768 PP |
458 | return; |
459 | default: | |
460 | break; | |
461 | } | |
462 | ||
5af447e5 PP |
463 | BT_LOGD("Finalizing notification iterator: addr=%p", iterator); |
464 | ||
df14f8af | 465 | if (iterator->state == BT_NOTIFICATION_ITERATOR_STATE_ENDED) { |
5af447e5 PP |
466 | BT_LOGD("Updating notification iterator's state: " |
467 | "new-state=BT_NOTIFICATION_ITERATOR_STATE_FINALIZED_AND_ENDED"); | |
df14f8af MD |
468 | iterator->state = BT_NOTIFICATION_ITERATOR_STATE_FINALIZED_AND_ENDED; |
469 | } else { | |
5af447e5 PP |
470 | BT_LOGD("Updating notification iterator's state: " |
471 | "new-state=BT_NOTIFICATION_ITERATOR_STATE_FINALIZED"); | |
df14f8af MD |
472 | iterator->state = BT_NOTIFICATION_ITERATOR_STATE_FINALIZED; |
473 | } | |
474 | ||
bd14d768 PP |
475 | assert(iterator->upstream_component); |
476 | comp_class = iterator->upstream_component->class; | |
477 | ||
478 | /* Call user-defined destroy method */ | |
479 | switch (comp_class->type) { | |
480 | case BT_COMPONENT_CLASS_TYPE_SOURCE: | |
481 | { | |
482 | struct bt_component_class_source *source_class; | |
483 | ||
484 | source_class = container_of(comp_class, struct bt_component_class_source, parent); | |
485 | finalize_method = source_class->methods.iterator.finalize; | |
486 | break; | |
487 | } | |
488 | case BT_COMPONENT_CLASS_TYPE_FILTER: | |
489 | { | |
490 | struct bt_component_class_filter *filter_class; | |
491 | ||
492 | filter_class = container_of(comp_class, struct bt_component_class_filter, parent); | |
493 | finalize_method = filter_class->methods.iterator.finalize; | |
494 | break; | |
495 | } | |
496 | default: | |
497 | /* Unreachable */ | |
0fbb9a9f | 498 | abort(); |
bd14d768 PP |
499 | } |
500 | ||
501 | if (finalize_method) { | |
5af447e5 PP |
502 | BT_LOGD("Calling user's finalization method: addr=%p", |
503 | iterator); | |
bd14d768 PP |
504 | finalize_method( |
505 | bt_private_notification_iterator_from_notification_iterator(iterator)); | |
506 | } | |
507 | ||
bd14d768 PP |
508 | iterator->upstream_component = NULL; |
509 | iterator->upstream_port = NULL; | |
5af447e5 | 510 | BT_LOGD("Finalized notification iterator: addr=%p", iterator); |
bd14d768 PP |
511 | } |
512 | ||
513 | BT_HIDDEN | |
514 | void bt_notification_iterator_set_connection( | |
515 | struct bt_notification_iterator *iterator, | |
516 | struct bt_connection *connection) | |
517 | { | |
518 | assert(iterator); | |
519 | iterator->connection = connection; | |
5af447e5 PP |
520 | BT_LOGV("Set notification iterator's connection: " |
521 | "iter-addr=%p, conn-addr=%p", iterator, connection); | |
bd14d768 PP |
522 | } |
523 | ||
fa054faf PP |
524 | static |
525 | int create_subscription_mask_from_notification_types( | |
526 | struct bt_notification_iterator *iterator, | |
527 | const enum bt_notification_type *notif_types) | |
528 | { | |
529 | const enum bt_notification_type *notif_type; | |
530 | int ret = 0; | |
531 | ||
532 | assert(notif_types); | |
533 | iterator->subscription_mask = 0; | |
534 | ||
535 | for (notif_type = notif_types; | |
536 | *notif_type != BT_NOTIFICATION_TYPE_SENTINEL; | |
537 | notif_type++) { | |
538 | switch (*notif_type) { | |
539 | case BT_NOTIFICATION_TYPE_ALL: | |
540 | iterator->subscription_mask |= | |
541 | BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_EVENT | | |
542 | BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_INACTIVITY | | |
543 | BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_STREAM_BEGIN | | |
544 | BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_STREAM_END | | |
545 | BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_PACKET_BEGIN | | |
b04a393b PP |
546 | BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_PACKET_END | |
547 | BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_DISCARDED_EVENTS | | |
548 | BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_DISCARDED_PACKETS; | |
fa054faf PP |
549 | break; |
550 | case BT_NOTIFICATION_TYPE_EVENT: | |
551 | iterator->subscription_mask |= BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_EVENT; | |
552 | break; | |
553 | case BT_NOTIFICATION_TYPE_INACTIVITY: | |
554 | iterator->subscription_mask |= BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_INACTIVITY; | |
555 | break; | |
556 | case BT_NOTIFICATION_TYPE_STREAM_BEGIN: | |
557 | iterator->subscription_mask |= BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_STREAM_BEGIN; | |
558 | break; | |
559 | case BT_NOTIFICATION_TYPE_STREAM_END: | |
560 | iterator->subscription_mask |= BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_STREAM_END; | |
561 | break; | |
562 | case BT_NOTIFICATION_TYPE_PACKET_BEGIN: | |
563 | iterator->subscription_mask |= BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_PACKET_BEGIN; | |
564 | break; | |
565 | case BT_NOTIFICATION_TYPE_PACKET_END: | |
566 | iterator->subscription_mask |= BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_PACKET_END; | |
567 | break; | |
b04a393b PP |
568 | case BT_NOTIFICATION_TYPE_DISCARDED_EVENTS: |
569 | iterator->subscription_mask |= BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_DISCARDED_EVENTS; | |
570 | break; | |
571 | case BT_NOTIFICATION_TYPE_DISCARDED_PACKETS: | |
572 | iterator->subscription_mask |= BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_DISCARDED_PACKETS; | |
573 | break; | |
fa054faf PP |
574 | default: |
575 | ret = -1; | |
576 | goto end; | |
577 | } | |
5af447e5 PP |
578 | |
579 | BT_LOGV("Added notification type to subscription mask: " | |
580 | "type=%s, mask=%x", | |
581 | bt_notification_type_string(*notif_type), | |
582 | iterator->subscription_mask); | |
fa054faf PP |
583 | } |
584 | ||
585 | end: | |
586 | return ret; | |
587 | } | |
588 | ||
47e5a032 | 589 | BT_HIDDEN |
73d5c1ad | 590 | enum bt_connection_status bt_notification_iterator_create( |
3230ee6b | 591 | struct bt_component *upstream_comp, |
fa054faf | 592 | struct bt_port *upstream_port, |
bd14d768 | 593 | const enum bt_notification_type *notification_types, |
73d5c1ad PP |
594 | struct bt_connection *connection, |
595 | struct bt_notification_iterator **user_iterator) | |
47e5a032 | 596 | { |
73d5c1ad | 597 | enum bt_connection_status status = BT_CONNECTION_STATUS_OK; |
d3e4dcd8 | 598 | enum bt_component_class_type type; |
47e5a032 JG |
599 | struct bt_notification_iterator *iterator = NULL; |
600 | ||
3230ee6b PP |
601 | assert(upstream_comp); |
602 | assert(upstream_port); | |
fa054faf | 603 | assert(notification_types); |
3230ee6b | 604 | assert(bt_port_is_connected(upstream_port)); |
73d5c1ad | 605 | assert(user_iterator); |
5af447e5 PP |
606 | BT_LOGD("Creating notification iterator: " |
607 | "upstream-comp-addr=%p, upstream-comp-name=\"%s\", " | |
608 | "upstream-port-addr=%p, upstream-port-name=\"%s\", " | |
609 | "conn-addr=%p", | |
610 | upstream_comp, bt_component_get_name(upstream_comp), | |
611 | upstream_port, bt_port_get_name(upstream_port), | |
612 | connection); | |
3230ee6b | 613 | type = bt_component_get_class_type(upstream_comp); |
ef2f7566 PP |
614 | assert(type == BT_COMPONENT_CLASS_TYPE_SOURCE || |
615 | type == BT_COMPONENT_CLASS_TYPE_FILTER); | |
47e5a032 JG |
616 | iterator = g_new0(struct bt_notification_iterator, 1); |
617 | if (!iterator) { | |
5af447e5 | 618 | BT_LOGE_STR("Failed to allocate one notification iterator."); |
73d5c1ad PP |
619 | status = BT_CONNECTION_STATUS_NOMEM; |
620 | goto end; | |
47e5a032 JG |
621 | } |
622 | ||
b8a06801 | 623 | bt_object_init(iterator, bt_notification_iterator_destroy); |
3230ee6b | 624 | |
fa054faf PP |
625 | if (create_subscription_mask_from_notification_types(iterator, |
626 | notification_types)) { | |
5af447e5 | 627 | BT_LOGW_STR("Cannot create subscription mask from notification types."); |
73d5c1ad PP |
628 | status = BT_CONNECTION_STATUS_INVALID; |
629 | goto end; | |
fa054faf PP |
630 | } |
631 | ||
3230ee6b PP |
632 | iterator->stream_states = g_hash_table_new_full(g_direct_hash, |
633 | g_direct_equal, NULL, (GDestroyNotify) destroy_stream_state); | |
634 | if (!iterator->stream_states) { | |
5af447e5 | 635 | BT_LOGE_STR("Failed to allocate a GHashTable."); |
73d5c1ad PP |
636 | status = BT_CONNECTION_STATUS_NOMEM; |
637 | goto end; | |
3230ee6b PP |
638 | } |
639 | ||
640 | iterator->queue = g_queue_new(); | |
641 | if (!iterator->queue) { | |
5af447e5 | 642 | BT_LOGE_STR("Failed to allocate a GQueue."); |
73d5c1ad PP |
643 | status = BT_CONNECTION_STATUS_NOMEM; |
644 | goto end; | |
3230ee6b PP |
645 | } |
646 | ||
647 | iterator->actions = g_array_new(FALSE, FALSE, sizeof(struct action)); | |
648 | if (!iterator->actions) { | |
5af447e5 | 649 | BT_LOGE_STR("Failed to allocate a GArray."); |
73d5c1ad PP |
650 | status = BT_CONNECTION_STATUS_NOMEM; |
651 | goto end; | |
3230ee6b PP |
652 | } |
653 | ||
bd14d768 PP |
654 | iterator->upstream_component = upstream_comp; |
655 | iterator->upstream_port = upstream_port; | |
656 | iterator->connection = connection; | |
dba1e5d8 | 657 | iterator->state = BT_NOTIFICATION_ITERATOR_STATE_NON_INITIALIZED; |
5af447e5 PP |
658 | BT_LOGD("Created notification iterator: " |
659 | "upstream-comp-addr=%p, upstream-comp-name=\"%s\", " | |
660 | "upstream-port-addr=%p, upstream-port-name=\"%s\", " | |
661 | "conn-addr=%p, iter-addr=%p", | |
662 | upstream_comp, bt_component_get_name(upstream_comp), | |
663 | upstream_port, bt_port_get_name(upstream_port), | |
664 | connection, iterator); | |
1a6a376a PP |
665 | |
666 | /* Move reference to user */ | |
667 | *user_iterator = iterator; | |
668 | iterator = NULL; | |
3230ee6b | 669 | |
47e5a032 | 670 | end: |
73d5c1ad PP |
671 | bt_put(iterator); |
672 | return status; | |
47e5a032 JG |
673 | } |
674 | ||
890882ef PP |
675 | void *bt_private_notification_iterator_get_user_data( |
676 | struct bt_private_notification_iterator *private_iterator) | |
ea8d3e58 | 677 | { |
890882ef PP |
678 | struct bt_notification_iterator *iterator = |
679 | bt_notification_iterator_from_private(private_iterator); | |
680 | ||
ea8d3e58 JG |
681 | return iterator ? iterator->user_data : NULL; |
682 | } | |
683 | ||
684 | enum bt_notification_iterator_status | |
890882ef PP |
685 | bt_private_notification_iterator_set_user_data( |
686 | struct bt_private_notification_iterator *private_iterator, | |
687 | void *data) | |
ea8d3e58 JG |
688 | { |
689 | enum bt_notification_iterator_status ret = | |
690 | BT_NOTIFICATION_ITERATOR_STATUS_OK; | |
890882ef PP |
691 | struct bt_notification_iterator *iterator = |
692 | bt_notification_iterator_from_private(private_iterator); | |
ea8d3e58 | 693 | |
d3eb6e8f | 694 | if (!iterator) { |
5af447e5 | 695 | BT_LOGW_STR("Invalid parameter: notification iterator is NULL."); |
fe8ad2b6 | 696 | ret = BT_NOTIFICATION_ITERATOR_STATUS_INVALID; |
ea8d3e58 JG |
697 | goto end; |
698 | } | |
699 | ||
700 | iterator->user_data = data; | |
5af447e5 PP |
701 | BT_LOGV("Set notification iterator's user data: " |
702 | "iter-addr=%p, user-data-addr=%p", iterator, data); | |
703 | ||
8738a040 JG |
704 | end: |
705 | return ret; | |
706 | } | |
413bc2c4 | 707 | |
53d45b87 JG |
708 | struct bt_notification *bt_notification_iterator_get_notification( |
709 | struct bt_notification_iterator *iterator) | |
710 | { | |
41a2b7ae | 711 | struct bt_notification *notification = NULL; |
d3eb6e8f | 712 | |
41a2b7ae | 713 | if (!iterator) { |
5af447e5 | 714 | BT_LOGW_STR("Invalid parameter: notification iterator is NULL."); |
41a2b7ae | 715 | goto end; |
d3eb6e8f | 716 | } |
d3eb6e8f | 717 | |
41a2b7ae | 718 | notification = bt_get(iterator->current_notification); |
d3eb6e8f | 719 | |
41a2b7ae PP |
720 | end: |
721 | return notification; | |
53d45b87 JG |
722 | } |
723 | ||
fa054faf PP |
724 | static |
725 | enum bt_notification_iterator_notif_type | |
726 | bt_notification_iterator_notif_type_from_notif_type( | |
727 | enum bt_notification_type notif_type) | |
728 | { | |
729 | enum bt_notification_iterator_notif_type iter_notif_type; | |
730 | ||
731 | switch (notif_type) { | |
732 | case BT_NOTIFICATION_TYPE_EVENT: | |
733 | iter_notif_type = BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_EVENT; | |
734 | break; | |
735 | case BT_NOTIFICATION_TYPE_INACTIVITY: | |
736 | iter_notif_type = BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_INACTIVITY; | |
737 | break; | |
738 | case BT_NOTIFICATION_TYPE_STREAM_BEGIN: | |
739 | iter_notif_type = BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_STREAM_BEGIN; | |
740 | break; | |
741 | case BT_NOTIFICATION_TYPE_STREAM_END: | |
742 | iter_notif_type = BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_STREAM_END; | |
743 | break; | |
744 | case BT_NOTIFICATION_TYPE_PACKET_BEGIN: | |
745 | iter_notif_type = BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_PACKET_BEGIN; | |
746 | break; | |
747 | case BT_NOTIFICATION_TYPE_PACKET_END: | |
748 | iter_notif_type = BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_PACKET_END; | |
749 | break; | |
b04a393b PP |
750 | case BT_NOTIFICATION_TYPE_DISCARDED_EVENTS: |
751 | iter_notif_type = BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_DISCARDED_EVENTS; | |
752 | break; | |
753 | case BT_NOTIFICATION_TYPE_DISCARDED_PACKETS: | |
754 | iter_notif_type = BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_DISCARDED_PACKETS; | |
755 | break; | |
fa054faf | 756 | default: |
0fbb9a9f | 757 | abort(); |
fa054faf PP |
758 | } |
759 | ||
760 | return iter_notif_type; | |
761 | } | |
762 | ||
3230ee6b | 763 | static |
c55a9f58 | 764 | bt_bool validate_notification(struct bt_notification_iterator *iterator, |
3230ee6b PP |
765 | struct bt_notification *notif, |
766 | struct bt_ctf_stream *notif_stream, | |
767 | struct bt_ctf_packet *notif_packet) | |
768 | { | |
c55a9f58 | 769 | bt_bool is_valid = BT_TRUE; |
3230ee6b PP |
770 | struct stream_state *stream_state; |
771 | struct bt_port *stream_comp_cur_port; | |
772 | ||
773 | assert(notif_stream); | |
774 | stream_comp_cur_port = | |
775 | bt_ctf_stream_port_for_component(notif_stream, | |
776 | iterator->upstream_component); | |
777 | if (!stream_comp_cur_port) { | |
778 | /* | |
779 | * This is the first time this notification iterator | |
780 | * bumps into this stream. Add an action to map the | |
781 | * iterator's upstream component to the iterator's | |
782 | * upstream port in this stream. | |
783 | */ | |
784 | struct action action = { | |
785 | .type = ACTION_TYPE_MAP_PORT_TO_COMP_IN_STREAM, | |
786 | .payload.map_port_to_comp_in_stream = { | |
787 | .stream = bt_get(notif_stream), | |
788 | .component = bt_get(iterator->upstream_component), | |
789 | .port = bt_get(iterator->upstream_port), | |
790 | }, | |
791 | }; | |
792 | ||
793 | add_action(iterator, &action); | |
794 | } else { | |
795 | if (stream_comp_cur_port != iterator->upstream_port) { | |
796 | /* | |
797 | * It looks like two different ports of the same | |
798 | * component are emitting notifications which | |
799 | * have references to the same stream. This is | |
800 | * bad: the API guarantees that it can never | |
801 | * happen. | |
802 | */ | |
5af447e5 PP |
803 | BT_LOGW("Two different ports of the same component are emitting notifications which refer to the same stream: " |
804 | "stream-addr=%p, stream-name=\"%s\", " | |
805 | "stream-comp-cur-port-addr=%p, " | |
806 | "stream-comp-cur-port-name=%p, " | |
807 | "iter-upstream-port-addr=%p, " | |
808 | "iter-upstream-port-name=%s", | |
809 | notif_stream, | |
810 | bt_ctf_stream_get_name(notif_stream), | |
811 | stream_comp_cur_port, | |
812 | bt_port_get_name(stream_comp_cur_port), | |
813 | iterator->upstream_port, | |
814 | bt_port_get_name(iterator->upstream_port)); | |
c55a9f58 | 815 | is_valid = BT_FALSE; |
3230ee6b PP |
816 | goto end; |
817 | } | |
818 | ||
819 | } | |
820 | ||
821 | stream_state = g_hash_table_lookup(iterator->stream_states, | |
822 | notif_stream); | |
823 | if (stream_state) { | |
5af447e5 PP |
824 | BT_LOGV("Stream state already exists: " |
825 | "stream-addr=%p, stream-name=\"%s\", " | |
826 | "stream-state-addr=%p", | |
827 | notif_stream, | |
828 | bt_ctf_stream_get_name(notif_stream), stream_state); | |
829 | ||
3230ee6b PP |
830 | if (stream_state->is_ended) { |
831 | /* | |
832 | * There's a new notification which has a | |
833 | * reference to a stream which, from this | |
834 | * iterator's point of view, is ended ("end of | |
835 | * stream" notification was returned). This is | |
836 | * bad: the API guarantees that it can never | |
837 | * happen. | |
838 | */ | |
5af447e5 PP |
839 | BT_LOGW("Stream is already ended: " |
840 | "stream-addr=%p, stream-name=\"%s\"", | |
841 | notif_stream, | |
842 | bt_ctf_stream_get_name(notif_stream)); | |
c55a9f58 | 843 | is_valid = BT_FALSE; |
3230ee6b PP |
844 | goto end; |
845 | } | |
846 | ||
847 | switch (notif->type) { | |
848 | case BT_NOTIFICATION_TYPE_STREAM_BEGIN: | |
849 | /* | |
850 | * We already have a stream state, which means | |
851 | * we already returned a "stream begin" | |
852 | * notification: this is an invalid duplicate. | |
853 | */ | |
5af447e5 PP |
854 | BT_LOGW("Duplicate stream beginning notification: " |
855 | "stream-addr=%p, stream-name=\"%s\"", | |
856 | notif_stream, | |
857 | bt_ctf_stream_get_name(notif_stream)); | |
c55a9f58 | 858 | is_valid = BT_FALSE; |
3230ee6b PP |
859 | goto end; |
860 | case BT_NOTIFICATION_TYPE_PACKET_BEGIN: | |
861 | if (notif_packet == stream_state->cur_packet) { | |
862 | /* Duplicate "packet begin" notification */ | |
5af447e5 PP |
863 | BT_LOGW("Duplicate stream beginning notification: " |
864 | "stream-addr=%p, stream-name=\"%s\", " | |
865 | "packet-addr=%p", | |
866 | notif_stream, | |
867 | bt_ctf_stream_get_name(notif_stream), | |
868 | notif_packet); | |
c55a9f58 | 869 | is_valid = BT_FALSE; |
3230ee6b PP |
870 | goto end; |
871 | } | |
872 | break; | |
873 | default: | |
874 | break; | |
875 | } | |
876 | } | |
877 | ||
878 | end: | |
879 | return is_valid; | |
880 | } | |
881 | ||
fa054faf | 882 | static |
c55a9f58 | 883 | bt_bool is_subscribed_to_notification_type(struct bt_notification_iterator *iterator, |
fa054faf PP |
884 | enum bt_notification_type notif_type) |
885 | { | |
886 | uint32_t iter_notif_type = | |
887 | (uint32_t) bt_notification_iterator_notif_type_from_notif_type( | |
888 | notif_type); | |
889 | ||
c55a9f58 | 890 | return (iter_notif_type & iterator->subscription_mask) ? BT_TRUE : BT_FALSE; |
fa054faf PP |
891 | } |
892 | ||
3230ee6b PP |
893 | static |
894 | void add_action_push_notif(struct bt_notification_iterator *iterator, | |
895 | struct bt_notification *notif) | |
896 | { | |
897 | struct action action = { | |
898 | .type = ACTION_TYPE_PUSH_NOTIF, | |
3230ee6b PP |
899 | }; |
900 | ||
901 | assert(notif); | |
fa054faf PP |
902 | |
903 | if (!is_subscribed_to_notification_type(iterator, notif->type)) { | |
904 | return; | |
905 | } | |
906 | ||
907 | action.payload.push_notif.notif = bt_get(notif); | |
3230ee6b | 908 | add_action(iterator, &action); |
5af447e5 | 909 | BT_LOGV("Added \"push notification\" action: notif-addr=%p", notif); |
3230ee6b PP |
910 | } |
911 | ||
912 | static | |
913 | int add_action_push_notif_stream_begin( | |
914 | struct bt_notification_iterator *iterator, | |
915 | struct bt_ctf_stream *stream) | |
916 | { | |
917 | int ret = 0; | |
918 | struct bt_notification *stream_begin_notif = NULL; | |
919 | ||
fa054faf PP |
920 | if (!is_subscribed_to_notification_type(iterator, |
921 | BT_NOTIFICATION_TYPE_STREAM_BEGIN)) { | |
5af447e5 PP |
922 | BT_LOGV("Not adding \"push stream beginning notification\" action: " |
923 | "notification iterator is not subscribed: addr=%p", | |
924 | iterator); | |
fa054faf PP |
925 | goto end; |
926 | } | |
927 | ||
3230ee6b PP |
928 | assert(stream); |
929 | stream_begin_notif = bt_notification_stream_begin_create(stream); | |
930 | if (!stream_begin_notif) { | |
5af447e5 | 931 | BT_LOGE_STR("Cannot create stream beginning notification."); |
3230ee6b PP |
932 | goto error; |
933 | } | |
934 | ||
935 | add_action_push_notif(iterator, stream_begin_notif); | |
5af447e5 PP |
936 | BT_LOGV("Added \"push stream beginning notification\" action: " |
937 | "stream-addr=%p, stream-name=\"%s\"", | |
938 | stream, bt_ctf_stream_get_name(stream)); | |
3230ee6b PP |
939 | goto end; |
940 | ||
941 | error: | |
942 | ret = -1; | |
943 | ||
944 | end: | |
945 | bt_put(stream_begin_notif); | |
946 | return ret; | |
947 | } | |
948 | ||
949 | static | |
950 | int add_action_push_notif_stream_end( | |
951 | struct bt_notification_iterator *iterator, | |
952 | struct bt_ctf_stream *stream) | |
953 | { | |
954 | int ret = 0; | |
955 | struct bt_notification *stream_end_notif = NULL; | |
956 | ||
fa054faf PP |
957 | if (!is_subscribed_to_notification_type(iterator, |
958 | BT_NOTIFICATION_TYPE_STREAM_END)) { | |
5af447e5 PP |
959 | BT_LOGV("Not adding \"push stream end notification\" action: " |
960 | "notification iterator is not subscribed: addr=%p", | |
961 | iterator); | |
fa054faf PP |
962 | goto end; |
963 | } | |
964 | ||
3230ee6b PP |
965 | assert(stream); |
966 | stream_end_notif = bt_notification_stream_end_create(stream); | |
967 | if (!stream_end_notif) { | |
5af447e5 | 968 | BT_LOGE_STR("Cannot create stream end notification."); |
3230ee6b PP |
969 | goto error; |
970 | } | |
971 | ||
972 | add_action_push_notif(iterator, stream_end_notif); | |
5af447e5 PP |
973 | BT_LOGV("Added \"push stream end notification\" action: " |
974 | "stream-addr=%p, stream-name=\"%s\"", | |
975 | stream, bt_ctf_stream_get_name(stream)); | |
3230ee6b PP |
976 | goto end; |
977 | ||
978 | error: | |
979 | ret = -1; | |
980 | ||
981 | end: | |
982 | bt_put(stream_end_notif); | |
983 | return ret; | |
984 | } | |
985 | ||
986 | static | |
987 | int add_action_push_notif_packet_begin( | |
988 | struct bt_notification_iterator *iterator, | |
989 | struct bt_ctf_packet *packet) | |
990 | { | |
991 | int ret = 0; | |
992 | struct bt_notification *packet_begin_notif = NULL; | |
993 | ||
fa054faf PP |
994 | if (!is_subscribed_to_notification_type(iterator, |
995 | BT_NOTIFICATION_TYPE_PACKET_BEGIN)) { | |
5af447e5 PP |
996 | BT_LOGV("Not adding \"push packet beginning notification\" action: " |
997 | "notification iterator is not subscribed: addr=%p", | |
998 | iterator); | |
fa054faf PP |
999 | goto end; |
1000 | } | |
1001 | ||
3230ee6b PP |
1002 | assert(packet); |
1003 | packet_begin_notif = bt_notification_packet_begin_create(packet); | |
1004 | if (!packet_begin_notif) { | |
5af447e5 | 1005 | BT_LOGE_STR("Cannot create packet beginning notification."); |
3230ee6b PP |
1006 | goto error; |
1007 | } | |
1008 | ||
1009 | add_action_push_notif(iterator, packet_begin_notif); | |
5af447e5 PP |
1010 | BT_LOGV("Added \"push packet beginning notification\" action: " |
1011 | "packet-addr=%p", packet); | |
3230ee6b PP |
1012 | goto end; |
1013 | ||
1014 | error: | |
1015 | ret = -1; | |
1016 | ||
1017 | end: | |
1018 | bt_put(packet_begin_notif); | |
1019 | return ret; | |
1020 | } | |
1021 | ||
1022 | static | |
1023 | int add_action_push_notif_packet_end( | |
1024 | struct bt_notification_iterator *iterator, | |
1025 | struct bt_ctf_packet *packet) | |
1026 | { | |
1027 | int ret = 0; | |
1028 | struct bt_notification *packet_end_notif = NULL; | |
1029 | ||
fa054faf PP |
1030 | if (!is_subscribed_to_notification_type(iterator, |
1031 | BT_NOTIFICATION_TYPE_PACKET_END)) { | |
5af447e5 PP |
1032 | BT_LOGV("Not adding \"push packet end notification\" action: " |
1033 | "notification iterator is not subscribed: addr=%p", | |
1034 | iterator); | |
fa054faf PP |
1035 | goto end; |
1036 | } | |
1037 | ||
3230ee6b PP |
1038 | assert(packet); |
1039 | packet_end_notif = bt_notification_packet_end_create(packet); | |
1040 | if (!packet_end_notif) { | |
5af447e5 | 1041 | BT_LOGE_STR("Cannot create packet end notification."); |
3230ee6b PP |
1042 | goto error; |
1043 | } | |
1044 | ||
1045 | add_action_push_notif(iterator, packet_end_notif); | |
5af447e5 PP |
1046 | BT_LOGV("Added \"push packet end notification\" action: " |
1047 | "packet-addr=%p", packet); | |
3230ee6b PP |
1048 | goto end; |
1049 | ||
1050 | error: | |
1051 | ret = -1; | |
1052 | ||
1053 | end: | |
1054 | bt_put(packet_end_notif); | |
1055 | return ret; | |
1056 | } | |
1057 | ||
1058 | static | |
1059 | void add_action_set_stream_state_is_ended( | |
1060 | struct bt_notification_iterator *iterator, | |
1061 | struct stream_state *stream_state) | |
1062 | { | |
1063 | struct action action = { | |
1064 | .type = ACTION_TYPE_SET_STREAM_STATE_IS_ENDED, | |
1065 | .payload.set_stream_state_is_ended = { | |
1066 | .stream_state = stream_state, | |
1067 | }, | |
1068 | }; | |
1069 | ||
1070 | assert(stream_state); | |
1071 | add_action(iterator, &action); | |
5af447e5 PP |
1072 | BT_LOGV("Added \"set stream state's ended\" action: " |
1073 | "stream-state-addr=%p", stream_state); | |
3230ee6b PP |
1074 | } |
1075 | ||
1076 | static | |
1077 | void add_action_set_stream_state_cur_packet( | |
1078 | struct bt_notification_iterator *iterator, | |
1079 | struct stream_state *stream_state, | |
1080 | struct bt_ctf_packet *packet) | |
1081 | { | |
1082 | struct action action = { | |
1083 | .type = ACTION_TYPE_SET_STREAM_STATE_CUR_PACKET, | |
1084 | .payload.set_stream_state_cur_packet = { | |
1085 | .stream_state = stream_state, | |
1086 | .packet = bt_get(packet), | |
1087 | }, | |
1088 | }; | |
1089 | ||
1090 | assert(stream_state); | |
1091 | add_action(iterator, &action); | |
5af447e5 PP |
1092 | BT_LOGV("Added \"set stream state's current packet\" action: " |
1093 | "stream-state-addr=%p, packet-addr=%p", | |
1094 | stream_state, packet); | |
3230ee6b PP |
1095 | } |
1096 | ||
b04a393b PP |
1097 | static |
1098 | void add_action_update_stream_state_discarded_elements( | |
1099 | struct bt_notification_iterator *iterator, | |
1100 | enum action_type type, | |
1101 | struct stream_state *stream_state, | |
1102 | struct bt_ctf_clock_value *cur_begin, | |
1103 | uint64_t cur_count) | |
1104 | { | |
1105 | struct action action = { | |
1106 | .type = type, | |
1107 | .payload.update_stream_state_discarded_elements = { | |
1108 | .stream_state = stream_state, | |
1109 | .cur_begin = bt_get(cur_begin), | |
1110 | .cur_count = cur_count, | |
1111 | }, | |
1112 | }; | |
1113 | ||
1114 | assert(stream_state); | |
1115 | assert(type == ACTION_TYPE_UPDATE_STREAM_STATE_DISCARDED_PACKETS || | |
1116 | type == ACTION_TYPE_UPDATE_STREAM_STATE_DISCARDED_EVENTS); | |
1117 | add_action(iterator, &action); | |
1118 | if (type == ACTION_TYPE_UPDATE_STREAM_STATE_DISCARDED_PACKETS) { | |
1119 | BT_LOGV("Added \"update stream state's discarded packets\" action: " | |
1120 | "stream-state-addr=%p, cur-begin-addr=%p, cur-count=%" PRIu64, | |
1121 | stream_state, cur_begin, cur_count); | |
1122 | } else if (type == ACTION_TYPE_UPDATE_STREAM_STATE_DISCARDED_EVENTS) { | |
1123 | BT_LOGV("Added \"update stream state's discarded events\" action: " | |
1124 | "stream-state-addr=%p, cur-begin-addr=%p, cur-count=%" PRIu64, | |
1125 | stream_state, cur_begin, cur_count); | |
1126 | } | |
1127 | } | |
1128 | ||
3230ee6b PP |
1129 | static |
1130 | int ensure_stream_state_exists(struct bt_notification_iterator *iterator, | |
1131 | struct bt_notification *stream_begin_notif, | |
1132 | struct bt_ctf_stream *notif_stream, | |
54cbb7c7 | 1133 | struct stream_state **_stream_state) |
3230ee6b PP |
1134 | { |
1135 | int ret = 0; | |
54cbb7c7 | 1136 | struct stream_state *stream_state = NULL; |
3230ee6b PP |
1137 | |
1138 | if (!notif_stream) { | |
1139 | /* | |
1140 | * The notification does not reference any stream: no | |
1141 | * need to get or create a stream state. | |
1142 | */ | |
1143 | goto end; | |
1144 | } | |
1145 | ||
54cbb7c7 | 1146 | stream_state = g_hash_table_lookup(iterator->stream_states, |
3230ee6b | 1147 | notif_stream); |
54cbb7c7 | 1148 | if (!stream_state) { |
3230ee6b PP |
1149 | /* |
1150 | * This iterator did not bump into this stream yet: | |
1151 | * create a stream state and a "stream begin" | |
1152 | * notification. | |
1153 | */ | |
1154 | struct action action = { | |
1155 | .type = ACTION_TYPE_ADD_STREAM_STATE, | |
1156 | .payload.add_stream_state = { | |
1157 | .stream = bt_get(notif_stream), | |
1158 | .stream_state = NULL, | |
1159 | }, | |
1160 | }; | |
1161 | ||
54cbb7c7 | 1162 | stream_state = create_stream_state(notif_stream); |
3230ee6b | 1163 | if (!stream_state) { |
5af447e5 | 1164 | BT_LOGE_STR("Cannot create stream state."); |
3230ee6b PP |
1165 | goto error; |
1166 | } | |
1167 | ||
54cbb7c7 | 1168 | action.payload.add_stream_state.stream_state = stream_state; |
3230ee6b PP |
1169 | add_action(iterator, &action); |
1170 | ||
1171 | if (stream_begin_notif) { | |
1172 | add_action_push_notif(iterator, stream_begin_notif); | |
1173 | } else { | |
1174 | ret = add_action_push_notif_stream_begin(iterator, | |
1175 | notif_stream); | |
1176 | if (ret) { | |
5af447e5 | 1177 | BT_LOGE_STR("Cannot add \"push stream beginning notification\" action."); |
3230ee6b PP |
1178 | goto error; |
1179 | } | |
1180 | } | |
1181 | } | |
3230ee6b PP |
1182 | goto end; |
1183 | ||
1184 | error: | |
54cbb7c7 MD |
1185 | destroy_stream_state(stream_state); |
1186 | stream_state = NULL; | |
3230ee6b PP |
1187 | ret = -1; |
1188 | ||
1189 | end: | |
54cbb7c7 | 1190 | *_stream_state = stream_state; |
3230ee6b PP |
1191 | return ret; |
1192 | } | |
1193 | ||
b04a393b PP |
1194 | static |
1195 | struct bt_ctf_field *get_struct_field_uint(struct bt_ctf_field *struct_field, | |
1196 | const char *field_name) | |
1197 | { | |
1198 | struct bt_ctf_field *field = NULL; | |
1199 | struct bt_ctf_field_type *ft = NULL; | |
1200 | ||
1201 | field = bt_ctf_field_structure_get_field_by_name(struct_field, | |
1202 | field_name); | |
1203 | if (!field) { | |
1204 | BT_LOGV_STR("`%s` field does not exist."); | |
1205 | goto end; | |
1206 | } | |
1207 | ||
1208 | if (!bt_ctf_field_is_integer(field)) { | |
1209 | BT_LOGV("Skipping `%s` field because its type is not an integer field type: " | |
1210 | "field-addr=%p, ft-addr=%p, ft-id=%s", field_name, | |
1211 | field, ft, bt_ctf_field_type_id_string( | |
1212 | bt_ctf_field_type_get_type_id(ft))); | |
1213 | BT_PUT(field); | |
1214 | goto end; | |
1215 | } | |
1216 | ||
1217 | ft = bt_ctf_field_get_type(field); | |
1218 | assert(ft); | |
1219 | ||
1220 | if (bt_ctf_field_type_integer_is_signed(ft)) { | |
1221 | BT_LOGV("Skipping `%s` integer field because its type is signed: " | |
1222 | "field-addr=%p, ft-addr=%p", field_name, field, ft); | |
1223 | BT_PUT(field); | |
1224 | goto end; | |
1225 | } | |
1226 | ||
1227 | end: | |
1228 | bt_put(ft); | |
1229 | return field; | |
1230 | } | |
1231 | ||
1232 | static | |
1233 | uint64_t get_packet_context_events_discarded(struct bt_ctf_packet *packet) | |
1234 | { | |
1235 | struct bt_ctf_field *packet_context = NULL; | |
1236 | struct bt_ctf_field *field = NULL; | |
1237 | uint64_t retval = -1ULL; | |
1238 | int ret; | |
1239 | ||
1240 | packet_context = bt_ctf_packet_get_context(packet); | |
1241 | if (!packet_context) { | |
1242 | goto end; | |
1243 | } | |
1244 | ||
1245 | field = get_struct_field_uint(packet_context, "events_discarded"); | |
1246 | if (!field) { | |
1247 | BT_LOGV("`events_discarded` field does not exist in packet's context field: " | |
1248 | "packet-addr=%p, packet-context-field-addr=%p", | |
1249 | packet, packet_context); | |
1250 | goto end; | |
1251 | } | |
1252 | ||
1253 | assert(bt_ctf_field_is_integer(field)); | |
1254 | ret = bt_ctf_field_unsigned_integer_get_value(field, &retval); | |
1255 | if (ret) { | |
1256 | BT_LOGV("Cannot get raw value of packet's context field's `events_discarded` integer field: " | |
1257 | "packet-addr=%p, field-addr=%p", | |
1258 | packet, field); | |
1259 | retval = -1ULL; | |
1260 | goto end; | |
1261 | } | |
1262 | ||
1263 | end: | |
1264 | bt_put(packet_context); | |
1265 | bt_put(field); | |
1266 | return retval; | |
1267 | } | |
1268 | ||
1269 | static | |
5cde4876 | 1270 | uint64_t get_packet_context_packet_seq_num(struct bt_ctf_packet *packet) |
b04a393b | 1271 | { |
5cde4876 | 1272 | struct bt_ctf_field *packet_context = NULL; |
b04a393b PP |
1273 | struct bt_ctf_field *field = NULL; |
1274 | uint64_t retval = -1ULL; | |
1275 | int ret; | |
1276 | ||
5cde4876 MD |
1277 | packet_context = bt_ctf_packet_get_context(packet); |
1278 | if (!packet_context) { | |
b04a393b PP |
1279 | goto end; |
1280 | } | |
1281 | ||
5cde4876 | 1282 | field = get_struct_field_uint(packet_context, "packet_seq_num"); |
b04a393b | 1283 | if (!field) { |
5cde4876 MD |
1284 | BT_LOGV("`packet_seq_num` field does not exist in packet's context field: " |
1285 | "packet-addr=%p, packet-context-field-addr=%p", | |
1286 | packet, packet_context); | |
b04a393b PP |
1287 | goto end; |
1288 | } | |
1289 | ||
1290 | assert(bt_ctf_field_is_integer(field)); | |
1291 | ret = bt_ctf_field_unsigned_integer_get_value(field, &retval); | |
1292 | if (ret) { | |
5cde4876 | 1293 | BT_LOGV("Cannot get raw value of packet's context field's `packet_seq_num` integer field: " |
b04a393b PP |
1294 | "packet-addr=%p, field-addr=%p", |
1295 | packet, field); | |
1296 | retval = -1ULL; | |
1297 | goto end; | |
1298 | } | |
1299 | ||
1300 | end: | |
5cde4876 | 1301 | bt_put(packet_context); |
b04a393b PP |
1302 | bt_put(field); |
1303 | return retval; | |
1304 | } | |
1305 | ||
1306 | static | |
1307 | int handle_discarded_packets(struct bt_notification_iterator *iterator, | |
1308 | struct bt_ctf_packet *packet, | |
1309 | struct bt_ctf_clock_value *ts_begin, | |
1310 | struct bt_ctf_clock_value *ts_end, | |
1311 | struct stream_state *stream_state) | |
1312 | { | |
1313 | struct bt_notification *notif = NULL; | |
1314 | uint64_t diff; | |
5cde4876 | 1315 | uint64_t prev_count, next_count; |
b04a393b PP |
1316 | int ret = 0; |
1317 | ||
5cde4876 | 1318 | next_count = get_packet_context_packet_seq_num(packet); |
b04a393b | 1319 | if (next_count == -1ULL) { |
5cde4876 MD |
1320 | /* |
1321 | * Stream does not have seqnum field, skip discarded | |
1322 | * packets feature. | |
1323 | */ | |
b04a393b PP |
1324 | goto end; |
1325 | } | |
5cde4876 | 1326 | prev_count = stream_state->discarded_packets_state.cur_count; |
b04a393b | 1327 | |
5cde4876 MD |
1328 | if (prev_count != -1ULL) { |
1329 | if (next_count < prev_count) { | |
1330 | BT_LOGW("Current value of packet's context field's `packet_seq_num` field is lesser than the previous value for the same stream: " | |
1331 | "not updating the stream state's current value: " | |
1332 | "packet-addr=%p, prev-count=%" PRIu64 ", " | |
1333 | "cur-count=%" PRIu64, | |
1334 | packet, prev_count, next_count); | |
1335 | goto end; | |
1336 | } | |
1337 | if (next_count == prev_count) { | |
1338 | BT_LOGW("Current value of packet's context field's `packet_seq_num` field is equal to the previous value for the same stream: " | |
1339 | "not updating the stream state's current value: " | |
1340 | "packet-addr=%p, prev-count=%" PRIu64 ", " | |
1341 | "cur-count=%" PRIu64, | |
1342 | packet, prev_count, next_count); | |
b04a393b PP |
1343 | goto end; |
1344 | } | |
1345 | ||
5cde4876 MD |
1346 | diff = next_count - prev_count; |
1347 | if (diff > 1) { | |
1348 | /* | |
1349 | * Add a discarded packets notification. The packets | |
1350 | * are considered to be lost between the state's last time | |
1351 | * and the current packet's beginning time. | |
1352 | * The counter is expected to monotonically increase of | |
1353 | * 1 for each packet. Therefore, the number of missing | |
1354 | * packets is 'diff - 1'. | |
1355 | */ | |
1356 | notif = bt_notification_discarded_elements_create( | |
1357 | BT_NOTIFICATION_TYPE_DISCARDED_PACKETS, | |
1358 | stream_state->stream, | |
1359 | stream_state->discarded_packets_state.cur_begin, | |
1360 | ts_begin, diff - 1); | |
1361 | if (!notif) { | |
1362 | BT_LOGE_STR("Cannot create discarded packets notification."); | |
1363 | ret = -1; | |
1364 | goto end; | |
1365 | } | |
1366 | ||
1367 | add_action_push_notif(iterator, notif); | |
1368 | } | |
b04a393b PP |
1369 | } |
1370 | ||
b04a393b PP |
1371 | add_action_update_stream_state_discarded_elements(iterator, |
1372 | ACTION_TYPE_UPDATE_STREAM_STATE_DISCARDED_PACKETS, | |
1373 | stream_state, ts_end, next_count); | |
1374 | ||
1375 | end: | |
1376 | bt_put(notif); | |
1377 | return ret; | |
1378 | } | |
1379 | ||
1380 | static | |
1381 | int handle_discarded_events(struct bt_notification_iterator *iterator, | |
1382 | struct bt_ctf_packet *packet, | |
1383 | struct bt_ctf_clock_value *ts_begin, | |
1384 | struct bt_ctf_clock_value *ts_end, | |
1385 | struct stream_state *stream_state) | |
1386 | { | |
1387 | struct bt_notification *notif = NULL; | |
1388 | uint64_t diff; | |
1389 | uint64_t next_count; | |
1390 | int ret = 0; | |
1391 | ||
1392 | next_count = get_packet_context_events_discarded(packet); | |
1393 | if (next_count == -1ULL) { | |
1394 | next_count = stream_state->discarded_events_state.cur_count; | |
1395 | goto update_state; | |
1396 | } | |
1397 | ||
1398 | if (next_count < stream_state->discarded_events_state.cur_count) { | |
1399 | BT_LOGW("Current value of packet's context field's `events_discarded` field is lesser than the previous value for the same stream: " | |
1400 | "not updating the stream state's current value: " | |
1401 | "packet-addr=%p, prev-count=%" PRIu64 ", " | |
1402 | "cur-count=%" PRIu64, | |
1403 | packet, stream_state->discarded_events_state.cur_count, | |
1404 | next_count); | |
1405 | goto end; | |
1406 | } | |
1407 | ||
1408 | diff = next_count - stream_state->discarded_events_state.cur_count; | |
1409 | if (diff > 0) { | |
1410 | /* | |
1411 | * Add a discarded events notification. The events are | |
1412 | * considered to be lost betweem the state's last time | |
1413 | * and the current packet's end time. | |
1414 | */ | |
1415 | notif = bt_notification_discarded_elements_create( | |
1416 | BT_NOTIFICATION_TYPE_DISCARDED_EVENTS, | |
1417 | stream_state->stream, | |
1418 | stream_state->discarded_events_state.cur_begin, | |
1419 | ts_end, diff); | |
1420 | if (!notif) { | |
1421 | BT_LOGE_STR("Cannot create discarded events notification."); | |
1422 | ret = -1; | |
1423 | goto end; | |
1424 | } | |
1425 | ||
1426 | add_action_push_notif(iterator, notif); | |
1427 | } | |
1428 | ||
1429 | update_state: | |
1430 | add_action_update_stream_state_discarded_elements(iterator, | |
1431 | ACTION_TYPE_UPDATE_STREAM_STATE_DISCARDED_EVENTS, | |
1432 | stream_state, ts_end, next_count); | |
1433 | ||
1434 | end: | |
1435 | bt_put(notif); | |
1436 | return ret; | |
1437 | } | |
1438 | ||
1439 | static | |
1440 | int get_field_clock_value(struct bt_ctf_field *root_field, | |
1441 | const char *field_name, | |
1442 | struct bt_ctf_clock_value **user_clock_val) | |
1443 | { | |
1444 | struct bt_ctf_field *field; | |
1445 | struct bt_ctf_field_type *ft = NULL; | |
1446 | struct bt_ctf_clock_class *clock_class = NULL; | |
1447 | struct bt_ctf_clock_value *clock_value = NULL; | |
1448 | uint64_t val; | |
1449 | int ret = 0; | |
1450 | ||
1451 | field = get_struct_field_uint(root_field, field_name); | |
1452 | if (!field) { | |
1453 | /* Not an error: skip this */ | |
1454 | goto end; | |
1455 | } | |
1456 | ||
1457 | ft = bt_ctf_field_get_type(field); | |
1458 | assert(ft); | |
1459 | clock_class = bt_ctf_field_type_integer_get_mapped_clock_class(ft); | |
1460 | if (!clock_class) { | |
1461 | BT_LOGW("Integer field type has no mapped clock class but it's expected to have one: " | |
1462 | "ft-addr=%p", ft); | |
1463 | ret = -1; | |
1464 | goto end; | |
1465 | } | |
1466 | ||
1467 | ret = bt_ctf_field_unsigned_integer_get_value(field, &val); | |
1468 | if (ret) { | |
1469 | BT_LOGW("Cannot get integer field's raw value: " | |
1470 | "field-addr=%p", field); | |
1471 | ret = -1; | |
1472 | goto end; | |
1473 | } | |
1474 | ||
1475 | clock_value = bt_ctf_clock_value_create(clock_class, val); | |
1476 | if (!clock_value) { | |
1477 | BT_LOGE_STR("Cannot create clock value from clock class."); | |
1478 | ret = -1; | |
1479 | goto end; | |
1480 | } | |
1481 | ||
1482 | /* Move clock value to user */ | |
1483 | *user_clock_val = clock_value; | |
1484 | clock_value = NULL; | |
1485 | ||
1486 | end: | |
1487 | bt_put(field); | |
1488 | bt_put(ft); | |
1489 | bt_put(clock_class); | |
1490 | bt_put(clock_value); | |
1491 | return ret; | |
1492 | } | |
1493 | ||
1494 | static | |
1495 | int get_ts_begin_ts_end_from_packet(struct bt_ctf_packet *packet, | |
1496 | struct bt_ctf_clock_value **user_ts_begin, | |
1497 | struct bt_ctf_clock_value **user_ts_end) | |
1498 | { | |
1499 | struct bt_ctf_field *packet_context = NULL; | |
1500 | struct bt_ctf_clock_value *ts_begin = NULL; | |
1501 | struct bt_ctf_clock_value *ts_end = NULL; | |
1502 | int ret = 0; | |
1503 | ||
1504 | packet_context = bt_ctf_packet_get_context(packet); | |
1505 | if (!packet_context) { | |
1506 | goto end; | |
1507 | } | |
1508 | ||
1509 | ret = get_field_clock_value(packet_context, "timestamp_begin", | |
1510 | &ts_begin); | |
1511 | if (ret) { | |
1512 | BT_LOGW("Cannot create clock value for packet context's `timestamp_begin` field: " | |
1513 | "packet-addr=%p, packet-context-field-addr=%p", | |
1514 | packet, packet_context); | |
1515 | goto end; | |
1516 | } | |
1517 | ||
1518 | ret = get_field_clock_value(packet_context, "timestamp_end", | |
1519 | &ts_end); | |
1520 | if (ret) { | |
1521 | BT_LOGW("Cannot create clock value for packet context's `timestamp_begin` field: " | |
1522 | "packet-addr=%p, packet-context-field-addr=%p", | |
1523 | packet, packet_context); | |
1524 | goto end; | |
1525 | } | |
1526 | ||
1527 | /* Move clock values to user */ | |
1528 | *user_ts_begin = ts_begin; | |
1529 | ts_begin = NULL; | |
1530 | *user_ts_end = ts_end; | |
1531 | ts_end = NULL; | |
1532 | ||
1533 | end: | |
1534 | bt_put(packet_context); | |
1535 | bt_put(ts_begin); | |
1536 | bt_put(ts_end); | |
1537 | return ret; | |
1538 | } | |
1539 | ||
1540 | static | |
1541 | int handle_discarded_elements(struct bt_notification_iterator *iterator, | |
1542 | struct bt_ctf_packet *packet, struct stream_state *stream_state) | |
1543 | { | |
1544 | struct bt_ctf_clock_value *ts_begin = NULL; | |
1545 | struct bt_ctf_clock_value *ts_end = NULL; | |
1546 | int ret; | |
1547 | ||
1548 | ret = get_ts_begin_ts_end_from_packet(packet, &ts_begin, &ts_end); | |
1549 | if (ret) { | |
1550 | BT_LOGW("Cannot get packet's beginning or end clock values: " | |
1551 | "packet-addr=%p, ret=%d", packet, ret); | |
1552 | ret = -1; | |
1553 | goto end; | |
1554 | } | |
1555 | ||
1556 | ret = handle_discarded_packets(iterator, packet, ts_begin, ts_end, | |
1557 | stream_state); | |
1558 | if (ret) { | |
1559 | BT_LOGW("Cannot handle discarded packets for packet: " | |
1560 | "packet-addr=%p, ret=%d", packet, ret); | |
1561 | ret = -1; | |
1562 | goto end; | |
1563 | } | |
1564 | ||
1565 | ret = handle_discarded_events(iterator, packet, ts_begin, ts_end, | |
1566 | stream_state); | |
1567 | if (ret) { | |
1568 | BT_LOGW("Cannot handle discarded events for packet: " | |
1569 | "packet-addr=%p, ret=%d", packet, ret); | |
1570 | ret = -1; | |
1571 | goto end; | |
1572 | } | |
1573 | ||
1574 | end: | |
1575 | bt_put(ts_begin); | |
1576 | bt_put(ts_end); | |
1577 | return ret; | |
1578 | } | |
1579 | ||
3230ee6b PP |
1580 | static |
1581 | int handle_packet_switch(struct bt_notification_iterator *iterator, | |
1582 | struct bt_notification *packet_begin_notif, | |
1583 | struct bt_ctf_packet *new_packet, | |
1584 | struct stream_state *stream_state) | |
1585 | { | |
1586 | int ret = 0; | |
1587 | ||
1588 | if (stream_state->cur_packet == new_packet) { | |
1589 | goto end; | |
1590 | } | |
1591 | ||
5af447e5 PP |
1592 | BT_LOGV("Handling packet switch: " |
1593 | "cur-packet-addr=%p, new-packet-addr=%p", | |
1594 | stream_state->cur_packet, new_packet); | |
1595 | ||
3230ee6b PP |
1596 | if (stream_state->cur_packet) { |
1597 | /* End of the current packet */ | |
1598 | ret = add_action_push_notif_packet_end(iterator, | |
1599 | stream_state->cur_packet); | |
1600 | if (ret) { | |
5af447e5 | 1601 | BT_LOGE_STR("Cannot add \"push packet end notification\" action."); |
3230ee6b PP |
1602 | goto error; |
1603 | } | |
1604 | } | |
1605 | ||
b04a393b | 1606 | /* |
5cde4876 MD |
1607 | * Check the new packet's context fields for discarded packets |
1608 | * and events to emit those automatic notifications. | |
b04a393b PP |
1609 | */ |
1610 | ret = handle_discarded_elements(iterator, new_packet, stream_state); | |
1611 | if (ret) { | |
1612 | BT_LOGE_STR("Cannot handle discarded elements for new packet."); | |
1613 | goto error; | |
1614 | } | |
1615 | ||
3230ee6b PP |
1616 | /* Beginning of the new packet */ |
1617 | if (packet_begin_notif) { | |
1618 | add_action_push_notif(iterator, packet_begin_notif); | |
1619 | } else if (new_packet) { | |
1620 | ret = add_action_push_notif_packet_begin(iterator, | |
1621 | new_packet); | |
1622 | if (ret) { | |
5af447e5 | 1623 | BT_LOGE_STR("Cannot add \"push packet beginning notification\" action."); |
3230ee6b PP |
1624 | goto error; |
1625 | } | |
1626 | } | |
1627 | ||
1628 | add_action_set_stream_state_cur_packet(iterator, stream_state, | |
1629 | new_packet); | |
1630 | goto end; | |
1631 | ||
1632 | error: | |
1633 | ret = -1; | |
1634 | ||
1635 | end: | |
1636 | return ret; | |
1637 | } | |
1638 | ||
1639 | static | |
1640 | int handle_notif_stream_begin( | |
1641 | struct bt_notification_iterator *iterator, | |
1642 | struct bt_notification *notif, | |
1643 | struct bt_ctf_stream *notif_stream) | |
1644 | { | |
1645 | int ret = 0; | |
1646 | struct stream_state *stream_state; | |
1647 | ||
1648 | assert(notif->type == BT_NOTIFICATION_TYPE_STREAM_BEGIN); | |
1649 | assert(notif_stream); | |
1650 | ret = ensure_stream_state_exists(iterator, notif, notif_stream, | |
1651 | &stream_state); | |
1652 | if (ret) { | |
5af447e5 | 1653 | BT_LOGE_STR("Cannot ensure that stream state exists."); |
3230ee6b PP |
1654 | goto error; |
1655 | } | |
1656 | ||
1657 | goto end; | |
1658 | ||
1659 | error: | |
1660 | ret = -1; | |
1661 | ||
1662 | end: | |
1663 | return ret; | |
1664 | } | |
1665 | ||
1666 | static | |
1667 | int handle_notif_stream_end( | |
1668 | struct bt_notification_iterator *iterator, | |
1669 | struct bt_notification *notif, | |
1670 | struct bt_ctf_stream *notif_stream) | |
1671 | { | |
1672 | int ret = 0; | |
1673 | struct stream_state *stream_state; | |
1674 | ||
1675 | assert(notif->type == BT_NOTIFICATION_TYPE_STREAM_END); | |
1676 | assert(notif_stream); | |
1677 | ret = ensure_stream_state_exists(iterator, NULL, notif_stream, | |
1678 | &stream_state); | |
1679 | if (ret) { | |
5af447e5 | 1680 | BT_LOGE_STR("Cannot ensure that stream state exists."); |
3230ee6b PP |
1681 | goto error; |
1682 | } | |
1683 | ||
1684 | ret = handle_packet_switch(iterator, NULL, NULL, stream_state); | |
1685 | if (ret) { | |
5af447e5 | 1686 | BT_LOGE_STR("Cannot handle packet switch."); |
3230ee6b PP |
1687 | goto error; |
1688 | } | |
1689 | ||
1690 | add_action_push_notif(iterator, notif); | |
1691 | add_action_set_stream_state_is_ended(iterator, stream_state); | |
1692 | goto end; | |
1693 | ||
1694 | error: | |
1695 | ret = -1; | |
1696 | ||
1697 | end: | |
1698 | return ret; | |
1699 | } | |
1700 | ||
b04a393b PP |
1701 | static |
1702 | int handle_notif_discarded_elements( | |
1703 | struct bt_notification_iterator *iterator, | |
1704 | struct bt_notification *notif, | |
1705 | struct bt_ctf_stream *notif_stream) | |
1706 | { | |
1707 | int ret = 0; | |
1708 | struct stream_state *stream_state; | |
1709 | ||
1710 | assert(notif->type == BT_NOTIFICATION_TYPE_DISCARDED_EVENTS || | |
1711 | notif->type == BT_NOTIFICATION_TYPE_DISCARDED_PACKETS); | |
1712 | assert(notif_stream); | |
1713 | ret = ensure_stream_state_exists(iterator, NULL, notif_stream, | |
1714 | &stream_state); | |
1715 | if (ret) { | |
1716 | BT_LOGE_STR("Cannot ensure that stream state exists."); | |
1717 | goto error; | |
1718 | } | |
1719 | ||
1720 | add_action_push_notif(iterator, notif); | |
1721 | goto end; | |
1722 | ||
1723 | error: | |
1724 | ret = -1; | |
1725 | ||
1726 | end: | |
1727 | return ret; | |
1728 | } | |
1729 | ||
3230ee6b PP |
1730 | static |
1731 | int handle_notif_packet_begin( | |
1732 | struct bt_notification_iterator *iterator, | |
1733 | struct bt_notification *notif, | |
1734 | struct bt_ctf_stream *notif_stream, | |
1735 | struct bt_ctf_packet *notif_packet) | |
1736 | { | |
1737 | int ret = 0; | |
1738 | struct stream_state *stream_state; | |
1739 | ||
1740 | assert(notif->type == BT_NOTIFICATION_TYPE_PACKET_BEGIN); | |
1741 | assert(notif_packet); | |
1742 | ret = ensure_stream_state_exists(iterator, NULL, notif_stream, | |
1743 | &stream_state); | |
1744 | if (ret) { | |
5af447e5 | 1745 | BT_LOGE_STR("Cannot ensure that stream state exists."); |
3230ee6b PP |
1746 | goto error; |
1747 | } | |
1748 | ||
1749 | ret = handle_packet_switch(iterator, notif, notif_packet, stream_state); | |
1750 | if (ret) { | |
5af447e5 | 1751 | BT_LOGE_STR("Cannot handle packet switch."); |
3230ee6b PP |
1752 | goto error; |
1753 | } | |
1754 | ||
1755 | goto end; | |
1756 | ||
1757 | error: | |
1758 | ret = -1; | |
1759 | ||
1760 | end: | |
1761 | return ret; | |
1762 | } | |
1763 | ||
1764 | static | |
1765 | int handle_notif_packet_end( | |
1766 | struct bt_notification_iterator *iterator, | |
1767 | struct bt_notification *notif, | |
1768 | struct bt_ctf_stream *notif_stream, | |
1769 | struct bt_ctf_packet *notif_packet) | |
1770 | { | |
1771 | int ret = 0; | |
1772 | struct stream_state *stream_state; | |
1773 | ||
1774 | assert(notif->type == BT_NOTIFICATION_TYPE_PACKET_END); | |
1775 | assert(notif_packet); | |
1776 | ret = ensure_stream_state_exists(iterator, NULL, notif_stream, | |
1777 | &stream_state); | |
1778 | if (ret) { | |
5af447e5 | 1779 | BT_LOGE_STR("Cannot ensure that stream state exists."); |
3230ee6b PP |
1780 | goto error; |
1781 | } | |
1782 | ||
1783 | ret = handle_packet_switch(iterator, NULL, notif_packet, stream_state); | |
1784 | if (ret) { | |
5af447e5 | 1785 | BT_LOGE_STR("Cannot handle packet switch."); |
3230ee6b PP |
1786 | goto error; |
1787 | } | |
1788 | ||
1789 | /* End of the current packet */ | |
1790 | add_action_push_notif(iterator, notif); | |
1791 | add_action_set_stream_state_cur_packet(iterator, stream_state, NULL); | |
1792 | goto end; | |
1793 | ||
1794 | error: | |
1795 | ret = -1; | |
1796 | ||
1797 | end: | |
1798 | return ret; | |
1799 | } | |
1800 | ||
1801 | static | |
1802 | int handle_notif_event( | |
1803 | struct bt_notification_iterator *iterator, | |
1804 | struct bt_notification *notif, | |
1805 | struct bt_ctf_stream *notif_stream, | |
1806 | struct bt_ctf_packet *notif_packet) | |
1807 | { | |
1808 | int ret = 0; | |
1809 | struct stream_state *stream_state; | |
1810 | ||
1811 | assert(notif->type == BT_NOTIFICATION_TYPE_EVENT); | |
1812 | assert(notif_packet); | |
1813 | ret = ensure_stream_state_exists(iterator, NULL, notif_stream, | |
1814 | &stream_state); | |
1815 | if (ret) { | |
5af447e5 | 1816 | BT_LOGE_STR("Cannot ensure that stream state exists."); |
3230ee6b PP |
1817 | goto error; |
1818 | } | |
1819 | ||
1820 | ret = handle_packet_switch(iterator, NULL, notif_packet, stream_state); | |
1821 | if (ret) { | |
5af447e5 | 1822 | BT_LOGE_STR("Cannot handle packet switch."); |
3230ee6b PP |
1823 | goto error; |
1824 | } | |
1825 | ||
1826 | add_action_push_notif(iterator, notif); | |
1827 | goto end; | |
1828 | ||
1829 | error: | |
1830 | ret = -1; | |
1831 | ||
1832 | end: | |
1833 | return ret; | |
1834 | } | |
1835 | ||
1836 | static | |
1837 | int enqueue_notification_and_automatic( | |
1838 | struct bt_notification_iterator *iterator, | |
1839 | struct bt_notification *notif) | |
1840 | { | |
1841 | int ret = 0; | |
1842 | struct bt_ctf_event *notif_event = NULL; | |
1843 | struct bt_ctf_stream *notif_stream = NULL; | |
1844 | struct bt_ctf_packet *notif_packet = NULL; | |
1845 | ||
1846 | assert(notif); | |
1847 | ||
5af447e5 PP |
1848 | BT_LOGV("Enqueuing user notification and automatic notifications: " |
1849 | "iter-addr=%p, notif-addr=%p", iterator, notif); | |
1850 | ||
fa054faf PP |
1851 | // TODO: Skip most of this if the iterator is only subscribed |
1852 | // to event/inactivity notifications. | |
1853 | ||
3230ee6b PP |
1854 | /* Get the stream and packet referred by the notification */ |
1855 | switch (notif->type) { | |
1856 | case BT_NOTIFICATION_TYPE_EVENT: | |
1857 | notif_event = bt_notification_event_borrow_event(notif); | |
1858 | assert(notif_event); | |
1859 | notif_packet = bt_ctf_event_borrow_packet(notif_event); | |
1860 | assert(notif_packet); | |
1861 | break; | |
1862 | case BT_NOTIFICATION_TYPE_STREAM_BEGIN: | |
1863 | notif_stream = | |
1864 | bt_notification_stream_begin_borrow_stream(notif); | |
1865 | assert(notif_stream); | |
1866 | break; | |
1867 | case BT_NOTIFICATION_TYPE_STREAM_END: | |
1868 | notif_stream = bt_notification_stream_end_borrow_stream(notif); | |
1869 | assert(notif_stream); | |
1870 | break; | |
1871 | case BT_NOTIFICATION_TYPE_PACKET_BEGIN: | |
1872 | notif_packet = | |
1873 | bt_notification_packet_begin_borrow_packet(notif); | |
1874 | assert(notif_packet); | |
1875 | break; | |
1876 | case BT_NOTIFICATION_TYPE_PACKET_END: | |
1877 | notif_packet = bt_notification_packet_end_borrow_packet(notif); | |
1878 | assert(notif_packet); | |
1879 | break; | |
1880 | case BT_NOTIFICATION_TYPE_INACTIVITY: | |
1881 | /* Always valid */ | |
7cdc2bab | 1882 | goto handle_notif; |
3230ee6b PP |
1883 | default: |
1884 | /* | |
1885 | * Invalid type of notification. Only the notification | |
1886 | * types above are allowed to be returned by a user | |
1887 | * component. | |
1888 | */ | |
b04a393b PP |
1889 | BT_LOGF("Unexpected notification type at this point: " |
1890 | "notif-addr=%p, notif-type=%s", notif, | |
1891 | bt_notification_type_string(notif->type)); | |
1892 | abort(); | |
3230ee6b PP |
1893 | } |
1894 | ||
1895 | if (notif_packet) { | |
1896 | notif_stream = bt_ctf_packet_borrow_stream(notif_packet); | |
1897 | assert(notif_stream); | |
1898 | } | |
1899 | ||
1900 | if (!notif_stream) { | |
1901 | /* | |
1902 | * The notification has no reference to a stream: it | |
1903 | * cannot cause the creation of automatic notifications. | |
1904 | */ | |
5af447e5 | 1905 | BT_LOGV_STR("Notification has no reference to any stream: skipping automatic notification generation."); |
3230ee6b PP |
1906 | goto end; |
1907 | } | |
1908 | ||
1909 | if (!validate_notification(iterator, notif, notif_stream, | |
1910 | notif_packet)) { | |
5af447e5 | 1911 | BT_LOGW_STR("Invalid notification."); |
3230ee6b PP |
1912 | goto error; |
1913 | } | |
1914 | ||
7cdc2bab | 1915 | handle_notif: |
3230ee6b PP |
1916 | switch (notif->type) { |
1917 | case BT_NOTIFICATION_TYPE_EVENT: | |
1918 | ret = handle_notif_event(iterator, notif, notif_stream, | |
1919 | notif_packet); | |
1920 | break; | |
1921 | case BT_NOTIFICATION_TYPE_STREAM_BEGIN: | |
1922 | ret = handle_notif_stream_begin(iterator, notif, notif_stream); | |
1923 | break; | |
1924 | case BT_NOTIFICATION_TYPE_STREAM_END: | |
1925 | ret = handle_notif_stream_end(iterator, notif, notif_stream); | |
1926 | break; | |
1927 | case BT_NOTIFICATION_TYPE_PACKET_BEGIN: | |
1928 | ret = handle_notif_packet_begin(iterator, notif, notif_stream, | |
1929 | notif_packet); | |
1930 | break; | |
1931 | case BT_NOTIFICATION_TYPE_PACKET_END: | |
1932 | ret = handle_notif_packet_end(iterator, notif, notif_stream, | |
1933 | notif_packet); | |
1934 | break; | |
b04a393b PP |
1935 | case BT_NOTIFICATION_TYPE_DISCARDED_EVENTS: |
1936 | case BT_NOTIFICATION_TYPE_DISCARDED_PACKETS: | |
1937 | ret = handle_notif_discarded_elements(iterator, notif, | |
1938 | notif_stream); | |
1939 | break; | |
7cdc2bab MD |
1940 | case BT_NOTIFICATION_TYPE_INACTIVITY: |
1941 | add_action_push_notif(iterator, notif); | |
1942 | break; | |
3230ee6b PP |
1943 | default: |
1944 | break; | |
1945 | } | |
1946 | ||
1947 | if (ret) { | |
5af447e5 | 1948 | BT_LOGW_STR("Failed to handle notification for automatic notification generation."); |
3230ee6b PP |
1949 | goto error; |
1950 | } | |
1951 | ||
1952 | apply_actions(iterator); | |
5af447e5 PP |
1953 | BT_LOGV("Enqueued user notification and automatic notifications: " |
1954 | "iter-addr=%p, notif-addr=%p", iterator, notif); | |
3230ee6b PP |
1955 | goto end; |
1956 | ||
1957 | error: | |
1958 | ret = -1; | |
1959 | ||
1960 | end: | |
1961 | return ret; | |
1962 | } | |
1963 | ||
1964 | static | |
1965 | int handle_end(struct bt_notification_iterator *iterator) | |
1966 | { | |
1967 | GHashTableIter stream_state_iter; | |
1968 | gpointer stream_gptr, stream_state_gptr; | |
1969 | int ret = 0; | |
1970 | ||
5af447e5 PP |
1971 | BT_LOGV("Handling end of iteration: addr=%p", iterator); |
1972 | ||
3230ee6b PP |
1973 | /* |
1974 | * Emit a "stream end" notification for each non-ended stream | |
1975 | * known by this iterator and mark them as ended. | |
1976 | */ | |
1977 | g_hash_table_iter_init(&stream_state_iter, iterator->stream_states); | |
1978 | ||
1979 | while (g_hash_table_iter_next(&stream_state_iter, &stream_gptr, | |
1980 | &stream_state_gptr)) { | |
1981 | struct stream_state *stream_state = stream_state_gptr; | |
1982 | ||
1983 | assert(stream_state_gptr); | |
1984 | ||
1985 | if (stream_state->is_ended) { | |
1986 | continue; | |
1987 | } | |
1988 | ||
1989 | ret = handle_packet_switch(iterator, NULL, NULL, stream_state); | |
1990 | if (ret) { | |
5af447e5 | 1991 | BT_LOGE_STR("Cannot handle packet switch."); |
3230ee6b PP |
1992 | goto error; |
1993 | } | |
1994 | ||
1995 | ret = add_action_push_notif_stream_end(iterator, stream_gptr); | |
1996 | if (ret) { | |
5af447e5 | 1997 | BT_LOGE_STR("Cannot add \"push stream end notification\" action."); |
3230ee6b PP |
1998 | goto error; |
1999 | } | |
2000 | ||
2001 | add_action_set_stream_state_is_ended(iterator, stream_state); | |
2002 | } | |
2003 | ||
2004 | apply_actions(iterator); | |
5af447e5 | 2005 | BT_LOGV("Handled end of iteration: addr=%p", iterator); |
3230ee6b PP |
2006 | goto end; |
2007 | ||
2008 | error: | |
2009 | ret = -1; | |
2010 | ||
2011 | end: | |
2012 | return ret; | |
2013 | } | |
2014 | ||
2015 | static | |
2016 | enum bt_notification_iterator_status ensure_queue_has_notifications( | |
2017 | struct bt_notification_iterator *iterator) | |
53d45b87 | 2018 | { |
890882ef PP |
2019 | struct bt_private_notification_iterator *priv_iterator = |
2020 | bt_private_notification_iterator_from_notification_iterator(iterator); | |
d3eb6e8f | 2021 | bt_component_class_notification_iterator_next_method next_method = NULL; |
fe8ad2b6 PP |
2022 | struct bt_notification_iterator_next_return next_return = { |
2023 | .status = BT_NOTIFICATION_ITERATOR_STATUS_OK, | |
2024 | .notification = NULL, | |
2025 | }; | |
3230ee6b PP |
2026 | enum bt_notification_iterator_status status = |
2027 | BT_NOTIFICATION_ITERATOR_STATUS_OK; | |
2028 | int ret; | |
41a2b7ae | 2029 | |
3230ee6b | 2030 | assert(iterator); |
5af447e5 | 2031 | BT_LOGD("Ensuring that notification iterator's queue has at least one notification: " |
8f9d7550 PP |
2032 | "iter-addr=%p, queue-size=%u, iter-state=%s", |
2033 | iterator, iterator->queue->length, | |
2034 | bt_notification_iterator_state_string(iterator->state)); | |
3230ee6b PP |
2035 | |
2036 | if (iterator->queue->length > 0) { | |
8f9d7550 PP |
2037 | /* |
2038 | * We already have enough. Even if this notification | |
2039 | * iterator is finalized, its user can still flush its | |
2040 | * current queue's content by calling its "next" method | |
2041 | * since this content is local and has no impact on what | |
2042 | * used to be the iterator's upstream component. | |
2043 | */ | |
5af447e5 | 2044 | BT_LOGD_STR("Queue already has at least one notification."); |
3230ee6b PP |
2045 | goto end; |
2046 | } | |
2047 | ||
bd14d768 PP |
2048 | switch (iterator->state) { |
2049 | case BT_NOTIFICATION_ITERATOR_STATE_FINALIZED_AND_ENDED: | |
8f9d7550 | 2050 | case BT_NOTIFICATION_ITERATOR_STATE_FINALIZED: |
5af447e5 | 2051 | BT_LOGD_STR("Notification iterator's \"next\" called, but it is finalized."); |
bd14d768 PP |
2052 | status = BT_NOTIFICATION_ITERATOR_STATUS_CANCELED; |
2053 | goto end; | |
2054 | case BT_NOTIFICATION_ITERATOR_STATE_ENDED: | |
5af447e5 | 2055 | BT_LOGD_STR("Notification iterator is ended."); |
3230ee6b | 2056 | status = BT_NOTIFICATION_ITERATOR_STATUS_END; |
41a2b7ae | 2057 | goto end; |
bd14d768 PP |
2058 | default: |
2059 | break; | |
41a2b7ae | 2060 | } |
d3eb6e8f | 2061 | |
3230ee6b PP |
2062 | assert(iterator->upstream_component); |
2063 | assert(iterator->upstream_component->class); | |
d3eb6e8f | 2064 | |
3230ee6b PP |
2065 | /* Pick the appropriate "next" method */ |
2066 | switch (iterator->upstream_component->class->type) { | |
d3eb6e8f PP |
2067 | case BT_COMPONENT_CLASS_TYPE_SOURCE: |
2068 | { | |
2069 | struct bt_component_class_source *source_class = | |
3230ee6b | 2070 | container_of(iterator->upstream_component->class, |
d3eb6e8f PP |
2071 | struct bt_component_class_source, parent); |
2072 | ||
2073 | assert(source_class->methods.iterator.next); | |
2074 | next_method = source_class->methods.iterator.next; | |
2075 | break; | |
2076 | } | |
2077 | case BT_COMPONENT_CLASS_TYPE_FILTER: | |
2078 | { | |
2079 | struct bt_component_class_filter *filter_class = | |
3230ee6b | 2080 | container_of(iterator->upstream_component->class, |
d3eb6e8f PP |
2081 | struct bt_component_class_filter, parent); |
2082 | ||
2083 | assert(filter_class->methods.iterator.next); | |
2084 | next_method = filter_class->methods.iterator.next; | |
2085 | break; | |
2086 | } | |
2087 | default: | |
0fbb9a9f | 2088 | abort(); |
d3eb6e8f PP |
2089 | } |
2090 | ||
3230ee6b PP |
2091 | /* |
2092 | * Call the user's "next" method to get the next notification | |
fa054faf | 2093 | * and status. |
3230ee6b | 2094 | */ |
d3eb6e8f | 2095 | assert(next_method); |
3230ee6b | 2096 | |
fa054faf | 2097 | while (iterator->queue->length == 0) { |
5af447e5 | 2098 | BT_LOGD_STR("Calling user's \"next\" method."); |
fa054faf | 2099 | next_return = next_method(priv_iterator); |
5af447e5 PP |
2100 | BT_LOGD("User method returned: status=%s", |
2101 | bt_notification_iterator_status_string(next_return.status)); | |
fa054faf | 2102 | if (next_return.status < 0) { |
5af447e5 | 2103 | BT_LOGW_STR("User method failed."); |
fa054faf | 2104 | status = next_return.status; |
3230ee6b PP |
2105 | goto end; |
2106 | } | |
2107 | ||
8cf27cc5 PP |
2108 | if (iterator->state == BT_NOTIFICATION_ITERATOR_STATE_FINALIZED || |
2109 | iterator->state == BT_NOTIFICATION_ITERATOR_STATE_FINALIZED_AND_ENDED) { | |
2110 | /* | |
2111 | * The user's "next" method, somehow, cancelled | |
2112 | * its own notification iterator. This can | |
2113 | * happen, for example, when the user's method | |
2114 | * removes the port on which there's the | |
2115 | * connection from which the iterator was | |
2116 | * created. In this case, said connection is | |
2117 | * ended, and all its notification iterators are | |
2118 | * finalized. | |
2119 | * | |
2120 | * Only bt_put() the returned notification if | |
2121 | * the status is | |
2122 | * BT_NOTIFICATION_ITERATOR_STATUS_OK because | |
2123 | * otherwise this field could be garbage. | |
2124 | */ | |
2125 | if (next_return.status == | |
2126 | BT_NOTIFICATION_ITERATOR_STATUS_OK) { | |
2127 | bt_put(next_return.notification); | |
2128 | } | |
2129 | ||
2130 | status = BT_NOTIFICATION_ITERATOR_STATUS_CANCELED; | |
2131 | goto end; | |
2132 | } | |
2133 | ||
fa054faf PP |
2134 | switch (next_return.status) { |
2135 | case BT_NOTIFICATION_ITERATOR_STATUS_END: | |
2136 | ret = handle_end(iterator); | |
2137 | if (ret) { | |
5af447e5 | 2138 | BT_LOGW_STR("Cannot handle end of iteration."); |
fa054faf PP |
2139 | status = BT_NOTIFICATION_ITERATOR_STATUS_ERROR; |
2140 | goto end; | |
2141 | } | |
3230ee6b | 2142 | |
8f9d7550 PP |
2143 | assert(iterator->state == |
2144 | BT_NOTIFICATION_ITERATOR_STATE_ACTIVE); | |
2145 | iterator->state = BT_NOTIFICATION_ITERATOR_STATE_ENDED; | |
bd14d768 | 2146 | |
8f9d7550 PP |
2147 | if (iterator->queue->length == 0) { |
2148 | status = BT_NOTIFICATION_ITERATOR_STATUS_END; | |
bd14d768 | 2149 | } |
5af447e5 PP |
2150 | |
2151 | BT_LOGD("Set new status: status=%s", | |
2152 | bt_notification_iterator_status_string(status)); | |
3230ee6b | 2153 | goto end; |
fa054faf PP |
2154 | case BT_NOTIFICATION_ITERATOR_STATUS_AGAIN: |
2155 | status = BT_NOTIFICATION_ITERATOR_STATUS_AGAIN; | |
2156 | goto end; | |
2157 | case BT_NOTIFICATION_ITERATOR_STATUS_OK: | |
2158 | if (!next_return.notification) { | |
5af447e5 | 2159 | BT_LOGW_STR("User method returned BT_NOTIFICATION_ITERATOR_STATUS_OK, but notification is NULL."); |
fa054faf PP |
2160 | status = BT_NOTIFICATION_ITERATOR_STATUS_ERROR; |
2161 | goto end; | |
2162 | } | |
2163 | ||
b04a393b PP |
2164 | /* |
2165 | * Ignore some notifications which are always | |
2166 | * automatically generated by the notification | |
2167 | * iterator to make sure they have valid values. | |
2168 | */ | |
2169 | switch (next_return.notification->type) { | |
2170 | case BT_NOTIFICATION_TYPE_DISCARDED_PACKETS: | |
2171 | case BT_NOTIFICATION_TYPE_DISCARDED_EVENTS: | |
2172 | BT_LOGV("Ignoring discarded elements notification returned by notification iterator's \"next\" method: " | |
2173 | "notif-type=%s", | |
2174 | bt_notification_type_string(next_return.notification->type)); | |
2175 | BT_PUT(next_return.notification); | |
2176 | continue; | |
2177 | default: | |
2178 | break; | |
2179 | } | |
2180 | ||
fa054faf PP |
2181 | /* |
2182 | * We know the notification is valid. Before we | |
2183 | * push it to the head of the queue, push the | |
2184 | * appropriate automatic notifications if any. | |
2185 | */ | |
2186 | ret = enqueue_notification_and_automatic(iterator, | |
2187 | next_return.notification); | |
2188 | BT_PUT(next_return.notification); | |
2189 | if (ret) { | |
5af447e5 | 2190 | BT_LOGW("Cannot enqueue notification and automatic notifications."); |
fa054faf PP |
2191 | status = BT_NOTIFICATION_ITERATOR_STATUS_ERROR; |
2192 | goto end; | |
2193 | } | |
2194 | break; | |
2195 | default: | |
2196 | /* Unknown non-error status */ | |
0fbb9a9f | 2197 | abort(); |
3230ee6b | 2198 | } |
41a2b7ae PP |
2199 | } |
2200 | ||
2201 | end: | |
3230ee6b PP |
2202 | return status; |
2203 | } | |
2204 | ||
2205 | enum bt_notification_iterator_status | |
2206 | bt_notification_iterator_next(struct bt_notification_iterator *iterator) | |
2207 | { | |
2208 | enum bt_notification_iterator_status status; | |
2209 | ||
2210 | if (!iterator) { | |
5af447e5 | 2211 | BT_LOGW_STR("Invalid parameter: notification iterator is NULL."); |
3230ee6b PP |
2212 | status = BT_NOTIFICATION_ITERATOR_STATUS_INVALID; |
2213 | goto end; | |
2214 | } | |
2215 | ||
5af447e5 PP |
2216 | BT_LOGD("Notification iterator's \"next\": iter-addr=%p", iterator); |
2217 | ||
3230ee6b PP |
2218 | /* |
2219 | * Make sure that the iterator's queue contains at least one | |
2220 | * notification. | |
2221 | */ | |
2222 | status = ensure_queue_has_notifications(iterator); | |
2223 | if (status != BT_NOTIFICATION_ITERATOR_STATUS_OK) { | |
5af447e5 | 2224 | /* Not an error */ |
3230ee6b PP |
2225 | goto end; |
2226 | } | |
2227 | ||
2228 | /* | |
2229 | * Move the notification at the tail of the queue to the | |
2230 | * iterator's current notification. | |
2231 | */ | |
2232 | assert(iterator->queue->length > 0); | |
2233 | bt_put(iterator->current_notification); | |
2234 | iterator->current_notification = g_queue_pop_tail(iterator->queue); | |
2235 | assert(iterator->current_notification); | |
2236 | ||
2237 | end: | |
2238 | return status; | |
53d45b87 JG |
2239 | } |
2240 | ||
413bc2c4 JG |
2241 | struct bt_component *bt_notification_iterator_get_component( |
2242 | struct bt_notification_iterator *iterator) | |
2243 | { | |
3230ee6b | 2244 | return bt_get(iterator->upstream_component); |
413bc2c4 JG |
2245 | } |
2246 | ||
91457551 PP |
2247 | struct bt_private_component * |
2248 | bt_private_notification_iterator_get_private_component( | |
2249 | struct bt_private_notification_iterator *private_iterator) | |
2250 | { | |
2251 | return bt_private_component_from_component( | |
2252 | bt_notification_iterator_get_component( | |
2253 | bt_notification_iterator_from_private(private_iterator))); | |
2254 | } | |
2255 | ||
9531634f JG |
2256 | enum bt_notification_iterator_status bt_notification_iterator_seek_time( |
2257 | struct bt_notification_iterator *iterator, | |
2258 | enum bt_notification_iterator_seek_origin seek_origin, | |
2259 | int64_t time) | |
2260 | { | |
b7726e32 MD |
2261 | enum bt_notification_iterator_status ret = |
2262 | BT_NOTIFICATION_ITERATOR_STATUS_UNSUPPORTED; | |
9531634f JG |
2263 | return ret; |
2264 | } |