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/base/platform/time.h" 19 #include "src/builtins/builtins.h" 20 #include "src/execution/vm-state.h" 21 #include "src/logging/code-events.h" 22 #include "src/profiler/output-stream-writer.h" 23 #include "src/profiler/strings-storage.h" 24 #include "src/utils/allocation.h" 25 26 namespace v8 { 27 namespace internal { 28 29 struct TickSample; 30 31 // Provides a mapping from the offsets within generated code or a bytecode array 32 // to the source line and inlining id. 33 class V8_EXPORT_PRIVATE SourcePositionTable : public Malloced { 34 public: 35 SourcePositionTable() = default; 36 SourcePositionTable(const SourcePositionTable&) = delete; 37 SourcePositionTable& operator=(const SourcePositionTable&) = delete; 38 39 void SetPosition(int pc_offset, int line, int inlining_id); 40 int GetSourceLineNumber(int pc_offset) const; 41 int GetInliningId(int pc_offset) const; 42 43 size_t Size() const; 44 void print() const; 45 46 private: 47 struct SourcePositionTuple { 48 bool operator<(const SourcePositionTuple& other) const { 49 return pc_offset < other.pc_offset; 50 } 51 int pc_offset; 52 int line_number; 53 int inlining_id; 54 }; 55 // This is logically a map, but we store it as a vector of tuples, sorted by 56 // the pc offset, so that we can save space and look up items using binary 57 // search. 58 std::vector<SourcePositionTuple> pc_offsets_to_lines_; 59 }; 60 61 struct CodeEntryAndLineNumber; 62 63 class CodeEntry { 64 public: 65 enum class CodeType { JS, WASM, OTHER }; 66 67 // CodeEntry may reference strings (|name|, |resource_name|) managed by a 68 // StringsStorage instance. These must be freed via ReleaseStrings. 69 inline CodeEntry(CodeEventListener::LogEventsAndTags tag, const char* name, 70 const char* resource_name = CodeEntry::kEmptyResourceName, 71 int line_number = v8::CpuProfileNode::kNoLineNumberInfo, 72 int column_number = v8::CpuProfileNode::kNoColumnNumberInfo, 73 std::unique_ptr<SourcePositionTable> line_info = nullptr, 74 bool is_shared_cross_origin = false, 75 CodeType code_type = CodeType::JS); 76 CodeEntry(const CodeEntry&) = delete; 77 CodeEntry& operator=(const CodeEntry&) = delete; ~CodeEntry()78 ~CodeEntry() { 79 // No alive handles should be associated with the CodeEntry at time of 80 // destruction. 81 DCHECK(!heap_object_location_); 82 DCHECK_EQ(ref_count_, 0UL); 83 } 84 name()85 const char* name() const { return name_; } resource_name()86 const char* resource_name() const { return resource_name_; } line_number()87 int line_number() const { return line_number_; } column_number()88 int column_number() const { return column_number_; } line_info()89 const SourcePositionTable* line_info() const { return line_info_.get(); } script_id()90 int script_id() const { return script_id_; } set_script_id(int script_id)91 void set_script_id(int script_id) { script_id_ = script_id; } position()92 int position() const { return position_; } set_position(int position)93 void set_position(int position) { position_ = position; } set_bailout_reason(const char * bailout_reason)94 void set_bailout_reason(const char* bailout_reason) { 95 EnsureRareData()->bailout_reason_ = bailout_reason; 96 } bailout_reason()97 const char* bailout_reason() const { 98 return rare_data_ ? rare_data_->bailout_reason_ : kEmptyBailoutReason; 99 } 100 101 void set_deopt_info(const char* deopt_reason, int deopt_id, 102 std::vector<CpuProfileDeoptFrame> inlined_frames); 103 104 size_t EstimatedSize() const; 105 CpuProfileDeoptInfo GetDeoptInfo(); has_deopt_info()106 bool has_deopt_info() const { 107 return rare_data_ && rare_data_->deopt_id_ != kNoDeoptimizationId; 108 } clear_deopt_info()109 void clear_deopt_info() { 110 if (!rare_data_) return; 111 // TODO(alph): Clear rare_data_ if that was the only field in use. 112 rare_data_->deopt_reason_ = kNoDeoptReason; 113 rare_data_->deopt_id_ = kNoDeoptimizationId; 114 } 115 code_type_string()116 const char* code_type_string() const { 117 switch (CodeTypeField::decode(bit_field_)) { 118 case CodeType::JS: 119 return "JS"; 120 case CodeType::WASM: 121 return "wasm"; 122 case CodeType::OTHER: 123 return "other"; 124 } 125 } 126 127 // Returns the start address of the instruction segment represented by this 128 // CodeEntry. Used as a key in the containing CodeMap. instruction_start()129 Address instruction_start() const { return instruction_start_; } set_instruction_start(Address address)130 void set_instruction_start(Address address) { instruction_start_ = address; } 131 heap_object_location_address()132 Address** heap_object_location_address() { return &heap_object_location_; } 133 134 void FillFunctionInfo(SharedFunctionInfo shared); 135 136 void SetBuiltinId(Builtin id); builtin()137 Builtin builtin() const { return BuiltinField::decode(bit_field_); } 138 is_shared_cross_origin()139 bool is_shared_cross_origin() const { 140 return SharedCrossOriginField::decode(bit_field_); 141 } 142 143 // Returns whether or not the lifetime of this CodeEntry is reference 144 // counted, and managed by a CodeMap. is_ref_counted()145 bool is_ref_counted() const { return RefCountedField::decode(bit_field_); } 146 147 uint32_t GetHash() const; 148 bool IsSameFunctionAs(const CodeEntry* entry) const; 149 150 int GetSourceLine(int pc_offset) const; 151 152 struct Equals { operatorEquals153 bool operator()(const CodeEntry* lhs, const CodeEntry* rhs) const { 154 return lhs->IsSameFunctionAs(rhs); 155 } 156 }; 157 struct Hasher { operatorHasher158 std::size_t operator()(CodeEntry* e) const { return e->GetHash(); } 159 }; 160 161 void SetInlineStacks( 162 std::unordered_set<CodeEntry*, Hasher, Equals> inline_entries, 163 std::unordered_map<int, std::vector<CodeEntryAndLineNumber>> 164 inline_stacks); 165 const std::vector<CodeEntryAndLineNumber>* GetInlineStack( 166 int pc_offset) const; 167 tag()168 CodeEventListener::LogEventsAndTags tag() const { 169 return TagField::decode(bit_field_); 170 } 171 172 V8_EXPORT_PRIVATE static const char* const kEmptyResourceName; 173 static const char* const kEmptyBailoutReason; 174 static const char* const kNoDeoptReason; 175 176 V8_EXPORT_PRIVATE static const char* const kProgramEntryName; 177 V8_EXPORT_PRIVATE static const char* const kIdleEntryName; 178 V8_EXPORT_PRIVATE static const char* const kGarbageCollectorEntryName; 179 // Used to represent frames for which we have no reliable way to 180 // detect function. 181 V8_EXPORT_PRIVATE static const char* const kUnresolvedFunctionName; 182 V8_EXPORT_PRIVATE static const char* const kRootEntryName; 183 184 V8_EXPORT_PRIVATE static CodeEntry* program_entry(); 185 V8_EXPORT_PRIVATE static CodeEntry* idle_entry(); 186 V8_EXPORT_PRIVATE static CodeEntry* gc_entry(); 187 V8_EXPORT_PRIVATE static CodeEntry* unresolved_entry(); 188 V8_EXPORT_PRIVATE static CodeEntry* root_entry(); 189 190 // Releases strings owned by this CodeEntry, which may be allocated in the 191 // provided StringsStorage instance. This instance is not stored directly 192 // with the CodeEntry in order to reduce memory footprint. 193 // Called before every destruction. 194 void ReleaseStrings(StringsStorage& strings); 195 196 void print() const; 197 198 private: 199 friend class CodeEntryStorage; 200 201 struct RareData { 202 const char* deopt_reason_ = kNoDeoptReason; 203 const char* bailout_reason_ = kEmptyBailoutReason; 204 int deopt_id_ = kNoDeoptimizationId; 205 std::unordered_map<int, std::vector<CodeEntryAndLineNumber>> inline_stacks_; 206 std::unordered_set<CodeEntry*, Hasher, Equals> inline_entries_; 207 std::vector<CpuProfileDeoptFrame> deopt_inlined_frames_; 208 }; 209 210 RareData* EnsureRareData(); 211 mark_ref_counted()212 void mark_ref_counted() { 213 bit_field_ = RefCountedField::update(bit_field_, true); 214 ref_count_ = 1; 215 } 216 AddRef()217 size_t AddRef() { 218 DCHECK(is_ref_counted()); 219 DCHECK_LT(ref_count_, std::numeric_limits<size_t>::max()); 220 ref_count_++; 221 return ref_count_; 222 } 223 DecRef()224 size_t DecRef() { 225 DCHECK(is_ref_counted()); 226 DCHECK_GT(ref_count_, 0UL); 227 ref_count_--; 228 return ref_count_; 229 } 230 231 using TagField = base::BitField<CodeEventListener::LogEventsAndTags, 0, 8>; 232 using BuiltinField = base::BitField<Builtin, 8, 20>; 233 static_assert(Builtins::kBuiltinCount <= BuiltinField::kNumValues, 234 "builtin_count exceeds size of bitfield"); 235 using RefCountedField = base::BitField<bool, 28, 1>; 236 using CodeTypeField = base::BitField<CodeType, 29, 2>; 237 using SharedCrossOriginField = base::BitField<bool, 31, 1>; 238 239 std::uint32_t bit_field_; 240 std::atomic<std::size_t> ref_count_ = {0}; 241 const char* name_; 242 const char* resource_name_; 243 int line_number_; 244 int column_number_; 245 int script_id_; 246 int position_; 247 std::unique_ptr<SourcePositionTable> line_info_; 248 std::unique_ptr<RareData> rare_data_; 249 Address instruction_start_ = kNullAddress; 250 Address* heap_object_location_ = nullptr; 251 }; 252 253 struct CodeEntryAndLineNumber { 254 CodeEntry* code_entry; 255 int line_number; 256 }; 257 258 using ProfileStackTrace = std::vector<CodeEntryAndLineNumber>; 259 260 // Filters stack frames from sources other than a target native context. 261 class ContextFilter { 262 public: 263 explicit ContextFilter(Address native_context_address = kNullAddress) native_context_address_(native_context_address)264 : native_context_address_(native_context_address) {} 265 266 // Invoked when a native context has changed address. 267 void OnMoveEvent(Address from_address, Address to_address); 268 Accept(Address native_context_address)269 bool Accept(Address native_context_address) const { 270 if (native_context_address_ == kNullAddress) return true; 271 return (native_context_address & ~kHeapObjectTag) == 272 native_context_address_; 273 } 274 275 // Update the context's tracked address based on VM-thread events. set_native_context_address(Address address)276 void set_native_context_address(Address address) { 277 native_context_address_ = address; 278 } native_context_address()279 Address native_context_address() const { return native_context_address_; } 280 281 private: 282 Address native_context_address_; 283 }; 284 285 class ProfileTree; 286 287 class V8_EXPORT_PRIVATE ProfileNode { 288 public: 289 inline ProfileNode(ProfileTree* tree, CodeEntry* entry, ProfileNode* parent, 290 int line_number = 0); 291 ~ProfileNode(); 292 ProfileNode(const ProfileNode&) = delete; 293 ProfileNode& operator=(const ProfileNode&) = delete; 294 295 ProfileNode* FindChild( 296 CodeEntry* entry, 297 int line_number = v8::CpuProfileNode::kNoLineNumberInfo); 298 ProfileNode* FindOrAddChild(CodeEntry* entry, int line_number = 0); IncrementSelfTicks()299 void IncrementSelfTicks() { ++self_ticks_; } IncreaseSelfTicks(unsigned amount)300 void IncreaseSelfTicks(unsigned amount) { self_ticks_ += amount; } 301 void IncrementLineTicks(int src_line); 302 entry()303 CodeEntry* entry() const { return entry_; } self_ticks()304 unsigned self_ticks() const { return self_ticks_; } children()305 const std::vector<ProfileNode*>* children() const { return &children_list_; } id()306 unsigned id() const { return id_; } parent()307 ProfileNode* parent() const { return parent_; } line_number()308 int line_number() const { 309 return line_number_ != 0 ? line_number_ : entry_->line_number(); 310 } 311 CpuProfileNode::SourceType source_type() const; 312 GetHitLineCount()313 unsigned int GetHitLineCount() const { 314 return static_cast<unsigned int>(line_ticks_.size()); 315 } 316 bool GetLineTicks(v8::CpuProfileNode::LineTick* entries, 317 unsigned int length) const; 318 void CollectDeoptInfo(CodeEntry* entry); deopt_infos()319 const std::vector<CpuProfileDeoptInfo>& deopt_infos() const { 320 return deopt_infos_; 321 } 322 Isolate* isolate() const; 323 324 void Print(int indent) const; 325 326 private: 327 struct Equals { operatorEquals328 bool operator()(CodeEntryAndLineNumber lhs, 329 CodeEntryAndLineNumber rhs) const { 330 return lhs.code_entry->IsSameFunctionAs(rhs.code_entry) && 331 lhs.line_number == rhs.line_number; 332 } 333 }; 334 struct Hasher { operatorHasher335 std::size_t operator()(CodeEntryAndLineNumber pair) const { 336 return pair.code_entry->GetHash() ^ ComputeUnseededHash(pair.line_number); 337 } 338 }; 339 340 ProfileTree* tree_; 341 CodeEntry* entry_; 342 unsigned self_ticks_; 343 std::unordered_map<CodeEntryAndLineNumber, ProfileNode*, Hasher, Equals> 344 children_; 345 int line_number_; 346 std::vector<ProfileNode*> children_list_; 347 ProfileNode* parent_; 348 unsigned id_; 349 // maps line number --> number of ticks 350 std::unordered_map<int, int> line_ticks_; 351 352 std::vector<CpuProfileDeoptInfo> deopt_infos_; 353 }; 354 355 class CodeEntryStorage; 356 357 class V8_EXPORT_PRIVATE ProfileTree { 358 public: 359 explicit ProfileTree(Isolate* isolate, CodeEntryStorage* storage = nullptr); 360 ~ProfileTree(); 361 ProfileTree(const ProfileTree&) = delete; 362 ProfileTree& operator=(const ProfileTree&) = delete; 363 364 using ProfilingMode = v8::CpuProfilingMode; 365 366 ProfileNode* AddPathFromEnd( 367 const std::vector<CodeEntry*>& path, 368 int src_line = v8::CpuProfileNode::kNoLineNumberInfo, 369 bool update_stats = true); 370 ProfileNode* AddPathFromEnd( 371 const ProfileStackTrace& path, 372 int src_line = v8::CpuProfileNode::kNoLineNumberInfo, 373 bool update_stats = true, 374 ProfilingMode mode = ProfilingMode::kLeafNodeLineNumbers); root()375 ProfileNode* root() const { return root_; } next_node_id()376 unsigned next_node_id() { return next_node_id_++; } 377 Print()378 void Print() const { root_->Print(0); } 379 isolate()380 Isolate* isolate() const { return isolate_; } 381 EnqueueNode(const ProfileNode * node)382 void EnqueueNode(const ProfileNode* node) { pending_nodes_.push_back(node); } pending_nodes_count()383 size_t pending_nodes_count() const { return pending_nodes_.size(); } TakePendingNodes()384 std::vector<const ProfileNode*> TakePendingNodes() { 385 return std::move(pending_nodes_); 386 } 387 code_entries()388 CodeEntryStorage* code_entries() { return code_entries_; } 389 390 private: 391 template <typename Callback> 392 void TraverseDepthFirst(Callback* callback); 393 394 std::vector<const ProfileNode*> pending_nodes_; 395 396 unsigned next_node_id_; 397 Isolate* isolate_; 398 CodeEntryStorage* const code_entries_; 399 ProfileNode* root_; 400 }; 401 402 class CpuProfiler; 403 404 class CpuProfile { 405 public: 406 struct SampleInfo { 407 ProfileNode* node; 408 base::TimeTicks timestamp; 409 int line; 410 StateTag state_tag; 411 EmbedderStateTag embedder_state_tag; 412 }; 413 414 V8_EXPORT_PRIVATE CpuProfile( 415 CpuProfiler* profiler, ProfilerId id, const char* title, 416 CpuProfilingOptions options, 417 std::unique_ptr<DiscardedSamplesDelegate> delegate = nullptr); 418 CpuProfile(const CpuProfile&) = delete; 419 CpuProfile& operator=(const CpuProfile&) = delete; 420 421 // Checks whether or not the given TickSample should be (sub)sampled, given 422 // the sampling interval of the profiler that recorded it (in microseconds). 423 V8_EXPORT_PRIVATE bool CheckSubsample(base::TimeDelta sampling_interval); 424 // Add pc -> ... -> main() call path to the profile. 425 void AddPath(base::TimeTicks timestamp, const ProfileStackTrace& path, 426 int src_line, bool update_stats, 427 base::TimeDelta sampling_interval, StateTag state, 428 EmbedderStateTag embedder_state); 429 void FinishProfile(); 430 title()431 const char* title() const { return title_; } top_down()432 const ProfileTree* top_down() const { return &top_down_; } 433 samples_count()434 int samples_count() const { return static_cast<int>(samples_.size()); } sample(int index)435 const SampleInfo& sample(int index) const { return samples_[index]; } 436 sampling_interval_us()437 int64_t sampling_interval_us() const { 438 return options_.sampling_interval_us(); 439 } 440 start_time()441 base::TimeTicks start_time() const { return start_time_; } end_time()442 base::TimeTicks end_time() const { return end_time_; } cpu_profiler()443 CpuProfiler* cpu_profiler() const { return profiler_; } context_filter()444 ContextFilter& context_filter() { return context_filter_; } id()445 ProfilerId id() const { return id_; } 446 447 void UpdateTicksScale(); 448 449 V8_EXPORT_PRIVATE void Print() const; 450 451 private: 452 void StreamPendingTraceEvents(); 453 454 const char* title_; 455 const CpuProfilingOptions options_; 456 std::unique_ptr<DiscardedSamplesDelegate> delegate_; 457 ContextFilter context_filter_; 458 base::TimeTicks start_time_; 459 base::TimeTicks end_time_; 460 std::deque<SampleInfo> samples_; 461 ProfileTree top_down_; 462 CpuProfiler* const profiler_; 463 size_t streaming_next_sample_; 464 const ProfilerId id_; 465 // Number of microseconds worth of profiler ticks that should elapse before 466 // the next sample is recorded. 467 base::TimeDelta next_sample_delta_; 468 }; 469 470 class CpuProfileMaxSamplesCallbackTask : public v8::Task { 471 public: CpuProfileMaxSamplesCallbackTask(std::unique_ptr<DiscardedSamplesDelegate> delegate)472 explicit CpuProfileMaxSamplesCallbackTask( 473 std::unique_ptr<DiscardedSamplesDelegate> delegate) 474 : delegate_(std::move(delegate)) {} 475 Run()476 void Run() override { delegate_->Notify(); } 477 478 private: 479 std::unique_ptr<DiscardedSamplesDelegate> delegate_; 480 }; 481 482 class V8_EXPORT_PRIVATE CodeMap { 483 public: 484 explicit CodeMap(CodeEntryStorage& storage); 485 ~CodeMap(); 486 CodeMap(const CodeMap&) = delete; 487 CodeMap& operator=(const CodeMap&) = delete; 488 489 // Adds the given CodeEntry to the CodeMap. The CodeMap takes ownership of 490 // the CodeEntry. 491 void AddCode(Address addr, CodeEntry* entry, unsigned size); 492 void MoveCode(Address from, Address to); 493 // Attempts to remove the given CodeEntry from the CodeMap. 494 // Returns true iff the entry was found and removed. 495 bool RemoveCode(CodeEntry*); 496 void ClearCodesInRange(Address start, Address end); 497 CodeEntry* FindEntry(Address addr, Address* out_instruction_start = nullptr); 498 void Print(); size()499 size_t size() const { return code_map_.size(); } 500 501 size_t GetEstimatedMemoryUsage() const; 502 code_entries()503 CodeEntryStorage& code_entries() { return code_entries_; } 504 505 void Clear(); 506 507 private: 508 struct CodeEntryMapInfo { 509 CodeEntry* entry; 510 unsigned size; 511 }; 512 513 std::multimap<Address, CodeEntryMapInfo> code_map_; 514 CodeEntryStorage& code_entries_; 515 }; 516 517 // Manages the lifetime of CodeEntry objects, and stores shared resources 518 // between them. 519 class V8_EXPORT_PRIVATE CodeEntryStorage { 520 public: 521 template <typename... Args> Create(Args &&...args)522 static CodeEntry* Create(Args&&... args) { 523 CodeEntry* const entry = new CodeEntry(std::forward<Args>(args)...); 524 entry->mark_ref_counted(); 525 return entry; 526 } 527 528 void AddRef(CodeEntry*); 529 void DecRef(CodeEntry*); 530 strings()531 StringsStorage& strings() { return function_and_resource_names_; } 532 533 private: 534 StringsStorage function_and_resource_names_; 535 }; 536 537 class V8_EXPORT_PRIVATE CpuProfilesCollection { 538 public: 539 explicit CpuProfilesCollection(Isolate* isolate); 540 CpuProfilesCollection(const CpuProfilesCollection&) = delete; 541 CpuProfilesCollection& operator=(const CpuProfilesCollection&) = delete; 542 set_cpu_profiler(CpuProfiler * profiler)543 void set_cpu_profiler(CpuProfiler* profiler) { profiler_ = profiler; } 544 CpuProfilingResult StartProfiling( 545 const char* title = nullptr, CpuProfilingOptions options = {}, 546 std::unique_ptr<DiscardedSamplesDelegate> delegate = nullptr); 547 548 // This Method is only visible for testing 549 CpuProfilingResult StartProfilingForTesting(ProfilerId id); 550 CpuProfile* StopProfiling(ProfilerId id); 551 bool IsLastProfileLeft(ProfilerId id); 552 CpuProfile* Lookup(const char* title); 553 profiles()554 std::vector<std::unique_ptr<CpuProfile>>* profiles() { 555 return &finished_profiles_; 556 } GetName(Name name)557 const char* GetName(Name name) { return resource_names_.GetName(name); } 558 void RemoveProfile(CpuProfile* profile); 559 560 // Finds a common sampling interval dividing each CpuProfile's interval, 561 // rounded up to the nearest multiple of the CpuProfiler's sampling interval. 562 // Returns 0 if no profiles are attached. 563 base::TimeDelta GetCommonSamplingInterval() const; 564 565 // Called from profile generator thread. 566 void AddPathToCurrentProfiles( 567 base::TimeTicks timestamp, const ProfileStackTrace& path, int src_line, 568 bool update_stats, base::TimeDelta sampling_interval, StateTag state, 569 EmbedderStateTag embedder_state_tag, 570 Address native_context_address = kNullAddress, 571 Address native_embedder_context_address = kNullAddress); 572 573 // Called from profile generator thread. 574 void UpdateNativeContextAddressForCurrentProfiles(Address from, Address to); 575 576 // Limits the number of profiles that can be simultaneously collected. 577 static const int kMaxSimultaneousProfiles = 100; 578 579 private: 580 CpuProfilingResult StartProfiling( 581 ProfilerId id, const char* title = nullptr, 582 CpuProfilingOptions options = {}, 583 std::unique_ptr<DiscardedSamplesDelegate> delegate = nullptr); 584 StringsStorage resource_names_; 585 std::vector<std::unique_ptr<CpuProfile>> finished_profiles_; 586 CpuProfiler* profiler_; 587 588 // Accessed by VM thread and profile generator thread. 589 std::vector<std::unique_ptr<CpuProfile>> current_profiles_; 590 base::Semaphore current_profiles_semaphore_; 591 static std::atomic<ProfilerId> last_id_; 592 Isolate* isolate_; 593 }; 594 595 class CpuProfileJSONSerializer { 596 public: CpuProfileJSONSerializer(CpuProfile * profile)597 explicit CpuProfileJSONSerializer(CpuProfile* profile) 598 : profile_(profile), writer_(nullptr) {} 599 CpuProfileJSONSerializer(const CpuProfileJSONSerializer&) = delete; 600 CpuProfileJSONSerializer& operator=(const CpuProfileJSONSerializer&) = delete; 601 void Serialize(v8::OutputStream* stream); 602 603 private: 604 void SerializePositionTicks(const v8::CpuProfileNode* node, int lineCount); 605 void SerializeCallFrame(const v8::CpuProfileNode* node); 606 void SerializeChildren(const v8::CpuProfileNode* node, int childrenCount); 607 void SerializeNode(const v8::CpuProfileNode* node); 608 void SerializeNodes(); 609 void SerializeSamples(); 610 void SerializeTimeDeltas(); 611 void SerializeImpl(); 612 613 static const int kEdgeFieldsCount; 614 static const int kNodeFieldsCount; 615 616 CpuProfile* profile_; 617 OutputStreamWriter* writer_; 618 }; 619 620 } // namespace internal 621 } // namespace v8 622 623 #endif // V8_PROFILER_PROFILE_GENERATOR_H_ 624