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 <map> 9 #include "src/allocation.h" 10 #include "src/base/hashmap.h" 11 #include "src/log.h" 12 #include "src/profiler/strings-storage.h" 13 #include "src/source-position.h" 14 15 namespace v8 { 16 namespace internal { 17 18 struct TickSample; 19 20 // Provides a mapping from the offsets within generated code to 21 // the source line. 22 class JITLineInfoTable : public Malloced { 23 public: 24 JITLineInfoTable(); 25 ~JITLineInfoTable(); 26 27 void SetPosition(int pc_offset, int line); 28 int GetSourceLineNumber(int pc_offset) const; 29 empty()30 bool empty() const { return pc_offset_map_.empty(); } 31 32 private: 33 // pc_offset -> source line 34 typedef std::map<int, int> PcOffsetMap; 35 PcOffsetMap pc_offset_map_; 36 DISALLOW_COPY_AND_ASSIGN(JITLineInfoTable); 37 }; 38 39 40 class CodeEntry { 41 public: 42 // CodeEntry doesn't own name strings, just references them. 43 inline CodeEntry(CodeEventListener::LogEventsAndTags tag, const char* name, 44 const char* name_prefix = CodeEntry::kEmptyNamePrefix, 45 const char* resource_name = CodeEntry::kEmptyResourceName, 46 int line_number = v8::CpuProfileNode::kNoLineNumberInfo, 47 int column_number = v8::CpuProfileNode::kNoColumnNumberInfo, 48 JITLineInfoTable* line_info = NULL, 49 Address instruction_start = NULL); 50 ~CodeEntry(); 51 name_prefix()52 const char* name_prefix() const { return name_prefix_; } has_name_prefix()53 bool has_name_prefix() const { return name_prefix_[0] != '\0'; } name()54 const char* name() const { return name_; } resource_name()55 const char* resource_name() const { return resource_name_; } line_number()56 int line_number() const { return line_number_; } column_number()57 int column_number() const { return column_number_; } line_info()58 const JITLineInfoTable* line_info() const { return line_info_; } script_id()59 int script_id() const { return script_id_; } set_script_id(int script_id)60 void set_script_id(int script_id) { script_id_ = script_id; } position()61 int position() const { return position_; } set_position(int position)62 void set_position(int position) { position_ = position; } set_bailout_reason(const char * bailout_reason)63 void set_bailout_reason(const char* bailout_reason) { 64 bailout_reason_ = bailout_reason; 65 } bailout_reason()66 const char* bailout_reason() const { return bailout_reason_; } 67 set_deopt_info(const char * deopt_reason,int deopt_id)68 void set_deopt_info(const char* deopt_reason, int deopt_id) { 69 DCHECK(!has_deopt_info()); 70 deopt_reason_ = deopt_reason; 71 deopt_id_ = deopt_id; 72 } 73 CpuProfileDeoptInfo GetDeoptInfo(); has_deopt_info()74 bool has_deopt_info() const { return deopt_id_ != kNoDeoptimizationId; } clear_deopt_info()75 void clear_deopt_info() { 76 deopt_reason_ = kNoDeoptReason; 77 deopt_id_ = kNoDeoptimizationId; 78 } 79 80 void FillFunctionInfo(SharedFunctionInfo* shared); 81 82 void SetBuiltinId(Builtins::Name id); builtin_id()83 Builtins::Name builtin_id() const { 84 return BuiltinIdField::decode(bit_field_); 85 } 86 87 uint32_t GetHash() const; 88 bool IsSameFunctionAs(CodeEntry* entry) const; 89 90 int GetSourceLine(int pc_offset) const; 91 92 void AddInlineStack(int pc_offset, std::vector<CodeEntry*> inline_stack); 93 const std::vector<CodeEntry*>* GetInlineStack(int pc_offset) const; 94 95 void AddDeoptInlinedFrames(int deopt_id, std::vector<CpuProfileDeoptFrame>); 96 bool HasDeoptInlinedFramesFor(int deopt_id) const; 97 instruction_start()98 Address instruction_start() const { return instruction_start_; } tag()99 CodeEventListener::LogEventsAndTags tag() const { 100 return TagField::decode(bit_field_); 101 } 102 103 static const char* const kEmptyNamePrefix; 104 static const char* const kEmptyResourceName; 105 static const char* const kEmptyBailoutReason; 106 static const char* const kNoDeoptReason; 107 108 static const char* const kProgramEntryName; 109 static const char* const kIdleEntryName; 110 static const char* const kGarbageCollectorEntryName; 111 // Used to represent frames for which we have no reliable way to 112 // detect function. 113 static const char* const kUnresolvedFunctionName; 114 program_entry()115 V8_INLINE static CodeEntry* program_entry() { 116 return kProgramEntry.Pointer(); 117 } idle_entry()118 V8_INLINE static CodeEntry* idle_entry() { return kIdleEntry.Pointer(); } gc_entry()119 V8_INLINE static CodeEntry* gc_entry() { return kGCEntry.Pointer(); } unresolved_entry()120 V8_INLINE static CodeEntry* unresolved_entry() { 121 return kUnresolvedEntry.Pointer(); 122 } 123 124 private: 125 struct ProgramEntryCreateTrait { 126 static CodeEntry* Create(); 127 }; 128 struct IdleEntryCreateTrait { 129 static CodeEntry* Create(); 130 }; 131 struct GCEntryCreateTrait { 132 static CodeEntry* Create(); 133 }; 134 struct UnresolvedEntryCreateTrait { 135 static CodeEntry* Create(); 136 }; 137 138 static base::LazyDynamicInstance<CodeEntry, ProgramEntryCreateTrait>::type 139 kProgramEntry; 140 static base::LazyDynamicInstance<CodeEntry, IdleEntryCreateTrait>::type 141 kIdleEntry; 142 static base::LazyDynamicInstance<CodeEntry, GCEntryCreateTrait>::type 143 kGCEntry; 144 static base::LazyDynamicInstance<CodeEntry, UnresolvedEntryCreateTrait>::type 145 kUnresolvedEntry; 146 147 class TagField : public BitField<Logger::LogEventsAndTags, 0, 8> {}; 148 class BuiltinIdField : public BitField<Builtins::Name, 8, 24> {}; 149 150 uint32_t bit_field_; 151 const char* name_prefix_; 152 const char* name_; 153 const char* resource_name_; 154 int line_number_; 155 int column_number_; 156 int script_id_; 157 int position_; 158 const char* bailout_reason_; 159 const char* deopt_reason_; 160 int deopt_id_; 161 JITLineInfoTable* line_info_; 162 Address instruction_start_; 163 // Should be an unordered_map, but it doesn't currently work on Win & MacOS. 164 std::map<int, std::vector<CodeEntry*>> inline_locations_; 165 std::map<int, std::vector<CpuProfileDeoptFrame>> deopt_inlined_frames_; 166 167 DISALLOW_COPY_AND_ASSIGN(CodeEntry); 168 }; 169 170 171 class ProfileTree; 172 173 class ProfileNode { 174 public: 175 inline ProfileNode(ProfileTree* tree, CodeEntry* entry, ProfileNode* parent); 176 177 ProfileNode* FindChild(CodeEntry* entry); 178 ProfileNode* FindOrAddChild(CodeEntry* entry); IncrementSelfTicks()179 void IncrementSelfTicks() { ++self_ticks_; } IncreaseSelfTicks(unsigned amount)180 void IncreaseSelfTicks(unsigned amount) { self_ticks_ += amount; } 181 void IncrementLineTicks(int src_line); 182 entry()183 CodeEntry* entry() const { return entry_; } self_ticks()184 unsigned self_ticks() const { return self_ticks_; } children()185 const List<ProfileNode*>* children() const { return &children_list_; } id()186 unsigned id() const { return id_; } 187 unsigned function_id() const; parent()188 ProfileNode* parent() const { return parent_; } GetHitLineCount()189 unsigned int GetHitLineCount() const { return line_ticks_.occupancy(); } 190 bool GetLineTicks(v8::CpuProfileNode::LineTick* entries, 191 unsigned int length) const; 192 void CollectDeoptInfo(CodeEntry* entry); deopt_infos()193 const std::vector<CpuProfileDeoptInfo>& deopt_infos() const { 194 return deopt_infos_; 195 } 196 Isolate* isolate() const; 197 198 void Print(int indent); 199 CodeEntriesMatch(void * entry1,void * entry2)200 static bool CodeEntriesMatch(void* entry1, void* entry2) { 201 return reinterpret_cast<CodeEntry*>(entry1) 202 ->IsSameFunctionAs(reinterpret_cast<CodeEntry*>(entry2)); 203 } 204 205 private: CodeEntryHash(CodeEntry * entry)206 static uint32_t CodeEntryHash(CodeEntry* entry) { return entry->GetHash(); } 207 LineTickMatch(void * a,void * b)208 static bool LineTickMatch(void* a, void* b) { return a == b; } 209 210 ProfileTree* tree_; 211 CodeEntry* entry_; 212 unsigned self_ticks_; 213 // Mapping from CodeEntry* to ProfileNode* 214 base::CustomMatcherHashMap children_; 215 List<ProfileNode*> children_list_; 216 ProfileNode* parent_; 217 unsigned id_; 218 base::CustomMatcherHashMap line_ticks_; 219 220 std::vector<CpuProfileDeoptInfo> deopt_infos_; 221 222 DISALLOW_COPY_AND_ASSIGN(ProfileNode); 223 }; 224 225 226 class ProfileTree { 227 public: 228 explicit ProfileTree(Isolate* isolate); 229 ~ProfileTree(); 230 231 ProfileNode* AddPathFromEnd( 232 const std::vector<CodeEntry*>& path, 233 int src_line = v8::CpuProfileNode::kNoLineNumberInfo, 234 bool update_stats = true); root()235 ProfileNode* root() const { return root_; } next_node_id()236 unsigned next_node_id() { return next_node_id_++; } 237 unsigned GetFunctionId(const ProfileNode* node); 238 Print()239 void Print() { 240 root_->Print(0); 241 } 242 isolate()243 Isolate* isolate() const { return isolate_; } 244 EnqueueNode(const ProfileNode * node)245 void EnqueueNode(const ProfileNode* node) { pending_nodes_.push_back(node); } pending_nodes_count()246 size_t pending_nodes_count() const { return pending_nodes_.size(); } TakePendingNodes()247 std::vector<const ProfileNode*> TakePendingNodes() { 248 return std::move(pending_nodes_); 249 } 250 251 private: 252 template <typename Callback> 253 void TraverseDepthFirst(Callback* callback); 254 255 std::vector<const ProfileNode*> pending_nodes_; 256 257 CodeEntry root_entry_; 258 unsigned next_node_id_; 259 ProfileNode* root_; 260 Isolate* isolate_; 261 262 unsigned next_function_id_; 263 base::CustomMatcherHashMap function_ids_; 264 265 DISALLOW_COPY_AND_ASSIGN(ProfileTree); 266 }; 267 268 269 class CpuProfile { 270 public: 271 CpuProfile(CpuProfiler* profiler, const char* title, bool record_samples); 272 273 // Add pc -> ... -> main() call path to the profile. 274 void AddPath(base::TimeTicks timestamp, const std::vector<CodeEntry*>& path, 275 int src_line, bool update_stats); 276 void FinishProfile(); 277 title()278 const char* title() const { return title_; } top_down()279 const ProfileTree* top_down() const { return &top_down_; } 280 samples_count()281 int samples_count() const { return samples_.length(); } sample(int index)282 ProfileNode* sample(int index) const { return samples_.at(index); } sample_timestamp(int index)283 base::TimeTicks sample_timestamp(int index) const { 284 return timestamps_.at(index); 285 } 286 start_time()287 base::TimeTicks start_time() const { return start_time_; } end_time()288 base::TimeTicks end_time() const { return end_time_; } cpu_profiler()289 CpuProfiler* cpu_profiler() const { return profiler_; } 290 291 void UpdateTicksScale(); 292 293 void Print(); 294 295 private: 296 void StreamPendingTraceEvents(); 297 298 const char* title_; 299 bool record_samples_; 300 base::TimeTicks start_time_; 301 base::TimeTicks end_time_; 302 List<ProfileNode*> samples_; 303 List<base::TimeTicks> timestamps_; 304 ProfileTree top_down_; 305 CpuProfiler* const profiler_; 306 int streaming_next_sample_; 307 308 DISALLOW_COPY_AND_ASSIGN(CpuProfile); 309 }; 310 311 class CodeMap { 312 public: CodeMap()313 CodeMap() {} 314 315 void AddCode(Address addr, CodeEntry* entry, unsigned size); 316 void MoveCode(Address from, Address to); 317 CodeEntry* FindEntry(Address addr); 318 void Print(); 319 320 private: 321 struct CodeEntryInfo { CodeEntryInfoCodeEntryInfo322 CodeEntryInfo(CodeEntry* an_entry, unsigned a_size) 323 : entry(an_entry), size(a_size) { } 324 CodeEntry* entry; 325 unsigned size; 326 }; 327 328 void DeleteAllCoveredCode(Address start, Address end); 329 330 std::map<Address, CodeEntryInfo> code_map_; 331 332 DISALLOW_COPY_AND_ASSIGN(CodeMap); 333 }; 334 335 class CpuProfilesCollection { 336 public: 337 explicit CpuProfilesCollection(Isolate* isolate); 338 ~CpuProfilesCollection(); 339 set_cpu_profiler(CpuProfiler * profiler)340 void set_cpu_profiler(CpuProfiler* profiler) { profiler_ = profiler; } 341 bool StartProfiling(const char* title, bool record_samples); 342 CpuProfile* StopProfiling(const char* title); profiles()343 List<CpuProfile*>* profiles() { return &finished_profiles_; } GetName(Name * name)344 const char* GetName(Name* name) { return resource_names_.GetName(name); } 345 bool IsLastProfile(const char* title); 346 void RemoveProfile(CpuProfile* profile); 347 348 // Called from profile generator thread. 349 void AddPathToCurrentProfiles(base::TimeTicks timestamp, 350 const std::vector<CodeEntry*>& path, 351 int src_line, bool update_stats); 352 353 // Limits the number of profiles that can be simultaneously collected. 354 static const int kMaxSimultaneousProfiles = 100; 355 356 private: 357 StringsStorage resource_names_; 358 List<CpuProfile*> finished_profiles_; 359 CpuProfiler* profiler_; 360 361 // Accessed by VM thread and profile generator thread. 362 List<CpuProfile*> current_profiles_; 363 base::Semaphore current_profiles_semaphore_; 364 365 DISALLOW_COPY_AND_ASSIGN(CpuProfilesCollection); 366 }; 367 368 369 class ProfileGenerator { 370 public: 371 explicit ProfileGenerator(CpuProfilesCollection* profiles); 372 373 void RecordTickSample(const TickSample& sample); 374 code_map()375 CodeMap* code_map() { return &code_map_; } 376 377 private: 378 CodeEntry* FindEntry(void* address); 379 CodeEntry* EntryForVMState(StateTag tag); 380 381 CpuProfilesCollection* profiles_; 382 CodeMap code_map_; 383 384 DISALLOW_COPY_AND_ASSIGN(ProfileGenerator); 385 }; 386 387 388 } // namespace internal 389 } // namespace v8 390 391 #endif // V8_PROFILER_PROFILE_GENERATOR_H_ 392