profile-generator.cc 25.6 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/profiler/profile-generator.h"
6

7
#include "src/base/adapters.h"
8
#include "src/debug/debug.h"
9
#include "src/deoptimizer.h"
10
#include "src/global-handles.h"
11
#include "src/objects-inl.h"
12
#include "src/profiler/cpu-profiler.h"
13
#include "src/profiler/profile-generator-inl.h"
14 15
#include "src/tracing/trace-event.h"
#include "src/tracing/traced-value.h"
16
#include "src/unicode.h"
17 18 19 20

namespace v8 {
namespace internal {

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 46
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;
}


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

lpy's avatar
lpy committed
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
const char* const CodeEntry::kProgramEntryName = "(program)";
const char* const CodeEntry::kIdleEntryName = "(idle)";
const char* const CodeEntry::kGarbageCollectorEntryName = "(garbage collector)";
const char* const CodeEntry::kUnresolvedFunctionName = "(unresolved function)";

base::LazyDynamicInstance<CodeEntry, CodeEntry::ProgramEntryCreateTrait>::type
    CodeEntry::kProgramEntry = LAZY_DYNAMIC_INSTANCE_INITIALIZER;

base::LazyDynamicInstance<CodeEntry, CodeEntry::IdleEntryCreateTrait>::type
    CodeEntry::kIdleEntry = LAZY_DYNAMIC_INSTANCE_INITIALIZER;

base::LazyDynamicInstance<CodeEntry, CodeEntry::GCEntryCreateTrait>::type
    CodeEntry::kGCEntry = LAZY_DYNAMIC_INSTANCE_INITIALIZER;

base::LazyDynamicInstance<CodeEntry,
                          CodeEntry::UnresolvedEntryCreateTrait>::type
    CodeEntry::kUnresolvedEntry = LAZY_DYNAMIC_INSTANCE_INITIALIZER;

CodeEntry* CodeEntry::ProgramEntryCreateTrait::Create() {
  return new CodeEntry(Logger::FUNCTION_TAG, CodeEntry::kProgramEntryName);
}

CodeEntry* CodeEntry::IdleEntryCreateTrait::Create() {
  return new CodeEntry(Logger::FUNCTION_TAG, CodeEntry::kIdleEntryName);
}

CodeEntry* CodeEntry::GCEntryCreateTrait::Create() {
  return new CodeEntry(Logger::BUILTIN_TAG,
                       CodeEntry::kGarbageCollectorEntryName);
}

CodeEntry* CodeEntry::UnresolvedEntryCreateTrait::Create() {
  return new CodeEntry(Logger::FUNCTION_TAG,
                       CodeEntry::kUnresolvedFunctionName);
}
87

88
CodeEntry::~CodeEntry() {
89
  delete line_info_;
90 91 92 93 94
  for (auto location : inline_locations_) {
    for (auto entry : location.second) {
      delete entry;
    }
  }
95 96 97
}


98
uint32_t CodeEntry::GetHash() const {
99
  uint32_t hash = ComputeIntegerHash(tag(), v8::internal::kZeroHashSeed);
100 101 102 103
  if (script_id_ != v8::UnboundScript::kNoScriptId) {
    hash ^= ComputeIntegerHash(static_cast<uint32_t>(script_id_),
                               v8::internal::kZeroHashSeed);
    hash ^= ComputeIntegerHash(static_cast<uint32_t>(position_),
104
                               v8::internal::kZeroHashSeed);
105 106
  } else {
    hash ^= ComputeIntegerHash(
107 108
        static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name_prefix_)),
        v8::internal::kZeroHashSeed);
109
    hash ^= ComputeIntegerHash(
110 111
        static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name_)),
        v8::internal::kZeroHashSeed);
112
    hash ^= ComputeIntegerHash(
113 114 115
        static_cast<uint32_t>(reinterpret_cast<uintptr_t>(resource_name_)),
        v8::internal::kZeroHashSeed);
    hash ^= ComputeIntegerHash(line_number_, v8::internal::kZeroHashSeed);
