1 // Copyright 2011 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 #ifndef V8_PROFILER_PROFILE_GENERATOR_H_ 6 #define V8_PROFILER_PROFILE_GENERATOR_H_ 7 8 #include <atomic> 9 #include <deque> 10 #include <limits> 11 #include <map> 12 #include <memory> 13 #include <unordered_map> 14 #include <utility> 15 #include <vector> 16 17 #include "include/v8-profiler.h" 18 #include "src/allocation.h" 19 #include "src/log.h" 20 #include "src/profiler/strings-storage.h" 21 #include "src/source-position.h" 22 23 namespace v8 { 24 namespace internal { 25 26 struct TickSample; 27 28 // Provides a mapping from the offsets within generated code or a bytecode array 29 // to the source line. 30 class SourcePositionTable : public Malloced { 31 public: 32 SourcePositionTable() = default; 33 34 void SetPosition(int pc_offset, int line); 35 int GetSourceLineNumber(int pc_offset) const; 36 37 private: 38 struct PCOffsetAndLineNumber { 39 bool operator<(const PCOffsetAndLineNumber& other) const { 40 return pc_offset < other.pc_offset; 41 } 42 int pc_offset; 43 int line_number; 44 }; 45 // This is logically a map, but we store it as a vector of pairs, sorted by 46 // the pc offset, so that we can save space and look up items using binary 47 // search. 48 std::vector<PCOffsetAndLineNumber> pc_offsets_to_lines_; 49 DISALLOW_COPY_AND_ASSIGN(SourcePositionTable); 50 }; 51 52 class CodeEntry { 53 public: 54 // CodeEntry doesn't own name strings, just references them. 55 inline CodeEntry(CodeEventListener::LogEventsAndTags tag, const char* name, 56 const char* resource_name = CodeEntry::kEmptyResourceName, 57 int line_number = v8::CpuProfileNode::kNoLineNumberInfo, 58 int column_number = v8::CpuProfileNode::kNoColumnNumberInfo, 59 std::unique_ptr<SourcePositionTable> line_info = nullptr, 60 Address instruction_start = kNullAddress); 61 name()62 const char* name() const { return name_; } resource_name()63 const char* resource_name() const { return resource_name_; } line_number()64 int line_number() const { return line_number_; } column_number()65 int column_number() const { return column_number_; } line_info()66 const SourcePositionTable* line_info() const { return line_info_.get(); } script_id()67 int script_id() const { return script_id_; } set_script_id(int script_id)68 void set_script_id(int script_id) { script_id_ = script_id; } position()69 int position() const { return position_; } set_position(int position)70 void set_position(int position) { position_ = position; } set_bailout_reason(const char * bailout_reason)71 void set_bailout_reason(const char* bailout_reason) { 72 EnsureRareData()->bailout_reason_ = bailout_reason; 73 } bailout_reason()74 const char* bailout_reason() const { 75 return rare_data_ ? rare_data_->bailout_reason_ : kEmptyBailoutReason; 76 } 77 78 void set_deopt_info(const char* deopt_reason, int deopt_id, 79 std::vector<CpuProfileDeoptFrame> inlined_frames); 80 81 CpuProfileDeoptInfo GetDeoptInfo(); has_deopt_info()82 bool has_deopt_info() const { 83 return rare_data_ && rare_data_->deopt_id_ != kNoDeoptimizationId; 84 } clear_deopt_info()85 void clear_deopt_info() { 86 if (!rare_data_) return; 87 // TODO(alph): Clear rare_data_ if that was the only field in use. 88 rare_data_->deopt_reason_ = kNoDeoptReason; 89 rare_data_->deopt_id_ = kNoDeoptimizationId; 90 } mark_used()91 void mark_used() { bit_field_ = UsedField::update(bit_field_, true); } used()92 bool used() const { return UsedField::decode(bit_field_); } 93 94 void FillFunctionInfo(SharedFunctionInfo* shared); 95 96 void SetBuiltinId(Builtins::Name id); builtin_id()97 Builtins::Name builtin_id() const { 98 return BuiltinIdField::decode(bit_field_); 99 } 100 101 uint32_t GetHash() const; 102 bool IsSameFunctionAs(const CodeEntry* entry) const; 103 104 int GetSourceLine(int pc_offset) const; 105 106 void AddInlineStack(int pc_offset, 107 std::vector<std::unique_ptr<CodeEntry>> inline_stack); 108 const std::vector<std::unique_ptr<CodeEntry>>* GetInlineStack( 109 int pc_offset) const; 110 set_instruction_start(Address start)111 void set_instruction_start(Address start) { instruction_start_ = start; } instruction_start()112 Address instruction_start() const { return instruction_start_; } 113 tag()114 CodeEventListener::LogEventsAndTags tag() const { 115 return TagField::decode(bit_field_); 116 } 117 118 static const char* const kWasmResourceNamePrefix; 119 static const char* const kEmptyResourceName; 120 static const char* const kEmptyBailoutReason; 121 static const char* const kNoDeoptReason; 122 123 static const char* const kProgramEntryName; 124 static const char* const kIdleEntryName; 125 static const char* const kGarbageCollectorEntryName; 126 // Used to represent frames for which we have no reliable way to 127 // detect function. 128 static const char* const kUnresolvedFunctionName; 129 program_entry()130 V8_INLINE static CodeEntry* program_entry() { 131 return kProgramEntry.Pointer(); 132 } idle_entry()133 V8_INLINE static CodeEntry* idle_entry() { return kIdleEntry.Pointer(); } gc_entry()134 V8_INLINE static CodeEntry* gc_entry() { return kGCEntry.Pointer(); } unresolved_entry()135 V8_INLINE static CodeEntry* unresolved_entry() { 136 return kUnresolvedEntry.Pointer(); 137 } 138 139 private: 140 struct RareData { 141 const char* deopt_reason_ = kNoDeoptReason; 142 const char* bailout_reason_ = kEmptyBailoutReason; 143 int deopt_id_ = kNoDeoptimizationId; 144 std::unordered_map<int, std::vector<std::unique_ptr<CodeEntry>>> 145 inline_locations_; 146 std::vector<CpuProfileDeoptFrame> deopt_inlined_frames_; 147 }; 148 149 RareData* EnsureRareData(); 150 151 struct ProgramEntryCreateTrait { 152 static CodeEntry* Create(); 153 }; 154 struct IdleEntryCreateTrait { 155 static CodeEntry* Create(); 156 }; 157 struct GCEntryCreateTrait { 158 static CodeEntry* Create(); 159 }; 160 struct UnresolvedEntryCreateTrait { 161 static CodeEntry* Create(); 162 }; 163 164 static base::LazyDynamicInstance<CodeEntry, ProgramEntryCreateTrait>::type 165 kProgramEntry; 166 static base::LazyDynamicInstance<CodeEntry, IdleEntryCreateTrait>::type 167 kIdleEntry; 168 static base::LazyDynamicInstance<CodeEntry, GCEntryCreateTrait>::type 169 kGCEntry; 170 static base::LazyDynamicInstance<CodeEntry, UnresolvedEntryCreateTrait>::type 171 kUnresolvedEntry; 172 173 using TagField = BitField<Logger::LogEventsAndTags, 0, 8>; 174 using BuiltinIdField = BitField<Builtins::Name, 8, 23>; 175 using UsedField = BitField<bool, 31, 1>; 176 177 uint32_t bit_field_; 178 const char* name_; 179 const char* resource_name_; 180 int line_number_; 181 int column_number_; 182 int script_id_; 183 int position_; 184 std::unique_ptr<SourcePositionTable> line_info_; 185 Address instruction_start_; 186 std::unique_ptr<RareData> rare_data_; 187 188 DISALLOW_COPY_AND_ASSIGN(CodeEntry); 189 }; 190 191 struct CodeEntryAndLineNumber { 192 CodeEntry* code_entry; 193 int line_number; 194 }; 195 196 typedef std::vector<CodeEntryAndLineNumber> ProfileStackTrace; 197 198 class ProfileTree; 199 200 class ProfileNode { 201 public: 202 inline ProfileNode(ProfileTree* tree, CodeEntry* entry, ProfileNode* parent, 203 int line_number = 0); 204 205 ProfileNode* FindChild( 206 CodeEntry* entry, 207 int line_number = v8::CpuProfileNode::kNoLineNumberInfo); 208 ProfileNode* FindOrAddChild(CodeEntry* entry, int line_number = 0); IncrementSelfTicks()209 void IncrementSelfTicks() { ++self_ticks_; } IncreaseSelfTicks(unsigned amount)210 void IncreaseSelfTicks(unsigned amount) { self_ticks_ += amount; } 211 void IncrementLineTicks(int src_line); 212 entry()213 CodeEntry* entry() const { return entry_; } self_ticks()214 unsigned self_ticks() const { return self_ticks_; } children()215 const std::vector<ProfileNode*>* children() const { return &children_list_; } id()216 unsigned id() const { return id_; } 217 unsigned function_id() const; parent()218 ProfileNode* parent() const { return parent_; } line_number()219 int line_number() const { 220 return line_number_ != 0 ? line_number_ : entry_->line_number(); 221 } 222 GetHitLineCount()223 unsigned int GetHitLineCount() const { 224 return static_cast<unsigned int>(line_ticks_.size()); 225 } 226 bool GetLineTicks(v8::CpuProfileNode::LineTick* entries, 227 unsigned int length) const; 228 void CollectDeoptInfo(CodeEntry* entry); deopt_infos()229 const std::vector<CpuProfileDeoptInfo>& deopt_infos() const { 230 return deopt_infos_; 231 } 232 Isolate* isolate() const; 233 234 void Print(int indent); 235 236 private: 237 struct Equals { operatorEquals238 bool operator()(CodeEntryAndLineNumber lhs, 239 CodeEntryAndLineNumber rhs) const { 240 return lhs.code_entry->IsSameFunctionAs(rhs.code_entry) && 241 lhs.line_number == rhs.line_number; 242 } 243 }; 244 struct Hasher { operatorHasher245 std::size_t operator()(CodeEntryAndLineNumber pair) const { 246 return pair.code_entry->GetHash() ^ ComputeIntegerHash(pair.line_number); 247 } 248 }; 249 250 ProfileTree* tree_; 251 CodeEntry* entry_; 252 unsigned self_ticks_; 253 std::unordered_map<CodeEntryAndLineNumber, ProfileNode*, Hasher, Equals> 254 children_; 255 int line_number_; 256 std::vector<ProfileNode*> children_list_; 257 ProfileNode* parent_; 258 unsigned id_; 259 // maps line number --> number of ticks 260 std::unordered_map<int, int> line_ticks_; 261 262 std::vector<CpuProfileDeoptInfo> deopt_infos_; 263 264 DISALLOW_COPY_AND_ASSIGN(ProfileNode); 265 }; 266 267 class ProfileTree { 268 public: 269 explicit ProfileTree(Isolate* isolate); 270 ~ProfileTree(); 271 272 typedef v8::CpuProfilingMode ProfilingMode; 273 274 ProfileNode* AddPathFromEnd( 275 const std::vector<CodeEntry*>& path, 276 int src_line = v8::CpuProfileNode::kNoLineNumberInfo, 277 bool update_stats = true); 278 ProfileNode* AddPathFromEnd( 279 const ProfileStackTrace& path, 280 int src_line = v8::CpuProfileNode::kNoLineNumberInfo, 281 bool update_stats = true, 282 ProfilingMode mode = ProfilingMode::kLeafNodeLineNumbers); root()283 ProfileNode* root() const { return root_; } next_node_id()284 unsigned next_node_id() { return next_node_id_++; } 285 unsigned GetFunctionId(const ProfileNode* node); 286 Print()287 void Print() { 288 root_->Print(0); 289 } 290 isolate()291 Isolate* isolate() const { return isolate_; } 292 EnqueueNode(const ProfileNode * node)293 void EnqueueNode(const ProfileNode* node) { pending_nodes_.push_back(node); } pending_nodes_count()294 size_t pending_nodes_count() const { return pending_nodes_.size(); } TakePendingNodes()295 std::vector<const ProfileNode*> TakePendingNodes() { 296 return std::move(pending_nodes_); 297 } 298 299 private: 300 template <typename Callback> 301 void TraverseDepthFirst(Callback* callback); 302 303 std::vector<const ProfileNode*> pending_nodes_; 304 305 CodeEntry root_entry_; 306 unsigned next_node_id_; 307 ProfileNode* root_; 308 Isolate* isolate_; 309 310 unsigned next_function_id_; 311 std::unordered_map<CodeEntry*, unsigned> function_ids_; 312 313 DISALLOW_COPY_AND_ASSIGN(ProfileTree); 314 }; 315 316 317 class CpuProfile { 318 public: 319 typedef v8::CpuProfilingMode ProfilingMode; 320 321 CpuProfile(CpuProfiler* profiler, const char* title, bool record_samples, 322 ProfilingMode mode); 323 324 // Add pc -> ... -> main() call path to the profile. 325 void AddPath(base::TimeTicks timestamp, const ProfileStackTrace& path, 326 int src_line, bool update_stats); 327 void FinishProfile(); 328 title()329 const char* title() const { return title_; } top_down()330 const ProfileTree* top_down() const { return &top_down_; } 331 samples_count()332 int samples_count() const { return static_cast<int>(samples_.size()); } sample(int index)333 ProfileNode* sample(int index) const { return samples_.at(index); } sample_timestamp(int index)334 base::TimeTicks sample_timestamp(int index) const { 335 return timestamps_.at(index); 336 } 337 start_time()338 base::TimeTicks start_time() const { return start_time_; } end_time()339 base::TimeTicks end_time() const { return end_time_; } cpu_profiler()340 CpuProfiler* cpu_profiler() const { return profiler_; } 341 342 void UpdateTicksScale(); 343 344 void Print(); 345 346 private: 347 void StreamPendingTraceEvents(); 348 349 const char* title_; 350 bool record_samples_; 351 ProfilingMode mode_; 352 base::TimeTicks start_time_; 353 base::TimeTicks end_time_; 354 std::vector<ProfileNode*> samples_; 355 std::vector<base::TimeTicks> timestamps_; 356 ProfileTree top_down_; 357 CpuProfiler* const profiler_; 358 size_t streaming_next_sample_; 359 uint32_t id_; 360 361 static std::atomic<uint32_t> last_id_; 362 363 DISALLOW_COPY_AND_ASSIGN(CpuProfile); 364 }; 365 366 class CodeMap { 367 public: 368 CodeMap(); 369 ~CodeMap(); 370 371 void AddCode(Address addr, CodeEntry* entry, unsigned size); 372 void MoveCode(Address from, Address to); 373 CodeEntry* FindEntry(Address addr); 374 void Print(); 375 376 private: 377 struct CodeEntryMapInfo { 378 unsigned index; 379 unsigned size; 380 }; 381 382 union CodeEntrySlotInfo { 383 CodeEntry* entry; 384 unsigned next_free_slot; 385 }; 386 387 static constexpr unsigned kNoFreeSlot = std::numeric_limits<unsigned>::max(); 388 389 void ClearCodesInRange(Address start, Address end); 390 unsigned AddCodeEntry(Address start, CodeEntry*); 391 void DeleteCodeEntry(unsigned index); 392 entry(unsigned index)393 CodeEntry* entry(unsigned index) { return code_entries_[index].entry; } 394 395 std::deque<CodeEntrySlotInfo> code_entries_; 396 std::map<Address, CodeEntryMapInfo> code_map_; 397 unsigned free_list_head_ = kNoFreeSlot; 398 399 DISALLOW_COPY_AND_ASSIGN(CodeMap); 400 }; 401 402 class CpuProfilesCollection { 403 public: 404 explicit CpuProfilesCollection(Isolate* isolate); 405 406 typedef v8::CpuProfilingMode ProfilingMode; 407 set_cpu_profiler(CpuProfiler * profiler)408 void set_cpu_profiler(CpuProfiler* profiler) { profiler_ = profiler; } 409 bool StartProfiling(const char* title, bool record_samples, 410 ProfilingMode mode = ProfilingMode::kLeafNodeLineNumbers); 411 CpuProfile* StopProfiling(const char* title); profiles()412 std::vector<std::unique_ptr<CpuProfile>>* profiles() { 413 return &finished_profiles_; 414 } GetName(Name * name)415 const char* GetName(Name* name) { return resource_names_.GetName(name); } 416 bool IsLastProfile(const char* title); 417 void RemoveProfile(CpuProfile* profile); 418 419 // Called from profile generator thread. 420 void AddPathToCurrentProfiles(base::TimeTicks timestamp, 421 const ProfileStackTrace& path, int src_line, 422 bool update_stats); 423 424 // Limits the number of profiles that can be simultaneously collected. 425 static const int kMaxSimultaneousProfiles = 100; 426 427 private: 428 StringsStorage resource_names_; 429 std::vector<std::unique_ptr<CpuProfile>> finished_profiles_; 430 CpuProfiler* profiler_; 431 432 // Accessed by VM thread and profile generator thread. 433 std::vector<std::unique_ptr<CpuProfile>> current_profiles_; 434 base::Semaphore current_profiles_semaphore_; 435 436 DISALLOW_COPY_AND_ASSIGN(CpuProfilesCollection); 437 }; 438 439 class ProfileGenerator { 440 public: 441 explicit ProfileGenerator(CpuProfilesCollection* profiles); 442 443 void RecordTickSample(const TickSample& sample); 444 code_map()445 CodeMap* code_map() { return &code_map_; } 446 447 private: 448 CodeEntry* FindEntry(Address address); 449 CodeEntry* EntryForVMState(StateTag tag); 450 451 CpuProfilesCollection* profiles_; 452 CodeMap code_map_; 453 454 DISALLOW_COPY_AND_ASSIGN(ProfileGenerator); 455 }; 456 457 } // namespace internal 458 } // namespace v8 459 460 #endif // V8_PROFILER_PROFILE_GENERATOR_H_ 461