| 1 | #ifdef LTTNGTOP_MMAP_LIVE |
| 2 | |
| 3 | static |
| 4 | ssize_t read_subbuffer(struct lttng_consumer_stream *kconsumerd_fd, |
| 5 | struct lttng_consumer_local_data *ctx) |
| 6 | { |
| 7 | unsigned long len; |
| 8 | int err; |
| 9 | long ret = 0; |
| 10 | int infd = helper_get_lttng_consumer_stream_wait_fd(kconsumerd_fd); |
| 11 | |
| 12 | if (helper_get_lttng_consumer_stream_output(kconsumerd_fd) == LTTNG_EVENT_SPLICE) { |
| 13 | /* Get the next subbuffer */ |
| 14 | err = helper_kernctl_get_next_subbuf(infd); |
| 15 | if (err != 0) { |
| 16 | ret = errno; |
| 17 | perror("Reserving sub buffer failed (everything is normal, " |
| 18 | "it is due to concurrency)"); |
| 19 | goto end; |
| 20 | } |
| 21 | /* read the whole subbuffer */ |
| 22 | err = helper_kernctl_get_padded_subbuf_size(infd, &len); |
| 23 | if (err != 0) { |
| 24 | ret = errno; |
| 25 | perror("Getting sub-buffer len failed."); |
| 26 | goto end; |
| 27 | } |
| 28 | |
| 29 | /* splice the subbuffer to the tracefile */ |
| 30 | ret = helper_lttng_consumer_on_read_subbuffer_splice(ctx, kconsumerd_fd, len, 0); |
| 31 | if (ret < 0) { |
| 32 | /* |
| 33 | * display the error but continue processing to try |
| 34 | * to release the subbuffer |
| 35 | */ |
| 36 | fprintf(stderr,"Error splicing to tracefile\n"); |
| 37 | } |
| 38 | err = helper_kernctl_put_next_subbuf(infd); |
| 39 | if (err != 0) { |
| 40 | ret = errno; |
| 41 | perror("Reserving sub buffer failed (everything is normal, " |
| 42 | "it is due to concurrency)"); |
| 43 | goto end; |
| 44 | } |
| 45 | sem_post(&metadata_available); |
| 46 | } |
| 47 | |
| 48 | end: |
| 49 | return 0; |
| 50 | } |
| 51 | |
| 52 | static |
| 53 | int on_update_fd(int key, uint32_t state) |
| 54 | { |
| 55 | /* let the lib handle the metadata FD */ |
| 56 | if (key == sessiond_metadata) |
| 57 | return 0; |
| 58 | return 1; |
| 59 | } |
| 60 | |
| 61 | static |
| 62 | int on_recv_fd(struct lttng_consumer_stream *kconsumerd_fd) |
| 63 | { |
| 64 | int ret; |
| 65 | struct bt_mmap_stream *new_mmap_stream; |
| 66 | |
| 67 | /* Opening the tracefile in write mode */ |
| 68 | if (helper_get_lttng_consumer_stream_path_name(kconsumerd_fd) != NULL) { |
| 69 | ret = open(helper_get_lttng_consumer_stream_path_name(kconsumerd_fd), |
| 70 | O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU|S_IRWXG|S_IRWXO); |
| 71 | if (ret < 0) { |
| 72 | perror("open"); |
| 73 | goto end; |
| 74 | } |
| 75 | helper_set_lttng_consumer_stream_out_fd(kconsumerd_fd, ret); |
| 76 | } |
| 77 | |
| 78 | if (helper_get_lttng_consumer_stream_output(kconsumerd_fd) == LTTNG_EVENT_MMAP) { |
| 79 | new_mmap_stream = malloc(sizeof(struct bt_mmap_stream)); |
| 80 | new_mmap_stream->fd = helper_get_lttng_consumer_stream_wait_fd( |
| 81 | kconsumerd_fd); |
| 82 | bt_list_add(&new_mmap_stream->list, &mmap_list.head); |
| 83 | |
| 84 | g_ptr_array_add(lttng_consumer_stream_array, kconsumerd_fd); |
| 85 | /* keep mmap FDs internally */ |
| 86 | ret = 1; |
| 87 | } else { |
| 88 | consumerd_metadata = helper_get_lttng_consumer_stream_wait_fd(kconsumerd_fd); |
| 89 | sessiond_metadata = helper_get_lttng_consumer_stream_key(kconsumerd_fd); |
| 90 | ret = 0; |
| 91 | } |
| 92 | |
| 93 | reload_trace = 1; |
| 94 | |
| 95 | end: |
| 96 | return ret; |
| 97 | } |
| 98 | |
| 99 | static |
| 100 | void live_consume(struct bt_context **bt_ctx) |
| 101 | { |
| 102 | int ret; |
| 103 | FILE *metadata_fp; |
| 104 | |
| 105 | sem_wait(&metadata_available); |
| 106 | if (access("/tmp/livesession/kernel/metadata", F_OK) != 0) { |
| 107 | fprintf(stderr,"no metadata\n"); |
| 108 | goto end; |
| 109 | } |
| 110 | metadata_fp = fopen("/tmp/livesession/kernel/metadata", "r"); |
| 111 | |
| 112 | *bt_ctx = bt_context_create(); |
| 113 | ret = bt_context_add_trace(*bt_ctx, NULL, "ctf", |
| 114 | lttngtop_ctf_packet_seek, &mmap_list, metadata_fp); |
| 115 | if (ret < 0) { |
| 116 | printf("Error adding trace\n"); |
| 117 | goto end; |
| 118 | } |
| 119 | |
| 120 | end: |
| 121 | return; |
| 122 | } |
| 123 | |
| 124 | static |
| 125 | int setup_consumer(char *command_sock_path, pthread_t *threads, |
| 126 | struct lttng_consumer_local_data *ctx) |
| 127 | { |
| 128 | int ret = 0; |
| 129 | |
| 130 | ctx = helper_lttng_consumer_create(HELPER_LTTNG_CONSUMER_KERNEL, |
| 131 | read_subbuffer, NULL, on_recv_fd, on_update_fd); |
| 132 | if (!ctx) |
| 133 | goto end; |
| 134 | |
| 135 | unlink(command_sock_path); |
| 136 | helper_lttng_consumer_set_command_sock_path(ctx, command_sock_path); |
| 137 | helper_lttng_consumer_init(); |
| 138 | |
| 139 | /* Create the thread to manage the receive of fd */ |
| 140 | ret = pthread_create(&threads[0], NULL, helper_lttng_consumer_thread_sessiond_poll, |
| 141 | (void *) ctx); |
| 142 | if (ret != 0) { |
| 143 | perror("pthread_create receive fd"); |
| 144 | goto end; |
| 145 | } |
| 146 | /* Create thread to manage the polling/writing of traces */ |
| 147 | ret = pthread_create(&threads[1], NULL, helper_lttng_consumer_thread_metadata_poll, |
| 148 | (void *) ctx); |
| 149 | if (ret != 0) { |
| 150 | perror("pthread_create poll fd"); |
| 151 | goto end; |
| 152 | } |
| 153 | |
| 154 | end: |
| 155 | return ret; |
| 156 | } |
| 157 | |
| 158 | static |
| 159 | int enable_kprobes(struct lttng_handle *handle, char *channel_name) |
| 160 | { |
| 161 | struct lttng_event ev; |
| 162 | struct kprobes *kprobe; |
| 163 | int ret = 0; |
| 164 | int i; |
| 165 | |
| 166 | for (i = 0; i < lttngtop.kprobes_table->len; i++) { |
| 167 | kprobe = g_ptr_array_index(lttngtop.kprobes_table, i); |
| 168 | |
| 169 | memset(&ev, '\0', sizeof(struct lttng_event)); |
| 170 | ev.type = LTTNG_EVENT_PROBE; |
| 171 | if (kprobe->symbol_name) |
| 172 | sprintf(ev.attr.probe.symbol_name, "%s", kprobe->symbol_name); |
| 173 | sprintf(ev.name, "%s", kprobe->probe_name); |
| 174 | ev.attr.probe.addr = kprobe->probe_addr; |
| 175 | ev.attr.probe.offset = kprobe->probe_offset; |
| 176 | if ((ret = lttng_enable_event(handle, &ev, channel_name)) < 0) { |
| 177 | fprintf(stderr,"error enabling kprobes : %s\n", |
| 178 | helper_lttcomm_get_readable_code(ret)); |
| 179 | goto end; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | end: |
| 184 | return ret; |
| 185 | } |
| 186 | |
| 187 | static |
| 188 | int setup_live_tracing() |
| 189 | { |
| 190 | struct lttng_domain dom; |
| 191 | struct lttng_channel chan; |
| 192 | char *channel_name = "mmapchan"; |
| 193 | struct lttng_event ev; |
| 194 | int ret = 0; |
| 195 | char *command_sock_path = "/tmp/consumerd_sock"; |
| 196 | static pthread_t threads[2]; /* recv_fd, poll */ |
| 197 | struct lttng_event_context kctxpid, kctxcomm, kctxppid, kctxtid, |
| 198 | kctxperf1, kctxperf2; |
| 199 | |
| 200 | struct lttng_handle *handle; |
| 201 | |
| 202 | BT_INIT_LIST_HEAD(&mmap_list.head); |
| 203 | |
| 204 | lttng_consumer_stream_array = g_ptr_array_new(); |
| 205 | |
| 206 | if ((ret = setup_consumer(command_sock_path, threads, ctx)) < 0) { |
| 207 | fprintf(stderr,"error setting up consumer\n"); |
| 208 | goto error; |
| 209 | } |
| 210 | |
| 211 | available_snapshots = g_ptr_array_new(); |
| 212 | |
| 213 | /* setup the session */ |
| 214 | dom.type = LTTNG_DOMAIN_KERNEL; |
| 215 | |
| 216 | ret = unlink("/tmp/livesession"); |
| 217 | |
| 218 | lttng_destroy_session("test"); |
| 219 | if ((ret = lttng_create_session("test", "/tmp/livesession")) < 0) { |
| 220 | fprintf(stderr,"error creating the session : %s\n", |
| 221 | helper_lttcomm_get_readable_code(ret)); |
| 222 | goto error; |
| 223 | } |
| 224 | |
| 225 | if ((handle = lttng_create_handle("test", &dom)) == NULL) { |
| 226 | fprintf(stderr,"error creating handle\n"); |
| 227 | goto error_session; |
| 228 | } |
| 229 | |
| 230 | /* |
| 231 | * FIXME : need to let the |
| 232 | * helper_lttng_consumer_thread_receive_fds create the |
| 233 | * socket. |
| 234 | * Cleaner solution ? |
| 235 | */ |
| 236 | while (access(command_sock_path, F_OK)) { |
| 237 | sleep(0.1); |
| 238 | } |
| 239 | |
| 240 | if ((ret = lttng_register_consumer(handle, command_sock_path)) < 0) { |
| 241 | fprintf(stderr,"error registering consumer : %s\n", |
| 242 | helper_lttcomm_get_readable_code(ret)); |
| 243 | goto error_session; |
| 244 | } |
| 245 | |
| 246 | strcpy(chan.name, channel_name); |
| 247 | chan.attr.overwrite = 0; |
| 248 | if (opt_tid && opt_textdump) { |
| 249 | chan.attr.subbuf_size = 32768; |
| 250 | chan.attr.num_subbuf = 8; |
| 251 | } else { |
| 252 | //chan.attr.subbuf_size = 1048576; /* 1MB */ |
| 253 | chan.attr.subbuf_size = 2097152; /* 1MB */ |
| 254 | chan.attr.num_subbuf = 4; |
| 255 | } |
| 256 | chan.attr.switch_timer_interval = 0; |
| 257 | chan.attr.read_timer_interval = 200; |
| 258 | chan.attr.output = LTTNG_EVENT_MMAP; |
| 259 | |
| 260 | if ((ret = lttng_enable_channel(handle, &chan)) < 0) { |
| 261 | fprintf(stderr,"error creating channel : %s\n", |
| 262 | helper_lttcomm_get_readable_code(ret)); |
| 263 | goto error_session; |
| 264 | } |
| 265 | |
| 266 | memset(&ev, '\0', sizeof(struct lttng_event)); |
| 267 | ev.type = LTTNG_EVENT_TRACEPOINT; |
| 268 | sprintf(ev.name, "sched_switch"); |
| 269 | if ((ret = lttng_enable_event(handle, &ev, channel_name)) < 0) { |
| 270 | fprintf(stderr,"error enabling event %s : %s\n", |
| 271 | ev.name, |
| 272 | helper_lttcomm_get_readable_code(ret)); |
| 273 | goto error_session; |
| 274 | } |
| 275 | sprintf(ev.name, "sched_process_free"); |
| 276 | if ((ret = lttng_enable_event(handle, &ev, channel_name)) < 0) { |
| 277 | fprintf(stderr,"error enabling event %s : %s\n", |
| 278 | ev.name, |
| 279 | helper_lttcomm_get_readable_code(ret)); |
| 280 | goto error_session; |
| 281 | } |
| 282 | sprintf(ev.name, "lttng_statedump_process_state"); |
| 283 | if ((ret = lttng_enable_event(handle, &ev, channel_name)) < 0) { |
| 284 | fprintf(stderr,"error enabling event %s : %s\n", |
| 285 | ev.name, |
| 286 | helper_lttcomm_get_readable_code(ret)); |
| 287 | goto error_session; |
| 288 | } |
| 289 | sprintf(ev.name, "lttng_statedump_file_descriptor"); |
| 290 | if ((ret = lttng_enable_event(handle, &ev, channel_name)) < 0) { |
| 291 | fprintf(stderr,"error enabling event %s : %s\n", |
| 292 | ev.name, |
| 293 | helper_lttcomm_get_readable_code(ret)); |
| 294 | goto error_session; |
| 295 | } |
| 296 | |
| 297 | memset(&ev, '\0', sizeof(struct lttng_event)); |
| 298 | ev.type = LTTNG_EVENT_SYSCALL; |
| 299 | if ((ret = lttng_enable_event(handle, &ev, channel_name)) < 0) { |
| 300 | fprintf(stderr,"error enabling syscalls : %s\n", |
| 301 | helper_lttcomm_get_readable_code(ret)); |
| 302 | goto error_session; |
| 303 | } |
| 304 | |
| 305 | if (lttngtop.kprobes_table) { |
| 306 | ret = enable_kprobes(handle, channel_name); |
| 307 | if (ret < 0) { |
| 308 | goto error_session; |
| 309 | } |
| 310 | } |
| 311 | |
| 312 | kctxperf1.ctx = LTTNG_EVENT_CONTEXT_PERF_COUNTER; |
| 313 | kctxperf1.u.perf_counter.type = 0; /* PERF_TYPE_HARDWARE */ |
| 314 | kctxperf1.u.perf_counter.config = 5; /* PERF_COUNT_HW_BRANCH_MISSES */ |
| 315 | sprintf(kctxperf1.u.perf_counter.name, "perf_branch_misses"); |
| 316 | ret = lttng_add_context(handle, &kctxperf1, NULL, NULL); |
| 317 | if (ret < 0) { |
| 318 | fprintf(stderr, "error enabling context %s\n", |
| 319 | kctxtid.u.perf_counter.name); |
| 320 | } |
| 321 | |
| 322 | kctxperf2.ctx = LTTNG_EVENT_CONTEXT_PERF_COUNTER; |
| 323 | kctxperf2.u.perf_counter.type = 1; /* PERF_TYPE_SOFTWARE */ |
| 324 | kctxperf2.u.perf_counter.config = 6; /* PERF_COUNT_SW_PAGE_FAULTS_MAJ */ |
| 325 | sprintf(kctxperf2.u.perf_counter.name, "perf_major_faults"); |
| 326 | ret = lttng_add_context(handle, &kctxperf2, NULL, NULL); |
| 327 | if (ret < 0) { |
| 328 | fprintf(stderr, "error enabling context %s\n", |
| 329 | kctxtid.u.perf_counter.name); |
| 330 | } |
| 331 | |
| 332 | kctxpid.ctx = LTTNG_EVENT_CONTEXT_PID; |
| 333 | lttng_add_context(handle, &kctxpid, NULL, NULL); |
| 334 | kctxtid.ctx = LTTNG_EVENT_CONTEXT_TID; |
| 335 | lttng_add_context(handle, &kctxtid, NULL, NULL); |
| 336 | kctxppid.ctx = LTTNG_EVENT_CONTEXT_PPID; |
| 337 | lttng_add_context(handle, &kctxppid, NULL, NULL); |
| 338 | kctxcomm.ctx = LTTNG_EVENT_CONTEXT_PROCNAME; |
| 339 | lttng_add_context(handle, &kctxcomm, NULL, NULL); |
| 340 | kctxpid.ctx = LTTNG_EVENT_CONTEXT_VPID; |
| 341 | lttng_add_context(handle, &kctxpid, NULL, NULL); |
| 342 | kctxtid.ctx = LTTNG_EVENT_CONTEXT_VTID; |
| 343 | lttng_add_context(handle, &kctxtid, NULL, NULL); |
| 344 | kctxtid.ctx = LTTNG_EVENT_CONTEXT_HOSTNAME; |
| 345 | lttng_add_context(handle, &kctxtid, NULL, NULL); |
| 346 | |
| 347 | |
| 348 | if ((ret = lttng_start_tracing("test")) < 0) { |
| 349 | fprintf(stderr,"error starting tracing : %s\n", |
| 350 | helper_lttcomm_get_readable_code(ret)); |
| 351 | goto error_session; |
| 352 | } |
| 353 | |
| 354 | helper_kernctl_buffer_flush(consumerd_metadata); |
| 355 | |
| 356 | /* block until metadata is ready */ |
| 357 | sem_init(&metadata_available, 0, 0); |
| 358 | |
| 359 | return 0; |
| 360 | |
| 361 | error_session: |
| 362 | lttng_destroy_session("test"); |
| 363 | error: |
| 364 | return -1; |
| 365 | } |
| 366 | |
| 367 | int mmap_live_loop(struct bt_context *bt_ctx, |
| 368 | struct bt_mmap_stream_list mmap_list) |
| 369 | { |
| 370 | struct bt_mmap_stream *mmap_info; |
| 371 | |
| 372 | ret = setup_live_tracing(); |
| 373 | if (ret < 0) { |
| 374 | goto end; |
| 375 | } |
| 376 | |
| 377 | while (!quit) { |
| 378 | reload_trace = 0; |
| 379 | live_consume(&bt_ctx); |
| 380 | ret = check_requirements(bt_ctx); |
| 381 | if (ret < 0) { |
| 382 | fprintf(stderr, "[error] some mandatory contexts were missing, exiting.\n"); |
| 383 | goto end; |
| 384 | } |
| 385 | iter_trace(bt_ctx); |
| 386 | /* |
| 387 | * FIXME : pb with cleanup in libbabeltrace |
| 388 | ret = bt_context_remove_trace(bt_ctx, 0); |
| 389 | if (ret != 0) { |
| 390 | fprintf(stderr, "error removing trace\n"); |
| 391 | goto error; |
| 392 | } |
| 393 | */ |
| 394 | if (bt_ctx) { |
| 395 | bt_context_put(bt_ctx); |
| 396 | } |
| 397 | |
| 398 | /* |
| 399 | * since we receive all FDs every time there is an |
| 400 | * update and the FD number is different every time, |
| 401 | * we don't know which one are valid. |
| 402 | * so we check if all FDs are usable with a simple |
| 403 | * ioctl call. |
| 404 | */ |
| 405 | bt_list_for_each_entry(mmap_info, &mmap_list.head, list) { |
| 406 | unsigned long mmap_len; |
| 407 | |
| 408 | ret = helper_kernctl_get_mmap_len(mmap_info->fd, &mmap_len); |
| 409 | if (ret != 0) { |
| 410 | bt_list_del(&mmap_info->list); |
| 411 | } |
| 412 | } |
| 413 | sem_post(&metadata_available); |
| 414 | } |
| 415 | |
| 416 | } |
| 417 | |
| 418 | void mmap_live_flush(struct bt_mmap_stream_list mmap_list) |
| 419 | { |
| 420 | struct bt_mmap_stream *mmap_info; |
| 421 | |
| 422 | bt_list_for_each_entry(mmap_info, &mmap_list.head, list) |
| 423 | helper_kernctl_buffer_flush(mmap_info->fd); |
| 424 | } |
| 425 | #endif /* LTTNGTOP_MMAP_LIVE */ |