blob: abca2c75f97974675b473568c0b742ec9685020c [file] [log] [blame]
// 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