116
  }
117 118 119 120
  return hash;
}


121 122 123 124 125 126 127 128
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_;
129 130 131
}


132
void CodeEntry::SetBuiltinId(Builtins::Name id) {
133
  bit_field_ = TagField::update(bit_field_, CodeEventListener::BUILTIN_TAG);
134
  bit_field_ = BuiltinIdField::update(bit_field_, id);
135 136 137
}


138 139 140 141 142 143 144
int CodeEntry::GetSourceLine(int pc_offset) const {
  if (line_info_ && !line_info_->empty()) {
    return line_info_->GetSourceLineNumber(pc_offset);
  }
  return v8::CpuProfileNode::kNoLineNumberInfo;
}

145
void CodeEntry::AddInlineStack(int pc_offset,
146 147
                               std::vector<CodeEntry*> inline_stack) {
  inline_locations_.insert(std::make_pair(pc_offset, std::move(inline_stack)));
148 149 150 151 152 153
}

const std::vector<CodeEntry*>* CodeEntry::GetInlineStack(int pc_offset) const {
  auto it = inline_locations_.find(pc_offset);
  return it != inline_locations_.end() ? &it->second : NULL;
}
154

155
void CodeEntry::AddDeoptInlinedFrames(
156 157 158
    int deopt_id, std::vector<CpuProfileDeoptFrame> inlined_frames) {
  deopt_inlined_frames_.insert(
      std::make_pair(deopt_id, std::move(inlined_frames)));
159 160 161 162 163 164
}

bool CodeEntry::HasDeoptInlinedFramesFor(int deopt_id) const {
  return deopt_inlined_frames_.find(deopt_id) != deopt_inlined_frames_.end();
}

165 166 167
void CodeEntry::FillFunctionInfo(SharedFunctionInfo* shared) {
  if (!shared->script()->IsScript()) return;
  Script* script = Script::cast(shared->script());
168
  set_script_id(script->id());
169 170 171 172
  set_position(shared->start_position());
  set_bailout_reason(GetBailoutReason(shared->disable_optimization_reason()));
}

173
CpuProfileDeoptInfo CodeEntry::GetDeoptInfo() {
174 175
  DCHECK(has_deopt_info());

176
  CpuProfileDeoptInfo info;
177
  info.deopt_reason = deopt_reason_;
178
  DCHECK_NE(kNoDeoptimizationId, deopt_id_);
179
  if (deopt_inlined_frames_.find(deopt_id_) == deopt_inlined_frames_.end()) {
180 181
    info.stack.push_back(CpuProfileDeoptFrame(
        {script_id_, static_cast<size_t>(std::max(0, position()))}));
182
  } else {
183
    info.stack = deopt_inlined_frames_[deopt_id_];
184 185 186 187 188
  }
  return info;
}


189
void ProfileNode::CollectDeoptInfo(CodeEntry* entry) {
190
  deopt_infos_.push_back(entry->GetDeoptInfo());
191 192 193 194
  entry->clear_deopt_info();
}


195
ProfileNode* ProfileNode::FindChild(CodeEntry* entry) {
lpy's avatar
lpy committed
196 197
  base::HashMap::Entry* map_entry =
      children_.Lookup(entry, CodeEntryHash(entry));
198 199 200 201 202 203
  return map_entry != NULL ?
      reinterpret_cast<ProfileNode*>(map_entry->value) : NULL;
}


ProfileNode* ProfileNode::FindOrAddChild(CodeEntry* entry) {
lpy's avatar
lpy committed
204
  base::HashMap::Entry* map_entry =
205
      children_.LookupOrInsert(entry, CodeEntryHash(entry));
206
  ProfileNode* node = reinterpret_cast<ProfileNode*>(map_entry->value);
207 208
  if (!node) {
    node = new ProfileNode(tree_, entry, this);
209 210
    map_entry->value = node;
    children_list_.Add(node);
211
  }
212
  return node;
213 214 215
}


