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
.ILttngTcpClientListener
;
32 import org
.lttng
.ust
.agent
.client
.LttngTcpSessiondClient
;
35 * Base implementation of a {@link ILttngAgent}.
37 * @author Alexandre Montplaisir
39 * The type of logging handler that should register to this agent
41 public abstract class AbstractLttngAgent
<T
extends ILttngHandler
>
42 implements ILttngAgent
<T
>, ILttngTcpClientListener
{
44 private static final String WILDCARD
= "*";
45 private static final int INIT_TIMEOUT
= 3; /* Seconds */
47 /** The handlers registered to this agent */
48 private final Set
<T
> registeredHandlers
= new HashSet
<T
>();
51 * The trace events currently enabled in the sessions.
53 * The key represents the event name, the value is the ref count (how many
54 * different sessions currently have this event enabled). Once the ref count
55 * falls to 0, this means we can avoid sending log events through JNI
56 * because nobody wants them.
58 * It uses a concurrent hash set", so that the {@link #isEventEnabled} and
59 * read methods do not need to take a synchronization lock.
61 private final Map
<String
, Integer
> enabledEvents
= new ConcurrentHashMap
<String
, Integer
>();
64 * The trace events prefixes currently enabled in the sessions, which means
65 * the event names finishing in *, like "abcd*". We track them separately
66 * from the standard event names, so that we can use {@link String#equals}
67 * and {@link String#startsWith} appropriately.
69 * We track the lone wildcard "*" separately, in {@link #enabledWildcards}.
71 private final NavigableMap
<String
, Integer
> enabledEventPrefixes
=
72 new ConcurrentSkipListMap
<String
, Integer
>();
74 /** Number of sessions currently enabling the wildcard "*" event */
75 private final AtomicInteger enabledWildcards
= new AtomicInteger(0);
77 /** Tracing domain. Defined by the sub-classes via the constructor. */
78 private final Domain domain
;
80 /* Lazy-loaded sessiond clients and their thread objects */
81 private LttngTcpSessiondClient rootSessiondClient
= null;
82 private LttngTcpSessiondClient userSessiondClient
= null;
83 private Thread rootSessiondClientThread
= null;
84 private Thread userSessiondClientThread
= null;
86 /** Indicates if this agent has been initialized. */
87 private boolean initialized
= false;
90 * Constructor. Should only be called by sub-classes via super(...);
93 * The tracing domain of this agent.
95 protected AbstractLttngAgent(Domain domain
) {
100 public Domain
getDomain() {
105 public void registerHandler(T handler
) {
106 synchronized (registeredHandlers
) {
107 if (registeredHandlers
.isEmpty()) {
109 * This is the first handler that registers, we will initialize
114 registeredHandlers
.add(handler
);
119 public void unregisterHandler(T handler
) {
120 synchronized (registeredHandlers
) {
121 registeredHandlers
.remove(handler
);
122 if (registeredHandlers
.isEmpty()) {
123 /* There are no more registered handlers, close the connection. */
129 private void init() {
131 * Only called from a synchronized (registeredHandlers) block, should
132 * not need additional synchronization.
137 String rootClientThreadName
= "Root sessiond client started by agent: " + this.getClass().getSimpleName();
139 rootSessiondClient
= new LttngTcpSessiondClient(this, getDomain().value(), true);
140 rootSessiondClientThread
= new Thread(rootSessiondClient
, rootClientThreadName
);
141 rootSessiondClientThread
.setDaemon(true);
142 rootSessiondClientThread
.start();
144 String userClientThreadName
= "User sessiond client started by agent: " + this.getClass().getSimpleName();
146 userSessiondClient
= new LttngTcpSessiondClient(this, getDomain().value(), false);
147 userSessiondClientThread
= new Thread(userSessiondClient
, userClientThreadName
);
148 userSessiondClientThread
.setDaemon(true);
149 userSessiondClientThread
.start();
151 /* Give the threads' registration a chance to end. */
152 if (!rootSessiondClient
.waitForConnection(INIT_TIMEOUT
)) {
153 userSessiondClient
.waitForConnection(INIT_TIMEOUT
);
162 private void dispose() {
164 * Only called from a synchronized (registeredHandlers) block, should
165 * not need additional synchronization.
167 rootSessiondClient
.close();
168 userSessiondClient
.close();
171 rootSessiondClientThread
.join();
172 userSessiondClientThread
.join();
174 } catch (InterruptedException e
) {
177 rootSessiondClient
= null;
178 rootSessiondClientThread
= null;
179 userSessiondClient
= null;
180 userSessiondClientThread
= null;
182 /* Reset all enabled event counts to 0 */
183 enabledEvents
.clear();
184 enabledEventPrefixes
.clear();
185 enabledWildcards
.set(0);
192 public boolean eventEnabled(String eventName
) {
193 if (eventName
.equals(WILDCARD
)) {
194 enabledWildcards
.incrementAndGet();
198 if (eventName
.endsWith(WILDCARD
)) {
199 /* Strip the "*" from the name. */
200 String prefix
= eventName
.substring(0, eventName
.length() - 1);
201 return incrementEventCount(prefix
, enabledEventPrefixes
);
204 return incrementEventCount(eventName
, enabledEvents
);
208 public boolean eventDisabled(String eventName
) {
209 if (eventName
.equals(WILDCARD
)) {
210 int newCount
= enabledWildcards
.decrementAndGet();
212 /* Event was not enabled, bring the count back to 0 */
213 enabledWildcards
.incrementAndGet();
219 if (eventName
.endsWith(WILDCARD
)) {
220 /* Strip the "*" from the name. */
221 String prefix
= eventName
.substring(0, eventName
.length() - 1);
222 return decrementEventCount(prefix
, enabledEventPrefixes
);
225 return decrementEventCount(eventName
, enabledEvents
);
229 public Iterable
<String
> listEnabledEvents() {
230 List
<String
> events
= new LinkedList
<String
>();
232 if (enabledWildcards
.get() > 0) {
233 events
.add(WILDCARD
);
235 for (String prefix
: enabledEventPrefixes
.keySet()) {
236 events
.add(new String(prefix
+ WILDCARD
));
238 events
.addAll(enabledEvents
.keySet());
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
)) {
263 private static boolean incrementEventCount(String eventName
, Map
<String
, Integer
> eventMap
) {
264 synchronized (eventMap
) {
265 Integer count
= eventMap
.get(eventName
);
267 /* This is the first instance of this event being enabled */
268 eventMap
.put(eventName
, Integer
.valueOf(1));
271 if (count
.intValue() <= 0) {
272 /* It should not have been in the map in the first place! */
273 throw new IllegalStateException();
275 /* The event was already enabled, increment its refcount */
276 eventMap
.put(eventName
, Integer
.valueOf(count
.intValue() + 1));
281 private static boolean decrementEventCount(String eventName
, Map
<String
, Integer
> eventMap
) {
282 synchronized (eventMap
) {
283 Integer count
= eventMap
.get(eventName
);
284 if (count
== null || count
.intValue() <= 0) {
286 * The sessiond asked us to disable an event that was not
287 * enabled previously. Command error?
291 if (count
.intValue() == 1) {
293 * This is the last instance of this event being disabled,
294 * remove it from the map so that we stop sending it.
296 eventMap
.remove(eventName
);
300 * Other sessions are still looking for this event, simply decrement
303 eventMap
.put(eventName
, Integer
.valueOf(count
.intValue() - 1));