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 E : DWORD length = ::GetModuleFileNameExW(process,
41 : module,
42 : filename.data(),
43 : 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 : std::unique_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 E : 0, FALSE, DUPLICATE_SAME_ACCESS) ||
102 : 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 : std::unique_ptr<Module> mod(
216 : new Module(this, module_handle, log2_bucket_size));
217 :
218 E : if (!mod->Init())
219 E : return false;
220 :
221 E : if (!mod->Start())
222 i : return false;
223 :
224 : // Initialization was successful so we can safely insert the initialized
225 : // (and currently profiling) module into the map.
226 E : mod_it = modules_.insert(std::make_pair(module_handle, mod.release())).first;
227 E : MarkAlive();
228 :
229 E : *status = kProfilingStarted;
230 E : *module = mod_it->second;
231 E : return true;
232 E : }
233 :
234 E : void SampledModuleCache::Process::MarkDead() {
235 : // Mark all of our children as dead, and ourselves.
236 E : alive_ = false;
237 E : for (ModuleMap::iterator it = modules_.begin(); it != modules_.end(); ++it)
238 E : it->second->MarkDead();
239 E : }
240 :
241 : void SampledModuleCache::Process::RemoveDeadModules(
242 E : DeadModuleCallback callback) {
243 E : ModuleMap::iterator mod_it = modules_.begin();
244 E : ModuleMap::iterator mod_it_next = mod_it;
245 :
246 E : while (mod_it != modules_.end()) {
247 E : DCHECK(mod_it->second != NULL);
248 E : ++mod_it_next;
249 :
250 E : if (!mod_it->second->alive()) {
251 : // Stop profiling.
252 E : mod_it->second->Stop();
253 :
254 : // Return the results to the callback if one has been provided.
255 E : if (!callback.is_null())
256 E : callback.Run(mod_it->second);
257 :
258 : // And clean things up.
259 E : Module* mod = mod_it->second;
260 E : delete mod;
261 E : modules_.erase(mod_it);
262 : }
263 :
264 E : mod_it = mod_it_next;
265 E : }
266 E : }
267 :
268 : SampledModuleCache::Module::Module(Process* process,
269 : HMODULE module,
270 : size_t log2_bucket_size)
271 E : : process_(process),
272 E : module_(module),
273 E : module_size_(0),
274 E : module_checksum_(0),
275 E : module_time_date_stamp_(0),
276 E : buckets_begin_(NULL),
277 E : buckets_end_(NULL),
278 E : log2_bucket_size_(log2_bucket_size),
279 E : profiling_start_time_(0),
280 E : profiling_stop_time_(0),
281 E : alive_(true) {
282 E : DCHECK(process != NULL);
283 E : DCHECK(module_ != INVALID_HANDLE_VALUE);
284 E : DCHECK_LE(2u, log2_bucket_size);
285 E : DCHECK_GE(31u, log2_bucket_size);
286 E : }
287 :
288 E : bool SampledModuleCache::Module::Init() {
289 E : if (!GetModulePath(process_->process(), module_, &module_path_))
290 E : return false;
291 :
292 : // Read the headers.
293 E : char headers[4096] = {};
294 E : size_t net_bytes_read = 0;
295 E : size_t empty_reads = 0;
296 E : while (net_bytes_read < sizeof(headers)) {
297 E : SIZE_T bytes_read = 0;
298 : if (::ReadProcessMemory(process_->process(),
299 : module_,
300 : headers + net_bytes_read,
301 : sizeof(headers) - net_bytes_read,
302 E : &bytes_read) == FALSE) {
303 i : DWORD error = ::GetLastError();
304 i : LOG(ERROR) << "ReadProcessMemory failed for module at address "
305 : << base::StringPrintf("0x%08X", module_)
306 : << " of process " << process_->pid() << ": "
307 : << common::LogWe(error);
308 i : return false;
309 : }
310 E : if (bytes_read == 0) {
311 i : if (++empty_reads == 3) {
312 i : LOG(ERROR) << "ReadProcessMemory unable to read headers for module at "
313 : << "address " << base::StringPrintf("0x%08X", module_)
314 : << " of process " << process_->pid() << ".";
315 i : return false;
316 : }
317 i : } else {
318 E : net_bytes_read += bytes_read;
319 E : empty_reads = 0;
320 : }
321 E : }
322 :
323 : const IMAGE_DOS_HEADER* dos_header =
324 E : reinterpret_cast<const IMAGE_DOS_HEADER*>(headers);
325 : static_assert(sizeof(IMAGE_DOS_HEADER) <= sizeof(headers),
326 : "headers must be big enough for DOS headers.");
327 :
328 : // Get the NT headers and make sure they're fully contained in the block we
329 : // read.
330 E : if (dos_header->e_lfanew > sizeof(headers))
331 i : return false;
332 : const IMAGE_NT_HEADERS* nt_headers =
333 E : reinterpret_cast<const IMAGE_NT_HEADERS*>(headers + dos_header->e_lfanew);
334 E : if (reinterpret_cast<const char*>(nt_headers + 1) - headers > sizeof(headers))
335 i : return false;
336 :
337 : // Get the section headers and make sure they're fully contained in the
338 : // block we read.
339 E : size_t section_count = nt_headers->FileHeader.NumberOfSections;
340 : const IMAGE_SECTION_HEADER* section_headers =
341 E : reinterpret_cast<const IMAGE_SECTION_HEADER*>(nt_headers + 1);
342 E : if (reinterpret_cast<const char*>(section_headers + section_count) - headers >
343 : sizeof(headers)) {
344 i : return false;
345 : }
346 :
347 E : module_size_ = nt_headers->OptionalHeader.SizeOfImage;
348 E : module_checksum_ = nt_headers->OptionalHeader.CheckSum;
349 E : module_time_date_stamp_ = nt_headers->FileHeader.TimeDateStamp;
350 :
351 : // Find the RVA range associated with any text segments in the module.
352 E : DWORD text_begin = SIZE_MAX;
353 E : DWORD text_end = 0;
354 E : for (size_t i = 0; i < section_count; ++i) {
355 E : const IMAGE_SECTION_HEADER& sh = section_headers[i];
356 : static const DWORD kExecFlags = IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_CNT_CODE;
357 E : if ((sh.Characteristics & kExecFlags) == 0)
358 E : continue;
359 :
360 E : DWORD sec_begin = sh.VirtualAddress;
361 E : DWORD sec_end = sec_begin + sh.Misc.VirtualSize;
362 E : if (sec_begin < text_begin)
363 E : text_begin = sec_begin;
364 E : if (sec_end > text_end)
365 E : text_end = sec_end;
366 E : }
367 :
368 : // Adjust the address range for the bucket size.
369 E : DWORD bucket_size = 1 << log2_bucket_size_;
370 E : text_begin = (text_begin / bucket_size) * bucket_size;
371 E : text_end = ((text_end + bucket_size - 1) / bucket_size) * bucket_size;
372 :
373 : // Calculate the number of buckets.
374 E : DCHECK_EQ(0u, (text_end - text_begin) % bucket_size);
375 E : DWORD bucket_count = (text_end - text_begin) / bucket_size;
376 :
377 : // Calculate the bucket range in the remote address space.
378 E : buckets_begin_ = reinterpret_cast<const void*>(
379 : reinterpret_cast<const char*>(module_) + text_begin);
380 E : buckets_end_ = reinterpret_cast<const void*>(
381 : reinterpret_cast<const char*>(module_) + text_end);
382 :
383 : // Initialize the profiler.
384 E : if (!profiler_.Initialize(process_->process(),
385 : const_cast<void*>(buckets_begin_),
386 : text_end - text_begin,
387 : log2_bucket_size_)) {
388 i : LOG(ERROR) << "Failed to initialize profiler for address range "
389 : << base::StringPrintf("0x%08X - 0x%08X",
390 : buckets_begin_,
391 : buckets_end_)
392 : << " of process " << process_->pid() << ".";
393 i : return false;
394 : }
395 E : DCHECK_EQ(bucket_count, profiler_.buckets().size());
396 :
397 E : return true;
398 E : }
399 :
400 E : bool SampledModuleCache::Module::Start() {
401 E : if (!profiler_.Start())
402 i : return false;
403 E : profiling_start_time_ = trace::common::GetTsc();
404 E : return true;
405 E : }
406 :
407 E : bool SampledModuleCache::Module::Stop() {
408 E : if (!profiler_.Stop())
409 i : return false;
410 E : profiling_stop_time_ = trace::common::GetTsc();
411 E : return true;
412 E : }
413 :
414 : } // namespace sampler
|