/******************************************************************************* * Copyright (c) 2015 IBH SYSTEMS GmbH. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBH SYSTEMS GmbH - initial API and implementation *******************************************************************************/ package org.eclipse.packagedrone.utils.profiler; import java.io.File; import java.lang.reflect.Proxy; import java.time.Duration; import java.time.Instant; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.concurrent.Callable; import org.eclipse.packagedrone.utils.profiler.internal.ProfilerInvocationHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public final class Profile { private final static Logger logger = LoggerFactory.getLogger ( Profile.class ); private Profile () { } public static interface Handle extends AutoCloseable { @Override public void close (); public Handle createChild ( String operation ); /** * Mark the beginning of a new task * * @param taskName * the name of the task */ public void task ( String taskName ); } private static Handle NOP = new Handle () { @Override public void close () { } @Override public Handle createChild ( final String operation ) { return NOP; } @Override public void task ( final String operation ) { } }; public static class DurationEntry { private final String operation; private final Duration duration; private List<DurationEntry> entries; public DurationEntry ( final String operation, final Duration duration ) { this.operation = operation; this.duration = duration; } public DurationEntry ( final String operation, final Duration duration, final List<DurationEntry> entries ) { this.operation = operation; this.duration = duration; this.entries = entries; } public String getOperation () { return this.operation; } public Duration getDuration () { return this.duration; } public List<DurationEntry> getEntries () { return this.entries; } } public static class HandleImpl implements Handle { private final HandleImpl parent; private final Instant start; private final String operation; private final LinkedList<DurationEntry> entries = new LinkedList<> (); private Instant taskStart; private String taskName; public HandleImpl ( final String operation ) { this ( operation, null ); } public HandleImpl ( final String operation, final HandleImpl parent ) { this.parent = parent; this.operation = operation; activeProfilers.set ( this ); this.start = Instant.now (); } @Override public Handle createChild ( final String operation ) { return new HandleImpl ( operation, this ); } @Override public String toString () { return "[" + this.operation + "]"; } @Override public void task ( final String taskName ) { debug ( "task : %s", taskName ); final Instant now = Instant.now (); closeTask ( now ); this.taskStart = now; this.taskName = taskName; } private void closeTask ( final Instant now ) { if ( this.taskStart != null ) { final Duration duration = Duration.between ( this.taskStart, now ); final DurationEntry entry = new DurationEntry ( this.taskName, duration, Collections.emptyList () ); this.entries.add ( entry ); } } @Override public void close () { final Instant now = Instant.now (); closeTask ( now ); final Duration duration = Duration.between ( this.start, now ); final DurationEntry entry = new DurationEntry ( this.operation, duration, this.entries ); activeProfilers.set ( this.parent ); if ( this.parent != null ) { debug ( "close : %s : %s", this.parent, this.operation ); this.parent.entries.add ( entry ); } else { debug ( "dump : %s", this.operation ); final ProfileDataHandler handler = getHandler (); if ( handler != null ) { handler.handle ( entry ); } } } } private static ThreadLocal<Handle> activeProfilers = new ThreadLocal<> (); private static ProfileDataHandler HANDLER; static { final String value = System.getProperty ( "drone.profile" ); if ( value != null ) { if ( "true".equalsIgnoreCase ( value ) ) { HANDLER = DumpProfileDataHandler.INSTANCE; } else if ( value.startsWith ( "xml:" ) ) { final String[] toks = value.split ( ":", 2 ); if ( toks.length == 2 ) { try { HANDLER = new XmlProfileDataHandler ( new File ( toks[1] ).toPath () ); } catch ( final Throwable e ) { logger.error ( "Failed to initialize XML profile data handler", e ); } } } if ( HANDLER == null ) { debug ( "Failed to initialize profile data handler. Activated as: '%s'", value ); } } } private static boolean isActive () { return HANDLER != null; } public static Handle start ( final Object service, final String methodName ) { return start ( makeOperation ( service, methodName ) ); } @SuppressWarnings ( "resource" ) public static Handle start ( final String operation ) { if ( !isActive () ) { return NOP; } else { final Handle handle = activeProfilers.get (); if ( handle == null ) { debug ( "create : %s", operation ); return new HandleImpl ( operation ); } else { debug ( "child : %s : %s", handle, operation ); return handle.createChild ( operation ); } } } private static String makeOperation ( final Object service, final String methodName ) { return String.format ( "%s.%s", service.getClass ().getName (), methodName ); } public static <T> T call ( final Object service, final String methodName, final Callable<T> call ) { return call ( makeOperation ( service, methodName ), call ); } public static <T> T call ( final String operation, final Callable<T> call ) { try ( Handle handle = start ( operation ) ) { return call.call (); } catch ( final Exception e ) { throw new RuntimeException ( e ); } } public static void run ( final Object service, final String methodName, final Runnable run ) { run ( makeOperation ( service, methodName ), run ); } public static void run ( final String operation, final Runnable run ) { call ( operation, () -> { run.run (); return null; } ); } public static <T> T proxy ( final T service, final Class<T> iface ) { if ( service == null ) { return null; } if ( isActive () ) { return iface.cast ( Proxy.newProxyInstance ( service.getClass ().getClassLoader (), new Class<?>[] { iface }, new ProfilerInvocationHandler ( service ) ) ); } else { return service; } } private static void debug ( final String format, final Object... args ) { if ( Boolean.getBoolean ( "drone.profile.debug" ) ) { System.out.print ( Thread.currentThread ().getName () ); System.out.print ( ": " ); System.out.format ( format, args ); System.out.println (); } } private static ProfileDataHandler getHandler () { return HANDLER; } }