2 * Copyright (C) 2019 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
4 * This library is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License, version 2.1 only,
6 * as published by the Free Software Foundation.
8 * This library is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this library; if not, write to the Free Software Foundation,
15 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 #include <lttng/constant.h>
19 #include <common/string-utils/format.h>
20 #include <common/trace-chunk.h>
21 #include <common/trace-chunk-registry.h>
22 #include <common/hashtable/utils.h>
23 #include <common/hashtable/hashtable.h>
24 #include <common/error.h>
25 #include <common/utils.h>
26 #include <common/time.h>
27 #include <common/optional.h>
28 #include <common/compat/directory-handle.h>
29 #include <common/credentials.h>
30 #include <common/defaults.h>
31 #include <common/dynamic-array.h>
34 #include <urcu/rculfhash.h>
41 * Two ISO 8601-compatible timestamps, separated by a hypen, followed an
42 * index, i.e. <start-iso-8601>-<end-iso-8601>-<id-uint64_t>.
44 #define GENERATED_CHUNK_NAME_LEN (2 * sizeof("YYYYmmddTHHMMSS+HHMM") + MAX_INT_DEC_LEN(uint64_t))
45 #define DIR_CREATION_MODE (S_IRWXU | S_IRWXG)
47 enum trace_chunk_mode
{
48 TRACE_CHUNK_MODE_USER
,
49 TRACE_CHUNK_MODE_OWNER
,
53 * Callback to invoke on release of a trace chunk. Note that there is no
54 * need to 'lock' the trace chunk during the execution of these callbacks
55 * since only one thread may access a chunk during its destruction (the last
56 * to release its reference to the chunk).
58 typedef void (*chunk_close_command
)(struct lttng_trace_chunk
*trace_chunk
);
60 /* Move a completed trace chunk to the 'completed' trace archive folder. */
62 void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk
*trace_chunk
);
64 struct chunk_credentials
{
65 bool use_current_user
;
66 struct lttng_credentials user
;
69 /* NOTE: Make sure to update lttng_trace_chunk_copy if you modify this. */
70 struct lttng_trace_chunk
{
73 LTTNG_OPTIONAL(enum trace_chunk_mode
) mode
;
75 * First-level directories created within the trace chunk.
76 * Elements are of type 'char *'.
78 * Only used by _owner_ mode chunks.
80 struct lttng_dynamic_pointer_array top_level_directories
;
82 * All files contained within the trace chunk.
83 * Array of paths (char *).
85 struct lttng_dynamic_pointer_array files
;
86 /* Is contained within an lttng_trace_chunk_registry_element? */
87 bool in_registry_element
;
90 /* An unset id means the chunk is anonymous. */
91 LTTNG_OPTIONAL(uint64_t) id
;
92 LTTNG_OPTIONAL(time_t) timestamp_creation
;
93 LTTNG_OPTIONAL(time_t) timestamp_close
;
94 LTTNG_OPTIONAL(struct chunk_credentials
) credentials
;
95 struct lttng_directory_handle
*session_output_directory
;
96 struct lttng_directory_handle
*chunk_directory
;
97 LTTNG_OPTIONAL(enum lttng_trace_chunk_command_type
) close_command
;
100 /* A trace chunk is uniquely identified by its (session id, chunk id) tuple. */
101 struct lttng_trace_chunk_registry_element
{
102 struct lttng_trace_chunk chunk
;
104 /* Weak and only set when added. */
105 struct lttng_trace_chunk_registry
*registry
;
106 struct cds_lfht_node trace_chunk_registry_ht_node
;
107 /* call_rcu delayed reclaim. */
108 struct rcu_head rcu_node
;
111 struct lttng_trace_chunk_registry
{
116 char *close_command_names
[] = {
117 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
] =
118 "move to completed chunk folder",
119 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
] =
121 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
] =
126 chunk_close_command close_command_funcs
[] = {
127 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
] =
128 lttng_trace_chunk_move_to_completed
,
132 bool lttng_trace_chunk_registry_element_equals(
133 const struct lttng_trace_chunk_registry_element
*a
,
134 const struct lttng_trace_chunk_registry_element
*b
)
136 if (a
->session_id
!= b
->session_id
) {
139 if (a
->chunk
.id
.is_set
!= b
->chunk
.id
.is_set
) {
142 if (a
->chunk
.id
.is_set
&& a
->chunk
.id
.value
!= b
->chunk
.id
.value
) {
151 int lttng_trace_chunk_registry_element_match(struct cds_lfht_node
*node
,
154 const struct lttng_trace_chunk_registry_element
*element_a
, *element_b
;
156 element_a
= (const struct lttng_trace_chunk_registry_element
*) key
;
157 element_b
= caa_container_of(node
, typeof(*element_b
),
158 trace_chunk_registry_ht_node
);
159 return lttng_trace_chunk_registry_element_equals(element_a
, element_b
);
163 unsigned long lttng_trace_chunk_registry_element_hash(
164 const struct lttng_trace_chunk_registry_element
*element
)
166 unsigned long hash
= hash_key_u64(&element
->session_id
,
169 if (element
->chunk
.id
.is_set
) {
170 hash
^= hash_key_u64(&element
->chunk
.id
.value
, lttng_ht_seed
);
177 char *generate_chunk_name(uint64_t chunk_id
, time_t creation_timestamp
,
178 const time_t *close_timestamp
)
181 char *new_name
= NULL
;
182 char start_datetime
[ISO8601_STR_LEN
] = {};
183 /* Add 1 for a '-' prefix. */
184 char end_datetime_suffix
[ISO8601_STR_LEN
+ 1] = {};
186 ret
= time_to_iso8601_str(
188 start_datetime
, sizeof(start_datetime
));
190 ERR("Failed to format trace chunk start date time");
193 if (close_timestamp
) {
194 *end_datetime_suffix
= '-';
195 ret
= time_to_iso8601_str(
197 end_datetime_suffix
+ 1,
198 sizeof(end_datetime_suffix
) - 1);
200 ERR("Failed to format trace chunk end date time");
204 new_name
= zmalloc(GENERATED_CHUNK_NAME_LEN
);
206 ERR("Failed to allocate buffer for automatically-generated trace chunk name");
209 ret
= snprintf(new_name
, GENERATED_CHUNK_NAME_LEN
, "%s%s-%" PRIu64
,
210 start_datetime
, end_datetime_suffix
, chunk_id
);
211 if (ret
>= GENERATED_CHUNK_NAME_LEN
|| ret
== -1) {
212 ERR("Failed to format trace chunk name");
223 void lttng_trace_chunk_init(struct lttng_trace_chunk
*chunk
)
225 urcu_ref_init(&chunk
->ref
);
226 pthread_mutex_init(&chunk
->lock
, NULL
);
227 lttng_dynamic_pointer_array_init(&chunk
->top_level_directories
, free
);
228 lttng_dynamic_pointer_array_init(&chunk
->files
, free
);
232 void lttng_trace_chunk_fini(struct lttng_trace_chunk
*chunk
)
234 if (chunk
->session_output_directory
) {
235 lttng_directory_handle_put(
236 chunk
->session_output_directory
);
237 chunk
->session_output_directory
= NULL
;
239 if (chunk
->chunk_directory
) {
240 lttng_directory_handle_put(chunk
->chunk_directory
);
241 chunk
->chunk_directory
= NULL
;
245 lttng_dynamic_pointer_array_reset(&chunk
->top_level_directories
);
246 lttng_dynamic_pointer_array_reset(&chunk
->files
);
247 pthread_mutex_destroy(&chunk
->lock
);
251 struct lttng_trace_chunk
*lttng_trace_chunk_allocate(void)
253 struct lttng_trace_chunk
*chunk
= NULL
;
255 chunk
= zmalloc(sizeof(*chunk
));
257 ERR("Failed to allocate trace chunk");
260 lttng_trace_chunk_init(chunk
);
266 struct lttng_trace_chunk
*lttng_trace_chunk_create_anonymous(void)
268 DBG("Creating anonymous trace chunk");
269 return lttng_trace_chunk_allocate();
273 struct lttng_trace_chunk
*lttng_trace_chunk_create(
274 uint64_t chunk_id
, time_t chunk_creation_time
)
276 struct lttng_trace_chunk
*chunk
;
277 char chunk_creation_datetime_buf
[16] = {};
278 const char *chunk_creation_datetime_str
= "(formatting error)";
279 struct tm timeinfo_buf
, *timeinfo
;
281 timeinfo
= localtime_r(&chunk_creation_time
, &timeinfo_buf
);
285 /* Don't fail because of this; it is only used for logging. */
286 strftime_ret
= strftime(chunk_creation_datetime_buf
,
287 sizeof(chunk_creation_datetime_buf
),
288 "%Y%m%d-%H%M%S", timeinfo
);
290 chunk_creation_datetime_str
=
291 chunk_creation_datetime_buf
;
295 DBG("Creating trace chunk: chunk_id = %" PRIu64
", creation time = %s",
296 chunk_id
, chunk_creation_datetime_str
);
297 chunk
= lttng_trace_chunk_allocate();
302 LTTNG_OPTIONAL_SET(&chunk
->id
, chunk_id
);
303 LTTNG_OPTIONAL_SET(&chunk
->timestamp_creation
, chunk_creation_time
);
305 chunk
->name
= generate_chunk_name(chunk_id
,
306 chunk_creation_time
, NULL
);
308 ERR("Failed to allocate trace chunk name storage");
313 DBG("Chunk name set to \"%s\"", chunk
->name
? : "(none)");
317 lttng_trace_chunk_put(chunk
);
322 struct lttng_trace_chunk
*lttng_trace_chunk_copy(
323 struct lttng_trace_chunk
*source_chunk
)
325 struct lttng_trace_chunk
*new_chunk
= lttng_trace_chunk_allocate();
331 pthread_mutex_lock(&source_chunk
->lock
);
333 * A new chunk is always a user; it shall create no new trace
336 new_chunk
->mode
= (typeof(new_chunk
->mode
)) {
338 .value
= TRACE_CHUNK_MODE_USER
,
341 * top_level_directories is not copied as it is never used
342 * by _user_ mode chunks.
344 /* The new chunk is not part of a registry (yet, at least). */
345 new_chunk
->in_registry_element
= false;
346 new_chunk
->name_overridden
= source_chunk
->name_overridden
;
347 if (source_chunk
->name
) {
348 new_chunk
->name
= strdup(source_chunk
->name
);
349 if (!new_chunk
->name
) {
350 ERR("Failed to copy source trace chunk name in %s()",
355 new_chunk
->id
= source_chunk
->id
;
356 new_chunk
->timestamp_creation
= source_chunk
->timestamp_creation
;
357 new_chunk
->timestamp_close
= source_chunk
->timestamp_close
;
358 new_chunk
->credentials
= source_chunk
->credentials
;
359 if (source_chunk
->session_output_directory
) {
360 const bool reference_acquired
= lttng_directory_handle_get(
361 source_chunk
->session_output_directory
);
363 assert(reference_acquired
);
364 new_chunk
->session_output_directory
=
365 source_chunk
->session_output_directory
;
367 if (source_chunk
->chunk_directory
) {
368 const bool reference_acquired
= lttng_directory_handle_get(
369 source_chunk
->chunk_directory
);
371 assert(reference_acquired
);
372 new_chunk
->chunk_directory
= source_chunk
->chunk_directory
;
374 new_chunk
->close_command
= source_chunk
->close_command
;
375 pthread_mutex_unlock(&source_chunk
->lock
);
379 pthread_mutex_unlock(&source_chunk
->lock
);
380 lttng_trace_chunk_put(new_chunk
);
385 enum lttng_trace_chunk_status
lttng_trace_chunk_get_id(
386 struct lttng_trace_chunk
*chunk
, uint64_t *id
)
388 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
390 pthread_mutex_lock(&chunk
->lock
);
391 if (chunk
->id
.is_set
) {
392 *id
= chunk
->id
.value
;
394 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
396 pthread_mutex_unlock(&chunk
->lock
);
401 enum lttng_trace_chunk_status
lttng_trace_chunk_get_creation_timestamp(
402 struct lttng_trace_chunk
*chunk
, time_t *creation_ts
)
405 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
407 pthread_mutex_lock(&chunk
->lock
);
408 if (chunk
->timestamp_creation
.is_set
) {
409 *creation_ts
= chunk
->timestamp_creation
.value
;
411 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
413 pthread_mutex_unlock(&chunk
->lock
);
418 enum lttng_trace_chunk_status
lttng_trace_chunk_get_close_timestamp(
419 struct lttng_trace_chunk
*chunk
, time_t *close_ts
)
421 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
423 pthread_mutex_lock(&chunk
->lock
);
424 if (chunk
->timestamp_close
.is_set
) {
425 *close_ts
= chunk
->timestamp_close
.value
;
427 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
429 pthread_mutex_unlock(&chunk
->lock
);
434 enum lttng_trace_chunk_status
lttng_trace_chunk_set_close_timestamp(
435 struct lttng_trace_chunk
*chunk
, time_t close_ts
)
437 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
439 pthread_mutex_lock(&chunk
->lock
);
440 if (!chunk
->timestamp_creation
.is_set
) {
441 ERR("Failed to set trace chunk close timestamp: creation timestamp is unset");
442 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
445 if (chunk
->timestamp_creation
.value
> close_ts
) {
446 ERR("Failed to set trace chunk close timestamp: close timestamp is before creation timestamp");
447 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
450 LTTNG_OPTIONAL_SET(&chunk
->timestamp_close
, close_ts
);
451 if (!chunk
->name_overridden
) {
453 chunk
->name
= generate_chunk_name(LTTNG_OPTIONAL_GET(chunk
->id
),
454 LTTNG_OPTIONAL_GET(chunk
->timestamp_creation
),
457 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
461 pthread_mutex_unlock(&chunk
->lock
);
466 enum lttng_trace_chunk_status
lttng_trace_chunk_get_name(
467 struct lttng_trace_chunk
*chunk
, const char **name
,
468 bool *name_overridden
)
470 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
472 pthread_mutex_lock(&chunk
->lock
);
473 if (name_overridden
) {
474 *name_overridden
= chunk
->name_overridden
;
477 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
482 pthread_mutex_unlock(&chunk
->lock
);
487 bool lttng_trace_chunk_get_name_overridden(struct lttng_trace_chunk
*chunk
)
489 bool name_overridden
;
491 pthread_mutex_lock(&chunk
->lock
);
492 name_overridden
= chunk
->name_overridden
;
493 pthread_mutex_unlock(&chunk
->lock
);
494 return name_overridden
;
498 bool is_valid_chunk_name(const char *name
)
506 len
= lttng_strnlen(name
, LTTNG_NAME_MAX
);
507 if (len
== 0 || len
== LTTNG_NAME_MAX
) {
511 if (strchr(name
, '/') || strchr(name
, '.')) {
519 enum lttng_trace_chunk_status
lttng_trace_chunk_override_name(
520 struct lttng_trace_chunk
*chunk
, const char *name
)
524 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
526 if (!is_valid_chunk_name(name
)) {
527 ERR("Attempted to set an invalid name on a trace chunk: name = %s",
529 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
533 pthread_mutex_lock(&chunk
->lock
);
534 if (!chunk
->id
.is_set
) {
535 ERR("Attempted to set an override name on an anonymous trace chunk: name = %s",
537 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
540 new_name
= strdup(name
);
542 ERR("Failed to allocate new trace chunk name");
543 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
547 chunk
->name
= new_name
;
548 chunk
->name_overridden
= true;
550 pthread_mutex_unlock(&chunk
->lock
);
556 enum lttng_trace_chunk_status
lttng_trace_chunk_get_credentials(
557 struct lttng_trace_chunk
*chunk
,
558 struct lttng_credentials
*credentials
)
560 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
562 pthread_mutex_lock(&chunk
->lock
);
563 if (chunk
->credentials
.is_set
) {
564 if (chunk
->credentials
.value
.use_current_user
) {
565 credentials
->uid
= geteuid();
566 credentials
->gid
= getegid();
568 *credentials
= chunk
->credentials
.value
.user
;
571 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
573 pthread_mutex_unlock(&chunk
->lock
);
578 enum lttng_trace_chunk_status
lttng_trace_chunk_set_credentials(
579 struct lttng_trace_chunk
*chunk
,
580 const struct lttng_credentials
*user_credentials
)
582 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
583 const struct chunk_credentials credentials
= {
584 .user
= *user_credentials
,
585 .use_current_user
= false,
588 pthread_mutex_lock(&chunk
->lock
);
589 if (chunk
->credentials
.is_set
) {
590 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
593 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
595 pthread_mutex_unlock(&chunk
->lock
);
600 enum lttng_trace_chunk_status
lttng_trace_chunk_set_credentials_current_user(
601 struct lttng_trace_chunk
*chunk
)
603 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
604 const struct chunk_credentials credentials
= {
605 .use_current_user
= true,
608 pthread_mutex_lock(&chunk
->lock
);
609 if (chunk
->credentials
.is_set
) {
610 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
613 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
615 pthread_mutex_unlock(&chunk
->lock
);
621 enum lttng_trace_chunk_status
lttng_trace_chunk_set_as_owner(
622 struct lttng_trace_chunk
*chunk
,
623 struct lttng_directory_handle
*session_output_directory
)
626 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
627 struct lttng_directory_handle
*chunk_directory_handle
= NULL
;
628 bool reference_acquired
;
630 pthread_mutex_lock(&chunk
->lock
);
631 if (chunk
->mode
.is_set
) {
632 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
635 if (!chunk
->credentials
.is_set
) {
637 * Fatal error, credentials must be set before a
638 * directory is created.
640 ERR("Credentials of trace chunk are unset: refusing to set session output directory");
641 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
647 * A nameless chunk does not need its own output directory.
648 * The session's output directory will be used.
650 ret
= lttng_directory_handle_create_subdirectory_as_user(
651 session_output_directory
,
654 !chunk
->credentials
.value
.use_current_user
?
655 &chunk
->credentials
.value
.user
: NULL
);
657 PERROR("Failed to create chunk output directory \"%s\"",
659 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
663 chunk_directory_handle
= lttng_directory_handle_create_from_handle(
665 session_output_directory
);
666 if (!chunk_directory_handle
) {
667 /* The function already logs on all error paths. */
668 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
671 chunk
->chunk_directory
= chunk_directory_handle
;
672 chunk_directory_handle
= NULL
;
673 reference_acquired
= lttng_directory_handle_get(
674 session_output_directory
);
675 assert(reference_acquired
);
676 chunk
->session_output_directory
= session_output_directory
;
677 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_OWNER
);
679 pthread_mutex_unlock(&chunk
->lock
);
684 enum lttng_trace_chunk_status
lttng_trace_chunk_set_as_user(
685 struct lttng_trace_chunk
*chunk
,
686 struct lttng_directory_handle
*chunk_directory
)
688 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
689 bool reference_acquired
;
691 pthread_mutex_lock(&chunk
->lock
);
692 if (chunk
->mode
.is_set
) {
693 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
696 if (!chunk
->credentials
.is_set
) {
697 ERR("Credentials of trace chunk are unset: refusing to set chunk output directory");
698 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
701 reference_acquired
= lttng_directory_handle_get(chunk_directory
);
702 assert(reference_acquired
);
703 chunk
->chunk_directory
= chunk_directory
;
704 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_USER
);
706 pthread_mutex_unlock(&chunk
->lock
);
711 enum lttng_trace_chunk_status
lttng_trace_chunk_borrow_chunk_directory_handle(
712 struct lttng_trace_chunk
*chunk
,
713 const struct lttng_directory_handle
**handle
)
715 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
717 pthread_mutex_lock(&chunk
->lock
);
718 if (!chunk
->chunk_directory
) {
719 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
723 *handle
= chunk
->chunk_directory
;
725 pthread_mutex_unlock(&chunk
->lock
);
729 /* Add a top-level directory to the trace chunk if it was previously unknown. */
731 int add_top_level_directory_unique(struct lttng_trace_chunk
*chunk
,
732 const char *new_path
)
736 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
737 &chunk
->top_level_directories
);
738 const char *new_path_separator_pos
= strchr(new_path
, '/');
739 const ptrdiff_t new_path_top_level_len
= new_path_separator_pos
?
740 new_path_separator_pos
- new_path
: strlen(new_path
);
742 for (i
= 0; i
< count
; i
++) {
743 const char *path
= lttng_dynamic_pointer_array_get_pointer(
744 &chunk
->top_level_directories
, i
);
745 const ptrdiff_t path_top_level_len
= strlen(path
);
747 if (path_top_level_len
!= new_path_top_level_len
) {
750 if (!strncmp(path
, new_path
, path_top_level_len
)) {
757 char *copy
= lttng_strndup(new_path
, new_path_top_level_len
);
759 DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"",
760 new_path
, chunk
->name
? : "(unnamed)");
762 PERROR("Failed to copy path");
766 ret
= lttng_dynamic_pointer_array_add_pointer(
767 &chunk
->top_level_directories
, copy
);
769 ERR("Allocation failure while adding top-level directory entry to a trace chunk");
779 enum lttng_trace_chunk_status
lttng_trace_chunk_create_subdirectory(
780 struct lttng_trace_chunk
*chunk
,
784 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
786 DBG("Creating trace chunk subdirectory \"%s\"", path
);
787 pthread_mutex_lock(&chunk
->lock
);
788 if (!chunk
->credentials
.is_set
) {
790 * Fatal error, credentials must be set before a
791 * directory is created.
793 ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"",
795 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
798 if (!chunk
->mode
.is_set
||
799 chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
) {
800 ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk",
802 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
805 if (!chunk
->chunk_directory
) {
806 ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory",
808 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
812 ERR("Refusing to create absolute trace chunk directory \"%s\"",
814 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
817 ret
= lttng_directory_handle_create_subdirectory_recursive_as_user(
818 chunk
->chunk_directory
, path
,
820 chunk
->credentials
.value
.use_current_user
?
821 NULL
: &chunk
->credentials
.value
.user
);
823 PERROR("Failed to create trace chunk subdirectory \"%s\"",
825 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
828 ret
= add_top_level_directory_unique(chunk
, path
);
830 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
834 pthread_mutex_unlock(&chunk
->lock
);
839 * TODO: Implement O(1) lookup.
842 bool lttng_trace_chunk_find_file(struct lttng_trace_chunk
*chunk
,
843 const char *path
, size_t *index
)
847 count
= lttng_dynamic_pointer_array_get_count(&chunk
->files
);
848 for (i
= 0; i
< count
; i
++) {
849 const char *iter_path
=
850 lttng_dynamic_pointer_array_get_pointer(
852 if (!strcmp(iter_path
, path
)) {
863 enum lttng_trace_chunk_status
lttng_trace_chunk_add_file(
864 struct lttng_trace_chunk
*chunk
,
869 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
871 if (lttng_trace_chunk_find_file(chunk
, path
, NULL
)) {
872 return LTTNG_TRACE_CHUNK_STATUS_OK
;
874 DBG("Adding new file \"%s\" to trace chunk \"%s\"",
875 path
, chunk
->name
? : "(unnamed)");
878 PERROR("Failed to copy path");
879 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
882 ret
= lttng_dynamic_pointer_array_add_pointer(
883 &chunk
->files
, copy
);
885 ERR("Allocation failure while adding file to a trace chunk");
887 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
895 void lttng_trace_chunk_remove_file(
896 struct lttng_trace_chunk
*chunk
,
903 found
= lttng_trace_chunk_find_file(chunk
, path
, &index
);
907 ret
= lttng_dynamic_pointer_array_remove_pointer(
908 &chunk
->files
, index
);
913 enum lttng_trace_chunk_status
lttng_trace_chunk_open_file(
914 struct lttng_trace_chunk
*chunk
, const char *file_path
,
915 int flags
, mode_t mode
, int *out_fd
, bool expect_no_file
)
918 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
920 DBG("Opening trace chunk file \"%s\"", file_path
);
921 pthread_mutex_lock(&chunk
->lock
);
922 if (!chunk
->credentials
.is_set
) {
924 * Fatal error, credentials must be set before a
927 ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"",
929 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
932 if (!chunk
->chunk_directory
) {
933 ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory",
935 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
938 status
= lttng_trace_chunk_add_file(chunk
, file_path
);
939 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
942 ret
= lttng_directory_handle_open_file_as_user(
943 chunk
->chunk_directory
, file_path
, flags
, mode
,
944 chunk
->credentials
.value
.use_current_user
?
945 NULL
: &chunk
->credentials
.value
.user
);
947 if (errno
== ENOENT
&& expect_no_file
) {
948 status
= LTTNG_TRACE_CHUNK_STATUS_NO_FILE
;
950 PERROR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d",
951 file_path
, flags
, (int) mode
);
952 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
954 lttng_trace_chunk_remove_file(chunk
, file_path
);
959 pthread_mutex_unlock(&chunk
->lock
);
964 int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk
*chunk
,
965 const char *file_path
)
968 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
970 DBG("Unlinking trace chunk file \"%s\"", file_path
);
971 pthread_mutex_lock(&chunk
->lock
);
972 if (!chunk
->credentials
.is_set
) {
974 * Fatal error, credentials must be set before a
975 * directory is created.
977 ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"",
979 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
982 if (!chunk
->chunk_directory
) {
983 ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory",
985 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
988 ret
= lttng_directory_handle_unlink_file_as_user(
989 chunk
->chunk_directory
, file_path
,
990 chunk
->credentials
.value
.use_current_user
?
991 NULL
: &chunk
->credentials
.value
.user
);
993 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
996 lttng_trace_chunk_remove_file(chunk
, file_path
);
998 pthread_mutex_unlock(&chunk
->lock
);
1003 void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk
*trace_chunk
)
1006 char *directory_to_rename
= NULL
;
1007 bool free_directory_to_rename
= false;
1008 char *archived_chunk_name
= NULL
;
1009 const uint64_t chunk_id
= LTTNG_OPTIONAL_GET(trace_chunk
->id
);
1010 const time_t creation_timestamp
=
1011 LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_creation
);
1012 const time_t close_timestamp
=
1013 LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_close
);
1014 struct lttng_directory_handle
*archived_chunks_directory
= NULL
;
1016 if (!trace_chunk
->mode
.is_set
||
1017 trace_chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
||
1018 !trace_chunk
->session_output_directory
) {
1020 * This command doesn't need to run if the output is remote
1021 * or if the trace chunk is not owned by this process.
1026 assert(trace_chunk
->mode
.value
== TRACE_CHUNK_MODE_OWNER
);
1027 assert(!trace_chunk
->name_overridden
);
1030 * The fist trace chunk of a session is directly output to the
1031 * session's output folder. In this case, the top level directories
1032 * must be moved to a temporary folder before that temporary directory
1033 * is renamed to match the chunk's name.
1035 if (chunk_id
== 0) {
1036 struct lttng_directory_handle
*temporary_rename_directory
=
1038 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
1039 &trace_chunk
->top_level_directories
);
1041 ret
= lttng_directory_handle_create_subdirectory_as_user(
1042 trace_chunk
->session_output_directory
,
1043 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY
,
1045 !trace_chunk
->credentials
.value
.use_current_user
?
1046 &trace_chunk
->credentials
.value
.user
: NULL
);
1048 PERROR("Failed to create temporary trace chunk rename directory \"%s\"",
1049 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY
);
1052 temporary_rename_directory
= lttng_directory_handle_create_from_handle(
1053 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY
,
1054 trace_chunk
->session_output_directory
);
1055 if (!temporary_rename_directory
) {
1056 ERR("Failed to get handle to temporary trace chunk rename directory");
1060 for (i
= 0; i
< count
; i
++) {
1061 const char *top_level_name
=
1062 lttng_dynamic_pointer_array_get_pointer(
1063 &trace_chunk
->top_level_directories
, i
);
1065 ret
= lttng_directory_handle_rename_as_user(
1066 trace_chunk
->session_output_directory
,
1068 temporary_rename_directory
,
1070 LTTNG_OPTIONAL_GET(trace_chunk
->credentials
).use_current_user
?
1072 &trace_chunk
->credentials
.value
.user
);
1074 PERROR("Failed to move \"%s\" to temporary trace chunk rename directory",
1076 lttng_directory_handle_put(
1077 temporary_rename_directory
);
1081 lttng_directory_handle_put(temporary_rename_directory
);
1082 directory_to_rename
= DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY
;
1083 free_directory_to_rename
= false;
1085 directory_to_rename
= generate_chunk_name(chunk_id
,
1086 creation_timestamp
, NULL
);
1087 if (!directory_to_rename
) {
1088 ERR("Failed to generate initial trace chunk name while renaming trace chunk");
1091 free_directory_to_rename
= true;
1094 archived_chunk_name
= generate_chunk_name(chunk_id
, creation_timestamp
,
1096 if (!archived_chunk_name
) {
1097 ERR("Failed to generate archived trace chunk name while renaming trace chunk");
1101 ret
= lttng_directory_handle_create_subdirectory_as_user(
1102 trace_chunk
->session_output_directory
,
1103 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
1105 !trace_chunk
->credentials
.value
.use_current_user
?
1106 &trace_chunk
->credentials
.value
.user
:
1109 PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
1110 "\" directory for archived trace chunks");
1114 archived_chunks_directory
= lttng_directory_handle_create_from_handle(
1115 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
1116 trace_chunk
->session_output_directory
);
1117 if (!archived_chunks_directory
) {
1118 PERROR("Failed to get handle to archived trace chunks directory");
1122 ret
= lttng_directory_handle_rename_as_user(
1123 trace_chunk
->session_output_directory
,
1124 directory_to_rename
,
1125 archived_chunks_directory
,
1126 archived_chunk_name
,
1127 LTTNG_OPTIONAL_GET(trace_chunk
->credentials
).use_current_user
?
1129 &trace_chunk
->credentials
.value
.user
);
1131 PERROR("Failed to rename folder \"%s\" to \"%s\"",
1132 directory_to_rename
, archived_chunk_name
);
1136 lttng_directory_handle_put(archived_chunks_directory
);
1137 free(archived_chunk_name
);
1138 if (free_directory_to_rename
) {
1139 free(directory_to_rename
);
1144 enum lttng_trace_chunk_status
lttng_trace_chunk_get_close_command(
1145 struct lttng_trace_chunk
*chunk
,
1146 enum lttng_trace_chunk_command_type
*command_type
)
1148 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1150 pthread_mutex_lock(&chunk
->lock
);
1151 if (chunk
->close_command
.is_set
) {
1152 *command_type
= chunk
->close_command
.value
;
1153 status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1155 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1157 pthread_mutex_unlock(&chunk
->lock
);
1162 enum lttng_trace_chunk_status
lttng_trace_chunk_set_close_command(
1163 struct lttng_trace_chunk
*chunk
,
1164 enum lttng_trace_chunk_command_type close_command
)
1166 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1168 if (close_command
< LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
||
1169 close_command
>= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX
) {
1170 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
1174 pthread_mutex_lock(&chunk
->lock
);
1175 if (chunk
->close_command
.is_set
) {
1176 DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
1177 close_command_names
[chunk
->close_command
.value
],
1178 close_command_names
[close_command
]);
1180 DBG("Setting trace chunk close command to \"%s\"",
1181 close_command_names
[close_command
]);
1184 * Unset close command for no-op for backward compatibility with relayd
1187 if (close_command
!= LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
) {
1188 LTTNG_OPTIONAL_SET(&chunk
->close_command
, close_command
);
1190 LTTNG_OPTIONAL_UNSET(&chunk
->close_command
);
1192 pthread_mutex_unlock(&chunk
->lock
);
1198 const char *lttng_trace_chunk_command_type_get_name(
1199 enum lttng_trace_chunk_command_type command
)
1202 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
:
1203 return "move to completed trace chunk folder";
1204 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
:
1205 return "no operation";
1206 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
:
1214 bool lttng_trace_chunk_get(struct lttng_trace_chunk
*chunk
)
1216 return urcu_ref_get_unless_zero(&chunk
->ref
);
1220 void free_lttng_trace_chunk_registry_element(struct rcu_head
*node
)
1222 struct lttng_trace_chunk_registry_element
*element
=
1223 container_of(node
, typeof(*element
), rcu_node
);
1225 lttng_trace_chunk_fini(&element
->chunk
);
1230 void lttng_trace_chunk_release(struct urcu_ref
*ref
)
1232 struct lttng_trace_chunk
*chunk
= container_of(ref
, typeof(*chunk
),
1235 if (chunk
->close_command
.is_set
) {
1236 close_command_funcs
[chunk
->close_command
.value
](chunk
);
1239 if (chunk
->in_registry_element
) {
1240 struct lttng_trace_chunk_registry_element
*element
;
1242 element
= container_of(chunk
, typeof(*element
), chunk
);
1243 if (element
->registry
) {
1245 cds_lfht_del(element
->registry
->ht
,
1246 &element
->trace_chunk_registry_ht_node
);
1248 call_rcu(&element
->rcu_node
,
1249 free_lttng_trace_chunk_registry_element
);
1251 /* Never published, can be free'd immediately. */
1252 free_lttng_trace_chunk_registry_element(
1253 &element
->rcu_node
);
1256 /* Not RCU-protected, free immediately. */
1257 lttng_trace_chunk_fini(chunk
);
1263 void lttng_trace_chunk_put(struct lttng_trace_chunk
*chunk
)
1268 assert(chunk
->ref
.refcount
);
1269 urcu_ref_put(&chunk
->ref
, lttng_trace_chunk_release
);
1273 struct lttng_trace_chunk_registry
*lttng_trace_chunk_registry_create(void)
1275 struct lttng_trace_chunk_registry
*registry
;
1277 registry
= zmalloc(sizeof(*registry
));
1282 registry
->ht
= cds_lfht_new(DEFAULT_HT_SIZE
, 1, 0,
1283 CDS_LFHT_AUTO_RESIZE
| CDS_LFHT_ACCOUNTING
, NULL
);
1284 if (!registry
->ht
) {
1290 lttng_trace_chunk_registry_destroy(registry
);
1295 void lttng_trace_chunk_registry_destroy(
1296 struct lttng_trace_chunk_registry
*registry
)
1302 int ret
= cds_lfht_destroy(registry
->ht
, NULL
);
1309 struct lttng_trace_chunk_registry_element
*
1310 lttng_trace_chunk_registry_element_create_from_chunk(
1311 struct lttng_trace_chunk
*chunk
, uint64_t session_id
)
1313 struct lttng_trace_chunk_registry_element
*element
=
1314 zmalloc(sizeof(*element
));
1319 cds_lfht_node_init(&element
->trace_chunk_registry_ht_node
);
1320 element
->session_id
= session_id
;
1322 element
->chunk
= *chunk
;
1323 lttng_trace_chunk_init(&element
->chunk
);
1324 if (chunk
->session_output_directory
) {
1325 /* Transferred ownership. */
1326 element
->chunk
.session_output_directory
=
1327 chunk
->session_output_directory
;
1328 chunk
->session_output_directory
= NULL
;
1330 if (chunk
->chunk_directory
) {
1331 /* Transferred ownership. */
1332 element
->chunk
.chunk_directory
= chunk
->chunk_directory
;
1333 chunk
->chunk_directory
= NULL
;
1336 * The original chunk becomes invalid; the name attribute is transferred
1337 * to the new chunk instance.
1340 element
->chunk
.in_registry_element
= true;
1346 struct lttng_trace_chunk
*
1347 lttng_trace_chunk_registry_publish_chunk(
1348 struct lttng_trace_chunk_registry
*registry
,
1349 uint64_t session_id
, struct lttng_trace_chunk
*chunk
)
1351 struct lttng_trace_chunk_registry_element
*element
;
1352 unsigned long element_hash
;
1354 pthread_mutex_lock(&chunk
->lock
);
1355 element
= lttng_trace_chunk_registry_element_create_from_chunk(chunk
,
1357 pthread_mutex_unlock(&chunk
->lock
);
1362 * chunk is now invalid, the only valid operation is a 'put' from the
1366 element_hash
= lttng_trace_chunk_registry_element_hash(element
);
1370 struct cds_lfht_node
*published_node
;
1371 struct lttng_trace_chunk
*published_chunk
;
1372 struct lttng_trace_chunk_registry_element
*published_element
;
1374 published_node
= cds_lfht_add_unique(registry
->ht
,
1376 lttng_trace_chunk_registry_element_match
,
1378 &element
->trace_chunk_registry_ht_node
);
1379 if (published_node
== &element
->trace_chunk_registry_ht_node
) {
1380 /* Successfully published the new element. */
1381 element
->registry
= registry
;
1382 /* Acquire a reference for the caller. */
1383 if (lttng_trace_chunk_get(&element
->chunk
)) {
1387 * Another thread concurrently unpublished the
1388 * trace chunk. This is currently unexpected.
1390 * Re-attempt to publish.
1392 ERR("Attempt to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
1398 * An equivalent trace chunk was published before this trace
1399 * chunk. Attempt to acquire a reference to the one that was
1400 * already published and release the reference to the copy we
1401 * created if successful.
1403 published_element
= container_of(published_node
,
1404 typeof(*published_element
),
1405 trace_chunk_registry_ht_node
);
1406 published_chunk
= &published_element
->chunk
;
1407 if (lttng_trace_chunk_get(published_chunk
)) {
1408 lttng_trace_chunk_put(&element
->chunk
);
1409 element
= published_element
;
1413 * A reference to the previously published trace chunk could not
1414 * be acquired. Hence, retry to publish our copy of the trace
1420 return element
? &element
->chunk
: NULL
;
1424 * Note that the caller must be registered as an RCU thread.
1425 * However, it does not need to hold the RCU read lock. The RCU read lock is
1426 * acquired to perform the look-up in the registry's hash table and held until
1427 * after a reference to the "found" trace chunk is acquired.
1429 * IOW, holding a reference guarantees the existence of the object for the
1433 struct lttng_trace_chunk
*_lttng_trace_chunk_registry_find_chunk(
1434 const struct lttng_trace_chunk_registry
*registry
,
1435 uint64_t session_id
, uint64_t *chunk_id
)
1437 const struct lttng_trace_chunk_registry_element target_element
= {
1438 .chunk
.id
.is_set
= !!chunk_id
,
1439 .chunk
.id
.value
= chunk_id
? *chunk_id
: 0,
1440 .session_id
= session_id
,
1442 const unsigned long element_hash
=
1443 lttng_trace_chunk_registry_element_hash(
1445 struct cds_lfht_node
*published_node
;
1446 struct lttng_trace_chunk_registry_element
*published_element
;
1447 struct lttng_trace_chunk
*published_chunk
= NULL
;
1448 struct cds_lfht_iter iter
;
1451 cds_lfht_lookup(registry
->ht
,
1453 lttng_trace_chunk_registry_element_match
,
1456 published_node
= cds_lfht_iter_get_node(&iter
);
1457 if (!published_node
) {
1461 published_element
= container_of(published_node
,
1462 typeof(*published_element
),
1463 trace_chunk_registry_ht_node
);
1464 if (lttng_trace_chunk_get(&published_element
->chunk
)) {
1465 published_chunk
= &published_element
->chunk
;
1469 return published_chunk
;
1473 struct lttng_trace_chunk
*
1474 lttng_trace_chunk_registry_find_chunk(
1475 const struct lttng_trace_chunk_registry
*registry
,
1476 uint64_t session_id
, uint64_t chunk_id
)
1478 return _lttng_trace_chunk_registry_find_chunk(registry
,
1479 session_id
, &chunk_id
);
1483 int lttng_trace_chunk_registry_chunk_exists(
1484 const struct lttng_trace_chunk_registry
*registry
,
1485 uint64_t session_id
, uint64_t chunk_id
, bool *chunk_exists
)
1488 const struct lttng_trace_chunk_registry_element target_element
= {
1489 .chunk
.id
.is_set
= true,
1490 .chunk
.id
.value
= chunk_id
,
1491 .session_id
= session_id
,
1493 const unsigned long element_hash
=
1494 lttng_trace_chunk_registry_element_hash(
1496 struct cds_lfht_node
*published_node
;
1497 struct cds_lfht_iter iter
;
1500 cds_lfht_lookup(registry
->ht
,
1502 lttng_trace_chunk_registry_element_match
,
1505 published_node
= cds_lfht_iter_get_node(&iter
);
1506 if (!published_node
) {
1507 *chunk_exists
= false;
1511 *chunk_exists
= !cds_lfht_is_node_deleted(published_node
);
1518 struct lttng_trace_chunk
*
1519 lttng_trace_chunk_registry_find_anonymous_chunk(
1520 const struct lttng_trace_chunk_registry
*registry
,
1521 uint64_t session_id
)
1523 return _lttng_trace_chunk_registry_find_chunk(registry
,
1527 unsigned int lttng_trace_chunk_registry_put_each_chunk(
1528 struct lttng_trace_chunk_registry
*registry
)
1530 struct cds_lfht_iter iter
;
1531 struct lttng_trace_chunk_registry_element
*chunk_element
;
1532 unsigned int trace_chunks_left
= 0;
1534 DBG("Releasing trace chunk registry to all trace chunks");
1536 cds_lfht_for_each_entry(registry
->ht
,
1537 &iter
, chunk_element
, trace_chunk_registry_ht_node
) {
1538 const char *chunk_id_str
= "none";
1539 char chunk_id_buf
[MAX_INT_DEC_LEN(uint64_t)];
1541 pthread_mutex_lock(&chunk_element
->chunk
.lock
);
1542 if (chunk_element
->chunk
.id
.is_set
) {
1545 fmt_ret
= snprintf(chunk_id_buf
, sizeof(chunk_id_buf
),
1547 chunk_element
->chunk
.id
.value
);
1548 if (fmt_ret
< 0 || fmt_ret
>= sizeof(chunk_id_buf
)) {
1549 chunk_id_str
= "formatting error";
1551 chunk_id_str
= chunk_id_buf
;
1555 DBG("Releasing reference to trace chunk: session_id = %" PRIu64
1556 "chunk_id = %s, name = \"%s\", status = %s",
1557 chunk_element
->session_id
,
1559 chunk_element
->chunk
.name
? : "none",
1560 chunk_element
->chunk
.close_command
.is_set
?
1562 pthread_mutex_unlock(&chunk_element
->chunk
.lock
);
1563 lttng_trace_chunk_put(&chunk_element
->chunk
);
1564 trace_chunks_left
++;
1567 DBG("Released reference to %u trace chunks in %s()", trace_chunks_left
,
1570 return trace_chunks_left
;