/*******************************************************************************
* 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.modeldata;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.TreeSet;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.eclipse.linuxtools.internal.oprofile.core.opxml.AbstractDataAdapter;
import org.eclipse.linuxtools.internal.oprofile.core.opxml.info.InfoAdapter;
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 'opreport -X --details' for
* the current session, and uses that data to modify it into the format
* expected by the SAX parser.
*/
public class ModelDataAdapter extends AbstractDataAdapter {
public final static String ID = "id"; //$NON-NLS-1$
public final static String IDREF = "idref"; //$NON-NLS-1$
public final static String NAME = "name"; //$NON-NLS-1$
public final static String COUNT = "count"; //$NON-NLS-1$
public final static String SAMPLE = "sample"; //$NON-NLS-1$
public final static String LINE = "line"; //$NON-NLS-1$
public final static String SYMBOL_DATA = "symboldata"; //$NON-NLS-1$
public final static String SYMBOL_DETAILS = "symboldetails"; //$NON-NLS-1$
public final static String SYMBOL = "symbol"; //$NON-NLS-1$
public final static String FILE = "file"; //$NON-NLS-1$
public final static String SETUP = "setup"; //$NON-NLS-1$
public final static String EVENT_SETUP = "eventsetup"; //$NON-NLS-1$
public final static String TIMER_SETUP = "timersetup"; //$NON-NLS-1$
public final static String SETUP_COUNT = "setupcount"; //$NON-NLS-1$
public final static String EVENT_NAME = "eventname"; //$NON-NLS-1$
public final static String RTC_INTERRUPTS = "rtcinterrupts"; //$NON-NLS-1$
public final static String PROFILE = "profile"; //$NON-NLS-1$
public final static String MODEL_DATA = "model-data"; //$NON-NLS-1$
public final static String MODULE = "module"; //$NON-NLS-1$
public final static String DEPENDENT = "dependent"; //$NON-NLS-1$
public final static String BINARY = "binary"; //$NON-NLS-1$
public final static String IMAGE = "image"; //$NON-NLS-1$
public final static String SYMBOLS = "symbols"; //$NON-NLS-1$
public final static String SYMBOL_TABLE = "symboltable"; //$NON-NLS-1$
public final static String DETAIL_TABLE = "detailtable"; //$NON-NLS-1$
public final static String DETAIL_DATA = "detaildata"; //$NON-NLS-1$
private boolean isParseable;
private Document newDoc; // the document we intend to build
private Element oldRoot; // the root of the document with data from opreport
private Element newRoot; // the root of the document we intent to build
/**
* Constructor to the ModelAdapter class
* @param is The input stream to be parsed
*/
public ModelDataAdapter(InputStream is) {
isParseable = true;
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder;
try {
builder = factory.newDocumentBuilder();
try {
Document oldDoc = builder.parse(is);
Element elem = (Element) oldDoc.getElementsByTagName(PROFILE).item(0);
oldRoot = elem;
newDoc = builder.newDocument();
newRoot = newDoc.createElement(MODEL_DATA);
newDoc.appendChild(newRoot);
} catch (IOException e) {
isParseable = false;
} catch (SAXException e) {
isParseable = false;
}
} catch (ParserConfigurationException e1) {
e1.printStackTrace();
}
}
@Override
public void process (){
createXML();
}
private void createXML() {
// get the binary name and the image count
Element oldImage = (Element) oldRoot.getElementsByTagName(BINARY).item(0);
Element newImage = newDoc.createElement(IMAGE);
String binName = oldImage.getAttribute(NAME);
newImage.setAttribute(NAME, binName);
Element countTag = (Element) oldImage.getElementsByTagName(COUNT).item(0);
String imageCount = countTag.getTextContent().trim();
newImage.setAttribute(COUNT, imageCount);
// There is no setup count in timer mode
if (!InfoAdapter.hasTimerSupport()){
// get the count that was used to profile
Element setupTag = (Element) oldRoot.getElementsByTagName(SETUP).item(0);
Element eventSetupTag = (Element) setupTag.getElementsByTagName(EVENT_SETUP).item(0);
String setupcount = eventSetupTag.getAttribute(SETUP_COUNT);
newImage.setAttribute(SETUP_COUNT, setupcount);
}
// these elements contain the data needed to populate the new symbol table
Element oldSymbolTableTag = (Element) oldRoot.getElementsByTagName(SYMBOL_TABLE).item(0);
NodeList oldSymbolDataList = oldSymbolTableTag.getElementsByTagName(SYMBOL_DATA);
Element oldDetailTableTag = (Element) oldRoot.getElementsByTagName(DETAIL_TABLE).item(0);
NodeList oldDetailTableList = oldDetailTableTag.getElementsByTagName(SYMBOL_DETAILS);
// parse the data into HashMaps for O(1) lookup time, as opposed to O(n).
HashMap<String, HashMap<String, String>> oldSymbolDataListMap = parseDataList (oldSymbolDataList);
HashMap<String, NodeList> oldDetailTableListMap = parseDetailTable (oldDetailTableList);
// An ArrayList to hold the binary and other modules
ArrayList<Element> oldImageList = new ArrayList<>();
// The first element is the original binary!
oldImageList.add(oldImage);
NodeList oldModuleList = oldImage.getElementsByTagName(MODULE);
// Set up the dependent tag for any modules run by this binary
Element dependentTag = newDoc.createElement(DEPENDENT);
if (oldModuleList.getLength() > 0){
dependentTag.setAttribute(COUNT, "0"); //$NON-NLS-1$
for (int t = 0; t < oldModuleList.getLength(); t++){
oldImageList.add((Element)oldModuleList.item(t));
}
}
// iterate through all (binary/modules)
for (Element oldImg : oldImageList) {
Element newImg;
if (oldImg.getTagName().equals(BINARY)){
newImg = newImage;
}else{
newImg = newDoc.createElement(IMAGE);
String imgName = oldImg.getAttribute(NAME);
newImg.setAttribute(NAME, imgName);
Element modCountTag = (Element) oldImg.getElementsByTagName(COUNT).item(0);
String imgCount = modCountTag.getTextContent().trim();
newImg.setAttribute(COUNT, imgCount);
}
Element newSymbolsTag = newDoc.createElement(SYMBOLS);
// these elements contain the data needed to populate the new symbol table
NodeList oldSymbolList = oldImg.getElementsByTagName(SYMBOL);
// iterate through all symbols
for (int i = 0; i < oldSymbolList.getLength(); i++) {
Element oldSymbol = (Element) oldSymbolList.item(i);
/**
* The original binary is a parent for all symbols
* We only want library function calls under their respective
* modules, and not under the original binary as well.
*/
if (!oldSymbol.getParentNode().isSameNode(oldImg)){
continue;
}
Element newSymbol = newDoc.createElement(SYMBOL);
String idref = oldSymbol.getAttribute(IDREF);
String symbolCount = ((Element) oldSymbol.getElementsByTagName(COUNT).item(0)).getTextContent().trim();
newSymbol.setAttribute(COUNT, symbolCount);
// get the symboltable entry corresponding to the id of this symbol
HashMap<String, String> symbolData = oldSymbolDataListMap.get(idref);
newSymbol.setAttribute(NAME, symbolData.get(NAME));
newSymbol.setAttribute(FILE, symbolData.get(FILE));
newSymbol.setAttribute(LINE, symbolData.get(LINE));
// get the symboldetails entry corresponding to the id of this symbol
NodeList detailDataList = oldDetailTableListMap.get(idref);
// go through the detail data of each symbol's details
HashMap<String, Element> tmp = new HashMap<>();
// temporary place to store the elements for sorting
TreeSet<Element> sorted = new TreeSet<>(SAMPLE_COUNT_ORDER);
for (int l = 0; l < detailDataList.getLength(); l++) {
Element detailData = (Element) detailDataList.item(l);
String sampleFile = detailData.getAttribute(FILE);
String sampleLine = detailData.getAttribute(LINE);
// The sample has a line number but no file
// This means that the file is the same as the symbol (parent)
if (sampleFile.equals("") && !sampleLine.isEmpty()){ //$NON-NLS-1$ $NON-NLS-2$
sampleFile = symbolData.get(FILE);
}else{
if (sampleFile.equals("")){ //$NON-NLS-1$
sampleFile = "??"; //$NON-NLS-1$
}
if (sampleLine.equals("")){ //$NON-NLS-1$
sampleLine = "0"; //$NON-NLS-1$
}
}
Element detailDataCount = (Element) detailData.getElementsByTagName(COUNT).item(0);
String count = detailDataCount.getTextContent().trim();
// if a sample at this line already exists then increase count for that line.
if (tmp.containsKey(sampleLine)) {
Element elem = (Element) tmp.get(sampleLine).getElementsByTagName(COUNT).item(0);
int val = Integer.parseInt(elem.getTextContent().trim()) + Integer.parseInt(count);
elem.setTextContent(String.valueOf(val));
} else {
Element sampleTag = newDoc.createElement(SAMPLE);
Element fileTag = newDoc.createElement(FILE);
fileTag.setTextContent(sampleFile);
Element lineTag = newDoc.createElement(LINE);
lineTag.setTextContent(sampleLine);
Element sampleCountTag = newDoc.createElement(COUNT);
sampleCountTag.setTextContent(count);
sampleTag.appendChild(fileTag);
sampleTag.appendChild(lineTag);
sampleTag.appendChild(sampleCountTag);
tmp.put(sampleLine, sampleTag);
}
}
// add the elements to the sorter
for (Element elem : tmp.values()) {
sorted.add(elem);
}
// append the elements in sorted order
for (Element e : sorted) {
newSymbol.appendChild(e);
}
newSymbolsTag.appendChild(newSymbol);
}
newImg.appendChild(newSymbolsTag);
// If this is a module, attach it to the dependent tag
if (oldImg.getTagName().equals(MODULE)){
dependentTag.appendChild(newImg);
int currVal = Integer.parseInt(dependentTag.getAttribute(COUNT));
int val = Integer.parseInt(newImg.getAttribute(COUNT));
dependentTag.setAttribute(COUNT, String.valueOf(currVal + val));
}else{
newRoot.appendChild(newImg);
}
}
if (oldModuleList.getLength() > 0){
newImage.appendChild(dependentTag);
}
}
/**
*
* @param oldDetailTableList the list of 'symboldetails' tags within detailtable
* @return a HashMap where the key is a function id and the value is a NodeList
* containing a list of the 'detaildata' tags that contain sample information.
*/
private HashMap<String, NodeList> parseDetailTable(NodeList oldDetailTableList) {
HashMap<String, NodeList> ret = new HashMap<> ();
for (int i = 0; i < oldDetailTableList.getLength(); i++){
Element symbolDetails = (Element) oldDetailTableList.item(i);
String id = symbolDetails.getAttribute(ID);
NodeList detailDataList = symbolDetails.getElementsByTagName(DETAIL_DATA);
ret.put(id, detailDataList);
}
return ret;
}
/**
*
* @param oldSymbolDataList the list of 'symboldata' tags within symboltable
* @return a Hashmap where the key is a function id and the value is a HashMap
* with various parameters of data
*/
private HashMap<String, HashMap<String, String>> parseDataList(NodeList oldSymbolDataList) {
HashMap<String, HashMap<String,String>> ret = new HashMap<> ();
for (int j = 0; j < oldSymbolDataList.getLength(); j++){
HashMap<String,String> tmp = new HashMap<> ();
Element symbolData = (Element) oldSymbolDataList.item(j);
String id = symbolData.getAttribute(ID);
String name = symbolData.getAttribute(NAME);
String file = symbolData.getAttribute(FILE);
if (file.equals("")){ //$NON-NLS-1$
file = "??"; //$NON-NLS-1$
}
String line = symbolData.getAttribute(LINE);
if (line.equals("")){ //$NON-NLS-1$
line = "0"; //$NON-NLS-1$
}
tmp.put(NAME, name);
tmp.put(FILE, file);
tmp.put(LINE, line);
ret.put(id, tmp);
}
return ret;
}
@Override
public Document getDocument() {
return newDoc;
}
/**
* Helper class to sort the samples of a given symbol in descending order from largest
* to smallest
*/
private static final Comparator<Element> SAMPLE_COUNT_ORDER = new Comparator<Element>()
{
@Override
public int compare(Element a, Element b) {
// sort from largest to smallest count in descending order
// items with the same count are sorted by line number from smallest
// to largest in descending order
Element a_countTag = (Element) a.getElementsByTagName(COUNT).item(0);
Element b_countTag = (Element) b.getElementsByTagName(COUNT).item(0);
Element a_LineTag = (Element) a.getElementsByTagName(LINE).item(0);
Element b_LineTag = (Element) b.getElementsByTagName(LINE).item(0);
Integer a_count = Integer.parseInt(a_countTag.getTextContent().trim());
Integer b_count = Integer.parseInt(b_countTag.getTextContent().trim());
Integer a_line = Integer.parseInt(a_LineTag.getTextContent().trim());
Integer b_line = Integer.parseInt(b_LineTag.getTextContent().trim());
if (a_count.compareTo(b_count) == 0){
return a_line.compareTo(b_line);
}
return -a_count.compareTo(b_count);
}
};
/**
* Returns if parseable
* @return isParseable boolean variable
*/
public boolean isParseable() {
return isParseable;
}
}