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

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