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();
}