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