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