#
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
#
# Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
#
# The contents of this file are subject to the terms of either the GNU
# General Public License Version 2 only ("GPL") or the Common Development
# and Distribution License("CDDL") (collectively, the "License").  You
# may not use this file except in compliance with the License.  You can
# obtain a copy of the License at
# https://oss.oracle.com/licenses/CDDL+GPL-1.1
# or LICENSE.txt.  See the License for the specific
# language governing permissions and limitations under the License.
#
# When distributing the software, include this License Header Notice in each
# file and include the License file at LICENSE.txt.
#
# GPL Classpath Exception:
# Oracle designates this particular file as subject to the "Classpath"
# exception as provided by Oracle in the GPL Version 2 section of the License
# file that accompanied this code.
#
# Modifications:
# If applicable, add the following below the License Header, with the fields
# enclosed by brackets [] replaced by your own identifying information:
# "Portions Copyright [year] [name of copyright owner]"
#
# Contributor(s):
# If you wish your version of this file to be governed by only the CDDL or
# only the GPL Version 2, indicate your decision by adding "[Contributor]
# elects to include this software in this distribution under the [CDDL or GPL
# Version 2] license."  If you don't indicate a single choice of license, a
# recipient has the option to distribute your version of this file under
# either the CDDL, the GPL Version 2 or to extend the choice of license to
# its licensees as provided above.  However, if you add GPL Version 2 code
# and therefore, elected the GPL Version 2 license, then the option applies
# only if the new code is made subject to such option by the copyright
# holder.
#

Extending the timer system

I have a couple of concerns with the current system:

1. It could easily get a bit unwieldy as we add timing points, 
   since we would need to enable large numbers of timing points
   for different experiments.  This leads to considering an
   idea of timer groups.

2. We need to log times from multiple threads, and we do
   not want BEGIN_A/BEGIN_A/END_A/END_A sequences where we
   cannot tell which END goes with which begin.  This suggests
   creating per-thread TimerLog objects, and then aggregating the
   data.

This has led to the development of the newtimer package.

How do we determine which TimerLog to use?
    The idea here is that each TimerLog instance records data 
    separately, but shares all configuration IDs and enabled/disabled
    state.  This allows us to easily implement per-thread tracing.

Basic client API:
========================================================================
interface TimerManager {
    /** Get the standard Timers and TimerGroups that we support.
     */
    TimerPoints points() ;

    /** Get the ORB's timer factory.  Each ORB instance has its own
     * TimerFactory.
     */
    TimerFactory factory() ;

    /** Register event handlers with this controller.
     */
    TimerController controller() ; // set to a default controller

    /** Override the ORB's default controller.  May not really
     * need this.
     */
    void controller( TimerController controller ) ;
}

// ORB code basically looks like this internally:
tp.enterFoo() ;
try {
    // do Foo
} finally {
    tp.exitFoo()
}

// Basic setup
ORB orb = ...
TimerManager tm = orb.getTimerManager() ;
TimerFactory tf = tm.factory() ;
TimerPoints tp = tm.points() ;
TimerController controller = tm.controller() ; 
StatsEventHandler seh = tf.makeStatsEventHandler() ;
controller.register( seh ) ;

// enable all desired Timers and Timer Groups
// You can also create Timers and TimerGroups for use in a test
// by using the TimerFactory.
// Probably easiest to create a TimerGroup as needed to
// contain all Timers and TimerGroups of interest for the test

tp.Foo().enable() ;

// run the test

tp.Foo().disable() ;

// Now look at the results:
Map<Timer,Statistics> result = seh.stats() ;

// To gather new data:
seh.clear() ;

========================================================================
We generate TimerPoints from a text file, which also includes
all of the group relationships.   Here is a simple example: 
(Note: require define before use)

