Coverage for /Syzygy/agent/profiler/profiler.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
97.0%2923010.C++source

Line-by-line coverage:

   1    :  // Copyright 2012 Google Inc.
   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 profiler DLL.
  16    :  #include "syzygy/agent/profiler/profiler.h"
  17    :  
  18    :  #include <windows.h>
  19    :  #include <algorithm>
  20    :  
  21    :  #include "base/at_exit.h"
  22    :  #include "base/command_line.h"
  23    :  #include "base/environment.h"
  24    :  #include "base/file_path.h"
  25    :  #include "base/lazy_instance.h"
  26    :  #include "base/logging.h"
  27    :  #include "base/string_util.h"
  28    :  #include "base/utf_string_conversions.h"
  29    :  #include "base/memory/scoped_ptr.h"
  30    :  #include "base/win/pe_image.h"
  31    :  #include "base/win/scoped_handle.h"
  32    :  #include "syzygy/agent/common/dlist.h"
  33    :  #include "syzygy/agent/common/process_utils.h"
  34    :  #include "syzygy/agent/common/scoped_last_error_keeper.h"
  35    :  #include "syzygy/agent/profiler/return_thunk_factory.h"
  36    :  #include "syzygy/common/logging.h"
  37    :  #include "syzygy/trace/client/client_utils.h"
  38    :  #include "syzygy/trace/protocol/call_trace_defs.h"
  39    :  
  40    :  namespace {
  41    :  
  42    :  using agent::common::ScopedLastErrorKeeper;
  43    :  
  44    :  // All tracing runs through this object.
  45    :  base::LazyInstance<agent::profiler::Profiler> static_profiler_instance =
  46    :      LAZY_INSTANCE_INITIALIZER;
  47    :  
  48    :  typedef std::pair<RetAddr, FuncAddr> InvocationKey;
  49    :  
  50    :  class HashInvocationKey {
  51    :   public:
  52    :    static const size_t bucket_size = 4;
  53    :    static const size_t min_buckets = 8;
  54    :  
  55  E :    size_t operator()(const InvocationKey& key) const {
  56    :      return reinterpret_cast<size_t>(key.first) ^
  57  E :          reinterpret_cast<size_t>(key.second);
  58  E :    }
  59    :  
  60  E :    bool operator()(const InvocationKey& a, const InvocationKey& b) const {
  61  E :      return a < b;
  62  E :    }
  63    :  };
  64    :  typedef base::hash_map<
  65    :      InvocationKey, InvocationInfo*, HashInvocationKey> InvocationMap;
  66    :  
  67    :  // Accessing a module acquired from process iteration calls is inherently racy,
  68    :  // as we don't hold any kind of reference to the module, and so the module
  69    :  // could be unloaded while we're accessing it. In practice this shouldn't
  70    :  // happen to us, as we'll be running under the loader's lock in all cases.
  71    :  bool CaptureModuleInformation(const base::win::PEImage& image,
  72    :                                TraceModuleData* module_event) {
  73    :    __try {
  74    :      // Populate the log record.
  75    :      module_event->module_base_size =
  76    :          image.GetNTHeaders()->OptionalHeader.SizeOfImage;
  77    :      module_event->module_checksum =
  78    :          image.GetNTHeaders()->OptionalHeader.CheckSum;
  79    :      module_event->module_time_date_stamp =
  80    :          image.GetNTHeaders()->FileHeader.TimeDateStamp;
  81    :    } __except(EXCEPTION_EXECUTE_HANDLER) {
  82    :      return false;
  83    :    }
  84    :  
  85    :    return true;
  86    :  }
  87    :  
  88    :  // The information on how to set the thread name comes from
  89    :  // a MSDN article: http://msdn2.microsoft.com/en-us/library/xcb2z8hs.aspx
  90    :  const DWORD kVCThreadNameException = 0x406D1388;
  91    :  
  92    :  typedef struct tagTHREADNAME_INFO {
  93    :    DWORD dwType;  // Must be 0x1000.
  94    :    LPCSTR szName;  // Pointer to name (in user addr space).
  95    :    DWORD dwThreadID;  // Thread ID (-1=caller thread).
  96    :    DWORD dwFlags;  // Reserved for future use, must be zero.
  97    :  } THREADNAME_INFO;
  98    :  
  99    :  }  // namespace
 100    :  
 101    :  // See client.cc for a description of the unconventional
 102    :  // calling conventions for this function.
 103  E :  extern "C" void __declspec(naked) _indirect_penter() {
 104    :    __asm {
 105    :      // Stash volatile registers.
 106  E :      push eax
 107  E :      push edx
 108    :  
 109    :      // Get the current cycle time ASAP.
 110  E :      rdtsc
 111    :  
 112  E :      push ecx
 113  E :      pushfd
 114    :  
 115    :      // Push the cycle time arg.
 116  E :      push edx
 117  E :      push eax
 118    :  
 119    :      // Retrieve the original function address, pushed by our caller.
 120  E :      mov eax, DWORD PTR[esp + 0x18]
 121  E :      push eax
 122    :  
 123    :      // Calculate the position of the return address on stack, and
 124    :      // push it. This becomes the EntryFrame argument.
 125  E :      lea eax, DWORD PTR[esp + 0x20]
 126  E :      push eax
 127  E :      call agent::profiler::Profiler::FunctionEntryHook
 128    :  
 129    :      // Restore volatile registers.
 130  E :      popfd
 131  E :      pop ecx
 132  E :      pop edx
 133  E :      pop eax
 134    :  
 135    :      // Return to the address pushed by our caller.
 136  E :      ret
 137    :    }
 138    :  }
 139    :  
 140  E :  extern "C" void __declspec(naked) _indirect_penter_dllmain() {
 141    :    __asm {
 142    :      // Stash volatile registers.
 143  E :      push eax
 144  E :      push edx
 145    :  
 146    :      // Get the current cycle time ASAP.
 147  E :      rdtsc
 148    :  
 149  E :      push ecx
 150  E :      pushfd
 151    :  
 152    :      // Push the cycle time arg.
 153  E :      push edx
 154  E :      push eax
 155    :  
 156    :      // Retrieve the address pushed by our caller.
 157  E :      mov eax, DWORD PTR[esp + 0x18]
 158  E :      push eax
 159    :  
 160    :      // Calculate the position of the return address on stack, and
 161    :      // push it. This becomes the EntryFrame argument.
 162  E :      lea eax, DWORD PTR[esp + 0x20]
 163  E :      push eax
 164  E :      call agent::profiler::Profiler::DllMainEntryHook
 165    :  
 166    :      // Restore volatile registers.
 167  E :      popfd
 168  E :      pop ecx
 169  E :      pop edx
 170  E :      pop eax
 171    :  
 172    :      // Return to the address pushed by our caller.
 173  E :      ret
 174    :    }
 175    :  }
 176    :  
 177    :  // On entry, pc_location should point to a location on our own stack.
 178    :  extern "C" uintptr_t __cdecl ResolveReturnAddressLocation(
 179  E :      uintptr_t pc_location) {
 180    :    using agent::profiler::Profiler;
 181  E :    Profiler* profiler = Profiler::Instance();
 182    :    return reinterpret_cast<uintptr_t>(
 183    :        profiler->ResolveReturnAddressLocation(
 184  E :            reinterpret_cast<RetAddr*>(pc_location)));
 185  E :  }
 186    :  
 187  E :  BOOL WINAPI DllMain(HMODULE instance, DWORD reason, LPVOID reserved) {
 188    :    using agent::profiler::Profiler;
 189    :  
 190    :    // Our AtExit manager required by base.
 191    :    static base::AtExitManager* at_exit = NULL;
 192    :  
 193  E :    switch (reason) {
 194    :      case DLL_PROCESS_ATTACH:
 195  E :        DCHECK(at_exit == NULL);
 196  E :        at_exit = new base::AtExitManager();
 197    :  
 198  E :        CommandLine::Init(0, NULL);
 199  E :        common::InitLoggingForDll(L"profiler");
 200  E :        break;
 201    :  
 202    :      case DLL_THREAD_DETACH:
 203  E :        Profiler::Instance()->OnThreadDetach();
 204  E :        break;
 205    :  
 206    :      case DLL_PROCESS_DETACH:
 207  E :        DCHECK(at_exit != NULL);
 208  E :        delete at_exit;
 209  E :        at_exit = NULL;
 210    :        break;
 211    :  
 212    :      default:
 213    :        break;
 214    :    }
 215    :  
 216  E :    return TRUE;
 217  E :  }
 218    :  
 219    :  namespace agent {
 220    :  namespace profiler {
 221    :  
 222    :  class Profiler::ThreadState
 223    :      : public ReturnThunkFactoryImpl<Profiler::ThreadState>,
 224    :        public agent::common::ThreadStateBase {
 225    :   public:
 226    :    explicit ThreadState(Profiler* profiler);
 227    :    ~ThreadState();
 228    :  
 229    :    // Logs @p module and all other modules in the process, then flushes
 230    :    // the current trace buffer.
 231    :    void LogAllModules(HMODULE module);
 232    :  
 233    :    // Logs @p module.
 234    :    void LogModule(HMODULE module);
 235    :  
 236    :    // Logs @p thread_name as the current thread's name.
 237    :    void LogThreadName(const base::StringPiece& thread_name);
 238    :  
 239    :    // Processes a single function entry.
 240    :    void OnFunctionEntry(EntryFrame* entry_frame,
 241    :                         FuncAddr function,
 242    :                         uint64 cycles);
 243    :  
 244    :    // @name Callback notification implementation.
 245    :    // @{
 246    :    virtual void OnPageAdded(const void* page) OVERRIDE;
 247    :    virtual void OnPageRemoved(const void* page) OVERRIDE;
 248    :    // @}
 249    :  
 250    :    // Function exit hook.
 251    :    void OnFunctionExit(const ThunkData* data, uint64 cycles_exit);
 252    :  
 253  E :    trace::client::TraceFileSegment* segment() { return &segment_; }
 254    :  
 255    :   private:
 256    :    friend class Profiler;
 257    :  
 258    :    void RecordInvocation(RetAddr caller,
 259    :                          FuncAddr function,
 260    :                          uint64 cycles);
 261    :  
 262    :    void UpdateOverhead(uint64 entry_cycles);
 263    :    InvocationInfo* AllocateInvocationInfo();
 264    :    bool FlushSegment();
 265    :  
 266    :    // The profiler we're attached to.
 267    :    Profiler* profiler_;
 268    :  
 269    :    // We keep a running tally of the rough amount of wall clock cycles spent
 270    :    // inside the profiler. We then subtract the profiler's overhead from the
 271    :    // wall clock cycle timer on each measurement. This results in a timer that
 272    :    // measures time exclusive of profiling overhead.
 273    :    uint64 cycles_overhead_;
 274    :  
 275    :    // The invocations we've recorded in our buffer.
 276    :    InvocationMap invocations_;
 277    :  
 278    :    // The trace file segment we're recording to.
 279    :    trace::client::TraceFileSegment segment_;
 280    :  
 281    :    // The current batch record we're writing to, if any.
 282    :    TraceBatchInvocationInfo* batch_;
 283    :  
 284    :    // The set of modules we've logged.
 285    :    ModuleSet logged_modules_;
 286    :  };
 287    :  
 288    :  Profiler::ThreadState::ThreadState(Profiler* profiler)
 289    :      : profiler_(profiler),
 290    :        cycles_overhead_(0LL),
 291  E :        batch_(NULL) {
 292  E :    Initialize();
 293  E :  }
 294    :  
 295  E :  Profiler::ThreadState::~ThreadState() {
 296  E :    batch_ = NULL;
 297  E :    invocations_.clear();
 298    :  
 299    :    // If we have an outstanding buffer, let's deallocate it now.
 300  E :    if (segment_.write_ptr != NULL)
 301  E :      profiler_->session_.ReturnBuffer(&segment_);
 302    :  
 303  E :    Uninitialize();
 304  E :  }
 305    :  
 306  E :  void Profiler::ThreadState::LogAllModules(HMODULE module) {
 307    :    // Bail early if we're disabled.
 308  E :    if (profiler_->session_.IsDisabled())
 309  E :      return;
 310    :  
 311  E :    agent::common::ModuleVector modules;
 312  E :    agent::common::GetProcessModules(&modules);
 313    :  
 314    :    // Our module should be in the process modules.
 315  E :    DCHECK(std::find(modules.begin(), modules.end(), module) != modules.end());
 316    :  
 317  E :    for (size_t i = 0; i < modules.size(); ++i) {
 318  E :      DCHECK(modules[i] != NULL);
 319  E :      LogModule(modules[i]);
 320  E :    }
 321    :  
 322    :    // We need to flush module events right away, so that the module is
 323    :    // defined in the trace file before events using that module start to
 324    :    // occur (in another thread).
 325  E :    FlushSegment();
 326  E :  }
 327    :  
 328  E :  void Profiler::ThreadState::LogModule(HMODULE module) {
 329  E :    batch_ = NULL;
 330  E :    agent::common::LogModule(module, &profiler_->session_, &segment_);
 331  E :  }
 332    :  
 333    :  void Profiler::ThreadState::LogThreadName(
 334  E :      const base::StringPiece& thread_name) {
 335  E :    if (thread_name.empty())
 336  i :      return;
 337    :  
 338    :    // Make sure the event we're about to write will fit.
 339  E :    if (!segment_.CanAllocate(thread_name.size() + 1) || !FlushSegment()) {
 340    :      // Failed to allocate a new segment.
 341  i :      return;
 342    :    }
 343    :  
 344  E :    DCHECK(segment_.CanAllocate(thread_name.size() + 1));
 345  E :    batch_ = NULL;
 346    :  
 347    :    // Allocate a record in the log.
 348    :    TraceThreadNameInfo* thread_name_event =
 349    :        reinterpret_cast<TraceThreadNameInfo*>(
 350    :          segment_.AllocateTraceRecordImpl(
 351  E :              TRACE_THREAD_NAME, thread_name.size() + 1));
 352  E :    DCHECK(thread_name_event != NULL);
 353    :    base::strlcpy(thread_name_event->thread_name,
 354  E :                  thread_name.data(), thread_name.size() + 1);
 355  E :  }
 356    :  
 357    :  void Profiler::ThreadState::OnFunctionEntry(EntryFrame* entry_frame,
 358    :                                              FuncAddr function,
 359  E :                                              uint64 cycles) {
 360  E :    if (profiler_->session_.IsDisabled())
 361  E :      return;
 362    :  
 363    :    // Record the details of the entry.
 364    :    // Note that on tail-recursion and tail-call elimination, the caller recorded
 365    :    // here will be a thunk. We cater for this case on exit as best we can.
 366  E :    ThunkData* data = MakeThunk(entry_frame->retaddr);
 367  E :    DCHECK(data != NULL);
 368  E :    data->caller = entry_frame->retaddr;
 369  E :    data->function = function;
 370  E :    data->cycles_entry = cycles - cycles_overhead_;
 371    :  
 372  E :    entry_frame->retaddr = data->thunk;
 373    :  
 374  E :    UpdateOverhead(cycles);
 375  E :  }
 376    :  
 377    :  void Profiler::ThreadState::OnFunctionExit(const ThunkData* data,
 378  E :                                             uint64 cycles_exit) {
 379    :    // Calculate the number of cycles in the invocation, exclusive our overhead.
 380  E :    uint64 cycles_executed = cycles_exit - cycles_overhead_ - data->cycles_entry;
 381    :  
 382    :    // See if the return address resolves to a data, which indicates
 383    :    // tail recursion or tail call elimination. In that case we record the
 384    :    // calling function as caller, which isn't totally accurate as that'll
 385    :    // attribute the cost to the first line of the calling function. In the
 386    :    // absence of more information, it's the best we can do, however.
 387  E :    Thunk* ret_thunk = CastToThunk(data->caller);
 388  E :    if (ret_thunk == NULL) {
 389  E :      RecordInvocation(data->caller, data->function, cycles_executed);
 390  E :    } else {
 391  E :      ThunkData* ret_data = DataFromThunk(ret_thunk);
 392  E :      RecordInvocation(ret_data->function, data->function, cycles_executed);
 393    :    }
 394    :  
 395  E :    UpdateOverhead(cycles_exit);
 396  E :  }
 397    :  
 398  E :  void Profiler::ThreadState::OnPageAdded(const void* page) {
 399  E :    profiler_->OnPageAdded(page);
 400  E :  }
 401    :  
 402  E :  void Profiler::ThreadState::OnPageRemoved(const void* page) {
 403  E :    profiler_->OnPageRemoved(page);
 404  E :  }
 405    :  
 406    :  void Profiler::ThreadState::RecordInvocation(RetAddr caller,
 407    :                                               FuncAddr function,
 408  E :                                               uint64 duration_cycles) {
 409    :    // See whether we've already recorded an entry for this function.
 410  E :    InvocationKey key(caller, function);
 411  E :    InvocationMap::iterator it = invocations_.find(key);
 412  E :    if (it != invocations_.end()) {
 413    :      // Yup, we already have an entry. Tally the new data.
 414  E :      InvocationInfo* info = it->second;
 415  E :      ++(info->num_calls);
 416  E :      info->cycles_sum += duration_cycles;
 417  E :      if (duration_cycles < info->cycles_min) {
 418  E :        info->cycles_min = duration_cycles;
 419  E :      } else if (duration_cycles > info->cycles_max) {
 420  E :        info->cycles_max = duration_cycles;
 421    :      }
 422  E :    } else {
 423    :      // The allocation below may touch last error.
 424  E :      ScopedLastErrorKeeper keep_last_error;
 425    :  
 426    :      // Nopes, allocate a new entry for this invocation.
 427  E :      InvocationInfo* info = AllocateInvocationInfo();
 428  E :      if (info != NULL) {
 429  E :        invocations_[key] = info;
 430  E :        info->caller = caller;
 431  E :        info->function = function;
 432  E :        info->num_calls = 1;
 433  E :        info->cycles_min = info->cycles_max = info->cycles_sum = duration_cycles;
 434    :      }
 435  E :    }
 436  E :  }
 437    :  
 438  E :  void Profiler::ThreadState::UpdateOverhead(uint64 entry_cycles) {
 439    :    // TODO(siggi): Measure the fixed overhead on setup,
 440    :    //     then add it on every update.
 441  E :    cycles_overhead_ += (__rdtsc() - entry_cycles);
 442  E :  }
 443    :  
 444  E :  InvocationInfo* Profiler::ThreadState::AllocateInvocationInfo() {
 445    :    // This is kind of self-evident for the moment, as an invocation info batch
 446    :    // contains at least one invocation info as currently declared.
 447    :    // If this fails, please recondsider your implementation, or else revisit
 448    :    // the allocation code below.
 449    :    COMPILE_ASSERT(sizeof(TraceBatchInvocationInfo) >= sizeof(InvocationInfo),
 450    :                   invocation_info_batch_must_be_larger_than_invocation_info);
 451    :  
 452    :    // Do we have a record that we can grow?
 453  E :    if (batch_ != NULL && segment_.CanAllocateRaw(sizeof(InvocationInfo))) {
 454    :      InvocationInfo* invocation_info =
 455  E :          reinterpret_cast<InvocationInfo*>(segment_.write_ptr);
 456  E :      RecordPrefix* prefix = trace::client::GetRecordPrefix(batch_);
 457  E :      prefix->size += sizeof(InvocationInfo);
 458    :  
 459    :      // Update the book-keeping.
 460  E :      segment_.write_ptr += sizeof(InvocationInfo);
 461  E :      segment_.header->segment_length += sizeof(InvocationInfo);
 462    :  
 463  E :      return invocation_info;
 464    :    }
 465    :  
 466    :    // Do we need to scarf a new buffer?
 467    :    if (!segment_.CanAllocate(sizeof(TraceBatchInvocationInfo)) &&
 468  E :        !FlushSegment()) {
 469    :      // We failed to allocate a new buffer.
 470  i :      return NULL;
 471    :    }
 472    :  
 473  E :    DCHECK(segment_.header != NULL);
 474    :  
 475  E :    batch_ = segment_.AllocateTraceRecord<TraceBatchInvocationInfo>();
 476  E :    return &batch_->invocations[0];
 477  E :  }
 478    :  
 479  E :  bool Profiler::ThreadState::FlushSegment() {
 480  E :    batch_ = NULL;
 481  E :    invocations_.clear();
 482    :  
 483  E :    return profiler_->session_.ExchangeBuffer(&segment_);
 484  E :  }
 485    :  
 486  E :  void Profiler::OnThreadDetach() {
 487  E :    ThreadState* state = GetThreadState();
 488  E :    if (state != NULL)
 489  E :      thread_state_manager_.MarkForDeath(state);
 490  E :  }
 491    :  
 492  E :  RetAddr* Profiler::ResolveReturnAddressLocation(RetAddr* pc_location) {
 493  E :    base::AutoLock lock(lock_);
 494    :  
 495    :    // In case of tail-call and tail recursion elimination, we can get chained
 496    :    // thunks, so we loop around here until we resolve to a non-thunk.
 497  E :    while (true) {
 498    :      // See whether the return address is one of our thunks.
 499  E :      RetAddr ret_addr = *pc_location;
 500    :  
 501    :      // Compute the page this return address lives in.
 502    :      const void* page = reinterpret_cast<const void*>(
 503  E :          reinterpret_cast<uintptr_t>(ret_addr) & ~0xFFF);
 504  E :      if (!std::binary_search(pages_.begin(), pages_.end(), page))
 505  E :        return pc_location;
 506    :  
 507    :      // It's one of our own, redirect to the thunk's stash.
 508    :      ThreadState::Thunk* thunk =
 509  E :          reinterpret_cast<ThreadState::Thunk*>(const_cast<void*>(ret_addr));
 510    :  
 511  E :      ThreadState::ThunkData* data = ThreadState::DataFromThunk(thunk);
 512    :  
 513    :      // Update the PC location and go around again, in case this
 514    :      // thunk links to another one.
 515  E :      pc_location = &data->caller;
 516  E :    }
 517  E :  }
 518    :  
 519    :  void Profiler::OnModuleEntry(EntryFrame* entry_frame,
 520    :                               FuncAddr function,
 521  E :                               uint64 cycles) {
 522    :    // The function invoked has a DllMain-like signature.
 523    :    // Get the module and reason from its invocation record.
 524  E :    HMODULE module = reinterpret_cast<HMODULE>(entry_frame->args[0]);
 525  E :    DWORD reason = entry_frame->args[1];
 526    :  
 527    :    // Only log module additions.
 528  E :    bool should_log_module = false;
 529  E :    switch (reason) {
 530    :      case DLL_PROCESS_ATTACH:
 531    :      case DLL_THREAD_ATTACH:
 532  E :        should_log_module = true;
 533  E :        break;
 534    :  
 535    :      case DLL_PROCESS_DETACH:
 536    :      case DLL_THREAD_DETACH:
 537  i :        break;
 538    :  
 539    :      default:
 540  i :        LOG(WARNING) << "Unrecognized module event: " << reason << ".";
 541    :        break;
 542    :    }
 543    :  
 544    :    // Make sure we only log each module once per process.
 545  E :    bool is_new_module = false;
 546  E :    if (should_log_module) {
 547  E :      base::AutoLock lock(lock_);
 548    :  
 549  E :      is_new_module = logged_modules_.insert(module).second;
 550  E :    }
 551    :  
 552  E :    ThreadState* data = GetOrAllocateThreadState();
 553  E :    DCHECK(data != NULL);
 554  E :    if (data == NULL)
 555  i :      return;
 556    :  
 557  E :    if (is_new_module) {
 558    :      // Delegate the logging to our per-thread data.
 559  E :      data->LogAllModules(module);
 560    :    }
 561    :  
 562    :    // Handle the function entry.
 563  E :    data->OnFunctionEntry(entry_frame, function, cycles);
 564  E :  }
 565    :  
 566  E :  void Profiler::OnPageAdded(const void* page) {
 567  E :    base::AutoLock lock(lock_);
 568    :  
 569    :    PageVector::iterator it =
 570  E :        std::lower_bound(pages_.begin(), pages_.end(), page);
 571  E :    DCHECK(it == pages_.end() || *it != page);
 572  E :    pages_.insert(it, page);
 573  E :  }
 574    :  
 575  E :  void Profiler::OnPageRemoved(const void* page) {
 576  E :    base::AutoLock lock(lock_);
 577    :  
 578    :    PageVector::iterator it =
 579  E :        std::lower_bound(pages_.begin(), pages_.end(), page);
 580    :    // The page must be in our list.
 581  E :    DCHECK(it != pages_.end());
 582  E :    DCHECK_EQ(page, *it);
 583  E :    pages_.erase(it);
 584  E :  }
 585    :  
 586  E :  void Profiler::OnThreadName(const base::StringPiece& thread_name) {
 587  E :    ThreadState* state = GetOrAllocateThreadState();
 588  E :    if (state != NULL)
 589  E :      state->LogThreadName(thread_name);
 590  E :  }
 591    :  
 592  E :  LONG CALLBACK Profiler::ExceptionHandler(EXCEPTION_POINTERS* ex_info) {
 593    :    // Log the thread if this is the VC thread name exception.
 594    :    if (ex_info->ExceptionRecord->ExceptionCode == kVCThreadNameException &&
 595    :        ex_info->ExceptionRecord->NumberParameters ==
 596  E :            sizeof(THREADNAME_INFO)/sizeof(DWORD)) {
 597    :      const THREADNAME_INFO* info =
 598    :          reinterpret_cast<const THREADNAME_INFO*>(
 599  E :              &ex_info->ExceptionRecord->ExceptionInformation);
 600    :  
 601  E :      if (info->dwType == 0x1000) {
 602  E :        Profiler* instance = Profiler::Instance();
 603  E :        if (instance != NULL)
 604  E :          instance->OnThreadName(info->szName);
 605  E :      } else {
 606  i :        LOG(WARNING) << "Unrecognised event type " << info->dwType;
 607    :      }
 608    :    }
 609    :  
 610  E :    return EXCEPTION_CONTINUE_SEARCH;
 611  E :  }
 612    :  
 613    :  
 614  E :  Profiler* Profiler::Instance() {
 615  E :    return static_profiler_instance.Pointer();
 616  E :  }
 617    :  
 618  E :  Profiler::Profiler() : handler_registration_(NULL) {
 619    :    // Create our RPC session and allocate our initial trace segment on first use.
 620  E :    ThreadState* data = CreateFirstThreadStateAndSession();
 621  E :    CHECK(data != NULL) << "Failed to allocate thread local state.";
 622    :  
 623  E :    handler_registration_ = ::AddVectoredExceptionHandler(TRUE, ExceptionHandler);
 624  E :  }
 625    :  
 626  E :  Profiler::~Profiler() {
 627    :    // Typically, this will happen on the last thread in the process. We must
 628    :    // explicitly clean up this thread's state as it will otherwise leak.
 629  E :    FreeThreadState();
 630    :  
 631    :    // Unregister our VEH.
 632  E :    if (handler_registration_ != NULL) {
 633  E :      ::RemoveVectoredExceptionHandler(handler_registration_);
 634  E :      handler_registration_ = NULL;
 635    :    }
 636  E :  }
 637    :  
 638  E :  Profiler::ThreadState* Profiler::CreateFirstThreadStateAndSession() {
 639  E :    Profiler::ThreadState* data = GetOrAllocateThreadStateImpl();
 640    :  
 641    :    // Create the session (and allocate the first segment).
 642  E :    scoped_ptr<base::Environment> env(base::Environment::Create());
 643  E :    std::string id;
 644  E :    env->GetVar(::kSyzygyRpcInstanceIdEnvVar, &id);
 645  E :    session_.set_instance_id(UTF8ToWide(id));
 646  E :    session_.CreateSession(data->segment());
 647    :  
 648  E :    return data;
 649  E :  }
 650    :  
 651  E :  Profiler::ThreadState* Profiler::GetOrAllocateThreadState() {
 652  E :    Profiler::ThreadState* data = GetOrAllocateThreadStateImpl();
 653  E :    if (!data->segment()->write_ptr && session_.IsTracing()) {
 654  E :      session_.AllocateBuffer(data->segment());
 655    :    }
 656  E :    return data;
 657  E :  }
 658    :  
 659  E :  Profiler::ThreadState* Profiler::GetOrAllocateThreadStateImpl() {
 660  E :    ThreadState *data = tls_.Get();
 661  E :    if (data != NULL)
 662  E :      return data;
 663    :  
 664  E :    data = new ThreadState(this);
 665  E :    if (data == NULL) {
 666  i :      LOG(ERROR) << "Unable to allocate per-thread data";
 667  i :      return NULL;
 668    :    }
 669    :  
 670  E :    thread_state_manager_.Register(data);
 671  E :    tls_.Set(data);
 672    :  
 673  E :    return data;
 674  E :  }
 675    :  
 676  E :  Profiler::ThreadState* Profiler::GetThreadState() const {
 677  E :    return tls_.Get();
 678  E :  }
 679    :  
 680  E :  void Profiler::FreeThreadState() {
 681  E :    ThreadState *data = GetThreadState();
 682  E :    if (data != NULL) {
 683  E :      tls_.Set(NULL);
 684  E :      thread_state_manager_.Unregister(data);
 685  E :      delete data;
 686    :    }
 687  E :  }
 688    :  
 689    :  void WINAPI Profiler::DllMainEntryHook(EntryFrame* entry_frame,
 690    :                                         FuncAddr function,
 691  E :                                         uint64 cycles) {
 692  E :    ScopedLastErrorKeeper keep_last_error;
 693    :  
 694  E :    Profiler* profiler = Profiler::Instance();
 695  E :    profiler->OnModuleEntry(entry_frame, function, cycles);
 696  E :  }
 697    :  
 698    :  void WINAPI Profiler::FunctionEntryHook(EntryFrame* entry_frame,
 699    :                                          FuncAddr function,
 700  E :                                          uint64 cycles) {
 701  E :    ScopedLastErrorKeeper keep_last_error;
 702    :  
 703  E :    Profiler* profiler = Profiler::Instance();
 704  E :    ThreadState* data = profiler->GetOrAllocateThreadState();
 705  E :    DCHECK(data != NULL);
 706  E :    if (data != NULL)
 707  E :      data->OnFunctionEntry(entry_frame, function, cycles);
 708  E :  }
 709    :  
 710    :  }  // namespace profiler
 711    :  }  // namespace agent

Coverage information generated Thu Sep 06 11:30:46 2012.