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 /** Conversion factor from nanoseconds to milliseconds. */ 038 public static final int TO_MILLIS = 1000000; 039 040 /** Default constructor. */ 041 public Profiler () { 042 super(); 043 reset(); 044 } 045 /** 046 * reset timers 047 */ 048 public void reset() { 049 start = partial = System.nanoTime(); 050 events = new LinkedHashMap<>(); 051 } 052 /** 053 * mark checkpoint 054 * @param detail checkpoint information 055 */ 056 @SuppressWarnings("unchecked") 057 public synchronized void checkPoint (String detail) { 058 long now = System.nanoTime(); 059 Entry e = new Entry(); 060 e.setDurationInNanos(now - partial); 061 e.setTotalDurationInNanos(now - start); 062 if (events.containsKey(detail)) { 063 for (int i=1; ;i++) { 064 String d = detail + "-" + i; 065 if (!events.containsKey (d)) { 066 detail = d; 067 break; 068 } 069 } 070 } 071 e.setEventName(detail); 072 events.put (detail, e); 073 partial = now; 074 } 075 /** 076 * Returns the total elapsed time since the last reset. 077 * @return total elapsed time since last reset 078 */ 079 public long getElapsed() { 080 return System.nanoTime() - start; 081 } 082 /** Returns total elapsed time in milliseconds since the last reset. 083 * @return elapsed time in milliseconds 084 */ 085 public long getElapsedInMillis() { 086 return getElapsed() / TO_MILLIS; 087 } 088 /** 089 * Returns the partial elapsed time since the last checkpoint. 090 * @return partial elapsed time since last reset 091 */ 092 public long getPartial() { 093 return System.nanoTime() - partial; 094 } 095 /** Returns partial elapsed time in milliseconds since the last checkpoint. 096 * @return partial elapsed time in milliseconds 097 */ 098 public long getPartialInMillis() { 099 return getPartial() / TO_MILLIS; 100 } 101 /** Dumps profiler results to the given print stream. 102 * @param p the output stream 103 * @param indent indent prefix 104 */ 105 public void dump (PrintStream p, String indent) { 106 String inner = indent + " "; 107 if (!events.containsKey("end")) 108 checkPoint ("end"); 109 Collection c = events.values(); 110 Iterator iter = c.iterator(); 111 p.println (indent + "<profiler>"); 112 while (iter.hasNext()) 113 p.println (inner + ISOUtil.normalize(iter.next().toString())); 114 p.println (indent + "</profiler>"); 115 } 116 /** Returns all profiler events collected since the last reset. 117 * @return ordered map of event name to Entry 118 */ 119 public LinkedHashMap<String, Entry> getEvents() { 120 return events; 121 } 122 /** Returns the profiler entry for the given event name. 123 * @param eventName the event name 124 * @return the corresponding Entry, or {@code null} if not found 125 */ 126 public Entry getEntry(String eventName) { 127 return events.get(eventName); 128 } 129 /** Removes the "end" checkpoint so profiling can continue. */ 130 public void reenable() { 131 events.remove("end"); 132 } 133 /** A single timed checkpoint entry recorded by the Profiler. */ 134 public static class Entry { 135 String eventName; 136 long duration; 137 long totalDuration; 138 /** Default constructor — initialises all fields to empty/zero. */ 139 public Entry() { 140 eventName = ""; 141 duration = 0L; 142 totalDuration = 0L; 143 } 144 /** Sets the event name for this entry. 145 * @param myEvent the event label 146 */ 147 public void setEventName (String myEvent) { 148 this.eventName = myEvent; 149 } 150 /** Returns the event name. 151 * @return event name 152 */ 153 public String getEventName () { 154 return eventName; 155 } 156 /** Sets the entry duration in nanoseconds. 157 * @param duration duration in nanoseconds 158 */ 159 public void setDurationInNanos (long duration) { 160 this.duration = duration; 161 } 162 /** Returns the entry duration in milliseconds. 163 * @return duration in milliseconds 164 */ 165 public long getDuration () { 166 return duration / TO_MILLIS; 167 } 168 /** Returns the raw entry duration in nanoseconds. 169 * @return duration in nanoseconds 170 */ 171 public long getDurationInNanos() { 172 return duration; 173 } 174 /** Sets the total elapsed duration in nanoseconds. 175 * @param totalDuration total duration in nanoseconds 176 */ 177 public void setTotalDurationInNanos (long totalDuration) { 178 this.totalDuration = totalDuration; 179 } 180 /** Returns the total elapsed duration in milliseconds. 181 * @return total duration in milliseconds 182 */ 183 public long getTotalDuration () { 184 return totalDuration / TO_MILLIS; 185 } 186 /** Returns the raw total elapsed duration in nanoseconds. 187 * @return total duration in nanoseconds 188 */ 189 public long getTotalDurationInNanos () { 190 return totalDuration; 191 } 192 public String toString() { 193 StringBuilder sb = new StringBuilder (eventName); 194 sb.append (" ["); 195 sb.append (getDuration()); 196 sb.append ('.'); 197 sb.append (duration % TO_MILLIS / 100000); 198 sb.append ('/'); 199 sb.append (getTotalDuration ()); 200 sb.append ('.'); 201 sb.append (totalDuration % TO_MILLIS / 100000); 202 sb.append (']'); 203 return sb.toString(); 204 } 205 } 206}