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

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
79.6%1561960.C++source

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    :  // Implementation of the basic-block entry counting agent library.
  16    :  
  17    :  #include "syzygy/agent/basic_block_entry/basic_block_entry.h"
  18    :  
  19    :  #include "base/at_exit.h"
  20    :  #include "base/command_line.h"
  21    :  #include "base/environment.h"
  22    :  #include "base/lazy_instance.h"
  23    :  #include "base/stringprintf.h"
  24    :  #include "base/utf_string_conversions.h"
  25    :  #include "base/memory/scoped_ptr.h"
  26    :  #include "sawbuck/common/com_utils.h"
  27    :  #include "syzygy/agent/common/process_utils.h"
  28    :  #include "syzygy/agent/common/scoped_last_error_keeper.h"
  29    :  #include "syzygy/common/logging.h"
  30    :  #include "syzygy/trace/protocol/call_trace_defs.h"
  31    :  
  32    :  // Save caller-save registers (eax, ecx, edx) and flags (eflags).
  33    :  #define BBENTRY_SAVE_REGISTERS  \
  34    :    __asm push eax  \
  35    :    __asm lahf  \
  36    :    __asm seto al  \
  37    :    __asm push eax  \
  38    :    __asm push ecx  \
  39    :    __asm push edx
  40    :  
  41    :  // Restore caller-save registers (eax, ecx, edx) and flags (eflags).
  42    :  #define BBENTRY_RESTORE_REGISTERS  \
  43    :    __asm pop edx  \
  44    :    __asm pop ecx  \
  45    :    __asm pop eax  \
  46    :    __asm add al, 0x7f  \
  47    :    __asm sahf  \
  48    :    __asm pop eax
  49    :  
  50    :  extern "C" uint32* _stdcall GetRawFrequencyData(
  51  E :      ::common::IndexedFrequencyData* data) {
  52  E :    DCHECK(data != NULL);
  53  E :    return agent::basic_block_entry::BasicBlockEntry::GetRawFrequencyData(data);
  54  E :  }
  55    :  
  56  i :  extern "C" void __declspec(naked) _increment_indexed_freq_data() {
  57    :    __asm {
  58    :      // This is expected to be called via instrumentation that looks like:
  59    :      //    push index
  60    :      //    push module_data
  61    :      //    call [_increment_indexed_freq_data]
  62    :      //
  63    :      // Stack: ... index, module_data, ret_addr.
  64    :  
  65    :      // Stash volatile registers.
  66  i :      BBENTRY_SAVE_REGISTERS
  67    :  
  68    :      // Stack: ... index, module_data, ret_addr, [4x register]
  69    :  
  70    :      // Push the original esp value onto the stack as the entry-hook data.
  71    :      // This gives the entry-hook a pointer to ret_addr, module_data and index.
  72  i :      lea eax, DWORD PTR[esp + 0x10]
  73  i :      push eax
  74    :  
  75    :      // Stack: ..., index, module_data, ret_addr, [4x register], esp, &ret_addr.
  76  i :      call agent::basic_block_entry::BasicBlockEntry::IncrementIndexedFreqDataHook
  77    :  
  78    :      // Stack: ... index, module_data, ret_addr, [4x register].
  79    :  
  80    :      // Restore volatile registers.
  81  i :      BBENTRY_RESTORE_REGISTERS
  82    :  
  83    :      // Stack: ... index, module_data, ret_addr.
  84    :  
  85    :      // Return to the address pushed by our caller, popping off the
  86    :      // index and module_data values from the stack.
  87  i :      ret 8
  88    :  
  89    :      // Stack: ...
  90    :    }
  91    :  }
  92    :  
  93  i :  extern "C" void __declspec(naked) _indirect_penter_dllmain() {
  94    :    __asm {
  95    :      // This is expected to be called via a thunk that looks like:
  96    :      //    push module_data
  97    :      //    push function
  98    :      //    jmp [_indirect_penter_dllmain]
  99    :      //
 100    :      // Stack: ... reserved, reason, module, ret_addr, module_data, function.
 101    :  
 102    :      // Stash volatile registers.
 103  i :      BBENTRY_SAVE_REGISTERS
 104    :  
 105    :      // Stack: ... reserved, reason, module, ret_addr, module_data, function,
 106    :      //        [4x register].
 107    :  
 108    :      // Push the original esp value onto the stack as the entry-hook data.
 109    :      // This gives the dll entry-hook a pointer to function, module_data,
 110    :      // ret_addr, module, reason and reserved.
 111  i :      lea eax, DWORD PTR[esp + 0x10]
 112  i :      push eax
 113    :  
 114    :      // Stack: ... reserved, reason, module, ret_addr, module_data, function,
 115    :      //        [4x register], &function.
 116    :  
 117  i :      call agent::basic_block_entry::BasicBlockEntry::DllMainEntryHook
 118    :  
 119    :      // Stack: ... reserved, reason, module, ret_addr, module_data, function,
 120    :      //        [4x register].
 121    :  
 122    :      // Restore volatile registers.
 123  i :      BBENTRY_RESTORE_REGISTERS
 124    :  
 125    :      // Stack: ... reserved, reason, module, ret_addr, module_data, function.
 126    :  
 127    :      // Return to the thunked function, popping module_data off the stack as
 128    :      // we go.
 129  i :      ret 4
 130    :  
 131    :      // Stack: ... reserved, reason, module, ret_addr.
 132    :    }
 133    :  }
 134    :  
 135  i :  extern "C" void __declspec(naked) _indirect_penter_exemain() {
 136    :    __asm {
 137    :      // This is expected to be called via a thunk that looks like:
 138    :      //    push module_data
 139    :      //    push function
 140    :      //    jmp [_indirect_penter_exe_main]
 141    :      //
 142    :      // Stack: ... ret_addr, module_data, function.
 143    :  
 144    :      // Stash volatile registers.
 145  i :      BBENTRY_SAVE_REGISTERS
 146    :  
 147    :      // Stack: ... ret_addr, module_data, function, [4x register].
 148    :  
 149    :      // Push the original esp value onto the stack as the entry-hook data.
 150    :      // This gives the exe entry-hook a pointer to function, module_data,
 151    :      // and ret_addr.
 152  i :      lea eax, DWORD PTR[esp + 0x10]
 153  i :      push eax
 154    :  
 155    :      // Stack: ... ret_addr, module_data, function, [4x register], frame.
 156    :  
 157  i :      call agent::basic_block_entry::BasicBlockEntry::ExeMainEntryHook
 158    :  
 159    :      // Stack: ... ret_addr, module_data, function, [4x register].
 160    :  
 161    :      // Restore volatile registers.
 162  i :      BBENTRY_RESTORE_REGISTERS
 163    :  
 164    :      // Stack: ... ret_addr, module_data, function.
 165    :  
 166    :      // Return to the thunked function, popping module_data off the stack as
 167    :      // we go.
 168  i :      ret 4
 169    :  
 170    :      // Stack: ... reserved, reason, module, ret_addr.
 171    :    }
 172    :  }
 173    :  
 174  E :  BOOL WINAPI DllMain(HMODULE instance, DWORD reason, LPVOID reserved) {
 175    :    // Our AtExit manager required by base.
 176    :    static base::AtExitManager* at_exit = NULL;
 177    :  
 178  E :    switch (reason) {
 179    :      case DLL_PROCESS_ATTACH:
 180  E :        DCHECK(at_exit == NULL);
 181  E :        at_exit = new base::AtExitManager();
 182    :  
 183  E :        CommandLine::Init(0, NULL);
 184  E :        common::InitLoggingForDll(L"basic_block_entry");
 185  E :        LOG(INFO) << "Initialized basic-block entry counting agent library.";
 186  E :        break;
 187    :  
 188    :      case DLL_THREAD_ATTACH:
 189  i :        break;
 190    :  
 191    :      case DLL_THREAD_DETACH:
 192  i :        break;
 193    :  
 194    :      case DLL_PROCESS_DETACH:
 195  E :        DCHECK(at_exit != NULL);
 196  E :        delete at_exit;
 197  E :        at_exit = NULL;
 198  E :        break;
 199    :  
 200    :      default:
 201  i :        NOTREACHED();
 202    :        break;
 203    :    }
 204    :  
 205  E :    return TRUE;
 206  E :  }
 207    :  
 208    :  namespace agent {
 209    :  namespace basic_block_entry {
 210    :  
 211    :  namespace {
 212    :  
 213    :  using ::common::IndexedFrequencyData;
 214    :  using agent::common::ScopedLastErrorKeeper;
 215    :  using trace::client::TraceFileSegment;
 216    :  
 217    :  // All tracing runs through this object.
 218    :  base::LazyInstance<BasicBlockEntry> static_bbentry_instance =
 219    :      LAZY_INSTANCE_INITIALIZER;
 220    :  
 221    :  // Get the address of the module containing @p addr. We do this by querying
 222    :  // for the allocation that contains @p addr. This must lie within the
 223    :  // instrumented module, and be part of the single allocation in  which the
 224    :  // image of the module lies. The base of the module will be the base address
 225    :  // of the allocation.
 226    :  // TODO(rogerm): Move to agent::common.
 227  E :  HMODULE GetModuleForAddr(const void* addr) {
 228  E :    MEMORY_BASIC_INFORMATION mem_info = {};
 229    :  
 230    :    // Lookup up the allocation in which addr is located.
 231  E :    if (::VirtualQuery(addr, &mem_info, sizeof(mem_info)) == 0) {
 232  i :      DWORD error = ::GetLastError();
 233  i :      LOG(ERROR) << "VirtualQuery failed: " << com::LogWe(error) << ".";
 234  i :      return NULL;
 235    :    }
 236    :  
 237    :    // Check that the allocation base has a valid PE header magic number.
 238  E :    base::win::PEImage image(reinterpret_cast<HMODULE>(mem_info.AllocationBase));
 239  E :    if (!image.VerifyMagic()) {
 240  i :      LOG(ERROR) << "Invalid module found for "
 241    :                 << base::StringPrintf("0x%08X", addr) << ".";
 242  i :      return NULL;
 243    :    }
 244    :  
 245    :    // Then it's a module.
 246  E :    return image.module();
 247  E :  }
 248    :  
 249    :  // Returns true if @p version is the expected version for @p datatype_id.
 250  E :  bool DatatypeVersionIsValid(uint32 datatype_id, uint32 version) {
 251  E :    if (datatype_id == ::common::IndexedFrequencyData::BASIC_BLOCK_ENTRY)
 252  E :      return version == ::common::kBasicBlockFrequencyDataVersion;
 253  i :    else if (datatype_id == ::common::IndexedFrequencyData::JUMP_TABLE)
 254  i :      return version == ::common::kJumpTableFrequencyDataVersion;
 255  i :    return false;
 256  E :  }
 257    :  
 258    :  }  // namespace
 259    :  
 260    :  // The IncrementIndexedFreqDataHook parameters.
 261    :  struct BasicBlockEntry::IncrementIndexedFreqDataFrame {
 262    :    const void* ret_addr;
 263    :    IndexedFrequencyData* module_data;
 264    :    uint32 index;
 265    :  };
 266    :  COMPILE_ASSERT_IS_POD_OF_SIZE(BasicBlockEntry::IncrementIndexedFreqDataFrame,
 267    :                                12);
 268    :  
 269    :  // The DllMainEntryHook parameters.
 270    :  struct BasicBlockEntry::DllMainEntryFrame {
 271    :    FuncAddr function;
 272    :    IndexedFrequencyData* module_data;
 273    :    const void* ret_addr;
 274    :    HMODULE module;
 275    :    DWORD reason;
 276    :    DWORD reserved;
 277    :  };
 278    :  COMPILE_ASSERT_IS_POD_OF_SIZE(BasicBlockEntry::DllMainEntryFrame, 24);
 279    :  
 280    :  // The ExeMainEntryHook parameters.
 281    :  struct BasicBlockEntry::ExeMainEntryFrame {
 282    :    FuncAddr function;
 283    :    IndexedFrequencyData* module_data;
 284    :    const void* ret_addr;
 285    :  };
 286    :  COMPILE_ASSERT_IS_POD_OF_SIZE(BasicBlockEntry::ExeMainEntryFrame, 12);
 287    :  
 288    :  // The per-thread-per-instrumented-module state managed by this agent.
 289    :  class BasicBlockEntry::ThreadState : public agent::common::ThreadStateBase {
 290    :   public:
 291    :    // Initialize a ThreadState instance.
 292    :    ThreadState(BasicBlockEntry* agent, void* buffer);
 293    :  
 294    :    // Destroy a ThreadState instance.
 295    :    ~ThreadState();
 296    :  
 297    :    // @name Accessors.
 298    :    // @{
 299  E :    uint32* frequency_data() { return frequency_data_; }
 300  E :    TraceFileSegment* segment() { return &segment_; }
 301    :    TraceIndexedFrequencyData* trace_data() { return trace_data_; }
 302    :    // @}
 303    :  
 304    :    // @name Mutators.
 305    :    // @{
 306    :    void set_frequency_data(void* buffer);
 307    :    void set_trace_data(TraceIndexedFrequencyData* trace_data);
 308    :    // @}
 309    :  
 310    :    // A helper to return a ThreadState pointer given a TLS index.
 311    :    static ThreadState* Get(DWORD tls_index);
 312    :  
 313    :    // A helper to assign a ThreadState pointer to a TLS index.
 314    :    void Assign(DWORD tls_index);
 315    :  
 316    :    // Saturation increment the frequency record for @p index. Note that in
 317    :    // Release mode, no range checking is performed on index.
 318    :    void Increment(uint32 index);
 319    :  
 320    :   protected:
 321    :    // As a shortcut, this points to the beginning of the array of basic-block
 322    :    // entry frequency values. With tracing enabled, this is equivalent to:
 323    :    //     reinterpret_cast<uint32*>(this->trace_data->frequency_data)
 324    :    // If tracing is not enabled, this will be set to point to a static
 325    :    // allocation of IndexedFrequencyData::frequency_data.
 326    :    uint32* frequency_data_;
 327    :  
 328    :    // The basic-block entry agent this thread state belongs to.
 329    :    BasicBlockEntry* agent_;
 330    :  
 331    :    // The thread's current trace-file segment, if any.
 332    :    trace::client::TraceFileSegment segment_;
 333    :  
 334    :    // The basic-block frequency record we're populating. This will point into
 335    :    // the associated trace file segment's buffer.
 336    :    TraceIndexedFrequencyData* trace_data_;
 337    :  
 338    :   private:
 339    :    DISALLOW_COPY_AND_ASSIGN(ThreadState);
 340    :  };
 341    :  
 342    :  BasicBlockEntry::ThreadState::ThreadState(BasicBlockEntry* agent, void* buffer)
 343    :      : agent_(agent),
 344    :        frequency_data_(static_cast<uint32*>(buffer)),
 345  E :        trace_data_(NULL) {
 346  E :    DCHECK(agent != NULL);
 347  E :    DCHECK(buffer != NULL);
 348  E :  }
 349    :  
 350  i :  BasicBlockEntry::ThreadState::~ThreadState() {
 351    :    // If we have an outstanding buffer, let's deallocate it now.
 352  i :    if (segment_.write_ptr != NULL && !agent_->session_.IsDisabled())
 353  i :      agent_->session_.ReturnBuffer(&segment_);
 354  i :  }
 355    :  
 356  E :  void BasicBlockEntry::ThreadState::set_frequency_data(void* buffer) {
 357  E :    DCHECK(buffer != NULL);
 358  E :    frequency_data_ = static_cast<uint32*>(buffer);
 359  E :  }
 360    :  
 361    :  void BasicBlockEntry::ThreadState::set_trace_data(
 362    :      TraceIndexedFrequencyData* trace_data) {
 363    :    DCHECK(trace_data != NULL);
 364    :    trace_data_ = trace_data;
 365    :  }
 366    :  
 367    :  BasicBlockEntry::ThreadState* BasicBlockEntry::ThreadState::Get(
 368  E :      DWORD tls_index) {
 369  E :    DCHECK_NE(TLS_OUT_OF_INDEXES, tls_index);
 370  E :    return static_cast<ThreadState*>(::TlsGetValue(tls_index));
 371  E :  }
 372    :  
 373  E :  void BasicBlockEntry::ThreadState::Assign(DWORD tls_index) {
 374  E :    DCHECK_NE(TLS_OUT_OF_INDEXES, tls_index);
 375  E :    ::TlsSetValue(tls_index, this);
 376  E :  }
 377    :  
 378  E :  inline void BasicBlockEntry::ThreadState::Increment(uint32 index) {
 379  E :    DCHECK(frequency_data_ != NULL);
 380  E :    DCHECK(trace_data_ == NULL || index < trace_data_->num_entries);
 381  E :    uint32& element = frequency_data_[index];
 382  E :    if (element != ~0U)
 383  E :      ++element;
 384  E :  }
 385    :  
 386  E :  BasicBlockEntry* BasicBlockEntry::Instance() {
 387  E :    return static_bbentry_instance.Pointer();
 388  E :  }
 389    :  
 390  E :  BasicBlockEntry::BasicBlockEntry() {
 391    :    // Create a session. We immediately return the buffer that gets allocated
 392    :    // to us. The client will perform thread-local buffer management on an as-
 393    :    // needed basis.
 394  E :    trace::client::TraceFileSegment dummy_segment;
 395  E :    if (trace::client::InitializeRpcSession(&session_, &dummy_segment))
 396  E :      CHECK(session_.ReturnBuffer(&dummy_segment));
 397  E :  }
 398    :  
 399  E :  BasicBlockEntry::~BasicBlockEntry() {
 400  E :  }
 401    :  
 402  E :  uint32* BasicBlockEntry::GetRawFrequencyData(IndexedFrequencyData* data) {
 403  E :    DCHECK(data != NULL);
 404  E :    ThreadState* state = ThreadState::Get(data->tls_index);
 405  E :    if (state == NULL)
 406  E :      state = Instance()->CreateThreadState(data);
 407  E :    return state->frequency_data();
 408  E :  }
 409    :  
 410    :  void BasicBlockEntry::IncrementIndexedFreqDataHook(
 411  E :      IncrementIndexedFreqDataFrame* entry_frame) {
 412  E :    ScopedLastErrorKeeper scoped_last_error_keeper;
 413  E :    DCHECK(entry_frame != NULL);
 414  E :    DCHECK(entry_frame->module_data != NULL);
 415    :    DCHECK_GT(entry_frame->module_data->num_entries,
 416  E :              entry_frame->index);
 417    :  
 418    :    // TODO(rogerm): Consider extracting a fast path for state != NULL? Inline it
 419    :    //     during instrumentation? Move it into the _increment_indexed_freq_data
 420    :    //     function?
 421  E :    ThreadState* state = ThreadState::Get(entry_frame->module_data->tls_index);
 422  E :    if (state == NULL)
 423  E :      state = Instance()->CreateThreadState(entry_frame->module_data);
 424  E :    state->Increment(entry_frame->index);
 425  E :  }
 426    :  
 427  E :  void BasicBlockEntry::DllMainEntryHook(DllMainEntryFrame* entry_frame) {
 428  E :    ScopedLastErrorKeeper scoped_last_error_keeper;
 429  E :    DCHECK(entry_frame != NULL);
 430  E :    switch (entry_frame->reason) {
 431    :      case DLL_PROCESS_ATTACH:
 432  E :        Instance()->OnProcessAttach(entry_frame->module_data);
 433  E :        break;
 434    :  
 435    :      case DLL_THREAD_ATTACH:
 436    :        // We don't handle this event because the thread may never actually
 437    :        // call into an instrumented module, so we don't want to allocate
 438    :        // resources needlessly. Further, we won't get this event for thread
 439    :        // that were created before the agent was loaded. On first use of
 440    :        // an instrumented basic-block in a given thread, any thread specific
 441    :        // resources will be allocated.
 442  i :        break;
 443    :  
 444    :      case DLL_PROCESS_DETACH:
 445    :      case DLL_THREAD_DETACH:
 446  E :        Instance()->OnThreadDetach(entry_frame->module_data);
 447  E :        break;
 448    :  
 449    :      default:
 450  i :        NOTREACHED();
 451    :    }
 452  E :  }
 453    :  
 454  E :  void BasicBlockEntry::ExeMainEntryHook(ExeMainEntryFrame* entry_frame) {
 455  E :    ScopedLastErrorKeeper scoped_last_error_keeper;
 456  E :    DCHECK(entry_frame != NULL);
 457  E :    Instance()->OnProcessAttach(entry_frame->module_data);
 458  E :  }
 459    :  
 460  E :  void BasicBlockEntry::RegisterModule(const void* addr) {
 461  E :    DCHECK(addr != NULL);
 462    :  
 463    :    // Allocate a segment for the module information.
 464  E :    trace::client::TraceFileSegment module_info_segment;
 465  E :    CHECK(session_.AllocateBuffer(&module_info_segment));
 466    :  
 467    :    // Log the module. This is required in order to associate basic-block
 468    :    // frequency with a module and PDB file during post-processing.
 469  E :    HMODULE module = GetModuleForAddr(addr);
 470  E :    CHECK(module != NULL);
 471  E :    CHECK(agent::common::LogModule(module, &session_, &module_info_segment));
 472    :  
 473    :    // Commit the module information.
 474  E :    CHECK(session_.ReturnBuffer(&module_info_segment));
 475  E :  }
 476    :  
 477  E :  void BasicBlockEntry::OnProcessAttach(IndexedFrequencyData* module_data) {
 478  E :    DCHECK(module_data != NULL);
 479    :  
 480    :    // Exit if the magic number does not match.
 481  E :    CHECK_EQ(::common::kBasicBlockEntryAgentId, module_data->agent_id);
 482    :  
 483    :    // Exit if the version does not match.
 484  E :    CHECK(DatatypeVersionIsValid(module_data->data_type, module_data->version));
 485    :  
 486    :    // We allow for this hook to be called multiple times. We expect the first
 487    :    // time to occur under the loader lock, so we don't need to worry about
 488    :    // concurrency for this check.
 489  E :    if (module_data->initialization_attempted)
 490  E :      return;
 491    :  
 492    :    // Flag the module as initialized.
 493  E :    module_data->initialization_attempted = 1U;
 494    :  
 495    :    // We expect this to be executed exactly once for each module.
 496  E :    CHECK_EQ(TLS_OUT_OF_INDEXES, module_data->tls_index);
 497  E :    module_data->tls_index = ::TlsAlloc();
 498  E :    CHECK_NE(TLS_OUT_OF_INDEXES, module_data->tls_index);
 499    :  
 500    :    // Register this module with the call_trace if the session is not disabled.
 501    :    // Note that we expect module_data to be statically defined within the
 502    :    // module of interest, so we can use its address to lookup the module.
 503  E :    if (!session_.IsDisabled())
 504  E :      RegisterModule(module_data);
 505  E :  }
 506    :  
 507  E :  void BasicBlockEntry::OnThreadDetach(IndexedFrequencyData* module_data) {
 508  E :    DCHECK(module_data != NULL);
 509  E :    DCHECK_EQ(1U, module_data->initialization_attempted);
 510  E :    DCHECK_NE(TLS_OUT_OF_INDEXES, module_data->tls_index);
 511    :  
 512  E :    ThreadState* state = ThreadState::Get(module_data->tls_index);
 513  E :    if (state != NULL)
 514  E :      thread_state_manager_.MarkForDeath(state);
 515  E :  }
 516    :  
 517    :  BasicBlockEntry::ThreadState* BasicBlockEntry::CreateThreadState(
 518  E :      IndexedFrequencyData* module_data) {
 519  E :    DCHECK(module_data != NULL);
 520  E :    CHECK_NE(IndexedFrequencyData::INVALID_DATA_TYPE, module_data->data_type);
 521    :  
 522    :    // Create the thread-local state for this thread. By default, just point the
 523    :    // counter array to the statically allocated fall-back area.
 524  E :    ThreadState* state = new ThreadState(this, module_data->frequency_data);
 525  E :    CHECK(state != NULL);
 526    :  
 527    :    // Associate the thread_state with the current thread.
 528  E :    state->Assign(module_data->tls_index);
 529    :  
 530    :    // Register the thread state with the thread state manager.
 531  E :    thread_state_manager_.Register(state);
 532    :  
 533    :    // If we're not actually tracing, then we're done.
 534  E :    if (session_.IsDisabled())
 535  E :      return state;
 536    :  
 537    :    // Nothing to allocate? We're done!
 538  E :    if (module_data->num_entries == 0) {
 539  i :      LOG(WARNING) << "Module contains no instrumented basic blocks, not "
 540    :                   << "allocating basic-block trace data segment.";
 541  i :      return state;
 542    :    }
 543    :  
 544    :    // Determine the size of the basic block frequency table.
 545  E :    size_t data_size = module_data->num_entries * sizeof(uint32);
 546    :  
 547    :    // Determine the size of the basic block frequency record.
 548  E :    size_t record_size = sizeof(TraceIndexedFrequencyData) + data_size - 1;
 549    :  
 550    :    // Determine the size of the buffer we need. We need room for the basic block
 551    :    // frequency struct plus a single RecordPrefix header.
 552  E :    size_t segment_size = sizeof(RecordPrefix) + record_size;
 553    :  
 554    :    // Allocate the actual segment for the basic block entry data.
 555  E :    CHECK(session_.AllocateBuffer(segment_size, state->segment()));
 556    :  
 557    :    // Ensure it's big enough to allocate the basic-block frequency data
 558    :    // we want. This automatically accounts for the RecordPrefix overhead.
 559  E :    CHECK(state->segment()->CanAllocate(record_size));
 560    :  
 561    :    // Allocate the basic-block frequency data. We will leave this allocated and
 562    :    // let it get flushed during tear-down of the call-trace client.
 563    :    TraceIndexedFrequencyData* trace_data =
 564    :        reinterpret_cast<TraceIndexedFrequencyData*>(
 565    :            state->segment()->AllocateTraceRecordImpl(TRACE_INDEXED_FREQUENCY,
 566  E :                                                      record_size));
 567  E :    DCHECK(trace_data != NULL);
 568    :  
 569    :    // Initialize the basic block frequency data struct.
 570  E :    HMODULE module = GetModuleForAddr(module_data);
 571  E :    CHECK(module != NULL);
 572  E :    const base::win::PEImage image(module);
 573  E :    const IMAGE_NT_HEADERS* nt_headers = image.GetNTHeaders();
 574  E :    trace_data->data_type = module_data->data_type;
 575  E :    trace_data->module_base_addr = reinterpret_cast<ModuleAddr>(image.module());
 576  E :    trace_data->module_base_size = nt_headers->OptionalHeader.SizeOfImage;
 577  E :    trace_data->module_checksum = nt_headers->OptionalHeader.CheckSum;
 578  E :    trace_data->module_time_date_stamp = nt_headers->FileHeader.TimeDateStamp;
 579  E :    trace_data->frequency_size = sizeof(uint32);
 580  E :    trace_data->num_entries = module_data->num_entries;
 581    :  
 582    :    // Hook up the newly allocated buffer to the call-trace instrumentation.
 583  E :    state->set_frequency_data(trace_data->frequency_data);
 584    :  
 585  E :    return state;
 586  E :  }
 587    :  
 588    :  }  // namespace basic_block_entry
 589    :  }  // namespace agent

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