001/*
002 * jPOS Project [http://jpos.org]
003 * Copyright (C) 2000-2026 jPOS Software SRL
004 *
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Affero General Public License as
007 * published by the Free Software Foundation, either version 3 of the
008 * License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013 * GNU Affero General Public License for more details.
014 *
015 * You should have received a copy of the GNU Affero General Public License
016 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
017 */
018
019package org.jpos.util;
020
021import org.jpos.iso.ISOUtil;
022
023import java.io.PrintStream;
024import java.util.Collection;
025import java.util.Iterator;
026import java.util.LinkedHashMap;
027
028/**
029 * Simple Profiler
030 * @author Alejandro P. Revilla
031 * @author David D. Bergert
032 * @version $Id$
033 */
034public class Profiler implements Loggeable {
035    long start, partial;
036    LinkedHashMap<String, Entry> events;
037    public static final int TO_MILLIS = 1000000;
038
039    public Profiler () {
040        super();
041        reset();
042    }
043    /**
044     * reset timers
045     */
046    public void reset() {
047        start = partial = System.nanoTime();
048        events = new LinkedHashMap<>();
049    }
050    /**
051     * mark checkpoint
052     * @param detail checkpoint information
053     */
054    @SuppressWarnings("unchecked")
055    public synchronized void checkPoint (String detail) {
056        long now = System.nanoTime();
057        Entry e = new Entry();
058        e.setDurationInNanos(now - partial);
059        e.setTotalDurationInNanos(now - start);
060        if (events.containsKey(detail)) {
061            for (int i=1; ;i++) {
062                String d = detail + "-" + i;
063                if (!events.containsKey (d)) {
064                    detail = d;
065                    break;
066                }
067            }
068        }
069        e.setEventName(detail);
070        events.put (detail, e);
071        partial = now;
072    }
073    /**
074     * @return total elapsed time since last reset
075     */
076    public long getElapsed() {
077        return System.nanoTime() - start;
078    }
079    public long getElapsedInMillis() {
080        return getElapsed() / TO_MILLIS;
081    }
082    /**
083     * @return parcial elapsed time since last reset
084     */
085    public long getPartial() {
086        return System.nanoTime() - partial;
087    }
088    public long getPartialInMillis() {
089        return getPartial() / TO_MILLIS;
090    }
091    public void dump (PrintStream p, String indent) {
092        String inner = indent + "  ";
093        if (!events.containsKey("end"))
094            checkPoint ("end");
095        Collection c = events.values();
096        Iterator iter = c.iterator();
097        p.println (indent + "<profiler>");
098        while (iter.hasNext()) 
099            p.println (inner + ISOUtil.normalize(iter.next().toString()));
100        p.println (indent + "</profiler>");
101    }
102    public LinkedHashMap<String, Entry> getEvents() {
103        return events;
104    }
105    public Entry getEntry(String eventName) {
106         return events.get(eventName);
107    }
108    public void reenable() {
109        events.remove("end");
110    }
111    public static class Entry  {
112        String  eventName;
113        long    duration;
114        long    totalDuration;          
115        public Entry() {
116           eventName     = "";
117           duration      = 0L;
118           totalDuration = 0L;        
119        }
120        public void setEventName (String myEvent) {
121            this.eventName = myEvent;
122        }
123        public String getEventName () {
124            return eventName;
125        }    
126        public void setDurationInNanos (long duration) {
127            this.duration = duration;
128        }
129        public long getDuration () {
130            return duration / TO_MILLIS;
131        }
132        public long getDurationInNanos() {
133            return duration;
134        }
135        public void setTotalDurationInNanos (long totalDuration) {
136            this.totalDuration = totalDuration;
137        }
138        public long getTotalDuration () {
139            return totalDuration / TO_MILLIS;
140        }
141        public long getTotalDurationInNanos () {
142            return totalDuration;
143        }
144        public String toString()  {
145            StringBuilder sb = new StringBuilder (eventName);
146            sb.append (" [");
147            sb.append (getDuration());
148            sb.append ('.');
149            sb.append (duration % TO_MILLIS / 100000);
150            sb.append ('/');
151            sb.append (getTotalDuration ());
152            sb.append ('.');
153            sb.append (totalDuration % TO_MILLIS / 100000);
154            sb.append (']');
155            return sb.toString();
156        }            
157    }
158}