Commit | Line | Data |
---|---|---|
2b408e85 AM |
1 | /* |
2 | * Copyright (C) 2015, EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com> | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation; either version 2 of the License, or | |
7 | * (at your option) any later version. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License along | |
15 | * with this program; if not, write to the Free Software Foundation, Inc., | |
16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
17 | */ | |
18 | ||
8576633f AM |
19 | package org.lttng.ust.agent.utils; |
20 | ||
21 | import java.io.IOException; | |
22 | import java.lang.ProcessBuilder.Redirect; | |
23 | import java.nio.file.FileVisitResult; | |
24 | import java.nio.file.Files; | |
25 | import java.nio.file.Path; | |
26 | import java.nio.file.Paths; | |
27 | import java.nio.file.SimpleFileVisitor; | |
28 | import java.nio.file.attribute.BasicFileAttributes; | |
29 | import java.util.Arrays; | |
30 | import java.util.List; | |
dd1cba3f | 31 | import java.util.StringJoiner; |
8576633f AM |
32 | import java.util.UUID; |
33 | import java.util.stream.Collectors; | |
34 | ||
8a0613fa AM |
35 | /** |
36 | * Java representation of a LTTng tracing session. It uses the command-line | |
37 | * "lttng" tool to manipulate the session. Creating an instance will run | |
38 | * "lttng create", close()'ing it will run "lttng destroy". | |
39 | * | |
40 | * @author Alexandre Montplaisir | |
41 | */ | |
8576633f AM |
42 | public class LttngSession implements AutoCloseable { |
43 | ||
8a0613fa AM |
44 | /** |
45 | * Tracing domains as they are defined by lttng-tools | |
46 | */ | |
8576633f | 47 | public enum Domain { |
8a0613fa AM |
48 | /** The JUL (java.util.logging) domain */ |
49 | JUL("-j"), /** The log4j (org.apache.log4j) domain */ | |
8576633f AM |
50 | LOG4J("-l"); |
51 | ||
52 | private final String flag; | |
53 | ||
54 | private Domain(String flag) { | |
55 | this.flag = flag; | |
56 | } | |
57 | ||
8a0613fa AM |
58 | /** |
59 | * @return The corresponding command-line flag to pass to options like | |
60 | * "lttng enable-event" | |
61 | */ | |
8576633f AM |
62 | public String flag() { |
63 | return flag; | |
64 | } | |
65 | } | |
66 | ||
67 | private final String sessionName; | |
68 | private final Domain domain; | |
69 | ||
70 | private volatile boolean channelCreated = false; | |
71 | ||
8a0613fa AM |
72 | /** |
73 | * Constructor to create a new LTTng tracing session. | |
74 | * | |
75 | * @param sessionName | |
76 | * The name of the session to use. It can be null, in which case | |
77 | * we will provide a unique random name. | |
78 | * @param domain | |
79 | * The tracing domain of this session | |
80 | */ | |
8576633f AM |
81 | public LttngSession(String sessionName, Domain domain) { |
82 | if (sessionName != null) { | |
83 | this.sessionName = sessionName; | |
84 | } else { | |
85 | this.sessionName = UUID.randomUUID().toString(); | |
86 | } | |
87 | this.domain = domain; | |
88 | ||
89 | /* Create the session in LTTng */ | |
90 | executeCommand(Arrays.asList("lttng", "create", this.sessionName)); | |
91 | } | |
92 | ||
93 | @Override | |
94 | public void close() { | |
95 | /* Destroy the session */ | |
96 | executeCommand(Arrays.asList("lttng", "destroy", sessionName)); | |
97 | // FIXME also delete the trace we generated ? | |
98 | } | |
99 | ||
100 | // ------------------------------------------------------------------------ | |
101 | // Public methods | |
102 | // ------------------------------------------------------------------------ | |
103 | ||
104 | /** | |
105 | * Enable all events in the given session (enable-event -a) | |
106 | * | |
107 | * @return If the command executed successfully (return code = 0). | |
108 | */ | |
109 | public boolean enableAllEvents() { | |
110 | channelCreated = true; | |
111 | return executeCommand(Arrays.asList( | |
112 | "lttng", "enable-event", domain.flag(), "-a", "-s", sessionName)); | |
113 | } | |
114 | ||
115 | /** | |
116 | * Enable individual event(s). | |
117 | * | |
118 | * @param enabledEvents | |
119 | * The list of events to enable. Should not be null or empty | |
120 | * @return If the command executed successfully (return code = 0). | |
121 | */ | |
122 | public boolean enableEvents(String... enabledEvents) { | |
123 | if (enabledEvents == null || enabledEvents.length == 0) { | |
124 | throw new IllegalArgumentException(); | |
125 | } | |
126 | channelCreated = true; | |
127 | return executeCommand(Arrays.asList( | |
128 | "lttng", "enable-event", domain.flag(), | |
129 | Arrays.stream(enabledEvents).collect(Collectors.joining(",")), | |
130 | "-s", sessionName)); | |
131 | } | |
132 | ||
133 | /** | |
134 | * Send a disable-event command. Used to disable events that were previously | |
135 | * enabled. | |
136 | * | |
137 | * @param disabledEvents | |
138 | * The list of disabled events. Should not be null or empty | |
139 | * @return If the command executed successfully (return code = 0). | |
140 | */ | |
141 | public boolean disableEvents(String... disabledEvents) { | |
142 | if (disabledEvents == null || disabledEvents.length == 0) { | |
143 | throw new IllegalArgumentException(); | |
144 | } | |
145 | return executeCommand(Arrays.asList( | |
146 | "lttng", "disable-event", domain.flag(), | |
147 | Arrays.stream(disabledEvents).collect(Collectors.joining(",")), | |
148 | "-s", sessionName)); | |
149 | } | |
150 | ||
8a0613fa AM |
151 | /** |
152 | * Start tracing | |
153 | * | |
154 | * @return If the command executed successfully (return code = 0). | |
155 | */ | |
8576633f AM |
156 | public boolean start() { |
157 | /* | |
158 | * We have to enable a channel for 'lttng start' to work. However, we | |
159 | * cannot enable a channel directly, see | |
160 | * https://bugs.lttng.org/issues/894 . Instead we will enable an event | |
161 | * we know does not exist | |
162 | */ | |
163 | if (!channelCreated) { | |
164 | enableEvents("non-event"); | |
165 | } | |
166 | return executeCommand(Arrays.asList("lttng", "start", sessionName)); | |
167 | } | |
168 | ||
169 | /** | |
170 | * Stop the tracing session | |
171 | * | |
172 | * @return If the command executed successfully (return code = 0). | |
173 | */ | |
174 | public boolean stop() { | |
175 | return executeCommand(Arrays.asList("lttng", "stop", sessionName)); | |
176 | } | |
177 | ||
178 | /** | |
179 | * Issue a "lttng view" command on the session, and returns its output. This | |
180 | * effectively returns the current content of the trace in text form. | |
181 | * | |
182 | * @return The output of Babeltrace on the session's current trace | |
183 | */ | |
184 | public List<String> view() { | |
24b260d9 | 185 | return MiscTestUtils.getOutputFromCommand(Arrays.asList("lttng", "view", sessionName)); |
8576633f AM |
186 | } |
187 | ||
e41ec02a AM |
188 | /** |
189 | * Utility method to destroy all existing sessions. Useful when first | |
190 | * setting up a test to make sure no existing session interferes. | |
191 | */ | |
192 | public static void destroyAllSessions() { | |
193 | executeCommand(Arrays.asList("lttng", "destroy", "-a")); | |
194 | } | |
195 | ||
8576633f AM |
196 | /** |
197 | * Outside of the scope of lttng-tools, but this utility method can be used | |
198 | * to delete all traces currently under ~/lttng-traces/. This can be used by | |
199 | * tests to cleanup a trace they have created. | |
200 | * | |
201 | * @return True if the command completes successfully, false if there was an | |
202 | * error. | |
203 | */ | |
204 | public static boolean deleteAllTracee() { | |
205 | String tracesDir = new String(System.getProperty("user.home") + "/lttng-traces/"); | |
206 | return deleteDirectory(Paths.get(tracesDir)); | |
207 | } | |
208 | ||
209 | // ------------------------------------------------------------------------ | |
210 | // Private helper methods | |
211 | // ------------------------------------------------------------------------ | |
212 | ||
213 | private static boolean deleteDirectory(Path directory) { | |
214 | try { | |
215 | Files.walkFileTree(directory, new SimpleFileVisitor<Path>() { | |
216 | @Override | |
217 | public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { | |
218 | Files.delete(file); | |
219 | return FileVisitResult.CONTINUE; | |
220 | } | |
221 | ||
222 | @Override | |
223 | public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { | |
224 | Files.delete(dir); | |
225 | return FileVisitResult.CONTINUE; | |
226 | } | |
227 | }); | |
228 | } catch (IOException e) { | |
229 | /* At least we tried... */ | |
230 | return false; | |
231 | } | |
232 | return true; | |
233 | } | |
234 | ||
235 | /** | |
8a0613fa AM |
236 | * Simple command to test that the environment / stdout are working |
237 | * correctly. | |
238 | * | |
239 | * @param args | |
240 | * Command-line arguments | |
8576633f AM |
241 | */ |
242 | public static void main(String[] args) { | |
243 | List<String> command = Arrays.asList("ls", "-l"); | |
244 | executeCommand(command); | |
245 | } | |
246 | ||
247 | private static boolean executeCommand(List<String> command) { | |
248 | try { | |
dd1cba3f AM |
249 | /* "echo" the command to stdout */ |
250 | StringJoiner sj = new StringJoiner(" ", "$ ", ""); | |
251 | command.stream().forEach(sj::add); | |
252 | System.out.println(sj.toString()); | |
253 | ||
8576633f AM |
254 | ProcessBuilder builder = new ProcessBuilder(command); |
255 | builder.redirectErrorStream(true); | |
256 | builder.redirectOutput(Redirect.INHERIT); | |
257 | ||
258 | Process p = builder.start(); | |
259 | int ret = p.waitFor(); | |
dd1cba3f AM |
260 | |
261 | System.out.println("(returned from command)"); | |
262 | ||
8576633f AM |
263 | return (ret == 0); |
264 | ||
265 | } catch (IOException | InterruptedException e) { | |
266 | return false; | |
267 | } | |
268 | } | |
269 | } |