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