profile-generator.cc 20.4 KB
Newer Older
1
// Copyright 2012 the V8 project authors. All rights reserved.
2 3
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
4

5
#include "src/v8.h"
6

7
#include "src/profile-generator-inl.h"
8

9 10
#include "src/compiler.h"
#include "src/debug.h"
11
#include "src/deoptimizer.h"
12
#include "src/global-handles.h"
13
#include "src/sampler.h"
14 15
#include "src/scopeinfo.h"
#include "src/unicode.h"
16 17 18 19

namespace v8 {
namespace internal {

20

21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
JITLineInfoTable::JITLineInfoTable() {}


JITLineInfoTable::~JITLineInfoTable() {}


void JITLineInfoTable::SetPosition(int pc_offset, int line) {
  DCHECK(pc_offset >= 0);
  DCHECK(line > 0);  // The 1-based number of the source line.
  if (GetSourceLineNumber(pc_offset) != line) {
    pc_offset_map_.insert(std::make_pair(pc_offset, line));
  }
}


int JITLineInfoTable::GetSourceLineNumber(int pc_offset) const {
  PcOffsetMap::const_iterator it = pc_offset_map_.lower_bound(pc_offset);
  if (it == pc_offset_map_.end()) {
    if (pc_offset_map_.empty()) return v8::CpuProfileNode::kNoLineNumberInfo;
    return (--pc_offset_map_.end())->second;
  }
  return it->second;
}


46
const char* const CodeEntry::kEmptyNamePrefix = "";
47
const char* const CodeEntry::kEmptyResourceName = "";
48
const char* const CodeEntry::kEmptyBailoutReason = "";
49
const char* const CodeEntry::kNoDeoptReason = "";
50 51


52 53
CodeEntry::~CodeEntry() {
  delete no_frame_ranges_;
54
  delete line_info_;
55 56 57
}


58
uint32_t CodeEntry::GetHash() const {
59
  uint32_t hash = ComputeIntegerHash(tag(), v8::internal::kZeroHashSeed);
60 61 62 63
  if (script_id_ != v8::UnboundScript::kNoScriptId) {
    hash ^= ComputeIntegerHash(static_cast<uint32_t>(script_id_),
                               v8::internal::kZeroHashSeed);
    hash ^= ComputeIntegerHash(static_cast<uint32_t>(position_),
64
                               v8::internal::kZeroHashSeed);
65 66
  } else {
    hash ^= ComputeIntegerHash(
67 68
        static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name_prefix_)),
        v8::internal::kZeroHashSeed);
69
    hash ^= ComputeIntegerHash(
70 71
        static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name_)),
        v8::internal::kZeroHashSeed);
72
    hash ^= ComputeIntegerHash(
73 74 75
        static_cast<uint32_t>(reinterpret_cast<uintptr_t>(resource_name_)),
        v8::internal::kZeroHashSeed);
    hash ^= ComputeIntegerHash(line_number_, v8::internal::kZeroHashSeed);
76
  }
77 78 79 80
  return hash;
}


81 82 83 84 85 86 87 88
bool CodeEntry::IsSameFunctionAs(CodeEntry* entry) const {
  if (this == entry) return true;
  if (script_id_ != v8::UnboundScript::kNoScriptId) {
    return script_id_ == entry->script_id_ && position_ == entry->position_;
  }
  return name_prefix_ == entry->name_prefix_ && name_ == entry->name_ &&
         resource_name_ == entry->resource_name_ &&
         line_number_ == entry->line_number_;
89 90 91
}


92
void CodeEntry::SetBuiltinId(Builtins::Name id) {
93 94
  bit_field_ = TagField::update(bit_field_, Logger::BUILTIN_TAG);
  bit_field_ = BuiltinIdField::update(bit_field_, id);
95 96 97
}


98 99 100 101 102 103 104 105
int CodeEntry::GetSourceLine(int pc_offset) const {
  if (line_info_ && !line_info_->empty()) {
    return line_info_->GetSourceLineNumber(pc_offset);
  }
  return v8::CpuProfileNode::kNoLineNumberInfo;
}


106 107 108 109 110 111 112 113 114
void CodeEntry::FillFunctionInfo(SharedFunctionInfo* shared) {
  if (!shared->script()->IsScript()) return;
  Script* script = Script::cast(shared->script());
  set_script_id(script->id()->value());
  set_position(shared->start_position());
  set_bailout_reason(GetBailoutReason(shared->disable_optimization_reason()));
}


