1 // Copyright 2011 the V8 project authors. All rights reserved. 2 // Redistribution and use in source and binary forms, with or without 3 // modification, are permitted provided that the following conditions are 4 // met: 5 // 6 // * Redistributions of source code must retain the above copyright 7 // notice, this list of conditions and the following disclaimer. 8 // * Redistributions in binary form must reproduce the above 9 // copyright notice, this list of conditions and the following 10 // disclaimer in the documentation and/or other materials provided 11 // with the distribution. 12 // * Neither the name of Google Inc. nor the names of its 13 // contributors may be used to endorse or promote products derived 14 // from this software without specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28 #ifndef V8_PROFILE_GENERATOR_H_ 29 #define V8_PROFILE_GENERATOR_H_ 30 31 #include "allocation.h" 32 #include "hashmap.h" 33 #include "../include/v8-profiler.h" 34 35 namespace v8 { 36 namespace internal { 37 38 struct OffsetRange; 39 40 // Provides a storage of strings allocated in C++ heap, to hold them 41 // forever, even if they disappear from JS heap or external storage. 42 class StringsStorage { 43 public: 44 explicit StringsStorage(Heap* heap); 45 ~StringsStorage(); 46 47 const char* GetCopy(const char* src); 48 const char* GetFormatted(const char* format, ...); 49 const char* GetVFormatted(const char* format, va_list args); 50 const char* GetName(Name* name); 51 const char* GetName(int index); 52 const char* GetFunctionName(Name* name); 53 const char* GetFunctionName(const char* name); 54 size_t GetUsedMemorySize() const; 55 56 private: 57 static const int kMaxNameSize = 1024; 58 59 static bool StringsMatch(void* key1, void* key2); 60 const char* BeautifyFunctionName(const char* name); 61 const char* AddOrDisposeString(char* str, int len); 62 HashMap::Entry* GetEntry(const char* str, int len); 63 64 uint32_t hash_seed_; 65 HashMap names_; 66 67 DISALLOW_COPY_AND_ASSIGN(StringsStorage); 68 }; 69 70 71 class CodeEntry { 72 public: 73 // CodeEntry doesn't own name strings, just references them. 74 inline CodeEntry(Logger::LogEventsAndTags tag, 75 const char* name, 76 const char* name_prefix = CodeEntry::kEmptyNamePrefix, 77 const char* resource_name = CodeEntry::kEmptyResourceName, 78 int line_number = v8::CpuProfileNode::kNoLineNumberInfo, 79 int column_number = v8::CpuProfileNode::kNoColumnNumberInfo); 80 ~CodeEntry(); 81 82 bool is_js_function() const { return is_js_function_tag(tag_); } 83 const char* name_prefix() const { return name_prefix_; } 84 bool has_name_prefix() const { return name_prefix_[0] != '\0'; } 85 const char* name() const { return name_; } 86 const char* resource_name() const { return resource_name_; } 87 int line_number() const { return line_number_; } 88 int column_number() const { return column_number_; } 89 void set_shared_id(int shared_id) { shared_id_ = shared_id; } 90 int script_id() const { return script_id_; } 91 void set_script_id(int script_id) { script_id_ = script_id; } 92 void set_bailout_reason(const char* bailout_reason) { 93 bailout_reason_ = bailout_reason; 94 } 95 const char* bailout_reason() const { return bailout_reason_; } 96 97 static inline bool is_js_function_tag(Logger::LogEventsAndTags tag); 98 99 List<OffsetRange>* no_frame_ranges() const { return no_frame_ranges_; } 100 void set_no_frame_ranges(List<OffsetRange>* ranges) { 101 no_frame_ranges_ = ranges; 102 } 103 104 void SetBuiltinId(Builtins::Name id); 105 Builtins::Name builtin_id() const { return builtin_id_; } 106 107 uint32_t GetCallUid() const; 108 bool IsSameAs(CodeEntry* entry) const; 109 110 static const char* const kEmptyNamePrefix; 111 static const char* const kEmptyResourceName; 112 static const char* const kEmptyBailoutReason; 113 114 private: 115 Logger::LogEventsAndTags tag_ : 8; 116 Builtins::Name builtin_id_ : 8; 117 const char* name_prefix_; 118 const char* name_; 119 const char* resource_name_; 120 int line_number_; 121 int column_number_; 122 int shared_id_; 123 int script_id_; 124 List<OffsetRange>* no_frame_ranges_; 125 const char* bailout_reason_; 126 127 DISALLOW_COPY_AND_ASSIGN(CodeEntry); 128 }; 129 130 131 class ProfileTree; 132 133 class ProfileNode { 134 public: 135 inline ProfileNode(ProfileTree* tree, CodeEntry* entry); 136 137 ProfileNode* FindChild(CodeEntry* entry); 138 ProfileNode* FindOrAddChild(CodeEntry* entry); 139 void IncrementSelfTicks() { ++self_ticks_; } 140 void IncreaseSelfTicks(unsigned amount) { self_ticks_ += amount; } 141 142 CodeEntry* entry() const { return entry_; } 143 unsigned self_ticks() const { return self_ticks_; } 144 const List<ProfileNode*>* children() const { return &children_list_; } 145 unsigned id() const { return id_; } 146 147 void Print(int indent); 148 149 private: 150 static bool CodeEntriesMatch(void* entry1, void* entry2) { 151 return reinterpret_cast<CodeEntry*>(entry1)->IsSameAs( 152 reinterpret_cast<CodeEntry*>(entry2)); 153 } 154 155 static uint32_t CodeEntryHash(CodeEntry* entry) { 156 return entry->GetCallUid(); 157 } 158 159 ProfileTree* tree_; 160 CodeEntry* entry_; 161 unsigned self_ticks_; 162 // Mapping from CodeEntry* to ProfileNode* 163 HashMap children_; 164 List<ProfileNode*> children_list_; 165 unsigned id_; 166 167 DISALLOW_COPY_AND_ASSIGN(ProfileNode); 168 }; 169 170 171 class ProfileTree { 172 public: 173 ProfileTree(); 174 ~ProfileTree(); 175 176 ProfileNode* AddPathFromEnd(const Vector<CodeEntry*>& path); 177 void AddPathFromStart(const Vector<CodeEntry*>& path); 178 ProfileNode* root() const { return root_; } 179 unsigned next_node_id() { return next_node_id_++; } 180 181 void Print() { 182 root_->Print(0); 183 } 184 185 private: 186 template <typename Callback> 187 void TraverseDepthFirst(Callback* callback); 188 189 CodeEntry root_entry_; 190 unsigned next_node_id_; 191 ProfileNode* root_; 192 193 DISALLOW_COPY_AND_ASSIGN(ProfileTree); 194 }; 195 196 197 class CpuProfile { 198 public: 199 CpuProfile(const char* title, unsigned uid, bool record_samples); 200 201 // Add pc -> ... -> main() call path to the profile. 202 void AddPath(const Vector<CodeEntry*>& path); 203 void CalculateTotalTicksAndSamplingRate(); 204 205 const char* title() const { return title_; } 206 unsigned uid() const { return uid_; } 207 const ProfileTree* top_down() const { return &top_down_; } 208 209 int samples_count() const { return samples_.length(); } 210 ProfileNode* sample(int index) const { return samples_.at(index); } 211 212 Time start_time() const { return start_time_; } 213 Time end_time() const { return end_time_; } 214 215 void UpdateTicksScale(); 216 217 void Print(); 218 219 private: 220 const char* title_; 221 unsigned uid_; 222 bool record_samples_; 223 Time start_time_; 224 Time end_time_; 225 ElapsedTimer timer_; 226 List<ProfileNode*> samples_; 227 ProfileTree top_down_; 228 229 DISALLOW_COPY_AND_ASSIGN(CpuProfile); 230 }; 231 232 233 class CodeMap { 234 public: 235 CodeMap() : next_shared_id_(1) { } 236 void AddCode(Address addr, CodeEntry* entry, unsigned size); 237 void MoveCode(Address from, Address to); 238 CodeEntry* FindEntry(Address addr, Address* start = NULL); 239 int GetSharedId(Address addr); 240 241 void Print(); 242 243 private: 244 struct CodeEntryInfo { 245 CodeEntryInfo(CodeEntry* an_entry, unsigned a_size) 246 : entry(an_entry), size(a_size) { } 247 CodeEntry* entry; 248 unsigned size; 249 }; 250 251 struct CodeTreeConfig { 252 typedef Address Key; 253 typedef CodeEntryInfo Value; 254 static const Key kNoKey; 255 static const Value NoValue() { return CodeEntryInfo(NULL, 0); } 256 static int Compare(const Key& a, const Key& b) { 257 return a < b ? -1 : (a > b ? 1 : 0); 258 } 259 }; 260 typedef SplayTree<CodeTreeConfig> CodeTree; 261 262 class CodeTreePrinter { 263 public: 264 void Call(const Address& key, const CodeEntryInfo& value); 265 }; 266 267 void DeleteAllCoveredCode(Address start, Address end); 268 269 // Fake CodeEntry pointer to distinguish shared function entries. 270 static CodeEntry* const kSharedFunctionCodeEntry; 271 272 CodeTree tree_; 273 int next_shared_id_; 274 275 DISALLOW_COPY_AND_ASSIGN(CodeMap); 276 }; 277 278 279 class CpuProfilesCollection { 280 public: 281 explicit CpuProfilesCollection(Heap* heap); 282 ~CpuProfilesCollection(); 283 284 bool StartProfiling(const char* title, unsigned uid, bool record_samples); 285 CpuProfile* StopProfiling(const char* title); 286 List<CpuProfile*>* profiles() { return &finished_profiles_; } 287 const char* GetName(Name* name) { 288 return function_and_resource_names_.GetName(name); 289 } 290 const char* GetName(int args_count) { 291 return function_and_resource_names_.GetName(args_count); 292 } 293 const char* GetFunctionName(Name* name) { 294 return function_and_resource_names_.GetFunctionName(name); 295 } 296 const char* GetFunctionName(const char* name) { 297 return function_and_resource_names_.GetFunctionName(name); 298 } 299 bool IsLastProfile(const char* title); 300 void RemoveProfile(CpuProfile* profile); 301 302 CodeEntry* NewCodeEntry( 303 Logger::LogEventsAndTags tag, 304 const char* name, 305 const char* name_prefix = CodeEntry::kEmptyNamePrefix, 306 const char* resource_name = CodeEntry::kEmptyResourceName, 307 int line_number = v8::CpuProfileNode::kNoLineNumberInfo, 308 int column_number = v8::CpuProfileNode::kNoColumnNumberInfo); 309 310 // Called from profile generator thread. 311 void AddPathToCurrentProfiles(const Vector<CodeEntry*>& path); 312 313 // Limits the number of profiles that can be simultaneously collected. 314 static const int kMaxSimultaneousProfiles = 100; 315 316 private: 317 StringsStorage function_and_resource_names_; 318 List<CodeEntry*> code_entries_; 319 List<CpuProfile*> finished_profiles_; 320 321 // Accessed by VM thread and profile generator thread. 322 List<CpuProfile*> current_profiles_; 323 Semaphore current_profiles_semaphore_; 324 325 DISALLOW_COPY_AND_ASSIGN(CpuProfilesCollection); 326 }; 327 328 329 class ProfileGenerator { 330 public: 331 explicit ProfileGenerator(CpuProfilesCollection* profiles); 332 333 void RecordTickSample(const TickSample& sample); 334 335 CodeMap* code_map() { return &code_map_; } 336 337 static const char* const kAnonymousFunctionName; 338 static const char* const kProgramEntryName; 339 static const char* const kIdleEntryName; 340 static const char* const kGarbageCollectorEntryName; 341 // Used to represent frames for which we have no reliable way to 342 // detect function. 343 static const char* const kUnresolvedFunctionName; 344 345 private: 346 CodeEntry* EntryForVMState(StateTag tag); 347 348 CpuProfilesCollection* profiles_; 349 CodeMap code_map_; 350 CodeEntry* program_entry_; 351 CodeEntry* idle_entry_; 352 CodeEntry* gc_entry_; 353 CodeEntry* unresolved_entry_; 354 355 DISALLOW_COPY_AND_ASSIGN(ProfileGenerator); 356 }; 357 358 359 } } // namespace v8::internal 360 361 #endif // V8_PROFILE_GENERATOR_H_ 362