// Copyright 2016 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/heap/code-stats.h" #include "src/codegen/code-comments.h" #include "src/codegen/reloc-info.h" #include "src/heap/heap-inl.h" #include "src/heap/large-spaces.h" #include "src/heap/paged-spaces-inl.h" // For PagedSpaceObjectIterator. #include "src/objects/objects-inl.h" namespace v8 { namespace internal { // Record code statisitcs. void CodeStatistics::RecordCodeAndMetadataStatistics(HeapObject object, Isolate* isolate) { PtrComprCageBase cage_base(isolate); if (object.IsScript(cage_base)) { Script script = Script::cast(object); // Log the size of external source code. Object source = script.source(cage_base); if (source.IsExternalString(cage_base)) { ExternalString external_source_string = ExternalString::cast(source); int size = isolate->external_script_source_size(); size += external_source_string.ExternalPayloadSize(); isolate->set_external_script_source_size(size); } } else if (object.IsAbstractCode(cage_base)) { // Record code+metadata statisitcs. AbstractCode abstract_code = AbstractCode::cast(object); int size = abstract_code.SizeIncludingMetadata(); if (abstract_code.IsCode(cage_base)) { size += isolate->code_and_metadata_size(); isolate->set_code_and_metadata_size(size); } else { size += isolate->bytecode_and_metadata_size(); isolate->set_bytecode_and_metadata_size(size); } #ifdef DEBUG // Record code kind and code comment statistics. isolate->code_kind_statistics()[static_cast(abstract_code.kind())] += abstract_code.Size(cage_base); CodeStatistics::CollectCodeCommentStatistics(object, isolate); #endif } } void CodeStatistics::ResetCodeAndMetadataStatistics(Isolate* isolate) { isolate->set_code_and_metadata_size(0); isolate->set_bytecode_and_metadata_size(0); isolate->set_external_script_source_size(0); #ifdef DEBUG ResetCodeStatistics(isolate); #endif } // Collects code size statistics: // - code and metadata size // - by code kind (only in debug mode) // - by code comment (only in debug mode) void CodeStatistics::CollectCodeStatistics(PagedSpace* space, Isolate* isolate) { PagedSpaceObjectIterator obj_it(isolate->heap(), space); for (HeapObject obj = obj_it.Next(); !obj.is_null(); obj = obj_it.Next()) { RecordCodeAndMetadataStatistics(obj, isolate); } } // Collects code size statistics in OldLargeObjectSpace: // - code and metadata size // - by code kind (only in debug mode) // - by code comment (only in debug mode) void CodeStatistics::CollectCodeStatistics(OldLargeObjectSpace* space, Isolate* isolate) { LargeObjectSpaceObjectIterator obj_it(space); for (HeapObject obj = obj_it.Next(); !obj.is_null(); obj = obj_it.Next()) { RecordCodeAndMetadataStatistics(obj, isolate); } } #ifdef DEBUG void CodeStatistics::ReportCodeStatistics(Isolate* isolate) { // Report code kind statistics int* code_kind_statistics = isolate->code_kind_statistics(); PrintF("\n Code kind histograms: \n"); for (int i = 0; i < kCodeKindCount; i++) { if (code_kind_statistics[i] > 0) { PrintF(" %-20s: %10d bytes\n", CodeKindToString(static_cast(i)), code_kind_statistics[i]); } } PrintF("\n"); // Report code and metadata statisitcs if (isolate->code_and_metadata_size() > 0) { PrintF("Code size including metadata : %10d bytes\n", isolate->code_and_metadata_size()); } if (isolate->bytecode_and_metadata_size() > 0) { PrintF("Bytecode size including metadata: %10d bytes\n", isolate->bytecode_and_metadata_size()); } // Report code comment statistics CommentStatistic* comments_statistics = isolate->paged_space_comments_statistics(); PrintF( "Code comment statistics (\" [ comment-txt : size/ " "count (average)\"):\n"); for (int i = 0; i <= CommentStatistic::kMaxComments; i++) { const CommentStatistic& cs = comments_statistics[i]; if (cs.size > 0) { PrintF(" %-30s: %10d/%6d (%d)\n", cs.comment, cs.size, cs.count, cs.size / cs.count); } } PrintF("\n"); } void CodeStatistics::ResetCodeStatistics(Isolate* isolate) { // Clear code kind statistics int* code_kind_statistics = isolate->code_kind_statistics(); for (int i = 0; i < kCodeKindCount; i++) { code_kind_statistics[i] = 0; } // Clear code comment statistics CommentStatistic* comments_statistics = isolate->paged_space_comments_statistics(); for (int i = 0; i < CommentStatistic::kMaxComments; i++) { comments_statistics[i].Clear(); } comments_statistics[CommentStatistic::kMaxComments].comment = "Unknown"; comments_statistics[CommentStatistic::kMaxComments].size = 0; comments_statistics[CommentStatistic::kMaxComments].count = 0; } // Adds comment to 'comment_statistics' table. Performance OK as long as // 'kMaxComments' is small void CodeStatistics::EnterComment(Isolate* isolate, const char* comment, int delta) { CommentStatistic* comments_statistics = isolate->paged_space_comments_statistics(); // Do not count empty comments if (delta <= 0) return; CommentStatistic* cs = &comments_statistics[CommentStatistic::kMaxComments]; // Search for a free or matching entry in 'comments_statistics': 'cs' // points to result. for (int i = 0; i < CommentStatistic::kMaxComments; i++) { if (comments_statistics[i].comment == nullptr) { cs = &comments_statistics[i]; cs->comment = comment; break; } else if (strcmp(comments_statistics[i].comment, comment) == 0) { cs = &comments_statistics[i]; break; } } // Update entry for 'comment' cs->size += delta; cs->count += 1; } // Call for each nested comment start (start marked with '[ xxx', end marked // with ']'. RelocIterator 'it' must point to a comment reloc info. void CodeStatistics::CollectCommentStatistics(Isolate* isolate, CodeCommentsIterator* cit) { DCHECK(cit->HasCurrent()); const char* comment_txt = cit->GetComment(); if (comment_txt[0] != '[') { // Not a nested comment; skip return; } // Search for end of nested comment or a new nested comment int prev_pc_offset = cit->GetPCOffset(); int flat_delta = 0; cit->Next(); for (; cit->HasCurrent(); cit->Next()) { // All nested comments must be terminated properly, and therefore exit // from loop. const char* const txt = cit->GetComment(); flat_delta += cit->GetPCOffset() - prev_pc_offset; if (txt[0] == ']') break; // End of nested comment // A new comment CollectCommentStatistics(isolate, cit); // Skip code that was covered with previous comment prev_pc_offset = cit->GetPCOffset(); } EnterComment(isolate, comment_txt, flat_delta); } // Collects code comment statistics. void CodeStatistics::CollectCodeCommentStatistics(HeapObject obj, Isolate* isolate) { // Bytecode objects do not contain RelocInfo. Only process code objects // for code comment statistics. if (!obj.IsCode()) { DCHECK(obj.IsBytecodeArray()); return; } Code code = Code::cast(obj); CodeCommentsIterator cit(code.code_comments(), code.code_comments_size()); int delta = 0; int prev_pc_offset = 0; while (cit.HasCurrent()) { delta += static_cast(cit.GetPCOffset() - prev_pc_offset); CollectCommentStatistics(isolate, &cit); prev_pc_offset = cit.GetPCOffset(); cit.Next(); } DCHECK(0 <= prev_pc_offset && prev_pc_offset <= code.InstructionSize()); delta += static_cast(code.InstructionSize() - prev_pc_offset); EnterComment(isolate, "NoComment", delta); } #endif } // namespace internal } // namespace v8