115
void ProfileNode::CollectDeoptInfo(CodeEntry* entry) {
116
  deopt_infos_.Add(DeoptInfo(entry->deopt_reason(), entry->deopt_position()));
117 118 119 120
  entry->clear_deopt_info();
}


121 122 123 124 125 126 127 128 129 130 131
ProfileNode* ProfileNode::FindChild(CodeEntry* entry) {
  HashMap::Entry* map_entry =
      children_.Lookup(entry, CodeEntryHash(entry), false);
  return map_entry != NULL ?
      reinterpret_cast<ProfileNode*>(map_entry->value) : NULL;
}


ProfileNode* ProfileNode::FindOrAddChild(CodeEntry* entry) {
  HashMap::Entry* map_entry =
      children_.Lookup(entry, CodeEntryHash(entry), true);
132 133
  ProfileNode* node = reinterpret_cast<ProfileNode*>(map_entry->value);
  if (node == NULL) {
134
    // New node added.
135 136 137
    node = new ProfileNode(tree_, entry);
    map_entry->value = node;
    children_list_.Add(node);
138
  }
139
  return node;
140 141 142
}


143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
void ProfileNode::IncrementLineTicks(int src_line) {
  if (src_line == v8::CpuProfileNode::kNoLineNumberInfo) return;
  // Increment a hit counter of a certain source line.
  // Add a new source line if not found.
  HashMap::Entry* e =
      line_ticks_.Lookup(reinterpret_cast<void*>(src_line), src_line, true);
  DCHECK(e);
  e->value = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(e->value) + 1);
}


bool ProfileNode::GetLineTicks(v8::CpuProfileNode::LineTick* entries,
                               unsigned int length) const {
  if (entries == NULL || length == 0) return false;

  unsigned line_count = line_ticks_.occupancy();

  if (line_count == 0) return true;
  if (length < line_count) return false;

  v8::CpuProfileNode::LineTick* entry = entries;

  for (HashMap::Entry* p = line_ticks_.Start(); p != NULL;
       p = line_ticks_.Next(p), entry++) {
    entry->line =
        static_cast<unsigned int>(reinterpret_cast<uintptr_t>(p->key));
    entry->hit_count =
        static_cast<unsigned int>(reinterpret_cast<uintptr_t>(p->value));
  }

  return true;
}


177
void ProfileNode::Print(int indent) {
178
  base::OS::Print("%5u %*s %s%s %d #%d", self_ticks_, indent, "",
179
                  entry_->name_prefix(), entry_->name(), entry_->script_id(),
180
                  id());
181
  if (entry_->resource_name()[0] != '\0')
182 183
    base::OS::Print(" %s:%d", entry_->resource_name(), entry_->line_number());
  base::OS::Print("\n");
184
  for (auto info : deopt_infos_) {
185 186 187 188 189 190 191 192
    if (FLAG_hydrogen_track_positions) {
      base::OS::Print("%*s deopted at %d_%d with reason '%s'\n", indent + 10,
                      "", info.deopt_position.inlining_id(),
                      info.deopt_position.position(), info.deopt_reason);
    } else {
      base::OS::Print("%*s deopted at %d with reason '%s'\n", indent + 10, "",
                      info.deopt_position.raw(), info.deopt_reason);
    }
193 194 195 196 197 198 199
  }
  const char* bailout_reason = entry_->bailout_reason();
  if (bailout_reason != GetBailoutReason(BailoutReason::kNoReason) &&
      bailout_reason != CodeEntry::kEmptyBailoutReason) {
    base::OS::Print("%*s bailed out due to '%s'\n", indent + 10, "",
                    bailout_reason);
  }
200 201 202 203 204 205 206 207 208 209
  for (HashMap::Entry* p = children_.Start();
       p != NULL;
       p = children_.Next(p)) {
    reinterpret_cast<ProfileNode*>(p->value)->Print(indent + 2);
  }
}


class DeleteNodesCallback {
 public:
210 211
  void BeforeTraversingChild(ProfileNode*, ProfileNode*) { }

212 213 214 215 216 217 218 219
  void AfterAllChildrenTraversed(ProfileNode* node) {
    delete node;
  }

