code-stats.cc 7.85 KB
Newer Older
1 2 3 4 5
// 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"
6

7 8
#include "src/codegen/code-comments.h"
#include "src/codegen/reloc-info.h"
9
#include "src/heap/heap-inl.h"
10
#include "src/heap/large-spaces.h"
11
#include "src/heap/paged-spaces-inl.h"  // For PagedSpaceObjectIterator.
12
#include "src/objects/objects-inl.h"
13 14 15 16 17

namespace v8 {
namespace internal {

// Record code statisitcs.
18
void CodeStatistics::RecordCodeAndMetadataStatistics(HeapObject object,
19
                                                     Isolate* isolate) {
20
  if (object.IsScript()) {
21
    Script script = Script::cast(object);
22
    // Log the size of external source code.
23 24
    Object source = script.source();
    if (source.IsExternalString()) {
25
      ExternalString external_source_string = ExternalString::cast(source);
26
      int size = isolate->external_script_source_size();
27
      size += external_source_string.ExternalPayloadSize();
28 29
      isolate->set_external_script_source_size(size);
    }
30
  } else if (object.IsAbstractCode()) {
31
    // Record code+metadata statisitcs.
32
    AbstractCode abstract_code = AbstractCode::cast(object);
33 34
    int size = abstract_code.SizeIncludingMetadata();
    if (abstract_code.IsCode()) {
35 36 37 38 39 40
      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);
    }
41 42

#ifdef DEBUG
43
    // Record code kind and code comment statistics.
44
    isolate->code_kind_statistics()[static_cast<int>(abstract_code.kind())] +=
45
        abstract_code.Size();
46
    CodeStatistics::CollectCodeCommentStatistics(object, isolate);
47
#endif
48
  }
49 50 51 52 53
}

void CodeStatistics::ResetCodeAndMetadataStatistics(Isolate* isolate) {
  isolate->set_code_and_metadata_size(0);
  isolate->set_bytecode_and_metadata_size(0);
54
  isolate->set_external_script_source_size(0);
55 56 57 58 59 60 61 62 63 64 65
#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) {
66
  PagedSpaceObjectIterator obj_it(isolate->heap(), space);
67
  for (HeapObject obj = obj_it.Next(); !obj.is_null(); obj = obj_it.Next()) {
68 69 70 71
    RecordCodeAndMetadataStatistics(obj, isolate);
  }
}

72
// Collects code size statistics in OldLargeObjectSpace:
73 74 75
// - code and metadata size
// - by code kind (only in debug mode)
// - by code comment (only in debug mode)
76
void CodeStatistics::CollectCodeStatistics(OldLargeObjectSpace* space,
77
                                           Isolate* isolate) {
78
  LargeObjectSpaceObjectIterator obj_it(space);
79
  for (HeapObject obj = obj_it.Next(); !obj.is_null(); obj = obj_it.Next()) {
80 81 82 83 84 85 86 87 88
    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");
89
  for (int i = 0; i < kCodeKindCount; i++) {
90 91
    if (code_kind_statistics[i] > 0) {
      PrintF("     %-20s: %10d bytes\n",
92
             CodeKindToString(static_cast<CodeKind>(i)),
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
             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();
127
  for (int i = 0; i < kCodeKindCount; i++) {
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
    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++) {
154
    if (comments_statistics[i].comment == nullptr) {
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
      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,
171 172 173 174
                                              CodeCommentsIterator* cit) {
  DCHECK(cit->HasCurrent());
  const char* comment_txt = cit->GetComment();
  if (comment_txt[0] != '[') {
175 176 177 178 179
    // Not a nested comment; skip
    return;
  }

  // Search for end of nested comment or a new nested comment
180
  int prev_pc_offset = cit->GetPCOffset();
181
  int flat_delta = 0;
182 183
  cit->Next();
  for (; cit->HasCurrent(); cit->Next()) {
184 185
    // All nested comments must be terminated properly, and therefore exit
    // from loop.
186 187 188 189 190 191 192
    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();
193 194 195 196
  }
  EnterComment(isolate, comment_txt, flat_delta);
}

197
// Collects code comment statistics.
198
void CodeStatistics::CollectCodeCommentStatistics(HeapObject obj,
199 200 201
                                                  Isolate* isolate) {
  // Bytecode objects do not contain RelocInfo. Only process code objects
  // for code comment statistics.
202 203 204 205
  if (!obj.IsCode()) {
    DCHECK(obj.IsBytecodeArray());
    return;
  }
206

207
  Code code = Code::cast(obj);
208
  CodeCommentsIterator cit(code.code_comments(), code.code_comments_size());
209
  int delta = 0;
210 211 212 213 214 215
  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();
216 217
  }

218 219
  DCHECK(0 <= prev_pc_offset && prev_pc_offset <= code.InstructionSize());
  delta += static_cast<int>(code.InstructionSize() - prev_pc_offset);
220 221 222 223 224 225
  EnterComment(isolate, "NoComment", delta);
}
#endif

}  // namespace internal
}  // namespace v8