2 * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 #define BT_LOG_TAG "PLUGIN-CTF-FS-SINK-TRACE"
26 #include <babeltrace/babeltrace.h>
30 #include <babeltrace/assert-internal.h>
31 #include <babeltrace/ctfser-internal.h>
33 #include "translate-trace-ir-to-ctf-ir.h"
34 #include "translate-ctf-ir-to-tsdl.h"
36 #include "fs-sink-trace.h"
37 #include "fs-sink-stream.h"
40 * Sanitizes `path` so as to:
42 * * Replace `.` subdirectories with `_`.
43 * * Replace `..` subdirectories with `__`.
44 * * Remove trailing slashes.
47 GString
*sanitize_trace_path(const char *path
)
49 GString
*san_path
= g_string_new(NULL
);
50 const char *ch
= path
;
51 bool dir_start
= true;
59 /* Start of directory */
61 g_string_append_c(san_path
, *ch
);
70 g_string_append_c(san_path
, '_');
78 g_string_append(san_path
, "__");
92 /* Not a special character */
93 g_string_append_c(san_path
, *ch
);
98 /* Remove trailing slashes */
99 while (san_path
->len
> 0 &&
100 san_path
->str
[san_path
->len
- 1] == '/') {
101 /* Remove trailing slash */
102 g_string_set_size(san_path
, san_path
->len
- 1);
105 if (san_path
->len
== 0) {
106 /* Looks like there's nothing left: just use `trace` */
107 g_string_assign(san_path
, "trace");
114 GString
*make_unique_trace_path(struct fs_sink_comp
*fs_sink
,
115 const char *output_dir_path
, const char *base
)
117 GString
*path
= g_string_new(output_dir_path
);
118 GString
*san_base
= NULL
;
119 unsigned int suffix
= 0;
121 if (fs_sink
->assume_single_trace
) {
122 /* Use output directory directly */
126 san_base
= sanitize_trace_path(base
);
127 g_string_append_printf(path
, "/%s", san_base
->str
);
129 while (g_file_test(path
->str
, G_FILE_TEST_IS_DIR
)) {
130 g_string_assign(path
, output_dir_path
);
131 g_string_append_printf(path
, "/%s%u", san_base
->str
, suffix
);
137 g_string_free(san_base
, TRUE
);
144 void fs_sink_trace_destroy(struct fs_sink_trace
*trace
)
146 GString
*tsdl
= NULL
;
154 if (trace
->ir_trace_destruction_listener_id
!= UINT64_C(-1)) {
156 * Remove the destruction listener, otherwise it could
157 * be called in the future, and its private data is this
158 * CTF FS sink trace object which won't exist anymore.
160 (void) bt_trace_remove_destruction_listener(trace
->ir_trace
,
161 trace
->ir_trace_destruction_listener_id
);
162 trace
->ir_trace_destruction_listener_id
= UINT64_C(-1);
165 if (trace
->streams
) {
166 g_hash_table_destroy(trace
->streams
);
167 trace
->streams
= NULL
;
170 tsdl
= g_string_new(NULL
);
172 translate_trace_class_ctf_ir_to_tsdl(trace
->tc
, tsdl
);
173 fh
= fopen(trace
->metadata_path
->str
, "wb");
175 BT_LOGF_ERRNO("In trace destruction listener: "
176 "cannot open metadata file for writing: ",
177 ": path=\"%s\"", trace
->metadata_path
->str
);
181 len
= fwrite(tsdl
->str
, sizeof(*tsdl
->str
), tsdl
->len
, fh
);
182 if (len
!= tsdl
->len
) {
183 BT_LOGF_ERRNO("In trace destruction listener: "
184 "cannot write metadata file: ",
185 ": path=\"%s\"", trace
->metadata_path
->str
);
189 if (!trace
->fs_sink
->quiet
) {
190 printf("Created CTF trace `%s`.\n", trace
->path
->str
);
194 g_string_free(trace
->path
, TRUE
);
198 if (trace
->metadata_path
) {
199 g_string_free(trace
->metadata_path
, TRUE
);
200 trace
->metadata_path
= NULL
;
203 fs_sink_ctf_trace_class_destroy(trace
->tc
);
209 int ret
= fclose(fh
);
212 BT_LOGW_ERRNO("In trace destruction listener: "
213 "cannot close metadata file: ",
214 ": path=\"%s\"", trace
->metadata_path
->str
);
219 g_string_free(tsdl
, TRUE
);
226 void ir_trace_destruction_listener(const bt_trace
*ir_trace
, void *data
)
228 struct fs_sink_trace
*trace
= data
;
231 * Prevent bt_trace_remove_destruction_listener() from being
232 * called in fs_sink_trace_destroy(), which is called by
233 * g_hash_table_remove() below.
235 trace
->ir_trace_destruction_listener_id
= UINT64_C(-1);
236 g_hash_table_remove(trace
->fs_sink
->traces
, ir_trace
);
240 struct fs_sink_trace
*fs_sink_trace_create(struct fs_sink_comp
*fs_sink
,
241 const bt_trace
*ir_trace
)
244 struct fs_sink_trace
*trace
= g_new0(struct fs_sink_trace
, 1);
245 const char *trace_name
= bt_trace_get_name(ir_trace
);
246 bt_trace_status trace_status
;
253 trace_name
= "trace";
256 trace
->fs_sink
= fs_sink
;
257 trace
->ir_trace
= ir_trace
;
258 trace
->ir_trace_destruction_listener_id
= UINT64_C(-1);
259 trace
->tc
= translate_trace_class_trace_ir_to_ctf_ir(
260 bt_trace_borrow_class_const(ir_trace
));
265 trace
->path
= make_unique_trace_path(fs_sink
,
266 fs_sink
->output_dir_path
->str
, trace_name
);
267 BT_ASSERT(trace
->path
);
268 ret
= g_mkdir_with_parents(trace
->path
->str
, 0755);
270 BT_LOGE_ERRNO("Cannot create directories for trace directory",
271 ": path=\"%s\"", trace
->path
->str
);
275 trace
->metadata_path
= g_string_new(trace
->path
->str
);
276 BT_ASSERT(trace
->metadata_path
);
277 g_string_append(trace
->metadata_path
, "/metadata");
278 trace
->streams
= g_hash_table_new_full(g_direct_hash
, g_direct_equal
,
279 NULL
, (GDestroyNotify
) fs_sink_stream_destroy
);
280 BT_ASSERT(trace
->streams
);
281 trace_status
= bt_trace_add_destruction_listener(ir_trace
,
282 ir_trace_destruction_listener
, trace
,
283 &trace
->ir_trace_destruction_listener_id
);
288 g_hash_table_insert(fs_sink
->traces
, (gpointer
) ir_trace
, trace
);
292 fs_sink_trace_destroy(trace
);