  void AfterChildTraversed(ProfileNode*, ProfileNode*) { }
};


220
ProfileTree::ProfileTree()
221
    : root_entry_(Logger::FUNCTION_TAG, "(root)"),
222
      next_node_id_(1),
223 224 225
      root_(new ProfileNode(this, &root_entry_)),
      next_function_id_(1),
      function_ids_(ProfileNode::CodeEntriesMatch) {}
226 227


228 229
ProfileTree::~ProfileTree() {
  DeleteNodesCallback cb;
230
  TraverseDepthFirst(&cb);
231 232 233
}


234 235 236 237 238 239 240 241 242 243 244
unsigned ProfileTree::GetFunctionId(const ProfileNode* node) {
  CodeEntry* code_entry = node->entry();
  HashMap::Entry* entry =
      function_ids_.Lookup(code_entry, code_entry->GetHash(), true);
  if (!entry->value) {
    entry->value = reinterpret_cast<void*>(next_function_id_++);
  }
  return static_cast<unsigned>(reinterpret_cast<uintptr_t>(entry->value));
}


245 246
ProfileNode* ProfileTree::AddPathFromEnd(const Vector<CodeEntry*>& path,
                                         int src_line) {
247
  ProfileNode* node = root_;
248
  CodeEntry* last_entry = NULL;
249 250 251 252 253
  for (CodeEntry** entry = path.start() + path.length() - 1;
       entry != path.start() - 1;
       --entry) {
    if (*entry != NULL) {
      node = node->FindOrAddChild(*entry);
254
      last_entry = *entry;
255 256
    }
  }
257 258 259
  if (last_entry && last_entry->has_deopt_info()) {
    node->CollectDeoptInfo(last_entry);
  }
260
  node->IncrementSelfTicks();
261 262 263
  if (src_line != v8::CpuProfileNode::kNoLineNumberInfo) {
    node->IncrementLineTicks(src_line);
  }
264
  return node;
265 266 267
}


268 269 270 271 272 273 274 275
struct NodesPair {
  NodesPair(ProfileNode* src, ProfileNode* dst)
      : src(src), dst(dst) { }
  ProfileNode* src;
  ProfileNode* dst;
};


276 277 278 279
class Position {
 public:
  explicit Position(ProfileNode* node)
      : node(node), child_idx_(0) { }
280
  INLINE(ProfileNode* current_child()) {
281
    return node->children()->at(child_idx_);
282
  }
283 284 285 286 287
  INLINE(bool has_current_child()) {
    return child_idx_ < node->children()->length();
  }
  INLINE(void next_child()) { ++child_idx_; }

288
  ProfileNode* node;
289 290
 private:
  int child_idx_;
291 292 293
};


294
// Non-recursive implementation of a depth-first post-order tree traversal.
295
template <typename Callback>
296
void ProfileTree::TraverseDepthFirst(Callback* callback) {
297
  List<Position> stack(10);
298
  stack.Add(Position(root_));
299
  while (stack.length() > 0) {
300
    Position& current = stack.last();
301
    if (current.has_current_child()) {
302
      callback->BeforeTraversingChild(current.node, current.current_child());
303
      stack.Add(Position(current.current_child()));
304 305 306 307 308
    } else {
      callback->AfterAllChildrenTraversed(current.node);
      if (stack.length() > 1) {
        Position& parent = stack[stack.length() - 2];
        callback->AfterChildTraversed(parent.node, current.node);
309
        parent.next_child();
310
      }
311 312
      // Remove child from the stack.
      stack.RemoveLast();
313
    }
314
  }
315 316 317
}


318
CpuProfile::CpuProfile(const char* title, bool record_samples)
319 320
    : title_(title),
      record_samples_(record_samples),
321
      start_time_(base::TimeTicks::HighResolutionNow()) {
322 323 324
}


325
void CpuProfile::AddPath(base::TimeTicks timestamp,
326 327
                         const Vector<CodeEntry*>& path, int src_line) {
  ProfileNode* top_frame_node = top_down_.AddPathFromEnd(path, src_line);
328 329 330 331
  if (record_samples_) {
    timestamps_.Add(timestamp);
    samples_.Add(top_frame_node);
  }
332 333 334
}


