Line data Source code
1 : // Copyright 2012 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // 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 : #include "src/tracing/trace-event.h"
15 : #include "src/tracing/traced-value.h"
16 : #include "src/unicode.h"
17 :
18 : namespace v8 {
19 : namespace internal {
20 :
21 :
22 6952 : JITLineInfoTable::JITLineInfoTable() {}
23 :
24 :
25 0 : JITLineInfoTable::~JITLineInfoTable() {}
26 :
27 :
28 55284 : void JITLineInfoTable::SetPosition(int pc_offset, int line) {
29 : DCHECK(pc_offset >= 0);
30 : DCHECK(line > 0); // The 1-based number of the source line.
31 55284 : if (GetSourceLineNumber(pc_offset) != line) {
32 17126 : pc_offset_map_.insert(std::make_pair(pc_offset, line));
33 : }
34 55284 : }
35 :
36 :
37 279003 : int JITLineInfoTable::GetSourceLineNumber(int pc_offset) const {
38 : PcOffsetMap::const_iterator it = pc_offset_map_.lower_bound(pc_offset);
39 279003 : if (it == pc_offset_map_.end()) {
40 52632 : if (pc_offset_map_.empty()) return v8::CpuProfileNode::kNoLineNumberInfo;
41 50432 : return (--pc_offset_map_.end())->second;
42 : }
43 226371 : return it->second;
44 : }
45 :
46 :
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 :
52 : const char* const CodeEntry::kProgramEntryName = "(program)";
53 : const char* const CodeEntry::kIdleEntryName = "(idle)";
54 : const char* const CodeEntry::kGarbageCollectorEntryName = "(garbage collector)";
55 : const char* const CodeEntry::kUnresolvedFunctionName = "(unresolved function)";
56 :
57 : base::LazyDynamicInstance<CodeEntry, CodeEntry::ProgramEntryCreateTrait>::type
58 : CodeEntry::kProgramEntry = LAZY_DYNAMIC_INSTANCE_INITIALIZER;
59 :
60 : base::LazyDynamicInstance<CodeEntry, CodeEntry::IdleEntryCreateTrait>::type
61 : CodeEntry::kIdleEntry = LAZY_DYNAMIC_INSTANCE_INITIALIZER;
62 :
63 : base::LazyDynamicInstance<CodeEntry, CodeEntry::GCEntryCreateTrait>::type
64 : CodeEntry::kGCEntry = LAZY_DYNAMIC_INSTANCE_INITIALIZER;
65 :
66 : base::LazyDynamicInstance<CodeEntry,
67 : CodeEntry::UnresolvedEntryCreateTrait>::type
68 : CodeEntry::kUnresolvedEntry = LAZY_DYNAMIC_INSTANCE_INITIALIZER;
69 :
70 184 : CodeEntry* CodeEntry::ProgramEntryCreateTrait::Create() {
71 368 : return new CodeEntry(Logger::FUNCTION_TAG, CodeEntry::kProgramEntryName);
72 : }
73 :
74 6 : CodeEntry* CodeEntry::IdleEntryCreateTrait::Create() {
75 12 : return new CodeEntry(Logger::FUNCTION_TAG, CodeEntry::kIdleEntryName);
76 : }
77 :
78 60 : CodeEntry* CodeEntry::GCEntryCreateTrait::Create() {
79 : return new CodeEntry(Logger::BUILTIN_TAG,
80 120 : CodeEntry::kGarbageCollectorEntryName);
81 : }
82 :
83 5 : CodeEntry* CodeEntry::UnresolvedEntryCreateTrait::Create() {
84 : return new CodeEntry(Logger::FUNCTION_TAG,
85 10 : CodeEntry::kUnresolvedFunctionName);
86 : }
87 :
88 497252 : CodeEntry::~CodeEntry() {
89 497252 : delete line_info_;
90 994506 : for (auto location : inline_locations_) {
91 7 : for (auto entry : location.second) {
92 3 : delete entry;
93 : }
94 : }
95 497252 : }
96 :
97 :
98 302926 : uint32_t CodeEntry::GetHash() const {
99 : uint32_t hash = ComputeIntegerHash(tag(), v8::internal::kZeroHashSeed);
100 302926 : if (script_id_ != v8::UnboundScript::kNoScriptId) {
101 : hash ^= ComputeIntegerHash(static_cast<uint32_t>(script_id_),
102 526646 : v8::internal::kZeroHashSeed);
103 : hash ^= ComputeIntegerHash(static_cast<uint32_t>(position_),
104 526646 : v8::internal::kZeroHashSeed);
105 : } else {
106 : hash ^= ComputeIntegerHash(
107 : static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name_prefix_)),
108 79206 : v8::internal::kZeroHashSeed);
109 : hash ^= ComputeIntegerHash(
110 : static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name_)),
111 79206 : v8::internal::kZeroHashSeed);
112 : hash ^= ComputeIntegerHash(
113 : static_cast<uint32_t>(reinterpret_cast<uintptr_t>(resource_name_)),
114 79206 : v8::internal::kZeroHashSeed);
115 79206 : hash ^= ComputeIntegerHash(line_number_, v8::internal::kZeroHashSeed);
116 : }
117 302926 : return hash;
118 : }
119 :
120 :
121 300142 : bool CodeEntry::IsSameFunctionAs(CodeEntry* entry) const {
122 300142 : if (this == entry) return true;
123 31 : if (script_id_ != v8::UnboundScript::kNoScriptId) {
124 17 : return script_id_ == entry->script_id_ && position_ == entry->position_;
125 : }
126 42 : return name_prefix_ == entry->name_prefix_ && name_ == entry->name_ &&
127 42 : resource_name_ == entry->resource_name_ &&
128 28 : line_number_ == entry->line_number_;
129 : }
130 :
131 :
132 120569 : void CodeEntry::SetBuiltinId(Builtins::Name id) {
133 241138 : bit_field_ = TagField::update(bit_field_, CodeEventListener::BUILTIN_TAG);
134 120569 : bit_field_ = BuiltinIdField::update(bit_field_, id);
135 120569 : }
136 :
137 :
138 225647 : int CodeEntry::GetSourceLine(int pc_offset) const {
139 449366 : if (line_info_ && !line_info_->empty()) {
140 223719 : return line_info_->GetSourceLineNumber(pc_offset);
141 : }
142 : return v8::CpuProfileNode::kNoLineNumberInfo;
143 : }
144 :
145 110 : void CodeEntry::AddInlineStack(int pc_offset,
146 : std::vector<CodeEntry*> inline_stack) {
147 110 : inline_locations_.insert(std::make_pair(pc_offset, std::move(inline_stack)));
148 110 : }
149 :
150 0 : const std::vector<CodeEntry*>* CodeEntry::GetInlineStack(int pc_offset) const {
151 : auto it = inline_locations_.find(pc_offset);
152 266019 : return it != inline_locations_.end() ? &it->second : NULL;
153 : }
154 :
155 260 : void CodeEntry::AddDeoptInlinedFrames(
156 : int deopt_id, std::vector<CpuProfileDeoptFrame> inlined_frames) {
157 : deopt_inlined_frames_.insert(
158 260 : std::make_pair(deopt_id, std::move(inlined_frames)));
159 260 : }
160 :
161 316 : bool CodeEntry::HasDeoptInlinedFramesFor(int deopt_id) const {
162 316 : return deopt_inlined_frames_.find(deopt_id) != deopt_inlined_frames_.end();
163 : }
164 :
165 133411 : void CodeEntry::FillFunctionInfo(SharedFunctionInfo* shared) {
166 266822 : if (!shared->script()->IsScript()) return;
167 : Script* script = Script::cast(shared->script());
168 : set_script_id(script->id());
169 : set_position(shared->start_position());
170 3860 : set_bailout_reason(GetBailoutReason(shared->disable_optimization_reason()));
171 : }
172 :
173 29 : CpuProfileDeoptInfo CodeEntry::GetDeoptInfo() {
174 : DCHECK(has_deopt_info());
175 :
176 : CpuProfileDeoptInfo info;
177 29 : info.deopt_reason = deopt_reason_;
178 : DCHECK_NE(kNoDeoptimizationId, deopt_id_);
179 58 : if (deopt_inlined_frames_.find(deopt_id_) == deopt_inlined_frames_.end()) {
180 : info.stack.push_back(CpuProfileDeoptFrame(
181 0 : {script_id_, static_cast<size_t>(std::max(0, position()))}));
182 : } else {
183 29 : info.stack = deopt_inlined_frames_[deopt_id_];
184 : }
185 29 : return info;
186 : }
187 :
188 :
189 29 : void ProfileNode::CollectDeoptInfo(CodeEntry* entry) {
190 58 : deopt_infos_.push_back(entry->GetDeoptInfo());
191 : entry->clear_deopt_info();
192 29 : }
193 :
194 :
195 467 : ProfileNode* ProfileNode::FindChild(CodeEntry* entry) {
196 : base::HashMap::Entry* map_entry =
197 934 : children_.Lookup(entry, CodeEntryHash(entry));
198 : return map_entry != NULL ?
199 467 : reinterpret_cast<ProfileNode*>(map_entry->value) : NULL;
200 : }
201 :
202 :
203 302459 : ProfileNode* ProfileNode::FindOrAddChild(CodeEntry* entry) {
204 : base::HashMap::Entry* map_entry =
205 604918 : children_.LookupOrInsert(entry, CodeEntryHash(entry));
206 302459 : ProfileNode* node = reinterpret_cast<ProfileNode*>(map_entry->value);
207 302459 : if (!node) {
208 2674 : node = new ProfileNode(tree_, entry, this);
209 2674 : map_entry->value = node;
210 2674 : children_list_.Add(node);
211 : }
212 302459 : return node;
213 : }
214 :
215 :
216 223564 : void ProfileNode::IncrementLineTicks(int src_line) {
217 447128 : if (src_line == v8::CpuProfileNode::kNoLineNumberInfo) return;
218 : // Increment a hit counter of a certain source line.
219 : // Add a new source line if not found.
220 : base::HashMap::Entry* e =
221 447128 : line_ticks_.LookupOrInsert(reinterpret_cast<void*>(src_line), src_line);
222 : DCHECK(e);
223 223564 : e->value = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(e->value) + 1);
224 : }
225 :
226 :
227 36 : bool ProfileNode::GetLineTicks(v8::CpuProfileNode::LineTick* entries,
228 : unsigned int length) const {
229 36 : if (entries == NULL || length == 0) return false;
230 :
231 36 : unsigned line_count = line_ticks_.occupancy();
232 :
233 36 : if (line_count == 0) return true;
234 36 : if (length < line_count) return false;
235 :
236 : v8::CpuProfileNode::LineTick* entry = entries;
237 :
238 84 : for (base::HashMap::Entry *p = line_ticks_.Start(); p != NULL;
239 : p = line_ticks_.Next(p), entry++) {
240 : entry->line =
241 48 : static_cast<unsigned int>(reinterpret_cast<uintptr_t>(p->key));
242 : entry->hit_count =
243 48 : static_cast<unsigned int>(reinterpret_cast<uintptr_t>(p->value));
244 : }
245 :
246 : return true;
247 : }
248 :
249 :
250 636 : 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 1932 : id());
254 1272 : if (entry_->resource_name()[0] != '\0')
255 24 : base::OS::Print(" %s:%d", entry_->resource_name(), entry_->line_number());
256 636 : base::OS::Print("\n");
257 1302 : for (size_t i = 0; i < deopt_infos_.size(); ++i) {
258 651 : CpuProfileDeoptInfo& info = deopt_infos_[i];
259 : base::OS::Print("%*s;;; deopted at script_id: %d position: %" PRIuS
260 : " with reason '%s'.\n",
261 : indent + 10, "", info.stack[0].script_id,
262 39 : info.stack[0].position, info.deopt_reason);
263 48 : for (size_t index = 1; index < info.stack.size(); ++index) {
264 : base::OS::Print("%*s;;; Inline point: script_id %d position: %" PRIuS
265 : ".\n",
266 : indent + 10, "", info.stack[index].script_id,
267 9 : info.stack[index].position);
268 : }
269 : }
270 636 : const char* bailout_reason = entry_->bailout_reason();
271 636 : if (bailout_reason != GetBailoutReason(BailoutReason::kNoReason) &&
272 : bailout_reason != CodeEntry::kEmptyBailoutReason) {
273 : base::OS::Print("%*s bailed out due to '%s'\n", indent + 10, "",
274 136 : bailout_reason);
275 : }
276 1803 : for (base::HashMap::Entry* p = children_.Start(); p != NULL;
277 : p = children_.Next(p)) {
278 531 : reinterpret_cast<ProfileNode*>(p->value)->Print(indent + 2);
279 : }
280 636 : }
281 :
282 :
283 : class DeleteNodesCallback {
284 : public:
285 : void BeforeTraversingChild(ProfileNode*, ProfileNode*) { }
286 :
287 3800 : void AfterAllChildrenTraversed(ProfileNode* node) {
288 3800 : delete node;
289 3800 : }
290 :
291 : void AfterChildTraversed(ProfileNode*, ProfileNode*) { }
292 : };
293 :
294 1126 : ProfileTree::ProfileTree(Isolate* isolate)
295 : : root_entry_(CodeEventListener::FUNCTION_TAG, "(root)"),
296 : next_node_id_(1),
297 1126 : root_(new ProfileNode(this, &root_entry_, nullptr)),
298 : isolate_(isolate),
299 : next_function_id_(1),
300 3378 : function_ids_(ProfileNode::CodeEntriesMatch) {}
301 :
302 2252 : ProfileTree::~ProfileTree() {
303 : DeleteNodesCallback cb;
304 1126 : TraverseDepthFirst(&cb);
305 1126 : }
306 :
307 :
308 0 : unsigned ProfileTree::GetFunctionId(const ProfileNode* node) {
309 : CodeEntry* code_entry = node->entry();
310 : base::HashMap::Entry* entry =
311 0 : function_ids_.LookupOrInsert(code_entry, code_entry->GetHash());
312 0 : if (!entry->value) {
313 0 : entry->value = reinterpret_cast<void*>(next_function_id_++);
314 : }
315 0 : return static_cast<unsigned>(reinterpret_cast<uintptr_t>(entry->value));
316 : }
317 :
318 232702 : ProfileNode* ProfileTree::AddPathFromEnd(const std::vector<CodeEntry*>& path,
319 : int src_line, bool update_stats) {
320 232702 : ProfileNode* node = root_;
321 232625 : CodeEntry* last_entry = NULL;
322 739412 : for (auto it = path.rbegin(); it != path.rend(); ++it) {
323 506710 : if (*it == NULL) continue;
324 : last_entry = *it;
325 302368 : node = node->FindOrAddChild(*it);
326 : }
327 465327 : if (last_entry && last_entry->has_deopt_info()) {
328 29 : node->CollectDeoptInfo(last_entry);
329 : }
330 232702 : if (update_stats) {
331 : node->IncrementSelfTicks();
332 232115 : if (src_line != v8::CpuProfileNode::kNoLineNumberInfo) {
333 223444 : node->IncrementLineTicks(src_line);
334 : }
335 : }
336 232702 : return node;
337 : }
338 :
339 :
340 : struct NodesPair {
341 : NodesPair(ProfileNode* src, ProfileNode* dst)
342 : : src(src), dst(dst) { }
343 : ProfileNode* src;
344 : ProfileNode* dst;
345 : };
346 :
347 :
348 : class Position {
349 : public:
350 : explicit Position(ProfileNode* node)
351 3800 : : node(node), child_idx_(0) { }
352 : INLINE(ProfileNode* current_child()) {
353 5348 : return node->children()->at(child_idx_);
354 : }
355 : INLINE(bool has_current_child()) {
356 6474 : return child_idx_ < node->children()->length();
357 : }
358 2674 : INLINE(void next_child()) { ++child_idx_; }
359 :
360 : ProfileNode* node;
361 : private:
362 : int child_idx_;
363 : };
364 :
365 :
366 : // Non-recursive implementation of a depth-first post-order tree traversal.
367 : template <typename Callback>
368 1126 : void ProfileTree::TraverseDepthFirst(Callback* callback) {
369 : List<Position> stack(10);
370 2252 : stack.Add(Position(root_));
371 8726 : while (stack.length() > 0) {
372 9148 : Position& current = stack.last();
373 6474 : if (current.has_current_child()) {
374 : callback->BeforeTraversingChild(current.node, current.current_child());
375 2674 : stack.Add(Position(current.current_child()));
376 : } else {
377 3800 : callback->AfterAllChildrenTraversed(current.node);
378 3800 : if (stack.length() > 1) {
379 2674 : Position& parent = stack[stack.length() - 2];
380 : callback->AfterChildTraversed(parent.node, current.node);
381 : parent.next_child();
382 : }
383 : // Remove child from the stack.
384 : stack.RemoveLast();
385 : }
386 : }
387 1126 : }
388 :
389 : using v8::tracing::TracedValue;
390 :
391 2154 : CpuProfile::CpuProfile(CpuProfiler* profiler, const char* title,
392 : bool record_samples)
393 : : title_(title),
394 : record_samples_(record_samples),
395 : start_time_(base::TimeTicks::HighResolutionNow()),
396 : top_down_(profiler->isolate()),
397 : profiler_(profiler),
398 2154 : streaming_next_sample_(0) {
399 1077 : auto value = TracedValue::Create();
400 : value->SetDouble("startTime",
401 2154 : (start_time_ - base::TimeTicks()).InMicroseconds());
402 2154 : TRACE_EVENT_SAMPLE_WITH_ID1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profiler"),
403 : "Profile", this, "data", std::move(value));
404 1077 : }
405 :
406 232569 : void CpuProfile::AddPath(base::TimeTicks timestamp,
407 : const std::vector<CodeEntry*>& path, int src_line,
408 : bool update_stats) {
409 : ProfileNode* top_frame_node =
410 232569 : top_down_.AddPathFromEnd(path, src_line, update_stats);
411 242123 : if (record_samples_ && !timestamp.IsNull()) {
412 9242 : timestamps_.Add(timestamp);
413 9242 : samples_.Add(top_frame_node);
414 : }
415 : const int kSamplesFlushCount = 100;
416 : const int kNodesFlushCount = 10;
417 465054 : if (samples_.length() - streaming_next_sample_ >= kSamplesFlushCount ||
418 : top_down_.pending_nodes_count() >= kNodesFlushCount) {
419 103 : StreamPendingTraceEvents();
420 : }
421 232569 : }
422 :
423 : namespace {
424 :
425 7872 : void BuildNodeValue(const ProfileNode* node, TracedValue* value) {
426 15744 : const CodeEntry* entry = node->entry();
427 2624 : value->BeginDictionary("callFrame");
428 2624 : value->SetString("functionName", entry->name());
429 2624 : if (*entry->resource_name()) {
430 36 : value->SetString("url", entry->resource_name());
431 : }
432 2624 : value->SetInteger("scriptId", entry->script_id());
433 2624 : if (entry->line_number()) {
434 380 : value->SetInteger("lineNumber", entry->line_number() - 1);
435 : }
436 2624 : if (entry->column_number()) {
437 380 : value->SetInteger("columnNumber", entry->column_number() - 1);
438 : }
439 2624 : value->EndDictionary();
440 2624 : value->SetInteger("id", node->id());
441 2624 : if (node->parent()) {
442 2354 : value->SetInteger("parent", node->parent()->id());
443 : }
444 : const char* deopt_reason = entry->bailout_reason();
445 2624 : if (deopt_reason && deopt_reason[0] && strcmp(deopt_reason, "no reason")) {
446 136 : value->SetString("deoptReason", deopt_reason);
447 : }
448 2624 : }
449 :
450 : } // namespace
451 :
452 372 : void CpuProfile::StreamPendingTraceEvents() {
453 : std::vector<const ProfileNode*> pending_nodes = top_down_.TakePendingNodes();
454 20326 : if (pending_nodes.empty() && !samples_.length()) return;
455 366 : auto value = TracedValue::Create();
456 :
457 444 : if (!pending_nodes.empty() || streaming_next_sample_ != samples_.length()) {
458 366 : value->BeginDictionary("cpuProfile");
459 366 : if (!pending_nodes.empty()) {
460 288 : value->BeginArray("nodes");
461 3200 : for (auto node : pending_nodes) {
462 2624 : value->BeginDictionary();
463 2624 : BuildNodeValue(node, value.get());
464 2624 : value->EndDictionary();
465 : }
466 288 : value->EndArray();
467 : }
468 732 : if (streaming_next_sample_ != samples_.length()) {
469 144 : value->BeginArray("samples");
470 18772 : for (int i = streaming_next_sample_; i < samples_.length(); ++i) {
471 18484 : value->AppendInteger(samples_[i]->id());
472 : }
473 144 : value->EndArray();
474 : }
475 366 : value->EndDictionary();
476 : }
477 732 : if (streaming_next_sample_ != samples_.length()) {
478 144 : value->BeginArray("timeDeltas");
479 : base::TimeTicks lastTimestamp =
480 27960 : streaming_next_sample_ ? timestamps_[streaming_next_sample_ - 1]
481 234 : : start_time();
482 18772 : for (int i = streaming_next_sample_; i < timestamps_.length(); ++i) {
483 : value->AppendInteger(
484 18484 : static_cast<int>((timestamps_[i] - lastTimestamp).InMicroseconds()));
485 9242 : lastTimestamp = timestamps_[i];
486 : }
487 144 : value->EndArray();
488 : DCHECK(samples_.length() == timestamps_.length());
489 144 : streaming_next_sample_ = samples_.length();
490 : }
491 :
492 732 : TRACE_EVENT_SAMPLE_WITH_ID1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profiler"),
493 : "ProfileChunk", this, "data", std::move(value));
494 : }
495 :
496 269 : void CpuProfile::FinishProfile() {
497 269 : end_time_ = base::TimeTicks::HighResolutionNow();
498 269 : StreamPendingTraceEvents();
499 269 : auto value = TracedValue::Create();
500 538 : value->SetDouble("endTime", (end_time_ - base::TimeTicks()).InMicroseconds());
501 538 : TRACE_EVENT_SAMPLE_WITH_ID1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profiler"),
502 : "ProfileChunk", this, "data", std::move(value));
503 269 : }
504 :
505 78 : void CpuProfile::Print() {
506 78 : base::OS::Print("[Top down]:\n");
507 78 : top_down_.Print();
508 78 : }
509 :
510 519612 : void CodeMap::AddCode(Address addr, CodeEntry* entry, unsigned size) {
511 519612 : DeleteAllCoveredCode(addr, addr + size);
512 519612 : code_map_.insert({addr, CodeEntryInfo(entry, size)});
513 519612 : }
514 :
515 519612 : void CodeMap::DeleteAllCoveredCode(Address start, Address end) {
516 : auto left = code_map_.upper_bound(start);
517 519612 : if (left != code_map_.begin()) {
518 : --left;
519 517880 : if (left->first + left->second.size <= start) ++left;
520 : }
521 : auto right = left;
522 1096696 : while (right != code_map_.end() && right->first < end) ++right;
523 : code_map_.erase(left, right);
524 519612 : }
525 :
526 1090463 : CodeEntry* CodeMap::FindEntry(Address addr) {
527 : auto it = code_map_.upper_bound(addr);
528 1090463 : if (it == code_map_.begin()) return nullptr;
529 : --it;
530 964247 : Address end_address = it->first + it->second.size;
531 964247 : return addr < end_address ? it->second.entry : nullptr;
532 : }
533 :
534 13 : void CodeMap::MoveCode(Address from, Address to) {
535 13 : if (from == to) return;
536 : auto it = code_map_.find(from);
537 13 : if (it == code_map_.end()) return;
538 13 : CodeEntryInfo info = it->second;
539 : code_map_.erase(it);
540 13 : AddCode(to, info.entry, info.size);
541 : }
542 :
543 0 : void CodeMap::Print() {
544 0 : for (auto it = code_map_.begin(); it != code_map_.end(); ++it) {
545 : base::OS::Print("%p %5d %s\n", static_cast<void*>(it->first),
546 0 : it->second.size, it->second.entry->name());
547 : }
548 0 : }
549 :
550 120670 : CpuProfilesCollection::CpuProfilesCollection(Isolate* isolate)
551 : : resource_names_(isolate->heap()),
552 : profiler_(nullptr),
553 362008 : current_profiles_semaphore_(1) {}
554 :
555 923 : static void DeleteCpuProfile(CpuProfile** profile_ptr) {
556 923 : delete *profile_ptr;
557 923 : }
558 :
559 :
560 357519 : CpuProfilesCollection::~CpuProfilesCollection() {
561 : finished_profiles_.Iterate(DeleteCpuProfile);
562 : current_profiles_.Iterate(DeleteCpuProfile);
563 119174 : }
564 :
565 :
566 1277 : bool CpuProfilesCollection::StartProfiling(const char* title,
567 : bool record_samples) {
568 1277 : current_profiles_semaphore_.Wait();
569 36152 : if (current_profiles_.length() >= kMaxSimultaneousProfiles) {
570 7 : current_profiles_semaphore_.Signal();
571 7 : return false;
572 : }
573 34682 : for (int i = 0; i < current_profiles_.length(); ++i) {
574 34875 : if (strcmp(current_profiles_[i]->title(), title) == 0) {
575 : // Ignore attempts to start profile with the same title...
576 193 : current_profiles_semaphore_.Signal();
577 : // ... though return true to force it collect a sample.
578 193 : return true;
579 : }
580 : }
581 1077 : current_profiles_.Add(new CpuProfile(profiler_, title, record_samples));
582 1077 : current_profiles_semaphore_.Signal();
583 1077 : return true;
584 : }
585 :
586 :
587 275 : CpuProfile* CpuProfilesCollection::StopProfiling(const char* title) {
588 : const int title_len = StrLength(title);
589 : CpuProfile* profile = nullptr;
590 275 : current_profiles_semaphore_.Wait();
591 462 : for (int i = current_profiles_.length() - 1; i >= 0; --i) {
592 456 : if (title_len == 0 || strcmp(current_profiles_[i]->title(), title) == 0) {
593 269 : profile = current_profiles_.Remove(i);
594 269 : break;
595 : }
596 : }
597 275 : current_profiles_semaphore_.Signal();
598 :
599 275 : if (!profile) return nullptr;
600 269 : profile->FinishProfile();
601 269 : finished_profiles_.Add(profile);
602 269 : return profile;
603 : }
604 :
605 :
606 230 : bool CpuProfilesCollection::IsLastProfile(const char* title) {
607 : // Called from VM thread, and only it can mutate the list,
608 : // so no locking is needed here.
609 383 : if (current_profiles_.length() != 1) return false;
610 : return StrLength(title) == 0
611 357 : || strcmp(current_profiles_[0]->title(), title) == 0;
612 : }
613 :
614 :
615 154 : void CpuProfilesCollection::RemoveProfile(CpuProfile* profile) {
616 : // Called from VM thread for a completed profile.
617 308 : for (int i = 0; i < finished_profiles_.length(); i++) {
618 462 : if (profile == finished_profiles_[i]) {
619 154 : finished_profiles_.Remove(i);
620 154 : return;
621 : }
622 : }
623 0 : UNREACHABLE();
624 : }
625 :
626 232511 : void CpuProfilesCollection::AddPathToCurrentProfiles(
627 : base::TimeTicks timestamp, const std::vector<CodeEntry*>& path,
628 : int src_line, bool update_stats) {
629 : // As starting / stopping profiles is rare relatively to this
630 : // method, we don't bother minimizing the duration of lock holding,
631 : // e.g. copying contents of the list to a local vector.
632 232511 : current_profiles_semaphore_.Wait();
633 930160 : for (int i = 0; i < current_profiles_.length(); ++i) {
634 930218 : current_profiles_[i]->AddPath(timestamp, path, src_line, update_stats);
635 : }
636 232511 : current_profiles_semaphore_.Signal();
637 232511 : }
638 :
639 351 : ProfileGenerator::ProfileGenerator(CpuProfilesCollection* profiles)
640 702 : : profiles_(profiles) {}
641 :
642 232511 : void ProfileGenerator::RecordTickSample(const TickSample& sample) {
643 : std::vector<CodeEntry*> entries;
644 : // Conservatively reserve space for stack frames + pc + function + vm-state.
645 : // There could in fact be more of them because of inlined entries.
646 232511 : entries.reserve(sample.frames_count + 3);
647 :
648 : // The ProfileNode knows nothing about all versions of generated code for
649 : // the same JS function. The line number information associated with
650 : // the latest version of generated code is used to find a source line number
651 : // for a JS function. Then, the detected source line is passed to
652 : // ProfileNode to increase the tick count for this source line.
653 : int src_line = v8::CpuProfileNode::kNoLineNumberInfo;
654 : bool src_line_not_found = true;
655 :
656 232511 : if (sample.pc != nullptr) {
657 231237 : if (sample.has_external_callback && sample.state == EXTERNAL) {
658 : // Don't use PC when in external callback code, as it can point
659 : // inside callback's code, and we will erroneously report
660 : // that a callback calls itself.
661 73548 : entries.push_back(FindEntry(sample.external_callback_entry));
662 : } else {
663 206721 : CodeEntry* pc_entry = FindEntry(sample.pc);
664 : // If there is no pc_entry we're likely in native code.
665 : // Find out, if top of stack was pointing inside a JS function
666 : // meaning that we have encountered a frameless invocation.
667 216915 : if (!pc_entry && !sample.has_external_callback) {
668 401028 : pc_entry = FindEntry(sample.tos);
669 : }
670 : // If pc is in the function code before it set up stack frame or after the
671 : // frame was destroyed SafeStackFrameIterator incorrectly thinks that
672 : // ebp contains return address of the current function and skips caller's
673 : // frame. Check for this case and just skip such samples.
674 206721 : if (pc_entry) {
675 9510 : int pc_offset = static_cast<int>(reinterpret_cast<Address>(sample.pc) -
676 4755 : pc_entry->instruction_start());
677 4755 : src_line = pc_entry->GetSourceLine(pc_offset);
678 4755 : if (src_line == v8::CpuProfileNode::kNoLineNumberInfo) {
679 : src_line = pc_entry->line_number();
680 : }
681 : src_line_not_found = false;
682 4755 : entries.push_back(pc_entry);
683 :
684 9510 : if (pc_entry->builtin_id() == Builtins::kFunctionPrototypeApply ||
685 : pc_entry->builtin_id() == Builtins::kFunctionPrototypeCall) {
686 : // When current function is either the Function.prototype.apply or the
687 : // Function.prototype.call builtin the top frame is either frame of
688 : // the calling JS function or internal frame.
689 : // In the latter case we know the caller for sure but in the
690 : // former case we don't so we simply replace the frame with
691 : // 'unresolved' entry.
692 5 : if (!sample.has_external_callback) {
693 10 : entries.push_back(CodeEntry::unresolved_entry());
694 : }
695 : }
696 : }
697 : }
698 :
699 470258 : for (unsigned i = 0; i < sample.frames_count; ++i) {
700 470258 : Address stack_pos = reinterpret_cast<Address>(sample.stack[i]);
701 470258 : CodeEntry* entry = FindEntry(stack_pos);
702 737521 : if (entry) {
703 : // Find out if the entry has an inlining stack associated.
704 : int pc_offset =
705 532038 : static_cast<int>(stack_pos - entry->instruction_start());
706 : const std::vector<CodeEntry*>* inline_stack =
707 : entry->GetInlineStack(pc_offset);
708 266019 : if (inline_stack) {
709 : entries.insert(entries.end(), inline_stack->rbegin(),
710 1 : inline_stack->rend());
711 : }
712 : // Skip unresolved frames (e.g. internal frame) and get source line of
713 : // the first JS caller.
714 266019 : if (src_line_not_found) {
715 220892 : src_line = entry->GetSourceLine(pc_offset);
716 220892 : if (src_line == v8::CpuProfileNode::kNoLineNumberInfo) {
717 : src_line = entry->line_number();
718 : }
719 : src_line_not_found = false;
720 : }
721 : }
722 470258 : entries.push_back(entry);
723 : }
724 : }
725 :
726 232511 : if (FLAG_prof_browser_mode) {
727 : bool no_symbolized_entries = true;
728 644546 : for (auto e : entries) {
729 405378 : if (e != NULL) {
730 : no_symbolized_entries = false;
731 : break;
732 : }
733 : }
734 : // If no frames were symbolized, put the VM state entry in.
735 232369 : if (no_symbolized_entries) {
736 13598 : entries.push_back(EntryForVMState(sample.state));
737 : }
738 : }
739 :
740 : profiles_->AddPathToCurrentProfiles(sample.timestamp, entries, src_line,
741 232511 : sample.update_stats);
742 232511 : }
743 :
744 0 : CodeEntry* ProfileGenerator::FindEntry(void* address) {
745 902009 : return code_map_.FindEntry(reinterpret_cast<Address>(address));
746 : }
747 :
748 6799 : CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) {
749 6799 : switch (tag) {
750 : case GC:
751 2135 : return CodeEntry::gc_entry();
752 : case JS:
753 : case COMPILER:
754 : // DOM events handlers are reported as OTHER / EXTERNAL entries.
755 : // To avoid confusing people, let's put all these entries into
756 : // one bucket.
757 : case OTHER:
758 : case EXTERNAL:
759 4646 : return CodeEntry::program_entry();
760 : case IDLE:
761 18 : return CodeEntry::idle_entry();
762 : default: return NULL;
763 : }
764 : }
765 :
766 : } // namespace internal
767 : } // namespace v8
|