| // 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) { |
| if (object.IsScript()) { |
| Script script = Script::cast(object); |
| // Log the size of external source code. |
| Object source = script.source(); |
| if (source.IsExternalString()) { |
| 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()) { |
| // Record code+metadata statisitcs. |
| AbstractCode abstract_code = AbstractCode::cast(object); |
| int size = abstract_code.SizeIncludingMetadata(); |
| if (abstract_code.IsCode()) { |
| 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<int>(abstract_code.kind())] += |
| abstract_code.Size(); |
| 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<CodeKind>(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<int>(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<int>(code.InstructionSize() - prev_pc_offset); |
| EnterComment(isolate, "NoComment", delta); |
| } |
| #endif |
| |
| } // namespace internal |
| } // namespace v8 |