335
void CpuProfile::CalculateTotalTicksAndSamplingRate() {
336
  end_time_ = base::TimeTicks::HighResolutionNow();
337 338 339 340
}


void CpuProfile::Print() {
341
  base::OS::Print("[Top down]:\n");
342 343 344 345 346 347 348
  top_down_.Print();
}


const CodeMap::CodeTreeConfig::Key CodeMap::CodeTreeConfig::kNoKey = NULL;


349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
void CodeMap::AddCode(Address addr, CodeEntry* entry, unsigned size) {
  DeleteAllCoveredCode(addr, addr + size);
  CodeTree::Locator locator;
  tree_.Insert(addr, &locator);
  locator.set_value(CodeEntryInfo(entry, size));
}


void CodeMap::DeleteAllCoveredCode(Address start, Address end) {
  List<Address> to_delete;
  Address addr = end - 1;
  while (addr >= start) {
    CodeTree::Locator locator;
    if (!tree_.FindGreatestLessThan(addr, &locator)) break;
    Address start2 = locator.key(), end2 = start2 + locator.value().size;
    if (start2 < end && start < end2) to_delete.Add(start2);
    addr = start2 - 1;
  }
  for (int i = 0; i < to_delete.length(); ++i) tree_.Remove(to_delete[i]);
}


371
CodeEntry* CodeMap::FindEntry(Address addr, Address* start) {
372 373 374 375
  CodeTree::Locator locator;
  if (tree_.FindGreatestLessThan(addr, &locator)) {
    // locator.key() <= addr. Need to check that addr is within entry.
    const CodeEntryInfo& entry = locator.value();
376 377 378 379
    if (addr < (locator.key() + entry.size)) {
      if (start) {
        *start = locator.key();
      }
380
      return entry.entry;
381
    }
382 383 384 385 386
  }
  return NULL;
}


387 388 389 390 391 392 393 394 395 396
void CodeMap::MoveCode(Address from, Address to) {
  if (from == to) return;
  CodeTree::Locator locator;
  if (!tree_.Find(from, &locator)) return;
  CodeEntryInfo entry = locator.value();
  tree_.Remove(from);
  AddCode(to, entry.entry, entry.size);
}


397 398
void CodeMap::CodeTreePrinter::Call(
    const Address& key, const CodeMap::CodeEntryInfo& value) {
399
  base::OS::Print("%p %5d %s\n", key, value.size, value.entry->name());
400 401 402 403 404 405 406 407 408
}


void CodeMap::Print() {
  CodeTreePrinter printer;
  tree_.ForEach(&printer);
}


409 410 411
CpuProfilesCollection::CpuProfilesCollection(Heap* heap)
    : function_and_resource_names_(heap),
      current_profiles_semaphore_(1) {
412 413 414
}


415
static void DeleteCodeEntry(CodeEntry** entry_ptr) {
416 417 418
  delete *entry_ptr;
}

419

420 421 422 423
static void DeleteCpuProfile(CpuProfile** profile_ptr) {
  delete *profile_ptr;
}

424

425
CpuProfilesCollection::~CpuProfilesCollection() {
426
  finished_profiles_.Iterate(DeleteCpuProfile);
427
  current_profiles_.Iterate(DeleteCpuProfile);
428 429
  code_entries_.Iterate(DeleteCodeEntry);
}
430

431

432
bool CpuProfilesCollection::StartProfiling(const char* title,
433
                                           bool record_samples) {
434
  current_profiles_semaphore_.Wait();
435
  if (current_profiles_.length() >= kMaxSimultaneousProfiles) {
436
    current_profiles_semaphore_.Signal();
437 438
    return false;
  }
439 440
  for (int i = 0; i < current_profiles_.length(); ++i) {
    if (strcmp(current_profiles_[i]->title(), title) == 0) {
441
      // Ignore attempts to start profile with the same title...
442
      current_profiles_semaphore_.Signal();
443 444
      // ... though return true to force it collect a sample.
      return true;
445 446
    }
  }
447
  current_profiles_.Add(new CpuProfile(title, record_samples));
448
  current_profiles_semaphore_.Signal();
449 450 451 452
  return true;
}


