runtime/nanoscope_sampler.cc (200 lines of code) (raw):
#include "nanoscope_sampler.h"
#if defined(__ANDROID__)
#include <linux/perf_event.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <asm/unistd.h>
#include <time.h>
#include "thread.h"
// perf_event_open API
static int perf_event_open(const perf_event_attr& attr, pid_t pid, int cpu,
int group_fd, unsigned long flags) { // NOLINT
return syscall(__NR_perf_event_open, &attr, pid, cpu, group_fd, flags);
}
#include <linux/unistd.h>
#include <signal.h>
#include <time.h>
#endif
namespace art{
Thread* NanoscopeSampler::sampling_thread_ = NULL;
SampleMode NanoscopeSampler::sample_mode_ = kSampleDisabled;
#if defined(__ANDROID__)
int64_t NanoscopeSampler::sample_interval_ = 1000000; // 1000000ns
int NanoscopeSampler::perf_timer_fd_ = -1;
int NanoscopeSampler::sample_fd_ [] = { [0 ... (COUNTER_TYPE_LIMIT - 1)] = -1 };
struct perf_event_mmap_page* NanoscopeSampler::perf_timer_page_ = NULL;
timer_t NanoscopeSampler::timer_id_ = 0;
#endif
void NanoscopeSampler::set_up_timer(){
#if defined(__ANDROID__)
if(sample_mode_ == kSamplePerf){
// Set up perf_event counter that acts as a timer
struct perf_event_attr pe;
memset(&pe, 0, sizeof(struct perf_event_attr));
pe.type = PERF_TYPE_SOFTWARE;
pe.size = sizeof(struct perf_event_attr);
pe.config = PERF_COUNT_SW_CPU_CLOCK;
pe.sample_period = sample_interval_;
pe.sample_type = PERF_SAMPLE_TIME;
pe.read_format = PERF_FORMAT_GROUP|PERF_FORMAT_ID;
pe.disabled = 1;
pe.pinned = 1;
pe.wakeup_events = 1;
// perf_event_open pid = -1, cpuid = 0, counts cpu time of all threads on cpu0
// which essentially gives us wall clock time
perf_timer_fd_ = perf_event_open(pe, -1, 0, -1, 0);
if (perf_timer_fd_ < 0) {
LOG(ERROR) << "nanoscope: Fail to open perf event file: master ";
LOG(ERROR) << "nanoscope: " << strerror(errno);
return;
}
void* p = mmap(NULL, (1+1)*PERF_PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, perf_timer_fd_, 0);
perf_timer_page_ = (struct perf_event_mmap_page*)p;
fcntl(perf_timer_fd_, F_SETFL, O_ASYNC);
fcntl(perf_timer_fd_, F_SETSIG, SIGTIMER);
// Deliver the signal to the tracing thread when counter overflows
struct f_owner_ex fown_ex;
fown_ex.type = F_OWNER_TID;
fown_ex.pid = sampling_thread_->GetTid();
int ret = fcntl(perf_timer_fd_, F_SETOWN_EX, &fown_ex);
if (ret == -1) {
LOG(ERROR) << "nanoscope: Failed to set the owner of the perf event file";
return;
}
} else if (sample_mode_ == kSampleCpu) {
struct sigevent sev;
struct itimerspec its;
long long freq_nanosecs;
sev.sigev_notify = SIGEV_THREAD_ID;
sev.sigev_signo = SIGTIMER;
sev.sigev_notify_thread_id = sampling_thread_ -> GetTid();
sev.sigev_value.sival_ptr = &timer_id_;
if (timer_create(CLOCK_THREAD_CPUTIME_ID, &sev, &timer_id_) == -1) {
LOG(ERROR) << "nanoscope: Failed to create timer";
}
freq_nanosecs = sample_interval_; // 1ms
its.it_value.tv_sec = freq_nanosecs / 1000000000;
its.it_value.tv_nsec = freq_nanosecs % 1000000000;
its.it_interval.tv_sec = its.it_value.tv_sec;
its.it_interval.tv_nsec = its.it_value.tv_nsec;
if (timer_settime(timer_id_, 0, &its, NULL) == -1) {
LOG(ERROR) << "nanoscope: Failed to set timer";
}
} else {
UNREACHABLE();
}
#endif
}
#if defined(__ANDROID__)
void NanoscopeSampler::set_up_sample_counter(CounterType counter_type, int groupfd){
uint64_t type, config;
switch(counter_type){
case COUNTER_TYPE_MAJOR_PAGE_FAULTS:
type = PERF_TYPE_SOFTWARE;
config = PERF_COUNT_SW_PAGE_FAULTS_MAJ;
break;
case COUNTER_TYPE_MINOR_PAGE_FAULTS:
type = PERF_TYPE_SOFTWARE;
config = PERF_COUNT_SW_PAGE_FAULTS_MIN;
break;
case COUNTER_TYPE_CONTEXT_SWITCHES:
type = PERF_TYPE_SOFTWARE;
config = PERF_COUNT_SW_CONTEXT_SWITCHES;
break;
default:
LOG(ERROR) << "nanoscope: wrong counter type";
return;
}
// Set up counter
struct perf_event_attr pe;
memset(&pe, 0, sizeof(struct perf_event_attr));
pe.type = type;
pe.size = sizeof(struct perf_event_attr);
pe.config = config;
pe.read_format = PERF_FORMAT_GROUP|PERF_FORMAT_ID;
pe.disabled = 1;
// Pid = tracing thread's tid, cpuid = -1. Counts tracing thread on any cpu.
sample_fd_[counter_type] = perf_event_open(pe, sampling_thread_->GetTid(), -1, groupfd, 0);
if (sample_fd_[counter_type] < 0) {
LOG(ERROR) << "nanoscope: Fail to open perf event file: slave ";
LOG(ERROR) << "nanoscope: " << strerror(errno);
return;
}
fcntl(sample_fd_[counter_type], F_SETFL, O_ASYNC);
}
void NanoscopeSampler::signal_handler(int sigo ATTRIBUTE_UNUSED, siginfo_t *siginfo ATTRIBUTE_UNUSED, void *ucontext ATTRIBUTE_UNUSED) {
// Read perf_event sample values
struct read_format rf;
int to_read_bytes = sizeof(struct read_format);
int read_bytes = 0;
do {
int r = read(sample_fd_[0], ((char*)(&rf))+read_bytes, to_read_bytes);
if (r == to_read_bytes) {
break;
} else if (r == -1) {
LOG(ERROR) << "nanoscope: error get clock time";
return;
} else {
read_bytes += r;
to_read_bytes -= r;
}
} while (true);
// Read thread CPU time
struct timespec thread_cpu_time;
if(clock_gettime(CLOCK_THREAD_CPUTIME_ID, &thread_cpu_time) < 0){
LOG(ERROR) << "nanoscope: error get clock time";
}
sampling_thread_ -> TimerHandler(thread_cpu_time.tv_sec * 1e+9 + thread_cpu_time.tv_nsec,
rf.values[0].value, rf.values[1].value, rf.values[2].value);
// Restart timer
if(sample_mode_ == kSamplePerf){
ioctl(perf_timer_fd_, PERF_EVENT_IOC_RESET, 0);
ioctl(perf_timer_fd_, PERF_EVENT_IOC_REFRESH, 1);
}
}
void NanoscopeSampler::install_sig_handler() {
struct sigaction sa;
memset(&sa, 0, sizeof(struct sigaction));
sa.sa_sigaction = signal_handler;
sa.sa_flags = SA_RESTART | SA_SIGINFO;
if (sigaction(SIGTIMER, &sa, NULL) < 0) {
LOG(ERROR) << "nanoscope: Error setting up signal handler.";
return;
}
LOG(INFO) << "nanoscope: set up sig handler for SIGTIMER";
}
#endif
void NanoscopeSampler::StartSampling(Thread* t, SampleMode sample_mode){
sampling_thread_ = t;
sample_mode_ = sample_mode;
#if defined(__ANDROID__)
// Set up sampling
install_sig_handler();
set_up_timer();
// Set up perf_event counters used to gather sampling data
// All counters are in the same perf_event group, with the leader being the first counter
// so that we can read all of them at the same time
set_up_sample_counter(COUNTER_TYPE_MAJOR_PAGE_FAULTS, -1);
set_up_sample_counter(COUNTER_TYPE_MINOR_PAGE_FAULTS, sample_fd_[0]);
set_up_sample_counter(COUNTER_TYPE_CONTEXT_SWITCHES, sample_fd_[0]);
// Enable allocation stats counter
Runtime::Current()->SetStatsEnabled(true);
// Starts all counters
if(sample_mode_ == kSamplePerf){
ioctl(perf_timer_fd_, PERF_EVENT_IOC_RESET, 0);
ioctl(perf_timer_fd_, PERF_EVENT_IOC_REFRESH, 1);
}
ioctl(sample_fd_[0], PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP);
ioctl(sample_fd_[0], PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP);
#endif
}
void NanoscopeSampler::StopSampling(){
#if defined(__ANDROID__)
if(sample_mode_ == kSamplePerf){
// Disable allocation stats counter
Runtime::Current()->SetStatsEnabled(false);
// Delete perf_event counter that acts as a timer
ioctl(perf_timer_fd_, PERF_EVENT_IOC_DISABLE, 0);
close(perf_timer_fd_);
perf_timer_fd_ = 0;
munmap(perf_timer_page_, 2 * PERF_PAGE_SIZE);
perf_timer_page_ = NULL;
// Delete perf_event counters used to gather sampling data
ioctl(sample_fd_[0], PERF_EVENT_IOC_DISABLE, 0);
for(int i = 0; i < COUNTER_TYPE_LIMIT; i++){
close(sample_fd_[i]);
sample_fd_[i] = 0;
}
} else if(sample_mode_ == kSampleCpu) {
// Disable allocation stats counter
Runtime::Current()->SetStatsEnabled(false);
// Delete timer_settime timer
timer_delete(timer_id_);
// Delete perf_event counters used to gather sampling data
ioctl(sample_fd_[0], PERF_EVENT_IOC_DISABLE, 0);
for(int i = 0; i < COUNTER_TYPE_LIMIT; i++){
close(sample_fd_[i]);
sample_fd_[i] = 0;
}
}
#endif
}
}