1// Copyright 2016 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#include "src/heap/code-stats.h"
6#include "src/objects-inl.h"
7
8namespace v8 {
9namespace internal {
10
11// Record code statisitcs.
12void CodeStatistics::RecordCodeAndMetadataStatistics(HeapObject* object,
13                                                     Isolate* isolate) {
14  if (!object->IsAbstractCode()) {
15    return;
16  }
17
18  // Record code+metadata statisitcs.
19  AbstractCode* abstract_code = AbstractCode::cast(object);
20  int size = abstract_code->SizeIncludingMetadata();
21  if (abstract_code->IsCode()) {
22    size += isolate->code_and_metadata_size();
23    isolate->set_code_and_metadata_size(size);
24  } else {
25    size += isolate->bytecode_and_metadata_size();
26    isolate->set_bytecode_and_metadata_size(size);
27  }
28
29#ifdef DEBUG
30  // Record code kind and code comment statistics.
31  isolate->code_kind_statistics()[abstract_code->kind()] +=
32      abstract_code->Size();
33  CodeStatistics::CollectCodeCommentStatistics(object, isolate);
34#endif
35}
36
37void CodeStatistics::ResetCodeAndMetadataStatistics(Isolate* isolate) {
38  isolate->set_code_and_metadata_size(0);
39  isolate->set_bytecode_and_metadata_size(0);
40#ifdef DEBUG
41  ResetCodeStatistics(isolate);
42#endif
43}
44
45// Collects code size statistics:
46// - code and metadata size
47// - by code kind (only in debug mode)
48// - by code comment (only in debug mode)
49void CodeStatistics::CollectCodeStatistics(PagedSpace* space,
50                                           Isolate* isolate) {
51  HeapObjectIterator obj_it(space);
52  for (HeapObject* obj = obj_it.Next(); obj != NULL; obj = obj_it.Next()) {
53    RecordCodeAndMetadataStatistics(obj, isolate);
54  }
55}
56
57// Collects code size statistics in LargeObjectSpace:
58// - code and metadata size
59// - by code kind (only in debug mode)
60// - by code comment (only in debug mode)
61void CodeStatistics::CollectCodeStatistics(LargeObjectSpace* space,
62                                           Isolate* isolate) {
63  LargeObjectIterator obj_it(space);
64  for (HeapObject* obj = obj_it.Next(); obj != NULL; obj = obj_it.Next()) {
65    RecordCodeAndMetadataStatistics(obj, isolate);
66  }
67}
68
69#ifdef DEBUG
70void CodeStatistics::ReportCodeStatistics(Isolate* isolate) {
71  // Report code kind statistics
72  int* code_kind_statistics = isolate->code_kind_statistics();
73  PrintF("\n   Code kind histograms: \n");
74  for (int i = 0; i < AbstractCode::NUMBER_OF_KINDS; i++) {
75    if (code_kind_statistics[i] > 0) {
76      PrintF("     %-20s: %10d bytes\n",
77             AbstractCode::Kind2String(static_cast<AbstractCode::Kind>(i)),
78             code_kind_statistics[i]);
79    }
80  }
81  PrintF("\n");
82
83  // Report code and metadata statisitcs
84  if (isolate->code_and_metadata_size() > 0) {
85    PrintF("Code size including metadata    : %10d bytes\n",
86           isolate->code_and_metadata_size());
87  }
88  if (isolate->bytecode_and_metadata_size() > 0) {
89    PrintF("Bytecode size including metadata: %10d bytes\n",
90           isolate->bytecode_and_metadata_size());
91  }
92
93  // Report code comment statistics
94  CommentStatistic* comments_statistics =
95      isolate->paged_space_comments_statistics();
96  PrintF(
97      "Code comment statistics (\"   [ comment-txt   :    size/   "
98      "count  (average)\"):\n");
99  for (int i = 0; i <= CommentStatistic::kMaxComments; i++) {
100    const CommentStatistic& cs = comments_statistics[i];
101    if (cs.size > 0) {
102      PrintF("   %-30s: %10d/%6d     (%d)\n", cs.comment, cs.size, cs.count,
103             cs.size / cs.count);
104    }
105  }
106  PrintF("\n");
107}
108
109void CodeStatistics::ResetCodeStatistics(Isolate* isolate) {
110  // Clear code kind statistics
111  int* code_kind_statistics = isolate->code_kind_statistics();
112  for (int i = 0; i < AbstractCode::NUMBER_OF_KINDS; i++) {
113    code_kind_statistics[i] = 0;
114  }
115
116  // Clear code comment statistics
117  CommentStatistic* comments_statistics =
118      isolate->paged_space_comments_statistics();
119  for (int i = 0; i < CommentStatistic::kMaxComments; i++) {
120    comments_statistics[i].Clear();
121  }
122  comments_statistics[CommentStatistic::kMaxComments].comment = "Unknown";
123  comments_statistics[CommentStatistic::kMaxComments].size = 0;
124  comments_statistics[CommentStatistic::kMaxComments].count = 0;
125}
126
127// Adds comment to 'comment_statistics' table. Performance OK as long as
128// 'kMaxComments' is small
129void CodeStatistics::EnterComment(Isolate* isolate, const char* comment,
130                                  int delta) {
131  CommentStatistic* comments_statistics =
132      isolate->paged_space_comments_statistics();
133  // Do not count empty comments
134  if (delta <= 0) return;
135  CommentStatistic* cs = &comments_statistics[CommentStatistic::kMaxComments];
136  // Search for a free or matching entry in 'comments_statistics': 'cs'
137  // points to result.
138  for (int i = 0; i < CommentStatistic::kMaxComments; i++) {
139    if (comments_statistics[i].comment == NULL) {
140      cs = &comments_statistics[i];
141      cs->comment = comment;
142      break;
143    } else if (strcmp(comments_statistics[i].comment, comment) == 0) {
144      cs = &comments_statistics[i];
145      break;
146    }
147  }
148  // Update entry for 'comment'
149  cs->size += delta;
150  cs->count += 1;
151}
152
153// Call for each nested comment start (start marked with '[ xxx', end marked
154// with ']'.  RelocIterator 'it' must point to a comment reloc info.
155void CodeStatistics::CollectCommentStatistics(Isolate* isolate,
156                                              RelocIterator* it) {
157  DCHECK(!it->done());
158  DCHECK(it->rinfo()->rmode() == RelocInfo::COMMENT);
159  const char* tmp = reinterpret_cast<const char*>(it->rinfo()->data());
160  if (tmp[0] != '[') {
161    // Not a nested comment; skip
162    return;
163  }
164
165  // Search for end of nested comment or a new nested comment
166  const char* const comment_txt =
167      reinterpret_cast<const char*>(it->rinfo()->data());
168  const byte* prev_pc = it->rinfo()->pc();
169  int flat_delta = 0;
170  it->next();
171  while (true) {
172    // All nested comments must be terminated properly, and therefore exit
173    // from loop.
174    DCHECK(!it->done());
175    if (it->rinfo()->rmode() == RelocInfo::COMMENT) {
176      const char* const txt =
177          reinterpret_cast<const char*>(it->rinfo()->data());
178      flat_delta += static_cast<int>(it->rinfo()->pc() - prev_pc);
179      if (txt[0] == ']') break;  // End of nested  comment
180      // A new comment
181      CollectCommentStatistics(isolate, it);
182      // Skip code that was covered with previous comment
183      prev_pc = it->rinfo()->pc();
184    }
185    it->next();
186  }
187  EnterComment(isolate, comment_txt, flat_delta);
188}
189
190// Collects code comment statistics
191void CodeStatistics::CollectCodeCommentStatistics(HeapObject* obj,
192                                                  Isolate* isolate) {
193  // Bytecode objects do not contain RelocInfo. Only process code objects
194  // for code comment statistics.
195  if (!obj->IsCode()) {
196    return;
197  }
198
199  Code* code = Code::cast(obj);
200  RelocIterator it(code);
201  int delta = 0;
202  const byte* prev_pc = code->instruction_start();
203  while (!it.done()) {
204    if (it.rinfo()->rmode() == RelocInfo::COMMENT) {
205      delta += static_cast<int>(it.rinfo()->pc() - prev_pc);
206      CollectCommentStatistics(isolate, &it);
207      prev_pc = it.rinfo()->pc();
208    }
209    it.next();
210  }
211
212  DCHECK(code->instruction_start() <= prev_pc &&
213         prev_pc <= code->instruction_end());
214  delta += static_cast<int>(code->instruction_end() - prev_pc);
215  EnterComment(isolate, "NoComment", delta);
216}
217#endif
218
219}  // namespace internal
220}  // namespace v8
221