// Copyright 2021 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/heap-layout-tracer.h"

#include <iostream>

#include "src/heap/new-spaces.h"
#include "src/heap/paged-spaces.h"
#include "src/heap/read-only-spaces.h"
#include "src/heap/spaces-inl.h"

namespace v8 {
namespace internal {

// static
void HeapLayoutTracer::GCProloguePrintHeapLayout(v8::Isolate* isolate,
                                                 v8::GCType gc_type,
                                                 v8::GCCallbackFlags flags,
                                                 void* data) {
  Heap* heap = reinterpret_cast<i::Isolate*>(isolate)->heap();
  // gc_count_ will increase after this callback, manually add 1.
  PrintF("Before GC:%d,", heap->gc_count() + 1);
  PrintF("collector_name:%s\n", Heap::CollectorName(gc_type));
  PrintHeapLayout(std::cout, heap);
}

// static
void HeapLayoutTracer::GCEpiloguePrintHeapLayout(v8::Isolate* isolate,
                                                 v8::GCType gc_type,
                                                 v8::GCCallbackFlags flags,
                                                 void* data) {
  Heap* heap = reinterpret_cast<i::Isolate*>(isolate)->heap();
  PrintF("After GC:%d,", heap->gc_count());
  PrintF("collector_name:%s\n", Heap::CollectorName(gc_type));
  PrintHeapLayout(std::cout, heap);
}

// static
void HeapLayoutTracer::PrintBasicMemoryChunk(std::ostream& os,
                                             BasicMemoryChunk* chunk,
                                             const char* owner_name) {
  os << "{owner:" << owner_name << ","
     << "address:" << chunk << ","
     << "size:" << chunk->size() << ","
     << "allocated_bytes:" << chunk->allocated_bytes() << ","
     << "wasted_memory:" << chunk->wasted_memory() << "}" << std::endl;
}

// static
void HeapLayoutTracer::PrintHeapLayout(std::ostream& os, Heap* heap) {
  for (PageIterator it = heap->new_space()->to_space().begin();
       it != heap->new_space()->to_space().end(); ++it) {
    PrintBasicMemoryChunk(os, *it, "to_space");
  }

  for (PageIterator it = heap->new_space()->from_space().begin();
       it != heap->new_space()->from_space().end(); ++it) {
    PrintBasicMemoryChunk(os, *it, "from_space");
  }

  OldGenerationMemoryChunkIterator it(heap);
  MemoryChunk* chunk;
  while ((chunk = it.next()) != nullptr) {
    PrintBasicMemoryChunk(os, chunk, chunk->owner()->name());
  }

  for (ReadOnlyPage* page : heap->read_only_space()->pages()) {
    PrintBasicMemoryChunk(os, page, "ro_space");
  }
}
}  // namespace internal
}  // namespace v8