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