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