Coverage for /Syzygy/sampler/sampled_module_cache.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
89.5%1962190.C++source

Line-by-line coverage:

   1    :  // Copyright 2013 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/sampled_module_cache.h"
  16    :  
  17    :  #include <psapi.h>
  18    :  
  19    :  #include "base/strings/stringprintf.h"
  20    :  #include "syzygy/common/align.h"
  21    :  #include "syzygy/common/com_utils.h"
  22    :  #include "syzygy/common/path_util.h"
  23    :  #include "syzygy/pe/pe_file.h"
  24    :  #include "syzygy/trace/common/clock.h"
  25    :  
  26    :  namespace sampler {
  27    :  
  28    :  namespace {
  29    :  
  30    :  typedef SampledModuleCache::Process::ModuleMap ModuleMap;
  31    :  
  32    :  // Gets the path associated with a module.
  33  E :  bool GetModulePath(HANDLE process, HMODULE module, base::FilePath* path) {
  34  E :    DCHECK(process != INVALID_HANDLE_VALUE);
  35  E :    DCHECK(module != INVALID_HANDLE_VALUE);
  36  E :    DCHECK(path != NULL);
  37    :  
  38  E :    std::vector<wchar_t> filename(1024);
  39  E :    while (true) {
  40    :      DWORD length = ::GetModuleFileNameExW(process,
  41    :                                            module,
  42    :                                            filename.data(),
  43  E :                                            filename.size());
  44  E :      if (length == 0) {
  45  E :        DWORD error = ::GetLastError();
  46  E :        LOG(ERROR) << "GetModuleFileNameExW failed: " << common::LogWe(error);
  47  E :        return false;
  48    :      }
  49    :  
  50    :      // If we didn't use the entire vector than we had enough room and we
  51    :      // managed to read the entire filename.
  52  E :      if (length < filename.size())
  53  E :        break;
  54    :  
  55    :      // Otherwise we need more space, so double the vector and try again.
  56  i :      filename.resize(filename.size() * 2);
  57  i :    }
  58    :  
  59  E :    base::FilePath temp_path = base::FilePath(filename.data());
  60    :  
  61  E :    if (!common::ConvertDevicePathToDrivePath(temp_path, path))
  62  i :      return false;
  63    :  
  64  E :    return true;
  65  E :  }
  66    :  
  67    :  }  // namespace
  68    :  
  69    :  SampledModuleCache::SampledModuleCache(size_t log2_bucket_size)
  70  E :        : log2_bucket_size_(log2_bucket_size), module_count_(0) {
  71  E :    DCHECK_LE(2u, log2_bucket_size);
  72  E :    DCHECK_GE(31u, log2_bucket_size);
  73  E :  }
  74    :  
  75  E :  SampledModuleCache::~SampledModuleCache() {
  76    :    // Force a clean up of all modules (and consequently all processes).
  77  E :    MarkAllModulesDead();
  78  E :    RemoveDeadModules();
  79  E :  }
  80    :  
  81    :  bool SampledModuleCache::AddModule(HANDLE process,
  82    :                                     HMODULE module_handle,
  83    :                                     ProfilingStatus* status,
  84  E :                                     const Module** module) {
  85  E :    DCHECK(process != INVALID_HANDLE_VALUE);
  86  E :    DCHECK(status != NULL);
  87  E :    DCHECK(module != NULL);
  88    :  
  89  E :    *module = NULL;
  90    :  
  91    :    // Create or find the process object. We don't actually insert it into the
  92    :    // map until everything has succeeded, saving us the cleanup on failure.
  93  E :    DWORD pid = ::GetProcessId(process);
  94  E :    scoped_ptr<Process> scoped_proc;
  95  E :    Process* proc = NULL;
  96  E :    ProcessMap::iterator proc_it = processes_.find(pid);
  97  E :    if (proc_it == processes_.end()) {
  98  E :      HANDLE temp_handle = INVALID_HANDLE_VALUE;
  99    :      if (!::DuplicateHandle(::GetCurrentProcess(), process,
 100    :                             ::GetCurrentProcess(), &temp_handle,
 101    :                             0, FALSE, DUPLICATE_SAME_ACCESS) ||
 102  E :          temp_handle == INVALID_HANDLE_VALUE) {
 103  i :        DWORD error = ::GetLastError();
 104  i :        LOG(ERROR) << "Failed to duplicate handle to process " << pid << ": "
 105    :                   << common::LogWe(error);
 106  i :        return false;
 107    :      }
 108    :  
 109  E :      scoped_proc.reset(new Process(temp_handle, pid));
 110    :  
 111  E :      if (!scoped_proc->Init())
 112  i :        return false;
 113    :  
 114  E :      proc = scoped_proc.get();
 115  E :    } else {
 116  E :      proc = proc_it->second;
 117    :    }
 118  E :    DCHECK(proc != NULL);
 119    :  
 120  E :    if (!proc->AddModule(module_handle, log2_bucket_size_, status, module))
 121  E :      return false;
 122    :  
 123  E :    DCHECK(*module != NULL);
 124  E :    if (*status == kProfilingStarted)
 125  E :      ++module_count_;
 126    :  
 127  E :    if (scoped_proc.get() != NULL) {
 128    :      // Initialization was successful so we can safely insert the newly created
 129    :      // process into the map.
 130  E :      processes_.insert(std::make_pair(pid, proc));
 131  E :      scoped_proc.release();
 132    :    }
 133    :  
 134  E :    return true;
 135  E :  }
 136    :  
 137  E :  void SampledModuleCache::MarkAllModulesDead() {
 138  E :    for (ProcessMap::iterator proc_it = processes_.begin();
 139  E :         proc_it != processes_.end(); ++proc_it) {
 140  E :      proc_it->second->MarkDead();
 141  E :    }
 142  E :  }
 143    :  
 144  E :  void SampledModuleCache::RemoveDeadModules() {
 145  E :    ProcessMap::iterator proc_it = processes_.begin();
 146  E :    ProcessMap::iterator proc_it_next = proc_it;
 147    :  
 148  E :    while (proc_it != processes_.end()) {
 149  E :      ++proc_it_next;
 150    :  
 151    :      // Remove any dead modules from the process.
 152  E :      size_t old_module_count = proc_it->second->modules().size();
 153  E :      proc_it->second->RemoveDeadModules(dead_module_callback_);
 154  E :      size_t new_module_count = proc_it->second->modules().size();
 155    :  
 156    :      // If the process itself is dead (contains no more profiling modules) then
 157    :      // remove it.
 158  E :      if (!proc_it->second->alive()) {
 159  E :        Process* proc = proc_it->second;
 160  E :        delete proc;
 161  E :        processes_.erase(proc_it);
 162    :      }
 163    :  
 164  E :      module_count_ += new_module_count;
 165  E :      DCHECK_LE(old_module_count, module_count_);
 166  E :      module_count_ -= old_module_count;
 167    :  
 168  E :      proc_it = proc_it_next;
 169  E :    }
 170  E :  }
 171    :  
 172    :  SampledModuleCache::Process::Process(HANDLE process, DWORD pid)
 173  E :      : process_(process), pid_(pid), alive_(true) {
 174  E :    DCHECK(process != INVALID_HANDLE_VALUE);
 175  E :  }
 176    :  
 177  E :  SampledModuleCache::Process::~Process() {
 178  E :    MarkDead();
 179  E :    RemoveDeadModules(DeadModuleCallback());
 180  E :  }
 181    :  
 182  E :  bool SampledModuleCache::Process::Init() {
 183  E :    if (!process_info_.Initialize(pid_))
 184  i :      return false;
 185  E :    return true;
 186  E :  }
 187    :  
 188    :  bool SampledModuleCache::Process::AddModule(HMODULE module_handle,
 189    :                                              size_t log2_bucket_size,
 190    :                                              ProfilingStatus* status,
 191  E :                                              const Module** module) {
 192  E :    DCHECK(module != INVALID_HANDLE_VALUE);
 193  E :    DCHECK_LE(2u, log2_bucket_size);
 194  E :    DCHECK_GE(31u, log2_bucket_size);
 195  E :    DCHECK(status != NULL);
 196  E :    DCHECK(module != NULL);
 197    :  
 198  E :    *module = NULL;
 199    :  
 200  E :    ModuleMap::iterator mod_it = modules_.find(module_handle);
 201  E :    if (mod_it != modules_.end()) {
 202    :      // The module is already being profiled. Simply mark it as being alive.
 203  E :      mod_it->second->MarkAlive();
 204    :  
 205    :      // And mark ourselves as being alive while we're at it.
 206  E :      MarkAlive();
 207    :  
 208  E :      *status = kProfilingContinued;
 209  E :      *module = mod_it->second;
 210  E :      return true;
 211    :    }
 212    :  
 213    :    // Create a new module object. We don't actually insert it into the map until
 214    :    // everything has succeeded, saving us the cleanup on failure.
 215  E :    scoped_ptr<Module> mod(new Module(this, module_handle, log2_bucket_size));
 216    :  
 217  E :    if (!mod->Init())
 218  E :      return false;
 219    :  
 220  E :    if (!mod->Start())
 221  i :      return false;
 222    :  
 223    :    // Initialization was successful so we can safely insert the initialized
 224    :    // (and currently profiling) module into the map.
 225  E :    mod_it = modules_.insert(std::make_pair(module_handle, mod.release())).first;
 226  E :    MarkAlive();
 227    :  
 228  E :    *status = kProfilingStarted;
 229  E :    *module = mod_it->second;
 230  E :    return true;
 231  E :  }
 232    :  
 233  E :  void SampledModuleCache::Process::MarkDead() {
 234    :    // Mark all of our children as dead, and ourselves.
 235  E :    alive_ = false;
 236  E :    for (ModuleMap::iterator it = modules_.begin(); it != modules_.end(); ++it)
 237  E :      it->second->MarkDead();
 238  E :  }
 239    :  
 240    :  void SampledModuleCache::Process::RemoveDeadModules(
 241  E :      DeadModuleCallback callback) {
 242  E :    ModuleMap::iterator mod_it = modules_.begin();
 243  E :    ModuleMap::iterator mod_it_next = mod_it;
 244    :  
 245  E :    while (mod_it != modules_.end()) {
 246  E :      DCHECK(mod_it->second != NULL);
 247  E :      ++mod_it_next;
 248    :  
 249  E :      if (!mod_it->second->alive()) {
 250    :        // Stop profiling.
 251  E :        mod_it->second->Stop();
 252    :  
 253    :        // Return the results to the callback if one has been provided.
 254  E :        if (!callback.is_null())
 255  E :          callback.Run(mod_it->second);
 256    :  
 257    :        // And clean things up.
 258  E :        Module* mod = mod_it->second;
 259  E :        delete mod;
 260  E :        modules_.erase(mod_it);
 261    :      }
 262    :  
 263  E :      mod_it = mod_it_next;
 264  E :    }
 265  E :  }
 266    :  
 267    :  SampledModuleCache::Module::Module(Process* process,
 268    :                                     HMODULE module,
 269    :                                     size_t log2_bucket_size)
 270    :      : process_(process),
 271    :        module_(module),
 272    :        module_size_(0),
 273    :        module_checksum_(0),
 274    :        module_time_date_stamp_(0),
 275    :        buckets_begin_(NULL),
 276    :        buckets_end_(NULL),
 277    :        log2_bucket_size_(log2_bucket_size),
 278    :        profiling_start_time_(0),
 279    :        profiling_stop_time_(0),
 280  E :        alive_(true) {
 281  E :    DCHECK(process != NULL);
 282  E :    DCHECK(module_ != INVALID_HANDLE_VALUE);
 283  E :    DCHECK_LE(2u, log2_bucket_size);
 284  E :    DCHECK_GE(31u, log2_bucket_size);
 285  E :  }
 286    :  
 287  E :  bool SampledModuleCache::Module::Init() {
 288  E :    if (!GetModulePath(process_->process(), module_, &module_path_))
 289  E :      return false;
 290    :  
 291    :    // Read the headers.
 292  E :    char headers[4096] = {};
 293  E :    size_t net_bytes_read = 0;
 294  E :    size_t empty_reads = 0;
 295  E :    while (net_bytes_read < sizeof(headers)) {
 296  E :      SIZE_T bytes_read = 0;
 297    :      if (::ReadProcessMemory(process_->process(),
 298    :                              module_,
 299    :                              headers + net_bytes_read,
 300    :                              sizeof(headers) - net_bytes_read,
 301  E :                              &bytes_read) == FALSE) {
 302  i :        DWORD error = ::GetLastError();
 303  i :        LOG(ERROR) << "ReadProcessMemory failed for module at address "
 304    :                   << base::StringPrintf("0x%08X", module_)
 305    :                   << " of process " << process_->pid() << ": "
 306    :                   << common::LogWe(error);
 307  i :        return false;
 308    :      }
 309  E :      if (bytes_read == 0) {
 310  i :        if (++empty_reads == 3) {
 311  i :          LOG(ERROR) << "ReadProcessMemory unable to read headers for module at "
 312    :                     << "address " << base::StringPrintf("0x%08X", module_)
 313    :                     << " of process " << process_->pid() << ".";
 314  i :          return false;
 315    :        }
 316  i :      } else {
 317  E :        net_bytes_read += bytes_read;
 318  E :        empty_reads = 0;
 319    :      }
 320  E :    }
 321    :  
 322    :    const IMAGE_DOS_HEADER* dos_header =
 323  E :        reinterpret_cast<const IMAGE_DOS_HEADER*>(headers);
 324    :    static_assert(sizeof(IMAGE_DOS_HEADER) <= sizeof(headers),
 325    :                  "headers must be big enough for DOS headers.");
 326    :  
 327    :    // Get the NT headers and make sure they're fully contained in the block we
 328    :    // read.
 329  E :    if (dos_header->e_lfanew > sizeof(headers))
 330  i :      return false;
 331    :    const IMAGE_NT_HEADERS* nt_headers =
 332  E :        reinterpret_cast<const IMAGE_NT_HEADERS*>(headers + dos_header->e_lfanew);
 333  E :    if (reinterpret_cast<const char*>(nt_headers + 1) - headers > sizeof(headers))
 334  i :      return false;
 335    :  
 336    :    // Get the section headers and make sure they're fully contained in the
 337    :    // block we read.
 338  E :    size_t section_count = nt_headers->FileHeader.NumberOfSections;
 339    :    const IMAGE_SECTION_HEADER* section_headers =
 340  E :        reinterpret_cast<const IMAGE_SECTION_HEADER*>(nt_headers + 1);
 341    :    if (reinterpret_cast<const char*>(section_headers + section_count) - headers >
 342  E :        sizeof(headers)) {
 343  i :      return false;
 344    :    }
 345    :  
 346  E :    module_size_ = nt_headers->OptionalHeader.SizeOfImage;
 347  E :    module_checksum_ = nt_headers->OptionalHeader.CheckSum;
 348  E :    module_time_date_stamp_ = nt_headers->FileHeader.TimeDateStamp;
 349    :  
 350    :    // Find the RVA range associated with any text segments in the module.
 351  E :    DWORD text_begin = SIZE_MAX;
 352  E :    DWORD text_end = 0;
 353  E :    for (size_t i = 0; i < section_count; ++i) {
 354  E :      const IMAGE_SECTION_HEADER& sh = section_headers[i];
 355    :      static const DWORD kExecFlags = IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_CNT_CODE;
 356  E :      if ((sh.Characteristics & kExecFlags) == 0)
 357  E :        continue;
 358    :  
 359  E :      DWORD sec_begin = sh.VirtualAddress;
 360  E :      DWORD sec_end = sec_begin + sh.Misc.VirtualSize;
 361  E :      if (sec_begin < text_begin)
 362  E :        text_begin = sec_begin;
 363  E :      if (sec_end > text_end)
 364  E :        text_end = sec_end;
 365  E :    }
 366    :  
 367    :    // Adjust the address range for the bucket size.
 368  E :    DWORD bucket_size = 1 << log2_bucket_size_;
 369  E :    text_begin = (text_begin / bucket_size) * bucket_size;
 370  E :    text_end = ((text_end + bucket_size - 1) / bucket_size) * bucket_size;
 371    :  
 372    :    // Calculate the number of buckets.
 373  E :    DCHECK_EQ(0u, (text_end - text_begin) % bucket_size);
 374  E :    DWORD bucket_count = (text_end - text_begin) / bucket_size;
 375    :  
 376    :    // Calculate the bucket range in the remote address space.
 377    :    buckets_begin_ = reinterpret_cast<const void*>(
 378  E :        reinterpret_cast<const char*>(module_) + text_begin);
 379    :    buckets_end_ = reinterpret_cast<const void*>(
 380  E :        reinterpret_cast<const char*>(module_) + text_end);
 381    :  
 382    :    // Initialize the profiler.
 383    :    if (!profiler_.Initialize(process_->process(),
 384    :                              const_cast<void*>(buckets_begin_),
 385    :                              text_end - text_begin,
 386  E :                              log2_bucket_size_)) {
 387  i :      LOG(ERROR) << "Failed to initialize profiler for address range "
 388    :                 << base::StringPrintf("0x%08X - 0x%08X",
 389    :                                       buckets_begin_,
 390    :                                       buckets_end_)
 391    :                 << " of process " << process_->pid() << ".";
 392  i :      return false;
 393    :    }
 394  E :    DCHECK_EQ(bucket_count, profiler_.buckets().size());
 395    :  
 396  E :    return true;
 397  E :  }
 398    :  
 399  E :  bool SampledModuleCache::Module::Start() {
 400  E :    if (!profiler_.Start())
 401  i :      return false;
 402  E :    profiling_start_time_ = trace::common::GetTsc();
 403  E :    return true;
 404  E :  }
 405    :  
 406  E :  bool SampledModuleCache::Module::Stop() {
 407  E :    if (!profiler_.Stop())
 408  i :      return false;
 409  E :    profiling_stop_time_ = trace::common::GetTsc();
 410  E :    return true;
 411  E :  }
 412    :  
 413    :  }  // namespace sampler

Coverage information generated Thu Jan 14 17:40:38 2016.