/*******************************************************************************
* Copyright (c) 2010 Red Hat, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Red Hat - initial API and implementation
*******************************************************************************/
package org.eclipse.linuxtools.internal.oprofile.core.opxml;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.linuxtools.internal.oprofile.core.Oprofile;
import org.eclipse.linuxtools.internal.oprofile.core.opxml.info.InfoAdapter;
import org.eclipse.linuxtools.profiling.launch.IRemoteFileProxy;
import org.eclipse.linuxtools.profiling.launch.RemoteProxyManager;
import org.eclipse.linuxtools.tools.launch.core.factory.RuntimeProcessFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/**
* Caches the event data used by the CheckEventAdapter. The performance
* improvement is targeted at the first time a call with the given arguments is
* made. The first given call to check-event will take roughly O(n), and all
* other calls whether they be new or recurring take O(1). Note that recurring
* calls are handled by an entirely different cache. This particular class
* simply parses the XML from ophelp -X and stores it.
*/
public class EventIdCache {
private static final String HELP_EVENTS = "help_events"; //$NON-NLS-1$
private static final String HEADER = "header"; //$NON-NLS-1$
private static final String SCHEMA = "schemaversion"; //$NON-NLS-1$
private static final String CATEGORY = "category"; //$NON-NLS-1$
private static final String OPHELP = "ophelp"; //$NON-NLS-1$
private static final String EVENT = "event"; //$NON-NLS-1$
private static final String EVENT_NAME = "event_name"; //$NON-NLS-1$
private static final String LOCAL = "local"; //$NON-NLS-1$
private Document eventDoc; // the document to hold the xml from ophelp
private Element eventRoot; // the root corresponding to the xml from ophelp
// name - the name of the event
// Element - the DOM node
private HashMap<String, Element> nameMap;
// Map containing the caches for remote machines
private static HashMap<String, EventIdCache> cacheMap;
public static EventIdCache getInstance(){
if (cacheMap == null) {
cacheMap = new HashMap<>();
}
IProject project = Oprofile.OprofileProject.getProject();
EventIdCache newCache = new EventIdCache();
if (project != null) {
EventIdCache eventIdCache = cacheMap.get(project.getLocationURI().getHost());
if (eventIdCache == null) {
cacheMap.put(project.getLocationURI().getHost(), newCache);
} else {
return eventIdCache;
}
} else {
// If no project associated we should launch locally
EventIdCache eventIdCache = cacheMap.get(LOCAL);
if (eventIdCache == null) {
cacheMap.put(LOCAL, newCache);
} else {
return eventIdCache;
}
}
return newCache;
}
/**
* @param id the id corresponding to an event
* @return the DOM Element corresponding to the event tag
*/
public Element getElementWithName (String name) {
IProject project = Oprofile.OprofileProject.getProject();
EventIdCache eventIdCache;
if (project != null) {
eventIdCache = cacheMap.get(project.getLocationURI().getHost());
} else {
eventIdCache = cacheMap.get(LOCAL);
}
if (eventIdCache.nameMap == null){
readXML(eventIdCache);
buildCache(eventIdCache);
}
return eventIdCache.nameMap.get(name) != null ? (Element)eventIdCache.nameMap.get(name) : null;
}
/**
* Build the cache
*/
private void buildCache(EventIdCache eventId) {
eventId.nameMap = new HashMap<> ();
NodeList eventList = eventId.eventRoot.getElementsByTagName(EVENT);
for (int i = 0; i < eventList.getLength(); i++){
Element elem = (Element) eventList.item(i);
String eventName = elem.getAttribute(EVENT_NAME);
eventId.nameMap.put(eventName, elem);
}
}
/**
* Read the XML from ophelp
*/
private void readXML(EventIdCache eventId) {
if (eventId.eventRoot != null) {
return;
}
try {
Process p = RuntimeProcessFactory.getFactory().exec(OPHELP + ' ' + "-X", Oprofile.OprofileProject.getProject()); //$NON-NLS-1$
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder;
try {
builder = factory.newDocumentBuilder();
try {
eventId.eventDoc = builder.parse(p.getInputStream());
Element elem = (Element) eventId.eventDoc.getElementsByTagName(HELP_EVENTS).item(0);
eventId.eventRoot = elem;
} catch (IOException|SAXException e) {
}
} catch (ParserConfigurationException e1) {
e1.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Get the unit mask type. Schema Version 1.1 and newer of ophelp XML
* will list the unit mask type as an attribute. Older version will not
* so we default to file lookups.
*
* @param name the name of the event
* @return the type of unit mask. This can be either mandatory, exclusive,
* bitmask, or null if none could be found.
*/
public String getUnitMaskType(String name) {
IProject project = Oprofile.OprofileProject.getProject();
EventIdCache eventIdCache;
if (project != null) {
eventIdCache = cacheMap.get(project.getLocationURI().getHost());
} else {
eventIdCache = cacheMap.get(LOCAL);
}
if (eventIdCache.eventRoot == null){
readXML(eventIdCache);
buildCache(eventIdCache);
}
Element header = (Element)eventIdCache.eventRoot.getElementsByTagName(HEADER).item(0);
double schemaVersion = 0;
if (!eventIdCache.eventRoot.getAttribute(SCHEMA).isEmpty()){
schemaVersion = Double.parseDouble(eventIdCache.eventRoot.getAttribute(SCHEMA));
} else {
schemaVersion = Double.parseDouble(header.getAttribute(SCHEMA));
}
String unitMaskType = null;
IRemoteFileProxy proxy = null;
try {
proxy = RemoteProxyManager.getInstance().getFileProxy(Oprofile.OprofileProject.getProject());
} catch (CoreException e) {
e.printStackTrace();
}
// Schema Version > 1.0 has the unit mask type within the XML
if (schemaVersion > 1.0){
Element event = getElementWithName(name);
Element unitMaskTag = (Element) event.getElementsByTagName(InfoAdapter.UNIT_MASKS).item(0);
return unitMaskTag.getAttribute(CATEGORY);
} else {
IFileStore fileStore = proxy.getResource(InfoAdapter.CPUTYPE);
try (InputStream fileInputStream = fileStore.openInputStream(
EFS.NONE, new NullProgressMonitor());
BufferedReader bi = new BufferedReader(
new InputStreamReader(fileInputStream))) {
String cpuType = bi.readLine();
IFileStore opArchEvents = proxy.getResource(InfoAdapter.OP_SHARE + cpuType + "/" + InfoAdapter.EVENTS); //$NON-NLS-1$
IFileStore opArchUnitMasks = proxy.getResource(InfoAdapter.OP_SHARE + cpuType + "/" + InfoAdapter.UNIT_MASKS); //$NON-NLS-1$
try (InputStream inputStreamEvents = opArchEvents.openInputStream(EFS.NONE, new NullProgressMonitor());
BufferedReader eventReader = new BufferedReader(new InputStreamReader(inputStreamEvents))) {
String line;
while ((line = eventReader.readLine()) != null){
// find the line with the event name
if (line.contains("name:"+name+' ')){ //$NON-NLS-1$
int start = line.indexOf("um:") + 3; //$NON-NLS-1$
int end = line.indexOf(' ', start);
// grab the string that references the unit mask type
String um = line.substring(start, end);
try (InputStream inputStreamMasks = opArchUnitMasks
.openInputStream(EFS.NONE,
new NullProgressMonitor());
BufferedReader unitMaskReader = new BufferedReader(
new InputStreamReader(inputStreamMasks))) {
while ((line = unitMaskReader.readLine()) != null) {
if (line.contains("name:" + um + ' ')) { //$NON-NLS-1$
start = line.indexOf("type:") + 5; //$NON-NLS-1$
end = line.indexOf(' ', start);
unitMaskType = line.substring(start, end);
return unitMaskType;
}
}
}
}
}
}
} catch (IOException|CoreException e) {
}
}
return unitMaskType;
}
/**
* @since 3.0
*/
public void setCacheDoc(Element oldRoot) {
eventRoot = oldRoot;
}
}