Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom analysis API and InAndOut Implementation #88

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ bin.includes = META-INF/,\
plugin.properties,\
.,\
plugin.xml,\
icons/
icons/,\
schema/
additional.bundles = org.eclipse.jdt.annotation
jars.extra.classpath = platform:/plugin/org.eclipse.jdt.annotation
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,11 @@
</tracetype>
</module>
</extension>
<extension
point="org.eclipse.tracecompass.tmf.core.dataprovider">
<dataProviderFactory
class="org.eclipse.tracecompass.incubator.internal.inandout.core.analysis.InAndOutDataProviderFactory"
id="org.eclipse.tracecompass.incubator.inandout.core.analysis.inAndOutdataProviderFactory">
</dataProviderFactory>
</extension>
</plugin>
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://org.eclipse.tracecompass/in-and-out-analysis.json",
"title": "InAndOut Analysis",
"description": "Custom Execution Analysis schema",
"type": "object",
"properties": {
"name" : {
"type": "string",
"description": "The name of this InAndOut configuration"
},
"description" : {
"type": "string",
"description": "The descrition of this InAndOut configuration"
},
"specifiers": {
"description": "array specifiers",
"type": "array",
"items": {
"type": "object",
"properties": {
"label": {
"type": "string",
"descritpion": "The label of the identifier"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A useful optional field here (apart from type and description) would be a list of values to select elements from. This could be used to provide an option to the user to select instead of type in.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we have to follow the JSON schema specification to see what is possible (https://json-schema.org/). The designer of the schema and extension will have look into that. I know, that you, for example, can specify a regex pattern to limit the input.

},
"inRegex": {
"type": "string",
"descritpion": "The IN regular expression"
},
"outRegex": {
"type": "string",
"descritpion": "The OUT regular expression"
},
"contextInRegex": {
"type": "string",
"descritpion": "The context IN regular expression"
},
"contextOutRegex": {
"type": "string",
"descritpion": "The context OUT regular expression"
},
"classifier": {
"type": "string",
"descritpion": "The classifier"
},
"required": [
"label",
"inRegex",
"outRegex",
"contextInRegex",
"contextOutRegex",
"classifier"
]
}
}
}
},
"required": ["name", "specifiers"]
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,18 @@
import java.util.List;
import java.util.Objects;

import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.tracecompass.analysis.profiling.core.instrumented.InstrumentedCallStackAnalysis;
import org.eclipse.tracecompass.incubator.internal.inandout.core.Activator;
import org.eclipse.tracecompass.tmf.core.analysis.IAnalysisModule;
import org.eclipse.tracecompass.tmf.core.config.ITmfConfiguration;
import org.eclipse.tracecompass.tmf.core.config.TmfConfiguration;
import org.eclipse.tracecompass.tmf.core.exceptions.TmfAnalysisException;
import org.eclipse.tracecompass.tmf.core.exceptions.TmfConfigurationException;
import org.eclipse.tracecompass.tmf.core.exceptions.TmfTraceException;
import org.eclipse.tracecompass.tmf.core.statesystem.ITmfStateProvider;
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
Expand All @@ -50,23 +59,69 @@ public class InAndOutAnalysisModule extends InstrumentedCallStackAnalysis {
*/
public static final String JSON = ".config.json"; //$NON-NLS-1$

private @Nullable ITmfConfiguration fConfiguration;
private @Nullable List<@NonNull SegmentSpecifier> fSpecifiers;

@Override
public @NonNull String getName() {
ITmfConfiguration config = fConfiguration;
if (config != null) {
return config.getName();
}
return super.getName();
}

@Override
public @NonNull String getHelpText() {
ITmfConfiguration config = fConfiguration;
if (config != null) {
return config.getDescription();
}
return super.getHelpText();
}

@Override
public void setConfiguration(ITmfConfiguration configuration) throws TmfConfigurationException {
if (configuration == null) {
throw new TmfConfigurationException("Can't set a null configuration"); //$NON-NLS-1$
}
SegmentSpecifierConfiguration config = SegmentSpecifierConfiguration.fromJsonMap(configuration.getParameters());
fConfiguration = configuration;
fSpecifiers = config.getSpecifiers();
}

@Override
public @Nullable ITmfConfiguration getConfiguration() {
return fConfiguration;
}

/**
* Reference (default) specifier configured.
*/
public static final SegmentSpecifier REFERENCE = new SegmentSpecifier("latency", "(\\S*)_entry", "(\\S*)_exit", "(\\S*)_entry", "(\\S*)_exit", "CPU"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$

@Override
public @NonNull String getId() {
return ID;
ITmfConfiguration config = fConfiguration;
if (config == null || fSpecifiers == null) {
return ID;
}
return ID + config.getId();
}

@Override
public boolean canExecute(@NonNull ITmfTrace trace) {
File config = getConfig(trace);
return config.exists() && super.canExecute(trace);
ITmfConfiguration configuration = fConfiguration;
if (configuration == null) {
// Eclipse Trace Compass
File config = getConfig(trace);
return config.exists() && super.canExecute(trace);
}
return true;
}

private static File getConfig(@NonNull ITmfTrace trace) {
// Eclipse Trace Compass
String folder = TmfTraceManager.getSupplementaryFileDir(trace);
File config = new File(folder + File.separator + ID + JSON);
if (!config.exists()) {
Expand All @@ -80,9 +135,13 @@ private static File getConfig(@NonNull ITmfTrace trace) {
@Override
protected @NonNull ITmfStateProvider createStateProvider() {
ITmfTrace trace = Objects.requireNonNull(getTrace(), "Trace should not be null at this point"); //$NON-NLS-1$
File configFile = getConfig(trace);
List<@NonNull SegmentSpecifier> list = read(configFile);
return new InAndOutAnalysisStateProvider(trace, list);
List<@NonNull SegmentSpecifier> specifiers = fSpecifiers;
if (specifiers == null) {
// Eclipse Trace Compass
File configFile = getConfig(trace);
specifiers = read(configFile);
}
return new InAndOutAnalysisStateProvider(trace, specifiers);
}

/**
Expand Down Expand Up @@ -116,10 +175,118 @@ private static File getConfig(@NonNull ITmfTrace trace) {
* the specifiers
*/
public static void write(File file, List<@NonNull SegmentSpecifier> specifiers) {
// Eclipse Trace Compass
try (Writer writer = new FileWriter(file)) {
writer.append(new Gson().toJson(specifiers));
} catch (IOException e) {
Activator.getInstance().logError(e.getMessage(), e);
}
}

/**
* Create the InAndOutAnalysisModule for a given configuration and trace
*
* @param config
* the input {@link ITmfConfiguration}
* @param trace
* the trace to apply it to
* @param writeConfig
* write the config (do only once)
* @return InAndOutAnalysisModule
* @throws TmfConfigurationException
* if an error occurs
*/
public static InAndOutAnalysisModule create(@NonNull ITmfConfiguration config, @NonNull ITmfTrace trace, boolean writeConfig) throws TmfConfigurationException {
/*
* Apply configuration to each trace (no need to check trace type here)
*/
InAndOutAnalysisModule module = new InAndOutAnalysisModule();
module.setConfiguration(config);
if (writeConfig) {
IPath traceConfigPath = getTraceRootFolder(trace, config.getSourceTypeId());
TmfConfiguration.writeConfiguration(config, traceConfigPath);
}
try {
if (module.setTrace(trace)) {
IAnalysisModule oldModule = trace.addAnalysisModule(module);
if (oldModule != null) {
oldModule.dispose();
oldModule.clearPersistentData();
}
} else {
module.dispose();
throw new TmfConfigurationException("InAndOut analysis module can't be created"); //$NON-NLS-1$
}
} catch (TmfAnalysisException | TmfTraceException e) {
module.dispose();
throw new TmfConfigurationException("Exception when setting trace", e); //$NON-NLS-1$
}
return module;
}

/**
* Removes configuration from trace:
* - delete configuration file
* - remove analysis module from trace object
*
* @param config
* the configuration to remove
* @param trace
* the
* @throws TmfConfigurationException if an error occurs
*/
public static void remove(ITmfConfiguration config, @NonNull ITmfTrace trace) throws TmfConfigurationException {
IPath traceConfig = getTraceRootFolder(trace, config.getSourceTypeId());
traceConfig = traceConfig.append(File.separator).append(config.getId()).addFileExtension(TmfConfiguration.JSON_EXTENSION);
File configFile = traceConfig.toFile();
if ((!configFile.exists()) || !configFile.delete()) {
throw new TmfConfigurationException("InAndOut configuration file can't be deleted from trace: configId=" + config.getId()); //$NON-NLS-1$
}

// Remove and clear persistent data
try {
IAnalysisModule module = trace.removeAnalysisModule(ID + config.getId());
if (module != null) {
module.dispose();
module.clearPersistentData();
}
} catch (TmfTraceException e) {
throw new TmfConfigurationException("Error removing analysis module from trace: analysis ID=" + ID + config.getId(), e); //$NON-NLS-1$
}
}

@SuppressWarnings("null")
private static @NonNull IPath getTraceRootFolder(@NonNull ITmfTrace trace, String subFolder) {
String supplFolder = TmfTraceManager.getSupplementaryFileDir(trace);
IPath supplPath = new Path(supplFolder);
supplPath = supplPath.addTrailingSeparator().append(subFolder);
return supplPath;
}

/**
* Reads the configurations for a given trace
*
* @param trace
* the trace to read configurations from
* @return list of configurations if any
* @throws TmfConfigurationException
* if an error occurs
*/
public static @NonNull List<ITmfConfiguration> readConfigurations(@NonNull ITmfTrace trace) throws TmfConfigurationException {
IPath rootPath = getTraceRootFolder(trace, SegmentSpecifierConfiguration.IN_AND_OUT_CONFIG_SOURCE_TYPE_ID);
File folder = rootPath.toFile();
List<ITmfConfiguration> list = new ArrayList<>();
if (folder.exists()) {
File[] listOfFiles = folder.listFiles();
for (File file : listOfFiles) {
IPath path = new Path(file.getName());
if (path.getFileExtension().equals(TmfConfiguration.JSON_EXTENSION)) {
ITmfConfiguration config = TmfConfiguration.fromJsonFile(file);
list.add(config);
}
}
}
return list;
}

}
Loading
Loading