| /** |
| * @file daemon/liblegacy/init.c |
| * Daemon set up and main loop for 2.4 |
| * |
| * @remark Copyright 2002 OProfile authors |
| * @remark Read the file COPYING |
| * |
| * @author John Levon |
| * @author Philippe Elie |
| */ |
| |
| #include "config.h" |
| |
| #include "opd_proc.h" |
| #include "opd_mapping.h" |
| #include "opd_24_stats.h" |
| #include "opd_sample_files.h" |
| #include "opd_image.h" |
| #include "opd_parse_proc.h" |
| #include "opd_kernel.h" |
| #include "opd_printf.h" |
| #include "oprofiled.h" |
| |
| #include "op_sample_file.h" |
| #include "op_config_24.h" |
| #include "op_interface.h" |
| #include "op_libiberty.h" |
| #include "op_deviceio.h" |
| #include "op_events.h" |
| #include "op_get_time.h" |
| #include "op_fileio.h" |
| |
| #include <stdio.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| |
| fd_t hashmapdevfd; |
| |
| int cpu_number; |
| |
| static fd_t devfd; |
| static fd_t notedevfd; |
| static struct op_buffer_head * sbuf; |
| static size_t s_buf_bytesize; |
| static struct op_note * nbuf; |
| static size_t n_buf_bytesize; |
| |
| static void opd_sighup(void); |
| static void opd_alarm(void); |
| static void opd_sigterm(void); |
| |
| |
| /** |
| * op_open_files - open necessary files |
| * |
| * Open the device files and the log file, |
| * and mmap() the hash map. |
| */ |
| static void op_open_files(void) |
| { |
| hashmapdevfd = op_open_device(op_hash_device); |
| if (hashmapdevfd == -1) { |
| perror("Failed to open hash map device"); |
| exit(EXIT_FAILURE); |
| } |
| |
| notedevfd = op_open_device(op_note_device); |
| if (notedevfd == -1) { |
| if (errno == EINVAL) |
| fprintf(stderr, "Failed to open note device. Possibly you have passed incorrect\n" |
| "parameters. Check /var/log/messages."); |
| else |
| perror("Failed to open note device"); |
| exit(EXIT_FAILURE); |
| } |
| |
| devfd = op_open_device(op_device); |
| if (devfd == -1) { |
| if (errno == EINVAL) |
| fprintf(stderr, "Failed to open device. Possibly you have passed incorrect\n" |
| "parameters. Check /var/log/messages."); |
| else |
| perror("Failed to open profile device"); |
| exit(EXIT_FAILURE); |
| } |
| |
| opd_init_hash_map(); |
| |
| /* give output before re-opening stdout as the logfile */ |
| printf("Using log file %s\n", op_log_file); |
| |
| /* set up logfile */ |
| close(0); |
| close(1); |
| |
| if (open("/dev/null", O_RDONLY) == -1) { |
| perror("oprofiled: couldn't re-open stdin as /dev/null: "); |
| exit(EXIT_FAILURE); |
| } |
| |
| opd_open_logfile(); |
| |
| printf("oprofiled started %s", op_get_time()); |
| fflush(stdout); |
| } |
| |
| |
| static void opd_do_samples(struct op_buffer_head const * buf); |
| static void opd_do_notes(struct op_note const * opd_buf, size_t count); |
| |
| /** |
| * do_shutdown - shutdown cleanly, reading as much remaining data as possible. |
| * @param buf sample buffer area |
| * @param size size of sample buffer |
| * @param nbuf note buffer area |
| * @param nsize size of note buffer |
| */ |
| static void opd_shutdown(struct op_buffer_head * buf, size_t size, struct op_note * nbuf, size_t nsize) |
| { |
| ssize_t count = -1; |
| ssize_t ncount = -1; |
| |
| /* the dump may have added no samples, so we must set |
| * non-blocking */ |
| if (fcntl(devfd, F_SETFL, fcntl(devfd, F_GETFL) | O_NONBLOCK) < 0) { |
| perror("Failed to set non-blocking read for device: "); |
| exit(EXIT_FAILURE); |
| } |
| |
| /* it's always OK to read the note device */ |
| while (ncount < 0) |
| ncount = op_read_device(notedevfd, nbuf, nsize); |
| |
| if (ncount > 0) |
| opd_do_notes(nbuf, ncount); |
| |
| /* read as much as we can until we have exhausted the data |
| * (EAGAIN is returned). |
| * |
| * This will not livelock as the profiler has been partially |
| * shut down by now. |
| */ |
| while (1) { |
| count = op_read_device(devfd, buf, size); |
| if (count < 0 && errno == EAGAIN) |
| break; |
| verbprintf(vmisc, "Shutting down, state %d\n", buf->state); |
| opd_do_samples(buf); |
| } |
| } |
| |
| |
| /** |
| * opd_do_read - enter processing loop |
| * @param buf buffer to read into |
| * @param size size of buffer |
| * @param nbuf note buffer |
| * @param nsize size of note buffer |
| * |
| * Read some of a buffer from the device and process |
| * the contents. |
| */ |
| static void opd_do_read(struct op_buffer_head * buf, size_t size, struct op_note * nbuf, size_t nsize) |
| { |
| while (1) { |
| ssize_t count = -1; |
| ssize_t ncount = -1; |
| |
| /* loop to handle EINTR */ |
| while (count < 0) |
| count = op_read_device(devfd, buf, size); |
| |
| while (ncount < 0) |
| ncount = op_read_device(notedevfd, nbuf, nsize); |
| |
| opd_do_notes(nbuf, ncount); |
| opd_do_samples(buf); |
| |
| // we can lost a signal alarm or a signal hup but we don't |
| // take care. |
| if (signal_alarm) { |
| signal_alarm = 0; |
| opd_alarm(); |
| } |
| |
| if (signal_hup) { |
| signal_hup = 0; |
| opd_sighup(); |
| } |
| |
| if (signal_term) |
| opd_sigterm(); |
| |
| /* request to stop arrived */ |
| if (buf->state == STOPPING) { |
| verbprintf(vmisc, "Shutting down by request.\n"); |
| opd_shutdown(buf, size, nbuf, nsize); |
| return; |
| } |
| } |
| } |
| |
| /** |
| * opd_do_notes - process a notes buffer |
| * @param opd_buf buffer to process |
| * @param count number of bytes in buffer |
| * |
| * Process a buffer of notes. |
| */ |
| static void opd_do_notes(struct op_note const * opd_buf, size_t count) |
| { |
| uint i; |
| struct op_note const * note; |
| |
| for (i = 0; i < count/sizeof(struct op_note); i++) { |
| note = &opd_buf[i]; |
| |
| opd_24_stats[OPD_NOTIFICATIONS]++; |
| |
| switch (note->type) { |
| case OP_MAP: |
| case OP_EXEC: |
| if (note->type == OP_EXEC) |
| opd_handle_exec(note->pid, note->tgid); |
| opd_handle_mapping(note); |
| break; |
| |
| case OP_FORK: |
| opd_handle_fork(note); |
| break; |
| |
| case OP_DROP_MODULES: |
| opd_clear_module_info(); |
| break; |
| |
| case OP_EXIT: |
| opd_handle_exit(note); |
| break; |
| |
| default: |
| fprintf(stderr, "Received unknown notification type %u\n", note->type); |
| abort(); |
| break; |
| } |
| } |
| } |
| |
| /** |
| * opd_do_samples - process a sample buffer |
| * @param opd_buf buffer to process |
| * |
| * Process a buffer of samples. |
| * The signals specified by the global variable maskset are |
| * masked. |
| * |
| * If the sample could be processed correctly, it is written |
| * to the relevant sample file. Additionally mapping and |
| * process notifications are handled here. |
| */ |
| static void opd_do_samples(struct op_buffer_head const * opd_buf) |
| { |
| uint i; |
| struct op_sample const * buffer = opd_buf->buffer; |
| |
| opd_24_stats[OPD_DUMP_COUNT]++; |
| |
| verbprintf(vmisc, "Read buffer of %d entries for cpu %d.\n", |
| (unsigned int)opd_buf->count, opd_buf->cpu_nr); |
| |
| if (separate_cpu) |
| cpu_number = opd_buf->cpu_nr; |
| for (i = 0; i < opd_buf->count; i++) { |
| verbprintf(vsamples, "%.6u: EIP: 0x%.8lx pid: %.6d\n", |
| i, buffer[i].eip, buffer[i].pid); |
| opd_put_sample(&buffer[i]); |
| } |
| } |
| |
| |
| /** |
| * opd_alarm - clean up old procs, msync, and report stats |
| */ |
| static void opd_alarm(void) |
| { |
| opd_sync_samples_files(); |
| |
| opd_age_procs(); |
| |
| opd_print_24_stats(); |
| |
| alarm(60 * 10); |
| } |
| |
| |
| /* re-open logfile for logrotate */ |
| static void opd_sighup(void) |
| { |
| printf("Received SIGHUP.\n"); |
| close(1); |
| close(2); |
| opd_open_logfile(); |
| /* We just close them, and re-open them lazily as usual. */ |
| opd_for_each_image(opd_close_image_samples_files); |
| } |
| |
| |
| static void clean_exit(void) |
| { |
| opd_cleanup_hash_name(); |
| op_free_events(); |
| unlink(op_lock_file); |
| } |
| |
| |
| static void opd_sigterm(void) |
| { |
| opd_print_24_stats(); |
| printf("oprofiled stopped %s", op_get_time()); |
| exit(EXIT_FAILURE); |
| } |
| |
| |
| static void opd_24_init(void) |
| { |
| size_t i; |
| int opd_buf_size = OP_DEFAULT_BUF_SIZE; |
| int opd_note_buf_size = OP_DEFAULT_NOTE_SIZE; |
| |
| if (!no_vmlinux) |
| opd_parse_kernel_range(kernel_range); |
| opd_buf_size = opd_read_fs_int(OP_MOUNT, "bufsize", 1); |
| opd_note_buf_size = opd_read_fs_int(OP_MOUNT, "notesize", 1); |
| |
| s_buf_bytesize = sizeof(struct op_buffer_head) + opd_buf_size * sizeof(struct op_sample); |
| |
| sbuf = xmalloc(s_buf_bytesize); |
| |
| n_buf_bytesize = opd_note_buf_size * sizeof(struct op_note); |
| nbuf = xmalloc(n_buf_bytesize); |
| |
| opd_init_images(); |
| opd_init_procs(); |
| opd_init_kernel_image(); |
| |
| for (i = 0; i < OPD_MAX_STATS; i++) |
| opd_24_stats[i] = 0; |
| |
| if (atexit(clean_exit)) { |
| perror("oprofiled: couldn't set exit cleanup: "); |
| exit(EXIT_FAILURE); |
| } |
| } |
| |
| |
| static void opd_24_start(void) |
| { |
| op_open_files(); |
| |
| /* yes, this is racey. */ |
| opd_get_ascii_procs(); |
| |
| /* simple sleep-then-process loop */ |
| opd_do_read(sbuf, s_buf_bytesize, nbuf, n_buf_bytesize); |
| } |
| |
| |
| static void opd_24_exit(void) |
| { |
| opd_print_24_stats(); |
| printf("oprofiled stopped %s", op_get_time()); |
| |
| free(sbuf); |
| free(nbuf); |
| opd_clear_module_info(); |
| opd_proc_cleanup(); |
| /* kernel/module image are not owned by a proc, we must cleanup them */ |
| opd_for_each_image(opd_delete_image); |
| } |
| |
| |
| struct oprofiled_ops opd_24_ops = { |
| .init = opd_24_init, |
| .start = opd_24_start, |
| .exit = opd_24_exit |
| }; |