216 217 218 219
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.
lpy's avatar
lpy committed
220
  base::HashMap::Entry* e =
221
      line_ticks_.LookupOrInsert(reinterpret_cast<void*>(src_line), src_line);
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
  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;

lpy's avatar
lpy committed
238
  for (base::HashMap::Entry *p = line_ticks_.Start(); p != NULL;
239 240 241 242 243 244 245 246 247 248 249
       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;
}


250
void ProfileNode::Print(int indent) {
251
  base::OS::Print("%5u %*s %s%s %d #%d", self_ticks_, indent, "",
252
                  entry_->name_prefix(), entry_->name(), entry_->script_id(),
253
                  id());
254
  if (entry_->resource_name()[0] != '\0')
255 256
    base::OS::Print(" %s:%d", entry_->resource_name(), entry_->line_number());
  base::OS::Print("\n");
257
  for (size_t i = 0; i < deopt_infos_.size(); ++i) {
258
    CpuProfileDeoptInfo& info = deopt_infos_[i];
jfb's avatar
jfb committed
259 260 261 262
    base::OS::Print("%*s;;; deopted at script_id: %d position: %" PRIuS
                    " with reason '%s'.\n",
                    indent + 10, "", info.stack[0].script_id,
                    info.stack[0].position, info.deopt_reason);
263
    for (size_t index = 1; index < info.stack.size(); ++index) {
jfb's avatar
jfb committed
264 265
      base::OS::Print("%*s;;;     Inline point: script_id %d position: %" PRIuS
                      ".\n",
266 267
                      indent + 10, "", info.stack[index].script_id,
                      info.stack[index].position);
268
    }
269 270 271 272 273 274 275
  }
  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);
  }
lpy's avatar
lpy committed
276
  for (base::HashMap::Entry* p = children_.Start(); p != NULL;
277 278 279 280 281 282 283 284
       p = children_.Next(p)) {
    reinterpret_cast<ProfileNode*>(p->value)->Print(indent + 2);
  }
}


