1 /*
   2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
   3  *
   4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   5  *
   6  * The contents of this file are subject to the terms of either the Universal Permissive License
   7  * v 1.0 as shown at http://oss.oracle.com/licenses/upl
   8  *
   9  * or the following license:
  10  *
  11  * Redistribution and use in source and binary forms, with or without modification, are permitted
  12  * provided that the following conditions are met:
  13  *
  14  * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
  15  * and the following disclaimer.
  16  *
  17  * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
  18  * conditions and the following disclaimer in the documentation and/or other materials provided with
  19  * the distribution.
  20  *
  21  * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
  22  * endorse or promote products derived from this software without specific prior written permission.
  23  *
  24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
  25  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  26  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
  27  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  29  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  30  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
  31  * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32  */
  33 package org.openjdk.jmc.flightrecorder.parser.synthetic;
  34 
  35 import static org.openjdk.jmc.common.item.Attribute.attr;
  36 import static org.openjdk.jmc.flightrecorder.JfrAttributes.EVENT_STACKTRACE;
  37 import static org.openjdk.jmc.flightrecorder.JfrAttributes.EVENT_THREAD;
  38 
  39 import java.util.ArrayList;
  40 import java.util.Arrays;
  41 import java.util.List;
  42 
  43 import org.openjdk.jmc.common.IMCFrame;
  44 import org.openjdk.jmc.common.IMCModule;
  45 import org.openjdk.jmc.common.IMCPackage;
  46 import org.openjdk.jmc.common.IMCStackTrace;
  47 import org.openjdk.jmc.common.IMCThread;
  48 import org.openjdk.jmc.common.item.IAttribute;
  49 import org.openjdk.jmc.common.unit.UnitLookup;
  50 import org.openjdk.jmc.common.util.LabeledIdentifier;
  51 import org.openjdk.jmc.flightrecorder.JfrAttributes;
  52 import org.openjdk.jmc.flightrecorder.internal.util.JfrInternalConstants;
  53 import org.openjdk.jmc.flightrecorder.jdk.JdkAttributes;
  54 import org.openjdk.jmc.flightrecorder.jdk.JdkTypeIDs;
  55 import org.openjdk.jmc.flightrecorder.parser.IEventSink;
  56 import org.openjdk.jmc.flightrecorder.parser.IEventSinkFactory;
  57 import org.openjdk.jmc.flightrecorder.parser.IParserExtension;
  58 import org.openjdk.jmc.flightrecorder.parser.ValueField;
  59 
  60 public class SyntheticAttributeExtension implements IParserExtension {
  61 
  62         private static final IAttribute<IMCStackTrace> EXECUTION_SAMPLES_STACKTRACE = attr("stackTrace", //$NON-NLS-1$
  63                         Messages.getString(Messages.SyntheticAttributeExtension_EXECUTION_SAMPLES_STACKTRACE),
  64                         EVENT_STACKTRACE.getContentType());
  65         private static final IAttribute<IMCThread> EXECUTION_SAMPLES_THREAD = attr("sampledThread", //$NON-NLS-1$
  66                         Messages.getString(Messages.SyntheticAttributeExtension_EXECUTION_SAMPLES_THREAD),
  67                         EVENT_THREAD.getContentType());
  68         private static final IAttribute<IMCThread> ALLOC_STATISTICS_THREAD = attr("thread", //$NON-NLS-1$
  69                         Messages.getString(Messages.SyntheticAttributeExtension_ALLOC_STATISTICS_THREAD),
  70                         EVENT_THREAD.getContentType());
  71         // FIXME: Break out constant
  72         static final IAttribute<LabeledIdentifier> REC_SETTING_EVENT_ID_ATTRIBUTE = attr("id", //$NON-NLS-1$
  73                         Messages.getString(Messages.SyntheticAttributeExtension_REC_SETTING_EVENT_ID_ATTRIBUTE),
  74                         UnitLookup.LABELED_IDENTIFIER);
  75 
  76         @Override
  77         public IEventSinkFactory getEventSinkFactory(final IEventSinkFactory sf) {
  78                 return SettingsTransformer.wrapSinkFactory(new IEventSinkFactory() {
  79 
  80                         @Override
  81                         public IEventSink create(
  82                                 String identifier, String label, String[] category, String description,
  83                                 List<ValueField> dataStructure) {
  84                                 if (JdkTypeIDs.EXECUTION_SAMPLE.equals(identifier)) {
  85                                         ValueField[] struct = new ValueField[dataStructure.size()];
  86                                         for (int i = 0; i < struct.length; i++) {
  87                                                 ValueField vf = dataStructure.get(i);
  88                                                 if (vf.matches(EXECUTION_SAMPLES_STACKTRACE)) {
  89                                                         vf = new ValueField(EVENT_STACKTRACE);
  90                                                 } else if (vf.matches(EXECUTION_SAMPLES_THREAD)) {
  91                                                         vf = new ValueField(EVENT_THREAD);
  92                                                 }
  93                                                 struct[i] = vf;
  94                                         }
  95                                         dataStructure = Arrays.asList(struct);
  96                                 } else if (JdkTypeIDs.THREAD_ALLOCATION_STATISTICS.equals(identifier)) {
  97                                         ValueField[] struct = new ValueField[dataStructure.size()];
  98                                         for (int i = 0; i < struct.length; i++) {
  99                                                 ValueField vf = dataStructure.get(i);
 100                                                 if (vf.matches(ALLOC_STATISTICS_THREAD)) {
 101                                                         vf = new ValueField(EVENT_THREAD);
 102                                                 }
 103                                                 struct[i] = vf;
 104                                         }
 105                                         dataStructure = Arrays.asList(struct);
 106                                 } else if (JdkTypeIDs.EXCEPTIONS_THROWN.equals(identifier)) {
 107                                         for (int i = 0; i < dataStructure.size(); i++) {
 108                                                 final int stacktraceIndex = i;
 109                                                 if (dataStructure.get(i).matches(JfrAttributes.EVENT_STACKTRACE)) {
 110                                                         final IEventSink subSink = sf.create(identifier, label, category, description,
 111                                                                         dataStructure);
 112                                                         return new IEventSink() {
 113 
 114                                                                 @Override
 115                                                                 public void addEvent(Object[] values) {
 116                                                                         IMCStackTrace st = (IMCStackTrace) values[stacktraceIndex];
 117                                                                         /*
 118                                                                          * NOTE: Filters out JavaExceptionThrow events created from the
 119                                                                          * Error constructor to avoid constructed errors being
 120                                                                          * represented both with a JavaErrorThrow and two
 121                                                                          * JavaExceptionThrow.
 122                                                                          */
 123                                                                         if (st == null || st.getFrames().size() < 2
 124                                                                                         || !isError(st.getFrames().get(0)) && !isError(st.getFrames().get(1))) {
 125                                                                                 subSink.addEvent(values);
 126                                                                         }
 127                                                                 }
 128 
 129                                                                 private boolean isError(IMCFrame frame) {
 130                                                                         return frame.getMethod().getType().getFullName().equals("java.lang.Error"); //$NON-NLS-1$
 131                                                                 }
 132                                                         };
 133                                                 }
 134                                         }
 135                                 } else if (JdkTypeIDs.RECORDING_SETTING.equals(identifier)) {
 136                                         ValueField[] struct = new ValueField[dataStructure.size()];
 137                                         for (int i = 0; i < struct.length; i++) {
 138                                                 ValueField vf = dataStructure.get(i);
 139                                                 if (vf.matches(REC_SETTING_EVENT_ID_ATTRIBUTE)) {
 140                                                         vf = new ValueField(JdkAttributes.REC_SETTING_FOR);
 141                                                 }
 142                                                 struct[i] = vf;
 143                                         }
 144                                         dataStructure = Arrays.asList(struct);
 145                                 } else if (JdkTypeIDs.MODULE_EXPORT.equals(identifier)) {
 146                                         // Unwrapping the exporting module field from the exported package as a separate attribute in the event type.
 147                                         int packageIndex = -1;
 148                                         for (int i = 0; i < dataStructure.size(); i++) {
 149                                                 ValueField vf = dataStructure.get(i);
 150                                                 if (vf.matches(JdkAttributes.EXPORTED_PACKAGE)) {
 151                                                         packageIndex = i;
 152                                                         break;
 153                                                 }
 154                                         }
 155                                         if (packageIndex != -1) {
 156                                                 List<ValueField> newDataStructure = new ArrayList<>(dataStructure);
 157                                                 newDataStructure.add(new ValueField(JdkAttributes.EXPORTING_MODULE));
 158                                                 IEventSink subSink = sf.create(identifier, label, category, description, newDataStructure);
 159                                                 IEventSink moduleExportSink = new ModuleExportSink(subSink, packageIndex);
 160                                                 return moduleExportSink;
 161                                         }
 162                                 }
 163                                 return sf.create(identifier, label, category, description, dataStructure);
 164                         }
 165 
 166                         @Override
 167                         public void flush() {
 168                                 sf.flush();
 169                         }
 170                 });
 171         }
 172 
 173         @Override
 174         public String getValueInterpretation(String eventTypeId, String fieldId) {
 175                 if (REC_SETTING_EVENT_ID_ATTRIBUTE.getIdentifier().equals(fieldId)
 176                                 && (JdkTypeIDsPreJdk11.RECORDING_SETTING.equals(eventTypeId) ||
 177                                     JdkTypeIDsPreJdk11.JDK9_RECORDING_SETTING.equals(eventTypeId) ||
 178                                     JdkTypeIDs.RECORDING_SETTING.equals(eventTypeId))) {
 179                         return JfrInternalConstants.TYPE_IDENTIFIER_VALUE_INTERPRETATION;
 180                 }
 181                 return null;
 182         }
 183 
 184         private static class ModuleExportSink implements IEventSink {
 185                 private final IEventSink subSink;
 186                 private final int packageFieldIndex;
 187 
 188                 public ModuleExportSink(IEventSink subSink, int packageFieldIndex) {
 189                         this.subSink = subSink;
 190                         this.packageFieldIndex = packageFieldIndex;
 191                 }
 192 
 193                 @Override
 194                 public void addEvent(Object[] values) {
 195                         IMCPackage thePackage = (IMCPackage) values[packageFieldIndex];
 196                         IMCModule exportingModule = thePackage.getModule();
 197                         Object[] newValues = new Object[values.length + 1];
 198                         System.arraycopy(values, 0, newValues, 0, values.length);
 199                         newValues[values.length] = exportingModule;
 200                         subSink.addEvent(newValues);
 201                 }
 202         }
 203 }