4 * BabelTrace - Convert Text Log to CTF
6 * Copyright 2010-2011 EfficiOS Inc. and Linux Foundation
8 * Author: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
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:
17 * The above copyright notice and this permission notice shall be included in
18 * all copies or substantial portions of the Software.
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
29 #include <sys/types.h>
32 #include <babeltrace/compat/mman-internal.h>
33 #include <babeltrace/compat/dirent-internal.h>
42 #include <glib/gstdio.h>
44 #include <babeltrace/babeltrace-internal.h>
45 #include <babeltrace/ctf/types.h>
46 #include <babeltrace/compat/uuid-internal.h>
47 #include <babeltrace/compat/utc-internal.h>
48 #include <babeltrace/compat/stdio-internal.h>
49 #include <babeltrace/endian-internal.h>
51 #define NSEC_PER_USEC 1000UL
52 #define NSEC_PER_MSEC 1000000UL
53 #define NSEC_PER_SEC 1000000000ULL
54 #define USEC_PER_SEC 1000000UL
56 bool babeltrace_debug
, babeltrace_verbose
;
58 static char *s_outputname
;
59 static int s_timestamp
;
61 static unsigned char s_uuid
[BABELTRACE_UUID_LEN
];
63 /* Metadata format string */
64 static const char metadata_fmt
[] =
66 "typealias integer { size = 8; align = 8; signed = false; } := uint8_t;\n"
67 "typealias integer { size = 32; align = 32; signed = false; } := uint32_t;\n"
68 "typealias integer { size = 64; align = 64; signed = false; } := uint64_t;\n"
71 " major = %u;\n" /* major (e.g. 0) */
72 " minor = %u;\n" /* minor (e.g. 1) */
73 " uuid = \"%s\";\n" /* UUID */
74 " byte_order = %s;\n" /* be or le */
75 " packet.header := struct {\n"
77 " uint8_t uuid[16];\n"
82 " packet.context := struct {\n"
83 " uint64_t content_size;\n"
84 " uint64_t packet_size;\n"
86 "%s" /* Stream event header (opt.) */
91 " fields := struct { string str; };\n"
94 static const char metadata_stream_event_header_timestamp
[] =
95 " typealias integer { size = 64; align = 64; signed = false; } := uint64_t;\n"
96 " event.header := struct {\n"
97 " uint64_t timestamp;\n"
101 void print_metadata(FILE *fp
)
103 char uuid_str
[BABELTRACE_UUID_STR_LEN
];
104 unsigned int major
= 0, minor
= 0;
107 ret
= sscanf(VERSION
, "%u.%u", &major
, &minor
);
109 fprintf(stderr
, "[warning] Incorrect babeltrace version format\n.");
110 bt_uuid_unparse(s_uuid
, uuid_str
);
111 fprintf(fp
, metadata_fmt
,
115 BYTE_ORDER
== LITTLE_ENDIAN
? "le" : "be",
116 s_timestamp
? metadata_stream_event_header_timestamp
: "");
120 void write_packet_header(struct ctf_stream_pos
*pos
, unsigned char *uuid
)
122 struct ctf_stream_pos dummy
;
125 ctf_dummy_pos(pos
, &dummy
);
126 if (!ctf_align_pos(&dummy
, sizeof(uint32_t) * CHAR_BIT
))
128 if (!ctf_move_pos(&dummy
, sizeof(uint32_t) * CHAR_BIT
))
130 assert(!ctf_pos_packet(&dummy
));
132 if (!ctf_align_pos(pos
, sizeof(uint32_t) * CHAR_BIT
))
134 *(uint32_t *) ctf_get_pos_addr(pos
) = 0xC1FC1FC1;
135 if (!ctf_move_pos(pos
, sizeof(uint32_t) * CHAR_BIT
))
139 ctf_dummy_pos(pos
, &dummy
);
140 if (!ctf_align_pos(&dummy
, sizeof(uint8_t) * CHAR_BIT
))
142 if (!ctf_move_pos(&dummy
, 16 * CHAR_BIT
))
144 assert(!ctf_pos_packet(&dummy
));
146 if (!ctf_align_pos(pos
, sizeof(uint8_t) * CHAR_BIT
))
148 memcpy(ctf_get_pos_addr(pos
), uuid
, BABELTRACE_UUID_LEN
);
149 if (!ctf_move_pos(pos
, BABELTRACE_UUID_LEN
* CHAR_BIT
))
154 fprintf(stderr
, "[error] Out of packet bounds when writing packet header\n");
159 void write_packet_context(struct ctf_stream_pos
*pos
)
161 struct ctf_stream_pos dummy
;
164 ctf_dummy_pos(pos
, &dummy
);
165 if (!ctf_align_pos(&dummy
, sizeof(uint64_t) * CHAR_BIT
))
167 if (!ctf_move_pos(&dummy
, sizeof(uint64_t) * CHAR_BIT
))
169 assert(!ctf_pos_packet(&dummy
));
171 if (!ctf_align_pos(pos
, sizeof(uint64_t) * CHAR_BIT
))
173 *(uint64_t *) ctf_get_pos_addr(pos
) = ~0ULL; /* Not known yet */
174 pos
->content_size_loc
= (uint64_t *) ctf_get_pos_addr(pos
);
175 if (!ctf_move_pos(pos
, sizeof(uint64_t) * CHAR_BIT
))
179 ctf_dummy_pos(pos
, &dummy
);
180 if (!ctf_align_pos(&dummy
, sizeof(uint64_t) * CHAR_BIT
))
182 if (!ctf_move_pos(&dummy
, sizeof(uint64_t) * CHAR_BIT
))
184 assert(!ctf_pos_packet(&dummy
));
186 if (!ctf_align_pos(pos
, sizeof(uint64_t) * CHAR_BIT
))
188 *(uint64_t *) ctf_get_pos_addr(pos
) = pos
->packet_size
;
189 if (!ctf_move_pos(pos
, sizeof(uint64_t) * CHAR_BIT
))
194 fprintf(stderr
, "[error] Out of packet bounds when writing packet context\n");
199 void write_event_header(struct ctf_stream_pos
*pos
, char *line
,
200 char **tline
, size_t len
, size_t *tlen
,
206 /* Only need to be executed on first pass (dummy) */
208 int has_timestamp
= 0;
209 unsigned long sec
, usec
, msec
;
210 unsigned int year
, mon
, mday
, hour
, min
;
212 /* Extract time from input line */
213 if (sscanf(line
, "[%lu.%lu] ", &sec
, &usec
) == 2) {
214 *ts
= (uint64_t) sec
* USEC_PER_SEC
+ (uint64_t) usec
;
216 * Default CTF clock has 1GHz frequency. Convert
219 *ts
*= NSEC_PER_USEC
;
221 } else if (sscanf(line
, "[%u-%u-%u %u:%u:%lu.%lu] ",
222 &year
, &mon
, &mday
, &hour
, &min
,
227 memset(&ti
, 0, sizeof(ti
));
228 ti
.tm_year
= year
- 1900; /* from 1900 */
229 ti
.tm_mon
= mon
- 1; /* 0 to 11 */
235 ep_sec
= bt_timegm(&ti
);
236 if (ep_sec
!= (time_t) -1) {
237 *ts
= (uint64_t) ep_sec
* NSEC_PER_SEC
238 + (uint64_t) msec
* NSEC_PER_MSEC
;
243 *tline
= strchr(line
, ']');
246 if ((*tline
)[0] == ' ') {
249 *tlen
= len
+ line
- *tline
;
253 if (!ctf_align_pos(pos
, sizeof(uint64_t) * CHAR_BIT
))
256 *(uint64_t *) ctf_get_pos_addr(pos
) = *ts
;
257 if (!ctf_move_pos(pos
, sizeof(uint64_t) * CHAR_BIT
))
262 fprintf(stderr
, "[error] Out of packet bounds when writing event header\n");
267 void trace_string(char *line
, struct ctf_stream_pos
*pos
, size_t len
)
269 struct ctf_stream_pos dummy
;
271 char *tline
= line
; /* tline is start of text, after timestamp */
275 printf_debug("read: %s\n", line
);
278 int packet_filled
= 0;
280 ctf_dummy_pos(pos
, &dummy
);
281 write_event_header(&dummy
, line
, &tline
, len
, &tlen
, &ts
);
282 if (!ctf_align_pos(&dummy
, sizeof(uint8_t) * CHAR_BIT
))
284 if (!ctf_move_pos(&dummy
, tlen
* CHAR_BIT
))
286 if (packet_filled
|| ctf_pos_packet(&dummy
)) {
287 ctf_pos_pad_packet(pos
);
288 write_packet_header(pos
, s_uuid
);
289 write_packet_context(pos
);
290 if (attempt
++ == 1) {
291 fprintf(stderr
, "[Error] Line too large for packet size (%" PRIu64
"kB) (discarded)\n",
292 pos
->packet_size
/ CHAR_BIT
/ 1024);
301 write_event_header(pos
, line
, &tline
, len
, &tlen
, &ts
);
302 if (!ctf_align_pos(pos
, sizeof(uint8_t) * CHAR_BIT
))
304 memcpy(ctf_get_pos_addr(pos
), tline
, tlen
);
305 if (!ctf_move_pos(pos
, tlen
* CHAR_BIT
))
310 fprintf(stderr
, "[error] Out of packet bounds when writing event payload\n");
315 void trace_text(FILE *input
, int output
)
317 struct ctf_stream_pos pos
;
319 char *line
= NULL
, *nl
;
323 memset(&pos
, 0, sizeof(pos
));
324 ret
= ctf_init_pos(&pos
, NULL
, output
, O_RDWR
);
326 fprintf(stderr
, "Error in ctf_init_pos\n");
329 ctf_packet_seek(&pos
.parent
, 0, SEEK_CUR
);
330 write_packet_header(&pos
, s_uuid
);
331 write_packet_context(&pos
);
333 len
= bt_getline(&line
, &linesize
, input
);
336 nl
= strrchr(line
, '\n');
339 trace_string(line
, &pos
, nl
- line
+ 1);
341 trace_string(line
, &pos
, strlen(line
) + 1);
344 ret
= ctf_fini_pos(&pos
);
346 fprintf(stderr
, "Error in ctf_fini_pos\n");
353 fprintf(fp
, "BabelTrace Log Converter %s\n", VERSION
);
355 fprintf(fp
, "Convert for a text log (read from standard input) to CTF.\n");
357 fprintf(fp
, "usage : babeltrace-log [OPTIONS] OUTPUT\n");
359 fprintf(fp
, " OUTPUT Output trace path\n");
361 fprintf(fp
, " -t With timestamps (format: [sec.usec] string\\n)\n");
362 fprintf(fp
, " (format: [YYYY-MM-DD HH:MM:SS.MS] string\\n)\n");
367 int parse_args(int argc
, char **argv
)
371 for (i
= 1; i
< argc
; i
++) {
372 if (!strcmp(argv
[i
], "-t"))
374 else if (!strcmp(argv
[i
], "-h")) {
377 } else if (argv
[i
][0] == '-')
380 s_outputname
= argv
[i
];
387 int main(int argc
, char **argv
)
389 int fd
, metadata_fd
, ret
;
394 ret
= parse_args(argc
, argv
);
396 fprintf(stderr
, "Error: invalid argument.\n");
406 ret
= g_mkdir(s_outputname
, S_IRWXU
|S_IRWXG
);
412 dir
= opendir(s_outputname
);
417 dir_fd
= bt_dirfd(dir
);
423 fd
= openat(dir_fd
, "datastream", O_RDWR
|O_CREAT
,
424 S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
);
427 goto error_closedirfd
;
430 metadata_fd
= openat(dir_fd
, "metadata", O_RDWR
|O_CREAT
,
431 S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
);
432 if (metadata_fd
< 0) {
434 goto error_closedatastream
;
436 metadata_fp
= fdopen(metadata_fd
, "w");
439 goto error_closemetadatafd
;
442 bt_uuid_generate(s_uuid
);
443 print_metadata(metadata_fp
);
444 trace_text(stdin
, fd
);
452 error_closemetadatafd
:
453 ret
= close(metadata_fd
);
456 error_closedatastream
:
469 ret
= rmdir(s_outputname
);