class DeleteNodesCallback {
 public:
285 286
  void BeforeTraversingChild(ProfileNode*, ProfileNode*) { }

287 288 289 290 291 292 293
  void AfterAllChildrenTraversed(ProfileNode* node) {
    delete node;
  }

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

294
ProfileTree::ProfileTree(Isolate* isolate)
295
    : root_entry_(CodeEventListener::FUNCTION_TAG, "(root)"),
296
      next_node_id_(1),
297
      root_(new ProfileNode(this, &root_entry_, nullptr)),
298
      isolate_(isolate),
299 300
      next_function_id_(1),
      function_ids_(ProfileNode::CodeEntriesMatch) {}
301

302 303
ProfileTree::~ProfileTree() {
  DeleteNodesCallback cb;
304
  TraverseDepthFirst(&cb);
305 306 307
}


308 309
unsigned ProfileTree::GetFunctionId(const ProfileNode* node) {
  CodeEntry* code_entry = node->entry();
lpy's avatar
lpy committed
310
  base::HashMap::Entry* entry =
311
      function_ids_.LookupOrInsert(code_entry, code_entry->GetHash());
312 313 314 315 316 317
  if (!entry->value) {
    entry->value = reinterpret_cast<void*>(next_function_id_++);
  }
  return static_cast<unsigned>(reinterpret_cast<uintptr_t>(entry->value));
}

318
ProfileNode* ProfileTree::AddPathFromEnd(const std::vector<CodeEntry*>& path,
319
                                         int src_line, bool update_stats) {
320
  ProfileNode* node = root_;
321
  CodeEntry* last_entry = NULL;
322 323 324 325
  for (auto it = path.rbegin(); it != path.rend(); ++it) {
    if (*it == NULL) continue;
    last_entry = *it;
    node = node->FindOrAddChild(*it);
326
  }
327 328 329
  if (last_entry && last_entry->has_deopt_info()) {
    node->CollectDeoptInfo(last_entry);
  }
330 331 332 333 334
  if (update_stats) {
    node->IncrementSelfTicks();
    if (src_line != v8::CpuProfileNode::kNoLineNumberInfo) {
      node->IncrementLineTicks(src_line);
    }
335
  }
336
  return node;
337 338 339
}


340 341 342 343 344 345 346 347
struct NodesPair {
  NodesPair(ProfileNode* src, ProfileNode* dst)
      : src(src), dst(dst) { }
  ProfileNode* src;
  ProfileNode* dst;
};


348 349 350 351
class Position {
 public:
  explicit Position(ProfileNode* node)
      : node(node), child_idx_(0) { }
352
  INLINE(ProfileNode* current_child()) {
353
    return node->children()->at(child_idx_);
354
  }
355 356 357 358 359
  INLINE(bool has_current_child()) {
    return child_idx_ < node->children()->length();
  }
  INLINE(void next_child()) { ++child_idx_; }

360
  ProfileNode* node;
361 362
 private:
  int child_idx_;
363 364 365
};


366
// Non-recursive implementation of a depth-first post-order tree traversal.
367
template <typename Callback>
368
void ProfileTree::TraverseDepthFirst(Callback* callback) {
369
  List<Position> stack(10);
370
  stack.Add(Position(root_));
371
  while (stack.length() > 0) {
372
    Position& current = stack.last();
373
    if (current.has_current_child()) {
374
      callback->BeforeTraversingChild(current.node, current.current_child());
375
      stack.Add(Position(current.current_child()));
376 377 378 379 380
    } else {
      callback->AfterAllChildrenTraversed(current.node);
      if (stack.length() > 1) {
        Position& parent = stack[stack.length() - 2];
        callback->AfterChildTraversed(parent.node, current.node);
381
        parent.next_child();
382
      }
383 384
      // Remove child from the stack.
      stack.RemoveLast();
385
    }
386
  }
387 388
}

389 390
using v8::tracing::TracedValue;

391 392
CpuProfile::CpuProfile(CpuProfiler* profiler, const char* title,
                       bool record_samples)
393 394
    : title_(title),
      record_samples_(record_samples),
395
      start_time_(base::TimeTicks::HighResolutionNow()),
396
      top_down_(profiler->isolate()),
397 398 399 400 401 402
      profiler_(profiler),
      streaming_next_sample_(0) {
  auto value = TracedValue::Create();
  value->SetDouble("startTime",
                   (start_time_ - base::TimeTicks()).InMicroseconds());
  TRACE_EVENT_SAMPLE_WITH_ID1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profiler"),
403
                              "Profile", this, "data", std::move(value));
404
}
405

406
void CpuProfile::AddPath(base::TimeTicks timestamp,
407
                         const std::vector<CodeEntry*>& path, int src_line,
408 409 410
                         bool update_stats) {
  ProfileNode* top_frame_node =
      top_down_.AddPathFromEnd(path, src_line, update_stats);
411
  if (record_samples_ && !timestamp.IsNull()) {
412 413 414
    timestamps_.Add(timestamp);
    samples_.Add(top_frame_node);
  }
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456
  const int kSamplesFlushCount = 100;
  const int kNodesFlushCount = 10;
  if (samples_.length() - streaming_next_sample_ >= kSamplesFlushCount ||
      top_down_.pending_nodes_count() >= kNodesFlushCount) {
    StreamPendingTraceEvents();
  }
}

namespace {

void BuildNodeValue(const ProfileNode* node, TracedValue* value) {
  const CodeEntry* entry = node->entry();
  value->BeginDictionary("callFrame");
  value->SetString("functionName", entry->name());
  if (*entry->resource_name()) {
    value->SetString("url", entry->resource_name());
  }
  value->SetInteger("scriptId", entry->script_id());
  if (entry->line_number()) {
    value->SetInteger("lineNumber", entry->line_number() - 1);
  }
  if (entry->column_number()) {
    value->SetInteger("columnNumber", entry->column_number() - 1);
  }
  value->EndDictionary();
  value->SetInteger("id", node->id());
  if (node->parent()) {
    value->SetInteger("parent", node->parent()->id());
  }
  const char* deopt_reason = entry->bailout_reason();
  if (deopt_reason && deopt_reason[0] && strcmp(deopt_reason, "no reason")) {
    value->SetString("deoptReason", deopt_reason);
  }
}

}  // namespace

