Adapt `sink.ctf.fs` to current API
[babeltrace.git] / plugins / ctf / fs-sink / fs-sink-trace.c
CommitLineData
15fe47e0
PP
1/*
2 * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
3 *
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:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
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
20 * SOFTWARE.
21 */
22
23#define BT_LOG_TAG "PLUGIN-CTF-FS-SINK-TRACE"
24#include "logging.h"
25
26#include <babeltrace/babeltrace.h>
27#include <stdio.h>
28#include <stdbool.h>
29#include <glib.h>
30#include <babeltrace/assert-internal.h>
31#include <babeltrace/ctfser-internal.h>
32
33#include "translate-trace-ir-to-ctf-ir.h"
34#include "translate-ctf-ir-to-tsdl.h"
35#include "fs-sink.h"
36#include "fs-sink-trace.h"
37#include "fs-sink-stream.h"
38
39/*
40 * Sanitizes `path` so as to:
41 *
42 * * Replace `.` subdirectories with `_`.
43 * * Replace `..` subdirectories with `__`.
44 * * Remove trailing slashes.
45 */
46static
47GString *sanitize_trace_path(const char *path)
48{
49 GString *san_path = g_string_new(NULL);
50 const char *ch = path;
51 bool dir_start = true;
52
53 BT_ASSERT(san_path);
54 BT_ASSERT(path);
55
56 while (*ch != '\0') {
57 switch (*ch) {
58 case '/':
59 /* Start of directory */
60 dir_start = true;
61 g_string_append_c(san_path, *ch);
62 ch++;
63 continue;
64 case '.':
65 if (dir_start) {
66 switch (ch[1]) {
67 case '\0':
68 case '/':
69 /* `.` -> `_` */
70 g_string_append_c(san_path, '_');
71 ch++;
72 continue;
73 case '.':
74 switch (ch[2]) {
75 case '\0':
76 case '/':
77 /* `..` -> `__` */
78 g_string_append(san_path, "__");
79 ch += 2;
80 continue;
81 default:
82 break;
83 }
84 default:
85 break;
86 }
87 }
88 default:
89 break;
90 }
91
92 /* Not a special character */
93 g_string_append_c(san_path, *ch);
94 ch++;
95 dir_start = false;
96 }
97
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);
103 }
104
105 if (san_path->len == 0) {
106 /* Looks like there's nothing left: just use `trace` */
107 g_string_assign(san_path, "trace");
108 }
109
110 return san_path;
111}
112
113static
114GString *make_unique_trace_path(struct fs_sink_comp *fs_sink,
115 const char *output_dir_path, const char *base)
116{
117 GString *path = g_string_new(output_dir_path);
118 GString *san_base = NULL;
119 unsigned int suffix = 0;
120
121 if (fs_sink->assume_single_trace) {
122 /* Use output directory directly */
123 goto end;
124 }
125
126 san_base = sanitize_trace_path(base);
127 g_string_append_printf(path, "/%s", san_base->str);
128
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);
132 suffix++;
133 }
134
135end:
136 if (san_base) {
137 g_string_free(san_base, TRUE);
138 }
139
140 return path;
141}
142
143BT_HIDDEN
144void fs_sink_trace_destroy(struct fs_sink_trace *trace)
145{
146 GString *tsdl = NULL;
147 FILE *fh = NULL;
148 size_t len;
149
150 if (!trace) {
151 goto end;
152 }
153
154 if (trace->ir_trace_destruction_listener_id != UINT64_C(-1)) {
155 /*
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.
159 */
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);
163 }
164
165 if (trace->streams) {
166 g_hash_table_destroy(trace->streams);
167 trace->streams = NULL;
168 }
169
170 tsdl = g_string_new(NULL);
171 BT_ASSERT(tsdl);
172 translate_trace_class_ctf_ir_to_tsdl(trace->tc, tsdl);
173 fh = fopen(trace->metadata_path->str, "wb");
174 if (!fh) {
175 BT_LOGF_ERRNO("In trace destruction listener: "
176 "cannot open metadata file for writing: ",
177 ": path=\"%s\"", trace->metadata_path->str);
178 abort();
179 }
180
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);
186 abort();
187 }
188
189 if (!trace->fs_sink->quiet) {
190 printf("Created CTF trace `%s`.\n", trace->path->str);
191 }
192
193 if (trace->path) {
194 g_string_free(trace->path, TRUE);
195 trace->path = NULL;
196 }
197
198 if (trace->metadata_path) {
199 g_string_free(trace->metadata_path, TRUE);
200 trace->metadata_path = NULL;
201 }
202
203 fs_sink_ctf_trace_class_destroy(trace->tc);
204 trace->tc = NULL;
205 g_free(trace);
206
207end:
208 if (fh) {
209 int ret = fclose(fh);
210
211 if (ret != 0) {
212 BT_LOGW_ERRNO("In trace destruction listener: "
213 "cannot close metadata file: ",
214 ": path=\"%s\"", trace->metadata_path->str);
215 }
216 }
217
218 if (tsdl) {
219 g_string_free(tsdl, TRUE);
220 }
221
222 return;
223}
224
225static
226void ir_trace_destruction_listener(const bt_trace *ir_trace, void *data)
227{
228 struct fs_sink_trace *trace = data;
229
230 /*
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.
234 */
235 trace->ir_trace_destruction_listener_id = UINT64_C(-1);
236 g_hash_table_remove(trace->fs_sink->traces, ir_trace);
237}
238
239BT_HIDDEN
240struct fs_sink_trace *fs_sink_trace_create(struct fs_sink_comp *fs_sink,
241 const bt_trace *ir_trace)
242{
243 int ret;
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;
247
248 if (!trace) {
249 goto end;
250 }
251
252 if (!trace_name) {
253 trace_name = "trace";
254 }
255
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));
261 if (!trace->tc) {
262 goto error;
263 }
264
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);
269 if (ret) {
270 BT_LOGE_ERRNO("Cannot create directories for trace directory",
271 ": path=\"%s\"", trace->path->str);
272 goto error;
273 }
274
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);
284 if (trace_status) {
285 goto error;
286 }
287
288 g_hash_table_insert(fs_sink->traces, (gpointer) ir_trace, trace);
289 goto end;
290
291error:
292 fs_sink_trace_destroy(trace);
293 trace = NULL;
294
295end:
296 return trace;
297}
This page took 0.033264 seconds and 4 git commands to generate.