/*
 * Copyright (c) 2001, 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
 *  @bug 4359247
 *  @summary Breakpoints on multiple threads have problems.
 *
 *  @author tbell, jjh
 *
 *  @build TestScaffold VMConnection TargetListener TargetAdapter
 *  @run compile -g MultiBreakpointsTest.java
 *  @run main MultiBreakpointsTest
 */

/*
 * This test runs a debuggee with n threads each of which just loops
 * doing some printlns and calling a method.  The debugger sets
 * bkpts on these methods and verifies that they are all hit.
 * The default number of threads is 4.  To change it to say 10,
 * pass this to the testcase on the cmd line:
 *      -Dnthreads=10
 * The current max allowed value of nthreads is 30.
 * You can also do this, for example,
 *     -Dnhits=30
 * to change the number of times the bkpts are to be hit from
 * the default of 100 to 30.
 */
import com.sun.jdi.*;
import com.sun.jdi.event.*;
import com.sun.jdi.request.*;

import java.util.*;

/********** target program **********/

import java.io.*;
import java.text.*;

class MultiBreakpointsTarg {

    MultiBreakpointsTarg(int numThreads, int numHits) {
        for (int ii = 0; ii < numThreads; ii++) {
            console(ii, numHits);
        }
    }

    public static void main(String args[]) {

        int nthreads;
        int nhits;
        String nStr = System.getProperty("nthreads");

        if (nStr == null) {
            throw new RuntimeException("nthreads = null in debuggee");
        }
        nthreads = Integer.parseInt(nStr);

        nStr = System.getProperty("nhits");
        if (nStr == null) {
            throw new RuntimeException("nhits = null in debuggee");
        }
        nhits = Integer.parseInt(nStr);

        System.out.println("Debuggee: nthreads = " + nthreads + ", nhits = " + nhits);

        MultiBreakpointsTarg ptr = new MultiBreakpointsTarg(nthreads, nhits);

//         for (int i = 0; i < nthreads; i++) {
//             ptr.console(i);
//         }
    }

    // The brute force approach for simplicity - don't use reflection
    // nor set thread specific bkpts.  Use of those features would
    // make for interesting tests too, and maybe would prove that
    // we don't really have to bother doing it this dumb way.

    void bkpt0() {}
    void bkpt1() {}
    void bkpt2() {}
    void bkpt3() {}
    void bkpt4() {}
    void bkpt5() {}
    void bkpt6() {}
    void bkpt7() {}
    void bkpt8() {}
    void bkpt9() {}
    void bkpt10() {}
    void bkpt11() {}
    void bkpt12() {}
    void bkpt13() {}
    void bkpt14() {}
    void bkpt15() {}
    void bkpt16() {}
    void bkpt17() {}
    void bkpt18() {}
    void bkpt19() {}
    void bkpt20() {}
    void bkpt21() {}
    void bkpt22() {}
    void bkpt23() {}
    void bkpt24() {}
    void bkpt25() {}
    void bkpt26() {}
    void bkpt27() {}
    void bkpt28() {}
    void bkpt29() {}

    void console(final int num, final int nhits) {
        final InputStreamReader isr = new InputStreamReader(System.in);
        final BufferedReader    br  = new BufferedReader(isr);

        // Create the threads
        //
        //final String threadName = "DebuggeeThread: " + num;
        final String threadName = "" + num;
        Thread thrd = new Thread( threadName ) {
                public void run() {
                    synchronized( isr ) {
                        boolean done = false;
                        try {
                            // For each thread, run until numHits bkpts have been hit
                            for( int i = 0; i < nhits; i++ ) {
                                // This is a tendril from the original jdb test.
                                // It could probably be deleted.
                                System.out.println("Thread " + threadName + " Enter a string: ");
                                String s = "test" + num;
                                switch (num) {
                                case 0: bkpt0(); break;
                                case 1: bkpt1(); break;
                                case 2: bkpt2(); break;
                                case 3: bkpt3(); break;
                                case 4: bkpt4(); break;
                                case 5: bkpt5(); break;
                                case 6: bkpt6(); break;
                                case 7: bkpt7(); break;
                                case 8: bkpt8(); break;
                                case 9: bkpt9(); break;
                                case 10: bkpt10(); break;
                                case 11: bkpt11(); break;
                                case 12: bkpt12(); break;
                                case 13: bkpt13(); break;
                                case 14: bkpt14(); break;
                                case 15: bkpt15(); break;
                                case 16: bkpt16(); break;
                                case 17: bkpt17(); break;
                                case 18: bkpt18(); break;
                                case 19: bkpt19(); break;
                                case 20: bkpt20(); break;
                                case 21: bkpt21(); break;
                                case 22: bkpt22(); break;
                                case 23: bkpt23(); break;
                                case 24: bkpt24(); break;
                                case 25: bkpt25(); break;
                                case 26: bkpt26(); break;
                                case 27: bkpt27(); break;
                                case 28: bkpt28(); break;
                                case 29: bkpt29(); break;
                                }
                                System.out.println("Thread " + threadName + " You entered : " + s);

                                if( s.compareTo( "quit" ) == 0 )
                                    done = true;
                            }
                        } catch(Exception e) {
                            System.out.println("WOOPS");
                        }
                    }
                }
            };
        thrd.setPriority(Thread.MAX_PRIORITY-1);
        thrd.start();
    }
}

