@@ -133,6 +133,10 @@ private void push(Object o) {
133133 }
134134
135135 private final AwkSettings settings ;
136+ private final boolean profiling ;
137+ private final Map <Opcode , ProfilingReport .Accumulator > tupleProfilingStats ;
138+ private final Map <String , ProfilingReport .Accumulator > functionProfilingStats ;
139+ private final Deque <ActiveFunction > activeProfilingFunctions ;
136140 private boolean inputSourceFilelistAssignmentsApplied ;
137141 private InputSource resolvedInputSource ;
138142 private AwkExpression installedEvalExpression ;
@@ -158,9 +162,34 @@ public AVM() {
158162 */
159163 public AVM (final AwkSettings parameters ,
160164 final Map <String , JawkExtension > extensionInstances ) {
165+ this (parameters , extensionInstances , false );
166+ }
167+
168+ /**
169+ * Construct the interpreter, optionally enabling runtime profiling.
170+ *
171+ * @param parameters The parameters affecting the behavior of the
172+ * interpreter.
173+ * @param extensionInstances Map of the extensions to load
174+ * @param profilingEnabled Whether to collect profiling statistics
175+ */
176+ public AVM (
177+ final AwkSettings parameters ,
178+ final Map <String , JawkExtension > extensionInstances ,
179+ final boolean profilingEnabled ) {
161180 this .settings = parameters != null ? parameters : AwkSettings .DEFAULT_SETTINGS ;
162181 this .extensionInstances = extensionInstances == null ?
163182 Collections .<String , JawkExtension >emptyMap () : extensionInstances ;
183+ this .profiling = profilingEnabled ;
184+ if (profilingEnabled ) {
185+ this .tupleProfilingStats = new java .util .EnumMap <Opcode , ProfilingReport .Accumulator >(Opcode .class );
186+ this .functionProfilingStats = new LinkedHashMap <String , ProfilingReport .Accumulator >();
187+ this .activeProfilingFunctions = new ArrayDeque <ActiveFunction >();
188+ } else {
189+ this .tupleProfilingStats = null ;
190+ this .functionProfilingStats = null ;
191+ this .activeProfilingFunctions = null ;
192+ }
164193
165194 arguments = Collections .emptyList ();
166195 sortedArrayKeys = this .settings .isUseSortedArrayKeys ();
@@ -975,10 +1004,14 @@ private void executeTuples(PositionTracker position)
9751004 IOException {
9761005 Map <Integer , ConditionPair > conditionPairs = null ;
9771006 Opcode opcode = null ;
1007+ long tupleStartNanos = 0L ;
9781008 try {
9791009 while (!position .isEOF ()) {
9801010 // System_out.println("--> "+position);
9811011 opcode = position .opcode ();
1012+ if (profiling ) {
1013+ tupleStartNanos = beforeProfiledTuple (position , opcode );
1014+ }
9821015 // switch on OPCODE
9831016 switch (opcode ) {
9841017 case PRINT : {
@@ -2217,7 +2250,8 @@ private void executeTuples(PositionTracker position)
22172250 if (!position .classArg ().isInstance (o )) {
22182251 throw new AwkRuntimeException (
22192252 position .lineNumber (),
2220- "Verification failed. Top-of-stack = " + o .getClass () + " isn't an instance of " + position .classArg ());
2253+ "Verification failed. Top-of-stack = " + o .getClass () + " isn't an instance of "
2254+ + position .classArg ());
22212255 }
22222256 push (o );
22232257 position .next ();
@@ -2811,8 +2845,16 @@ private void executeTuples(PositionTracker position)
28112845 default :
28122846 throw new Error ("invalid opcode: " + position .opcode ());
28132847 }
2848+ if (profiling ) {
2849+ afterProfiledTuple (opcode , tupleStartNanos );
2850+ }
28142851 }
28152852
2853+ } catch (ExitException ee ) {
2854+ if (profiling && (opcode == Opcode .EXIT_WITH_CODE || opcode == Opcode .EXIT_WITHOUT_CODE )) {
2855+ afterProfiledTuple (opcode , tupleStartNanos );
2856+ }
2857+ throw ee ;
28162858 } catch (IOException ioe ) {
28172859 // clear runtime stack
28182860 runtimeStack .popAllFrames ();
@@ -2842,6 +2884,86 @@ private void executeTuples(PositionTracker position)
28422884 }
28432885 }
28442886
2887+ /**
2888+ * Clears all collected profiling statistics.
2889+ */
2890+ public void resetProfiling () {
2891+ if (!profiling ) {
2892+ return ;
2893+ }
2894+ tupleProfilingStats .clear ();
2895+ functionProfilingStats .clear ();
2896+ activeProfilingFunctions .clear ();
2897+ }
2898+
2899+ /**
2900+ * Returns an immutable snapshot of the collected profiling statistics.
2901+ *
2902+ * @return profiling report snapshot
2903+ */
2904+ public ProfilingReport getProfilingReport () {
2905+ if (!profiling ) {
2906+ return ProfilingReport .empty ();
2907+ }
2908+ return new ProfilingReport (tupleProfilingStats , functionProfilingStats );
2909+ }
2910+
2911+ private long beforeProfiledTuple (PositionTracker position , Opcode opcode ) {
2912+ long now = System .nanoTime ();
2913+ if (opcode == Opcode .CALL_FUNCTION ) {
2914+ activeProfilingFunctions .push (new ActiveFunction (position .stringArg (1 ), now ));
2915+ } else if (opcode == Opcode .EXTENSION ) {
2916+ ExtensionFunction function = position .extensionFunctionArg ();
2917+ activeProfilingFunctions .push (new ActiveFunction (function .getKeyword (), now ));
2918+ }
2919+ return now ;
2920+ }
2921+
2922+ private void afterProfiledTuple (Opcode opcode , long tupleStartNanos ) {
2923+ long now = System .nanoTime ();
2924+ statisticsFor (tupleProfilingStats , opcode ).add (now - tupleStartNanos );
2925+ if (opcode == Opcode .EXIT_WITH_CODE || opcode == Opcode .EXIT_WITHOUT_CODE ) {
2926+ recordAllFunctionExits (now );
2927+ } else if (opcode == Opcode .EXTENSION || opcode == Opcode .RETURN_FROM_FUNCTION ) {
2928+ recordFunctionExit (now );
2929+ }
2930+ }
2931+
2932+ private static <K > ProfilingReport .Accumulator statisticsFor (
2933+ Map <K , ProfilingReport .Accumulator > stats ,
2934+ K key ) {
2935+ ProfilingReport .Accumulator accumulator = stats .get (key );
2936+ if (accumulator == null ) {
2937+ accumulator = new ProfilingReport .Accumulator ();
2938+ stats .put (key , accumulator );
2939+ }
2940+ return accumulator ;
2941+ }
2942+
2943+ private void recordFunctionExit (long now ) {
2944+ if (activeProfilingFunctions .isEmpty ()) {
2945+ return ;
2946+ }
2947+ ActiveFunction function = activeProfilingFunctions .pop ();
2948+ statisticsFor (functionProfilingStats , function .name ).add (now - function .startNanos );
2949+ }
2950+
2951+ private void recordAllFunctionExits (long now ) {
2952+ while (!activeProfilingFunctions .isEmpty ()) {
2953+ recordFunctionExit (now );
2954+ }
2955+ }
2956+
2957+ private static final class ActiveFunction {
2958+ private final String name ;
2959+ private final long startNanos ;
2960+
2961+ private ActiveFunction (String name , long startNanos ) {
2962+ this .name = name ;
2963+ this .startNanos = startNanos ;
2964+ }
2965+ }
2966+
28452967 /**
28462968 * Releases any prepared input source and runtime I/O resources owned by this
28472969 * AVM.
0 commit comments