This source file includes following definitions.
- SetJoinable
- Start
- Join
- DoRun
- Delay
- IsTimerEnabled
- Run
- threads_have_separate_timers
- stop_work
- set_stop_work
- Run
- Run
- TickCounter
- SetUpTestCase
- SetUp
- TearDown
- RegisterThread
- StartWorker
- StopWorker
- IsSignalEnabled
- GetCallbackCount
- GetInterruptCount
- VerifyRegistration
- VerifyUnregistration
- VerifyDisabled
- RegisterCallback
- UnregisterCallback
- RUN_ALL_TESTS
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- main
#include "config.h"
#include "profile-handler.h"
#include <assert.h>
#include <pthread.h>
#include <sys/time.h>
#include <time.h>
#include "base/logging.h"
#include "base/simple_mutex.h"
#define TEST_F(cls, fn) void cls :: fn()
DEFINE_bool(test_profiler_enabled, true,
"expect profiler to be enabled during tests");
DEFINE_bool(test_profiler_signal_handler, true,
"check profiler signal handler during tests");
namespace {
class Thread {
public:
Thread() : joinable_(false) { }
void SetJoinable(bool value) { joinable_ = value; }
void Start() {
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, joinable_ ? PTHREAD_CREATE_JOINABLE
: PTHREAD_CREATE_DETACHED);
pthread_create(&thread_, &attr, &DoRun, this);
pthread_attr_destroy(&attr);
}
void Join() {
assert(joinable_);
pthread_join(thread_, NULL);
}
virtual void Run() = 0;
private:
static void* DoRun(void* cls) {
ProfileHandlerRegisterThread();
reinterpret_cast<Thread*>(cls)->Run();
return NULL;
}
pthread_t thread_;
bool joinable_;
};
int kSleepInterval = 200000000;
int kTimerResetInterval = 5000000;
static bool timer_separate_ = false;
static int timer_type_ = ITIMER_PROF;
static int signal_number_ = SIGPROF;
void Delay(int delay_ns) {
static const int kNumNSecInSecond = 1000000000;
EXPECT_LT(delay_ns, kNumNSecInSecond);
struct timespec delay = { 0, delay_ns };
nanosleep(&delay, 0);
}
bool IsTimerEnabled() {
itimerval current_timer;
EXPECT_EQ(0, getitimer(timer_type_, ¤t_timer));
if ((current_timer.it_value.tv_sec == 0) &&
(current_timer.it_value.tv_usec != 0)) {
Delay(kTimerResetInterval);
EXPECT_EQ(0, getitimer(timer_type_, ¤t_timer));
}
return (current_timer.it_value.tv_sec != 0 ||
current_timer.it_value.tv_usec != 0);
}
class VirtualTimerGetterThread : public Thread {
public:
VirtualTimerGetterThread() {
memset(&virtual_timer_, 0, sizeof virtual_timer_);
}
struct itimerval virtual_timer_;
private:
void Run() {
CHECK_EQ(0, getitimer(ITIMER_VIRTUAL, &virtual_timer_));
}
};
static bool threads_have_separate_timers() {
struct itimerval new_timer_val;
memset(&new_timer_val, 0, sizeof new_timer_val);
new_timer_val.it_value.tv_sec = 1000000;
CHECK_EQ(0, setitimer(ITIMER_VIRTUAL, &new_timer_val, NULL));
VirtualTimerGetterThread thread;
thread.SetJoinable(true);
thread.Start();
thread.Join();
memset(&new_timer_val, 0, sizeof new_timer_val);
CHECK_EQ(0, setitimer(ITIMER_VIRTUAL, &new_timer_val, NULL));
bool target_timer_enabled = (thread.virtual_timer_.it_value.tv_sec != 0 ||
thread.virtual_timer_.it_value.tv_usec != 0);
if (!target_timer_enabled) {
LOG(INFO, "threads have separate timers");
return true;
} else {
LOG(INFO, "threads have shared timers");
return false;
}
}
class BusyThread : public Thread {
public:
BusyThread() : stop_work_(false) {
}
bool stop_work() {
MutexLock lock(&mu_);
return stop_work_;
}
void set_stop_work(bool stop_work) {
MutexLock lock(&mu_);
stop_work_ = stop_work;
}
private:
Mutex mu_;
bool stop_work_;
void Run() {
while (!stop_work()) {
}
EXPECT_TRUE(!timer_separate_ || IsTimerEnabled());
}
};
class NullThread : public Thread {
private:
void Run() {
EXPECT_TRUE(!timer_separate_ || IsTimerEnabled());
}
};
static void TickCounter(int sig, siginfo_t* sig_info, void *vuc,
void* tick_counter) {
int* counter = static_cast<int*>(tick_counter);
++(*counter);
}
class ProfileHandlerTest {
protected:
static void SetUpTestCase() {
timer_type_ = (getenv("CPUPROFILE_REALTIME") ? ITIMER_REAL : ITIMER_PROF);
signal_number_ = (getenv("CPUPROFILE_REALTIME") ? SIGALRM : SIGPROF);
timer_separate_ = threads_have_separate_timers();
Delay(kTimerResetInterval);
}
virtual void SetUp() {
ProfileHandlerReset();
EXPECT_EQ(0, GetCallbackCount());
VerifyDisabled();
RegisterThread();
RegisterThread();
VerifyDisabled();
StartWorker();
}
virtual void TearDown() {
ProfileHandlerReset();
StopWorker();
}
void RegisterThread() {
NullThread t;
t.SetJoinable(true);
t.Start();
t.Join();
}
void StartWorker() {
busy_worker_ = new BusyThread();
busy_worker_->SetJoinable(true);
busy_worker_->Start();
Delay(kSleepInterval);
}
void StopWorker() {
busy_worker_->set_stop_work(true);
busy_worker_->Join();
delete busy_worker_;
}
bool IsSignalEnabled() {
struct sigaction sa;
CHECK_EQ(sigaction(signal_number_, NULL, &sa), 0);
return ((sa.sa_handler == SIG_IGN) || (sa.sa_handler == SIG_DFL)) ?
false : true;
}
uint32 GetCallbackCount() {
ProfileHandlerState state;
ProfileHandlerGetState(&state);
return state.callback_count;
}
uint64 GetInterruptCount() {
ProfileHandlerState state;
ProfileHandlerGetState(&state);
return state.interrupts;
}
void VerifyRegistration(const int& tick_counter) {
EXPECT_GT(GetCallbackCount(), 0);
EXPECT_EQ(FLAGS_test_profiler_enabled, IsTimerEnabled());
if (FLAGS_test_profiler_signal_handler) {
EXPECT_EQ(FLAGS_test_profiler_enabled, IsSignalEnabled());
}
uint64 interrupts_before = GetInterruptCount();
int old_tick_count = tick_counter;
Delay(kSleepInterval);
int new_tick_count = tick_counter;
uint64 interrupts_after = GetInterruptCount();
if (FLAGS_test_profiler_enabled) {
EXPECT_GT(new_tick_count, old_tick_count);
EXPECT_GT(interrupts_after, interrupts_before);
} else {
EXPECT_EQ(new_tick_count, old_tick_count);
EXPECT_EQ(interrupts_after, interrupts_before);
}
}
void VerifyUnregistration(const int& tick_counter) {
int old_tick_count = tick_counter;
Delay(kSleepInterval);
int new_tick_count = tick_counter;
EXPECT_EQ(old_tick_count, new_tick_count);
if (GetCallbackCount() == 0) {
if (FLAGS_test_profiler_signal_handler) {
EXPECT_FALSE(IsSignalEnabled());
}
if (timer_separate_) {
EXPECT_TRUE(IsTimerEnabled());
} else {
EXPECT_FALSE(IsTimerEnabled());
}
}
}
void VerifyDisabled() {
if (FLAGS_test_profiler_signal_handler) {
EXPECT_FALSE(IsSignalEnabled());
}
EXPECT_EQ(0, GetCallbackCount());
if (timer_separate_) {
EXPECT_TRUE(IsTimerEnabled());
} else {
EXPECT_FALSE(IsTimerEnabled());
}
uint64 interrupts_before = GetInterruptCount();
Delay(kSleepInterval);
uint64 interrupts_after = GetInterruptCount();
EXPECT_EQ(interrupts_before, interrupts_after);
}
ProfileHandlerToken* RegisterCallback(void* callback_arg) {
ProfileHandlerToken* token = ProfileHandlerRegisterCallback(
TickCounter, callback_arg);
Delay(kTimerResetInterval);
return token;
}
void UnregisterCallback(ProfileHandlerToken* token) {
ProfileHandlerUnregisterCallback(token);
Delay(kTimerResetInterval);
}
BusyThread* busy_worker_;
private:
void RegisterUnregisterCallback();
void MultipleCallbacks();
void Reset();
void RegisterCallbackBeforeThread();
public:
#define RUN(test) do { \
printf("Running %s\n", #test); \
ProfileHandlerTest pht; \
pht.SetUp(); \
pht.test(); \
pht.TearDown(); \
} while (0)
static int RUN_ALL_TESTS() {
SetUpTestCase();
RUN(RegisterUnregisterCallback);
RUN(MultipleCallbacks);
RUN(Reset);
RUN(RegisterCallbackBeforeThread);
printf("Done\n");
return 0;
}
};
TEST_F(ProfileHandlerTest, RegisterUnregisterCallback) {
int tick_count = 0;
ProfileHandlerToken* token = RegisterCallback(&tick_count);
VerifyRegistration(tick_count);
UnregisterCallback(token);
VerifyUnregistration(tick_count);
}
TEST_F(ProfileHandlerTest, MultipleCallbacks) {
int first_tick_count;
ProfileHandlerToken* token1 = RegisterCallback(&first_tick_count);
VerifyRegistration(first_tick_count);
EXPECT_EQ(1, GetCallbackCount());
int second_tick_count;
ProfileHandlerToken* token2 = RegisterCallback(&second_tick_count);
VerifyRegistration(second_tick_count);
EXPECT_EQ(2, GetCallbackCount());
UnregisterCallback(token1);
VerifyUnregistration(first_tick_count);
EXPECT_EQ(1, GetCallbackCount());
VerifyRegistration(second_tick_count);
UnregisterCallback(token2);
VerifyUnregistration(second_tick_count);
EXPECT_EQ(0, GetCallbackCount());
VerifyDisabled();
}
TEST_F(ProfileHandlerTest, Reset) {
VerifyDisabled();
int first_tick_count;
RegisterCallback(&first_tick_count);
VerifyRegistration(first_tick_count);
EXPECT_EQ(1, GetCallbackCount());
int second_tick_count;
RegisterCallback(&second_tick_count);
VerifyRegistration(second_tick_count);
EXPECT_EQ(2, GetCallbackCount());
ProfileHandlerReset();
VerifyUnregistration(first_tick_count);
VerifyUnregistration(second_tick_count);
VerifyDisabled();
}
TEST_F(ProfileHandlerTest, RegisterCallbackBeforeThread) {
StopWorker();
ProfileHandlerReset();
EXPECT_EQ(0, GetCallbackCount());
VerifyDisabled();
StartWorker();
int tick_count;
RegisterCallback(&tick_count);
EXPECT_EQ(1, GetCallbackCount());
VerifyRegistration(tick_count);
RegisterThread();
EXPECT_EQ(1, GetCallbackCount());
EXPECT_EQ(FLAGS_test_profiler_enabled, IsTimerEnabled());
if (FLAGS_test_profiler_signal_handler) {
EXPECT_EQ(FLAGS_test_profiler_enabled, IsSignalEnabled());
}
}
}
int main(int argc, char** argv) {
return ProfileHandlerTest::RUN_ALL_TESTS();
}