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