Coverage for /Syzygy/sampler/sampled_module_cache.h

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
96.9%31320.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    :  // Defines SampledModuleCache. This is container for storing profiling
  16    :  // information for many modules that are being profiled across many processes.
  17    :  // It is intended to be used by a polling monitor which periodically looks for
  18    :  // new modules to be profiled, and detects when old modules are no longer
  19    :  // loaded or when processes have terminated.
  20    :  //
  21    :  // Because of the polling nature the cache contains mechanisms for doing mark
  22    :  // and sweep garbage collection. It is intended to be used as follows:
  23    :  //
  24    :  // // All modules will be profiled with the same bucket size.
  25    :  // SampledModuleCache cache(log2_bucket_size);
  26    :  //
  27    :  // // Set up a callback that will be invoked when profiling is done for a
  28    :  // // module.
  29    :  // cache.set_dead_module_callback(some_callback);
  30    :  //
  31    :  // while (... we wish the profiler to continue running...) {
  32    :  //   // Mark all currently profiling modules as dead.
  33    :  //   cache.MarkAllModulesDead();
  34    :  //
  35    :  //   for (... each module we want to profile ...) {
  36    :  //     // We have a |handle| to the process to be profiled and the
  37    :  //     // |module_handle| to be processed. The module may already be in the
  38    :  //     // process of being profiled, but this will simply mark it as still
  39    :  //     // being alive and eligible for continued profiling. The |status| of the
  40    :  //     // operation and a pointer to the |module| will be set.
  41    :  //     if (cache.AddModule(handle, module_handle, &status, &module))
  42    :  //       ...
  43    :  //   }
  44    :  //
  45    :  //   // Clean up any modules that haven't been added (or re-added and marked as
  46    :  //   // alive). This invokes our callback with the gathered profile data.
  47    :  //   cache.RemoveDeadModules();
  48    :  // }
  49    :  
  50    :  #ifndef SYZYGY_SAMPLER_SAMPLED_MODULE_CACHE_H_
  51    :  #define SYZYGY_SAMPLER_SAMPLED_MODULE_CACHE_H_
  52    :  
  53    :  #include <map>
  54    :  
  55    :  #include "base/callback.h"
  56    :  #include "base/files/file_path.h"
  57    :  #include "base/win/scoped_handle.h"
  58    :  #include "syzygy/application/application.h"
  59    :  #include "syzygy/sampler/sampling_profiler.h"
  60    :  #include "syzygy/trace/common/clock.h"
  61    :  #include "syzygy/trace/service/process_info.h"
  62    :  
  63    :  namespace sampler {
  64    :  
  65    :  class SampledModuleCache {
  66    :   public:
  67    :    // Forward declarations. See below for details.
  68    :    class Process;
  69    :    class Module;
  70    :  
  71    :    enum ProfilingStatus {
  72    :      kProfilingStarted,
  73    :      kProfilingContinued,
  74    :    };
  75    :  
  76    :    typedef std::map<WORD, Process*> ProcessMap;
  77    :  
  78    :    // This is the callback that is used to indicate that a module has been
  79    :    // unloaded and/or we have stopped profiling it (from our point of view, it is
  80    :    // dead). It is up to the callback to deal with the sample data.
  81    :    typedef base::Callback<void(const Module* module)> DeadModuleCallback;
  82    :  
  83    :    // Constructor.
  84    :    // @param log2_bucket_size The number of bits in the bucket size to be used
  85    :    //     by the sampling profiler. This must be in the range 2-31, for bucket
  86    :    //     sizes of 4 bytes to 2 gigabytes. See sampling_profiler.h for more
  87    :    //     details.
  88    :    explicit SampledModuleCache(size_t log2_bucket_size);
  89    :  
  90    :    // Destructor.
  91    :    ~SampledModuleCache();
  92    :  
  93    :    // Sets the callback that is invoked as 'dead' modules are removed from the
  94    :    // cache.
  95    :    // @param callback The callback to be invoked. This may be a default
  96    :    //     constructed (empty) callback to indicate that no callback is to be
  97    :    //     invoked.
  98  E :    void set_dead_module_callback(const DeadModuleCallback& callback) {
  99  E :      dead_module_callback_ = callback;
 100  E :    }
 101    :  
 102    :    // @name Accessors.
 103    :    // @{
 104  E :    const ProcessMap& processes() const { return processes_; }
 105  E :    size_t log2_bucket_size() const { return log2_bucket_size_; }
 106  E :    const DeadModuleCallback& dead_module_callback() const {
 107  E :      return dead_module_callback_;
 108  E :    }
 109    :    // @}
 110    :  
 111    :    // Starts profiling the given module in the given process. If the process and
 112    :    // module are already being profiled this simply marks them as still alive.
 113    :    // @param process A handle to the process. This handle will be duplicated
 114    :    //     and the cache will take responsibility for lifetime management of the
 115    :    //     copy.
 116    :    // @param module_handle The handle to the module to be profiled.
 117    :    // @param status The status of the profiled module. This will only be set on
 118    :    //     success.
 119    :    // @param module A pointer to the added module. This will only be non-NULL on
 120    :    //     success.
 121    :    // @returns true if the process and module were added and profiling was
 122    :    //     successfully started.
 123    :    bool AddModule(HANDLE process,
 124    :                   HMODULE module_handle,
 125    :                   ProfilingStatus* status,
 126    :                   const Module** module);
 127    :  
 128    :    // Marks all processes and modules as dead.
 129    :    void MarkAllModulesDead();
 130    :  
 131    :    // Cleans up no longer running modules and processes. Prior to removal of a
 132    :    // module the dead module callback will be invoked, if set.
 133    :    void RemoveDeadModules();
 134    :  
 135    :    // @returns the total number of modules currently being profiled across all
 136    :    // processes.
 137  E :    size_t module_count() const { return module_count_; }
 138    :  
 139    :   private:
 140    :    // The set of all processes, and modules within them, that are currently
 141    :    // begin profiled.
 142    :    ProcessMap processes_;
 143    :  
 144    :    // The bucket size to be used by all profiler instances created by this
 145    :    // cache.
 146    :    size_t log2_bucket_size_;
 147    :  
 148    :    // The callback that is being invoked when dead modules are removed, or when
 149    :    // the entire cache is being destroyed.
 150    :    DeadModuleCallback dead_module_callback_;
 151    :  
 152    :    // The total number of modules being profiled across all processes.
 153    :    size_t module_count_;
 154    :  
 155    :    DISALLOW_COPY_AND_ASSIGN(SampledModuleCache);
 156    :  };
 157    :  
 158    :  // A Process tracks a process containing one or more modules that are
 159    :  // currently being profiled. Processes are polled so there is no guarantee
 160    :  // that a tracked process is still running.
 161    :  class SampledModuleCache::Process {
 162    :   public:
 163    :    typedef std::map<HMODULE, Module*> ModuleMap;
 164    :  
 165    :    // Constructor. Creates a sampled process for the given process handle.
 166    :    // @param process The handle to the process to be profiled. Ownership of this
 167    :    //     handle is transferred to this object.
 168    :    // @param pid The PID of the process.
 169    :    Process(HANDLE process, DWORD pid);
 170    :  
 171    :    // Destructor.
 172    :    ~Process();
 173    :  
 174    :    // Initializes this process object.
 175    :    // @returns true on success, false otherwise.
 176    :    bool Init();
 177    :  
 178    :    // @name Accessors.
 179    :    // @{
 180  E :    HANDLE process() const { return process_.Get(); }
 181  i :    DWORD pid() const { return pid_; }
 182  E :    ModuleMap& modules() { return modules_; }
 183  E :    const ModuleMap& modules() const { return modules_; }
 184  E :    const trace::service::ProcessInfo& process_info() const {
 185  E :      return process_info_;
 186  E :    }
 187    :    // @}
 188    :  
 189    :    // Adds the provided module to the set of modules that are being profiled in
 190    :    // this process. Only returns true if the module is able to be successfully
 191    :    // queried and the sampling profiler is started.
 192    :    // @param module_handle The handle to the module to be added.
 193    :    // @param log2_bucket_size The number of bits in the bucket size to be used
 194    :    //     by the sampling profiler. This must be in the range 2-31, for bucket
 195    :    //     sizes of 4 bytes to 2 gigabytes. See sampling_profiler.h for more
 196    :    //     details.
 197    :    // @param status The status of the profiled module. This will only be set on
 198    :    //     success.
 199    :    // @param module A pointer to the added module. This will only be non-NULL on
 200    :    //     success.
 201    :    // @returns true on success, false otherwise.
 202    :    bool AddModule(HMODULE module_handle,
 203    :                   size_t log2_bucket_size,
 204    :                   ProfilingStatus* status,
 205    :                   const Module** module);
 206    :  
 207    :   protected:
 208    :    friend class SampledModuleCache;
 209    :  
 210    :    // @name Mark and sweep accessors.  These are used internally by the parent
 211    :    //     SampledModuleCache.
 212    :    // @{
 213  E :    bool alive() const { return alive_; }
 214  E :    void MarkAlive() { alive_ = true; }
 215    :    void MarkDead();
 216    :    // @}
 217    :  
 218    :    // Cleans up no longer running modules.
 219    :    // @param callback The callback to be invoked on each module just prior to
 220    :    //     removing it. May be empty.
 221    :    void RemoveDeadModules(DeadModuleCallback callback);
 222    :  
 223    :   private:
 224    :    friend class SampledModuleCacheTest;  // Testing seam.
 225    :  
 226    :    // A scoped handle to the running process.
 227    :    base::win::ScopedHandle process_;
 228    :  
 229    :    // The process ID of the process. This is used as the key for a
 230    :    // Process.
 231    :    DWORD pid_;
 232    :  
 233    :    // The set of all modules that are currently being profiled.
 234    :    ModuleMap modules_;
 235    :  
 236    :    // Information about this process. This is required for writing trace files.
 237    :    trace::service::ProcessInfo process_info_;
 238    :  
 239    :    // This is used for cleaning up no longer running processes using a mark and
 240    :    // sweep technique. The containing SampledModuleCache enforces the invariant
 241    :    // that alive_ is false if and only if all of our child modules are dead.
 242    :    bool alive_;
 243    :  
 244    :    DISALLOW_COPY_AND_ASSIGN(SampledModuleCache::Process);
 245    :  };
 246    :  
 247    :  // A Module tracks a module (belonging to a Process) that is currently
 248    :  // being profiled by an instance of a SamplingProfiler. Modules are polled so
 249    :  // there is no guarantee that a tracked module is still loaded, nor if its
 250    :  // parent process is still running.
 251    :  class SampledModuleCache::Module {
 252    :   public:
 253    :    // Constructor.
 254    :    // @param process The process to which this module belongs.
 255    :    // @param module The handle to the module to be profiled.
 256    :    // @param log2_bucket_size The number of bits in the bucket size to be used
 257    :    //     by the sampling profiler. This must be in the range 2-31, for bucket
 258    :    //     sizes of 4 bytes to 2 gigabytes. See sampling_profiler.h for more
 259    :    //     details.
 260    :    Module(Process* process, HMODULE module, size_t log2_bucket_size);
 261    :  
 262    :    // @name Accessors.
 263    :    // @{
 264    :    Process* process() { return process_; }
 265  E :    const Process* process() const { return process_; }
 266  E :    HMODULE module() const { return module_; }
 267  E :    const base::FilePath& module_path() const { return module_path_; }
 268  E :    size_t module_size() const { return module_size_; }
 269  E :    uint32_t module_checksum() const { return module_checksum_; }
 270  E :    uint32_t module_time_date_stamp() const { return module_time_date_stamp_; }
 271  E :    const void* buckets_begin() const { return buckets_begin_; }
 272    :    const void* buckets_end() const { return buckets_end_; }
 273  E :    size_t log2_bucket_size() const { return log2_bucket_size_; }
 274  E :    uint64_t profiling_start_time() const { return profiling_start_time_; }
 275  E :    uint64_t profiling_stop_time() const { return profiling_stop_time_; }
 276    :    SamplingProfiler& profiler() { return profiler_; }
 277  E :    const SamplingProfiler& profiler() const { return profiler_; }
 278    :    // @}
 279    :  
 280    :   protected:
 281    :    friend class SampledModuleCache;
 282    :  
 283    :    // @name Mark and sweep accessors. These are used internally by the parent
 284    :    //     SampledModuleCache.
 285    :    // @{
 286  E :    bool alive() const { return alive_; }
 287  E :    void MarkAlive() { alive_ = true; }
 288  E :    void MarkDead() { alive_ = false; }
 289    :    // @}
 290    :  
 291    :    // Initializes this module by reaching into the other process and getting
 292    :    // information about it.
 293    :    // @returns true on success, false otherwise.
 294    :    bool Init();
 295    :  
 296    :    // Starts the sampling profiler.
 297    :    // @returns true on success, false otherwise.
 298    :    bool Start();
 299    :  
 300    :    // Stops the sampling profiler.
 301    :    // @returns true on success, false otherwise.
 302    :    bool Stop();
 303    :  
 304    :   private:
 305    :    friend class SampledModuleCacheTest;  // Testing seam.
 306    :  
 307    :    // A pointer to the metadata about the process in which this module is loaded.
 308    :    Process* process_;
 309    :  
 310    :    // A handle to the module. This is simply a pointer to the base address of the
 311    :    // module in the other process' address space. Modules are stored in
 312    :    // a set in their parent Process, keyed off the module handle.
 313    :    HMODULE module_;
 314    :  
 315    :    // The path to the module.
 316    :    base::FilePath module_path_;
 317    :  
 318    :    // Information that uniquely identifies the module. This information is needed
 319    :    // when we output the TraceSampleData record to the trace file.
 320    :    size_t module_size_;
 321    :    uint32_t module_checksum_;
 322    :    uint32_t module_time_date_stamp_;
 323    :  
 324    :    // Information about the portion of the module being profiled. These are
 325    :    // addresses in the remove module and minimally highlight the .text section
 326    :    // of the image.
 327    :    const void* buckets_begin_;
 328    :    const void* buckets_end_;
 329    :    size_t log2_bucket_size_;
 330    :  
 331    :    // The time when we started and stopped profiling this module, as reported by
 332    :    // RDTSC.
 333    :    uint64_t profiling_start_time_;
 334    :    uint64_t profiling_stop_time_;
 335    :  
 336    :    // The sampling profiler instance that is profiling this module.
 337    :    SamplingProfiler profiler_;
 338    :  
 339    :    // This is used for cleaning up no longer loaded modules using a mark and
 340    :    // sweep technique.
 341    :    bool alive_;
 342    :  
 343    :    DISALLOW_COPY_AND_ASSIGN(SampledModuleCache::Module);
 344    :  };
 345    :  
 346    :  }  // namespace sampler
 347    :  
 348    :  #endif  // SYZYGY_SAMPLER_SAMPLED_MODULE_CACHE_H_

Coverage information generated Fri Jul 29 11:00:21 2016.