/** * @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 };