453
CpuProfile* CpuProfilesCollection::StopProfiling(const char* title) {
454
  const int title_len = StrLength(title);
455
  CpuProfile* profile = NULL;
456
  current_profiles_semaphore_.Wait();
457 458 459 460 461 462
  for (int i = current_profiles_.length() - 1; i >= 0; --i) {
    if (title_len == 0 || strcmp(current_profiles_[i]->title(), title) == 0) {
      profile = current_profiles_.Remove(i);
      break;
    }
  }
463
  current_profiles_semaphore_.Signal();
464

465
  if (profile == NULL) return NULL;
466
  profile->CalculateTotalTicksAndSamplingRate();
467 468
  finished_profiles_.Add(profile);
  return profile;
469 470 471
}


472 473 474 475 476 477 478 479 480
bool CpuProfilesCollection::IsLastProfile(const char* title) {
  // Called from VM thread, and only it can mutate the list,
  // so no locking is needed here.
  if (current_profiles_.length() != 1) return false;
  return StrLength(title) == 0
      || strcmp(current_profiles_[0]->title(), title) == 0;
}


481 482
void CpuProfilesCollection::RemoveProfile(CpuProfile* profile) {
  // Called from VM thread for a completed profile.
483
  for (int i = 0; i < finished_profiles_.length(); i++) {
484
    if (profile == finished_profiles_[i]) {
485 486
      finished_profiles_.Remove(i);
      return;
487 488
    }
  }
489
  UNREACHABLE();
490 491 492
}


493
void CpuProfilesCollection::AddPathToCurrentProfiles(
494
    base::TimeTicks timestamp, const Vector<CodeEntry*>& path, int src_line) {
495 496 497
  // As starting / stopping profiles is rare relatively to this
  // method, we don't bother minimizing the duration of lock holding,
  // e.g. copying contents of the list to a local vector.
498
  current_profiles_semaphore_.Wait();
499
  for (int i = 0; i < current_profiles_.length(); ++i) {
500
    current_profiles_[i]->AddPath(timestamp, path, src_line);
501
  }
502
  current_profiles_semaphore_.Signal();
503 504 505
}


506
CodeEntry* CpuProfilesCollection::NewCodeEntry(
507 508 509 510 511 512
    Logger::LogEventsAndTags tag, const char* name, const char* name_prefix,
    const char* resource_name, int line_number, int column_number,
    JITLineInfoTable* line_info, Address instruction_start) {
  CodeEntry* code_entry =
      new CodeEntry(tag, name, name_prefix, resource_name, line_number,
                    column_number, line_info, instruction_start);
513 514 515 516 517
  code_entries_.Add(code_entry);
  return code_entry;
}


518 519
const char* const ProfileGenerator::kProgramEntryName =
    "(program)";
520 521
const char* const ProfileGenerator::kIdleEntryName =
    "(idle)";
522 523
const char* const ProfileGenerator::kGarbageCollectorEntryName =
    "(garbage collector)";
524 525
const char* const ProfileGenerator::kUnresolvedFunctionName =
    "(unresolved function)";
526

527

528
ProfileGenerator::ProfileGenerator(CpuProfilesCollection* profiles)
529 530
    : profiles_(profiles),
      program_entry_(
531
          profiles->NewCodeEntry(Logger::FUNCTION_TAG, kProgramEntryName)),
532 533
      idle_entry_(
          profiles->NewCodeEntry(Logger::FUNCTION_TAG, kIdleEntryName)),
534 535
      gc_entry_(
          profiles->NewCodeEntry(Logger::BUILTIN_TAG,
536 537 538 539
                                 kGarbageCollectorEntryName)),
      unresolved_entry_(
          profiles->NewCodeEntry(Logger::FUNCTION_TAG,
                                 kUnresolvedFunctionName)) {
540 541
}

542 543

