Commit | Line | Data |
---|---|---|
273b65be JG |
1 | /* |
2 | * writer.c | |
3 | * | |
4 | * Babeltrace CTF Writer | |
5 | * | |
de9dd397 | 6 | * Copyright 2013, 2014 Jérémie Galarneau <jeremie.galarneau@efficios.com> |
273b65be JG |
7 | * |
8 | * Author: Jérémie Galarneau <jeremie.galarneau@efficios.com> | |
9 | * | |
10 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
11 | * of this software and associated documentation files (the "Software"), to deal | |
12 | * in the Software without restriction, including without limitation the rights | |
13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
14 | * copies of the Software, and to permit persons to whom the Software is | |
15 | * furnished to do so, subject to the following conditions: | |
16 | * | |
17 | * The above copyright notice and this permission notice shall be included in | |
18 | * all copies or substantial portions of the Software. | |
19 | * | |
20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
26 | * SOFTWARE. | |
27 | */ | |
28 | ||
7dd841e4 | 29 | #define BT_LOG_TAG "CTF-WRITER" |
67d2ce02 | 30 | #include "logging.h" |
20eee76e | 31 | |
16ca5ff0 PP |
32 | #include <errno.h> |
33 | #include <fcntl.h> | |
34 | #include <inttypes.h> | |
273b65be JG |
35 | #include <stdio.h> |
36 | #include <stdlib.h> | |
37 | #include <sys/stat.h> | |
273b65be | 38 | #include <unistd.h> |
273b65be | 39 | |
578e048b MJ |
40 | #include <babeltrace2/ctf-writer/object.h> |
41 | ||
42 | #include "common/assert.h" | |
43 | #include "compat/compiler.h" | |
44 | #include "compat/endian.h" | |
45 | #include "compat/uuid.h" | |
46 | ||
47 | #include "clock.h" | |
48 | #include "fields.h" | |
49 | #include "field-types.h" | |
50 | #include "functor.h" | |
51 | #include "stream-class.h" | |
52 | #include "stream.h" | |
53 | #include "trace.h" | |
54 | #include "writer.h" | |
55 | ||
273b65be | 56 | static |
e1e02a22 | 57 | void bt_ctf_writer_destroy(struct bt_ctf_object *obj); |
83509119 | 58 | |
488e09a7 | 59 | static |
3dca2276 | 60 | int init_trace_packet_header(struct bt_ctf_trace *trace) |
488e09a7 PP |
61 | { |
62 | int ret = 0; | |
3dca2276 | 63 | struct bt_ctf_field_type *_uint32_t = |
488e09a7 | 64 | get_field_type(FIELD_TYPE_ALIAS_UINT32_T); |
3dca2276 | 65 | struct bt_ctf_field_type *_uint8_t = |
488e09a7 | 66 | get_field_type(FIELD_TYPE_ALIAS_UINT8_T); |
3dca2276 PP |
67 | struct bt_ctf_field_type *trace_packet_header_type = |
68 | bt_ctf_field_type_structure_create(); | |
69 | struct bt_ctf_field_type *uuid_array_type = | |
70 | bt_ctf_field_type_array_create(_uint8_t, 16); | |
488e09a7 PP |
71 | |
72 | if (!trace_packet_header_type || !uuid_array_type) { | |
73 | ret = -1; | |
74 | goto end; | |
75 | } | |
76 | ||
3dca2276 | 77 | ret = bt_ctf_field_type_structure_add_field(trace_packet_header_type, |
488e09a7 PP |
78 | _uint32_t, "magic"); |
79 | if (ret) { | |
80 | goto end; | |
81 | } | |
82 | ||
3dca2276 | 83 | ret = bt_ctf_field_type_structure_add_field(trace_packet_header_type, |
488e09a7 PP |
84 | uuid_array_type, "uuid"); |
85 | if (ret) { | |
86 | goto end; | |
87 | } | |
88 | ||
3dca2276 | 89 | ret = bt_ctf_field_type_structure_add_field(trace_packet_header_type, |
488e09a7 PP |
90 | _uint32_t, "stream_id"); |
91 | if (ret) { | |
92 | goto end; | |
93 | } | |
94 | ||
3dca2276 | 95 | ret = bt_ctf_trace_set_packet_header_field_type(trace, |
488e09a7 PP |
96 | trace_packet_header_type); |
97 | if (ret) { | |
98 | goto end; | |
99 | } | |
100 | end: | |
e1e02a22 PP |
101 | bt_ctf_object_put_ref(uuid_array_type); |
102 | bt_ctf_object_put_ref(_uint32_t); | |
103 | bt_ctf_object_put_ref(_uint8_t); | |
104 | bt_ctf_object_put_ref(trace_packet_header_type); | |
488e09a7 PP |
105 | return ret; |
106 | } | |
107 | ||
273b65be JG |
108 | struct bt_ctf_writer *bt_ctf_writer_create(const char *path) |
109 | { | |
3f5808e5 | 110 | int ret; |
273b65be | 111 | struct bt_ctf_writer *writer = NULL; |
4a32fda0 | 112 | unsigned char uuid[16]; |
ebd04048 | 113 | char *metadata_path = NULL; |
273b65be JG |
114 | |
115 | if (!path) { | |
116 | goto error; | |
117 | } | |
118 | ||
119 | writer = g_new0(struct bt_ctf_writer, 1); | |
120 | if (!writer) { | |
121 | goto error; | |
122 | } | |
123 | ||
ebd04048 MJ |
124 | metadata_path = g_build_filename(path, "metadata", NULL); |
125 | ||
e1e02a22 | 126 | bt_ctf_object_init_shared(&writer->base, bt_ctf_writer_destroy); |
273b65be JG |
127 | writer->path = g_string_new(path); |
128 | if (!writer->path) { | |
129 | goto error_destroy; | |
130 | } | |
131 | ||
3dca2276 | 132 | writer->trace = bt_ctf_trace_create(); |
bc37ae52 JG |
133 | if (!writer->trace) { |
134 | goto error_destroy; | |
135 | } | |
136 | ||
488e09a7 PP |
137 | ret = init_trace_packet_header(writer->trace); |
138 | if (ret) { | |
139 | goto error_destroy; | |
140 | } | |
141 | ||
4a32fda0 | 142 | /* Generate a UUID for this writer's trace */ |
20eee76e MJ |
143 | ret = bt_uuid_generate(uuid); |
144 | if (ret) { | |
145 | BT_LOGE_STR("Cannot generate UUID for CTF writer's trace."); | |
146 | goto error_destroy; | |
147 | } | |
148 | ||
3dca2276 | 149 | ret = bt_ctf_trace_set_uuid(writer->trace, uuid); |
4a32fda0 PP |
150 | if (ret) { |
151 | goto error_destroy; | |
152 | } | |
153 | ||
e1e02a22 PP |
154 | bt_ctf_object_set_parent(&writer->trace->common.base, &writer->base); |
155 | bt_ctf_object_put_ref(writer->trace); | |
3f5808e5 PP |
156 | |
157 | /* Default to little-endian */ | |
3dca2276 | 158 | ret = bt_ctf_writer_set_byte_order(writer, BT_CTF_BYTE_ORDER_NATIVE); |
f6ccaed9 | 159 | BT_ASSERT(ret == 0); |
3f5808e5 | 160 | |
273b65be JG |
161 | /* Create trace directory if necessary and open a metadata file */ |
162 | if (g_mkdir_with_parents(path, S_IRWXU | S_IRWXG)) { | |
163 | perror("g_mkdir_with_parents"); | |
164 | goto error_destroy; | |
165 | } | |
166 | ||
ebd04048 MJ |
167 | writer->metadata_fd = open(metadata_path, |
168 | O_WRONLY | O_CREAT | O_TRUNC, | |
169 | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); | |
170 | if (writer->metadata_fd < 0) { | |
273b65be JG |
171 | perror("open"); |
172 | goto error_destroy; | |
173 | } | |
174 | ||
ebd04048 | 175 | g_free(metadata_path); |
273b65be | 176 | return writer; |
bc37ae52 | 177 | |
273b65be | 178 | error_destroy: |
e1e02a22 | 179 | BT_CTF_OBJECT_PUT_REF_AND_RESET(writer); |
273b65be | 180 | error: |
ebd04048 | 181 | g_free(metadata_path); |
273b65be JG |
182 | return writer; |
183 | } | |
184 | ||
e1e02a22 | 185 | void bt_ctf_writer_destroy(struct bt_ctf_object *obj) |
273b65be JG |
186 | { |
187 | struct bt_ctf_writer *writer; | |
273b65be | 188 | |
83509119 | 189 | writer = container_of(obj, struct bt_ctf_writer, base); |
273b65be JG |
190 | bt_ctf_writer_flush_metadata(writer); |
191 | if (writer->path) { | |
192 | g_string_free(writer->path, TRUE); | |
193 | } | |
194 | ||
273b65be | 195 | if (writer->metadata_fd > 0) { |
9bb7e58b MD |
196 | if (close(writer->metadata_fd)) { |
197 | perror("close"); | |
9bb7e58b | 198 | } |
273b65be JG |
199 | } |
200 | ||
e1e02a22 | 201 | bt_ctf_object_try_spec_release(&writer->trace->common.base); |
273b65be JG |
202 | g_free(writer); |
203 | } | |
204 | ||
3dca2276 | 205 | struct bt_ctf_trace *bt_ctf_writer_get_trace(struct bt_ctf_writer *writer) |
a2540e85 | 206 | { |
3dca2276 | 207 | struct bt_ctf_trace *trace = NULL; |
a2540e85 JG |
208 | |
209 | if (!writer) { | |
210 | goto end; | |
211 | } | |
212 | ||
213 | trace = writer->trace; | |
e1e02a22 | 214 | bt_ctf_object_get_ref(trace); |
a2540e85 JG |
215 | end: |
216 | return trace; | |
217 | } | |
218 | ||
3dca2276 PP |
219 | struct bt_ctf_stream *bt_ctf_writer_create_stream(struct bt_ctf_writer *writer, |
220 | struct bt_ctf_stream_class *stream_class) | |
273b65be | 221 | { |
3dca2276 | 222 | struct bt_ctf_stream *stream = NULL; |
319fd969 | 223 | int stream_class_count; |
c55a9f58 | 224 | bt_bool stream_class_found = BT_FALSE; |
319fd969 | 225 | int i; |
273b65be JG |
226 | |
227 | if (!writer || !stream_class) { | |
228 | goto error; | |
229 | } | |
230 | ||
319fd969 | 231 | /* Make sure the stream class is part of the writer's trace */ |
3dca2276 | 232 | stream_class_count = bt_ctf_trace_get_stream_class_count(writer->trace); |
319fd969 | 233 | if (stream_class_count < 0) { |
273b65be JG |
234 | goto error; |
235 | } | |
236 | ||
319fd969 | 237 | for (i = 0; i < stream_class_count; i++) { |
3dca2276 PP |
238 | struct bt_ctf_stream_class *existing_stream_class = |
239 | bt_ctf_trace_get_stream_class_by_index( | |
9ac68eb1 | 240 | writer->trace, i); |
319fd969 PP |
241 | |
242 | if (existing_stream_class == stream_class) { | |
c55a9f58 | 243 | stream_class_found = BT_TRUE; |
319fd969 PP |
244 | } |
245 | ||
e1e02a22 | 246 | BT_CTF_OBJECT_PUT_REF_AND_RESET(existing_stream_class); |
319fd969 PP |
247 | |
248 | if (stream_class_found) { | |
249 | break; | |
250 | } | |
251 | } | |
252 | ||
253 | if (!stream_class_found) { | |
3dca2276 | 254 | int ret = bt_ctf_trace_add_stream_class(writer->trace, |
319fd969 PP |
255 | stream_class); |
256 | ||
257 | if (ret) { | |
258 | goto error; | |
259 | } | |
260 | } | |
261 | ||
3dca2276 | 262 | stream = bt_ctf_stream_create_with_id(stream_class, NULL, -1ULL); |
319fd969 | 263 | if (!stream) { |
273b65be JG |
264 | goto error; |
265 | } | |
266 | ||
273b65be | 267 | return stream; |
bc37ae52 | 268 | |
273b65be | 269 | error: |
e1e02a22 | 270 | BT_CTF_OBJECT_PUT_REF_AND_RESET(stream); |
83509119 | 271 | return stream; |
273b65be JG |
272 | } |
273 | ||
274 | int bt_ctf_writer_add_environment_field(struct bt_ctf_writer *writer, | |
275 | const char *name, | |
276 | const char *value) | |
277 | { | |
bc37ae52 | 278 | int ret = -1; |
273b65be | 279 | |
bc37ae52 JG |
280 | if (!writer || !name || !value) { |
281 | goto end; | |
273b65be JG |
282 | } |
283 | ||
3dca2276 | 284 | ret = bt_ctf_trace_set_environment_field_string(writer->trace, |
bc37ae52 JG |
285 | name, value); |
286 | end: | |
273b65be JG |
287 | return ret; |
288 | } | |
289 | ||
d7503815 | 290 | int bt_ctf_writer_add_environment_field_int64(struct bt_ctf_writer *writer, |
9ac68eb1 | 291 | const char *name, int64_t value) |
d7503815 SM |
292 | { |
293 | int ret = -1; | |
294 | ||
295 | if (!writer || !name) { | |
296 | goto end; | |
297 | } | |
298 | ||
3dca2276 | 299 | ret = bt_ctf_trace_set_environment_field_integer(writer->trace, name, |
d7503815 SM |
300 | value); |
301 | end: | |
302 | return ret; | |
303 | } | |
304 | ||
273b65be JG |
305 | int bt_ctf_writer_add_clock(struct bt_ctf_writer *writer, |
306 | struct bt_ctf_clock *clock) | |
307 | { | |
bc37ae52 | 308 | int ret = -1; |
273b65be JG |
309 | |
310 | if (!writer || !clock) { | |
12af6048 JG |
311 | goto end; |
312 | } | |
273b65be | 313 | |
3dca2276 | 314 | ret = bt_ctf_trace_add_clock_class(writer->trace, clock->clock_class); |
12af6048 JG |
315 | end: |
316 | return ret; | |
273b65be JG |
317 | } |
318 | ||
273b65be JG |
319 | char *bt_ctf_writer_get_metadata_string(struct bt_ctf_writer *writer) |
320 | { | |
bc37ae52 | 321 | char *metadata_string = NULL; |
273b65be JG |
322 | |
323 | if (!writer) { | |
324 | goto end; | |
325 | } | |
326 | ||
3dca2276 | 327 | metadata_string = bt_ctf_trace_get_metadata_string( |
bc37ae52 | 328 | writer->trace); |
273b65be | 329 | end: |
bc37ae52 | 330 | return metadata_string; |
273b65be JG |
331 | } |
332 | ||
333 | void bt_ctf_writer_flush_metadata(struct bt_ctf_writer *writer) | |
334 | { | |
335 | int ret; | |
336 | char *metadata_string = NULL; | |
337 | ||
338 | if (!writer) { | |
339 | goto end; | |
340 | } | |
341 | ||
3dca2276 | 342 | metadata_string = bt_ctf_trace_get_metadata_string( |
bc37ae52 | 343 | writer->trace); |
273b65be JG |
344 | if (!metadata_string) { |
345 | goto end; | |
346 | } | |
347 | ||
348 | if (lseek(writer->metadata_fd, 0, SEEK_SET) == (off_t)-1) { | |
349 | perror("lseek"); | |
350 | goto end; | |
351 | } | |
352 | ||
353 | if (ftruncate(writer->metadata_fd, 0)) { | |
354 | perror("ftruncate"); | |
355 | goto end; | |
356 | } | |
357 | ||
358 | ret = write(writer->metadata_fd, metadata_string, | |
359 | strlen(metadata_string)); | |
360 | if (ret < 0) { | |
361 | perror("write"); | |
362 | goto end; | |
363 | } | |
364 | end: | |
365 | g_free(metadata_string); | |
366 | } | |
367 | ||
368 | int bt_ctf_writer_set_byte_order(struct bt_ctf_writer *writer, | |
3dca2276 | 369 | enum bt_ctf_byte_order byte_order) |
273b65be JG |
370 | { |
371 | int ret = 0; | |
273b65be JG |
372 | |
373 | if (!writer || writer->frozen) { | |
374 | ret = -1; | |
375 | goto end; | |
376 | } | |
377 | ||
3dca2276 | 378 | if (byte_order == BT_CTF_BYTE_ORDER_NATIVE) { |
013f35c6 PP |
379 | if (BYTE_ORDER == LITTLE_ENDIAN) { |
380 | byte_order = BT_CTF_BYTE_ORDER_LITTLE_ENDIAN; | |
381 | } else { | |
382 | byte_order = BT_CTF_BYTE_ORDER_BIG_ENDIAN; | |
383 | } | |
3f5808e5 PP |
384 | } |
385 | ||
3dca2276 | 386 | ret = bt_ctf_trace_set_native_byte_order(writer->trace, |
bc37ae52 | 387 | byte_order); |
273b65be JG |
388 | end: |
389 | return ret; | |
390 | } | |
391 | ||
3dca2276 PP |
392 | BT_HIDDEN |
393 | void bt_ctf_writer_freeze(struct bt_ctf_writer *writer) | |
273b65be | 394 | { |
3dca2276 | 395 | writer->frozen = 1; |
273b65be JG |
396 | } |
397 | ||
3dca2276 PP |
398 | static |
399 | const unsigned int field_type_aliases_alignments[] = { | |
400 | [FIELD_TYPE_ALIAS_UINT5_T] = 1, | |
401 | [FIELD_TYPE_ALIAS_UINT8_T ... FIELD_TYPE_ALIAS_UINT16_T] = 8, | |
402 | [FIELD_TYPE_ALIAS_UINT27_T] = 1, | |
403 | [FIELD_TYPE_ALIAS_UINT32_T ... FIELD_TYPE_ALIAS_UINT64_T] = 8, | |
404 | }; | |
405 | ||
406 | static | |
407 | const unsigned int field_type_aliases_sizes[] = { | |
408 | [FIELD_TYPE_ALIAS_UINT5_T] = 5, | |
409 | [FIELD_TYPE_ALIAS_UINT8_T] = 8, | |
410 | [FIELD_TYPE_ALIAS_UINT16_T] = 16, | |
411 | [FIELD_TYPE_ALIAS_UINT27_T] = 27, | |
412 | [FIELD_TYPE_ALIAS_UINT32_T] = 32, | |
413 | [FIELD_TYPE_ALIAS_UINT64_T] = 64, | |
414 | }; | |
415 | ||
416 | BT_HIDDEN | |
417 | struct bt_ctf_field_type *get_field_type(enum field_type_alias alias) | |
273b65be | 418 | { |
3dca2276 PP |
419 | int ret; |
420 | unsigned int alignment, size; | |
421 | struct bt_ctf_field_type *field_type = NULL; | |
422 | ||
423 | if (alias >= NR_FIELD_TYPE_ALIAS) { | |
424 | goto end; | |
425 | } | |
426 | ||
427 | alignment = field_type_aliases_alignments[alias]; | |
428 | size = field_type_aliases_sizes[alias]; | |
429 | field_type = bt_ctf_field_type_integer_create(size); | |
430 | ret = bt_ctf_field_type_set_alignment(field_type, alignment); | |
431 | if (ret) { | |
e1e02a22 | 432 | BT_CTF_OBJECT_PUT_REF_AND_RESET(field_type); |
3dca2276 PP |
433 | } |
434 | end: | |
435 | return field_type; | |
273b65be JG |
436 | } |
437 | ||
319fd969 | 438 | BT_HIDDEN |
16ca5ff0 | 439 | const char *bt_ctf_get_byte_order_string(enum bt_ctf_byte_order byte_order) |
273b65be | 440 | { |
3dca2276 PP |
441 | const char *string; |
442 | ||
443 | switch (byte_order) { | |
16ca5ff0 | 444 | case BT_CTF_BYTE_ORDER_LITTLE_ENDIAN: |
3dca2276 PP |
445 | string = "le"; |
446 | break; | |
16ca5ff0 | 447 | case BT_CTF_BYTE_ORDER_BIG_ENDIAN: |
3dca2276 PP |
448 | string = "be"; |
449 | break; | |
16ca5ff0 | 450 | case BT_CTF_BYTE_ORDER_NATIVE: |
3dca2276 PP |
451 | string = "native"; |
452 | break; | |
453 | default: | |
454 | abort(); | |
455 | } | |
456 | ||
457 | return string; | |
273b65be | 458 | } |