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 is_valid_chunk_name(const char *name
)
495 len
= lttng_strnlen(name
, LTTNG_NAME_MAX
);
496 if (len
== 0 || len
== LTTNG_NAME_MAX
) {
500 if (strchr(name
, '/') || strchr(name
, '.')) {
508 enum lttng_trace_chunk_status
lttng_trace_chunk_override_name(
509 struct lttng_trace_chunk
*chunk
, const char *name
)
513 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
515 if (!is_valid_chunk_name(name
)) {
516 ERR("Attempted to set an invalid name on a trace chunk: name = %s",
518 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
522 pthread_mutex_lock(&chunk
->lock
);
523 if (!chunk
->id
.is_set
) {
524 ERR("Attempted to set an override name on an anonymous trace chunk: name = %s",
526 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
529 new_name
= strdup(name
);
531 ERR("Failed to allocate new trace chunk name");
532 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
536 chunk
->name
= new_name
;
537 chunk
->name_overridden
= true;
539 pthread_mutex_unlock(&chunk
->lock
);
545 enum lttng_trace_chunk_status
lttng_trace_chunk_get_credentials(
546 struct lttng_trace_chunk
*chunk
,
547 struct lttng_credentials
*credentials
)
549 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
551 pthread_mutex_lock(&chunk
->lock
);
552 if (chunk
->credentials
.is_set
) {
553 if (chunk
->credentials
.value
.use_current_user
) {
554 credentials
->uid
= geteuid();
555 credentials
->gid
= getegid();
557 *credentials
= chunk
->credentials
.value
.user
;
560 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
562 pthread_mutex_unlock(&chunk
->lock
);
567 enum lttng_trace_chunk_status
lttng_trace_chunk_set_credentials(
568 struct lttng_trace_chunk
*chunk
,
569 const struct lttng_credentials
*user_credentials
)
571 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
572 const struct chunk_credentials credentials
= {
573 .user
= *user_credentials
,
574 .use_current_user
= false,
577 pthread_mutex_lock(&chunk
->lock
);
578 if (chunk
->credentials
.is_set
) {
579 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
582 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
584 pthread_mutex_unlock(&chunk
->lock
);
589 enum lttng_trace_chunk_status
lttng_trace_chunk_set_credentials_current_user(
590 struct lttng_trace_chunk
*chunk
)
592 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
593 const struct chunk_credentials credentials
= {
594 .use_current_user
= true,
597 pthread_mutex_lock(&chunk
->lock
);
598 if (chunk
->credentials
.is_set
) {
599 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
602 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
604 pthread_mutex_unlock(&chunk
->lock
);
610 enum lttng_trace_chunk_status
lttng_trace_chunk_set_as_owner(
611 struct lttng_trace_chunk
*chunk
,
612 struct lttng_directory_handle
*session_output_directory
)
615 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
616 struct lttng_directory_handle
*chunk_directory_handle
= NULL
;
617 bool reference_acquired
;
619 pthread_mutex_lock(&chunk
->lock
);
620 if (chunk
->mode
.is_set
) {
621 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
624 if (!chunk
->credentials
.is_set
) {
626 * Fatal error, credentials must be set before a
627 * directory is created.
629 ERR("Credentials of trace chunk are unset: refusing to set session output directory");
630 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
636 * A nameless chunk does not need its own output directory.
637 * The session's output directory will be used.
639 ret
= lttng_directory_handle_create_subdirectory_as_user(
640 session_output_directory
,
643 !chunk
->credentials
.value
.use_current_user
?
644 &chunk
->credentials
.value
.user
: NULL
);
646 PERROR("Failed to create chunk output directory \"%s\"",
648 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
652 chunk_directory_handle
= lttng_directory_handle_create_from_handle(
654 session_output_directory
);
655 if (!chunk_directory_handle
) {
656 /* The function already logs on all error paths. */
657 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
660 chunk
->chunk_directory
= chunk_directory_handle
;
661 chunk_directory_handle
= NULL
;
662 reference_acquired
= lttng_directory_handle_get(
663 session_output_directory
);
664 assert(reference_acquired
);
665 chunk
->session_output_directory
= session_output_directory
;
666 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_OWNER
);
668 pthread_mutex_unlock(&chunk
->lock
);
673 enum lttng_trace_chunk_status
lttng_trace_chunk_set_as_user(
674 struct lttng_trace_chunk
*chunk
,
675 struct lttng_directory_handle
*chunk_directory
)
677 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
678 bool reference_acquired
;
680 pthread_mutex_lock(&chunk
->lock
);
681 if (chunk
->mode
.is_set
) {
682 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
685 if (!chunk
->credentials
.is_set
) {
686 ERR("Credentials of trace chunk are unset: refusing to set chunk output directory");
687 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
690 reference_acquired
= lttng_directory_handle_get(chunk_directory
);
691 assert(reference_acquired
);
692 chunk
->chunk_directory
= chunk_directory
;
693 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_USER
);
695 pthread_mutex_unlock(&chunk
->lock
);
700 enum lttng_trace_chunk_status
lttng_trace_chunk_borrow_chunk_directory_handle(
701 struct lttng_trace_chunk
*chunk
,
702 const struct lttng_directory_handle
**handle
)
704 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
706 pthread_mutex_lock(&chunk
->lock
);
707 if (!chunk
->chunk_directory
) {
708 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
712 *handle
= chunk
->chunk_directory
;
714 pthread_mutex_unlock(&chunk
->lock
);
718 /* Add a top-level directory to the trace chunk if it was previously unknown. */
720 int add_top_level_directory_unique(struct lttng_trace_chunk
*chunk
,
721 const char *new_path
)
725 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
726 &chunk
->top_level_directories
);
727 const char *new_path_separator_pos
= strchr(new_path
, '/');
728 const ptrdiff_t new_path_top_level_len
= new_path_separator_pos
?
729 new_path_separator_pos
- new_path
: strlen(new_path
);
731 for (i
= 0; i
< count
; i
++) {
732 const char *path
= lttng_dynamic_pointer_array_get_pointer(
733 &chunk
->top_level_directories
, i
);
734 const ptrdiff_t path_top_level_len
= strlen(path
);
736 if (path_top_level_len
!= new_path_top_level_len
) {
739 if (!strncmp(path
, new_path
, path_top_level_len
)) {
746 char *copy
= lttng_strndup(new_path
, new_path_top_level_len
);
748 DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"",
749 new_path
, chunk
->name
? : "(unnamed)");
751 PERROR("Failed to copy path");
755 ret
= lttng_dynamic_pointer_array_add_pointer(
756 &chunk
->top_level_directories
, copy
);
758 ERR("Allocation failure while adding top-level directory entry to a trace chunk");
768 enum lttng_trace_chunk_status
lttng_trace_chunk_create_subdirectory(
769 struct lttng_trace_chunk
*chunk
,
773 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
775 DBG("Creating trace chunk subdirectory \"%s\"", path
);
776 pthread_mutex_lock(&chunk
->lock
);
777 if (!chunk
->credentials
.is_set
) {
779 * Fatal error, credentials must be set before a
780 * directory is created.
782 ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"",
784 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
787 if (!chunk
->mode
.is_set
||
788 chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
) {
789 ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk",
791 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
794 if (!chunk
->chunk_directory
) {
795 ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory",
797 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
801 ERR("Refusing to create absolute trace chunk directory \"%s\"",
803 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
806 ret
= lttng_directory_handle_create_subdirectory_recursive_as_user(
807 chunk
->chunk_directory
, path
,
809 chunk
->credentials
.value
.use_current_user
?
810 NULL
: &chunk
->credentials
.value
.user
);
812 PERROR("Failed to create trace chunk subdirectory \"%s\"",
814 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
817 ret
= add_top_level_directory_unique(chunk
, path
);
819 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
823 pthread_mutex_unlock(&chunk
->lock
);
828 * TODO: Implement O(1) lookup.
831 bool lttng_trace_chunk_find_file(struct lttng_trace_chunk
*chunk
,
832 const char *path
, size_t *index
)
836 count
= lttng_dynamic_pointer_array_get_count(&chunk
->files
);
837 for (i
= 0; i
< count
; i
++) {
838 const char *iter_path
=
839 lttng_dynamic_pointer_array_get_pointer(
841 if (!strcmp(iter_path
, path
)) {
852 enum lttng_trace_chunk_status
lttng_trace_chunk_add_file(
853 struct lttng_trace_chunk
*chunk
,
858 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
860 if (lttng_trace_chunk_find_file(chunk
, path
, NULL
)) {
861 return LTTNG_TRACE_CHUNK_STATUS_OK
;
863 DBG("Adding new file \"%s\" to trace chunk \"%s\"",
864 path
, chunk
->name
? : "(unnamed)");
867 PERROR("Failed to copy path");
868 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
871 ret
= lttng_dynamic_pointer_array_add_pointer(
872 &chunk
->files
, copy
);
874 ERR("Allocation failure while adding file to a trace chunk");
876 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
884 void lttng_trace_chunk_remove_file(
885 struct lttng_trace_chunk
*chunk
,
892 found
= lttng_trace_chunk_find_file(chunk
, path
, &index
);
896 ret
= lttng_dynamic_pointer_array_remove_pointer(
897 &chunk
->files
, index
);
902 enum lttng_trace_chunk_status
lttng_trace_chunk_open_file(
903 struct lttng_trace_chunk
*chunk
, const char *file_path
,
904 int flags
, mode_t mode
, int *out_fd
, bool expect_no_file
)
907 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
909 DBG("Opening trace chunk file \"%s\"", file_path
);
910 pthread_mutex_lock(&chunk
->lock
);
911 if (!chunk
->credentials
.is_set
) {
913 * Fatal error, credentials must be set before a
916 ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"",
918 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
921 if (!chunk
->chunk_directory
) {
922 ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory",
924 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
927 status
= lttng_trace_chunk_add_file(chunk
, file_path
);
928 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
931 ret
= lttng_directory_handle_open_file_as_user(
932 chunk
->chunk_directory
, file_path
, flags
, mode
,
933 chunk
->credentials
.value
.use_current_user
?
934 NULL
: &chunk
->credentials
.value
.user
);
936 if (errno
== ENOENT
&& expect_no_file
) {
937 status
= LTTNG_TRACE_CHUNK_STATUS_NO_FILE
;
939 PERROR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d",
940 file_path
, flags
, (int) mode
);
941 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
943 lttng_trace_chunk_remove_file(chunk
, file_path
);
948 pthread_mutex_unlock(&chunk
->lock
);
953 int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk
*chunk
,
954 const char *file_path
)
957 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
959 DBG("Unlinking trace chunk file \"%s\"", file_path
);
960 pthread_mutex_lock(&chunk
->lock
);
961 if (!chunk
->credentials
.is_set
) {
963 * Fatal error, credentials must be set before a
964 * directory is created.
966 ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"",
968 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
971 if (!chunk
->chunk_directory
) {
972 ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory",
974 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
977 ret
= lttng_directory_handle_unlink_file_as_user(
978 chunk
->chunk_directory
, file_path
,
979 chunk
->credentials
.value
.use_current_user
?
980 NULL
: &chunk
->credentials
.value
.user
);
982 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
985 lttng_trace_chunk_remove_file(chunk
, file_path
);
987 pthread_mutex_unlock(&chunk
->lock
);
992 void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk
*trace_chunk
)
995 char *directory_to_rename
= NULL
;
996 bool free_directory_to_rename
= false;
997 char *archived_chunk_name
= NULL
;
998 const uint64_t chunk_id
= LTTNG_OPTIONAL_GET(trace_chunk
->id
);
999 const time_t creation_timestamp
=
1000 LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_creation
);
1001 const time_t close_timestamp
=
1002 LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_close
);
1003 struct lttng_directory_handle
*archived_chunks_directory
= NULL
;
1005 if (!trace_chunk
->mode
.is_set
||
1006 trace_chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
||
1007 !trace_chunk
->session_output_directory
) {
1009 * This command doesn't need to run if the output is remote
1010 * or if the trace chunk is not owned by this process.
1015 assert(trace_chunk
->mode
.value
== TRACE_CHUNK_MODE_OWNER
);
1016 assert(!trace_chunk
->name_overridden
);
1019 * The fist trace chunk of a session is directly output to the
1020 * session's output folder. In this case, the top level directories
1021 * must be moved to a temporary folder before that temporary directory
1022 * is renamed to match the chunk's name.
1024 if (chunk_id
== 0) {
1025 struct lttng_directory_handle
*temporary_rename_directory
=
1027 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
1028 &trace_chunk
->top_level_directories
);
1030 ret
= lttng_directory_handle_create_subdirectory_as_user(
1031 trace_chunk
->session_output_directory
,
1032 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY
,
1034 !trace_chunk
->credentials
.value
.use_current_user
?
1035 &trace_chunk
->credentials
.value
.user
: NULL
);
1037 PERROR("Failed to create temporary trace chunk rename directory \"%s\"",
1038 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY
);
1041 temporary_rename_directory
= lttng_directory_handle_create_from_handle(
1042 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY
,
1043 trace_chunk
->session_output_directory
);
1044 if (!temporary_rename_directory
) {
1045 ERR("Failed to get handle to temporary trace chunk rename directory");
1049 for (i
= 0; i
< count
; i
++) {
1050 const char *top_level_name
=
1051 lttng_dynamic_pointer_array_get_pointer(
1052 &trace_chunk
->top_level_directories
, i
);
1054 ret
= lttng_directory_handle_rename_as_user(
1055 trace_chunk
->session_output_directory
,
1057 temporary_rename_directory
,
1059 LTTNG_OPTIONAL_GET(trace_chunk
->credentials
).use_current_user
?
1061 &trace_chunk
->credentials
.value
.user
);
1063 PERROR("Failed to move \"%s\" to temporary trace chunk rename directory",
1065 lttng_directory_handle_put(
1066 temporary_rename_directory
);
1070 lttng_directory_handle_put(temporary_rename_directory
);
1071 directory_to_rename
= DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY
;
1072 free_directory_to_rename
= false;
1074 directory_to_rename
= generate_chunk_name(chunk_id
,
1075 creation_timestamp
, NULL
);
1076 if (!directory_to_rename
) {
1077 ERR("Failed to generate initial trace chunk name while renaming trace chunk");
1080 free_directory_to_rename
= true;
1083 archived_chunk_name
= generate_chunk_name(chunk_id
, creation_timestamp
,
1085 if (!archived_chunk_name
) {
1086 ERR("Failed to generate archived trace chunk name while renaming trace chunk");
1090 ret
= lttng_directory_handle_create_subdirectory_as_user(
1091 trace_chunk
->session_output_directory
,
1092 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
1094 !trace_chunk
->credentials
.value
.use_current_user
?
1095 &trace_chunk
->credentials
.value
.user
:
1098 PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
1099 "\" directory for archived trace chunks");
1103 archived_chunks_directory
= lttng_directory_handle_create_from_handle(
1104 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
1105 trace_chunk
->session_output_directory
);
1106 if (!archived_chunks_directory
) {
1107 PERROR("Failed to get handle to archived trace chunks directory");
1111 ret
= lttng_directory_handle_rename_as_user(
1112 trace_chunk
->session_output_directory
,
1113 directory_to_rename
,
1114 archived_chunks_directory
,
1115 archived_chunk_name
,
1116 LTTNG_OPTIONAL_GET(trace_chunk
->credentials
).use_current_user
?
1118 &trace_chunk
->credentials
.value
.user
);
1120 PERROR("Failed to rename folder \"%s\" to \"%s\"",
1121 directory_to_rename
, archived_chunk_name
);
1125 lttng_directory_handle_put(archived_chunks_directory
);
1126 free(archived_chunk_name
);
1127 if (free_directory_to_rename
) {
1128 free(directory_to_rename
);
1133 enum lttng_trace_chunk_status
lttng_trace_chunk_get_close_command(
1134 struct lttng_trace_chunk
*chunk
,
1135 enum lttng_trace_chunk_command_type
*command_type
)
1137 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1139 pthread_mutex_lock(&chunk
->lock
);
1140 if (chunk
->close_command
.is_set
) {
1141 *command_type
= chunk
->close_command
.value
;
1142 status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1144 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1146 pthread_mutex_unlock(&chunk
->lock
);
1151 enum lttng_trace_chunk_status
lttng_trace_chunk_set_close_command(
1152 struct lttng_trace_chunk
*chunk
,
1153 enum lttng_trace_chunk_command_type close_command
)
1155 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1157 if (close_command
< LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
||
1158 close_command
>= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX
) {
1159 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
1163 pthread_mutex_lock(&chunk
->lock
);
1164 if (chunk
->close_command
.is_set
) {
1165 DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
1166 close_command_names
[chunk
->close_command
.value
],
1167 close_command_names
[close_command
]);
1169 DBG("Setting trace chunk close command to \"%s\"",
1170 close_command_names
[close_command
]);
1173 * Unset close command for no-op for backward compatibility with relayd
1176 if (close_command
!= LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
) {
1177 LTTNG_OPTIONAL_SET(&chunk
->close_command
, close_command
);
1179 LTTNG_OPTIONAL_UNSET(&chunk
->close_command
);
1181 pthread_mutex_unlock(&chunk
->lock
);
1187 const char *lttng_trace_chunk_command_type_get_name(
1188 enum lttng_trace_chunk_command_type command
)
1191 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
:
1192 return "move to completed trace chunk folder";
1193 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
:
1194 return "no operation";
1195 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
:
1203 bool lttng_trace_chunk_get(struct lttng_trace_chunk
*chunk
)
1205 return urcu_ref_get_unless_zero(&chunk
->ref
);
1209 void free_lttng_trace_chunk_registry_element(struct rcu_head
*node
)
1211 struct lttng_trace_chunk_registry_element
*element
=
1212 container_of(node
, typeof(*element
), rcu_node
);
1214 lttng_trace_chunk_fini(&element
->chunk
);
1219 void lttng_trace_chunk_release(struct urcu_ref
*ref
)
1221 struct lttng_trace_chunk
*chunk
= container_of(ref
, typeof(*chunk
),
1224 if (chunk
->close_command
.is_set
) {
1225 close_command_funcs
[chunk
->close_command
.value
](chunk
);
1228 if (chunk
->in_registry_element
) {
1229 struct lttng_trace_chunk_registry_element
*element
;
1231 element
= container_of(chunk
, typeof(*element
), chunk
);
1232 if (element
->registry
) {
1234 cds_lfht_del(element
->registry
->ht
,
1235 &element
->trace_chunk_registry_ht_node
);
1237 call_rcu(&element
->rcu_node
,
1238 free_lttng_trace_chunk_registry_element
);
1240 /* Never published, can be free'd immediately. */
1241 free_lttng_trace_chunk_registry_element(
1242 &element
->rcu_node
);
1245 /* Not RCU-protected, free immediately. */
1246 lttng_trace_chunk_fini(chunk
);
1252 void lttng_trace_chunk_put(struct lttng_trace_chunk
*chunk
)
1257 assert(chunk
->ref
.refcount
);
1258 urcu_ref_put(&chunk
->ref
, lttng_trace_chunk_release
);
1262 struct lttng_trace_chunk_registry
*lttng_trace_chunk_registry_create(void)
1264 struct lttng_trace_chunk_registry
*registry
;
1266 registry
= zmalloc(sizeof(*registry
));
1271 registry
->ht
= cds_lfht_new(DEFAULT_HT_SIZE
, 1, 0,
1272 CDS_LFHT_AUTO_RESIZE
| CDS_LFHT_ACCOUNTING
, NULL
);
1273 if (!registry
->ht
) {
1279 lttng_trace_chunk_registry_destroy(registry
);
1284 void lttng_trace_chunk_registry_destroy(
1285 struct lttng_trace_chunk_registry
*registry
)
1291 int ret
= cds_lfht_destroy(registry
->ht
, NULL
);
1298 struct lttng_trace_chunk_registry_element
*
1299 lttng_trace_chunk_registry_element_create_from_chunk(
1300 struct lttng_trace_chunk
*chunk
, uint64_t session_id
)
1302 struct lttng_trace_chunk_registry_element
*element
=
1303 zmalloc(sizeof(*element
));
1308 cds_lfht_node_init(&element
->trace_chunk_registry_ht_node
);
1309 element
->session_id
= session_id
;
1311 element
->chunk
= *chunk
;
1312 lttng_trace_chunk_init(&element
->chunk
);
1313 if (chunk
->session_output_directory
) {
1314 /* Transferred ownership. */
1315 element
->chunk
.session_output_directory
=
1316 chunk
->session_output_directory
;
1317 chunk
->session_output_directory
= NULL
;
1319 if (chunk
->chunk_directory
) {
1320 /* Transferred ownership. */
1321 element
->chunk
.chunk_directory
= chunk
->chunk_directory
;
1322 chunk
->chunk_directory
= NULL
;
1325 * The original chunk becomes invalid; the name attribute is transferred
1326 * to the new chunk instance.
1329 element
->chunk
.in_registry_element
= true;
1335 struct lttng_trace_chunk
*
1336 lttng_trace_chunk_registry_publish_chunk(
1337 struct lttng_trace_chunk_registry
*registry
,
1338 uint64_t session_id
, struct lttng_trace_chunk
*chunk
)
1340 struct lttng_trace_chunk_registry_element
*element
;
1341 unsigned long element_hash
;
1343 pthread_mutex_lock(&chunk
->lock
);
1344 element
= lttng_trace_chunk_registry_element_create_from_chunk(chunk
,
1346 pthread_mutex_unlock(&chunk
->lock
);
1351 * chunk is now invalid, the only valid operation is a 'put' from the
1355 element_hash
= lttng_trace_chunk_registry_element_hash(element
);
1359 struct cds_lfht_node
*published_node
;
1360 struct lttng_trace_chunk
*published_chunk
;
1361 struct lttng_trace_chunk_registry_element
*published_element
;
1363 published_node
= cds_lfht_add_unique(registry
->ht
,
1365 lttng_trace_chunk_registry_element_match
,
1367 &element
->trace_chunk_registry_ht_node
);
1368 if (published_node
== &element
->trace_chunk_registry_ht_node
) {
1369 /* Successfully published the new element. */
1370 element
->registry
= registry
;
1371 /* Acquire a reference for the caller. */
1372 if (lttng_trace_chunk_get(&element
->chunk
)) {
1376 * Another thread concurrently unpublished the
1377 * trace chunk. This is currently unexpected.
1379 * Re-attempt to publish.
1381 ERR("Attempt to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
1387 * An equivalent trace chunk was published before this trace
1388 * chunk. Attempt to acquire a reference to the one that was
1389 * already published and release the reference to the copy we
1390 * created if successful.
1392 published_element
= container_of(published_node
,
1393 typeof(*published_element
),
1394 trace_chunk_registry_ht_node
);
1395 published_chunk
= &published_element
->chunk
;
1396 if (lttng_trace_chunk_get(published_chunk
)) {
1397 lttng_trace_chunk_put(&element
->chunk
);
1398 element
= published_element
;
1402 * A reference to the previously published trace chunk could not
1403 * be acquired. Hence, retry to publish our copy of the trace
1409 return element
? &element
->chunk
: NULL
;
1413 * Note that the caller must be registered as an RCU thread.
1414 * However, it does not need to hold the RCU read lock. The RCU read lock is
1415 * acquired to perform the look-up in the registry's hash table and held until
1416 * after a reference to the "found" trace chunk is acquired.
1418 * IOW, holding a reference guarantees the existence of the object for the
1422 struct lttng_trace_chunk
*_lttng_trace_chunk_registry_find_chunk(
1423 const struct lttng_trace_chunk_registry
*registry
,
1424 uint64_t session_id
, uint64_t *chunk_id
)
1426 const struct lttng_trace_chunk_registry_element target_element
= {
1427 .chunk
.id
.is_set
= !!chunk_id
,
1428 .chunk
.id
.value
= chunk_id
? *chunk_id
: 0,
1429 .session_id
= session_id
,
1431 const unsigned long element_hash
=
1432 lttng_trace_chunk_registry_element_hash(
1434 struct cds_lfht_node
*published_node
;
1435 struct lttng_trace_chunk_registry_element
*published_element
;
1436 struct lttng_trace_chunk
*published_chunk
= NULL
;
1437 struct cds_lfht_iter iter
;
1440 cds_lfht_lookup(registry
->ht
,
1442 lttng_trace_chunk_registry_element_match
,
1445 published_node
= cds_lfht_iter_get_node(&iter
);
1446 if (!published_node
) {
1450 published_element
= container_of(published_node
,
1451 typeof(*published_element
),
1452 trace_chunk_registry_ht_node
);
1453 if (lttng_trace_chunk_get(&published_element
->chunk
)) {
1454 published_chunk
= &published_element
->chunk
;
1458 return published_chunk
;
1462 struct lttng_trace_chunk
*
1463 lttng_trace_chunk_registry_find_chunk(
1464 const struct lttng_trace_chunk_registry
*registry
,
1465 uint64_t session_id
, uint64_t chunk_id
)
1467 return _lttng_trace_chunk_registry_find_chunk(registry
,
1468 session_id
, &chunk_id
);
1472 int lttng_trace_chunk_registry_chunk_exists(
1473 const struct lttng_trace_chunk_registry
*registry
,
1474 uint64_t session_id
, uint64_t chunk_id
, bool *chunk_exists
)
1477 const struct lttng_trace_chunk_registry_element target_element
= {
1478 .chunk
.id
.is_set
= true,
1479 .chunk
.id
.value
= chunk_id
,
1480 .session_id
= session_id
,
1482 const unsigned long element_hash
=
1483 lttng_trace_chunk_registry_element_hash(
1485 struct cds_lfht_node
*published_node
;
1486 struct cds_lfht_iter iter
;
1489 cds_lfht_lookup(registry
->ht
,
1491 lttng_trace_chunk_registry_element_match
,
1494 published_node
= cds_lfht_iter_get_node(&iter
);
1495 if (!published_node
) {
1496 *chunk_exists
= false;
1500 *chunk_exists
= !cds_lfht_is_node_deleted(published_node
);
1507 struct lttng_trace_chunk
*
1508 lttng_trace_chunk_registry_find_anonymous_chunk(
1509 const struct lttng_trace_chunk_registry
*registry
,
1510 uint64_t session_id
)
1512 return _lttng_trace_chunk_registry_find_chunk(registry
,
1516 unsigned int lttng_trace_chunk_registry_put_each_chunk(
1517 struct lttng_trace_chunk_registry
*registry
)
1519 struct cds_lfht_iter iter
;
1520 struct lttng_trace_chunk_registry_element
*chunk_element
;
1521 unsigned int trace_chunks_left
= 0;
1523 DBG("Releasing trace chunk registry to all trace chunks");
1525 cds_lfht_for_each_entry(registry
->ht
,
1526 &iter
, chunk_element
, trace_chunk_registry_ht_node
) {
1527 const char *chunk_id_str
= "none";
1528 char chunk_id_buf
[MAX_INT_DEC_LEN(uint64_t)];
1530 pthread_mutex_lock(&chunk_element
->chunk
.lock
);
1531 if (chunk_element
->chunk
.id
.is_set
) {
1534 fmt_ret
= snprintf(chunk_id_buf
, sizeof(chunk_id_buf
),
1536 chunk_element
->chunk
.id
.value
);
1537 if (fmt_ret
< 0 || fmt_ret
>= sizeof(chunk_id_buf
)) {
1538 chunk_id_str
= "formatting error";
1540 chunk_id_str
= chunk_id_buf
;
1544 DBG("Releasing reference to trace chunk: session_id = %" PRIu64
1545 "chunk_id = %s, name = \"%s\", status = %s",
1546 chunk_element
->session_id
,
1548 chunk_element
->chunk
.name
? : "none",
1549 chunk_element
->chunk
.close_command
.is_set
?
1551 pthread_mutex_unlock(&chunk_element
->chunk
.lock
);
1552 lttng_trace_chunk_put(&chunk_element
->chunk
);
1553 trace_chunks_left
++;
1556 DBG("Released reference to %u trace chunks in %s()", trace_chunks_left
,
1559 return trace_chunks_left
;