diff options
author | Andrew Ferrazzutti | 2014-07-22 15:07:31 +0000 |
---|---|---|
committer | Jeff Johnston | 2014-08-15 22:29:00 +0000 |
commit | 74a99f1c9f3ee6aba26a31063a0ee23a8d2f5cb3 (patch) | |
tree | bc4e6e8404c8973233150ad0d567f6fb50a5b784 /systemtap | |
parent | 931bda546c3e30a7a42f24ca2c810479cfe98662 (diff) | |
download | org.eclipse.linuxtools-74a99f1c9f3ee6aba26a31063a0ee23a8d2f5cb3.tar.gz org.eclipse.linuxtools-74a99f1c9f3ee6aba26a31063a0ee23a8d2f5cb3.tar.xz org.eclipse.linuxtools-74a99f1c9f3ee6aba26a31063a0ee23a8d2f5cb3.zip |
Systemtap: Implement selective tapset loading.
When the list of imported tapsets is changed (with
Preferences->SystemTap->IDE->Tapsets), instead of
triggering a new tapset load operation to re-load
all tapset contents, only load in the newly-added
tapsets and delete the ones that were removed.
Change-Id: I3dac162704f2bee5fc30217c3578a098c66aa724
Signed-off-by: Andrew Ferrazzutti <aferrazz@redhat.com>
Reviewed-on: https://git.eclipse.org/r/31464
Tested-by: Hudson CI
Reviewed-by: Jeff Johnston <jjohnstn@redhat.com>
Tested-by: Jeff Johnston <jjohnstn@redhat.com>
Diffstat (limited to 'systemtap')
8 files changed, 442 insertions, 137 deletions
diff --git a/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/structures/FunctionParser.java b/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/structures/FunctionParser.java index 3e1ca26c87..3e325220dc 100644 --- a/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/structures/FunctionParser.java +++ b/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/structures/FunctionParser.java @@ -45,7 +45,7 @@ public final class FunctionParser extends TreeTapsetParser { private static final Pattern P_FUNCTION = Pattern.compile("function (?!_)(\\w+) \\(.*?\\)"); //$NON-NLS-1$ private static final Pattern P_PARAM = Pattern.compile("(\\w+)(?:\\s*:\\s*(\\w+))?"); //$NON-NLS-1$ private static final Pattern P_ALL_CAP = Pattern.compile("[A-Z_1-9]*"); //$NON-NLS-1$ - private static final Pattern P_RETURN = Pattern.compile("\\sreturn\\W"); //$NON-NLS-1$ + private static final Pattern P_RETURN = Pattern.compile("(?<!\\w)return\\W"); //$NON-NLS-1$ public static FunctionParser getInstance() { if (parser != null) { @@ -56,16 +56,7 @@ public final class FunctionParser extends TreeTapsetParser { } private FunctionParser() { - super("Function Parser"); //$NON-NLS-1$ - } - - /** - * Runs stap to collect all available tapset functions. - */ - @Override - protected IStatus runAction(IProgressMonitor monitor) { - addFunctions(monitor); - return super.runAction(monitor); + super(Messages.FunctionParser_name); } /** @@ -74,58 +65,68 @@ public final class FunctionParser extends TreeTapsetParser { * * FunctionTree organized as: * Root->Functions->Parameters - * - * @return <code>false</code> if a cancelation prevented all functions from being added; - * <code>true</code> otherwise. */ - private boolean addFunctions(IProgressMonitor monitor) { + @Override + protected int runAction(IProgressMonitor monitor) { if (monitor.isCanceled()) { - return false; + return IStatus.CANCEL; } String tapsetContents = SharedParser.getInstance().getTapsetContents(); - if (tapsetContents == null) { - // Functions are only drawn from the tapset dump, so exit if it's empty. - return true; + int result = verifyRunResult(tapsetContents); + if (result != IStatus.OK) { + return result; } boolean canceled = false; - try (Scanner st = new Scanner(tapsetContents)) { - String filename = null; - String scriptText = null; - - SharedParser sparser = SharedParser.getInstance(); - while (st.hasNextLine()) { + try (Scanner scanner = new Scanner(tapsetContents)) { + scanner.useDelimiter("(?=" + SharedParser.TAG_FILE + ")"); //$NON-NLS-1$ //$NON-NLS-2$ + while (scanner.hasNext()) { if (monitor.isCanceled()) { canceled = true; break; } - String tok = st.nextLine(); - Matcher mFilename = sparser.filePattern.matcher(tok); - if (mFilename.matches()) { - filename = mFilename.group(1).toString(); - scriptText = null; - } else if (filename != null) { - Matcher mFunction = P_FUNCTION.matcher(tok); - if (mFunction.matches()) { - String functionName = mFunction.group(1); - if (P_ALL_CAP.matcher(functionName).matches()) { - // Ignore ALL_CAPS functions, since they are not meant for end-user use. - continue; - } - if (scriptText == null) { - // If this is the first time seeing this file, remove its comments. - scriptText = CommentRemover.execWithFile(filename); - } - addFunctionFromScript(functionName, scriptText, filename); - } - } + addFunctionsFromFileContents(scanner.next()); + } + } + tree.sortLevel(); + return !canceled ? IStatus.OK : IStatus.CANCEL; + } + + /** + * Uses the tapset content dump of a single file to collect all + * functions provided by that file. + * @param fileContents The tapset contents of a single file. + */ + private void addFunctionsFromFileContents(String fileContents) { + String filename; + try (Scanner st = new Scanner(fileContents)) { + filename = SharedParser.findFileNameInTag(st.nextLine()); + } + + Matcher matcher = P_FUNCTION.matcher(fileContents); + String scriptText = null; + while (matcher.find()) { + String functionName = matcher.group(1); + if (P_ALL_CAP.matcher(functionName).matches()) { + // Ignore ALL_CAPS functions, since they are not meant for end-user use. + continue; } + if (scriptText == null) { + // If this is the first time seeing this file, remove its comments. + scriptText = CommentRemover.execWithFile(filename); + } + addFunctionFromScript(functionName, scriptText, filename); } - tree.sortTree(); - return !canceled; } + /** + * Searches the actual contents of a .stp script file for a specific function, and adds + * @param functionName The name of the function to search for. + * @param scriptText The contents of the script to search, with its comments removed + * (Use {@link CommentRemover} on file contents before passing them here, if necessary). + * @param scriptFilename The name of the script file being searched. + */ private void addFunctionFromScript(String functionName, String scriptText, String scriptFilename) { String regex = MessageFormat.format(FUNC_REGEX, functionName); Matcher mScript = Pattern.compile(regex).matcher(scriptText); @@ -134,7 +135,7 @@ public final class FunctionParser extends TreeTapsetParser { String functionType = mScript.group(1); // If the function has no return type, look for a "return" statement to check // if it's really a void function, or if its return type is just unspecified - if (functionType == null && getNextBlockContents(scriptText, mScript.end(), P_RETURN)) { + if (functionType == null && isPatternInScriptBlock(scriptText, mScript.end(), P_RETURN)) { functionType = UNKNOWN_TYPE; } TreeDefinitionNode function = new TreeDefinitionNode( @@ -145,7 +146,7 @@ public final class FunctionParser extends TreeTapsetParser { } } - private boolean getNextBlockContents(String scriptText, int start, Pattern p) { + private boolean isPatternInScriptBlock(String scriptText, int start, Pattern p) { int end, bcount = 1; start = scriptText.indexOf('{', start) + 1; for (end = start; end < scriptText.length(); end++) { @@ -168,6 +169,58 @@ public final class FunctionParser extends TreeTapsetParser { mParams.group(1), false)); } } + parentFunction.sortLevel(); + } + + @Override + protected int delTapsets(String[] deletions, IProgressMonitor monitor) { + for (int i = 0; i < deletions.length; i++) { + for (int f = 0, fn = tree.getChildCount(); f < fn; f++) { + if (monitor.isCanceled()) { + return IStatus.CANCEL; + } + String definition = ((TreeDefinitionNode) tree.getChildAt(f)).getDefinition(); + if (definition != null && definition.startsWith(deletions[i])) { + tree.remove(f--); + fn--; + } + } + } + return IStatus.OK; + } + + @Override + protected int addTapsets(String[] additions, IProgressMonitor monitor) { + String tapsetContents = SharedParser.getInstance().getTapsetContents(); + boolean canceled = false; + + // Search tapset contents for all files provided by each added directory. + for (int i = 0; i < additions.length; i++) { + int firstTagIndex = 0; + while (true) { + // Get the contents of each file provided by the directory additions[i]. + firstTagIndex = tapsetContents.indexOf( + SharedParser.makeFileTag(additions[i]), firstTagIndex); + if (firstTagIndex == -1) { + break; + } + int nextTagIndex = tapsetContents.indexOf(SharedParser.TAG_FILE, firstTagIndex + 1); + String fileContents = nextTagIndex != -1 + ? tapsetContents.substring(firstTagIndex, nextTagIndex) + : tapsetContents.substring(firstTagIndex); + + if (monitor.isCanceled()) { + canceled = true; + break; + } + addFunctionsFromFileContents(fileContents); + // Remove the contents of the file that was just examined from the total contents. + tapsetContents = tapsetContents.substring(0, firstTagIndex).concat( + tapsetContents.substring(firstTagIndex + fileContents.length())); + } + } + tree.sortLevel(); + return !canceled ? IStatus.OK : IStatus.CANCEL; } } diff --git a/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/structures/Messages.java b/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/structures/Messages.java index 3ae0b62588..f1647a9700 100644 --- a/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/structures/Messages.java +++ b/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/structures/Messages.java @@ -17,6 +17,9 @@ import org.eclipse.osgi.util.NLS; */ public class Messages extends NLS { private static final String BUNDLE_NAME = "org.eclipse.linuxtools.internal.systemtap.ui.ide.structures.messages"; //$NON-NLS-1$ + public static String SharedParser_name; + public static String FunctionParser_name; + public static String ProbeParser_name; public static String ProbeParser_errorInitializingStaticProbes; public static String ProbeParser_staticProbes; public static String ProbeParser_aliasProbes; @@ -26,7 +29,6 @@ public class Messages extends NLS { public static String TapsetParser_ErrorRunningSystemtap; public static String TapsetParser_RemoteCredentialErrorTitle; public static String TapsetParser_RemoteCredentialErrorMessage; - public static String SharedParser_NoOutput; static { // initialize resource bundle NLS.initializeMessages(BUNDLE_NAME, Messages.class); diff --git a/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/structures/ProbeParser.java b/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/structures/ProbeParser.java index c096d6f6a1..d2ab9fc06f 100644 --- a/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/structures/ProbeParser.java +++ b/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/structures/ProbeParser.java @@ -12,6 +12,9 @@ package org.eclipse.linuxtools.internal.systemtap.ui.ide.structures; import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; import java.util.Scanner; import java.util.StringTokenizer; import java.util.regex.Matcher; @@ -36,6 +39,7 @@ public final class ProbeParser extends TreeTapsetParser { public static final String PROBE_REGEX = "(?s)(?<!\\w)probe\\s+{0}\\s*\\+?="; //$NON-NLS-1$ private static final String TAPSET_PROBE_REGEX = "probe {0} \\+?="; //$NON-NLS-1$ + private static final Pattern PROBE_GROUP_PATTERN = Pattern.compile("[^\\.\\( ]+"); //$NON-NLS-1$ private static ProbeParser parser = null; public static ProbeParser getInstance(){ @@ -47,7 +51,7 @@ public final class ProbeParser extends TreeTapsetParser { } private ProbeParser() { - super("Probe Parser"); //$NON-NLS-1$ + super(Messages.ProbeParser_name); } /** @@ -67,33 +71,34 @@ public final class ProbeParser extends TreeTapsetParser { * Root->Named Groups->ProbePoints->Variables */ @Override - protected IStatus runAction(IProgressMonitor monitor) { - addStaticProbes(monitor); - addProbeAliases(monitor, tree.getChildAt(0)); - return super.runAction(monitor); + protected int runAction(IProgressMonitor monitor) { + int result = addStaticProbes(monitor); + if (result == IStatus.OK) { + result = addProbeAliases(monitor); + } + return result; } /** * Runs stap to obtain a log of all static probes, and populate the probe tree with them. - * - * @return <code>false</code> if a cancelation prevented all probes from being added; - * <code>true</code> otherwise. + * @return An {@link IStatus} severity level for the result of the operation. */ - private boolean addStaticProbes(IProgressMonitor monitor) { + private int addStaticProbes(IProgressMonitor monitor) { TreeNode statics = new TreeNode(Messages.ProbeParser_staticProbes, false); tree.add(statics); if (monitor.isCanceled()) { - return false; + return IStatus.CANCEL; } String probeDump = runStap(new String[]{"--dump-probe-types"}, null, false); //$NON-NLS-1$ - if (probeDump == null) { - return false; + int result = verifyRunResult(probeDump); + if (result != IStatus.OK) { + return result; } boolean canceled = false; try (Scanner st = new Scanner(probeDump)) { - TreeNode group = null; + TreeNode groupNode = null; while (st.hasNextLine()) { if (monitor.isCanceled()) { canceled = true; @@ -101,36 +106,39 @@ public final class ProbeParser extends TreeTapsetParser { } String tokenString = st.nextLine(); String probeName = (new StringTokenizer(tokenString)).nextToken(); - group = addOrFindProbeGroup(extractProbeGroupName(probeName), group, statics); - group.add(makeStaticProbeNode(probeName)); + groupNode = addOrFindProbeGroup(extractProbeGroupName(probeName), groupNode, statics); + groupNode.add(makeStaticProbeNode(probeName)); } } statics.sortTree(); - return !canceled; + return !canceled ? IStatus.OK : IStatus.CANCEL; } /** * Runs stap to obtain a log of all probe aliases & their variables, * and populate the probe tree with them. - * - * @return <code>false</code> if a cancelation prevented all probes from being added; - * <code>true</code> otherwise. + * @return An {@link IStatus} severity level for the result of the operation. */ - private boolean addProbeAliases(IProgressMonitor monitor, TreeNode statics) { + private int addProbeAliases(IProgressMonitor monitor) { + TreeNode statics = tree.getChildByName(Messages.ProbeParser_staticProbes); + if (statics == null) { + return IStatus.ERROR; + } TreeNode aliases = new TreeNode(Messages.ProbeParser_aliasProbes, false); tree.add(aliases); - if (statics == null || monitor.isCanceled()) { - return false; + if (monitor.isCanceled()) { + return IStatus.CANCEL; } String probeDump = runStap(new String[]{"-L"}, "**", false); //$NON-NLS-1$ //$NON-NLS-2$ - if (probeDump == null) { - return false; + int result = verifyRunResult(probeDump); + if (result != IStatus.OK) { + return result; } boolean canceled = false; try (Scanner st = new Scanner(probeDump)) { - TreeNode group = null; + TreeNode groupNode = null; while (st.hasNextLine()) { if (monitor.isCanceled()) { canceled = false; @@ -142,35 +150,56 @@ public final class ProbeParser extends TreeTapsetParser { if (tokenString.startsWith("_")) { //$NON-NLS-1$ continue; } - - StringTokenizer probeTokenizer = new StringTokenizer(tokenString); - String probeName = probeTokenizer.nextToken(); - - String groupName = extractProbeGroupName(tokenString); // Only add this group if it is not a static probe group - if (statics.getChildByName(groupName) == null) { - TreeNode probeNode = makeProbeAliasNode(probeName); - group = addOrFindProbeGroup(groupName, group, aliases); - group.add(probeNode); - addAllVarNodesToProbeNode(probeTokenizer, probeNode); + String groupName = extractProbeGroupName(tokenString); + if (statics.getChildByName(groupName) != null) { + continue; } + groupNode = addSingleProbeAlias(tokenString, aliases, groupNode, groupName, null); } } aliases.sortTree(); - return !canceled; + return !canceled ? IStatus.OK : IStatus.CANCEL; + } + + /** + * Adds a single probe alias to the collection. + * @param probeLine A line of probe information printed by a call to "stap -L". + * @param aliases The tree of probe aliases. The probe will be added to this tree. + * @param groupNode For optimization, pass an existing group node here, as it will be used if the + * probe belongs in it. Otherwise, or if <code>null</code> is passed, a new one will be created. + * @param groupName The name of the probe group, or <code>null</code> if it is unknown at the time + * this method is called. + * @param definition The path of the file in which this probe is defined, or <code>null</code> if it + * is unknown at the time this method is called. + */ + private TreeNode addSingleProbeAlias(String probeLine, TreeNode aliases, TreeNode groupNode, + String groupName, String definition) { + StringTokenizer probeTokenizer = new StringTokenizer(probeLine); + String probeName = probeTokenizer.nextToken(); + + TreeNode probeNode = makeProbeAliasNode(probeName, + definition == null ? findDefinitionOf(probeName) : definition); + + groupNode = addOrFindProbeGroup( + groupName == null ? extractProbeGroupName(probeName) : groupName, + groupNode, aliases); + + groupNode.add(probeNode); + addAllVarNodesToProbeNode(probeTokenizer, probeNode); + return groupNode; } /** - * Find the appropriate parent group node for a probe alias to group probes by name. + * Finds the appropriate parent group node for a probe alias to group probes by name. * If it doesn't yet exist, create it and add it to the view's tree. - * @param probeLine The name of the probe group. + * @param groupName The name of the probe group. * @param groupNode For optimization, pass an existing group node here, as it will be * used if the probe belongs in it. Otherwise, or if <code>null</code> is passed, a new one will be created. * @param category The parent tree node in which to put the group node. * @return The found or created group node that will be the parent of the probe's entry item in the view. */ private TreeNode addOrFindProbeGroup(String groupName, TreeNode groupNode, TreeNode category) { - // If the current probe belongs to a group other than // the most recent group. This should rarely be needed because the // probe list is sorted... mostly. @@ -190,50 +219,38 @@ public final class ProbeParser extends TreeTapsetParser { * @return the name of the group a probe belongs to, based on the probe's name. */ private String extractProbeGroupName(String probeName) { - int dotIndex = probeName.indexOf('.'); - int parenIndex = probeName.indexOf('('); - if (dotIndex > 0 && parenIndex > 0) { - return probeName.substring(0, Math.min(dotIndex, parenIndex)); - } - if (dotIndex > 0) { - return probeName.substring(0, dotIndex); - } - if (parenIndex > 0) { - return probeName.substring(0, parenIndex); - } - return probeName; + Matcher m = PROBE_GROUP_PATTERN.matcher(probeName); + return m.find() ? m.group() : probeName; } private TreeNode makeStaticProbeNode(String probeName) { return new TreeNode(new ProbeNodeData(probeName), probeName, true); } - private TreeNode makeProbeAliasNode(String probeName) { - return new TreeDefinitionNode(new ProbeNodeData(probeName), probeName, findDefinitionOf(probeName), true); + private TreeNode makeProbeAliasNode(String probeName, String definition) { + return new TreeDefinitionNode(new ProbeNodeData(probeName), probeName, definition, true); } /** - * Search the tapset content dump for the path of the file which defines the provided probe alias. + * Searches the tapset content dump for the path of the file which defines the provided probe alias. * @param probeName The alias of the probe to find the definition file of. * @return The path of the probe's definition file, or <code>null</code> if a definition * file can't be found (which is the case for static probes). */ private String findDefinitionOf(String probeName) { - SharedParser sparser = SharedParser.getInstance(); - String tapsetContents = sparser.getTapsetContents(); + String tapsetContents = SharedParser.getInstance().getTapsetContents(); if (tapsetContents == null) { return null; } - Matcher probeMatcher = Pattern.compile(MessageFormat.format(TAPSET_PROBE_REGEX, Pattern.quote(probeName))).matcher(tapsetContents); + Matcher probeMatcher = Pattern.compile(MessageFormat.format( + TAPSET_PROBE_REGEX, Pattern.quote(probeName))).matcher(tapsetContents); if (!probeMatcher.find()) { return null; } - int fileLocIndex = tapsetContents.substring(0, probeMatcher.start()).lastIndexOf(SharedParser.TAG_FILE); + int fileLocIndex = tapsetContents.substring(0, probeMatcher.start()) + .lastIndexOf(SharedParser.TAG_FILE); try (Scanner scanner = new Scanner(tapsetContents.substring(fileLocIndex))) { - Matcher fileMatcher = sparser.filePattern.matcher(scanner.nextLine()); - return fileMatcher.matches() - ? fileMatcher.group(1) - : null; + return SharedParser.findFileNameInTag(scanner.nextLine()); } } @@ -264,10 +281,99 @@ public final class ProbeParser extends TreeTapsetParser { prev.setLength(prev.length() - 1); // Remove the trailing space. addVarNodeToProbeNode(prev.toString(), probeNode); } + probeNode.sortLevel(); } private void addVarNodeToProbeNode(String info, TreeNode probeNode) { probeNode.add(new TreeNode(new ProbevarNodeData(info), info, false)); } + @Override + protected int delTapsets(String[] tapsets, IProgressMonitor monitor) { + TreeNode aliases = tree.getChildByName(Messages.ProbeParser_aliasProbes); + + // Search through alias groups for probes whose definition files + // come from removed directories, and remove them from the group. + for (int i = 0; i < tapsets.length; i++) { + for (int g = 0, gn = aliases.getChildCount(); g < gn; g++) { + if (monitor.isCanceled()) { + return IStatus.CANCEL; + } + TreeNode group = aliases.getChildAt(g); + for (int p = 0, pn = group.getChildCount(); p < pn; p++) { + String definition = ((TreeDefinitionNode) group.getChildAt(p)).getDefinition(); + if (definition != null && definition.startsWith(tapsets[i])) { + group.remove(p--); + pn--; + } + } + // If removing the only probe left in a probe group, remove the group. + if (group.getChildCount() == 0) { + aliases.remove(g--); + gn--; + } + } + } + return IStatus.OK; + } + + @Override + protected int addTapsets(String[] additions, IProgressMonitor monitor) { + String tapsetContents = SharedParser.getInstance().getTapsetContents(); + boolean canceled = false; + TreeNode aliases = tree.getChildByName(Messages.ProbeParser_aliasProbes); + Map<String, ArrayList<String>> fileToItemMap = new HashMap<>(); + + // Search tapset contents for all files provided by each added directory. + for (int i = 0; i < additions.length; i++) { + int firstTagIndex = 0; + while (true) { + // Get the contents of each file provided by the directory additions[i]. + firstTagIndex = tapsetContents.indexOf( + SharedParser.makeFileTag(additions[i]), firstTagIndex); + if (firstTagIndex == -1) { + break; + } + int nextTagIndex = tapsetContents.indexOf(SharedParser.TAG_FILE, firstTagIndex + 1); + String fileContents = nextTagIndex != -1 + ? tapsetContents.substring(firstTagIndex, nextTagIndex) + : tapsetContents.substring(firstTagIndex); + + String filename; + try (Scanner st = new Scanner(fileContents)) { + filename = SharedParser.findFileNameInTag(st.nextLine()); + } + + // Search file contents for the probes the file provides. + ArrayList<String> newItems = new ArrayList<>(); + Matcher matcher = Pattern.compile(MessageFormat.format( + TAPSET_PROBE_REGEX, "(\\S+)")) //$NON-NLS-1$ + .matcher(fileContents); + while (matcher.find()) { + newItems.add(matcher.group(1)); + } + if (!newItems.isEmpty()) { + fileToItemMap.put(filename, newItems); + } + // Remove the contents of the file that was just examined from the total contents. + tapsetContents = tapsetContents.substring(0, firstTagIndex).concat( + tapsetContents.substring(firstTagIndex + fileContents.length())); + } + } + // Run stap on each discovered probe to obtain their variable information. + for (String file : fileToItemMap.keySet()) { + for (String newitem : fileToItemMap.get(file)) { + if (canceled || monitor.isCanceled()) { + canceled = true; + break; + } + addSingleProbeAlias(runStap(new String[]{"-L"}, newitem, false), //$NON-NLS-1$ + aliases, null, null, file); + } + } + + aliases.sortTree(); + return !canceled ? IStatus.OK : IStatus.CANCEL; + } + } diff --git a/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/structures/SharedParser.java b/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/structures/SharedParser.java index 18e88f6f19..c845f28fff 100644 --- a/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/structures/SharedParser.java +++ b/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/structures/SharedParser.java @@ -11,12 +11,11 @@ package org.eclipse.linuxtools.internal.systemtap.ui.ide.structures; +import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Status; -import org.eclipse.linuxtools.internal.systemtap.ui.ide.IDEPlugin; /** * A helper class for performing tapset-loading operations, @@ -26,13 +25,43 @@ import org.eclipse.linuxtools.internal.systemtap.ui.ide.IDEPlugin; */ public final class SharedParser extends TapsetParser { - static final String TAG_FILE = "# file"; //$NON-NLS-1$ private static final String[] STAP_OPTIONS = new String[] {"-v", "-p1", "-e"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ private static final String STAP_DUMMYPROBE = "probe begin{}"; //$NON-NLS-1$ + + /* + * Note: tapset content dumps (as printed by a pass 1 call of stap) list the contents + * of each script file found in all tapset directores. The contents of each file are + * prefaced/tagged with the line "# file <filename>", so contents of individual files + * can be found by searching the dump for such a tag. + */ + /** + * The start of the tag that is always printed immediately before a file's contents in + * a tapset dump. Useful for searching through dumps for the contents of an individual file. + */ + static final String TAG_FILE = "# file "; //$NON-NLS-1$ + private static final Pattern FILE_PATTERN = Pattern.compile(TAG_FILE.concat("(/.*\\.stp)")); //$NON-NLS-1$ + /** - * A pattern that can be used to locate file paths listed in stap tapset dumps. + * Returns the entire tag that is printed immediately before a given file's contents in + * a tapset dump. + * @param fileName The name of the file (or a regex string) to get a tag for. + * @return The tag for the given file's contents as it appears in the tapset dump. */ - final Pattern filePattern = Pattern.compile("# file (/.*\\.stp)"); //$NON-NLS-1$ + static String makeFileTag(String fileName) { + return TAG_FILE.concat(fileName); + } + + /** + * Searches a string of tapset contents for a file tag and extracts the filename + * found in the tag. + * @param contents The tapset contents to search through. Preferably pass just a file tag + * here (generated by a call to {@linkplain #makeFileTag(String)}. + * @return The file name found. + */ + static String findFileNameInTag(String contents) { + Matcher matcher = FILE_PATTERN.matcher(contents); + return matcher.find() ? matcher.group(1) : null; + } private String tapsetContents = null; @@ -46,7 +75,7 @@ public final class SharedParser extends TapsetParser { } private SharedParser() { - super("Shared Parser"); //$NON-NLS-1$ + super(Messages.SharedParser_name); } /** @@ -75,8 +104,9 @@ public final class SharedParser extends TapsetParser { @Override protected IStatus run(IProgressMonitor monitor) { String contents = runStap(STAP_OPTIONS, STAP_DUMMYPROBE, false); - if (contents == null) { - return new Status(IStatus.ERROR, IDEPlugin.PLUGIN_ID, Messages.SharedParser_NoOutput); + int result = verifyRunResult(contents); + if (result != IStatus.OK) { + return createStatus(result); } // Exclude the dump of the test script by excluding everything before the second pathname // (which is the first actual tapset file, not the input script). @@ -87,7 +117,7 @@ public final class SharedParser extends TapsetParser { tapsetContents = contents.substring(beginIndex); } } - return new Status(IStatus.OK, IDEPlugin.PLUGIN_ID, ""); //$NON-NLS-1$ + return createStatus(IStatus.OK); } } diff --git a/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/structures/TapsetLibrary.java b/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/structures/TapsetLibrary.java index 1902b5d5ba..e3a96c7b83 100644 --- a/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/structures/TapsetLibrary.java +++ b/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/structures/TapsetLibrary.java @@ -12,6 +12,9 @@ package org.eclipse.linuxtools.internal.systemtap.ui.ide.structures; import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import org.eclipse.core.runtime.jobs.IJobChangeEvent; import org.eclipse.core.runtime.jobs.Job; @@ -29,8 +32,6 @@ import org.eclipse.linuxtools.systemtap.ui.consolelog.internal.ConsoleLogPlugin; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.PlatformUI; - - /** * This class is used for obtaining all probes and functions from the tapsets. * If stored tapsets are in use, it will try to obtain the list from the TreeSettings memento. @@ -94,8 +95,9 @@ public final class TapsetLibrary { @Override public void propertyChange(PropertyChangeEvent event) { String property = event.getProperty(); - if (property.equals(IDEPreferenceConstants.P_TAPSETS) - || property.equals(PreferenceConstants.P_ENV.SYSTEMTAP_TAPSET.toPrefKey()) + if (property.equals(IDEPreferenceConstants.P_TAPSETS)) { + applyTapsetChanges((String) event.getOldValue(), (String) event.getNewValue()); + } else if (property.equals(PreferenceConstants.P_ENV.SYSTEMTAP_TAPSET.toPrefKey()) || property.equals(IDEPreferenceConstants.P_REMOTE_PROBES)) { runStapParser(); } else if (property.equals(IDEPreferenceConstants.P_STORED_TREE)) { @@ -155,6 +157,22 @@ public final class TapsetLibrary { probeParser.schedule(); } + private static void applyTapsetChanges(String oldTapsets, String newTapsets) { + List<String> oldList = Arrays.asList(oldTapsets.split(File.pathSeparator)); + List<String> newList = Arrays.asList(newTapsets.split(File.pathSeparator)); + List<String> additions = new ArrayList<>(newList); + additions.removeAll(oldList); + additions.remove(""); //$NON-NLS-1$ + List<String> deletions = new ArrayList<>(oldList); + deletions.removeAll(newList); + deletions.remove(""); //$NON-NLS-1$ + String[] additionArray = additions.toArray(new String[additions.size()]); + String[] deletionArray = deletions.toArray(new String[deletions.size()]); + SharedParser.getInstance().clearTapsetContents(); + probeParser.runUpdate(additionArray, deletionArray); + functionParser.runUpdate(additionArray, deletionArray); + } + private static void clearTrees() { if (functionTree != null) { functionTree.dispose(); diff --git a/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/structures/TapsetParser.java b/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/structures/TapsetParser.java index 59b73dbeab..efdfb9a31b 100644 --- a/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/structures/TapsetParser.java +++ b/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/structures/TapsetParser.java @@ -16,6 +16,8 @@ import java.io.IOException; import java.net.ConnectException; import java.util.concurrent.atomic.AtomicBoolean; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.preference.IPreferenceStore; @@ -65,6 +67,13 @@ public abstract class TapsetParser extends Job { } /** + * Generates a {@link Status} with the provided severity. + */ + protected IStatus createStatus(int severity) { + return new Status(severity, IDEPlugin.PLUGIN_ID, ""); //$NON-NLS-1$ + } + + /** * Runs stap with the given options and returns the output generated, * or <code>null</code> if the case of an error. * @param options String[] of any optional parameters to pass to stap @@ -72,6 +81,8 @@ public abstract class TapsetParser extends Job { * or <code>null</code> for scriptless commands * @param getErrors Set this to <code>true</code> if the script's error * stream contents should be returned instead of its standard output + * @return The output generated from the stap run, or <code>null</code> + * in the case of an error, or an empty string if the run was canceled. */ protected String runStap(String[] options, String probe, boolean getErrors) { String[] args = null; @@ -118,12 +129,26 @@ public abstract class TapsetParser extends Job { } catch (IOException e) { displayError(Messages.TapsetParser_ErrorRunningSystemtap, e.getMessage()); } catch (InterruptedException e) { - // Interrupted; exit. + return ""; //$NON-NLS-1$ } return null; } + /** + * Return an {@link IStatus} severity constant for the result of a call to + * {@link TapsetParser#runStap(String[], String, boolean)}. + * @param result The output generated by a call to {@link #runStap(String[], String, boolean)}. + */ + protected int verifyRunResult(String result) { + if (result == null) { + return IStatus.ERROR; + } else if (result.isEmpty()) { + return IStatus.CANCEL; + } + return IStatus.OK; + } + private String runLocalStap(String[] args, boolean getErrors) throws IOException, InterruptedException { Process process = RuntimeProcessFactory.getFactory().exec( args, EnvironmentVariablesPreferencePage.getEnvironmentVariables(), null); diff --git a/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/structures/TreeTapsetParser.java b/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/structures/TreeTapsetParser.java index e3b5f2ebe1..095c867f84 100644 --- a/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/structures/TreeTapsetParser.java +++ b/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/structures/TreeTapsetParser.java @@ -13,8 +13,6 @@ package org.eclipse.linuxtools.internal.systemtap.ui.ide.structures; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Status; -import org.eclipse.linuxtools.internal.systemtap.ui.ide.IDEPlugin; import org.eclipse.linuxtools.systemtap.structures.TreeNode; /** @@ -23,8 +21,20 @@ import org.eclipse.linuxtools.systemtap.structures.TreeNode; */ public abstract class TreeTapsetParser extends TapsetParser { + private class TapsetChanges { + private String[] additions; + private String[] deletions; + private TapsetChanges(String[] additions, String[] deletions) { + this.additions = new String[additions.length]; + this.deletions = new String[deletions.length]; + System.arraycopy(additions, 0, this.additions, 0, additions.length); + System.arraycopy(deletions, 0, this.deletions, 0, deletions.length); + } + } + protected TreeNode tree = null; private TreeNode forcedTree = null; + private TapsetChanges tapsetChanges = null; protected TreeTapsetParser(String jobTitle) { super(jobTitle); @@ -37,21 +47,80 @@ public abstract class TreeTapsetParser extends TapsetParser { @Override protected final synchronized IStatus run(IProgressMonitor monitor) { if (forcedTree != null) { + tapsetChanges = null; tree = forcedTree; forcedTree = null; - return new Status(IStatus.OK, IDEPlugin.PLUGIN_ID, ""); //$NON-NLS-1$ - } else { - tree = new TreeNode(null, false); - return runAction(monitor); + return createStatus(IStatus.OK); } + if (tapsetChanges != null) { + return performUpdate(monitor); + } + tree = new TreeNode(null, false); + return createStatus(runAction(monitor)); + } + + /** + * Loads the tapset contents and saves them. + * @param monitor The progress monitor for the operation. + * @return An {@link IStatus} severity level for the result of the operation. + */ + protected abstract int runAction(IProgressMonitor monitor); + + /** + * After adding / removing tapsets, scheduled the job that + * loads in / discards the tapsets that were added / removed. + * @param additions The list of added tapset directories. + * @param deletions The list of removed tapset directories. + */ + public synchronized void runUpdate(String[] additions, String[] deletions) { + tapsetChanges = new TapsetChanges(additions, deletions); + schedule(); } - protected IStatus runAction(IProgressMonitor monitor) { - return new Status(!monitor.isCanceled() ? IStatus.OK : IStatus.CANCEL, - IDEPlugin.PLUGIN_ID, ""); //$NON-NLS-1$ + /** + * Performs both stages of a tapset update operation, ensuring that the new + * tapset contents will be available when they are necessary. + * @param monitor The operation's progress monitor. + * @return The status of the operation's outcome. + */ + private IStatus performUpdate(IProgressMonitor monitor) { + int result = IStatus.OK; + if (tapsetChanges.deletions.length > 0) { + result = delTapsets(tapsetChanges.deletions, monitor); + } + if (result == IStatus.OK && tapsetChanges.additions.length > 0) { + if (monitor.isCanceled()) { + result = IStatus.CANCEL; + } else { + String tapsetContents = SharedParser.getInstance().getTapsetContents(); + result = verifyRunResult(tapsetContents); + if (result == IStatus.OK) { + result = addTapsets(tapsetChanges.additions, monitor); + } + } + } + tapsetChanges = null; + return createStatus(result); } /** + * After changing the list of imported tapsets, discards the tapsets that were removed. + * @param deletions A non-empty list of removed tapset directories. + * @param monitor The progress monitor for the operation. + * @return An {@link IStatus} severity level for the result of the operation. + */ + protected abstract int delTapsets(String[] deletions, IProgressMonitor monitor); + + /** + * After changing the list of imported tapsets, loads in the tapsets that were added. + * Tapset contents are guaranteed to be loaded at the time this method is called. + * @param additions A non-empty list of added tapset directories. + * @param monitor The progress monitor for the operation. + * @return An {@link IStatus} severity level for the result of the operation. + */ + protected abstract int addTapsets(String[] additions, IProgressMonitor monitor); + + /** * @return The tree that this parser constructs. */ public final synchronized TreeNode getTree() { @@ -76,7 +145,7 @@ public abstract class TreeTapsetParser extends TapsetParser { /** * Check if the provided tree a valid tree for this parser. * Called internally by {@link #setTree(TreeNode)}. - * @param tree The tree to check for validity. + * @param tree The tree to check for validity. * @return <code>null</code> if the tree is valid; otherwise, * an error message signifying why the tree is invalid. */ diff --git a/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/structures/messages.properties b/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/structures/messages.properties index 0a1e9dafeb..e5be6a7a6c 100644 --- a/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/structures/messages.properties +++ b/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/structures/messages.properties @@ -9,6 +9,9 @@ # Red Hat - initial API and implementation ############################################################################### +SharedParser_name=Preparation for tapset parsers +FunctionParser_name=Function Parser +ProbeParser_name=Probe Parser ProbeParser_errorInitializingStaticProbes=Could not initialize static probe list ProbeParser_staticProbes=Static Probes ProbeParser_aliasProbes=Probe Aliases @@ -18,4 +21,3 @@ TapsetParser_CannotRunStapTitle=Cannot Run SystemTap TapsetParser_ErrorRunningSystemtap=Error Running SystemTap TapsetParser_RemoteCredentialErrorTitle=Remote Login Error TapsetParser_RemoteCredentialErrorMessage=Unable to login to the remote host for loading tapset contents. Edit the login credentials now? -SharedParser_NoOutput=No stap output was generated
\ No newline at end of file |