X-Git-Url: http://git.lttng.org/?a=blobdiff_plain;f=liblttng-ust-java-agent%2Fjava%2Flttng-ust-agent-common%2Forg%2Flttng%2Fust%2Fagent%2FAbstractLttngAgent.java;h=acbdc4f124512225828f900369522db6b81348d8;hb=c0c0989ab70574e09b2f7e8b48c2da6af664a849;hp=ef3b1dbe9afddddd5922a0e91d9999254c40209e;hpb=3daf5cba66edbd7d8b608be1372a1665189b3c67;p=lttng-ust.git diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/AbstractLttngAgent.java b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/AbstractLttngAgent.java index ef3b1dbe..acbdc4f1 100644 --- a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/AbstractLttngAgent.java +++ b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/AbstractLttngAgent.java @@ -1,36 +1,28 @@ /* - * Copyright (C) 2015 - EfficiOS Inc., Alexandre Montplaisir - * Copyright (C) 2013 - David Goulet + * SPDX-License-Identifier: LGPL-2.1-only * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License, version 2.1 only, - * as published by the Free Software Foundation. - * - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Copyright (C) 2015 EfficiOS Inc. + * Copyright (C) 2015 Alexandre Montplaisir + * Copyright (C) 2013 David Goulet */ package org.lttng.ust.agent; +import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; import java.util.Map; -import java.util.NavigableMap; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentSkipListMap; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.regex.Matcher; 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}. @@ -42,7 +34,6 @@ import org.lttng.ust.agent.session.EventRule; public abstract class AbstractLttngAgent implements ILttngAgent, ILttngTcpClientListener { - private static final String WILDCARD = "*"; private static final int INIT_TIMEOUT = 3; /* Seconds */ /** The handlers registered to this agent */ @@ -51,29 +42,44 @@ public abstract class AbstractLttngAgent /** * The trace events currently enabled in the sessions. * - * The key represents the event name, the value is the ref count (how many - * different sessions currently have this event enabled). Once the ref count - * falls to 0, this means we can avoid sending log events through JNI - * because nobody wants them. + * The key is the {@link EventNamePattern} that comes from the event name. + * The value is the ref count (how many different sessions currently have + * this event enabled). Once the ref count falls to 0, this means we can + * avoid sending log events through JNI because nobody wants them. * - * It uses a concurrent hash map, so that the {@link #isEventEnabled} and - * read methods do not need to take a synchronization lock. + * Its accesses should be protected by the {@link #enabledEventNamesLock} + * below. */ - private final Map enabledEvents = new ConcurrentHashMap(); + private final Map enabledPatterns = new HashMap(); /** - * The trace events prefixes currently enabled in the sessions, which means - * the event names finishing in *, like "abcd*". We track them separately - * from the standard event names, so that we can use {@link String#equals} - * and {@link String#startsWith} appropriately. + * Cache of already-checked event names. As long as enabled/disabled events + * don't change in the session, we can avoid re-checking events that were + * previously checked against all known enabled patterns. * - * We track the lone wildcard "*" separately, in {@link #enabledWildcards}. + * Its accesses should be protected by the {@link #enabledEventNamesLock} + * below, with the exception of concurrent get operations. + */ + private final Map enabledEventNamesCache = new ConcurrentHashMap(); + + /** + * Lock protecting accesses to the {@link #enabledPatterns} and + * {@link #enabledEventNamesCache} maps. */ - private final NavigableMap enabledEventPrefixes = - new ConcurrentSkipListMap(); + private final Lock enabledEventNamesLock = new ReentrantLock(); - /** 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> enabledAppContexts = new ConcurrentHashMap>(); /** Tracing domain. Defined by the sub-classes via the constructor. */ private final Domain domain; @@ -135,6 +141,9 @@ public abstract class AbstractLttngAgent 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); @@ -161,6 +170,8 @@ public abstract class AbstractLttngAgent * 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. @@ -180,94 +191,159 @@ public abstract class AbstractLttngAgent userSessiondClient = null; userSessiondClientThread = null; - /* Reset all enabled event counts to 0 */ - enabledEvents.clear(); - enabledEventPrefixes.clear(); - enabledWildcards.set(0); + /* + * Send filter change notifications for all event rules currently + * active, then clear them. + */ + FilterChangeNotifier fcn = FilterChangeNotifier.getInstance(); - initialized = false; + enabledEventNamesLock.lock(); + try { + for (Map.Entry entry : enabledPatterns.entrySet()) { + String eventName = entry.getKey().getEventName(); + Integer nb = entry.getValue(); + for (int i = 0; i < nb.intValue(); i++) { + fcn.removeEventRules(eventName); + } + } + enabledPatterns.clear(); + enabledEventNamesCache.clear(); + } finally { + enabledEventNamesLock.unlock(); + } + + /* + * Also clear tracked app contexts (no filter notifications sent for + * those currently). + */ + enabledAppContexts.clear(); + initialized = false; } @Override public boolean eventEnabled(EventRule eventRule) { + /* Notify the filter change manager of the command */ + FilterChangeNotifier.getInstance().addEventRule(eventRule); + String eventName = eventRule.getEventName(); + EventNamePattern pattern = new EventNamePattern(eventName); - 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); + enabledEventNamesLock.lock(); + try { + boolean ret = incrementRefCount(pattern, enabledPatterns); + enabledEventNamesCache.clear(); + return ret; + } finally { + enabledEventNamesLock.unlock(); } - - return incrementEventCount(eventName, enabledEvents); } @Override public boolean eventDisabled(String eventName) { - if (eventName.equals(WILDCARD)) { - int newCount = enabledWildcards.decrementAndGet(); - if (newCount < 0) { - /* Event was not enabled, bring the count back to 0 */ - enabledWildcards.incrementAndGet(); - return false; - } - return true; - } + /* Notify the filter change manager of the command */ + FilterChangeNotifier.getInstance().removeEventRules(eventName); - if (eventName.endsWith(WILDCARD)) { - /* Strip the "*" from the name. */ - String prefix = eventName.substring(0, eventName.length() - 1); - return decrementEventCount(prefix, enabledEventPrefixes); - } + EventNamePattern pattern = new EventNamePattern(eventName); - return decrementEventCount(eventName, enabledEvents); + enabledEventNamesLock.lock(); + try { + boolean ret = decrementRefCount(pattern, enabledPatterns); + enabledEventNamesCache.clear(); + return ret; + } finally { + enabledEventNamesLock.unlock(); + } } @Override - public Iterable listEnabledEvents() { - List events = new LinkedList(); + public boolean appContextEnabled(String contextRetrieverName, String contextName) { + synchronized (enabledAppContexts) { + Map retrieverMap = enabledAppContexts.get(contextRetrieverName); + if (retrieverMap == null) { + /* There is no submap for this retriever, let's create one. */ + retrieverMap = new ConcurrentHashMap(); + 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 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 listAvailableEvents(); + @Override public boolean isEventEnabled(String eventName) { - /* If at least one session enabled the "*" wildcard, send the event */ - if (enabledWildcards.get() > 0) { - return true; + Boolean cachedEnabled = enabledEventNamesCache.get(eventName); + if (cachedEnabled != null) { + /* We have seen this event previously */ + /* + * Careful! enabled == null could also mean that the null value is + * associated with the key. But we should have never inserted null + * values in the map. + */ + return cachedEnabled.booleanValue(); } - /* Check if at least one session wants this exact event name */ - if (enabledEvents.containsKey(eventName)) { - return true; - } + /* + * We have not previously checked this event. Run it against all known + * enabled event patterns to determine if it should pass or not. + */ + enabledEventNamesLock.lock(); + try { + boolean enabled = false; + for (EventNamePattern enabledPattern : enabledPatterns.keySet()) { + Matcher matcher = enabledPattern.getPattern().matcher(eventName); + if (matcher.matches()) { + enabled = true; + break; + } + } - /* Look in the enabled prefixes if one of them matches the event */ - String potentialMatch = enabledEventPrefixes.floorKey(eventName); - if (potentialMatch != null && eventName.startsWith(potentialMatch)) { - return true; + /* Add the result to the cache */ + enabledEventNamesCache.put(eventName, Boolean.valueOf(enabled)); + return enabled; + + } finally { + enabledEventNamesLock.unlock(); } + } - return false; + @Override + public Collection>> getEnabledAppContexts() { + return enabledAppContexts.entrySet(); } - private static boolean incrementEventCount(String eventName, Map eventMap) { - synchronized (eventMap) { - Integer count = eventMap.get(eventName); + private static boolean incrementRefCount(T key, Map 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) { @@ -275,14 +351,14 @@ public abstract class AbstractLttngAgent 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 eventMap) { - synchronized (eventMap) { - Integer count = eventMap.get(eventName); + private static boolean decrementRefCount(T key, Map 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 @@ -295,14 +371,14 @@ public abstract class AbstractLttngAgent * 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; } }