Manage complete "event rules" in the Java agent
[lttng-ust.git] / liblttng-ust-java-agent / java / lttng-ust-agent-common / org / lttng / ust / agent / AbstractLttngAgent.java
CommitLineData
d60dfbe4
AM
1/*
2 * Copyright (C) 2015 - EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
3 * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
4 *
5 * This library is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU Lesser General Public License, version 2.1 only,
7 * as published by the Free Software Foundation.
8 *
9 * This library is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this library; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19package org.lttng.ust.agent;
20
21import java.util.HashSet;
22import java.util.LinkedList;
23import java.util.List;
24import java.util.Map;
25import java.util.NavigableMap;
26import java.util.Set;
27import java.util.concurrent.ConcurrentHashMap;
28import java.util.concurrent.ConcurrentSkipListMap;
29import java.util.concurrent.atomic.AtomicInteger;
30
3165c2f5 31import org.lttng.ust.agent.client.ILttngTcpClientListener;
d60dfbe4 32import org.lttng.ust.agent.client.LttngTcpSessiondClient;
3daf5cba 33import org.lttng.ust.agent.session.EventRule;
d60dfbe4
AM
34
35/**
36 * Base implementation of a {@link ILttngAgent}.
37 *
38 * @author Alexandre Montplaisir
39 * @param <T>
40 * The type of logging handler that should register to this agent
41 */
3165c2f5
AM
42public abstract class AbstractLttngAgent<T extends ILttngHandler>
43 implements ILttngAgent<T>, ILttngTcpClientListener {
d60dfbe4
AM
44
45 private static final String WILDCARD = "*";
46 private static final int INIT_TIMEOUT = 3; /* Seconds */
47
48 /** The handlers registered to this agent */
49 private final Set<T> registeredHandlers = new HashSet<T>();
50
51 /**
52 * The trace events currently enabled in the sessions.
53 *
54 * The key represents the event name, the value is the ref count (how many
55 * different sessions currently have this event enabled). Once the ref count
56 * falls to 0, this means we can avoid sending log events through JNI
57 * because nobody wants them.
58 *
3daf5cba 59 * It uses a concurrent hash map, so that the {@link #isEventEnabled} and
d60dfbe4
AM
60 * read methods do not need to take a synchronization lock.
61 */
62 private final Map<String, Integer> enabledEvents = new ConcurrentHashMap<String, Integer>();
63
64 /**
65 * The trace events prefixes currently enabled in the sessions, which means
66 * the event names finishing in *, like "abcd*". We track them separately
67 * from the standard event names, so that we can use {@link String#equals}
68 * and {@link String#startsWith} appropriately.
69 *
70 * We track the lone wildcard "*" separately, in {@link #enabledWildcards}.
71 */
72 private final NavigableMap<String, Integer> enabledEventPrefixes =
73 new ConcurrentSkipListMap<String, Integer>();
74
75 /** Number of sessions currently enabling the wildcard "*" event */
76 private final AtomicInteger enabledWildcards = new AtomicInteger(0);
77
78 /** Tracing domain. Defined by the sub-classes via the constructor. */
79 private final Domain domain;
80
81 /* Lazy-loaded sessiond clients and their thread objects */
82 private LttngTcpSessiondClient rootSessiondClient = null;
83 private LttngTcpSessiondClient userSessiondClient = null;
84 private Thread rootSessiondClientThread = null;
85 private Thread userSessiondClientThread = null;
86
87 /** Indicates if this agent has been initialized. */
88 private boolean initialized = false;
89
90 /**
91 * Constructor. Should only be called by sub-classes via super(...);
92 *
93 * @param domain
94 * The tracing domain of this agent.
95 */
96 protected AbstractLttngAgent(Domain domain) {
97 this.domain = domain;
98 }
99
100 @Override
101 public Domain getDomain() {
102 return domain;
103 }
104
105 @Override
106 public void registerHandler(T handler) {
107 synchronized (registeredHandlers) {
108 if (registeredHandlers.isEmpty()) {
109 /*
110 * This is the first handler that registers, we will initialize
111 * the agent.
112 */
113 init();
114 }
115 registeredHandlers.add(handler);
116 }
117 }
118
119 @Override
120 public void unregisterHandler(T handler) {
121 synchronized (registeredHandlers) {
122 registeredHandlers.remove(handler);
123 if (registeredHandlers.isEmpty()) {
124 /* There are no more registered handlers, close the connection. */
125 dispose();
126 }
127 }
128 }
129
130 private void init() {
131 /*
132 * Only called from a synchronized (registeredHandlers) block, should
133 * not need additional synchronization.
134 */
135 if (initialized) {
136 return;
137 }
138 String rootClientThreadName = "Root sessiond client started by agent: " + this.getClass().getSimpleName();
139
3165c2f5 140 rootSessiondClient = new LttngTcpSessiondClient(this, getDomain().value(), true);
d60dfbe4
AM
141 rootSessiondClientThread = new Thread(rootSessiondClient, rootClientThreadName);
142 rootSessiondClientThread.setDaemon(true);
143 rootSessiondClientThread.start();
144
145 String userClientThreadName = "User sessiond client started by agent: " + this.getClass().getSimpleName();
146
3165c2f5 147 userSessiondClient = new LttngTcpSessiondClient(this, getDomain().value(), false);
d60dfbe4
AM
148 userSessiondClientThread = new Thread(userSessiondClient, userClientThreadName);
149 userSessiondClientThread.setDaemon(true);
150 userSessiondClientThread.start();
151
152 /* Give the threads' registration a chance to end. */
153 if (!rootSessiondClient.waitForConnection(INIT_TIMEOUT)) {
154 userSessiondClient.waitForConnection(INIT_TIMEOUT);
155 }
156
157 initialized = true;
158 }
159
160 /**
161 * Dispose the agent
162 */
163 private void dispose() {
164 /*
165 * Only called from a synchronized (registeredHandlers) block, should
166 * not need additional synchronization.
167 */
168 rootSessiondClient.close();
169 userSessiondClient.close();
170
171 try {
172 rootSessiondClientThread.join();
173 userSessiondClientThread.join();
174
175 } catch (InterruptedException e) {
176 e.printStackTrace();
177 }
178 rootSessiondClient = null;
179 rootSessiondClientThread = null;
180 userSessiondClient = null;
181 userSessiondClientThread = null;
182
183 /* Reset all enabled event counts to 0 */
184 enabledEvents.clear();
185 enabledEventPrefixes.clear();
186 enabledWildcards.set(0);
187
188 initialized = false;
189
190 }
191
3165c2f5 192 @Override
3daf5cba
AM
193 public boolean eventEnabled(EventRule eventRule) {
194 String eventName = eventRule.getEventName();
195
d60dfbe4
AM
196 if (eventName.equals(WILDCARD)) {
197 enabledWildcards.incrementAndGet();
198 return true;
199 }
d60dfbe4
AM
200 if (eventName.endsWith(WILDCARD)) {
201 /* Strip the "*" from the name. */
202 String prefix = eventName.substring(0, eventName.length() - 1);
203 return incrementEventCount(prefix, enabledEventPrefixes);
204 }
205
206 return incrementEventCount(eventName, enabledEvents);
207 }
208
3165c2f5 209 @Override
d60dfbe4
AM
210 public boolean eventDisabled(String eventName) {
211 if (eventName.equals(WILDCARD)) {
212 int newCount = enabledWildcards.decrementAndGet();
213 if (newCount < 0) {
214 /* Event was not enabled, bring the count back to 0 */
215 enabledWildcards.incrementAndGet();
216 return false;
217 }
cbaa5167 218 return true;
d60dfbe4
AM
219 }
220
221 if (eventName.endsWith(WILDCARD)) {
222 /* Strip the "*" from the name. */
223 String prefix = eventName.substring(0, eventName.length() - 1);
224 return decrementEventCount(prefix, enabledEventPrefixes);
225 }
226
227 return decrementEventCount(eventName, enabledEvents);
228 }
229
3165c2f5
AM
230 @Override
231 public Iterable<String> listEnabledEvents() {
232 List<String> events = new LinkedList<String>();
233
234 if (enabledWildcards.get() > 0) {
235 events.add(WILDCARD);
236 }
237 for (String prefix : enabledEventPrefixes.keySet()) {
238 events.add(new String(prefix + WILDCARD));
239 }
240 events.addAll(enabledEvents.keySet());
241 return events;
242 }
243
d60dfbe4
AM
244 @Override
245 public boolean isEventEnabled(String eventName) {
246 /* If at least one session enabled the "*" wildcard, send the event */
247 if (enabledWildcards.get() > 0) {
248 return true;
249 }
250
251 /* Check if at least one session wants this exact event name */
252 if (enabledEvents.containsKey(eventName)) {
253 return true;
254 }
255
256 /* Look in the enabled prefixes if one of them matches the event */
257 String potentialMatch = enabledEventPrefixes.floorKey(eventName);
258 if (potentialMatch != null && eventName.startsWith(potentialMatch)) {
259 return true;
260 }
261
262 return false;
263 }
264
d60dfbe4
AM
265 private static boolean incrementEventCount(String eventName, Map<String, Integer> eventMap) {
266 synchronized (eventMap) {
267 Integer count = eventMap.get(eventName);
268 if (count == null) {
269 /* This is the first instance of this event being enabled */
270 eventMap.put(eventName, Integer.valueOf(1));
271 return true;
272 }
273 if (count.intValue() <= 0) {
274 /* It should not have been in the map in the first place! */
275 throw new IllegalStateException();
276 }
277 /* The event was already enabled, increment its refcount */
278 eventMap.put(eventName, Integer.valueOf(count.intValue() + 1));
279 return true;
280 }
281 }
282
283 private static boolean decrementEventCount(String eventName, Map<String, Integer> eventMap) {
284 synchronized (eventMap) {
285 Integer count = eventMap.get(eventName);
286 if (count == null || count.intValue() <= 0) {
287 /*
288 * The sessiond asked us to disable an event that was not
289 * enabled previously. Command error?
290 */
291 return false;
292 }
293 if (count.intValue() == 1) {
294 /*
295 * This is the last instance of this event being disabled,
296 * remove it from the map so that we stop sending it.
297 */
298 eventMap.remove(eventName);
299 return true;
300 }
301 /*
302 * Other sessions are still looking for this event, simply decrement
303 * its refcount.
304 */
305 eventMap.put(eventName, Integer.valueOf(count.intValue() - 1));
306 return true;
307 }
308 }
309}
310
This page took 0.03618 seconds and 4 git commands to generate.