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