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