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