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 
8 namespace v8 {
9 namespace internal {
10 
11 // Record code statisitcs.
RecordCodeAndMetadataStatistics(HeapObject * object,Isolate * isolate)12 void 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 
ResetCodeAndMetadataStatistics(Isolate * isolate)37 void 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)
CollectCodeStatistics(PagedSpace * space,Isolate * isolate)49 void 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)
CollectCodeStatistics(LargeObjectSpace * space,Isolate * isolate)61 void 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
ReportCodeStatistics(Isolate * isolate)70 void 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 
ResetCodeStatistics(Isolate * isolate)109 void 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
EnterComment(Isolate * isolate,const char * comment,int delta)129 void 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.
CollectCommentStatistics(Isolate * isolate,RelocIterator * it)155 void 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
CollectCodeCommentStatistics(HeapObject * obj,Isolate * isolate)191 void 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