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