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