void CpuProfile::StreamPendingTraceEvents() {
  std::vector<const ProfileNode*> pending_nodes = top_down_.TakePendingNodes();
  if (pending_nodes.empty() && !samples_.length()) return;
  auto value = TracedValue::Create();

457 458 459 460 461 462 463 464 465 466
  if (!pending_nodes.empty() || streaming_next_sample_ != samples_.length()) {
    value->BeginDictionary("cpuProfile");
    if (!pending_nodes.empty()) {
      value->BeginArray("nodes");
      for (auto node : pending_nodes) {
        value->BeginDictionary();
        BuildNodeValue(node, value.get());
        value->EndDictionary();
      }
      value->EndArray();
467
    }
468 469 470 471 472 473 474 475
    if (streaming_next_sample_ != samples_.length()) {
      value->BeginArray("samples");
      for (int i = streaming_next_sample_; i < samples_.length(); ++i) {
        value->AppendInteger(samples_[i]->id());
      }
      value->EndArray();
    }
    value->EndDictionary();
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492
  }
  if (streaming_next_sample_ != samples_.length()) {
    value->BeginArray("timeDeltas");
    base::TimeTicks lastTimestamp =
        streaming_next_sample_ ? timestamps_[streaming_next_sample_ - 1]
                               : start_time();
    for (int i = streaming_next_sample_; i < timestamps_.length(); ++i) {
      value->AppendInteger(
          static_cast<int>((timestamps_[i] - lastTimestamp).InMicroseconds()));
      lastTimestamp = timestamps_[i];
    }
    value->EndArray();
    DCHECK(samples_.length() == timestamps_.length());
    streaming_next_sample_ = samples_.length();
  }

  TRACE_EVENT_SAMPLE_WITH_ID1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profiler"),
493
                              "ProfileChunk", this, "data", std::move(value));
494 495
}

496
void CpuProfile::FinishProfile() {
497
  end_time_ = base::TimeTicks::HighResolutionNow();
498 499 500 501
  StreamPendingTraceEvents();
  auto value = TracedValue::Create();
  value->SetDouble("endTime", (end_time_ - base::TimeTicks()).InMicroseconds());
  TRACE_EVENT_SAMPLE_WITH_ID1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profiler"),
502
                              "ProfileChunk", this, "data", std::move(value));
503 504 505
}

void CpuProfile::Print() {
506
  base::OS::Print("[Top down]:\n");
507 508 509
  top_down_.Print();
}

510 511
void CodeMap::AddCode(Address addr, CodeEntry* entry, unsigned size) {
  DeleteAllCoveredCode(addr, addr + size);
512
  code_map_.insert({addr, CodeEntryInfo(entry, size)});
513 514 515
}

void CodeMap::DeleteAllCoveredCode(Address start, Address end) {
516 517 518 519
  auto left = code_map_.upper_bound(start);
  if (left != code_map_.begin()) {
    --left;
    if (left->first + left->second.size <= start) ++left;
520
  }
521 522 523
  auto right = left;
  while (right != code_map_.end() && right->first < end) ++right;
  code_map_.erase(left, right);
524 525
}

yurys's avatar
yurys committed
526
CodeEntry* CodeMap::FindEntry(Address addr) {
527 528 529 530 531
  auto it = code_map_.upper_bound(addr);
  if (it == code_map_.begin()) return nullptr;
  --it;
  Address end_address = it->first + it->second.size;
  return addr < end_address ? it->second.entry : nullptr;
532 533
}

