/*
 * Copyright (c) 2014, 2022, Oracle and/or its affiliates. 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.
 */

/**
 * @test SharedBaseAddress
 * @bug 8265705 8267351
 * @summary Test variety of values for SharedBaseAddress, making sure
 *          VM handles normal values as well as edge values w/o a crash.
 * @requires vm.cds
 * @library /test/lib
 * @run driver SharedBaseAddress 0
 */

/**
 * @test SharedBaseAddress
 * @bug 8265705 8267351
 * @summary Test variety of values for SharedBaseAddress, making sure
 *          VM handles normal values as well as edge values w/o a crash.
 * @requires vm.cds
 * @library /test/lib
 * @run driver SharedBaseAddress 1
 */

/**
 * @test SharedBaseAddress
 * @bug 8265705 8267351
 * @summary Test variety of values for SharedBaseAddress, making sure
 *          VM handles normal values as well as edge values w/o a crash.
 * @requires vm.cds
 * @requires vm.bits == 64
 * @library /test/lib
 * @run driver SharedBaseAddress 0 provoke
 */

/**
 * @test SharedBaseAddress
 * @bug 8265705 8267351
 * @summary Test variety of values for SharedBaseAddress, making sure
 *          VM handles normal values as well as edge values w/o a crash.
 * @requires vm.cds
 * @requires vm.bits == 64
 * @library /test/lib
 * @run driver SharedBaseAddress 1 provoke
 */

import jdk.test.lib.cds.CDSTestUtils;
import jdk.test.lib.cds.CDSOptions;
import jdk.test.lib.Platform;
import jdk.test.lib.process.OutputAnalyzer;

public class SharedBaseAddress {

    // shared base address test table for {32, 64}bit VM
    private static final String[] testTableShared = {
        "1g", "0",
        "1", "64k", "64M",
        "0xfff80000",         // archive top wraps around 32-bit address space
        "0xffffffff",         // archive bottom wraps around 32-bit address space -- due to align_up()
        "0"                   // always let OS pick the base address at runtime (ASLR for CDS archive)
    };

    // shared base address test table for 64bit VM only
    private static final String[] testTable64 = {
        "8g", "64g","512g", "4t",
        "32t", "128t",
        "0x800001000",        // Default base address + 1 page - probably valid but unaligned to metaspace alignment, see JDK 8247522
        "0xfffffffffff00000", // archive top wraps around 64-bit address space
        "0xffffffffffffffff", // archive bottom wraps around 64-bit address space -- due to align_up()
        "0x00007ffffff00000", // end of archive will go past the end of user space on linux/x64
        "0x500000000"         // (20g) below 32g at a 4g aligned address, but cannot be expressed with a logical
                              //    immediate on aarch64 (0x5_0000_0000) (see JDK-8265705)
    };

    // failed pattern
    private static String failedPattern = "os::release_memory\\(0x[0-9a-fA-F]*,\\s[0-9]*\\)\\sfailed";

    public static void test(String[] args, String[] testTable) throws Exception {
        int mid = testTable.length / 2;
        int start = args[0].equals("0") ? 0 : mid;
        int end   = args[0].equals("0") ? mid : testTable.length;
        boolean provoke = (args.length > 1 && args[1].equals("provoke"));

        // provoke == true: we want to increase the chance that mapping the generated archive at the designated base
        // succeeds, to test Klass pointer encoding at that weird location. We do this by sizing heap + class space
        // small, and by switching off compressed oops.

        // provoke == false:  we just go with default parameters. This is more of a test of
        // CDS' ability to recover if mapping at runtime fails.
        for (int i = start; i < end; i++) {
            String testEntry = testTable[i];
            String filename = "SharedBaseAddress-base" + testEntry + ".jsa";
            System.out.println("sharedBaseAddress = " + testEntry);
            CDSOptions opts = (new CDSOptions())
                        .setArchiveName(filename)
                        .addPrefix("-XX:SharedBaseAddress=" + testEntry)
                        .addPrefix("-Xlog:cds=debug")
                        .addPrefix("-Xlog:cds+reloc=debug")
                        .addPrefix("-Xlog:nmt=debug")
                        .addPrefix("-Xlog:os=debug")
                        .addPrefix("-Xlog:gc+metaspace")
                        .addPrefix("-XX:NativeMemoryTracking=detail");

            if (provoke) {
                opts.addPrefix("-Xmx128m")
                    .addPrefix("-XX:CompressedClassSpaceSize=32m")
                    .addPrefix("-XX:-UseCompressedOops");
            }
            if (Platform.isDebugBuild()) {
                // Make VM start faster in debug build with large heap.
                opts.addPrefix("-XX:-ZapUnusedHeapArea");
            }

            CDSTestUtils.createArchiveAndCheck(opts);
            OutputAnalyzer out = CDSTestUtils.runWithArchiveAndCheck(opts);
            if (testEntry.equals("0")) {
                out.shouldContain("Archive(s) were created with -XX:SharedBaseAddress=0. Always map at os-selected address.")
                   .shouldContain("Try to map archive(s) at an alternative address")
                   .shouldNotMatch(failedPattern);
            }
        }
    }

    public static void main(String[] args) throws Exception {
        test(args, testTableShared);
        if (Platform.is64bit()) {
            test(args, testTable64);
        }
    }
}