/*******************************************************************************
* Copyright (c) 2009, 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:
* Red Hat - initial API and implementation
*******************************************************************************/
package org.eclipse.linuxtools.internal.callgraph;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.linuxtools.internal.callgraph.core.SystemTapParser;
import org.eclipse.linuxtools.internal.callgraph.core.SystemTapUIErrorMessages;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
/**
* This class is used only in the case that we are rendering a graph
* using GEF (specifically Zest).
*
* After a stap command is sent to be executed, and after data is stored
* into some temporary file, the data must be parsed to be used. This class
* handles all of the parsing. All data is stored into Maps and this class
* also starts the job responsible for taking the parsed data and rendering it.
*/
public class StapGraphParser extends SystemTapParser {
public Map<Integer, Long> timeMap;
public Map<Integer, String> serialMap;
public Map<Integer, HashMap<Integer, ArrayList<Integer>>> neighbourMaps;
public Map<String, Long> aggregateTimeMap;
public Map<String, Integer> countMap;
public List<Integer> callOrderList;
public Map<Integer, String> markedMap;
public Long endingTimeInNS;
public long totalTime;
public Map<Integer, Integer> lastFunctionMap;
public ICProject project;
private static final String DELIM = ",,"; //$NON-NLS-1$
private boolean encounteredMain = false;
private List<Integer> shouldGetEndingTimeForID = new ArrayList <>();
private Map<Integer, List<String>> nameMaps;
private Map<Integer, List<Integer>> idMaps;
private boolean skippedDirectives = false;
private int firstNode = -1;
public long startTime = -1;
@Override
protected void initialize() {
//INITIALIZE MAPS
neighbourMaps = new HashMap<>();
timeMap = new HashMap<>();
serialMap = new TreeMap<>();
aggregateTimeMap = new HashMap<>();
countMap = new HashMap<>();
endingTimeInNS = 0l;
callOrderList = new ArrayList<>();
markedMap = new HashMap<>();
lastFunctionMap = new HashMap<>();
nameMaps = new HashMap<>();
idMaps = new HashMap<>();
project = null;
startTime = -1;
}
@Override
public IStatus nonRealTimeParsing(){
//Clear maps (in case a previous execution left values hanging)
neighbourMaps.clear();
timeMap.clear();
serialMap.clear();
aggregateTimeMap.clear();
countMap.clear();
callOrderList.clear();
shouldGetEndingTimeForID.clear();
nameMaps.clear();
idMaps.clear();
encounteredMain = false;
skippedDirectives = false;
firstNode = -1;
startTime = -1;
BufferedReader buff = null;
try {
buff = new BufferedReader(new FileReader(sourcePath));
} catch (FileNotFoundException e1) {
Display.getDefault().asyncExec(() -> MessageDialog.openError(new Shell(), Messages.getString("StapGraphParser.FileNotFound"), //$NON-NLS-1$
Messages.getString("StapGraphParser.CouldNotOpen") + sourcePath)); //$NON-NLS-1$
return Status.CANCEL_STATUS;
}
internalData = buff;
return realTimeParsing();
}
private void parseEnd() {
//CHECK FOR EXIT() CALL
for (Map.Entry<Integer, List<Integer>> entry : idMaps.entrySet()) {
List<Integer> idList = entry.getValue();
int lastFunctionCalled = lastFunctionMap.get(entry.getKey());
if (idList.size() > 1) {
for (int val : idList) {
String name = serialMap.get(val);
long time = endingTimeInNS - timeMap.get(val);
timeMap.put(val, time);
if (val == firstNode) {
showTime(val, time);
}
if (shouldGetEndingTimeForID.contains(val)) {
long cumulativeTime = aggregateTimeMap.get(name)
+ endingTimeInNS;
aggregateTimeMap.put(name, cumulativeTime);
}
lastFunctionCalled = val;
}
String tmp = markedMap.get(lastFunctionCalled);
if (tmp == null) {
tmp = ""; //$NON-NLS-1$
}
markedMap
.put(lastFunctionCalled,
tmp
+ "\n" + Messages.getString("StapGraphParser.Term")); //$NON-NLS-1$ //$NON-NLS-2$
}
}
//timecheck is true if the total execution time is less than 10ms
//and the first function is more than 1% off from the total time.
boolean timeCheck = totalTime < 50000000 &&
(((float)timeMap.get(firstNode)/totalTime) > 1.01 ||
((float)timeMap.get(firstNode)/totalTime) < 0.99);
/*
* Indicate whether or not we had to manipulate total time, and why
*/
if (skippedDirectives || timeCheck) {
totalTime = timeMap.get(firstNode);
String markedMessage = ""; //$NON-NLS-1$
if (markedMap.containsKey(firstNode)) {
markedMessage = markedMap.get(firstNode) + "\n"; //$NON-NLS-1$
}
if (skippedDirectives) {
markedMessage += Messages.getString("StapGraphParser.CDirectives"); //$NON-NLS-1$
}
if (timeCheck) {
markedMessage += Messages.getString("StapGraphParser.TooFast"); //$NON-NLS-1$
}
markedMessage += Messages.getString("StapGraphParser.TimeForThisNode"); //$NON-NLS-1$
markedMap.put(firstNode, markedMessage);
}
}
private void parseMarked(String msg) {
/*
* Append message
*/
String[] parsed = msg.split(",,", 2); //$NON-NLS-1$
int key = Integer.parseInt(parsed[0]);
List<Integer> idList = idMaps.get(key);
if (idList == null || msg.length() < 1 || idList.size() < 1) {
return;
}
int id = idList.get(idList.size() -1);
if (parsed[1].equals("<unknown>")) { //$NON-NLS-1$
parsed[1] = parsed[1] + Messages.getString("StapGraphParser.UnknownMarkers"); //$NON-NLS-1$
}
markedMap.put(id, (markedMap.get(id) == null ? "" : markedMap.get(id)) + parsed[1]); //$NON-NLS-1$
}
private IStatus parse(String s) {
try {
if (s.length() < 1) {
return Status.OK_STATUS;
}
switch (s.charAt(0)) {
case '<' :
/*
*
* Open tag -- function call
*
*
*/
String[] args = s.substring(1, s.length()).split(DELIM);
// args[0] = name
// args[1] = id
// arsg[2] = time of event
int id = Integer.parseInt(args[1]);
long time = Long.parseLong(args[2]);
int tid = Integer.parseInt(args[3]);
String name = args[0];
//If we haven't encountered a main function yet and the name isn't clean,
//and the name contains "__", then this is probably a C directive
if (!encounteredMain && !isFunctionNameClean(name) && name.contains("__")) { //$NON-NLS-1$
skippedDirectives = true;
break;
}
List<String> nameList = nameMaps.get(tid);
if (nameList == null) {
nameList = new ArrayList<>();
}
List<Integer> idList = idMaps.get(tid);
if (idList == null) {
idList = new ArrayList<>();
}
HashMap<Integer, ArrayList<Integer>> outNeighbours = neighbourMaps.get(tid);
if (outNeighbours == null) {
outNeighbours = new HashMap<>();
}
if (startTime < 1) {
startTime = time;
}
endingTimeInNS=time;
name = cleanFunctionName(name);
if (name.equals("main")) { //$NON-NLS-1$
encounteredMain = true;
}
if (firstNode == -1) {
firstNode = id;
}
serialMap.put(id, name);
timeMap.put(id, time);
if (aggregateTimeMap.get(name) == null){
aggregateTimeMap.put(name, (long) 0);
}
//IF THERE ARE PREVIOUS FUNCTIONS WITH THE SAME NAME
//WE ARE IN ONE OF THEM SO DO NOT ADD TO CUMULATIVE TIME
if (nameList.indexOf(name) == -1) {
long cumulativeTime = aggregateTimeMap.get(name) - time;
aggregateTimeMap.put(name, cumulativeTime);
shouldGetEndingTimeForID.add(id);
}
if (countMap.get(name) == null){
countMap.put(name, 0);
}
countMap.put(name, countMap.get(name) + 1);
nameList.add(name);
idList.add(id);
if (outNeighbours.get(id) == null){
outNeighbours.put(id, new ArrayList<Integer>());
}
if (idList.size() > 1) {
int parentID = idList.get(idList.size() - 2);
outNeighbours.get(parentID).add(id);
}
callOrderList.add(id);
lastFunctionMap.put(tid,id);
neighbourMaps.put(tid, outNeighbours);
nameMaps.put(tid, nameList);
idMaps.put(tid, idList);
break;
case '>' :
/*
*
* Close tag -- Function return
*
*/
args = s.substring(1, s.length()).split(DELIM);
//args[0] = name
//args[1] = time of event
name = args[0];
tid = Integer.parseInt(args[2]);
nameList = nameMaps.get(tid);
if (nameList == null) {
nameList = new ArrayList<>();
}
idList = idMaps.get(tid);
if (idList == null) {
idList = new ArrayList<>();
}
//If we haven't encountered a main function yet and the name isn't clean,
//and the name contains "__", then this is probably a C directive
if (!encounteredMain && !isFunctionNameClean(name) && name.contains("__")) { //$NON-NLS-1$
skippedDirectives = true;
break;
}
name = cleanFunctionName(name);
int lastOccurance = nameList.lastIndexOf(name);
if (lastOccurance < 0) {
parsingError(Messages.getString("StapGraphParser.RetMismatch") + name); //$NON-NLS-1$
return Status.CANCEL_STATUS;
}
nameList.remove(lastOccurance);
id = idList.remove(lastOccurance);
if (timeMap.get(id) == null) {
parsingError(Messages.getString("StapGraphParser.NoStartTime") + name); //$NON-NLS-1$
return Status.CANCEL_STATUS;
}
endingTimeInNS=Long.parseLong(args[1]);
time = endingTimeInNS - timeMap.get(id);
timeMap.put(id, time);
if (id == firstNode) {
showTime(id, time);
}
//IF AN ID IS IN THIS ARRAY IT IS BECAUSE WE NEED THE ENDING TIME
// TO BE ADDED TO THE CUMULATIVE TIME FOR FUNCTIONS OF THIS NAME
if (shouldGetEndingTimeForID.contains(id)){
long cumulativeTime = aggregateTimeMap.get(name) + Long.parseLong(args[1]);
aggregateTimeMap.put(name, cumulativeTime);
}
nameMaps.put(tid, nameList);
idMaps.put(tid, idList);
break;
default :
/*
*
* Anything else -- error
*
*/
// parsingError(Messages.getString("StapGraphParser.14") + s.charAt(0) + //$NON-NLS-1$
// Messages.getString("StapGraphParser.15") ); //$NON-NLS-1$
return Status.CANCEL_STATUS;
}
} catch (NumberFormatException e) {
SystemTapUIErrorMessages mess = new SystemTapUIErrorMessages
(Messages.getString("StapGraphParser.BadSymbol"), //$NON-NLS-1$
Messages.getString("StapGraphParser.BadSymbol"), //$NON-NLS-1$
Messages.getString("StapGraphParser.BadSymbolMsg1") + //$NON-NLS-1$
Messages.getString("StapGraphParser.BadSymbolMsg2")); //$NON-NLS-1$
mess.schedule();
return Status.CANCEL_STATUS;
}
return Status.OK_STATUS;
}
private IStatus parseDotFile() {
BufferedReader buff = internalData;
HashMap <Integer, ArrayList<Integer>> outNeighbours= new HashMap<>();
ArrayList<String> nameList = new ArrayList<>();
ArrayList<Integer> idList = new ArrayList<>();
endingTimeInNS =0l;
totalTime=10000l;
try {
String line;
while ((line = buff.readLine()) != null) {
if (line.equals("}")) { //$NON-NLS-1$
break;
}
if (monitor.isCanceled()) {
return Status.CANCEL_STATUS;
}
if (line.length() < 1) {
continue;
}
String[] args = line.split(" ", 2); //$NON-NLS-1$
if (args[0].contains("->")) { //$NON-NLS-1$
//connection
int[] ids = new int[2];
int called = 1;
try {
ids[0] = Integer.parseInt(args[0].split("->")[0]); //$NON-NLS-1$
ids[1] = Integer.parseInt(args[0].split("->")[1]); //$NON-NLS-1$
int index1 = args[1].indexOf("=\""); //$NON-NLS-1$
int index2 = args[1].indexOf("\"]"); //$NON-NLS-1$
called = Integer.parseInt(args[1].substring(index1 + 2,index2));
} catch (NumberFormatException e) {
SystemTapUIErrorMessages m = new SystemTapUIErrorMessages(
Messages.getString("StapGraphParser.idOrLabel"), Messages.getString("StapGraphParser.idOrLabel"), //$NON-NLS-1$ //$NON-NLS-2$
Messages.getString("StapGraphParser.nonNumericLabel")); //$NON-NLS-1$
m.schedule();
return Status.CANCEL_STATUS;
}
//Set neighbour
ArrayList<Integer> tmpList = outNeighbours.get(ids[0]);
if (tmpList == null) {
tmpList = new ArrayList<>();
}
for (int i = 0; i < called; i++) {
tmpList.add(ids[1]);
}
outNeighbours.put(ids[0], tmpList);
} else {
//node
try {
int id = Integer.parseInt(args[0]);
if (firstNode == -1) {
firstNode = id;
}
int index = args[1].indexOf("=\""); //$NON-NLS-1$
String name = args[1].substring(index + 2, args[1].indexOf(' ', index));
double dtime = 0.0;
dtime = Double.parseDouble(args[1].substring(args[1].indexOf(' ') + 1, args[1].indexOf('%')));
long time = (long) (dtime*100);
nameList.add(name);
idList.add(id);
timeMap.put(id, time);
serialMap.put(id, name);
if (countMap.get(name) == null){
countMap.put(name, 0);
}
countMap.put(name, countMap.get(name) + 1);
long cumulativeTime = (aggregateTimeMap.get(name) != null ? aggregateTimeMap.get(name) : 0) + time;
aggregateTimeMap.put(name, cumulativeTime);
} catch (NumberFormatException e) {
SystemTapUIErrorMessages m = new SystemTapUIErrorMessages(
Messages.getString("StapGraphParser.idOrTime"), Messages.getString("StapGraphParser.idOrTime"), //$NON-NLS-1$ //$NON-NLS-2$
Messages.getString("StapGraphParser.nonNumericTime")); //$NON-NLS-1$
m.schedule();
return Status.CANCEL_STATUS;
}
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
buff.close();
} catch (IOException e) {
//Do nothing
}
}
neighbourMaps.put(0, outNeighbours);
nameMaps.put(0, nameList);
idMaps.put(0, idList);
try {
view.update();
} catch (InterruptedException e) {
e.printStackTrace();
}
return Status.OK_STATUS;
}
@Override
public IStatus realTimeParsing() {
BufferedReader buff = internalData;
String line;
boolean draw = false;
boolean first = true;
try {
while ((line = buff.readLine()) != null) {
if (monitor.isCanceled()) {
return Status.CANCEL_STATUS;
}
if (line.length() < 1) {
continue;
}
if (first && (line.contains(Messages.getString("StapGraphParser.17")))) { //$NON-NLS-1$
return parseDotFile();
}
first = false;
draw = true;
if (line.equals("PROBE_BEGIN")) { //$NON-NLS-1$
buff.mark(100);
String tmp = buff.readLine();
if (tmp != null && tmp.length() > 0) {
char tchar = tmp.charAt(0);
if (tchar != '-' && tchar != '+' && tchar != '?' && tchar != '>' && tchar != '<') {
project = CoreModel.getDefault().getCModel().getCProject(tmp);
} else {
buff.reset();
}
}
} else if (line.charAt(0) == '-') {
endingTimeInNS = Long.parseLong(line.substring(1));
} else if (line.charAt(0) == '+') {
totalTime = Long.parseLong(line.substring(1));
//Total time should be the last line in the output
parseEnd();
} else if (line.charAt(0) == '?') {
if (line.length() > 1) {
parseMarked(line.substring(1));
}
} else {
if (parse(line) == Status.CANCEL_STATUS) {
break;
}
}
}
if (draw && view != null) {
view.update();
//Repeat until all lines are read
realTimeParsing();
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return Status.OK_STATUS;
}
/**
* Mark node id with a message giving its actual time.
*/
private void showTime(int id, long time) {
String tmp = markedMap.get(id);
if (tmp == null) {
tmp = ""; //$NON-NLS-1$
}
markedMap.put(id, tmp +
Messages.getString("StapGraphParser.ActualTime") + time/1000000 //$NON-NLS-1$
+ Messages.getString("StapGraphParser.TimeUnits")); //$NON-NLS-1$
}
}