/*******************************************************************************
* Copyright (c) 2004, 2016 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:
* Keith Seitz <keiths@redhat.com> - initial API and implementation
* Kent Sebastian <ksebasti@redhat.com> -
*******************************************************************************/
package org.eclipse.linuxtools.internal.oprofile.core.linux;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
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.OprofileProperties;
import org.eclipse.linuxtools.internal.oprofile.core.opxml.AbstractDataAdapter;
import org.eclipse.linuxtools.internal.oprofile.core.opxml.OprofileSAXHandler;
import org.eclipse.linuxtools.internal.oprofile.core.opxml.checkevent.CheckEventAdapter;
import org.eclipse.linuxtools.internal.oprofile.core.opxml.info.InfoAdapter;
import org.eclipse.linuxtools.internal.oprofile.core.opxml.modeldata.ModelDataAdapter;
import org.eclipse.linuxtools.internal.oprofile.core.opxml.sessions.SessionManager;
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.eclipse.osgi.util.NLS;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
/**
* This class will run opxml.
*
* opxml is a small program which acts as a textual interface between Oprofile
* and BFD and the oprofile plugins.
*/
public class OpxmlRunner {
/**
* Runs opxml with the given arguments.
*
* @param args
* the arguments to pass to opxml
* @param callData
* any callData to pass to the processor
* @return boolean indicating the success/failure of opxml
*/
public boolean run(String[] args, Object callData) {
XMLReader reader = null;
OprofileSAXHandler handler = OprofileSAXHandler.getInstance(callData);
// Create XMLReader
SAXParserFactory factory = SAXParserFactory.newInstance();
try {
reader = factory.newSAXParser().getXMLReader();
} catch (Exception e) {
e.printStackTrace();
return false;
}
// Set content/error handlers
reader.setContentHandler(handler);
reader.setErrorHandler(handler);
// Check for timer support
InfoAdapter.checkTimerSupport();
// Run opxml
try {
File file = constructFile(args);
// handle the opxml_session file
if (args[0].equals(SessionManager.SESSIONS)) {
SessionManager sessManNew = new SessionManager(SessionManager.SESSION_LOCATION);
populateWithCurrentSession(sessManNew);
sessManNew.write();
FileReader fr = new FileReader(file);
reader.parse(new InputSource(fr));
// file has not been saved
} else if (!file.exists()) {
AbstractDataAdapter aea;
if (args[0].equals(CheckEventAdapter.CHECK_EVENTS)) {
aea = new CheckEventAdapter(args[1], args[2], args[3]);
aea.process();
BufferedReader bi = new BufferedReader(new InputStreamReader(aea.getInputStream()));
reader.parse(new InputSource(bi));
} else if (args[0].equals(InfoAdapter.INFO)) {
aea = new InfoAdapter();
aea.process();
BufferedReader bi = new BufferedReader(new InputStreamReader(aea.getInputStream()));
reader.parse(new InputSource(bi));
} else if (args[0].equals(ModelDataAdapter.MODEL_DATA)) {
// this should only happen initially when the current
// session
// has not been generated
if (!handleModelData(args)) {
return false;
}
FileReader fr = new FileReader(file);
reader.parse(new InputSource(fr));
} else {
throw new RuntimeException("Unrecognized argument encountered"); //$NON-NLS-1$
}
} else {
// always regenerate the 'current' session file
if (args.length == 3 && args[0].equals(SessionManager.MODEL_DATA)
&& args[2].equals(SessionManager.CURRENT)) {
if (!handleModelData(args)) {
return false;
}
}
FileReader fr = new FileReader(file);
reader.parse(new InputSource(fr));
}
return true;
} catch (SAXException e) {
e.printStackTrace();
OprofileCorePlugin.showErrorDialog("opxmlSAXParseException", null); //$NON-NLS-1$
} catch (IOException e) {
e.printStackTrace();
OprofileCorePlugin.showErrorDialog("opxmlParse", null); //$NON-NLS-1$
}
return false;
}
private File saveOpxmlToFile(BufferedReader bi, String[] args) {
String fileName = ""; //$NON-NLS-1$
for (String arg : args) {
fileName += arg;
}
File file = new File(SessionManager.OPXML_PREFIX + fileName);
String line;
try {
file.createNewFile();
try (BufferedWriter bw = new BufferedWriter(new FileWriter(file))) {
while ((line = bi.readLine()) != null) {
bw.write(line + "\n"); //$NON-NLS-1$
}
bi.close();
}
} catch (IOException e) {
e.printStackTrace();
}
return file;
}
private File constructFile(String[] args) {
String fileName = ""; //$NON-NLS-1$
for (int i = 0; i < args.length; i++) {
fileName += args[i];
}
return new File(SessionManager.OPXML_PREFIX + fileName);
}
private boolean handleModelData(String[] args) {
ArrayList<String> cmd = new ArrayList<>();
cmd.add("-Xdg"); //$NON-NLS-1$
if (!InfoAdapter.hasTimerSupport()) {
cmd.add("event:" + args[1]); //$NON-NLS-1$
}
String[] a = {};
InputStream is = runOpReport(cmd.toArray(a));
if (is == null) {
return false;
}
ModelDataAdapter mda = new ModelDataAdapter(is);
if (!mda.isParseable()) {
return false;
}
mda.process();
BufferedReader bi = new BufferedReader(new InputStreamReader(mda.getInputStream()));
saveOpxmlToFile(bi, args);
return true;
}
/**
* Add the current session to the session manager for each event that it was
* profiled under.
*
* @param session
* the session manager to populate
*/
private void populateWithCurrentSession(SessionManager session) {
session.removeAllCurrentSessions();
String[] eventName = getEventNames();
if (eventName != null) {
for (int i = 0; i < eventName.length; i++) {
session.addSession(SessionManager.CURRENT, eventName[i]);
}
}
}
private String[] getEventNames() {
String[] ret = null;
try {
String cmd[] = { "-X", "-d" }; //$NON-NLS-1$ //$NON-NLS-2$
InputStream is = runOpReport(cmd);
if (is != null) {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder;
builder = factory.newDocumentBuilder();
Document doc = builder.parse(is);
Element root = (Element) doc.getElementsByTagName(ModelDataAdapter.PROFILE).item(0);
String eventOrTimerSetup;
String eventOrTimerName;
// Determine if we are in timer-mode or not as the XML will vary
if (!InfoAdapter.hasTimerSupport()) {
eventOrTimerSetup = ModelDataAdapter.EVENT_SETUP;
eventOrTimerName = ModelDataAdapter.EVENT_NAME;
} else {
eventOrTimerSetup = ModelDataAdapter.TIMER_SETUP;
eventOrTimerName = ModelDataAdapter.RTC_INTERRUPTS;
}
Element setupTag = (Element) root.getElementsByTagName(ModelDataAdapter.SETUP).item(0);
NodeList eventSetupList = setupTag.getElementsByTagName(eventOrTimerSetup);
// get the event names for the current session
ret = new String[eventSetupList.getLength()];
for (int i = 0; i < eventSetupList.getLength(); i++) {
Element elm = (Element) eventSetupList.item(i);
ret[i] = elm.getAttribute(eventOrTimerName);
}
}
} catch (IOException e) {
e.printStackTrace();
OprofileCorePlugin.showErrorDialog("opxmlParse", null); //$NON-NLS-1$
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
OprofileCorePlugin.showErrorDialog("opxmlSAXParseException", null); //$NON-NLS-1$
}
return ret;
}
/**
* Run opreport with specified arguments <code>args</code> and return
* InputStream to output of report for parsing.
*
* @param args
* arguments to run with opreport
* @return InputStream to output of report
*/
private InputStream runOpReport(String[] args) {
ArrayList<String> cmd = new ArrayList<>();
cmd.add("opreport"); //$NON-NLS-1$
if (OprofileProject.getProfilingBinary().equals(OprofileProject.OPERF_BINARY)) {
/*
* The session-dir parameter is relative to project's working dir,
* which might be local or remote. So it should use the proxy
* manager to determine working dir.
*/
String workingDir = ""; //$NON-NLS-1$
RemoteProxyManager proxy = RemoteProxyManager.getInstance();
try {
IRemoteFileProxy rfile = proxy.getFileProxy(Oprofile.OprofileProject.getProject());
workingDir = rfile.getWorkingDir().getPath();
} catch (CoreException e) {
e.printStackTrace();
return null;
}
cmd.add(1, "--session-dir=" + workingDir + IPath.SEPARATOR + "oprofile_data"); //$NON-NLS-1$ //$NON-NLS-2$
}
Collections.addAll(cmd, args);
Process p = null;
try {
p = RuntimeProcessFactory.getFactory().exec(cmd.toArray(new String[0]),
Oprofile.OprofileProject.getProject());
StringBuilder output = new StringBuilder();
StringBuilder errorOutput = new StringBuilder();
String s = null;
try (BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
BufferedReader stdError = new BufferedReader(new InputStreamReader(p.getErrorStream()))) {
// Read output of opreport. We need to do this, since this might
// cause the plug-in to hang. See Eclipse bug 341621 for more
// info.
// FIXME: Both of those while loops should really be done in two
// separate
// threads, so that we avoid this very problem when the error
// input
// stream buffer fills up.
while ((s = stdInput.readLine()) != null) {
output.append(s + System.getProperty("line.separator")); //$NON-NLS-1$
}
while ((s = stdError.readLine()) != null) {
errorOutput.append(s + System.getProperty("line.separator")); //$NON-NLS-1$
}
if (!errorOutput.toString().trim().equals("")) { //$NON-NLS-1$
OprofileCorePlugin.log(IStatus.ERROR, NLS.bind(OprofileProperties.getString("process.log.stderr"), //$NON-NLS-1$
"opreport", errorOutput.toString().trim())); //$NON-NLS-1$
}
} catch (IOException e) {
e.printStackTrace();
}
if (p.waitFor() == 0) {
// convert the string to inputstream to pass to builder.parse
return new ByteArrayInputStream(output.toString().getBytes(StandardCharsets.UTF_8));
}
} catch (IOException e1) {
e1.printStackTrace();
OprofileCorePlugin.showErrorDialog("opxmlParse", null); //$NON-NLS-1$
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
}