| /** |
| * @file daemon/init.c |
| * Daemon set up and main loop for 2.6 |
| * |
| * @remark Copyright 2002 OProfile authors |
| * @remark Read the file COPYING |
| * |
| * @author John Levon |
| * @author Philippe Elie |
| */ |
| |
| #include "config.h" |
| |
| #include "oprofiled.h" |
| #include "opd_stats.h" |
| #include "opd_sfile.h" |
| #include "opd_kernel.h" |
| #include "opd_trans.h" |
| #include "opd_anon.h" |
| #include "opd_perfmon.h" |
| #include "opd_printf.h" |
| |
| #include "op_version.h" |
| #include "op_config.h" |
| #include "op_deviceio.h" |
| #include "op_get_time.h" |
| #include "op_libiberty.h" |
| #include "op_fileio.h" |
| |
| #include <fcntl.h> |
| #include <stdio.h> |
| #include <errno.h> |
| #include <stdlib.h> |
| |
| size_t kernel_pointer_size; |
| |
| static fd_t devfd; |
| static char * sbuf; |
| static size_t s_buf_bytesize; |
| |
| static void opd_sighup(void); |
| static void opd_alarm(void); |
| static void opd_sigterm(void); |
| |
| /** |
| * opd_open_files - open necessary files |
| * |
| * Open the device files and the log file, |
| * and mmap() the hash map. |
| */ |
| static void opd_open_files(void) |
| { |
| devfd = op_open_device("/dev/oprofile/buffer"); |
| 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); |
| } |
| |
| /* give output before re-opening stdout as the logfile */ |
| printf("Using log file " OP_LOG_FILE "\n"); |
| |
| /* 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()); |
| printf("kernel pointer size: %lu\n", |
| (unsigned long)kernel_pointer_size); |
| fflush(stdout); |
| } |
| |
| |
| /** Done writing out the samples, indicate with complete_dump file */ |
| static void complete_dump(void) |
| { |
| FILE * status_file; |
| |
| retry: |
| status_file = fopen(OP_DUMP_STATUS, "w"); |
| |
| if (!status_file && errno == EMFILE) { |
| if (sfile_lru_clear()) { |
| printf("LRU cleared but file open fails for %s.\n", |
| OP_DUMP_STATUS); |
| abort(); |
| } |
| goto retry; |
| } |
| |
| if (!status_file) { |
| perror("warning: couldn't set complete_dump: "); |
| return; |
| } |
| |
| fprintf(status_file, "1\n"); |
| fclose(status_file); |
| } |
| |
| |
| /** |
| * opd_do_samples - process a sample buffer |
| * @param opd_buf buffer to process |
| * |
| * Process a buffer of samples. |
| * |
| * If the sample could be processed correctly, it is written |
| * to the relevant sample file. |
| */ |
| static void opd_do_samples(char const * opd_buf, ssize_t count) |
| { |
| size_t num = count / kernel_pointer_size; |
| |
| opd_stats[OPD_DUMP_COUNT]++; |
| |
| verbprintf(vmisc, "Read buffer of %d entries.\n", (unsigned int)num); |
| |
| opd_process_samples(opd_buf, num); |
| |
| complete_dump(); |
| } |
| |
| |
| /** |
| * opd_do_read - enter processing loop |
| * @param buf buffer to read into |
| * @param size size of buffer |
| * |
| * Read some of a buffer from the device and process |
| * the contents. |
| */ |
| static void opd_do_read(char * buf, size_t size) |
| { |
| while (1) { |
| ssize_t count = -1; |
| |
| /* loop to handle EINTR */ |
| while (count < 0) { |
| count = op_read_device(devfd, buf, size); |
| |
| /* we can lose an alarm or a hup but |
| * we don't care. |
| */ |
| if (signal_alarm) { |
| signal_alarm = 0; |
| opd_alarm(); |
| } |
| |
| if (signal_hup) { |
| signal_hup = 0; |
| opd_sighup(); |
| } |
| |
| if (signal_term) |
| opd_sigterm(); |
| |
| if (signal_usr1) { |
| signal_usr1 = 0; |
| perfmon_start(); |
| } |
| |
| if (signal_usr2) { |
| signal_usr2 = 0; |
| perfmon_stop(); |
| } |
| } |
| |
| opd_do_samples(buf, count); |
| } |
| } |
| |
| |
| /** opd_alarm - sync files and report stats */ |
| static void opd_alarm(void) |
| { |
| sfile_sync_files(); |
| opd_print_stats(); |
| alarm(60 * 10); |
| } |
| |
| |
| /** re-open files for logrotate/opcontrol --reset */ |
| static void opd_sighup(void) |
| { |
| printf("Received SIGHUP.\n"); |
| /* We just close them, and re-open them lazily as usual. */ |
| sfile_close_files(); |
| close(1); |
| close(2); |
| opd_open_logfile(); |
| } |
| |
| |
| static void clean_exit(void) |
| { |
| perfmon_exit(); |
| unlink(OP_LOCK_FILE); |
| } |
| |
| |
| static void opd_sigterm(void) |
| { |
| opd_print_stats(); |
| printf("oprofiled stopped %s", op_get_time()); |
| exit(EXIT_FAILURE); |
| } |
| |
| |
| static void opd_26_init(void) |
| { |
| size_t i; |
| size_t opd_buf_size; |
| |
| opd_create_vmlinux(vmlinux, kernel_range); |
| |
| opd_buf_size = opd_read_fs_int("/dev/oprofile/", "buffer_size", 1); |
| kernel_pointer_size = opd_read_fs_int("/dev/oprofile/", "pointer_size", 1); |
| |
| s_buf_bytesize = opd_buf_size * kernel_pointer_size; |
| |
| sbuf = xmalloc(s_buf_bytesize); |
| |
| opd_reread_module_info(); |
| |
| for (i = 0; i < OPD_MAX_STATS; i++) |
| opd_stats[i] = 0; |
| |
| perfmon_init(); |
| |
| cookie_init(); |
| sfile_init(); |
| anon_init(); |
| |
| /* must be /after/ perfmon_init() at least */ |
| if (atexit(clean_exit)) { |
| perfmon_exit(); |
| perror("oprofiled: couldn't set exit cleanup: "); |
| exit(EXIT_FAILURE); |
| } |
| } |
| |
| |
| static void opd_26_start(void) |
| { |
| opd_open_files(); |
| |
| /* simple sleep-then-process loop */ |
| opd_do_read(sbuf, s_buf_bytesize); |
| } |
| |
| |
| static void opd_26_exit(void) |
| { |
| opd_print_stats(); |
| printf("oprofiled stopped %s", op_get_time()); |
| |
| free(sbuf); |
| free(vmlinux); |
| /* FIXME: free kernel images, sfiles etc. */ |
| } |
| |
| struct oprofiled_ops opd_26_ops = { |
| .init = opd_26_init, |
| .start = opd_26_start, |
| .exit = opd_26_exit, |
| }; |