/*******************************************************************************
* Copyright (c) 2010, 2017 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.info;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
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.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.linuxtools.internal.oprofile.core.Oprofile;
import org.eclipse.linuxtools.internal.oprofile.core.Oprofile.OprofileProject;
import org.eclipse.linuxtools.internal.oprofile.core.OprofileCorePlugin;
import org.eclipse.linuxtools.internal.oprofile.core.opxml.AbstractDataAdapter;
import org.eclipse.linuxtools.internal.oprofile.core.opxml.EventIdCache;
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;
/**
* This class takes the XML that is output from 'ophelp -X' for and uses that
* data to modify it into the format expected by the SAX parser.
*/
public class InfoAdapter extends AbstractDataAdapter {
public static final String HELP_EVENTS = "help_events"; //$NON-NLS-1$
public static final String INFO = "info"; //$NON-NLS-1$
public static final String DEFAULTS = "defaults"; //$NON-NLS-1$
public static final String NUM_COUNTERS = "num-counters"; //$NON-NLS-1$
public static final String CPU_FREQUENCY = "cpu-frequency"; //$NON-NLS-1$
public static final String TIMER_MODE = "timer-mode"; //$NON-NLS-1$
public static final String TIMER = "timer"; //$NON-NLS-1$
public static final String EVENT_LIST = "event-list"; //$NON-NLS-1$
public static final String COUNTER = "counter"; //$NON-NLS-1$
public static final String EVENT = "event"; //$NON-NLS-1$
public static final String EVENT_NAME = "event_name"; //$NON-NLS-1$
public static final String NAME = "name"; //$NON-NLS-1$
public static final String DESC = "desc"; //$NON-NLS-1$
public static final String DESCRIPTION = "description"; //$NON-NLS-1$
public static final String MIN_COUNT = "min_count"; //$NON-NLS-1$
public static final String MINIMUM = "minimum"; //$NON-NLS-1$
public static final String VALUE = "value"; //$NON-NLS-1$
public static final String UNIT_MASKS = "unit_masks"; //$NON-NLS-1$
public static final String UNITMASK = "unit-mask"; //$NON-NLS-1$
public static final String DEFAULT = "default"; //$NON-NLS-1$
public static final String TYPE = "type"; //$NON-NLS-1$
public static final String UNIT_MASK = "unit_mask"; //$NON-NLS-1$
public static final String MASK = "mask"; //$NON-NLS-1$
public static final String SAMPLE_DIR = "sample-dir"; //$NON-NLS-1$
public static final String LOCK_FILE = "lock-file"; //$NON-NLS-1$
public static final String LOG_FILE = "log-file"; //$NON-NLS-1$
public static final String DUMP_STATUS = "dump-status"; //$NON-NLS-1$
public static final String CPUINFO = "/proc/cpuinfo"; //$NON-NLS-1$
public static String DEV_OPROFILE = "/dev/oprofile/"; //$NON-NLS-1$
public static String CPUTYPE = DEV_OPROFILE + "cpu_type"; //$NON-NLS-1$
public static final String OP_SHARE = "/usr/share/oprofile/"; //$NON-NLS-1$
public static final String EVENTS = "events"; //$NON-NLS-1$
public static final String SAMPLE_DIR_VAL = "/var/lib/oprofile/samples/"; //$NON-NLS-1$
public static final String LOCK_FILE_VAL = "/var/lib/oprofile/lock"; //$NON-NLS-1$
public static final String LOG_FILE_VAL = "/var/lib/oprofile/samples/oprofiled.log"; //$NON-NLS-1$
public static final String DUMP_STATUS_VAL = "/var/lib/oprofile/complete_dump"; //$NON-NLS-1$
private Document newDoc; // the document we intend to build
private Element oldRoot; // the root of the document with data from ophelp
private Element newRoot; // the root of the document we intent to build
private static IRemoteFileProxy proxy;
private static boolean hasTimerSupport;
public InfoAdapter() {
try {
if (hasTimerSupport()) {
// In timer mode, we have no relevant XML generated by ophelp
createDOM(null);
} else {
Process p = RuntimeProcessFactory.getFactory().exec("ophelp -X", Oprofile.OprofileProject.getProject()); //$NON-NLS-1$
if (p != null) {
InputStream is = p.getInputStream();
createDOM(is);
} else {
createDOM(null);
}
}
} catch (IOException e) {
createDOM(null);
}
}
/**
* @since 1.1
*/
public InfoAdapter(IFileStore resourceFile) {
InputStream inputStream = null;
try {
inputStream = resourceFile.openInputStream(EFS.NONE, new NullProgressMonitor());
createDOM(inputStream);
setEventIdCacheDoc(oldRoot);
} catch (CoreException e) {
e.printStackTrace();
}
}
/**
* Set up the DOM for later manipulation
*
* @param is
* the InpuStream resulting from running the ophelp command. This
* will be passed in as null for timer mode.
*/
private void createDOM(InputStream is) {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder;
try {
builder = factory.newDocumentBuilder();
if (is != null) {
try {
Document oldDoc = builder.parse(is);
Element elem = (Element) oldDoc.getElementsByTagName(HELP_EVENTS).item(0);
oldRoot = elem;
} catch (SAXException | IOException e) {
e.printStackTrace();
CoreException opcontrolException = new CoreException(
OprofileCorePlugin.createErrorStatus("ophelpRun", null)); //$NON-NLS-1$
OprofileCorePlugin.showErrorDialog("opxmlSAXParseException", opcontrolException); //$NON-NLS-1$
}
} else {
CoreException opcontrolException = new CoreException(
OprofileCorePlugin.createErrorStatus("ophelpRun", null)); //$NON-NLS-1$
OprofileCorePlugin.showErrorDialog("ophelpInputStream", opcontrolException); //$NON-NLS-1$
}
newDoc = builder.newDocument();
try {
newRoot = newDoc.createElement(INFO);
newDoc.appendChild(newRoot);
} catch (Exception e) {
e.printStackTrace();
}
} catch (ParserConfigurationException e1) {
e1.printStackTrace();
}
}
@Override
public void process() {
if (getNrCounters() == -1) {
Element numCountersTag = newDoc.createElement(NUM_COUNTERS);
numCountersTag.setTextContent("error"); //$NON-NLS-1$
newRoot.appendChild(numCountersTag);
return;
}
createHeaders();
if (!hasTimerSupport() && oldRoot != null) {
createXML();
}
}
private void createHeaders() {
// number of counters
String numCounters = String.valueOf(getNrCounters());
Element numCountersTag = newDoc.createElement(NUM_COUNTERS);
numCountersTag.setTextContent(String.valueOf(numCounters));
newRoot.appendChild(numCountersTag);
// cpu frequency
int cpuFreq = getCPUFrequency();
Element cpuFreqTag = newDoc.createElement(CPU_FREQUENCY);
cpuFreqTag.setTextContent(String.valueOf(cpuFreq));
newRoot.appendChild(cpuFreqTag);
// file defaults
Element defaultsTag = newDoc.createElement(DEFAULTS);
Element sampleDirTag = newDoc.createElement(SAMPLE_DIR);
sampleDirTag.setTextContent(SAMPLE_DIR_VAL);
defaultsTag.appendChild(sampleDirTag);
Element lockFileTag = newDoc.createElement(LOCK_FILE);
lockFileTag.setTextContent(LOCK_FILE_VAL);
defaultsTag.appendChild(lockFileTag);
Element logFileTag = newDoc.createElement(LOG_FILE);
logFileTag.setTextContent(LOG_FILE_VAL);
defaultsTag.appendChild(logFileTag);
Element dumpStatusTag = newDoc.createElement(DUMP_STATUS);
dumpStatusTag.setTextContent(DUMP_STATUS_VAL);
defaultsTag.appendChild(dumpStatusTag);
newRoot.appendChild(defaultsTag);
// timer mode
Element timerModeTag = newDoc.createElement(TIMER_MODE);
timerModeTag.setTextContent(String.valueOf(hasTimerSupport()));
newRoot.appendChild(timerModeTag);
}
/**
* @since 3.0
*/
public static void setOprofileDir(String dir) {
DEV_OPROFILE = dir;
CPUTYPE = DEV_OPROFILE + "cpu_type"; //$NON-NLS-1$
}
/**
* Determine whether the cpu supports timer mode
*
* @return true if it is true, and false otherwise
*/
public static boolean hasTimerSupport() {
return hasTimerSupport;
}
/**
* Set whether the cpu supports timer mode
*/
public static void checkTimerSupport() {
try {
proxy = RemoteProxyManager.getInstance().getFileProxy(Oprofile.OprofileProject.getProject());
IFileStore fileStore = proxy.getResource(CPUTYPE);
if (fileStore.fetchInfo().exists()) {
try (InputStream is = fileStore.openInputStream(EFS.NONE, new NullProgressMonitor());
BufferedReader bi = new BufferedReader(new InputStreamReader(is))) {
String cpuType = bi.readLine();
if (cpuType.equals(TIMER)) {
hasTimerSupport = true;
} else {
hasTimerSupport = false;
}
}
}
} catch (FileNotFoundException e) {
hasTimerSupport = true;
} catch (IOException e) {
hasTimerSupport = true;
e.printStackTrace();
} catch (CoreException e) {
e.printStackTrace();
}
}
/**
* Get the system's cpu frequency
*
* @return the system's cpu frequency
*/
private int getCPUFrequency() {
int val = 0;
try {
proxy = RemoteProxyManager.getInstance().getFileProxy(Oprofile.OprofileProject.getProject());
IFileStore fileStore = proxy.getResource(CPUINFO);
if (fileStore.fetchInfo().exists()) {
InputStream is = fileStore.openInputStream(EFS.NONE, new NullProgressMonitor());
try (BufferedReader bi = new BufferedReader(new InputStreamReader(is))) {
String line;
while ((line = bi.readLine()) != null) {
int index = line.indexOf(':');
if (index != -1) {
String substr;
// x86/ia64/x86_64
if (line.startsWith("cpu MHz")) { //$NON-NLS-1$
substr = line.substring(index + 1).trim();
return (int) Double.parseDouble(substr);
// ppc/pc64
} else if (line.startsWith("clock")) { //$NON-NLS-1$
int MHzLoc = line.indexOf("MHz"); //$NON-NLS-1$
substr = line.substring(index + 1, MHzLoc);
return (int) Double.parseDouble(substr);
// alpha
} else if (line.startsWith("cycle frequency [Hz]")) { //$NON-NLS-1$
substr = line.substring(index + 1).trim();
return (int) (Double.parseDouble(substr) / 1E6);
// sparc64
} else if (line.startsWith("Cpu0ClkTck")) { //$NON-NLS-1$
substr = line.substring(index + 1).trim();
return (int) (Double.parseDouble(substr) / 1E6);
}
}
}
} catch (IOException | NumberFormatException e) {
e.printStackTrace();
}
}
} catch (CoreException e) {
e.printStackTrace();
}
return val;
}
/**
* Get the number of counters for the system
*
* @return the number of counters for the system
*/
private int getNrCounters() {
/*
* TODO: Originally the number of counters for a given arch were hard-coded in a
* list. This method may not be entirely correct, although much simpler.
*/
/*
* Returning 1 for operf since it multiplexes the events through the counters
* and it is not possible to read data from opcontrol /dev dir if the opcontrol
* module was not initialized. TODO: Make possible to select more than one event
* in a tab.
*/
if ((OprofileProject.getProfilingBinary().equals(OprofileProject.OPERF_BINARY))
|| (OprofileProject.getProfilingBinary().equals(OprofileProject.OCOUNT_BINARY))) {
return 1;
}
try {
proxy = RemoteProxyManager.getInstance().getFileProxy(Oprofile.OprofileProject.getProject());
} catch (CoreException e) {
e.printStackTrace();
}
final int MAXCPUS = Integer.MAX_VALUE;
for (int i = 0; i < MAXCPUS; i++) {
IFileStore fileStore = proxy.getResource(DEV_OPROFILE + i);
if (!fileStore.fetchInfo().exists()) {
return i;
}
}
return -1;
}
private void createXML() {
NodeList eventList = oldRoot.getElementsByTagName(EVENT);
Element newEventList = newDoc.createElement(EVENT_LIST);
for (int i = 0; i < eventList.getLength(); i++) {
// get the event data
Element event = (Element) eventList.item(i);
String name = event.getAttribute(EVENT_NAME);
String desc = event.getAttribute(DESC);
String min_count = event.getAttribute(MIN_COUNT);
// create the data for the new event
Element newEventTag = newDoc.createElement(EVENT);
Element nameTag = newDoc.createElement(NAME);
nameTag.setTextContent(name);
Element descTag = newDoc.createElement(DESCRIPTION);
descTag.setTextContent(desc);
Element minimumTag = newDoc.createElement(MINIMUM);
minimumTag.setTextContent(min_count);
newEventTag.appendChild(nameTag);
newEventTag.appendChild(descTag);
newEventTag.appendChild(minimumTag);
Element unitMaskTag = (Element) event.getElementsByTagName(UNIT_MASKS).item(0);
// check if there are any unit masks for this event
if (unitMaskTag != null) {
String defaultVal = unitMaskTag.getAttribute(DEFAULT);
// Get the unit mask type (compatible with 1.0 and 1.1 ophelp
// xml schemas)
String type = EventIdCache.getInstance().getUnitMaskType(name);
Element newUnitMaskTag = newDoc.createElement(UNITMASK);
Element typeTag = newDoc.createElement(TYPE);
typeTag.setTextContent(type);
Element defaultValTag = newDoc.createElement(DEFAULT);
defaultValTag.setTextContent(defaultVal);
newUnitMaskTag.appendChild(typeTag);
newUnitMaskTag.appendChild(defaultValTag);
newEventTag.appendChild(newUnitMaskTag);
NodeList unitMaskList = unitMaskTag.getElementsByTagName(UNIT_MASK);
for (int j = 0; j < unitMaskList.getLength(); j++) {
Element unitMask = (Element) unitMaskList.item(j);
String maskVal = unitMask.getAttribute(MASK);
String maskDesc = unitMask.getAttribute(DESC);
Element newMask = newDoc.createElement(MASK);
Element newVal = newDoc.createElement(VALUE);
newVal.setTextContent(maskVal);
Element newDesc = newDoc.createElement(DESCRIPTION);
newDesc.setTextContent(maskDesc);
newMask.appendChild(newVal);
newMask.appendChild(newDesc);
newUnitMaskTag.appendChild(newMask);
}
// not unit mask for this event
} else {
String defaultVal = "0"; //$NON-NLS-1$
String type = "mandatory"; //$NON-NLS-1$
Element newUnitMaskTag = newDoc.createElement(UNITMASK);
Element typeTag = newDoc.createElement(TYPE);
typeTag.setTextContent(type);
Element defaultValTag = newDoc.createElement(DEFAULT);
defaultValTag.setTextContent(defaultVal);
newUnitMaskTag.appendChild(typeTag);
newUnitMaskTag.appendChild(defaultValTag);
newEventTag.appendChild(newUnitMaskTag);
Element newMask = newDoc.createElement(MASK);
Element newVal = newDoc.createElement(VALUE);
newVal.setTextContent("0"); //$NON-NLS-1$
Element newDesc = newDoc.createElement(DESCRIPTION);
newDesc.setTextContent("No unit mask"); //$NON-NLS-1$
newMask.appendChild(newVal);
newMask.appendChild(newDesc);
newUnitMaskTag.appendChild(newMask);
}
newEventList.appendChild(newEventTag);
}
for (int i = 0; i < getNrCounters(); i++) {
Element eventListTag = (Element) newEventList.cloneNode(true);
eventListTag.setAttribute(COUNTER, String.valueOf(i));
newRoot.appendChild(eventListTag);
}
}
@Override
public Document getDocument() {
return newDoc;
}
/**
* @since 3.0
*/
private void setEventIdCacheDoc(Element elem) {
EventIdCache.getInstance().setCacheDoc(elem);
}
}