2 * SPDX-License-Identifier: LGPL-2.1-only
4 * Copyright (C) 2015 EfficiOS Inc.
5 * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
6 * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
9 package org
.lttng
.ust
.agent
;
11 import java
.util
.Collection
;
12 import java
.util
.HashMap
;
13 import java
.util
.HashSet
;
16 import java
.util
.concurrent
.ConcurrentHashMap
;
17 import java
.util
.concurrent
.locks
.Lock
;
18 import java
.util
.concurrent
.locks
.ReentrantLock
;
19 import java
.util
.regex
.Matcher
;
21 import org
.lttng
.ust
.agent
.client
.ILttngTcpClientListener
;
22 import org
.lttng
.ust
.agent
.client
.LttngTcpSessiondClient
;
23 import org
.lttng
.ust
.agent
.filter
.FilterChangeNotifier
;
24 import org
.lttng
.ust
.agent
.session
.EventRule
;
25 import org
.lttng
.ust
.agent
.utils
.LttngUstAgentLogger
;
28 * Base implementation of a {@link ILttngAgent}.
30 * @author Alexandre Montplaisir
32 * The type of logging handler that should register to this agent
34 public abstract class AbstractLttngAgent
<T
extends ILttngHandler
>
35 implements ILttngAgent
<T
>, ILttngTcpClientListener
{
37 private static final int INIT_TIMEOUT
= 3; /* Seconds */
39 /** The handlers registered to this agent */
40 private final Set
<T
> registeredHandlers
= new HashSet
<T
>();
43 * The trace events currently enabled in the sessions.
45 * The key is the {@link EventNamePattern} that comes from the event name.
46 * The value is the ref count (how many different sessions currently have
47 * this event enabled). Once the ref count falls to 0, this means we can
48 * avoid sending log events through JNI because nobody wants them.
50 * Its accesses should be protected by the {@link #enabledEventNamesLock}
53 private final Map
<EventNamePattern
, Integer
> enabledPatterns
= new HashMap
<EventNamePattern
, Integer
>();
56 * Cache of already-checked event names. As long as enabled/disabled events
57 * don't change in the session, we can avoid re-checking events that were
58 * previously checked against all known enabled patterns.
60 * Its accesses should be protected by the {@link #enabledEventNamesLock}
61 * below, with the exception of concurrent get operations.
63 private final Map
<String
, Boolean
> enabledEventNamesCache
= new ConcurrentHashMap
<String
, Boolean
>();
66 * Lock protecting accesses to the {@link #enabledPatterns} and
67 * {@link #enabledEventNamesCache} maps.
69 private final Lock enabledEventNamesLock
= new ReentrantLock();
72 * The application contexts currently enabled in the tracing sessions.
74 * It is first indexed by context retriever, then by context name. This
75 * allows to efficiently query all the contexts for a given retriever.
77 * Works similarly as {@link #enabledEvents}, but for app contexts (and with
78 * an extra degree of indexing).
80 * TODO Could be changed to a Guava Table once/if we start using it.
82 private final Map
<String
, Map
<String
, Integer
>> enabledAppContexts
= new ConcurrentHashMap
<String
, Map
<String
, Integer
>>();
84 /** Tracing domain. Defined by the sub-classes via the constructor. */
85 private final Domain domain
;
87 /* Lazy-loaded sessiond clients and their thread objects */
88 private LttngTcpSessiondClient rootSessiondClient
= null;
89 private LttngTcpSessiondClient userSessiondClient
= null;
90 private Thread rootSessiondClientThread
= null;
91 private Thread userSessiondClientThread
= null;
93 /** Indicates if this agent has been initialized. */
94 private boolean initialized
= false;
97 * Constructor. Should only be called by sub-classes via super(...);
100 * The tracing domain of this agent.
102 protected AbstractLttngAgent(Domain domain
) {
103 this.domain
= domain
;
107 public Domain
getDomain() {
112 public void registerHandler(T handler
) {
113 synchronized (registeredHandlers
) {
114 if (registeredHandlers
.isEmpty()) {
116 * This is the first handler that registers, we will initialize
121 registeredHandlers
.add(handler
);
126 public void unregisterHandler(T handler
) {
127 synchronized (registeredHandlers
) {
128 registeredHandlers
.remove(handler
);
129 if (registeredHandlers
.isEmpty()) {
130 /* There are no more registered handlers, close the connection. */
136 private void init() {
138 * Only called from a synchronized (registeredHandlers) block, should
139 * not need additional synchronization.
145 LttngUstAgentLogger
.log(AbstractLttngAgent
.class, "Initializing Agent for domain: " + domain
.name());
147 String rootClientThreadName
= "Root sessiond client started by agent: " + this.getClass().getSimpleName();
149 rootSessiondClient
= new LttngTcpSessiondClient(this, getDomain().value(), true);
150 rootSessiondClientThread
= new Thread(rootSessiondClient
, rootClientThreadName
);
151 rootSessiondClientThread
.setDaemon(true);
152 rootSessiondClientThread
.start();
154 String userClientThreadName
= "User sessiond client started by agent: " + this.getClass().getSimpleName();
156 userSessiondClient
= new LttngTcpSessiondClient(this, getDomain().value(), false);
157 userSessiondClientThread
= new Thread(userSessiondClient
, userClientThreadName
);
158 userSessiondClientThread
.setDaemon(true);
159 userSessiondClientThread
.start();
161 /* Give the threads' registration a chance to end. */
162 if (!rootSessiondClient
.waitForConnection(INIT_TIMEOUT
)) {
163 userSessiondClient
.waitForConnection(INIT_TIMEOUT
);
172 private void dispose() {
173 LttngUstAgentLogger
.log(AbstractLttngAgent
.class, "Disposing Agent for domain: " + domain
.name());
176 * Only called from a synchronized (registeredHandlers) block, should
177 * not need additional synchronization.
179 rootSessiondClient
.close();
180 userSessiondClient
.close();
183 rootSessiondClientThread
.join();
184 userSessiondClientThread
.join();
186 } catch (InterruptedException e
) {
189 rootSessiondClient
= null;
190 rootSessiondClientThread
= null;
191 userSessiondClient
= null;
192 userSessiondClientThread
= null;
195 * Send filter change notifications for all event rules currently
196 * active, then clear them.
198 FilterChangeNotifier fcn
= FilterChangeNotifier
.getInstance();
200 enabledEventNamesLock
.lock();
202 for (Map
.Entry
<EventNamePattern
, Integer
> entry
: enabledPatterns
.entrySet()) {
203 String eventName
= entry
.getKey().getEventName();
204 Integer nb
= entry
.getValue();
205 for (int i
= 0; i
< nb
.intValue(); i
++) {
206 fcn
.removeEventRules(eventName
);
209 enabledPatterns
.clear();
210 enabledEventNamesCache
.clear();
212 enabledEventNamesLock
.unlock();
216 * Also clear tracked app contexts (no filter notifications sent for
219 enabledAppContexts
.clear();
225 public boolean eventEnabled(EventRule eventRule
) {
226 /* Notify the filter change manager of the command */
227 FilterChangeNotifier
.getInstance().addEventRule(eventRule
);
229 String eventName
= eventRule
.getEventName();
230 EventNamePattern pattern
= new EventNamePattern(eventName
);
232 enabledEventNamesLock
.lock();
234 boolean ret
= incrementRefCount(pattern
, enabledPatterns
);
235 enabledEventNamesCache
.clear();
238 enabledEventNamesLock
.unlock();
243 public boolean eventDisabled(String eventName
) {
244 /* Notify the filter change manager of the command */
245 FilterChangeNotifier
.getInstance().removeEventRules(eventName
);
247 EventNamePattern pattern
= new EventNamePattern(eventName
);
249 enabledEventNamesLock
.lock();
251 boolean ret
= decrementRefCount(pattern
, enabledPatterns
);
252 enabledEventNamesCache
.clear();
255 enabledEventNamesLock
.unlock();
260 public boolean appContextEnabled(String contextRetrieverName
, String contextName
) {
261 synchronized (enabledAppContexts
) {
262 Map
<String
, Integer
> retrieverMap
= enabledAppContexts
.get(contextRetrieverName
);
263 if (retrieverMap
== null) {
264 /* There is no submap for this retriever, let's create one. */
265 retrieverMap
= new ConcurrentHashMap
<String
, Integer
>();
266 enabledAppContexts
.put(contextRetrieverName
, retrieverMap
);
269 return incrementRefCount(contextName
, retrieverMap
);
274 public boolean appContextDisabled(String contextRetrieverName
, String contextName
) {
275 synchronized (enabledAppContexts
) {
276 Map
<String
, Integer
> retrieverMap
= enabledAppContexts
.get(contextRetrieverName
);
277 if (retrieverMap
== null) {
278 /* There was no submap for this retriever, invalid command? */
282 boolean ret
= decrementRefCount(contextName
, retrieverMap
);
284 /* If the submap is now empty we can remove it from the main map. */
285 if (retrieverMap
.isEmpty()) {
286 enabledAppContexts
.remove(contextRetrieverName
);
294 * Implementation of this method is domain-specific.
297 public abstract Collection
<String
> listAvailableEvents();
300 public boolean isEventEnabled(String eventName
) {
301 Boolean cachedEnabled
= enabledEventNamesCache
.get(eventName
);
302 if (cachedEnabled
!= null) {
303 /* We have seen this event previously */
305 * Careful! enabled == null could also mean that the null value is
306 * associated with the key. But we should have never inserted null
309 return cachedEnabled
.booleanValue();
313 * We have not previously checked this event. Run it against all known
314 * enabled event patterns to determine if it should pass or not.
316 enabledEventNamesLock
.lock();
318 boolean enabled
= false;
319 for (EventNamePattern enabledPattern
: enabledPatterns
.keySet()) {
320 Matcher matcher
= enabledPattern
.getPattern().matcher(eventName
);
321 if (matcher
.matches()) {
327 /* Add the result to the cache */
328 enabledEventNamesCache
.put(eventName
, Boolean
.valueOf(enabled
));
332 enabledEventNamesLock
.unlock();
337 public Collection
<Map
.Entry
<String
, Map
<String
, Integer
>>> getEnabledAppContexts() {
338 return enabledAppContexts
.entrySet();
341 private static <T
> boolean incrementRefCount(T key
, Map
<T
, Integer
> refCountMap
) {
342 synchronized (refCountMap
) {
343 Integer count
= refCountMap
.get(key
);
345 /* This is the first instance of this event being enabled */
346 refCountMap
.put(key
, Integer
.valueOf(1));
349 if (count
.intValue() <= 0) {
350 /* It should not have been in the map in the first place! */
351 throw new IllegalStateException();
353 /* The event was already enabled, increment its refcount */
354 refCountMap
.put(key
, Integer
.valueOf(count
.intValue() + 1));
359 private static <T
> boolean decrementRefCount(T key
, Map
<T
, Integer
> refCountMap
) {
360 synchronized (refCountMap
) {
361 Integer count
= refCountMap
.get(key
);
362 if (count
== null || count
.intValue() <= 0) {
364 * The sessiond asked us to disable an event that was not
365 * enabled previously. Command error?
369 if (count
.intValue() == 1) {
371 * This is the last instance of this event being disabled,
372 * remove it from the map so that we stop sending it.
374 refCountMap
.remove(key
);
378 * Other sessions are still looking for this event, simply decrement
381 refCountMap
.put(key
, Integer
.valueOf(count
.intValue() - 1));