|
Packit |
577717 |
/* rtop.c - a simple PMU-based CPU utilization tool
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Copyright (c) 2004-2006 Hewlett-Packard Development Company, L.P.
|
|
Packit |
577717 |
* Contributed by Stephane Eranian <eranian@hpl.hp.com>
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
Packit |
577717 |
* of this software and associated documentation files (the "Software"), to deal
|
|
Packit |
577717 |
* in the Software without restriction, including without limitation the rights
|
|
Packit |
577717 |
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
Packit |
577717 |
* of the Software, and to permit persons to whom the Software is furnished to do so,
|
|
Packit |
577717 |
* subject to the following conditions:
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* The above copyright notice and this permission notice shall be included in all
|
|
Packit |
577717 |
* copies or substantial portions of the Software.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
Packit |
577717 |
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
Packit |
577717 |
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
Packit |
577717 |
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
|
Packit |
577717 |
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
|
Packit |
577717 |
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
#ifndef _GNU_SOURCE
|
|
Packit |
577717 |
#define _GNU_SOURCE /* for getline */
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
#include <sys/types.h>
|
|
Packit |
577717 |
#include <inttypes.h>
|
|
Packit |
577717 |
#include <stdio.h>
|
|
Packit |
577717 |
#include <stdlib.h>
|
|
Packit |
577717 |
#include <stdarg.h>
|
|
Packit |
577717 |
#include <errno.h>
|
|
Packit |
577717 |
#include <unistd.h>
|
|
Packit |
577717 |
#include <string.h>
|
|
Packit |
577717 |
#include <syscall.h>
|
|
Packit |
577717 |
#include <getopt.h>
|
|
Packit |
577717 |
#include <curses.h>
|
|
Packit |
577717 |
#include <termios.h>
|
|
Packit |
577717 |
#include <signal.h>
|
|
Packit |
577717 |
#include <ctype.h>
|
|
Packit |
577717 |
#include <math.h>
|
|
Packit |
577717 |
#include <pthread.h>
|
|
Packit |
577717 |
#include <semaphore.h>
|
|
Packit |
577717 |
#include <limits.h>
|
|
Packit |
577717 |
#include <fcntl.h>
|
|
Packit |
577717 |
#include <sys/ioctl.h>
|
|
Packit |
577717 |
#include <sys/poll.h>
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#include <perfmon/pfmlib.h>
|
|
Packit |
577717 |
#include <perfmon/perfmon.h>
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#include "detect_pmcs.h"
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#define SWITCH_TIMEOUT 1000000000 /* in nanoseconds */
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#define RTOP_VERSION "0.1"
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#define RTOP_MAX_CPUS 1024 /* maximum number of CPU supported */
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#define MAX_EVT_NAME_LEN 128
|
|
Packit |
577717 |
#define RTOP_NUM_PMCS 4
|
|
Packit |
577717 |
#define RTOP_NUM_PMDS 4
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* max number of cpus (threads) supported
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
#define RTOP_MAX_CPUS 1024 /* MUST BE power of 2 */
|
|
Packit |
577717 |
#define RTOP_CPUMASK_BITS (sizeof(unsigned long)<<3)
|
|
Packit |
577717 |
#define RTOP_CPUMASK_COUNT (RTOP_MAX_CPUS/RTOP_CPUMASK_BITS)
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#define RTOP_CPUMASK_SET(m, g) ((m)[(g)/RTOP_CPUMASK_BITS] |= (1UL << ((g) % RTOP_CPUMASK_BITS)))
|
|
Packit |
577717 |
#define RTOP_CPUMASK_CLEAR(m, g) ((m)[(g)/RTOP_CPUMASK_BITS] &= ~(1UL << ((g) % RTOP_CPUMASK_BITS)))
|
|
Packit |
577717 |
#define RTOP_CPUMASK_ISSET(m, g) ((m)[(g)/RTOP_CPUMASK_BITS] & (1UL << ((g) % RTOP_CPUMASK_BITS)))
|
|
Packit |
577717 |
|
|
Packit |
577717 |
typedef unsigned long rtop_cpumask_t[RTOP_CPUMASK_COUNT];
|
|
Packit |
577717 |
|
|
Packit |
577717 |
typedef struct {
|
|
Packit |
577717 |
struct {
|
|
Packit |
577717 |
int opt_verbose;
|
|
Packit |
577717 |
int opt_delay; /* refresh delay in second */
|
|
Packit |
577717 |
int opt_delay_set;
|
|
Packit |
577717 |
} program_opt_flags;
|
|
Packit |
577717 |
rtop_cpumask_t cpu_mask; /* which CPUs to use in system wide mode */
|
|
Packit |
577717 |
long online_cpus;
|
|
Packit |
577717 |
long selected_cpus;
|
|
Packit |
577717 |
unsigned long cpu_mhz;
|
|
Packit |
577717 |
char *outfile;
|
|
Packit |
577717 |
} program_options_t;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#define opt_verbose program_opt_flags.opt_verbose
|
|
Packit |
577717 |
#define opt_delay program_opt_flags.opt_delay
|
|
Packit |
577717 |
#define opt_delay_set program_opt_flags.opt_delay_set
|
|
Packit |
577717 |
|
|
Packit |
577717 |
|
|
Packit |
577717 |
typedef struct {
|
|
Packit |
577717 |
char *name;
|
|
Packit |
577717 |
unsigned int plm;
|
|
Packit |
577717 |
} eventdesc_t;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
typedef enum {
|
|
Packit |
577717 |
THREAD_STARTED,
|
|
Packit |
577717 |
THREAD_RUN,
|
|
Packit |
577717 |
THREAD_DONE,
|
|
Packit |
577717 |
THREAD_ERROR
|
|
Packit |
577717 |
} thread_state_t;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
|
|
Packit |
577717 |
typedef struct {
|
|
Packit |
577717 |
pthread_t tid; /* logical thread identification */
|
|
Packit |
577717 |
long cpuid;
|
|
Packit |
577717 |
unsigned int id;
|
|
Packit |
577717 |
thread_state_t state;
|
|
Packit |
577717 |
int is_last;
|
|
Packit |
577717 |
sem_t his_sem;
|
|
Packit |
577717 |
sem_t my_sem;
|
|
Packit |
577717 |
FILE *fp;
|
|
Packit |
577717 |
uint64_t nsamples;
|
|
Packit |
577717 |
int has_msg;
|
|
Packit |
577717 |
} thread_desc_t;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
typedef struct _setdesc_t {
|
|
Packit |
577717 |
pfarg_pmc_t pc[RTOP_NUM_PMDS];
|
|
Packit |
577717 |
pfarg_pmd_t pd[RTOP_NUM_PMCS];
|
|
Packit |
577717 |
pfmlib_input_param_t inp;
|
|
Packit |
577717 |
pfmlib_output_param_t outp;
|
|
Packit |
577717 |
uint16_t set_id;
|
|
Packit |
577717 |
uint32_t set_flags;
|
|
Packit |
577717 |
uint32_t set_timeout; /* actual timeout */
|
|
Packit |
577717 |
int (*handler)(int fd, FILE *fp, thread_desc_t *td, struct _setdesc_t *my_sdesc);
|
|
Packit |
577717 |
void *data;
|
|
Packit |
577717 |
eventdesc_t *evt_desc;
|
|
Packit |
577717 |
} setdesc_t;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
typedef struct _barrier {
|
|
Packit |
577717 |
pthread_mutex_t mutex;
|
|
Packit |
577717 |
pthread_cond_t cond;
|
|
Packit |
577717 |
unsigned long counter;
|
|
Packit |
577717 |
unsigned long max;
|
|
Packit |
577717 |
unsigned long generation; /* avoid race condition on wake-up */
|
|
Packit |
577717 |
} barrier_t;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
typedef enum {
|
|
Packit |
577717 |
SESSION_INIT,
|
|
Packit |
577717 |
SESSION_RUN,
|
|
Packit |
577717 |
SESSION_STOP,
|
|
Packit |
577717 |
SESSION_ABORTED
|
|
Packit |
577717 |
} session_state_t;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
typedef struct {
|
|
Packit |
577717 |
uint64_t prev_k_cycles;
|
|
Packit |
577717 |
uint64_t prev_u_cycles;
|
|
Packit |
577717 |
} set0_data_t;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static barrier_t barrier;
|
|
Packit |
577717 |
static session_state_t session_state;
|
|
Packit |
577717 |
static program_options_t options;
|
|
Packit |
577717 |
static pfarg_ctx_t master_ctx;
|
|
Packit |
577717 |
static thread_desc_t *thread_info;
|
|
Packit |
577717 |
static struct termios saved_tty;
|
|
Packit |
577717 |
static int time_to_quit;
|
|
Packit |
577717 |
static int term_rows, term_cols;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static eventdesc_t set0_evt[]={
|
|
Packit |
577717 |
{ .name = "*", .plm = PFM_PLM0 },
|
|
Packit |
577717 |
{ .name = "*", .plm = PFM_PLM3 },
|
|
Packit |
577717 |
{ .name = NULL}
|
|
Packit |
577717 |
};
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int handler_set0(int fd, FILE *fp, thread_desc_t *td, setdesc_t *my_sdesc);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static setdesc_t setdesc_tab[]={
|
|
Packit |
577717 |
{ .set_id = 0,
|
|
Packit |
577717 |
.evt_desc = set0_evt,
|
|
Packit |
577717 |
.handler = handler_set0
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
};
|
|
Packit |
577717 |
#define RTOP_NUM_SDESC (sizeof(setdesc_tab)/sizeof(setdesc_t))
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int
|
|
Packit |
577717 |
barrier_init(barrier_t *b, unsigned long count)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
int r;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
r = pthread_mutex_init(&b->mutex, NULL);
|
|
Packit |
577717 |
if (r == -1) return -1;
|
|
Packit |
577717 |
r = pthread_cond_init(&b->cond, NULL);
|
|
Packit |
577717 |
if (r == -1) return -1;
|
|
Packit |
577717 |
b->max = b->counter = count;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
b->generation = 0;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void
|
|
Packit |
577717 |
cleanup_barrier(void *arg)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
int r;
|
|
Packit |
577717 |
barrier_t *b = (barrier_t *)arg;
|
|
Packit |
577717 |
r = pthread_mutex_unlock(&b->mutex);
|
|
Packit |
577717 |
(void)r;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int
|
|
Packit |
577717 |
barrier_wait(barrier_t *b)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
unsigned long generation;
|
|
Packit |
577717 |
int oldstate;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
pthread_cleanup_push(cleanup_barrier, b);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
pthread_mutex_lock(&b->mutex);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
pthread_testcancel();
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (--b->counter == 0) {
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* reset barrier */
|
|
Packit |
577717 |
b->counter = b->max;
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* bump generation number, this avoids thread getting stuck in the
|
|
Packit |
577717 |
* wake up loop below in case a thread just out of the barrier goes
|
|
Packit |
577717 |
* back in right away before all the thread from the previous "round"
|
|
Packit |
577717 |
* have "escaped".
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
b->generation++;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
pthread_cond_broadcast(&b->cond);
|
|
Packit |
577717 |
} else {
|
|
Packit |
577717 |
|
|
Packit |
577717 |
generation = b->generation;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
while (b->counter != b->max && generation == b->generation) {
|
|
Packit |
577717 |
pthread_cond_wait(&b->cond, &b->mutex);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
pthread_setcancelstate(oldstate, NULL);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
pthread_mutex_unlock(&b->mutex);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
pthread_cleanup_pop(0);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void fatal_error(char *fmt,...) __attribute__((noreturn));
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void
|
|
Packit |
577717 |
warning(char *fmt, ...)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
va_list ap;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
va_start(ap, fmt);
|
|
Packit |
577717 |
vfprintf(stderr, fmt, ap);
|
|
Packit |
577717 |
va_end(ap);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void
|
|
Packit |
577717 |
fatal_error(char *fmt, ...)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
va_list ap;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
va_start(ap, fmt);
|
|
Packit |
577717 |
vfprintf(stderr, fmt, ap);
|
|
Packit |
577717 |
va_end(ap);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
exit(1);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int
|
|
Packit |
577717 |
gettid(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
int tmp;
|
|
Packit |
577717 |
tmp = syscall(__NR_gettid);
|
|
Packit |
577717 |
return tmp;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#ifndef __NR_sched_setaffinity
|
|
Packit |
577717 |
#error "you need to define __NR_sched_setaffinity"
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* Hack to get this to work without libc support
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
int
|
|
Packit |
577717 |
pin_self_cpu(unsigned int cpu)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
unsigned long my_mask;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
my_mask = 1UL << cpu;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return syscall(__NR_sched_setaffinity, gettid(), sizeof(my_mask), &my_mask);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void
|
|
Packit |
577717 |
sigint_handler(int n)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
time_to_quit = 1;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static unsigned long
|
|
Packit |
577717 |
find_cpu_speed(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
FILE *fp1;
|
|
Packit |
577717 |
unsigned long f1 = 0, f2 = 0;
|
|
Packit |
577717 |
char buffer[128], *p, *value;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
memset(buffer, 0, sizeof(buffer));
|
|
Packit |
577717 |
|
|
Packit |
577717 |
fp1 = fopen("/proc/cpuinfo", "r");
|
|
Packit |
577717 |
if (fp1 == NULL) return 0;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for (;;) {
|
|
Packit |
577717 |
buffer[0] = '\0';
|
|
Packit |
577717 |
|
|
Packit |
577717 |
p = fgets(buffer, 127, fp1);
|
|
Packit |
577717 |
if (p == NULL)
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* skip blank lines */
|
|
Packit |
577717 |
if (*p == '\n') continue;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
p = strchr(buffer, ':');
|
|
Packit |
577717 |
if (p == NULL)
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* p+2: +1 = space, +2= firt character
|
|
Packit |
577717 |
* strlen()-1 gets rid of \n
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
*p = '\0';
|
|
Packit |
577717 |
value = p+2;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
value[strlen(value)-1] = '\0';
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (!strncasecmp("cpu MHz", buffer, 7)) {
|
|
Packit |
577717 |
float fl;
|
|
Packit |
577717 |
sscanf(value, "%f", &fl);
|
|
Packit |
577717 |
f1 = lroundf(fl);
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
if (!strncasecmp("BogoMIPS", buffer, 8)) {
|
|
Packit |
577717 |
float fl;
|
|
Packit |
577717 |
sscanf(value, "%f", &fl);
|
|
Packit |
577717 |
f2 = lroundf(fl);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
fclose(fp1);
|
|
Packit |
577717 |
return f1 == 0 ? f2 : f1;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void
|
|
Packit |
577717 |
get_term_size(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
int ret;
|
|
Packit |
577717 |
struct winsize ws;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ret = ioctl(1, TIOCGWINSZ, &ws);
|
|
Packit |
577717 |
if (ret == -1)
|
|
Packit |
577717 |
fatal_error("cannot determine screen size\n");
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (ws.ws_row > 10) {
|
|
Packit |
577717 |
term_cols = ws.ws_col;
|
|
Packit |
577717 |
term_rows = ws.ws_row;
|
|
Packit |
577717 |
} else {
|
|
Packit |
577717 |
term_cols = 80;
|
|
Packit |
577717 |
term_rows = 24;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (term_rows < options.selected_cpus)
|
|
Packit |
577717 |
fatal_error("you need at least %ld rows on your terminal to display all CPUs\n", options.selected_cpus);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void
|
|
Packit |
577717 |
sigwinch_handler(int n)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
get_term_size();
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void
|
|
Packit |
577717 |
setup_screen(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
int ret;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ret = tcgetattr(0, &saved_tty);
|
|
Packit |
577717 |
if (ret == -1)
|
|
Packit |
577717 |
fatal_error("cannot save tty settings\n");
|
|
Packit |
577717 |
|
|
Packit |
577717 |
get_term_size();
|
|
Packit |
577717 |
|
|
Packit |
577717 |
initscr();
|
|
Packit |
577717 |
nocbreak();
|
|
Packit |
577717 |
resizeterm(term_rows, term_cols);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void
|
|
Packit |
577717 |
close_screen(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
int ret;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
endwin();
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ret = tcsetattr(0, TCSAFLUSH, &saved_tty);
|
|
Packit |
577717 |
if (ret == -1)
|
|
Packit |
577717 |
warning("cannot restore tty settings\n");
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void
|
|
Packit |
577717 |
setup_signals(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct sigaction act;
|
|
Packit |
577717 |
sigset_t my_set;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* SIGINT is a asynchronous signal
|
|
Packit |
577717 |
* sent to the process (not a specific thread). POSIX states
|
|
Packit |
577717 |
* that one and only one thread will execute the handler. This
|
|
Packit |
577717 |
* could be any thread that does not have the signal blocked.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* install SIGINT handler
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
memset(&act,0,sizeof(act));
|
|
Packit |
577717 |
sigemptyset(&my_set);
|
|
Packit |
577717 |
act.sa_handler = (__sighandler_t)sigint_handler;
|
|
Packit |
577717 |
sigaction (SIGINT, &act, 0);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* install SIGWINCH handler
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
memset(&act,0,sizeof(act));
|
|
Packit |
577717 |
sigemptyset(&my_set);
|
|
Packit |
577717 |
act.sa_handler = (__sighandler_t)sigwinch_handler;
|
|
Packit |
577717 |
sigaction (SIGWINCH, &act, 0);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void
|
|
Packit |
577717 |
setup_worker_signals(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct sigaction act;
|
|
Packit |
577717 |
sigset_t my_set;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* SIGINT is a asynchronous signal
|
|
Packit |
577717 |
* sent to the process (not a specific thread). POSIX states
|
|
Packit |
577717 |
* that one and only one thread will execute the handler. This
|
|
Packit |
577717 |
* could be any thread that does not have the signal blocked.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* block SIGINT, forcing it to master thread only
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
memset(&act,0,sizeof(act));
|
|
Packit |
577717 |
|
|
Packit |
577717 |
sigemptyset(&my_set);
|
|
Packit |
577717 |
sigaddset(&my_set, SIGINT);
|
|
Packit |
577717 |
sigaddset(&my_set, SIGWINCH);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
pthread_sigmask(SIG_BLOCK, &my_set, NULL);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static struct option rtop_cmd_options[]={
|
|
Packit |
577717 |
{ "help", 0, 0, 1 },
|
|
Packit |
577717 |
{ "version", 0, 0, 2 },
|
|
Packit |
577717 |
{ "delay", 0, 0, 3 },
|
|
Packit |
577717 |
{ "cpu-list", 1, 0, 4 },
|
|
Packit |
577717 |
{ "outfile", 1, 0, 5 },
|
|
Packit |
577717 |
|
|
Packit |
577717 |
{ "verbose", 0, &options.opt_verbose, 1 },
|
|
Packit |
577717 |
{ 0, 0, 0, 0}
|
|
Packit |
577717 |
};
|
|
Packit |
577717 |
|
|
Packit |
577717 |
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int
|
|
Packit |
577717 |
handler_set0(int fd, FILE *fp, thread_desc_t *td, setdesc_t *my_sdesc)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
double k_cycles, u_cycles, i_cycles;
|
|
Packit |
577717 |
set0_data_t *sd1;
|
|
Packit |
577717 |
uint64_t itc_delta;
|
|
Packit |
577717 |
long mycpu;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
mycpu = td->cpuid;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (my_sdesc->data == NULL) {
|
|
Packit |
577717 |
my_sdesc->data = sd1 = calloc(1, sizeof(set0_data_t));
|
|
Packit |
577717 |
if (sd1 == NULL)
|
|
Packit |
577717 |
return -1;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
sd1 = my_sdesc->data;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* now read the results
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
if (pfm_read_pmds(fd, my_sdesc->pd, my_sdesc->inp.pfp_event_count) == -1) {
|
|
Packit |
577717 |
warning( "CPU%ld pfm_read_pmds error errno %d\n", mycpu, errno);
|
|
Packit |
577717 |
return -1;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* expected maximum duration with monitoring active for this set
|
|
Packit |
577717 |
* set_timeout is in nanoseconds, we need to divide mhz by 1000
|
|
Packit |
577717 |
* to get cycles.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
itc_delta = (my_sdesc->set_timeout*(uint64_t)options.cpu_mhz)/1000;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
k_cycles = (double)(my_sdesc->pd[0].reg_value - sd1->prev_k_cycles)*100.0/ (double)itc_delta;
|
|
Packit |
577717 |
u_cycles = (double)(my_sdesc->pd[1].reg_value - sd1->prev_u_cycles)*100.0/ (double)itc_delta;
|
|
Packit |
577717 |
i_cycles = 100.0 - (k_cycles + u_cycles);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* adjust for rounding errors
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
if (i_cycles < 0.0) i_cycles = 0.0;
|
|
Packit |
577717 |
if (i_cycles > 100.0) i_cycles = 100.0;
|
|
Packit |
577717 |
if (k_cycles > 100.0) k_cycles = 100.0;
|
|
Packit |
577717 |
if (u_cycles > 100.0) u_cycles = 100.0;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
printw("CPU%-2ld %6.2f%% usr %6.2f%% sys %6.2f%% idle\n",
|
|
Packit |
577717 |
mycpu,
|
|
Packit |
577717 |
u_cycles,
|
|
Packit |
577717 |
k_cycles,
|
|
Packit |
577717 |
i_cycles);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
sd1->prev_k_cycles = my_sdesc->pd[0].reg_value;
|
|
Packit |
577717 |
sd1->prev_u_cycles = my_sdesc->pd[1].reg_value;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (fp)
|
|
Packit |
577717 |
fprintf(fp, "%"PRIu64" %6.2f %6.2f %6.2f\n",
|
|
Packit |
577717 |
td->nsamples,
|
|
Packit |
577717 |
u_cycles,
|
|
Packit |
577717 |
k_cycles,
|
|
Packit |
577717 |
i_cycles);
|
|
Packit |
577717 |
td->nsamples++;
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void
|
|
Packit |
577717 |
do_measure_one_cpu(void *data)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
thread_desc_t *arg= (thread_desc_t *)data;
|
|
Packit |
577717 |
pfarg_ctx_t ctx;
|
|
Packit |
577717 |
pfarg_load_t load_args;
|
|
Packit |
577717 |
pfarg_setdesc_t setd;
|
|
Packit |
577717 |
setdesc_t *my_sdesc, *my_sdesc_tab = NULL;
|
|
Packit |
577717 |
long mycpu;
|
|
Packit |
577717 |
sem_t *his_sem;
|
|
Packit |
577717 |
unsigned int id;
|
|
Packit |
577717 |
int fd, ret, j;
|
|
Packit |
577717 |
int old_rows;
|
|
Packit |
577717 |
char cpu_str[16];
|
|
Packit |
577717 |
char *fn;
|
|
Packit |
577717 |
FILE *fp = NULL;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
setup_worker_signals();
|
|
Packit |
577717 |
|
|
Packit |
577717 |
mycpu = arg->cpuid;
|
|
Packit |
577717 |
id = arg->id;
|
|
Packit |
577717 |
his_sem = &arg->his_sem;
|
|
Packit |
577717 |
old_rows = term_rows;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (options.outfile) {
|
|
Packit |
577717 |
sprintf(cpu_str,".cpu%ld", mycpu);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
fn = malloc(strlen(options.outfile)+1+strlen(cpu_str));
|
|
Packit |
577717 |
if (fn == NULL) goto error;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
strcpy(fn, options.outfile);
|
|
Packit |
577717 |
strcat(fn, cpu_str);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
fp = fopen(fn, "w");
|
|
Packit |
577717 |
if (fp == NULL) {
|
|
Packit |
577717 |
warning("cannot open %s\n", fn);
|
|
Packit |
577717 |
free(fn);
|
|
Packit |
577717 |
goto error;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
free(fn);
|
|
Packit |
577717 |
fprintf(fp, "# Results for CPU%ld\n"
|
|
Packit |
577717 |
"# sample delay %d seconds\n"
|
|
Packit |
577717 |
"# Column1 : sample number\n"
|
|
Packit |
577717 |
"# Column2 : %% user time\n"
|
|
Packit |
577717 |
"# Column3 : %% system time\n"
|
|
Packit |
577717 |
"# Column4 : %% idle\n"
|
|
Packit |
577717 |
"# Column5 : kernel entry-exit\n",
|
|
Packit |
577717 |
mycpu,
|
|
Packit |
577717 |
options.opt_delay);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
memset(&load_args, 0, sizeof(load_args));
|
|
Packit |
577717 |
memset(&setd, 0, sizeof(setd));
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ret = pin_self_cpu(mycpu);
|
|
Packit |
577717 |
if (ret) {
|
|
Packit |
577717 |
warning("CPU%ld cannot pin\n");
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ctx = master_ctx;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
my_sdesc_tab = malloc(sizeof(setdesc_t)*RTOP_NUM_SDESC);
|
|
Packit |
577717 |
if (my_sdesc_tab == NULL) {
|
|
Packit |
577717 |
warning("CPU%ld cannot allocate sdesc\n", mycpu);
|
|
Packit |
577717 |
goto error;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
memcpy(my_sdesc_tab, setdesc_tab, sizeof(setdesc_t)*RTOP_NUM_SDESC);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
fd = pfm_create_context(&ctx, NULL, NULL, 0);
|
|
Packit |
577717 |
if (fd == -1) {
|
|
Packit |
577717 |
if (errno == ENOSYS) {
|
|
Packit |
577717 |
fatal_error("Your kernel does not have performance monitoring support!\n");
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
warning("CPU%ld cannot create context: %d\n", mycpu, errno);
|
|
Packit |
577717 |
goto error;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* WARNING: on processors where the idle loop goes into some power-saving
|
|
Packit |
577717 |
* state, the results of this program may be incorrect
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
for(j=0; j < RTOP_NUM_SDESC; j++) {
|
|
Packit |
577717 |
|
|
Packit |
577717 |
my_sdesc = my_sdesc_tab+j;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
setd.set_id = my_sdesc->set_id;
|
|
Packit |
577717 |
setd.set_flags = my_sdesc->set_flags;
|
|
Packit |
577717 |
setd.set_timeout = SWITCH_TIMEOUT; /* in nsecs */
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* do not bother if we have only one set
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
if (RTOP_NUM_SDESC > 1 && pfm_create_evtsets(fd, &setd, 1) == -1) {
|
|
Packit |
577717 |
warning("CPU%ld cannot create set%u: %d\n", mycpu, j, errno);
|
|
Packit |
577717 |
goto error;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
my_sdesc->set_timeout = setd.set_timeout;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (pfm_write_pmcs(fd, my_sdesc->pc, my_sdesc->outp.pfp_pmc_count) == -1) {
|
|
Packit |
577717 |
warning("CPU%ld pfm_write_pmcs error errno %d\n", mycpu, errno);
|
|
Packit |
577717 |
goto error;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* To be read, each PMD must be either written or declared
|
|
Packit |
577717 |
* as being part of a sample (reg_smpl_pmds)
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
if (pfm_write_pmds(fd, my_sdesc->pd, my_sdesc->inp.pfp_event_count) == -1) {
|
|
Packit |
577717 |
warning("CPU%ld pfm_write_pmds error errno %d\n", mycpu, errno);
|
|
Packit |
577717 |
goto error;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* in system-wide mode, this field must provide the CPU the caller wants
|
|
Packit |
577717 |
* to monitor. The kernel checks and if calling from the wrong CPU, the
|
|
Packit |
577717 |
* call fails. The affinity is not affected.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
load_args.load_pid = mycpu;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (pfm_load_context(fd, &load_args) == -1) {
|
|
Packit |
577717 |
warning("CPU%ld pfm_load_context error errno %d\n", mycpu, errno);
|
|
Packit |
577717 |
goto error;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
thread_info[id].state = THREAD_RUN;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
barrier_wait(&barrier);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* must wait until we are sure the master is out of its thread_create loop
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
barrier_wait(&barrier);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for(;session_state == SESSION_RUN;) {
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (pfm_start(fd, NULL) == -1) {
|
|
Packit |
577717 |
warning("CPU%ld pfm_start error errno %d\n", mycpu, errno);
|
|
Packit |
577717 |
goto error;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* wait for order from master
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
sem_wait(his_sem);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (pfm_stop(fd) == -1) {
|
|
Packit |
577717 |
warning("CPU%ld pfm_stop error %d\n", mycpu, errno);
|
|
Packit |
577717 |
goto error;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (old_rows != term_rows) {
|
|
Packit |
577717 |
resizeterm(term_rows, term_cols);
|
|
Packit |
577717 |
old_rows = term_rows;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for(j=0; j < RTOP_NUM_SDESC; j++) {
|
|
Packit |
577717 |
|
|
Packit |
577717 |
move(id*RTOP_NUM_SDESC+j, 0);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (my_sdesc_tab[j].handler)
|
|
Packit |
577717 |
(*my_sdesc_tab[j].handler)(fd, fp, arg, my_sdesc_tab+j);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
if (session_state == SESSION_RUN) {
|
|
Packit |
577717 |
sem_post(&arg->my_sem);
|
|
Packit |
577717 |
barrier_wait(&barrier);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
if (fp) fclose(fp);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
close(fd);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
thread_info[id].state = THREAD_DONE;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
pthread_exit((void *)(0));
|
|
Packit |
577717 |
error:
|
|
Packit |
577717 |
thread_info[id].state = THREAD_ERROR;
|
|
Packit |
577717 |
barrier_wait(&barrier);
|
|
Packit |
577717 |
if (fp) fclose(fp);
|
|
Packit |
577717 |
if (my_sdesc_tab) free(my_sdesc_tab);
|
|
Packit |
577717 |
pthread_exit((void *)(~0));
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void
|
|
Packit |
577717 |
mainloop(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
long i, j, ncpus = 0;
|
|
Packit |
577717 |
int ret;
|
|
Packit |
577717 |
void *retval;
|
|
Packit |
577717 |
struct pollfd fds;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ncpus = options.selected_cpus;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
barrier_init(&barrier, ncpus+1);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
thread_info = malloc(sizeof(thread_desc_t)*ncpus);
|
|
Packit |
577717 |
if (thread_info == NULL) {
|
|
Packit |
577717 |
fatal_error("cannot allocate thread_desc for %ld CPUs\n", ncpus);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for(i=0, j = 0; ncpus; i++) {
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (RTOP_CPUMASK_ISSET(options.cpu_mask, i) == 0) continue;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
thread_info[j].id = j;
|
|
Packit |
577717 |
thread_info[j].cpuid = i;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
sem_init(&thread_info[j].his_sem, 0, 0);
|
|
Packit |
577717 |
sem_init(&thread_info[j].my_sem, 0, 0);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ret = pthread_create(&thread_info[j].tid,
|
|
Packit |
577717 |
NULL,
|
|
Packit |
577717 |
(void *(*)(void *))do_measure_one_cpu,
|
|
Packit |
577717 |
(void *)(thread_info+j));
|
|
Packit |
577717 |
if (ret != 0) goto abort;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ncpus--;
|
|
Packit |
577717 |
j++;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* set last marker */
|
|
Packit |
577717 |
thread_info[j-1].is_last = 1;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ncpus = j;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
barrier_wait(&barrier);
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* check if some threads got problems
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
for(i=0; i < ncpus ; i++) {
|
|
Packit |
577717 |
if (thread_info[i].state == THREAD_ERROR) {
|
|
Packit |
577717 |
printw("aborting\n"); refresh();
|
|
Packit |
577717 |
goto abort;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
session_state = SESSION_RUN;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
barrier_wait(&barrier);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
fds.fd = 0;
|
|
Packit |
577717 |
fds.events = POLLIN;
|
|
Packit |
577717 |
fds.revents = 0;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for(;time_to_quit == 0;) {
|
|
Packit |
577717 |
ret = poll(&fds, 1, options.opt_delay*1000);
|
|
Packit |
577717 |
switch(ret) {
|
|
Packit |
577717 |
case 0:
|
|
Packit |
577717 |
for(i=0; i < ncpus ; i++) {
|
|
Packit |
577717 |
/* give order to print */
|
|
Packit |
577717 |
sem_post(&thread_info[i].his_sem);
|
|
Packit |
577717 |
/* wait for thread to be done */
|
|
Packit |
577717 |
sem_wait(&thread_info[i].my_sem);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
/* give order to start measuring again */
|
|
Packit |
577717 |
refresh();
|
|
Packit |
577717 |
barrier_wait(&barrier);
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case -1:
|
|
Packit |
577717 |
/* restart in case of signal */
|
|
Packit |
577717 |
if (errno == EINTR)
|
|
Packit |
577717 |
continue;
|
|
Packit |
577717 |
warning("polling error: %s\n", strerror(errno));
|
|
Packit |
577717 |
/* fall through */
|
|
Packit |
577717 |
default: time_to_quit = 1;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
session_state = SESSION_STOP;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* get worker thread out of their mainloop
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
for (i=0; i < ncpus; i++)
|
|
Packit |
577717 |
sem_post(&thread_info[i].his_sem);
|
|
Packit |
577717 |
join_all:
|
|
Packit |
577717 |
for(i=0; i< ncpus; i++) {
|
|
Packit |
577717 |
ret = pthread_join(thread_info[i].tid, &retval);
|
|
Packit |
577717 |
if (ret !=0) fatal_error("cannot join thread %ld\n", i);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
free(thread_info);
|
|
Packit |
577717 |
return;
|
|
Packit |
577717 |
abort:
|
|
Packit |
577717 |
session_state = SESSION_ABORTED;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for(i=0; i < ncpus; i++) {
|
|
Packit |
577717 |
pthread_cancel(thread_info[i].tid);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
goto join_all;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void
|
|
Packit |
577717 |
setup_measurement(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
|
|
Packit |
577717 |
pfmlib_options_t pfmlib_options;
|
|
Packit |
577717 |
eventdesc_t *evt;
|
|
Packit |
577717 |
setdesc_t *sdesc;
|
|
Packit |
577717 |
pfmlib_event_t trigger_event;
|
|
Packit |
577717 |
unsigned int i, j;
|
|
Packit |
577717 |
int ret;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* pass options to library (optional)
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
memset(&pfmlib_options, 0, sizeof(pfmlib_options));
|
|
Packit |
577717 |
pfmlib_options.pfm_debug = 0;
|
|
Packit |
577717 |
pfmlib_options.pfm_verbose = 0;
|
|
Packit |
577717 |
pfm_set_options(&pfmlib_options);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* Initialize pfm library (required before we can use it)
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
ret = pfm_initialize();
|
|
Packit |
577717 |
if (ret != PFMLIB_SUCCESS)
|
|
Packit |
577717 |
fatal_error("Cannot initialize library: %s\n", pfm_strerror(ret));
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* In system wide mode, the perfmon context cannot be inherited.
|
|
Packit |
577717 |
* Also in this mode, we cannot use the blocking form of user level notification.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
master_ctx.ctx_flags = PFM_FL_SYSTEM_WIDE;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (pfm_get_cycle_event(&trigger_event) != PFMLIB_SUCCESS)
|
|
Packit |
577717 |
fatal_error("cannot find cycle event for trigger\n");
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for(i=0; i < RTOP_NUM_SDESC; i++) {
|
|
Packit |
577717 |
|
|
Packit |
577717 |
sdesc = setdesc_tab+i;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
sdesc->inp.pfp_dfl_plm = PFM_PLM3|PFM_PLM0;
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* indicate we are using the monitors for a system-wide session.
|
|
Packit |
577717 |
* This may impact the way the library sets up the PMC values.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
sdesc->inp.pfp_flags = PFMLIB_PFP_SYSTEMWIDE;
|
|
Packit |
577717 |
evt = sdesc->evt_desc;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for(j=0; evt[j].name ; j++) {
|
|
Packit |
577717 |
if (*evt[j].name == '*')
|
|
Packit |
577717 |
sdesc->inp.pfp_events[j] = trigger_event;
|
|
Packit |
577717 |
else if (pfm_find_full_event(evt[j].name, &sdesc->inp.pfp_events[j]) != PFMLIB_SUCCESS)
|
|
Packit |
577717 |
fatal_error("cannot find %s event\n", evt[j].name);
|
|
Packit |
577717 |
sdesc->inp.pfp_events[j].plm = evt[j].plm;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* how many counters we use in this set (add the overflow trigger)
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
sdesc->inp.pfp_event_count = j;
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* build the pfp_unavail_pmcs bitmask by looking
|
|
Packit |
577717 |
* at what perfmon has available. It is not always
|
|
Packit |
577717 |
* the case that all PMU registers are actually available
|
|
Packit |
577717 |
* to applications. For instance, on IA-32 platforms, some
|
|
Packit |
577717 |
* registers may be reserved for the NMI watchdog timer.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* With this bitmap, the library knows which registers NOT to
|
|
Packit |
577717 |
* use. Of source, it is possible that no valid assignement may
|
|
Packit |
577717 |
* be possible if certina PMU registers are not available.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
detect_unavail_pmcs(-1, &sdesc->inp.pfp_unavail_pmcs);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* let the library figure out the values for the PMCS
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
if ((ret=pfm_dispatch_events(&sdesc->inp, NULL, &sdesc->outp, NULL)) != PFMLIB_SUCCESS)
|
|
Packit |
577717 |
fatal_error("cannot configure events: %s\n", pfm_strerror(ret));
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for (j=0; j < sdesc->outp.pfp_pmc_count; j++) {
|
|
Packit |
577717 |
sdesc->pc[j].reg_set = i;
|
|
Packit |
577717 |
sdesc->pc[j].reg_num = sdesc->outp.pfp_pmcs[j].reg_num;
|
|
Packit |
577717 |
sdesc->pc[j].reg_value = sdesc->outp.pfp_pmcs[j].reg_value;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
for (j=0; j < sdesc->outp.pfp_pmd_count; j++) {
|
|
Packit |
577717 |
sdesc->pd[j].reg_num = sdesc->outp.pfp_pmds[j].reg_num;
|
|
Packit |
577717 |
sdesc->pd[j].reg_set = i;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
void
|
|
Packit |
577717 |
populate_cpumask(char *cpu_list)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
char *p;
|
|
Packit |
577717 |
unsigned long start_cpu, end_cpu = 0;
|
|
Packit |
577717 |
unsigned long i, count = 0;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
options.online_cpus = sysconf(_SC_NPROCESSORS_ONLN);
|
|
Packit |
577717 |
if (options.online_cpus == -1)
|
|
Packit |
577717 |
fatal_error("cannot figure out the number of online processors\n");
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (cpu_list == NULL) {
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* The limit is mostly driven by the affinity support in NPTL and glibc __CPU_SETSIZE.
|
|
Packit |
577717 |
* the kernel interface does not expose any limitation.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
if (options.online_cpus >= RTOP_MAX_CPUS)
|
|
Packit |
577717 |
fatal_error("rtop can only handle to %u CPUs\n", RTOP_MAX_CPUS);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for(i=0; i < options.online_cpus; i++) {
|
|
Packit |
577717 |
RTOP_CPUMASK_SET(options.cpu_mask, i);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
options.selected_cpus = options.online_cpus;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
while(isdigit(*cpu_list)) {
|
|
Packit |
577717 |
p = NULL;
|
|
Packit |
577717 |
start_cpu = strtoul(cpu_list, &p, 0); /* auto-detect base */
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (start_cpu == ULONG_MAX || (*p != '\0' && *p != ',' && *p != '-')) goto invalid;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (p && *p == '-') {
|
|
Packit |
577717 |
cpu_list = ++p;
|
|
Packit |
577717 |
p = NULL;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
end_cpu = strtoul(cpu_list, &p, 0); /* auto-detect base */
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (end_cpu == ULONG_MAX || (*p != '\0' && *p != ',')) goto invalid;
|
|
Packit |
577717 |
if (end_cpu < start_cpu) goto invalid_range;
|
|
Packit |
577717 |
} else {
|
|
Packit |
577717 |
end_cpu = start_cpu;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (start_cpu >= RTOP_MAX_CPUS || end_cpu >= RTOP_MAX_CPUS) goto too_big;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for (; start_cpu <= end_cpu; start_cpu++) {
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (start_cpu >= options.online_cpus) goto not_online; /* XXX: assume contiguous range of CPUs */
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (RTOP_CPUMASK_ISSET(options.cpu_mask, start_cpu)) continue;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
RTOP_CPUMASK_SET(options.cpu_mask, start_cpu);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
count++;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (*p) ++p;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
cpu_list = p;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
options.selected_cpus = count;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return;
|
|
Packit |
577717 |
invalid:
|
|
Packit |
577717 |
fatal_error("invalid cpu list argument: %s\n", cpu_list);
|
|
Packit |
577717 |
/* no return */
|
|
Packit |
577717 |
not_online:
|
|
Packit |
577717 |
fatal_error("cpu %lu is not online\n", start_cpu);
|
|
Packit |
577717 |
/* no return */
|
|
Packit |
577717 |
invalid_range:
|
|
Packit |
577717 |
fatal_error("cpu range %lu - %lu is invalid\n", start_cpu, end_cpu);
|
|
Packit |
577717 |
/* no return */
|
|
Packit |
577717 |
too_big:
|
|
Packit |
577717 |
fatal_error("rtop is limited to %u CPUs\n", RTOP_MAX_CPUS);
|
|
Packit |
577717 |
/* no return */
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void
|
|
Packit |
577717 |
usage(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
printf( "usage: rtop [options]:\n"
|
|
Packit |
577717 |
"-h, --help\t\t\tdisplay this help and exit\n"
|
|
Packit |
577717 |
"-v, --verbose\t\t\tverbose output\n"
|
|
Packit |
577717 |
"-V, --version\t\t\tshow version and exit\n"
|
|
Packit |
577717 |
"-d nsec, --delay=nsec\t\tnumber of seconds between refresh (default=1s)\n"
|
|
Packit |
577717 |
"--cpu-list=cpu1,cpu2\t\tlist of CPUs to monitor(default=all)\n"
|
|
Packit |
577717 |
);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int
|
|
Packit |
577717 |
main(int argc, char **argv)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
int c;
|
|
Packit |
577717 |
char *cpu_list = NULL;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
while ((c=getopt_long(argc, argv,"+vhVd:", rtop_cmd_options, 0)) != -1) {
|
|
Packit |
577717 |
switch(c) {
|
|
Packit |
577717 |
case 0: continue; /* fast path for options */
|
|
Packit |
577717 |
case 'v': options.opt_verbose = 1;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 1:
|
|
Packit |
577717 |
case 'h':
|
|
Packit |
577717 |
usage();
|
|
Packit |
577717 |
exit(0);
|
|
Packit |
577717 |
case 2:
|
|
Packit |
577717 |
case 'V':
|
|
Packit |
577717 |
printf("rtop version " RTOP_VERSION " Date: " __DATE__ "\n"
|
|
Packit |
577717 |
"Copyright (C) 2004 Hewlett-Packard Company\n");
|
|
Packit |
577717 |
exit(0);
|
|
Packit |
577717 |
case 3:
|
|
Packit |
577717 |
case 'd':
|
|
Packit |
577717 |
if (options.opt_delay_set) fatal_error("cannot set delay twice\n");
|
|
Packit |
577717 |
options.opt_delay = atoi(optarg);
|
|
Packit |
577717 |
if (options.opt_delay < 0) {
|
|
Packit |
577717 |
fatal_error("invalid delay, must be >= 0\n");
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
options.opt_delay_set = 1;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 4:
|
|
Packit |
577717 |
if (cpu_list) fatal_error("cannot specify --cpu-list more than once\n");
|
|
Packit |
577717 |
if (*optarg == '\0') fatal_error("--cpu-list needs an argument\n");
|
|
Packit |
577717 |
cpu_list = optarg;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 5:
|
|
Packit |
577717 |
if (options.outfile) fatal_error("cannot specify --outfile more than once\n");
|
|
Packit |
577717 |
if (*optarg == '\0') fatal_error("--outfile needs an argument\n");
|
|
Packit |
577717 |
options.outfile = optarg;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
default:
|
|
Packit |
577717 |
fatal_error("unknown option\n");
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* default refresh delay
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
if (options.opt_delay_set == 0) options.opt_delay = 1;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
options.cpu_mhz = find_cpu_speed();
|
|
Packit |
577717 |
|
|
Packit |
577717 |
populate_cpumask(cpu_list);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
setup_measurement();
|
|
Packit |
577717 |
setup_signals();
|
|
Packit |
577717 |
setup_screen();
|
|
Packit |
577717 |
mainloop();
|
|
Packit |
577717 |
close_screen();
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|