1005 lines
26 KiB
C
1005 lines
26 KiB
C
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/param.h>
|
|
#include <time.h>
|
|
#include <sys/wait.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <stdlib.h>
|
|
#include <search.h>
|
|
#include <sys/sysmp.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/utsname.h>
|
|
|
|
#include <sys/klstat.h>
|
|
|
|
|
|
#ifdef TIMER
|
|
extern void cycletrace(char*);
|
|
#define TSTAMP(s) cycletrace(s);
|
|
#else
|
|
#define TSTAMP(s)
|
|
#endif
|
|
|
|
char *helpstr[] = {
|
|
"Name"
|
|
" lockstat - display kernel lock statistics",
|
|
"",
|
|
"SYNOPSIS",
|
|
" lockstat on/off",
|
|
" lockstat [<options> ...] command [args...]",
|
|
" lockstat [<options> ...] <sec> [<repeat>]",
|
|
"",
|
|
"DESCRIPTION",
|
|
" This command will display statistics on the number of times kernel locks",
|
|
" are set and the amount on contention that occurs for each lock.",
|
|
"",
|
|
" The first form of the command is used to turn lock statistics collection",
|
|
" on or off.",
|
|
" The second form of the command is used to collect lock statistics during",
|
|
" execution of the specified command.",
|
|
" The third form if the command is used to periodically sample lock ",
|
|
" statistics. The sample time is specified by <sec> and the sample may",
|
|
" be repeated <repeat> times.",
|
|
"",
|
|
"OPTIONS",
|
|
" -u <unix> Location of unix to use for symbol table. If not",
|
|
" specified, the value of the UNIX environment variable",
|
|
" will be used. If neither is specified, /unix is used",
|
|
" -i <string> String to print as part of the title for the output.",
|
|
" -c <cpu>|a By default, the statistics for all cpus are summed",
|
|
" together. This argument specifies individual cpus that",
|
|
" should be reported separately. This option may be",
|
|
" repeated multiple times to select multiple cpus.",
|
|
" -t Normally, incremental statistics are reported. If -t",
|
|
" is specified, total counts since lockstats were enabled",
|
|
" is reported.",
|
|
" -S Show semaphore information. This is not selected by default",
|
|
" because I am not sure it is useful.",
|
|
" -p <persec> Report only on locks set more than <persec>",
|
|
" times per second.",
|
|
" -k <percent> Report only on locks with more than <percent> contention.",
|
|
" -w Report on \"warm or hot locks\" only. This option reports on",
|
|
" all locks with any contention.",
|
|
" -h Report on \"hot locks\" only. This option is the same as",
|
|
" selecting: -p 100 -k 5",
|
|
"",
|
|
"REPORT",
|
|
" The report is divided into several sections:",
|
|
" SPINLOCKS & MUTEXES",
|
|
" MRLOCKS",
|
|
" MRLOCK SPINLOCK (the spinlock that protects the mrlock",
|
|
" SEMAPHORES (if -S is selected)",
|
|
"",
|
|
" The following data is cellected for each lock:",
|
|
"",
|
|
" TOT/SEC Number of times per second that the lock was set.",
|
|
" CON Amount of contention that occurred for the lock. The ",
|
|
" number represents the percent of time that lock was NOT",
|
|
" acquired without spinning or sleeping. Note: for",
|
|
" semaphores, the number represents the % of the time",
|
|
" psema slept.",
|
|
" SPIN Gives a rough measure of the amount of time that ",
|
|
" we spent waiting for a lock. For spinlock, the number",
|
|
" represents the number of times thru the spinloop. For",
|
|
" sleeping locks, the number is the wait time in microseconds.",
|
|
" TOTAL Total number of times that the lock was set.",
|
|
" NOWAIT Number of times the lock was acquired without waiting",
|
|
" SPIN Number of times it was necessary to spin waiting for",
|
|
" a spinlock.",
|
|
" SLEEP Number of times it was necessary to sleep for a lock",
|
|
" REJECT Number of time a \"trylock\" failed.",
|
|
" NAME Identifies the lock and/or the routine that set the lock.",
|
|
" If the the is statically defined and not part of an array,",
|
|
" both the lock name and the functions that set the ",
|
|
" lock are listed. If the lock is dynamically allocated,",
|
|
" only the function name that set the lock will be listed.",
|
|
" only the function name that set the lock will be listed.",
|
|
"",
|
|
"Examples",
|
|
" To activate collection:",
|
|
" lockstat on",
|
|
"",
|
|
" To collect information on hot locks for execution of a command:",
|
|
" lockstat -h <command>",
|
|
"",
|
|
" To collect a 60 second snapshot of lock activity:",
|
|
" lockstat 60",
|
|
"",
|
|
"",
|
|
"CONFIGURING",
|
|
" The default kernel does not include the necessary components for",
|
|
" collecting lock statistics. You need to change the following lines",
|
|
" in the system/irix.sm file:",
|
|
" Change:",
|
|
" INCLUDE: hardlocks",
|
|
" INCLUDE: ksync",
|
|
" To:",
|
|
" INCLUDE: hardlocks_statistics",
|
|
" INCLUDE: ksync_statistics",
|
|
"",
|
|
" Then rebuild the kernel & reboot.",
|
|
NULL};
|
|
|
|
|
|
#define MAXCPUS 512
|
|
#define STRMAX 100
|
|
#define SPACEINC 16384*4
|
|
|
|
#define perrorx(s) perror(s), exit(1)
|
|
#define fatalx(m) fprintf(stderr, "ERROR - %s\n", m), exit(1)
|
|
#define notex(m) fprintf(stderr, "%s\n", m), exit(0)
|
|
#define max(a,b) (((a)<(b)) ? (b) : (a))
|
|
#define min(a,b) (((a)>(b)) ? (b) : (a))
|
|
|
|
typedef enum {Buf_Previous, Buf_Current} get_data_buffer_enum ;
|
|
typedef enum {Null_Entry, Special_Entry, Normal_Entry, Mr_Entry, MrSpin_Entry, Sema_Entry} entry_type_enum;
|
|
|
|
typedef struct {
|
|
lstat_lock_counts_t counts;
|
|
uint32_t total;
|
|
int contention;
|
|
double persec;
|
|
} lock_summary_t;
|
|
|
|
typedef struct {
|
|
__psunsigned_t caller_ra;
|
|
char *caller_name;
|
|
char *lock_name;
|
|
char multilock;
|
|
char title_index;
|
|
char caller_name_len;
|
|
entry_type_enum entry_type;
|
|
} directory_entry_t;
|
|
|
|
directory_entry_t directory[LSTAT_MAX_STAT_INDEX];
|
|
int next_free_dir_index;
|
|
|
|
lstat_directory_entry_t kernel_directory[LSTAT_MAX_STAT_INDEX];
|
|
lstat_directory_entry_t prev_directory[LSTAT_MAX_STAT_INDEX];
|
|
|
|
lstat_cpu_counts_t *kernel_counts;
|
|
lstat_cpu_counts_t *prev_counts;
|
|
lstat_lock_counts_t total_counts[LSTAT_MAX_STAT_INDEX];
|
|
short sorti[LSTAT_MAX_STAT_INDEX+2];
|
|
|
|
int numcpus = 0;
|
|
int enabled;
|
|
void *kernel_magic_addr;
|
|
void *kernel_end_addr;
|
|
time_t start_time, end_time;
|
|
double deltatime;
|
|
int skipline = 0;
|
|
char *current_header, *last_header;
|
|
|
|
char *dashes = "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n";
|
|
char *special_names[] = {
|
|
"ID 00",
|
|
"ID 04",
|
|
"ID 08",
|
|
"ID 16",
|
|
"ID 20",
|
|
"ID 24",
|
|
"ID 28",
|
|
"ID 32",
|
|
"ID 36",
|
|
"ID 40",
|
|
"ID 44",
|
|
"ID 48",
|
|
"ID 52",
|
|
"ID 56",
|
|
"ID 60",
|
|
""
|
|
};
|
|
|
|
#define SPECIAL_LO ((void*)4LL)
|
|
#define SPECIAL_HI ((void*)60LL)
|
|
char *special_titles[] = {
|
|
"SPINLOCKS & MUTEXES\n TOT/SEC CON SPIN TOTAL NOWAIT SPIN SLEEP REJECT NAME", /* normal */
|
|
"MRLOCKS\n TOT/SEC CON WAIT TOTAL NOWAIT SPIN SLEEP REJECT NAME", /* normal */
|
|
"MRLOCK SPINLOCK\n TOT/SEC CON SPIN TOTAL NOWAIT SPIN SLEEP REJECT NAME", /* normal */
|
|
"SEMAPHORES \n TOT/SEC CON TOTAL NOWAIT SPIN SLEEP REJECT NAME", /* sema */
|
|
"SPEC4 \n TOT/SEC CON TOTAL VAL1 VAL2 VAL3 VAL4 NAME",
|
|
"SPEC8 \n TOT/SEC CON TOTAL VAL1 VAL2 VAL3 VAL4 NAME",
|
|
"SPEC12\n TOT/SEC CON TOTAL VAL1 VAL2 VAL3 VAL4 NAME",
|
|
"SPEC16\n TOT/SEC CON TOTAL VAL1 VAL2 VAL3 VAL4 NAME",
|
|
"SPEC20\n TOT/SEC CON TOTAL VAL1 VAL2 VAL3 VAL4 NAME",
|
|
"SPEC24\n TOT/SEC CON TOTAL VAL1 VAL2 VAL3 VAL4 NAME",
|
|
"SPEC28\n TOT/SEC CON TOTAL VAL1 VAL2 VAL3 VAL4 NAME",
|
|
"SPEC32\n TOT/SEC CON TOTAL VAL1 VAL2 VAL3 VAL4 NAME",
|
|
"SPEC36\n TOT/SEC CON TOTAL VAL1 VAL2 VAL3 VAL4 NAME",
|
|
"SPEC40\n TOT/SEC CON TOTAL VAL1 VAL2 VAL3 VAL4 NAME",
|
|
"SPEC44\n TOT/SEC CON TOTAL VAL1 VAL2 VAL3 VAL4 NAME",
|
|
"SPEC48\n TOT/SEC CON TOTAL VAL1 VAL2 VAL3 VAL4 NAME",
|
|
"SPEC52\n TOT/SEC CON TOTAL VAL1 VAL2 VAL3 VAL4 NAME",
|
|
"SPEC56\n TOT/SEC CON TOTAL VAL1 VAL2 VAL3 VAL4 NAME",
|
|
"SPEC60\n TOT/SEC CON TOTAL VAL1 VAL2 VAL3 VAL4 NAME",
|
|
""};
|
|
|
|
|
|
void set_header(char*);
|
|
void enable_statistic_collection(int);
|
|
void do_report(char *, int);
|
|
void print_stats(char *, char *);
|
|
void set_header(char *);
|
|
void reset_counts(lock_summary_t *);
|
|
void add_counts(lock_summary_t *, lstat_lock_counts_t *);
|
|
int sum_counts(lock_summary_t *, entry_type_enum);
|
|
int set_counts(lock_summary_t *, lstat_lock_counts_t *, entry_type_enum);
|
|
void do_help(void);
|
|
void print_header(void);
|
|
void print_title(char *, char *);
|
|
void print_lock(char *, lock_summary_t *, int, entry_type_enum);
|
|
void get_collection_state(void);
|
|
void get_kernel_data(get_data_buffer_enum);
|
|
void build_sort_directory(void);
|
|
int sortcmp(const void *, const void *);
|
|
void sum_data (int, int);
|
|
int loadsymtab (char *, void *, void *);
|
|
void myaddress_to_symbol(void *, char *);
|
|
char* strspace(void);
|
|
int read_diff_file(void);
|
|
void write_diff_file(void);
|
|
|
|
|
|
extern int optind, opterr, errno;
|
|
extern char *optarg;
|
|
|
|
int semaopt = 0, topt = 0, debugopt = 0;
|
|
double opt_contention = -1.0, opt_persec = 0.0;
|
|
char *debugname=NULL;
|
|
|
|
char *ident = 0, *namelist = "/unix";
|
|
|
|
char cpulist[MAXCPUS];
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
static char optstr[] = "HSfwhk:p:tc:i:u:D:";
|
|
int c, err = 0;
|
|
int args, cpunum;
|
|
char title[120];
|
|
|
|
if (getenv("UNIX"))
|
|
namelist = getenv("UNIX");
|
|
|
|
while ((c = getopt(argc, argv, optstr)) != EOF)
|
|
switch (c) {
|
|
case 'D':
|
|
debugopt = 1;
|
|
debugname = optarg;
|
|
break;
|
|
case 'H':
|
|
do_help();
|
|
exit(0);
|
|
break;
|
|
case 'c':
|
|
if (*optarg == 'a') {
|
|
for (cpunum = 0; cpunum< MAXCPUS; cpunum++)
|
|
cpulist[cpunum]++;
|
|
} else {
|
|
cpunum = atoi(optarg);
|
|
if (cpunum < 0 || cpunum >= MAXCPUS)
|
|
fatalx("invalid cpu number specified");
|
|
cpulist[cpunum]++;
|
|
}
|
|
break;
|
|
case 'w':
|
|
opt_persec = 0.0;
|
|
opt_contention = 0.0;
|
|
break;
|
|
case 'h':
|
|
opt_persec = 100.0;
|
|
opt_contention = 5.0;
|
|
break;
|
|
case 'p':
|
|
opt_persec = (double)atoi(optarg);
|
|
break;
|
|
case 'k':
|
|
opt_contention = (double)atoi(optarg);
|
|
break;
|
|
case 'i':
|
|
ident = optarg;
|
|
break;
|
|
case 'S':
|
|
semaopt++;
|
|
break;
|
|
case 't':
|
|
topt++;
|
|
break;
|
|
case 'u':
|
|
namelist = optarg;
|
|
break;
|
|
case '?':
|
|
err = 1;
|
|
break;
|
|
}
|
|
|
|
TSTAMP("start");
|
|
if (debugopt && read_diff_file())
|
|
debugopt = 2;
|
|
else
|
|
get_collection_state();
|
|
|
|
TSTAMP("inited");
|
|
args = argc - optind;
|
|
if (err || args < 0)
|
|
fatalx("invalid arguments specified");
|
|
|
|
if (!args && !topt) {
|
|
do_help();
|
|
return(0);
|
|
}
|
|
|
|
|
|
if (args == 1) {
|
|
if (strcmp(argv[optind], "on") == 0) {
|
|
enable_statistic_collection (LSTAT_ON);
|
|
return(0);
|
|
|
|
} else if (strcmp(argv[optind], "off") == 0) {
|
|
enable_statistic_collection (LSTAT_OFF);
|
|
return(0);
|
|
}
|
|
|
|
}
|
|
|
|
if (!enabled)
|
|
fatalx ("lockstat collection not enabled");
|
|
|
|
TSTAMP("loadsymtab");
|
|
loadsymtab(namelist, kernel_magic_addr, kernel_end_addr);
|
|
TSTAMP("loadsymtab complete");
|
|
|
|
if (topt && !args || isdigit(*argv[optind])) {
|
|
int i, sleepsec=0, sleepcnt=1;
|
|
if (args) {
|
|
sleepsec = atoi(argv[optind]);
|
|
sleepcnt = (args>1) ? atoi(argv[optind+1]) : 1;
|
|
}
|
|
for (i=1; i<= sleepcnt; i++) {
|
|
if (i > 1)
|
|
fprintf(stderr, "\n\n");
|
|
if (!topt)
|
|
get_kernel_data (Buf_Previous);
|
|
if (sleepsec)
|
|
sleep(sleepsec);
|
|
get_kernel_data (Buf_Current);
|
|
if (topt)
|
|
sprintf(title, "Total counts since lock statistics were enabled\n");
|
|
else
|
|
sprintf(title, "Periodic sample %d of %d. Sample period: %d secs\n",
|
|
i, sleepcnt, sleepsec);
|
|
do_report(title, (i == sleepcnt));
|
|
}
|
|
|
|
} else {
|
|
int pid, stat;
|
|
|
|
get_kernel_data (Buf_Previous);
|
|
if ((pid=fork()) == 0) {
|
|
execvp(argv[optind], &argv[optind]);
|
|
perrorx("unable to exec command");
|
|
exit(1);
|
|
} else if (pid < 0)
|
|
perrorx("fork failed");
|
|
signal(SIGINT, SIG_IGN);
|
|
signal(SIGQUIT, SIG_IGN);
|
|
while (wait(&stat) != pid);
|
|
if((stat&0377) != 0)
|
|
fprintf(stderr,"Command terminated abnormally.\n");
|
|
signal(SIGINT, SIG_DFL);
|
|
signal(SIGQUIT, SIG_DFL);
|
|
get_kernel_data (Buf_Current);
|
|
strcpy(title, "Command: ");
|
|
for (; optind < argc; optind++) {
|
|
if(5+strlen(title)+strlen(argv[optind]) > sizeof(title)) {
|
|
strcat(title, " ...");
|
|
break;
|
|
}
|
|
strcat(title, argv[optind]);
|
|
strcat(title, " ");
|
|
}
|
|
do_report(title, 1);
|
|
}
|
|
|
|
TSTAMP("done");
|
|
return(0);
|
|
}
|
|
|
|
|
|
void
|
|
do_help(void)
|
|
{
|
|
char **p;
|
|
|
|
for (p=helpstr; *p; p++)
|
|
printf("%s\n", *p);
|
|
}
|
|
|
|
void
|
|
do_report(char *title, int last_report)
|
|
{
|
|
int cpu;
|
|
char name[100];
|
|
|
|
TSTAMP("start do report");
|
|
build_sort_directory();
|
|
|
|
TSTAMP("finish sort");
|
|
sum_data(0, numcpus - 1);
|
|
sprintf(name, "All (%d) CPUs", numcpus);
|
|
print_stats(title, name);
|
|
|
|
for (cpu = 0; cpu < numcpus; cpu++) {
|
|
if (cpulist[cpu] == 0)
|
|
continue;
|
|
sum_data(cpu, cpu);
|
|
sprintf(name, "CPU:%d", cpu);
|
|
print_stats(title, name);
|
|
}
|
|
if (last_report)
|
|
fprintf (stderr, "___________________________________________________________________________________________\n");
|
|
fflush(stderr);
|
|
TSTAMP("end do report");
|
|
}
|
|
|
|
|
|
void
|
|
print_stats(char *title1, char *title2)
|
|
{
|
|
entry_type_enum entry_type, current_entry_type = Null_Entry;
|
|
lock_summary_t sum_count;
|
|
int i, j, k, si, sj;
|
|
|
|
|
|
print_title(title1, title2);
|
|
|
|
for (i = 1; i < next_free_dir_index; i++) {
|
|
si = sorti[i];
|
|
entry_type = directory[si].entry_type;
|
|
if (entry_type == Sema_Entry && !semaopt)
|
|
continue;
|
|
set_header (special_titles[directory[si].title_index]);
|
|
|
|
if (entry_type != current_entry_type) {
|
|
current_entry_type = entry_type;
|
|
reset_counts (&sum_count);
|
|
for (j = i; j < next_free_dir_index; j++) {
|
|
sj = sorti[j];
|
|
if (directory[sj].entry_type != entry_type)
|
|
break;
|
|
add_counts (&sum_count, &total_counts[sj]);
|
|
}
|
|
sum_counts (&sum_count, entry_type);
|
|
skipline = 1;
|
|
print_lock("*TOTAL*", &sum_count, 0, entry_type);
|
|
skipline = 1;
|
|
}
|
|
|
|
|
|
if (!directory[si].multilock) {
|
|
k = i;
|
|
reset_counts (&sum_count);
|
|
while (k < next_free_dir_index && strcmp(directory[sorti[k]].lock_name, directory[si].lock_name) == 0) {
|
|
add_counts (&sum_count, &total_counts[sorti[k]]);
|
|
k++;
|
|
}
|
|
skipline = 1;
|
|
if (sum_counts (&sum_count, entry_type)) {
|
|
print_lock(directory[si].lock_name, &sum_count, 0, entry_type);
|
|
for (j = i; j < k; j++) {
|
|
sj = sorti[j];
|
|
set_counts (&sum_count, &total_counts[sj], entry_type);
|
|
if (sum_count.total)
|
|
print_lock(directory[sj].caller_name, &sum_count, 1, entry_type);
|
|
}
|
|
}
|
|
i = k - 1;
|
|
skipline = 1;
|
|
} else {
|
|
if (set_counts (&sum_count, &total_counts[si], entry_type))
|
|
print_lock(directory[si].caller_name, &sum_count, 0, entry_type);
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
reset_counts(lock_summary_t *sump)
|
|
{
|
|
int j;
|
|
sump->counts.ticks = 0;
|
|
for (j = 0; j < LSTAT_ACT_MAX_VALUES; j++)
|
|
sump->counts.count[j] = 0;
|
|
}
|
|
|
|
|
|
int
|
|
set_counts(lock_summary_t *sump, lstat_lock_counts_t *countp, entry_type_enum entry_type)
|
|
{
|
|
int j;
|
|
|
|
sump->counts.ticks = countp->ticks;
|
|
for (j = 0; j < LSTAT_ACT_MAX_VALUES; j++)
|
|
sump->counts.count[j] = countp->count[j];
|
|
return(sum_counts(sump, entry_type));
|
|
}
|
|
|
|
|
|
void
|
|
add_counts(lock_summary_t *sump, lstat_lock_counts_t *countp)
|
|
{
|
|
int j;
|
|
|
|
sump->counts.ticks += countp->ticks;
|
|
for (j = 0; j < LSTAT_ACT_MAX_VALUES; j++)
|
|
sump->counts.count[j] += countp->count[j];
|
|
}
|
|
|
|
|
|
int
|
|
sum_counts(lock_summary_t *sump, entry_type_enum entry_type)
|
|
{
|
|
int total, j;
|
|
double contention;
|
|
|
|
for (total = 0, j = 0; j < LSTAT_ACT_MAX_VALUES; j++)
|
|
total += sump->counts.count[j];
|
|
sump->total = total;
|
|
sump->persec = total / deltatime;
|
|
|
|
if (!total || entry_type == Special_Entry)
|
|
contention = 0.0;
|
|
else if (sump->counts.count[LSTAT_ACT_NO_WAIT] == 0)
|
|
contention = 100.0;
|
|
else
|
|
contention = 100.0 - (100.0 * sump->counts.count[LSTAT_ACT_NO_WAIT] / total);
|
|
|
|
sump->contention = (int)(contention+0.99999);
|
|
return (total && sump->persec > opt_persec &&
|
|
(contention > opt_contention || entry_type == Sema_Entry));
|
|
}
|
|
|
|
|
|
void
|
|
set_header(char *header)
|
|
{
|
|
if (header != last_header) {
|
|
current_header = header;
|
|
last_header = header;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
print_header(void)
|
|
{
|
|
if (current_header != NULL) {
|
|
fprintf (stderr, "\n%s", dashes);
|
|
fprintf (stderr, "%s\n", current_header);
|
|
current_header = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
print_title(char *title1, char *title2)
|
|
{
|
|
struct utsname uts;
|
|
fprintf (stderr, "___________________________________________________________________________________________\n");
|
|
uname (&uts);
|
|
fprintf (stderr, "System: %s %s %s %s %s\n", uts.sysname, uts.nodename, uts.release, uts.version, uts.machine);
|
|
if (ident)
|
|
fprintf(stderr, "Ident: %s\n", ident);
|
|
fprintf(stderr, "%s\n", title1);
|
|
fprintf(stderr, "%s\n", title2);
|
|
if (opt_persec >= 0 || opt_contention >= 0) {
|
|
fprintf(stderr, "Selecting locks: ");
|
|
if (opt_persec >= 0)
|
|
fprintf(stderr, "threshhold: >%d/sec ", (int)opt_persec);
|
|
if (opt_contention >= 0)
|
|
fprintf(stderr, "contention: >%d%%", (int)opt_contention);
|
|
fprintf(stderr, "\n");
|
|
}
|
|
fprintf(stderr, "\n");
|
|
fprintf(stderr, "Start time: %s", ctime(&start_time));
|
|
fprintf(stderr, "End time: %s", ctime(&end_time));
|
|
fprintf(stderr, "Delta Time: %.2f sec, slot in use: %d, \n", deltatime, next_free_dir_index);
|
|
|
|
}
|
|
|
|
|
|
void
|
|
print_lock(char *name, lock_summary_t *sump, int indent, entry_type_enum entry_type)
|
|
{
|
|
uint32_t ticks;
|
|
char tickch;
|
|
|
|
print_header();
|
|
|
|
if (skipline)
|
|
fprintf(stderr, "\n");
|
|
skipline = 0;
|
|
|
|
fprintf(stderr, "%9.2f", sump->persec);
|
|
fprintf(stderr, "%4d", sump->contention);
|
|
if (entry_type == Mr_Entry || entry_type == Normal_Entry || entry_type == MrSpin_Entry) {
|
|
ticks = sump->counts.ticks;
|
|
if (ticks) {
|
|
if (sump->counts.count[LSTAT_ACT_SLEPT]) {
|
|
ticks /= sump->counts.count[LSTAT_ACT_SLEPT];
|
|
/*fprintf(stderr, "SLEPT %d %d %d\n", sump->counts.ticks, sump->counts.count[LSTAT_ACT_SLEPT], ticks);*/
|
|
} else if (sump->counts.count[LSTAT_ACT_SPIN]) {
|
|
ticks /= sump->counts.count[LSTAT_ACT_SPIN];
|
|
/*fprintf(stderr, "SPIN %d %d %d\n", sump->counts.ticks, sump->counts.count[LSTAT_ACT_SLEPT], ticks);*/
|
|
} else {
|
|
fatalx("spin/sleep error");
|
|
}
|
|
if (ticks > 1000000000) {
|
|
tickch = 'G';
|
|
ticks /= 1000000000;
|
|
} else if (ticks > 10000000) {
|
|
tickch = 'M';
|
|
ticks /= 1000000;
|
|
} else if (ticks > 10000) {
|
|
tickch = 'K';
|
|
ticks /= 1000;
|
|
} else {
|
|
tickch = ' ';
|
|
}
|
|
fprintf(stderr, "%6d%c", ticks, tickch);
|
|
} else {
|
|
fprintf(stderr, " 0 ");
|
|
}
|
|
}
|
|
fprintf(stderr, "%12d", sump->total);
|
|
fprintf(stderr, "%12d", sump->counts.count[LSTAT_ACT_NO_WAIT]);
|
|
fprintf(stderr, "%9d", sump->counts.count[LSTAT_ACT_SPIN]);
|
|
fprintf(stderr, "%9d", sump->counts.count[LSTAT_ACT_SLEPT]);
|
|
fprintf(stderr, "%9d", sump->counts.count[LSTAT_ACT_REJECT]);
|
|
fprintf(stderr, "%s %s", (indent ? " ":""), name);
|
|
fprintf(stderr, "\n");
|
|
|
|
}
|
|
|
|
|
|
void
|
|
get_collection_state(void)
|
|
{
|
|
lstat_user_request_t request;
|
|
|
|
if (sysmp (MP_KLSTAT, LSTAT_STAT, &request) < 0) {
|
|
if (errno == ENOTSUP)
|
|
fatalx ("lockstat data collection is not available in this kernel.\n"
|
|
"\tLink with sema_statistics & hardlocks_statistics\n"
|
|
"\t (type lockstat -H for more information\n");
|
|
else
|
|
perrorx ("Unexpected error trying to get check if lock statistics are available");
|
|
}
|
|
|
|
numcpus = request.maxcpus;
|
|
enabled = request.lstat_is_enabled;
|
|
kernel_magic_addr = request.kernel_magic_addr;
|
|
kernel_end_addr = request.kernel_end_addr;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
enable_statistic_collection(int on_off)
|
|
{
|
|
if (enabled && on_off)
|
|
notex("Lock statistics collection is already ON");
|
|
else if (! enabled && !on_off)
|
|
notex("Lock statistics collection is already OFF");
|
|
if (sysmp (MP_KLSTAT, on_off, NULL) < 0) {
|
|
if (errno == EPERM)
|
|
fatalx ("You are not authorized to change the state of lockstat collection");
|
|
else
|
|
perrorx ("Unexpected error trying to change lockstat collection state");
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void
|
|
get_kernel_data(get_data_buffer_enum bufid)
|
|
{
|
|
lstat_user_request_t request;
|
|
static int start_lbolt, end_lbolt;
|
|
|
|
if (debugopt == 2)
|
|
return;
|
|
|
|
if (bufid == Buf_Previous) {
|
|
if (!prev_counts)
|
|
prev_counts = malloc(numcpus*sizeof(lstat_cpu_counts_t));
|
|
request.cpu_counts_ptr = prev_counts;
|
|
request.directory_ptr = prev_directory;
|
|
} else {
|
|
if (!kernel_counts)
|
|
kernel_counts = malloc(numcpus*sizeof(lstat_cpu_counts_t));
|
|
request.cpu_counts_ptr = kernel_counts;
|
|
request.directory_ptr = kernel_directory;
|
|
}
|
|
|
|
if (sysmp(MP_KLSTAT, LSTAT_READ, &request))
|
|
perrorx ("unexpected err reading lockstat data");
|
|
|
|
if (bufid == Buf_Previous) {
|
|
start_time = request.current_time;
|
|
start_lbolt = request.current_lbolt;
|
|
} else {
|
|
if (topt) {
|
|
start_lbolt = request.enabled_lbolt;
|
|
start_time = request.current_time -
|
|
(request.current_lbolt-request.enabled_lbolt)/HZ;
|
|
}
|
|
end_time = request.current_time;
|
|
end_lbolt = request.current_lbolt;
|
|
deltatime = (double) (end_lbolt - start_lbolt) / 100.0;
|
|
}
|
|
|
|
next_free_dir_index = request.next_free_dir_index;
|
|
if (debugopt && bufid == Buf_Current)
|
|
write_diff_file();
|
|
}
|
|
|
|
|
|
void
|
|
build_sort_directory(void)
|
|
{
|
|
static int last_next_free_dir_index = 1;
|
|
int i, lowbits;
|
|
char *namep;
|
|
|
|
#ifdef ZZZ
|
|
{
|
|
int chain[64];
|
|
int i, j, k, n;
|
|
for (i=0; i<64; i++)
|
|
chain[i] = 0;
|
|
for (i = 0; i < next_free_dir_index; i++) {
|
|
for (j=kernel_directory[i].next_stat_index, n=0; j; j = kernel_directory[j].next_stat_index)
|
|
n++;
|
|
chain[n]++;
|
|
}
|
|
printf ("Total entries %d\n", next_free_dir_index);
|
|
for (i=0; i<64; i++)
|
|
printf("Chain %3d, %5d\n", i, chain[i]);
|
|
exit(0);
|
|
}
|
|
#endif
|
|
for (i = last_next_free_dir_index; i < next_free_dir_index; i++) {
|
|
sorti[i] = i;
|
|
lowbits = (int) ((__psunsigned_t)kernel_directory[i].caller_ra & 3);
|
|
directory[i].caller_ra = ((__psunsigned_t) kernel_directory[i].caller_ra & ~3);
|
|
directory[i].caller_name = strspace();
|
|
myaddress_to_symbol((void*)directory[i].caller_ra, directory[i].caller_name);
|
|
namep = strchr(directory[i].caller_name, '+');
|
|
if (namep)
|
|
directory[i].caller_name_len = (char) (namep - directory[i].caller_name);
|
|
else
|
|
directory[i].caller_name_len = (char)strlen(directory[i].caller_name);
|
|
|
|
directory[i].lock_name = NULL;
|
|
directory[i].multilock = 0;
|
|
if (kernel_directory[i].lock_ptr >= SPECIAL_LO &&
|
|
kernel_directory[i].lock_ptr <= SPECIAL_HI) {
|
|
directory[i].lock_name = special_names[(size_t)kernel_directory[i].lock_ptr/4];
|
|
directory[i].title_index = 4 + (int)((uint64_t)kernel_directory[i].lock_ptr / 4);
|
|
directory[i].entry_type = Special_Entry;
|
|
} else {
|
|
if (kernel_directory[i].lock_ptr != LSTAT_MULTI_LOCK_ADDRESS) {
|
|
namep = strspace(); /* ZZZ */
|
|
myaddress_to_symbol(kernel_directory[i].lock_ptr, namep);
|
|
if (!isdigit(*namep))
|
|
directory[i].lock_name = namep;
|
|
else
|
|
*namep = '\0';
|
|
}
|
|
switch (lowbits) {
|
|
case LSTAT_RA_MR:
|
|
directory[i].entry_type = Mr_Entry;
|
|
directory[i].title_index = 1;
|
|
break;
|
|
case LSTAT_RA_MRSPIN:
|
|
directory[i].entry_type = MrSpin_Entry;
|
|
directory[i].title_index = 2;
|
|
break;
|
|
case LSTAT_RA_SEMA:
|
|
directory[i].entry_type = Sema_Entry;
|
|
directory[i].title_index = 3;
|
|
break;
|
|
default:
|
|
directory[i].entry_type = Normal_Entry;
|
|
directory[i].title_index = 0;
|
|
}
|
|
if (directory[i].lock_name == NULL) {
|
|
directory[i].lock_name = directory[i].caller_name;
|
|
directory[i].multilock = 1;
|
|
}
|
|
}
|
|
}
|
|
last_next_free_dir_index = next_free_dir_index;
|
|
|
|
qsort ((void *) &sorti[1], next_free_dir_index - 1, sizeof(sorti[0]), &sortcmp);
|
|
|
|
}
|
|
|
|
|
|
int
|
|
sortcmp(const void *ip, const void *jp)
|
|
{
|
|
int si0, si1, k;
|
|
|
|
si0 = *(short *)ip;
|
|
si1 = *(short *)jp;
|
|
|
|
k = directory[si0].entry_type - directory[si1].entry_type;
|
|
if (k)
|
|
return (k);
|
|
|
|
|
|
k = directory[si0].multilock - directory[si1].multilock;
|
|
if (k)
|
|
return (k);
|
|
|
|
k = strcmp(directory[si0].lock_name, directory[si1].lock_name);
|
|
if (k)
|
|
return (k);
|
|
|
|
k = strncmp(directory[si0].caller_name, directory[si1].caller_name,
|
|
min(directory[si0].caller_name_len,
|
|
directory[si1].caller_name_len));
|
|
if (k)
|
|
return (k);
|
|
|
|
k = (int)(directory[si0].caller_ra - directory[si1].caller_ra);
|
|
return(k);
|
|
|
|
}
|
|
|
|
|
|
void
|
|
sum_data (int start_cpu, int end_cpu)
|
|
{
|
|
int i, j, cpu;
|
|
for (i = 0; i < next_free_dir_index; i++) {
|
|
total_counts[i].ticks = 0;
|
|
for (j = 0; j < LSTAT_ACT_MAX_VALUES; j++)
|
|
total_counts[i].count[j] = 0;
|
|
for (cpu = start_cpu; cpu <= end_cpu; cpu++) {
|
|
total_counts[i].ticks += kernel_counts[cpu][i].ticks -
|
|
(topt ? 0 : prev_counts[cpu][i].ticks);
|
|
for (j = 0; j < LSTAT_ACT_MAX_VALUES; j++)
|
|
total_counts[i].count[j] += kernel_counts[cpu][i].count[j] -
|
|
(topt ? 0 : prev_counts[cpu][i].count[j]);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *symtab_name(void *addr);
|
|
|
|
void
|
|
myaddress_to_symbol(void *adr, char *namep)
|
|
{
|
|
strcpy(namep, symtab_name(adr));
|
|
}
|
|
|
|
|
|
/*
|
|
* strspace
|
|
*
|
|
* A crude allocation manager for string space.
|
|
* Returns pointer to where a string of length 0 .. STRMAX can be placed.
|
|
* On next call, a free space pointer is updated to point to
|
|
* the location just beyond the last string allocated.
|
|
* NOTE: does not support expanding a string once allocated &
|
|
* another call to strspace is made.
|
|
*/
|
|
char*
|
|
strspace(void)
|
|
{
|
|
static char *space=NULL, *endspace=NULL;
|
|
|
|
if (space)
|
|
space += (1 + strlen(space));
|
|
|
|
if (space >= endspace) {
|
|
space = malloc(SPACEINC);
|
|
endspace = space + SPACEINC - STRMAX;
|
|
}
|
|
return(space);
|
|
}
|
|
|
|
|
|
|
|
void
|
|
verify_diff_file (void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < next_free_dir_index; i++) {
|
|
if (kernel_directory[i].caller_ra != prev_directory[i].caller_ra && prev_directory[i].caller_ra != 0) {
|
|
fprintf(stderr, "caller address mismatch: index:%d, old:%llx, new:%llx",
|
|
i, prev_directory[i].caller_ra, kernel_directory[i].caller_ra);
|
|
perrorx("caller address mismatch");
|
|
}
|
|
}
|
|
}
|
|
|
|
#define WRITE(s) if (write(fd, (char *)&s, sizeof(s)) != sizeof(s)) \
|
|
perrorx("write diff stats");
|
|
#define READ(s) if (read(fd, (char *)&s, sizeof(s)) != sizeof(s)) \
|
|
perrorx("read diff stats s ");
|
|
#define WRITEN(s,n) if (write(fd, (char *)&s, (n)) != (n)) \
|
|
perrorx("write diff stats");
|
|
#define READN(s,n) if (read(fd, (char *)&s, (n)) != (n)) \
|
|
perrorx("read diff stats");
|
|
int
|
|
read_diff_file (void)
|
|
{
|
|
int fd;
|
|
|
|
if ((fd = open(debugname, O_RDONLY, 0)) < 0)
|
|
return (0);
|
|
|
|
READ(numcpus);
|
|
prev_counts = malloc(numcpus*sizeof(lstat_cpu_counts_t));
|
|
kernel_counts = malloc(numcpus*sizeof(lstat_cpu_counts_t));
|
|
READ(start_time);
|
|
READ(end_time);
|
|
READ(deltatime);
|
|
READ(next_free_dir_index);
|
|
READN(kernel_directory[0], next_free_dir_index*sizeof(lstat_directory_entry_t));
|
|
READN(kernel_counts[0], numcpus*sizeof(lstat_cpu_counts_t));
|
|
READN(prev_counts[0], numcpus*sizeof(lstat_cpu_counts_t));
|
|
READ(kernel_magic_addr);
|
|
READ(kernel_end_addr);
|
|
|
|
close(fd);
|
|
verify_diff_file ();
|
|
enabled = 1;
|
|
return(1);
|
|
}
|
|
|
|
|
|
void
|
|
write_diff_file (void)
|
|
{
|
|
int fd;
|
|
|
|
if ((fd = open(debugname, O_WRONLY | O_CREAT, 0666)) < 0)
|
|
perrorx("cant create diff file");
|
|
|
|
WRITE(numcpus);
|
|
WRITE(start_time);
|
|
WRITE(end_time);
|
|
WRITE(deltatime);
|
|
WRITE(next_free_dir_index);
|
|
WRITEN(kernel_directory[0], next_free_dir_index*sizeof(lstat_directory_entry_t));
|
|
WRITEN(kernel_counts[0], numcpus*sizeof(lstat_cpu_counts_t));
|
|
WRITEN(prev_counts[0], numcpus*sizeof(lstat_cpu_counts_t));
|
|
WRITE(kernel_magic_addr);
|
|
WRITE(kernel_end_addr);
|
|
|
|
close(fd);
|
|
exit(0);
|
|
}
|
|
|