#include <cpus.h>
#include <mach_mp_debug.h>
#include <mach_lock_mon.h>
#include <time_stamp.h>
#include <sys/types.h>
#include <mach/machine/vm_types.h>
#include <mach/boolean.h>
#include <kern/thread.h>
#include <kern/lock.h>
decl_simple_lock_data(extern, kdb_lock)
decl_simple_lock_data(extern, printf_lock)
#if NCPUS > 1 && MACH_LOCK_MON
#if TIME_STAMP
extern time_stamp_t time_stamp;
#else TIME_STAMP
typedef unsigned int time_stamp_t;
#define time_stamp 0
#endif TIME_STAMP
#define LOCK_INFO_MAX (1024*32)
#define LOCK_INFO_HASH_COUNT 1024
#define LOCK_INFO_PER_BUCKET (LOCK_INFO_MAX/LOCK_INFO_HASH_COUNT)
#define HASH_LOCK(lock) ((long)lock>>5 & (LOCK_INFO_HASH_COUNT-1))
struct lock_info {
unsigned int success;
unsigned int fail;
unsigned int masked;
unsigned int stack;
unsigned int time;
#if MACH_SLOCKS
simple_lock_data_t * lock;
#endif
vm_offset_t caller;
};
struct lock_info_bucket {
struct lock_info info[LOCK_INFO_PER_BUCKET];
};
struct lock_info_bucket lock_info[LOCK_INFO_HASH_COUNT];
struct lock_info default_lock_info;
unsigned default_lock_stack = 0;
extern int curr_ipl[];
struct lock_info *
locate_lock_info(lock)
simple_lock_data_t ** lock;
{
struct lock_info *li = &(lock_info[HASH_LOCK(*lock)].info[0]);
register i;
for (i=0; i < LOCK_INFO_PER_BUCKET; i++, li++)
if (li->lock) {
if (li->lock == *lock)
return(li);
} else {
li->lock = *lock;
li->caller = *((vm_offset_t *)lock - 1);
return(li);
}
db_printf("out of lock_info slots\n");
li = &default_lock_info;
return(li);
}
simple_lock(lock)
decl_simple_lock_data(, *lock)
{
register struct lock_info *li = locate_lock_info(&lock);
if (current_thread())
li->stack = current_thread()->lock_stack++;
mp_disable_preemption();
if (curr_ipl[cpu_number()])
li->masked++;
mp_enable_preemption();
if (_simple_lock_try(lock))
li->success++;
else {
_simple_lock(lock);
li->fail++;
}
li->time = time_stamp - li->time;
}
simple_lock_try(lock)
decl_simple_lock_data(, *lock)
{
register struct lock_info *li = locate_lock_info(&lock);
mp_disable_preemption();
if (curr_ipl[cpu_number()])
li->masked++;
mp_enable_preemption();
if (_simple_lock_try(lock)) {
li->success++;
li->time = time_stamp - li->time;
if (current_thread())
li->stack = current_thread()->lock_stack++;
return(1);
} else {
li->fail++;
return(0);
}
}
simple_unlock(lock)
decl_simple_lock_data(, *lock)
{
register time_stamp_t stamp = time_stamp;
register time_stamp_t *time = &locate_lock_info(&lock)->time;
register unsigned *lock_stack;
*time = stamp - *time;
_simple_unlock(lock);
if (current_thread()) {
lock_stack = ¤t_thread()->lock_stack;
if (*lock_stack)
(*lock_stack)--;
}
}
lip() {
lis(4, 1, 0);
}
#define lock_info_sort lis
unsigned scurval, ssum;
struct lock_info *sli;
lock_info_sort(arg, abs, count)
{
struct lock_info *li, mean;
int bucket = 0;
int i;
unsigned max_val;
unsigned old_val = (unsigned)-1;
struct lock_info *target_li = &lock_info[0].info[0];
unsigned sum;
unsigned empty, total;
unsigned curval;
printf("\nSUCCESS FAIL MASKED STACK TIME LOCK/CALLER\n");
if (!count)
count = 8 ;
while (count && target_li) {
empty = LOCK_INFO_HASH_COUNT;
target_li = 0;
total = 0;
max_val = 0;
mean.success = 0;
mean.fail = 0;
mean.masked = 0;
mean.stack = 0;
mean.time = 0;
mean.lock = (simple_lock_data_t *) &lock_info;
mean.caller = (vm_offset_t) &lock_info;
for (bucket = 0; bucket < LOCK_INFO_HASH_COUNT; bucket++) {
li = &lock_info[bucket].info[0];
if (li->lock)
empty--;
for (i= 0; i< LOCK_INFO_PER_BUCKET && li->lock; i++, li++) {
if (li->lock == &kdb_lock || li->lock == &printf_lock)
continue;
total++;
curval = *((int *)li + arg);
sum = li->success + li->fail;
if(!sum && !abs)
continue;
scurval = curval;
ssum = sum;
sli = li;
if (!abs) switch(arg) {
case 0:
break;
case 1:
case 2:
curval = (curval*100) / sum;
break;
case 3:
case 4:
curval = curval / sum;
break;
}
if (curval > max_val && curval < old_val) {
max_val = curval;
target_li = li;
}
if (curval == old_val && count != 0) {
print_lock_info(li);
count--;
}
mean.success += li->success;
mean.fail += li->fail;
mean.masked += li->masked;
mean.stack += li->stack;
mean.time += li->time;
}
}
if (target_li)
old_val = max_val;
}
db_printf("\n%d total locks, %d empty buckets", total, empty );
if (default_lock_info.success)
db_printf(", default: %d", default_lock_info.success + default_lock_info.fail);
db_printf("\n");
print_lock_info(&mean);
}
#define lock_info_clear lic
lock_info_clear()
{
struct lock_info *li;
int bucket = 0;
int i;
for (bucket = 0; bucket < LOCK_INFO_HASH_COUNT; bucket++) {
li = &lock_info[bucket].info[0];
for (i= 0; i< LOCK_INFO_PER_BUCKET; i++, li++) {
bzero(li, sizeof(struct lock_info));
}
}
bzero(&default_lock_info, sizeof(struct lock_info));
}
print_lock_info(li)
struct lock_info *li;
{
int off;
int sum = li->success + li->fail;
db_printf("%d %d/%d %d/%d %d/%d %d/%d ", li->success,
li->fail, (li->fail*100)/sum,
li->masked, (li->masked*100)/sum,
li->stack, li->stack/sum,
li->time, li->time/sum);
db_search_symbol(li->lock, 0, &off);
if (off < 1024)
db_printsym(li->lock, 0);
else {
db_printsym(li->caller, 0);
db_printf("(%X)", li->lock);
}
db_printf("\n");
}
#endif NCPUS > 1 && MACH_LOCK_MON
#if TIME_STAMP
time_lock(loops)
{
decl_simple_lock_data(, lock)
register time_stamp_t stamp;
register int i;
if (!loops)
loops = 1000;
simple_lock_init(&lock);
stamp = time_stamp;
for (i = 0; i < loops; i++) {
simple_lock(&lock);
simple_unlock(&lock);
}
stamp = time_stamp - stamp;
db_printf("%d stamps for simple_locks\n", stamp/loops);
#if MACH_LOCK_MON
stamp = time_stamp;
for (i = 0; i < loops; i++) {
_simple_lock(&lock);
_simple_unlock(&lock);
}
stamp = time_stamp - stamp;
db_printf("%d stamps for _simple_locks\n", stamp/loops);
#endif MACH_LOCK_MON
}
#endif TIME_STAMP