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