534 535
void CodeMap::MoveCode(Address from, Address to) {
  if (from == to) return;
536 537 538 539 540
  auto it = code_map_.find(from);
  if (it == code_map_.end()) return;
  CodeEntryInfo info = it->second;
  code_map_.erase(it);
  AddCode(to, info.entry, info.size);
541 542
}

543
void CodeMap::Print() {
544 545 546 547
  for (auto it = code_map_.begin(); it != code_map_.end(); ++it) {
    base::OS::Print("%p %5d %s\n", static_cast<void*>(it->first),
                    it->second.size, it->second.entry->name());
  }
548 549
}

550
CpuProfilesCollection::CpuProfilesCollection(Isolate* isolate)
lpy's avatar
lpy committed
551
    : resource_names_(isolate->heap()),
552
      profiler_(nullptr),
553
      current_profiles_semaphore_(1) {}
554

555 556 557 558
static void DeleteCpuProfile(CpuProfile** profile_ptr) {
  delete *profile_ptr;
}

559

560
CpuProfilesCollection::~CpuProfilesCollection() {
561
  finished_profiles_.Iterate(DeleteCpuProfile);
562
  current_profiles_.Iterate(DeleteCpuProfile);
563
}
564

565

566
bool CpuProfilesCollection::StartProfiling(const char* title,
567
                                           bool record_samples) {
568
  current_profiles_semaphore_.Wait();
569
  if (current_profiles_.length() >= kMaxSimultaneousProfiles) {
570
    current_profiles_semaphore_.Signal();
571 572
    return false;
  }
573 574
  for (int i = 0; i < current_profiles_.length(); ++i) {
    if (strcmp(current_profiles_[i]->title(), title) == 0) {
575
      // Ignore attempts to start profile with the same title...
576
      current_profiles_semaphore_.Signal();
577 578
      // ... though return true to force it collect a sample.
      return true;
579 580
    }
  }
581
  current_profiles_.Add(new CpuProfile(profiler_, title, record_samples));
582
  current_profiles_semaphore_.Signal();
583 584 585 586
  return true;
}


587
CpuProfile* CpuProfilesCollection::StopProfiling(const char* title) {
588
  const int title_len = StrLength(title);
589
  CpuProfile* profile = nullptr;
590
  current_profiles_semaphore_.Wait();
591 592 593 594 595 596
  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;
    }
  }
597
  current_profiles_semaphore_.Signal();
598

599 600
  if (!profile) return nullptr;
  profile->FinishProfile();
601 602
  finished_profiles_.Add(profile);
  return profile;
603 604 605
}


606 607 608 609 610 611 612 613 614
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;
}


615 616
void CpuProfilesCollection::RemoveProfile(CpuProfile* profile) {
  // Called from VM thread for a completed profile.
617
  for (int i = 0; i < finished_profiles_.length(); i++) {
618
    if (profile == finished_profiles_[i]) {
619 620
      finished_profiles_.Remove(i);
      return;
621 622
    }
  }
623
  UNREACHABLE();
624 625
}

626
void CpuProfilesCollection::AddPathToCurrentProfiles(
627 628
    base::TimeTicks timestamp, const std::vector<CodeEntry*>& path,
    int src_line, bool update_stats) {
629 630 631
  // 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.
632
  current_profiles_semaphore_.Wait();
633
  for (int i = 0; i < current_profiles_.length(); ++i) {
634
    current_profiles_[i]->AddPath(timestamp, path, src_line, update_stats);
635
  }
636
  current_profiles_semaphore_.Signal();
637 638
}

639 640
ProfileGenerator::ProfileGenerator(CpuProfilesCollection* profiles)
    : profiles_(profiles) {}
641 642

