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 StringsStorage(); 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 inline const char* GetFunctionName(Name* name); 53 inline const char* GetFunctionName(const char* name); 54 size_t GetUsedMemorySize() const; 55 56 private: 57 static const int kMaxNameSize = 1024; 58 59 INLINE(static bool StringsMatch(void* key1, void* key2)) { 60 return strcmp(reinterpret_cast<char*>(key1), 61 reinterpret_cast<char*>(key2)) == 0; 62 } 63 const char* AddOrDisposeString(char* str, uint32_t hash); 64 65 // Mapping of strings by String::Hash to const char* strings. 66 HashMap names_; 67 68 DISALLOW_COPY_AND_ASSIGN(StringsStorage); 69 }; 70 71 72 class CodeEntry { 73 public: 74 // CodeEntry doesn't own name strings, just references them. 75 INLINE(CodeEntry(Logger::LogEventsAndTags tag, 76 const char* name, 77 const char* name_prefix = CodeEntry::kEmptyNamePrefix, 78 const char* resource_name = CodeEntry::kEmptyResourceName, 79 int line_number = v8::CpuProfileNode::kNoLineNumberInfo)); 80 ~CodeEntry(); 81 82 INLINE(bool is_js_function() const) { return is_js_function_tag(tag_); } 83 INLINE(const char* name_prefix() const) { return name_prefix_; } 84 INLINE(bool has_name_prefix() const) { return name_prefix_[0] != '\0'; } 85 INLINE(const char* name() const) { return name_; } 86 INLINE(const char* resource_name() const) { return resource_name_; } 87 INLINE(int line_number() const) { return line_number_; } 88 INLINE(void set_shared_id(int shared_id)) { shared_id_ = shared_id; } 89 INLINE(int script_id() const) { return script_id_; } 90 INLINE(void set_script_id(int script_id)) { script_id_ = script_id; } 91 92 INLINE(static bool is_js_function_tag(Logger::LogEventsAndTags tag)); 93 94 List<OffsetRange>* no_frame_ranges() const { return no_frame_ranges_; } 95 void set_no_frame_ranges(List<OffsetRange>* ranges) { 96 no_frame_ranges_ = ranges; 97 } 98 99 void SetBuiltinId(Builtins::Name id); 100 Builtins::Name builtin_id() const { return builtin_id_; } 101 102 void CopyData(const CodeEntry& source); 103 uint32_t GetCallUid() const; 104 bool IsSameAs(CodeEntry* entry) const; 105 106 static const char* const kEmptyNamePrefix; 107 static const char* const kEmptyResourceName; 108 109 private: 110 Logger::LogEventsAndTags tag_ : 8; 111 Builtins::Name builtin_id_ : 8; 112 const char* name_prefix_; 113 const char* name_; 114 const char* resource_name_; 115 int line_number_; 116 int shared_id_; 117 int script_id_; 118 List<OffsetRange>* no_frame_ranges_; 119 120 DISALLOW_COPY_AND_ASSIGN(CodeEntry); 121 }; 122 123 124 class ProfileTree; 125 126 class ProfileNode { 127 public: 128 INLINE(ProfileNode(ProfileTree* tree, CodeEntry* entry)); 129 130 ProfileNode* FindChild(CodeEntry* entry); 131 ProfileNode* FindOrAddChild(CodeEntry* entry); 132 INLINE(void IncrementSelfTicks()) { ++self_ticks_; } 133 INLINE(void IncreaseSelfTicks(unsigned amount)) { self_ticks_ += amount; } 134 INLINE(void IncreaseTotalTicks(unsigned amount)) { total_ticks_ += amount; } 135 136 INLINE(CodeEntry* entry() const) { return entry_; } 137 INLINE(unsigned self_ticks() const) { return self_ticks_; } 138 INLINE(unsigned total_ticks() const) { return total_ticks_; } 139 INLINE(const List<ProfileNode*>* children() const) { return &children_list_; } 140 double GetSelfMillis() const; 141 double GetTotalMillis() const; 142 unsigned id() const { return id_; } 143 144 void Print(int indent); 145 146 private: 147 INLINE(static bool CodeEntriesMatch(void* entry1, void* entry2)) { 148 return reinterpret_cast<CodeEntry*>(entry1)->IsSameAs( 149 reinterpret_cast<CodeEntry*>(entry2)); 150 } 151 152 INLINE(static uint32_t CodeEntryHash(CodeEntry* entry)) { 153 return entry->GetCallUid(); 154 } 155 156 ProfileTree* tree_; 157 CodeEntry* entry_; 158 unsigned total_ticks_; 159 unsigned self_ticks_; 160 // Mapping from CodeEntry* to ProfileNode* 161 HashMap children_; 162 List<ProfileNode*> children_list_; 163 unsigned id_; 164 165 DISALLOW_COPY_AND_ASSIGN(ProfileNode); 166 }; 167 168 169 class ProfileTree { 170 public: 171 ProfileTree(); 172 ~ProfileTree(); 173 174 ProfileNode* AddPathFromEnd(const Vector<CodeEntry*>& path); 175 void AddPathFromStart(const Vector<CodeEntry*>& path); 176 void CalculateTotalTicks(); 177 178 double TicksToMillis(unsigned ticks) const { 179 return ticks * ms_to_ticks_scale_; 180 } 181 ProfileNode* root() const { return root_; } 182 void SetTickRatePerMs(double ticks_per_ms); 183 184 unsigned next_node_id() { return next_node_id_++; } 185 186 void ShortPrint(); 187 void Print() { 188 root_->Print(0); 189 } 190 191 private: 192 template <typename Callback> 193 void TraverseDepthFirst(Callback* callback); 194 195 CodeEntry root_entry_; 196 unsigned next_node_id_; 197 ProfileNode* root_; 198 double ms_to_ticks_scale_; 199 200 DISALLOW_COPY_AND_ASSIGN(ProfileTree); 201 }; 202 203 204 class CpuProfile { 205 public: 206 CpuProfile(const char* title, unsigned uid, bool record_samples); 207 208 // Add pc -> ... -> main() call path to the profile. 209 void AddPath(const Vector<CodeEntry*>& path); 210 void CalculateTotalTicksAndSamplingRate(); 211 212 const char* title() const { return title_; } 213 unsigned uid() const { return uid_; } 214 const ProfileTree* top_down() const { return &top_down_; } 215 216 int samples_count() const { return samples_.length(); } 217 ProfileNode* sample(int index) const { return samples_.at(index); } 218 219 int64_t start_time_us() const { return start_time_us_; } 220 int64_t end_time_us() const { return end_time_us_; } 221 222 void UpdateTicksScale(); 223 224 void ShortPrint(); 225 void Print(); 226 227 private: 228 const char* title_; 229 unsigned uid_; 230 bool record_samples_; 231 int64_t start_time_us_; 232 int64_t end_time_us_; 233 List<ProfileNode*> samples_; 234 ProfileTree top_down_; 235 236 DISALLOW_COPY_AND_ASSIGN(CpuProfile); 237 }; 238 239 240 class CodeMap { 241 public: 242 CodeMap() : next_shared_id_(1) { } 243 void AddCode(Address addr, CodeEntry* entry, unsigned size); 244 void MoveCode(Address from, Address to); 245 CodeEntry* FindEntry(Address addr, Address* start = NULL); 246 int GetSharedId(Address addr); 247 248 void Print(); 249 250 private: 251 struct CodeEntryInfo { 252 CodeEntryInfo(CodeEntry* an_entry, unsigned a_size) 253 : entry(an_entry), size(a_size) { } 254 CodeEntry* entry; 255 unsigned size; 256 }; 257 258 struct CodeTreeConfig { 259 typedef Address Key; 260 typedef CodeEntryInfo Value; 261 static const Key kNoKey; 262 static const Value NoValue() { return CodeEntryInfo(NULL, 0); } 263 static int Compare(const Key& a, const Key& b) { 264 return a < b ? -1 : (a > b ? 1 : 0); 265 } 266 }; 267 typedef SplayTree<CodeTreeConfig> CodeTree; 268 269 class CodeTreePrinter { 270 public: 271 void Call(const Address& key, const CodeEntryInfo& value); 272 }; 273 274 void DeleteAllCoveredCode(Address start, Address end); 275 276 // Fake CodeEntry pointer to distinguish shared function entries. 277 static CodeEntry* const kSharedFunctionCodeEntry; 278 279 CodeTree tree_; 280 int next_shared_id_; 281 282 DISALLOW_COPY_AND_ASSIGN(CodeMap); 283 }; 284 285 286 class CpuProfilesCollection { 287 public: 288 CpuProfilesCollection(); 289 ~CpuProfilesCollection(); 290 291 bool StartProfiling(const char* title, unsigned uid, bool record_samples); 292 CpuProfile* StopProfiling(const char* title); 293 List<CpuProfile*>* profiles() { return &finished_profiles_; } 294 const char* GetName(Name* name) { 295 return function_and_resource_names_.GetName(name); 296 } 297 const char* GetName(int args_count) { 298 return function_and_resource_names_.GetName(args_count); 299 } 300 const char* GetFunctionName(Name* name) { 301 return function_and_resource_names_.GetFunctionName(name); 302 } 303 const char* GetFunctionName(const char* name) { 304 return function_and_resource_names_.GetFunctionName(name); 305 } 306 bool IsLastProfile(const char* title); 307 void RemoveProfile(CpuProfile* profile); 308 309 CodeEntry* NewCodeEntry( 310 Logger::LogEventsAndTags tag, 311 const char* name, 312 const char* name_prefix = CodeEntry::kEmptyNamePrefix, 313 const char* resource_name = CodeEntry::kEmptyResourceName, 314 int line_number = v8::CpuProfileNode::kNoLineNumberInfo); 315 316 // Called from profile generator thread. 317 void AddPathToCurrentProfiles(const Vector<CodeEntry*>& path); 318 319 // Limits the number of profiles that can be simultaneously collected. 320 static const int kMaxSimultaneousProfiles = 100; 321 322 private: 323 StringsStorage function_and_resource_names_; 324 List<CodeEntry*> code_entries_; 325 List<CpuProfile*> finished_profiles_; 326 327 // Accessed by VM thread and profile generator thread. 328 List<CpuProfile*> current_profiles_; 329 Semaphore* current_profiles_semaphore_; 330 331 DISALLOW_COPY_AND_ASSIGN(CpuProfilesCollection); 332 }; 333 334 335 class ProfileGenerator { 336 public: 337 explicit ProfileGenerator(CpuProfilesCollection* profiles); 338 339 void RecordTickSample(const TickSample& sample); 340 341 INLINE(CodeMap* code_map()) { return &code_map_; } 342 343 static const char* const kAnonymousFunctionName; 344 static const char* const kProgramEntryName; 345 static const char* const kIdleEntryName; 346 static const char* const kGarbageCollectorEntryName; 347 // Used to represent frames for which we have no reliable way to 348 // detect function. 349 static const char* const kUnresolvedFunctionName; 350 351 private: 352 INLINE(CodeEntry* EntryForVMState(StateTag tag)); 353 354 CpuProfilesCollection* profiles_; 355 CodeMap code_map_; 356 CodeEntry* program_entry_; 357 CodeEntry* idle_entry_; 358 CodeEntry* gc_entry_; 359 CodeEntry* unresolved_entry_; 360 361 DISALLOW_COPY_AND_ASSIGN(ProfileGenerator); 362 }; 363 364 365 } } // namespace v8::internal 366 367 #endif // V8_PROFILE_GENERATOR_H_ 368