Coverage for /Syzygy/sampler/sampling_profiler.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
84.7%72850.C++source

Line-by-line coverage:

   1    :  // Copyright 2014 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    :  #include "syzygy/sampler/sampling_profiler.h"
  16    :  
  17    :  #include <winternl.h>  // for NTSTATUS.
  18    :  
  19    :  #include "base/lazy_instance.h"
  20    :  
  21    :  // Copied from wdm.h in the WDK as we don't want to take
  22    :  // a dependency on the WDK.
  23    :  typedef enum _KPROFILE_SOURCE {
  24    :      ProfileTime,
  25    :      ProfileAlignmentFixup,
  26    :      ProfileTotalIssues,
  27    :      ProfilePipelineDry,
  28    :      ProfileLoadInstructions,
  29    :      ProfilePipelineFrozen,
  30    :      ProfileBranchInstructions,
  31    :      ProfileTotalNonissues,
  32    :      ProfileDcacheMisses,
  33    :      ProfileIcacheMisses,
  34    :      ProfileCacheMisses,
  35    :      ProfileBranchMispredictions,
  36    :      ProfileStoreInstructions,
  37    :      ProfileFpInstructions,
  38    :      ProfileIntegerInstructions,
  39    :      Profile2Issue,
  40    :      Profile3Issue,
  41    :      Profile4Issue,
  42    :      ProfileSpecialInstructions,
  43    :      ProfileTotalCycles,
  44    :      ProfileIcacheIssues,
  45    :      ProfileDcacheAccesses,
  46    :      ProfileMemoryBarrierCycles,
  47    :      ProfileLoadLinkedIssues,
  48    :      ProfileMaximum
  49    :  } KPROFILE_SOURCE;
  50    :  
  51    :  
  52    :  namespace {
  53    :  
  54    :  // Signatures for the native functions we need to access the sampling profiler.
  55    :  typedef NTSTATUS (NTAPI *ZwSetIntervalProfileFunc)(ULONG, KPROFILE_SOURCE);
  56    :  typedef NTSTATUS (NTAPI *ZwQueryIntervalProfileFunc)(KPROFILE_SOURCE, PULONG);
  57    :  
  58    :  typedef NTSTATUS (NTAPI *ZwCreateProfileFunc)(PHANDLE profile,
  59    :                                                HANDLE process,
  60    :                                                PVOID code_start,
  61    :                                                ULONG code_size,
  62    :                                                ULONG eip_bucket_shift,
  63    :                                                PULONG buckets,
  64    :                                                ULONG buckets_byte_size,
  65    :                                                KPROFILE_SOURCE source,
  66    :                                                DWORD_PTR processor_mask);
  67    :  
  68    :  typedef NTSTATUS (NTAPI *ZwStartProfileFunc)(HANDLE);
  69    :  typedef NTSTATUS (NTAPI *ZwStopProfileFunc)(HANDLE);
  70    :  
  71    :  // This class is used to lazy-initialize pointers to the native
  72    :  // functions we need to access.
  73    :  class ProfilerFuncs {
  74    :   public:
  75    :    ProfilerFuncs();
  76    :  
  77    :    ZwSetIntervalProfileFunc ZwSetIntervalProfile;
  78    :    ZwQueryIntervalProfileFunc ZwQueryIntervalProfile;
  79    :    ZwCreateProfileFunc ZwCreateProfile;
  80    :    ZwStartProfileFunc ZwStartProfile;
  81    :    ZwStopProfileFunc ZwStopProfile;
  82    :  
  83    :    // True iff all of the function pointers above were successfully initialized.
  84    :    bool initialized_;
  85    :  };
  86    :  
  87    :  ProfilerFuncs::ProfilerFuncs()
  88    :      : ZwSetIntervalProfile(NULL),
  89    :        ZwQueryIntervalProfile(NULL),
  90    :        ZwCreateProfile(NULL),
  91    :        ZwStartProfile(NULL),
  92    :        ZwStopProfile(NULL),
  93  E :        initialized_(false) {
  94  E :    HMODULE ntdll = ::GetModuleHandle(L"ntdll.dll");
  95  E :    if (ntdll != NULL) {
  96    :      ZwSetIntervalProfile = reinterpret_cast<ZwSetIntervalProfileFunc>(
  97  E :          ::GetProcAddress(ntdll, "ZwSetIntervalProfile"));
  98    :      ZwQueryIntervalProfile = reinterpret_cast<ZwQueryIntervalProfileFunc>(
  99  E :          ::GetProcAddress(ntdll, "ZwQueryIntervalProfile"));
 100    :      ZwCreateProfile = reinterpret_cast<ZwCreateProfileFunc>(
 101  E :          ::GetProcAddress(ntdll, "ZwCreateProfile"));
 102    :      ZwStartProfile = reinterpret_cast<ZwStartProfileFunc>(
 103  E :          ::GetProcAddress(ntdll, "ZwStartProfile"));
 104    :      ZwStopProfile = reinterpret_cast<ZwStopProfileFunc>(
 105  E :          ::GetProcAddress(ntdll, "ZwStopProfile"));
 106    :  
 107    :      if (ZwSetIntervalProfile &&
 108    :          ZwQueryIntervalProfile &&
 109    :          ZwCreateProfile &&
 110    :          ZwStartProfile &&
 111  E :          ZwStopProfile) {
 112  E :        initialized_ = true;
 113    :      }
 114    :    }
 115  E :  }
 116    :  
 117    :  base::LazyInstance<ProfilerFuncs>::Leaky funcs = LAZY_INSTANCE_INITIALIZER;
 118    :  
 119    :  }  // namespace
 120    :  
 121    :  
 122    :  namespace sampler {
 123    :  
 124  E :  SamplingProfiler::SamplingProfiler() : is_started_(false) {
 125  E :  }
 126    :  
 127  E :  SamplingProfiler::~SamplingProfiler() {
 128  E :    if (is_started_) {
 129  i :      CHECK(Stop()) <<
 130    :          "Unable to stop sampling profiler, this will cause memory corruption.";
 131    :    }
 132  E :  }
 133    :  
 134    :  bool SamplingProfiler::Initialize(HANDLE process,
 135    :                                    void* start,
 136    :                                    size_t size,
 137  E :                                    size_t log2_bucket_size) {
 138    :    // You only get to initialize each instance once.
 139  E :    DCHECK(!profile_handle_.IsValid());
 140  E :    DCHECK(!is_started_);
 141  E :    DCHECK(start != NULL);
 142  E :    DCHECK_NE(0U, size);
 143  E :    DCHECK_LE(2u, log2_bucket_size);
 144  E :    DCHECK_GE(32u, log2_bucket_size);
 145    :  
 146    :    // Bail if the native functions weren't found.
 147  E :    if (!funcs.Get().initialized_)
 148  i :      return false;
 149    :  
 150  E :    size_t bucket_size = 1 << log2_bucket_size;
 151  E :    size_t num_buckets = (size + bucket_size - 1) / bucket_size;
 152  E :    DCHECK(num_buckets != 0);
 153  E :    buckets_.resize(num_buckets);
 154    :  
 155    :    // Get our affinity mask for the call below.
 156  E :    DWORD_PTR process_affinity = 0;
 157  E :    DWORD_PTR system_affinity = 0;
 158  E :    if (!::GetProcessAffinityMask(process, &process_affinity, &system_affinity)) {
 159  i :      LOG(ERROR) << "Failed to get process affinity mask.";
 160  i :      return false;
 161    :    }
 162    :  
 163  E :    HANDLE profile = NULL;
 164    :    NTSTATUS status =
 165    :        funcs.Get().ZwCreateProfile(&profile,
 166    :                                    process,
 167    :                                    start,
 168    :                                    static_cast<ULONG>(size),
 169    :                                    static_cast<ULONG>(log2_bucket_size),
 170    :                                    &buckets_[0],
 171    :                                    static_cast<ULONG>(
 172    :                                        sizeof(buckets_[0]) * num_buckets),
 173    :                                    ProfileTime,
 174  E :                                    process_affinity);
 175    :  
 176  E :    if (!NT_SUCCESS(status)) {
 177    :      // Might as well deallocate the buckets.
 178  i :      buckets_.resize(0);
 179  i :      LOG(ERROR) << "Failed to create profile, error 0x" << std::hex << status;
 180  i :      return false;
 181    :    }
 182    :  
 183  E :    DCHECK(profile != NULL);
 184  E :    profile_handle_.Set(profile);
 185    :  
 186  E :    return true;
 187  E :  }
 188    :  
 189  E :  bool SamplingProfiler::Start() {
 190  E :    DCHECK(profile_handle_.IsValid());
 191  E :    DCHECK(!is_started_);
 192  E :    DCHECK(funcs.Get().initialized_);
 193    :  
 194  E :    NTSTATUS status = funcs.Get().ZwStartProfile(profile_handle_.Get());
 195  E :    if (!NT_SUCCESS(status))
 196  i :      return false;
 197    :  
 198  E :    is_started_ = true;
 199    :  
 200  E :    return true;
 201  E :  }
 202    :  
 203  E :  bool SamplingProfiler::Stop() {
 204  E :    DCHECK(profile_handle_.IsValid());
 205  E :    DCHECK(is_started_);
 206  E :    DCHECK(funcs.Get().initialized_);
 207    :  
 208  E :    NTSTATUS status = funcs.Get().ZwStopProfile(profile_handle_.Get());
 209  E :    if (!NT_SUCCESS(status))
 210  i :      return false;
 211  E :    is_started_ = false;
 212    :  
 213  E :    return true;
 214  E :  }
 215    :  
 216  E :  bool SamplingProfiler::SetSamplingInterval(base::TimeDelta sampling_interval) {
 217  E :    if (!funcs.Get().initialized_)
 218  i :      return false;
 219    :  
 220    :    // According to Nebbet, the sampling interval is in units of 100ns.
 221  E :    ULONG interval = sampling_interval.InMicroseconds() * 10;
 222  E :    NTSTATUS status = funcs.Get().ZwSetIntervalProfile(interval, ProfileTime);
 223  E :    if (!NT_SUCCESS(status))
 224  i :      return false;
 225    :  
 226  E :    return true;
 227  E :  }
 228    :  
 229  E :  bool SamplingProfiler::GetSamplingInterval(base::TimeDelta* sampling_interval) {
 230  E :    DCHECK(sampling_interval != NULL);
 231    :  
 232  E :    if (!funcs.Get().initialized_)
 233  i :      return false;
 234    :  
 235  E :    ULONG interval = 0;
 236  E :    NTSTATUS status = funcs.Get().ZwQueryIntervalProfile(ProfileTime, &interval);
 237  E :    if (!NT_SUCCESS(status))
 238  i :      return false;
 239    :  
 240    :    // According to Nebbet, the sampling interval is in units of 100ns.
 241  E :    *sampling_interval = base::TimeDelta::FromMicroseconds(interval / 10);
 242    :  
 243  E :    return true;
 244  E :  }
 245    :  
 246    :  }  // namespace sampler

Coverage information generated Thu Mar 26 16:15:41 2015.