Coverage for /Syzygy/grinder/profile_grinder.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
77.4%2773580.C++source

Line-by-line coverage:

   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

Coverage information generated Tue Jun 25 13:56:24 2013.