(com.sun.corba.se.impl.timer TimerPoints 
 (timer FOO "The FOO timer")
 (timer BAR "The BAR timer")
 (timer BAZ "The BAZ timer")
 (timer BAX "The BAX timer")
 (timer BAY "The BAY timer") 
 (group GAR "The GAR timer group"(FOO BAR))
 (group GAP "The GAP timer group"(BAR BAZ))
 (group FRO "The FRO timer group"(GAR BAY)))

This generates the code needed to fully initialize a 
TimerFactory.  We just allocate the TimerFactory in a static.
The TimerFactory is then available.

Something like:

package com.sun.corba.se.impl.timer ;

class TimerPoints {
    private final ORB orb ;
    private final TimerManager tm ;

    public TimerPoints( ORB orb ) {
	this.orb = orb ;
	tm = orb.getTimerManager() ;
	TimerFactory factory = tm.getTimerFactory() ;

	FOO = factory.makeTimer( "FOO" ) ;
	BAR = factory.makeTimer( "BAR" ) ;
	BAZ = factory.makeTimer( "BAZ" ) ;
	BAX = factory.makeTimer( "BAX" ) ;
	BAY = factory.makeTimer( "BAY" ) ;

	GAR = factory.makeTimerGroup( "GAR" ) ;
	GAR.contents().add( FOO ) ;
	GAR.contents().add( BAR ) ;

	GAP = factory.makeTimerGroup( "GAP" ) ;
	GAP.add( BAR ) ;
	GAP.add( BAZ ) ;

	FRO = factory.makeTimerGroup( "FRO" ) ;
	FRO.add( GAR ) ;
	FRO.add( BAY ) ;
    }

    public final Timer FOO ;

    void enterFoo() {
	tm.getController().enter( FOO ) ;
    }

    void exitFoo() {
	tm.getController().exit( FOO ) ;
    }

    // ibid for BAR BAZ BAX and BAY

    public final Timer BAR ;
    public final Timer BAZ ;
    public final Timer BAX ;
    public final Timer BAY ;

    public final TimerGroup GAR ;
    public final TimerGroup GAP ;
    public final TimerGroup FRO ;
}

Extensions
==========================================================================
1. Other kinds of stats handlers (e.g. decile distribution).
2. Use log handler to get tracing information.
3. Create Timers wherever needed, and use them as markers in a 
   log to find interesting data.
4. ORB config can add interesting Timers based on configuration.
   The main one I'm thinking of here is for interceptors: automatically
   generate timer points for interceptors, and add them to an
   interceptor group (that can be in TimerPoints).
5. Generate proxies for SPI interfaces (either dynamic, or via codegen for
   greater efficiency), and create a Timer for each method.  The Proxy
   just calls enter/exit before/after the method executes.
6. May want to extend stats to support threshholds and event generation
   (e.g. detecting busy connections).  May want to keep only a running
   average.
7. Can use stats for extending ORB monitoring data.

Merging Data.
==========================================================================
We probably want a way to merge all of the per-thread timer logs into a
single sorted file.  For example, suppose we have:

T1: 
    0:	B_A		23
    1:	B_A		29
    2:	E_A		37
    3:	E_A		45

T2:
    0:	B_A		25
    1:	E_A		33


In T1, 1 and 2 represent a recursive call, and 0 and 3 are an interval around 1 and 2.
This is usually the case w/o threads, although it is certainly possible to construct 
cases where stack order is not followed.  Then we need to turn each file of timer
points into begin/end intervals ordered by start time and merge-sort the results into
a single data set.  This results in:

T1.0:	B_A	23	E_A	45	22
T2.0:	B_A	25	E_A	33	 8
T1.1:	B_A	29	E_A	37	 8

Using timers for internal purposes

Basic idea: 

1. Use TimerFactoryBuilder to get a TimerFactory to use for a related 
   collections of Timer points.  Current examples include: TPs for
   benchmarking (large set with complex groups), TPs for message
   arrival timer interval measurement (only one point).
2. Use TimerFactory to set up the Timers.
3. Create TimerLogs for each event stream we need to collect.
   For example, message arrival measurement requires one TimerLog
   per connection.

