Mercurial > hg > release > thermostat-1.6
changeset 1932:9e860337de11
Backport StackTraceProfiler
review-thread: http://icedtea.classpath.org/pipermail/thermostat/2016-June/019712.html
reviewwed-by: jerboaa
PR3037
line wrap: on
line diff
--- a/experimental/components/pom.xml Thu Jun 23 14:51:52 2016 +0200 +++ b/experimental/components/pom.xml Thu Jun 23 14:51:53 2016 +0200 @@ -103,8 +103,7 @@ <configuration> <instructions> <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor> - <Bundle-SymbolicName>com.redhat.thermostat.experimental.components - </Bundle-SymbolicName> + <Bundle-SymbolicName>com.redhat.thermostat.experimental.components</Bundle-SymbolicName> <Export-Package> com.redhat.thermostat.experimental.components, com.redhat.thermostat.experimental.components.graph,
--- a/thread/client-common/pom.xml Thu Jun 23 14:51:52 2016 +0200 +++ b/thread/client-common/pom.xml Thu Jun 23 14:51:53 2016 +0200 @@ -101,7 +101,20 @@ <version>${project.version}</version> <scope>test</scope> </dependency> - + + <dependency> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-experimental-collections</artifactId> + <version>1.5.8-SNAPSHOT</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-experimental-components</artifactId> + <version>${parent.version}</version> + </dependency> + </dependencies> <build>
--- a/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/locale/LocaleResources.java Thu Jun 23 14:51:52 2016 +0200 +++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/locale/LocaleResources.java Thu Jun 23 14:51:53 2016 +0200 @@ -84,6 +84,8 @@ ZOOM_IN, ZOOM_OUT, + STACK_TRACE_PROFILER, + ; public static final String RESOURCE_BUNDLE =
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/view/StackTraceProfilerView.java Thu Jun 23 14:51:53 2016 +0200 @@ -0,0 +1,49 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.thread.client.common.view; + +import com.redhat.thermostat.client.core.views.BasicView; +import com.redhat.thermostat.experimental.model.Trace; + +/** + */ +public abstract class StackTraceProfilerView extends BasicView { + public abstract void createModel(String modelID); + public abstract void rebuild(); + public abstract void addTrace(Trace trace); + public abstract void clear(); +}
--- a/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/view/ThreadView.java Thu Jun 23 14:51:52 2016 +0200 +++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/view/ThreadView.java Thu Jun 23 14:51:53 2016 +0200 @@ -125,5 +125,6 @@ public abstract VmDeadLockView createDeadLockView(); public abstract void displayTimelineSessionList(List<ThreadSession> threadSessions); + + public abstract StackTraceProfilerView createStackTraceProfilerView(); } -
--- a/thread/client-common/src/main/resources/com/redhat/thermostat/thread/client/common/locale/strings.properties Thu Jun 23 14:51:52 2016 +0200 +++ b/thread/client-common/src/main/resources/com/redhat/thermostat/thread/client/common/locale/strings.properties Thu Jun 23 14:51:53 2016 +0200 @@ -43,3 +43,5 @@ THREAD_MONITOR_DISPLAY_SESSIONS = List Recordings RECORDING_LIST = RECORDING_LIST = List of Recorded Thread Timelines RECORDING_LIST_HINT = Open the list of Recoded Thread Timeline Sessions + +STACK_TRACE_PROFILER = Stack Trace Profiler \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/SessionCheckingAction.java Thu Jun 23 14:51:53 2016 +0200 @@ -0,0 +1,153 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.thread.client.controller.impl; + +import com.redhat.thermostat.common.model.Range; +import com.redhat.thermostat.thread.model.SessionID; + +import java.util.concurrent.TimeUnit; + +/** + */ +public abstract class SessionCheckingAction implements Runnable { + + private SessionID lastSession; + private long lastUpdate; + + public SessionCheckingAction() { + lastUpdate = getTimeDeltaOnNewSession(); + } + + protected long getTimeDeltaOnNewSession() { + return System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1); + } + + /** + * Returns the sessions ID the user wants to track, which can be null. + */ + protected abstract SessionID getCurrentSessionID(); + + /** + * Returns the last available sessions ID. This is typically the sessions + * with the most recent timestamp from a set of sessions. + */ + protected abstract SessionID getLastAvailableSessionID() ; + + /** + * Returns the sessions id to track by this action, which is either the + * session returned by {@link #getCurrentSessionID()} or, if null, + * the one returned by {@link #getLastAvailableSessionID()}. + */ + protected SessionID getSessionID() { + SessionID session = getCurrentSessionID(); + if (session == null) { + // no session selected, but let's try to default to the last + // available + session = getLastAvailableSessionID(); + } + return session; + } + + + /** + * The actual action to perform. + */ + protected abstract void actionPerformed(SessionID session, + Range<Long> range, + Range<Long> totalRange); + + protected abstract Range<Long> getTotalRange(SessionID session); + + protected Range<Long> getRange(SessionID session, Range<Long> totalRange) { + if (totalRange == null) { + // there is range to check + return null; + } + + if (lastUpdate == totalRange.getMax()) { + // we already covered this range + return null; + } + + Range<Long> result = new Range<>(lastUpdate, totalRange.getMax()); + lastUpdate = totalRange.getMax(); + return result; + } + + protected boolean isRangeValid(Range<Long> range, Range<Long> totalRange) { + return totalRange != null && range != null; + } + + protected boolean isSessionValid(SessionID session) { + return session != null; + } + + /** + * Determine if the session in input is a new session. By default the + * implementation checks the sessions against the last session. + */ + protected boolean isNewSession(SessionID session) { + boolean result = (lastSession == null || !session.get().equals(lastSession.get())); + lastSession = session; + return result; + } + + /** + * Called when a new session is detected, as defined by + * {@link #isNewSession(SessionID)}. + */ + protected void onNewSession() {} + + @Override + public void run() { + SessionID session = getSessionID(); + if (!isSessionValid(session)) { + return; + } + + if (isNewSession(session)) { + lastUpdate = getTimeDeltaOnNewSession(); + onNewSession(); + } + + Range<Long> totalRange = getTotalRange(session); + Range<Long> range = getRange(session, totalRange); + if (isRangeValid(range, totalRange)) { + actionPerformed(session, range, totalRange); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/StackTraceProfilerController.java Thu Jun 23 14:51:53 2016 +0200 @@ -0,0 +1,171 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.thread.client.controller.impl; + +import com.redhat.thermostat.common.Timer; +import com.redhat.thermostat.common.model.Range; +import com.redhat.thermostat.experimental.model.Trace; +import com.redhat.thermostat.experimental.model.TraceElement; +import com.redhat.thermostat.storage.core.VmRef; +import com.redhat.thermostat.storage.core.experimental.statement.ResultHandler; +import com.redhat.thermostat.thread.client.common.collector.ThreadCollector; +import com.redhat.thermostat.thread.client.common.view.StackTraceProfilerView; +import com.redhat.thermostat.thread.model.SessionID; +import com.redhat.thermostat.thread.model.StackFrame; +import com.redhat.thermostat.thread.model.StackTrace; +import com.redhat.thermostat.thread.model.ThreadState; + +import java.util.List; + +/** + */ +public class StackTraceProfilerController extends CommonController { + + private ThreadCollector collector; + private StackTraceProfilerView stackTraceProfilerView; + public StackTraceProfilerController(StackTraceProfilerView view, + ThreadCollector collector, + Timer timer, + VmRef ref) + { + super(timer, view); + this.stackTraceProfilerView = view; + this.collector = collector; + + view.createModel(ref.getName()); + + timer.setAction(new StackTraceProfilerControllerAction()); + } + + private class StackTraceProfilerControllerAction extends SessionCheckingAction { + + private ThreadStateResultHandler threadStateResultHandler; + + public StackTraceProfilerControllerAction() { + threadStateResultHandler = new ThreadStateResultHandler(); + resetState(); + } + + private void resetState() { + stackTraceProfilerView.clear(); + } + + @Override + protected SessionID getCurrentSessionID() { + return session; + } + + @Override + protected SessionID getLastAvailableSessionID() { + return collector.getLastThreadSession(); + } + + @Override + protected Range<Long> getTotalRange(SessionID session) { + return collector.getThreadRange(session); + } + + @Override + protected void actionPerformed(SessionID session, Range<Long> range, + Range<Long> totalRange) + { + collector.getThreadStates(session, threadStateResultHandler, range); + stackTraceProfilerView.rebuild(); + } + + @Override + protected void onNewSession() { + resetState(); + } + } + + // for testing + StackTrace getStackTrace(ThreadState state) { + return StackTrace.fromJson(state.getStackTrace()); + } + + private volatile boolean stopLooping; + + @Override + protected void onViewVisible() { + stopLooping = false; + } + + @Override + protected void onViewHidden() { + stopLooping = true; + } + + private class ThreadStateResultHandler implements ResultHandler<ThreadState> { + + @Override + public boolean onResult(ThreadState state) { + + Thread.State threadState = Thread.State.valueOf(state.getState()); + switch (threadState) { + // all other cases, we want to track this thread + case BLOCKED: + case WAITING: + case TIMED_WAITING: + case TERMINATED: + return true; + } + + StackTrace stackTrace = getStackTrace(state); + + Trace trace = new Trace(state.getName()); + List<StackFrame> frames = stackTrace.getFrames(); + int frameID = frames.size() - 1; + + while (frameID >= 0) { + StackFrame frame = frames.get(frameID); + frameID--; + + TraceElement element = + new TraceElement(frame.getClassName() + "." + + frame.getMethodName() + ":" + + frame.getLineNumber()); + trace.add(element); + } + + stackTraceProfilerView.addTrace(trace); + + boolean _stopLooping = stopLooping; + return !_stopLooping; + } + } +}
--- a/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationController.java Thu Jun 23 14:51:52 2016 +0200 +++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationController.java Thu Jun 23 14:51:53 2016 +0200 @@ -86,6 +86,7 @@ private CommonController threadTableController; private CommonController threadCountController; private VmDeadLockController deadLockController; + private CommonController stackTraceProfilerController; public ThreadInformationController(VmRef ref, ApplicationService appService, VmInfoDAO vmInfoDao, @@ -152,6 +153,7 @@ if (session != null) { threadTimeline.setSession(session.getSessionID()); threadTableController.setSession(session.getSessionID()); + stackTraceProfilerController.setSession(session.getSessionID()); } } break; @@ -273,6 +275,14 @@ tf.createTimer()); threadTimeline.setSession(lastThreadSession); threadTimeline.initialize(); + + stackTraceProfilerController = + new StackTraceProfilerController(view.createStackTraceProfilerView(), + collector, + tf.createTimer(), + ref); + stackTraceProfilerController.initialize(); + stackTraceProfilerController.setSession(lastThreadSession); } void ___injectControllersForTesting(ThreadTimelineController timeline,
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/SessionCheckingActionTest.java Thu Jun 23 14:51:53 2016 +0200 @@ -0,0 +1,119 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.thread.client.controller.impl; + +import com.redhat.thermostat.common.model.Range; +import com.redhat.thermostat.thread.model.SessionID; +import org.junit.Test; + +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertEquals; + +/** + */ +public class SessionCheckingActionTest { + + private static final long ONE_HOUR = TimeUnit.HOURS.toMillis(1); + + @Test + public void getRangeOnlyReturnsDelta() throws Exception { + + final long[] delta = new long[1]; + final Range<Long> [] results = new Range [2]; + + SessionCheckingAction action = new SessionCheckingAction() { + + @Override + protected Range<Long> getTotalRange(SessionID session) { + Range<Long> range = new Range<>(0l, delta[0] + ONE_HOUR); + return range; + } + + @Override + protected boolean isNewSession(SessionID session) { + return false; + } + + @Override + protected boolean isSessionValid(SessionID session) { + return true; + } + + @Override + protected long getTimeDeltaOnNewSession() { + return 0; + } + + @Override + protected SessionID getCurrentSessionID() { + return null; + } + + @Override + protected SessionID getLastAvailableSessionID() { + return null; + } + + @Override + protected void actionPerformed(SessionID session, Range<Long> range, Range<Long> totalRange) { + results[0] = range; + results[1] = totalRange; + } + }; + + action.run(); + + Range<Long> range = results[0]; + Range<Long> totalRange = results[1]; + + // the first run, we should get the full range + assertEquals(range, totalRange); + + // simulate an elapsed second + delta[0] = 1000; + long lastMax = totalRange.getMax(); + + action.run(); + + range = results[0]; + totalRange = results[1]; + + assertEquals(lastMax, (long) range.getMin()); + assertEquals((long) totalRange.getMax(), (long) range.getMax()); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/StackTraceProfilerControllerTest.java Thu Jun 23 14:51:53 2016 +0200 @@ -0,0 +1,171 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.thread.client.controller.impl; + +import com.redhat.thermostat.common.ActionListener; +import com.redhat.thermostat.common.Timer; +import com.redhat.thermostat.common.model.Range; +import com.redhat.thermostat.experimental.model.Trace; +import com.redhat.thermostat.storage.core.VmRef; +import com.redhat.thermostat.storage.core.experimental.statement.ResultHandler; +import com.redhat.thermostat.thread.client.common.collector.ThreadCollector; +import com.redhat.thermostat.thread.client.common.view.StackTraceProfilerView; +import com.redhat.thermostat.thread.model.SessionID; +import com.redhat.thermostat.thread.model.StackFrame; +import com.redhat.thermostat.thread.model.StackTrace; +import com.redhat.thermostat.thread.model.ThreadState; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + */ +public class StackTraceProfilerControllerTest { + private Timer timer; + private Runnable threadAction; + private StackTraceProfilerView view; + private ThreadCollector collector; + + private ActionListener<StackTraceProfilerView.Action> actionListener; + + private VmRef ref; + + private static final String VM_ID = "42"; + + private SessionID session; + + @Before + public void setUp() { + timer = mock(Timer.class); + view = mock(StackTraceProfilerView.class); + collector = mock(ThreadCollector.class); + ref = mock(VmRef.class); + when(ref.getName()).thenReturn(VM_ID); + + session = mock(SessionID.class); + } + + @Test + public void testModelCreated() { + StackTraceProfilerController profiler = + new StackTraceProfilerController(view, collector, timer, ref); + + verify(view).createModel(VM_ID); + } + + @Test + public void testGetThreadTraces() { + + final ThreadState state0 = mock(ThreadState.class); + when(state0.getName()).thenReturn("state0"); + when(state0.getId()).thenReturn(0l); + when(state0.getState()).thenReturn("NEW"); + final StackTrace trace0 = mock(StackTrace.class); + + ThreadState state1 = mock(ThreadState.class); + when(state1.getName()).thenReturn("state1"); + when(state1.getId()).thenReturn(1l); + when(state1.getState()).thenReturn("BLOCKED"); + StackTrace trace1 = mock(StackTrace.class); + + ThreadState state2 = mock(ThreadState.class); + when(state2.getName()).thenReturn("state2"); + when(state2.getId()).thenReturn(2l); + when(state2.getState()).thenReturn("WAITING"); + StackTrace trace2 = mock(StackTrace.class); + + ArgumentCaptor<SessionCheckingAction> captor = ArgumentCaptor.forClass(SessionCheckingAction.class); + doNothing().when(timer).setAction(captor.capture()); + ArgumentCaptor<ResultHandler> resultHandlerCaptor = ArgumentCaptor.forClass(ResultHandler.class); + + Range<Long> range = new Range<>(0l, 10l); + when(collector.getThreadRange(session)).thenReturn(range); + + StackTraceProfilerController profiler = + new StackTraceProfilerController(view, collector, timer, ref) { + @Override + StackTrace getStackTrace(ThreadState state) { + // the other are blocked or waiting + assertEquals(state, state0); + return trace0; + } + }; + + SessionCheckingAction action = captor.getValue(); + assertNotNull(action); + + action.actionPerformed(session, range, range); + + verify(view).rebuild(); + verify(collector).getThreadStates(any(SessionID.class), resultHandlerCaptor.capture(), any(Range.class)); + + StackFrame frame0 = new StackFrame("file", "package", "class", "method0", 1, false); + StackFrame frame1 = new StackFrame("file", "package", "class", "method1", 2, false); + + List<StackFrame> frames = new ArrayList<>(); + frames.add(frame0); + frames.add(frame1); + + when(trace0.getFrames()).thenReturn(frames); + + ArgumentCaptor<Trace> traceCaptor = ArgumentCaptor.forClass(Trace.class); + + ResultHandler handler = resultHandlerCaptor.getValue(); + handler.onResult(state0); + handler.onResult(state1); + handler.onResult(state2); + + verify(view).addTrace(traceCaptor.capture()); + + Trace trace = traceCaptor.getValue(); + assertEquals("state0", trace.getName()); + assertEquals(2, trace.getTrace().size()); + assertEquals("class.method1:2", trace.getTrace().get(0).getName()); + assertEquals("class.method0:1", trace.getTrace().get(1).getName()); + } +}
--- a/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationControllerTest.java Thu Jun 23 14:51:52 2016 +0200 +++ b/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationControllerTest.java Thu Jun 23 14:51:53 2016 +0200 @@ -52,6 +52,7 @@ import com.redhat.thermostat.thread.client.common.ThreadViewProvider; import com.redhat.thermostat.thread.client.common.collector.ThreadCollector; import com.redhat.thermostat.thread.client.common.collector.ThreadCollectorFactory; +import com.redhat.thermostat.thread.client.common.view.StackTraceProfilerView; import com.redhat.thermostat.thread.client.common.view.ThreadCountView; import com.redhat.thermostat.thread.client.common.view.ThreadTableView; import com.redhat.thermostat.thread.client.common.view.ThreadTableView.ThreadSelectionAction; @@ -92,11 +93,14 @@ private VmInfoDAO vmInfoDao; private ThreadTableView threadTableView; + private StackTraceProfilerView stackTraceProfilerView; private VmDeadLockView deadLockView; private ThreadTimelineView threadTimelineView; private ThreadCountView threadCountView; private ExecutorService appExecutor; + + @Before public void setUp() { @@ -111,6 +115,7 @@ } private void setUpView() { + stackTraceProfilerView = mock(StackTraceProfilerView.class); deadLockView = mock(VmDeadLockView.class); threadTableView = mock(ThreadTableView.class); threadTimelineView = mock(ThreadTimelineView.class); @@ -124,7 +129,7 @@ when(view.createThreadTableView()).thenReturn(threadTableView); when(view.createThreadTimelineView()).thenReturn(threadTimelineView); when(view.createThreadCountView()).thenReturn(threadCountView); - + when(view.createStackTraceProfilerView()).thenReturn(stackTraceProfilerView); } private void setupExecutor() {
--- a/thread/client-swing/pom.xml Thu Jun 23 14:51:52 2016 +0200 +++ b/thread/client-swing/pom.xml Thu Jun 23 14:51:53 2016 +0200 @@ -101,6 +101,12 @@ <version>${project.version}</version> </dependency> + <dependency> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-experimental-components</artifactId> + <version>${parent.version}</version> + </dependency> + </dependencies> <build>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingStackTraceProfilerView.java Thu Jun 23 14:51:53 2016 +0200 @@ -0,0 +1,117 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.thread.client.swing.impl; + +import com.redhat.thermostat.client.swing.SwingComponent; +import com.redhat.thermostat.client.swing.components.ThermostatScrollPane; +import com.redhat.thermostat.client.swing.experimental.ComponentVisibilityNotifier; +import com.redhat.thermostat.experimental.components.graph.GraphContainer; +import com.redhat.thermostat.experimental.model.Trace; +import com.redhat.thermostat.experimental.model.graph.GraphModel; +import com.redhat.thermostat.thread.client.common.view.StackTraceProfilerView; + +import javax.swing.ScrollPaneConstants; +import javax.swing.SwingUtilities; +import java.awt.Component; + +/** + */ +public class SwingStackTraceProfilerView extends StackTraceProfilerView implements SwingComponent { + + private ComponentVisibilityNotifier visibilityNotifier; + + private ThermostatScrollPane contentPane; + private GraphModel graphModel; + private GraphContainer graphContainer; + + public SwingStackTraceProfilerView() { + graphContainer = new GraphContainer(); + + contentPane = new ThermostatScrollPane(graphContainer); + contentPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS); + contentPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); + + visibilityNotifier = new ComponentVisibilityNotifier(); + visibilityNotifier.initialize(graphContainer, notifier); + } + + @Override + public void createModel(final String modelID) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + graphModel = new GraphModel(modelID); + graphContainer.setModel(graphModel); + } + }); + } + + @Override + public void rebuild() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + graphModel.rebuild(); + } + }); + } + + @Override + public void addTrace(final Trace trace) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + graphModel.addTrace(trace); + } + }); + } + + @Override + public void clear() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + graphModel.clear(); + } + }); + } + + @Override + public Component getUiComponent() { + return contentPane; + } +}
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadView.java Thu Jun 23 14:51:52 2016 +0200 +++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadView.java Thu Jun 23 14:51:53 2016 +0200 @@ -45,6 +45,7 @@ import com.redhat.thermostat.shared.locale.Translate; import com.redhat.thermostat.thread.client.common.ThreadTableBean; import com.redhat.thermostat.thread.client.common.locale.LocaleResources; +import com.redhat.thermostat.thread.client.common.view.StackTraceProfilerView; import com.redhat.thermostat.thread.client.common.view.ThreadCountView; import com.redhat.thermostat.thread.client.common.view.ThreadTableView; import com.redhat.thermostat.thread.client.common.view.ThreadTimelineView; @@ -78,6 +79,7 @@ private SwingVmDeadLockView vmDeadLockView; private SwingThreadTimelineView threadTimelineView; private SwingThreadDetailsView threadDetailsView; + private SwingStackTraceProfilerView stackTraceProfilerView; private JTabbedPane topPane; private JTabbedPane bottomPane; @@ -194,7 +196,12 @@ comp = threadTimelineView.getUiComponent(); comp.setName("timeline"); topPane.addTab(t.localize(LocaleResources.TIMELINE).getContents(), comp); - + + stackTraceProfilerView = new SwingStackTraceProfilerView(); + comp = stackTraceProfilerView.getUiComponent(); + comp.setName("stackTraceProfiler"); + topPane.addTab(t.localize(LocaleResources.STACK_TRACE_PROFILER).getContents(), comp); + panel.getSplitPane().setTopComponent(topPane); } @@ -318,5 +325,10 @@ } }); } + + @Override + public StackTraceProfilerView createStackTraceProfilerView() { + return stackTraceProfilerView; + } }
--- a/thread/distribution/thermostat-plugin.xml Thu Jun 23 14:51:52 2016 +0200 +++ b/thread/distribution/thermostat-plugin.xml Thu Jun 23 14:51:53 2016 +0200 @@ -47,6 +47,8 @@ <bundle><symbolic-name>com.redhat.thermostat.thread.client.swing</symbolic-name><version>${project.version}</version></bundle> <bundle><symbolic-name>com.redhat.thermostat.thread.client.controller</symbolic-name><version>${project.version}</version></bundle> <bundle><symbolic-name>com.redhat.thermostat.thread.client.common</symbolic-name><version>${project.version}</version></bundle> + <bundle><symbolic-name>com.redhat.thermostat.experimental.collections</symbolic-name><version>${project.version}</version></bundle> + <bundle><symbolic-name>com.redhat.thermostat.experimental.components</symbolic-name><version>${project.version}</version></bundle> </bundles> </extension> <extension>