Commit | Line | Data |
---|---|---|
e98a2d6e | 1 | /* |
0235b0db | 2 | * SPDX-License-Identifier: MIT |
e98a2d6e | 3 | * |
0235b0db MJ |
4 | * Copyright 2016-2017 Philippe Proulx <pproulx@efficios.com> |
5 | * Copyright 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com> | |
6 | * Copyright 2010-2011 EfficiOS Inc. and Linux Foundation | |
e98a2d6e PP |
7 | */ |
8 | ||
27a14e13 SM |
9 | #define BT_CLOG_CFG (logCfg) |
10 | #define BT_LOG_TAG "PLUGIN/SRC.CTF.FS/DS" | |
98903a3e | 11 | |
0fbb9a9f | 12 | #include <stdlib.h> |
e98a2d6e PP |
13 | #include <stdio.h> |
14 | #include <stdint.h> | |
15 | #include <stdlib.h> | |
e98a2d6e PP |
16 | #include <glib.h> |
17 | #include <inttypes.h> | |
578e048b MJ |
18 | #include "compat/mman.h" |
19 | #include "compat/endian.h" | |
3fadfbc0 | 20 | #include <babeltrace2/babeltrace.h> |
578e048b | 21 | #include "common/common.h" |
087cd0f5 SM |
22 | #include "file.hpp" |
23 | #include "metadata.hpp" | |
364f5320 | 24 | #include "../common/src/msg-iter/msg-iter.hpp" |
578e048b | 25 | #include "common/assert.h" |
087cd0f5 | 26 | #include "data-stream-file.hpp" |
e9383dfd | 27 | #include <string.h> |
27a14e13 SM |
28 | #include "cpp-common/cfg-logging.hpp" |
29 | #include "cpp-common/cfg-logging-error-reporting.hpp" | |
f2b5ec1f | 30 | #include "cpp-common/make-unique.hpp" |
e6b5d7dd | 31 | #include "fs.hpp" |
e98a2d6e | 32 | |
4164020e | 33 | static inline size_t remaining_mmap_bytes(struct ctf_fs_ds_file *ds_file) |
e98a2d6e | 34 | { |
4164020e SM |
35 | BT_ASSERT_DBG(ds_file->mmap_len >= ds_file->request_offset_in_mapping); |
36 | return ds_file->mmap_len - ds_file->request_offset_in_mapping; | |
e98a2d6e PP |
37 | } |
38 | ||
127e2341 SM |
39 | /* |
40 | * Return true if `offset_in_file` is in the current mapping. | |
41 | */ | |
42 | ||
4164020e | 43 | static bool offset_ist_mapped(struct ctf_fs_ds_file *ds_file, off_t offset_in_file) |
127e2341 | 44 | { |
4164020e SM |
45 | return offset_in_file >= ds_file->mmap_offset_in_file && |
46 | offset_in_file < (ds_file->mmap_offset_in_file + ds_file->mmap_len); | |
127e2341 SM |
47 | } |
48 | ||
4164020e | 49 | static enum ctf_msg_iter_medium_status ds_file_munmap(struct ctf_fs_ds_file *ds_file) |
e98a2d6e | 50 | { |
4164020e | 51 | enum ctf_msg_iter_medium_status status; |
27a14e13 | 52 | const bt2_common::LogCfg& logCfg = ds_file->logCfg; |
4164020e SM |
53 | |
54 | BT_ASSERT(ds_file); | |
55 | ||
56 | if (!ds_file->mmap_addr) { | |
57 | status = CTF_MSG_ITER_MEDIUM_STATUS_OK; | |
58 | goto end; | |
59 | } | |
60 | ||
61 | if (bt_munmap(ds_file->mmap_addr, ds_file->mmap_len)) { | |
27a14e13 SM |
62 | BT_CLOGE_ERRNO("Cannot memory-unmap file", |
63 | ": address=%p, size=%zu, file_path=\"%s\", file=%p", ds_file->mmap_addr, | |
31f55dc1 | 64 | ds_file->mmap_len, ds_file->file ? ds_file->file->path.c_str() : "NULL", |
d42345a4 | 65 | ds_file->file ? ds_file->file->fp.get() : NULL); |
4164020e SM |
66 | status = CTF_MSG_ITER_MEDIUM_STATUS_ERROR; |
67 | goto end; | |
68 | } | |
69 | ||
70 | ds_file->mmap_addr = NULL; | |
71 | ||
72 | status = CTF_MSG_ITER_MEDIUM_STATUS_OK; | |
fc9a526c | 73 | end: |
4164020e | 74 | return status; |
e98a2d6e PP |
75 | } |
76 | ||
127e2341 SM |
77 | /* |
78 | * mmap a region of `ds_file` such that `requested_offset_in_file` is in the | |
79 | * mapping. If the currently mmap-ed region already contains | |
80 | * `requested_offset_in_file`, the mapping is kept. | |
81 | * | |
f6e68e70 SM |
82 | * Set `ds_file->requested_offset_in_mapping` based on `request_offset_in_file`, |
83 | * such that the next call to `request_bytes` will return bytes starting at that | |
84 | * position. | |
127e2341 SM |
85 | * |
86 | * `requested_offset_in_file` must be a valid offset in the file. | |
87 | */ | |
4164020e SM |
88 | static enum ctf_msg_iter_medium_status ds_file_mmap(struct ctf_fs_ds_file *ds_file, |
89 | off_t requested_offset_in_file) | |
e98a2d6e | 90 | { |
4164020e | 91 | enum ctf_msg_iter_medium_status status; |
27a14e13 | 92 | const bt2_common::LogCfg& logCfg = ds_file->logCfg; |
4164020e SM |
93 | |
94 | /* Ensure the requested offset is in the file range. */ | |
95 | BT_ASSERT(requested_offset_in_file >= 0); | |
96 | BT_ASSERT(requested_offset_in_file < ds_file->file->size); | |
97 | ||
98 | /* | |
99 | * If the mapping already contains the requested offset, just adjust | |
100 | * requested_offset_in_mapping. | |
101 | */ | |
102 | if (offset_ist_mapped(ds_file, requested_offset_in_file)) { | |
103 | ds_file->request_offset_in_mapping = | |
104 | requested_offset_in_file - ds_file->mmap_offset_in_file; | |
105 | status = CTF_MSG_ITER_MEDIUM_STATUS_OK; | |
106 | goto end; | |
107 | } | |
108 | ||
109 | /* Unmap old region */ | |
110 | status = ds_file_munmap(ds_file); | |
111 | if (status != CTF_MSG_ITER_MEDIUM_STATUS_OK) { | |
112 | goto end; | |
113 | } | |
114 | ||
115 | /* | |
116 | * Compute a mapping that has the required alignment properties and | |
117 | * contains `requested_offset_in_file`. | |
118 | */ | |
119 | ds_file->request_offset_in_mapping = | |
27a14e13 | 120 | requested_offset_in_file % bt_mmap_get_offset_align_size(logCfg.logLevel()); |
4164020e SM |
121 | ds_file->mmap_offset_in_file = requested_offset_in_file - ds_file->request_offset_in_mapping; |
122 | ds_file->mmap_len = | |
123 | MIN(ds_file->file->size - ds_file->mmap_offset_in_file, ds_file->mmap_max_len); | |
124 | ||
125 | BT_ASSERT(ds_file->mmap_len > 0); | |
126 | ||
127 | ds_file->mmap_addr = | |
d42345a4 SM |
128 | bt_mmap((void *) 0, ds_file->mmap_len, PROT_READ, MAP_PRIVATE, |
129 | fileno(ds_file->file->fp.get()), ds_file->mmap_offset_in_file, logCfg.logLevel()); | |
4164020e | 130 | if (ds_file->mmap_addr == MAP_FAILED) { |
27a14e13 | 131 | BT_CLOGE("Cannot memory-map address (size %zu) of file \"%s\" (%p) at offset %jd: %s", |
31f55dc1 | 132 | ds_file->mmap_len, ds_file->file->path.c_str(), ds_file->file->fp.get(), |
27a14e13 | 133 | (intmax_t) ds_file->mmap_offset_in_file, strerror(errno)); |
4164020e SM |
134 | status = CTF_MSG_ITER_MEDIUM_STATUS_ERROR; |
135 | goto end; | |
136 | } | |
137 | ||
138 | status = CTF_MSG_ITER_MEDIUM_STATUS_OK; | |
127e2341 SM |
139 | |
140 | end: | |
4164020e | 141 | return status; |
127e2341 SM |
142 | } |
143 | ||
144 | /* | |
145 | * Change the mapping of the file to read the region that follows the current | |
146 | * mapping. | |
147 | * | |
148 | * If the file hasn't been mapped yet, then everything (mmap_offset_in_file, | |
149 | * mmap_len, request_offset_in_mapping) should have the value 0, which will | |
150 | * result in the beginning of the file getting mapped. | |
151 | * | |
152 | * return _EOF if the current mapping is the end of the file. | |
153 | */ | |
154 | ||
4164020e | 155 | static enum ctf_msg_iter_medium_status ds_file_mmap_next(struct ctf_fs_ds_file *ds_file) |
127e2341 | 156 | { |
4164020e SM |
157 | enum ctf_msg_iter_medium_status status; |
158 | ||
159 | /* | |
160 | * If we're called, it's because more bytes are requested but we have | |
161 | * given all the bytes of the current mapping. | |
162 | */ | |
163 | BT_ASSERT(ds_file->request_offset_in_mapping == ds_file->mmap_len); | |
164 | ||
165 | /* | |
166 | * If the current mapping coincides with the end of the file, there is | |
167 | * no next mapping. | |
168 | */ | |
169 | if (ds_file->mmap_offset_in_file + ds_file->mmap_len == ds_file->file->size) { | |
170 | status = CTF_MSG_ITER_MEDIUM_STATUS_EOF; | |
171 | goto end; | |
172 | } | |
173 | ||
174 | status = ds_file_mmap(ds_file, ds_file->mmap_offset_in_file + ds_file->mmap_len); | |
127e2341 | 175 | |
e98a2d6e | 176 | end: |
4164020e | 177 | return status; |
e98a2d6e PP |
178 | } |
179 | ||
4164020e SM |
180 | static enum ctf_msg_iter_medium_status medop_request_bytes(size_t request_sz, uint8_t **buffer_addr, |
181 | size_t *buffer_sz, void *data) | |
e98a2d6e | 182 | { |
4164020e SM |
183 | enum ctf_msg_iter_medium_status status = CTF_MSG_ITER_MEDIUM_STATUS_OK; |
184 | struct ctf_fs_ds_file *ds_file = (struct ctf_fs_ds_file *) data; | |
27a14e13 | 185 | const bt2_common::LogCfg& logCfg = ds_file->logCfg; |
4164020e SM |
186 | |
187 | BT_ASSERT(request_sz > 0); | |
188 | ||
189 | /* | |
190 | * Check if we have at least one memory-mapped byte left. If we don't, | |
191 | * mmap the next file. | |
192 | */ | |
193 | if (remaining_mmap_bytes(ds_file) == 0) { | |
194 | /* Are we at the end of the file? */ | |
195 | if (ds_file->mmap_offset_in_file >= ds_file->file->size) { | |
31f55dc1 | 196 | BT_CLOGD("Reached end of file \"%s\" (%p)", ds_file->file->path.c_str(), |
d42345a4 | 197 | ds_file->file->fp.get()); |
4164020e SM |
198 | status = CTF_MSG_ITER_MEDIUM_STATUS_EOF; |
199 | goto end; | |
200 | } | |
201 | ||
202 | status = ds_file_mmap_next(ds_file); | |
203 | switch (status) { | |
204 | case CTF_MSG_ITER_MEDIUM_STATUS_OK: | |
205 | break; | |
206 | case CTF_MSG_ITER_MEDIUM_STATUS_EOF: | |
207 | goto end; | |
208 | default: | |
31f55dc1 SM |
209 | BT_CLOGE("Cannot memory-map next region of file \"%s\" (%p)", |
210 | ds_file->file->path.c_str(), ds_file->file->fp.get()); | |
4164020e SM |
211 | goto error; |
212 | } | |
213 | } | |
214 | ||
215 | BT_ASSERT(remaining_mmap_bytes(ds_file) > 0); | |
216 | *buffer_sz = MIN(remaining_mmap_bytes(ds_file), request_sz); | |
217 | ||
218 | BT_ASSERT(ds_file->mmap_addr); | |
219 | *buffer_addr = ((uint8_t *) ds_file->mmap_addr) + ds_file->request_offset_in_mapping; | |
220 | ||
221 | ds_file->request_offset_in_mapping += *buffer_sz; | |
222 | goto end; | |
e98a2d6e PP |
223 | |
224 | error: | |
4164020e | 225 | status = CTF_MSG_ITER_MEDIUM_STATUS_ERROR; |
e98a2d6e PP |
226 | |
227 | end: | |
4164020e | 228 | return status; |
e98a2d6e PP |
229 | } |
230 | ||
4164020e | 231 | static bt_stream *medop_borrow_stream(bt_stream_class *stream_class, int64_t stream_id, void *data) |
e98a2d6e | 232 | { |
4164020e SM |
233 | struct ctf_fs_ds_file *ds_file = (struct ctf_fs_ds_file *) data; |
234 | bt_stream_class *ds_file_stream_class; | |
235 | bt_stream *stream = NULL; | |
e5be10ef | 236 | |
22c7ae6c | 237 | ds_file_stream_class = (*ds_file->stream)->cls().libObjPtr(); |
94cf822e | 238 | |
4164020e SM |
239 | if (stream_class != ds_file_stream_class) { |
240 | /* | |
241 | * Not supported: two packets described by two different | |
242 | * stream classes within the same data stream file. | |
243 | */ | |
244 | goto end; | |
245 | } | |
e98a2d6e | 246 | |
22c7ae6c | 247 | stream = (*ds_file->stream)->libObjPtr(); |
94cf822e PP |
248 | |
249 | end: | |
4164020e | 250 | return stream; |
e98a2d6e PP |
251 | } |
252 | ||
4164020e | 253 | static enum ctf_msg_iter_medium_status medop_seek(off_t offset, void *data) |
9e0c8dbb | 254 | { |
4164020e | 255 | struct ctf_fs_ds_file *ds_file = (struct ctf_fs_ds_file *) data; |
9e0c8dbb | 256 | |
4164020e SM |
257 | BT_ASSERT(offset >= 0); |
258 | BT_ASSERT(offset < ds_file->file->size); | |
9e0c8dbb | 259 | |
4164020e | 260 | return ds_file_mmap(ds_file, offset); |
9e0c8dbb JG |
261 | } |
262 | ||
6de92955 | 263 | BT_HIDDEN |
18a1979b | 264 | struct ctf_msg_iter_medium_ops ctf_fs_ds_file_medops = { |
4164020e SM |
265 | medop_request_bytes, |
266 | medop_seek, | |
267 | nullptr, | |
268 | medop_borrow_stream, | |
e98a2d6e | 269 | }; |
6de92955 | 270 | |
4164020e SM |
271 | struct ctf_fs_ds_group_medops_data |
272 | { | |
27a14e13 SM |
273 | explicit ctf_fs_ds_group_medops_data(const bt2_common::LogCfg& logCfgParam) noexcept : |
274 | logCfg {logCfgParam} | |
275 | { | |
276 | } | |
277 | ||
4164020e | 278 | /* Weak, set once at creation time. */ |
6269f212 | 279 | struct ctf_fs_ds_file_group *ds_file_group = nullptr; |
4164020e SM |
280 | |
281 | /* | |
282 | * Index (as in element rank) of the index entry of ds_file_groups' | |
283 | * index we will read next (so, the one after the one we are reading | |
284 | * right now). | |
285 | */ | |
6269f212 | 286 | guint next_index_entry_index = 0; |
4164020e SM |
287 | |
288 | /* | |
289 | * File we are currently reading. Changes whenever we switch to | |
290 | * reading another data file. | |
291 | * | |
292 | * Owned by this. | |
293 | */ | |
cc68331f | 294 | ctf_fs_ds_file::UP file; |
4164020e SM |
295 | |
296 | /* Weak, for context / logging / appending causes. */ | |
6269f212 | 297 | bt_self_message_iterator *self_msg_iter = nullptr; |
27a14e13 | 298 | const bt2_common::LogCfg logCfg; |
f6e68e70 SM |
299 | }; |
300 | ||
4164020e SM |
301 | static enum ctf_msg_iter_medium_status medop_group_request_bytes(size_t request_sz, |
302 | uint8_t **buffer_addr, | |
303 | size_t *buffer_sz, void *void_data) | |
f6e68e70 | 304 | { |
4164020e | 305 | struct ctf_fs_ds_group_medops_data *data = (struct ctf_fs_ds_group_medops_data *) void_data; |
f6e68e70 | 306 | |
4164020e | 307 | /* Return bytes from the current file. */ |
cc68331f | 308 | return medop_request_bytes(request_sz, buffer_addr, buffer_sz, data->file.get()); |
f6e68e70 SM |
309 | } |
310 | ||
4164020e SM |
311 | static bt_stream *medop_group_borrow_stream(bt_stream_class *stream_class, int64_t stream_id, |
312 | void *void_data) | |
f6e68e70 | 313 | { |
4164020e | 314 | struct ctf_fs_ds_group_medops_data *data = (struct ctf_fs_ds_group_medops_data *) void_data; |
f6e68e70 | 315 | |
cc68331f | 316 | return medop_borrow_stream(stream_class, stream_id, data->file.get()); |
f6e68e70 SM |
317 | } |
318 | ||
319 | /* | |
320 | * Set `data->file` to prepare it to read the packet described | |
321 | * by `index_entry`. | |
322 | */ | |
323 | ||
4164020e SM |
324 | static enum ctf_msg_iter_medium_status |
325 | ctf_fs_ds_group_medops_set_file(struct ctf_fs_ds_group_medops_data *data, | |
27a14e13 | 326 | struct ctf_fs_ds_index_entry *index_entry) |
f6e68e70 | 327 | { |
4164020e SM |
328 | enum ctf_msg_iter_medium_status status; |
329 | ||
330 | BT_ASSERT(data); | |
331 | BT_ASSERT(index_entry); | |
332 | ||
333 | /* Check if that file is already the one mapped. */ | |
31f55dc1 | 334 | if (!data->file || strcmp(index_entry->path, data->file->file->path.c_str()) != 0) { |
4164020e | 335 | /* Create the new file. */ |
22c7ae6c SM |
336 | data->file = |
337 | ctf_fs_ds_file_create(data->ds_file_group->ctf_fs_trace, data->ds_file_group->stream, | |
cc68331f SM |
338 | index_entry->path, data->logCfg); |
339 | ||
4164020e | 340 | if (!data->file) { |
27a14e13 SM |
341 | const bt2_common::LogCfg& logCfg = data->logCfg; |
342 | BT_CLOGE_APPEND_CAUSE("failed to create ctf_fs_ds_file."); | |
4164020e SM |
343 | status = CTF_MSG_ITER_MEDIUM_STATUS_ERROR; |
344 | goto end; | |
345 | } | |
346 | } | |
347 | ||
348 | /* | |
349 | * Ensure the right portion of the file will be returned on the next | |
350 | * request_bytes call. | |
351 | */ | |
cc68331f | 352 | status = ds_file_mmap(data->file.get(), index_entry->offset.bytes()); |
4164020e SM |
353 | if (status != CTF_MSG_ITER_MEDIUM_STATUS_OK) { |
354 | goto end; | |
355 | } | |
356 | ||
357 | status = CTF_MSG_ITER_MEDIUM_STATUS_OK; | |
f6e68e70 SM |
358 | |
359 | end: | |
4164020e | 360 | return status; |
f6e68e70 SM |
361 | } |
362 | ||
4164020e | 363 | static enum ctf_msg_iter_medium_status medop_group_switch_packet(void *void_data) |
f6e68e70 | 364 | { |
4164020e SM |
365 | struct ctf_fs_ds_group_medops_data *data = (struct ctf_fs_ds_group_medops_data *) void_data; |
366 | struct ctf_fs_ds_index_entry *index_entry; | |
367 | enum ctf_msg_iter_medium_status status; | |
368 | ||
369 | /* If we have gone through all index entries, we are done. */ | |
5ce40cf7 | 370 | if (data->next_index_entry_index >= data->ds_file_group->index->entries.size()) { |
4164020e SM |
371 | status = CTF_MSG_ITER_MEDIUM_STATUS_EOF; |
372 | goto end; | |
373 | } | |
374 | ||
375 | /* | |
376 | * Otherwise, look up the next index entry / packet and prepare it | |
377 | * for reading. | |
378 | */ | |
5ce40cf7 | 379 | index_entry = data->ds_file_group->index->entries[data->next_index_entry_index].get(); |
4164020e | 380 | |
27a14e13 | 381 | status = ctf_fs_ds_group_medops_set_file(data, index_entry); |
4164020e SM |
382 | if (status != CTF_MSG_ITER_MEDIUM_STATUS_OK) { |
383 | goto end; | |
384 | } | |
385 | ||
386 | data->next_index_entry_index++; | |
387 | ||
388 | status = CTF_MSG_ITER_MEDIUM_STATUS_OK; | |
f6e68e70 | 389 | end: |
4164020e | 390 | return status; |
f6e68e70 SM |
391 | } |
392 | ||
37638dce SM |
393 | void ctf_fs_ds_group_medops_data_deleter::operator()(ctf_fs_ds_group_medops_data *data) |
394 | { | |
11f2ee0d | 395 | delete data; |
37638dce SM |
396 | } |
397 | ||
f6e68e70 | 398 | enum ctf_msg_iter_medium_status ctf_fs_ds_group_medops_data_create( |
4164020e | 399 | struct ctf_fs_ds_file_group *ds_file_group, bt_self_message_iterator *self_msg_iter, |
37638dce | 400 | const bt2_common::LogCfg& logCfg, ctf_fs_ds_group_medops_data_up& out) |
f6e68e70 | 401 | { |
4164020e SM |
402 | BT_ASSERT(self_msg_iter); |
403 | BT_ASSERT(ds_file_group); | |
404 | BT_ASSERT(ds_file_group->index); | |
5ce40cf7 | 405 | BT_ASSERT(!ds_file_group->index->entries.empty()); |
4164020e | 406 | |
37638dce SM |
407 | out.reset(new ctf_fs_ds_group_medops_data {logCfg}); |
408 | ||
409 | out->ds_file_group = ds_file_group; | |
410 | out->self_msg_iter = self_msg_iter; | |
4164020e SM |
411 | |
412 | /* | |
413 | * No need to prepare the first file. ctf_msg_iter will call | |
414 | * switch_packet before reading the first packet, it will be | |
415 | * done then. | |
416 | */ | |
417 | ||
6269f212 | 418 | return CTF_MSG_ITER_MEDIUM_STATUS_OK; |
f6e68e70 SM |
419 | } |
420 | ||
421 | void ctf_fs_ds_group_medops_data_reset(struct ctf_fs_ds_group_medops_data *data) | |
422 | { | |
4164020e | 423 | data->next_index_entry_index = 0; |
f6e68e70 SM |
424 | } |
425 | ||
426 | struct ctf_msg_iter_medium_ops ctf_fs_ds_group_medops = { | |
4164020e | 427 | .request_bytes = medop_group_request_bytes, |
f6e68e70 | 428 | |
4164020e SM |
429 | /* |
430 | * We don't support seeking using this medops. It would probably be | |
431 | * possible, but it's not needed at the moment. | |
432 | */ | |
433 | .seek = NULL, | |
087cd0f5 | 434 | |
4164020e SM |
435 | .switch_packet = medop_group_switch_packet, |
436 | .borrow_stream = medop_group_borrow_stream, | |
f6e68e70 SM |
437 | }; |
438 | ||
3b5041e6 SM |
439 | static ctf_fs_ds_index_entry::UP ctf_fs_ds_index_entry_create(const bt2_common::DataLen offset, |
440 | const bt2_common::DataLen packetSize) | |
6834784d | 441 | { |
3b5041e6 SM |
442 | ctf_fs_ds_index_entry::UP entry = |
443 | bt2_common::makeUnique<ctf_fs_ds_index_entry>(offset, packetSize); | |
cfa1646e | 444 | |
4164020e | 445 | entry->packet_seq_num = UINT64_MAX; |
6834784d | 446 | |
4164020e | 447 | return entry; |
6834784d SM |
448 | } |
449 | ||
4164020e | 450 | static int convert_cycles_to_ns(struct ctf_clock_class *clock_class, uint64_t cycles, int64_t *ns) |
b6c3dcb2 | 451 | { |
4164020e SM |
452 | return bt_util_clock_cycles_to_ns_from_origin(cycles, clock_class->frequency, |
453 | clock_class->offset_seconds, | |
454 | clock_class->offset_cycles, ns); | |
97ade20b JG |
455 | } |
456 | ||
11e6a045 SM |
457 | static ctf_fs_ds_index::UP build_index_from_idx_file(struct ctf_fs_ds_file *ds_file, |
458 | struct ctf_fs_ds_file_info *file_info, | |
459 | struct ctf_msg_iter *msg_iter) | |
97ade20b | 460 | { |
4164020e | 461 | int ret; |
111314d6 SM |
462 | bt2_common::GCharUP directory; |
463 | bt2_common::GCharUP basename; | |
3b56d776 | 464 | std::string index_basename; |
111314d6 | 465 | bt2_common::GCharUP index_file_path; |
4164020e SM |
466 | GMappedFile *mapped_file = NULL; |
467 | gsize filesize; | |
468 | const char *mmap_begin = NULL, *file_pos = NULL; | |
469 | const struct ctf_packet_index_file_hdr *header = NULL; | |
11e6a045 | 470 | ctf_fs_ds_index::UP index; |
3b5041e6 SM |
471 | ctf_fs_ds_index_entry::UP index_entry; |
472 | ctf_fs_ds_index_entry *prev_index_entry = NULL; | |
cfa1646e | 473 | bt2_common::DataLen totalPacketsSize = bt2_common::DataLen::fromBytes(0); |
4164020e SM |
474 | size_t file_index_entry_size; |
475 | size_t file_entry_count; | |
476 | size_t i; | |
477 | struct ctf_stream_class *sc; | |
478 | struct ctf_msg_iter_packet_properties props; | |
479 | uint32_t version_major, version_minor; | |
27a14e13 | 480 | const bt2_common::LogCfg& logCfg = ds_file->logCfg; |
4164020e | 481 | |
31f55dc1 | 482 | BT_CLOGI("Building index from .idx file of stream file %s", ds_file->file->path.c_str()); |
4164020e SM |
483 | ret = ctf_msg_iter_get_packet_properties(msg_iter, &props); |
484 | if (ret) { | |
27a14e13 | 485 | BT_CLOGI_STR("Cannot read first packet's header and context fields."); |
4164020e SM |
486 | goto error; |
487 | } | |
488 | ||
489 | sc = ctf_trace_class_borrow_stream_class_by_id(ds_file->metadata->tc, props.stream_class_id); | |
490 | BT_ASSERT(sc); | |
491 | if (!sc->default_clock_class) { | |
27a14e13 | 492 | BT_CLOGI_STR("Cannot find stream class's default clock class."); |
4164020e SM |
493 | goto error; |
494 | } | |
495 | ||
496 | /* Look for index file in relative path index/name.idx. */ | |
111314d6 | 497 | basename.reset(g_path_get_basename(ds_file->file->path.c_str())); |
4164020e | 498 | if (!basename) { |
31f55dc1 | 499 | BT_CLOGE("Cannot get the basename of datastream file %s", ds_file->file->path.c_str()); |
4164020e SM |
500 | goto error; |
501 | } | |
502 | ||
111314d6 | 503 | directory.reset(g_path_get_dirname(ds_file->file->path.c_str())); |
4164020e | 504 | if (!directory) { |
31f55dc1 | 505 | BT_CLOGE("Cannot get dirname of datastream file %s", ds_file->file->path.c_str()); |
4164020e SM |
506 | goto error; |
507 | } | |
508 | ||
3b56d776 SM |
509 | index_basename = basename.get(); |
510 | index_basename += ".idx"; | |
4164020e | 511 | |
3b56d776 | 512 | index_file_path.reset(g_build_filename(directory.get(), "index", index_basename.c_str(), NULL)); |
111314d6 | 513 | mapped_file = g_mapped_file_new(index_file_path.get(), FALSE, NULL); |
4164020e | 514 | if (!mapped_file) { |
111314d6 | 515 | BT_CLOGD("Cannot create new mapped file %s", index_file_path.get()); |
4164020e SM |
516 | goto error; |
517 | } | |
518 | ||
519 | /* | |
520 | * The g_mapped_file API limits us to 4GB files on 32-bit. | |
521 | * Traces with such large indexes have never been seen in the wild, | |
522 | * but this would need to be adjusted to support them. | |
523 | */ | |
524 | filesize = g_mapped_file_get_length(mapped_file); | |
525 | if (filesize < sizeof(*header)) { | |
27a14e13 SM |
526 | BT_CLOGW("Invalid LTTng trace index file: " |
527 | "file size (%zu bytes) < header size (%zu bytes)", | |
528 | filesize, sizeof(*header)); | |
4164020e SM |
529 | goto error; |
530 | } | |
531 | ||
532 | mmap_begin = g_mapped_file_get_contents(mapped_file); | |
533 | header = (struct ctf_packet_index_file_hdr *) mmap_begin; | |
534 | ||
535 | file_pos = g_mapped_file_get_contents(mapped_file) + sizeof(*header); | |
536 | if (be32toh(header->magic) != CTF_INDEX_MAGIC) { | |
27a14e13 | 537 | BT_CLOGW_STR("Invalid LTTng trace index: \"magic\" field validation failed"); |
4164020e SM |
538 | goto error; |
539 | } | |
540 | ||
541 | version_major = be32toh(header->index_major); | |
542 | version_minor = be32toh(header->index_minor); | |
543 | if (version_major != 1) { | |
27a14e13 SM |
544 | BT_CLOGW("Unknown LTTng trace index version: " |
545 | "major=%" PRIu32 ", minor=%" PRIu32, | |
546 | version_major, version_minor); | |
4164020e SM |
547 | goto error; |
548 | } | |
549 | ||
550 | file_index_entry_size = be32toh(header->packet_index_len); | |
551 | if (file_index_entry_size < CTF_INDEX_1_0_SIZE) { | |
27a14e13 | 552 | BT_CLOGW( |
4164020e SM |
553 | "Invalid `packet_index_len` in LTTng trace index file (`packet_index_len` < CTF index 1.0 index entry size): " |
554 | "packet_index_len=%zu, CTF_INDEX_1_0_SIZE=%zu", | |
555 | file_index_entry_size, CTF_INDEX_1_0_SIZE); | |
556 | goto error; | |
557 | } | |
558 | ||
559 | file_entry_count = (filesize - sizeof(*header)) / file_index_entry_size; | |
560 | if ((filesize - sizeof(*header)) % file_index_entry_size) { | |
27a14e13 SM |
561 | BT_CLOGW("Invalid LTTng trace index: the index's size after the header " |
562 | "(%zu bytes) is not a multiple of the index entry size " | |
563 | "(%zu bytes)", | |
564 | (filesize - sizeof(*header)), sizeof(*header)); | |
4164020e SM |
565 | goto error; |
566 | } | |
567 | ||
32ac3801 | 568 | index = bt2_common::makeUnique<ctf_fs_ds_index>(); |
4164020e SM |
569 | |
570 | for (i = 0; i < file_entry_count; i++) { | |
571 | struct ctf_packet_index *file_index = (struct ctf_packet_index *) file_pos; | |
cfa1646e SM |
572 | bt2_common::DataLen packetSize = |
573 | bt2_common::DataLen::fromBits(be64toh(file_index->packet_size)); | |
4164020e | 574 | |
cfa1646e | 575 | if (packetSize.hasExtraBits()) { |
27a14e13 | 576 | BT_CLOGW("Invalid packet size encountered in LTTng trace index file"); |
4164020e SM |
577 | goto error; |
578 | } | |
579 | ||
cfa1646e SM |
580 | bt2_common::DataLen offset = bt2_common::DataLen::fromBytes(be64toh(file_index->offset)); |
581 | if (i != 0 && offset < prev_index_entry->offset) { | |
582 | BT_CLOGW("Invalid, non-monotonic, packet offset encountered in LTTng trace index file: " | |
583 | "previous offset=%llu bytes, current offset=%llu bytes", | |
584 | prev_index_entry->offset.bytes(), offset.bytes()); | |
585 | goto error; | |
586 | } | |
587 | ||
588 | index_entry = ctf_fs_ds_index_entry_create(offset, packetSize); | |
4164020e | 589 | if (!index_entry) { |
27a14e13 | 590 | BT_CLOGE_APPEND_CAUSE("Failed to create a ctf_fs_ds_index_entry."); |
4164020e SM |
591 | goto error; |
592 | } | |
593 | ||
594 | /* Set path to stream file. */ | |
473546f5 | 595 | index_entry->path = file_info->path.c_str(); |
4164020e | 596 | |
4164020e SM |
597 | index_entry->timestamp_begin = be64toh(file_index->timestamp_begin); |
598 | index_entry->timestamp_end = be64toh(file_index->timestamp_end); | |
599 | if (index_entry->timestamp_end < index_entry->timestamp_begin) { | |
27a14e13 | 600 | BT_CLOGW( |
4164020e SM |
601 | "Invalid packet time bounds encountered in LTTng trace index file (begin > end): " |
602 | "timestamp_begin=%" PRIu64 "timestamp_end=%" PRIu64, | |
603 | index_entry->timestamp_begin, index_entry->timestamp_end); | |
604 | goto error; | |
605 | } | |
606 | ||
607 | /* Convert the packet's bound to nanoseconds since Epoch. */ | |
608 | ret = convert_cycles_to_ns(sc->default_clock_class, index_entry->timestamp_begin, | |
609 | &index_entry->timestamp_begin_ns); | |
610 | if (ret) { | |
27a14e13 | 611 | BT_CLOGI_STR( |
4164020e SM |
612 | "Failed to convert raw timestamp to nanoseconds since Epoch during index parsing"); |
613 | goto error; | |
614 | } | |
615 | ret = convert_cycles_to_ns(sc->default_clock_class, index_entry->timestamp_end, | |
616 | &index_entry->timestamp_end_ns); | |
617 | if (ret) { | |
27a14e13 | 618 | BT_CLOGI_STR( |
4164020e SM |
619 | "Failed to convert raw timestamp to nanoseconds since Epoch during LTTng trace index parsing"); |
620 | goto error; | |
621 | } | |
622 | ||
623 | if (version_minor >= 1) { | |
624 | index_entry->packet_seq_num = be64toh(file_index->packet_seq_num); | |
625 | } | |
626 | ||
cfa1646e | 627 | totalPacketsSize += packetSize; |
4164020e SM |
628 | file_pos += file_index_entry_size; |
629 | ||
3b5041e6 | 630 | prev_index_entry = index_entry.get(); |
4164020e | 631 | |
5ce40cf7 | 632 | index->entries.emplace_back(std::move(index_entry)); |
4164020e SM |
633 | } |
634 | ||
635 | /* Validate that the index addresses the complete stream. */ | |
cfa1646e | 636 | if (ds_file->file->size != totalPacketsSize.bytes()) { |
27a14e13 | 637 | BT_CLOGW("Invalid LTTng trace index file; indexed size != stream file size: " |
cfa1646e SM |
638 | "file-size=%" PRIu64 " bytes, total-packets-size=%llu bytes", |
639 | ds_file->file->size, totalPacketsSize.bytes()); | |
4164020e SM |
640 | goto error; |
641 | } | |
b6c3dcb2 | 642 | end: |
4164020e SM |
643 | if (mapped_file) { |
644 | g_mapped_file_unref(mapped_file); | |
645 | } | |
646 | return index; | |
97ade20b | 647 | error: |
11e6a045 | 648 | index.reset(); |
4164020e | 649 | goto end; |
b6c3dcb2 JG |
650 | } |
651 | ||
4164020e | 652 | static int init_index_entry(struct ctf_fs_ds_index_entry *entry, struct ctf_fs_ds_file *ds_file, |
cfa1646e | 653 | struct ctf_msg_iter_packet_properties *props) |
9e0c8dbb | 654 | { |
4164020e SM |
655 | int ret = 0; |
656 | struct ctf_stream_class *sc; | |
4164020e SM |
657 | |
658 | sc = ctf_trace_class_borrow_stream_class_by_id(ds_file->metadata->tc, props->stream_class_id); | |
659 | BT_ASSERT(sc); | |
27a14e13 | 660 | const bt2_common::LogCfg& logCfg = ds_file->logCfg; |
4164020e SM |
661 | |
662 | if (props->snapshots.beginning_clock != UINT64_C(-1)) { | |
663 | entry->timestamp_begin = props->snapshots.beginning_clock; | |
664 | ||
665 | /* Convert the packet's bound to nanoseconds since Epoch. */ | |
666 | ret = convert_cycles_to_ns(sc->default_clock_class, props->snapshots.beginning_clock, | |
667 | &entry->timestamp_begin_ns); | |
668 | if (ret) { | |
27a14e13 | 669 | BT_CLOGI_STR("Failed to convert raw timestamp to nanoseconds since Epoch."); |
4164020e SM |
670 | goto end; |
671 | } | |
672 | } else { | |
673 | entry->timestamp_begin = UINT64_C(-1); | |
674 | entry->timestamp_begin_ns = UINT64_C(-1); | |
675 | } | |
676 | ||
677 | if (props->snapshots.end_clock != UINT64_C(-1)) { | |
678 | entry->timestamp_end = props->snapshots.end_clock; | |
679 | ||
680 | /* Convert the packet's bound to nanoseconds since Epoch. */ | |
681 | ret = convert_cycles_to_ns(sc->default_clock_class, props->snapshots.end_clock, | |
682 | &entry->timestamp_end_ns); | |
683 | if (ret) { | |
27a14e13 | 684 | BT_CLOGI_STR("Failed to convert raw timestamp to nanoseconds since Epoch."); |
4164020e SM |
685 | goto end; |
686 | } | |
687 | } else { | |
688 | entry->timestamp_end = UINT64_C(-1); | |
689 | entry->timestamp_end_ns = UINT64_C(-1); | |
690 | } | |
0b29603d | 691 | |
9e0c8dbb | 692 | end: |
4164020e | 693 | return ret; |
9e0c8dbb JG |
694 | } |
695 | ||
11e6a045 SM |
696 | static ctf_fs_ds_index::UP build_index_from_stream_file(struct ctf_fs_ds_file *ds_file, |
697 | struct ctf_fs_ds_file_info *file_info, | |
698 | struct ctf_msg_iter *msg_iter) | |
9e0c8dbb | 699 | { |
4164020e | 700 | int ret; |
4164020e | 701 | enum ctf_msg_iter_status iter_status = CTF_MSG_ITER_STATUS_OK; |
cfa1646e | 702 | bt2_common::DataLen currentPacketOffset = bt2_common::DataLen::fromBytes(0); |
27a14e13 | 703 | const bt2_common::LogCfg& logCfg = ds_file->logCfg; |
4164020e | 704 | |
31f55dc1 | 705 | BT_CLOGI("Indexing stream file %s", ds_file->file->path.c_str()); |
4164020e | 706 | |
32ac3801 | 707 | ctf_fs_ds_index::UP index = bt2_common::makeUnique<ctf_fs_ds_index>(); |
4164020e SM |
708 | |
709 | while (true) { | |
3b5041e6 | 710 | ctf_fs_ds_index_entry::UP index_entry; |
4164020e SM |
711 | struct ctf_msg_iter_packet_properties props; |
712 | ||
cfa1646e | 713 | if (currentPacketOffset.bytes() > ds_file->file->size) { |
27a14e13 | 714 | BT_CLOGE_STR("Unexpected current packet's offset (larger than file)."); |
4164020e | 715 | goto error; |
cfa1646e | 716 | } else if (currentPacketOffset.bytes() == ds_file->file->size) { |
4164020e SM |
717 | /* No more data */ |
718 | break; | |
719 | } | |
720 | ||
cfa1646e | 721 | iter_status = ctf_msg_iter_seek(msg_iter, currentPacketOffset.bytes()); |
4164020e SM |
722 | if (iter_status != CTF_MSG_ITER_STATUS_OK) { |
723 | goto error; | |
724 | } | |
725 | ||
726 | iter_status = ctf_msg_iter_get_packet_properties(msg_iter, &props); | |
727 | if (iter_status != CTF_MSG_ITER_STATUS_OK) { | |
728 | goto error; | |
729 | } | |
730 | ||
cfa1646e SM |
731 | /* |
732 | * Get the current packet size from the packet header, if set. Else, | |
733 | * assume there is a single packet in the file, so take the file size | |
734 | * as the packet size. | |
735 | */ | |
736 | bt2_common::DataLen currentPacketSize = | |
737 | props.exp_packet_total_size >= 0 ? | |
738 | bt2_common::DataLen::fromBits(props.exp_packet_total_size) : | |
739 | bt2_common::DataLen::fromBytes(ds_file->file->size); | |
4164020e | 740 | |
cfa1646e | 741 | if ((currentPacketOffset + currentPacketSize).bytes() > ds_file->file->size) { |
27a14e13 | 742 | BT_CLOGW("Invalid packet size reported in file: stream=\"%s\", " |
cfa1646e SM |
743 | "packet-offset-bytes=%llu, packet-size-bytes=%llu, " |
744 | "file-size-bytes=%jd", | |
31f55dc1 | 745 | ds_file->file->path.c_str(), currentPacketOffset.bytes(), |
cfa1646e | 746 | currentPacketSize.bytes(), (intmax_t) ds_file->file->size); |
4164020e SM |
747 | goto error; |
748 | } | |
749 | ||
cfa1646e | 750 | index_entry = ctf_fs_ds_index_entry_create(currentPacketOffset, currentPacketSize); |
4164020e | 751 | if (!index_entry) { |
27a14e13 | 752 | BT_CLOGE_APPEND_CAUSE("Failed to create a ctf_fs_ds_index_entry."); |
4164020e SM |
753 | goto error; |
754 | } | |
755 | ||
756 | /* Set path to stream file. */ | |
473546f5 | 757 | index_entry->path = file_info->path.c_str(); |
4164020e | 758 | |
3b5041e6 | 759 | ret = init_index_entry(index_entry.get(), ds_file, &props); |
4164020e | 760 | if (ret) { |
4164020e SM |
761 | goto error; |
762 | } | |
763 | ||
5ce40cf7 | 764 | index->entries.emplace_back(std::move(index_entry)); |
4164020e | 765 | |
cfa1646e SM |
766 | currentPacketOffset += currentPacketSize; |
767 | BT_CLOGD("Seeking to next packet: current-packet-offset-bytes=%llu, " | |
768 | "next-packet-offset-bytes=%llu", | |
769 | (currentPacketOffset - currentPacketSize).bytes(), currentPacketOffset.bytes()); | |
4164020e | 770 | } |
312c056a | 771 | |
9e0c8dbb | 772 | end: |
4164020e | 773 | return index; |
312c056a | 774 | |
9e0c8dbb | 775 | error: |
11e6a045 | 776 | index.reset(); |
4164020e | 777 | goto end; |
9e0c8dbb JG |
778 | } |
779 | ||
e7a4393b | 780 | BT_HIDDEN |
6d0b3c49 SM |
781 | ctf_fs_ds_file::UP ctf_fs_ds_file_create(struct ctf_fs_trace *ctf_fs_trace, |
782 | nonstd::optional<bt2::Stream::Shared> stream, | |
783 | const char *path, const bt2_common::LogCfg& logCfg) | |
e98a2d6e | 784 | { |
4164020e | 785 | int ret; |
27a14e13 | 786 | const size_t offset_align = bt_mmap_get_offset_align_size(logCfg.logLevel()); |
6d0b3c49 | 787 | ctf_fs_ds_file::UP ds_file = bt2_common::makeUnique<ctf_fs_ds_file>(logCfg); |
4164020e | 788 | |
81432af2 | 789 | ds_file->file = bt2_common::makeUnique<ctf_fs_file>(logCfg); |
22c7ae6c | 790 | ds_file->stream = std::move(stream); |
32ef7474 | 791 | ds_file->metadata = ctf_fs_trace->metadata.get(); |
31f55dc1 | 792 | ds_file->file->path = path; |
2d54e7ff | 793 | ret = ctf_fs_file_open(ds_file->file.get(), "rb"); |
4164020e SM |
794 | if (ret) { |
795 | goto error; | |
796 | } | |
797 | ||
798 | ds_file->mmap_max_len = offset_align * 2048; | |
799 | ||
800 | goto end; | |
1a9f7075 | 801 | |
e98a2d6e | 802 | error: |
4164020e | 803 | /* Do not touch "borrowed" file. */ |
6d0b3c49 | 804 | ds_file.reset(); |
1a9f7075 | 805 | |
e98a2d6e | 806 | end: |
4164020e | 807 | return ds_file; |
e98a2d6e PP |
808 | } |
809 | ||
97ade20b | 810 | BT_HIDDEN |
11e6a045 SM |
811 | ctf_fs_ds_index::UP ctf_fs_ds_file_build_index(struct ctf_fs_ds_file *ds_file, |
812 | struct ctf_fs_ds_file_info *file_info, | |
813 | struct ctf_msg_iter *msg_iter) | |
97ade20b | 814 | { |
11e6a045 | 815 | ctf_fs_ds_index::UP index; |
27a14e13 | 816 | const bt2_common::LogCfg& logCfg = ds_file->logCfg; |
4164020e SM |
817 | |
818 | index = build_index_from_idx_file(ds_file, file_info, msg_iter); | |
819 | if (index) { | |
820 | goto end; | |
821 | } | |
822 | ||
27a14e13 SM |
823 | BT_CLOGI("Failed to build index from .index file; " |
824 | "falling back to stream indexing."); | |
4164020e | 825 | index = build_index_from_stream_file(ds_file, file_info, msg_iter); |
9e0c8dbb | 826 | end: |
4164020e | 827 | return index; |
97ade20b JG |
828 | } |
829 | ||
42871aa4 | 830 | ctf_fs_ds_file::~ctf_fs_ds_file() |
e98a2d6e | 831 | { |
42871aa4 | 832 | (void) ds_file_munmap(this); |
e98a2d6e | 833 | } |
4f1f88a6 | 834 | |
f2b5ec1f | 835 | BT_HIDDEN ctf_fs_ds_file_info::UP ctf_fs_ds_file_info_create(const char *path, int64_t begin_ns) |
0b067334 | 836 | { |
f2b5ec1f | 837 | ctf_fs_ds_file_info::UP ds_file_info = bt2_common::makeUnique<ctf_fs_ds_file_info>(); |
0b067334 | 838 | |
473546f5 | 839 | ds_file_info->path = path; |
0b067334 SM |
840 | ds_file_info->begin_ns = begin_ns; |
841 | ||
0b067334 SM |
842 | return ds_file_info; |
843 | } | |
844 | ||
f0ab0592 SM |
845 | BT_HIDDEN ctf_fs_ds_file_group::UP ctf_fs_ds_file_group_create(struct ctf_fs_trace *ctf_fs_trace, |
846 | struct ctf_stream_class *sc, | |
847 | uint64_t stream_instance_id, | |
6cde7b05 | 848 | ctf_fs_ds_index::UP index) |
f0ab0592 SM |
849 | { |
850 | ctf_fs_ds_file_group::UP ds_file_group {new ctf_fs_ds_file_group}; | |
851 | ||
6cde7b05 | 852 | ds_file_group->index = std::move(index); |
0b067334 SM |
853 | |
854 | ds_file_group->stream_id = stream_instance_id; | |
855 | BT_ASSERT(sc); | |
856 | ds_file_group->sc = sc; | |
857 | ds_file_group->ctf_fs_trace = ctf_fs_trace; | |
0b067334 | 858 | |
0b067334 SM |
859 | return ds_file_group; |
860 | } |