/* * Copyright (c) 2010-2012 Grid Dynamics Consulting Services, Inc, All Rights Reserved * http://www.griddynamics.com * * This library is free software; you can redistribute it and/or modify it under the terms of * the Apache License; either * version 2.0 of the License, or any later version. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.griddynamics.jagger.diagnostics.reporting; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.griddynamics.jagger.agent.model.MethodElement; import com.griddynamics.jagger.dbapi.DatabaseService; import com.griddynamics.jagger.dbapi.entity.TaskData; import com.griddynamics.jagger.diagnostics.thread.sampling.InvocationProfile; import com.griddynamics.jagger.diagnostics.thread.sampling.MethodProfile; import com.griddynamics.jagger.diagnostics.thread.sampling.RuntimeGraph; import com.griddynamics.jagger.diagnostics.visualization.GraphVisualizationHelper; import com.griddynamics.jagger.dbapi.entity.ProfilingSuT; import com.griddynamics.jagger.monitoring.reporting.AbstractMonitoringReportProvider; import com.griddynamics.jagger.util.SerializationUtils; import edu.uci.ics.jung.graph.Graph; import net.sf.jasperreports.engine.JRDataSource; import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Required; import java.awt.*; import java.util.*; import java.util.List; /** * @author Alexey Kiselyov * Date: 26.07.11 */ public class ProfileReporter extends AbstractMonitoringReportProvider<String> { private Logger log = LoggerFactory.getLogger(ProfileReporter.class); private boolean enable; private int maxHotSpots; private int maxCallersInGraph; private int maxCallTreeDepth; private int callGraphImageWidth; private int callGraphImageHeight; private boolean renderGraph; private DatabaseService databaseService; @Override public void clearCache() { super.clearCache(); } public boolean isRenderGraph() { return this.renderGraph; } public void setRenderGraph(boolean renderGraph) { this.renderGraph = renderGraph; } public int getMaxHotSpots() { return this.maxHotSpots; } public void setMaxHotSpots(int maxHotSpots) { this.maxHotSpots = maxHotSpots; } public int getMaxCallersInGraph() { return this.maxCallersInGraph; } public void setMaxCallersInGraph(int maxCallersInGraph) { this.maxCallersInGraph = maxCallersInGraph; } public int getMaxCallTreeDepth() { return this.maxCallTreeDepth; } public void setMaxCallTreeDepth(int maxCallTreeDepth) { this.maxCallTreeDepth = maxCallTreeDepth; } public int getCallGraphImageWidth() { return this.callGraphImageWidth; } public void setCallGraphImageWidth(int callGraphImageWidth) { this.callGraphImageWidth = callGraphImageWidth; } public int getCallGraphImageHeight() { return this.callGraphImageHeight; } public void setCallGraphImageHeight(int callGraphImageHeight) { this.callGraphImageHeight = callGraphImageHeight; } @Override public JRDataSource getDataSource(String id, String sessionId) { if (!enable) { return new JRBeanCollectionDataSource(Collections.emptySet()); } Map<String, List<SysUnderTestDTO>> sysUnderTests = loadData(sessionId); List<SysUnderTestDTO> data = null; String taskId = null; Long longId = Long.valueOf(id); Map<Long,TaskData> taskDataMap = databaseService.getTaskData(Arrays.asList(longId)); if (taskDataMap.keySet().contains(longId)) { taskId = taskDataMap.get(longId).getTaskId(); } if (taskId != null) { data = sysUnderTests.get(taskId); // required after monitoring moved to metrics if (data == null) { data = sysUnderTests.get(parentOf(taskId)); } } return new JRBeanCollectionDataSource(data); } @Deprecated private Map<String, List<SysUnderTestDTO>> loadData(String sessionId) { Map<String, List<SysUnderTestDTO>> result = Maps.newTreeMap(); //todo JFG-722 We should delete all queries from reporting-part jagger @SuppressWarnings("unchecked") List<ProfilingSuT> profilingSuTListFull = (List<ProfilingSuT>) getHibernateTemplate() .find("from ProfilingSuT where sessionId=? and taskData_id is not null order by taskData, sysUnderTestUrl", sessionId); for (ProfilingSuT profilingSuT : profilingSuTListFull) { String taskId = profilingSuT.getTaskData().getTaskId(); List<SysUnderTestDTO> sysUnderTestDTOs = result.get(taskId); if (sysUnderTestDTOs == null) { sysUnderTestDTOs = Lists.newArrayList(); result.put(taskId, sysUnderTestDTOs); } String currentSysUnderTestUrl = profilingSuT.getSysUnderTestUrl(); SysUnderTestDTO sysUnderTestDTO = new SysUnderTestDTO(); sysUnderTestDTO.setSysUnderTestUrl(currentSysUnderTestUrl); sysUnderTestDTOs.add(sysUnderTestDTO); RuntimeGraph profiler = SerializationUtils.fromString(profilingSuT.getContext()); List<MethodProfile> profiles = profiler.getSelfTimeHotSpots(maxHotSpots); for (MethodProfile profile : profiles) { Graph<MethodProfile, InvocationProfile> neighborhood = profiler.getNeighborhood(profile.getMethod(), maxCallersInGraph, maxCallTreeDepth); List<MethodElement> traceback = Lists.newArrayList(); assembleTraceBack(profile, neighborhood, traceback, maxCallTreeDepth, 0); traceback.remove(0); HotSpotDTO dto = new HotSpotDTO(); dto.setSysUnderTestUrl(currentSysUnderTestUrl); dto.setMethodId(profile.getMethod().toString()); dto.setInStackRatio(profile.getInStackRatio()); dto.setOnTopRatio(profile.getOnTopRatio()); dto.setTraceback(traceback); if (renderGraph) { Map<MethodProfile, Paint> customPainter = Maps.newHashMap(); customPainter.put(profile, Color.WHITE); Image image = GraphVisualizationHelper.renderGraph( neighborhood, callGraphImageWidth, callGraphImageHeight, GraphVisualizationHelper.GraphLayout.KK, GraphVisualizationHelper.ColorTheme.LIGHT, customPainter); dto.setInvocationGraphImage(image); } sysUnderTestDTO.getHotSpotDTOs().add(dto); } } return result; } public void setEnable(boolean enable) { this.enable = enable; } public static class SysUnderTestDTO extends MethodElement { private String sysUnderTestUrl; private List<HotSpotDTO> hotSpotDTOs = Lists.newLinkedList(); public String getSysUnderTestUrl() { return sysUnderTestUrl; } public void setSysUnderTestUrl(String sysUnderTestUrl) { this.sysUnderTestUrl = sysUnderTestUrl; } public List<HotSpotDTO> getHotSpotDTOs() { return hotSpotDTOs; } public void setHotSpotDTOs(List<HotSpotDTO> hotSpotDTOs) { this.hotSpotDTOs = hotSpotDTOs; } } public static class HotSpotDTO extends MethodElement { private String sysUnderTestUrl; private Image invocationGraphImage; private List<MethodElement> traceback; public Image getInvocationGraphImage() { return invocationGraphImage; } public void setInvocationGraphImage(Image invocationGraphImage) { this.invocationGraphImage = invocationGraphImage; } public List<MethodElement> getTraceback() { return traceback; } public void setTraceback(List<MethodElement> traceback) { this.traceback = traceback; } public String getSysUnderTestUrl() { return sysUnderTestUrl; } public void setSysUnderTestUrl(String sysUnderTestUrl) { this.sysUnderTestUrl = sysUnderTestUrl; } } private void assembleTraceBack(MethodProfile pivot, Graph<MethodProfile, InvocationProfile> neighborhood, List<MethodElement> bag, int maxDepth, int depth) { bag.add(assembleMethodElement(pivot, depth)); if (depth < maxDepth) { Collection<MethodProfile> callers = neighborhood.getPredecessors(pivot); if (callers != null) { List<MethodProfile> callersList = Lists.newArrayList(callers); Collections.sort(callersList, new Comparator<MethodProfile>() { @Override public int compare(MethodProfile p1, MethodProfile p2) { return -Double.compare(p1.getInStackRatio(), p2.getInStackRatio()); } }); for (MethodProfile caller : callersList) { assembleTraceBack(caller, neighborhood, bag, maxDepth, depth + 1); } } } } private static MethodElement assembleMethodElement(MethodProfile profile, int offset) { MethodElement dto = new MethodElement(); dto.setMethodId(StringUtils.repeat(" ", offset) + profile.getMethod().toString()); dto.setInStackRatio(profile.getInStackRatio()); dto.setOnTopRatio(profile.getOnTopRatio()); return dto; } @Required public void setDatabaseService(DatabaseService databaseService) { this.databaseService = databaseService; } }