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_
|