/*
 * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
 * Copyright (c) 2020, 2022 SAP SE. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 *
 */

#include "memory/metaspace/chunklevel.hpp"
#include "memory/metaspace/blockTree.hpp"
#include "memory/resourceArea.hpp"
#include "utilities/debug.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/growableArray.hpp"
#include "utilities/ostream.hpp"

namespace metaspace {

// Needed to prevent linker errors on MacOS and AIX
const size_t BlockTree::MinWordSize;

#define NODE_FORMAT \
  "@" PTR_FORMAT \
  ": canary " INTPTR_FORMAT \
  ", parent " PTR_FORMAT \
  ", left " PTR_FORMAT \
  ", right " PTR_FORMAT \
  ", next " PTR_FORMAT \
  ", size %zu"

#define NODE_FORMAT_ARGS(n) \
  p2i(n), \
  (n)->_canary, \
  p2i((n)->_parent), \
  p2i((n)->_left), \
  p2i((n)->_right), \
  p2i((n)->_next), \
  (n)->_word_size

#ifdef ASSERT

// Tree verification

// This assert prints the tree too
#define tree_assert(cond, format, ...) \
  do { \
    if (!(cond)) { \
      tty->print("Error in tree @" PTR_FORMAT ": ", p2i(this)); \
      tty->print_cr(format, __VA_ARGS__); \
      tty->print_cr("Tree:"); \
      print_tree(tty); \
      assert(cond, format, __VA_ARGS__); \
    } \
  } while (0)

// Assert, prints tree and specific given node
#define tree_assert_invalid_node(cond, failure_node) \
  tree_assert(cond, "Invalid node: " NODE_FORMAT, NODE_FORMAT_ARGS(failure_node))

// walkinfo keeps a node plus the size corridor it and its children
//  are supposed to be in.
struct BlockTree::walkinfo {
  BlockTree::Node* n;
  int depth;
  size_t lim1; // (
  size_t lim2; // )
};

// Helper for verify()
void BlockTree::verify_node_pointer(const Node* n) const {
  tree_assert(os::is_readable_pointer(n),
              "Invalid node: @" PTR_FORMAT " is unreadable.", p2i(n));
  // If the canary is broken, this is either an invalid node pointer or
  // the node has been overwritten. Either way, print a hex dump, then
  // assert away.
  if (n->_canary != Node::_canary_value) {
    os::print_hex_dump(tty, (address)n, (address)n + sizeof(Node), 1);
    tree_assert(false, "Invalid node: @" PTR_FORMAT " canary broken or pointer invalid", p2i(n));
  }
}

void BlockTree::verify() const {
  // Traverse the tree and test that all nodes are in the correct order.

  MemRangeCounter counter;
  if (_root != nullptr) {

    ResourceMark rm;
    GrowableArray<walkinfo> stack;

    walkinfo info;
    info.n = _root;
    info.lim1 = 0;
    info.lim2 = SIZE_MAX;
    info.depth = 0;

    stack.push(info);

    while (stack.length() > 0) {
      info = stack.pop();
      const Node* n = info.n;

      verify_node_pointer(n);

      // Assume a (ridiculously large) edge limit to catch cases
      //  of badly degenerated or circular trees.
      tree_assert(info.depth < 10000, "too deep (%d)", info.depth);
      counter.add(n->_word_size);

      if (n == _root) {
        tree_assert_invalid_node(n->_parent == nullptr, n);
      } else {
        tree_assert_invalid_node(n->_parent != nullptr, n);
      }

      // check size and ordering
      tree_assert_invalid_node(n->_word_size >= MinWordSize, n);
      tree_assert_invalid_node(n->_word_size <= chunklevel::MAX_CHUNK_WORD_SIZE, n);
      tree_assert_invalid_node(n->_word_size > info.lim1, n);
      tree_assert_invalid_node(n->_word_size < info.lim2, n);

      // Check children
      if (n->_left != nullptr) {
        tree_assert_invalid_node(n->_left != n, n);
        tree_assert_invalid_node(n->_left->_parent == n, n);

        walkinfo info2;
        info2.n = n->_left;
        info2.lim1 = info.lim1;
        info2.lim2 = n->_word_size;
        info2.depth = info.depth + 1;
        stack.push(info2);
      }

      if (n->_right != nullptr) {
        tree_assert_invalid_node(n->_right != n, n);
        tree_assert_invalid_node(n->_right->_parent == n, n);

        walkinfo info2;
        info2.n = n->_right;
        info2.lim1 = n->_word_size;
        info2.lim2 = info.lim2;
        info2.depth = info.depth + 1;
        stack.push(info2);
      }

      // If node has same-sized siblings check those too.
      const Node* n2 = n->_next;
      while (n2 != nullptr) {
        verify_node_pointer(n2);
        tree_assert_invalid_node(n2 != n, n2); // catch simple circles
        tree_assert_invalid_node(n2->_word_size == n->_word_size, n2);
        counter.add(n2->_word_size);
        n2 = n2->_next;
      }
    }
  }

  // At the end, check that counters match
  // (which also verifies that we visited every node, or at least
  //  as many nodes as are in this tree)
  _counter.check(counter);
}

void BlockTree::zap_block(MetaBlock bl) {
  memset(bl.base(), 0xF3, bl.word_size() * sizeof(MetaWord));
}

void BlockTree::print_tree(outputStream* st) const {

  // Note: we do not print the tree indented, since I found that printing it
  //  as a quasi list is much clearer to the eye.
  // We print the tree depth-first, with stacked nodes below normal ones
  //  (normal "real" nodes are marked with a leading '+')
  if (_root != nullptr) {

    ResourceMark rm;
    GrowableArray<walkinfo> stack;

    walkinfo info;
    info.n = _root;
    info.depth = 0;

    stack.push(info);
    while (stack.length() > 0) {
      info = stack.pop();
      const Node* n = info.n;

      // Print node.
      st->print("%4d + ", info.depth);
      if (os::is_readable_pointer(n)) {
        st->print_cr(NODE_FORMAT, NODE_FORMAT_ARGS(n));
      } else {
        st->print_cr("@" PTR_FORMAT ": unreadable (skipping subtree)", p2i(n));
        continue; // don't print this subtree
      }

      // Print same-sized-nodes stacked under this node
      for (Node* n2 = n->_next; n2 != nullptr; n2 = n2->_next) {
        st->print_raw("       ");
        if (os::is_readable_pointer(n2)) {
          st->print_cr(NODE_FORMAT, NODE_FORMAT_ARGS(n2));
        } else {
          st->print_cr("@" PTR_FORMAT ": unreadable (skipping rest of chain).", p2i(n2));
          break; // stop printing this chain.
        }
      }

      // Handle simple circularities
      if (n == n->_right || n == n->_left || n == n->_next) {
        st->print_cr("@" PTR_FORMAT ": circularity detected.", p2i(n));
        return; // stop printing
      }

      // Handle children.
      if (n->_right != nullptr) {
        walkinfo info2;
        info2.n = n->_right;
        info2.depth = info.depth + 1;
        stack.push(info2);
      }
      if (n->_left != nullptr) {
        walkinfo info2;
        info2.n = n->_left;
        info2.depth = info.depth + 1;
        stack.push(info2);
      }
    }

  } else {
    st->print_cr("<no nodes>");
  }
}

#endif // ASSERT

} // namespace metaspace
