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