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 : #include "syzygy/sampler/sampled_module_cache.h"
16 :
17 : #include <psapi.h>
18 :
19 : #include "base/strings/stringprintf.h"
20 : #include "syzygy/common/align.h"
21 : #include "syzygy/common/com_utils.h"
22 : #include "syzygy/common/path_util.h"
23 : #include "syzygy/pe/pe_file.h"
24 : #include "syzygy/trace/common/clock.h"
25 :
26 : namespace sampler {
27 :
28 : namespace {
29 :
30 : typedef SampledModuleCache::Process::ModuleMap ModuleMap;
31 :
32 : // Gets the path associated with a module.
33 E : bool GetModulePath(HANDLE process, HMODULE module, base::FilePath* path) {
34 E : DCHECK(process != INVALID_HANDLE_VALUE);
35 E : DCHECK(module != INVALID_HANDLE_VALUE);
36 E : DCHECK(path != NULL);
37 :
38 E : std::vector<wchar_t> filename(1024);
39 E : while (true) {
40 : DWORD length = ::GetModuleFileNameExW(process,
41 : module,
42 : filename.data(),
43 E : filename.size());
44 E : if (length == 0) {
45 E : DWORD error = ::GetLastError();
46 E : LOG(ERROR) << "GetModuleFileNameExW failed: " << common::LogWe(error);
47 E : return false;
48 : }
49 :
50 : // If we didn't use the entire vector than we had enough room and we
51 : // managed to read the entire filename.
52 E : if (length < filename.size())
53 E : break;
54 :
55 : // Otherwise we need more space, so double the vector and try again.
56 i : filename.resize(filename.size() * 2);
57 i : }
58 :
59 E : base::FilePath temp_path = base::FilePath(filename.data());
60 :
61 E : if (!common::ConvertDevicePathToDrivePath(temp_path, path))
62 i : return false;
63 :
64 E : return true;
65 E : }
66 :
67 : } // namespace
68 :
69 : SampledModuleCache::SampledModuleCache(size_t log2_bucket_size)
70 E : : log2_bucket_size_(log2_bucket_size), module_count_(0) {
71 E : DCHECK_LE(2u, log2_bucket_size);
72 E : DCHECK_GE(31u, log2_bucket_size);
73 E : }
74 :
75 E : SampledModuleCache::~SampledModuleCache() {
76 : // Force a clean up of all modules (and consequently all processes).
77 E : MarkAllModulesDead();
78 E : RemoveDeadModules();
79 E : }
80 :
81 : bool SampledModuleCache::AddModule(HANDLE process,
82 : HMODULE module_handle,
83 : ProfilingStatus* status,
84 E : const Module** module) {
85 E : DCHECK(process != INVALID_HANDLE_VALUE);
86 E : DCHECK(status != NULL);
87 E : DCHECK(module != NULL);
88 :
89 E : *module = NULL;
90 :
91 : // Create or find the process object. We don't actually insert it into the
92 : // map until everything has succeeded, saving us the cleanup on failure.
93 E : DWORD pid = ::GetProcessId(process);
94 E : scoped_ptr<Process> scoped_proc;
95 E : Process* proc = NULL;
96 E : ProcessMap::iterator proc_it = processes_.find(pid);
97 E : if (proc_it == processes_.end()) {
98 E : HANDLE temp_handle = INVALID_HANDLE_VALUE;
99 : if (!::DuplicateHandle(::GetCurrentProcess(), process,
100 : ::GetCurrentProcess(), &temp_handle,
101 : 0, FALSE, DUPLICATE_SAME_ACCESS) ||
102 E : temp_handle == INVALID_HANDLE_VALUE) {
103 i : DWORD error = ::GetLastError();
104 i : LOG(ERROR) << "Failed to duplicate handle to process " << pid << ": "
105 : << common::LogWe(error);
106 i : return false;
107 : }
108 :
109 E : scoped_proc.reset(new Process(temp_handle, pid));
110 :
111 E : if (!scoped_proc->Init())
112 i : return false;
113 :
114 E : proc = scoped_proc.get();
115 E : } else {
116 E : proc = proc_it->second;
117 : }
118 E : DCHECK(proc != NULL);
119 :
120 E : if (!proc->AddModule(module_handle, log2_bucket_size_, status, module))
121 E : return false;
122 :
123 E : DCHECK(*module != NULL);
124 E : if (*status == kProfilingStarted)
125 E : ++module_count_;
126 :
127 E : if (scoped_proc.get() != NULL) {
128 : // Initialization was successful so we can safely insert the newly created
129 : // process into the map.
130 E : processes_.insert(std::make_pair(pid, proc));
131 E : scoped_proc.release();
132 : }
133 :
134 E : return true;
135 E : }
136 :
137 E : void SampledModuleCache::MarkAllModulesDead() {
138 E : for (ProcessMap::iterator proc_it = processes_.begin();
139 E : proc_it != processes_.end(); ++proc_it) {
140 E : proc_it->second->MarkDead();
141 E : }
142 E : }
143 :
144 E : void SampledModuleCache::RemoveDeadModules() {
145 E : ProcessMap::iterator proc_it = processes_.begin();
146 E : ProcessMap::iterator proc_it_next = proc_it;
147 :
148 E : while (proc_it != processes_.end()) {
149 E : ++proc_it_next;
150 :
151 : // Remove any dead modules from the process.
152 E : size_t old_module_count = proc_it->second->modules().size();
153 E : proc_it->second->RemoveDeadModules(dead_module_callback_);
154 E : size_t new_module_count = proc_it->second->modules().size();
155 :
156 : // If the process itself is dead (contains no more profiling modules) then
157 : // remove it.
158 E : if (!proc_it->second->alive()) {
159 E : Process* proc = proc_it->second;
160 E : delete proc;
161 E : processes_.erase(proc_it);
162 : }
163 :
164 E : module_count_ += new_module_count;
165 E : DCHECK_LE(old_module_count, module_count_);
166 E : module_count_ -= old_module_count;
167 :
168 E : proc_it = proc_it_next;
169 E : }
170 E : }
171 :
172 : SampledModuleCache::Process::Process(HANDLE process, DWORD pid)
173 E : : process_(process), pid_(pid), alive_(true) {
174 E : DCHECK(process != INVALID_HANDLE_VALUE);
175 E : }
176 :
177 E : SampledModuleCache::Process::~Process() {
178 E : MarkDead();
179 E : RemoveDeadModules(DeadModuleCallback());
180 E : }
181 :
182 E : bool SampledModuleCache::Process::Init() {
183 E : if (!process_info_.Initialize(pid_))
184 i : return false;
185 E : return true;
186 E : }
187 :
188 : bool SampledModuleCache::Process::AddModule(HMODULE module_handle,
189 : size_t log2_bucket_size,
190 : ProfilingStatus* status,
191 E : const Module** module) {
192 E : DCHECK(module != INVALID_HANDLE_VALUE);
193 E : DCHECK_LE(2u, log2_bucket_size);
194 E : DCHECK_GE(31u, log2_bucket_size);
195 E : DCHECK(status != NULL);
196 E : DCHECK(module != NULL);
197 :
198 E : *module = NULL;
199 :
200 E : ModuleMap::iterator mod_it = modules_.find(module_handle);
201 E : if (mod_it != modules_.end()) {
202 : // The module is already being profiled. Simply mark it as being alive.
203 E : mod_it->second->MarkAlive();
204 :
205 : // And mark ourselves as being alive while we're at it.
206 E : MarkAlive();
207 :
208 E : *status = kProfilingContinued;
209 E : *module = mod_it->second;
210 E : return true;
211 : }
212 :
213 : // Create a new module object. We don't actually insert it into the map until
214 : // everything has succeeded, saving us the cleanup on failure.
215 E : scoped_ptr<Module> mod(new Module(this, module_handle, log2_bucket_size));
216 :
217 E : if (!mod->Init())
218 E : return false;
219 :
220 E : if (!mod->Start())
221 i : return false;
222 :
223 : // Initialization was successful so we can safely insert the initialized
224 : // (and currently profiling) module into the map.
225 E : mod_it = modules_.insert(std::make_pair(module_handle, mod.release())).first;
226 E : MarkAlive();
227 :
228 E : *status = kProfilingStarted;
229 E : *module = mod_it->second;
230 E : return true;
231 E : }
232 :
233 E : void SampledModuleCache::Process::MarkDead() {
234 : // Mark all of our children as dead, and ourselves.
235 E : alive_ = false;
236 E : for (ModuleMap::iterator it = modules_.begin(); it != modules_.end(); ++it)
237 E : it->second->MarkDead();
238 E : }
239 :
240 : void SampledModuleCache::Process::RemoveDeadModules(
241 E : DeadModuleCallback callback) {
242 E : ModuleMap::iterator mod_it = modules_.begin();
243 E : ModuleMap::iterator mod_it_next = mod_it;
244 :
245 E : while (mod_it != modules_.end()) {
246 E : DCHECK(mod_it->second != NULL);
247 E : ++mod_it_next;
248 :
249 E : if (!mod_it->second->alive()) {
250 : // Stop profiling.
251 E : mod_it->second->Stop();
252 :
253 : // Return the results to the callback if one has been provided.
254 E : if (!callback.is_null())
255 E : callback.Run(mod_it->second);
256 :
257 : // And clean things up.
258 E : Module* mod = mod_it->second;
259 E : delete mod;
260 E : modules_.erase(mod_it);
261 : }
262 :
263 E : mod_it = mod_it_next;
264 E : }
265 E : }
266 :
267 : SampledModuleCache::Module::Module(Process* process,
268 : HMODULE module,
269 : size_t log2_bucket_size)
270 : : process_(process),
271 : module_(module),
272 : module_size_(0),
273 : module_checksum_(0),
274 : module_time_date_stamp_(0),
275 : buckets_begin_(NULL),
276 : buckets_end_(NULL),
277 : log2_bucket_size_(log2_bucket_size),
278 : profiling_start_time_(0),
279 : profiling_stop_time_(0),
280 E : alive_(true) {
281 E : DCHECK(process != NULL);
282 E : DCHECK(module_ != INVALID_HANDLE_VALUE);
283 E : DCHECK_LE(2u, log2_bucket_size);
284 E : DCHECK_GE(31u, log2_bucket_size);
285 E : }
286 :
287 E : bool SampledModuleCache::Module::Init() {
288 E : if (!GetModulePath(process_->process(), module_, &module_path_))
289 E : return false;
290 :
291 : // Read the headers.
292 E : char headers[4096] = {};
293 E : size_t net_bytes_read = 0;
294 E : size_t empty_reads = 0;
295 E : while (net_bytes_read < sizeof(headers)) {
296 E : SIZE_T bytes_read = 0;
297 : if (::ReadProcessMemory(process_->process(),
298 : module_,
299 : headers + net_bytes_read,
300 : sizeof(headers) - net_bytes_read,
301 E : &bytes_read) == FALSE) {
302 i : DWORD error = ::GetLastError();
303 i : LOG(ERROR) << "ReadProcessMemory failed for module at address "
304 : << base::StringPrintf("0x%08X", module_)
305 : << " of process " << process_->pid() << ": "
306 : << common::LogWe(error);
307 i : return false;
308 : }
309 E : if (bytes_read == 0) {
310 i : if (++empty_reads == 3) {
311 i : LOG(ERROR) << "ReadProcessMemory unable to read headers for module at "
312 : << "address " << base::StringPrintf("0x%08X", module_)
313 : << " of process " << process_->pid() << ".";
314 i : return false;
315 : }
316 i : } else {
317 E : net_bytes_read += bytes_read;
318 E : empty_reads = 0;
319 : }
320 E : }
321 :
322 : const IMAGE_DOS_HEADER* dos_header =
323 E : reinterpret_cast<const IMAGE_DOS_HEADER*>(headers);
324 : static_assert(sizeof(IMAGE_DOS_HEADER) <= sizeof(headers),
325 : "headers must be big enough for DOS headers.");
326 :
327 : // Get the NT headers and make sure they're fully contained in the block we
328 : // read.
329 E : if (dos_header->e_lfanew > sizeof(headers))
330 i : return false;
331 : const IMAGE_NT_HEADERS* nt_headers =
332 E : reinterpret_cast<const IMAGE_NT_HEADERS*>(headers + dos_header->e_lfanew);
333 E : if (reinterpret_cast<const char*>(nt_headers + 1) - headers > sizeof(headers))
334 i : return false;
335 :
336 : // Get the section headers and make sure they're fully contained in the
337 : // block we read.
338 E : size_t section_count = nt_headers->FileHeader.NumberOfSections;
339 : const IMAGE_SECTION_HEADER* section_headers =
340 E : reinterpret_cast<const IMAGE_SECTION_HEADER*>(nt_headers + 1);
341 : if (reinterpret_cast<const char*>(section_headers + section_count) - headers >
342 E : sizeof(headers)) {
343 i : return false;
344 : }
345 :
346 E : module_size_ = nt_headers->OptionalHeader.SizeOfImage;
347 E : module_checksum_ = nt_headers->OptionalHeader.CheckSum;
348 E : module_time_date_stamp_ = nt_headers->FileHeader.TimeDateStamp;
349 :
350 : // Find the RVA range associated with any text segments in the module.
351 E : DWORD text_begin = SIZE_MAX;
352 E : DWORD text_end = 0;
353 E : for (size_t i = 0; i < section_count; ++i) {
354 E : const IMAGE_SECTION_HEADER& sh = section_headers[i];
355 : static const DWORD kExecFlags = IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_CNT_CODE;
356 E : if ((sh.Characteristics & kExecFlags) == 0)
357 E : continue;
358 :
359 E : DWORD sec_begin = sh.VirtualAddress;
360 E : DWORD sec_end = sec_begin + sh.Misc.VirtualSize;
361 E : if (sec_begin < text_begin)
362 E : text_begin = sec_begin;
363 E : if (sec_end > text_end)
364 E : text_end = sec_end;
365 E : }
366 :
367 : // Adjust the address range for the bucket size.
368 E : DWORD bucket_size = 1 << log2_bucket_size_;
369 E : text_begin = (text_begin / bucket_size) * bucket_size;
370 E : text_end = ((text_end + bucket_size - 1) / bucket_size) * bucket_size;
371 :
372 : // Calculate the number of buckets.
373 E : DCHECK_EQ(0u, (text_end - text_begin) % bucket_size);
374 E : DWORD bucket_count = (text_end - text_begin) / bucket_size;
375 :
376 : // Calculate the bucket range in the remote address space.
377 : buckets_begin_ = reinterpret_cast<const void*>(
378 E : reinterpret_cast<const char*>(module_) + text_begin);
379 : buckets_end_ = reinterpret_cast<const void*>(
380 E : reinterpret_cast<const char*>(module_) + text_end);
381 :
382 : // Initialize the profiler.
383 : if (!profiler_.Initialize(process_->process(),
384 : const_cast<void*>(buckets_begin_),
385 : text_end - text_begin,
386 E : log2_bucket_size_)) {
387 i : LOG(ERROR) << "Failed to initialize profiler for address range "
388 : << base::StringPrintf("0x%08X - 0x%08X",
389 : buckets_begin_,
390 : buckets_end_)
391 : << " of process " << process_->pid() << ".";
392 i : return false;
393 : }
394 E : DCHECK_EQ(bucket_count, profiler_.buckets().size());
395 :
396 E : return true;
397 E : }
398 :
399 E : bool SampledModuleCache::Module::Start() {
400 E : if (!profiler_.Start())
401 i : return false;
402 E : profiling_start_time_ = trace::common::GetTsc();
403 E : return true;
404 E : }
405 :
406 E : bool SampledModuleCache::Module::Stop() {
407 E : if (!profiler_.Stop())
408 i : return false;
409 E : profiling_stop_time_ = trace::common::GetTsc();
410 E : return true;
411 E : }
412 :
413 : } // namespace sampler
|