Home | History | Annotate | Download | only in heap
      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