2 * Copyright (C) 2015 - EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
3 * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
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.
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
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
19 package org
.lttng
.ust
.agent
;
21 import java
.util
.HashSet
;
22 import java
.util
.LinkedList
;
23 import java
.util
.List
;
25 import java
.util
.NavigableMap
;
27 import java
.util
.concurrent
.ConcurrentHashMap
;
28 import java
.util
.concurrent
.ConcurrentSkipListMap
;
29 import java
.util
.concurrent
.atomic
.AtomicInteger
;
31 import org
.lttng
.ust
.agent
.client
.LttngTcpSessiondClient
;
34 * Base implementation of a {@link ILttngAgent}.
36 * @author Alexandre Montplaisir
38 * The type of logging handler that should register to this agent
40 public abstract class AbstractLttngAgent
<T
extends ILttngHandler
> implements ILttngAgent
<T
> {
42 private static final String WILDCARD
= "*";
43 private static final int INIT_TIMEOUT
= 3; /* Seconds */
45 /** The handlers registered to this agent */
46 private final Set
<T
> registeredHandlers
= new HashSet
<T
>();
49 * The trace events currently enabled in the sessions.
51 * The key represents the event name, the value is the ref count (how many
52 * different sessions currently have this event enabled). Once the ref count
53 * falls to 0, this means we can avoid sending log events through JNI
54 * because nobody wants them.
56 * It uses a concurrent hash set", so that the {@link #isEventEnabled} and
57 * read methods do not need to take a synchronization lock.
59 private final Map
<String
, Integer
> enabledEvents
= new ConcurrentHashMap
<String
, Integer
>();
62 * The trace events prefixes currently enabled in the sessions, which means
63 * the event names finishing in *, like "abcd*". We track them separately
64 * from the standard event names, so that we can use {@link String#equals}
65 * and {@link String#startsWith} appropriately.
67 * We track the lone wildcard "*" separately, in {@link #enabledWildcards}.
69 private final NavigableMap
<String
, Integer
> enabledEventPrefixes
=
70 new ConcurrentSkipListMap
<String
, Integer
>();
72 /** Number of sessions currently enabling the wildcard "*" event */
73 private final AtomicInteger enabledWildcards
= new AtomicInteger(0);
75 /** Tracing domain. Defined by the sub-classes via the constructor. */
76 private final Domain domain
;
78 /* Lazy-loaded sessiond clients and their thread objects */
79 private LttngTcpSessiondClient rootSessiondClient
= null;
80 private LttngTcpSessiondClient userSessiondClient
= null;
81 private Thread rootSessiondClientThread
= null;
82 private Thread userSessiondClientThread
= null;
84 /** Indicates if this agent has been initialized. */
85 private boolean initialized
= false;
88 * Constructor. Should only be called by sub-classes via super(...);
91 * The tracing domain of this agent.
93 protected AbstractLttngAgent(Domain domain
) {
98 public Domain
getDomain() {
103 public void registerHandler(T handler
) {
104 synchronized (registeredHandlers
) {
105 if (registeredHandlers
.isEmpty()) {
107 * This is the first handler that registers, we will initialize
112 registeredHandlers
.add(handler
);
117 public void unregisterHandler(T handler
) {
118 synchronized (registeredHandlers
) {
119 registeredHandlers
.remove(handler
);
120 if (registeredHandlers
.isEmpty()) {
121 /* There are no more registered handlers, close the connection. */
127 private void init() {
129 * Only called from a synchronized (registeredHandlers) block, should
130 * not need additional synchronization.
135 String rootClientThreadName
= "Root sessiond client started by agent: " + this.getClass().getSimpleName();
137 rootSessiondClient
= new LttngTcpSessiondClient(this, true);
138 rootSessiondClientThread
= new Thread(rootSessiondClient
, rootClientThreadName
);
139 rootSessiondClientThread
.setDaemon(true);
140 rootSessiondClientThread
.start();
142 String userClientThreadName
= "User sessiond client started by agent: " + this.getClass().getSimpleName();
144 userSessiondClient
= new LttngTcpSessiondClient(this, false);
145 userSessiondClientThread
= new Thread(userSessiondClient
, userClientThreadName
);
146 userSessiondClientThread
.setDaemon(true);
147 userSessiondClientThread
.start();
149 /* Give the threads' registration a chance to end. */
150 if (!rootSessiondClient
.waitForConnection(INIT_TIMEOUT
)) {
151 userSessiondClient
.waitForConnection(INIT_TIMEOUT
);
160 private void dispose() {
162 * Only called from a synchronized (registeredHandlers) block, should
163 * not need additional synchronization.
165 rootSessiondClient
.close();
166 userSessiondClient
.close();
169 rootSessiondClientThread
.join();
170 userSessiondClientThread
.join();
172 } catch (InterruptedException e
) {
175 rootSessiondClient
= null;
176 rootSessiondClientThread
= null;
177 userSessiondClient
= null;
178 userSessiondClientThread
= null;
180 /* Reset all enabled event counts to 0 */
181 enabledEvents
.clear();
182 enabledEventPrefixes
.clear();
183 enabledWildcards
.set(0);
190 * Callback for the TCP clients to notify the agent that a request for
191 * enabling an event was sent from the session daemon.
194 * The name of the event that was requested to be enabled.
195 * @return Since we do not track individual sessions, right now this command
196 * cannot fail. It will always return true.
198 public boolean eventEnabled(String eventName
) {
199 if (eventName
.equals(WILDCARD
)) {
200 enabledWildcards
.incrementAndGet();
204 if (eventName
.endsWith(WILDCARD
)) {
205 /* Strip the "*" from the name. */
206 String prefix
= eventName
.substring(0, eventName
.length() - 1);
207 return incrementEventCount(prefix
, enabledEventPrefixes
);
210 return incrementEventCount(eventName
, enabledEvents
);
214 * Callback for the TCP clients to notify the agent that a request for
215 * disabling an event was sent from the session daemon.
218 * The name of the event that was requested to be disabled.
219 * @return True if the command completed successfully, false if we should
220 * report an error (event was not enabled, etc.)
222 public boolean eventDisabled(String eventName
) {
223 if (eventName
.equals(WILDCARD
)) {
224 int newCount
= enabledWildcards
.decrementAndGet();
226 /* Event was not enabled, bring the count back to 0 */
227 enabledWildcards
.incrementAndGet();
233 if (eventName
.endsWith(WILDCARD
)) {
234 /* Strip the "*" from the name. */
235 String prefix
= eventName
.substring(0, eventName
.length() - 1);
236 return decrementEventCount(prefix
, enabledEventPrefixes
);
239 return decrementEventCount(eventName
, enabledEvents
);
243 public boolean isEventEnabled(String eventName
) {
244 /* If at least one session enabled the "*" wildcard, send the event */
245 if (enabledWildcards
.get() > 0) {
249 /* Check if at least one session wants this exact event name */
250 if (enabledEvents
.containsKey(eventName
)) {
254 /* Look in the enabled prefixes if one of them matches the event */
255 String potentialMatch
= enabledEventPrefixes
.floorKey(eventName
);
256 if (potentialMatch
!= null && eventName
.startsWith(potentialMatch
)) {
264 public Iterable
<String
> listEnabledEvents() {
265 List
<String
> events
= new LinkedList
<String
>();
267 if (enabledWildcards
.get() > 0) {
268 events
.add(WILDCARD
);
270 for (String prefix
: enabledEventPrefixes
.keySet()) {
271 events
.add(new String(prefix
+ WILDCARD
));
273 events
.addAll(enabledEvents
.keySet());
277 private static boolean incrementEventCount(String eventName
, Map
<String
, Integer
> eventMap
) {
278 synchronized (eventMap
) {
279 Integer count
= eventMap
.get(eventName
);
281 /* This is the first instance of this event being enabled */
282 eventMap
.put(eventName
, Integer
.valueOf(1));
285 if (count
.intValue() <= 0) {
286 /* It should not have been in the map in the first place! */
287 throw new IllegalStateException();
289 /* The event was already enabled, increment its refcount */
290 eventMap
.put(eventName
, Integer
.valueOf(count
.intValue() + 1));
295 private static boolean decrementEventCount(String eventName
, Map
<String
, Integer
> eventMap
) {
296 synchronized (eventMap
) {
297 Integer count
= eventMap
.get(eventName
);
298 if (count
== null || count
.intValue() <= 0) {
300 * The sessiond asked us to disable an event that was not
301 * enabled previously. Command error?
305 if (count
.intValue() == 1) {
307 * This is the last instance of this event being disabled,
308 * remove it from the map so that we stop sending it.
310 eventMap
.remove(eventName
);
314 * Other sessions are still looking for this event, simply decrement
317 eventMap
.put(eventName
, Integer
.valueOf(count
.intValue() - 1));