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