Commit | Line | Data |
---|---|---|
8ba26eae | 1 | /* liblttd |
008e2515 MSL |
2 | * |
3 | * Linux Trace Toolkit Daemon | |
4 | * | |
5 | * This is a simple daemon that reads a few relay+debugfs channels and save | |
6 | * them in a trace. | |
7 | * | |
8 | * CPU hot-plugging is supported using inotify. | |
9 | * | |
10 | * Copyright 2005 - | |
11 | * Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca> | |
d9cbca27 MSL |
12 | * Copyright 2010 - |
13 | * Michael Sills-Lavoie <michael.sills-lavoie@polymtl.ca> | |
14 | * Oumarou Dicko <oumarou.dicko@polymtl.ca> | |
8ba26eae MD |
15 | * |
16 | * This library is free software; you can redistribute it and/or | |
17 | * modify it under the terms of the GNU Lesser General Public | |
18 | * License as published by the Free Software Foundation; either | |
19 | * version 2.1 of the License, or (at your option) any later version. | |
20 | * | |
21 | * This library is distributed in the hope that it will be useful, | |
22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
24 | * Lesser General Public License for more details. | |
25 | * | |
26 | * You should have received a copy of the GNU Lesser General Public | |
27 | * License along with this library; if not, write to the Free Software | |
28 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
008e2515 MSL |
29 | */ |
30 | ||
31 | #ifdef HAVE_CONFIG_H | |
32 | #include <config.h> | |
33 | #endif | |
34 | ||
35 | #include "liblttd.h" | |
36 | ||
37 | #define _REENTRANT | |
38 | #define _GNU_SOURCE | |
39 | #include <features.h> | |
40 | #include <stdio.h> | |
41 | #include <unistd.h> | |
42 | #include <errno.h> | |
43 | #include <sys/types.h> | |
44 | #include <stdlib.h> | |
45 | #include <dirent.h> | |
46 | #include <string.h> | |
47 | #include <fcntl.h> | |
48 | #include <sys/stat.h> | |
49 | #include <sys/poll.h> | |
50 | #include <sys/mman.h> | |
51 | #include <sys/syscall.h> | |
52 | #include <unistd.h> | |
53 | #include <asm/ioctls.h> | |
54 | ||
55 | #include <linux/version.h> | |
56 | ||
57 | /* Relayfs IOCTL */ | |
58 | #include <asm/ioctl.h> | |
59 | #include <asm/types.h> | |
60 | ||
61 | /* Get the next sub buffer that can be read. */ | |
62 | #define RELAY_GET_SB _IOR(0xF5, 0x00,__u32) | |
63 | /* Release the oldest reserved (by "get") sub buffer. */ | |
64 | #define RELAY_PUT_SB _IOW(0xF5, 0x01,__u32) | |
65 | /* returns the number of sub buffers in the per cpu channel. */ | |
66 | #define RELAY_GET_N_SB _IOR(0xF5, 0x02,__u32) | |
67 | /* returns the size of the current sub buffer. */ | |
68 | #define RELAY_GET_SB_SIZE _IOR(0xF5, 0x03, __u32) | |
69 | /* returns the size of data to consume in the current sub-buffer. */ | |
70 | #define RELAY_GET_MAX_SB_SIZE _IOR(0xF5, 0x04, __u32) | |
71 | ||
72 | ||
73 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14) | |
74 | #include <sys/inotify.h> | |
75 | ||
76 | #define HAS_INOTIFY | |
77 | #else | |
78 | static inline int inotify_init (void) | |
79 | { | |
80 | return -1; | |
81 | } | |
82 | ||
83 | static inline int inotify_add_watch (int fd, const char *name, __u32 mask) | |
84 | { | |
85 | return 0; | |
86 | } | |
87 | ||
88 | static inline int inotify_rm_watch (int fd, __u32 wd) | |
89 | { | |
90 | return 0; | |
91 | } | |
92 | #undef HAS_INOTIFY | |
93 | #endif | |
94 | ||
d6d516b7 MSL |
95 | struct liblttd_thread_data { |
96 | int thread_num; | |
97 | struct liblttd_instance *instance; | |
98 | }; | |
008e2515 MSL |
99 | |
100 | #define printf_verbose(fmt, args...) \ | |
101 | do { \ | |
d6d516b7 | 102 | if (instance->verbose_mode) \ |
008e2515 MSL |
103 | printf(fmt, ##args); \ |
104 | } while (0) | |
105 | ||
106 | ||
d6d516b7 MSL |
107 | int open_buffer_file(struct liblttd_instance *instance, char *filename, |
108 | char *path_channel, char *base_path_channel) | |
008e2515 MSL |
109 | { |
110 | int open_ret = 0; | |
111 | int ret = 0; | |
112 | ||
8ba26eae MD |
113 | if (strncmp(filename, "flight-", sizeof("flight-")-1) != 0) { |
114 | if (instance->dump_flight_only) { | |
008e2515 MSL |
115 | printf_verbose("Skipping normal channel %s\n", |
116 | path_channel); | |
117 | return 0; | |
118 | } | |
119 | } else { | |
8ba26eae | 120 | if (instance->dump_normal_only) { |
008e2515 MSL |
121 | printf_verbose("Skipping flight channel %s\n", |
122 | path_channel); | |
123 | return 0; | |
124 | } | |
125 | } | |
126 | printf_verbose("Opening file.\n"); | |
127 | ||
d6d516b7 MSL |
128 | instance->fd_pairs.pair = realloc(instance->fd_pairs.pair, |
129 | ++instance->fd_pairs.num_pairs * sizeof(struct fd_pair)); | |
008e2515 MSL |
130 | |
131 | /* Open the channel in read mode */ | |
d6d516b7 | 132 | instance->fd_pairs.pair[instance->fd_pairs.num_pairs-1].channel = |
008e2515 | 133 | open(path_channel, O_RDONLY | O_NONBLOCK); |
8ba26eae | 134 | if (instance->fd_pairs.pair[instance->fd_pairs.num_pairs-1].channel == -1) { |
008e2515 | 135 | perror(path_channel); |
d6d516b7 | 136 | instance->fd_pairs.num_pairs--; |
008e2515 MSL |
137 | return 0; /* continue */ |
138 | } | |
139 | ||
8ba26eae | 140 | if (instance->callbacks->on_open_channel) ret = instance->callbacks->on_open_channel( |
d6d516b7 | 141 | instance->callbacks, &instance->fd_pairs.pair[instance->fd_pairs.num_pairs-1], |
008e2515 MSL |
142 | base_path_channel); |
143 | ||
8ba26eae | 144 | if (ret != 0) { |
008e2515 | 145 | open_ret = -1; |
d6d516b7 MSL |
146 | close(instance->fd_pairs.pair[instance->fd_pairs.num_pairs-1].channel); |
147 | instance->fd_pairs.num_pairs--; | |
008e2515 MSL |
148 | goto end; |
149 | } | |
150 | ||
151 | end: | |
152 | return open_ret; | |
153 | } | |
154 | ||
d6d516b7 MSL |
155 | int open_channel_trace_pairs(struct liblttd_instance *instance, |
156 | char *subchannel_name, char *base_subchannel_name) | |
008e2515 MSL |
157 | { |
158 | DIR *channel_dir = opendir(subchannel_name); | |
159 | struct dirent *entry; | |
160 | struct stat stat_buf; | |
161 | int ret; | |
162 | char path_channel[PATH_MAX]; | |
163 | int path_channel_len; | |
164 | char *path_channel_ptr; | |
165 | char *base_subchannel_ptr; | |
166 | ||
167 | int open_ret = 0; | |
168 | ||
8ba26eae | 169 | if (channel_dir == NULL) { |
008e2515 MSL |
170 | perror(subchannel_name); |
171 | open_ret = ENOENT; | |
172 | goto end; | |
173 | } | |
174 | ||
d9cbca27 | 175 | printf_verbose("Calling : on new channels folder\n"); |
8ba26eae | 176 | if (instance->callbacks->on_new_channels_folder) ret = instance->callbacks-> |
d6d516b7 | 177 | on_new_channels_folder(instance->callbacks, |
008e2515 | 178 | base_subchannel_name); |
8ba26eae | 179 | if (ret == -1) { |
008e2515 MSL |
180 | open_ret = -1; |
181 | goto end; | |
182 | } | |
183 | ||
184 | strncpy(path_channel, subchannel_name, PATH_MAX-1); | |
185 | path_channel_len = strlen(path_channel); | |
186 | path_channel[path_channel_len] = '/'; | |
187 | path_channel_len++; | |
188 | path_channel_ptr = path_channel + path_channel_len; | |
189 | base_subchannel_ptr = path_channel + | |
190 | (base_subchannel_name - subchannel_name); | |
191 | ||
192 | #ifdef HAS_INOTIFY | |
d6d516b7 MSL |
193 | instance->inotify_watch_array.elem = realloc(instance->inotify_watch_array.elem, |
194 | ++instance->inotify_watch_array.num * sizeof(struct inotify_watch)); | |
008e2515 MSL |
195 | |
196 | printf_verbose("Adding inotify for channel %s\n", path_channel); | |
d6d516b7 MSL |
197 | instance->inotify_watch_array.elem[instance->inotify_watch_array.num-1].wd = inotify_add_watch(instance->inotify_fd, path_channel, IN_CREATE); |
198 | strcpy(instance->inotify_watch_array.elem[instance->inotify_watch_array.num-1].path_channel, path_channel); | |
199 | instance->inotify_watch_array.elem[instance->inotify_watch_array.num-1].base_path_channel = | |
200 | instance->inotify_watch_array.elem[instance->inotify_watch_array.num-1].path_channel + | |
008e2515 MSL |
201 | (base_subchannel_name - subchannel_name); |
202 | printf_verbose("Added inotify for channel %s, wd %u\n", | |
d6d516b7 MSL |
203 | instance->inotify_watch_array.elem[instance->inotify_watch_array.num-1].path_channel, |
204 | instance->inotify_watch_array.elem[instance->inotify_watch_array.num-1].wd); | |
008e2515 MSL |
205 | #endif |
206 | ||
207 | while((entry = readdir(channel_dir)) != NULL) { | |
208 | ||
8ba26eae | 209 | if (entry->d_name[0] == '.') continue; |
008e2515 MSL |
210 | |
211 | strncpy(path_channel_ptr, entry->d_name, PATH_MAX - path_channel_len); | |
212 | ||
213 | ret = stat(path_channel, &stat_buf); | |
8ba26eae | 214 | if (ret == -1) { |
008e2515 MSL |
215 | perror(path_channel); |
216 | continue; | |
217 | } | |
218 | ||
219 | printf_verbose("Channel file : %s\n", path_channel); | |
220 | ||
8ba26eae | 221 | if (S_ISDIR(stat_buf.st_mode)) { |
008e2515 MSL |
222 | |
223 | printf_verbose("Entering channel subdirectory...\n"); | |
d6d516b7 | 224 | ret = open_channel_trace_pairs(instance, path_channel, base_subchannel_ptr); |
8ba26eae MD |
225 | if (ret < 0) continue; |
226 | } else if (S_ISREG(stat_buf.st_mode)) { | |
d6d516b7 MSL |
227 | open_ret = open_buffer_file(instance, entry->d_name, |
228 | path_channel, base_subchannel_ptr); | |
8ba26eae | 229 | if (open_ret) |
008e2515 MSL |
230 | goto end; |
231 | } | |
232 | } | |
233 | ||
234 | end: | |
235 | closedir(channel_dir); | |
236 | ||
237 | return open_ret; | |
238 | } | |
239 | ||
240 | ||
d6d516b7 | 241 | int read_subbuffer(struct liblttd_instance *instance, struct fd_pair *pair) |
008e2515 MSL |
242 | { |
243 | unsigned int consumed_old, len; | |
244 | int err; | |
245 | long ret; | |
246 | off_t offset; | |
247 | ||
008e2515 MSL |
248 | err = ioctl(pair->channel, RELAY_GET_SB, &consumed_old); |
249 | printf_verbose("cookie : %u\n", consumed_old); | |
8ba26eae | 250 | if (err != 0) { |
008e2515 MSL |
251 | ret = errno; |
252 | perror("Reserving sub buffer failed (everything is normal, it is due to concurrency)"); | |
253 | goto get_error; | |
254 | } | |
255 | ||
256 | err = ioctl(pair->channel, RELAY_GET_SB_SIZE, &len); | |
8ba26eae | 257 | if (err != 0) { |
008e2515 MSL |
258 | ret = errno; |
259 | perror("Getting sub-buffer len failed."); | |
260 | goto get_error; | |
261 | } | |
262 | ||
8ba26eae MD |
263 | if (instance->callbacks->on_read_subbuffer) |
264 | ret = instance->callbacks->on_read_subbuffer( | |
265 | instance->callbacks, pair, len); | |
008e2515 MSL |
266 | |
267 | write_error: | |
268 | ret = 0; | |
269 | err = ioctl(pair->channel, RELAY_PUT_SB, &consumed_old); | |
8ba26eae | 270 | if (err != 0) { |
008e2515 | 271 | ret = errno; |
8ba26eae | 272 | if (errno == EFAULT) { |
008e2515 | 273 | perror("Error in unreserving sub buffer\n"); |
8ba26eae | 274 | } else if (errno == EIO) { |
008e2515 MSL |
275 | /* Should never happen with newer LTTng versions */ |
276 | perror("Reader has been pushed by the writer, last sub-buffer corrupted."); | |
277 | } | |
278 | goto get_error; | |
279 | } | |
280 | ||
281 | get_error: | |
282 | return ret; | |
283 | } | |
284 | ||
285 | ||
d6d516b7 | 286 | int map_channels(struct liblttd_instance *instance, int idx_begin, int idx_end) |
008e2515 MSL |
287 | { |
288 | int i,j; | |
289 | int ret=0; | |
290 | ||
8ba26eae | 291 | if (instance->fd_pairs.num_pairs <= 0) { |
008e2515 MSL |
292 | printf("No channel to read\n"); |
293 | goto end; | |
294 | } | |
295 | ||
296 | /* Get the subbuf sizes and number */ | |
297 | ||
298 | for(i=idx_begin;i<idx_end;i++) { | |
d6d516b7 | 299 | struct fd_pair *pair = &instance->fd_pairs.pair[i]; |
008e2515 MSL |
300 | |
301 | ret = ioctl(pair->channel, RELAY_GET_N_SB, &pair->n_sb); | |
8ba26eae | 302 | if (ret != 0) { |
008e2515 MSL |
303 | perror("Error in getting the number of sub-buffers"); |
304 | goto end; | |
305 | } | |
306 | ret = ioctl(pair->channel, RELAY_GET_MAX_SB_SIZE, | |
307 | &pair->max_sb_size); | |
8ba26eae | 308 | if (ret != 0) { |
008e2515 MSL |
309 | perror("Error in getting the max sub-buffer size"); |
310 | goto end; | |
311 | } | |
312 | ret = pthread_mutex_init(&pair->mutex, NULL); /* Fast mutex */ | |
8ba26eae | 313 | if (ret != 0) { |
008e2515 MSL |
314 | perror("Error in mutex init"); |
315 | goto end; | |
316 | } | |
317 | } | |
318 | ||
319 | end: | |
320 | return ret; | |
321 | } | |
322 | ||
d6d516b7 | 323 | int unmap_channels(struct liblttd_instance *instance) |
008e2515 MSL |
324 | { |
325 | int j; | |
326 | int ret=0; | |
327 | ||
328 | /* Munmap each FD */ | |
d6d516b7 MSL |
329 | for(j=0;j<instance->fd_pairs.num_pairs;j++) { |
330 | struct fd_pair *pair = &instance->fd_pairs.pair[j]; | |
008e2515 MSL |
331 | int err_ret; |
332 | ||
333 | err_ret = pthread_mutex_destroy(&pair->mutex); | |
8ba26eae | 334 | if (err_ret != 0) { |
008e2515 MSL |
335 | perror("Error in mutex destroy"); |
336 | } | |
337 | ret |= err_ret; | |
338 | } | |
339 | ||
340 | return ret; | |
341 | } | |
342 | ||
343 | #ifdef HAS_INOTIFY | |
344 | /* Inotify event arrived. | |
345 | * | |
346 | * Only support add file for now. | |
347 | */ | |
d6d516b7 | 348 | int read_inotify(struct liblttd_instance *instance) |
008e2515 MSL |
349 | { |
350 | char buf[sizeof(struct inotify_event) + PATH_MAX]; | |
351 | char path_channel[PATH_MAX]; | |
352 | ssize_t len; | |
353 | struct inotify_event *ievent; | |
354 | size_t offset; | |
355 | unsigned int i; | |
356 | int ret; | |
357 | int old_num; | |
358 | ||
359 | offset = 0; | |
d6d516b7 | 360 | len = read(instance->inotify_fd, buf, sizeof(struct inotify_event) + PATH_MAX); |
8ba26eae | 361 | if (len < 0) { |
008e2515 | 362 | |
8ba26eae | 363 | if (errno == EAGAIN) |
008e2515 MSL |
364 | return 0; /* another thread got the data before us */ |
365 | ||
366 | printf("Error in read from inotify FD %s.\n", strerror(len)); | |
367 | return -1; | |
368 | } | |
369 | while(offset < len) { | |
370 | ievent = (struct inotify_event *)&(buf[offset]); | |
d6d516b7 | 371 | for(i=0; i<instance->inotify_watch_array.num; i++) { |
8ba26eae | 372 | if (instance->inotify_watch_array.elem[i].wd == ievent->wd && |
008e2515 MSL |
373 | ievent->mask == IN_CREATE) { |
374 | printf_verbose( | |
375 | "inotify wd %u event mask : %u for %s%s\n", | |
376 | ievent->wd, ievent->mask, | |
d6d516b7 | 377 | instance->inotify_watch_array.elem[i].path_channel, |
008e2515 | 378 | ievent->name); |
d6d516b7 MSL |
379 | old_num = instance->fd_pairs.num_pairs; |
380 | strcpy(path_channel, instance->inotify_watch_array.elem[i].path_channel); | |
008e2515 | 381 | strcat(path_channel, ievent->name); |
8ba26eae | 382 | if (ret = open_buffer_file(instance, ievent->name, path_channel, |
d6d516b7 MSL |
383 | path_channel + (instance->inotify_watch_array.elem[i].base_path_channel - |
384 | instance->inotify_watch_array.elem[i].path_channel))) { | |
008e2515 MSL |
385 | printf("Error opening buffer file\n"); |
386 | return -1; | |
387 | } | |
8ba26eae | 388 | if (ret = map_channels(instance, old_num, instance->fd_pairs.num_pairs)) { |
008e2515 MSL |
389 | printf("Error mapping channel\n"); |
390 | return -1; | |
391 | } | |
392 | ||
393 | } | |
394 | } | |
395 | offset += sizeof(*ievent) + ievent->len; | |
396 | } | |
397 | } | |
398 | #endif //HAS_INOTIFY | |
399 | ||
8ba26eae MD |
400 | /* |
401 | * read_channels | |
008e2515 MSL |
402 | * |
403 | * Thread worker. | |
404 | * | |
405 | * Read the debugfs channels and write them in the paired tracefiles. | |
406 | * | |
407 | * @fd_pairs : paired channels and trace files. | |
408 | * | |
409 | * returns 0 on success, -1 on error. | |
410 | * | |
411 | * Note that the high priority polled channels are consumed first. We then poll | |
412 | * again to see if these channels are still in priority. Only when no | |
413 | * high priority channel is left, we start reading low priority channels. | |
414 | * | |
415 | * Note that a channel is considered high priority when the buffer is almost | |
416 | * full. | |
417 | */ | |
418 | ||
d6d516b7 | 419 | int read_channels(struct liblttd_instance *instance, unsigned long thread_num) |
008e2515 MSL |
420 | { |
421 | struct pollfd *pollfd = NULL; | |
422 | int num_pollfd; | |
423 | int i,j; | |
424 | int num_rdy, num_hup; | |
425 | int high_prio; | |
426 | int ret = 0; | |
427 | int inotify_fds; | |
428 | unsigned int old_num; | |
429 | ||
430 | #ifdef HAS_INOTIFY | |
431 | inotify_fds = 1; | |
432 | #else | |
433 | inotify_fds = 0; | |
434 | #endif | |
435 | ||
d6d516b7 | 436 | pthread_rwlock_rdlock(&instance->fd_pairs_lock); |
008e2515 MSL |
437 | |
438 | /* Start polling the FD. Keep one fd for inotify */ | |
d6d516b7 | 439 | pollfd = malloc((inotify_fds + instance->fd_pairs.num_pairs) * sizeof(struct pollfd)); |
008e2515 MSL |
440 | |
441 | #ifdef HAS_INOTIFY | |
d6d516b7 | 442 | pollfd[0].fd = instance->inotify_fd; |
008e2515 MSL |
443 | pollfd[0].events = POLLIN|POLLPRI; |
444 | #endif | |
445 | ||
d6d516b7 MSL |
446 | for(i=0;i<instance->fd_pairs.num_pairs;i++) { |
447 | pollfd[inotify_fds+i].fd = instance->fd_pairs.pair[i].channel; | |
008e2515 MSL |
448 | pollfd[inotify_fds+i].events = POLLIN|POLLPRI; |
449 | } | |
d6d516b7 | 450 | num_pollfd = inotify_fds + instance->fd_pairs.num_pairs; |
008e2515 MSL |
451 | |
452 | ||
d6d516b7 | 453 | pthread_rwlock_unlock(&instance->fd_pairs_lock); |
008e2515 MSL |
454 | |
455 | while(1) { | |
456 | high_prio = 0; | |
457 | num_hup = 0; | |
458 | #ifdef DEBUG | |
459 | printf("Press a key for next poll...\n"); | |
460 | char buf[1]; | |
461 | read(STDIN_FILENO, &buf, 1); | |
462 | printf("Next poll (polling %d fd) :\n", num_pollfd); | |
463 | #endif //DEBUG | |
464 | ||
465 | /* Have we received a signal ? */ | |
8ba26eae | 466 | if (instance->quit_program) break; |
008e2515 MSL |
467 | |
468 | num_rdy = poll(pollfd, num_pollfd, -1); | |
469 | ||
8ba26eae | 470 | if (num_rdy == -1) { |
008e2515 MSL |
471 | perror("Poll error"); |
472 | goto free_fd; | |
473 | } | |
474 | ||
475 | printf_verbose("Data received\n"); | |
476 | #ifdef HAS_INOTIFY | |
477 | switch(pollfd[0].revents) { | |
478 | case POLLERR: | |
479 | printf_verbose( | |
480 | "Error returned in polling inotify fd %d.\n", | |
481 | pollfd[0].fd); | |
482 | break; | |
483 | case POLLHUP: | |
484 | printf_verbose( | |
485 | "Polling inotify fd %d tells it has hung up.\n", | |
486 | pollfd[0].fd); | |
487 | break; | |
488 | case POLLNVAL: | |
489 | printf_verbose( | |
490 | "Polling inotify fd %d tells fd is not open.\n", | |
491 | pollfd[0].fd); | |
492 | break; | |
493 | case POLLPRI: | |
494 | case POLLIN: | |
495 | printf_verbose( | |
496 | "Polling inotify fd %d : data ready.\n", | |
497 | pollfd[0].fd); | |
498 | ||
d6d516b7 MSL |
499 | pthread_rwlock_wrlock(&instance->fd_pairs_lock); |
500 | read_inotify(instance); | |
501 | pthread_rwlock_unlock(&instance->fd_pairs_lock); | |
008e2515 MSL |
502 | |
503 | break; | |
504 | } | |
505 | #endif | |
506 | ||
507 | for(i=inotify_fds;i<num_pollfd;i++) { | |
508 | switch(pollfd[i].revents) { | |
509 | case POLLERR: | |
510 | printf_verbose( | |
511 | "Error returned in polling fd %d.\n", | |
512 | pollfd[i].fd); | |
513 | num_hup++; | |
514 | break; | |
515 | case POLLHUP: | |
516 | printf_verbose( | |
517 | "Polling fd %d tells it has hung up.\n", | |
518 | pollfd[i].fd); | |
519 | num_hup++; | |
520 | break; | |
521 | case POLLNVAL: | |
522 | printf_verbose( | |
523 | "Polling fd %d tells fd is not open.\n", | |
524 | pollfd[i].fd); | |
525 | num_hup++; | |
526 | break; | |
527 | case POLLPRI: | |
d6d516b7 | 528 | pthread_rwlock_rdlock(&instance->fd_pairs_lock); |
8ba26eae | 529 | if (pthread_mutex_trylock(&instance->fd_pairs.pair[i-inotify_fds].mutex) == 0) { |
008e2515 MSL |
530 | printf_verbose( |
531 | "Urgent read on fd %d\n", | |
532 | pollfd[i].fd); | |
533 | /* Take care of high priority channels first. */ | |
534 | high_prio = 1; | |
535 | /* it's ok to have an unavailable sub-buffer */ | |
d6d516b7 | 536 | ret = read_subbuffer(instance, &instance->fd_pairs.pair[i-inotify_fds]); |
8ba26eae | 537 | if (ret == EAGAIN) ret = 0; |
008e2515 | 538 | |
d6d516b7 | 539 | ret = pthread_mutex_unlock(&instance->fd_pairs.pair[i-inotify_fds].mutex); |
8ba26eae | 540 | if (ret) |
008e2515 MSL |
541 | printf("Error in mutex unlock : %s\n", strerror(ret)); |
542 | } | |
d6d516b7 | 543 | pthread_rwlock_unlock(&instance->fd_pairs_lock); |
008e2515 MSL |
544 | break; |
545 | } | |
546 | } | |
547 | /* If every buffer FD has hung up, we end the read loop here */ | |
8ba26eae | 548 | if (num_hup == num_pollfd - inotify_fds) break; |
008e2515 | 549 | |
8ba26eae | 550 | if (!high_prio) { |
008e2515 MSL |
551 | for(i=inotify_fds;i<num_pollfd;i++) { |
552 | switch(pollfd[i].revents) { | |
553 | case POLLIN: | |
d6d516b7 | 554 | pthread_rwlock_rdlock(&instance->fd_pairs_lock); |
8ba26eae | 555 | if (pthread_mutex_trylock(&instance->fd_pairs.pair[i-inotify_fds].mutex) == 0) { |
008e2515 MSL |
556 | /* Take care of low priority channels. */ |
557 | printf_verbose( | |
558 | "Normal read on fd %d\n", | |
559 | pollfd[i].fd); | |
560 | /* it's ok to have an unavailable subbuffer */ | |
d6d516b7 | 561 | ret = read_subbuffer(instance, &instance->fd_pairs.pair[i-inotify_fds]); |
8ba26eae | 562 | if (ret == EAGAIN) ret = 0; |
008e2515 | 563 | |
d6d516b7 | 564 | ret = pthread_mutex_unlock(&instance->fd_pairs.pair[i-inotify_fds].mutex); |
8ba26eae | 565 | if (ret) |
008e2515 MSL |
566 | printf("Error in mutex unlock : %s\n", strerror(ret)); |
567 | } | |
d6d516b7 | 568 | pthread_rwlock_unlock(&instance->fd_pairs_lock); |
008e2515 MSL |
569 | break; |
570 | } | |
571 | } | |
572 | } | |
573 | ||
574 | /* Update pollfd array if an entry was added to fd_pairs */ | |
d6d516b7 | 575 | pthread_rwlock_rdlock(&instance->fd_pairs_lock); |
8ba26eae | 576 | if ((inotify_fds + instance->fd_pairs.num_pairs) != num_pollfd) { |
008e2515 | 577 | pollfd = realloc(pollfd, |
d6d516b7 MSL |
578 | (inotify_fds + instance->fd_pairs.num_pairs) * sizeof(struct pollfd)); |
579 | for(i=num_pollfd-inotify_fds;i<instance->fd_pairs.num_pairs;i++) { | |
580 | pollfd[inotify_fds+i].fd = instance->fd_pairs.pair[i].channel; | |
008e2515 MSL |
581 | pollfd[inotify_fds+i].events = POLLIN|POLLPRI; |
582 | } | |
d6d516b7 | 583 | num_pollfd = instance->fd_pairs.num_pairs + inotify_fds; |
008e2515 | 584 | } |
d6d516b7 | 585 | pthread_rwlock_unlock(&instance->fd_pairs_lock); |
008e2515 MSL |
586 | |
587 | /* NB: If the fd_pairs structure is updated by another thread from this | |
588 | * point forward, the current thread will wait in the poll without | |
589 | * monitoring the new channel. However, this thread will add the | |
590 | * new channel on next poll (and this should not take too much time | |
591 | * on a loaded system). | |
592 | * | |
593 | * This event is quite unlikely and can only occur if a CPU is | |
594 | * hot-plugged while multple lttd threads are running. | |
595 | */ | |
596 | } | |
597 | ||
598 | free_fd: | |
599 | free(pollfd); | |
600 | ||
601 | end: | |
602 | return ret; | |
603 | } | |
604 | ||
605 | ||
d6d516b7 | 606 | void close_channel_trace_pairs(struct liblttd_instance *instance) |
008e2515 MSL |
607 | { |
608 | int i; | |
609 | int ret; | |
610 | ||
d6d516b7 MSL |
611 | for(i=0;i<instance->fd_pairs.num_pairs;i++) { |
612 | ret = close(instance->fd_pairs.pair[i].channel); | |
8ba26eae MD |
613 | if (ret == -1) perror("Close error on channel"); |
614 | if (instance->callbacks->on_close_channel) { | |
d6d516b7 MSL |
615 | ret = instance->callbacks->on_close_channel( |
616 | instance->callbacks, &instance->fd_pairs.pair[i]); | |
8ba26eae | 617 | if (ret != 0) perror("Error on close channel callback"); |
008e2515 MSL |
618 | } |
619 | } | |
d6d516b7 MSL |
620 | free(instance->fd_pairs.pair); |
621 | free(instance->inotify_watch_array.elem); | |
008e2515 MSL |
622 | } |
623 | ||
624 | /* Thread worker */ | |
625 | void * thread_main(void *arg) | |
626 | { | |
627 | long ret = 0; | |
d6d516b7 | 628 | struct liblttd_thread_data *thread_data = (struct liblttd_thread_data*) arg; |
008e2515 | 629 | |
8ba26eae | 630 | if (thread_data->instance->callbacks->on_new_thread) |
d6d516b7 MSL |
631 | ret = thread_data->instance->callbacks->on_new_thread( |
632 | thread_data->instance->callbacks, thread_data->thread_num); | |
008e2515 MSL |
633 | |
634 | if (ret < 0) { | |
635 | return (void*)ret; | |
636 | } | |
d6d516b7 MSL |
637 | ret = read_channels(thread_data->instance, thread_data->thread_num); |
638 | ||
8ba26eae | 639 | if (thread_data->instance->callbacks->on_close_thread) |
d6d516b7 MSL |
640 | thread_data->instance->callbacks->on_close_thread( |
641 | thread_data->instance->callbacks, thread_data->thread_num); | |
008e2515 | 642 | |
d6d516b7 | 643 | free(thread_data); |
008e2515 MSL |
644 | |
645 | return (void*)ret; | |
646 | } | |
647 | ||
d6d516b7 | 648 | int channels_init(struct liblttd_instance *instance) |
008e2515 MSL |
649 | { |
650 | int ret = 0; | |
651 | ||
d6d516b7 MSL |
652 | instance->inotify_fd = inotify_init(); |
653 | fcntl(instance->inotify_fd, F_SETFL, O_NONBLOCK); | |
008e2515 | 654 | |
8ba26eae | 655 | if (ret = open_channel_trace_pairs(instance, instance->channel_name, |
d6d516b7 MSL |
656 | instance->channel_name + |
657 | strlen(instance->channel_name))) | |
008e2515 | 658 | goto close_channel; |
d6d516b7 | 659 | if (instance->fd_pairs.num_pairs == 0) { |
008e2515 MSL |
660 | printf("No channel available for reading, exiting\n"); |
661 | ret = -ENOENT; | |
662 | goto close_channel; | |
663 | } | |
d6d516b7 | 664 | |
8ba26eae | 665 | if (ret = map_channels(instance, 0, instance->fd_pairs.num_pairs)) |
008e2515 MSL |
666 | goto close_channel; |
667 | return 0; | |
668 | ||
669 | close_channel: | |
d6d516b7 | 670 | close_channel_trace_pairs(instance); |
8ba26eae | 671 | if (instance->inotify_fd >= 0) |
d6d516b7 | 672 | close(instance->inotify_fd); |
008e2515 MSL |
673 | return ret; |
674 | } | |
675 | ||
d6d516b7 MSL |
676 | int delete_instance(struct liblttd_instance *instance) |
677 | { | |
678 | pthread_rwlock_destroy(&instance->fd_pairs_lock); | |
679 | free(instance); | |
680 | return 0; | |
681 | } | |
682 | ||
683 | int liblttd_start_instance(struct liblttd_instance *instance) | |
684 | { | |
008e2515 MSL |
685 | int ret = 0; |
686 | pthread_t *tids; | |
687 | unsigned long i; | |
688 | void *tret; | |
689 | ||
8ba26eae | 690 | if (!instance) |
d6d516b7 | 691 | return -EINVAL; |
008e2515 | 692 | |
8ba26eae | 693 | if (ret = channels_init(instance)) |
008e2515 MSL |
694 | return ret; |
695 | ||
d6d516b7 MSL |
696 | tids = malloc(sizeof(pthread_t) * instance->num_threads); |
697 | for(i=0; i<instance->num_threads; i++) { | |
698 | struct liblttd_thread_data *thread_data = | |
699 | malloc(sizeof(struct liblttd_thread_data)); | |
700 | thread_data->thread_num = i; | |
701 | thread_data->instance = instance; | |
008e2515 | 702 | |
d6d516b7 | 703 | ret = pthread_create(&tids[i], NULL, thread_main, thread_data); |
8ba26eae | 704 | if (ret) { |
008e2515 MSL |
705 | perror("Error creating thread"); |
706 | break; | |
707 | } | |
708 | } | |
709 | ||
d6d516b7 | 710 | for(i=0; i<instance->num_threads; i++) { |
008e2515 | 711 | ret = pthread_join(tids[i], &tret); |
8ba26eae | 712 | if (ret) { |
008e2515 MSL |
713 | perror("Error joining thread"); |
714 | break; | |
715 | } | |
8ba26eae | 716 | if ((long)tret != 0) { |
008e2515 MSL |
717 | printf("Error %s occured in thread %ld\n", |
718 | strerror((long)tret), i); | |
719 | } | |
720 | } | |
721 | ||
722 | free(tids); | |
d6d516b7 MSL |
723 | ret = unmap_channels(instance); |
724 | close_channel_trace_pairs(instance); | |
8ba26eae | 725 | if (instance->inotify_fd >= 0) |
d6d516b7 | 726 | close(instance->inotify_fd); |
008e2515 | 727 | |
8ba26eae | 728 | if (instance->callbacks->on_trace_end) |
d9cbca27 | 729 | instance->callbacks->on_trace_end(instance); |
d6d516b7 MSL |
730 | |
731 | delete_instance(instance); | |
008e2515 MSL |
732 | |
733 | return ret; | |
734 | } | |
735 | ||
d6d516b7 MSL |
736 | struct liblttd_instance * liblttd_new_instance( |
737 | struct liblttd_callbacks *callbacks, char *channel_path, | |
738 | unsigned long n_threads, int flight_only, int normal_only, int verbose) | |
739 | { | |
740 | struct liblttd_instance * instance; | |
8ba26eae MD |
741 | |
742 | if (!channel_path || !callbacks) | |
743 | return NULL; | |
744 | if (n_threads == 0) | |
745 | n_threads = 1; | |
746 | if (flight_only && normal_only) | |
747 | return NULL; | |
d6d516b7 MSL |
748 | |
749 | instance = malloc(sizeof(struct liblttd_instance)); | |
8ba26eae MD |
750 | if (!instance) |
751 | return NULL; | |
d6d516b7 MSL |
752 | |
753 | instance->callbacks = callbacks; | |
754 | ||
755 | instance->inotify_fd = -1; | |
756 | ||
757 | instance->fd_pairs.pair = NULL; | |
758 | instance->fd_pairs.num_pairs = 0; | |
759 | ||
760 | instance->inotify_watch_array.elem = NULL; | |
761 | instance->inotify_watch_array.num = 0; | |
762 | ||
763 | pthread_rwlock_init(&instance->fd_pairs_lock, NULL); | |
764 | ||
765 | strncpy(instance->channel_name, channel_path, PATH_MAX -1); | |
766 | instance->num_threads = n_threads; | |
767 | instance->dump_flight_only = flight_only; | |
768 | instance->dump_normal_only = normal_only; | |
769 | instance->verbose_mode = verbose; | |
f51abe5f | 770 | instance->quit_program = 0; |
d6d516b7 MSL |
771 | |
772 | return instance; | |
773 | } | |
774 | ||
775 | int liblttd_stop_instance(struct liblttd_instance *instance) | |
776 | { | |
777 | instance->quit_program = 1; | |
008e2515 MSL |
778 | return 0; |
779 | } | |
780 |