/********** test program **********/

public class MultiBreakpointsTest extends TestScaffold {
    ReferenceType targetClass;
    ThreadReference mainThread;
    EventRequestManager erm;

    static int nthreads;
    static int nhits;

    BreakpointRequest bkpts[];
    int hits[];

    MultiBreakpointsTest (String args[]) {
        super(args);
        bkpts = new BreakpointRequest[nthreads];
        hits = new int[nthreads];
    }

    public static void main(String[] args)      throws Exception {
        String countStr = System.getProperty("nthreads");
        if (countStr == null) {
            nthreads = 4;
        } else {
            nthreads = Integer.parseInt(countStr);
        }
        if ( nthreads > 30) {
            throw new RuntimeException("nthreads is greater than 30: " + nthreads);
        }
        countStr = System.getProperty("nhits");
        if (countStr == null) {
            nhits = 100;
        } else {
            nhits = Integer.parseInt(countStr);
        }

        args = new String[] { "-J-Dnthreads=" + nthreads, "-J-Dnhits=" + nhits} ;
        new MultiBreakpointsTest(args).startTests();
    }

    /********** event handlers **********/


    public void breakpointReached(BreakpointEvent event) {
        BreakpointRequest req = (BreakpointRequest)event.request();
        for ( int ii = 0; ii < nthreads; ii++) {
            if (req == bkpts[ii]) {
                println("Hit bkpt on thread: " +ii+ ": " +  ++hits[ii]);
                break;
            }
        }
    }


    public BreakpointRequest setBreakpoint(String clsName,
                                           String methodName,
                                           String methodSignature) {
        ReferenceType rt = findReferenceType(clsName);
        if (rt == null) {
            rt = resumeToPrepareOf(clsName).referenceType();
        }

        Method method = findMethod(rt, methodName, methodSignature);
        if (method == null) {
            throw new IllegalArgumentException("Bad method name/signature");
        }
        BreakpointRequest bpr = erm.createBreakpointRequest(method.location());
        bpr.setSuspendPolicy(EventRequest.SUSPEND_ALL);
        //bpr.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
        bpr.enable();
        return bpr;
    }

    /********** test core **********/

    protected void runTests() throws Exception {
        /*
         * Get to the top of main()
         * to determine targetClass and mainThread
         */

        BreakpointEvent bpe = startToMain("MultiBreakpointsTarg");

        targetClass = bpe.location().declaringType();
        mainThread = bpe.thread();
        erm = vm().eventRequestManager();

        for (int ii = 0 ; ii < nthreads; ii++) {
            bkpts[ii] = setBreakpoint("MultiBreakpointsTarg",
                              "bkpt" + ii,
                              "()V");
        }
        /*
         * resume the target listening for events
         */
        listenUntilVMDisconnect();

        for ( int ii = 0; ii < nthreads; ii++) {
            if (hits[ii] != nhits) {
                failure("FAILED: Expected " + nhits + " breakpoints for thread " + ii + " but only got " + hits[ii]);
            }
        }

        /*
         * deal with results of test
         * if anything has called failure("foo") testFailed will be true
         */
        if (!testFailed) {
            println("MultiBreakpointsTest: passed");
        } else {
            throw new Exception("MultiBreakpointsTest: failed");
        }
    }
}
