Fix: Correctly report filter notifications on Java agent teardown
[lttng-ust.git] / liblttng-ust-java-agent / java / lttng-ust-agent-common / org / lttng / ust / agent / AbstractLttngAgent.java
index c3fc339bc628b09234e1ed41962f2197fb221b8f..a58803640f79c15e931fd51ce23e39c31aca394d 100644 (file)
@@ -18,9 +18,8 @@
 
 package org.lttng.ust.agent;
 
+import java.util.Collection;
 import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
 import java.util.Map;
 import java.util.NavigableMap;
 import java.util.Set;
@@ -30,6 +29,9 @@ import java.util.concurrent.atomic.AtomicInteger;
 
 import org.lttng.ust.agent.client.ILttngTcpClientListener;
 import org.lttng.ust.agent.client.LttngTcpSessiondClient;
+import org.lttng.ust.agent.filter.FilterChangeNotifier;
+import org.lttng.ust.agent.session.EventRule;
+import org.lttng.ust.agent.utils.LttngUstAgentLogger;
 
 /**
  * Base implementation of a {@link ILttngAgent}.
@@ -55,7 +57,7 @@ public abstract class AbstractLttngAgent<T extends ILttngHandler>
         * falls to 0, this means we can avoid sending log events through JNI
         * because nobody wants them.
         *
-        * It uses a concurrent hash set", so that the {@link #isEventEnabled} and
+        * It uses a concurrent hash map, so that the {@link #isEventEnabled} and
         * read methods do not need to take a synchronization lock.
         */
        private final Map<String, Integer> enabledEvents = new ConcurrentHashMap<String, Integer>();
@@ -74,6 +76,19 @@ public abstract class AbstractLttngAgent<T extends ILttngHandler>
        /** Number of sessions currently enabling the wildcard "*" event */
        private final AtomicInteger enabledWildcards = new AtomicInteger(0);
 
+       /**
+        * The application contexts currently enabled in the tracing sessions.
+        *
+        * It is first indexed by context retriever, then by context name. This
+        * allows to efficiently query all the contexts for a given retriever.
+        *
+        * Works similarly as {@link #enabledEvents}, but for app contexts (and with
+        * an extra degree of indexing).
+        *
+        * TODO Could be changed to a Guava Table once/if we start using it.
+        */
+       private final Map<String, Map<String, Integer>> enabledAppContexts = new ConcurrentHashMap<String, Map<String, Integer>>();
+
        /** Tracing domain. Defined by the sub-classes via the constructor. */
        private final Domain domain;
 
@@ -134,6 +149,9 @@ public abstract class AbstractLttngAgent<T extends ILttngHandler>
                if (initialized) {
                        return;
                }
+
+               LttngUstAgentLogger.log(AbstractLttngAgent.class, "Initializing Agent for domain: " + domain.name());
+
                String rootClientThreadName = "Root sessiond client started by agent: " + this.getClass().getSimpleName();
 
                rootSessiondClient = new LttngTcpSessiondClient(this, getDomain().value(), true);
@@ -160,6 +178,8 @@ public abstract class AbstractLttngAgent<T extends ILttngHandler>
         * Dispose the agent
         */
        private void dispose() {
+               LttngUstAgentLogger.log(AbstractLttngAgent.class, "Disposing Agent for domain: " + domain.name());
+
                /*
                 * Only called from a synchronized (registeredHandlers) block, should
                 * not need additional synchronization.
@@ -179,33 +199,70 @@ public abstract class AbstractLttngAgent<T extends ILttngHandler>
                userSessiondClient = null;
                userSessiondClientThread = null;
 
-               /* Reset all enabled event counts to 0 */
+               /*
+                * Send filter change notifications for all event rules currently
+                * active, then clear them.
+                */
+               FilterChangeNotifier fcn = FilterChangeNotifier.getInstance();
+
+               for (Map.Entry<String, Integer> entry : enabledEvents.entrySet()) {
+                       String eventName = entry.getKey();
+                       Integer nb = entry.getValue();
+                       for (int i = 0; i < nb.intValue(); i++) {
+                               fcn.removeEventRules(eventName);
+                       }
+               }
                enabledEvents.clear();
+
+               for (Map.Entry<String, Integer> entry : enabledEventPrefixes.entrySet()) {
+                       /* Re-add the * at the end, the FCN tracks the rules that way */
+                       String eventName = (entry.getKey() + "*");
+                       Integer nb = entry.getValue();
+                       for (int i = 0; i < nb.intValue(); i++) {
+                               fcn.removeEventRules(eventName);
+                       }
+               }
                enabledEventPrefixes.clear();
-               enabledWildcards.set(0);
 
-               initialized = false;
+               int wildcardRules = enabledWildcards.getAndSet(0);
+               for (int i = 0; i < wildcardRules; i++) {
+                       fcn.removeEventRules(WILDCARD);
+               }
+
+               /*
+                * Also clear tracked app contexts (no filter notifications sent for
+                * those currently).
+                */
+               enabledAppContexts.clear();
 
+               initialized = false;
        }
 
        @Override
