Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 3bf8c17681b716c456e26940a7c9d18cdf240686 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
/*******************************************************************************
 * Copyright (c) 2006,2012 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.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;
import java.util.regex.Pattern;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.linuxtools.internal.systemtap.ui.ide.structures.nodedata.ProbeNodeData;
import org.eclipse.linuxtools.internal.systemtap.ui.ide.structures.nodedata.ProbevarNodeData;
import org.eclipse.linuxtools.systemtap.structures.TreeDefinitionNode;
import org.eclipse.linuxtools.systemtap.structures.TreeNode;

/**
 * Runs stap -vp1 & stap -L in order to get all of the probes
 * that are defined in the tapsets.  Builds probeAlias trees
 * with the values obtained from the tapsets.
 *
 * @author Ryan Morse
 * @since 2.0
 */
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(){
        if (parser != null) {
            return parser;
        }
        parser = new ProbeParser();
        return parser;
    }

    private ProbeParser() {
        super(Messages.ProbeParser_name);
    }

    /**
     * @param tree To be valid, the first-level children of this tree must
     * be two nodes respectively named "Static Probes" and "Probe Alias".
     */
    @Override
    protected String isValidTree(TreeNode tree) {
        return tree.getChildByName(Messages.ProbeParser_staticProbes) != null
                && tree.getChildByName(Messages.ProbeParser_aliasProbes) != null
                ? null : Messages.ProbeParser_illegalArgMessage;
    }

    /**
     * Runs stap to collect all available tapset probes.
     * ProbeTree organized as:
     *    Root->Named Groups->ProbePoints->Variables
     */
    @Override
    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 An {@link IStatus} severity level for the result of the operation.
     */
    private int addStaticProbes(IProgressMonitor monitor) {
        TreeNode statics = new TreeNode(Messages.ProbeParser_staticProbes, false);
        tree.add(statics);
        if (monitor.isCanceled()) {
            return IStatus.CANCEL;
        }

        String probeDump = runStap(new String[]{"--dump-probe-types"}, null, false); //$NON-NLS-1$
        int result = verifyRunResult(probeDump);
        if (result != IStatus.OK) {
            return result;
        }

        boolean canceled = false;
        try (Scanner st = new Scanner(probeDump)) {
            TreeNode groupNode = null;
            while (st.hasNextLine()) {
                if (monitor.isCanceled()) {
                    canceled = true;
                    break;
                }
                String tokenString = st.nextLine();
                String probeName = (new StringTokenizer(tokenString)).nextToken();
                groupNode = addOrFindProbeGroup(extractProbeGroupName(probeName), groupNode, statics);
                groupNode.add(makeStaticProbeNode(probeName));
            }
        }
        statics.sortTree();
        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 An {@link IStatus} severity level for the result of the operation.
     */
    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 (monitor.isCanceled()) {
            return IStatus.CANCEL;
        }

        String probeDump = runStap(new String[]{"-L"}, "**", false); //$NON-NLS-1$ //$NON-NLS-2$
        int result = verifyRunResult(probeDump);
        if (result != IStatus.OK) {
            return result;
        }

        boolean canceled = false;
        try (Scanner st = new Scanner(probeDump)) {
            TreeNode groupNode = null;
            while (st.hasNextLine()) {
                if (monitor.isCanceled()) {
                    canceled = true;
                    break;
                }
                String tokenString = st.nextLine();
                // If the token starts with '_' or '__' it is a private probe so
                // skip it.
                if (tokenString.startsWith("_")) { //$NON-NLS-1$
                    continue;
                }
                // Only add this group if it is not a static probe group
                String groupName = extractProbeGroupName(tokenString);
                if (statics.getChildByName(groupName) != null) {
                    continue;
                }
                groupNode = addSingleProbeAlias(tokenString, aliases, groupNode, groupName, null);
            }
        }
        aliases.sortTree();
        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;
    }

    /**
     * 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 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.
        if (groupNode == null || !groupNode.toString().equals(groupName)) {
            groupNode = category.getChildByName(groupName);
        }

        // Create a new group and add it
        if (groupNode == null) {
            groupNode = new TreeNode(groupName, true);
            category.add(groupNode);
        }
        return groupNode;
    }

    /**
     * @return the name of the group a probe belongs to, based on the probe's name.
     */
    private String extractProbeGroupName(String 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, String definition) {
        return new TreeDefinitionNode(new ProbeNodeData(probeName), probeName, definition, true);
    }

    /**
     * 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) {
        String tapsetContents = SharedParser.getInstance().getTapsetContents();
        if (tapsetContents == null) {
            return null;
        }
        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);
        try (Scanner scanner = new Scanner(tapsetContents.substring(fileLocIndex))) {
            return SharedParser.findFileNameInTag(scanner.nextLine());
        }
    }

    /**
     * Extracts the local variables from a (partially examined) probe alias token, and
     * adds them as child tree entries of their parent probe.
     */
    private void addAllVarNodesToProbeNode(StringTokenizer varTokenizer, TreeNode probeNode) {
        StringBuilder prev = new StringBuilder(""); //$NON-NLS-1$
        // the remaining tokens are variable names and variable types name:type.
        while (varTokenizer.hasMoreTokens()) {
            String token = varTokenizer.nextToken();

            // Because some variable types contain spaces (var2:struct task_struct)
            // the only way to know if we have the entire string representing a
            // variable is if we reach the next token containing a ':' or we reach
            // the end of the stream.
            if (token.contains(":") && prev.length() > 0) { //$NON-NLS-1$
                prev.setLength(prev.length() - 1); // Remove the trailing space.
                addVarNodeToProbeNode(prev.toString(), probeNode);
                prev.setLength(0);
            }
            prev.append(token + " "); //$NON-NLS-1$
        }

        // Add the last token if there is one
        if (prev.length() > 0) {
            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;
    }

}

Back to the top