1 : // Copyright 2012 Google Inc.
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/grinder/profile_grinder.h"
16 :
17 : #include "base/at_exit.h"
18 : #include "base/command_line.h"
19 : #include "base/win/scoped_bstr.h"
20 : #include "sawbuck/common/com_utils.h"
21 : #include "syzygy/pe/find.h"
22 :
23 : namespace grinder {
24 :
25 : using base::win::ScopedBstr;
26 : using base::win::ScopedComPtr;
27 : using trace::parser::AbsoluteAddress64;
28 : using trace::parser::ParseEventHandler;
29 : using sym_util::ModuleInformation;
30 :
31 : namespace {
32 :
33 : // Compares module information without regard to base address.
34 : // Used to canonicalize module information, even across processes, or multiple
35 : // loads for the same module at different addresses in the same process.
36 : bool ModuleInformationKeyLess(const ModuleInformation& a,
37 E : const ModuleInformation& b) {
38 E : if (a.module_size > b.module_size)
39 E : return false;
40 E : if (a.module_size < b.module_size)
41 E : return true;
42 :
43 E : if (a.image_checksum > b.image_checksum)
44 i : return false;
45 E : if (a.image_checksum < b.image_checksum)
46 i : return true;
47 :
48 E : if (a.time_date_stamp > b.time_date_stamp)
49 i : return false;
50 E : if (a.time_date_stamp < b.time_date_stamp)
51 i : return true;
52 :
53 E : return a.image_file_name < b.image_file_name;
54 E : }
55 :
56 : } // namespace
57 :
58 E : ProfileGrinder::PartData::PartData() {
59 E : }
60 :
61 : ProfileGrinder::ProfileGrinder()
62 : : modules_(ModuleInformationKeyLess),
63 : thread_parts_(true),
64 E : parser_(NULL) {
65 E : }
66 :
67 E : ProfileGrinder::~ProfileGrinder() {
68 E : }
69 :
70 E : bool ProfileGrinder::ParseCommandLine(const CommandLine* command_line) {
71 E : thread_parts_ = command_line->HasSwitch("thread-parts");
72 E : return true;
73 E : }
74 :
75 E : void ProfileGrinder::SetParser(Parser* parser) {
76 E : DCHECK(parser != NULL);
77 E : parser_ = parser;
78 E : }
79 :
80 E : bool ProfileGrinder::Grind() {
81 E : if (!ResolveCallers()) {
82 i : LOG(ERROR) << "Error resolving callers.";
83 i : return false;
84 : }
85 E : return true;
86 E : }
87 :
88 : bool ProfileGrinder::GetSessionForModule(const ModuleInformation* module,
89 E : IDiaSession** session_out) {
90 E : DCHECK(module != NULL);
91 E : DCHECK(session_out != NULL);
92 E : DCHECK(*session_out == NULL);
93 :
94 : ModuleSessionMap::const_iterator it(
95 E : module_sessions_.find(module));
96 :
97 E : if (it == module_sessions_.end()) {
98 E : ScopedComPtr<IDiaDataSource> source;
99 E : HRESULT hr = source.CreateInstance(CLSID_DiaSource);
100 E : if (FAILED(hr)) {
101 i : LOG(ERROR) << "Failed to create DiaSource: "
102 : << com::LogHr(hr) << ".";
103 i : return false;
104 : }
105 :
106 E : pe::PEFile::Signature signature;
107 E : signature.path = module->image_file_name;
108 : signature.base_address = core::AbsoluteAddress(
109 E : static_cast<uint32>(module->base_address));
110 E : signature.module_size = module->module_size;
111 E : signature.module_time_date_stamp = module->time_date_stamp;
112 E : signature.module_checksum = module->image_checksum;
113 :
114 E : FilePath module_path;
115 : if (!pe::FindModuleBySignature(signature, &module_path) ||
116 E : module_path.empty()) {
117 i : LOG(ERROR) << "Unable to find module matching signature.";
118 i : return false;
119 : }
120 :
121 E : ScopedComPtr<IDiaSession> new_session;
122 : // We first try loading straight-up for the module. If the module is at
123 : // this path and the symsrv machinery is available, this will bring that
124 : // machinery to bear.
125 : // The downside is that if the module at this path does not match the
126 : // original module, we may load the wrong symbol information for the
127 : // module.
128 E : hr = source->loadDataForExe(module_path.value().c_str(), NULL, NULL);
129 E : if (SUCCEEDED(hr)) {
130 E : hr = source->openSession(new_session.Receive());
131 E : if (FAILED(hr))
132 i : LOG(ERROR) << "Failure in openSession: " << com::LogHr(hr) << ".";
133 E : } else {
134 E : DCHECK(FAILED(hr));
135 :
136 E : FilePath pdb_path;
137 : if (!pe::FindPdbForModule(module_path, &pdb_path) ||
138 E : pdb_path.empty()) {
139 E : LOG(ERROR) << "Unable to find PDB for module \""
140 : << module_path.value() << "\".";
141 E : return false;
142 : }
143 :
144 i : hr = source->loadDataFromPdb(pdb_path.value().c_str());
145 i : if (SUCCEEDED(hr)) {
146 i : hr = source->openSession(new_session.Receive());
147 i : if (FAILED(hr))
148 i : LOG(ERROR) << "Failure in openSession: " << com::LogHr(hr) << ".";
149 i : } else {
150 i : LOG(WARNING) << "Failure in loadDataFromPdb('"
151 : << module_path.value().c_str() << "'): "
152 : << com::LogHr(hr) << ".";
153 : }
154 i : }
155 :
156 : DCHECK((SUCCEEDED(hr) && new_session.get() != NULL) ||
157 E : (FAILED(hr) && new_session.get() == NULL));
158 :
159 : // We store an entry to the cache irrespective of whether we succeeded
160 : // in opening a session above. This allows us to cache the failures, which
161 : // means we attempt to load each module only once, and consequently log
162 : // each failing module only once.
163 : it = module_sessions_.insert(
164 E : std::make_pair(module, new_session)).first;
165 E : }
166 E : DCHECK(it != module_sessions_.end());
167 :
168 E : if (it->second.get() == NULL) {
169 : // A negative session cache entry - we were previously unable to
170 : // load this module.
171 i : return false;
172 : }
173 :
174 E : *session_out = it->second;
175 E : (*session_out)->AddRef();
176 :
177 E : return true;
178 E : }
179 :
180 : ProfileGrinder::PartData* ProfileGrinder::FindOrCreatePart(DWORD process_id,
181 E : DWORD thread_id) {
182 E : if (!thread_parts_) {
183 E : process_id = 0;
184 E : thread_id = 0;
185 : }
186 :
187 : // Lookup the part to aggregate to.
188 E : PartDataMap::iterator it = parts_.find(thread_id);
189 E : if (it == parts_.end()) {
190 E : PartData part;
191 E : part.process_id_ = process_id;
192 E : part.thread_id_ = thread_id;
193 :
194 E : it = parts_.insert(std::make_pair(thread_id, part)).first;
195 E : }
196 :
197 E : return &it->second;
198 E : }
199 :
200 :
201 : bool ProfileGrinder::GetFunctionByRVA(IDiaSession* session,
202 : RVA address,
203 E : IDiaSymbol** symbol) {
204 E : DCHECK(session != NULL);
205 E : DCHECK(symbol != NULL && *symbol == NULL);
206 :
207 E : ScopedComPtr<IDiaSymbol> function;
208 : HRESULT hr = session->findSymbolByRVA(address,
209 : SymTagFunction,
210 E : function.Receive());
211 E : if (FAILED(hr) || function.get() == NULL) {
212 : // No private function, let's try for a public symbol.
213 : hr = session->findSymbolByRVA(address,
214 : SymTagPublicSymbol,
215 i : function.Receive());
216 i : if (FAILED(hr))
217 i : return false;
218 : }
219 E : if (function.get() == NULL) {
220 i : LOG(ERROR) << "NULL function returned from findSymbolByRVA.";
221 i : return false;
222 : }
223 :
224 E : *symbol = function.Detach();
225 :
226 E : return true;
227 E : }
228 :
229 : bool ProfileGrinder::GetInfoForCallerRVA(const ModuleRVA& caller,
230 : RVA* function_rva,
231 E : size_t* line) {
232 E : DCHECK(function_rva != NULL);
233 E : DCHECK(line != NULL);
234 :
235 E : ScopedComPtr<IDiaSession> session;
236 E : if (!GetSessionForModule(caller.module, session.Receive()))
237 E : return false;
238 :
239 E : ScopedComPtr<IDiaSymbol> function;
240 E : if (!GetFunctionByRVA(session.get(), caller.rva, function.Receive())) {
241 i : LOG(ERROR) << "No symbol info available for function in module '"
242 : << caller.module->image_file_name << "'";
243 : }
244 :
245 : // Get the RVA of the function.
246 E : DWORD rva = 0;
247 E : HRESULT hr = function->get_relativeVirtualAddress(&rva);
248 E : if (FAILED(hr)) {
249 i : LOG(ERROR) << "Failure in get_relativeVirtualAddress: "
250 : << com::LogHr(hr) << ".";
251 i : return false;
252 : }
253 E : *function_rva = rva;
254 :
255 E : ULONGLONG length = 0;
256 E : hr = function->get_length(&length);
257 E : if (FAILED(hr)) {
258 i : LOG(ERROR) << "Failure in get_length: " << com::LogHr(hr) << ".";
259 i : return false;
260 : }
261 :
262 E : DWORD line_number = 0;
263 E : if (length != 0) {
264 E : ScopedComPtr<IDiaEnumLineNumbers> enum_lines;
265 :
266 : hr = session->findLinesByRVA(caller.rva,
267 : length,
268 E : enum_lines.Receive());
269 E : if (FAILED(hr)) {
270 i : LOG(ERROR) << "Failure in findLinesByRVA: " << com::LogHr(hr) << ".";
271 i : return false;
272 : }
273 :
274 E : ScopedComPtr<IDiaLineNumber> line;
275 E : ULONG fetched = 0;
276 E : hr = enum_lines->Next(1, line.Receive(), &fetched);
277 E : if (FAILED(hr)) {
278 i : LOG(ERROR) << "Failure in IDiaLineNumber::Next: "
279 : << com::LogHr(hr) << ".";
280 i : return false;
281 : }
282 :
283 E : if (fetched == 1) {
284 E : hr = line->get_lineNumber(&line_number);
285 E : if (FAILED(hr)) {
286 i : LOG(ERROR) << "Failure in get_lineNumber: " << com::LogHr(hr) << ".";
287 i : return false;
288 E : }
289 E : } else if (fetched != 0) {
290 i : NOTREACHED() << "IDiaLineNumber::Next unexpectedly returned "
291 : << fetched << " elements.";
292 : }
293 E : }
294 :
295 E : *line = line_number;
296 E : return true;
297 E : }
298 :
299 : bool ProfileGrinder::GetInfoForFunctionRVA(const ModuleRVA& function,
300 : std::wstring* function_name,
301 : std::wstring* file_name,
302 E : size_t* line) {
303 E : DCHECK(function_name != NULL);
304 E : DCHECK(file_name != NULL);
305 E : DCHECK(line != NULL);
306 :
307 E : ScopedComPtr<IDiaSession> session;
308 E : if (!GetSessionForModule(function.module, session.Receive()))
309 i : return false;
310 :
311 E : ScopedComPtr<IDiaSymbol> function_sym;
312 E : if (!GetFunctionByRVA(session.get(), function.rva, function_sym.Receive())) {
313 i : LOG(ERROR) << "No symbol info available for function in module '"
314 : << function.module->image_file_name << "'";
315 i : return false;
316 : }
317 :
318 E : ScopedBstr function_name_bstr;
319 E : HRESULT hr = function_sym->get_name(function_name_bstr.Receive());
320 E : if (FAILED(hr)) {
321 i : LOG(ERROR) << "Failure in get_name: " << com::LogHr(hr) << ".";
322 i : return false;
323 : }
324 :
325 E : *function_name = com::ToString(function_name_bstr);
326 :
327 E : ULONGLONG length = 0;
328 E : hr = function_sym->get_length(&length);
329 E : if (FAILED(hr)) {
330 i : LOG(ERROR) << "Failure in get_length: " << com::LogHr(hr) << ".";
331 i : return false;
332 : }
333 :
334 E : ScopedBstr file_name_bstr;
335 E : DWORD line_number = 0;
336 E : if (length != 0) {
337 E : ScopedComPtr<IDiaEnumLineNumbers> enum_lines;
338 :
339 : hr = session->findLinesByRVA(function.rva,
340 : length,
341 E : enum_lines.Receive());
342 E : if (FAILED(hr)) {
343 i : LOG(ERROR) << "Failure in findLinesByRVA: " << com::LogHr(hr) << ".";
344 i : return false;
345 : }
346 :
347 E : ScopedComPtr<IDiaLineNumber> line;
348 E : ULONG fetched = 0;
349 E : hr = enum_lines->Next(1, line.Receive(), &fetched);
350 E : if (FAILED(hr)) {
351 i : LOG(ERROR) << "Failure in IDialineNumber::Next: "
352 : << com::LogHr(hr) << ".";
353 i : return false;
354 : }
355 E : if (fetched == 1) {
356 E : hr = line->get_lineNumber(&line_number);
357 E : if (FAILED(hr)) {
358 i : LOG(ERROR) << "Failure in get_lineNumber: " << com::LogHr(hr) << ".";
359 i : return false;
360 : }
361 E : ScopedComPtr<IDiaSourceFile> source_file;
362 E : hr = line->get_sourceFile(source_file.Receive());
363 E : if (FAILED(hr)) {
364 i : LOG(ERROR) << "Failure in get_sourceFile: " << com::LogHr(hr) << ".";
365 i : return false;
366 : }
367 E : hr = source_file->get_fileName(file_name_bstr.Receive());
368 E : if (FAILED(hr)) {
369 i : LOG(ERROR) << "Failure in get_fileName: " << com::LogHr(hr) << ".";
370 i : return false;
371 : }
372 E : }
373 E : }
374 :
375 E : *file_name = com::ToString(file_name_bstr);
376 E : *line = line_number;
377 E : return true;
378 E : }
379 :
380 E : bool ProfileGrinder::ResolveCallers() {
381 E : PartDataMap::iterator it = parts_.begin();
382 E : for (; it != parts_.end(); ++it) {
383 E : if (!ResolveCallersForPart(&it->second))
384 i : return false;
385 E : }
386 :
387 E : return true;
388 E : }
389 :
390 E : bool ProfileGrinder::ResolveCallersForPart(PartData* part) {
391 : // We start by iterating all the edges, connecting them up to their caller,
392 : // and subtracting the edge metric(s) to compute the inclusive metrics for
393 : // each function.
394 E : InvocationEdgeMap::iterator edge_it(part->edges_.begin());
395 E : for (; edge_it != part->edges_.end(); ++edge_it) {
396 E : InvocationEdge& edge = edge_it->second;
397 E : RVA function_rva = 0;
398 E : if (GetInfoForCallerRVA(edge.caller, &function_rva, &edge.line)) {
399 E : ModuleRVA node_key;
400 E : node_key.module = edge.caller.module;
401 E : node_key.rva = function_rva;
402 E : InvocationNodeMap::iterator node_it(part->nodes_.find(node_key));
403 E : if (node_it == part->nodes_.end()) {
404 : // This is a fringe node - e.g. this is a non-instrumented caller
405 : // calling into an instrumented function. Create the node now,
406 : // but note that we won't have any metrics recorded for the function
407 : // and must be careful not to try and tally exclusive stats for it.
408 : node_it = part->nodes_.insert(
409 i : std::make_pair(node_key, InvocationNode())).first;
410 :
411 i : node_it->second.function = node_key;
412 i : DCHECK_EQ(0, node_it->second.metrics.num_calls);
413 i : DCHECK_EQ(0, node_it->second.metrics.cycles_sum);
414 : }
415 :
416 E : InvocationNode& node = node_it->second;
417 :
418 : // Hook the edge up to the node's list of outgoing edges.
419 E : edge.next_call = node.first_call;
420 E : node.first_call = &edge;
421 :
422 : // Make the function's cycle count exclusive, by subtracting all
423 : // the outbound (inclusive) cycle counts from the total. We make
424 : // special allowance for the "fringe" nodes mentioned above, by
425 : // noting they have no recorded calls.
426 E : if (node.metrics.num_calls != 0) {
427 E : node.metrics.cycles_sum -= edge.metrics.cycles_sum;
428 : }
429 E : } else {
430 : // TODO(siggi): The profile instrumentation currently doesn't record
431 : // sufficient module information that we can resolve calls from
432 : // system and dependent modules.
433 E : LOG(WARNING) << "Found no info for module: '"
434 : << edge.caller.module->image_file_name << "'.";
435 : }
436 E : }
437 :
438 E : return true;
439 E : }
440 :
441 E : bool ProfileGrinder::OutputData(FILE* file) {
442 : // Output the file header.
443 :
444 E : bool succeeded = true;
445 E : PartDataMap::iterator it = parts_.begin();
446 E : for (; it != parts_.end(); ++it) {
447 E : if (!OutputDataForPart(it->second, file)) {
448 : // Keep going despite problems in output
449 i : succeeded = false;
450 : }
451 E : }
452 :
453 E : return succeeded;
454 E : }
455 :
456 E : bool ProfileGrinder::OutputDataForPart(const PartData& part, FILE* file) {
457 : // TODO(siggi): Output command line here.
458 E : ::fprintf(file, "pid: %d\n", part.process_id_);
459 E : if (part.thread_id_ != 0)
460 i : ::fprintf(file, "thread: %d\n", part.thread_id_);
461 E : ::fprintf(file, "events: Calls Cycles Cycles-Min Cycles-Max\n");
462 :
463 E : if (!part.thread_name_.empty())
464 i : ::fprintf(file, "desc: Trigger: %s\n", part.thread_name_.c_str());
465 :
466 : // Walk the nodes and output the data.
467 E : InvocationNodeMap::const_iterator node_it(part.nodes_.begin());
468 E : for (; node_it != part.nodes_.end(); ++node_it) {
469 E : const InvocationNode& node = node_it->second;
470 E : std::wstring function_name;
471 E : std::wstring file_name;
472 E : size_t line = 0;
473 : if (GetInfoForFunctionRVA(node.function,
474 : &function_name,
475 : &file_name,
476 E : &line)) {
477 : // Output the function information.
478 E : ::fprintf(file, "fl=%ws\n", file_name.c_str());
479 E : ::fprintf(file, "fn=%ws\n", function_name.c_str());
480 : ::fprintf(file, "%d %I64d %I64d %I64d\n", line,
481 : node.metrics.num_calls, node.metrics.cycles_sum,
482 E : node.metrics.cycles_min, node.metrics.cycles_max);
483 :
484 : // Output the call information from this function.
485 E : const InvocationEdge* call = node.first_call;
486 E : for (; call != NULL; call = call->next_call) {
487 : if (GetInfoForFunctionRVA(call->function,
488 : &function_name,
489 : &file_name,
490 E : &line)) {
491 E : ::fprintf(file, "cfl=%ws\n", file_name.c_str());
492 E : ::fprintf(file, "cfn=%ws\n", function_name.c_str());
493 E : ::fprintf(file, "calls=%d %d\n", call->metrics.num_calls, line);
494 : ::fprintf(file, "%d %I64d %I64d %I64d\n", call->line,
495 : call->metrics.num_calls, call->metrics.cycles_sum,
496 E : call->metrics.cycles_min, call->metrics.cycles_max);
497 E : }
498 E : }
499 : } else {
500 i : LOG(ERROR) << "Unable to resolve function.";
501 i : return false;
502 : }
503 E : }
504 :
505 E : return true;
506 E : }
507 :
508 : void ProfileGrinder::OnInvocationBatch(base::Time time,
509 : DWORD process_id,
510 : DWORD thread_id,
511 : size_t num_invocations,
512 E : const TraceBatchInvocationInfo* data) {
513 E : PartData* part = FindOrCreatePart(process_id, thread_id);
514 E : DCHECK(data != NULL);
515 :
516 : // Process and aggregate the individual invocation entries.
517 E : for (size_t i = 0; i < num_invocations; ++i) {
518 E : const InvocationInfo& info = data->invocations[i];
519 E : if (info.caller == NULL || info.function == NULL) {
520 : // This may happen due to a termination race when the traces are captured.
521 i : LOG(WARNING) << "Empty invocation record. Record " << i << " of " <<
522 : num_invocations << ".";
523 i : break;
524 : }
525 :
526 : AbsoluteAddress64 function =
527 E : reinterpret_cast<AbsoluteAddress64>(info.function);
528 :
529 E : ModuleRVA function_rva;
530 E : ConvertToModuleRVA(process_id, function, &function_rva);
531 :
532 : // We should always have module information for functions.
533 E : DCHECK(function_rva.module != NULL);
534 :
535 : AbsoluteAddress64 caller =
536 E : reinterpret_cast<AbsoluteAddress64>(info.caller);
537 E : ModuleRVA caller_rva;
538 E : ConvertToModuleRVA(process_id, caller, &caller_rva);
539 :
540 E : AggregateEntryToPart(function_rva, caller_rva, info, part);
541 : }
542 E : }
543 :
544 : void ProfileGrinder::OnThreadName(base::Time time,
545 : DWORD process_id,
546 : DWORD thread_id,
547 i : const base::StringPiece& thread_name) {
548 i : if (!thread_parts_)
549 i : return;
550 :
551 i : PartData* part = FindOrCreatePart(process_id, thread_id);
552 i : part->thread_name_ = thread_name.as_string();
553 i : }
554 :
555 : void ProfileGrinder::AggregateEntryToPart(const ModuleRVA& function_rva,
556 : const ModuleRVA& caller_rva,
557 : const InvocationInfo& info,
558 E : PartData* part) {
559 : // Have we recorded this node before?
560 E : InvocationNodeMap::iterator node_it(part->nodes_.find(function_rva));
561 E : if (node_it != part->nodes_.end()) {
562 : // Yups, we've seen this edge before.
563 : // Aggregate the new data with the old.
564 E : InvocationNode& found = node_it->second;
565 E : found.metrics.num_calls += info.num_calls;
566 : found.metrics.cycles_min = std::min(found.metrics.cycles_min,
567 E : info.cycles_min);
568 : found.metrics.cycles_max = std::max(found.metrics.cycles_max,
569 E : info.cycles_max);
570 E : found.metrics.cycles_sum += info.cycles_sum;
571 E : } else {
572 : // Nopes, we haven't seen this pair before, insert it.
573 E : InvocationNode& node = part->nodes_[function_rva];
574 E : node.function = function_rva;
575 E : node.metrics.num_calls = info.num_calls;
576 E : node.metrics.cycles_min = info.cycles_min;
577 E : node.metrics.cycles_max = info.cycles_max;
578 E : node.metrics.cycles_sum = info.cycles_sum;
579 : }
580 :
581 : // If the caller is NULL, we can't do anything with the edge as the
582 : // caller is unknown, so skip recording it. The data will be aggregated
583 : // to the edge above.
584 E : if (caller_rva.module != NULL) {
585 E : InvocationEdgeKey key(function_rva, caller_rva);
586 :
587 : // Have we recorded this edge before?
588 E : InvocationEdgeMap::iterator edge_it(part->edges_.find(key));
589 E : if (edge_it != part->edges_.end()) {
590 : // Yups, we've seen this edge before.
591 : // Aggregate the new data with the old.
592 i : InvocationEdge& found = edge_it->second;
593 i : found.metrics.num_calls += info.num_calls;
594 : found.metrics.cycles_min = std::min(found.metrics.cycles_min,
595 i : info.cycles_min);
596 : found.metrics.cycles_max = std::max(found.metrics.cycles_max,
597 i : info.cycles_max);
598 i : found.metrics.cycles_sum += info.cycles_sum;
599 i : } else {
600 : // Nopes, we haven't seen this edge before, insert it.
601 E : InvocationEdge& edge = part->edges_[key];
602 E : edge.function = function_rva;
603 E : edge.caller = caller_rva;
604 E : edge.metrics.num_calls = info.num_calls;
605 E : edge.metrics.cycles_min = info.cycles_min;
606 E : edge.metrics.cycles_max = info.cycles_max;
607 E : edge.metrics.cycles_sum = info.cycles_sum;
608 : }
609 E : }
610 E : }
611 :
612 : void ProfileGrinder::ConvertToModuleRVA(uint32 process_id,
613 : AbsoluteAddress64 addr,
614 E : ModuleRVA* rva) {
615 E : DCHECK(rva != NULL);
616 :
617 : const ModuleInformation* module =
618 E : parser_->GetModuleInformation(process_id, addr);
619 :
620 E : if (module == NULL) {
621 : // We have no module information for this address.
622 i : rva->module = NULL;
623 i : rva->rva = 0;
624 i : return;
625 : }
626 :
627 : // Convert the address to an RVA.
628 E : rva->rva = static_cast<RVA>(addr - module->base_address);
629 :
630 : // And find or record the canonical module information
631 : // for this module.
632 E : ModuleInformationSet::iterator it(modules_.find(*module));
633 E : if (it == modules_.end()) {
634 E : it = modules_.insert(*module).first;
635 : }
636 E : DCHECK(it != modules_.end());
637 :
638 E : rva->module = &(*it);
639 E : }
640 :
641 : } // namespace grinder
|