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. 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 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) 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) 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 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 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 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. 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 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