Commit | Line | Data |
---|---|---|
da8308fe AM |
1 | package org.lttng.ust.agent.utils; |
2 | ||
3 | import java.io.IOException; | |
4 | import java.lang.ProcessBuilder.Redirect; | |
9368bd5e | 5 | import java.nio.file.FileVisitResult; |
da8308fe AM |
6 | import java.nio.file.Files; |
7 | import java.nio.file.Path; | |
9368bd5e AM |
8 | import java.nio.file.Paths; |
9 | import java.nio.file.SimpleFileVisitor; | |
10 | import java.nio.file.attribute.BasicFileAttributes; | |
11 | import java.util.ArrayList; | |
da8308fe AM |
12 | import java.util.Arrays; |
13 | import java.util.List; | |
14 | import java.util.stream.Collectors; | |
15 | ||
16 | public final class LttngSessionControl { | |
17 | ||
18 | private LttngSessionControl() {} | |
19 | ||
20 | public enum Domain { | |
21 | JUL("-j"), | |
22 | LOG4J("-l"); | |
23 | ||
24 | private final String flag; | |
25 | ||
26 | private Domain(String flag) { | |
27 | this.flag = flag; | |
28 | } | |
29 | ||
30 | public String flag() { | |
31 | return flag; | |
32 | } | |
33 | } | |
34 | ||
35 | // ------------------------------------------------------------------------ | |
36 | // Public utility methods | |
37 | // ------------------------------------------------------------------------ | |
38 | ||
39 | /** | |
40 | * Setup a LTTng session by enabling certain events (or none). | |
41 | * | |
42 | * @param sessionName | |
43 | * The name of the session to create. May be null to use the | |
44 | * default one from lttng-tools. | |
45 | * @param domain | |
46 | * The tracing domain | |
47 | * @param enabledEvents | |
48 | * The list of events to enable. May be null or empty, to not | |
49 | * enable any events. | |
50 | * @return If the command executed successfully (return code = 0). | |
51 | */ | |
52 | public static boolean setupSession(String sessionName, Domain domain, String... enabledEvents) { | |
53 | String[] createCommand = (sessionName == null ? | |
54 | new String[] { "lttng", "create" } : | |
55 | new String[] { "lttng", "create", sessionName} | |
56 | ); | |
57 | ||
58 | String eventsToEnable = (enabledEvents == null || enabledEvents.length == 0 ? | |
59 | /* | |
60 | * We have to enable a channel for 'lttng start' to work. | |
61 | * However, we cannot enable a channel directly, see | |
62 | * https://bugs.lttng.org/issues/894 . Instead we will enable an | |
63 | * event we know does not exist | |
64 | */ | |
65 | "non-event" : | |
66 | Arrays.stream(enabledEvents).collect(Collectors.joining(",")) | |
67 | ); | |
68 | ||
69 | return executeCommands(new String[][] { | |
70 | createCommand, | |
71 | { "lttng", "enable-event", domain.flag(), eventsToEnable}, | |
72 | { "lttng", "start" } | |
73 | }); | |
74 | } | |
75 | ||
76 | /** | |
77 | * Setup a LTTng session with all events enabled (lttng enable-event -a). | |
78 | * | |
79 | * @param sessionName | |
80 | * The name of the session to create. May be null to use the | |
81 | * default one from lttng-tools. | |
82 | * @param domain | |
83 | * The tracing domain | |
84 | * @return If the command executed successfully (return code = 0). | |
85 | */ | |
86 | public static boolean setupSessionAllEvents(String sessionName, Domain domain) { | |
87 | String[] createCommand = (sessionName == null ? | |
88 | new String[] { "lttng", "create" } : | |
89 | new String[] { "lttng", "create", sessionName} | |
90 | ); | |
91 | ||
92 | return executeCommands(new String[][] { | |
93 | createCommand, | |
94 | { "lttng", "enable-event", domain.flag(), "-a" }, | |
95 | { "lttng", "start" } | |
96 | }); | |
97 | } | |
98 | ||
9368bd5e AM |
99 | /** |
100 | * Send a separate enable-event command. | |
101 | * | |
102 | * @param sessionName | |
103 | * Name of the session in which to enable events. Use null for | |
104 | * current session. | |
105 | * @param domain | |
106 | * The tracing domain | |
107 | * @param enabledEvents | |
108 | * The list of events to enable. Should not be null or empty | |
109 | * @return If the command executed successfully (return code = 0). | |
110 | */ | |
111 | public static boolean enableEvents(String sessionName, Domain domain, String... enabledEvents) { | |
112 | if (enabledEvents == null || enabledEvents.length == 0) { | |
113 | throw new IllegalArgumentException(); | |
114 | } | |
115 | List<String> command = new ArrayList<String>(); | |
116 | command.add("lttng"); | |
117 | command.add("enable-event"); | |
118 | command.add(domain.flag()); | |
119 | command.add(Arrays.stream(enabledEvents).collect(Collectors.joining(","))); | |
120 | if (sessionName != null) { | |
121 | command.add("-s"); | |
122 | command.add(sessionName); | |
123 | } | |
124 | return executeCommand(command.toArray(new String[0])); | |
125 | } | |
126 | ||
127 | /** | |
128 | * Send a disable-event command. Used to disable events that were previously | |
129 | * enabled. | |
130 | * | |
131 | * @param sessionName | |
132 | * Name of the session in which to disable events. Use null for | |
133 | * current session. | |
134 | * @param domain | |
135 | * The tracing domain | |
136 | * @param disabledEvents | |
137 | * The list of disabled events. Should not be null or empty | |
138 | * @return If the command executed successfully (return code = 0). | |
139 | */ | |
140 | public static boolean disableEvents(String sessionName, Domain domain, String... disabledEvents) { | |
141 | if (disabledEvents == null || disabledEvents.length == 0) { | |
142 | throw new IllegalArgumentException(); | |
143 | } | |
144 | List<String> command = new ArrayList<String>(); | |
145 | command.add("lttng"); | |
146 | command.add("disable-event"); | |
147 | command.add(domain.flag()); | |
148 | command.add(Arrays.stream(disabledEvents).collect(Collectors.joining(","))); | |
149 | if (sessionName != null) { | |
150 | command.add("-s"); | |
151 | command.add(sessionName); | |
152 | } | |
153 | return executeCommand(command.toArray(new String[0])); | |
154 | } | |
155 | ||
da8308fe AM |
156 | /** |
157 | * Stop the current tracing session | |
158 | * | |
9368bd5e AM |
159 | * @param sessionName |
160 | * The name of the session to stop. Use null for the current | |
161 | * session. | |
da8308fe AM |
162 | * @return If the command executed successfully (return code = 0). |
163 | */ | |
9368bd5e AM |
164 | public static boolean stopSession(String sessionName) { |
165 | List<String> command = new ArrayList<String>(); | |
166 | command.add("lttng"); | |
167 | command.add("stop"); | |
168 | if (sessionName != null) { | |
169 | command.add(sessionName); | |
170 | } | |
171 | return executeCommand(command.toArray(new String[0])); | |
da8308fe AM |
172 | } |
173 | ||
9368bd5e AM |
174 | /** |
175 | * Issue a "lttng view" command on the provided session, and returns its | |
176 | * output. This effectively returns the current content of the trace in text | |
177 | * form. | |
178 | * | |
179 | * @param sessionName | |
180 | * The name of the session to print. Use null for the current | |
181 | * session. | |
182 | * @return The output of Babeltrace on the session's current trace | |
183 | */ | |
184 | public static List<String> viewSession(String sessionName) { | |
185 | List<String> command = new ArrayList<String>(); | |
186 | command.add("lttng"); | |
187 | command.add("view"); | |
188 | if (sessionName != null) { | |
189 | command.add(sessionName); | |
190 | } | |
191 | return getOutputFromCommand(command.toArray(new String[0])); | |
da8308fe AM |
192 | } |
193 | ||
194 | /** | |
195 | * Destroy the current tracing session | |
196 | * | |
9368bd5e AM |
197 | * @param sessionName |
198 | * The name of the session to destroy. Use null for the current | |
199 | * session. | |
da8308fe AM |
200 | * @return If the command executed successfully (return code = 0). |
201 | */ | |
9368bd5e AM |
202 | public static boolean destroySession(String sessionName) { |
203 | List<String> command = new ArrayList<String>(); | |
204 | command.add("lttng"); | |
205 | command.add("destroy"); | |
206 | if (sessionName != null) { | |
207 | command.add(sessionName); | |
208 | } | |
209 | return executeCommand(command.toArray(new String[0])); | |
210 | } | |
211 | ||
212 | /** | |
213 | * Try destroying the given tracing session, fail silently if there is no | |
214 | * session. | |
215 | * | |
216 | * @param sessionName | |
217 | * The name of the session to destroy. Use null for the current | |
218 | * session. | |
219 | */ | |
220 | public static void tryDestroySession(String sessionName) { | |
221 | getOutputFromCommand(false, new String[] { "lttng", "destroy" }); | |
222 | } | |
223 | ||
224 | /** | |
225 | * Outside of the scope of lttng-tools, but this utility method can be used | |
226 | * to delete all traces currently under ~/lttng-traces/. This can be used by | |
227 | * tests to cleanup a trace they have created. | |
228 | * | |
229 | * @return True if the command completes successfully, false if there was an | |
230 | * error. | |
231 | */ | |
232 | public static boolean deleteAllTracee() { | |
233 | String tracesDir = new String(System.getProperty("user.home") + "/lttng-traces/"); | |
234 | return deleteDirectory(Paths.get(tracesDir)); | |
235 | } | |
236 | ||
237 | private static boolean deleteDirectory(Path directory) { | |
238 | try { | |
239 | Files.walkFileTree(directory, new SimpleFileVisitor<Path>() { | |
240 | @Override | |
241 | public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { | |
242 | Files.delete(file); | |
243 | return FileVisitResult.CONTINUE; | |
244 | } | |
245 | ||
246 | @Override | |
247 | public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { | |
248 | Files.delete(dir); | |
249 | return FileVisitResult.CONTINUE; | |
250 | } | |
251 | }); | |
252 | } catch (IOException e) { | |
253 | /* At least we tried... */ | |
254 | return false; | |
255 | } | |
256 | return true; | |
da8308fe AM |
257 | } |
258 | ||
259 | // ------------------------------------------------------------------------ | |
260 | // Private helper methods | |
261 | // ------------------------------------------------------------------------ | |
262 | ||
263 | private static boolean executeCommands(String [][] commands) { | |
264 | for (String[] command : commands) { | |
265 | if (executeCommand(command) == false) { | |
266 | return false; | |
267 | } | |
268 | } | |
269 | return true; | |
270 | } | |
271 | ||
272 | /** | |
273 | * Just to test the environment / stdout are working correctly | |
274 | */ | |
275 | public static void main(String[] args) { | |
276 | executeCommand(new String[] {"ls", "-l"}); | |
277 | } | |
278 | ||
279 | private static boolean executeCommand(String[] command) { | |
280 | try { | |
281 | ProcessBuilder builder = new ProcessBuilder(command); | |
282 | builder.redirectErrorStream(true); | |
283 | builder.redirectOutput(Redirect.INHERIT); | |
284 | ||
285 | Process p = builder.start(); | |
286 | int ret = p.waitFor(); | |
287 | return (ret == 0); | |
288 | ||
289 | } catch (IOException | InterruptedException e) { | |
290 | return false; | |
291 | } | |
292 | } | |
293 | ||
294 | private static List<String> getOutputFromCommand(String[] command) { | |
9368bd5e AM |
295 | return getOutputFromCommand(true, command); |
296 | } | |
297 | ||
298 | private static List<String> getOutputFromCommand(boolean print, String[] command) { | |
da8308fe AM |
299 | try { |
300 | Path tempFile = Files.createTempFile("test-output", null); | |
301 | ||
302 | ProcessBuilder builder = new ProcessBuilder(command); | |
303 | builder.redirectErrorStream(true); | |
304 | builder.redirectOutput(Redirect.to(tempFile.toFile())); | |
305 | ||
306 | Process p = builder.start(); | |
307 | p.waitFor(); | |
308 | ||
309 | List<String> lines = Files.readAllLines(tempFile); | |
310 | Files.delete(tempFile); | |
311 | ||
9368bd5e AM |
312 | if (print) { |
313 | /* Also print the output to the console */ | |
314 | lines.stream().forEach(s -> System.out.println(s)); | |
315 | } | |
da8308fe AM |
316 | |
317 | return lines; | |
318 | ||
319 | } catch (IOException | InterruptedException e) { | |
320 | return null; | |
321 | } | |
322 | } | |
323 | } |