void ProfileGenerator::RecordTickSample(const TickSample& sample) {
643 644 645 646
  std::vector<CodeEntry*> entries;
  // Conservatively reserve space for stack frames + pc + function + vm-state.
  // There could in fact be more of them because of inlined entries.
  entries.reserve(sample.frames_count + 3);
647 648 649 650 651 652 653 654 655

  // 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;

656 657
  if (sample.pc != nullptr) {
    if (sample.has_external_callback && sample.state == EXTERNAL) {
658 659 660
      // 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.
661
      entries.push_back(FindEntry(sample.external_callback_entry));
662
    } else {
663
      CodeEntry* pc_entry = FindEntry(sample.pc);
664 665 666
      // If there is no pc_entry we're likely in native code.
      // Find out, if top of stack was pointing inside a JS function
      // meaning that we have encountered a frameless invocation.
667
      if (!pc_entry && !sample.has_external_callback) {
668
        pc_entry = FindEntry(sample.tos);
669
      }
670 671 672 673 674
      // 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) {
675 676
        int pc_offset = static_cast<int>(reinterpret_cast<Address>(sample.pc) -
                                         pc_entry->instruction_start());
677 678 679 680 681
        src_line = pc_entry->GetSourceLine(pc_offset);
        if (src_line == v8::CpuProfileNode::kNoLineNumberInfo) {
          src_line = pc_entry->line_number();
        }
        src_line_not_found = false;
682
        entries.push_back(pc_entry);
683

684 685 686 687 688 689
        if (pc_entry->builtin_id() == Builtins::kFunctionPrototypeApply ||
            pc_entry->builtin_id() == Builtins::kFunctionPrototypeCall) {
          // When current function is either the Function.prototype.apply or the
          // Function.prototype.call 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
690 691
          // former case we don't so we simply replace the frame with
          // 'unresolved' entry.
692
          if (!sample.has_external_callback) {
lpy's avatar
lpy committed
693
            entries.push_back(CodeEntry::unresolved_entry());
694
          }
695 696
        }
      }
697 698
    }

699 700
    for (unsigned i = 0; i < sample.frames_count; ++i) {
      Address stack_pos = reinterpret_cast<Address>(sample.stack[i]);
701
      CodeEntry* entry = FindEntry(stack_pos);
702 703
      if (entry) {
        // Find out if the entry has an inlining stack associated.
704
        int pc_offset =
705
            static_cast<int>(stack_pos - entry->instruction_start());
706 707 708 709 710 711 712 713 714 715 716 717 718 719
        const std::vector<CodeEntry*>* inline_stack =
            entry->GetInlineStack(pc_offset);
        if (inline_stack) {
          entries.insert(entries.end(), inline_stack->rbegin(),
                         inline_stack->rend());
        }
        // Skip unresolved frames (e.g. internal frame) and get source line of
        // the first JS caller.
        if (src_line_not_found) {
          src_line = entry->GetSourceLine(pc_offset);
          if (src_line == v8::CpuProfileNode::kNoLineNumberInfo) {
            src_line = entry->line_number();
          }
          src_line_not_found = false;
720 721
        }
      }
722
      entries.push_back(entry);
723
    }
724 725
  }

726
  if (FLAG_prof_browser_mode) {
727
    bool no_symbolized_entries = true;
728 729
    for (auto e : entries) {
      if (e != NULL) {
730 731 732 733 734 735
        no_symbolized_entries = false;
        break;
      }
    }
    // If no frames were symbolized, put the VM state entry in.
    if (no_symbolized_entries) {
736
      entries.push_back(EntryForVMState(sample.state));
737
    }
738
  }
739

740 741
  profiles_->AddPathToCurrentProfiles(sample.timestamp, entries, src_line,
                                      sample.update_stats);
742 743
}

744
CodeEntry* ProfileGenerator::FindEntry(void* address) {
745
  return code_map_.FindEntry(reinterpret_cast<Address>(address));
746
}
747

748 749 750
CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) {
  switch (tag) {
    case GC:
lpy's avatar
lpy committed
751
      return CodeEntry::gc_entry();
752 753 754 755 756 757 758
    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:
lpy's avatar
lpy committed
759
      return CodeEntry::program_entry();
760
    case IDLE:
lpy's avatar
lpy committed
761
      return CodeEntry::idle_entry();
762 763 764 765
    default: return NULL;
  }
}

766 767
}  // namespace internal
}  // namespace v8