/**
* @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
* @Modifications Daniel Hansel
* Modified by Aravind Menon for Xen
* These modifications are:
* Copyright (C) 2005 Hewlett-Packard Co.
*/
#include "config.h"
#include "oprofiled.h"
#include "opd_stats.h"
#include "opd_sfile.h"
#include "opd_pipe.h"
#include "opd_kernel.h"
#include "opd_trans.h"
#include "opd_anon.h"
#include "opd_perfmon.h"
#include "opd_printf.h"
#include "opd_extended.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 <limits.h>
#include <stdlib.h>
#include <sys/time.h>
#if ANDROID
#include <sys/wait.h>
#else
#include <wait.h>
#endif
#include <string.h>
size_t kernel_pointer_size;
static fd_t devfd;
static char * sbuf;
static size_t s_buf_bytesize;
extern char * session_dir;
static char start_time_str[32];
static int jit_conversion_running;
static void opd_sighup(void);
static void opd_alarm(void);
static void opd_sigterm(void);
static void opd_sigchild(void);
static void opd_do_jitdumps(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 %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();
opd_create_pipe();
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();
}
static void opd_do_jitdumps(void)
{
pid_t childpid;
int arg_num;
unsigned long long end_time = 0ULL;
struct timeval tv;
char end_time_str[32];
char opjitconv_path[PATH_MAX + 1];
char * exec_args[6];
if (jit_conversion_running)
return;
jit_conversion_running = 1;
childpid = fork();
switch (childpid) {
case -1:
perror("Error forking JIT dump process!");
break;
case 0:
gettimeofday(&tv, NULL);
end_time = tv.tv_sec;
sprintf(end_time_str, "%llu", end_time);
sprintf(opjitconv_path, "%s/%s", OP_BINDIR, "opjitconv");
arg_num = 0;
exec_args[arg_num++] = "opjitconv";
if (vmisc)
exec_args[arg_num++] = "-d";
exec_args[arg_num++] = session_dir;
exec_args[arg_num++] = start_time_str;
exec_args[arg_num++] = end_time_str;
exec_args[arg_num] = (char *) NULL;
execvp(opjitconv_path, exec_args);
fprintf(stderr, "Failed to exec %s: %s\n",
exec_args[0], strerror(errno));
/* We don't want any cleanup in the child */
_exit(EXIT_FAILURE);
default:
break;
}
}
/**
* 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)
{
opd_open_pipe();
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_child)
opd_sigchild();
if (signal_usr1) {
signal_usr1 = 0;
perfmon_start();
}
if (signal_usr2) {
signal_usr2 = 0;
perfmon_stop();
}
if (is_jitconv_requested()) {
verbprintf(vmisc, "Start opjitconv was triggered\n");
opd_do_jitdumps();
}
}
opd_do_samples(buf, count);
}
opd_close_pipe();
}
/** 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_do_jitdumps();
opd_print_stats();
printf("oprofiled stopped %s", op_get_time());
opd_ext_deinitialize();
exit(EXIT_FAILURE);
}
/* SIGCHLD received from JIT dump child process. */
static void opd_sigchild(void)
{
int child_status;
wait(&child_status);
jit_conversion_running = 0;
if (WIFEXITED(child_status) && (!WEXITSTATUS(child_status))) {
verbprintf(vmisc, "JIT dump processing complete.\n");
} else {
printf("JIT dump processing exited abnormally: %d\n",
WEXITSTATUS(child_status));
}
}
static void opd_26_init(void)
{
size_t i;
size_t opd_buf_size;
unsigned long long start_time = 0ULL;
struct timeval tv;
opd_create_vmlinux(vmlinux, kernel_range);
opd_create_xen(xenimage, xen_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);
}
/* trigger kernel module setup before returning control to opcontrol */
opd_open_files();
gettimeofday(&tv, NULL);
start_time = 0ULL;
start_time = tv.tv_sec;
sprintf(start_time_str, "%llu", start_time);
}
static void opd_26_start(void)
{
/* 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,
};