void ProfileGenerator::RecordTickSample(const TickSample& sample) {
544
  // Allocate space for stack frames + pc + function + vm-state.
545
  ScopedVector<CodeEntry*> entries(sample.frames_count + 3);
546 547
  // As actual number of decoded code entries may vary, initialize
  // entries vector with NULL values.
548
  CodeEntry** entry = entries.start();
549
  memset(entry, 0, entries.length() * sizeof(*entry));
550 551 552 553 554 555 556 557 558

  // The ProfileNode knows nothing about all versions of generated code for
  // the same JS function. The line number information associated with
  // the latest version of generated code is used to find a source line number
  // for a JS function. Then, the detected source line is passed to
  // ProfileNode to increase the tick count for this source line.
  int src_line = v8::CpuProfileNode::kNoLineNumberInfo;
  bool src_line_not_found = true;

559
  if (sample.pc != NULL) {
560 561
    if (sample.has_external_callback && sample.state == EXTERNAL &&
        sample.top_frame_type == StackFrame::EXIT) {
562 563 564 565
      // Don't use PC when in external callback code, as it can point
      // inside callback's code, and we will erroneously report
      // that a callback calls itself.
      *entry++ = code_map_.FindEntry(sample.external_callback);
566 567 568 569 570 571 572 573 574
    } else {
      Address start;
      CodeEntry* pc_entry = code_map_.FindEntry(sample.pc, &start);
      // If pc is in the function code before it set up stack frame or after the
      // frame was destroyed SafeStackFrameIterator incorrectly thinks that
      // ebp contains return address of the current function and skips caller's
      // frame. Check for this case and just skip such samples.
      if (pc_entry) {
        List<OffsetRange>* ranges = pc_entry->no_frame_ranges();
575 576
        int pc_offset =
            static_cast<int>(sample.pc - pc_entry->instruction_start());
577 578 579 580 581 582 583 584
        if (ranges) {
          for (int i = 0; i < ranges->length(); i++) {
            OffsetRange& range = ranges->at(i);
            if (range.from <= pc_offset && pc_offset < range.to) {
              return;
            }
          }
        }
585 586 587 588 589
        src_line = pc_entry->GetSourceLine(pc_offset);
        if (src_line == v8::CpuProfileNode::kNoLineNumberInfo) {
          src_line = pc_entry->line_number();
        }
        src_line_not_found = false;
590 591
        *entry++ = pc_entry;

592 593 594 595 596 597 598 599 600 601
        if (pc_entry->builtin_id() == Builtins::kFunctionCall ||
            pc_entry->builtin_id() == Builtins::kFunctionApply) {
          // When current function is FunctionCall or FunctionApply builtin the
          // top frame is either frame of the calling JS function or internal
          // frame. In the latter case we know the caller for sure but in the
          // former case we don't so we simply replace the frame with
          // 'unresolved' entry.
          if (sample.top_frame_type == StackFrame::JAVA_SCRIPT) {
            *entry++ = unresolved_entry_;
          }
602 603
        }
      }
604 605
    }

606
    for (const Address* stack_pos = sample.stack,
607 608 609
           *stack_end = stack_pos + sample.frames_count;
         stack_pos != stack_end;
         ++stack_pos) {
610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
      Address start = NULL;
      *entry = code_map_.FindEntry(*stack_pos, &start);

      // Skip unresolved frames (e.g. internal frame) and get source line of
      // the first JS caller.
      if (src_line_not_found && *entry) {
        int pc_offset =
            static_cast<int>(*stack_pos - (*entry)->instruction_start());
        src_line = (*entry)->GetSourceLine(pc_offset);
        if (src_line == v8::CpuProfileNode::kNoLineNumberInfo) {
          src_line = (*entry)->line_number();
        }
        src_line_not_found = false;
      }

      entry++;
626
    }
627 628
  }

629
  if (FLAG_prof_browser_mode) {
630 631 632 633 634 635 636 637 638 639 640
    bool no_symbolized_entries = true;
    for (CodeEntry** e = entries.start(); e != entry; ++e) {
      if (*e != NULL) {
        no_symbolized_entries = false;
        break;
      }
    }
    // If no frames were symbolized, put the VM state entry in.
    if (no_symbolized_entries) {
      *entry++ = EntryForVMState(sample.state);
    }
641
  }
642

643
  profiles_->AddPathToCurrentProfiles(sample.timestamp, entries, src_line);
644 645
}

646

647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664
CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) {
  switch (tag) {
    case GC:
      return gc_entry_;
    case JS:
    case COMPILER:
    // DOM events handlers are reported as OTHER / EXTERNAL entries.
    // To avoid confusing people, let's put all these entries into
    // one bucket.
    case OTHER:
    case EXTERNAL:
      return program_entry_;
    case IDLE:
      return idle_entry_;
    default: return NULL;
  }
}

665
} }  // namespace v8::internal