Coverage for /Syzygy/sampler/sampled_module_cache.h

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

Coverage information generated Thu Jul 04 09:34:53 2013.