minix/minix/commands/profile/profile.c
David van Moolenbroek 1dcfbcd173 Remove support for call profiling
The entire infrastructure relied on an ACK feature, and as such, it
has been broken for years now, with no easy way to repair it.

Change-Id: I783c2a21276967af115a642199f31fef0f14a572
2014-09-30 12:39:03 +00:00

416 lines
9.2 KiB
C

/* profile - profile operating system
*
* The profile command is used to control statistical profiling.
* It writes the profiling data collected by the kernel to a file.
*
* Changes:
* 14 Aug, 2006 Created (Rogier Meurs)
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#undef SPROFILE
#define SPROFILE 1
#include <minix/profile.h>
#define EHELP 1
#define ESYNTAX 2
#define EMEM 3
#define EOUTFILE 4
#define EFREQ 5
#define EACTION 6
#define START 1
#define STOP 2
#define DEF_OUTFILE "profile.stat.out"
#define NPIPE "/tmp/profile.npipe"
#define MIN_MEMSIZE 1
#define DEF_MEMSIZE 64
#define MIN_FREQ 3
#define MAX_FREQ 15
#define DEF_FREQ 6
#define BUFSIZE 1024
#define MB (1024*1024)
#define SYNCING "SYNC"
#define DEV_LOG "/dev/log"
int action = 0;
int mem_size = 0;
int mem_used = 0;
int freq = 0;
int intr_type = PROF_RTC;
char *outfile = "";
char *mem_ptr;
int outfile_fd, npipe_fd;
struct sprof_info_s sprof_info;
#define HASH_MOD 128
struct sproc {
endpoint_t ep;
char name[8];
struct sproc * next;
};
static struct sproc * proc_hash[HASH_MOD];
int handle_args(int argc, char *argv[]);
int start(void);
int stop(void);
int create_named_pipe(void);
int alloc_mem(void);
int init_outfile(void);
int write_outfile(void);
int write_outfile_sprof(void);
void detach(void);
int main(int argc, char *argv[])
{
int res;
if ((res = handle_args(argc, argv))) {
switch(res) {
case ESYNTAX:
printf("Error in parameters.\n");
return 1;
break;
case EACTION:
printf("Specify one of start|stop|get|reset.\n");
return 1;
break;
case EMEM:
printf("Incorrect memory size.\n");
return 1;
break;
case EFREQ:
printf("Incorrect frequency.\n");
return 1;
break;
case EOUTFILE:
printf("Output filename missing.\n");
return 1;
break;
default:
break;
}
/*
* Check the frequency when we know the intr type. Only selected values
* are correct for RTC
*/
if (action == START && intr_type == PROF_RTC &&
(freq < MIN_FREQ || freq > MAX_FREQ)) {
printf("Incorrect frequency.\n");
return 1;
}
printf("Usage:\n");
printf(" profile start [--rtc | --nmi] "
"[-m memsize] [-o outfile] [-f frequency]\n");
printf(" profile stop\n\n");
printf(" - --rtc is default, --nmi allows kernel profiling\n");
printf(" - memsize in MB, default: %u\n", DEF_MEMSIZE);
printf(" - default output file: profile.stat.out\n");
printf(" - sample frequencies for --rtc (default: %u):\n", DEF_FREQ);
printf(" 3 8192 Hz 10 64 Hz\n");
printf(" 4 4096 Hz 11 32 Hz\n");
printf(" 5 2048 Hz 12 16 Hz\n");
printf(" 6 1024 Hz 13 8 Hz\n");
printf(" 7 512 Hz 14 4 Hz\n");
printf(" 8 256 Hz 15 2 Hz\n");
printf(" 9 128 Hz\n\n");
printf("Use sprofalyze to analyze output file.\n");
return 1;
}
switch(action) {
case START:
if (start()) return 1;
break;
case STOP:
if (stop()) return 1;
break;
default:
break;
}
return 0;
}
int handle_args(int argc, char *argv[])
{
while (--argc) {
++argv;
if (strcmp(*argv, "-h") == 0 || strcmp(*argv, "help") == 0 ||
strcmp(*argv, "--help") == 0) {
return EHELP;
} else
if (strcmp(*argv, "-m") == 0) {
if (--argc == 0) return ESYNTAX;
if (sscanf(*++argv, "%u", &mem_size) != 1 ||
mem_size < MIN_MEMSIZE ) return EMEM;
} else
if (strcmp(*argv, "-f") == 0) {
if (--argc == 0) return ESYNTAX;
if (sscanf(*++argv, "%u", &freq) != 1)
return EFREQ;
} else
if (strcmp(*argv, "-o") == 0) {
if (--argc == 0) return ESYNTAX;
outfile = *++argv;
} else
if (strcmp(*argv, "--rtc") == 0) {
intr_type = PROF_RTC;
} else
if (strcmp(*argv, "--nmi") == 0) {
intr_type = PROF_NMI;
} else
if (strcmp(*argv, "start") == 0) {
if (action) return EACTION;
action = START;
} else
if (strcmp(*argv, "stop") == 0) {
if (action) return EACTION;
action = STOP;
}
}
/* No action specified. */
if (!action) return EHELP;
/* Init unspecified parameters. */
if (action == START) {
if (strcmp(outfile, "") == 0) outfile = DEF_OUTFILE;
if (mem_size == 0) mem_size = DEF_MEMSIZE;
mem_size *= MB; /* mem_size in bytes */
mem_size -= mem_size % sizeof(struct sprof_sample); /* align to sample size */
if (freq == 0) freq = DEF_FREQ; /* default frequency */
}
return 0;
}
int start()
{
/* This is the "starter process" for statistical profiling.
*
* Create output file for profiling data. Create named pipe to
* synchronize with stopper process. Fork so the parent can exit.
* Allocate memory for profiling data. Start profiling in kernel.
* Complete detachment from terminal. Write known string to named
* pipe, which blocks until read by stopper process. Redirect
* stdout/stderr to the named pipe. Write profiling data to file.
* Clean up.
*/
int log_fd;
if (init_outfile() || create_named_pipe()) return 1;
printf("Starting statistical profiling.\n");
if (fork() != 0) exit(0);
if (alloc_mem()) return 1;
if (sprofile(PROF_START, mem_size, freq, intr_type, &sprof_info, mem_ptr)) {
perror("sprofile");
fprintf(stderr, "Error starting profiling.\n");
return 1;
}
detach();
/* Temporarily redirect to system log to catch errors. */
log_fd = open(DEV_LOG, O_WRONLY);
dup2(log_fd, 1);
dup2(log_fd, 2);
if ((npipe_fd = open(NPIPE, O_WRONLY)) < 0) {
fprintf(stderr, "Unable to open named pipe %s.\n", NPIPE);
return 1;
} else
/* Synchronize with stopper process. */
write(npipe_fd, SYNCING, strlen(SYNCING));
/* Now redirect to named pipe. */
dup2(npipe_fd, 1);
dup2(npipe_fd, 2);
mem_used = sprof_info.mem_used;
if (mem_used == -1) {
fprintf(stderr, "WARNING: Profiling was stopped prematurely due to ");
fprintf(stderr, "insufficient memory.\n");
fprintf(stderr, "Try increasing available memory using the -m switch.\n");
}
if (write_outfile()) return 1;
close(log_fd);
close(npipe_fd);
unlink(NPIPE);
close(outfile_fd);
free(mem_ptr);
return 0;
}
int stop()
{
/* This is the "stopper" process for statistical profiling.
*
* Stop profiling in kernel. Read known string from named pipe
* to synchronize with starter proces. Read named pipe until EOF
* and write to stdout, this allows feedback from started process
* to be printed.
*/
int n;
char buf[BUFSIZE];
if (sprofile(PROF_STOP, 0, 0, 0, 0, 0)) {
perror("sprofile");
fprintf(stderr, "Error stopping profiling.\n");
return 1;
} else printf("Statistical profiling stopped.\n");
if ((npipe_fd = open(NPIPE, O_RDONLY)) < 0) {
fprintf(stderr, "Unable to open named pipe %s.\n", NPIPE);
return 1;
} else
/* Synchronize with starter process. */
read(npipe_fd, buf, strlen(SYNCING));
while ((n = read(npipe_fd, buf, BUFSIZE)) > 0)
write(1, buf, n);
close(npipe_fd);
return 0;
}
int alloc_mem()
{
if ((mem_ptr = malloc(mem_size)) == 0) {
fprintf(stderr, "Unable to allocate memory.\n");
fprintf(stderr, "Used chmem to increase available proces memory?\n");
return 1;
} else memset(mem_ptr, '\0', mem_size);
return 0;
}
int init_outfile()
{
if ((outfile_fd = open(outfile, O_CREAT | O_TRUNC | O_WRONLY)) <= 0) {
fprintf(stderr, "Unable to create outfile %s.\n", outfile);
return 1;
} else chmod(outfile, S_IRUSR | S_IWUSR);
return 0;
}
int create_named_pipe()
{
if ((mkfifo(NPIPE, S_IRUSR | S_IWUSR) == -1) && (errno != EEXIST)) {
fprintf(stderr, "Unable to create named pipe %s.\n", NPIPE);
return 1;
} else
return 0;
}
void detach()
{
setsid();
(void) chdir("/");
close(0);
close(1);
close(2);
}
static void add_proc(struct sprof_proc * p)
{
struct sproc * n;
int slot = ((unsigned)(p->proc)) % HASH_MOD;
n = malloc(sizeof(struct sproc));
if (!n)
abort();
n->ep = p->proc;
memcpy(n->name, p->name, 8);
n->next = proc_hash[slot];
proc_hash[slot] = n;
}
static char * get_proc_name(endpoint_t ep)
{
struct sproc * p;
for (p = proc_hash[((unsigned)ep) % HASH_MOD]; p; p = p->next) {
if (p->ep == ep)
return p->name;
}
return NULL;
}
int write_outfile()
{
ssize_t n;
int written;
char header[80];
printf("Writing to %s ...", outfile);
/* Write header. */
sprintf(header, "stat\n%u %u %u\n", sizeof(struct sprof_info_s),
sizeof(struct sprof_sample),
sizeof(struct sprof_proc));
n = write(outfile_fd, header, strlen(header));
if (n < 0) {
fprintf(stderr, "Error writing to outfile %s.\n", outfile);
return 1;
}
written = write_outfile_sprof();
if (written < 0) return -1;
printf(" header %d bytes, data %d bytes.\n", strlen(header), written);
return 0;
}
int write_outfile_sprof()
{
int towrite;
ssize_t written, written_total = 0;
/* write profiling totals */
written = write(outfile_fd, &sprof_info, sizeof(sprof_info));
if (written != sizeof(sprof_info)) goto error;
written_total += written;
/* write raw samples */
towrite = mem_used == -1 ? mem_size : mem_used;
written = write(outfile_fd, mem_ptr, towrite);
if (written != towrite) goto error;
written_total += written;
return written_total;
error:
fprintf(stderr, "Error writing to outfile %s.\n", outfile);
return -1;
}