-       public boolean eventEnabled(String eventName) {
+       public boolean eventEnabled(EventRule eventRule) {
+               /* Notify the filter change manager of the command */
+               FilterChangeNotifier.getInstance().addEventRule(eventRule);
+
+               String eventName = eventRule.getEventName();
+
                if (eventName.equals(WILDCARD)) {
                        enabledWildcards.incrementAndGet();
                        return true;
                }
-
                if (eventName.endsWith(WILDCARD)) {
                        /* Strip the "*" from the name. */
                        String prefix = eventName.substring(0, eventName.length() - 1);
-                       return incrementEventCount(prefix, enabledEventPrefixes);
+                       return incrementRefCount(prefix, enabledEventPrefixes);
                }
 
-               return incrementEventCount(eventName, enabledEvents);
+               return incrementRefCount(eventName, enabledEvents);
        }
 
        @Override
        public boolean eventDisabled(String eventName) {
+               /* Notify the filter change manager of the command */
+               FilterChangeNotifier.getInstance().removeEventRules(eventName);
+
                if (eventName.equals(WILDCARD)) {
                        int newCount = enabledWildcards.decrementAndGet();
                        if (newCount < 0) {
@@ -219,26 +276,52 @@ public abstract class AbstractLttngAgent<T extends ILttngHandler>
                if (eventName.endsWith(WILDCARD)) {
                        /* Strip the "*" from the name. */
                        String prefix = eventName.substring(0, eventName.length() - 1);
-                       return decrementEventCount(prefix, enabledEventPrefixes);
+                       return decrementRefCount(prefix, enabledEventPrefixes);
                }
 
-               return decrementEventCount(eventName, enabledEvents);
+               return decrementRefCount(eventName, enabledEvents);
        }
 
        @Override
-       public Iterable<String> listEnabledEvents() {
-               List<String> events = new LinkedList<String>();
+       public boolean appContextEnabled(String contextRetrieverName, String contextName) {
+               synchronized (enabledAppContexts) {
+                       Map<String, Integer> retrieverMap = enabledAppContexts.get(contextRetrieverName);
+                       if (retrieverMap == null) {
+                               /* There is no submap for this retriever, let's create one. */
+                               retrieverMap = new ConcurrentHashMap<String, Integer>();
+                               enabledAppContexts.put(contextRetrieverName, retrieverMap);
+                       }
 
-               if (enabledWildcards.get() > 0) {
-                       events.add(WILDCARD);
+                       return incrementRefCount(contextName, retrieverMap);
                }
-               for (String prefix : enabledEventPrefixes.keySet()) {
-                       events.add(new String(prefix + WILDCARD));
+       }
+
+       @Override
+       public boolean appContextDisabled(String contextRetrieverName, String contextName) {
+               synchronized (enabledAppContexts) {
+                       Map<String, Integer> retrieverMap = enabledAppContexts.get(contextRetrieverName);
+                       if (retrieverMap == null) {
+                               /* There was no submap for this retriever, invalid command? */
+                               return false;
+                       }
+
+                       boolean ret = decrementRefCount(contextName, retrieverMap);
+
+                       /* If the submap is now empty we can remove it from the main map. */
+                       if (retrieverMap.isEmpty()) {
+                               enabledAppContexts.remove(contextRetrieverName);
+                       }
+
+                       return ret;
                }
-               events.addAll(enabledEvents.keySet());
-               return events;
        }
 
+       /*
+        * Implementation of this method is domain-specific.
+        */
+       @Override
+       public abstract Collection<String> listAvailableEvents();
+
        @Override
        public boolean isEventEnabled(String eventName) {
                /* If at least one session enabled the "*" wildcard, send the event */
@@ -260,12 +343,17 @@ public abstract class AbstractLttngAgent<T extends ILttngHandler>
                return false;
        }
 
-       private static boolean incrementEventCount(String eventName, Map<String, Integer> eventMap) {
-               synchronized (eventMap) {
-                       Integer count = eventMap.get(eventName);
+       @Override
+       public Collection<Map.Entry<String, Map<String, Integer>>> getEnabledAppContexts() {
+               return enabledAppContexts.entrySet();
+       }
+
+       private static boolean incrementRefCount(String key, Map<String, Integer> refCountMap) {
+               synchronized (refCountMap) {
+                       Integer count = refCountMap.get(key);
                        if (count == null) {
                                /* This is the first instance of this event being enabled */
-                               eventMap.put(eventName, Integer.valueOf(1));
+                               refCountMap.put(key, Integer.valueOf(1));
                                return true;
                        }
                        if (count.intValue() <= 0) {
@@ -273,14 +361,14 @@ public abstract class AbstractLttngAgent<T extends ILttngHandler>
                                throw new IllegalStateException();
                        }
                        /* The event was already enabled, increment its refcount */
-                       eventMap.put(eventName, Integer.valueOf(count.intValue() + 1));
+                       refCountMap.put(key, Integer.valueOf(count.intValue() + 1));
                        return true;
                }
        }
 
-       private static boolean decrementEventCount(String eventName, Map<String, Integer> eventMap) {
-               synchronized (eventMap) {
-                       Integer count = eventMap.get(eventName);
+       private static boolean decrementRefCount(String key, Map<String, Integer> refCountMap) {
+               synchronized (refCountMap) {
+                       Integer count = refCountMap.get(key);
                        if (count == null || count.intValue() <= 0) {
                                /*
                                 * The sessiond asked us to disable an event that was not
@@ -293,14 +381,14 @@ public abstract class AbstractLttngAgent<T extends ILttngHandler>
                                 * This is the last instance of this event being disabled,
                                 * remove it from the map so that we stop sending it.
                                 */
-                               eventMap.remove(eventName);
+                               refCountMap.remove(key);
                                return true;
                        }
                        /*
                         * Other sessions are still looking for this event, simply decrement
                         * its refcount.
                         */
-                       eventMap.put(eventName, Integer.valueOf(count.intValue() - 1));
+                       refCountMap.put(key, Integer.valueOf(count.intValue() - 1));
                        return true;
                }
        }
This page took 0.028028 seconds and 4 git commands to generate.