Coverage for /Syzygy/agent/basic_block_entry/basic_block_entry_unittest.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
89.8%1771970.C++test

Line-by-line coverage:

   1    :  // Copyright 2012 Google Inc. All Rights Reserved.
   2    :  //
   3    :  // Licensed under the Apache License, Version 2.0 (the "License");
   4    :  // you may not use this file except in compliance with the License.
   5    :  // You may obtain a copy of the License at
   6    :  //
   7    :  //     http://www.apache.org/licenses/LICENSE-2.0
   8    :  //
   9    :  // Unless required by applicable law or agreed to in writing, software
  10    :  // distributed under the License is distributed on an "AS IS" BASIS,
  11    :  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12    :  // See the License for the specific language governing permissions and
  13    :  // limitations under the License.
  14    :  //
  15    :  // BasicBlockEntry trace agent unit-tests.
  16    :  
  17    :  #include "syzygy/agent/basic_block_entry/basic_block_entry.h"
  18    :  
  19    :  #include "base/file_util.h"
  20    :  #include "base/files/scoped_temp_dir.h"
  21    :  #include "gmock/gmock.h"
  22    :  #include "gtest/gtest.h"
  23    :  #include "syzygy/common/indexed_frequency_data.h"
  24    :  #include "syzygy/trace/common/unittest_util.h"
  25    :  #include "syzygy/trace/parse/unittest_util.h"
  26    :  
  27    :  // There's a quick and dirty way to get the HMODULE of this module for MS
  28    :  // linkers. HINSTANCE == HMODULE == &__ImageBase;
  29    :  // See http://blogs.msdn.com/b/oldnewthing/archive/2004/10/25/247180.aspx
  30    :  EXTERN_C IMAGE_DOS_HEADER __ImageBase;
  31    :  
  32    :  namespace agent {
  33    :  namespace basic_block_entry {
  34    :  
  35    :  namespace {
  36    :  
  37    :  using ::common::IndexedFrequencyData;
  38    :  using testing::_;
  39    :  using testing::StrictMockParseEventHandler;
  40    :  using trace::parser::Parser;
  41    :  
  42    :  // This is the name of the agent DLL.
  43    :  const wchar_t kBasicBlockEntryClientDll[] = L"basic_block_entry_client.dll";
  44    :  
  45    :  // The number of basic blocks we'll work with for these tests.
  46    :  const uint32 kNumBasicBlocks = 2;
  47    :  
  48    :  // The module defining this lib/executable.
  49    :  const HMODULE kThisModule = reinterpret_cast<HMODULE>(&__ImageBase);
  50    :  
  51    :  // A helper to match modules by base address.
  52  E :  MATCHER_P(ModuleAtAddress, module, "") {
  53  E :    return arg->module_base_addr == module;
  54  E :  }
  55    :  
  56    :  // A helper to match basic-block frequency results to expectations.
  57  E :  MATCHER_P3(FrequencyDataMatches, module, bb_count, bb_freqs, "") {
  58  E :    if (arg->module_base_addr != module)
  59  i :      return false;
  60    :  
  61  E :    if (arg->frequency_size != sizeof(uint32))
  62  i :      return false;
  63    :  
  64  E :    if (arg->num_entries != bb_count)
  65  i :      return false;
  66    :  
  67  E :    return ::memcmp(bb_freqs, arg->frequency_data, bb_count) == 0;
  68  E :  }
  69    :  
  70    :  // The test fixture for the basic-block entry agent.
  71    :  class BasicBlockEntryTest : public testing::Test {
  72    :   public:
  73  E :    BasicBlockEntryTest()
  74    :        : agent_module_(NULL) {
  75  E :      module_data_.agent_id = ::common::kBasicBlockEntryAgentId;
  76  E :      module_data_.data_type = ::common::IndexedFrequencyData::BASIC_BLOCK_ENTRY;
  77  E :      module_data_.version = ::common::kBasicBlockFrequencyDataVersion;
  78  E :      module_data_.tls_index = TLS_OUT_OF_INDEXES;
  79  E :      module_data_.initialization_attempted = 0U;
  80  E :      module_data_.num_entries = kNumBasicBlocks;
  81  E :      module_data_.frequency_data = default_frequency_data_;
  82  E :      ::memset(&default_frequency_data_, 0, sizeof(default_frequency_data_));
  83  E :    }
  84    :  
  85  E :    virtual void SetUp() OVERRIDE {
  86  E :      testing::Test::SetUp();
  87  E :      ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
  88  E :      service_.SetEnvironment();
  89  E :    }
  90    :  
  91  E :    virtual void TearDown() OVERRIDE {
  92  E :      UnloadDll();
  93  E :      service_.Stop();
  94  E :    }
  95    :  
  96  E :    void StartService() {
  97  E :      service_.Start(temp_dir_.path());
  98  E :    }
  99    :  
 100  E :    void StopService() {
 101  E :      service_.Stop();
 102  E :    }
 103    :  
 104  E :    void ReplayLogs(size_t files_expected) {
 105    :      // Stop the service if it's running.
 106  E :      ASSERT_NO_FATAL_FAILURE(StopService());
 107    :  
 108  E :      Parser parser;
 109  E :      ASSERT_TRUE(parser.Init(&handler_));
 110    :  
 111    :      // Queue up the trace file(s) we engendered.
 112    :      file_util::FileEnumerator enumerator(temp_dir_.path(),
 113    :                                           false,
 114  E :                                           file_util::FileEnumerator::FILES);
 115  E :      size_t num_files = 0;
 116  E :      while (true) {
 117  E :        base::FilePath trace_file = enumerator.Next();
 118  E :        if (trace_file.empty())
 119  E :          break;
 120  E :        ASSERT_TRUE(parser.OpenTraceFile(trace_file));
 121  E :        ++num_files;
 122  E :      }
 123    :  
 124  E :      EXPECT_EQ(files_expected, num_files);
 125    :  
 126  E :      if (num_files > 0)
 127  E :        ASSERT_TRUE(parser.Consume());
 128  E :    }
 129    :  
 130  E :    void LoadDll() {
 131  E :      ASSERT_EQ(NULL, agent_module_);
 132  E :      ASSERT_EQ(NULL, basic_block_enter_stub_);
 133  E :      ASSERT_EQ(NULL, ::GetModuleHandle(kBasicBlockEntryClientDll));
 134    :  
 135  E :      agent_module_ = ::LoadLibrary(kBasicBlockEntryClientDll);
 136  E :      ASSERT_TRUE(agent_module_ != NULL);
 137    :  
 138    :      basic_block_enter_stub_ =
 139  E :          ::GetProcAddress(agent_module_, "_increment_indexed_freq_data");
 140  E :      ASSERT_TRUE(basic_block_enter_stub_ != NULL);
 141    :  
 142    :      indirect_penter_dllmain_stub_ =
 143  E :          ::GetProcAddress(agent_module_, "_indirect_penter_dllmain");
 144  E :      ASSERT_TRUE(indirect_penter_dllmain_stub_ != NULL);
 145    :  
 146    :      indirect_penter_exemain_stub_ =
 147  E :          ::GetProcAddress(agent_module_, "_indirect_penter_exemain");
 148  E :      ASSERT_TRUE(indirect_penter_exemain_stub_ != NULL);
 149    :  
 150    :      get_raw_frequency_data_stub_ =
 151  E :          ::GetProcAddress(agent_module_, "GetRawFrequencyData");
 152  E :      ASSERT_TRUE(get_raw_frequency_data_stub_ != NULL);
 153  E :    }
 154    :  
 155  E :    void UnloadDll() {
 156  E :      if (agent_module_ != NULL) {
 157  E :        ASSERT_TRUE(::FreeLibrary(agent_module_));
 158  E :        agent_module_ = NULL;
 159  E :        basic_block_enter_stub_ = NULL;
 160  E :        indirect_penter_dllmain_stub_ = NULL;
 161  E :        indirect_penter_exemain_stub_ = NULL;
 162  E :        get_raw_frequency_data_stub_ = NULL;
 163    :      }
 164  E :    }
 165    :  
 166    :   protected:
 167    :    static BOOL WINAPI DllMain(HMODULE module, DWORD reason, LPVOID reserved);
 168    :    static BOOL WINAPI DllMainThunk(
 169    :        HMODULE module, DWORD reason, LPVOID reserved);
 170    :    static int __cdecl ExeMain();
 171    :    static int __cdecl ExeMainThunk();
 172    :    static int __cdecl GetFrequencyDataThunk();
 173    :  
 174  E :     void SimulateModuleEvent(DWORD reason) {
 175  E :       DllMainThunk(kThisModule, reason, NULL);
 176  E :     }
 177    :  
 178  i :     void SimulateBasicBlockEntry(uint32 basic_block_id) {
 179    :       __asm {
 180  i :         push basic_block_id
 181  i :         push offset module_data_
 182  i :         call basic_block_enter_stub_
 183    :       }
 184  i :     }
 185    :  
 186    :    // The directory where trace file output will be written.
 187    :    base::ScopedTempDir temp_dir_;
 188    :  
 189    :    // The handler to which the trace file parser will delegate events.
 190    :    StrictMockParseEventHandler handler_;
 191    :  
 192    :    // Our call trace service process instance.
 193    :    testing::CallTraceService service_;
 194    :  
 195    :    // The basic-block entry client module.
 196    :    HMODULE agent_module_;
 197    :  
 198    :    // This will be a stand-in for the (usually statically allocated) trace
 199    :    // data which would have been referenced by the instrumentation.
 200    :    static IndexedFrequencyData module_data_;
 201    :  
 202    :    // This will be a stand-in for the (usually statically allocated) fall-back
 203    :    // frequency to which module_data_.frequency_data will point.
 204    :    static uint32 default_frequency_data_[kNumBasicBlocks];
 205    :  
 206    :    // The basic-block entry entrance hook.
 207    :    static FARPROC basic_block_enter_stub_;
 208    :  
 209    :    // The DllMain entry stub.
 210    :    static FARPROC indirect_penter_dllmain_stub_;
 211    :  
 212    :    // The ExeMain entry stub.
 213    :    static FARPROC indirect_penter_exemain_stub_;
 214    :  
 215    :    // The entry stub to get a pointer to data frequency.
 216    :    static FARPROC get_raw_frequency_data_stub_;
 217    :  };
 218    :  
 219    :  BOOL WINAPI BasicBlockEntryTest::DllMain(
 220  E :      HMODULE module, DWORD reason, LPVOID reserved) {
 221  E :    return TRUE;
 222  E :  }
 223    :  
 224    :  BOOL __declspec(naked) WINAPI BasicBlockEntryTest::DllMainThunk(
 225  i :      HMODULE module, DWORD reason, LPVOID reserved) {
 226    :    __asm {
 227  i :      push offset module_data_
 228  i :      push DllMain
 229  i :      jmp indirect_penter_dllmain_stub_
 230    :    }
 231    :  }
 232    :  
 233  E :  int __cdecl BasicBlockEntryTest::ExeMain() {
 234  E :    return 0;
 235  E :  }
 236    :  
 237  i :  int __declspec(naked) __cdecl BasicBlockEntryTest::GetFrequencyDataThunk() {
 238    :    __asm {
 239  i :      push offset module_data_
 240  i :      call get_raw_frequency_data_stub_
 241  i :      ret
 242    :    }
 243    :  }
 244    :  
 245  i :  BOOL __declspec(naked) __cdecl BasicBlockEntryTest::ExeMainThunk() {
 246    :    __asm {
 247  i :      push offset module_data_
 248  i :      push ExeMain
 249  i :      jmp indirect_penter_exemain_stub_
 250    :    }
 251    :  }
 252    :  
 253    :  IndexedFrequencyData BasicBlockEntryTest::module_data_ = {};
 254    :  uint32 BasicBlockEntryTest::default_frequency_data_[] = {};
 255    :  FARPROC BasicBlockEntryTest::basic_block_enter_stub_ = NULL;
 256    :  FARPROC BasicBlockEntryTest::indirect_penter_dllmain_stub_ = NULL;
 257    :  FARPROC BasicBlockEntryTest::indirect_penter_exemain_stub_ = NULL;
 258    :  FARPROC BasicBlockEntryTest::get_raw_frequency_data_stub_ = NULL;
 259    :  
 260    :  }  // namespace
 261    :  
 262  E :  TEST_F(BasicBlockEntryTest, NoServerNoCrash) {
 263    :    // Load the agent dll.
 264  E :    ASSERT_NO_FATAL_FAILURE(LoadDll());
 265    :  
 266    :    // Simulate the process attach event.
 267  E :    SimulateModuleEvent(DLL_PROCESS_ATTACH);
 268    :  
 269    :    // Validate that it only modified the tls_index and initialization_attempted
 270    :    // values.
 271  E :    ASSERT_EQ(::common::kBasicBlockEntryAgentId, module_data_.agent_id);
 272  E :    ASSERT_EQ(::common::kBasicBlockFrequencyDataVersion, module_data_.version);
 273  E :    ASSERT_EQ(IndexedFrequencyData::BASIC_BLOCK_ENTRY, module_data_.data_type);
 274  E :    ASSERT_NE(TLS_OUT_OF_INDEXES, module_data_.tls_index);
 275  E :    ASSERT_NE(0U, module_data_.initialization_attempted);
 276  E :    ASSERT_EQ(kNumBasicBlocks, module_data_.num_entries);
 277  E :    ASSERT_EQ(default_frequency_data_, module_data_.frequency_data);
 278    :  
 279    :    // Visiting an initial basic-block should not fail. It should initialize the
 280    :    // TLS index, map the frequency data to the default array, and increment the
 281    :    // call count in the default array.
 282  E :    SimulateBasicBlockEntry(0);
 283  E :    ASSERT_EQ(1U, default_frequency_data_[0]);
 284  E :    ASSERT_EQ(0U, default_frequency_data_[1]);
 285    :  
 286    :    // Re-visiting the same basic-block should only update the frequency array.
 287  E :    DWORD new_tls_index = module_data_.tls_index;
 288  E :    SimulateBasicBlockEntry(0);
 289  E :    ASSERT_EQ(new_tls_index, module_data_.tls_index);
 290  E :    ASSERT_EQ(2U, default_frequency_data_[0]);
 291  E :    ASSERT_EQ(0U, default_frequency_data_[1]);
 292    :  
 293    :    // Visiting a different basic-block should only update the frequency array.
 294  E :    SimulateBasicBlockEntry(1);
 295  E :    ASSERT_EQ(new_tls_index, module_data_.tls_index);
 296  E :    ASSERT_EQ(2U, default_frequency_data_[0]);
 297  E :    ASSERT_EQ(1U, default_frequency_data_[1]);
 298    :  
 299    :    // Simulate the process detach event.
 300  E :    SimulateModuleEvent(DLL_PROCESS_DETACH);
 301    :  
 302    :    // Unload the DLL.
 303  E :    ASSERT_NO_FATAL_FAILURE(UnloadDll());
 304    :  
 305    :    // Replay the log. There should be none as we didn't start the service.
 306  E :    ASSERT_NO_FATAL_FAILURE(ReplayLogs(0));
 307  E :  }
 308    :  
 309  E :  TEST_F(BasicBlockEntryTest, SingleThreadedDllBasicBlockEvents) {
 310  E :    ASSERT_NO_FATAL_FAILURE(StartService());
 311  E :    ASSERT_NO_FATAL_FAILURE(LoadDll());
 312    :  
 313    :    // Simulate the process attach event.
 314  E :    SimulateModuleEvent(DLL_PROCESS_ATTACH);
 315    :  
 316    :    // Validate that it does not modify any of our initialization values.
 317  E :    ASSERT_EQ(::common::kBasicBlockEntryAgentId, module_data_.agent_id);
 318  E :    ASSERT_EQ(::common::kBasicBlockFrequencyDataVersion, module_data_.version);
 319  E :    ASSERT_EQ(IndexedFrequencyData::BASIC_BLOCK_ENTRY, module_data_.data_type);
 320  E :    ASSERT_NE(TLS_OUT_OF_INDEXES, module_data_.tls_index);
 321  E :    ASSERT_NE(0U, module_data_.initialization_attempted);
 322  E :    ASSERT_EQ(kNumBasicBlocks, module_data_.num_entries);
 323  E :    ASSERT_EQ(default_frequency_data_, module_data_.frequency_data);
 324    :  
 325    :    // Visiting an initial basic-block should not fail. It should initialize the
 326    :    // TLS index, allocate a frequency map for this thread, and increment the
 327    :    // call count in the allocated frequency map. The default frequency data
 328    :    // should be left unchanged.
 329  E :    SimulateBasicBlockEntry(0);
 330  E :    ASSERT_EQ(default_frequency_data_, module_data_.frequency_data);
 331  E :    ASSERT_EQ(0U, default_frequency_data_[0]);
 332    :  
 333    :    // Make a few more calls, just to keep things interesting.
 334  E :    SimulateBasicBlockEntry(0);
 335  E :    SimulateBasicBlockEntry(1);
 336  E :    SimulateBasicBlockEntry(0);
 337    :  
 338    :    // Simulate the process attach event.
 339  E :    SimulateModuleEvent(DLL_PROCESS_DETACH);
 340    :  
 341    :    // Unload the DLL and stop the service.
 342  E :    ASSERT_NO_FATAL_FAILURE(UnloadDll());
 343    :  
 344  E :    HMODULE self = ::GetModuleHandle(NULL);
 345  E :    DWORD process_id = ::GetCurrentProcessId();
 346  E :    DWORD thread_id = ::GetCurrentThreadId();
 347    :  
 348    :    static const uint32 kExpectedFrequencyData[kNumBasicBlocks] = { 3, 1 };
 349    :  
 350    :    // Set up expectations for what should be in the trace.
 351  E :    EXPECT_CALL(handler_, OnProcessStarted(_, process_id, _));
 352    :    EXPECT_CALL(handler_, OnProcessAttach(_,
 353    :                                          process_id,
 354    :                                          thread_id,
 355  E :                                          ModuleAtAddress(self)));;
 356    :    EXPECT_CALL(handler_, OnIndexedFrequency(
 357    :        _,
 358    :        process_id,
 359    :        thread_id,
 360  E :        FrequencyDataMatches(self, kNumBasicBlocks, kExpectedFrequencyData)));
 361  E :    EXPECT_CALL(handler_, OnProcessEnded(_, process_id));
 362    :  
 363    :    // Replay the log.
 364  E :    ASSERT_NO_FATAL_FAILURE(ReplayLogs(1));
 365  E :  }
 366    :  
 367  E :  TEST_F(BasicBlockEntryTest, SingleThreadedExeBasicBlockEvents) {
 368  E :    ASSERT_NO_FATAL_FAILURE(StartService());
 369  E :    ASSERT_NO_FATAL_FAILURE(LoadDll());
 370    :  
 371    :    // Simulate the process attach event.
 372  E :    ExeMainThunk();
 373    :  
 374    :    // Validate that it does not modify any of our initialization values.
 375  E :    ASSERT_EQ(::common::kBasicBlockEntryAgentId, module_data_.agent_id);
 376  E :    ASSERT_EQ(::common::kBasicBlockFrequencyDataVersion, module_data_.version);
 377  E :    ASSERT_EQ(IndexedFrequencyData::BASIC_BLOCK_ENTRY, module_data_.data_type);
 378  E :    ASSERT_NE(TLS_OUT_OF_INDEXES, module_data_.tls_index);
 379  E :    ASSERT_NE(0U, module_data_.initialization_attempted);
 380  E :    ASSERT_EQ(kNumBasicBlocks, module_data_.num_entries);
 381  E :    ASSERT_EQ(default_frequency_data_, module_data_.frequency_data);
 382    :  
 383    :    // Visiting an initial basic-block should not fail. It should initialize the
 384    :    // TLS index, allocate a frequency map for this thread, and increment the
 385    :    // call count in the allocated frequency map. The default frequency data
 386    :    // should be left unchanged.
 387  E :    SimulateBasicBlockEntry(0);
 388  E :    ASSERT_EQ(default_frequency_data_, module_data_.frequency_data);
 389  E :    ASSERT_EQ(0U, default_frequency_data_[0]);
 390    :  
 391    :    // Make a few more calls, just to keep things interesting.
 392  E :    SimulateBasicBlockEntry(0);
 393  E :    SimulateBasicBlockEntry(1);
 394  E :    SimulateBasicBlockEntry(0);
 395    :  
 396    :    // Unload the DLL and stop the service.
 397  E :    ASSERT_NO_FATAL_FAILURE(UnloadDll());
 398    :  
 399  E :    HMODULE self = ::GetModuleHandle(NULL);
 400  E :    DWORD process_id = ::GetCurrentProcessId();
 401  E :    DWORD thread_id = ::GetCurrentThreadId();
 402    :  
 403    :    static const uint32 kExpectedFrequencyData[kNumBasicBlocks] = { 3, 1 };
 404    :  
 405    :    // Set up expectations for what should be in the trace.
 406  E :    EXPECT_CALL(handler_, OnProcessStarted(_, process_id, _));
 407    :    EXPECT_CALL(handler_, OnProcessAttach(_,
 408    :                                          process_id,
 409    :                                          thread_id,
 410  E :                                          ModuleAtAddress(self)));;
 411    :    EXPECT_CALL(handler_, OnIndexedFrequency(
 412    :        _,
 413    :        process_id,
 414    :        thread_id,
 415  E :        FrequencyDataMatches(self, kNumBasicBlocks, kExpectedFrequencyData)));
 416  E :    EXPECT_CALL(handler_, OnProcessEnded(_, process_id));
 417    :  
 418    :    // Replay the log.
 419  E :    ASSERT_NO_FATAL_FAILURE(ReplayLogs(1));
 420  E :  }
 421    :  
 422  E :  TEST_F(BasicBlockEntryTest, InvokeGetFrequencyData) {
 423  E :    ASSERT_NO_FATAL_FAILURE(StartService());
 424  E :    ASSERT_NO_FATAL_FAILURE(LoadDll());
 425    :  
 426    :    // Simulate the process attach event.
 427  E :    ExeMainThunk();
 428    :  
 429    :    // Check creation of a buffer on first call.
 430  E :    EXPECT_TRUE(::TlsGetValue(module_data_.tls_index) == NULL);
 431  E :    uint32* data1 = reinterpret_cast<uint32*>(GetFrequencyDataThunk());
 432  E :    EXPECT_TRUE(data1 != NULL);
 433  E :    EXPECT_TRUE(::TlsGetValue(module_data_.tls_index) != NULL);
 434    :  
 435    :    // Next calls should return the same buffer.
 436  E :    uint32* data2 = reinterpret_cast<uint32*>(GetFrequencyDataThunk());
 437  E :    EXPECT_EQ(data1, data2);
 438    :  
 439    :    // Unload the DLL and stop the service.
 440  E :    ASSERT_NO_FATAL_FAILURE(UnloadDll());
 441  E :  }
 442    :  
 443    :  // TODO(rogerm): Add a decent multi-thread test case.
 444    :  
 445    :  }  // namespace basic_block_entry
 446    :  }  // namespace agent

Coverage information generated Thu Jul 04 09:34:53 2013.