diff options
Diffstat (limited to 'systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/systemtap/ui/ide/structures/TapsetParser.java')
-rw-r--r-- | systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/systemtap/ui/ide/structures/TapsetParser.java | 679 |
1 files changed, 679 insertions, 0 deletions
diff --git a/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/systemtap/ui/ide/structures/TapsetParser.java b/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/systemtap/ui/ide/structures/TapsetParser.java new file mode 100644 index 0000000000..e7a41bdf45 --- /dev/null +++ b/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/systemtap/ui/ide/structures/TapsetParser.java @@ -0,0 +1,679 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation. + * 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: + * IBM Corporation - Jeff Briggs, Henry Hughes, Ryan Morse + *******************************************************************************/ + +package org.eclipse.linuxtools.systemtap.ui.ide.structures; + +import java.util.ArrayList; + +import org.eclipse.linuxtools.systemtap.ui.logging.LogManager; +import org.eclipse.linuxtools.systemtap.ui.structures.TreeDefinitionNode; +import org.eclipse.linuxtools.systemtap.ui.structures.TreeNode; +import org.eclipse.linuxtools.systemtap.ui.structures.listeners.IUpdateListener; +import org.eclipse.linuxtools.systemtap.ui.structures.runnable.LoggedCommand; + + + +/** + * Runs stap -vp1 & stap -up2 in order to get all of the probes/functions + * that are defined in the tapsets. Builds probeAlias and function trees + * with the values obtained from the tapsets. + * + * Ugly code is a result of two issues with getting stap output. First, + * many tapsets do not work under stap -up2. Second since the output + * is not a regular language, we can't create a nice lexor/parser combination + * to do everything nicely. + * @author Ryan Morse + */ +public class TapsetParser implements Runnable { + public TapsetParser(String[] tapsets) { + this.tapsets = tapsets; + listeners = new ArrayList<IUpdateListener>(); + } + + /** + * This method sets up everything that is needed before actually creating + * a new process. It will run the first pass of stap to build the tapset + * tree framework. + */ + protected void init() { + disposed = false; + functions = new TreeNode("", false); + probes = new TreeNode("", false); + + String s = readPass1(null); + parseLevel1(s); + cleanupTrees(); + } + + /** + * This method will initialize everything and then start the process running. + * This method should be called by any class wishing to run the parser. + */ + public void start() { + stopped = false; + init(); + Thread t = new Thread(this, "TapsetParser"); + t.start(); + } + + /** + * This method chanegs the stop variable which is checked periodically by the + * running thread to see if it should stop running. + */ + public synchronized void stop() { + stopped = true; + } + + /** + * This method checks to see if the process has been asked to stop + * @return Boolean indicating whether or not a stop command has been received + */ + public boolean isRunning() { + return !stopped; + } + + /** + * This method checks to see if the process has been disposed or not. + * @return Boolean indicating whether or not the parser has been disposed. + */ + public boolean isDisposed() { + return disposed; + } + + /** + * Returns the root node of the tree of functions generated by + * parseFiles. Functions are grouped by source file. + * @return A tree of tapset functions grouped by file. + */ + public synchronized TreeNode getFunctions() { + return functions; + } + + /** + * Returns the root node of the tree of the probe alias generated by + * parseFiles. Probes are grouped by target location. + * @return A tree of tapset probe aliases grouped by probe location. + */ + public synchronized TreeNode getProbes() { + return probes; + } + + /** + * This method checks to see if the parser completed executing on its own. + * @return Boolean indicating whether or not the thread finished on its own. + */ + public boolean isFinishSuccessful() { + return successfulFinish; + } + + /** + * Runs stap -up2 on both the function and the probe trees. At this + * point the trees are both filled with all data obtained from stap -vp1 + * After each tree is finished, an update event will be fired so callers + * know that they can update. + */ + public void run() { + runPass2Functions(); + fireUpdateEvent(); //Inform listeners that a new batch of functions has variable info + runPass2Probes(); + fireUpdateEvent(); //Inform listeners that a new batch of probes has variable info + stop(); + successfulFinish = true; + fireUpdateEvent(); //Inform listeners that everything is done + } + + /** + * This method will register a new listener with the parser + * @param listener The listener that will receive updateEvents + */ + public void addListener(IUpdateListener listener) { + if(null != listener) + listeners.add(listener); + } + + /** + * This method will unregister the listener with the parser + * @param listener The listener that no longer wants to recieve update events + */ + public void removeListener(IUpdateListener listener) { + if(null != listener) + listeners.remove(listener); + } + + /** + * This method will fire an updateEvent to all listeners. + */ + private void fireUpdateEvent() { + for(int i=0; i<listeners.size(); i++) + ((IUpdateListener)listeners.get(i)).handleUpdateEvent(); + } + + /** + * Runs the stap with the given options and returns the output generated + * @param options String[] of any optional parameters to pass to stap + * @param probe String containing the script to run stap on + * @param level integer representing what point to stop stap at (1,2,3,4,5) + */ + protected String runStap(String[] options, String probe, int level) { + String[] script = null; + + int size = 4; //start at 4 for stap, -pX, -e, script + if(null != tapsets && tapsets.length > 0 && tapsets[0].trim().length() > 0) + size += tapsets.length<<1; + if(null != options && options.length > 0 && options[0].trim().length() > 0) + size += options.length; + + script = new String[size]; + script[0] = "stap"; + script[1] = "-p" + level; + script[size-2] = "-e"; + script[size-1] = probe; + + //Add extra tapset directories + if(null != tapsets && tapsets.length > 0 && tapsets[0].trim().length() > 0) { + for(int i=0; i<tapsets.length; i++) { + script[2+(i<<1)] = "-I"; + script[3+(i<<1)] = tapsets[i]; + } + } + if(null != options && options.length > 0 && options[0].trim().length() > 0) { + for(int i=0; i<options.length; i++) + script[script.length-options.length-2+i] = options[i]; + } + + LoggedCommand cmd = new LoggedCommand(script, null, null, 0); + cmd.start(); + + //Block to prevent errors. + while(cmd.isRunning()) { + try { + Thread.sleep(100); + } catch(InterruptedException e) { + LogManager.logCritical("InterruptedException runStap: " + e.getMessage(), this); + } + } + + cmd.stop(); //While stop was already called we do this to ensure things are shutdown before proceding + String s = cmd.getOutput(); + cmd.dispose(); + + return s; + } + + /** + * Returns a String containing all of the content from the files + * contained in the tapset libraries. This file always returns + * what ever it could get, even if an exception was generated. + * + * stap -vp1 -e 'probe begin{}' + * Will list everything defined in the tapsets + * @return the tapset library consolodated into a single string + */ + private String readPass1(String script) { + String[] options; + if(null == script) { + script = "probe begin{}"; + options = new String[] {"-v"}; + } else { + options = null; + } + + return runStap(options, script, 1); + } + + /** + * Parses the output generated from running stap -vp1. Pulls out all functions + * and probe aliases from the provided string. Populates the probe and function + * trees. + * + * ProbeTree organized as: + * Root->Files->ProbePoints->Variables + * + * FunctionTree organized as: + * Root->Files->Functions + * @param s The entire output from running stap -vp1. + */ + private void parseLevel1(String s) { + String prev = null; + String prev2 = null; + StringBuilder token = new StringBuilder(""); + TreeNode parent; + TreeNode item; + char currChar; + boolean isProbe = false; + + boolean contains; + TreeNode child; + int z; + + for(int i=0; i<s.length(); i++) { + currChar = s.charAt(i); + + if(!Character.isWhitespace(currChar) && '}' != currChar && '{' != currChar) { + token.append(currChar); + } else if(token.length() > 0){ + prev2 = prev; + prev = token.toString(); + token.delete(0, token.length()); + } + + //Only check for new values when starting a fresh token. + if(1 == token.length()) { + if("probe".equals(prev2) && "=".equals(token.toString())) { + //Probe alias found + do { + currChar = s.charAt(++i); + token.append(currChar); + } while('{' != currChar && i < s.length()); + + parent = probes.getChildAt(probes.getChildCount()-1); + parent.add(new TreeDefinitionNode("probe " + token.toString().substring(2, token.length()-1), prev, parent.getData().toString(), true)); + isProbe = true; + } else if("function".equals(prev2)) { + //Function found + do { + currChar = s.charAt(++i); + token.append(currChar); + } while(')' != currChar && i < s.length()); + + parent = functions.getChildAt(functions.getChildCount()-1); + parent.add(new TreeDefinitionNode(prev + token.toString(), prev + token.toString(), parent.getData().toString(), true)); + isProbe = false; + } else if("file".equals(prev2)) { + //New file started + if(prev.lastIndexOf('/') > 0) + prev2 = prev.substring(prev.lastIndexOf('/')+1); + functions.add(new TreeNode(prev, prev2, false)); + probes.add(new TreeNode(prev, prev2, false)); + isProbe = false; + } + } else if(prev2 != null && prev2.length() > 2 && token.length() > 2 && isProbe && + '(' == prev2.charAt(0) && ')' == prev2.charAt(prev2.length()-1) && + '(' == token.charAt(0) && ')' == token.charAt(token.length()-1) && + "=".equals(prev)) { + //Put all variables in the probe tree + item = probes.getChildAt(probes.getChildCount()-1); + prev2 = prev2.substring(1,prev2.length()-1); + + child = item.getChildAt(item.getChildCount()-1); + contains = false; + for(z=0; z<child.getChildCount(); z++) { + if(child.getChildAt(z).toString().equals(prev2)) { + contains = true; + break; + } + } + + if(!contains) + child.add(new TreeNode(prev2 + ":unknown", prev2, false)); + + prev2 = null; + } + } + } + + /** + * This method is used to build up the list of functions that were found + * durring the first pass of stap. These functions will then all be passed + * on to have stap -up2 run on them in order to find the variable types + * associated with them. + */ + private void runPass2Functions() { + int i, j, k, l=0; + TreeNode child; + String function; + String[] parameters = new String[0]; + StringBuilder probe = new StringBuilder(""); + + ArrayList<String> functionNames = new ArrayList<String>(); + + //Add Functions + for(i=0; i<functions.getChildCount(); i++) { + child = functions.getChildAt(i); + for(j=0; j<child.getChildCount(); j++) { + probe.delete(0, probe.length()); + function = child.getChildAt(j).toString(); + probe.append(function.substring(0, function.indexOf("(")+1)); + function = function.substring(function.indexOf("(")+1, function.indexOf(")")); + parameters = function.split(","); + + //Make sure each parameter has a distinct name so there isn't a type problem + if(parameters[0].length() > 0) { + for(k=0; k<parameters.length; k++) { + if(k>0) + probe.append(","); + probe.append(parameters[k] + l++); + } + } + probe.append(")\n"); + functionNames.add(probe.toString()); + } + } + parameters = (String[])functionNames.toArray(parameters); + runPass2FunctionSet(parameters, 0, parameters.length-1); + } + + /** + * This method runs stap -up2 on the specified group of functions. + * If errors result, it will break the batch in half and run again + * on each subset + * @param funcs The list of all functions available in the tapsets + * @param low The lower bound of functions to use in this set + * @param high The upper bound of functions to use in this set + */ + private void runPass2FunctionSet(String[] funcs, int low, int high) { + if(low == high) + return; + if(stopped) + return; + + StringBuilder functionStr = new StringBuilder("probe begin{\n"); + for(int i=low; i<high; i++) + functionStr.append(funcs[i]); + functionStr.append("}\n"); + + String result = runStap(new String[] {"-u"}, functionStr.toString(), 2); + + if(0 < result.trim().length()) { + parsePass2Functions(result); + } else if(low+1 != high) { + runPass2FunctionSet(funcs, low, low+((high-low)>>1)); + runPass2FunctionSet(funcs, low+((high-low)>>1), high); + } + } + + /** + * Runs stap -up2 on the probe tree. The tree is broken up into + * smaller components to allow components to be completed at a time. + */ + private void runPass2Probes() { + //Add Probes + TreeNode temp; + for(int i=0; i<probes.getChildCount(); i++) { + if(stopped) + return; + temp = probes.getChildAt(i); + runPass2ProbeSet(temp, 0, temp.getChildCount()); + } + } + + /** + * Runs stap -up2 on the selected probe group, using high and low + * to determin which subelements to select. + * @param probe The top level probe group to probe. + * @param low The lower bound of child elements of probe to include + * @param high The upper bound of child elements of probe to inclue + */ + private void runPass2ProbeSet(TreeNode probe, int low, int high) { + if(low == high) + return; + + TreeNode temp; + StringBuilder probeStr = new StringBuilder(""); + String result; + + for(int i=low; i<high; i++) { + temp = probe.getChildAt(i); + if(temp.getData().toString().startsWith("probe")) + probeStr.append("\nprobe " + temp.toString() + "{}"); + else + runPass2ProbeSet(temp, 0, temp.getChildCount()); + } + result = runStap(new String[] {"-u"}, probeStr.toString(), 2); + + if(0 < result.trim().length()) { + boolean success = parsePass2Probes(result, probe); + if(!success) { + runPass2ProbeSet(probe, low, low+((high-low)>>1)); + runPass2ProbeSet(probe, low+((high-low)>>1), high); + } + } else if(low+1 != high) { + runPass2ProbeSet(probe, low, low+((high-low)>>1)); + runPass2ProbeSet(probe, low+((high-low)>>1), high); + } + } + + /** + * Removes all directories that do not contain any fuctions or + * probe aliases from both trees. + */ + protected void cleanupTrees() { + for(int i=functions.getChildCount()-1; i>=0; i--) { + if(0 == functions.getChildAt(i).getChildCount()) + functions.remove(i); + if(0 == probes.getChildAt(i).getChildCount()) + probes.remove(i); + } + + functions.sortTree(); + probes.sortTree(); + + formatProbes(); + } + + /** + * Reorders the probes tree so that probes are grouped by type + * instead of by file they were defined in. + * + * ProbeTree organized by class grouping. ie: + * syscall + * syscall.open + * filename + * flags + * mode + * name + * syscall.open.return + * name + * retstr + * syscall.read + * ... + * tcp + * tcp.disconnect + * tcp.disconnect.return + */ + private void formatProbes() { + TreeNode probes2 = new TreeNode("", false); + TreeNode probe, fileNode, probeGroup, probeFolder; + String directory; + String[] folders; + boolean added; + + for(int j,i=0; i<probes.getChildCount(); i++) { //Probe main group + fileNode = probes.getChildAt(i); + for(j=0; j<fileNode.getChildCount(); j++) { //Actual probes + probe = fileNode.getChildAt(j); + + directory = probe.toString(); + if(directory.endsWith(".return") || directory.endsWith(".entry")) + directory = directory.substring(0, directory.lastIndexOf('.')); + folders = directory.split("\\."); + + probeGroup = probes2; + for(int k=0; k<folders.length-1; k++) { //Complete path directory + added = false; + for(int l=0; l<probeGroup.getChildCount(); l++) { //Destination folder + probeFolder = probeGroup.getChildAt(l); + if(probeFolder.toString().equals(folders[k])) { + probeGroup = probeFolder; + added = true; + break; + } + } + if(!added) { //Create brand new folder since it doesn't exist yet + probeFolder = new TreeNode(folders[k], false); + probeGroup.add(probeFolder); + probeGroup = probeFolder; + } + } + probeGroup.add(probe); //Add the probe to its appropriate directory + } + } + probes = probes2; + probes.sortTree(); + } + + /** + * Parses the output generated from running stap -up2 on the list of functions. + * Will update the function tree with return values for each function. + * @param s The entire output from running stap -up2 on the functions. + */ + private void parsePass2Functions(String s) { + int i, j, k; + TreeNode child, child2; + String childString; + String[] functionLines = new String[0]; + if(s.contains("# functions") && s.contains("# probes")) + functionLines = s.substring(s.indexOf("# functions"), s.indexOf("# probes")).split("\n"); + + //Rename the functions with types + for(i=0; i<functionLines.length; i++) { + for(j=0; j<functions.getChildCount(); j++) { + child = functions.getChildAt(j); + for(k=0; k<child.getChildCount(); k++) { + child2 = child.getChildAt(k); + childString = child2.toString(); + if (childString.indexOf("(") != -1) { + if(functionLines[i].startsWith(childString.substring(0, childString.indexOf("(")).trim() + ":")) { + child2.setData(functionLines[i]); + break; + } + } + } + } + } + } + + /** + * Parses the output generated from running stap -up2 on the list of probes. + * Will update the probe alias tree with additional variables, as well as + * placing the type associated with the variable. + * @param s The entire output from running stap -up2 on the provided probeSet. + * @param probeSet The group of probes that the String s corresponds to + */ + private boolean parsePass2Probes(String s, TreeNode probeSet) { + LogManager.logDebug("Start parseLevel2Probes: probeSet-" + probeSet, this); + TreeNode tree = new TreeNode("", false); + TreeNode probe = null; + String[] probeLines = null; + boolean variables = false; + String line; + + if(s.contains("# probes")) + probeLines = s.substring(s.indexOf("# probes")).split("\n"); + + if(null == probeLines) + return false; + + //Build Pass 2 tree + for(int i=0; i<probeLines.length; i++) { + line = probeLines[i].trim(); + + if(line.startsWith("kernel.")) { + probe = new TreeNode(line, false); + tree.add(probe); + //probe = lookupProbe(line, probeSet); + variables = false; + } else if(line.equals("# locals") && null != probe) { + variables = true; + } else if(null != probe && variables) { + if(line.contains(":")) + probe.add(new TreeNode(line, line.substring(0, line.lastIndexOf(":")).trim(), false)); + } else { + probe = null; + } + } + + //Consolidate pass1 and pass2 trees + int i, j, k, l; + boolean matched; + TreeNode one, two, oneC, twoC; + for(i=0; i<probeSet.getChildCount(); i++) { + for(j=0; j<tree.getChildCount(); j++) { + one = probeSet.getChildAt(i); + two = tree.getChildAt(j); + + if(probesMatch(one, two)) { + for(l=0; l<two.getChildCount(); l++) { + matched = false; + twoC = two.getChildAt(l); + for(k=0; k<one.getChildCount(); k++) { + oneC = one.getChildAt(k); + if(oneC.getData().toString().substring(0, oneC.getData().toString().indexOf(":")). + equals(twoC.getData().toString().substring(0, twoC.getData().toString().indexOf(":")))) { + oneC.setData(twoC.getData()); + matched = true; + } + } + if(!matched) + one.add(new TreeNode(twoC.getData(), twoC.toString(), false)); + } + } + } + } + + tree.dispose(); + tree = null; + return true; + } + + /** + * Compares the probes contained in the treeNodes to make sure they + * are actually probing the same kernel location. + * @param one A treeNode generated from stap -p1. + * @param two A treeNode generated from stap -up2. + * @return A boolean signifing if the treeNodes represent the same probe point. + */ + private boolean probesMatch(TreeNode one, TreeNode two) { + try { + String valOneA = one.getData().toString(); + String valTwoA = two.getData().toString(); + String valOneB = ""; + String valTwoB = valTwoA.substring(valTwoA.indexOf("\"")+1, valTwoA.indexOf("@")); + + if(valOneA.contains("\"")) + valOneB = valOneA.substring(valOneA.indexOf("\"")+1); + if(valOneB.contains("\"")) + valOneB = valOneB.substring(0, valOneB.indexOf("\"")); + + if(valOneB.equals(valTwoB)) { + if(valOneA.contains(".return") == valTwoA.contains(".return")) + return true; + } + } catch(Exception e) { + LogManager.logCritical("Exception probesMatch: " + e.getMessage() + "\n" + one + "\n" + two, this); + } + return false; + } + + /** + * This method will clean up everything from the run. + */ + public void dispose() { + if(!disposed) { + disposed = true; + functions.dispose(); + functions = null; + probes.dispose(); + probes = null; + tapsets = null; + listeners.clear(); + listeners = null; + } + } + + private boolean stopped = true; + private boolean disposed = true; + private boolean successfulFinish = false; + private ArrayList<IUpdateListener> listeners; + private TreeNode functions; + private TreeNode probes; + private String[] tapsets; +}
\ No newline at end of file |