1 /*
  2  * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 package jdk.jfr.event.runtime;
 24 
 25 import java.util.HashMap;
 26 import java.util.List;
 27 import java.util.Map;
 28 
 29 import jdk.jfr.Configuration;
 30 import jdk.jfr.Event;
 31 import jdk.jfr.EventType;
 32 import jdk.jfr.FlightRecorder;
 33 import jdk.jfr.Recording;
 34 import jdk.jfr.Registered;
 35 import jdk.jfr.SettingDescriptor;
 36 import jdk.jfr.consumer.RecordedEvent;
 37 import jdk.test.lib.Asserts;
 38 import jdk.test.lib.jfr.EventNames;
 39 import jdk.test.lib.jfr.Events;
 40 
 41 /**
 42  * @test
 43  * @summary Tests that active setting are available in the ActiveSettingevent
 44  * @key jfr
 45  * @requires vm.hasJFR
 46  * @library /test/lib
 47  * @run main/othervm jdk.jfr.event.runtime.TestActiveSettingEvent
 48  */
 49 public final class TestActiveSettingEvent {
 50 
 51     private static class MyEvent extends Event {
 52     }
 53 
 54     @Registered(false)
 55     private static class MyRegistrationEvent extends Event {
 56     }
 57 
 58     private static final String ACTIVE_SETTING_EVENT_NAME = EventNames.ActiveSetting;
 59 
 60     public static void main(String[] args) throws Throwable {
 61         testDefaultSettings();;
 62         testProfileSettings();;
 63         testNewSettings();
 64         testChangedSetting();
 65         testUnregistered();
 66         testRegistration();
 67     }
 68 
 69     private static void testProfileSettings() throws Exception {
 70         testSettingConfiguration("profile");
 71     }
 72 
 73     private static void testDefaultSettings() throws Exception {
 74         testSettingConfiguration("default");
 75     }
 76 
 77     private static void testRegistration() throws Exception {
 78         // Register new
 79         try (Recording recording = new Recording()) {
 80             recording.enable(ACTIVE_SETTING_EVENT_NAME);
 81             recording.start();
 82             FlightRecorder.register(MyRegistrationEvent.class);
 83             recording.stop();
 84             List<RecordedEvent> events = Events.fromRecording(recording);
 85             Events.hasEvents(events);
 86             EventType type = EventType.getEventType(MyRegistrationEvent.class);
 87             assertSetting(events, type, "threshold", "0 ns");
 88             assertSetting(events, type, "enabled", "true");
 89             assertSetting(events, type, "stackTrace", "true");
 90         }
 91         // Register unregistered
 92         FlightRecorder.unregister(MyEvent.class);
 93         try (Recording recording = new Recording()) {
 94             recording.enable(ACTIVE_SETTING_EVENT_NAME);
 95             recording.start();
 96             FlightRecorder.register(MyRegistrationEvent.class);
 97             recording.stop();
 98             EventType type = EventType.getEventType(MyRegistrationEvent.class);
 99             List<RecordedEvent> events = Events.fromRecording(recording);
100             Events.hasEvents(events);
101             type = EventType.getEventType(MyRegistrationEvent.class);
102             assertSetting(events, type, "threshold", "0 ns");
103             assertSetting(events, type, "enabled", "true");
104             assertSetting(events, type, "stackTrace", "true");
105         }
106     }
107 
108     private static void testUnregistered() throws Exception {
109         FlightRecorder.register(MyEvent.class);
110         EventType type = EventType.getEventType(MyEvent.class);
111         FlightRecorder.unregister(MyEvent.class);
112         try (Recording recording = new Recording()) {
113             recording.enable(ACTIVE_SETTING_EVENT_NAME);
114             recording.start();
115             MyEvent m = new MyEvent();
116             m.commit();
117             recording.stop();
118             List<RecordedEvent> events = Events.fromRecording(recording);
119             Events.hasEvents(events);
120             assertNotSetting(events, type, "threshold", "0 ns");
121             assertNotSetting(events, type, "enabled", "true");
122             assertNotSetting(events, type, "stackTrace", "true");
123         }
124     }
125 
126     private static void testNewSettings() throws Exception {
127         try (Recording recording = new Recording()) {
128             recording.enable(ACTIVE_SETTING_EVENT_NAME);
129             recording.start();
130             MyEvent m = new MyEvent();
131             m.commit();
132             recording.stop();
133             List<RecordedEvent> events = Events.fromRecording(recording);
134             Events.hasEvents(events);
135             EventType type = EventType.getEventType(MyEvent.class);
136             assertSetting(events, type, "threshold", "0 ns");
137             assertSetting(events, type, "enabled", "true");
138             assertSetting(events, type, "stackTrace", "true");
139             assertNotSetting(events, type, "period", "everyChunk");
140         }
141     }
142 
143     private static void testChangedSetting() throws Exception {
144         EventType type = EventType.getEventType(MyEvent.class);
145         Map<String, String> base = new HashMap<>();
146         base.put(ACTIVE_SETTING_EVENT_NAME + "#enabled", "true");
147         try (Recording recording = new Recording()) {
148             recording.setSettings(base);
149             recording.start();
150             Map<String, String> newS = new HashMap<>(base);
151             newS.put(type.getName() + "#enabled", "true");
152             newS.put(type.getName() + "#threshold", "11 ns");
153             recording.setSettings(newS);
154             recording.stop();
155             List<RecordedEvent> events = Events.fromRecording(recording);
156             Events.hasEvents(events);
157             assertSetting(events, type, "threshold", "0 ns"); // initial value
158             assertSetting(events, type, "enabled", "true");
159             assertSetting(events, type, "threshold", "11 ns"); // changed value
160         }
161     }
162 
163     private static void assertSetting(List<RecordedEvent> events, EventType evenType, String settingName, String settingValue) throws Exception {
164         if (!hasSetting(events, evenType, settingName, settingValue)) {
165             throw new Exception("Could not find setting " + settingName + " with value " + settingValue + " for event type " + evenType.getName());
166         }
167     }
168 
169     private static void assertNotSetting(List<RecordedEvent> events, EventType evenType, String settingName, String settingValue) throws Exception {
170         if (hasSetting(events, evenType, settingName, settingValue)) {
171             throw new Exception("Found unexpected setting " + settingName + " with value " + settingValue + " for event type " + evenType.getName());
172         }
173     }
174 
175     private static boolean hasSetting(List<RecordedEvent> events, EventType evenType, String settingName, String settingValue) throws Exception {
176         for (RecordedEvent e : events) {
177             if (e.getEventType().getName().equals(ACTIVE_SETTING_EVENT_NAME)) {
178                 String name = e.getValue("name");
179                 String value = e.getValue("value");
180                 Long id = e.getValue("id");
181                 if (evenType.getId() == id && name.equals(settingName) && settingValue.equals(value)) {
182                     return true;
183                 }
184             }
185         }
186         return false;
187     }
188 
189     private static void testSettingConfiguration(String configurationName) throws Exception {
190         System.out.println("Testing configuration " + configurationName);
191         Configuration c = Configuration.getConfiguration(configurationName);
192         Map<String, String> settingValues = c.getSettings();
193         // Don't want to add these settings to the jfc-files we ship since they
194         // are not useful to configure. They are however needed to make the test
195         // pass.
196         settingValues.put(EventNames.ActiveSetting + "#stackTrace", "false");
197         settingValues.put(EventNames.ActiveSetting + "#threshold", "0 ns");
198         settingValues.put(EventNames.ActiveRecording + "#stackTrace", "false");
199         settingValues.put(EventNames.ActiveRecording + "#threshold", "0 ns");
200         settingValues.put(EventNames.JavaExceptionThrow + "#threshold", "0 ns");
201         settingValues.put(EventNames.JavaErrorThrow + "#threshold", "0 ns");
202         settingValues.put(EventNames.SecurityProperty + "#threshold", "0 ns");
203         settingValues.put(EventNames.TLSHandshake + "#threshold", "0 ns");
204         settingValues.put(EventNames.X509Certificate + "#threshold", "0 ns");
205         settingValues.put(EventNames.X509Validation + "#threshold", "0 ns");
206         settingValues.put(EventNames.ProcessStart + "#threshold", "0 ns");
207         settingValues.put(EventNames.Deserialization + "#threshold", "0 ns");
208 
209         try (Recording recording = new Recording(c)) {
210             Map<Long, EventType> eventTypes = new HashMap<>();
211             for (EventType et : FlightRecorder.getFlightRecorder().getEventTypes()) {
212                 eventTypes.put(et.getId(), et);
213             }
214             recording.start();
215             Map<String, String> expectedSettings = new HashMap<>();
216             for (EventType type : FlightRecorder.getFlightRecorder().getEventTypes()) {
217                 for (SettingDescriptor s : type.getSettingDescriptors()) {
218                     String settingName = type.getName() + "#" + s.getName();
219                     String value = settingValues.get(settingName);
220                     if (value == null) {
221                         throw new Exception("Could not find setting with name " + settingName);
222                     }
223                     // Prefer to have ms unit in jfc file
224                     if (value.equals("0 ms")) {
225                         value = "0 ns";
226                     }
227                     expectedSettings.put(settingName, value);
228                 }
229             }
230             recording.stop();
231 
232             for (RecordedEvent e : Events.fromRecording(recording)) {
233                 if (e.getEventType().getName().equals(ACTIVE_SETTING_EVENT_NAME)) {
234                     Long id = e.getValue("id");
235                     EventType et = eventTypes.get(id);
236                     if (et == null) {
237                         throw new Exception("Could not find event type with id " + id);
238                     }
239                     String name = e.getValue("name");
240                     String settingName = et.getName() + "#" + name;
241                     String value = e.getValue("value");
242                     String expectedValue = expectedSettings.get(settingName);
243                     if (expectedValue != null) {
244                         if (value.equals("0 ms")) {
245                             value = "0 ns";
246                         }
247                         Asserts.assertEquals(expectedValue, value, "Incorrect settings value for " + settingName + " was " + value + ", expected " + expectedValue);
248                         expectedSettings.remove(settingName);
249                     }
250                 }
251             }
252             if (!expectedSettings.isEmpty()) {
253                 throw new Exception("Not all setting in event. Missing " + expectedSettings.keySet());
254             }
255         }
256     }
257 }