Coverage for /Syzygy/grinder/profile_grinder.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
77.5%2793600.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  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

Coverage information generated Thu Mar 14 11:53:36 2013.