text: // Copyright 2016 the V8 project authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "src/heap/code-stats.h"
  
  #include "src/codegen/code-comments.h"
  #include "src/codegen/reloc-info.h"
  #include "src/heap/spaces-inl.h"  // For PagedSpaceObjectIterator.
  #include "src/objects/objects-inl.h"
  
  namespace v8 {
  namespace internal {
  
  // Record code statisitcs.
  void CodeStatistics::RecordCodeAndMetadataStatistics(HeapObject object,
                                                       Isolate* isolate) {
    if (object.IsScript()) {
      Script script = Script::cast(object);
      // Log the size of external source code.
      Object source = script.source();
      if (source.IsExternalString()) {
        ExternalString external_source_string = ExternalString::cast(source);
        int size = isolate->external_script_source_size();
        size += external_source_string.ExternalPayloadSize();
        isolate->set_external_script_source_size(size);
      }
    } else if (object.IsAbstractCode()) {
      // Record code+metadata statisitcs.
      AbstractCode abstract_code = AbstractCode::cast(object);
      int size = abstract_code.SizeIncludingMetadata();
      if (abstract_code.IsCode()) {
        size += isolate->code_and_metadata_size();
        isolate->set_code_and_metadata_size(size);
      } else {
        size += isolate->bytecode_and_metadata_size();
        isolate->set_bytecode_and_metadata_size(size);
      }
  
  #ifdef DEBUG
      // Record code kind and code comment statistics.
      isolate->code_kind_statistics()[abstract_code.kind()] +=
          abstract_code.Size();
      CodeStatistics::CollectCodeCommentStatistics(object, isolate);
  #endif
    }
  }
  
  void CodeStatistics::ResetCodeAndMetadataStatistics(Isolate* isolate) {
    isolate->set_code_and_metadata_size(0);
    isolate->set_bytecode_and_metadata_size(0);
    isolate->set_external_script_source_size(0);
  #ifdef DEBUG
    ResetCodeStatistics(isolate);
  #endif
  }
  
  // Collects code size statistics:
  // - code and metadata size
  // - by code kind (only in debug mode)
  // - by code comment (only in debug mode)
  void CodeStatistics::CollectCodeStatistics(PagedSpace* space,
                                             Isolate* isolate) {
    PagedSpaceObjectIterator obj_it(space);
    for (HeapObject obj = obj_it.Next(); !obj.is_null(); obj = obj_it.Next()) {
      RecordCodeAndMetadataStatistics(obj, isolate);
    }
  }
  
  // Collects code size statistics in OldLargeObjectSpace:
  // - code and metadata size
  // - by code kind (only in debug mode)
  // - by code comment (only in debug mode)
  void CodeStatistics::CollectCodeStatistics(OldLargeObjectSpace* space,
                                             Isolate* isolate) {
    LargeObjectSpaceObjectIterator obj_it(space);
    for (HeapObject obj = obj_it.Next(); !obj.is_null(); obj = obj_it.Next()) {
      RecordCodeAndMetadataStatistics(obj, isolate);
    }
  }
  
  #ifdef DEBUG
  void CodeStatistics::ReportCodeStatistics(Isolate* isolate) {
    // Report code kind statistics
    int* code_kind_statistics = isolate->code_kind_statistics();
    PrintF("\n   Code kind histograms: \n");
    for (int i = 0; i < AbstractCode::NUMBER_OF_KINDS; i++) {
      if (code_kind_statistics[i] > 0) {
        PrintF("     %-20s: %10d bytes\n",
               AbstractCode::Kind2String(static_cast<AbstractCode::Kind>(i)),
               code_kind_statistics[i]);
      }
    }
    PrintF("\n");
  
    // Report code and metadata statisitcs
    if (isolate->code_and_metadata_size() > 0) {
      PrintF("Code size including metadata    : %10d bytes\n",
             isolate->code_and_metadata_size());
    }
    if (isolate->bytecode_and_metadata_size() > 0) {
      PrintF("Bytecode size including metadata: %10d bytes\n",
             isolate->bytecode_and_metadata_size());
    }
  
    // Report code comment statistics
    CommentStatistic* comments_statistics =
        isolate->paged_space_comments_statistics();
    PrintF(
        "Code comment statistics (\"   [ comment-txt   :    size/   "
        "count  (average)\"):\n");
    for (int i = 0; i <= CommentStatistic::kMaxComments; i++) {
      const CommentStatistic& cs = comments_statistics[i];
      if (cs.size > 0) {
        PrintF("   %-30s: %10d/%6d     (%d)\n", cs.comment, cs.size, cs.count,
               cs.size / cs.count);
      }
    }
    PrintF("\n");
  }
  
  void CodeStatistics::ResetCodeStatistics(Isolate* isolate) {
    // Clear code kind statistics
    int* code_kind_statistics = isolate->code_kind_statistics();
    for (int i = 0; i < AbstractCode::NUMBER_OF_KINDS; i++) {
      code_kind_statistics[i] = 0;
    }
  
    // Clear code comment statistics
    CommentStatistic* comments_statistics =
        isolate->paged_space_comments_statistics();
    for (int i = 0; i < CommentStatistic::kMaxComments; i++) {
      comments_statistics[i].Clear();
    }
    comments_statistics[CommentStatistic::kMaxComments].comment = "Unknown";
    comments_statistics[CommentStatistic::kMaxComments].size = 0;
    comments_statistics[CommentStatistic::kMaxComments].count = 0;
  }
  
  // Adds comment to 'comment_statistics' table. Performance OK as long as
  // 'kMaxComments' is small
  void CodeStatistics::EnterComment(Isolate* isolate, const char* comment,
                                    int delta) {
    CommentStatistic* comments_statistics =
        isolate->paged_space_comments_statistics();
    // Do not count empty comments
    if (delta <= 0) return;
    CommentStatistic* cs = &comments_statistics[CommentStatistic::kMaxComments];
    // Search for a free or matching entry in 'comments_statistics': 'cs'
    // points to result.
    for (int i = 0; i < CommentStatistic::kMaxComments; i++) {
      if (comments_statistics[i].comment == nullptr) {
        cs = &comments_statistics[i];
        cs->comment = comment;
        break;
      } else if (strcmp(comments_statistics[i].comment, comment) == 0) {
        cs = &comments_statistics[i];
        break;
      }
    }
    // Update entry for 'comment'
    cs->size += delta;
    cs->count += 1;
  }
  
  // Call for each nested comment start (start marked with '[ xxx', end marked
  // with ']'.  RelocIterator 'it' must point to a comment reloc info.
  void CodeStatistics::CollectCommentStatistics(Isolate* isolate,
                                                CodeCommentsIterator* cit) {
    DCHECK(cit->HasCurrent());
    const char* comment_txt = cit->GetComment();
    if (comment_txt[0] != '[') {
      // Not a nested comment; skip
      return;
    }
  
    // Search for end of nested comment or a new nested comment
    int prev_pc_offset = cit->GetPCOffset();
    int flat_delta = 0;
    cit->Next();
    for (; cit->HasCurrent(); cit->Next()) {
      // All nested comments must be terminated properly, and therefore exit
      // from loop.
      const char* const txt = cit->GetComment();
      flat_delta += cit->GetPCOffset() - prev_pc_offset;
      if (txt[0] == ']') break;  // End of nested  comment
      // A new comment
      CollectCommentStatistics(isolate, cit);
      // Skip code that was covered with previous comment
      prev_pc_offset = cit->GetPCOffset();
    }
    EnterComment(isolate, comment_txt, flat_delta);
  }
  
  // Collects code comment statistics
  void CodeStatistics::CollectCodeCommentStatistics(HeapObject obj,
                                                    Isolate* isolate) {
    // Bytecode objects do not contain RelocInfo. Only process code objects
    // for code comment statistics.
    if (!obj.IsCode()) {
      return;
    }
  
    Code code = Code::cast(obj);
    CodeCommentsIterator cit(code.code_comments(), code.code_comments_size());
    int delta = 0;
    int prev_pc_offset = 0;
    while (cit.HasCurrent()) {
      delta += static_cast<int>(cit.GetPCOffset() - prev_pc_offset);
      CollectCommentStatistics(isolate, &cit);
      prev_pc_offset = cit.GetPCOffset();
      cit.Next();
    }
  
    DCHECK(0 <= prev_pc_offset && prev_pc_offset <= code.raw_instruction_size());
    delta += static_cast<int>(code.raw_instruction_size() - prev_pc_offset);
    EnterComment(isolate, "NoComment", delta);
  }
  #endif
  
  }  // namespace internal
  }  // namespace v8
  